From 081240b92abe7fa9c4110000e82795e641557998 Mon Sep 17 00:00:00 2001 From: shahor02 Date: Fri, 27 Mar 2026 09:25:06 +0100 Subject: [PATCH 001/285] Fix BasicCCDBManager::isCacheValid(ts) method (#15223) --- CCDB/include/CCDB/BasicCCDBManager.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CCDB/include/CCDB/BasicCCDBManager.h b/CCDB/include/CCDB/BasicCCDBManager.h index fd0fe7aa6d05b..b76beb2576eb3 100644 --- a/CCDB/include/CCDB/BasicCCDBManager.h +++ b/CCDB/include/CCDB/BasicCCDBManager.h @@ -64,8 +64,8 @@ class CCDBManagerInstance bool isValid(long ts) { return ts < endvalidity && ts >= startvalidity; } bool isCacheValid(long ts) { - LOGP(debug, "isCacheValid : {} : {} : {} --> {}", cacheValidFrom, ts, cacheValidUntil, ts < cacheValidUntil && ts >= cacheValidFrom); - return ts < cacheValidUntil && ts >= cacheValidFrom; + LOGP(debug, "isCacheValid : {} : {} : {} --> {}", cacheValidFrom, ts, cacheValidUntil, isValid(ts)); + return ts < cacheValidUntil && isValid(ts); } void clear() { From ea49c665efc212679910be88db82651dfec588a9 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Fri, 27 Mar 2026 10:37:28 +0100 Subject: [PATCH 002/285] ITS3: alignment code (#15161) * ITS3: template detector coord conversion on input type Signed-off-by: Felix Schlepper * ITS3: alignment Signed-off-by: Felix Schlepper * Replace math.h with cmath in AlignmentHierarchy.cxx Removed math.h and added cmath for better compatibility. --------- Signed-off-by: Felix Schlepper --- .../Upgrades/ITS3/alignment/CMakeLists.txt | 37 +- Detectors/Upgrades/ITS3/alignment/README.md | 30 + .../include/ITS3Align/AlignmentHierarchy.h | 339 ++++++ .../include/ITS3Align/AlignmentParams.h | 67 ++ .../include/ITS3Align/AlignmentSensors.h | 41 + .../include/ITS3Align/AlignmentSpec.h | 34 + .../include/ITS3Align/AlignmentTypes.h | 64 ++ .../include/ITS3Align/Deformations.h | 84 -- .../include/ITS3Align/MisalignmentHits.h | 216 ---- .../include/ITS3Align/MisalignmentManager.h | 53 - .../ITS3Align/MisalignmentParameters.h | 93 -- .../alignment/include/ITS3Align/TrackFit.h | 175 +++ .../ITS3/alignment/src/AlignmentHierarchy.cxx | 486 ++++++++ .../ITS3/alignment/src/AlignmentParams.cxx | 13 + .../ITS3/alignment/src/AlignmentSensors.cxx | 201 ++++ .../ITS3/alignment/src/AlignmentSpec.cxx | 1003 +++++++++++++++++ .../ITS3/alignment/src/AlignmentTypes.cxx | 24 + .../ITS3/alignment/src/Deformations.cxx | 41 - .../ITS3/alignment/src/ITS3AlignLinkDef.h | 8 +- .../ITS3/alignment/src/MisalignmentHits.cxx | 368 ------ .../alignment/src/MisalignmentManager.cxx | 195 ---- .../alignment/src/MisalignmentParameters.cxx | 80 -- .../ITS3/alignment/src/alignment-workflow.cxx | 71 ++ .../ITS3/base/include/ITS3Base/ITS3Params.h | 5 - .../include/ITS3Base/SegmentationMosaix.h | 19 +- .../ITS3/base/include/ITS3Base/SpecsV2.h | 2 +- .../Upgrades/ITS3/macros/align/CMakeLists.txt | 6 +- .../ITS3/macros/align/CheckHitResiduals.C | 131 +++ .../macros/align/CreateMisalignmentITS3.C | 94 -- .../ITS3/macros/align/MisAlignGeoITS3.notest | 129 --- .../ITS3/macros/align/ShowCoefficients.C | 333 ------ .../ITS3/macros/align/TestLegendrePol.C | 257 ----- .../include/ITS3Reconstruction/IOUtils.h | 23 +- .../ITS3/reconstruction/src/IOUtils.cxx | 46 +- .../reconstruction/src/TopologyDictionary.cxx | 2 + Detectors/Upgrades/ITS3/study/CMakeLists.txt | 6 +- .../ITS3TrackingStudyParam.h | 18 +- .../include/ITS3TrackingStudy/TrackingStudy.h | 2 +- .../Upgrades/ITS3/study/macros/CMakeLists.txt | 8 + .../ITS3/study/macros/PlotMisalignment.C | 228 ++++ .../ITS3/study/macros/PlotResiduals.C | 70 ++ .../Upgrades/ITS3/study/src/TrackingStudy.cxx | 667 ++++++++--- .../src/its3-tracking-study-workflow.cxx | 8 +- Steer/DigitizerWorkflow/CMakeLists.txt | 1 - .../src/ITS3DigitizerSpec.cxx | 6 - 45 files changed, 3627 insertions(+), 2157 deletions(-) create mode 100644 Detectors/Upgrades/ITS3/alignment/README.md create mode 100644 Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentHierarchy.h create mode 100644 Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentParams.h create mode 100644 Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentSensors.h create mode 100644 Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentSpec.h create mode 100644 Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentTypes.h delete mode 100644 Detectors/Upgrades/ITS3/alignment/include/ITS3Align/Deformations.h delete mode 100644 Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentHits.h delete mode 100644 Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentManager.h delete mode 100644 Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentParameters.h create mode 100644 Detectors/Upgrades/ITS3/alignment/include/ITS3Align/TrackFit.h create mode 100644 Detectors/Upgrades/ITS3/alignment/src/AlignmentHierarchy.cxx create mode 100644 Detectors/Upgrades/ITS3/alignment/src/AlignmentParams.cxx create mode 100644 Detectors/Upgrades/ITS3/alignment/src/AlignmentSensors.cxx create mode 100644 Detectors/Upgrades/ITS3/alignment/src/AlignmentSpec.cxx create mode 100644 Detectors/Upgrades/ITS3/alignment/src/AlignmentTypes.cxx delete mode 100644 Detectors/Upgrades/ITS3/alignment/src/Deformations.cxx delete mode 100644 Detectors/Upgrades/ITS3/alignment/src/MisalignmentHits.cxx delete mode 100644 Detectors/Upgrades/ITS3/alignment/src/MisalignmentManager.cxx delete mode 100644 Detectors/Upgrades/ITS3/alignment/src/MisalignmentParameters.cxx create mode 100644 Detectors/Upgrades/ITS3/alignment/src/alignment-workflow.cxx create mode 100644 Detectors/Upgrades/ITS3/macros/align/CheckHitResiduals.C delete mode 100644 Detectors/Upgrades/ITS3/macros/align/CreateMisalignmentITS3.C delete mode 100644 Detectors/Upgrades/ITS3/macros/align/MisAlignGeoITS3.notest delete mode 100644 Detectors/Upgrades/ITS3/macros/align/ShowCoefficients.C delete mode 100644 Detectors/Upgrades/ITS3/macros/align/TestLegendrePol.C create mode 100644 Detectors/Upgrades/ITS3/study/macros/PlotMisalignment.C create mode 100644 Detectors/Upgrades/ITS3/study/macros/PlotResiduals.C diff --git a/Detectors/Upgrades/ITS3/alignment/CMakeLists.txt b/Detectors/Upgrades/ITS3/alignment/CMakeLists.txt index f89ad821c65e7..0bc8080c7a1b8 100644 --- a/Detectors/Upgrades/ITS3/alignment/CMakeLists.txt +++ b/Detectors/Upgrades/ITS3/alignment/CMakeLists.txt @@ -9,18 +9,37 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. +# add_compile_options(-O0 -g -fPIC -fno-omit-frame-pointer) o2_add_library(ITS3Align - SOURCES src/MisalignmentParameters.cxx - src/MisalignmentHits.cxx - src/MisalignmentManager.cxx - src/Deformations.cxx + TARGETVARNAME targetName + SOURCES src/AlignmentHierarchy.cxx + src/AlignmentSensors.cxx + src/AlignmentParams.cxx + src/AlignmentTypes.cxx + src/AlignmentSpec.cxx PUBLIC_LINK_LIBRARIES O2::MathUtils O2::Steer O2::ITSBase - O2::ITSMFTSimulation) + O2::ITSMFTSimulation + O2::ITS3Reconstruction + O2::Framework + O2::GlobalTrackingWorkflowReaders + O2::GlobalTrackingWorkflowHelpers + O2::DataFormatsGlobalTracking + O2::DetectorsVertexing + nlohmann_json::nlohmann_json + GBL::GBL) +if (OpenMP_CXX_FOUND) + target_compile_definitions(${targetName} PRIVATE WITH_OPENMP) + target_link_libraries(${targetName} PRIVATE OpenMP::OpenMP_CXX) +endif() o2_target_root_dictionary(ITS3Align - HEADERS include/ITS3Align/MisalignmentParameters.h - include/ITS3Align/MisalignmentHits.h - include/ITS3Align/MisalignmentHits.h - include/ITS3Align/Deformations.h) + HEADERS include/ITS3Align/AlignmentParams.h + include/ITS3Align/AlignmentTypes.h) + + +o2_add_executable(alignment-workflow + SOURCES src/alignment-workflow.cxx + COMPONENT_NAME its3 + PUBLIC_LINK_LIBRARIES O2::ITS3Align) diff --git a/Detectors/Upgrades/ITS3/alignment/README.md b/Detectors/Upgrades/ITS3/alignment/README.md new file mode 100644 index 0000000000000..62633d1d7d313 --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/README.md @@ -0,0 +1,30 @@ +# Simulate ITS3 misalignment and re-alignment + + +```bash +o2-its3-alignment-workflow --track-sources ITS --output MilleData,MilleSteer --configKeyValues "ITS3AlignmentParams.minPt=0.1;ITS3AlignmentParams.doMisalignmentLeg=true;ITS3AlignmentParams.doMisalignmentRB=true;ITS3AlignmentParams.misAlgJson=test_closure.json;ITS3AlignmentParams.extraClsErrZ[0]=10e-4;ITS3AlignmentParams.extraClsErrY[0]=10e-4;ITS3AlignmentParams.extraClsErrZ[3]=10e-4;ITS3AlignmentParams.extraClsErrY[3]=10e-4;ITS3AlignmentParams.dofConfigJson=dofSet.json" -b --run +``` + +test_closure.json: +```json +[ + { + "id": 0, + "rigidBody": [0.001, 0.0005, 0.0, 0.0, 0.0001, 0.0], + "matrix": [[0.0], [0.0008, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0]] + } +] +``` + +dofSet.json: +```json +{ + "defaults": { "rigidBody": "fixed" }, + "rules": [ + { + "match": "ITS3Layer0/ITS3CarbonForm0", + "rigidBody": ["TX", "TY", "RY"], + "calib": { "type": "legendre", "order": 1, "fix": [0, 2] } + } + ] +}``` diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentHierarchy.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentHierarchy.h new file mode 100644 index 0000000000000..04b8157084d0a --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentHierarchy.h @@ -0,0 +1,339 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_ITS3_ALIGNMENT_HIERARCHY_H +#define O2_ITS3_ALIGNMENT_HIERARCHY_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace o2::its3::align +{ +using Matrix36 = Eigen::Matrix; +using Matrix66 = Eigen::Matrix; + +// indices for rigid body parameters in LOC frame +enum RigidBodyDOF : uint8_t { + TX = 0, + TY, + TZ, + RX, + RY, + RZ, + NDOF, +}; +static constexpr const char* RigidBodyDOFNames[RigidBodyDOF::NDOF] = {"TX", "TY", "TZ", "RX", "RY", "RZ"}; + +// return the rigid body derivatives +// trk has be at in the measurment frame +auto getRigidBodyDerivatives(const auto& trk) +{ + // calculate slopes + const double tgl = trk.getTgl(), snp = trk.getSnp(); + const double csp = 1. / sqrt(1. + (tgl * tgl)); + const double u = trk.getY(), v = trk.getZ(); + const double uP = snp * csp, vP = tgl * csp; + Matrix36 der; + der.setZero(); + // columns: Tt, Tu, Tv, Rt, Ru, Rv + // (X) (Y) (Z) (RX) (RY) (RZ) + der << uP, -1., 0., v, v * uP, -u * uP, + vP, 0., -1., -u, v * vP, -u * vP; + return der; +} + +class DOFSet +{ + public: + enum class Type : uint8_t { RigidBody, + Legendre }; + virtual ~DOFSet() = default; + virtual Type type() const = 0; + int nDOFs() const { return static_cast(mFree.size()); } + virtual std::string dofName(int idx) const = 0; + bool isFree(int idx) const { return mFree[idx]; } + void setFree(int idx, bool f) { mFree[idx] = f; } + void setAllFree(bool f) { std::fill(mFree.begin(), mFree.end(), f); } + int nFreeDOFs() const + { + int n = 0; + for (bool f : mFree) { + n += f; + } + return n; + } + + protected: + DOFSet(int n) : mFree(n, true) {} + std::vector mFree; +}; + +class RigidBodyDOFSet final : public DOFSet +{ + public: + static constexpr int NDOF = RigidBodyDOF::NDOF; + RigidBodyDOFSet() : DOFSet(NDOF) {} + // mask: bitmask of free DOFs (bit i = DOF i is free) + explicit RigidBodyDOFSet(uint8_t mask) : DOFSet(NDOF) + { + for (int i = 0; i < NDOF; ++i) { + mFree[i] = (mask >> i) & 1; + } + } + Type type() const override { return Type::RigidBody; } + std::string dofName(int idx) const override { return RigidBodyDOFNames[idx]; } + uint8_t mask() const + { + uint8_t m = 0; + for (int i = 0; i < NDOF; ++i) { + m |= (uint8_t(mFree[i]) << i); + } + return m; + } +}; + +class LegendreDOFSet final : public DOFSet +{ + public: + explicit LegendreDOFSet(int order) : DOFSet((order + 1) * (order + 2) / 2), mOrder(order) {} + Type type() const override { return Type::Legendre; } + int order() const { return mOrder; } + std::string dofName(int idx) const override + { + int i = 0; + while ((i + 1) * (i + 2) / 2 <= idx) { + ++i; + } + int j = idx - (i * (i + 1) / 2); + return std::format("L({},{})", i, j); + } + + private: + int mOrder; +}; + +class GlobalLabel +{ + // Millepede label is any positive integer [1....) + // Layout: DOF(5) | CALIB(1) | ID(22) | SENS(1) | DET(2) = 31 usable bits (MSB reserved, GBL uses signed int) + public: + using T = uint32_t; + static constexpr int DOF_BITS = 5; // bits 0-4 + static constexpr int CALIB_BITS = 1; // bit 5: 0 = rigid body, 1 = calibration + static constexpr int ID_BITS = 22; // bits 6-27 + static constexpr int SENS_BITS = 1; // bit 28 + static constexpr int TOTAL_BITS = sizeof(T) * 8; + static constexpr int DET_BITS = TOTAL_BITS - (DOF_BITS + CALIB_BITS + ID_BITS + SENS_BITS) - 1; // one less bit since GBL uses int! + static constexpr T bitMask(int b) noexcept + { + return (T(1) << b) - T(1); + } + static constexpr int DOF_SHIFT = 0; + static constexpr T DOF_MAX = (T(1) << DOF_BITS) - T(1); + static constexpr T DOF_MASK = DOF_MAX << DOF_SHIFT; + static constexpr int CALIB_SHIFT = DOF_BITS; + static constexpr T CALIB_MAX = (T(1) << CALIB_BITS) - T(1); + static constexpr T CALIB_MASK = CALIB_MAX << CALIB_SHIFT; + static constexpr int ID_SHIFT = DOF_BITS + CALIB_BITS; + static constexpr T ID_MAX = (T(1) << ID_BITS) - T(1); + static constexpr T ID_MASK = ID_MAX << ID_SHIFT; + static constexpr int SENS_SHIFT = DOF_BITS + CALIB_BITS + ID_BITS; + static constexpr T SENS_MAX = (T(1) << SENS_BITS) - T(1); + static constexpr T SENS_MASK = SENS_MAX << SENS_SHIFT; + static constexpr int DET_SHIFT = DOF_BITS + CALIB_BITS + ID_BITS + SENS_BITS; + static constexpr T DET_MAX = (T(1) << DET_BITS) - T(1); + static constexpr T DET_MASK = DET_MAX << DET_SHIFT; + + GlobalLabel(T det, T id, bool sens, bool calib = false) + : mID((((id + 1) & ID_MAX) << ID_SHIFT) | + ((det & DET_MAX) << DET_SHIFT) | + ((T(sens) & SENS_MAX) << SENS_SHIFT) | + ((T(calib) & CALIB_MAX) << CALIB_SHIFT)) + { + } + + /// produce the raw Millepede label for a given DOF index (rigid body: calib=0 in label) + constexpr T raw(T dof) const noexcept { return (mID & ~DOF_MASK) | ((dof & DOF_MAX) << DOF_SHIFT); } + constexpr int rawGBL(T dof) const noexcept { return static_cast(raw(dof)); } + + /// return a copy of this label with the CALIB bit set (for calibration DOFs on same volume) + GlobalLabel asCalib() const noexcept + { + GlobalLabel c{*this}; + c.mID |= (T(1) << CALIB_SHIFT); + return c; + } + + constexpr T id() const noexcept { return ((mID >> ID_SHIFT) & ID_MAX) - 1; } + constexpr T det() const noexcept { return (mID & DET_MASK) >> DET_SHIFT; } + constexpr bool sens() const noexcept { return (mID & SENS_MASK) >> SENS_SHIFT; } + constexpr bool calib() const noexcept { return (mID & CALIB_MASK) >> CALIB_SHIFT; } + + std::string asString() const + { + return std::format("Det:{} Id:{} Sens:{} Calib:{}", det(), id(), sens(), calib()); + } + + constexpr auto operator<=>(const GlobalLabel&) const noexcept = default; + + private: + T mID{0}; +}; + +class HierarchyConstraint +{ + public: + HierarchyConstraint(std::string name, double value) : mName(std::move(name)), mValue(value) {} + void add(uint32_t lab, double coeff) + { + mLabels.push_back(lab); + mCoeff.push_back(coeff); + } + void write(std::ostream& os) const; + auto getSize() const noexcept { return mLabels.size(); } + + private: + std::string mName; // name of the constraint + double mValue{0.0}; // constraint value + std::vector mLabels; // parameter labels + std::vector mCoeff; // their coefficients +}; + +// --- AlignableVolume --- + +class AlignableVolume +{ + public: + using Ptr = std::unique_ptr; + using SensorMapping = std::map; + + AlignableVolume(const AlignableVolume&) = delete; + AlignableVolume(AlignableVolume&&) = delete; + AlignableVolume& operator=(const AlignableVolume&) = delete; + AlignableVolume& operator=(AlignableVolume&&) = delete; + AlignableVolume(const char* symName, uint32_t label, uint32_t det, bool sens); + AlignableVolume(const char* symName, GlobalLabel label); + virtual ~AlignableVolume() = default; + + void finalise(uint8_t level = 0); + + // steering file output + void writeRigidBodyConstraints(std::ostream& os) const; + void writeParameters(std::ostream& os) const; + void writeTree(std::ostream& os, int indent = 0) const; + + // tree-like + auto getLevel() const noexcept { return mLevel; } + bool isRoot() const noexcept { return mParent == nullptr; } + bool isLeaf() const noexcept { return mChildren.empty(); } + template + requires std::derived_from + AlignableVolume* addChild(const char* symName, uint32_t label, uint32_t det, bool sens) + { + auto c = std::make_unique(symName, label, det, sens); + return setParent(std::move(c)); + } + template + requires std::derived_from + AlignableVolume* addChild(const char* symName, GlobalLabel lbl) + { + auto c = std::make_unique(symName, lbl); + return setParent(std::move(c)); + } + + // bfs traversal + void traverse(const std::function& visitor) + { + visitor(this); + for (auto& c : mChildren) { + c->traverse(visitor); + } + } + + std::string getSymName() const noexcept { return mSymName; } + GlobalLabel getLabel() const noexcept { return mLabel; } + AlignableVolume* getParent() const { return mParent; } + size_t getNChildren() const noexcept { return mChildren.size(); } + + // DOF management + void setRigidBody(std::unique_ptr rb) { mRigidBody = std::move(rb); } + void setCalib(std::unique_ptr cal) { mCalib = std::move(cal); } + DOFSet* getRigidBody() const { return mRigidBody.get(); } + DOFSet* getCalib() const { return mCalib.get(); } + void setPseudo(bool p) noexcept { mIsPseudo = p; } + bool isPseudo() const noexcept { return mIsPseudo; } + void setSensorId(int id) noexcept { mSensorId = id; } + int getSensorId() const noexcept { return mSensorId; } + // true if this volume participates in the hierarchy (has DOFs or is pseudo) + bool isActive() const noexcept { return mRigidBody != nullptr || mIsPseudo; } + + // transformation matrices + virtual void defineMatrixL2G() {} + virtual void defineMatrixT2L() {} + virtual void computeJacobianL2T(const double* pos, Matrix66& jac) const {}; + const TGeoHMatrix& getL2P() const { return mL2P; } + const TGeoHMatrix& getT2L() const { return mT2L; } + const Matrix66& getJL2P() const { return mJL2P; } + const Matrix66& getJP2L() const { return mJP2L; } + + protected: + /// matrices + AlignableVolume* mParent{nullptr}; // parent + TGeoPNEntry* mPNE{nullptr}; // physical entry + TGeoPhysicalNode* mPN{nullptr}; // physical node + TGeoHMatrix mL2G; // (LOC) -> (GLO) + TGeoHMatrix mL2P; // (LOC) -> (PAR) + Matrix66 mJL2P; // jac (LOC) -> (PAR) + Matrix66 mJP2L; // jac (PAR) -> (LOC) + TGeoHMatrix mT2L; // (TRK) -> (LOC) + + private: + std::string mSymName; + GlobalLabel mLabel; + uint8_t mLevel{0}; + bool mIsPseudo{false}; + int mSensorId{-1}; + std::unique_ptr mRigidBody; + std::unique_ptr mCalib; + + AlignableVolume* setParent(Ptr c) + { + c->mParent = this; + mChildren.push_back(std::move(c)); + return mChildren.back().get(); + } + std::vector mChildren; // children + + void init(); +}; + +// apply DOF configuration from a JSON file to the hierarchy +void applyDOFConfig(AlignableVolume* root, const std::string& jsonPath); + +// parse millepede.res and write result.json with fitted parameters for ITS3 half barrels +void writeMillepedeResults(AlignableVolume* root, const std::string& milleResPath, const std::string& outJsonPath, const std::string& injectedJsonPath = ""); + +} // namespace o2::its3::align + +#endif diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentParams.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentParams.h new file mode 100644 index 0000000000000..a7785a2c04e11 --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentParams.h @@ -0,0 +1,67 @@ +// 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_ITS3_ALIGNMENTPARAMS_H_ +#define ALICEO2_ITS3_ALIGNMENTPARAMS_H_ + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" +#include "DetectorsBase/Propagator.h" + +namespace o2::its3::align +{ +struct AlignmentParams : public o2::conf::ConfigurableParamHelper { + // Track selection + float minPt = 1.f; // minimum pt required + int minITSCls = 7; // minimum number of ITS clusters + float maxITSChi2Ndf = 1.2; // maximum ITS track chi2 + + // propagation opt + double maxSnp = 0.85; + double maxStep = 2.0; + // o2::base::PropagatorD::MatCorrType matCorrType = o2::base::PropagatorD::MatCorrType::USEMatCorrTGeo; + o2::base::PropagatorD::MatCorrType corrType = o2::base::PropagatorD::MatCorrType::USEMatCorrLUT; + + bool useStableRef = true; // use input tracks as linearization point + float minMS = 1e-6f; // minimum scattering to account for + float maxChi2Ndf = 10; // maximum Chi2/Ndf allowed for GBL fit + + // per chip extra error + float extraClsErrY[6] = {0}; + float extraClsErrZ[6] = {0}; + + // misalignment simulation + bool doMisalignmentLeg = false; // simulate Legendre deformation on ITS3 layers + bool doMisalignmentRB = false; // simulate rigid body misalignment on ITS3 layers + std::string misAlgJson; // JSON file with deformation and/or rigid body params + + // DOF configuration (JSON file defining which volumes have which DOFs) + std::string dofConfigJson; // if empty, no DOFs are configured + + // Ridder options + int ridderMaxExtrap = 10; + double ridderRelIniStep[5] = {0.01, 0.01, 0.02, 0.02, 0.02}; + double ridderMaxIniStep[5] = {0.1, 0.1, 0.05, 0.05, 0.05}; + double ridderShrinkFac = 2.0; + double ridderEps = 1e-16; + + // MillePede output + std::string milleBinFile = "mp2data.bin"; + std::string milleConFile = "mp2con.txt"; + std::string milleParamFile = "mp2param.txt"; + std::string milleTreeFile = "mp2tree.txt"; + std::string milleResFile = "millepede.res"; + std::string milleResOutJson = "result.json"; + + O2ParamDef(AlignmentParams, "ITS3AlignmentParams"); +}; +} // namespace o2::its3::align + +#endif diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentSensors.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentSensors.h new file mode 100644 index 0000000000000..535f67156a16c --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentSensors.h @@ -0,0 +1,41 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_ITS3_ALIGNMENT_SENSORS_H +#define O2_ITS3_ALIGNMENT_SENSORS_H + +#include "ITS3Align/AlignmentHierarchy.h" + +namespace o2::its3::align +{ + +AlignableVolume::Ptr buildHierarchyITS(AlignableVolume::SensorMapping& sensorMap); +AlignableVolume::Ptr buildHierarchyIT3(AlignableVolume::SensorMapping& sensorMap); + +class AlignableSensorITS final : public AlignableVolume +{ + using AlignableVolume::AlignableVolume; + void defineMatrixL2G() final; + void defineMatrixT2L() final; + void computeJacobianL2T(const double* pos, Matrix66& jac) const final; +}; + +class AlignableSensorIT3 final : public AlignableVolume +{ + using AlignableVolume::AlignableVolume; + void defineMatrixL2G() final; + void defineMatrixT2L() final; + void computeJacobianL2T(const double* pos, Matrix66& jac) const final; +}; + +} // namespace o2::its3::align + +#endif diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentSpec.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentSpec.h new file mode 100644 index 0000000000000..2344889657558 --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentSpec.h @@ -0,0 +1,34 @@ +// 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_ITS3_ALIGNMENT_H +#define O2_ITS3_ALIGNMENT_H + +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "Framework/DataProcessorSpec.h" +#include "CommonUtils/EnumFlags.h" + +namespace o2::its3::align +{ + +enum class OutputOpt : uint8_t { + VerboseGBL = 0, + MilleData, + MilleSteer, + MilleRes, + Debug, +}; +using OutputEnum = utils::EnumFlags; + +o2::framework::DataProcessorSpec getAlignmentSpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool useMC, bool withPV, bool withITS3, OutputEnum out); +} // namespace o2::its3::align + +#endif diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentTypes.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentTypes.h new file mode 100644 index 0000000000000..6dc84b2323d35 --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentTypes.h @@ -0,0 +1,64 @@ +// 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_ITS3_ALIGNMENT_TYPES_H +#define O2_ITS3_ALIGNMENT_TYPES_H + +#include +#include "ReconstructionDataFormats/Track.h" +#include "DataFormatsITS/TrackITS.h" + +namespace o2::its3::align +{ + +struct Measurement final { + double dy = 0.f; + double dz = 0.f; + double sig2y = 0.f; + double sig2z = 0.f; + double phi = 0.f; + double z = 0.f; + ClassDefNV(Measurement, 1) +}; + +struct FrameInfoExt final { + int16_t sens = -1; + int8_t lr = -1; // -1 = vtx + double x{-999.f}; + double alpha{-999.f}; + std::array positionTrackingFrame = {999., 999.}; + std::array covarianceTrackingFrame = {999., 999., 999.}; + + std::string asString() const; + + ClassDefNV(FrameInfoExt, 1) +}; + +struct FitInfo final { + float chi2Ndf{-1}; // Chi2/Ndf of track refit + float chi2{-1}; // Chi2 + int ndf{-1}; // ndf + ClassDefNV(FitInfo, 1) +}; + +struct Track { + o2::its::TrackITS its; // original ITS track + o2::track::TrackParCovD track; // track at innermost update point, refitted from outwards seed + FitInfo kfFit; // kf fit information + FitInfo gblFit; // gbl fit information + std::vector points; // measurment point + std::vector info; // frame info + ClassDefNV(Track, 1) +}; + +} // namespace o2::its3::align + +#endif diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/Deformations.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/Deformations.h deleted file mode 100644 index dfaade51e82ff..0000000000000 --- a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/Deformations.h +++ /dev/null @@ -1,84 +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 ITS3_DEFORMATIONS_H_ -#define ITS3_DEFORMATIONS_H_ - -#include "ITS3Align/MisalignmentParameters.h" -#include "MathUtils/LegendrePols.h" - -#include - -namespace o2::its3::align -{ - -class Deformations -{ - public: - // init deformations from the parameter file - void init(const std::filesystem::path&); - - double getDeformationX(unsigned int id, double u, double v) const { return getDeformation<0>(id, u, v); } - double getDeformationY(unsigned int id, double u, double v) const { return getDeformation<1>(id, u, v); } - double getDeformationZ(unsigned int id, double u, double v) const { return getDeformation<2>(id, u, v); } - double getDeformation(unsigned int id, unsigned int axis, double u, double v) const - { - if (axis == 0) { - return mLegX[id](u, v); - } else if (axis == 1) { - return mLegY[id](u, v); - } else { - return mLegZ[id](u, v); - } - } - std::array getDeformation(unsigned int id, double u, double v) const - { - return {getDeformation<0>(id, u, v), - getDeformation<1>(id, u, v), - getDeformation<2>(id, u, v)}; - } - std::array getOrders(unsigned int id) const - { - return {mLegX[id].NOrder(), mLegY[id].NOrder(), mLegZ[id].NOrder()}; - } - const o2::math_utils::Legendre2DPolynominal& getLegendre(unsigned int id, unsigned int axis) const - { - if (axis == 0) { - return mLegX[id]; - } else if (axis == 1) { - return mLegY[id]; - } else { - return mLegZ[id]; - } - } - - private: - template - double getDeformation(unsigned int id, double u, double v) const - { - if constexpr (axis == 0) { - return mLegX[id](u, v); - } else if constexpr (axis == 1) { - return mLegY[id](u, v); - } else { - return mLegZ[id](u, v); - } - } - - // 3 Legendre polynominals to model deformations in x,y,z; parameterized by normalized phi (u) and z (v) coordinates - std::array mLegX; - std::array mLegY; - std::array mLegZ; -}; - -} // namespace o2::its3::align - -#endif diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentHits.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentHits.h deleted file mode 100644 index 37f5c9fdf701d..0000000000000 --- a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentHits.h +++ /dev/null @@ -1,216 +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 ITS3_MISALIGNMENTHITS_H_ -#define ITS3_MISALIGNMENTHITS_H_ - -#include "Math/IFunction.h" -#include "Math/Minimizer.h" - -#include "ReconstructionDataFormats/Track.h" -#include "ITS3Align/Deformations.h" -#include "ITSBase/GeometryTGeo.h" -#include "ITSMFTSimulation/Hit.h" -#include "MathUtils/Cartesian.h" -#include "MathUtils/Utils.h" -#include "Steer/MCKinematicsReader.h" - -#include -#include -#include -#include -#include - -namespace o2::its3::align -{ - -class MisAlignmentHits -{ - public: - enum class PropMethod { - Propagator, - Line, - }; - - void init(); - - std::optional processHit(int iEvent, const o2::itsmft::Hit& hit); - - void resetStats() { mStats.fill(0ull); } - void printStats() const; - - private: - Deformations mDeformations; - std::unique_ptr mMinimizer; - PropMethod mMethod{PropMethod::Line}; - o2::its::GeometryTGeo* mGeo{nullptr}; - std::unique_ptr mMCReader; - - short getDetID(const o2::math_utils::Point3D& point); - short getDetIDFromCords(const o2::math_utils::Point3D& point); - short getDetIDFromPath(const std::string& path) const; - - // We treat each hit as two separate hits', one for the entering and one for the exiting hit - struct WorkingHit { - enum HitType : uint8_t { - kEntering = 0, - kExiting, - kTypes, - }; - - WorkingHit() = default; - - WorkingHit(int eventID, HitType t, const o2::itsmft::Hit& hit) : mEvent(eventID), - mTrackID(hit.GetTrackID()), - mType(t), - mDetID(hit.GetDetectorID()), - mLayerID(constants::detID::getDetID2Layer(mDetID)), - mSensorID(constants::detID::getSensorID(mDetID)) - { - if (mType == kEntering) { - mRadius = constants::radiiInner[mLayerID]; - mPoint = hit.GetPosStart(); - } else { - mRadius = constants::radiiOuter[mLayerID]; - mPoint = hit.GetPos(); - } - - // Pre-calculate the normalized u,v coordinates as starting parameters - const bool isTop = mSensorID % 2 == 0; - mPhi = o2::math_utils::to02Pi(std::atan2(mPoint.Y(), mPoint.X())); - mPhiBorder1 = o2::math_utils::to02Pi(((isTop) ? 0.f : 1.f) * TMath::Pi() + std::asin(constants::equatorialGap / 2.f / mRadius)); - mPhiBorder2 = o2::math_utils::to02Pi(((isTop) ? 1.f : 2.f) * TMath::Pi() - std::asin(constants::equatorialGap / 2.f / mRadius)); - mU = ((mPhi - mPhiBorder1) * 2.f) / (mPhiBorder2 - mPhiBorder1) - 1.f; - mV = (2.f * mPoint.Z() + constants::segment::lengthSensitive) / constants::segment::lengthSensitive - 1.f; - } - - void recalculateIdeal(float phi, float z) - { - mPointDef.SetX(mRadius * std::cos(phi)); - mPointDef.SetY(mRadius * std::sin(phi)); - mPointDef.SetZ(z); - } - - int mEvent; - int mTrackID; - HitType mType; - short mDetID; - int mLayerID; - int mSensorID; - float mRadius; - float mPhi; - o2::math_utils::Point3D mPoint; - o2::math_utils::Point3D mPointDef; - float mU; // u is normalized phi - float mV; // u is normalized z - - float mPhiBorder1; - float mPhiBorder2; - }; - std::array mCurWorkingHits; - o2::itsmft::Hit mCurHit; - - bool deformHit(WorkingHit::HitType t); - - auto getDeformation(unsigned int id, double u, double v) const - { - return mDeformations.getDeformation(id, u, v); - } - - // Mimize function assuming a straight line - // given in the parametric representation by y_v = t * d_x + x_s - // assuming no offset is needed - class StraightLine : public ROOT::Math::IBaseFunctionMultiDim - { - public: - StraightLine(const MisAlignmentHits* m) : mMis(m) {} - - std::array mD; - o2::math_utils::Point3D mStart; - unsigned int mSensorID; - double mRadius; - const MisAlignmentHits* mMis; - - double mPhiTot; - double mPhi1; - - unsigned int NDim() const override { return 3; } - ROOT::Math::IBaseFunctionMultiDim* Clone() const override { return nullptr; } - - private: - double DoEval(const double* x) const override; - }; - StraightLine mLine{this}; - void prepareLineMethod(WorkingHit::HitType from); - - // Mimize function using the MCTrack - class Propagator : public ROOT::Math::IBaseFunctionMultiDim - { - public: - Propagator(const MisAlignmentHits* m) : mMis(m) {} - - o2::track::TrackPar mTrack; - float mBz; - unsigned int mSensorID; - double mRadius; - const MisAlignmentHits* mMis; - - double mPhiTot; - double mPhi1; - - unsigned int NDim() const override { return 3; } - ROOT::Math::IBaseFunctionMultiDim* Clone() const override { return nullptr; } - - private: - double DoEval(const double* x) const override; - }; - Propagator mPropagator{this}; - bool preparePropagatorMethod(WorkingHit::HitType from); - - enum Stats : uint8_t { - kHitTotal = 0, - kHitIsOB, - kHitIsIB, - kHitDead, - kHitAlive, - kHitSuccess, - kHitMigrated, - kHitNotMigrated, - kHitEntBoundary, - kHitExtBoundary, - kHitNoBoundary, - kHitSameBoundary, - kFindNodeFailed, - kFindNodeSuccess, - kProjSensitive, - kProjNonSensitive, - kDetIDOk, - kDetIDBad, - kMinimizerStatusOk, - kMinimizerStatusBad, - kMinimizerValueOk, - kMinimizerValueBad, - kMinimizerConverged, - kMinimizerCovPos, - kMinimizerHesse, - kMinimizerEDM, - kMinimizerLimit, - kMinimizerOther, - kPropTrackNull, - kPropPDGNull, - kALL, - }; - std::array mStats; -}; - -} // namespace o2::its3::align - -#endif diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentManager.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentManager.h deleted file mode 100644 index 0fe972442809d..0000000000000 --- a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentManager.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2020-2022 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 ITS3_MISALIGNMENTMANAGER_H_ -#define ITS3_MISALIGNMENTMANAGER_H_ - -#include "Math/Transform3D.h" -#include "Math/Translation3D.h" -#include "Math/Rotation3D.h" -#include "Math/EulerAngles.h" -#include "Math/PositionVector3D.h" -#include "TGeoMatrix.h" - -#include - -namespace o2::its3::align -{ - -/// Collection of static functions and types to perform misalignment studies -struct MisalignmentManager { - using Vector3D = ROOT::Math::DisplacementVector3D, ROOT::Math::DefaultCoordinateSystemTag>; - using Point3D = ROOT::Math::PositionVector3D, ROOT::Math::DefaultCoordinateSystemTag>; - using Trans3D = ROOT::Math::Translation3DF; - using Rot3D = ROOT::Math::Rotation3D; - using Euler3D = ROOT::Math::EulerAngles; - using Trafo3D = ROOT::Math::Transform3DF; - - static void misalignHits(); - - static void createBackup(const std::filesystem::path& src, const std::filesystem::path& dest); - - static std::string appendStem(const std::string& filename, const std::string& add); - - static std::vector split(const std::string& s, char delimiter = '/'); - - static void navigate(const std::string& path); - - static std::string composePathSensor(int sensor); - - static void applyGlobalMatrixVolume(const std::string& path, const TGeoHMatrix& globalMatrix); -}; - -} // namespace o2::its3::align - -#endif diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentParameters.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentParameters.h deleted file mode 100644 index 243623cc650e1..0000000000000 --- a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentParameters.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. - -/// \file MisalignmentParameters.h -/// \brief Definition of the MisalignmentParameters class - -#ifndef ITS3_MISALIGNMENTPARAMETERS_H_ -#define ITS3_MISALIGNMENTPARAMETERS_H_ - -#include "ITS3Base/SpecsV2.h" - -#include "TNamed.h" -#include "TFile.h" -#include "TMatrixD.h" - -#include -#include - -namespace o2::its3::align -{ - -class MisalignmentParameters : public TNamed -{ - public: - MisalignmentParameters(); - - // IO - bool store(const std::string& file) const; - static MisalignmentParameters* load(const std::string& file); - - /// Global getters - double getGloTransX(unsigned int detID) const { return mGloTransX[detID]; } - double getGloTransY(unsigned int detID) const { return mGloTransY[detID]; } - double getGloTransZ(unsigned int detID) const { return mGloTransZ[detID]; } - double getGloRotX(unsigned int detID) const { return mGloRotX[detID]; } - double getGloRotY(unsigned int detID) const { return mGloRotY[detID]; } - double getGloRotZ(unsigned int detID) const { return mGloRotZ[detID]; } - /// Global setters - void setGloTransX(unsigned int detID, double v) { mGloTransX[detID] = v; } - void setGloTransY(unsigned int detID, double v) { mGloTransY[detID] = v; } - void setGloTransZ(unsigned int detID, double v) { mGloTransZ[detID] = v; } - void setGloRotX(unsigned int detID, double v) { mGloRotX[detID] = v; } - void setGloRotY(unsigned int detID, double v) { mGloRotY[detID] = v; } - void setGloRotZ(unsigned int detID, double v) { mGloRotZ[detID] = v; } - - /// Legendre Coeff. getters - const TMatrixD& getLegendreCoeffX(unsigned int sensorID) const { return mLegCoeffX[sensorID]; } - const TMatrixD& getLegendreCoeffY(unsigned int sensorID) const { return mLegCoeffY[sensorID]; } - const TMatrixD& getLegendreCoeffZ(unsigned int sensorID) const { return mLegCoeffZ[sensorID]; } - /// Legendre Coeff. setters - void setLegendreCoeffX(unsigned int sensorID, const TMatrixD& m) { setMatrix(mLegCoeffX[sensorID], m); } - void setLegendreCoeffY(unsigned int sensorID, const TMatrixD& m) { setMatrix(mLegCoeffY[sensorID], m); } - void setLegendreCoeffZ(unsigned int sensorID, const TMatrixD& m) { setMatrix(mLegCoeffZ[sensorID], m); } - - void printParams(unsigned int detID) const; - void printLegendreParams(unsigned int sensorID) const; - - private: - inline void setMatrix(TMatrixD& o, const TMatrixD& n) - { - o.ResizeTo(n.GetNrows(), n.GetNcols()); - o = n; - } - - static constexpr unsigned int nDetectors{constants::detID::nChips}; ///! for now just the IB - - // Global parameters - std::array mGloTransX; ///< Array to hold the global misalignment in x-direction - std::array mGloTransY; ///< Array to hold the global misalignment in y-direction - std::array mGloTransZ; ///< Array to hold the global misalignment in z-direction - std::array mGloRotX; ///< Array to hold the global misalignment in x-direction - std::array mGloRotY; ///< Array to hold the global misalignment in y-direction - std::array mGloRotZ; ///< Array to hold the global misalignment in z-direction - - // Legendre Polynominals coefficients - std::array mLegCoeffX; - std::array mLegCoeffY; - std::array mLegCoeffZ; - - ClassDefOverride(MisalignmentParameters, 1); -}; - -} // namespace o2::its3::align - -#endif diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/TrackFit.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/TrackFit.h new file mode 100644 index 0000000000000..3f36705271c9b --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/TrackFit.h @@ -0,0 +1,175 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_ITS3_ALIGN_TRACKFIT +#define O2_ITS3_ALIGN_TRACKFIT + +#include + +#include "ITSBase/GeometryTGeo.h" +#include "DetectorsBase/Propagator.h" +#include "ReconstructionDataFormats/Track.h" +#include "DataFormatsITS/TrackITS.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" + +namespace o2::its3::align +{ +using Mat51 = Eigen::Matrix; +using Mat55 = Eigen::Matrix; +using TrackD = o2::track::TrackParCovD; + +template +struct TrackingCluster : public o2::BaseCluster { + using o2::BaseCluster::BaseCluster; + T alpha{}; +}; + +template +track::TrackParametrizationWithError convertTrack(const track::TrackParametrizationWithError& trk) +{ + if constexpr (std::is_same_v) { + return trk; + } + track::TrackParametrizationWithError dst; + dst.setX(trk.getX()); + dst.setAlpha(trk.getAlpha()); + for (int iPar{0}; iPar < track::kNParams; ++iPar) { + dst.setParam(trk.getParam(iPar), iPar); + } + dst.setAbsCharge(trk.getAbsCharge()); + dst.setPID(trk.getPID()); + dst.setUserField(trk.getUserField()); + for (int iCov{0}; iCov < track::kCovMatSize; ++iCov) { + dst.setCov(trk.getCov()[iCov], iCov); + } + return dst; +} + +// Both tracks must be at the same (alpha, x). +// Returns the interpolated track. +template +o2::track::TrackParametrizationWithError interpolateTrackParCov( + const o2::track::TrackParametrizationWithError& tA, + const o2::track::TrackParametrizationWithError& tB) +{ + auto res = tA; + if (!tA.isValid() || !tB.isValid() || tA.getAlpha() != tB.getAlpha() || tA.getX() != tB.getX()) { + res.invalidate(); + return res; + } + auto unpack = [](const std::array& c) { + Mat55 m; + for (int i = 0, k = 0; i < 5; ++i) { + for (int j = 0; j <= i; ++j, ++k) { + m(i, j) = m(j, i) = (double)c[k]; + } + } + return m; + }; + Mat55 cA = unpack(tA.getCov()); + Mat55 cB = unpack(tB.getCov()); + Mat55 wA = cA.inverse(); + Mat55 wB = cB.inverse(); + Mat55 wTot = wA + wB; + Mat55 cTot = wTot.inverse(); + Mat51 pA, pB; + for (int i = 0; i < 5; ++i) { + pA(i) = tA.getParam(i); + pB(i) = tB.getParam(i); + } + Mat51 pTot = cTot * (wA * pA + wB * pB); + // build result - same alpha/x as inputs + for (int i = 0; i < 5; ++i) { + res.setParam(pTot(i), i); + } + for (int i = 0, k = 0; i < 5; ++i) { + for (int j = 0; j <= i; ++j, ++k) { + res.setCov(static_cast(cTot(i, j)), k); + } + } + return res; +} + +// Performs an outward (0->7) and inward (7->0) Kalman refit storing the +// extrapolation *before* the cluster update at each layer. +// cluster array clArr[0] = PV (optional), clArr[1..7] = layers 0-6. +// chi2 is accumulated only for the outward direction +template +bool doBidirRefit( + const o2::its::TrackITS& iTrack, + std::array*, 8>& clArr, + std::array, 8>& extrapOut, + std::array, 8>& extrapInw, + T& chi2, + bool useStableRef, + typename o2::base::PropagatorImpl::MatCorrType corrType) +{ + const auto prop = o2::base::PropagatorImpl::Instance(); + const auto geom = o2::its::GeometryTGeo::Instance(); + const auto bz = prop->getNominalBz(); + + auto rotateTrack = [bz](o2::track::TrackParametrizationWithError& tr, T alpha, o2::track::TrackParametrization* refLin) { + return refLin ? tr.rotate(alpha, *refLin, bz) : tr.rotate(alpha); + }; + auto accountCluster = [&](int i, std::array, 8>& extrapDest, o2::track::TrackParametrizationWithError& tr, o2::track::TrackParametrization* refLin) -> int { + if (clArr[i]) { + bool outward = tr.getX() < clArr[i]->getX(); + if (!rotateTrack(tr, clArr[i]->alpha, refLin) || !prop->propagateTo(tr, refLin, clArr[i]->getX(), false, base::PropagatorImpl::MAX_SIN_PHI, base::PropagatorImpl::MAX_STEP, corrType)) { + return 0; + } + if (outward) { + chi2 += tr.getPredictedChi2Quiet(*clArr[i]); + } + extrapDest[i] = tr; // before update + if (!tr.update(*clArr[i])) { + return 0; + } + } else { + extrapDest[i].invalidate(); + return -1; + } + return 1; + }; + auto trFitInw = convertTrack(iTrack.getParamOut()); + auto trFitOut = convertTrack(iTrack.getParamIn()); + if (clArr[0]) { // propagate outward seed to PV cluster's tracking frame + if (!trFitOut.rotate(clArr[0]->alpha) || !prop->propagateToX(trFitOut, clArr[0]->getX(), bz, base::PropagatorImpl::MAX_SIN_PHI, base::PropagatorImpl::MAX_STEP, corrType)) { + return false; + } + } + // linearization references + o2::track::TrackParametrization refLinInw0, refLinOut0, *refLinOut = nullptr, *refLinInw = nullptr; + if (useStableRef) { + refLinOut = &(refLinOut0 = trFitOut); + refLinInw = &(refLinInw0 = trFitInw); + } + + auto resetTrackCov = [bz](auto& trk) { + trk.resetCovariance(); + float qptB5Scale = std::abs(bz) > 0.1f ? std::abs(bz) / 5.006680f : 1.f; + float q2pt2 = trk.getQ2Pt() * trk.getQ2Pt(), q2pt2Wgh = q2pt2 * qptB5Scale * qptB5Scale; + float err2 = (100.f + q2pt2Wgh) / (1.f + q2pt2Wgh) * q2pt2; // -> 100 for high pTs, -> 1 for low pTs. + trk.setCov(err2, 14); // 100% error + }; + resetTrackCov(trFitOut); + resetTrackCov(trFitInw); + + for (int i = 0; i <= 7; i++) { + if (!accountCluster(i, extrapOut, trFitOut, refLinOut) || !accountCluster(7 - i, extrapInw, trFitInw, refLinInw)) { + return false; + } + } + return true; +} + +} // namespace o2::its3::align + +#endif diff --git a/Detectors/Upgrades/ITS3/alignment/src/AlignmentHierarchy.cxx b/Detectors/Upgrades/ITS3/alignment/src/AlignmentHierarchy.cxx new file mode 100644 index 0000000000000..9170165a36a41 --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/src/AlignmentHierarchy.cxx @@ -0,0 +1,486 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ITS3Align/AlignmentHierarchy.h" +#include "ITSBase/GeometryTGeo.h" +#include "Framework/Logger.h" +#include "MathUtils/Utils.h" + +namespace o2::its3::align +{ + +void HierarchyConstraint::write(std::ostream& os) const +{ + os << "!!! " << mName << '\n'; + os << "Constraint " << mValue << '\n'; + for (size_t i{0}; i < mLabels.size(); ++i) { + os << mLabels[i] << " " << mCoeff[i] << '\n'; + } + os << '\n'; +} + +AlignableVolume::AlignableVolume(const char* symName, uint32_t label, uint32_t det, bool sens) : mSymName(symName), mLabel(det, label, sens) +{ + init(); +} + +AlignableVolume::AlignableVolume(const char* symName, GlobalLabel label) : mSymName(symName), mLabel(label) +{ + init(); +} + +void AlignableVolume::init() +{ + // check if this sym volume actually exists + mPNE = gGeoManager->GetAlignableEntry(mSymName.c_str()); + if (mPNE == nullptr) { + LOGP(fatal, "Symbolic volume '{}' has no corresponding alignable entry!", mSymName); + } + mPN = mPNE->GetPhysicalNode(); + if (mPN == nullptr) { + LOGP(debug, "Adding physical node to {}", mSymName); + mPN = gGeoManager->MakePhysicalNode(mPNE->GetTitle()); + if (mPN == nullptr) { + LOGP(fatal, "Failed to make physical node for {}", mSymName); + } + } +} + +void AlignableVolume::finalise(uint8_t level) +{ + if (level == 0 && !isRoot()) { + LOGP(fatal, "Finalise should be called only from the root node!"); + } + mLevel = level; + if (!isLeaf()) { + // depth first + for (const auto& c : mChildren) { + c->finalise(level + 1); + } + // auto-disable parent RB DOFs if no children are active + if (mRigidBody) { + int nActiveChildren = 0; + for (const auto& c : mChildren) { + if (c->isActive()) { + ++nActiveChildren; + } + } + if (!nActiveChildren) { + for (int iDOF = 0; iDOF < mRigidBody->nDOFs(); ++iDOF) { + if (mRigidBody->isFree(iDOF)) { + LOGP(warn, "Auto-disabling DOF {} for {} since no active children", + mRigidBody->dofName(iDOF), mSymName); + mRigidBody->setFree(iDOF, false); + } + } + } + } + } else { + // for sensors we need also to define the transformation from the measurment (TRK) to the local frame (LOC) + // need to it with including possible pre-alignment to allow for iterative convergence + // (TRK) is defined wrt global z-axis + defineMatrixL2G(); + defineMatrixT2L(); + } + if (!isRoot()) { + // prepare the transformation matrices, e.g. from child frame to parent frame + // this is not necessarily just one level transformation + TGeoHMatrix mat = *mPN->GetMatrix(); // global matrix (including possible pre-alignment) from this volume to the global frame + if (isLeaf()) { + mat = mL2G; // for sensor volumes they might have redefined the L2G definition + } + auto inv = mParent->mPN->GetMatrix()->Inverse(); // global (including possible pre-alignment) from this volume to the global frame + mat.MultiplyLeft(inv); // left mult. effectively subtracts the parent transformation which is included in the the childs + mL2P = mat; // now this is directly the child to the parent transformation (LOC) (including possible pre-alignment) + + // prepare jacobian from child to parent frame + Eigen::Map> rotL2P(mL2P.GetRotationMatrix()); + Eigen::Matrix3d rotInv = rotL2P.transpose(); // parent-to-child rotation + const double* t = mL2P.GetTranslation(); // child origin in parent frame + Eigen::Matrix3d skewT; + skewT << 0, -t[2], t[1], t[2], 0, -t[0], -t[1], t[0], 0; + mJL2P.setZero(); + mJL2P.topLeftCorner<3, 3>() = rotInv; + mJL2P.topRightCorner<3, 3>() = -rotInv * skewT; + mJL2P.bottomRightCorner<3, 3>() = rotInv; + mJP2L = mJL2P.inverse(); + } +} + +void AlignableVolume::writeRigidBodyConstraints(std::ostream& os) const +{ + if (isLeaf() || !mRigidBody) { + // recurse even if this node has no RB DOFs + for (const auto& c : mChildren) { + c->writeRigidBodyConstraints(os); + } + return; + } + + for (int iDOF = 0; iDOF < mRigidBody->nDOFs(); ++iDOF) { + if (!mRigidBody->isFree(iDOF)) { + continue; + } + double nActiveChildren = 0.; + for (const auto& c : mChildren) { + if (c->isActive()) { + ++nActiveChildren; + } + } + if (nActiveChildren == 0.) { + LOGP(fatal, "{} has dof {} active but no active children!", mSymName, mRigidBody->dofName(iDOF)); + } + const double invN = 1.0 / nActiveChildren; + HierarchyConstraint con(std::format("DOF {} for {}", mRigidBody->dofName(iDOF), mSymName), 0.0); + for (const auto& c : mChildren) { + if (!c->mRigidBody) { + continue; + } + for (int jDOF = 0; jDOF < c->mRigidBody->nDOFs(); ++jDOF) { + if (!c->mRigidBody->isFree(jDOF)) { + continue; + } + double coeff = invN * c->getJP2L()(iDOF, jDOF); + if (std::abs(coeff) > 1e-16f) { + con.add(c->getLabel().raw(jDOF), coeff); + } + } + } + + if (con.getSize() > 1) { + con.write(os); + } + } + for (const auto& c : mChildren) { + c->writeRigidBodyConstraints(os); + } +} + +void AlignableVolume::writeParameters(std::ostream& os) const +{ + if (isRoot()) { + os << "Parameter\n"; + } + if (mRigidBody) { + for (int iDOF = 0; iDOF < mRigidBody->nDOFs(); ++iDOF) { + os << std::format("{:<10} {:>+15g} {:>+15g} ! {} {} ", + mLabel.raw(iDOF), 0.0, (mRigidBody->isFree(iDOF) ? 0.0 : -1.0), + (mRigidBody->isFree(iDOF) ? 'V' : 'F'), mRigidBody->dofName(iDOF)) + << mSymName << '\n'; + } + } + if (mCalib) { + auto calibLbl = mLabel.asCalib(); + for (int iDOF = 0; iDOF < mCalib->nDOFs(); ++iDOF) { + os << std::format("{:<10} {:>+15g} {:>+15g} ! {} {} ", + calibLbl.raw(iDOF), 0.0, (mCalib->isFree(iDOF) ? 0.0 : -1.0), + (mCalib->isFree(iDOF) ? 'V' : 'F'), mCalib->dofName(iDOF)) + << mSymName << '\n'; + } + } + for (const auto& c : mChildren) { + c->writeParameters(os); + } +} + +void AlignableVolume::writeTree(std::ostream& os, int indent) const +{ + os << std::string(static_cast(indent * 2), ' ') << mSymName << (mLabel.sens() ? " (sens)" : " (pasv)"); + if (mIsPseudo) { + os << " pseudo"; + } else { + int nFreeDofs{0}; + if (mRigidBody && mRigidBody->nFreeDOFs()) { + nFreeDofs += mRigidBody->nFreeDOFs(); + os << " RB["; + for (int i = 0; i < mRigidBody->nDOFs(); ++i) { + if (mRigidBody->isFree(i)) { + os << " " << mRigidBody->dofName(i) << "(" << mLabel.raw(i) << ")"; + } + } + os << " ]"; + } + if (mCalib && mCalib->nFreeDOFs()) { + nFreeDofs += mCalib->nFreeDOFs(); + os << " CAL["; + auto calibLbl = mLabel.asCalib(); + for (int i = 0; i < mCalib->nDOFs(); ++i) { + if (mCalib->isFree(i)) { + os << " " << mCalib->dofName(i) << "(" << calibLbl.raw(i) << ")"; + } + } + os << " ]"; + } + if (!nFreeDofs) { + os << " no DOFs"; + } + } + os << '\n'; + for (const auto& c : mChildren) { + c->writeTree(os, indent + 2); + } +} + +void applyDOFConfig(AlignableVolume* root, const std::string& jsonPath) +{ + using json = nlohmann::json; + std::ifstream f(jsonPath); + if (!f.is_open()) { + LOGP(fatal, "Cannot open DOF config file: {}", jsonPath); + } + auto data = json::parse(f); + json rules = data.is_array() ? data : data.value("rules", json::array()); + + static const std::map rbNameToIdx = { + {"TX", 0}, {"TY", 1}, {"TZ", 2}, {"RX", 3}, {"RY", 4}, {"RZ", 5}}; + + auto matchPattern = [](const std::string& pattern, const std::string& sym) -> bool { + if (fnmatch(pattern.c_str(), sym.c_str(), 0) == 0) { + return true; + } + std::string prefixed = "*" + pattern; + return fnmatch(prefixed.c_str(), sym.c_str(), 0) == 0; + }; + + if (data.is_object() && data.contains("defaults")) { + json defRule = data["defaults"]; + defRule["match"] = "*"; + rules.insert(rules.begin(), defRule); + } + + root->traverse([&](AlignableVolume* vol) { + const std::string& sym = vol->getSymName(); + for (const auto& rule : rules) { + const auto pattern = rule["match"].get(); + if (!matchPattern(pattern, sym)) { + continue; + } + // rigid body DOFs + if (rule.contains("rigidBody")) { + const auto& rb = rule["rigidBody"]; + if (rb.is_string()) { + auto s = rb.get(); + if (s == "all" || s == "free") { + vol->setRigidBody(std::make_unique()); + } else if (s == "fixed") { + auto dofSet = std::make_unique(); + dofSet->setAllFree(false); + vol->setRigidBody(std::move(dofSet)); + } + } else if (rb.is_array()) { + auto dofSet = std::make_unique(); + dofSet->setAllFree(false); + for (const auto& name : rb) { + auto it = rbNameToIdx.find(name.get()); + if (it != rbNameToIdx.end()) { + dofSet->setFree(it->second, true); + } + } + vol->setRigidBody(std::move(dofSet)); + } else if (rb.is_object()) { + auto dofs = rb.value("dofs", std::string("all")); + bool fixed = rb.value("fixed", false); + if (dofs == "all") { + auto dofSet = std::make_unique(); + if (fixed) { + dofSet->setAllFree(false); + } + vol->setRigidBody(std::move(dofSet)); + } else if (rb["dofs"].is_array()) { + auto dofSet = std::make_unique(); + dofSet->setAllFree(false); + for (const auto& name : rb["dofs"]) { + auto it = rbNameToIdx.find(name.get()); + if (it != rbNameToIdx.end()) { + dofSet->setFree(it->second, !fixed); + } + } + vol->setRigidBody(std::move(dofSet)); + } + } + } + // calibration DOFs + if (rule.contains("calib")) { + const auto& cal = rule["calib"]; + auto calType = cal.value("type", std::string("")); + if (calType == "legendre") { + int order = cal.value("order", 3); + auto dofSet = std::make_unique(order); + bool fixed = cal.value("fixed", false); + if (fixed) { + dofSet->setAllFree(false); + } + // fix/free individual coefficients by name or index + if (cal.contains("free")) { + dofSet->setAllFree(false); + for (const auto& item : cal["free"]) { + if (item.is_number_integer()) { + dofSet->setFree(item.get(), true); + } else if (item.is_string()) { + // match by name e.g. "L(1,0)" + for (int k = 0; k < dofSet->nDOFs(); ++k) { + if (dofSet->dofName(k) == item.get()) { + dofSet->setFree(k, true); + } + } + } + } + } + if (cal.contains("fix")) { + for (const auto& item : cal["fix"]) { + if (item.is_number_integer()) { + dofSet->setFree(item.get(), false); + } else if (item.is_string()) { + for (int k = 0; k < dofSet->nDOFs(); ++k) { + if (dofSet->dofName(k) == item.get()) { + dofSet->setFree(k, false); + } + } + } + } + } + vol->setCalib(std::move(dofSet)); + } + } + } + }); +} + +void writeMillepedeResults(AlignableVolume* root, const std::string& milleResPath, const std::string& outJsonPath, const std::string& injectedJsonPath) +{ + using json = nlohmann::json; + + // parse millepede.res: label fittedValue presigma [...] + std::ifstream fin(milleResPath); + if (!fin.is_open()) { + LOGP(fatal, "Cannot open millepede result file: {}", milleResPath); + } + std::map labelToValue; + std::string line; + while (std::getline(fin, line)) { + if (line.empty() || line[0] == '!' || line[0] == '*') { + continue; + } + if (line.find("Parameter") != std::string::npos) { + continue; + } + std::istringstream iss(line); + uint32_t label = 0; + double value = NAN, presigma = NAN; + if (!(iss >> label >> value >> presigma)) { + continue; + } + if (presigma >= 0.0) { // skip fixed parameters + labelToValue[label] = value; + } + } + fin.close(); + LOGP(info, "Parsed {} not fixed parameters from {}", labelToValue.size(), milleResPath); + + // load injected misalignment if provided (same format as closure test input) + // indexed by sensorID + std::map> injRB; + std::map>> injMatrix; + if (!injectedJsonPath.empty()) { + std::ifstream injFile(injectedJsonPath); + if (injFile.is_open()) { + json injData = json::parse(injFile); + for (const auto& item : injData) { + int id = item["id"].get(); + if (item.contains("rigidBody")) { + injRB[id] = item["rigidBody"].get>(); + } + if (item.contains("matrix")) { + injMatrix[id] = item["matrix"].get>>(); + } + } + LOGP(info, "Loaded injected misalignment for {} sensors", injData.size()); + } else { + LOGP(warn, "Cannot open injected misalignment file: {}, writing absolute values", injectedJsonPath); + } + } + + // collect results per volume that has RB or calib DOFs + json output = json::array(); + root->traverse([&](AlignableVolume* vol) { + auto* rb = vol->getRigidBody(); + auto* cal = vol->getCalib(); + if ((!rb && !cal) || vol->isPseudo()) { + return; + } + int id = vol->getSensorId(); + json entry; + entry["symName"] = vol->getSymName(); + entry["id"] = id; + bool write = false; + + // rigid body parameters + if (rb && rb->nFreeDOFs()) { + write = true; + json rbArr = json::array(); + const auto& inj = injRB.contains(id) ? injRB[id] : std::vector{}; + for (int i = 0; i < rb->nDOFs(); ++i) { + uint32_t raw = vol->getLabel().raw(i); + auto it = labelToValue.find(raw); + double fitted = it != labelToValue.end() ? it->second : 0.0; + double ref = i < static_cast(inj.size()) ? inj[i] : 0.0; + rbArr.push_back(fitted - ref); + } + entry["rigidBody"] = rbArr; + } + + // calibration (Legendre) parameters + if (cal && cal->nFreeDOFs() && cal->type() == DOFSet::Type::Legendre) { + write = true; + auto* leg = dynamic_cast(cal); + int order = leg->order(); + auto calibLbl = vol->getLabel().asCalib(); + const auto& inj = injMatrix.contains(id) ? injMatrix[id] : std::vector>{}; + json matrix = json::array(); + int idx = 0; + for (int i = 0; i <= order; ++i) { + json row = json::array(); + for (int j = 0; j <= i; ++j) { + uint32_t raw = calibLbl.raw(idx); + auto it = labelToValue.find(raw); + double fitted = it != labelToValue.end() ? it->second : 0.0; + double ref = (i < static_cast(inj.size()) && j < static_cast(inj[i].size())) ? inj[i][j] : 0.0; + row.push_back(fitted - ref); + ++idx; + } + matrix.push_back(row); + } + entry["matrix"] = matrix; + } + if (write) { + output.push_back(entry); + } + }); + + std::ofstream fout(outJsonPath); + if (!fout.is_open()) { + LOGP(fatal, "Cannot open output file: {}", outJsonPath); + } + fout << output.dump(2) << '\n'; + fout.close(); + LOGP(info, "Wrote millepede results to {}", outJsonPath); +} + +} // namespace o2::its3::align diff --git a/Detectors/Upgrades/ITS3/alignment/src/AlignmentParams.cxx b/Detectors/Upgrades/ITS3/alignment/src/AlignmentParams.cxx new file mode 100644 index 0000000000000..0d89cb4d4cffd --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/src/AlignmentParams.cxx @@ -0,0 +1,13 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "ITS3Align/AlignmentParams.h" +O2ParamImpl(o2::its3::align::AlignmentParams); diff --git a/Detectors/Upgrades/ITS3/alignment/src/AlignmentSensors.cxx b/Detectors/Upgrades/ITS3/alignment/src/AlignmentSensors.cxx new file mode 100644 index 0000000000000..7644c37107104 --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/src/AlignmentSensors.cxx @@ -0,0 +1,201 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include + +#include "Framework/Logger.h" +#include "ITSMFTBase/SegmentationAlpide.h" +#include "ITS3Align/AlignmentSensors.h" +#include "ITSBase/GeometryTGeo.h" + +namespace o2::its3::align +{ + +AlignableVolume::Ptr buildHierarchyITS(AlignableVolume::SensorMapping& sensorMap) +{ + uint32_t gLbl{0}, det{0}; + auto geom = o2::its::GeometryTGeo::Instance(); + AlignableVolume *volHB{nullptr}, *volSt{nullptr}, *volHSt{nullptr}, *volMod{nullptr}; + std::unordered_map sym2vol; + + auto root = std::make_unique(geom->composeSymNameITS(), gLbl++, det, false); + sym2vol[root->getSymName()] = root.get(); + for (int ilr = 0; ilr < geom->getNumberOfLayers(); ilr++) { + for (int ihb = 0; ihb < geom->getNumberOfHalfBarrels(); ihb++) { + volHB = root->addChild(geom->composeSymNameHalfBarrel(ilr, ihb), gLbl++, det, false); + sym2vol[volHB->getSymName()] = volHB; + int nstavesHB = geom->getNumberOfStaves(ilr) / 2; + for (int ist = 0; ist < nstavesHB; ist++) { + volSt = volHB->addChild(geom->composeSymNameStave(ilr, ihb, ist), gLbl++, det, false); + sym2vol[volSt->getSymName()] = volSt; + for (int ihst = 0; ihst < geom->getNumberOfHalfStaves(ilr); ihst++) { + volHSt = volSt->addChild(geom->composeSymNameHalfStave(ilr, ihb, ist, ihst), gLbl++, det, false); + sym2vol[volHSt->getSymName()] = volHSt; + for (int imd = 0; imd < geom->getNumberOfModules(ilr); imd++) { + volMod = volHSt->addChild(geom->composeSymNameModule(ilr, ihb, ist, ihst, imd), gLbl++, det, false); + sym2vol[volMod->getSymName()] = volMod; + } + } + } + } + } + + // NOTE: for ITS sensors the local x and y are swapped + int lay = 0, hba = 0, sta = 0, ssta = 0, modd = 0, chip = 0; + for (int ich = 0; ich < geom->getNumberOfChips(); ich++) { + geom->getChipId(ich, lay, hba, sta, ssta, modd, chip); + GlobalLabel lbl(det, ich, true); + AlignableVolume* parVol = sym2vol[modd < 0 ? geom->composeSymNameStave(lay, hba, sta) : geom->composeSymNameModule(lay, hba, sta, ssta, modd)]; + if (!parVol) { + LOGP(fatal, "did not find parent for chip {}", ich); + } + int nch = modd < 0 ? geom->getNumberOfChipsPerStave(lay) : geom->getNumberOfChipsPerModule(lay); + int jch = ich % nch; + auto* chip = parVol->addChild(geom->composeSymNameChip(lay, hba, sta, ssta, modd, jch), lbl); + chip->setSensorId(ich); + sensorMap[lbl] = chip; + } + return root; +} + +AlignableVolume::Ptr buildHierarchyIT3(AlignableVolume::SensorMapping& sensorMap) +{ + uint32_t gLbl{0}, det{0}; + auto geom = o2::its::GeometryTGeo::Instance(); + AlignableVolume *volHB{nullptr}, *volSt{nullptr}, *volHSt{nullptr}, *volMod{nullptr}; + std::unordered_map sym2vol; + + auto root = std::make_unique(geom->composeSymNameITS(), gLbl++, det, false); + sym2vol[root->getSymName()] = root.get(); + for (int ilr = 0; ilr < geom->getNumberOfLayers(); ilr++) { + const bool isLayITS3 = (ilr < 3); + for (int ihb = 0; ihb < geom->getNumberOfHalfBarrels(); ihb++) { + volHB = root->addChild(geom->composeSymNameHalfBarrel(ilr, ihb, isLayITS3), gLbl++, det, false); + sym2vol[volHB->getSymName()] = volHB; + if (isLayITS3) { + volHB->setSensorId((2 * ilr) + ihb); + continue; // no deeper hierarchy for ITS3 layers + } + int nstavesHB = geom->getNumberOfStaves(ilr) / 2; + for (int ist = 0; ist < nstavesHB; ist++) { + volSt = volHB->addChild(geom->composeSymNameStave(ilr, ihb, ist), gLbl++, det, false); + sym2vol[volSt->getSymName()] = volSt; + for (int ihst = 0; ihst < geom->getNumberOfHalfStaves(ilr); ihst++) { + volHSt = volSt->addChild(geom->composeSymNameHalfStave(ilr, ihb, ist, ihst), gLbl++, det, false); + sym2vol[volHSt->getSymName()] = volHSt; + for (int imd = 0; imd < geom->getNumberOfModules(ilr); imd++) { + volMod = volHSt->addChild(geom->composeSymNameModule(ilr, ihb, ist, ihst, imd), gLbl++, det, false); + sym2vol[volMod->getSymName()] = volMod; + } + } + } + } + } + + int lay = 0, hba = 0, sta = 0, ssta = 0, modd = 0, chip = 0; + for (int ich = 0; ich < geom->getNumberOfChips(); ich++) { + geom->getChipId(ich, lay, hba, sta, ssta, modd, chip); + const bool isLayITS3 = (lay < 3); + GlobalLabel lbl(det, ich, true); + if (isLayITS3) { + // ITS3 chips by construction do not have any DOFs still add them to have the measurment to alignable layer relation + AlignableVolume* parVol = sym2vol[geom->composeSymNameHalfBarrel(lay, hba, true)]; + if (!parVol) { + LOGP(fatal, "did not find parent for chip {}", ich); + } + auto* tile = parVol->addChild(geom->composeSymNameChip(lay, hba, sta, ssta, modd, chip, true), lbl); + tile->setPseudo(true); + tile->setSensorId(ich); + sensorMap[lbl] = tile; + } else { + AlignableVolume* parVol = sym2vol[modd < 0 ? geom->composeSymNameStave(lay, hba, sta) : geom->composeSymNameModule(lay, hba, sta, ssta, modd)]; + if (!parVol) { + LOGP(fatal, "did not find parent for chip {}", ich); + } + int nch = modd < 0 ? geom->getNumberOfChipsPerStave(lay) : geom->getNumberOfChipsPerModule(lay); + int jch = ich % nch; + auto* chip = parVol->addChild(geom->composeSymNameChip(lay, hba, sta, ssta, modd, jch), lbl); + chip->setSensorId(ich); + sensorMap[lbl] = chip; + } + } + return root; +} + +void AlignableSensorITS::defineMatrixL2G() +{ + // the chip volume is not the measurment plane, need to correct for the epitaxial layer + const auto* chipL2G = mPN->GetMatrix(); + mL2G = *chipL2G; + double delta = itsmft::SegmentationAlpide::SensorLayerThickness - itsmft::SegmentationAlpide::SensorLayerThicknessEff; + TGeoTranslation tra(0., 0.5 * delta, 0.); + mL2G *= tra; +} + +void AlignableSensorITS::defineMatrixT2L() +{ + double locA[3] = {-100., 0., 0.}, locB[3] = {100., 0., 0.}, gloA[3], gloB[3]; + mL2G.LocalToMaster(locA, gloA); + mL2G.LocalToMaster(locB, gloB); + double dx = gloB[0] - gloA[0], dy = gloB[1] - gloA[1]; + double t = (gloB[0] * dx + gloB[1] * dy) / (dx * dx + dy * dy); + double xp = gloB[0] - (dx * t), yp = gloB[1] - (dy * t); + double alp = std::atan2(yp, xp); + o2::math_utils::bringTo02Pid(alp); + mT2L.RotateZ(alp * TMath::RadToDeg()); // mT2L before is identity and afterwards rotated + const TGeoHMatrix l2gI = mL2G.Inverse(); + mT2L.MultiplyLeft(l2gI); +} + +void AlignableSensorITS::computeJacobianL2T(const double* posLoc, Matrix66& jac) const +{ + jac.setZero(); + Eigen::Map> rotT2L(mT2L.GetRotationMatrix()); + Eigen::Matrix3d skew, rotL2T = rotT2L.transpose(); + skew << 0, -posLoc[2], posLoc[1], posLoc[2], 0, -posLoc[0], -posLoc[1], posLoc[0], 0; + jac.topLeftCorner<3, 3>() = rotL2T; + jac.topRightCorner<3, 3>() = -rotL2T * skew; + jac.bottomRightCorner<3, 3>() = rotL2T; +} + +void AlignableSensorIT3::defineMatrixL2G() +{ + mL2G = *mPN->GetMatrix(); +} + +void AlignableSensorIT3::defineMatrixT2L() +{ + double locA[3] = {-100., 0., 0.}, locB[3] = {100., 0., 0.}, gloA[3], gloB[3]; + mL2G.LocalToMaster(locA, gloA); + mL2G.LocalToMaster(locB, gloB); + double dx = gloB[0] - gloA[0], dy = gloB[1] - gloA[1]; + double t = (gloB[0] * dx + gloB[1] * dy) / (dx * dx + dy * dy); + double xp = gloB[0] - (dx * t), yp = gloB[1] - (dy * t); + double alp = std::atan2(yp, xp); + o2::math_utils::bringTo02Pid(alp); + mT2L.RotateZ(alp * TMath::RadToDeg()); + const TGeoHMatrix l2gI = mL2G.Inverse(); + mT2L.MultiplyLeft(l2gI); +} + +void AlignableSensorIT3::computeJacobianL2T(const double* posLoc, Matrix66& jac) const +{ + jac.setZero(); + Eigen::Map> rotT2L(mT2L.GetRotationMatrix()); + Eigen::Matrix3d skew, rotL2T = rotT2L.transpose(); + skew << 0, -posLoc[2], posLoc[1], posLoc[2], 0, -posLoc[0], -posLoc[1], posLoc[0], 0; + jac.topLeftCorner<3, 3>() = rotL2T; + jac.topRightCorner<3, 3>() = -rotL2T * skew; + jac.bottomRightCorner<3, 3>() = rotL2T; +} + +} // namespace o2::its3::align diff --git a/Detectors/Upgrades/ITS3/alignment/src/AlignmentSpec.cxx b/Detectors/Upgrades/ITS3/alignment/src/AlignmentSpec.cxx new file mode 100644 index 0000000000000..d381abc6aa567 --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/src/AlignmentSpec.cxx @@ -0,0 +1,1003 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include +#include + +#ifdef WITH_OPENMP +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "Framework/ConfigParamRegistry.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "ITSBase/GeometryTGeo.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DetectorsBase/Propagator.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "Steer/MCKinematicsReader.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "ReconstructionDataFormats/VtxTrackRef.h" +#include "ITS3Reconstruction/TopologyDictionary.h" +#include "DataFormatsITSMFT/TopologyDictionary.h" +#include "ITStracking/MathUtils.h" +#include "ITStracking/IOUtils.h" +#include "ITS3Reconstruction/IOUtils.h" +#include "ITS3Align/TrackFit.h" +#include "ITS3Align/AlignmentSpec.h" +#include "ITS3Align/AlignmentParams.h" +#include "ITS3Align/AlignmentTypes.h" +#include "ITS3Align/AlignmentHierarchy.h" +#include "ITS3Align/AlignmentSensors.h" +#include "MathUtils/LegendrePols.h" + +namespace o2::its3::align +{ +using namespace o2::framework; +using DetID = o2::detectors::DetID; +using DataRequest = o2::globaltracking::DataRequest; +using PVertex = o2::dataformats::PrimaryVertex; +using V2TRef = o2::dataformats::VtxTrackRef; +using VTIndex = o2::dataformats::VtxTrackIndex; +using GTrackID = o2::dataformats::GlobalTrackID; +using TrackD = o2::track::TrackParCovD; + +namespace +{ +// compute normalized (u,v) in [-1,1] from global position on a sensor +std::pair computeUV(double gloX, double gloY, double gloZ, int sensorID, double radius) +{ + const bool isTop = sensorID % 2 == 0; + const double phi = o2::math_utils::to02Pid(std::atan2(gloY, gloX)); + const double phiBorder1 = o2::math_utils::to02Pid(((isTop ? 0. : 1.) * TMath::Pi()) + std::asin(constants::equatorialGap / 2. / radius)); + const double phiBorder2 = o2::math_utils::to02Pid(((isTop ? 1. : 2.) * TMath::Pi()) - std::asin(constants::equatorialGap / 2. / radius)); + const double u = (((phi - phiBorder1) * 2.) / (phiBorder2 - phiBorder1)) - 1.; + const double v = ((2. * gloZ + constants::segment::lengthSensitive) / constants::segment::lengthSensitive) - 1.; + return {u, v}; +} + +// evaluate Legendre polynomials P_0(x) through P_order(x) via recurrence +std::vector legendrePols(int order, double x) +{ + std::vector p(order + 1); + p[0] = 1.; + if (order > 0) { + p[1] = x; + } + for (int n = 1; n < order; ++n) { + p[n + 1] = ((2 * n + 1) * x * p[n] - n * p[n - 1]) / (n + 1); + } + return p; +} +} // namespace + +class AlignmentSpec final : public Task +{ + public: + ~AlignmentSpec() final = default; + AlignmentSpec(const AlignmentSpec&) = delete; + AlignmentSpec(AlignmentSpec&&) = delete; + AlignmentSpec& operator=(const AlignmentSpec&) = delete; + AlignmentSpec& operator=(AlignmentSpec&&) = delete; + AlignmentSpec(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool useMC, bool withPV, bool withITS, OutputEnum out) + : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mUseMC(useMC), mWithPV(withPV), mIsITS3(!withITS), mOutOpt(out) + { + } + + void init(InitContext& ic) final; + void run(ProcessingContext& pc) final; + void endOfStream(EndOfStreamContext& ec) final; + void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; + void process(); + + private: + void updateTimeDependentParams(ProcessingContext& pc); + void buildHierarchy(); + + // calculate the transport jacobian for points FROM and TO numerically via ridder's method + // this assumes the track is already at point FROM and will be extrapolated to TO's x (xTo) + // method does not modify the original track + bool getTransportJacobian(const TrackD& track, double xTo, double alphaTo, gbl::Matrix5d& jac, gbl::Matrix5d& err); + + // refit ITS track with inward/outward fit (opt. impose pv as additional constraint) + // after this we have the refitted track at the innermost update point + bool prepareITSTrack(int iTrk, const o2::its::TrackITS& itsTrack, Track& resTrack); + + // prepare ITS measuremnt points + void prepareMeasurments(std::span clusters, std::span pattIt); + + // build track to vertex association + void buildT2V(); + + // apply some misalignment on inner ITS3 layers + // it can happen that a measurement is pushed outside of + // ITS3 acceptance so false is to discard track + bool applyMisalignment(Eigen::Vector2d& res, const FrameInfoExt& frame, const TrackD& wTrk, size_t iTrk); + + OutputEnum mOutOpt; + std::unique_ptr mDBGOut; + std::vector mPVs; + std::vector mT2PV; + bool mIsITS3{true}; + const o2::itsmft::TopologyDictionary* mITSDict{nullptr}; + const o2::its3::TopologyDictionary* mIT3Dict{nullptr}; + o2::globaltracking::RecoContainer* mRecoData = nullptr; + std::unique_ptr mcReader; + std::vector mITSTrackingInfo; + std::shared_ptr mDataRequest; + std::shared_ptr mGGCCDBRequest; + std::unique_ptr mHierarchy; // tree-hiearchy + AlignableVolume::SensorMapping mChip2Hiearchy; // global label mapping to leaves in the tree + bool mUseMC{false}; + bool mWithPV{false}; + GTrackID::mask_t mTracksSrc; + int mNThreads{1}; + const AlignmentParams* mParams{nullptr}; + std::array mDeformations; // one per sensorID (0-5) + std::array, 6> mRigidBodyParams; // (dx,dy,dz,rx,ry,rz) in LOC per sensorID +}; + +void AlignmentSpec::init(InitContext& ic) +{ + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + mNThreads = ic.options().get("nthreads"); + if (mOutOpt) { + LOG(info) << mOutOpt.pstring(); + mDBGOut = std::make_unique("its3_debug_alg.root", "recreate"); + } + if (mUseMC) { + mcReader = std::make_unique("collisioncontext.root"); + } +} + +void AlignmentSpec::run(ProcessingContext& pc) +{ + if (mOutOpt[OutputOpt::MilleRes]) { + updateTimeDependentParams(pc); + writeMillepedeResults(mHierarchy.get(), mParams->milleResFile, mParams->milleResOutJson, mParams->misAlgJson); + } else { + o2::globaltracking::RecoContainer recoData; + mRecoData = &recoData; + mRecoData->collectData(pc, *mDataRequest); + updateTimeDependentParams(pc); + process(); + } + mRecoData = nullptr; +} + +void AlignmentSpec::process() +{ + if (!mITSDict && !mIT3Dict) { + LOGP(fatal, "ITS data is not loaded"); + } + auto prop = o2::base::PropagatorD::Instance(); + const auto bz = prop->getNominalBz(); + const auto itsTracks = mRecoData->getITSTracks(); + const auto itsClRefs = mRecoData->getITSTracksClusterRefs(); + const auto clusITS = mRecoData->getITSClusters(); + const auto patterns = mRecoData->getITSClustersPatterns(); + std::span mcLbls; + if (mUseMC) { + mcLbls = mRecoData->getITSTracksMCLabels(); + } + prepareMeasurments(clusITS, patterns); + + if (mWithPV) { + buildT2V(); + } + + LOGP(info, "Starting fits with {} threads", mNThreads); + + // Data + std::vector> gblTrajSlots(mNThreads); + std::vector> resTrackSlots(mNThreads); + + auto timeStart = std::chrono::high_resolution_clock::now(); + int cFailedRefit{0}, cFailedProp{0}, cSelected{0}, cGBLFit{0}, cGBLFitFail{0}, cGBLChi2Rej{0}, cGBLConstruct{0}; + double chi2Sum{0}, lostWeightSum{0}; + int ndfSum{0}; +#ifdef WITH_OPENMP +#pragma omp parallel num_threads(mNThreads) \ + reduction(+ : cFailedRefit) \ + reduction(+ : cFailedProp) \ + reduction(+ : cSelected) \ + reduction(+ : cGBLFit) \ + reduction(+ : cGBLFitFail) \ + reduction(+ : cGBLChi2Rej) \ + reduction(+ : cGBLConstruct) \ + reduction(+ : chi2Sum) \ + reduction(+ : lostWeightSum) \ + reduction(+ : ndfSum) +#endif + { +#ifdef WITH_OPENMP + const int tid = omp_get_thread_num(); +#else + const int tid = 0; +#endif + auto& gblTrajSlot = gblTrajSlots[tid]; + auto& resTrackSlot = resTrackSlots[tid]; + +#ifdef WITH_OPENMP +#pragma omp for schedule(dynamic) +#endif + for (size_t iTrk = 0; iTrk < (int)itsTracks.size(); ++iTrk) { + const auto& trk = itsTracks[iTrk]; + if (trk.getNClusters() < mParams->minITSCls || + (trk.getChi2() / ((float)trk.getNClusters() * 2 - 5)) >= mParams->maxITSChi2Ndf || + trk.getPt() < mParams->minPt || + (mUseMC && (!mcLbls[iTrk].isValid() || !mcLbls[iTrk].isCorrect()))) { + continue; + } + ++cSelected; + Track& resTrack = resTrackSlot.emplace_back(); + if (!prepareITSTrack((int)iTrk, trk, resTrack)) { + ++cFailedRefit; + resTrackSlot.pop_back(); + continue; + } + + o2::track::TrackParD* refLin = nullptr; + if (mParams->useStableRef) { + refLin = &resTrack.track; + } + + // outward stepping from track IU + auto wTrk = resTrack.track; + const bool hasPV = resTrack.info[0].lr == -1; + std::vector points; + bool failed = false; + const int np = (int)resTrack.points.size(); + track::TrackLTIntegral lt; + lt.setTimeNotNeeded(); + constexpr int perm[5] = {4, 2, 3, 0, 1}; // ALICE->GBL: Q/Pt,Snp,Tgl,Y,Z + for (int ip{0}; ip < np; ++ip) { + const auto& frame = resTrack.info[ip]; + gbl::Matrix5d err = gbl::Matrix5d::Identity(), jacALICE = gbl::Matrix5d::Identity(), jacGBL; + float msErr = 0.f; + if (ip) { + // numerically calculates the transport jacobian from prev. point to this point + // then we actually do the step to the point and accumulate the material + if (!getTransportJacobian(wTrk, frame.x, frame.alpha, jacALICE, err) || + !prop->propagateToAlphaX(wTrk, refLin, frame.alpha, frame.x, false, mParams->maxSnp, mParams->maxStep, 1, mParams->corrType, <)) { + ++cFailedProp; + failed = true; + break; + } + msErr = its::math_utils::MSangle(trk.getPID().getMass(), trk.getP(), lt.getX2X0()); + // after computing jac, reorder to GBL convention + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 5; j++) { + jacGBL(i, j) = jacALICE(perm[i], perm[j]); + } + } + } + + // wTrk is now in the measurment frame + gbl::GblPoint point(jacGBL); + // measurement + Eigen::Vector2d res, prec; + res << frame.positionTrackingFrame[0] - wTrk.getY(), frame.positionTrackingFrame[1] - wTrk.getZ(); + + // here we can apply some misalignment on the measurment + if (!applyMisalignment(res, frame, wTrk, iTrk)) { + failed = true; + break; + } + + prec << 1. / resTrack.points[ip].sig2y, 1. / resTrack.points[ip].sig2z; + // the projection matrix is in the tracking frame the idendity so no need to diagonalize it + point.addMeasurement(res, prec); + if (msErr > mParams->minMS && ip < np - 1) { + Eigen::Vector2d scat(0., 0.), scatPrec = Eigen::Vector2d::Constant(1. / (msErr * msErr)); + point.addScatterer(scat, scatPrec); + lt.clearFast(); // clear if accounted + } + + if (frame.lr >= 0) { + GlobalLabel lbl(0, frame.sens, true); + if (mChip2Hiearchy.find(lbl) == mChip2Hiearchy.end()) { + LOGP(fatal, "Cannot find global label: {}", lbl.asString()); + } + + // derivatives for all sensitive volumes and their parents + // this is the derivative in TRK but we want to align in LOC + // so dr/da_(LOC) = dr/da_(TRK) * da_(TRK)/da_(LOC) + const auto* tileVol = mChip2Hiearchy.at(lbl); + Matrix36 der = getRigidBodyDerivatives(wTrk); + + // count rigid body columns: only volumes with real DOFs (not DOFPseudo) + int nColRB{0}; + for (const auto* v = tileVol; v && !v->isRoot(); v = v->getParent()) { + if (v->getRigidBody()) { + nColRB += v->getRigidBody()->nDOFs(); + } + } + + // count calibration columns + const auto* sensorVol = tileVol->getParent(); + const auto* calibSet = sensorVol ? sensorVol->getCalib() : nullptr; + const int nCalib = calibSet ? calibSet->nDOFs() : 0; + const int nCol = nColRB + nCalib; + + std::vector gLabels; + gLabels.reserve(nCol); + Eigen::MatrixXd gDer(3, nCol); + gDer.setZero(); + Eigen::Index curCol{0}; + + // 1) tile: TRK -> LOC via precomputed T2L and J_L2T + const double posTrk[3] = {frame.x, 0., 0.}; + double posLoc[3]; + tileVol->getT2L().LocalToMaster(posTrk, posLoc); + Matrix66 jacL2T; + tileVol->computeJacobianL2T(posLoc, jacL2T); + der *= jacL2T; + if (tileVol->getRigidBody()) { + const int nd = tileVol->getRigidBody()->nDOFs(); + for (int iDOF = 0; iDOF < nd; ++iDOF) { + gLabels.push_back(tileVol->getLabel().rawGBL(iDOF)); + } + gDer.middleCols(curCol, nd) = der; + curCol += nd; + } + + // 2) chain through parents: child's J_L2P + for (const auto* child = tileVol; child->getParent() && !child->getParent()->isRoot(); child = child->getParent()) { + der *= child->getJL2P(); + const auto* parent = child->getParent(); + if (parent->getRigidBody()) { + const int nd = parent->getRigidBody()->nDOFs(); + for (int iDOF = 0; iDOF < nd; ++iDOF) { + gLabels.push_back(parent->getLabel().rawGBL(iDOF)); + } + gDer.middleCols(curCol, nd) = der; + curCol += nd; + } + } + + // 3) calibration derivatives (e.g. Legendre for ITS3 sensors, apply directly on the whole sensor, not on inidividual tiles) + if (calibSet && calibSet->type() == DOFSet::Type::Legendre) { + const auto* legSet = static_cast(calibSet); + const int N = legSet->order(); + const int sensorID = constants::detID::getSensorID(frame.sens); + const int layerID = constants::detID::getDetID2Layer(frame.sens); + + const double r = frame.x; + const double gX = r * std::cos(frame.alpha); + const double gY = r * std::sin(frame.alpha); + const double gZ = frame.positionTrackingFrame[1]; + auto [u, v] = computeUV(gX, gY, gZ, sensorID, constants::radii[layerID]); + + const double snp = wTrk.getSnp(); + const double tgl = wTrk.getTgl(); + const double csci = 1. / std::sqrt(1. - (snp * snp)); + const double dydx = snp * csci; + const double dzdx = tgl * csci; + + auto pu = legendrePols(N, u); + auto pv = legendrePols(N, v); + + int legIdx = 0; + const int legColStart = nColRB; + for (int i = 0; i <= N; ++i) { + for (int j = 0; j <= i; ++j) { + const double basis = pu[j] * pv[i - j]; + gLabels.push_back(sensorVol->getLabel().asCalib().rawGBL(legIdx)); + gDer(0, legColStart + legIdx) = dydx * basis; + gDer(1, legColStart + legIdx) = dzdx * basis; + ++legIdx; + } + } + } + point.addGlobals(gLabels, gDer); + } + + if (mOutOpt[OutputOpt::VerboseGBL]) { + static Eigen::IOFormat fmt(4, 0, ", ", "\n", "[", "]"); + LOGP(info, "WORKING-POINT {}", ip); + LOGP(info, "Track: {}", wTrk.asString()); + LOGP(info, "FrameInfo: {}", frame.asString()); + std::cout << "jacALICE:\n" + << jacALICE.format(fmt) << '\n'; + std::cout << "jacGBL:\n" + << jacGBL.format(fmt) << '\n'; + LOGP(info, "Point {}: GBL res=({}, {}), KF stored res=({}, {})", + ip, res[0], res[1], resTrack.points[ip].dy, resTrack.points[ip].dz); + LOGP(info, "residual: dy={} dz={}", res[0], res[1]); + LOGP(info, "precision: precY={} precZ={}", prec[0], prec[1]); + point.printPoint(5); + } + points.push_back(point); + } + if (!failed) { + gbl::GblTrajectory traj(points, std::abs(bz) > 0.01); + if (traj.isValid()) { + double chi2 = NAN, lostWeight = NAN; + int ndf = 0; + if (auto ierr = traj.fit(chi2, ndf, lostWeight); !ierr) { + if (mOutOpt[OutputOpt::VerboseGBL]) { + LOGP(info, "GBL FIT chi2 {} ndf {}", chi2, ndf); + traj.printTrajectory(5); + } + if (chi2 / ndf > mParams->maxChi2Ndf && cGBLChi2Rej++ < 10) { + LOGP(error, "GBL fit exceeded red chi2 {}", chi2 / ndf); + if (std::abs(resTrack.kfFit.chi2Ndf - 1) < 0.02) { + LOGP(error, "\tGBL is far away from good KF fit!!!!"); + continue; + } + } else { + ++cGBLFit; + chi2Sum += chi2; + lostWeightSum += lostWeight; + ndfSum += ndf; + if (mOutOpt[OutputOpt::MilleData]) { + gblTrajSlot.push_back(traj); + } + FitInfo fit; + fit.ndf = ndf; + fit.chi2 = (float)chi2; + fit.chi2Ndf = (float)chi2 / (float)ndf; + resTrack.gblFit = fit; + } + } else { + ++cGBLFitFail; + } + } else { + ++cGBLConstruct; + } + } + } + } + auto timeEnd = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(timeEnd - timeStart); + LOGP(info, "Fitted {} tracks out of {} (selected {}) in {} sec", cGBLFit, itsTracks.size(), cSelected, duration.count() / 1e3); + LOGP(info, "\tRefit failed for {} tracks; Failed prop for {} tracks", cFailedRefit, cFailedProp); + LOGP(info, "\tGBL SUMMARY:"); + LOGP(info, "\t\tGBL construction failed {}", cGBLConstruct); + LOGP(info, "\t\tGBL fit failed {}", cGBLFitFail); + LOGP(info, "\t\tGBL chi2Ndf rejected {}", cGBLChi2Rej); + if (!ndfSum) { + LOGP(info, "\t\tGBL Chi2/Ndf = NDF IS 0"); + } else { + LOGP(info, "\t\tGBL Chi2/Ndf = {}", chi2Sum / ndfSum); + } + LOGP(info, "\t\tGBL LostWeight = {}", lostWeightSum); + LOGP(info, "Streaming results to output"); + if (mOutOpt[OutputOpt::MilleData]) { + gbl::MilleBinary mille(mParams->milleBinFile, true); + for (auto& slot : gblTrajSlots) { + for (auto& traj : slot) { + traj.milleOut(mille); + } + } + } + if (mOutOpt[OutputOpt::Debug]) { + for (auto& slot : resTrackSlots) { + for (auto& res : slot) { + (*mDBGOut) << "res" + << "trk=" << res + << "\n"; + } + } + } +} + +void AlignmentSpec::updateTimeDependentParams(ProcessingContext& pc) +{ + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + if (static bool initOnce{false}; !initOnce) { + initOnce = true; + auto geom = o2::its::GeometryTGeo::Instance(); + o2::its::GeometryTGeo::Instance()->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G, o2::math_utils::TransformType::T2G)); + mParams = &AlignmentParams::Instance(); + mParams->printKeyValues(true, true); + + buildHierarchy(); + + if (mParams->doMisalignmentLeg || mParams->doMisalignmentRB) { + TMatrixD null(1, 1); + null(0, 0) = 0; + for (int i = 0; i < 6; ++i) { + mDeformations[i] = o2::math_utils::Legendre2DPolynominal(null); + mRigidBodyParams[i].setZero(); + } + if (!mParams->misAlgJson.empty()) { + using json = nlohmann::json; + std::ifstream f(mParams->misAlgJson); + auto data = json::parse(f); + for (const auto& item : data) { + int id = item["id"].get(); + if (mParams->doMisalignmentLeg && item.contains("matrix")) { + auto v = item["matrix"].get>>(); + TMatrixD m(v.size(), v[v.size() - 1].size()); + for (size_t r{0}; r < v.size(); ++r) { + for (size_t c{0}; c < v[r].size(); ++c) { + m(r, c) = v[r][c]; + } + } + mDeformations[id] = o2::math_utils::Legendre2DPolynominal(m); + } + if (mParams->doMisalignmentRB && item.contains("rigidBody")) { + auto rb = item["rigidBody"].get>(); + for (int k = 0; k < 6 && k < (int)rb.size(); ++k) { + mRigidBodyParams[id](k) = rb[k]; + } + } + } + } + } + } +} + +void AlignmentSpec::buildHierarchy() +{ + if (mIsITS3) { + mHierarchy = buildHierarchyIT3(mChip2Hiearchy); + } else { + mHierarchy = buildHierarchyITS(mChip2Hiearchy); + } + + if (!mParams->dofConfigJson.empty()) { + applyDOFConfig(mHierarchy.get(), mParams->dofConfigJson); + } + + mHierarchy->finalise(); + if (mOutOpt[OutputOpt::MilleSteer]) { + std::ofstream tree(mParams->milleTreeFile); + mHierarchy->writeTree(tree); + std::ofstream cons(mParams->milleConFile); + mHierarchy->writeRigidBodyConstraints(cons); + std::ofstream par(mParams->milleParamFile); + mHierarchy->writeParameters(par); + } +} + +bool AlignmentSpec::getTransportJacobian(const TrackD& track, double xTo, double alphaTo, gbl::Matrix5d& jac, gbl::Matrix5d& err) +{ + auto prop = o2::base::PropagatorD::Instance(); + const auto bz = prop->getNominalBz(); + const auto minStep = std::sqrt(std::numeric_limits::epsilon()); + const gbl::Vector5d x0(track.getParams()); + auto trackC = track; + o2::track::TrackParD* refLin{nullptr}; + if (mParams->useStableRef) { + refLin = &trackC; + } + + auto propagate = [&](gbl::Vector5d& p) -> bool { + TrackD tmp(track); + for (int i{0}; i < track::kNParams; ++i) { + tmp.setParam(p[i], i); + } + if (!prop->propagateToAlphaX(tmp, refLin, alphaTo, xTo, false, mParams->maxSnp, mParams->maxStep, 1, mParams->corrType)) { + return false; + } + p = gbl::Vector5d(tmp.getParams()); + return true; + }; + + for (int iPar{0}; iPar < track::kNParams; ++iPar) { + // step size + double h = std::min(mParams->ridderMaxIniStep[iPar], std::max(minStep, std::abs(track.getParam(iPar)) * mParams->ridderRelIniStep[iPar]) * std::pow(mParams->ridderShrinkFac, mParams->ridderMaxExtrap / 2)); + ; + // romberg tableu + Eigen::MatrixXd cur(track::kNParams, mParams->ridderMaxExtrap); + Eigen::MatrixXd pre(track::kNParams, mParams->ridderMaxExtrap); + double normErr = std::numeric_limits::max(); + gbl::Vector5d bestDeriv = gbl::Vector5d::Constant(std::numeric_limits::max()); + for (int iExt{0}; iExt < mParams->ridderMaxExtrap; ++iExt) { + gbl::Vector5d xPlus = x0, xMinus = x0; + xPlus(iPar) += h; + xMinus(iPar) -= h; + if (!propagate(xPlus) || !propagate(xMinus)) { + return false; + } + cur.col(0) = (xPlus - xMinus) / (2.0 * h); + if (!iExt) { + bestDeriv = cur.col(0); + } + // shrink step in next iteration + h /= mParams->ridderShrinkFac; + // richardson extrapolation + double fac = mParams->ridderShrinkFac * mParams->ridderShrinkFac; + for (int k{1}; k <= iExt; ++k) { + cur.col(k) = (fac * cur.col(k - 1) - pre.col(k - 1)) / (fac - 1.0); + fac *= mParams->ridderShrinkFac * mParams->ridderShrinkFac; + double e = std::max((cur.col(k) - cur.col(k - 1)).norm(), (cur.col(k) - pre.col(k - 1)).norm()); + if (e <= normErr) { + normErr = e; + bestDeriv = cur.col(k); + if (normErr < mParams->ridderEps) { + break; + } + } + } + if (normErr < mParams->ridderEps) { + break; + } + // check stability + if (iExt > 0) { + double tableauErr = (cur.col(iExt) - pre.col(iExt - 1)).norm(); + if (tableauErr >= 2.0 * normErr) { + break; + } + } + std::swap(cur, pre); + } + if (bestDeriv.isApproxToConstant(std::numeric_limits::max())) { + return false; + } + jac.col(iPar) = bestDeriv; + err.col(iPar) = gbl::Vector5d::Constant(normErr); + } + + if (jac.isIdentity(1e-8)) { + LOGP(error, "Near jacobian idendity for taking track from {} to {}", track.getX(), xTo); + return false; + } + + return true; +} + +bool AlignmentSpec::prepareITSTrack(int iTrk, const o2::its::TrackITS& itsTrack, align::Track& resTrack) +{ + const auto itsClRefs = mRecoData->getITSTracksClusterRefs(); + auto trFit = convertTrack(itsTrack.getParamOut()); // take outer track fit as start of refit + auto prop = o2::base::PropagatorD::Instance(); + auto geom = o2::its::GeometryTGeo::Instance(); + const auto bz = prop->getNominalBz(); + std::array frameArr{}; + o2::track::TrackParD trkOut, *refLin = nullptr; + if (mParams->useStableRef) { + refLin = &(trkOut = trFit); + } + + auto accountCluster = [&](int i, TrackD& tr, float& chi2, Measurement& meas, o2::track::TrackParD* refLin) { + if (frameArr[i]) { // update with cluster + if (!prop->propagateToAlphaX(tr, refLin, frameArr[i]->alpha, frameArr[i]->x, false, mParams->maxSnp, mParams->maxStep, 1, mParams->corrType)) { + return 2; + } + meas.dy = frameArr[i]->positionTrackingFrame[0] - tr.getY(); + meas.dz = frameArr[i]->positionTrackingFrame[1] - tr.getZ(); + meas.sig2y = frameArr[i]->covarianceTrackingFrame[0]; + meas.sig2z = frameArr[i]->covarianceTrackingFrame[2]; + meas.z = tr.getZ(); + meas.phi = tr.getPhi(); + o2::math_utils::bringTo02Pid(meas.phi); + chi2 += (float)tr.getPredictedChi2Quiet(frameArr[i]->positionTrackingFrame, frameArr[i]->covarianceTrackingFrame); + if (!tr.update(frameArr[i]->positionTrackingFrame, frameArr[i]->covarianceTrackingFrame)) { + return 2; + } + if (refLin) { // displace the reference to the last updated cluster + refLin->setY(frameArr[i]->positionTrackingFrame[0]); + refLin->setZ(frameArr[i]->positionTrackingFrame[1]); + } + return 0; + } + return 1; + }; + + FrameInfoExt pvInfo; + if (mWithPV) { // add PV as constraint + const int iPV = mT2PV[iTrk]; + if (iPV < 0) { + return false; + } + const auto& pv = mPVs[iPV]; + auto tmp = convertTrack(itsTrack.getParamIn()); + if (!prop->propagateToDCA(pv, tmp, bz)) { + return false; + } + pvInfo.alpha = (float)tmp.getAlpha(); + double ca{0}, sa{0}; + o2::math_utils::bringToPMPid(pvInfo.alpha); + o2::math_utils::sincosd(pvInfo.alpha, sa, ca); + pvInfo.x = tmp.getX(); + pvInfo.positionTrackingFrame[0] = -pv.getX() * sa + pv.getY() * ca; + pvInfo.positionTrackingFrame[1] = pv.getZ(); + pvInfo.covarianceTrackingFrame[0] = 0.5 * (pv.getSigmaX2() + pv.getSigmaY2()); + pvInfo.covarianceTrackingFrame[2] = pv.getSigmaY2(); + pvInfo.sens = -1; + pvInfo.lr = -1; + frameArr[0] = &pvInfo; + } + + // collect all track clusters to array, placing them to layer+1 slot + int nCl = itsTrack.getNClusters(); + for (int i = 0; i < nCl; i++) { // clusters are ordered from the outermost to the innermost + const auto& curInfo = mITSTrackingInfo[itsClRefs[itsTrack.getClusterEntry(i)]]; + frameArr[1 + curInfo.lr] = &curInfo; + } + + // start refit + resTrack.points.clear(); + resTrack.info.clear(); + trFit.resetCovariance(); + trFit.setCov(trFit.getQ2Pt() * trFit.getQ2Pt() * trFit.getCov()[14], 14); + float chi2{0}; + for (int i{7}; i >= 0; --i) { + Measurement point; + int res = accountCluster(i, trFit, chi2, point, refLin); + if (res == 2) { + return false; + } else if (res == 0) { + resTrack.points.push_back(point); + resTrack.info.push_back(*frameArr[i]); + resTrack.track = trFit; // put track to whatever the IU is + } + } + // reverse inserted points so they are in the same order as the track + std::reverse(resTrack.info.begin(), resTrack.info.end()); + std::reverse(resTrack.points.begin(), resTrack.points.end()); + resTrack.kfFit.chi2 = chi2; + resTrack.kfFit.ndf = (int)resTrack.info.size() * 2 - 5; + resTrack.kfFit.chi2Ndf = chi2 / (float)resTrack.kfFit.ndf; + + return true; +} + +void AlignmentSpec::prepareMeasurments(std::span clusters, std::span patterns) +{ + LOGP(info, "Preparing {} measurments", clusters.size()); + auto geom = its::GeometryTGeo::Instance(); + geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); + mITSTrackingInfo.clear(); + mITSTrackingInfo.reserve(clusters.size()); + auto pattIt = patterns.begin(); + for (const auto& cls : clusters) { + const auto sens = cls.getSensorID(); + const auto lay = geom->getLayer(sens); + double sigmaY2{0}, sigmaZ2{0}; + math_utils::Point3D locXYZ; + if (mIsITS3) { + locXYZ = o2::its3::ioutils::extractClusterData(cls, pattIt, mIT3Dict, sigmaY2, sigmaZ2); + } else { + locXYZ = o2::its::ioutils::extractClusterData(cls, pattIt, mITSDict, sigmaY2, sigmaZ2); + } + sigmaY2 += mParams->extraClsErrY[lay] * mParams->extraClsErrY[lay]; + sigmaZ2 += mParams->extraClsErrZ[lay] * mParams->extraClsErrZ[lay]; + // Transformation to the local --> global + const auto gloXYZ = geom->getMatrixL2G(sens) * locXYZ; + // Inverse transformation to the local --> tracking + auto trkXYZf = geom->getMatrixT2L(sens) ^ locXYZ; + o2::math_utils::Point3D trkXYZ; + trkXYZ.SetCoordinates(trkXYZf.X(), trkXYZf.Y(), trkXYZf.Z()); + // Tracking alpha angle + // We want that each cluster rotates its tracking frame to the clusters phi + // that way the track linearization around the measurement is less biases to the arc + // this means automatically that the measurement on the arc is at 0 for the curved layers + double alpha = geom->getSensorRefAlpha(sens); + double x = trkXYZ.x(); + if (mIsITS3 && constants::detID::isDetITS3(sens)) { + trkXYZ.SetY(0.f); + // alpha&x always have to be defined wrt to the global Z axis! + x = std::hypot(gloXYZ.x(), gloXYZ.y()); + trkXYZ.SetX(x); + alpha = std::atan2(gloXYZ.y(), gloXYZ.x()); + auto chip = constants::detID::getSensorID(sens); + sigmaY2 += mParams->extraClsErrY[chip] * mParams->extraClsErrY[chip]; + sigmaZ2 += mParams->extraClsErrZ[chip] * mParams->extraClsErrZ[chip]; + } + math_utils::bringToPMPid(alpha); + mITSTrackingInfo.emplace_back(sens, lay, x, alpha, + std::array{trkXYZ.y(), trkXYZ.z()}, + std::array{sigmaY2, 0., sigmaZ2}); + } +} + +void AlignmentSpec::buildT2V() +{ + const auto& itsTracks = mRecoData->getITSTracks(); + mT2PV.clear(); + mT2PV.resize(itsTracks.size(), -1); + if (mUseMC) { + mPVs.reserve(mcReader->getNEvents(0)); + for (int iEve{0}; iEve < mcReader->getNEvents(0); ++iEve) { + const auto& eve = mcReader->getMCEventHeader(0, iEve); + dataformats::VertexBase vtx; + constexpr float err{22e-4f}; + vtx.setX((float)eve.GetX()); + vtx.setY((float)eve.GetY()); + vtx.setZ((float)eve.GetZ()); + vtx.setSigmaX(err); + vtx.setSigmaY(err); + vtx.setSigmaZ(err); + mPVs.push_back(vtx); + } + const auto& mcLbls = mRecoData->getITSTracksMCLabels(); + for (size_t iTrk{0}; iTrk < mcLbls.size(); ++iTrk) { + const auto& lbl = mcLbls[iTrk]; + if (!lbl.isValid() || !lbl.isCorrect()) { + continue; + } + const auto& mcTrk = mcReader->getTrack(lbl); + if (mcTrk->isPrimary()) { + mT2PV[iTrk] = lbl.getEventID(); + } + } + } else { + LOGP(fatal, "Data PV to track TODO"); + } +} + +bool AlignmentSpec::applyMisalignment(Eigen::Vector2d& res, const FrameInfoExt& frame, const TrackD& wTrk, size_t iTrk) +{ + if (!constants::detID::isDetITS3(frame.sens)) { + return true; + } + + const int sensorID = constants::detID::getSensorID(frame.sens); + const int layerID = constants::detID::getDetID2Layer(frame.sens); + + // --- Legendre deformation (non-rigid-body) --- + if (mParams->doMisalignmentLeg && mIsITS3 && mUseMC) { + const auto prop = o2::base::PropagatorD::Instance(); + + const auto lbl = mRecoData->getITSTracksMCLabels()[iTrk]; + const auto mcTrk = mcReader->getTrack(lbl); + if (!mcTrk) { + return false; + } + std::array xyz{mcTrk->GetStartVertexCoordinatesX(), mcTrk->GetStartVertexCoordinatesY(), mcTrk->GetStartVertexCoordinatesZ()}; + std::array pxyz{mcTrk->GetStartVertexMomentumX(), mcTrk->GetStartVertexMomentumY(), mcTrk->GetStartVertexMomentumZ()}; + TParticlePDG* pPDG = TDatabasePDG::Instance()->GetParticle(mcTrk->GetPdgCode()); + if (!pPDG) { + return false; + } + o2::track::TrackParD mcPar(xyz, pxyz, TMath::Nint(pPDG->Charge() / 3), false); + + const double r = frame.x; + const double gloX = r * std::cos(frame.alpha); + const double gloY = r * std::sin(frame.alpha); + const double gloZ = frame.positionTrackingFrame[1]; + auto [u, v] = computeUV(gloX, gloY, gloZ, sensorID, constants::radii[layerID]); + const double h = mDeformations[sensorID](u, v); + + auto mcAtCl = mcPar; + if (!mcAtCl.rotate(frame.alpha) || !prop->PropagateToXBxByBz(mcAtCl, frame.x)) { + return false; + } + + const double snp = mcAtCl.getSnp(); + const double tgl = mcAtCl.getTgl(); + const double csci = 1. / std::sqrt(1. - (snp * snp)); + const double dydx = snp * csci; + const double dzdx = tgl * csci; + const double dy = dydx * h; + const double dz = dzdx * h; + + const double newGloY = (r * std::sin(frame.alpha)) + (dy * std::cos(frame.alpha)); + const double newGloX = (r * std::cos(frame.alpha)) - (dy * std::sin(frame.alpha)); + const double newGloZ = gloZ + dz; + auto [uNew, vNew] = computeUV(newGloX, newGloY, newGloZ, sensorID, constants::radii[layerID]); + if (std::abs(uNew) > 1. || std::abs(vNew) > 1.) { + return false; + } + + res[0] += dy; + res[1] += dz; + } + + // --- Rigid body misalignment --- + // Must use the same derivative chain as GBL: + // dres/da_parent = dres/da_TRK * J_L2T_tile * J_L2P_tile + // The tile is a pseudo-volume; Millepede fits at the halfBarrel (parent) level. + if (mParams->doMisalignmentRB) { + GlobalLabel lbl(0, frame.sens, true); + if (mChip2Hiearchy.find(lbl) == mChip2Hiearchy.end()) { + return true; // sensor not in hierarchy, skip + } + const auto* tileVol = mChip2Hiearchy.at(lbl); + + // derivative in TRK frame (3x6: rows = dy, dz, dsnp) + Matrix36 der = getRigidBodyDerivatives(wTrk); + + // TRK -> tile LOC + const double posTrk[3] = {frame.x, 0., 0.}; + double posLoc[3]; + tileVol->getT2L().LocalToMaster(posTrk, posLoc); + Matrix66 jacL2T; + tileVol->computeJacobianL2T(posLoc, jacL2T); + der *= jacL2T; + + // tile LOC -> halfBarrel LOC (same chain as GBL hierarchy walk) + der *= tileVol->getJL2P(); + + // apply: delta_res = der * delta_a_halfBarrel + Eigen::Vector3d shift = der * mRigidBodyParams[sensorID]; + res[0] += shift[0]; // dy + res[1] += shift[1]; // dz + } + + return true; +} + +void AlignmentSpec::endOfStream(EndOfStreamContext& /*ec*/) +{ + mDBGOut->Close(); + mDBGOut.reset(); +} + +void AlignmentSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } + if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { + LOG(info) << "its cluster dictionary updated"; + mITSDict = (const o2::itsmft::TopologyDictionary*)obj; + return; + } + if (matcher == ConcreteDataMatcher("IT3", "CLUSDICT", 0)) { + LOG(info) << "it3 cluster dictionary updated"; + mIT3Dict = (const o2::its3::TopologyDictionary*)obj; + return; + } +} + +DataProcessorSpec getAlignmentSpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC, bool withPV, bool withITS, OutputEnum out) +{ + auto dataRequest = std::make_shared(); + std::shared_ptr ggRequest{nullptr}; + if (!out[OutputOpt::MilleRes]) { + dataRequest->requestTracks(srcTracks, useMC); + if (!withITS) { + dataRequest->requestIT3Clusters(useMC); + } else { + dataRequest->requestClusters(srcClusters, useMC); + } + if (withPV && !useMC) { + dataRequest->requestPrimaryVertices(useMC); + } + ggRequest = std::make_shared(false, // orbitResetTime + false, // GRPECS=true + true, // GRPLHCIF + true, // GRPMagField + true, // askMatLUT + o2::base::GRPGeomRequest::Aligned, // geometry + dataRequest->inputs, // inputs + true, // askOnce + true); // propagatorD + } else { + dataRequest->inputs.emplace_back("dummy", "GLO", "DUMMY_OUT", 0); + ggRequest = std::make_shared(false, // orbitResetTime + false, // GRPECS=true + false, // GRPLHCIF + false, // GRPMagField + false, // askMatLUT + o2::base::GRPGeomRequest::Aligned, // geometry + dataRequest->inputs); + } + + Options opts{ + {"nthreads", VariantType::Int, 1, {"number of threads"}}, + }; + + return DataProcessorSpec{ + .name = "its3-alignment", + .inputs = dataRequest->inputs, + .outputs = {}, + .algorithm = AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, useMC, withPV, withITS, out)}, + .options = opts}; +} +} // namespace o2::its3::align diff --git a/Detectors/Upgrades/ITS3/alignment/src/AlignmentTypes.cxx b/Detectors/Upgrades/ITS3/alignment/src/AlignmentTypes.cxx new file mode 100644 index 0000000000000..5ad06a6c78381 --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/src/AlignmentTypes.cxx @@ -0,0 +1,24 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include + +#include "ITS3Align/AlignmentTypes.h" +ClassImp(o2::its3::align::Point); +ClassImp(o2::its3::align::FrameInfoExt); +ClassImp(o2::its3::align::FitInfo); +ClassImp(o2::its3::align::Track); + +std::string o2::its3::align::FrameInfoExt::asString() const +{ + return std::format("Sensor={} Layer={} X={} Alpha={}\n\tMEAS: y={} z={}", sens, lr, x, alpha, positionTrackingFrame[0], positionTrackingFrame[1]); +} diff --git a/Detectors/Upgrades/ITS3/alignment/src/Deformations.cxx b/Detectors/Upgrades/ITS3/alignment/src/Deformations.cxx deleted file mode 100644 index 38a959cf7030f..0000000000000 --- a/Detectors/Upgrades/ITS3/alignment/src/Deformations.cxx +++ /dev/null @@ -1,41 +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 "ITS3Align/Deformations.h" -#include "ITS3Align/MisalignmentParameters.h" - -#include "Framework/Logger.h" - -#include - -namespace fs = std::filesystem; - -namespace o2::its3::align -{ - -void Deformations::init(const fs::path& path) -{ - if (!fs::exists(path)) { - LOGP(fatal, "File {} does not exists!", path.c_str()); - } - - auto params = MisalignmentParameters::load(path.string()); - LOGP(info, "Loaded Parameters"); - - // Set the legendre pols - for (int iSensor{0}; iSensor < 6; ++iSensor) { - mLegX[iSensor] = o2::math_utils::Legendre2DPolynominal(params->getLegendreCoeffX(iSensor)); - mLegY[iSensor] = o2::math_utils::Legendre2DPolynominal(params->getLegendreCoeffY(iSensor)); - mLegZ[iSensor] = o2::math_utils::Legendre2DPolynominal(params->getLegendreCoeffZ(iSensor)); - } -} - -} // namespace o2::its3::align diff --git a/Detectors/Upgrades/ITS3/alignment/src/ITS3AlignLinkDef.h b/Detectors/Upgrades/ITS3/alignment/src/ITS3AlignLinkDef.h index ef526284f3a58..e6e6a8c2cc73c 100644 --- a/Detectors/Upgrades/ITS3/alignment/src/ITS3AlignLinkDef.h +++ b/Detectors/Upgrades/ITS3/alignment/src/ITS3AlignLinkDef.h @@ -15,6 +15,12 @@ #pragma link off all classes; #pragma link off all functions; -#pragma link C++ class o2::its3::align::MisalignmentParameters + ; +#pragma link C++ struct o2::its3::align::AlignmentParams + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its3::align::AlignmentParams> + ; + +#pragma link C++ struct o2::its3::align::Measurement + ; +#pragma link C++ struct o2::its3::align::FrameInfoExt + ; +#pragma link C++ struct o2::its3::align::FitInfo + ; +#pragma link C++ struct o2::its3::align::Track + ; #endif diff --git a/Detectors/Upgrades/ITS3/alignment/src/MisalignmentHits.cxx b/Detectors/Upgrades/ITS3/alignment/src/MisalignmentHits.cxx deleted file mode 100644 index 66ab4c8090b54..0000000000000 --- a/Detectors/Upgrades/ITS3/alignment/src/MisalignmentHits.cxx +++ /dev/null @@ -1,368 +0,0 @@ -// Copyright 2020-2022 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 "ITS3Align/MisalignmentHits.h" -#include "ITS3Base/ITS3Params.h" -#include "SimConfig/DigiParams.h" -#include "DetectorsBase/Propagator.h" -#include "Framework/Logger.h" - -#include "Math/Factory.h" -#include "Math/UnaryOperators.h" -#include "TGeoNode.h" -#include "TGeoBBox.h" -#include "TString.h" - -#include -#include -#include -#include - -namespace o2::its3::align -{ - -void MisAlignmentHits::init() -{ - if (o2::its3::ITS3Params::Instance().misalignmentHitsUseProp) { - mMethod = PropMethod::Propagator; - } else { - mMethod = PropMethod::Line; - } - - mGeo = o2::its::GeometryTGeo::Instance(); - - mMinimizer.reset(ROOT::Math::Factory::CreateMinimizer("Minuit2", "Migrad")); - if (mMinimizer == nullptr) { - LOGP(fatal, "Cannot create minimizer"); - } - mMinimizer->SetMaxFunctionCalls(1'000'000'000); - mMinimizer->SetStrategy(0); - mMinimizer->SetPrintLevel(0); - - if (mMethod == PropMethod::Propagator) { - LOGP(info, "Using propagator to find intersection"); - const auto& prefix = o2::conf::DigiParams::Instance().digitizationgeometry_prefix; - mMCReader = std::make_unique(prefix, o2::steer::MCKinematicsReader::Mode::kMCKine); - mMinimizer->SetFunction(mPropagator); - } else { - LOGP(info, "Using local straight-line to find intersection"); - mMinimizer->SetFunction(mLine); - } - - resetStats(); - - if (auto file = o2::its3::ITS3Params::Instance().misalignmentHitsParams; file.empty()) { - LOGP(fatal, "No parameter file specified"); - } else { - mDeformations.init(file); - } -} - -std::optional MisAlignmentHits::processHit(int iEvent, const o2::itsmft::Hit& hit) -{ - ++mStats[Stats::kHitTotal]; - - if (!constants::detID::isDetITS3(hit.GetDetectorID())) { - ++mStats[Stats::kHitIsOB]; - return hit; - } - ++mStats[Stats::kHitIsIB]; - - // Set the working hits - mCurHit = hit; - mCurWorkingHits[WorkingHit::kEntering] = WorkingHit(iEvent, WorkingHit::kEntering, hit); - mCurWorkingHits[WorkingHit::kExiting] = WorkingHit(iEvent, WorkingHit::kExiting, hit); - - // Do work - if (!deformHit(WorkingHit::kEntering) || !deformHit(WorkingHit::kExiting)) { - ++mStats[Stats::kHitDead]; - return std::nullopt; - } - ++mStats[Stats::kHitAlive]; - - // Set the possibly new detectorIDs with mid point approximation - auto midPointOrig = mCurWorkingHits[WorkingHit::kEntering].mPoint + (mCurWorkingHits[WorkingHit::kExiting].mPoint - mCurWorkingHits[WorkingHit::kEntering].mPoint) * 0.5; - auto midPointDef = mCurWorkingHits[WorkingHit::kEntering].mPointDef + (mCurWorkingHits[WorkingHit::kExiting].mPointDef - mCurWorkingHits[WorkingHit::kEntering].mPointDef) * 0.5; - const short idDef = getDetID(midPointDef), idOrig = getDetID(midPointOrig); - if (idDef == -1) { - return std::nullopt; - } - - if (idDef != idOrig) { - ++mStats[Stats::kHitMigrated]; - } else { - ++mStats[Stats::kHitNotMigrated]; - } - - if constexpr (false) { - /// TODO Does not yet work correctly - /// Check if we crossed a boundary within the entering and exiting hit from the midpoint - bool crossesBoundary{false}; - TGeoNode *nEnt{nullptr}, *nExt{nullptr}; - { - auto dirEnt = mCurWorkingHits[WorkingHit::kEntering].mPointDef - midPointDef; - auto stepEnt = std::min(static_cast(dirEnt.R()), std::abs(dirEnt.R() - 5.e-4)); - auto dirEntU = dirEnt.Unit(); - gGeoManager->SetCurrentPoint(midPointDef.X(), midPointDef.Y(), midPointDef.Z()); - gGeoManager->SetCurrentDirection(dirEntU.X(), dirEntU.Y(), dirEntU.Z()); - nEnt = gGeoManager->FindNextBoundaryAndStep(stepEnt, false); - if (gGeoManager->IsOnBoundary()) { - ++mStats[Stats::kHitEntBoundary]; - crossesBoundary = true; - } - } - { - auto dirExt = midPointDef - mCurWorkingHits[WorkingHit::kEntering].mPointDef; - auto stepExt = std::min(static_cast(dirExt.R()), std::abs(dirExt.R() - 5.e-4)); - auto dirExtU = dirExt.Unit(); - gGeoManager->SetCurrentPoint(midPointDef.X(), midPointDef.Y(), midPointDef.Z()); - gGeoManager->SetCurrentDirection(dirExtU.X(), dirExtU.Y(), dirExtU.Z()); - nExt = gGeoManager->FindNextBoundaryAndStep(stepExt, false); - if (gGeoManager->IsOnBoundary()) { - ++mStats[Stats::kHitExtBoundary]; - crossesBoundary = true; - } - } - - if (crossesBoundary && nEnt != nullptr && nExt != nullptr) { - if (nEnt != nExt) { - return std::nullopt; - } else { - ++mStats[Stats::kHitSameBoundary]; // indicates that the step size is too large and we end up in the mother volume; just pretend that his fine for now - } - } - ++mStats[Stats::kHitNoBoundary]; - } - - // Get new postion - mCurHit.SetPosStart(mCurWorkingHits[WorkingHit::kEntering].mPointDef); - mCurHit.SetPos(mCurWorkingHits[WorkingHit::kExiting].mPointDef); - mCurHit.SetDetectorID(idDef); - - ++mStats[Stats::kHitSuccess]; - return mCurHit; -} - -bool MisAlignmentHits::deformHit(WorkingHit::HitType t) -{ - auto& wHit = mCurWorkingHits[t]; - - mMinimizer->Clear(); // clear for next iteration - constexpr double minStep{1e-5}; - constexpr double zMargin{4.0}; - constexpr double phiMargin{0.4}; - if (mMethod == PropMethod::Line) { - prepareLineMethod(t); - mMinimizer->SetVariable(0, "t", 0.0, minStep); // this is left as a free parameter on since t is very small since start and end of hit are close - } else { - if (!preparePropagatorMethod(t)) { - return false; - } - mMinimizer->SetVariable(0, "r", mPropagator.mTrack.getX(), minStep); // this is left as a free parameter on since t is very small since start and end of hit are close - } - mMinimizer->SetLimitedVariable(1, "phiStar", wHit.mPhi, minStep, - std::max(static_cast(wHit.mPhiBorder1), static_cast(wHit.mPhi) - phiMargin), - std::min(static_cast(wHit.mPhiBorder2), static_cast(wHit.mPhi) + phiMargin)); - mMinimizer->SetLimitedVariable(2, "zStar", wHit.mPoint.Z(), minStep, - std::max(static_cast(-constants::segment::lengthSensitive / 2.f), static_cast(wHit.mPoint.Z()) - zMargin), - std::min(static_cast(constants::segment::lengthSensitive / 2.f), static_cast(wHit.mPoint.Z()) + zMargin)); - - mMinimizer->Minimize(); // perform the actual minimization - - auto ss = mMinimizer->Status(); - if (ss == 1) { - ++mStats[Stats::kMinimizerCovPos]; - } else if (ss == 2) { - ++mStats[Stats::kMinimizerHesse]; - } else if (ss == 3) { - ++mStats[Stats::kMinimizerEDM]; - } else if (ss == 4) { - ++mStats[Stats::kMinimizerLimit]; - } else if (ss == 5) { - ++mStats[Stats::kMinimizerOther]; - } else { - ++mStats[Stats::kMinimizerConverged]; - } - - if (ss == 0 || ss == 1) { // for Minuit2 0=ok, 1=ok with pos. forced hesse - ++mStats[Stats::kMinimizerStatusOk]; - if (mMinimizer->MinValue() < 2e-4) { // within 2 um considering the pixel pitch this good enough - ++mStats[Stats::kMinimizerValueOk]; - } else { - ++mStats[Stats::kMinimizerValueBad]; - return false; - } - } else { - ++mStats[Stats::kMinimizerStatusBad]; - return false; - } - - // Valid solution found; calculate new position on ideal geo - wHit.recalculateIdeal(static_cast(mMinimizer->X()[1]), static_cast(mMinimizer->X()[2])); - - return true; -} - -short MisAlignmentHits::getDetID(const o2::math_utils::Point3D& point) -{ - // Do not modify the path, I do not know if this is needed but lets be safe - gGeoManager->PushPath(); - auto id = getDetIDFromCords(point); - gGeoManager->PopPath(); - return id; -} - -short MisAlignmentHits::getDetIDFromCords(const o2::math_utils::Point3D& point) -{ - // retrive if any the node which constains the point - const auto node = gGeoManager->FindNode(point.X(), point.Y(), point.Z()); - if (node == nullptr) { - ++mStats[Stats::kFindNodeFailed]; - return -1; - } - ++mStats[Stats::kFindNodeSuccess]; - - // check if this node is a sensitive volume - const std::string path = gGeoManager->GetPath(); - if (path.find(o2::its::GeometryTGeo::getITS3SensorPattern()) == std::string::npos) { - ++mStats[Stats::kProjNonSensitive]; - return -1; - } - ++mStats[Stats::kProjSensitive]; - - return getDetIDFromPath(path); -} - -short MisAlignmentHits::getDetIDFromPath(const std::string& path) const -{ - static const std::regex pattern{R"(/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITS3Layer(\d+)_(\d+)/ITS3CarbonForm(\d+)_(\d+)/ITS3Chip(\d+)_(\d+)/ITS3Segment(\d+)_(\d+)/ITS3RSU(\d+)_(\d+)/ITS3Tile(\d+)_(\d+)/ITS3PixelArray(\d+)_(\d+))"}; - if (std::smatch matches; std::regex_search(path, matches, pattern)) { - if (matches.size() == 15) { - int iLayer = std::stoi(matches[1]); - int iCarbonForm = std::stoi(matches[4]); - int iSegment = std::stoi(matches[8]); - int iRSU = std::stoi(matches[10]); - int iTile = std::stoi(matches[12]); - return mGeo->getChipIndex(iLayer, iCarbonForm, 0, iSegment, iRSU, iTile); - } else { - LOGP(fatal, "Path did not contain expected number of matches ({})!", matches.size()); - } - } else { - LOGP(fatal, "Path was not matched ({})!", path); - } - __builtin_unreachable(); -} - -void MisAlignmentHits::printStats() const -{ - auto makeFraction = [&](Stats n, Stats d) -> float { return static_cast(mStats[n]) / static_cast(mStats[d] + mStats[n]); }; - LOGP(info, "Processed {} Hits (IB:{}; OB:{}) ({:.2f}%):", mStats[Stats::kHitTotal], mStats[Stats::kHitIsIB], mStats[Stats::kHitIsOB], makeFraction(Stats::kHitIsIB, Stats::kHitIsOB)); - LOGP(info, " - Minimizer Status: {} ok {} bad ({:.2f}%)", mStats[Stats::kMinimizerStatusOk], mStats[Stats::kMinimizerStatusBad], makeFraction(Stats::kMinimizerStatusOk, Stats::kMinimizerStatusBad)); - LOGP(info, " - Minimizer Value: {} ok {} bad ({:.2f}%)", mStats[Stats::kMinimizerValueOk], mStats[Stats::kMinimizerValueBad], makeFraction(Stats::kMinimizerValueOk, Stats::kMinimizerValueBad)); - LOGP(info, " - Minimizer Detailed: {} Converged {} pos. forced Hesse ({:.2f}%)", mStats[Stats::kMinimizerConverged], mStats[Stats::kMinimizerHesse], makeFraction(Stats::kMinimizerConverged, Stats::kMinimizerHesse)); - LOGP(info, " - Minimizer Detailed: {} EDM {} call limit {} other ({:.2f}%)", mStats[Stats::kMinimizerEDM], mStats[Stats::kMinimizerLimit], mStats[Stats::kMinimizerOther], makeFraction(Stats::kMinimizerEDM, Stats::kMinimizerLimit)); - LOGP(info, " - FindNode: {} ok {} failed", mStats[Stats::kFindNodeSuccess], mStats[Stats::kFindNodeFailed]); - LOGP(info, " - IsSensitve: {} yes {} no ({:.2f}%)", mStats[Stats::kProjSensitive], mStats[Stats::kProjNonSensitive], makeFraction(Stats::kProjSensitive, Stats::kProjNonSensitive)); - LOGP(info, " - IsAlive: {} yes {} no ({:.2f}%)", mStats[Stats::kHitAlive], mStats[Stats::kHitDead], makeFraction(Stats::kHitAlive, Stats::kHitDead)); - LOGP(info, " - HasMigrated: {} yes {} no ({:.2f}%)", mStats[Stats::kHitMigrated], mStats[Stats::kHitNotMigrated], makeFraction(Stats::kHitMigrated, Stats::kHitNotMigrated)); - // LOGP(info, " - Crosses Boundary: {} entering {} exiting {} same {} no", mStats[Stats::kHitEntBoundary], mStats[Stats::kHitExtBoundary], mStats[Stats::kHitSameBoundary], mStats[Stats::kHitNoBoundary]); - if (mMethod == PropMethod::Propagator) { - LOGP(info, " - Propagator: {} null track {} null pdg", mStats[Stats::kPropTrackNull], mStats[Stats::kPropPDGNull]); - } - LOGP(info, " --> Good Hits {} ({:.2f}%)", mStats[Stats::kHitSuccess], makeFraction(Stats::kHitSuccess, Stats::kHitIsIB)); -} - -void MisAlignmentHits::prepareLineMethod(WorkingHit::HitType from) -{ - // Set the starint point and radius - // always start from the entering hit that way t is always pos. defined - mLine.mStart = mCurWorkingHits[WorkingHit::kEntering].mPoint; - mLine.mRadius = mCurWorkingHits[from].mRadius; - mLine.mSensorID = mCurWorkingHits[from].mSensorID; - mLine.mPhiTot = mCurWorkingHits[from].mPhiBorder2 - mCurWorkingHits[from].mPhiBorder1; - mLine.mPhi1 = mCurWorkingHits[from].mPhiBorder1; - // Calculate the direction vector - mLine.mD[0] = mCurWorkingHits[WorkingHit::kExiting].mPoint.X() - mCurWorkingHits[WorkingHit::kEntering].mPoint.X(); - mLine.mD[1] = mCurWorkingHits[WorkingHit::kExiting].mPoint.Y() - mCurWorkingHits[WorkingHit::kEntering].mPoint.Y(); - mLine.mD[2] = mCurWorkingHits[WorkingHit::kExiting].mPoint.Z() - mCurWorkingHits[WorkingHit::kEntering].mPoint.Z(); -} - -double MisAlignmentHits::StraightLine::DoEval(const double* x) const -{ - const double t = x[0]; - const double phi = x[1]; - const double z = x[2]; - const double nphi = std::clamp((phi - mPhi1) * 2.0 / mPhiTot - 1.0, -1.0, 1.0); - const double nz = std::clamp((z - (-constants::segment::lengthSensitive / 2.0)) * 2.0 / constants::segment::lengthSensitive - 1.0, -1.0, 1.0); - - /// Find the point along the line given current t - double xline = mStart.X() + t * mD[0], - yline = mStart.Y() + t * mD[1], - zline = mStart.Z() + t * mD[2]; - - // Find the point of the deformed geometry given a certain phi' and z' - double xideal = mRadius * std::cos(phi), yideal = mRadius * std::sin(phi), - zideal = z; - const auto [dx, dy, dz] = mMis->getDeformation(mSensorID, nphi, nz); - double xdef = xideal + dx, ydef = yideal + dy, zdef = zideal + dz; - - // Minimize the euclidean distance of the line point and the deformed point - return std::hypot(xline - xdef, yline - ydef, zline - zdef); -} - -bool MisAlignmentHits::preparePropagatorMethod(WorkingHit::HitType from) -{ - mPropagator.mRadius = mCurWorkingHits[from].mRadius; - mPropagator.mSensorID = mCurWorkingHits[from].mSensorID; - mPropagator.mPhiTot = mCurWorkingHits[from].mPhiBorder2 - mCurWorkingHits[from].mPhiBorder1; - mPropagator.mPhi1 = mCurWorkingHits[from].mPhiBorder1; - const auto mcTrack = mMCReader->getTrack(mCurWorkingHits[from].mEvent, mCurWorkingHits[from].mTrackID); - if (mcTrack == nullptr) { - ++mStats[Stats::kPropTrackNull]; - return false; - } - const std::array xyz{(float)mcTrack->GetStartVertexCoordinatesX(), (float)mcTrack->GetStartVertexCoordinatesY(), (float)mcTrack->GetStartVertexCoordinatesZ()}, - pxyz{(float)mcTrack->GetStartVertexMomentumX(), (float)mcTrack->GetStartVertexMomentumY(), (float)mcTrack->GetStartVertexMomentumZ()}; - const TParticlePDG* pPDG = TDatabasePDG::Instance()->GetParticle(mcTrack->GetPdgCode()); - if (pPDG == nullptr) { - ++mStats[Stats::kPropPDGNull]; - return false; - } - mPropagator.mTrack = o2::track::TrackPar(xyz, pxyz, TMath::Nint(pPDG->Charge() / 3), false); - mPropagator.mBz = o2::base::Propagator::Instance()->getNominalBz(); - return true; -} - -double MisAlignmentHits::Propagator::DoEval(const double* x) const -{ - const double r = x[0]; - const double phi = x[1]; - const double z = x[2]; - const double nphi = (phi - mPhi1) * 2.0 / mPhiTot - 1.0; - const double nz = (z - (-constants::segment::lengthSensitive / 2.0)) * 2.0 / constants::segment::lengthSensitive - 1.0; - - auto trc = mTrack; - if (!trc.propagateTo(r, mBz)) { - return 999; - } - const auto glo = trc.getXYZGlo(); - - // Find the point of the deformed geometry given a certain phi' and z' - double xideal = mRadius * std::cos(phi), yideal = mRadius * std::sin(phi), - zideal = z; - const auto [dx, dy, dz] = mMis->getDeformation(mSensorID, nphi, nz); - double xdef = xideal + dx, ydef = yideal + dy, zdef = zideal + dz; - - // Minimize the euclidean distance of the propagator point and the deformed point - return std::hypot(glo.X() - xdef, glo.Y() - ydef, glo.Z() - zdef); -} - -} // namespace o2::its3::align diff --git a/Detectors/Upgrades/ITS3/alignment/src/MisalignmentManager.cxx b/Detectors/Upgrades/ITS3/alignment/src/MisalignmentManager.cxx deleted file mode 100644 index c9d71541bcd0e..0000000000000 --- a/Detectors/Upgrades/ITS3/alignment/src/MisalignmentManager.cxx +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright 2020-2022 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 "Framework/Logger.h" -#include "ITS3Align/MisalignmentManager.h" -#include "ITS3Align/MisalignmentHits.h" -#include "SimConfig/DigiParams.h" - -#include "TFile.h" -#include "TStopwatch.h" -#include "TGeoManager.h" - -#include -#include -#include -#include -#include -#include - -namespace fs = std::filesystem; - -namespace o2::its3::align -{ - -void MisalignmentManager::createBackup(const fs::path& src, const fs::path& dest) -{ - if (fs::exists(dest)) { - LOGP(info, "Previous orignal file found, using this as src"); - } else { - if (!fs::exists(src)) { - LOGP(fatal, "File {} does not exist", src.c_str()); - } - LOGP(info, "Trying to backup file to {}", dest.c_str()); - try { - fs::rename(src, dest); - } catch (const fs::filesystem_error& err) { - LOGP(fatal, "Cannot create backup file for Hit-File: {}", err.what()); - } - } -} - -void MisalignmentManager::misalignHits() -{ - LOGP(info, "{:*^90}", " ITS3 LOCAL MISALIGNMENT START "); - - TStopwatch timer; - timer.Start(); - - MisAlignmentHits MisAligner; - MisAligner.init(); - - const fs::path oldHitFileSrc{fs::current_path().string() + "/" + o2::conf::DigiParams::Instance().digitizationgeometry_prefix + "_HitsIT3.root"}; - const fs::path oldHitFileDest{fs::current_path().string() + "/" + o2::conf::DigiParams::Instance().digitizationgeometry_prefix + "_HitsIT3_Orig.root"}; - createBackup(oldHitFileSrc, oldHitFileDest); - - std::unique_ptr origFile{TFile::Open(oldHitFileDest.c_str(), "READ")}; - if (origFile == nullptr || origFile->IsZombie()) { - LOGP(fatal, "Original file {} cannot be opened", oldHitFileDest.c_str()); - } - - std::unique_ptr origTree{origFile->Get("o2sim")}; - if (origTree == nullptr) { - LOGP(fatal, "Cannot get hit-tree from orignal file"); - } - std::vector origHits, *origHitsPtr{&origHits}; - origTree->SetBranchAddress("IT3Hit", &origHitsPtr); - - std::unique_ptr newFile{TFile::Open(oldHitFileSrc.c_str(), "RECREATE")}; - if (newFile == nullptr || newFile->IsZombie()) { - LOGP(fatal, "New file {} cannot be opened", oldHitFileSrc.c_str()); - } - - auto newTree = std::make_unique("o2sim", "o2sim"); - std::vector newHits, *newHitsPtr{nullptr}; - newTree->Branch("IT3Hit", &newHitsPtr); - - LOGP(info, "Preparations done; starting hit loop"); - auto nEntries = origTree->GetEntries(); - ULong64_t totalOrigHits{0}, totalNewHits{0}; - for (Long64_t iEntry{0}; origTree->LoadTree(iEntry) >= 0; ++iEntry) { - if (origTree->GetEntry(iEntry) <= 0) { - continue; - } - - const auto progress = (iEntry * 100) / nEntries; - LOG_IF(info, progress % 10 == 0) << "Processing event " << iEntry << " / " << nEntries; - - newHits.clear(); - newHits.reserve(origHits.size()); - for (const auto& origHit : origHits) { - if (auto newHit = MisAligner.processHit(iEntry, origHit)) { - newHits.emplace_back(*newHit); - } - } - - newHitsPtr = &newHits; - newTree->Fill(); - - totalNewHits += newHits.size(); - totalOrigHits += origHits.size(); - } - - newFile->WriteTObject(newTree.get()); - - timer.Stop(); - - MisAligner.printStats(); - - auto totalDiscardedHits = totalOrigHits - totalNewHits; - LOGP(info, "Summary: Total orignal Hits {}", totalOrigHits); - LOGP(info, "Summary: Total misaligned Hits {} ({:.2f}%)", totalNewHits, static_cast(totalNewHits) / static_cast(totalOrigHits) * 100); - LOGP(info, "Summary: Total discarded Hits {} ({:.2f}%)", totalDiscardedHits, static_cast(totalDiscardedHits) / static_cast(totalOrigHits) * 100); - LOGP(info, "Summary: Misalignment took {:.2f}s", timer.CpuTime()); - LOGP(info, "{:*^90}", " ITS3 LOCAL MISALIGNMENT END "); -} - -std::string MisalignmentManager::appendStem(const std::string& filename, const std::string& add) -{ - fs::path filepath{filename}; - auto stem = filepath.stem().string(); - auto extension = filepath.extension().string(); - return stem + add + extension; -} - -std::vector MisalignmentManager::split(const std::string& s, char delimiter) -{ - std::vector tokens; - std::string token; - std::istringstream tokenStream(s); - while (std::getline(tokenStream, token, delimiter)) { - if (!token.empty()) { - tokens.push_back(token); - } - } - return tokens; -} - -void MisalignmentManager::navigate(const std::string& path) -{ - if (!gGeoManager->cd(path.c_str())) { - LOGP(fatal, "Cannot navigate to {}", path); - } -} - -std::string MisalignmentManager::composePathSensor(int sensor) -{ - const int layerID{sensor / 2}; - const int sensorID{sensor % 2}; - return fmt::format("/cave/barrel_1/ITSV_2/ITSUWrapVol0_1/ITS3Layer{}_0/ITS3CarbonForm{}_{}", - layerID, layerID, sensorID); -} - -void MisalignmentManager::applyGlobalMatrixVolume(const std::string& path, const TGeoHMatrix& globalMatrix) -{ - gGeoManager->CdTop(); - TGeoHMatrix* pgMatrix{nullptr}; - TGeoHMatrix gAccMatrix; - std::string curPath{}; - for (const auto& comp : split(path)) { - curPath += "/" + comp; - navigate(curPath); - pgMatrix = gGeoManager->GetCurrentMatrix(); - gAccMatrix.Multiply(pgMatrix); - } - navigate(path); - auto node = gGeoManager->GetCurrentNode(); - if (node == nullptr) { - LOGP(fatal, "Nullptr for node at {}", path); - } - auto motherVol = node->GetMotherVolume(); - if (motherVol == nullptr) { - LOGP(fatal, "Nullptr for motherVol at {}", path); - } - // Compute the inverse of the accumulated global transformation matrix - auto gAccMatrix1 = gAccMatrix.Inverse(); - // Compute the relative transformation matrix for the volume - auto relativeMatrix = globalMatrix; - relativeMatrix.MultiplyLeft(gAccMatrix1); - - auto nodemat = dynamic_cast(node); - nodemat->SetMatrix(new TGeoHMatrix(globalMatrix)); - - // Force the container volume of the object to update itself - motherVol->Voxelize(""); -} - -} // namespace o2::its3::align diff --git a/Detectors/Upgrades/ITS3/alignment/src/MisalignmentParameters.cxx b/Detectors/Upgrades/ITS3/alignment/src/MisalignmentParameters.cxx deleted file mode 100644 index 0842b7252486a..0000000000000 --- a/Detectors/Upgrades/ITS3/alignment/src/MisalignmentParameters.cxx +++ /dev/null @@ -1,80 +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 MisalignmentParameter.cxx -/// \brief Implementation of the MisalignmentParameter class - -#include "ITS3Align/MisalignmentParameters.h" -#include "Framework/Logger.h" - -#include "TFile.h" - -#include - -ClassImp(o2::its3::align::MisalignmentParameters); - -namespace o2::its3::align -{ - -MisalignmentParameters::MisalignmentParameters() -{ - SetName("MisalignmentParameters"); - SetTitle("ITS3 MisalignmentParameters"); -} - -bool MisalignmentParameters::store(const std::string& file) const -{ - std::unique_ptr fOut(TFile::Open(file.c_str(), "RECREATE")); - if (fOut == nullptr || fOut->IsZombie()) { - LOGP(info, "Unable to save misalignment parameters"); - return false; - } - fOut->WriteObjectAny(this, "o2::its3::align::MisalignmentParameters", "ccdb_object"); - return true; -} - -MisalignmentParameters* MisalignmentParameters::load(const std::string& file) -{ - std::unique_ptr fIn(TFile::Open(file.c_str(), "READ")); - auto p = fIn->Get("ccdb_object"); - if (p == nullptr) { - LOGP(fatal, "Unable to load parameters from file!"); - } - return p; -} - -void MisalignmentParameters::printParams(unsigned int detID) const -{ - LOGP(info, "Parameters for ID={}:", detID); - LOGP(info, " - Global Trans: X={} Y={} Z={}", getGloTransX(detID), getGloTransY(detID), getGloTransZ(detID)); - LOGP(info, " - Global Rots: X={} Y={} Z={}", getGloRotX(detID), getGloRotY(detID), getGloRotZ(detID)); - if (constants::detID::isDetITS3(detID)) { - auto sensorID = constants::detID::getSensorID(detID); - LOGP(info, " - Legendre Pol X:"); - getLegendreCoeffX(sensorID).Print(); - LOGP(info, " - Legendre Pol Y:"); - getLegendreCoeffY(sensorID).Print(); - LOGP(info, " - Legendre Pol Z:"); - getLegendreCoeffZ(sensorID).Print(); - } -} - -void MisalignmentParameters::printLegendreParams(unsigned int sensorID) const -{ - LOGP(info, " - Legendre Pol X:"); - getLegendreCoeffX(sensorID).Print(); - LOGP(info, " - Legendre Pol Y:"); - getLegendreCoeffY(sensorID).Print(); - LOGP(info, " - Legendre Pol Z:"); - getLegendreCoeffZ(sensorID).Print(); -} - -} // namespace o2::its3::align diff --git a/Detectors/Upgrades/ITS3/alignment/src/alignment-workflow.cxx b/Detectors/Upgrades/ITS3/alignment/src/alignment-workflow.cxx new file mode 100644 index 0000000000000..6ec7615885556 --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/src/alignment-workflow.cxx @@ -0,0 +1,71 @@ +// 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 "CommonUtils/ConfigurableParam.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/CallbacksPolicy.h" +#include "GlobalTrackingWorkflowHelpers/InputHelper.h" +#include "GlobalTrackingWorkflowHelpers/NoInpDummyOutSpec.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" +#include "ITS3Align/AlignmentSpec.h" + +using namespace o2::framework; +using namespace o2::its3::align; +using GID = o2::dataformats::GlobalTrackID; +using DetID = o2::detectors::DetID; + +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} +void customize(std::vector& workflowOptions) +{ + std::vector options{ + {"disable-mc", o2::framework::VariantType::Bool, false, {"enable MC propagation"}}, + {"track-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of track sources to use"}}, + {"cluster-sources", VariantType::String, "ITS", {"comma-separated list of cluster sources to use"}}, + {"with-its", VariantType::Bool, false, {"ITS alignment mode"}}, + {"without-pv", VariantType::Bool, false, {"Do not use in track refit the PV as an additional constraint"}}, + {"output", VariantType::String, "", {"output steering"}}, + {"disable-root-input", VariantType::Bool, false, {"disable root-files input reader"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::raw::HBFUtilsInitializer::addConfigOption(options); + std::swap(workflowOptions, options); +} +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& cfg) +{ + o2::conf::ConfigurableParam::updateFromString(cfg.options().get("configKeyValues")); + const GID::mask_t allowedSourcesTrc = GID::getSourcesMask("ITS,TPC,ITS-TPC,ITS-TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD-TOF"); + const GID::mask_t allowedSourcesClus = GID::getSourcesMask("ITS"); + const GID::mask_t srcTrc = allowedSourcesTrc & GID::getSourcesMask(cfg.options().get("track-sources")); + const GID::mask_t srcCls = allowedSourcesClus & GID::getSourcesMask(cfg.options().get("cluster-sources")); + const auto useMC = !cfg.options().get("disable-mc"); + const auto withPV = !cfg.options().get("without-pv"); + const auto withITS = cfg.options().get("with-its"); + const OutputEnum output(cfg.options().get("output")); + + WorkflowSpec specs; + if (!output[OutputOpt::MilleRes]) { + o2::globaltracking::InputHelper::addInputSpecs(cfg, specs, srcCls, srcTrc, srcTrc, useMC); + if (withPV && !useMC) { + o2::globaltracking::InputHelper::addInputSpecsPVertex(cfg, specs, useMC); + } + } else { + specs.emplace_back(o2::globaltracking::getNoInpDummyOutSpec(0)); + } + + specs.emplace_back(o2::its3::align::getAlignmentSpec(srcTrc, srcCls, useMC, withPV, withITS, output)); + + o2::raw::HBFUtilsInitializer hbfIni(cfg, specs); + return std::move(specs); +} diff --git a/Detectors/Upgrades/ITS3/base/include/ITS3Base/ITS3Params.h b/Detectors/Upgrades/ITS3/base/include/ITS3Base/ITS3Params.h index 0bd548cef953d..1e3f4f47b8a29 100644 --- a/Detectors/Upgrades/ITS3/base/include/ITS3Base/ITS3Params.h +++ b/Detectors/Upgrades/ITS3/base/include/ITS3Base/ITS3Params.h @@ -19,11 +19,6 @@ namespace o2::its3 { struct ITS3Params : public o2::conf::ConfigurableParamHelper { - // Alignment studies - bool applyMisalignmentHits{false}; // Apply detector misalignment on hit level - std::string misalignmentHitsParams{}; // Path to parameter file for mis-alignment - bool misalignmentHitsUseProp{false}; // Use propagtor for mis-alignment - std::string globalGeoMisAlignerMacro{"${O2_ROOT}/share/macro/MisAlignGeoITS3.C"}; // Path to macro for global geometry mis-alignment // Chip studies bool useDeadChannelMap{false}; // Query for a dead channel map to study disabling individual tiles std::string chipResponseFunction{"APTS"}; // Chip response function one of "Alpide", "APTS" or "Mosaix" (not yet available) diff --git a/Detectors/Upgrades/ITS3/base/include/ITS3Base/SegmentationMosaix.h b/Detectors/Upgrades/ITS3/base/include/ITS3Base/SegmentationMosaix.h index fbf9a59e6da4b..088dd858fff73 100644 --- a/Detectors/Upgrades/ITS3/base/include/ITS3Base/SegmentationMosaix.h +++ b/Detectors/Upgrades/ITS3/base/include/ITS3Base/SegmentationMosaix.h @@ -151,7 +151,8 @@ class SegmentationMosaix } // Same as localToDetector w.o. checks. - constexpr void localToDetectorUnchecked(float const xRow, float const zCol, int& iRow, int& iCol) const noexcept + template + constexpr void localToDetectorUnchecked(T const xRow, T const zCol, int& iRow, int& iCol) const noexcept { iRow = static_cast(std::floor((WidthH - xRow) / PitchRow)); iCol = static_cast(std::floor((zCol + LengthH) / PitchCol)); @@ -167,7 +168,8 @@ class SegmentationMosaix /// center of the sensitive volume. /// If iRow and or iCol is outside of the segmentation range a value of -0.5*Dx() /// or -0.5*Dz() is returned. - bool detectorToLocal(float const row, float const col, float& xRow, float& zCol) const noexcept + template + bool detectorToLocal(T const row, T const col, L& xRow, L& zCol) const noexcept { if (!isValidDet(row, col)) { return false; @@ -178,15 +180,17 @@ class SegmentationMosaix // Same as detectorToLocal w.o. checks. // We position ourself in the middle of the pixel. - void detectorToLocalUnchecked(float const row, float const col, float& xRow, float& zCol) const noexcept + template + void detectorToLocalUnchecked(T const row, T const col, L& xRow, L& zCol) const noexcept { xRow = -(row + 0.5f) * PitchRow + WidthH; zCol = (col + 0.5f) * PitchCol - LengthH; } - bool detectorToLocal(float const row, float const col, math_utils::Point3D& loc) const noexcept + template + bool detectorToLocal(T const row, T const col, math_utils::Point3D& loc) const noexcept { - float xRow{0.}, zCol{0.}; + L xRow{0.}, zCol{0.}; if (!detectorToLocal(row, col, xRow, zCol)) { return false; } @@ -194,9 +198,10 @@ class SegmentationMosaix return true; } - void detectorToLocalUnchecked(float const row, float const col, math_utils::Point3D& loc) const noexcept + template + void detectorToLocalUnchecked(T const row, T const col, math_utils::Point3D& loc) const noexcept { - float xRow{0.}, zCol{0.}; + L xRow{0.}, zCol{0.}; detectorToLocalUnchecked(row, col, xRow, zCol); loc.SetCoordinates(xRow, 0.0f, zCol); } diff --git a/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h b/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h index 937fa8d2e982c..270b1a7148f61 100644 --- a/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h +++ b/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h @@ -209,7 +209,7 @@ inline T getSensorID(T detID) template inline bool isDetITS3(T detID) { - return detID < static_cast(nChips); + return detID < static_cast(nChips) && detID >= 0; } } // namespace detID diff --git a/Detectors/Upgrades/ITS3/macros/align/CMakeLists.txt b/Detectors/Upgrades/ITS3/macros/align/CMakeLists.txt index 86ebd989133e0..1d5c18cc0f4f3 100644 --- a/Detectors/Upgrades/ITS3/macros/align/CMakeLists.txt +++ b/Detectors/Upgrades/ITS3/macros/align/CMakeLists.txt @@ -9,9 +9,5 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. -# install(FILES MisAlignGeoITS3.C DESTINATION share/macro/) -# its3_add_macro(MisAlignGeoITS3.C) -its3_add_macro(TestLegendrePol.C) -its3_add_macro(CreateMisalignmentITS3.C) -its3_add_macro(ShowCoefficients.C) its3_add_macro(CheckResidualsITS3.C) +its3_add_macro(CheckHitResiduals.C) diff --git a/Detectors/Upgrades/ITS3/macros/align/CheckHitResiduals.C b/Detectors/Upgrades/ITS3/macros/align/CheckHitResiduals.C new file mode 100644 index 0000000000000..555ba177a60ce --- /dev/null +++ b/Detectors/Upgrades/ITS3/macros/align/CheckHitResiduals.C @@ -0,0 +1,131 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include +#include + +#include "MathUtils/Utils.h" +#include "ITSMFTSimulation/Hit.h" +#include "ITS3Base/SpecsV2.h" +#endif + +void CheckHitResiduals(const std::string& hitOFile = "o2sim_HitsIT3_Orig.root", + const std::string& hitRFile = "o2sim_HitsIT3.root") +{ + gStyle->SetOptStat(0); + + TFile fileO(hitOFile.data()); + auto* hitOTree = dynamic_cast(fileO.Get("o2sim")); + std::vector* hitOArray = nullptr; + hitOTree->SetBranchAddress("IT3Hit", &hitOArray); + + TFile fileR(hitRFile.data()); + auto* hitRTree = dynamic_cast(fileR.Get("o2sim")); + std::vector* hitRArray = nullptr; + hitRTree->SetBranchAddress("IT3Hit", &hitRArray); + + struct Hits { + o2::itsmft::Hit oHit, rHit; + int oEve{-1}, rEve{-1}; + bool hasBoth() const noexcept { return oEve >= 0 && rEve >= 0 && oEve == rEve && oHit.GetDetectorID() == rHit.GetDetectorID() && rHit.GetTrackID() == oHit.GetTrackID(); } + }; + std::unordered_map hits; + + size_t total{0}; + for (int iEntry{0}; iEntry < hitOTree->GetEntries(); ++iEntry) { + hitOTree->GetEntry(iEntry); + total += hitOArray->size(); + for (const auto& h : *hitOArray) { + if (!o2::its3::constants::detID::isDetITS3(h.GetDetectorID())) { + continue; + } + uint64_t key = (uint64_t(iEntry) << 48) | (uint64_t(h.GetTrackID()) << 24) | uint64_t(h.GetDetectorID()); + auto& hh = hits[key]; + hh.oHit = h; + hh.oEve = iEntry; + } + } + printf("placed %zu hits saw %zu\n", hits.size(), total); + for (int iEntry{0}; iEntry < hitRTree->GetEntries(); ++iEntry) { + hitRTree->GetEntry(iEntry); + for (const auto& h : *hitRArray) { + if (!o2::its3::constants::detID::isDetITS3(h.GetDetectorID())) { + continue; + } + uint64_t key = (uint64_t(iEntry) << 48) | (uint64_t(h.GetTrackID()) << 24) | uint64_t(h.GetDetectorID()); + auto& hh = hits[key]; + hh.rHit = h; + hh.rEve = iEntry; + } + } + + // plot the residuals in dRPhi, dZ against ideal phi,z for each layer + std::array mDRPhi{}, mDZ{}; + for (int i{0}; i < 3; ++i) { + mDRPhi[i] = new TProfile2D(Form("hDRPhi_%d", i), Form("#Delta_{r#varphi};z (cm); r#varphi (cm)"), 100, -15, 15, 100, 0, 2 * TMath::Pi()); + mDRPhi[i]->SetDirectory(nullptr); + mDZ[i] = new TProfile2D(Form("hDZ_%d", i), Form("#Delta_{z};z (cm); r#varphi (cm)"), 100, -15, 15, 100, 0, 2 * TMath::Pi()); + mDZ[i]->SetDirectory(nullptr); + } + std::array cIB{}; + for (const auto& [_, h] : hits) { + if (h.hasBoth()) { + int chip = o2::its3::constants::detID::getSensorID(h.oHit.GetDetectorID()) / 2; + ++cIB[chip]; + auto gloO = h.oHit.GetPosStart(); + auto gloR = h.rHit.GetPosStart(); + // ideal (original) cluster: phi, z, rphi + float phiO = std::atan2(gloO.Y(), gloO.X()); + o2::math_utils::bringTo02Pi(phiO); + const float rO = std::hypot(gloO.X(), gloO.Y()); + + // deformed (reconstructed) cluster + float phiR = std::atan2(gloR.Y(), gloR.X()); + o2::math_utils::bringTo02Pi(phiR); + const float rR = std::hypot(gloR.X(), gloR.Y()); + + // residuals + const float dRPhi = rO * (phiR - phiO); // or use average r, doesn't matter at this precision + const float dZ = gloR.Z() - gloO.Z(); + + // fill vs (z, phi) of ideal position + mDRPhi[chip]->Fill(gloO.Z(), phiO, dRPhi * 1e4); + mDZ[chip]->Fill(gloO.Z(), phiO, dZ * 1e4); + } + } + for (int lay{0}; lay < 3; ++lay) { + printf("\t%d has %d\n", lay, cIB[lay]); + } + auto c1 = new TCanvas; + c1->Divide(3, 1); + for (int i{0}; i < 3; ++i) { + c1->cd(1 + i); + gPad->SetRightMargin(3); + mDRPhi[i]->GetZaxis()->SetRangeUser(-200, 200); + mDRPhi[i]->Draw("colz"); + } + c1->Draw(); + c1->SaveAs("its3_clus_res_rphi.pdf"); + auto c2 = new TCanvas; + c2->Divide(3, 1); + for (int i{0}; i < 3; ++i) { + c2->cd(1 + i); + gPad->SetRightMargin(3); + mDZ[i]->GetZaxis()->SetRangeUser(-200, 200); + mDZ[i]->Draw("colz"); + } + c2->Draw(); + c2->SaveAs("its3_clus_res_z.pdf"); +} diff --git a/Detectors/Upgrades/ITS3/macros/align/CreateMisalignmentITS3.C b/Detectors/Upgrades/ITS3/macros/align/CreateMisalignmentITS3.C deleted file mode 100644 index 8df00ee25de00..0000000000000 --- a/Detectors/Upgrades/ITS3/macros/align/CreateMisalignmentITS3.C +++ /dev/null @@ -1,94 +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. - -#if !defined(__CLING__) || defined(__ROOTCLING__) -#include "TRandom.h" -#include "TMatrixD.h" - -#include "ITS3Align/MisalignmentParameters.h" -#endif - -void CreateMisalignmentITS3(bool dummy = false, bool manual = false) -{ - gRandom->SetSeed(42); - - // Legendre coeff. - constexpr int nOrder{2}; - auto getRandom = []() { - constexpr double scale{50.e-4}; - return scale * gRandom->Uniform(-1.0, 1.0); - }; - - auto getSign = []() { return gRandom->Uniform() ? -1.0 : 1.0; }; - - o2::its3::align::MisalignmentParameters params; - - if (dummy) { - TMatrixD coeffNull(0 + 1, 0 + 1); - for (int sensorID{0}; sensorID < 6; ++sensorID) { - params.setLegendreCoeffX(sensorID, coeffNull); - params.setLegendreCoeffY(sensorID, coeffNull); - params.setLegendreCoeffZ(sensorID, coeffNull); - } - } else if (manual) { - // (0,0) -> shift - // (1,0) -> - for (int sensorID{0}; sensorID < 6; ++sensorID) { - constexpr double scale{20e-4}; - TMatrixD coeffNull(1, 1); - - TMatrixD coeffMinusX(1 + 1, 1 + 1); - TMatrixD coeffPlusX(1 + 1, 1 + 1); - coeffMinusX(1, 1) = -scale; - coeffPlusX(1, 1) = scale; - - TMatrixD coeffMinusY(4 + 1, 4 + 1); - TMatrixD coeffPlusY(4 + 1, 4 + 1); - coeffMinusY(0, 0) = scale; - coeffPlusY(0, 0) = -scale; - coeffMinusY(4, 4) = -scale; - coeffPlusY(4, 4) = scale; - if (sensorID % 2 == 0) { - params.setLegendreCoeffX(sensorID, coeffPlusX); - params.setLegendreCoeffY(sensorID, coeffPlusY); - params.setLegendreCoeffZ(sensorID, coeffNull); - } else { - params.setLegendreCoeffX(sensorID, coeffMinusX); - params.setLegendreCoeffY(sensorID, coeffMinusY); - params.setLegendreCoeffZ(sensorID, coeffNull); - } - } - } else { - for (int sensorID{0}; sensorID < 6; ++sensorID) { - TMatrixD coeffX(nOrder + 1, nOrder + 1); - TMatrixD coeffY(nOrder + 1, nOrder + 1); - TMatrixD coeffZ(nOrder + 1, nOrder + 1); - for (int i{0}; i <= nOrder; ++i) { - for (int j{0}; j <= i; ++j) { - // some random scaling as higher order parameters have higher influence - coeffX(i, j) = getRandom() / (1.0 + i * j * 2.0); - coeffZ(i, j) = getRandom() / (1.0 + i * j * 2.0); - coeffY(i, j) = getRandom() / (1.0 + i * j * 2.0); - } - } - - params.setLegendreCoeffX(sensorID, coeffX); - params.setLegendreCoeffY(sensorID, coeffY); - params.setLegendreCoeffZ(sensorID, coeffZ); - } - } - - for (int sensorID{0}; sensorID < 6; ++sensorID) { - params.printLegendreParams(sensorID); - } - - params.store("misparams.root"); -} diff --git a/Detectors/Upgrades/ITS3/macros/align/MisAlignGeoITS3.notest b/Detectors/Upgrades/ITS3/macros/align/MisAlignGeoITS3.notest deleted file mode 100644 index 2a4bef978d0da..0000000000000 --- a/Detectors/Upgrades/ITS3/macros/align/MisAlignGeoITS3.notest +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2020-2022 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 MisAlignGeoITS3.C -/// \brief Misalign the global geometry of ITS3 -/// \author felix.schlepper@cern.ch - -#include "ITS3Align/MisalignmentManager.h" - -#include "TGeoManager.h" -#include "TGeoMatrix.h" -#include "TMath.h" - -#include "CommonUtils/ConfigurableParam.h" -#include "CommonUtils/ConfigurableParamHelper.h" -#include "Framework/Logger.h" -#include "ITS3Base/SpecsV2.h" - -#include - -#include "boost/property_tree/ptree.hpp" - -using MM = o2::its3::align::MisalignmentManager; -namespace fs = std::filesystem; -namespace pt = boost::property_tree; -namespace its3c = o2::its3::constants; - -#define DECLARE_SENSOR(id) \ - float Sensor##id##Dx = 0.f; \ - float Sensor##id##Dy = 0.f; \ - float Sensor##id##Dz = 0.f; \ - float Sensor##id##Phi = 0.f; \ - float Sensor##id##Theta = 0.f; \ - float Sensor##id##Psi = 0.f; - -struct MisAlignGlobalParams : public o2::conf::ConfigurableParamHelper { - DECLARE_SENSOR(0) - DECLARE_SENSOR(1) - DECLARE_SENSOR(2) - DECLARE_SENSOR(3) - DECLARE_SENSOR(4) - DECLARE_SENSOR(5) - - O2ParamDef(MisAlignGlobalParams, "MisAlignGlobalParams"); -}; -O2ParamImpl(MisAlignGlobalParams); - -void MisAlignGeoITS3(const std::string& configFilePath = "", bool _export = false, bool draw = false, bool check = false, const std::string& geomFile = "o2sim_geometry-aligned.root") -{ - LOGP(info, "{:*^90}", " ITS3 GLOBAL MISALIGNMENT START "); - auto& params = MisAlignGlobalParams::Instance(); - params.writeINI("default_parameters_global.ini", "MisAlignGlobalParams"); - if (configFilePath.empty()) { - LOGP(info, "No user config provided using defaults"); - } else { - LOGP(info, "User config at {}", configFilePath); - params.updateFromFile(configFilePath); - } - params.writeINI("used_parameters_global.ini", "MisAlignGlobalParams"); - params.printKeyValues(true, true); - - const fs::path srcFile{geomFile}; - const fs::path destFile{MM::appendStem(geomFile, "_Orig")}; - if (gGeoManager == nullptr) { - MM::createBackup(srcFile, destFile); - TGeoManager::Import(destFile.c_str()); - } - - LOGP(info, "Building matrices"); - std::array gRotoTranslations{}; - for (int iSensor{0}; iSensor < (int)its3c::nSensorsIB; ++iSensor) { - auto& mat = gRotoTranslations[iSensor]; - // Phi Z rotation angle (first) defined in [-PI,PI] - // Theta X rotation angle (second) defined only [0,PI] - // Psi Z rotation angle (third) defined in [-PI,PI] - MM::Euler3D euler{ - ((iSensor % 2 == 0) ? 0. : -TMath::Pi()) + - TMath::DegToRad() * params.getValueAs(fmt::format("MisAlignGlobalParams.Sensor{}Phi", iSensor)), - TMath::DegToRad() * params.getValueAs(fmt::format("MisAlignGlobalParams.Sensor{}Theta", iSensor)), - TMath::DegToRad() * params.getValueAs(fmt::format("MisAlignGlobalParams.Sensor{}Psi", iSensor)), - }; - MM::Rot3D rot(euler); - std::array rota; - rot.GetComponents(std::begin(rota)); - mat.SetRotation(rota.data()); - std::array trans{ - params.getValueAs(fmt::format("MisAlignGlobalParams.Sensor{}Dx", iSensor)), - params.getValueAs(fmt::format("MisAlignGlobalParams.Sensor{}Dy", iSensor)), - params.getValueAs(fmt::format("MisAlignGlobalParams.Sensor{}Dz", iSensor)), - }; - mat.SetTranslation(trans.data()); - } - - LOGP(info, "Appying Global RotoTranslations"); - for (int iSensor{0}; iSensor < (int)its3c::nSensorsIB; ++iSensor) { - auto path = MM::composePathSensor(iSensor); - auto& mat = gRotoTranslations[iSensor]; - MM::applyGlobalMatrixVolume(path, mat); - } - - if (_export) { - gGeoManager->Export(srcFile.c_str()); - } - if (draw) { - MM::navigate("cave/barrel_1/ITSV_2/ITSUWrapVol0_1"); - gGeoManager->GetCurrentVolume()->Draw(); - gGeoManager->SetTopVisible(); - gGeoManager->RestoreMasterVolume(); - } - if (check) { - gGeoManager->CdTop(); - gGeoManager->CloseGeometry(); - gGeoManager->CheckGeometryFull(); - gGeoManager->CheckOverlaps(0.1, "s"); - gGeoManager->PrintOverlaps(); - auto overlaps = gGeoManager->GetListOfOverlaps(); - overlaps->At(0)->Print(); - overlaps->At(0)->Draw("ogl"); - } - LOGP(info, "{:*^90}", " ITS3 GLOBAL MISALIGNMENT END "); -} diff --git a/Detectors/Upgrades/ITS3/macros/align/ShowCoefficients.C b/Detectors/Upgrades/ITS3/macros/align/ShowCoefficients.C deleted file mode 100644 index 42749b707e81d..0000000000000 --- a/Detectors/Upgrades/ITS3/macros/align/ShowCoefficients.C +++ /dev/null @@ -1,333 +0,0 @@ -// Copyright 2020-2022 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#if !defined(__CLING__) || defined(__ROOTCLING__) -#include "Math/Factory.h" -#include "Math/Minimizer.h" -#include "TAxis.h" -#include "TCanvas.h" -#include "TGraph.h" -#include "TGraph2D.h" -#include "TLegend.h" -#include "TMarker.h" -#include "TMultiGraph.h" -#include "TRandom.h" -#include "TMath.h" - -#include "MathUtils/LegendrePols.h" -#include "ITS3Align/Deformations.h" -#include "MathUtils/Utils.h" -#endif - -static ROOT::Math::Minimizer* gMin; - -void ShowCoefficients(const std::string& fileName = "misparams.root", bool findMin = false) -{ - constexpr double factor{10}; - o2::its3::align::Deformations def; - def.init(fileName); - - if (findMin) { - gMin = ROOT::Math::Factory::CreateMinimizer("Minuit2", "Migrad"); - if (gMin == nullptr) { - Error("", "Cannot create minimizer !"); - return; - } - gMin->SetMaxFunctionCalls(1000000); // for Minuit/Minuit2 - gMin->SetTolerance(0.00001); - gMin->SetPrintLevel(1); - } - - if constexpr (1) { - constexpr int nPoints{1000}; - const std::array zFix{-12., -8.67, -4.33, 0., 4.33, 8.67, 12.}; - const std::array phiFix{1. / 4. * TMath::Pi(), 0.5 * TMath::Pi(), 3. / 4. * TMath::Pi(), TMath::Pi(), 5. / 4. * TMath::Pi(), 6. / 4. * TMath::Pi(), 7. / 4. * TMath::Pi()}; - const std::array phiFixName{"#frac{1}{4}", "#frac{1}{2}", "#frac{3}{4}", "1", "#frac{5}{4}", "#frac{6}{4}", "#frac{7}{4}"}; - const std::array phiFixTop{true, true, true, false, false, false, false}; - const std::array, 2> sensorN{{{0, 2, 4}, {1, 3, 5}}}; - constexpr double z1{-o2::its3::constants::segment::lengthSensitive / 2.0}, z2{o2::its3::constants::segment::lengthSensitive / 2.0}, zTot{z2 - z1}, zStep{zTot / (nPoints - 1)}; - auto canv = new TCanvas(); - canv->Divide(7, 2); - for (int i{0}; i < 7; ++i) { - std::array xi, yi, xd, yd; - for (int s{0}; s < 6; ++s) { - const bool isTop = s % 2 == 0; - const double radius = o2::its3::constants::radii[s / 2]; - const double nzFix = (zFix[i] - z1) * 2.0 / zTot - 1.0; - const double phi1 = o2::math_utils::to02Pi(((isTop) ? 0.f : 1.f) * TMath::Pi() + std::asin(o2::its3::constants::equatorialGap / 2.f / radius)); - const double phi2 = o2::math_utils::to02Pi(((isTop) ? 1.f : 2.f) * TMath::Pi() - std::asin(o2::its3::constants::equatorialGap / 2.f / radius)); - const double phiTot{phi2 - phi1}, phiStep{phiTot / (nPoints - 1)}; - const double nphiFix = ((phiFix[i]) - phi1) * 2.0 / phiTot - 1.0; - - for (int j{0}; j < nPoints; ++j) { - const int idx = s * nPoints + j; - const double phi = phi1 + j * phiStep; - const double nphi = (phi - phi1) * 2.0 / phiTot - 1.0; - - xi[idx] = radius * std::cos(phi); - yi[idx] = radius * std::sin(phi); - const auto [dxXY, dyXY, _] = def.getDeformation(s, nphi, nzFix); - xd[idx] = xi[idx] + dxXY * factor; - yd[idx] = yi[idx] + dyXY * factor; - } - } - canv->cd(i + 1); - auto gixy = new TGraph(nPoints * 6, xi.data(), yi.data()); - gixy->SetNameTitle(Form("g_i_xy_%d", i), Form("Ideal xy at z=%.2f; x (cm); y (cm)", zFix[i])); - gixy->SetMarkerColor(kBlue); - gixy->Draw("AP"); - auto gdxy = new TGraph(nPoints * 6, xd.data(), yd.data()); - gdxy->SetNameTitle(Form("g_d_xy_%d", i), Form("Deformed (x%.0f) xy at z=%.2f; x (cm); y (cm)", factor, zFix[i])); - gdxy->SetMarkerColor(kRed); - gdxy->Draw("P;same"); - - if (i == 3) { - continue; - } - - std::array zi, ri, zd, rd; - const bool isTop = phiFixTop[i]; - for (const int s : ((isTop) ? sensorN[0] : sensorN[1])) { - const double radius = o2::its3::constants::radii[s / 2]; - const double phi1 = o2::math_utils::to02Pi(((isTop) ? 0.f : 1.f) * TMath::Pi() + std::asin(o2::its3::constants::equatorialGap / 2.f / radius)); - const double phi2 = o2::math_utils::to02Pi(((isTop) ? 1.f : 2.f) * TMath::Pi() - std::asin(o2::its3::constants::equatorialGap / 2.f / radius)); - const double phiTot{phi2 - phi1}, phiStep{phiTot / (nPoints - 1)}; - const double nphiFix = ((phiFix[i]) - phi1) * 2.0 / phiTot - 1.0; - for (int j{0}; j < nPoints; ++j) { - const int idx = (s / 2) * nPoints + j; - const double z = z1 + j * zStep; - const double nz = (z - z1) * 2.0 / zTot - 1.0; - const double xi = radius * std::cos(phiFix[i]), yi = radius * std::sin(phiFix[i]); - ri[idx] = radius; - zi[idx] = z; - const auto [dxZR, dyZR, dzZR] = def.getDeformation(s, nphiFix, nz); - zd[idx] = z + dzZR * factor; - const double xxd = xi + dxZR * factor, yyd = yi + dyZR * factor; - rd[idx] = std::sqrt(xxd * xxd + yyd * yyd); - } - } - canv->cd(i + 8); - auto gizr = new TGraph(nPoints * 3, zi.data(), ri.data()); - gizr->SetNameTitle(Form("g_i_zr_%d", i), Form("Ideal zr at #varphi=%s #Pi; z (cm); r (cm)", phiFixName[i])); - gizr->SetMarkerColor(kBlue); - gizr->Draw("AP"); - auto gdzr = new TGraph(nPoints * 3, zd.data(), rd.data()); - gdzr->SetNameTitle(Form("g_d_zr_%d", i), Form("Deformed (x%0.f) zr at #varphi=%s #Pi; z (cm); r (cm)", factor, phiFixName[i])); - gdzr->SetMarkerColor(kRed); - gdzr->Draw("P;same"); - } - canv->Draw(); - canv->SaveAs("its3_deformation.pdf"); - } - - if constexpr (1) { - const std::array axisName{"x", "y", "z"}; - constexpr int nPoints{100}; - constexpr int nPoints2{nPoints * nPoints}; - constexpr double minX{-1.0}, maxX{1.0}, - stepX{(maxX - minX) / static_cast(nPoints)}; - - for (unsigned int iSensor{0}; iSensor < 6; ++iSensor) { - const auto nOrders = def.getOrders(iSensor); - for (unsigned int iAxis{0}; iAxis < 3; ++iAxis) { - std::array x, y, z; - auto canv = new TCanvas(Form("c_sensor%d_d%s", iSensor, axisName[iAxis]), Form("Legendre Coefficients Sensor %d - Axis %s", iSensor, axisName[iAxis])); - canv->Divide(nOrders[iAxis] + 1, nOrders[iAxis] + 1); - - { // Draw total polynominal - for (int iPoint{0}; iPoint < nPoints; ++iPoint) { - double xcur = minX + iPoint * stepX; - for (int jPoint{0}; jPoint < nPoints; ++jPoint) { - double ycur = minX + jPoint * stepX; - int i = iPoint * nPoints + jPoint; - x[i] = xcur; - y[i] = ycur; - z[i] = def.getDeformation(iSensor, iAxis, xcur, ycur); - } - } - canv->cd(nOrders[iAxis] + 1); - auto g = new TGraph2D(nPoints2, x.data(), y.data(), z.data()); - g->SetTitle(Form("Legendre Polynominal %d-deg Sensor %d #Delta%s;u;v;w", nOrders[iAxis], iSensor, axisName[iAxis])); - g->SetName(Form("g_%d_%s", iSensor, axisName[iAxis])); - g->Draw("surf1"); - g->GetXaxis()->SetRangeUser(minX, maxX); - g->GetYaxis()->SetRangeUser(minX, maxX); - gPad->Update(); - } - - { // Draw decomposition of contributions to polynominal - const auto& leg = def.getLegendre(iSensor, iAxis); - const auto coeff = leg.getCoefficients(); - for (unsigned int iOrder{0}; iOrder <= nOrders[iAxis]; ++iOrder) { - for (unsigned int jOrder{0}; jOrder <= iOrder; ++jOrder) { - for (int iPoint{0}; iPoint < nPoints; ++iPoint) { - double xcur = minX + iPoint * stepX; - for (int jPoint{0}; jPoint < nPoints; ++jPoint) { - double ycur = minX + jPoint * stepX; - int i = iPoint * nPoints + jPoint; - x[i] = xcur; - y[i] = ycur; - z[i] = leg(iOrder, jOrder, xcur, ycur); - } - } - canv->cd(1 + iOrder * (nOrders[iAxis] + 1) + jOrder); - auto g = new TGraph2D(nPoints2, x.data(), y.data(), z.data()); - g->SetTitle(Form("c_{%d,%d}=%.4f;u;v;w", iOrder, jOrder, coeff(iOrder, jOrder))); - g->SetName(Form("g_%d_%d_%d_%d", iSensor, iAxis, iOrder, jOrder)); - if (iOrder == 0 && jOrder == 0) { // Fix display of constant value - g->Draw("P0"); - } else { - g->Draw("surf1"); - } - g->GetXaxis()->SetRangeUser(minX, maxX); - g->GetYaxis()->SetRangeUser(minX, maxX); - gPad->Update(); - } - } - } - - canv->Draw(); - canv->SaveAs(Form("its3_sensor%d_%s.pdf", iSensor, axisName[iAxis])); - } - } - } - - if constexpr (1) { - constexpr int nPoints{50}; - constexpr int nPoints2{nPoints * nPoints}; - constexpr double radL = o2::its3::constants::radii[2] + 0.3, zL = o2::its3::constants::segment::lengthSensitive / 2.0 + 2.0; - - // Plot the whole geometry - std::array gIdeal; - std::array gDeformed; - constexpr double z1{-o2::its3::constants::segment::lengthSensitive / 2.0}, z2{o2::its3::constants::segment::lengthSensitive / 2.0}, zTot{z2 - z1}, zStep{zTot / (nPoints - 1)}; - auto canv = new TCanvas(); - canv->Divide(2, 1); - for (unsigned int iSensor{0}; iSensor < 6; ++iSensor) { - std::array xi, yi, zi, xd, yd, zd; - const double radius = o2::its3::constants::radii[iSensor / 2]; - const bool isTop = iSensor % 2 == 0; - const double phi1 = o2::math_utils::to02Pi(((isTop) ? 0.f : 1.f) * TMath::Pi() + std::asin(o2::its3::constants::equatorialGap / 2.f / radius)); - const double phi2 = o2::math_utils::to02Pi(((isTop) ? 1.f : 2.f) * TMath::Pi() - std::asin(o2::its3::constants::equatorialGap / 2.f / radius)); - const double phiTot{phi2 - phi1}, phiStep{phiTot / (nPoints - 1)}; - for (int iZ{0}; iZ < nPoints; ++iZ) { - double z = z1 + iZ * zStep; - for (int iPhi{0}; iPhi < nPoints; ++iPhi) { - int i = iZ * nPoints + iPhi; - double phi = phi1 + iPhi * phiStep; - - xi[i] = radius * std::cos(phi); - yi[i] = radius * std::sin(phi); - zi[i] = z; - - const double u = ((phi - phi1) * 2.f) / phiTot - 1.f; - const double v = ((z - z1)) / zTot - 1.f; - const auto [dx, dy, dz] = def.getDeformation(iSensor, u, v); - xd[i] = xi[i] + dx * factor; - yd[i] = yi[i] + dy * factor; - zd[i] = zi[i] + dz * factor; - } - } - - canv->cd(1); - auto ideal = new TGraph2D(Form("g_ideal_%d", iSensor), "Ideal Geometry", nPoints2, xi.data(), zi.data(), yi.data()); - ideal->SetMarkerStyle(kFullCircle); - ideal->SetMarkerSize(1); - ideal->SetMarkerColor(kBlue); - ideal->SetLineColor(kBlue); - if (iSensor == 0) { - ideal->Draw("p0"); - } else { - ideal->Draw("p0;same"); - if (iSensor == 5) { - gPad->Update(); - ideal->GetXaxis()->SetTitle("X"); - ideal->GetYaxis()->SetTitle("Z"); - ideal->GetZaxis()->SetTitle("Y"); - ideal->GetXaxis()->SetRangeUser(-radL, radL); - ideal->GetZaxis()->SetRangeUser(-radL, radL); - ideal->GetYaxis()->SetRangeUser(-zL, zL); - } - } - - canv->cd(2); - auto deformed = new TGraph2D(Form("g_def_%d", iSensor), "Deformed Geometry", nPoints2, xd.data(), zd.data(), yd.data()); - deformed->SetMarkerStyle(kFullCircle); - deformed->SetMarkerSize(1); - deformed->SetMarkerColor(kRed); - deformed->SetLineColor(kRed); - if (iSensor == 0) { - deformed->Draw("p0"); - } else { - deformed->Draw("p0;same"); - if (iSensor == 5) { - gPad->Update(); - deformed->GetXaxis()->SetTitle("X"); - deformed->GetYaxis()->SetTitle("Z"); - deformed->GetZaxis()->SetTitle("Y"); - deformed->GetXaxis()->SetRangeUser(-radL, radL); - deformed->GetZaxis()->SetRangeUser(-radL, radL); - deformed->GetYaxis()->SetRangeUser(-zL, zL); - } - } - } - - // Optionally find a deformation - /* if (findMin2D) { */ - /* std::vector cccc(nOrder + 2, 0.0); */ - /* cccc[0] = nOrder; */ - /* for (int i{0}; i <= nOrder; ++i) { */ - /* for (int j{0}; j <= i; ++j) { */ - /* int k = i * (i + 1) / 2 + j; */ - /* cccc[1 + k] = coeff(i, j); */ - /* } */ - /* } */ - /* auto ig = legendre_poly2D_integral(cccc.data()); */ - - /* gMin->Clear(); */ - /* ROOT::Math::Functor fmin(&legendre_poly2D_integral, */ - /* 2 + nOrder * (nOrder + 1) / 2 + nOrder); */ - /* Info("", "ig=%f parameters=%d", ig, */ - /* 2 + nOrder * (nOrder + 1) / 2 + nOrder); */ - /* gMin->SetFunction(fmin); */ - /* constexpr double minStep{0.001}; */ - /* gMin->SetFixedVariable(0, "nOrder", nOrder); */ - /* gMin->SetFixedVariable(1, "c_00", coeff(0, 0)); */ - /* for (int iOrder{1}; iOrder <= nOrder; ++iOrder) { */ - /* for (int jOrder{0}; jOrder <= iOrder; ++jOrder) { */ - /* int k = iOrder * (iOrder + 1) / 2 + jOrder + 1; */ - /* Info("", "Setting parameter %d", k); */ - /* if (getRandom() < 0.0) { */ - /* gMin->SetFixedVariable(k, Form("c_%d_%d", iOrder, jOrder), */ - /* coeff(iOrder, jOrder)); */ - /* } else { */ - /* gMin->SetVariable(k, Form("c_%d_%d", iOrder, jOrder), */ - /* coeff(iOrder, jOrder), minStep); */ - /* } */ - /* } */ - /* } */ - /* gMin->Minimize(); */ - /* return; */ - /* auto stat = gMin->Status(); */ - /* auto min = gMin->MinValue(); */ - /* if ((stat == 0 || stat == 1) && min < 0.01) { */ - /* Info("", "Minimizer converged with %f; using new values!", min); */ - /* const double *cmins = gMin->X(); */ - /* for (int iOrder{1}; iOrder <= nOrder; ++iOrder) { */ - /* for (int jOrder{0}; jOrder <= iOrder; ++jOrder) { */ - /* int k = iOrder * (iOrder + 1) / 2 + jOrder; */ - /* coeff(iOrder, jOrder) = cmins[k + 1]; */ - /* } */ - /* } */ - /* } */ - /* } */ - } -} diff --git a/Detectors/Upgrades/ITS3/macros/align/TestLegendrePol.C b/Detectors/Upgrades/ITS3/macros/align/TestLegendrePol.C deleted file mode 100644 index 720ab80264838..0000000000000 --- a/Detectors/Upgrades/ITS3/macros/align/TestLegendrePol.C +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright 2020-2022 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#if !defined(__CLING__) || defined(__ROOTCLING__) -#include "Math/Factory.h" -#include "Math/Minimizer.h" -#include "TAxis.h" -#include "TCanvas.h" -#include "TGraph.h" -#include "TGraph2D.h" -#include "TLegend.h" -#include "TMarker.h" -#include "TMultiGraph.h" -#include "TRandom.h" - -#include "MathUtils/LegendrePols.h" -#endif - -static ROOT::Math::Minimizer* gMin; - -void TestLegendrePol(bool findMin1D = false, bool findMin2D = false) -{ - constexpr int nMaxOrder{2}; - constexpr int nPoints{100}; - constexpr int nPoints2{nPoints * nPoints}; - constexpr double minX{-1.0}, maxX{1.0}, - stepX{(maxX - minX) / static_cast(nPoints)}; - - gRandom->SetSeed(0); - auto getRandom = []() { - constexpr double scale{80.e-4}; - return scale * gRandom->Uniform(-1.0, 1.0); - }; - - if (findMin1D || findMin2D) { - gMin = ROOT::Math::Factory::CreateMinimizer("Minuit2", "Migrad"); - if (gMin == nullptr) { - Error("", "Cannot create minimizer !"); - return; - } - gMin->SetMaxFunctionCalls(1000000); // for Minuit/Minuit2 - gMin->SetTolerance(0.00001); - gMin->SetPrintLevel(1); - } - - { // 1D - Info("", "---------------- 1D -------------"); - std::array x, y; - for (int nOrder{0}; nOrder <= nMaxOrder; ++nOrder) { - std::vector coeff(nOrder + 1, 0.0); - std::generate(std::begin(coeff), std::end(coeff), getRandom); - - o2::math_utils::Legendre1DPolynominal leg1D(coeff); - - // Optionally find a deformation - if (findMin1D) { - std::vector cccc(nOrder + 2, 0.0); - cccc[0] = nOrder; - for (int iOrder{0}; iOrder <= nOrder; ++iOrder) { - cccc[1 + iOrder] = coeff[iOrder]; - } - - gMin->Clear(); - /* gMin->SetFunction(&leg1D.DoIntegralPar); */ - constexpr double minStep{0.001}; - gMin->SetFixedVariable(0, "c_0", coeff[0]); - for (int iOrder{1}; iOrder <= nOrder; ++iOrder) { - gMin->SetVariable(iOrder, Form("c_%d", iOrder), coeff[iOrder], - minStep); - } - gMin->Minimize(); - auto stat = gMin->Status(); - auto min = gMin->MinValue(); - if ((stat == 0 || stat == 1) && min < 0.01) { - Info("", "Minimizer converged with %f; using new values!", min); - const double* cmins = gMin->X(); - for (int i{0}; i <= nOrder; ++i) { - Info("", "New values are c_%d=%.4f -> %.4f", i, coeff[i], - cmins[1 + i]); - coeff[i] = cmins[1 + i]; - } - } - } - - auto c1d = new TCanvas(Form("c1D_%d", nOrder), - Form("Legendre 1D Order %d", nOrder)); - c1d->Divide(2, 1); - - { // Draw total polynominal - for (int iPoint{0}; iPoint < nPoints; ++iPoint) { - double xcur = minX + iPoint * stepX; - x[iPoint] = xcur; - y[iPoint] = leg1D(xcur); - } - c1d->cd(2); - auto g = new TGraph(nPoints, x.data(), y.data()); - g->SetName(Form("g1d_%d", nOrder)); - g->SetTitle(Form("Legendre Polynominal %d-deg;u;w", nOrder)); - g->Draw(); - } - - { // Draw single contributions - auto mg = new TMultiGraph(); - auto leg = new TLegend(); - for (int iOrder{0}; iOrder <= nOrder; ++iOrder) { - for (int iPoint{0}; iPoint < nPoints; ++iPoint) { - double xcur = minX + iPoint * stepX; - x[iPoint] = xcur; - y[iPoint] = leg1D(iOrder, xcur); - } - auto g = new TGraph(nPoints, x.data(), y.data()); - g->SetName(Form("g1d_%d_%d", nOrder, iOrder)); - g->SetTitle(Form("c_{%d}=%.4f;u;w", iOrder, coeff[iOrder])); - mg->Add(g, "PL"); - leg->AddEntry(g, Form("c_{%d}=%.4f", iOrder, coeff[iOrder]), "lp"); - } - c1d->cd(1); - mg->SetTitle("Contribution decomposition;u;w"); - mg->Draw("A pmc plc"); - leg->Draw(); - gPad->Update(); - } - } - } - - { // 2D - Info("", "---------------- 2D -------------"); - std::array x, y, z; - for (int nOrder{0}; nOrder <= nMaxOrder; ++nOrder) { - auto c2d = new TCanvas(Form("c2D_%d", nOrder), - Form("Legendre 2D Order %d", nOrder)); - c2d->Divide(nOrder + 1, nOrder + 1); - - TMatrixD coeff(nOrder + 1, nOrder + 1); - // Random initialization - for (int i{0}; i <= nOrder; ++i) { - for (int j{0}; j <= i; ++j) { - coeff(i, j) = getRandom(); - } - } - - o2::math_utils::Legendre2DPolynominal leg2D(coeff); - - // Optionally find a deformation - /* if (findMin2D) { */ - /* std::vector cccc(nOrder + 2, 0.0); */ - /* cccc[0] = nOrder; */ - /* for (int i{0}; i <= nOrder; ++i) { */ - /* for (int j{0}; j <= i; ++j) { */ - /* int k = i * (i + 1) / 2 + j; */ - /* cccc[1 + k] = coeff(i, j); */ - /* } */ - /* } */ - /* auto ig = legendre_poly2D_integral(cccc.data()); */ - - /* gMin->Clear(); */ - /* ROOT::Math::Functor fmin(&legendre_poly2D_integral, */ - /* 2 + nOrder * (nOrder + 1) / 2 + nOrder); */ - /* Info("", "ig=%f parameters=%d", ig, */ - /* 2 + nOrder * (nOrder + 1) / 2 + nOrder); */ - /* gMin->SetFunction(fmin); */ - /* constexpr double minStep{0.001}; */ - /* gMin->SetFixedVariable(0, "nOrder", nOrder); */ - /* gMin->SetFixedVariable(1, "c_00", coeff(0, 0)); */ - /* for (int iOrder{1}; iOrder <= nOrder; ++iOrder) { */ - /* for (int jOrder{0}; jOrder <= iOrder; ++jOrder) { */ - /* int k = iOrder * (iOrder + 1) / 2 + jOrder + 1; */ - /* Info("", "Setting parameter %d", k); */ - /* if (getRandom() < 0.0) { */ - /* gMin->SetFixedVariable(k, Form("c_%d_%d", iOrder, jOrder), */ - /* coeff(iOrder, jOrder)); */ - /* } else { */ - /* gMin->SetVariable(k, Form("c_%d_%d", iOrder, jOrder), */ - /* coeff(iOrder, jOrder), minStep); */ - /* } */ - /* } */ - /* } */ - /* gMin->Minimize(); */ - /* return; */ - /* auto stat = gMin->Status(); */ - /* auto min = gMin->MinValue(); */ - /* if ((stat == 0 || stat == 1) && min < 0.01) { */ - /* Info("", "Minimizer converged with %f; using new values!", min); */ - /* const double *cmins = gMin->X(); */ - /* for (int iOrder{1}; iOrder <= nOrder; ++iOrder) { */ - /* for (int jOrder{0}; jOrder <= iOrder; ++jOrder) { */ - /* int k = iOrder * (iOrder + 1) / 2 + jOrder; */ - /* coeff(iOrder, jOrder) = cmins[k + 1]; */ - /* } */ - /* } */ - /* } */ - /* } */ - - leg2D.printCoefficients(); - - { // Draw total polynominal - for (int iPoint{0}; iPoint < nPoints; ++iPoint) { - double xcur = minX + iPoint * stepX; - for (int jPoint{0}; jPoint < nPoints; ++jPoint) { - double ycur = minX + jPoint * stepX; - int i = iPoint * nPoints + jPoint; - x[i] = xcur; - y[i] = ycur; - z[i] = leg2D(xcur, ycur); - } - } - c2d->cd(nOrder + 1); - auto g = new TGraph2D(nPoints2, x.data(), y.data(), z.data()); - g->SetTitle(Form("Legendre Polynominal %d-deg;u;v;w", nOrder)); - g->SetName(Form("g2d_%d", nOrder)); - g->Draw("surf1"); - g->GetXaxis()->SetRangeUser(minX, maxX); - g->GetYaxis()->SetRangeUser(minX, maxX); - gPad->Update(); - } - - { // Draw decomposition of contributions to polynominal - for (int iOrder{0}; iOrder <= nOrder; ++iOrder) { - for (int jOrder{0}; jOrder <= iOrder; ++jOrder) { - for (int iPoint{0}; iPoint < nPoints; ++iPoint) { - double xcur = minX + iPoint * stepX; - for (int jPoint{0}; jPoint < nPoints; ++jPoint) { - double ycur = minX + jPoint * stepX; - int i = iPoint * nPoints + jPoint; - x[i] = xcur; - y[i] = ycur; - z[i] = leg2D(iOrder, jOrder, xcur, ycur); - } - } - c2d->cd(1 + iOrder * (nOrder + 1) + jOrder); - auto g = new TGraph2D(nPoints2, x.data(), y.data(), z.data()); - g->SetTitle(Form("c_{%d,%d}=%.4f;u;v;w", iOrder, jOrder, - coeff(iOrder, jOrder))); - g->SetName(Form("g2d_%d_%d_%d", nOrder, iOrder, jOrder)); - if (iOrder == 0 && jOrder == 0) { // Fix display of constant value - g->Draw("P0"); - } else { - g->Draw("surf1"); - } - g->GetXaxis()->SetRangeUser(minX, maxX); - g->GetYaxis()->SetRangeUser(minX, maxX); - gPad->Update(); - } - } - } - c2d->Draw(); - } - } -} diff --git a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/IOUtils.h b/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/IOUtils.h index fa15e73118524..d82cd26e63c56 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/IOUtils.h +++ b/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/IOUtils.h @@ -14,12 +14,19 @@ #include "DataFormatsITSMFT/CompCluster.h" #include "DataFormatsITSMFT/ROFRecord.h" #include "ITS3Reconstruction/TopologyDictionary.h" -#include "ITStracking/TimeFrame.h" #include "ITStracking/IOUtils.h" #include "ITS3Base/SegmentationMosaix.h" #include "ITS3Base/SpecsV2.h" -namespace o2::its3::ioutils +namespace o2 +{ +namespace its +{ +template +class TimeFrame; +} + +namespace its3::ioutils { constexpr float DefClusErrorRow = o2::its3::SegmentationMosaix::PitchRow * 0.5; constexpr float DefClusErrorCol = o2::its3::SegmentationMosaix::PitchCol * 0.5; @@ -27,7 +34,7 @@ constexpr float DefClusError2Row = DefClusErrorRow * DefClusErrorRow; constexpr float DefClusError2Col = DefClusErrorCol * DefClusErrorCol; template -o2::math_utils::Point3D extractClusterData(const itsmft::CompClusterExt& c, iterator& iter, const its3::TopologyDictionary* dict, T& sig2y, T& sig2z) +o2::math_utils::Point3D extractClusterData(const itsmft::CompClusterExt& c, iterator& iter, const o2::its3::TopologyDictionary* dict, T& sig2y, T& sig2z) { auto pattID = c.getPatternID(); auto ib = constants::detID::isDetITS3(c.getSensorID()); @@ -50,7 +57,7 @@ o2::math_utils::Point3D extractClusterData(const itsmft::CompClusterExt& c, i } template -o2::math_utils::Point3D extractClusterData(const itsmft::CompClusterExt& c, iterator& iter, const its3::TopologyDictionary* dict, T& sig2y, T& sig2z, uint8_t& cls) +o2::math_utils::Point3D extractClusterData(const itsmft::CompClusterExt& c, iterator& iter, const o2::its3::TopologyDictionary* dict, T& sig2y, T& sig2z, uint8_t& cls) { auto pattID = c.getPatternID(); auto ib = constants::detID::isDetITS3(c.getSensorID()); @@ -69,13 +76,13 @@ o2::math_utils::Point3D extractClusterData(const itsmft::CompClusterExt& c, i void convertCompactClusters(gsl::span clusters, gsl::span::iterator& pattIt, std::vector>& output, - const its3::TopologyDictionary* dict); + const o2::its3::TopologyDictionary* dict); int loadROFrameDataITS3(its::TimeFrame<7>* tf, gsl::span rofs, gsl::span clusters, gsl::span::iterator& pattIt, - const its3::TopologyDictionary* dict, + const o2::its3::TopologyDictionary* dict, const dataformats::MCTruthContainer* mcLabels = nullptr); - -} // namespace o2::its3::ioutils +} // namespace its3::ioutils +} // namespace o2 diff --git a/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx b/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx index d7ba4d48dbce4..0fea07743b3df 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx +++ b/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// 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. // @@ -10,18 +10,13 @@ // or submit itself to any jurisdiction. #include "ITS3Reconstruction/IOUtils.h" -#include "ITStracking/IOUtils.h" #include "ITStracking/TimeFrame.h" #include "ITStracking/BoundedAllocator.h" #include "DataFormatsITSMFT/CompCluster.h" #include "DataFormatsITSMFT/ROFRecord.h" #include "ITS3Reconstruction/TopologyDictionary.h" #include "ITSBase/GeometryTGeo.h" -#include "ITS3Base/SpecsV2.h" #include "ITStracking/TrackingConfigParam.h" -#include "Framework/Logger.h" - -#include namespace o2::its3::ioutils { @@ -45,16 +40,19 @@ void convertCompactClusters(gsl::span clusters, } for (auto& c : clusters) { - float sigmaY2, sigmaZ2, sigmaYZ = 0; + float sigmaY2 = NAN, sigmaZ2 = NAN; auto locXYZ = extractClusterData(c, pattIt, dict, sigmaY2, sigmaZ2); const auto detID = c.getSensorID(); + // NOTE: this is not consistent with the TRK definition below! + // There we put the alpha for everything cluster to its phi + // here we extract it from the middle of the tile auto& cl3d = output.emplace_back(detID, geom->getMatrixT2L(detID) ^ locXYZ); // local --> tracking if (applyMisalignment) { - auto lrID = geom->getLayer(detID); + const auto lrID = geom->getLayer(detID); sigmaY2 += conf.sysErrY2[lrID]; sigmaZ2 += conf.sysErrZ2[lrID]; } - cl3d.setErrors(sigmaY2, sigmaZ2, sigmaYZ); + cl3d.setErrors(sigmaY2, sigmaZ2, 0.f); } } @@ -76,26 +74,36 @@ int loadROFrameDataITS3(its::TimeFrame<7>* tf, for (size_t iRof{0}; iRof < rofs.size(); ++iRof) { const auto& rof = rofs[iRof]; for (int clusterId{rof.getFirstEntry()}; clusterId < rof.getFirstEntry() + rof.getNEntries(); ++clusterId) { - auto& c = clusters[clusterId]; - auto sensorID = c.getSensorID(); - auto layer = geom->getLayer(sensorID); + const auto& c = clusters[clusterId]; + const auto sensorID = c.getSensorID(); + const auto layer = geom->getLayer(sensorID); float sigmaY2{0}, sigmaZ2{0}, sigmaYZ{0}; uint8_t clusterSize{0}; - auto locXYZ = extractClusterData(c, pattIt, dict, sigmaY2, sigmaZ2, clusterSize); + const auto locXYZ = extractClusterData(c, pattIt, dict, sigmaY2, sigmaZ2, clusterSize); clusterSizeVec.push_back(clusterSize); // Transformation to the local --> global - auto gloXYZ = geom->getMatrixL2G(sensorID) * locXYZ; + const auto gloXYZ = geom->getMatrixL2G(sensorID) * locXYZ; // Inverse transformation to the local --> tracking - o2::math_utils::Point3D trkXYZ = geom->getMatrixT2L(sensorID) ^ locXYZ; + const o2::math_utils::Point3D trkXYZ = geom->getMatrixT2L(sensorID) ^ locXYZ; // Tracking alpha angle + // We want that each cluster rotates its tracking frame to the clusters phi + // that way the track linearization around the measurement is less biases to the arc + // this means automatically that the measurement on the arc is at 0 for the curved layers float alpha = geom->getSensorRefAlpha(sensorID); - - tf->addTrackingFrameInfoToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), trkXYZ.x(), alpha, - std::array{trkXYZ.y(), trkXYZ.z()}, + float x = trkXYZ.x(), y = trkXYZ.y(); + if (constants::detID::isDetITS3(sensorID)) { + y = 0.f; + x = std::hypot(gloXYZ.x(), gloXYZ.y()); + alpha = std::atan2(gloXYZ.y(), gloXYZ.x()); + } + math_utils::detail::bringToPMPi(alpha); // alpha is defined on -Pi,Pi + + tf->addTrackingFrameInfoToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), x, alpha, + std::array{y, trkXYZ.z()}, std::array{sigmaY2, sigmaYZ, sigmaZ2}); /// Rotate to the global frame @@ -103,7 +111,7 @@ int loadROFrameDataITS3(its::TimeFrame<7>* tf, tf->addClusterExternalIndexToLayer(layer, clusterId); } for (unsigned int iL{0}; iL < tf->getUnsortedClusters().size(); ++iL) { - tf->mROFramesClusters[iL][iRof + 1] = tf->getUnsortedClusters()[iL].size(); + tf->mROFramesClusters[iL][iRof + 1] = (int)tf->getUnsortedClusters()[iL].size(); } } diff --git a/Detectors/Upgrades/ITS3/reconstruction/src/TopologyDictionary.cxx b/Detectors/Upgrades/ITS3/reconstruction/src/TopologyDictionary.cxx index 0d1deb77b7c2e..c7bb4dbe9b6ee 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/src/TopologyDictionary.cxx +++ b/Detectors/Upgrades/ITS3/reconstruction/src/TopologyDictionary.cxx @@ -255,5 +255,7 @@ TopologyDictionary* TopologyDictionary::loadFrom(const std::string& fname, const // Explicitly instaniate templates template math_utils::Point3D TopologyDictionary::getClusterCoordinates(const itsmft::CompClusterExt& cl) const; template math_utils::Point3D TopologyDictionary::getClusterCoordinates(const itsmft::CompClusterExt& cl, const itsmft::ClusterPattern& patt, bool isGroup); +template math_utils::Point3D TopologyDictionary::getClusterCoordinates(const itsmft::CompClusterExt& cl) const; +template math_utils::Point3D TopologyDictionary::getClusterCoordinates(const itsmft::CompClusterExt& cl, const itsmft::ClusterPattern& patt, bool isGroup); } // namespace o2::its3 diff --git a/Detectors/Upgrades/ITS3/study/CMakeLists.txt b/Detectors/Upgrades/ITS3/study/CMakeLists.txt index 4bb1cbca7dcb0..314b21c529ebf 100644 --- a/Detectors/Upgrades/ITS3/study/CMakeLists.txt +++ b/Detectors/Upgrades/ITS3/study/CMakeLists.txt @@ -17,12 +17,16 @@ o2_add_library(ITS3TrackingStudy src/TrackingStudy.cxx src/ParticleInfoExt.cxx PUBLIC_LINK_LIBRARIES O2::ITS3Workflow + O2::ITS3Align O2::GlobalTracking O2::GlobalTrackingWorkflowReaders O2::GlobalTrackingWorkflowHelpers O2::DataFormatsGlobalTracking O2::DetectorsVertexing - O2::SimulationDataFormat) + O2::SimulationDataFormat + Eigen3::Eigen + nlohmann_json::nlohmann_json +) o2_target_root_dictionary(ITS3TrackingStudy HEADERS include/ITS3TrackingStudy/ITS3TrackingStudyParam.h diff --git a/Detectors/Upgrades/ITS3/study/include/ITS3TrackingStudy/ITS3TrackingStudyParam.h b/Detectors/Upgrades/ITS3/study/include/ITS3TrackingStudy/ITS3TrackingStudyParam.h index 2e718622daa90..1150dc19a1162 100644 --- a/Detectors/Upgrades/ITS3/study/include/ITS3TrackingStudy/ITS3TrackingStudyParam.h +++ b/Detectors/Upgrades/ITS3/study/include/ITS3TrackingStudy/ITS3TrackingStudyParam.h @@ -22,7 +22,7 @@ namespace o2::its3::study struct ITS3TrackingStudyParam : o2::conf::ConfigurableParamHelper { /// general track selection float maxChi2{36}; - float maxEta{1.0}; + float maxEta{1.5}; float minPt{0.1}; float maxPt{1e2}; /// PV selection @@ -30,6 +30,8 @@ struct ITS3TrackingStudyParam : o2::conf::ConfigurableParamHelper::MatCorrType CorrType = o2::base::PropagatorImpl::MatCorrType::USEMatCorrLUT; /// studies - bool doDCA = true; - bool doDCARefit = true; - bool doPull = true; + bool doDCA = false; + bool doDCARefit = false; + bool doPull = false; + bool doResid = false; bool doMC = false; + bool doMisalignment = false; + + // misalignment + std::string misAlgJson; // json file containing to be applied misalignment + float misAlgExtCY[6] = {0.f}; // extra uncertainty on y + float misAlgExtCZ[6] = {0.f}; // extra uncertainty on z + O2ParamDef(ITS3TrackingStudyParam, "ITS3TrackingStudyParam"); }; diff --git a/Detectors/Upgrades/ITS3/study/include/ITS3TrackingStudy/TrackingStudy.h b/Detectors/Upgrades/ITS3/study/include/ITS3TrackingStudy/TrackingStudy.h index 065629058fd32..6d55dfc80aa8a 100644 --- a/Detectors/Upgrades/ITS3/study/include/ITS3TrackingStudy/TrackingStudy.h +++ b/Detectors/Upgrades/ITS3/study/include/ITS3TrackingStudy/TrackingStudy.h @@ -18,7 +18,7 @@ namespace o2::its3::study { -o2::framework::DataProcessorSpec getTrackingStudySpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool useMC); +o2::framework::DataProcessorSpec getTrackingStudySpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool useMC, bool withPV); } // namespace o2::its3::study diff --git a/Detectors/Upgrades/ITS3/study/macros/CMakeLists.txt b/Detectors/Upgrades/ITS3/study/macros/CMakeLists.txt index aaf763888c5e0..a772913c51f15 100644 --- a/Detectors/Upgrades/ITS3/study/macros/CMakeLists.txt +++ b/Detectors/Upgrades/ITS3/study/macros/CMakeLists.txt @@ -16,3 +16,11 @@ o2_add_test_root_macro(PlotDCA.C o2_add_test_root_macro(PlotPulls.C PUBLIC_LINK_LIBRARIES O2::ITS3TrackingStudy LABELS its COMPILE_ONLY) + +o2_add_test_root_macro(PlotResiduals.C + PUBLIC_LINK_LIBRARIES O2::ITS3TrackingStudy + LABELS its COMPILE_ONLY) + +o2_add_test_root_macro(PlotMisalignment.C + PUBLIC_LINK_LIBRARIES O2::ITS3TrackingStudy + LABELS its COMPILE_ONLY) diff --git a/Detectors/Upgrades/ITS3/study/macros/PlotMisalignment.C b/Detectors/Upgrades/ITS3/study/macros/PlotMisalignment.C new file mode 100644 index 0000000000000..5b9372bc3402e --- /dev/null +++ b/Detectors/Upgrades/ITS3/study/macros/PlotMisalignment.C @@ -0,0 +1,228 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ReconstructionDataFormats/Track.h" +#include +#endif + +constexpr int kNLay = 8; // slots: PV(-1) + layers 0-6 +constexpr int kNVar = 2; // dY, dZ +constexpr int kNPar = 5; // Y, Z, Snp, Tgl, Q2Pt +// constexpr int kNPtBins = 6; // integrated + 5 differential +// const float kPtEdges[kNPtBins] = {0., 0.3, 0.8, 2., 5., 10.}; +// const char* kPtLabels[kNPtBins] = {"", "0.0= kPtEdges[i] && pt < kPtEdges[i + 1]) { + return i + 1; // 1-indexed, 0 = integrated + } + } + return -1; +} + +void processTree(TFile* f, const char* treeName) +{ + auto* tree = f->Get(treeName); + if (!tree) { + return; + } + + // branch variables + float dY, dZ, phi, eta; + int lay; + auto* trk = new o2::track::TrackParCov; + auto* mcTrk = new o2::track::TrackPar; + tree->SetBranchAddress("dY", &dY); + tree->SetBranchAddress("dZ", &dZ); + tree->SetBranchAddress("phi", &phi); + tree->SetBranchAddress("eta", &eta); + tree->SetBranchAddress("lay", &lay); + tree->SetBranchAddress("trk", &trk); + tree->SetBranchAddress("mcTrk", &mcTrk); + + // --- book histograms --- + // [ptBin][lay] for each plot type + // dY/dZ vs phi + TH2F* hVsPhi[kNVar][kNPtBins][kNLay]; + // dY/dZ vs eta + TH2F* hVsEta[kNVar][kNPtBins][kNLay]; + // profile2D phi vs eta + TProfile2D* hProf[kNVar][kNPtBins][kNLay]; + // pulls + TH1F* hPull[kNPar][kNPtBins][kNLay]; + + for (int ipt = 0; ipt < kNPtBins; ipt++) { + for (int ilay = 0; ilay < kNLay; ilay++) { + for (int iv = 0; iv < kNVar; iv++) { + hVsPhi[iv][ipt][ilay] = new TH2F( + Form("%s_%s_phi%s_l%d", treeName, kVarNames[iv], kPtTags[ipt], ilay), + Form("Layer %d %s;#phi (rad);%s", ilay - 1, kPtLabels[ipt], kVarTitles[iv]), + 100, 0, 2 * TMath::Pi(), 100, -100, 100); + hVsEta[iv][ipt][ilay] = new TH2F( + Form("%s_%s_eta%s_l%d", treeName, kVarNames[iv], kPtTags[ipt], ilay), + Form("Layer %d %s;#eta;%s", ilay - 1, kPtLabels[ipt], kVarTitles[iv]), + 100, -1.5, 1.5, 100, -100, 100); + hProf[iv][ipt][ilay] = new TProfile2D( + Form("%s_%s_prof%s_l%d", treeName, kVarNames[iv], kPtTags[ipt], ilay), + Form("Layer %d %s;#phi (rad);#eta;#LT%s#GT", ilay - 1, kPtLabels[ipt], kVarTitles[iv]), + 50, 0, 2 * TMath::Pi(), 50, -1.5, 1.5); + } + for (int ip = 0; ip < kNPar; ip++) { + hPull[ip][ipt][ilay] = new TH1F( + Form("%s_pull_%s%s_l%d", treeName, kParNames[ip], kPtTags[ipt], ilay), + Form("Layer %d %s;pull_{%s};counts", ilay - 1, kPtLabels[ipt], kParNames[ip]), + 100, -5, 5); + } + } + } + + // --- fill loop --- + const Long64_t nEntries = tree->GetEntries(); + for (Long64_t i = 0; i < nEntries; i++) { + tree->GetEntry(i); + if (i % 100000 == 0) { + std::cout << "Progress: " << i << "/" << nEntries << " (" << (100.0 * i / nEntries) << "%)" << std::endl; + } + + int ilay = lay + 1; + float pt = trk->getPt(); + float dYum = dY * 10000.f; + float dZum = dZ * 10000.f; + + // integrated (ipt=0) + differential + int iptDiff = getPtBin(pt); + for (int ipt : {0, iptDiff}) { + if (ipt < 0) { + continue; + } + for (int iv = 0; iv < kNVar; iv++) { + float val = (iv == 0) ? dYum : dZum; + hVsPhi[iv][ipt][ilay]->Fill(phi, val); + hVsEta[iv][ipt][ilay]->Fill(eta, val); + hProf[iv][ipt][ilay]->Fill(phi, eta, val); + } + for (int ip = 0; ip < kNPar; ip++) { + float sigma2 = trk->getDiagError2(ip); + if (sigma2 > 0) { + hPull[ip][ipt][ilay]->Fill((trk->getParam(ip) - mcTrk->getParam(ip)) / std::sqrt(sigma2)); + } + } + } + } + + // --- draw & save --- + auto drawSliceFits = [](TH2F* h) { + h->FitSlicesY(nullptr, 0, -1, 5); + auto* hMean = (TH1D*)gDirectory->Get(Form("%s_1", h->GetName())); + auto* hSigma = (TH1D*)gDirectory->Get(Form("%s_2", h->GetName())); + if (hMean && hSigma) { + for (auto* hh : {hMean, hSigma}) { + hh->SetMarkerStyle(20); + hh->SetMarkerSize(0.6); + } + hMean->SetMarkerColor(kRed); + hMean->SetLineColor(kRed); + hSigma->SetMarkerColor(kOrange + 1); + hSigma->SetLineColor(kOrange + 1); + hMean->Draw("same"); + hSigma->Draw("same"); + } + }; + + for (int ipt = 0; ipt < kNPtBins; ipt++) { + // dY/dZ vs phi + for (int iv = 0; iv < kNVar; iv++) { + auto* c = new TCanvas(Form("%s_%s_vs_phi%s", treeName, kVarNames[iv], kPtTags[ipt]), + Form("%s vs #phi %s", kVarNames[iv], kPtLabels[ipt]), 800, 1600); + c->Divide(2, 4); + for (int ilay = 0; ilay < kNLay; ilay++) { + c->cd(ilay + 1); + gPad->SetRightMargin(0.13); + hVsPhi[iv][ipt][ilay]->Draw("col"); + drawSliceFits(hVsPhi[iv][ipt][ilay]); + } + c->SaveAs(Form("%s.png", c->GetName())); + } + + // dY/dZ vs eta + for (int iv = 0; iv < kNVar; iv++) { + auto* c = new TCanvas(Form("%s_%s_vs_eta%s", treeName, kVarNames[iv], kPtTags[ipt]), + Form("%s vs #eta %s", kVarNames[iv], kPtLabels[ipt]), 800, 1600); + c->Divide(2, 4); + for (int ilay = 0; ilay < kNLay; ilay++) { + c->cd(ilay + 1); + gPad->SetRightMargin(0.13); + hVsEta[iv][ipt][ilay]->Draw("col"); + drawSliceFits(hVsEta[iv][ipt][ilay]); + } + c->SaveAs(Form("%s.png", c->GetName())); + } + + // profile2D + for (int iv = 0; iv < kNVar; iv++) { + auto* c = new TCanvas(Form("%s_%s_prof2d%s", treeName, kVarNames[iv], kPtTags[ipt]), + Form("%s #phi vs #eta %s", kVarNames[iv], kPtLabels[ipt]), 800, 1600); + c->Divide(2, 4); + for (int ilay = 0; ilay < kNLay; ilay++) { + c->cd(ilay + 1); + gPad->SetRightMargin(0.15); + hProf[iv][ipt][ilay]->Draw("colz"); + hProf[iv][ipt][ilay]->GetZaxis()->SetRangeUser(-100, 100); + } + c->SaveAs(Form("%s.png", c->GetName())); + } + + // pulls + for (int ilay = 0; ilay < kNLay; ilay++) { + auto* c = new TCanvas(Form("%s_pulls_l%d%s", treeName, ilay, kPtTags[ipt]), + Form("Pulls layer %d %s", ilay - 1, kPtLabels[ipt]), 1200, 800); + c->Divide(3, 2); + for (int ip = 0; ip < kNPar; ip++) { + c->cd(ip + 1); + hPull[ip][ipt][ilay]->Draw(); + if (hPull[ip][ipt][ilay]->GetEntries() > 20) { + hPull[ip][ipt][ilay]->Fit("gaus", "Q"); + } + } + c->SaveAs(Form("%s.png", c->GetName())); + } + } +} + +void PlotMisalignment(const char* fname = "its3TrackStudy.root") +{ + gStyle->SetOptStat(0); + gStyle->SetOptFit(1); + auto f = TFile::Open(fname); + processTree(f, "idealRes"); + processTree(f, "misRes"); +} diff --git a/Detectors/Upgrades/ITS3/study/macros/PlotResiduals.C b/Detectors/Upgrades/ITS3/study/macros/PlotResiduals.C new file mode 100644 index 0000000000000..e35fd4df125e5 --- /dev/null +++ b/Detectors/Upgrades/ITS3/study/macros/PlotResiduals.C @@ -0,0 +1,70 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include +#include +#endif + +void PlotResiduals(const char* fname = "its3TrackStudy.root") +{ + auto f = TFile::Open(fname); + auto res = f->Get("res"); + + const int nLay = 8; + const int nVar = 6; + const char* vars[nVar] = {"dYInt", "dZInt", "dYIn", "dZIn", "dYOut", "dZOut"}; + const char* titles[nVar] = {"d_{Y} (#mum) (weighted)", "d_{Z} (#mum) (weighted)", "d_{Y} (#mu) (inward)", "d_{Y} (#mu) (inward)", "d_{Y} (#mu) (outward)", "d_{Z} (#mu) (outward)"}; + + TCanvas* canvs[nVar]; + for (int iv = 0; iv < nVar; iv++) { + canvs[iv] = new TCanvas(vars[iv], Form("%s residuals", vars[iv]), 800, 1600); + canvs[iv]->Divide(2, 4); + } + + for (int iv = 0; iv < nVar; iv++) { + canvs[iv]->cd(0); + for (int lay = -1; lay <= 6; lay++) { + canvs[iv]->cd(lay + 2); + gPad->SetRightMargin(0.13); + + TString hname = Form("h_%s_lay%d", vars[iv], lay + 1); + TString expr = Form("%s*10000:phi>>%s(100,0,6.283,100,-100,100)", vars[iv], hname.Data()); + TString sel = Form("lay==%d", lay); + res->Draw(expr, sel, "col"); + + auto* h = (TH2F*)gDirectory->Get(hname); + h->SetTitle(Form("Layer %d ;#phi (rad);%s", lay, titles[iv])); + h->GetZaxis()->SetLabelSize(0.035); + + // fit y-slices with gaussian + h->FitSlicesY(nullptr, 0, -1, 5); + auto* hMean = (TH1D*)gDirectory->Get(Form("%s_1", hname.Data())); + auto* hSigma = (TH1D*)gDirectory->Get(Form("%s_2", hname.Data())); + + for (auto* hh : {hMean, hSigma}) { + hh->SetMarkerStyle(20); + hh->SetMarkerSize(0.6); + } + hMean->SetMarkerColor(kRed); + hMean->SetLineColor(kRed); + hSigma->SetMarkerColor(kOrange + 1); + hSigma->SetLineColor(kOrange + 1); + hMean->Draw("same"); + hSigma->Draw("same"); + } + canvs[iv]->SaveAs(Form("%s.png", canvs[iv]->GetName())); + } +} diff --git a/Detectors/Upgrades/ITS3/study/src/TrackingStudy.cxx b/Detectors/Upgrades/ITS3/study/src/TrackingStudy.cxx index cb1d7f381983d..4ce2c79cb23f1 100644 --- a/Detectors/Upgrades/ITS3/study/src/TrackingStudy.cxx +++ b/Detectors/Upgrades/ITS3/study/src/TrackingStudy.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// 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. // @@ -11,9 +11,12 @@ #include #include +#include #include #include +#include +#include #include "CommonUtils/TreeStreamRedirector.h" #include "DataFormatsGlobalTracking/RecoContainer.h" @@ -35,6 +38,7 @@ #include "ITS3Reconstruction/IOUtils.h" #include "ITS3TrackingStudy/ITS3TrackingStudyParam.h" #include "ITS3TrackingStudy/ParticleInfoExt.h" +#include "ITS3Align/TrackFit.h" #include "ReconstructionDataFormats/DCA.h" #include "ReconstructionDataFormats/GlobalTrackID.h" #include "ReconstructionDataFormats/PrimaryVertex.h" @@ -43,6 +47,8 @@ #include "SimulationDataFormat/MCEventLabel.h" #include "SimulationDataFormat/MCUtils.h" #include "Steer/MCKinematicsReader.h" +#include "MathUtils/LegendrePols.h" +#include "Framework/Logger.h" namespace o2::its3::study { @@ -58,6 +64,10 @@ using T2VMap = std::unordered_map; class TrackingStudySpec : public Task { public: + TrackingStudySpec(const TrackingStudySpec&) = delete; + TrackingStudySpec(TrackingStudySpec&&) = delete; + TrackingStudySpec& operator=(const TrackingStudySpec&) = delete; + TrackingStudySpec& operator=(TrackingStudySpec&&) = delete; TrackingStudySpec(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool useMC) : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mUseMC(useMC) {} ~TrackingStudySpec() final = default; @@ -67,16 +77,19 @@ class TrackingStudySpec : public Task void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; private: - void process(o2::globaltracking::RecoContainer& recoData); + void process(); void updateTimeDependentParams(ProcessingContext& pc); - std::vector> prepareITSClusters(const o2::globaltracking::RecoContainer& data) const; - bool selectTrack(GTrackID trkID, o2::globaltracking::RecoContainer& recoData, bool checkMCTruth = true) const; - T2VMap buildT2V(o2::globaltracking::RecoContainer& recoData, bool includeCont = false, bool requireMCMatch = true) const; - bool refitITSPVTrack(o2::globaltracking::RecoContainer& recoData, o2::track::TrackParCov& trFit, GTrackID gidx); - void doDCAStudy(o2::globaltracking::RecoContainer& recoData); - void doDCARefitStudy(o2::globaltracking::RecoContainer& recoData); - void doPullStudy(o2::globaltracking::RecoContainer& recoData); - void doMCStudy(o2::globaltracking::RecoContainer& recoData); + void prepareITSClusters(); + bool selectTrack(GTrackID trkID, bool checkMCTruth = true) const; + T2VMap buildT2V(bool includeCont = false, bool requireMCMatch = true) const; + bool refitITSPVTrack(o2::track::TrackParCov& trFit, GTrackID gidx); + + void doDCAStudy(); + void doDCARefitStudy(); + void doPullStudy(); + void doMCStudy(); + void doResidStudy(); + void doMisalignmentStudy(); struct TrackCounter { TrackCounter() = default; @@ -126,14 +139,21 @@ class TrackingStudySpec : public Task }; TrackCounter mTrackCounter; + using TrackingCluster = align::TrackingCluster; + std::vector mITScl; + std::span mITSclRef; + + const ITS3TrackingStudyParam* mParams{nullptr}; std::unique_ptr mDBGOut; std::shared_ptr mDataRequest; std::shared_ptr mGGCCDBRequest; bool mUseMC{false}; GTrackID::mask_t mTracksSrc; o2::vertexing::PVertexer mVertexer; - o2::steer::MCKinematicsReader mcReader; // reader of MC information + o2::steer::MCKinematicsReader mMCReader; // reader of MC information const o2::its3::TopologyDictionary* mITSDict = nullptr; // cluster patterns dictionary + o2::globaltracking::RecoContainer mRecoData; + std::array mDeformations; // one per sensorID (0-5) }; void TrackingStudySpec::init(InitContext& ic) @@ -144,17 +164,16 @@ void TrackingStudySpec::init(InitContext& ic) std::string dbgnm = maxLanes == 1 ? "its3TrackStudy.root" : fmt::format("its3TrackStudy_{}.root", lane); mDBGOut = std::make_unique(dbgnm.c_str(), "recreate"); - if (mUseMC && !mcReader.initFromDigitContext(o2::base::NameConf::getCollisionContextFileName())) { + if (mUseMC && !mMCReader.initFromDigitContext(o2::base::NameConf::getCollisionContextFileName())) { LOGP(fatal, "initialization of MCKinematicsReader failed"); } } void TrackingStudySpec::run(ProcessingContext& pc) { - o2::globaltracking::RecoContainer recoData; - recoData.collectData(pc, *mDataRequest); + mRecoData.collectData(pc, *mDataRequest); updateTimeDependentParams(pc); - process(recoData); + process(); } void TrackingStudySpec::updateTimeDependentParams(ProcessingContext& pc) @@ -165,6 +184,30 @@ void TrackingStudySpec::updateTimeDependentParams(ProcessingContext& pc) auto grp = o2::base::GRPGeomHelper::instance().getGRPECS(); mVertexer.init(); o2::its::GeometryTGeo::Instance()->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G, o2::math_utils::TransformType::T2G)); + mParams = &ITS3TrackingStudyParam::Instance(); + if (mParams->doMisalignment) { + TMatrixD null(1, 1); + null(0, 0) = 0; + for (int i = 0; i < 6; ++i) { + mDeformations[i] = o2::math_utils::Legendre2DPolynominal(null); + } + if (!mParams->misAlgJson.empty()) { + using json = nlohmann::json; + std::ifstream f(mParams->misAlgJson); + auto data = json::parse(f); + for (const auto& item : data) { + int id = item["id"].get(); + auto v = item["matrix"].get>>(); + TMatrixD m(v.size(), v[v.size() - 1].size()); + for (size_t r{0}; r < v.size(); ++r) { + for (size_t c{0}; c < v[r].size(); ++c) { + m(r, c) = v[r][c]; + } + } + mDeformations[id] = o2::math_utils::Legendre2DPolynominal(m); + } + } + } } } @@ -185,76 +228,105 @@ void TrackingStudySpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) } } -void TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) +void TrackingStudySpec::process() { - const auto& conf = ITS3TrackingStudyParam::Instance(); - if (conf.doDCA) { - doDCAStudy(recoData); + prepareITSClusters(); + if (mParams->doDCA) { + doDCAStudy(); + } + if (mParams->doDCARefit) { + doDCARefitStudy(); + } + if (mUseMC && mParams->doPull) { + doPullStudy(); } - if (conf.doDCARefit) { - doDCARefitStudy(recoData); + if (mUseMC && mParams->doMC) { + doMCStudy(); } - if (mUseMC && conf.doPull) { - doPullStudy(recoData); + if (mParams->doResid) { + doResidStudy(); } - if (mUseMC && conf.doMC) { - doMCStudy(recoData); + if (mUseMC && mParams->doMisalignment) { + doMisalignmentStudy(); } } -std::vector> TrackingStudySpec::prepareITSClusters(const o2::globaltracking::RecoContainer& data) const +void TrackingStudySpec::prepareITSClusters() { - std::vector> itscl; - const auto& clusITS = data.getITSClusters(); - if (clusITS.size()) { - const auto& patterns = data.getITSClustersPatterns(); - itscl.reserve(clusITS.size()); - auto pattIt = patterns.begin(); - o2::its3::ioutils::convertCompactClusters(clusITS, pattIt, itscl, mITSDict); - } - return std::move(itscl); + const auto& clusITS = mRecoData.getITSClusters(); + LOGP(info, "Preparing {} measurments", clusITS.size()); + const auto& patterns = mRecoData.getITSClustersPatterns(); + mITScl.reserve(clusITS.size()); + auto pattIt = patterns.begin(); + auto geom = its::GeometryTGeo::Instance(); + mITSclRef = mRecoData.getITSTracksClusterRefs(); + mITScl.clear(); + mITScl.reserve(clusITS.size()); + for (const auto& cls : clusITS) { + const auto sens = cls.getSensorID(); + float sigmaY2{0}, sigmaZ2{0}; + math_utils::Point3D locXYZ = o2::its3::ioutils::extractClusterData(cls, pattIt, mITSDict, sigmaY2, sigmaZ2); + // Transformation to the local --> global + const auto gloXYZ = geom->getMatrixL2G(sens) * locXYZ; + // Inverse transformation to the local --> tracking + o2::math_utils::Point3D trkXYZ = geom->getMatrixT2L(sens) ^ locXYZ; + // Tracking alpha angle + // We want that each cluster rotates its tracking frame to the clusters phi + // that way the track linearization around the measurement is less biases to the arc + // this means automatically that the measurement on the arc is at 0 for the curved layers + float alpha = geom->getSensorRefAlpha(sens); + if (constants::detID::isDetITS3(sens)) { + trkXYZ.SetY(0.f); + // alpha&x always have to be defined wrt to the global Z axis! + trkXYZ.SetX(std::hypot(gloXYZ.x(), gloXYZ.y())); + alpha = std::atan2(gloXYZ.y(), gloXYZ.x()); + } + auto& cl3d = mITScl.emplace_back(sens, trkXYZ); + cl3d.setErrors(sigmaY2, sigmaZ2, 0.f); + cl3d.alpha = alpha; + math_utils::detail::bringToPMPi(cl3d.alpha); // alpha is defined on -Pi,Pi + } } -bool TrackingStudySpec::selectTrack(GTrackID trkID, o2::globaltracking::RecoContainer& recoData, bool checkMCTruth) const +bool TrackingStudySpec::selectTrack(GTrackID trkID, bool checkMCTruth) const { - const auto& conf = ITS3TrackingStudyParam::Instance(); if (!trkID.includesDet(GTrackID::ITS)) { return false; } - if (!recoData.isTrackSourceLoaded(trkID.getSource())) { + if (!mRecoData.isTrackSourceLoaded(trkID.getSource())) { return false; } - auto contributorsGID = recoData.getSingleDetectorRefs(trkID); + auto contributorsGID = mRecoData.getSingleDetectorRefs(trkID); if (!contributorsGID[GTrackID::ITS].isIndexSet()) { // we need of course ITS return false; } // ITS specific - const auto& itsTrk = recoData.getITSTrack(contributorsGID[GTrackID::ITS]); - if (itsTrk.getChi2() > conf.maxChi2 || itsTrk.getNClusters() < conf.minITSCls) { + const auto& itsTrk = mRecoData.getITSTrack(contributorsGID[GTrackID::ITS]); + if (itsTrk.getChi2() > mParams->maxChi2 || itsTrk.getNClusters() < mParams->minITSCls) { return false; } // TPC specific if (contributorsGID[GTrackID::TPC].isIndexSet()) { - const auto& tpcTrk = recoData.getTPCTrack(contributorsGID[GTrackID::TPC]); - if (tpcTrk.getNClusters() < conf.minTPCCls) { + const auto& tpcTrk = mRecoData.getTPCTrack(contributorsGID[GTrackID::TPC]); + if (tpcTrk.getNClusters() < mParams->minTPCCls) { return false; } } // general - const auto& gTrk = recoData.getTrackParam(trkID); - if (gTrk.getPt() < conf.minPt || gTrk.getPt() > conf.maxPt) { + const auto& gTrk = mRecoData.getTrackParam(trkID); + if (gTrk.getPt() < mParams->minPt || gTrk.getPt() > mParams->maxPt) { return false; } - if (std::abs(gTrk.getEta()) > conf.maxEta) { + if (std::abs(gTrk.getEta()) > mParams->maxEta) { return false; } if (mUseMC && checkMCTruth) { - const auto& itsLbl = recoData.getTrackMCLabel(contributorsGID[GTrackID::ITS]); + const auto& itsLbl = mRecoData.getTrackMCLabel(contributorsGID[GTrackID::ITS]); if (!itsLbl.isValid()) { return false; } if (contributorsGID[GTrackID::TPC].isIndexSet()) { - const auto& tpcLbl = recoData.getTrackMCLabel(contributorsGID[GTrackID::TPC]); + const auto& tpcLbl = mRecoData.getTrackMCLabel(contributorsGID[GTrackID::TPC]); if (itsLbl != tpcLbl) { return false; } @@ -263,7 +335,7 @@ bool TrackingStudySpec::selectTrack(GTrackID trkID, o2::globaltracking::RecoCont // TODO } if (contributorsGID[GTrackID::TOF].isIndexSet()) { - const auto& tofLbls = recoData.getTOFClustersMCLabels()->getLabels(contributorsGID[GTrackID::TOF]); + const auto& tofLbls = mRecoData.getTOFClustersMCLabels()->getLabels(contributorsGID[GTrackID::TOF]); for (const auto& lbl : tofLbls) { if (lbl.isValid()) { return true; @@ -274,22 +346,21 @@ bool TrackingStudySpec::selectTrack(GTrackID trkID, o2::globaltracking::RecoCont return true; } -T2VMap TrackingStudySpec::buildT2V(o2::globaltracking::RecoContainer& recoData, bool includeCont, bool requireMCMatch) const +T2VMap TrackingStudySpec::buildT2V(bool includeCont, bool requireMCMatch) const { // build track->vertex assoc., maybe including contributor tracks - const auto& conf = ITS3TrackingStudyParam::Instance(); - auto pvvec = recoData.getPrimaryVertices(); - auto trackIndex = recoData.getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks - auto vtxRefs = recoData.getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs - auto nv = vtxRefs.size() - 1; // last entry is for unassigned tracks, ignore them + auto pvvec = mRecoData.getPrimaryVertices(); + auto trackIndex = mRecoData.getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks + auto vtxRefs = mRecoData.getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs + auto nv = vtxRefs.size() - 1; // last entry is for unassigned tracks, ignore them T2VMap t2v; for (size_t iv = 0; iv < nv; ++iv) { const auto& pv = pvvec[iv]; - if (pv.getNContributors() - 1 < conf.minPVCont) { + if (pv.getNContributors() - 1 < mParams->minPVCont) { continue; } if (requireMCMatch) { - auto pvl = recoData.getPrimaryVertexMCLabel(iv); + auto pvl = mRecoData.getPrimaryVertexMCLabel(iv); } const auto& vtxRef = vtxRefs[iv]; int it = vtxRef.getFirstEntry(), itLim = it + vtxRef.getEntries(); @@ -298,26 +369,26 @@ T2VMap TrackingStudySpec::buildT2V(o2::globaltracking::RecoContainer& recoData, if (tvid.isAmbiguous()) { continue; } - if (!recoData.isTrackSourceLoaded(tvid.getSource())) { + if (!mRecoData.isTrackSourceLoaded(tvid.getSource())) { continue; } if (mUseMC && requireMCMatch) { - const auto& pvlbl = recoData.getPrimaryVertexMCLabel(iv); - if (pvlbl.getEventID() != recoData.getTrackMCLabel(tvid).getEventID()) { + const auto& pvlbl = mRecoData.getPrimaryVertexMCLabel(iv); + if (pvlbl.getEventID() != mRecoData.getTrackMCLabel(tvid).getEventID()) { continue; } } t2v[tvid] = iv; if (includeCont) { - auto contributorsGID = recoData.getSingleDetectorRefs(tvid); + auto contributorsGID = mRecoData.getSingleDetectorRefs(tvid); for (int cis = 0; cis < GTrackID::NSources; cis++) { const auto cdm = GTrackID::getSourceDetectorsMask(cis); - if (!recoData.isTrackSourceLoaded(cis) || !cdm[DetID::ITS] || !contributorsGID[cis].isIndexSet()) { + if (!mRecoData.isTrackSourceLoaded(cis) || !cdm[DetID::ITS] || !contributorsGID[cis].isIndexSet()) { continue; } if (mUseMC && requireMCMatch) { - const auto& pvlbl = recoData.getPrimaryVertexMCLabel(iv); - if (pvlbl.getEventID() != recoData.getTrackMCLabel(contributorsGID[cis]).getEventID()) { + const auto& pvlbl = mRecoData.getPrimaryVertexMCLabel(iv); + if (pvlbl.getEventID() != mRecoData.getTrackMCLabel(contributorsGID[cis]).getEventID()) { continue; } } @@ -329,19 +400,18 @@ T2VMap TrackingStudySpec::buildT2V(o2::globaltracking::RecoContainer& recoData, return std::move(t2v); } -bool TrackingStudySpec::refitITSPVTrack(o2::globaltracking::RecoContainer& recoData, o2::track::TrackParCov& trFit, GTrackID gidx) +bool TrackingStudySpec::refitITSPVTrack(o2::track::TrackParCov& trFit, GTrackID gidx) { if (gidx.getSource() != GTrackID::ITS) { return false; } - static auto pvvec = recoData.getPrimaryVertices(); - static auto t2v = buildT2V(recoData, true, true); - static const auto itsClusters = prepareITSClusters(recoData); + static auto pvvec = mRecoData.getPrimaryVertices(); + static auto t2v = buildT2V(true, true); static std::vector itsTracksROF; if (static bool done{false}; !done) { done = true; - const auto& itsTracksROFRec = recoData.getITSTracksROFRecords(); - itsTracksROF.resize(recoData.getITSTracks().size()); + const auto& itsTracksROFRec = mRecoData.getITSTracksROFRecords(); + itsTracksROF.resize(mRecoData.getITSTracks().size()); for (unsigned irf = 0, cnt = 0; irf < itsTracksROFRec.size(); irf++) { int ntr = itsTracksROFRec[irf].getNEntries(); for (int itr = 0; itr < ntr; itr++) { @@ -350,20 +420,18 @@ bool TrackingStudySpec::refitITSPVTrack(o2::globaltracking::RecoContainer& recoD } } auto prop = o2::base::Propagator::Instance(); - const auto& conf = ITS3TrackingStudyParam::Instance(); - std::array, 8> clArr{}; - std::array clAlpha{}; - const auto trkIn = recoData.getTrackParam(gidx); - const auto trkOut = recoData.getTrackParamOut(gidx); - const auto& itsTrOrig = recoData.getITSTrack(gidx); + std::array clArr{nullptr}; + const auto trkIn = mRecoData.getTrackParam(gidx); + const auto trkOut = mRecoData.getTrackParamOut(gidx); + const auto& itsTrOrig = mRecoData.getITSTrack(gidx); int ncl = itsTrOrig.getNumberOfClusters(), rof = itsTracksROF[gidx.getIndex()]; - const auto& itsTrackClusRefs = recoData.getITSTracksClusterRefs(); + const auto& itsTrackClusRefs = mRecoData.getITSTracksClusterRefs(); int clEntry = itsTrOrig.getFirstClusterEntry(); const auto propagator = o2::base::Propagator::Instance(); // convert PV to a fake cluster in the track DCA frame const auto& pv = pvvec[t2v[gidx]]; auto trkPV = trkIn; - if (!prop->propagateToDCA(pv, trkPV, prop->getNominalBz(), 2.0, conf.CorrType)) { + if (!prop->propagateToDCA(pv, trkPV, prop->getNominalBz(), 2.0, mParams->CorrType)) { mTrackCounter -= gidx.getSource(); return false; } @@ -371,25 +439,25 @@ bool TrackingStudySpec::refitITSPVTrack(o2::globaltracking::RecoContainer& recoD float cosAlp = NAN, sinAlp = NAN; o2::math_utils::sincos(trkPV.getAlpha(), sinAlp, cosAlp); // vertex position rotated to track frame - clArr[0].setXYZ(pv.getX() * cosAlp + pv.getY() * sinAlp, -pv.getX() * sinAlp + pv.getY() * cosAlp, pv.getZ()); - clArr[0].setSigmaY2(0.5 * (pv.getSigmaX2() + pv.getSigmaY2())); - clArr[0].setSigmaZ2(pv.getSigmaZ2()); - clAlpha[0] = trkPV.getAlpha(); + TrackingCluster pvCls; + pvCls.setXYZ((pv.getX() * cosAlp) + (pv.getY() * sinAlp), (-pv.getX() * sinAlp) + (pv.getY() * cosAlp), pv.getZ()); + pvCls.setSigmaY2(0.5f * (pv.getSigmaX2() + pv.getSigmaY2())); + pvCls.setSigmaZ2(pv.getSigmaZ2()); + clArr[0] = &pvCls; for (int icl = 0; icl < ncl; ++icl) { // ITS clusters are referred in layer decreasing order - clArr[ncl - icl] = itsClusters[itsTrackClusRefs[clEntry + icl]]; - clAlpha[ncl - icl] = o2::its::GeometryTGeo::Instance()->getSensorRefAlpha(clArr[ncl - icl].getSensorID()); + clArr[ncl - icl] = &mITScl[itsTrackClusRefs[clEntry + icl]]; } // start refit trFit = trkOut; trFit.resetCovariance(1'000); float chi2{0}; for (int icl = ncl; icl >= 0; --icl) { // go backwards - if (!trFit.rotate(clAlpha[icl]) || !prop->propagateToX(trFit, clArr[icl].getX(), prop->getNominalBz(), 0.85, 2.0, conf.CorrType)) { + if (!trFit.rotate(clArr[icl]->alpha) || !prop->propagateToX(trFit, clArr[icl]->getX(), prop->getNominalBz(), 0.85, 2.0, mParams->CorrType)) { mTrackCounter -= gidx.getSource(); return false; } - chi2 += trFit.getPredictedChi2(clArr[icl]); - if (!trFit.update(clArr[icl])) { + chi2 += trFit.getPredictedChi2(*clArr[icl]); + if (!trFit.update(*clArr[icl])) { mTrackCounter -= gidx.getSource(); return false; } @@ -398,27 +466,26 @@ bool TrackingStudySpec::refitITSPVTrack(o2::globaltracking::RecoContainer& recoD return true; }; -void TrackingStudySpec::doDCAStudy(o2::globaltracking::RecoContainer& recoData) +void TrackingStudySpec::doDCAStudy() { /// analyse DCA of impact parameter for different track types LOGP(info, "Doing DCA study"); mTrackCounter.reset(); - const auto& conf = ITS3TrackingStudyParam::Instance(); auto prop = o2::base::Propagator::Instance(); TStopwatch sw; sw.Start(); int nDCAFits{0}, nDCAFitsFail{0}; - auto pvvec = recoData.getPrimaryVertices(); - auto trackIndex = recoData.getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks - auto vtxRefs = recoData.getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs - auto nv = vtxRefs.size() - 1; // last entry is for unassigned tracks, ignore them + auto pvvec = mRecoData.getPrimaryVertices(); + auto trackIndex = mRecoData.getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks + auto vtxRefs = mRecoData.getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs + auto nv = vtxRefs.size() - 1; // last entry is for unassigned tracks, ignore them auto& stream = (*mDBGOut) << "dca"; for (int iv = 0; iv < nv; iv++) { const auto& pv = pvvec[iv]; const auto& vtref = vtxRefs[iv]; for (int is = 0; is < GTrackID::NSources; is++) { const auto dm = GTrackID::getSourceDetectorsMask(is); - if (!recoData.isTrackSourceLoaded(is) || !dm[DetID::ITS]) { + if (!mRecoData.isTrackSourceLoaded(is) || !dm[DetID::ITS]) { mTrackCounter &= is; continue; } @@ -432,23 +499,23 @@ void TrackingStudySpec::doDCAStudy(o2::globaltracking::RecoContainer& recoData) // we fit each different sub-track type, that include ITS, e.g. // ITS,ITS-TPC,ITS-TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD-TOF - auto contributorsGID = recoData.getSingleDetectorRefs(vid); + auto contributorsGID = mRecoData.getSingleDetectorRefs(vid); for (int cis = 0; cis < GTrackID::NSources && cis <= is; cis++) { const auto cdm = GTrackID::getSourceDetectorsMask(cis); - if (!recoData.isTrackSourceLoaded(cis) || !cdm[DetID::ITS] || !contributorsGID[cis].isIndexSet()) { + if (!mRecoData.isTrackSourceLoaded(cis) || !cdm[DetID::ITS] || !contributorsGID[cis].isIndexSet()) { mTrackCounter &= cis; continue; } - if (!selectTrack(contributorsGID[cis], recoData)) { + if (!selectTrack(contributorsGID[cis])) { mTrackCounter &= vid.getSource(); continue; } o2::dataformats::DCA dcaInfo; - const auto& trk = recoData.getTrackParam(contributorsGID[cis]); + const auto& trk = mRecoData.getTrackParam(contributorsGID[cis]); auto trkRefit = trk; // for ITS standalone tracks instead of having the trk at the pv we refit with the pv - if (conf.refitITS && cis == GTrackID::ITS && !refitITSPVTrack(recoData, trkRefit, contributorsGID[cis])) { + if (mParams->refitITS && cis == GTrackID::ITS && !refitITSPVTrack(trkRefit, contributorsGID[cis])) { mTrackCounter -= cis; continue; } else { @@ -456,7 +523,7 @@ void TrackingStudySpec::doDCAStudy(o2::globaltracking::RecoContainer& recoData) }; auto trkDCA = trk; - if (!prop->propagateToDCABxByBz(pv, trkDCA, 2.f, conf.CorrType, &dcaInfo)) { + if (!prop->propagateToDCABxByBz(pv, trkDCA, 2.f, mParams->CorrType, &dcaInfo)) { mTrackCounter -= cis; ++nDCAFitsFail; continue; @@ -470,19 +537,19 @@ void TrackingStudySpec::doDCAStudy(o2::globaltracking::RecoContainer& recoData) << "dca=" << dcaInfo; if (mUseMC) { - const auto& lbl = recoData.getTrackMCLabel(contributorsGID[cis]); + const auto& lbl = mRecoData.getTrackMCLabel(contributorsGID[cis]); lbl.print(); o2::dataformats::DCA dcaInfoMC; - const auto& eve = mcReader.getMCEventHeader(lbl.getSourceID(), lbl.getEventID()); + const auto& eve = mMCReader.getMCEventHeader(lbl.getSourceID(), lbl.getEventID()); o2::dataformats::VertexBase mcEve; mcEve.setPos({(float)eve.GetX(), (float)eve.GetY(), (float)eve.GetZ()}); auto trkC = trk; - if (!prop->propagateToDCABxByBz(mcEve, trkC, 2.f, conf.CorrType, &dcaInfoMC)) { + if (!prop->propagateToDCABxByBz(mcEve, trkC, 2.f, mParams->CorrType, &dcaInfoMC)) { mTrackCounter -= cis; ++nDCAFitsFail; continue; } - const auto& mcTrk = mcReader.getTrack(lbl); + const auto& mcTrk = mMCReader.getTrack(lbl); if (mcTrk == nullptr) { LOGP(fatal, "mcTrk is null did selection fail?"); } @@ -503,21 +570,20 @@ void TrackingStudySpec::doDCAStudy(o2::globaltracking::RecoContainer& recoData) mTrackCounter.print(); } -void TrackingStudySpec::doDCARefitStudy(o2::globaltracking::RecoContainer& recoData) +void TrackingStudySpec::doDCARefitStudy() { /// analyse DCA of impact parameter for different track types while refitting the PV without the cand track LOGP(info, "Doing DCARefit study"); mTrackCounter.reset(); - const auto& conf = ITS3TrackingStudyParam::Instance(); auto prop = o2::base::Propagator::Instance(); TStopwatch sw; sw.Start(); // build track->vertex assoc. - auto pvvec = recoData.getPrimaryVertices(); - auto vtxRefs = recoData.getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs - auto nv = vtxRefs.size() - 1; // last entry is for unassigned tracks, ignore them - auto t2v = buildT2V(recoData); + auto pvvec = mRecoData.getPrimaryVertices(); + auto vtxRefs = mRecoData.getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs + auto nv = vtxRefs.size() - 1; // last entry is for unassigned tracks, ignore them + auto t2v = buildT2V(); std::vector> v2t; v2t.resize(nv); auto creator = [&](const auto& trk, GTrackID trkID, float _t0, float terr) -> bool { @@ -531,11 +597,11 @@ void TrackingStudySpec::doDCARefitStudy(o2::globaltracking::RecoContainer& recoD } // general if constexpr (isBarrelTrack()) { - if (trk.getPt() < conf.minPt || trk.getPt() > conf.maxPt) { + if (trk.getPt() < mParams->minPt || trk.getPt() > mParams->maxPt) { mTrackCounter &= trkID.getSource(); return false; } - if (std::abs(trk.getEta()) > conf.maxEta) { + if (std::abs(trk.getEta()) > mParams->maxEta) { mTrackCounter &= trkID.getSource(); return false; } @@ -543,7 +609,7 @@ void TrackingStudySpec::doDCARefitStudy(o2::globaltracking::RecoContainer& recoD mTrackCounter &= trkID.getSource(); return false; } - if (!selectTrack(trkID, recoData, mUseMC)) { + if (!selectTrack(trkID, mUseMC)) { mTrackCounter &= trkID.getSource(); return false; } @@ -551,20 +617,20 @@ void TrackingStudySpec::doDCARefitStudy(o2::globaltracking::RecoContainer& recoD v2t[t2v[trkID]].push_back(trkID); return true; }; - recoData.createTracksVariadic(creator); + mRecoData.createTracksVariadic(creator); int nDCAFits{0}, nDCAFitsFail{0}; auto& stream = (*mDBGOut) << "dcaRefit"; for (size_t iv = 0; iv < nv; ++iv) { const auto& pv = pvvec[iv]; const auto& trkIDs = v2t[iv]; - if (trkIDs.size() - 1 < conf.minPVCont) { + if (trkIDs.size() - 1 < mParams->minPVCont) { continue; } std::vector trks; trks.reserve(trkIDs.size()); for (const auto& trkID : trkIDs) { - trks.push_back(recoData.getTrackParam(trkID)); + trks.push_back(mRecoData.getTrackParam(trkID)); } if (!mVertexer.prepareVertexRefit(trks, pv)) { @@ -585,14 +651,14 @@ void TrackingStudySpec::doDCARefitStudy(o2::globaltracking::RecoContainer& recoD // check DCA both for refitted and original PV o2::dataformats::DCA dcaInfo; auto trkC = trks[it]; - if (!prop->propagateToDCABxByBz(pv, trkC, 2.f, conf.CorrType, &dcaInfo)) { + if (!prop->propagateToDCABxByBz(pv, trkC, 2.f, mParams->CorrType, &dcaInfo)) { mTrackCounter -= trkIDs[it].getSource(); ++nDCAFitsFail; continue; } o2::dataformats::DCA dcaInfoRefit; auto trkCRefit = trks[it]; - if (!prop->propagateToDCABxByBz(pv, trkCRefit, 2.f, conf.CorrType, &dcaInfoRefit)) { + if (!prop->propagateToDCABxByBz(pv, trkCRefit, 2.f, mParams->CorrType, &dcaInfoRefit)) { mTrackCounter -= trkIDs[it].getSource(); ++nDCAFitsFail; continue; @@ -606,7 +672,7 @@ void TrackingStudySpec::doDCARefitStudy(o2::globaltracking::RecoContainer& recoD << "trkAtPVRefit=" << trkC << "dcaRefit=" << dcaInfoRefit; if (mUseMC) { - const auto& mcTrk = mcReader.getTrack(recoData.getTrackMCLabel(trkIDs[it])); + const auto& mcTrk = mMCReader.getTrack(mRecoData.getTrackMCLabel(trkIDs[it])); if (mcTrk == nullptr) { LOGP(fatal, "mcTrk is null did selection fail?"); } @@ -622,7 +688,7 @@ void TrackingStudySpec::doDCARefitStudy(o2::globaltracking::RecoContainer& recoD mTrackCounter.print(); } -void TrackingStudySpec::doPullStudy(o2::globaltracking::RecoContainer& recoData) +void TrackingStudySpec::doPullStudy() { // check track pulls compared to mc generation LOGP(info, "Doing Pull study"); @@ -631,21 +697,20 @@ void TrackingStudySpec::doPullStudy(o2::globaltracking::RecoContainer& recoData) sw.Start(); int nPulls{0}, nPullsFail{0}; auto prop = o2::base::Propagator::Instance(); - const auto& conf = ITS3TrackingStudyParam::Instance(); auto checkInTrack = [&](GTrackID trkID) { - if (!selectTrack(trkID, recoData)) { + if (!selectTrack(trkID)) { mTrackCounter &= trkID.getSource(); return; } - const auto mcTrk = mcReader.getTrack(recoData.getTrackMCLabel(trkID)); + const auto mcTrk = mMCReader.getTrack(mRecoData.getTrackMCLabel(trkID)); if (!mcTrk) { return; } - auto trk = recoData.getTrackParam(trkID); + auto trk = mRecoData.getTrackParam(trkID); // for ITS standalone tracks we add the PV as an additional measurement point - if (conf.refitITS && trkID.getSource() == GTrackID::ITS && !refitITSPVTrack(recoData, trk, trkID)) { + if (mParams->refitITS && trkID.getSource() == GTrackID::ITS && !refitITSPVTrack(trk, trkID)) { mTrackCounter -= trkID.getSource(); ++nPullsFail; return; @@ -666,8 +731,8 @@ void TrackingStudySpec::doPullStudy(o2::globaltracking::RecoContainer& recoData) ++nPullsFail; return; } - const auto contTrk = recoData.getSingleDetectorRefs(trkID); - const auto& itsTrk = recoData.getITSTrack(contTrk[GTrackID::ITS]); + const auto contTrk = mRecoData.getSingleDetectorRefs(trkID); + const auto& itsTrk = mRecoData.getITSTrack(contTrk[GTrackID::ITS]); (*mDBGOut) << "pull" @@ -681,19 +746,19 @@ void TrackingStudySpec::doPullStudy(o2::globaltracking::RecoContainer& recoData) mTrackCounter += trkID.getSource(); }; - for (size_t iTrk{0}; iTrk < recoData.getITSTracks().size(); ++iTrk) { + for (size_t iTrk{0}; iTrk < mRecoData.getITSTracks().size(); ++iTrk) { checkInTrack(GTrackID(iTrk, GTrackID::ITS)); } - for (size_t iTrk{0}; iTrk < recoData.getTPCITSTracks().size(); ++iTrk) { + for (size_t iTrk{0}; iTrk < mRecoData.getTPCITSTracks().size(); ++iTrk) { checkInTrack(GTrackID(iTrk, GTrackID::ITSTPC)); } - for (size_t iTrk{0}; iTrk < recoData.getITSTPCTRDTracksMCLabels().size(); ++iTrk) { + for (size_t iTrk{0}; iTrk < mRecoData.getITSTPCTRDTracksMCLabels().size(); ++iTrk) { checkInTrack(GTrackID(iTrk, GTrackID::ITSTPCTRD)); } - for (size_t iTrk{0}; iTrk < recoData.getITSTPCTOFMatches().size(); ++iTrk) { + for (size_t iTrk{0}; iTrk < mRecoData.getITSTPCTOFMatches().size(); ++iTrk) { checkInTrack(GTrackID(iTrk, GTrackID::ITSTPCTOF)); } - for (size_t iTrk{0}; iTrk < recoData.getITSTPCTRDTOFMatches().size(); ++iTrk) { + for (size_t iTrk{0}; iTrk < mRecoData.getITSTPCTRDTOFMatches().size(); ++iTrk) { checkInTrack(GTrackID(iTrk, GTrackID::ITSTPCTRDTOF)); } sw.Stop(); @@ -701,7 +766,7 @@ void TrackingStudySpec::doPullStudy(o2::globaltracking::RecoContainer& recoData) mTrackCounter.print(); } -void TrackingStudySpec::doMCStudy(o2::globaltracking::RecoContainer& recoData) +void TrackingStudySpec::doMCStudy() { LOGP(info, "Doing MC study"); mTrackCounter.reset(); @@ -710,12 +775,12 @@ void TrackingStudySpec::doMCStudy(o2::globaltracking::RecoContainer& recoData) int nTracks{0}; const int iSrc{0}; - const int nev = mcReader.getNEvents(iSrc); + const int nev = mMCReader.getNEvents(iSrc); std::unordered_map info; LOGP(info, "** Filling particle table ... "); for (int iEve{0}; iEve < nev; ++iEve) { - const auto& mcTrks = mcReader.getTracks(iSrc, iEve); + const auto& mcTrks = mMCReader.getTracks(iSrc, iEve); for (int iTrk{0}; iTrk < mcTrks.size(); ++iTrk) { const auto& mcTrk = mcTrks[iTrk]; const auto pdg = mcTrk.GetPdgCode(); @@ -732,8 +797,8 @@ void TrackingStudySpec::doMCStudy(o2::globaltracking::RecoContainer& recoData) } } LOGP(info, "** Creating particle/clusters correspondence ... "); - const auto& clusters = recoData.getITSClusters(); - const auto& clustersMCLCont = recoData.getITSClustersMCLabels(); + const auto& clusters = mRecoData.getITSClusters(); + const auto& clustersMCLCont = mRecoData.getITSClustersMCLabels(); for (auto iCluster{0}; iCluster < clusters.size(); ++iCluster) { auto labs = clustersMCLCont->getLabels(iCluster); for (auto& lab : labs) { @@ -755,7 +820,7 @@ void TrackingStudySpec::doMCStudy(o2::globaltracking::RecoContainer& recoData) LOGP(info, "** Analysing tracks ... "); auto accountLbl = [&](const globaltracking::RecoContainer::GlobalIDSet& contributorsGID, DetID::ID det) { if (contributorsGID[det].isIndexSet()) { - const auto& lbl = recoData.getTrackMCLabel(contributorsGID[det]); + const auto& lbl = mRecoData.getTrackMCLabel(contributorsGID[det]); if (lbl.isValid()) { o2::MCCompLabel iLbl(lbl.getTrackID(), lbl.getEventID(), lbl.getSourceID()); if (info.contains(iLbl)) { @@ -776,11 +841,11 @@ void TrackingStudySpec::doMCStudy(o2::globaltracking::RecoContainer& recoData) return false; } // general - auto contributorsGID = recoData.getSingleDetectorRefs(trkID); + auto contributorsGID = mRecoData.getSingleDetectorRefs(trkID); if (!contributorsGID[GTrackID::ITS].isIndexSet()) { // we need of course ITS return false; } - const auto& gLbl = recoData.getTrackMCLabel(trkID); + const auto& gLbl = mRecoData.getTrackMCLabel(trkID); if (!gLbl.isValid()) { return false; } @@ -789,7 +854,7 @@ void TrackingStudySpec::doMCStudy(o2::globaltracking::RecoContainer& recoData) return false; } auto& part = info[iLbl]; - part.recoTrack = recoData.getTrackParam(trkID); + part.recoTrack = mRecoData.getTrackParam(trkID); accountLbl(contributorsGID, DetID::ITS); accountLbl(contributorsGID, DetID::TPC); @@ -799,7 +864,7 @@ void TrackingStudySpec::doMCStudy(o2::globaltracking::RecoContainer& recoData) ++nTracks; return true; }; - recoData.createTracksVariadic(creator); + mRecoData.createTracksVariadic(creator); LOGP(info, "Streaming output to tree"); for (const auto& [_, part] : info) { @@ -812,7 +877,319 @@ void TrackingStudySpec::doMCStudy(o2::globaltracking::RecoContainer& recoData) LOGP(info, "doMCStudy: accounted {} MCParticles and {} tracks (in {:.2f} seconds)", info.size(), nTracks, sw.RealTime()); } -DataProcessorSpec getTrackingStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC) +void TrackingStudySpec::doResidStudy() +{ + LOGP(info, "Doing residual study"); + const auto geom = o2::its::GeometryTGeo::Instance(); + const auto prop = o2::base::Propagator::Instance(); + const float bz = prop->getNominalBz(); + + int goodRefit{0}, notPassedSel{0}, fitFail{0}; + + auto doRefits = [&](const o2::its::TrackITS& iTrack, const o2::MCCompLabel& lbl) { + std::array cl; + std::array clArr{nullptr}; + if (mParams->addPVAsCluster) { + const auto& eve = mMCReader.getMCEventHeader(lbl.getSourceID(), lbl.getEventID()); + dataformats::VertexBase pv; + auto trFitOut = iTrack.getParamIn(); + pv.setXYZ(eve.GetX(), eve.GetY(), eve.GetZ()); + if (!prop->propagateToDCA(pv, trFitOut, bz, base::Propagator::MAX_STEP, mParams->CorrType)) { + return; + } + pv.setSigmaX(20e-4f); + pv.setSigmaY(20e-4f); + pv.setSigmaZ(20e-4f); + float cosAlp = NAN, sinAlp = NAN; + o2::math_utils::sincos(trFitOut.getAlpha(), sinAlp, cosAlp); + cl[0].alpha = trFitOut.getAlpha(); + cl[0].setXYZ((pv.getX() * cosAlp) + (pv.getY() * sinAlp), (-pv.getX() * sinAlp) + (pv.getY() * cosAlp), pv.getZ()); + cl[0].setSigmaY2(0.5f * (pv.getSigmaX2() + pv.getSigmaY2())); + cl[0].setSigmaZ2(pv.getSigmaZ2()); + cl[0].setSensorID(-1); + clArr[0] = &cl[0]; + } + + // collect track clusters into layer slots + int nCl = iTrack.getNClusters(); + for (int i = 0; i < nCl; i++) { + const auto& curClu = mITScl[mITSclRef[iTrack.getClusterEntry(i)]]; + int sens = curClu.getSensorID(); + int llr = geom->getLayer(sens); + if (clArr[1 + llr]) { + LOGP(fatal, "Cluster at lr {} was already assigned, old sens {}, new sens {}", llr, clArr[1 + llr]->getSensorID(), sens); + } + clArr[1 + llr] = &curClu; + } + + std::array extrapOut, extrapInw; + float chi2{0}; + if (!align::doBidirRefit(iTrack, clArr, extrapOut, extrapInw, chi2, mParams->useStableRef, mParams->CorrType)) { + ++fitFail; + return; + } + + for (int i = 0; i <= 7; i++) { + if (clArr[i]) { + const auto tInt = align::interpolateTrackParCov(extrapInw[i], extrapOut[i]); + if (!tInt.isValid()) { + continue; + } + auto phi = i == 0 ? tInt.getPhi() : tInt.getPhiPos(); + o2::math_utils::bringTo02Pi(phi); + (*mDBGOut) << "res" + << "dYInt=" << clArr[i]->getY() - tInt.getY() + << "dZInt=" << clArr[i]->getZ() - tInt.getZ() + << "dYIn=" << clArr[i]->getY() - extrapInw[i].getY() + << "dZIn=" << clArr[i]->getZ() - extrapInw[i].getZ() + << "dYOut=" << clArr[i]->getY() - extrapOut[i].getY() + << "dZOut=" << clArr[i]->getZ() - extrapOut[i].getZ() + << "chi2=" << chi2 + << "clY=" << clArr[i]->getY() + << "clZ=" << clArr[i]->getZ() + << "clX=" << clArr[i]->getX() + << "alpha=" << clArr[i]->alpha + << "sens=" << clArr[i]->getSensorID() + << "phi=" << phi + << "pt=" << tInt.getPt() + << "chip=" << constants::detID::getSensorID(clArr[i]->getSensorID()) + << "lay=" << i - 1 + << "\n"; + } + } + ++goodRefit; + }; + + const auto itsTracks = mRecoData.getITSTracks(); + const auto itsMC = mRecoData.getITSTracksMCLabels(); + for (size_t iTrk{0}; iTrk < itsTracks.size(); ++iTrk) { + const auto& iTrack = itsTracks[iTrk]; + const auto& lbl = itsMC[iTrk]; + const auto& mc = mMCReader.getTrack(lbl); + if (std::abs(iTrack.getEta()) > mParams->maxEta || iTrack.getChi2() > mParams->maxChi2 || iTrack.getNClusters() < mParams->minITSCls || iTrack.getPt() < mParams->minPt || !lbl.isCorrect() || !mc->isPrimary()) { + ++notPassedSel; + continue; + } + doRefits(iTrack, lbl); + } + + LOGP(info, "\trefitted {} out of {} tracks ({} !sel, {} !fit)", goodRefit, itsTracks.size(), notPassedSel, fitFail); +} + +void TrackingStudySpec::doMisalignmentStudy() +{ + LOGP(info, "Doing misalignment study"); + const auto prop = o2::base::Propagator::Instance(); + const auto geom = o2::its::GeometryTGeo::Instance(); + + int goodRefit{0}, notPassedSel{0}, fitFail{0}, fitFailMis{0}; + + // compute normalized (u,v) in [-1,1] from global position + auto computeUV = [](float gloX, float gloY, float gloZ, int sensorID, float radius) -> std::pair { + const bool isTop = sensorID % 2 == 0; + const double phi = o2::math_utils::to02Pi(std::atan2(gloY, gloX)); + const double phiBorder1 = o2::math_utils::to02Pi(((isTop ? 0. : 1.) * TMath::Pi()) + std::asin(constants::equatorialGap / 2. / radius)); + const double phiBorder2 = o2::math_utils::to02Pi(((isTop ? 1. : 2.) * TMath::Pi()) - std::asin(constants::equatorialGap / 2. / radius)); + const double u = (((phi - phiBorder1) * 2.) / (phiBorder2 - phiBorder1)) - 1.; + const double v = ((2. * gloZ + constants::segment::lengthSensitive) / constants::segment::lengthSensitive) - 1.; + return {u, v}; + }; + + float chi2{0}; + auto writeTree = [&](const char* treeName, + const std::array& clArr, + const std::array& extrapOut, + const std::array& extrapInw, + const o2::MCCompLabel& lbl) { + for (int i = 0; i <= 7; i++) { + if (!clArr[i]) { + continue; + } + // interpolated result + auto tInt = align::interpolateTrackParCov(extrapInw[i], extrapOut[i]); + if (!tInt.isValid()) { + continue; + } + float dY = clArr[i]->getY() - tInt.getY(); + float dZ = clArr[i]->getZ() - tInt.getZ(); + // MC truth at same (alpha, x) + o2::track::TrackPar mcTrkAtX; + const auto mcTrk = mMCReader.getTrack(lbl); + if (mcTrk) { + std::array xyz{(float)mcTrk->GetStartVertexCoordinatesX(), (float)mcTrk->GetStartVertexCoordinatesY(), (float)mcTrk->GetStartVertexCoordinatesZ()}; + std::array pxyz{(float)mcTrk->GetStartVertexMomentumX(), (float)mcTrk->GetStartVertexMomentumY(), (float)mcTrk->GetStartVertexMomentumZ()}; + TParticlePDG* pPDG = TDatabasePDG::Instance()->GetParticle(mcTrk->GetPdgCode()); + if (pPDG) { + mcTrkAtX = o2::track::TrackPar(xyz, pxyz, TMath::Nint(pPDG->Charge() / 3), false); + if (mcTrkAtX.rotate(tInt.getAlpha()) && prop->PropagateToXBxByBz(mcTrkAtX, tInt.getX())) { + auto phi = i == 0 ? tInt.getPhi() : tInt.getPhiPos(); + o2::math_utils::bringTo02Pi(phi); + (*mDBGOut) << treeName + << "trk=" << tInt + << "mcTrk=" << mcTrkAtX + << "chi2=" << chi2 + << "dY=" << dY + << "dZ=" << dZ + << "phi=" << phi + << "eta=" << tInt.getEta() + << "lay=" << i - 1 + << "\n"; + } + } + } + } + }; + + const auto itsTracks = mRecoData.getITSTracks(); + const auto itsMC = mRecoData.getITSTracksMCLabels(); + for (size_t iTrk{0}; iTrk < itsTracks.size(); ++iTrk) { + const auto& iTrack = itsTracks[iTrk]; + if (std::abs(iTrack.getEta()) > mParams->maxEta || iTrack.getChi2() > mParams->maxChi2 || iTrack.getNClusters() < mParams->minITSCls || iTrack.getPt() < mParams->minPt) { + ++notPassedSel; + continue; + } + const auto& lbl = itsMC[iTrk]; + if (!lbl.isCorrect() || !lbl.isValid()) { + ++notPassedSel; + continue; + } + const auto& mc = mMCReader.getTrack(lbl); + if (!mc->isPrimary()) { + ++notPassedSel; + continue; + } + + // ideal clusters + std::array cl; + std::array clArr{nullptr}; + if (mParams->addPVAsCluster) { + const auto& eve = mMCReader.getMCEventHeader(lbl.getSourceID(), lbl.getEventID()); + dataformats::VertexBase pv; + auto trFitOut = iTrack.getParamIn(); + pv.setXYZ(eve.GetX(), eve.GetY(), eve.GetZ()); + if (!prop->propagateToDCA(pv, trFitOut, prop->getNominalBz(), base::Propagator::MAX_STEP, mParams->CorrType)) { + return; + } + pv.setSigmaX(20e-4f); + pv.setSigmaY(20e-4f); + pv.setSigmaZ(20e-4f); + float cosAlp = NAN, sinAlp = NAN; + o2::math_utils::sincos(trFitOut.getAlpha(), sinAlp, cosAlp); + cl[0].alpha = trFitOut.getAlpha(); + cl[0].setXYZ((pv.getX() * cosAlp) + (pv.getY() * sinAlp), (-pv.getX() * sinAlp) + (pv.getY() * cosAlp), pv.getZ()); + cl[0].setSigmaY2(0.5f * (pv.getSigmaX2() + pv.getSigmaY2())); + cl[0].setSigmaZ2(pv.getSigmaZ2()); + cl[0].setSensorID(-1); + clArr[0] = &cl[0]; + } + + // collect track clusters into layer slots + int nCl = iTrack.getNClusters(); + for (int i = 0; i < nCl; i++) { + const auto& curClu = mITScl[mITSclRef[iTrack.getClusterEntry(i)]]; + int sens = curClu.getSensorID(); + int llr = geom->getLayer(sens); + if (clArr[1 + llr]) { + LOGP(fatal, "Cluster at lr {} was already assigned, old sens {}, new sens {}", llr, clArr[1 + llr]->getSensorID(), sens); + } + clArr[1 + llr] = &curClu; + } + std::array extrapOut, extrapInw; + chi2 = 0; + if (!align::doBidirRefit(iTrack, clArr, extrapOut, extrapInw, chi2, mParams->useStableRef, mParams->CorrType)) { + ++fitFail; + continue; + } + writeTree("idealRes", clArr, extrapOut, extrapInw, lbl); + + // Propagate MC truth to each cluster's (alpha, x) to get true track direction, + // then compute dy = dydx*h(u,v), dz = dzdx*h(u,v) - first Newton step. + const auto mcTrk = mMCReader.getTrack(lbl); + if (!mcTrk) { + continue; + } + std::array xyz{(float)mcTrk->GetStartVertexCoordinatesX(), (float)mcTrk->GetStartVertexCoordinatesY(), (float)mcTrk->GetStartVertexCoordinatesZ()}; + std::array pxyz{(float)mcTrk->GetStartVertexMomentumX(), (float)mcTrk->GetStartVertexMomentumY(), (float)mcTrk->GetStartVertexMomentumZ()}; + TParticlePDG* pPDG = TDatabasePDG::Instance()->GetParticle(mcTrk->GetPdgCode()); + if (!pPDG) { + continue; + } + o2::track::TrackPar mcPar(xyz, pxyz, TMath::Nint(pPDG->Charge() / 3), false); + + std::array misClArr; // shifted copies for up to 3 IT3 layers + std::array clArrMis{}; + for (int i = 0; i <= 7; i++) { + clArrMis[i] = clArr[i]; // PV and OB clusters stay the same + } + for (int iLay = 0; iLay < 3; ++iLay) { + if (!clArr[1 + iLay]) { + continue; + } + const auto& orig = *clArr[1 + iLay]; + const int sens = orig.getSensorID(); + if (!constants::detID::isDetITS3(sens)) { + continue; + } + const int sensorID = constants::detID::getSensorID(sens); + const int layerID = constants::detID::getDetID2Layer(sens); + + // compute h(u,v) at this cluster + const float r = orig.getX(); + const float gloX = r * std::cos(orig.alpha); + const float gloY = r * std::sin(orig.alpha); + const float gloZ = orig.getZ(); + auto [u, v] = computeUV(gloX, gloY, gloZ, sensorID, constants::radii[layerID]); + const double h = mDeformations[sensorID](u, v); + + // propagate MC track to cluster's tracking frame to get true slopes + auto mcAtCl = mcPar; + if (!mcAtCl.rotate(orig.alpha) || !prop->PropagateToXBxByBz(mcAtCl, orig.getX())) { + clArrMis[1 + iLay] = nullptr; // can't compute slopes -> drop cluster + continue; + } + const float snp = mcAtCl.getSnp(); + const float tgl = mcAtCl.getTgl(); + const float csci = 1.f / std::sqrt(1.f - (snp * snp)); + const float dydx = snp * csci; + const float dzdx = tgl * csci; + const float dy = dydx * static_cast(h); + const float dz = dzdx * static_cast(h); + + // check if shifted position is still within sensor acceptance + const float newGloY = (r * std::sin(orig.alpha)) + (dy * std::cos(orig.alpha)); + const float newGloX = (r * std::cos(orig.alpha)) - (dy * std::sin(orig.alpha)); + const float newGloZ = gloZ + dz; + auto [uNew, vNew] = computeUV(newGloX, newGloY, newGloZ, sensorID, constants::radii[layerID]); + if (std::abs(uNew) > 1. || std::abs(vNew) > 1.) { + clArrMis[1 + iLay] = nullptr; // shifted outside acceptance + continue; + } + + // create shifted copy: keep x=r (nominal), shift y and z + misClArr[iLay] = orig; + misClArr[iLay].setY(orig.getY() + dy); + misClArr[iLay].setZ(orig.getZ() + dz); + misClArr[iLay].setSigmaY2(orig.getSigmaY2() + (mParams->misAlgExtCY[sensorID] * mParams->misAlgExtCY[sensorID])); + misClArr[iLay].setSigmaZ2(orig.getSigmaZ2() + (mParams->misAlgExtCZ[sensorID] * mParams->misAlgExtCZ[sensorID])); + clArrMis[1 + iLay] = &misClArr[iLay]; + } + + // refit with shifted clusters + chi2 = 0; + if (!align::doBidirRefit(iTrack, clArrMis, extrapOut, extrapInw, chi2, mParams->useStableRef, mParams->CorrType)) { + ++fitFailMis; + ++goodRefit; // ideal still succeeded + continue; + } + writeTree("misRes", clArrMis, extrapOut, extrapInw, lbl); + + ++goodRefit; + } + + LOGP(info, "\tdoMisalignmentStudy: refitted {} out of {} tracks ({} !sel, {} !fit, {} !fitMis)", goodRefit, itsTracks.size(), notPassedSel, fitFail, fitFailMis); +} + +DataProcessorSpec getTrackingStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC, bool withPV) { std::vector outputs; auto dataRequest = std::make_shared(); @@ -820,7 +1197,9 @@ DataProcessorSpec getTrackingStudySpec(GTrackID::mask_t srcTracks, GTrackID::mas dataRequest->requestTracks(srcTracks, useMC); dataRequest->requestIT3Clusters(useMC); dataRequest->requestClusters(srcClusters, useMC); - dataRequest->requestPrimaryVertices(useMC); + if (withPV) { + dataRequest->requestPrimaryVertices(useMC); + } auto ggRequest = std::make_shared(false, // orbitResetTime true, // GRPECS=true true, // GRPLHCIF diff --git a/Detectors/Upgrades/ITS3/study/src/its3-tracking-study-workflow.cxx b/Detectors/Upgrades/ITS3/study/src/its3-tracking-study-workflow.cxx index e0a0aea1c368a..482ef2bb71e1d 100644 --- a/Detectors/Upgrades/ITS3/study/src/its3-tracking-study-workflow.cxx +++ b/Detectors/Upgrades/ITS3/study/src/its3-tracking-study-workflow.cxx @@ -39,6 +39,7 @@ void customize(std::vector& workflowOptions) {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation"}}, {"track-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of track sources to use"}}, {"cluster-sources", VariantType::String, "ITS,TRD,TOF", {"comma-separated list of cluster sources to use"}}, + {"without-pv", VariantType::Bool, false, {"do not use the PV as an additional fit point"}}, {"disable-root-input", VariantType::Bool, false, {"disable root-files input reader"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; o2::raw::HBFUtilsInitializer::addConfigOption(options); @@ -58,14 +59,17 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); auto useMC = !configcontext.options().get("disable-mc"); + auto usePV = !configcontext.options().get("without-pv"); GID::mask_t srcTrc = allowedSourcesTrc & GID::getSourcesMask(configcontext.options().get("track-sources")); GID::mask_t srcCls = allowedSourcesClus & GID::getSourcesMask(configcontext.options().get("cluster-sources")); o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC); - o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); + if (usePV) { + o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); + } - specs.emplace_back(o2::its3::study::getTrackingStudySpec(srcTrc, srcCls, useMC)); + specs.emplace_back(o2::its3::study::getTrackingStudySpec(srcTrc, srcCls, useMC, usePV)); o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); diff --git a/Steer/DigitizerWorkflow/CMakeLists.txt b/Steer/DigitizerWorkflow/CMakeLists.txt index 6b31550c83636..10e8dc2b13995 100644 --- a/Steer/DigitizerWorkflow/CMakeLists.txt +++ b/Steer/DigitizerWorkflow/CMakeLists.txt @@ -68,7 +68,6 @@ o2_add_executable(digitizer-workflow O2::DetectorsRaw $<$:O2::ITS3Simulation> $<$:O2::ITS3Workflow> - $<$:O2::ITS3Align> $<$:O2::TRKSimulation> $<$:O2::TRKWorkflow>) diff --git a/Steer/DigitizerWorkflow/src/ITS3DigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/ITS3DigitizerSpec.cxx index 639203bdd6d38..60a1660288b9d 100644 --- a/Steer/DigitizerWorkflow/src/ITS3DigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/ITS3DigitizerSpec.cxx @@ -31,7 +31,6 @@ #include "ITSMFTBase/DPLAlpideParam.h" #include "ITSBase/GeometryTGeo.h" #include "ITS3Base/ITS3Params.h" -#include "ITS3Align/MisalignmentManager.h" #include #include @@ -79,11 +78,6 @@ class ITS3DPLDigitizerTask : BaseDPLDigitizer } updateTimeDependentParams(pc); - if (ITS3Params::Instance().applyMisalignmentHits) { - LOGP(info, "Applying misalignment to ITS3 Hits"); - o2::its3::align::MisalignmentManager::misalignHits(); - } - // read collision context from input auto context = pc.inputs().get("collisioncontext"); context->initSimChains(mID, mSimChains); From 5ca454afeb0f9689f14dd46c252e7965ddfd6bf5 Mon Sep 17 00:00:00 2001 From: shahoian Date: Fri, 27 Mar 2026 13:46:12 +0100 Subject: [PATCH 003/285] Proter time-slice calibration from stray TFs The TFs with bogus orbit, creation time or orbit(); static int errCount = 0; - if (tinfo.firstTForbit == -1U || tinfo.creation == -1) { - if (errCount++ < 5) { - LOGP(warn, "Ignoring dummy input with orbit {} and creation time {} in fillTFIDInfo", tinfo.firstTForbit, tinfo.creation); - } - return; + ti.fill(tinfo.firstTForbit, tinfo.tfCounter, tinfo.runNumber, tinfo.timeslice, tinfo.creation); + if (ti.discard && errCount++ < 5) { + LOGP(warn, "Bad input with orbit {}, TFcounter {} and creation time {} in fillTFIDInfo", tinfo.firstTForbit, tinfo.tfCounter, tinfo.creation); } - ti.firstTForbit = tinfo.firstTForbit; - ti.tfCounter = tinfo.tfCounter; - ti.runNumber = tinfo.runNumber; - ti.startTime = tinfo.timeslice; - ti.creation = tinfo.creation; + return; } diff --git a/Detectors/Calibration/include/DetectorsCalibration/TimeSlotCalibration.h b/Detectors/Calibration/include/DetectorsCalibration/TimeSlotCalibration.h index 87562afddf2ca..a8be3644619a4 100644 --- a/Detectors/Calibration/include/DetectorsCalibration/TimeSlotCalibration.h +++ b/Detectors/Calibration/include/DetectorsCalibration/TimeSlotCalibration.h @@ -281,6 +281,15 @@ template template bool TimeSlotCalibration::process(const DATA&... data) { + if (mCurrentTFInfo.discard) { + LOGP(warn, "Ignoring TF with discard flag on: Orbit {}, TFcounter {}, Run:{}, StartTime:{} CreationTime {}, ", + mCurrentTFInfo.firstTForbit, + mCurrentTFInfo.tfCounter, + mCurrentTFInfo.runNumber, + mCurrentTFInfo.startTime, + mCurrentTFInfo.creation); + return false; // ignore bad TF + } static bool firstCall = true; if (firstCall) { firstCall = false; diff --git a/Detectors/PHOS/calib/src/PHOSEnergyCalibDevice.cxx b/Detectors/PHOS/calib/src/PHOSEnergyCalibDevice.cxx index 979ca690c03e0..51956a4dbf96e 100644 --- a/Detectors/PHOS/calib/src/PHOSEnergyCalibDevice.cxx +++ b/Detectors/PHOS/calib/src/PHOSEnergyCalibDevice.cxx @@ -133,6 +133,7 @@ void PHOSEnergyCalibDevice::run(o2::framework::ProcessingContext& pc) LOG(warning) << "LHCPeriod is not available, using current month " << mLHCPeriod; } } + o2::base::TFIDInfoHelper::fillTFIDInfo(pc, mCalibrator->getCurrentTFInfo()); mCalibrator->process(tfcounter, clusters, cluelements, cluTR, mOutputDigits); fillOutputTree(); diff --git a/Detectors/PHOS/calib/src/PHOSL1phaseCalibDevice.cxx b/Detectors/PHOS/calib/src/PHOSL1phaseCalibDevice.cxx index c50a3faff4b01..baade755f2adf 100644 --- a/Detectors/PHOS/calib/src/PHOSL1phaseCalibDevice.cxx +++ b/Detectors/PHOS/calib/src/PHOSL1phaseCalibDevice.cxx @@ -30,7 +30,7 @@ void PHOSL1phaseCalibDevice::init(o2::framework::InitContext& ic) void PHOSL1phaseCalibDevice::run(o2::framework::ProcessingContext& pc) { - + o2::base::TFIDInfoHelper::fillTFIDInfo(pc, mCalibrator->getCurrentTFInfo()); o2::base::GRPGeomHelper::instance().checkUpdates(pc); auto crTime = pc.services().get().creation; if (mRunStartTime == 0 || crTime < mRunStartTime) { diff --git a/Detectors/PHOS/calib/src/PHOSRunbyrunCalibDevice.cxx b/Detectors/PHOS/calib/src/PHOSRunbyrunCalibDevice.cxx index cf767cb76c7ad..3c59ed8477940 100644 --- a/Detectors/PHOS/calib/src/PHOSRunbyrunCalibDevice.cxx +++ b/Detectors/PHOS/calib/src/PHOSRunbyrunCalibDevice.cxx @@ -54,6 +54,7 @@ void PHOSRunbyrunCalibDevice::run(o2::framework::ProcessingContext& pc) auto tfcounter = o2::header::get(pc.inputs().get("clusters").header)->tfCounter; auto clusters = pc.inputs().get>("clusters"); auto cluTR = pc.inputs().get>("cluTR"); + o2::base::TFIDInfoHelper::fillTFIDInfo(pc, mCalibrator->getCurrentTFInfo()); LOG(detail) << "Processing TF with " << clusters.size() << " clusters and " << cluTR.size() << " TriggerRecords"; mCalibrator->process(tfcounter, clusters, cluTR); } diff --git a/Detectors/PHOS/calib/src/PHOSTurnonCalibDevice.cxx b/Detectors/PHOS/calib/src/PHOSTurnonCalibDevice.cxx index 52ec8cef0b438..c2b04aea381a3 100644 --- a/Detectors/PHOS/calib/src/PHOSTurnonCalibDevice.cxx +++ b/Detectors/PHOS/calib/src/PHOSTurnonCalibDevice.cxx @@ -49,7 +49,7 @@ void PHOSTurnonCalibDevice::run(o2::framework::ProcessingContext& pc) auto cellTR = pc.inputs().get>("cellTriggerRecords"); auto clusters = pc.inputs().get>("clusters"); auto cluTR = pc.inputs().get>("clusterTriggerRecords"); - + o2::base::TFIDInfoHelper::fillTFIDInfo(pc, mCalibrator->getCurrentTFInfo()); LOG(detail) << "[PHOSTurnonCalibDevice - run] Received " << cells.size() << " cells and " << clusters.size() << " clusters, running calibration"; mCalibrator->process(tfcounter, cells, cellTR, clusters, cluTR); From 462abe14ddecb634a9bb597e3440d022d961a6b7 Mon Sep 17 00:00:00 2001 From: shahoian Date: Fri, 27 Mar 2026 13:54:38 +0100 Subject: [PATCH 004/285] Avoid premature loop termination in ITS vertexer --- Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx index 0c4ecb0b12df1..6d51f7bab5d36 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx @@ -370,7 +370,7 @@ void VertexerTraits::computeVertices(const int iteration) std::array tmpVertex{mTimeFrame->getTrackletClusters(rofId).back().getVertex()}; if (tmpVertex[0] * tmpVertex[0] + tmpVertex[1] * tmpVertex[1] > 4.f) { mTimeFrame->getTrackletClusters(rofId).pop_back(); - break; + continue; } usedTracklets[line1] = true; usedTracklets[line2] = true; From eeffa2752d17c70071bb1b656a0330ca56f293eb Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Fri, 27 Mar 2026 06:51:38 +0100 Subject: [PATCH 005/285] DPL: remove deprecated header / payload code Rest of the usecases removed. Abstract header / payload retrieval, with the idea that get_header / get_payload will work on any range of fair::mq::MessagePtrs. --- Framework/Core/include/Framework/MessageSet.h | 24 ------ Framework/Core/test/test_MessageSet.cxx | 78 +++++-------------- 2 files changed, 20 insertions(+), 82 deletions(-) diff --git a/Framework/Core/include/Framework/MessageSet.h b/Framework/Core/include/Framework/MessageSet.h index 166934238d647..323c0ad4608af 100644 --- a/Framework/Core/include/Framework/MessageSet.h +++ b/Framework/Core/include/Framework/MessageSet.h @@ -136,30 +136,6 @@ struct MessageSet { } } - fair::mq::MessagePtr& header(size_t partIndex) - { - return messages[messageMap[partIndex].position]; - } - - fair::mq::MessagePtr& payload(size_t partIndex, size_t payloadIndex = 0) - { - assert(partIndex < messageMap.size()); - assert(messageMap[partIndex].position + payloadIndex + 1 < messages.size()); - return messages[messageMap[partIndex].position + payloadIndex + 1]; - } - - fair::mq::MessagePtr const& header(size_t partIndex) const - { - return messages[messageMap[partIndex].position]; - } - - fair::mq::MessagePtr const& payload(size_t partIndex, size_t payloadIndex = 0) const - { - assert(partIndex < messageMap.size()); - assert(messageMap[partIndex].position + payloadIndex + 1 < messages.size()); - return messages[messageMap[partIndex].position + payloadIndex + 1]; - } - fair::mq::MessagePtr const& associatedHeader(size_t pos) const { return messages[messageMap[pairMap[pos].partIndex].position]; diff --git a/Framework/Core/test/test_MessageSet.cxx b/Framework/Core/test/test_MessageSet.cxx index aa7b49c1d1d3c..c6d5030cf5e33 100644 --- a/Framework/Core/test/test_MessageSet.cxx +++ b/Framework/Core/test/test_MessageSet.cxx @@ -45,12 +45,8 @@ TEST_CASE("MessageSet") REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); CHECK_THROWS((msgSet.messages | get_pair{1})); - // Validate pipe operators match old API - REQUIRE(&(msgSet.messages | get_header{0}) == &msgSet.header(0)); - REQUIRE(&(msgSet.messages | get_payload{0, 0}) == &msgSet.payload(0)); - REQUIRE((msgSet.messages | get_num_payloads{0}) == msgSet.messageMap[0].size); - REQUIRE((msgSet.messages | count_parts{}) == msgSet.messageMap.size()); - REQUIRE((msgSet.messages | count_payloads{}) == msgSet.pairMap.size()); + REQUIRE((msgSet.messages | get_num_payloads{0}) == 1); + REQUIRE((msgSet.messages | count_parts{}) == 1); } TEST_CASE("MessageSetWithFunction") @@ -76,11 +72,8 @@ TEST_CASE("MessageSetWithFunction") REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); CHECK_THROWS((msgSet.messages | get_pair{1})); - REQUIRE(&(msgSet.messages | get_header{0}) == &msgSet.header(0)); - REQUIRE(&(msgSet.messages | get_payload{0, 0}) == &msgSet.payload(0)); - REQUIRE((msgSet.messages | get_num_payloads{0}) == msgSet.messageMap[0].size); - REQUIRE((msgSet.messages | count_parts{}) == msgSet.messageMap.size()); - REQUIRE((msgSet.messages | count_payloads{}) == msgSet.pairMap.size()); + REQUIRE((msgSet.messages | get_num_payloads{0}) == 1); + REQUIRE((msgSet.messages | count_parts{}) == 1); } TEST_CASE("MessageSetWithMultipart") @@ -112,13 +105,8 @@ TEST_CASE("MessageSetWithMultipart") REQUIRE((msgSet.messages | get_pair{1}).headerIdx == 0); REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 2); CHECK_THROWS((msgSet.messages | get_pair{2})); - // Validate pipe operators match old API for multi-payload - REQUIRE(&(msgSet.messages | get_header{0}) == &msgSet.header(0)); - REQUIRE(&(msgSet.messages | get_payload{0, 0}) == &msgSet.payload(0, 0)); - REQUIRE(&(msgSet.messages | get_payload{0, 1}) == &msgSet.payload(0, 1)); - REQUIRE((msgSet.messages | get_num_payloads{0}) == msgSet.messageMap[0].size); - REQUIRE((msgSet.messages | count_parts{}) == msgSet.messageMap.size()); - REQUIRE((msgSet.messages | count_payloads{}) == msgSet.pairMap.size()); + REQUIRE((msgSet.messages | get_num_payloads{0}) == 2); + REQUIRE((msgSet.messages | count_parts{}) == 1); } TEST_CASE("MessageSetAddPartRef") @@ -190,18 +178,11 @@ TEST_CASE("MessageSetAddMultiple") REQUIRE((msgSet.messages | get_pair{2}).payloadIdx == 5); REQUIRE((msgSet.messages | get_pair{3}).headerIdx == 4); REQUIRE((msgSet.messages | get_pair{3}).payloadIdx == 6); - // Validate pipe operators match old API for mixed modes - for (size_t i = 0; i < 3; ++i) { - REQUIRE(&(msgSet.messages | get_header{i}) == &msgSet.header(i)); - REQUIRE(&(msgSet.messages | get_payload{i, 0}) == &msgSet.payload(i, 0)); - } - // Part 2 has a second payload (multi-payload with splitPayloadParts=2, splitPayloadIndex=2) - REQUIRE(&(msgSet.messages | get_payload{2, 1}) == &msgSet.payload(2, 1)); - for (size_t i = 0; i < 3; ++i) { - REQUIRE((msgSet.messages | get_num_payloads{i}) == msgSet.messageMap[i].size); - } - REQUIRE((msgSet.messages | count_parts{}) == msgSet.messageMap.size()); - REQUIRE((msgSet.messages | count_payloads{}) == msgSet.pairMap.size()); + REQUIRE((msgSet.messages | get_num_payloads{0}) == 1); + REQUIRE((msgSet.messages | get_num_payloads{1}) == 1); + REQUIRE((msgSet.messages | get_num_payloads{2}) == 2); + REQUIRE((msgSet.messages | count_parts{}) == 3); + REQUIRE((msgSet.messages | count_payloads{}) == 4); } TEST_CASE("GetHeaderPayloadOperators") @@ -251,13 +232,8 @@ TEST_CASE("GetHeaderPayloadOperators") REQUIRE(pl1.get() != nullptr); REQUIRE(pl1->GetSize() == 200); - // Validate pipe operators match old API - for (size_t i = 0; i < 2; ++i) { - REQUIRE(&(msgSet.messages | get_header{i}) == &msgSet.header(i)); - REQUIRE(&(msgSet.messages | get_payload{i, 0}) == &msgSet.payload(i, 0)); - } - REQUIRE((msgSet.messages | count_parts{}) == msgSet.messageMap.size()); - REQUIRE((msgSet.messages | count_payloads{}) == msgSet.pairMap.size()); + REQUIRE((msgSet.messages | count_parts{}) == 2); + REQUIRE((msgSet.messages | count_payloads{}) == 2); } TEST_CASE("GetHeaderPayloadMultiPayload") @@ -343,18 +319,10 @@ TEST_CASE("GetHeaderPayloadMultiPayload") // get_num_payloads for part 1 should be 3 REQUIRE((msgSet.messages | get_num_payloads{1}) == 3); - // Validate pipe operators match old API for multi-payload (header, pl, pl, pl) - REQUIRE(&(msgSet.messages | get_header{0}) == &msgSet.header(0)); - REQUIRE(&(msgSet.messages | get_header{1}) == &msgSet.header(1)); - REQUIRE(&(msgSet.messages | get_payload{0, 0}) == &msgSet.payload(0, 0)); - REQUIRE(&(msgSet.messages | get_payload{1, 0}) == &msgSet.payload(1, 0)); - REQUIRE(&(msgSet.messages | get_payload{1, 1}) == &msgSet.payload(1, 1)); - REQUIRE(&(msgSet.messages | get_payload{1, 2}) == &msgSet.payload(1, 2)); - for (size_t i = 0; i < 2; ++i) { - REQUIRE((msgSet.messages | get_num_payloads{i}) == msgSet.messageMap[i].size); - } - REQUIRE((msgSet.messages | count_parts{}) == msgSet.messageMap.size()); - REQUIRE((msgSet.messages | count_payloads{}) == msgSet.pairMap.size()); + REQUIRE((msgSet.messages | get_num_payloads{0}) == 1); + REQUIRE((msgSet.messages | get_num_payloads{1}) == 3); + REQUIRE((msgSet.messages | count_parts{}) == 2); + REQUIRE((msgSet.messages | count_payloads{}) == 4); } TEST_CASE("TraditionalSplitParts") @@ -418,14 +386,8 @@ TEST_CASE("TraditionalSplitParts") // get_num_payloads: each traditional split pair has 1 payload for (size_t i = 0; i < 3; ++i) { - REQUIRE((msgSet.messages | get_num_payloads{i}) == msgSet.messageMap[i].size); + REQUIRE((msgSet.messages | get_num_payloads{i}) == 1); } - - // Validate pipe operators match old MessageSet::header()/payload() API - for (size_t i = 0; i < 3; ++i) { - REQUIRE(&(msgSet.messages | get_header{i}) == &msgSet.header(i)); - REQUIRE(&(msgSet.messages | get_payload{i, 0}) == &msgSet.payload(i)); - } - REQUIRE((msgSet.messages | count_parts{}) == msgSet.messageMap.size()); - REQUIRE((msgSet.messages | count_payloads{}) == msgSet.pairMap.size()); + REQUIRE((msgSet.messages | count_parts{}) == 3); + REQUIRE((msgSet.messages | count_payloads{}) == 3); } From 77efa21500fdad2b21ac56559874b2f25cfd9284 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Fri, 27 Mar 2026 06:51:38 +0100 Subject: [PATCH 006/285] DPL: replace MessageSet::associateHeader / associatePayload --- Framework/Core/src/DataProcessingDevice.cxx | 5 +- Framework/Core/test/test_MessageSet.cxx | 64 +++++++++++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index 0fa70947bf18c..bb6502758a95a 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -2140,8 +2140,9 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v // sequence is the header message // - each part has one or more payload messages // - InputRecord provides all payloads as header-payload pair - auto const& headerMsg = currentSetOfInputs[i].associatedHeader(partindex); - auto const& payloadMsg = currentSetOfInputs[i].associatedPayload(partindex); + auto const indices = currentSetOfInputs[i].messages | get_pair{partindex}; + auto const& headerMsg = currentSetOfInputs[i].messages[indices.headerIdx]; + auto const& payloadMsg = currentSetOfInputs[i].messages[indices.payloadIdx]; headerptr = static_cast(headerMsg->GetData()); payloadptr = payloadMsg ? static_cast(payloadMsg->GetData()) : nullptr; payloadSize = payloadMsg ? payloadMsg->GetSize() : 0; diff --git a/Framework/Core/test/test_MessageSet.cxx b/Framework/Core/test/test_MessageSet.cxx index c6d5030cf5e33..8c9ed4a7cbf1c 100644 --- a/Framework/Core/test/test_MessageSet.cxx +++ b/Framework/Core/test/test_MessageSet.cxx @@ -47,6 +47,14 @@ TEST_CASE("MessageSet") CHECK_THROWS((msgSet.messages | get_pair{1})); REQUIRE((msgSet.messages | get_num_payloads{0}) == 1); REQUIRE((msgSet.messages | count_parts{}) == 1); + // messages: [hdr, pl] — one pair + REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); + REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); + for (size_t i = 0; i < msgSet.pairMap.size(); ++i) { + auto indices = msgSet.messages | get_pair{i}; + REQUIRE(&msgSet.messages[indices.headerIdx] == &msgSet.associatedHeader(i)); + REQUIRE(&msgSet.messages[indices.payloadIdx] == &msgSet.associatedPayload(i)); + } } TEST_CASE("MessageSetWithFunction") @@ -74,6 +82,11 @@ TEST_CASE("MessageSetWithFunction") CHECK_THROWS((msgSet.messages | get_pair{1})); REQUIRE((msgSet.messages | get_num_payloads{0}) == 1); REQUIRE((msgSet.messages | count_parts{}) == 1); + for (size_t i = 0; i < msgSet.pairMap.size(); ++i) { + auto indices = msgSet.messages | get_pair{i}; + REQUIRE(&msgSet.messages[indices.headerIdx] == &msgSet.associatedHeader(i)); + REQUIRE(&msgSet.messages[indices.payloadIdx] == &msgSet.associatedPayload(i)); + } } TEST_CASE("MessageSetWithMultipart") @@ -107,6 +120,16 @@ TEST_CASE("MessageSetWithMultipart") CHECK_THROWS((msgSet.messages | get_pair{2})); REQUIRE((msgSet.messages | get_num_payloads{0}) == 2); REQUIRE((msgSet.messages | count_parts{}) == 1); + // messages: [hdr, pl0, pl1] — one header, two payloads + REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); + REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); + REQUIRE((msgSet.messages | get_pair{1}).headerIdx == 0); + REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 2); + for (size_t i = 0; i < msgSet.pairMap.size(); ++i) { + auto indices = msgSet.messages | get_pair{i}; + REQUIRE(&msgSet.messages[indices.headerIdx] == &msgSet.associatedHeader(i)); + REQUIRE(&msgSet.messages[indices.payloadIdx] == &msgSet.associatedPayload(i)); + } } TEST_CASE("MessageSetAddPartRef") @@ -183,6 +206,11 @@ TEST_CASE("MessageSetAddMultiple") REQUIRE((msgSet.messages | get_num_payloads{2}) == 2); REQUIRE((msgSet.messages | count_parts{}) == 3); REQUIRE((msgSet.messages | count_payloads{}) == 4); + for (size_t i = 0; i < msgSet.pairMap.size(); ++i) { + auto indices = msgSet.messages | get_pair{i}; + REQUIRE(&msgSet.messages[indices.headerIdx] == &msgSet.associatedHeader(i)); + REQUIRE(&msgSet.messages[indices.payloadIdx] == &msgSet.associatedPayload(i)); + } } TEST_CASE("GetHeaderPayloadOperators") @@ -234,6 +262,16 @@ TEST_CASE("GetHeaderPayloadOperators") REQUIRE((msgSet.messages | count_parts{}) == 2); REQUIRE((msgSet.messages | count_payloads{}) == 2); + // messages: [hdr0, pl0, hdr1, pl1] — two standard pairs + REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); + REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); + REQUIRE((msgSet.messages | get_pair{1}).headerIdx == 2); + REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 3); + for (size_t i = 0; i < msgSet.pairMap.size(); ++i) { + auto indices = msgSet.messages | get_pair{i}; + REQUIRE(&msgSet.messages[indices.headerIdx] == &msgSet.associatedHeader(i)); + REQUIRE(&msgSet.messages[indices.payloadIdx] == &msgSet.associatedPayload(i)); + } } TEST_CASE("GetHeaderPayloadMultiPayload") @@ -323,6 +361,20 @@ TEST_CASE("GetHeaderPayloadMultiPayload") REQUIRE((msgSet.messages | get_num_payloads{1}) == 3); REQUIRE((msgSet.messages | count_parts{}) == 2); REQUIRE((msgSet.messages | count_payloads{}) == 4); + // messages: [hdr0, pl0, hdr1, pl1_0, pl1_1, pl1_2] + REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); + REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); + REQUIRE((msgSet.messages | get_pair{1}).headerIdx == 2); + REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 3); + REQUIRE((msgSet.messages | get_pair{2}).headerIdx == 2); + REQUIRE((msgSet.messages | get_pair{2}).payloadIdx == 4); + REQUIRE((msgSet.messages | get_pair{3}).headerIdx == 2); + REQUIRE((msgSet.messages | get_pair{3}).payloadIdx == 5); + for (size_t i = 0; i < msgSet.pairMap.size(); ++i) { + auto indices = msgSet.messages | get_pair{i}; + REQUIRE(&msgSet.messages[indices.headerIdx] == &msgSet.associatedHeader(i)); + REQUIRE(&msgSet.messages[indices.payloadIdx] == &msgSet.associatedPayload(i)); + } } TEST_CASE("TraditionalSplitParts") @@ -390,4 +442,16 @@ TEST_CASE("TraditionalSplitParts") } REQUIRE((msgSet.messages | count_parts{}) == 3); REQUIRE((msgSet.messages | count_payloads{}) == 3); + // messages: [hdr0, pl0, hdr1, pl1, hdr2, pl2] — three traditional split pairs + REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); + REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); + REQUIRE((msgSet.messages | get_pair{1}).headerIdx == 2); + REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 3); + REQUIRE((msgSet.messages | get_pair{2}).headerIdx == 4); + REQUIRE((msgSet.messages | get_pair{2}).payloadIdx == 5); + for (size_t i = 0; i < msgSet.pairMap.size(); ++i) { + auto indices = msgSet.messages | get_pair{i}; + REQUIRE(&msgSet.messages[indices.headerIdx] == &msgSet.associatedHeader(i)); + REQUIRE(&msgSet.messages[indices.payloadIdx] == &msgSet.associatedPayload(i)); + } } From 389aaf1627773d27a3fbfcfdbab8be6932f4efa0 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 25 Mar 2026 14:56:52 +0100 Subject: [PATCH 007/285] DPL: treat --ccdb-fetchers like --readers --- Framework/Core/src/runDataProcessing.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/Framework/Core/src/runDataProcessing.cxx b/Framework/Core/src/runDataProcessing.cxx index 815fce47544d0..98cbf70370c3d 100644 --- a/Framework/Core/src/runDataProcessing.cxx +++ b/Framework/Core/src/runDataProcessing.cxx @@ -2048,6 +2048,7 @@ int runStateMachine(DataProcessorSpecs const& workflow, "--driver-client-backend", "--fairmq-ipc-prefix", "--readers", + "--ccdb-fetchers", "--resources-monitoring", "--resources-monitoring-file", "--resources-monitoring-dump-interval", From 2fd34a0af1eef89c33571e21b4d9ad7f3c87a92b Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sat, 28 Mar 2026 14:57:49 +0100 Subject: [PATCH 008/285] DPL: exponential back-off for missing resources. --- Framework/Core/src/DataProcessingDevice.cxx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index bb6502758a95a..31b7b02172af7 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -1377,6 +1377,7 @@ void DataProcessingDevice::Run() std::atomic numberOfUnscheduledSinceLastScheduled = 0; std::atomic numberOfUnscheduled = 0; std::atomic numberOfScheduled = 0; + std::atomic nextWarnAt = 1; }; static SchedulingStats schedulingStats; O2_SIGNPOST_ID_GENERATE(sid, scheduling); @@ -1387,6 +1388,7 @@ void DataProcessingDevice::Run() schedulingStats.lastScheduled = uv_now(state.loop); schedulingStats.numberOfScheduled++; schedulingStats.numberOfUnscheduledSinceLastScheduled = 0; + schedulingStats.nextWarnAt = 1; O2_SIGNPOST_EVENT_EMIT(scheduling, sid, "Run", "Enough resources to schedule computation on stream %d", streamRef.index); if (dplEnableMultithreding) [[unlikely]] { stream.task = &handle; @@ -1396,12 +1398,12 @@ void DataProcessingDevice::Run() run_completion(&handle, 0); } } else { - if (schedulingStats.numberOfUnscheduledSinceLastScheduled > 100 || - (uv_now(state.loop) - schedulingStats.lastScheduled) > 30000) { + if (schedulingStats.numberOfUnscheduledSinceLastScheduled >= schedulingStats.nextWarnAt) { O2_SIGNPOST_EVENT_EMIT_WARN(scheduling, sid, "Run", "Not enough resources to schedule computation. %zu skipped so far. Last scheduled at %zu. Data is not lost and it will be scheduled again.", schedulingStats.numberOfUnscheduledSinceLastScheduled.load(), schedulingStats.lastScheduled.load()); + schedulingStats.nextWarnAt = schedulingStats.nextWarnAt * 2; } else { O2_SIGNPOST_EVENT_EMIT(scheduling, sid, "Run", "Not enough resources to schedule computation. %zu skipped so far. Last scheduled at %zu. Data is not lost and it will be scheduled again.", From db1ede319f113e56c5fd0fb4131ac1ee276f1b52 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sat, 28 Mar 2026 22:49:56 +0100 Subject: [PATCH 009/285] Revert "DPL: Better detection for injected workflows (fixed) (#15202)" This reverts commit 87b9775293c9734b0be767feb5915e614560a05c. --- Framework/Core/src/ArrowSupport.cxx | 8 +-- Framework/Core/src/WorkflowHelpers.cxx | 15 +--- run/o2sim_hepmc_publisher.cxx | 94 +++++++++++++------------- run/o2sim_kine_publisher.cxx | 3 +- run/o2sim_mctracks_to_aod.cxx | 12 ++-- 5 files changed, 58 insertions(+), 74 deletions(-) diff --git a/Framework/Core/src/ArrowSupport.cxx b/Framework/Core/src/ArrowSupport.cxx index b701ba5f8e01c..c5cc021a53478 100644 --- a/Framework/Core/src/ArrowSupport.cxx +++ b/Framework/Core/src/ArrowSupport.cxx @@ -680,12 +680,8 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() workflow.erase(reader); } else { // load reader algorithm before deployment - auto tfnsource = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { - return !spec.name.starts_with("internal-dpl-aod-reader") && std::ranges::any_of(spec.outputs, [](OutputSpec const& output) { - return DataSpecUtils::match(output, "TFN", "TFNumber", 0); - }); - }); - if (tfnsource == workflow.end()) { // add normal reader algorithm only if no on-the-fly generator is injected + auto mctracks2aod = std::find_if(workflow.begin(), workflow.end(), [](auto const& x) { return x.name == "mctracks-to-aod"; }); + if (mctracks2aod == workflow.end()) { // add normal reader algorithm only if no on-the-fly generator is injected reader->algorithm = CommonDataProcessors::wrapWithTimesliceConsumption(PluginManager::loadAlgorithmFromPlugin("O2FrameworkAnalysisSupport", "ROOTFileReader", ctx)); } // otherwise the algorithm was set in injectServiceDevices } diff --git a/Framework/Core/src/WorkflowHelpers.cxx b/Framework/Core/src/WorkflowHelpers.cxx index 2ef3df9426fde..abe566e239618 100644 --- a/Framework/Core/src/WorkflowHelpers.cxx +++ b/Framework/Core/src/WorkflowHelpers.cxx @@ -411,17 +411,13 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext // add the reader if (aodReader.outputs.empty() == false) { - auto tfnsource = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { - return std::ranges::any_of(spec.outputs, [](OutputSpec const& output) { - return DataSpecUtils::match(output, "TFN", "TFNumber", 0); - }); - }); - if (tfnsource == workflow.end()) { + auto mctracks2aod = std::ranges::find_if(workflow, [](auto const& x) { return x.name == "mctracks-to-aod"; }); + if (mctracks2aod == workflow.end()) { // add normal reader aodReader.outputs.emplace_back(OutputSpec{"TFN", "TFNumber"}); aodReader.outputs.emplace_back(OutputSpec{"TFF", "TFFilename"}); } else { - // AODs are being injected the tfnsource is the entry point, add error-handler reader + // AODs are being injected on-the-fly, add error-handler reader aodReader.algorithm = AlgorithmSpec{ adaptStateful( [](DeviceSpec const& spec) { @@ -704,11 +700,6 @@ void WorkflowHelpers::injectAODWriter(WorkflowSpec& workflow, ConfigContext cons return DataSpecUtils::partialMatch(spec, o2::header::DataOrigin("TFN")); }); dec.isDangling[std::distance(dec.outputsInputs.begin(), it)] = false; - - it = std::find_if(dec.outputsInputs.begin(), dec.outputsInputs.end(), [](InputSpec const& spec) -> bool { - return DataSpecUtils::partialMatch(spec, o2::header::DataOrigin("TFF")); - }); - dec.isDangling[std::distance(dec.outputsInputs.begin(), it)] = false; } } diff --git a/run/o2sim_hepmc_publisher.cxx b/run/o2sim_hepmc_publisher.cxx index f255b4a3a4f62..bf40abacb134f 100644 --- a/run/o2sim_hepmc_publisher.cxx +++ b/run/o2sim_hepmc_publisher.cxx @@ -37,9 +37,7 @@ struct O2simHepmcPublisher { int tfCounter = 0; std::shared_ptr hepMCReader; bool eos = false; - - std::vector*> mctracks_vector; - std::vector mcheader_vector; + std::vector mcTracks; void init(o2::framework::InitContext& /*ic*/) { @@ -52,19 +50,13 @@ struct O2simHepmcPublisher { LOGP(fatal, "Cannot open HEPMC kine file {}", (std::string)hepmcFileName); } // allocate the memory upfront to prevent reallocations later - mctracks_vector.reserve(aggregate); - mcheader_vector.reserve(aggregate); + mcTracks.reserve(1e3 * aggregate); } void run(o2::framework::ProcessingContext& pc) { HepMC3::GenEvent event; - auto batch = maxEvents > 0 ? std::min((int)aggregate, (int)maxEvents - eventCounter) : (int)aggregate; - for (auto i = 0; i < batch; ++i) { - mctracks_vector.push_back(&pc.outputs().make>(Output{"MC", "MCTRACKS", 0})); - auto& mctracks = mctracks_vector.back(); - mcheader_vector.push_back(&pc.outputs().make(Output{"MC", "MCHEADER", 0})); - auto& mcheader = mcheader_vector.back(); + for (auto i = 0; i < (int)aggregate; ++i) { // read next entry hepMCReader->read_event(event); if (hepMCReader->failed()) { @@ -74,60 +66,61 @@ struct O2simHepmcPublisher { } // create O2 MCHeader and MCtracks vector out of HEPMC event - mcheader->SetEventID(event.event_number()); - mcheader->SetVertex(event.event_pos().px(), event.event_pos().py(), event.event_pos().pz()); + o2::dataformats::MCEventHeader mcHeader; + mcHeader.SetEventID(event.event_number()); + mcHeader.SetVertex(event.event_pos().px(), event.event_pos().py(), event.event_pos().pz()); auto xsecInfo = event.cross_section(); if (xsecInfo != nullptr) { - mcheader->putInfo(MCInfoKeys::acceptedEvents, (uint64_t)xsecInfo->get_accepted_events()); - mcheader->putInfo(MCInfoKeys::attemptedEvents, (uint64_t)xsecInfo->get_attempted_events()); - mcheader->putInfo(MCInfoKeys::xSection, (float)xsecInfo->xsec()); - mcheader->putInfo(MCInfoKeys::xSectionError, (float)xsecInfo->xsec_err()); + mcHeader.putInfo(MCInfoKeys::acceptedEvents, (uint64_t)xsecInfo->get_accepted_events()); + mcHeader.putInfo(MCInfoKeys::attemptedEvents, (uint64_t)xsecInfo->get_attempted_events()); + mcHeader.putInfo(MCInfoKeys::xSection, (float)xsecInfo->xsec()); + mcHeader.putInfo(MCInfoKeys::xSectionError, (float)xsecInfo->xsec_err()); } auto scale = event.attribute(MCInfoKeys::eventScale); if (scale != nullptr) { - mcheader->putInfo(MCInfoKeys::eventScale, (float)scale->value()); + mcHeader.putInfo(MCInfoKeys::eventScale, (float)scale->value()); } auto nMPI = event.attribute(MCInfoKeys::mpi); if (nMPI != nullptr) { - mcheader->putInfo(MCInfoKeys::mpi, nMPI->value()); + mcHeader.putInfo(MCInfoKeys::mpi, nMPI->value()); } auto sid = event.attribute(MCInfoKeys::processCode); auto scode = event.attribute(MCInfoKeys::processID); // default pythia8 hepmc3 interface uses signal_process_id if (sid != nullptr) { - mcheader->putInfo(MCInfoKeys::processCode, sid->value()); + mcHeader.putInfo(MCInfoKeys::processCode, sid->value()); } else if (scode != nullptr) { - mcheader->putInfo(MCInfoKeys::processCode, scode->value()); + mcHeader.putInfo(MCInfoKeys::processCode, scode->value()); } auto pdfInfo = event.pdf_info(); if (pdfInfo != nullptr) { - mcheader->putInfo(MCInfoKeys::pdfParton1Id, pdfInfo->parton_id[0]); - mcheader->putInfo(MCInfoKeys::pdfParton2Id, pdfInfo->parton_id[1]); - mcheader->putInfo(MCInfoKeys::pdfCode1, pdfInfo->pdf_id[0]); - mcheader->putInfo(MCInfoKeys::pdfCode2, pdfInfo->pdf_id[1]); - mcheader->putInfo(MCInfoKeys::pdfX1, (float)pdfInfo->x[0]); - mcheader->putInfo(MCInfoKeys::pdfX2, (float)pdfInfo->x[1]); - mcheader->putInfo(MCInfoKeys::pdfScale, (float)pdfInfo->scale); - mcheader->putInfo(MCInfoKeys::pdfXF1, (float)pdfInfo->xf[0]); - mcheader->putInfo(MCInfoKeys::pdfXF2, (float)pdfInfo->xf[1]); + mcHeader.putInfo(MCInfoKeys::pdfParton1Id, pdfInfo->parton_id[0]); + mcHeader.putInfo(MCInfoKeys::pdfParton2Id, pdfInfo->parton_id[1]); + mcHeader.putInfo(MCInfoKeys::pdfCode1, pdfInfo->pdf_id[0]); + mcHeader.putInfo(MCInfoKeys::pdfCode2, pdfInfo->pdf_id[1]); + mcHeader.putInfo(MCInfoKeys::pdfX1, (float)pdfInfo->x[0]); + mcHeader.putInfo(MCInfoKeys::pdfX2, (float)pdfInfo->x[1]); + mcHeader.putInfo(MCInfoKeys::pdfScale, (float)pdfInfo->scale); + mcHeader.putInfo(MCInfoKeys::pdfXF1, (float)pdfInfo->xf[0]); + mcHeader.putInfo(MCInfoKeys::pdfXF2, (float)pdfInfo->xf[1]); } auto heavyIon = event.heavy_ion(); if (heavyIon != nullptr) { - mcheader->putInfo(MCInfoKeys::nCollHard, heavyIon->Ncoll_hard); - mcheader->putInfo(MCInfoKeys::nPartProjectile, heavyIon->Npart_proj); - mcheader->putInfo(MCInfoKeys::nPartTarget, heavyIon->Npart_targ); - mcheader->putInfo(MCInfoKeys::nColl, heavyIon->Ncoll); - mcheader->putInfo(MCInfoKeys::nCollNNWounded, heavyIon->N_Nwounded_collisions); - mcheader->putInfo(MCInfoKeys::nCollNWoundedN, heavyIon->Nwounded_N_collisions); - mcheader->putInfo(MCInfoKeys::nCollNWoundedNwounded, heavyIon->Nwounded_Nwounded_collisions); - mcheader->putInfo(MCInfoKeys::nSpecProjectileNeutron, heavyIon->Nspec_proj_n); - mcheader->putInfo(MCInfoKeys::nSpecProjectileProton, heavyIon->Nspec_proj_p); - mcheader->putInfo(MCInfoKeys::nSpecTargetNeutron, heavyIon->Nspec_targ_n); - mcheader->putInfo(MCInfoKeys::nSpecTargetProton, heavyIon->Nspec_targ_p); - mcheader->putInfo(MCInfoKeys::impactParameter, (float)heavyIon->impact_parameter); - mcheader->putInfo(MCInfoKeys::planeAngle, (float)heavyIon->event_plane_angle); - mcheader->putInfo("eccentricity", (float)heavyIon->eccentricity); - mcheader->putInfo(MCInfoKeys::sigmaInelNN, (float)heavyIon->sigma_inel_NN); - mcheader->putInfo(MCInfoKeys::centrality, (float)heavyIon->centrality); + mcHeader.putInfo(MCInfoKeys::nCollHard, heavyIon->Ncoll_hard); + mcHeader.putInfo(MCInfoKeys::nPartProjectile, heavyIon->Npart_proj); + mcHeader.putInfo(MCInfoKeys::nPartTarget, heavyIon->Npart_targ); + mcHeader.putInfo(MCInfoKeys::nColl, heavyIon->Ncoll); + mcHeader.putInfo(MCInfoKeys::nCollNNWounded, heavyIon->N_Nwounded_collisions); + mcHeader.putInfo(MCInfoKeys::nCollNWoundedN, heavyIon->Nwounded_N_collisions); + mcHeader.putInfo(MCInfoKeys::nCollNWoundedNwounded, heavyIon->Nwounded_Nwounded_collisions); + mcHeader.putInfo(MCInfoKeys::nSpecProjectileNeutron, heavyIon->Nspec_proj_n); + mcHeader.putInfo(MCInfoKeys::nSpecProjectileProton, heavyIon->Nspec_proj_p); + mcHeader.putInfo(MCInfoKeys::nSpecTargetNeutron, heavyIon->Nspec_targ_n); + mcHeader.putInfo(MCInfoKeys::nSpecTargetProton, heavyIon->Nspec_targ_p); + mcHeader.putInfo(MCInfoKeys::impactParameter, (float)heavyIon->impact_parameter); + mcHeader.putInfo(MCInfoKeys::planeAngle, (float)heavyIon->event_plane_angle); + mcHeader.putInfo("eccentricity", (float)heavyIon->eccentricity); + mcHeader.putInfo(MCInfoKeys::sigmaInelNN, (float)heavyIon->sigma_inel_NN); + mcHeader.putInfo(MCInfoKeys::centrality, (float)heavyIon->centrality); } auto particles = event.particles(); @@ -138,7 +131,7 @@ struct O2simHepmcPublisher { auto has_children = children.size() > 0; auto p = particle->momentum(); auto v = particle->production_vertex(); - mctracks->emplace_back( + mcTracks.emplace_back( particle->pid(), has_parents ? parents.front()->id() : -1, has_parents ? parents.back()->id() : -1, has_children ? children.front()->id() : -1, has_children ? children.back()->id() : -1, @@ -146,13 +139,18 @@ struct O2simHepmcPublisher { v->position().x(), v->position().y(), v->position().z(), v->position().t(), 0); } + + // add to the message + pc.outputs().snapshot(Output{"MC", "MCHEADER", 0}, mcHeader); + pc.outputs().snapshot(Output{"MC", "MCTRACKS", 0}, mcTracks); + mcTracks.clear(); ++eventCounter; } // report number of TFs injected for the rate limiter to work ++tfCounter; pc.services().get().send(o2::monitoring::Metric{(uint64_t)tfCounter, "df-sent"}.addTag(o2::monitoring::tags::Key::Subsystem, o2::monitoring::tags::Value::DPL)); - if (eos || (maxEvents > 0 && eventCounter >= maxEvents)) { + if (eos || (maxEvents > 0 && eventCounter == maxEvents)) { pc.services().get().endOfStream(); pc.services().get().readyToQuit(QuitRequest::Me); } diff --git a/run/o2sim_kine_publisher.cxx b/run/o2sim_kine_publisher.cxx index 5920743c3fafa..cfbea6ae02a5f 100644 --- a/run/o2sim_kine_publisher.cxx +++ b/run/o2sim_kine_publisher.cxx @@ -40,8 +40,7 @@ struct O2simKinePublisher { void run(o2::framework::ProcessingContext& pc) { - auto batch = std::min((int)aggregate, nEvents - eventCounter); - for (auto i = 0; i < batch; ++i) { + for (auto i = 0; i < std::min((int)aggregate, nEvents - eventCounter); ++i) { auto mcevent = mcKinReader->getMCEventHeader(0, eventCounter); auto mctracks = mcKinReader->getTracks(0, eventCounter); pc.outputs().snapshot(Output{"MC", "MCHEADER", 0}, mcevent); diff --git a/run/o2sim_mctracks_to_aod.cxx b/run/o2sim_mctracks_to_aod.cxx index d95a3b33cc38f..124e8aa7b3e42 100644 --- a/run/o2sim_mctracks_to_aod.cxx +++ b/run/o2sim_mctracks_to_aod.cxx @@ -70,7 +70,7 @@ struct MctracksToAod { /** Run the conversion */ void run(o2::framework::ProcessingContext& pc) { - LOG(detail) << "=== Running extended MC AOD exporter ==="; + LOG(debug) << "=== Running extended MC AOD exporter ==="; using namespace o2::aodmchelpers; using McHeader = o2::dataformats::MCEventHeader; using McTrack = o2::MCTrack; @@ -94,13 +94,13 @@ struct MctracksToAod { // TODO: include BC simulation auto bcCounter = 0UL; size_t offset = 0; - LOG(detail) << "--- Loop over " << nParts << " parts ---"; + LOG(debug) << "--- Loop over " << nParts << " parts ---"; for (auto i = 0U; i < nParts; ++i) { auto record = mSampler.generateCollisionTime(); auto header = pc.inputs().get("mcheader", i); auto tracks = pc.inputs().get("mctracks", i); - LOG(detail) << "Updating collision table"; + LOG(debug) << "Updating collision table"; auto genID = updateMCCollisions(mCollisions.cursor, bcCounter, record.timeInBCNS * 1.e-3, @@ -108,12 +108,12 @@ struct MctracksToAod { 0, i); - LOG(detail) << "Updating HepMC tables"; + LOG(debug) << "Updating HepMC tables"; updateHepMCXSection(mXSections.cursor, bcCounter, genID, *header); updateHepMCPdfInfo(mPdfInfos.cursor, bcCounter, genID, *header); updateHepMCHeavyIon(mHeavyIons.cursor, bcCounter, genID, *header); - LOG(detail) << "Updating particles table"; + LOG(debug) << "Updating particles table"; TrackToIndex preselect; offset = updateParticles(mParticles.cursor, bcCounter, @@ -123,7 +123,7 @@ struct MctracksToAod { (bool)filt, false); - LOG(detail) << "Increment BC counter"; + LOG(debug) << "Increment BC counter"; bcCounter++; } From 757173be61c6ccf7d962476c222c60c64be2f785 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 29 Mar 2026 08:40:38 +0200 Subject: [PATCH 010/285] DPL: drop MessageSet::associateHeader / associatePayload (#15234) --- Framework/Core/include/Framework/MessageSet.h | 11 ------ Framework/Core/test/test_MessageSet.cxx | 35 ------------------- 2 files changed, 46 deletions(-) diff --git a/Framework/Core/include/Framework/MessageSet.h b/Framework/Core/include/Framework/MessageSet.h index 323c0ad4608af..6ccabd8c5ffb5 100644 --- a/Framework/Core/include/Framework/MessageSet.h +++ b/Framework/Core/include/Framework/MessageSet.h @@ -136,17 +136,6 @@ struct MessageSet { } } - fair::mq::MessagePtr const& associatedHeader(size_t pos) const - { - return messages[messageMap[pairMap[pos].partIndex].position]; - } - - fair::mq::MessagePtr const& associatedPayload(size_t pos) const - { - auto partIndex = pairMap[pos].partIndex; - auto payloadIndex = pairMap[pos].payloadIndex; - return messages[messageMap[partIndex].position + payloadIndex + 1]; - } }; } // namespace o2::framework diff --git a/Framework/Core/test/test_MessageSet.cxx b/Framework/Core/test/test_MessageSet.cxx index 8c9ed4a7cbf1c..bfbffb166da8d 100644 --- a/Framework/Core/test/test_MessageSet.cxx +++ b/Framework/Core/test/test_MessageSet.cxx @@ -50,11 +50,6 @@ TEST_CASE("MessageSet") // messages: [hdr, pl] — one pair REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); - for (size_t i = 0; i < msgSet.pairMap.size(); ++i) { - auto indices = msgSet.messages | get_pair{i}; - REQUIRE(&msgSet.messages[indices.headerIdx] == &msgSet.associatedHeader(i)); - REQUIRE(&msgSet.messages[indices.payloadIdx] == &msgSet.associatedPayload(i)); - } } TEST_CASE("MessageSetWithFunction") @@ -82,11 +77,6 @@ TEST_CASE("MessageSetWithFunction") CHECK_THROWS((msgSet.messages | get_pair{1})); REQUIRE((msgSet.messages | get_num_payloads{0}) == 1); REQUIRE((msgSet.messages | count_parts{}) == 1); - for (size_t i = 0; i < msgSet.pairMap.size(); ++i) { - auto indices = msgSet.messages | get_pair{i}; - REQUIRE(&msgSet.messages[indices.headerIdx] == &msgSet.associatedHeader(i)); - REQUIRE(&msgSet.messages[indices.payloadIdx] == &msgSet.associatedPayload(i)); - } } TEST_CASE("MessageSetWithMultipart") @@ -125,11 +115,6 @@ TEST_CASE("MessageSetWithMultipart") REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); REQUIRE((msgSet.messages | get_pair{1}).headerIdx == 0); REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 2); - for (size_t i = 0; i < msgSet.pairMap.size(); ++i) { - auto indices = msgSet.messages | get_pair{i}; - REQUIRE(&msgSet.messages[indices.headerIdx] == &msgSet.associatedHeader(i)); - REQUIRE(&msgSet.messages[indices.payloadIdx] == &msgSet.associatedPayload(i)); - } } TEST_CASE("MessageSetAddPartRef") @@ -206,11 +191,6 @@ TEST_CASE("MessageSetAddMultiple") REQUIRE((msgSet.messages | get_num_payloads{2}) == 2); REQUIRE((msgSet.messages | count_parts{}) == 3); REQUIRE((msgSet.messages | count_payloads{}) == 4); - for (size_t i = 0; i < msgSet.pairMap.size(); ++i) { - auto indices = msgSet.messages | get_pair{i}; - REQUIRE(&msgSet.messages[indices.headerIdx] == &msgSet.associatedHeader(i)); - REQUIRE(&msgSet.messages[indices.payloadIdx] == &msgSet.associatedPayload(i)); - } } TEST_CASE("GetHeaderPayloadOperators") @@ -267,11 +247,6 @@ TEST_CASE("GetHeaderPayloadOperators") REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); REQUIRE((msgSet.messages | get_pair{1}).headerIdx == 2); REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 3); - for (size_t i = 0; i < msgSet.pairMap.size(); ++i) { - auto indices = msgSet.messages | get_pair{i}; - REQUIRE(&msgSet.messages[indices.headerIdx] == &msgSet.associatedHeader(i)); - REQUIRE(&msgSet.messages[indices.payloadIdx] == &msgSet.associatedPayload(i)); - } } TEST_CASE("GetHeaderPayloadMultiPayload") @@ -370,11 +345,6 @@ TEST_CASE("GetHeaderPayloadMultiPayload") REQUIRE((msgSet.messages | get_pair{2}).payloadIdx == 4); REQUIRE((msgSet.messages | get_pair{3}).headerIdx == 2); REQUIRE((msgSet.messages | get_pair{3}).payloadIdx == 5); - for (size_t i = 0; i < msgSet.pairMap.size(); ++i) { - auto indices = msgSet.messages | get_pair{i}; - REQUIRE(&msgSet.messages[indices.headerIdx] == &msgSet.associatedHeader(i)); - REQUIRE(&msgSet.messages[indices.payloadIdx] == &msgSet.associatedPayload(i)); - } } TEST_CASE("TraditionalSplitParts") @@ -449,9 +419,4 @@ TEST_CASE("TraditionalSplitParts") REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 3); REQUIRE((msgSet.messages | get_pair{2}).headerIdx == 4); REQUIRE((msgSet.messages | get_pair{2}).payloadIdx == 5); - for (size_t i = 0; i < msgSet.pairMap.size(); ++i) { - auto indices = msgSet.messages | get_pair{i}; - REQUIRE(&msgSet.messages[indices.headerIdx] == &msgSet.associatedHeader(i)); - REQUIRE(&msgSet.messages[indices.payloadIdx] == &msgSet.associatedPayload(i)); - } } From cafa4ce8912a88addbce3ad515b6d215d3377923 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 29 Mar 2026 14:46:13 +0200 Subject: [PATCH 011/285] DPL: get rid of MessageSet::pairMap (#15237) Everything calculated on the fly --- Framework/Core/include/Framework/MessageSet.h | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/Framework/Core/include/Framework/MessageSet.h b/Framework/Core/include/Framework/MessageSet.h index 6ccabd8c5ffb5..bc718ca82714c 100644 --- a/Framework/Core/include/Framework/MessageSet.h +++ b/Framework/Core/include/Framework/MessageSet.h @@ -50,22 +50,21 @@ struct MessageSet { // payload index within the O2 message size_t payloadIndex = 0; }; - std::vector pairMap; MessageSet() - : messages(), messageMap(), pairMap() + : messages(), messageMap() { } template MessageSet(F getter, size_t size) - : messages(), messageMap(), pairMap() + : messages(), messageMap() { add(std::forward(getter), size); } MessageSet(MessageSet&& other) - : messages(std::move(other.messages)), messageMap(std::move(other.messageMap)), pairMap(std::move(other.pairMap)) + : messages(std::move(other.messages)), messageMap(std::move(other.messageMap)) { other.clear(); } @@ -77,7 +76,6 @@ struct MessageSet { } messages = std::move(other.messages); messageMap = std::move(other.messageMap); - pairMap = std::move(other.pairMap); other.clear(); return *this; } @@ -99,7 +97,6 @@ struct MessageSet { { messages.clear(); messageMap.clear(); - pairMap.clear(); } // this is more or less legacy @@ -116,7 +113,6 @@ struct MessageSet { // add content of the part ref void add(PartRef&& ref) { - pairMap.emplace_back(messageMap.size(), 0); messageMap.emplace_back(messages.size(), 1); messages.emplace_back(std::move(ref.header)); messages.emplace_back(std::move(ref.payload)); @@ -126,12 +122,8 @@ struct MessageSet { template void add(F getter, size_t size) { - auto partid = messageMap.size(); messageMap.emplace_back(messages.size(), size - 1); for (size_t i = 0; i < size; ++i) { - if (i > 0) { - pairMap.emplace_back(partid, i - 1); - } messages.emplace_back(std::move(getter(i))); } } From e22690985d4b374e3b82770994e92779bb63b00b Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 29 Mar 2026 14:50:58 +0200 Subject: [PATCH 012/285] DPL: drop MessageSet::messageMap --- Framework/Core/include/Framework/MessageSet.h | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/Framework/Core/include/Framework/MessageSet.h b/Framework/Core/include/Framework/MessageSet.h index bc718ca82714c..9cc11f0c35ee0 100644 --- a/Framework/Core/include/Framework/MessageSet.h +++ b/Framework/Core/include/Framework/MessageSet.h @@ -38,11 +38,6 @@ struct MessageSet { }; // linear storage of messages std::vector messages; - // message map describes O2 messages consisting of a header message and - // payload message(s), index describes position in the linear storage - std::vector messageMap; - // pair map describes all messages in one sequence of header-payload pairs and - // where in the message index the associated header and payload can be found struct PairMapping { PairMapping(size_t partId, size_t payloadId) : partIndex(partId), payloadIndex(payloadId) {} // O2 message where the pair is located in @@ -52,19 +47,19 @@ struct MessageSet { }; MessageSet() - : messages(), messageMap() + : messages() { } template MessageSet(F getter, size_t size) - : messages(), messageMap() + : messages() { add(std::forward(getter), size); } MessageSet(MessageSet&& other) - : messages(std::move(other.messages)), messageMap(std::move(other.messageMap)) + : messages(std::move(other.messages)) { other.clear(); } @@ -75,7 +70,6 @@ struct MessageSet { return *this; } messages = std::move(other.messages); - messageMap = std::move(other.messageMap); other.clear(); return *this; } @@ -96,7 +90,6 @@ struct MessageSet { void clear() { messages.clear(); - messageMap.clear(); } // this is more or less legacy @@ -113,7 +106,6 @@ struct MessageSet { // add content of the part ref void add(PartRef&& ref) { - messageMap.emplace_back(messages.size(), 1); messages.emplace_back(std::move(ref.header)); messages.emplace_back(std::move(ref.payload)); } @@ -122,7 +114,6 @@ struct MessageSet { template void add(F getter, size_t size) { - messageMap.emplace_back(messages.size(), size - 1); for (size_t i = 0; i < size; ++i) { messages.emplace_back(std::move(getter(i))); } From 4d74d840a04adc3099486ab53f5b0ae097bd26c3 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 29 Mar 2026 14:50:58 +0200 Subject: [PATCH 013/285] DPL: drop get number of pairs --- Framework/Core/include/Framework/MessageSet.h | 6 ------ Framework/Core/src/DataProcessingDevice.cxx | 4 ++-- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/Framework/Core/include/Framework/MessageSet.h b/Framework/Core/include/Framework/MessageSet.h index 9cc11f0c35ee0..440b98514eb51 100644 --- a/Framework/Core/include/Framework/MessageSet.h +++ b/Framework/Core/include/Framework/MessageSet.h @@ -74,12 +74,6 @@ struct MessageSet { return *this; } - /// get number of header-payload pairs - [[nodiscard]] size_t getNumberOfPairs() const - { - return messages | count_payloads{}; - } - /// get number of payloads for an in-flight message [[nodiscard]] size_t getNumberOfPayloads(size_t mi) const { diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index 31b7b02172af7..9b6395a02916a 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -2133,7 +2133,7 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v currentSetOfInputs = relayer.consumeExistingInputsForTimeslice(slot); } auto getter = [¤tSetOfInputs](size_t i, size_t partindex) -> DataRef { - if (currentSetOfInputs[i].getNumberOfPairs() > partindex) { + if ((currentSetOfInputs[i].messages | count_payloads{}) > partindex) { const char* headerptr = nullptr; const char* payloadptr = nullptr; size_t payloadSize = 0; @@ -2153,7 +2153,7 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v return DataRef{}; }; auto nofPartsGetter = [¤tSetOfInputs](size_t i) -> size_t { - return currentSetOfInputs[i].getNumberOfPairs(); + return (currentSetOfInputs[i].messages | count_payloads{}); }; auto refCountGetter = [¤tSetOfInputs](size_t idx) -> int { auto& header = static_cast(*(currentSetOfInputs[idx].messages | get_header{0})); From 1175e89185809f040661c9b928427f64fb9bd4a3 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 29 Mar 2026 14:50:58 +0200 Subject: [PATCH 014/285] DPL: fix also getNumberOfPayloads --- Framework/Core/include/Framework/MessageSet.h | 6 ------ Framework/Core/test/test_DataRelayer.cxx | 6 +++--- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/Framework/Core/include/Framework/MessageSet.h b/Framework/Core/include/Framework/MessageSet.h index 440b98514eb51..8a5aca854ca82 100644 --- a/Framework/Core/include/Framework/MessageSet.h +++ b/Framework/Core/include/Framework/MessageSet.h @@ -74,12 +74,6 @@ struct MessageSet { return *this; } - /// get number of payloads for an in-flight message - [[nodiscard]] size_t getNumberOfPayloads(size_t mi) const - { - return messages | get_num_payloads{mi}; - } - /// clear the set void clear() { diff --git a/Framework/Core/test/test_DataRelayer.cxx b/Framework/Core/test/test_DataRelayer.cxx index 1f7518860bf57..332a87970eda0 100644 --- a/Framework/Core/test/test_DataRelayer.cxx +++ b/Framework/Core/test/test_DataRelayer.cxx @@ -738,7 +738,7 @@ TEST_CASE("DataRelayer") // payloads REQUIRE(messageSet.size() == 1); REQUIRE((messageSet[0].messages | count_parts{}) == nSplitParts); - REQUIRE(messageSet[0].getNumberOfPayloads(0) == 1); + REQUIRE((messageSet[0].messages | get_num_payloads{0}) == 1); } SECTION("SplitPayloadSequence") @@ -803,8 +803,8 @@ TEST_CASE("DataRelayer") REQUIRE((messageSet[0].messages | count_parts{}) == sequenceSize.size()); size_t counter = 0; for (size_t seqid = 0; seqid < sequenceSize.size(); ++seqid) { - REQUIRE(messageSet[0].getNumberOfPayloads(seqid) == sequenceSize[seqid]); - for (size_t pi = 0; pi < messageSet[0].getNumberOfPayloads(seqid); ++pi) { + REQUIRE((messageSet[0].messages | get_num_payloads{seqid}) == sequenceSize[seqid]); + for (size_t pi = 0; pi < (messageSet[0].messages | get_num_payloads{seqid}); ++pi) { REQUIRE((messageSet[0].messages | get_payload{seqid, pi})); auto const* data = (messageSet[0].messages | get_payload{seqid, pi})->GetData(); REQUIRE(*(reinterpret_cast(data)) == counter); From 3b23b764efc5c6255c2e0e1d40d0dcecdeff81f5 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 29 Mar 2026 14:50:58 +0200 Subject: [PATCH 015/285] Remove unneeded parts in MessageSet --- Framework/Core/include/Framework/MessageSet.h | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/Framework/Core/include/Framework/MessageSet.h b/Framework/Core/include/Framework/MessageSet.h index 8a5aca854ca82..4f5943890ae68 100644 --- a/Framework/Core/include/Framework/MessageSet.h +++ b/Framework/Core/include/Framework/MessageSet.h @@ -31,20 +31,8 @@ namespace o2::framework /// O2 message model. For this purpose, also the pair index is filled and can /// be used to access header and payload associated with a pair struct MessageSet { - struct Index { - Index(size_t p, size_t s) : position(p), size(s) {} - size_t position = 0; - size_t size = 0; - }; // linear storage of messages std::vector messages; - struct PairMapping { - PairMapping(size_t partId, size_t payloadId) : partIndex(partId), payloadIndex(payloadId) {} - // O2 message where the pair is located in - size_t partIndex = 0; - // payload index within the O2 message - size_t payloadIndex = 0; - }; MessageSet() : messages() From 8c44308e7f0cc953fa4e2ece1c31886e6652dec4 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 29 Mar 2026 14:50:58 +0200 Subject: [PATCH 016/285] Drop add, reset --- Framework/Core/include/Framework/MessageSet.h | 32 ++------------- Framework/Core/src/DataRelayer.cxx | 11 +++-- Framework/Core/test/test_ForwardInputs.cxx | 40 +++++++++++++------ Framework/Core/test/test_MessageSet.cxx | 20 ++++++---- 4 files changed, 50 insertions(+), 53 deletions(-) diff --git a/Framework/Core/include/Framework/MessageSet.h b/Framework/Core/include/Framework/MessageSet.h index 4f5943890ae68..233099e67dc0f 100644 --- a/Framework/Core/include/Framework/MessageSet.h +++ b/Framework/Core/include/Framework/MessageSet.h @@ -43,7 +43,9 @@ struct MessageSet { MessageSet(F getter, size_t size) : messages() { - add(std::forward(getter), size); + for (size_t i = 0; i < size; ++i) { + messages.emplace_back(std::move(getter(i))); + } } MessageSet(MessageSet&& other) @@ -67,34 +69,6 @@ struct MessageSet { { messages.clear(); } - - // this is more or less legacy - // PartRef has been earlier used to store fixed header-payload pairs - // reset the set and store content of the part ref - void reset(PartRef&& ref) - { - clear(); - add(std::move(ref)); - } - - // this is more or less legacy - // PartRef has been earlier used to store fixed header-payload pairs - // add content of the part ref - void add(PartRef&& ref) - { - messages.emplace_back(std::move(ref.header)); - messages.emplace_back(std::move(ref.payload)); - } - - /// add an O2 message - template - void add(F getter, size_t size) - { - for (size_t i = 0; i < size; ++i) { - messages.emplace_back(std::move(getter(i))); - } - } - }; } // namespace o2::framework diff --git a/Framework/Core/src/DataRelayer.cxx b/Framework/Core/src/DataRelayer.cxx index 4cda75ed001b0..d34d12b282a9d 100644 --- a/Framework/Core/src/DataRelayer.cxx +++ b/Framework/Core/src/DataRelayer.cxx @@ -242,7 +242,9 @@ DataRelayer::ActivityStats DataRelayer::processDanglingInputs(std::vector(messages + mi, messages + mi + nPayloads + 1); // Notice this will split [(header, payload), (header, payload)] multiparts // in N different subParts for the message spec. - target.add([&span](size_t i) -> fair::mq::MessagePtr& { return span[i]; }, nPayloads + 1); + for (size_t i = 0; i < nPayloads + 1; ++i) { + target.messages.emplace_back(std::move(span[i])); + } mi += nPayloads; saved += nPayloads; } @@ -955,7 +959,8 @@ std::vector DataRelayer::consumeExistingInputsForTime auto& header = cache[cacheId].messages | get_header{pi}; auto&& newHeader = header->GetTransport()->CreateMessage(); newHeader->Copy(*header); - messages[arg].add(PartRef{std::move(newHeader), std::move(cache[cacheId].messages | get_payload{pi, 0})}); + messages[arg].messages.emplace_back(std::move(newHeader)); + messages[arg].messages.emplace_back(std::move(cache[cacheId].messages | get_payload{pi, 0})); } }; diff --git a/Framework/Core/test/test_ForwardInputs.cxx b/Framework/Core/test/test_ForwardInputs.cxx index e3031b7e72a69..6da42c5a94aca 100644 --- a/Framework/Core/test/test_ForwardInputs.cxx +++ b/Framework/Core/test/test_ForwardInputs.cxx @@ -91,7 +91,8 @@ TEST_CASE("ForwardInputsSingleMessageSingleRoute") fair::mq::MessagePtr payload(transport->CreateMessage()); auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); auto header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph}); - messageSet.add(PartRef{std::move(header), std::move(payload)}); + messageSet.messages.emplace_back(std::move(header)); + messageSet.messages.emplace_back(std::move(payload)); REQUIRE((messageSet.messages | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); @@ -142,7 +143,8 @@ TEST_CASE("ForwardInputsSingleMessageSingleRouteNoConsume") REQUIRE(payload.get() == nullptr); auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); auto header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph}); - messageSet.add(PartRef{std::move(header), std::move(payload)}); + messageSet.messages.emplace_back(std::move(header)); + messageSet.messages.emplace_back(std::move(payload)); REQUIRE((messageSet.messages | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); @@ -197,7 +199,8 @@ TEST_CASE("ForwardInputsSingleMessageSingleRouteAtEOS") auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); auto header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph, sih}); REQUIRE(o2::header::get(header->GetData())); - messageSet.add(PartRef{std::move(header), std::move(payload)}); + messageSet.messages.emplace_back(std::move(header)); + messageSet.messages.emplace_back(std::move(payload)); REQUIRE((messageSet.messages | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); @@ -255,7 +258,8 @@ TEST_CASE("ForwardInputsSingleMessageSingleRouteWithOldestPossible") auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); auto header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph, dih}); REQUIRE(o2::header::get(header->GetData())); - messageSet.add(PartRef{std::move(header), std::move(payload)}); + messageSet.messages.emplace_back(std::move(header)); + messageSet.messages.emplace_back(std::move(payload)); REQUIRE((messageSet.messages | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); @@ -320,7 +324,8 @@ TEST_CASE("ForwardInputsSingleMessageMultipleRoutes") fair::mq::MessagePtr payload(transport->CreateMessage()); auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); auto header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph}); - messageSet.add(PartRef{std::move(header), std::move(payload)}); + messageSet.messages.emplace_back(std::move(header)); + messageSet.messages.emplace_back(std::move(payload)); REQUIRE((messageSet.messages | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); @@ -383,7 +388,8 @@ TEST_CASE("ForwardInputsSingleMessageMultipleRoutesExternals") fair::mq::MessagePtr payload(transport->CreateMessage()); auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); auto header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph}); - messageSet.add(PartRef{std::move(header), std::move(payload)}); + messageSet.messages.emplace_back(std::move(header)); + messageSet.messages.emplace_back(std::move(payload)); REQUIRE((messageSet.messages | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); @@ -454,12 +460,14 @@ TEST_CASE("ForwardInputsMultiMessageMultipleRoutes") auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); auto header1 = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh1, dph}); MessageSet messageSet1; - messageSet1.add(PartRef{std::move(header1), std::move(payload1)}); + messageSet1.messages.emplace_back(std::move(header1)); + messageSet1.messages.emplace_back(std::move(payload1)); REQUIRE((messageSet1.messages | count_parts{}) == 1); auto header2 = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh2, dph}); MessageSet messageSet2; - messageSet2.add(PartRef{std::move(header2), std::move(payload2)}); + messageSet2.messages.emplace_back(std::move(header2)); + messageSet2.messages.emplace_back(std::move(payload2)); REQUIRE((messageSet2.messages | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet1)); currentSetOfInputs.emplace_back(std::move(messageSet2)); @@ -524,7 +532,8 @@ TEST_CASE("ForwardInputsSingleMessageMultipleRoutesOnlyOneMatches") fair::mq::MessagePtr payload(transport->CreateMessage()); auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); auto header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph}); - messageSet.add(PartRef{std::move(header), std::move(payload)}); + messageSet.messages.emplace_back(std::move(header)); + messageSet.messages.emplace_back(std::move(payload)); REQUIRE((messageSet.messages | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); @@ -602,10 +611,13 @@ TEST_CASE("ForwardInputsSplitPayload") auto fillMessages = [&messages](size_t t) -> fair::mq::MessagePtr { return std::move(messages[t]); }; - messageSet.add(fillMessages, 3); + for (size_t i = 0; i < 3; ++i) { + messageSet.messages.emplace_back(fillMessages(i)); + } auto header2 = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh2, dph}); PartRef part{std::move(header2), transport->CreateMessage()}; - messageSet.add(std::move(part)); + messageSet.messages.emplace_back(std::move(part.header)); + messageSet.messages.emplace_back(std::move(part.payload)); REQUIRE((messageSet.messages | count_parts{}) == 2); currentSetOfInputs.emplace_back(std::move(messageSet)); @@ -726,7 +738,8 @@ TEST_CASE("ForwardInputEOSSingleRoute") fair::mq::MessagePtr payload(transport->CreateMessage()); auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); auto header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, sih}); - messageSet.add(PartRef{std::move(header), std::move(payload)}); + messageSet.messages.emplace_back(std::move(header)); + messageSet.messages.emplace_back(std::move(payload)); REQUIRE((messageSet.messages | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); @@ -771,7 +784,8 @@ TEST_CASE("ForwardInputOldestPossibleSingleRoute") fair::mq::MessagePtr payload(transport->CreateMessage()); auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); auto header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dih}); - messageSet.add(PartRef{std::move(header), std::move(payload)}); + messageSet.messages.emplace_back(std::move(header)); + messageSet.messages.emplace_back(std::move(payload)); REQUIRE((messageSet.messages | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); diff --git a/Framework/Core/test/test_MessageSet.cxx b/Framework/Core/test/test_MessageSet.cxx index bfbffb166da8d..d62a804e7681d 100644 --- a/Framework/Core/test/test_MessageSet.cxx +++ b/Framework/Core/test/test_MessageSet.cxx @@ -36,7 +36,9 @@ TEST_CASE("MessageSet") std::vector ptrs; ptrs.emplace_back(std::move(header)); ptrs.emplace_back(std::move(msg2)); - msgSet.add([&ptrs](size_t i) -> fair::mq::MessagePtr& { return ptrs[i]; }, 2); + for (size_t i = 0; i < 2; ++i) { + msgSet.messages.emplace_back(std::move(ptrs[i])); + } REQUIRE(msgSet.messages.size() == 2); REQUIRE((msgSet.messages | count_payloads{}) == 1); @@ -126,7 +128,8 @@ TEST_CASE("MessageSetAddPartRef") ptrs.emplace_back(std::move(msg2)); PartRef ref{std::move(msg), std::move(msg2)}; o2::framework::MessageSet msgSet; - msgSet.add(std::move(ref)); + msgSet.messages.emplace_back(std::move(ref.header)); + msgSet.messages.emplace_back(std::move(ref.payload)); REQUIRE(msgSet.messages.size() == 2); } @@ -155,17 +158,18 @@ TEST_CASE("MessageSetAddMultiple") std::unique_ptr msg3(nullptr); PartRef ref{std::move(header1), std::move(msg2)}; o2::framework::MessageSet msgSet; - msgSet.add(std::move(ref)); + msgSet.messages.emplace_back(std::move(ref.header)); + msgSet.messages.emplace_back(std::move(ref.payload)); PartRef ref2{std::move(header2), std::move(msg2)}; - msgSet.add(std::move(ref2)); + msgSet.messages.emplace_back(std::move(ref2.header)); + msgSet.messages.emplace_back(std::move(ref2.payload)); std::vector msgs; msgs.push_back(std::move(header3)); msgs.push_back(std::unique_ptr(nullptr)); msgs.push_back(std::unique_ptr(nullptr)); - msgSet.add([&msgs](size_t i) { - return std::move(msgs[i]); - }, - 3); + for (size_t i = 0; i < 3; ++i) { + msgSet.messages.emplace_back(std::move(msgs[i])); + } REQUIRE(msgSet.messages.size() == 7); From 65b055fdccb1fd6f8c850cb959a49cb615c3828f Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 29 Mar 2026 14:50:58 +0200 Subject: [PATCH 017/285] Get rid of the clear Method --- Framework/Core/include/Framework/MessageSet.h | 9 ++------- Framework/Core/src/DataProcessingDevice.cxx | 2 +- Framework/Core/src/DataRelayer.cxx | 6 +++--- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/Framework/Core/include/Framework/MessageSet.h b/Framework/Core/include/Framework/MessageSet.h index 233099e67dc0f..1da8ca35c46f4 100644 --- a/Framework/Core/include/Framework/MessageSet.h +++ b/Framework/Core/include/Framework/MessageSet.h @@ -51,7 +51,7 @@ struct MessageSet { MessageSet(MessageSet&& other) : messages(std::move(other.messages)) { - other.clear(); + other.messages.clear(); } MessageSet& operator=(MessageSet&& other) @@ -60,15 +60,10 @@ struct MessageSet { return *this; } messages = std::move(other.messages); - other.clear(); + other.messages.clear(); return *this; } - /// clear the set - void clear() - { - messages.clear(); - } }; } // namespace o2::framework diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index 9b6395a02916a..af23219bfb509 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -2215,7 +2215,7 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v continue; } // This will hopefully delete the message. - currentSetOfInputs[ii].clear(); + currentSetOfInputs[ii].messages.clear(); } }; diff --git a/Framework/Core/src/DataRelayer.cxx b/Framework/Core/src/DataRelayer.cxx index d34d12b282a9d..dd051a2189c07 100644 --- a/Framework/Core/src/DataRelayer.cxx +++ b/Framework/Core/src/DataRelayer.cxx @@ -431,7 +431,7 @@ void DataRelayer::pruneCache(TimesliceSlot slot, OnDropCallback onDrop) // will be ignored. assert(numInputTypes * slot.index < cache.size()); for (size_t ai = slot.index * numInputTypes, ae = ai + numInputTypes; ai != ae; ++ai) { - cache[ai].clear(); + cache[ai].messages.clear(); cachedStateMetrics[ai] = CacheEntryStatus::EMPTY; } }; @@ -914,7 +914,7 @@ std::vector DataRelayer::consumeAllInputsForTimeslice auto invalidateCacheFor = [&numInputTypes, &index, &cache](TimesliceSlot s) { for (size_t ai = s.index * numInputTypes, ae = ai + numInputTypes; ai != ae; ++ai) { assert(std::accumulate(cache[ai].messages.begin(), cache[ai].messages.end(), true, [](bool result, auto const& element) { return result && element.get() == nullptr; })); - cache[ai].clear(); + cache[ai].messages.clear(); } index.markAsInvalid(s); }; @@ -978,7 +978,7 @@ void DataRelayer::clear() std::scoped_lock lock(mMutex); for (auto& cache : mCache) { - cache.clear(); + cache.messages.clear(); } for (size_t s = 0; s < mTimesliceIndex.size(); ++s) { mTimesliceIndex.markAsInvalid(TimesliceSlot{s}); From c85788dfb6aa87cf3e7df348d58deddb2ba673ba Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 29 Mar 2026 14:50:58 +0200 Subject: [PATCH 018/285] DPL: migrate away from MessageSet Use a vector of messages instead. To be replaced by a B-Tree which is able to keep track of all inputs / slots in a less rigid manner. --- .../Core/include/Framework/DataModelViews.h | 8 +- .../include/Framework/DataProcessingHelpers.h | 4 +- .../Core/include/Framework/DataRelayer.h | 10 +- Framework/Core/include/Framework/MessageSet.h | 71 ---- Framework/Core/src/DataProcessingDevice.cxx | 27 +- Framework/Core/src/DataProcessingHelpers.cxx | 4 +- Framework/Core/src/DataRelayer.cxx | 79 ++--- Framework/Core/test/benchmark_DataRelayer.cxx | 23 +- Framework/Core/test/test_DataRelayer.cxx | 25 +- Framework/Core/test/test_ForwardInputs.cxx | 125 ++++--- Framework/Core/test/test_MessageSet.cxx | 331 ++++++++---------- 11 files changed, 296 insertions(+), 411 deletions(-) delete mode 100644 Framework/Core/include/Framework/MessageSet.h diff --git a/Framework/Core/include/Framework/DataModelViews.h b/Framework/Core/include/Framework/DataModelViews.h index 7c39a94950e9c..285f5ef15154e 100644 --- a/Framework/Core/include/Framework/DataModelViews.h +++ b/Framework/Core/include/Framework/DataModelViews.h @@ -16,7 +16,9 @@ #include "DomainInfoHeader.h" #include "SourceInfoHeader.h" #include "Headers/DataHeader.h" +#include "Framework/TimesliceSlot.h" #include +#include namespace o2::framework { @@ -213,13 +215,11 @@ struct get_num_payloads { } }; -struct MessageSet; - struct inputs_for_slot { TimesliceSlot slot; template requires requires(R r) { requires std::ranges::random_access_range; } - friend std::span operator|(R&& r, inputs_for_slot self) + friend auto operator|(R&& r, inputs_for_slot self) { return std::span(r.sets[self.slot.index * r.inputsPerSlot]); } @@ -231,7 +231,7 @@ struct messages_for_input { requires std::ranges::random_access_range friend std::span operator|(R&& r, messages_for_input self) { - return r[self.inputIdx].messages; + return std::span(r[self.inputIdx]); } }; diff --git a/Framework/Core/include/Framework/DataProcessingHelpers.h b/Framework/Core/include/Framework/DataProcessingHelpers.h index 87aeeb8922da3..f414e3aa4ae00 100644 --- a/Framework/Core/include/Framework/DataProcessingHelpers.h +++ b/Framework/Core/include/Framework/DataProcessingHelpers.h @@ -15,6 +15,7 @@ #include "Framework/TimesliceSlot.h" #include "Framework/TimesliceIndex.h" #include +#include #include #include @@ -29,7 +30,6 @@ struct OutputChannelState; struct ProcessingPolicies; struct DeviceSpec; struct FairMQDeviceProxy; -struct MessageSet; struct ChannelIndex; enum struct StreamingState; enum struct TransitionHandlingState; @@ -54,7 +54,7 @@ struct DataProcessingHelpers { /// starts the EoS timers and returns the new TransitionHandlingState in case as new state is requested static TransitionHandlingState updateStateTransition(ServiceRegistryRef const& ref, ProcessingPolicies const& policies); /// Helper to route messages for forwarding - static std::vector routeForwardedMessageSet(FairMQDeviceProxy& proxy, std::vector& currentSetOfInputs, + static std::vector routeForwardedMessageSet(FairMQDeviceProxy& proxy, std::vector>& currentSetOfInputs, bool copy, bool consume); /// Helper to route messages for forwarding static void routeForwardedMessages(FairMQDeviceProxy& proxy, std::span& currentSetOfInputs, std::vector& forwardedParts, diff --git a/Framework/Core/include/Framework/DataRelayer.h b/Framework/Core/include/Framework/DataRelayer.h index e5a2aecea1de4..b56a2cb59ff10 100644 --- a/Framework/Core/include/Framework/DataRelayer.h +++ b/Framework/Core/include/Framework/DataRelayer.h @@ -16,7 +16,7 @@ #include "Framework/DataDescriptorMatcher.h" #include "Framework/ForwardRoute.h" #include "Framework/CompletionPolicy.h" -#include "Framework/MessageSet.h" +#include #include "Framework/TimesliceIndex.h" #include "Framework/Tracing.h" #include "Framework/TimesliceSlot.h" @@ -113,7 +113,7 @@ class DataRelayer ActivityStats processDanglingInputs(std::vector const&, ServiceRegistryRef context, bool createNew); - using OnDropCallback = std::function&, TimesliceIndex::OldestOutputInfo info)>; + using OnDropCallback = std::function>&, TimesliceIndex::OldestOutputInfo info)>; // Callback for when some messages are about to be owned by the the DataRelayer using OnInsertionCallback = std::function&)>; @@ -156,8 +156,8 @@ class DataRelayer /// Returns an input registry associated to the given timeslice and gives /// ownership to the caller. This is because once the inputs are out of the /// DataRelayer they need to be deleted once the processing is concluded. - std::vector consumeAllInputsForTimeslice(TimesliceSlot id); - std::vector consumeExistingInputsForTimeslice(TimesliceSlot id); + std::vector> consumeAllInputsForTimeslice(TimesliceSlot id); + std::vector> consumeExistingInputsForTimeslice(TimesliceSlot id); /// Returns how many timeslices we can handle in parallel [[nodiscard]] size_t getParallelTimeslices() const; @@ -203,7 +203,7 @@ class DataRelayer /// Notice that we store them as a NxM sized vector, where /// N is the maximum number of inflight timeslices, while /// M is the number of inputs which are requested. - std::vector mCache; + std::vector> mCache; /// This is the index which maps a given timestamp to the associated /// cacheline. diff --git a/Framework/Core/include/Framework/MessageSet.h b/Framework/Core/include/Framework/MessageSet.h deleted file mode 100644 index 1da8ca35c46f4..0000000000000 --- a/Framework/Core/include/Framework/MessageSet.h +++ /dev/null @@ -1,71 +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 FRAMEWORK_MESSAGESET_H -#define FRAMEWORK_MESSAGESET_H - -#include "Framework/PartRef.h" -#include -#include "Framework/DataModelViews.h" -#include -#include -#include - -namespace o2::framework -{ - -/// A set of inflight messages. -/// The messages are stored in a linear vector. Originally, an O2 message was -/// comprised of a header-payload pair which makes indexing of pairs in the -/// storage simple. To support O2 messages with multiple payloads in a future -/// update of the data model, a message index is needed to store position in the -/// linear storage and number of messages. -/// DPL InputRecord API is providing refs of header-payload pairs, the original -/// O2 message model. For this purpose, also the pair index is filled and can -/// be used to access header and payload associated with a pair -struct MessageSet { - // linear storage of messages - std::vector messages; - - MessageSet() - : messages() - { - } - - template - MessageSet(F getter, size_t size) - : messages() - { - for (size_t i = 0; i < size; ++i) { - messages.emplace_back(std::move(getter(i))); - } - } - - MessageSet(MessageSet&& other) - : messages(std::move(other.messages)) - { - other.messages.clear(); - } - - MessageSet& operator=(MessageSet&& other) - { - if (&other == this) { - return *this; - } - messages = std::move(other.messages); - other.messages.clear(); - return *this; - } - -}; - -} // namespace o2::framework - -#endif // FRAMEWORK_MESSAGESET_H diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index af23219bfb509..6b90747550278 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -50,6 +50,7 @@ #include "DecongestionService.h" #include "Framework/DataProcessingHelpers.h" +#include "Framework/DataModelViews.h" #include "DataRelayerHelpers.h" #include "Headers/DataHeader.h" #include "Headers/DataHeaderHelpers.h" @@ -585,7 +586,7 @@ auto decongestionCallbackLate = [](AsyncTask& task, size_t aid) -> void { // the inputs which are shared between this device and others // to the next one in the daisy chain. // FIXME: do it in a smarter way than O(N^2) -static auto forwardInputs = [](ServiceRegistryRef registry, TimesliceSlot slot, std::vector& currentSetOfInputs, +static auto forwardInputs = [](ServiceRegistryRef registry, TimesliceSlot slot, std::vector>& currentSetOfInputs, TimesliceIndex::OldestOutputInfo oldestTimeslice, bool copy, bool consume = true) { auto& proxy = registry.get(); @@ -617,7 +618,7 @@ static auto forwardInputs = [](ServiceRegistryRef registry, TimesliceSlot slot, O2_SIGNPOST_END(forwarding, sid, "forwardInputs", "Forwarding done"); }; -static auto cleanEarlyForward = [](ServiceRegistryRef registry, TimesliceSlot slot, std::vector& currentSetOfInputs, +static auto cleanEarlyForward = [](ServiceRegistryRef registry, TimesliceSlot slot, std::vector>& currentSetOfInputs, TimesliceIndex::OldestOutputInfo oldestTimeslice, bool copy, bool consume = true) { auto& proxy = registry.get(); @@ -627,7 +628,7 @@ static auto cleanEarlyForward = [](ServiceRegistryRef registry, TimesliceSlot sl // Always copy them, because we do not want to actually send them. // We merely need the side effect of the consume, if applicable. for (size_t ii = 0, ie = currentSetOfInputs.size(); ii < ie; ++ii) { - auto span = std::span(currentSetOfInputs[ii].messages); + auto span = std::span(currentSetOfInputs[ii]); DataProcessingHelpers::cleanForwardedMessages(span, consume); } @@ -1278,7 +1279,7 @@ void DataProcessingDevice::Run() // - we can trigger further events from the queue // - we can guarantee this is the last thing we do in the loop ( // assuming no one else is adding to the queue before this point). - auto onDrop = [®istry = mServiceRegistry, lid](TimesliceSlot slot, std::vector& dropped, TimesliceIndex::OldestOutputInfo oldestOutputInfo) { + auto onDrop = [®istry = mServiceRegistry, lid](TimesliceSlot slot, std::vector>& dropped, TimesliceIndex::OldestOutputInfo oldestOutputInfo) { O2_SIGNPOST_START(device, lid, "run_loop", "Dropping message from slot %" PRIu64 ". Forwarding as needed.", (uint64_t)slot.index); ServiceRegistryRef ref{registry}; ref.get(); @@ -1944,7 +1945,7 @@ void DataProcessingDevice::handleData(ServiceRegistryRef ref, InputChannelInfo& nPayloadsPerHeader = 1; ii += (nMessages / 2) - 1; } - auto onDrop = [ref](TimesliceSlot slot, std::vector& dropped, TimesliceIndex::OldestOutputInfo oldestOutputInfo) { + auto onDrop = [ref](TimesliceSlot slot, std::vector>& dropped, TimesliceIndex::OldestOutputInfo oldestOutputInfo) { O2_SIGNPOST_ID_GENERATE(cid, async_queue); O2_SIGNPOST_EVENT_EMIT(async_queue, cid, "onDrop", "Dropping message from slot %zu. Forwarding as needed. Timeslice %zu", slot.index, oldestOutputInfo.timeslice.value); @@ -2122,7 +2123,7 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v // want to support multithreaded dispatching of operations, I can simply // move these to some thread local store and the rest of the lambdas // should work just fine. - std::vector currentSetOfInputs; + std::vector> currentSetOfInputs; // auto getInputSpan = [ref, ¤tSetOfInputs](TimesliceSlot slot, bool consume = true) { @@ -2133,7 +2134,7 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v currentSetOfInputs = relayer.consumeExistingInputsForTimeslice(slot); } auto getter = [¤tSetOfInputs](size_t i, size_t partindex) -> DataRef { - if ((currentSetOfInputs[i].messages | count_payloads{}) > partindex) { + if ((currentSetOfInputs[i] | count_payloads{}) > partindex) { const char* headerptr = nullptr; const char* payloadptr = nullptr; size_t payloadSize = 0; @@ -2142,9 +2143,9 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v // sequence is the header message // - each part has one or more payload messages // - InputRecord provides all payloads as header-payload pair - auto const indices = currentSetOfInputs[i].messages | get_pair{partindex}; - auto const& headerMsg = currentSetOfInputs[i].messages[indices.headerIdx]; - auto const& payloadMsg = currentSetOfInputs[i].messages[indices.payloadIdx]; + auto const indices = currentSetOfInputs[i] | get_pair{partindex}; + auto const& headerMsg = currentSetOfInputs[i][indices.headerIdx]; + auto const& payloadMsg = currentSetOfInputs[i][indices.payloadIdx]; headerptr = static_cast(headerMsg->GetData()); payloadptr = payloadMsg ? static_cast(payloadMsg->GetData()) : nullptr; payloadSize = payloadMsg ? payloadMsg->GetSize() : 0; @@ -2153,10 +2154,10 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v return DataRef{}; }; auto nofPartsGetter = [¤tSetOfInputs](size_t i) -> size_t { - return (currentSetOfInputs[i].messages | count_payloads{}); + return (currentSetOfInputs[i] | count_payloads{}); }; auto refCountGetter = [¤tSetOfInputs](size_t idx) -> int { - auto& header = static_cast(*(currentSetOfInputs[idx].messages | get_header{0})); + auto& header = static_cast(*(currentSetOfInputs[idx] | get_header{0})); return header.GetRefCount(); }; return InputSpan{getter, nofPartsGetter, refCountGetter, currentSetOfInputs.size()}; @@ -2215,7 +2216,7 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v continue; } // This will hopefully delete the message. - currentSetOfInputs[ii].messages.clear(); + currentSetOfInputs[ii].clear(); } }; diff --git a/Framework/Core/src/DataProcessingHelpers.cxx b/Framework/Core/src/DataProcessingHelpers.cxx index 334a0fc6045f6..b8399a4c591e7 100644 --- a/Framework/Core/src/DataProcessingHelpers.cxx +++ b/Framework/Core/src/DataProcessingHelpers.cxx @@ -393,14 +393,14 @@ void DataProcessingHelpers::cleanForwardedMessages(std::span& currentSetOfInputs, + std::vector>& currentSetOfInputs, const bool copyByDefault, bool consume) -> std::vector { // we collect all messages per forward in a map and send them together std::vector forwardedParts(proxy.getNumForwardChannels()); for (size_t ii = 0, ie = currentSetOfInputs.size(); ii < ie; ++ii) { - auto span = std::span(currentSetOfInputs[ii].messages); + auto span = std::span(currentSetOfInputs[ii]); routeForwardedMessages(proxy, span, forwardedParts, copyByDefault, consume); } return forwardedParts; diff --git a/Framework/Core/src/DataRelayer.cxx b/Framework/Core/src/DataRelayer.cxx index dd051a2189c07..fc9966ffad643 100644 --- a/Framework/Core/src/DataRelayer.cxx +++ b/Framework/Core/src/DataRelayer.cxx @@ -38,6 +38,7 @@ #include "Framework/DataTakingContext.h" #include "Framework/DefaultsHelpers.h" #include "Framework/RawDeviceService.h" +#include "Framework/DataModelViews.h" #include "Headers/DataHeaderHelpers.h" #include "Framework/Formatters.h" @@ -184,11 +185,11 @@ DataRelayer::ActivityStats DataRelayer::processDanglingInputs(std::vector std::span { + auto getPartialRecord = [&cache = mCache, numInputTypes = mDistinctRoutesIndex.size()](int li) -> std::span const> { auto offset = li * numInputTypes; assert(cache.size() >= offset + numInputTypes); auto const start = cache.data() + offset; @@ -213,9 +214,9 @@ DataRelayer::ActivityStats DataRelayer::processDanglingInputs(std::vector(header->GetData()), reinterpret_cast(payload ? payload->GetData() : nullptr), @@ -224,10 +225,10 @@ DataRelayer::ActivityStats DataRelayer::processDanglingInputs(std::vector int { - auto& header = static_cast(*(partial[idx].messages | get_header{0})); + auto& header = static_cast(*(partial[idx] | get_header{0})); return header.GetRefCount(); }; InputSpan span{getter, nPartsGetter, refCountGetter, static_cast(partial.size())}; @@ -242,14 +243,14 @@ DataRelayer::ActivityStats DataRelayer::processDanglingInputs(std::vector(); @@ -355,7 +356,7 @@ void DataRelayer::setOldestPossibleInput(TimesliceId proposed, ChannelIndex chan continue; } auto& element = mCache[si * mInputs.size() + mi]; - if (element.messages.empty()) { + if (element.empty()) { auto& state = mContext.get(); if (state.transitionHandling != TransitionHandlingState::NoTransition && DefaultsHelpers::onlineDeploymentMode()) { if (state.allowedProcessing == DeviceState::CalibrationOnly) { @@ -407,17 +408,17 @@ void DataRelayer::pruneCache(TimesliceSlot slot, OnDropCallback onDrop) if (onDrop) { auto oldestPossibleTimeslice = index.getOldestPossibleOutput(); // State of the computation - std::vector dropped(numInputTypes); + std::vector> dropped(numInputTypes); for (size_t ai = 0, ae = numInputTypes; ai != ae; ++ai) { auto cacheId = slot.index * numInputTypes + ai; cachedStateMetrics[cacheId] = CacheEntryStatus::RUNNING; // TODO: in the original implementation of the cache, there have been only two messages per entry, // check if the 2 above corresponds to the number of messages. - if (!cache[cacheId].messages.empty()) { + if (!cache[cacheId].empty()) { dropped[ai] = std::move(cache[cacheId]); } } - bool anyDropped = std::any_of(dropped.begin(), dropped.end(), [](auto& m) { return !m.messages.empty(); }); + bool anyDropped = std::any_of(dropped.begin(), dropped.end(), [](auto& m) { return !m.empty(); }); if (anyDropped) { O2_SIGNPOST_ID_GENERATE(aid, data_relayer); O2_SIGNPOST_EVENT_EMIT(data_relayer, aid, "pruneCache", "Dropping stuff from slot %zu with timeslice %zu", slot.index, oldestPossibleTimeslice.timeslice.value); @@ -431,7 +432,7 @@ void DataRelayer::pruneCache(TimesliceSlot slot, OnDropCallback onDrop) // will be ignored. assert(numInputTypes * slot.index < cache.size()); for (size_t ai = slot.index * numInputTypes, ae = ai + numInputTypes; ai != ae; ++ai) { - cache[ai].messages.clear(); + cache[ai].clear(); cachedStateMetrics[ai] = CacheEntryStatus::EMPTY; } }; @@ -508,7 +509,7 @@ DataRelayer::RelayChoice timeslice.value, slot.index, info.index.value == ChannelIndex::INVALID ? "invalid" : services.get().getInputChannel(info.index)->GetName().c_str()); auto cacheIdx = numInputTypes * slot.index + input; - MessageSet& target = cache[cacheIdx]; + auto& target = cache[cacheIdx]; cachedStateMetrics[cacheIdx] = CacheEntryStatus::PENDING; // TODO: make sure that multiple parts can only be added within the same call of // DataRelayer::relay @@ -539,7 +540,7 @@ DataRelayer::RelayChoice // Notice this will split [(header, payload), (header, payload)] multiparts // in N different subParts for the message spec. for (size_t i = 0; i < nPayloads + 1; ++i) { - target.messages.emplace_back(std::move(span[i])); + target.emplace_back(std::move(span[i])); } mi += nPayloads; saved += nPayloads; @@ -732,7 +733,7 @@ void DataRelayer::getReadyToProcess(std::vector& comp // // We use this to bail out early from the check as soon as we find something // which we know is not complete. - auto getPartialRecord = [&cache, &numInputTypes](int li) -> std::span { + auto getPartialRecord = [&cache, &numInputTypes](int li) -> std::span const> { auto offset = li * numInputTypes; assert(cache.size() >= offset + numInputTypes); auto const start = cache.data() + offset; @@ -790,9 +791,9 @@ void DataRelayer::getReadyToProcess(std::vector& comp auto partial = getPartialRecord(li); // TODO: get the data ref from message model auto getter = [&partial](size_t idx, size_t part) { - if (!partial[idx].messages.empty() && (partial[idx].messages | get_header{part}).get()) { - auto header = (partial[idx].messages | get_header{part}).get(); - auto payload = (partial[idx].messages | get_payload{part, 0}).get(); + if (!partial[idx].empty() && (partial[idx] | get_header{part}).get()) { + auto header = (partial[idx] | get_header{part}).get(); + auto payload = (partial[idx] | get_payload{part, 0}).get(); return DataRef{nullptr, reinterpret_cast(header->GetData()), reinterpret_cast(payload ? payload->GetData() : nullptr), @@ -801,10 +802,10 @@ void DataRelayer::getReadyToProcess(std::vector& comp return DataRef{}; }; auto nPartsGetter = [&partial](size_t idx) { - return partial[idx].messages | count_parts{}; + return partial[idx] | count_parts{}; }; auto refCountGetter = [&partial](size_t idx) -> int { - auto& header = static_cast(*(partial[idx].messages | get_header{0})); + auto& header = static_cast(*(partial[idx] | get_header{0})); return header.GetRefCount(); }; InputSpan span{getter, nPartsGetter, refCountGetter, static_cast(partial.size())}; @@ -875,13 +876,13 @@ void DataRelayer::updateCacheStatus(TimesliceSlot slot, CacheEntryStatus oldStat } } -std::vector DataRelayer::consumeAllInputsForTimeslice(TimesliceSlot slot) +std::vector> DataRelayer::consumeAllInputsForTimeslice(TimesliceSlot slot) { std::scoped_lock lock(mMutex); const auto numInputTypes = mDistinctRoutesIndex.size(); // State of the computation - std::vector messages(numInputTypes); + std::vector> messages(numInputTypes); auto& cache = mCache; auto& index = mTimesliceIndex; @@ -901,7 +902,7 @@ std::vector DataRelayer::consumeAllInputsForTimeslice cachedStateMetrics[cacheId] = CacheEntryStatus::RUNNING; // TODO: in the original implementation of the cache, there have been only two messages per entry, // check if the 2 above corresponds to the number of messages. - if (!cache[cacheId].messages.empty()) { + if (!cache[cacheId].empty()) { messages[arg] = std::move(cache[cacheId]); } index.markAsInvalid(s); @@ -913,8 +914,8 @@ std::vector DataRelayer::consumeAllInputsForTimeslice // FIXME: what happens when we have enough timeslices to hit the invalid one? auto invalidateCacheFor = [&numInputTypes, &index, &cache](TimesliceSlot s) { for (size_t ai = s.index * numInputTypes, ae = ai + numInputTypes; ai != ae; ++ai) { - assert(std::accumulate(cache[ai].messages.begin(), cache[ai].messages.end(), true, [](bool result, auto const& element) { return result && element.get() == nullptr; })); - cache[ai].messages.clear(); + assert(std::accumulate(cache[ai].begin(), cache[ai].end(), true, [](bool result, auto const& element) { return result && element.get() == nullptr; })); + cache[ai].clear(); } index.markAsInvalid(s); }; @@ -929,13 +930,13 @@ std::vector DataRelayer::consumeAllInputsForTimeslice return messages; } -std::vector DataRelayer::consumeExistingInputsForTimeslice(TimesliceSlot slot) +std::vector> DataRelayer::consumeExistingInputsForTimeslice(TimesliceSlot slot) { std::scoped_lock lock(mMutex); const auto numInputTypes = mDistinctRoutesIndex.size(); // State of the computation - std::vector messages(numInputTypes); + std::vector> messages(numInputTypes); auto& cache = mCache; auto& index = mTimesliceIndex; @@ -955,12 +956,12 @@ std::vector DataRelayer::consumeExistingInputsForTime cachedStateMetrics[cacheId] = CacheEntryStatus::RUNNING; // TODO: in the original implementation of the cache, there have been only two messages per entry, // check if the 2 above corresponds to the number of messages. - for (size_t pi = 0; pi < (cache[cacheId].messages | count_parts{}); pi++) { - auto& header = cache[cacheId].messages | get_header{pi}; + for (size_t pi = 0; pi < (cache[cacheId] | count_parts{}); pi++) { + auto& header = cache[cacheId] | get_header{pi}; auto&& newHeader = header->GetTransport()->CreateMessage(); newHeader->Copy(*header); - messages[arg].messages.emplace_back(std::move(newHeader)); - messages[arg].messages.emplace_back(std::move(cache[cacheId].messages | get_payload{pi, 0})); + messages[arg].emplace_back(std::move(newHeader)); + messages[arg].emplace_back(std::move(cache[cacheId] | get_payload{pi, 0})); } }; @@ -978,7 +979,7 @@ void DataRelayer::clear() std::scoped_lock lock(mMutex); for (auto& cache : mCache) { - cache.messages.clear(); + cache.clear(); } for (size_t s = 0; s < mTimesliceIndex.size(); ++s) { mTimesliceIndex.markAsInvalid(TimesliceSlot{s}); diff --git a/Framework/Core/test/benchmark_DataRelayer.cxx b/Framework/Core/test/benchmark_DataRelayer.cxx index 312711d73e95e..e7df8fbb2fe9b 100644 --- a/Framework/Core/test/benchmark_DataRelayer.cxx +++ b/Framework/Core/test/benchmark_DataRelayer.cxx @@ -14,6 +14,7 @@ #include "Headers/Stack.h" #include "Framework/CompletionPolicyHelpers.h" #include "Framework/DataRelayer.h" +#include "Framework/DataModelViews.h" #include "Framework/DataProcessingHeader.h" #include "Framework/DataProcessingStates.h" #include "Framework/DataProcessingStats.h" @@ -138,8 +139,8 @@ static void BM_RelaySingleSlot(benchmark::State& state) assert(ready[0].op == CompletionPolicy::CompletionOp::Consume); auto result = relayer.consumeAllInputsForTimeslice(ready[0].slot); assert(result.size() == 1); - assert((result.at(0).messages | count_parts{}) == 1); - inflightMessages = std::move(result[0].messages); + assert((result.at(0) | count_parts{}) == 1); + inflightMessages = std::move(result[0]); } } @@ -194,8 +195,8 @@ static void BM_RelayMultipleSlots(benchmark::State& state) assert(ready[0].op == CompletionPolicy::CompletionOp::Consume); auto result = relayer.consumeAllInputsForTimeslice(ready[0].slot); assert(result.size() == 1); - assert((result.at(0).messages | count_parts{}) == 1); - inflightMessages = std::move(result[0].messages); + assert((result.at(0) | count_parts{}) == 1); + inflightMessages = std::move(result[0]); } } @@ -268,11 +269,11 @@ static void BM_RelayMultipleRoutes(benchmark::State& state) assert(ready[0].op == CompletionPolicy::CompletionOp::Consume); auto result = relayer.consumeAllInputsForTimeslice(ready[0].slot); assert(result.size() == 2); - assert((result.at(0).messages | count_parts{}) == 1); - assert((result.at(1).messages | count_parts{}) == 1); - inflightMessages = std::move(result[0].messages); - inflightMessages.emplace_back(std::move(result[1].messages[0])); - inflightMessages.emplace_back(std::move(result[1].messages[1])); + assert((result.at(0) | count_parts{}) == 1); + assert((result.at(1) | count_parts{}) == 1); + inflightMessages = std::move(result[0]); + inflightMessages.emplace_back(std::move(result[1][0])); + inflightMessages.emplace_back(std::move(result[1][1])); } } @@ -332,7 +333,7 @@ static void BM_RelaySplitParts(benchmark::State& state) relayer.getReadyToProcess(ready); assert(ready.size() == 1); assert(ready[0].op == CompletionPolicy::CompletionOp::Consume); - inflightMessages = std::move(relayer.consumeAllInputsForTimeslice(ready[0].slot)[0].messages); + inflightMessages = std::move(relayer.consumeAllInputsForTimeslice(ready[0].slot)[0]); } } @@ -386,7 +387,7 @@ static void BM_RelayMultiplePayloads(benchmark::State& state) relayer.getReadyToProcess(ready); assert(ready.size() == 1); assert(ready[0].op == CompletionPolicy::CompletionOp::Consume); - inflightMessages = std::move(relayer.consumeAllInputsForTimeslice(ready[0].slot)[0].messages); + inflightMessages = std::move(relayer.consumeAllInputsForTimeslice(ready[0].slot)[0]); } } diff --git a/Framework/Core/test/test_DataRelayer.cxx b/Framework/Core/test/test_DataRelayer.cxx index 332a87970eda0..271b7829a9525 100644 --- a/Framework/Core/test/test_DataRelayer.cxx +++ b/Framework/Core/test/test_DataRelayer.cxx @@ -16,6 +16,7 @@ #include "MemoryResources/MemoryResources.h" #include "Framework/CompletionPolicyHelpers.h" #include "Framework/DataRelayer.h" +#include "Framework/DataModelViews.h" #include "Framework/DataProcessingStats.h" #include "Framework/DataProcessingStates.h" #include "Framework/DriverConfig.h" @@ -119,7 +120,7 @@ TEST_CASE("DataRelayer") auto result = relayer.consumeAllInputsForTimeslice(ready[0].slot); // one MessageSet with one PartRef with header and payload REQUIRE(result.size() == 1); - REQUIRE((result.at(0).messages | count_parts{}) == 1); + REQUIRE((result.at(0) | count_parts{}) == 1); } // @@ -169,7 +170,7 @@ TEST_CASE("DataRelayer") auto result = relayer.consumeAllInputsForTimeslice(ready[0].slot); // one MessageSet with one PartRef with header and payload REQUIRE(result.size() == 1); - REQUIRE((result.at(0).messages | count_parts{}) == 1); + REQUIRE((result.at(0) | count_parts{}) == 1); } // This test a more complicated set of inputs, and verifies that data is @@ -249,8 +250,8 @@ TEST_CASE("DataRelayer") auto result = relayer.consumeAllInputsForTimeslice(ready[0].slot); // two MessageSets, each with one PartRef REQUIRE(result.size() == 2); - REQUIRE((result.at(0).messages | count_parts{}) == 1); - REQUIRE((result.at(1).messages | count_parts{}) == 1); + REQUIRE((result.at(0) | count_parts{}) == 1); + REQUIRE((result.at(1) | count_parts{}) == 1); } // This test a more complicated set of inputs, and verifies that data is @@ -737,8 +738,8 @@ TEST_CASE("DataRelayer") // we have one input route and thus one message set containing pairs for all // payloads REQUIRE(messageSet.size() == 1); - REQUIRE((messageSet[0].messages | count_parts{}) == nSplitParts); - REQUIRE((messageSet[0].messages | get_num_payloads{0}) == 1); + REQUIRE((messageSet[0] | count_parts{}) == nSplitParts); + REQUIRE((messageSet[0] | get_num_payloads{0}) == 1); } SECTION("SplitPayloadSequence") @@ -800,13 +801,13 @@ TEST_CASE("DataRelayer") // we have one input route REQUIRE(messageSet.size() == 1); // one message set containing number of added sequences of messages - REQUIRE((messageSet[0].messages | count_parts{}) == sequenceSize.size()); + REQUIRE((messageSet[0] | count_parts{}) == sequenceSize.size()); size_t counter = 0; for (size_t seqid = 0; seqid < sequenceSize.size(); ++seqid) { - REQUIRE((messageSet[0].messages | get_num_payloads{seqid}) == sequenceSize[seqid]); - for (size_t pi = 0; pi < (messageSet[0].messages | get_num_payloads{seqid}); ++pi) { - REQUIRE((messageSet[0].messages | get_payload{seqid, pi})); - auto const* data = (messageSet[0].messages | get_payload{seqid, pi})->GetData(); + REQUIRE((messageSet[0] | get_num_payloads{seqid}) == sequenceSize[seqid]); + for (size_t pi = 0; pi < (messageSet[0] | get_num_payloads{seqid}); ++pi) { + REQUIRE((messageSet[0] | get_payload{seqid, pi})); + auto const* data = (messageSet[0] | get_payload{seqid, pi})->GetData(); REQUIRE(*(reinterpret_cast(data)) == counter); ++counter; } @@ -891,7 +892,7 @@ TEST_CASE("DataRelayer") auto result = relayer.consumeAllInputsForTimeslice(ready[0].slot); REQUIRE(result.size() == 1); - REQUIRE((result.at(0).messages | count_parts{}) == 1); + REQUIRE((result.at(0) | count_parts{}) == 1); } SECTION("ProcessDanglingInputsSkipsWhenDataPresent") diff --git a/Framework/Core/test/test_ForwardInputs.cxx b/Framework/Core/test/test_ForwardInputs.cxx index 6da42c5a94aca..0263158ee0f9b 100644 --- a/Framework/Core/test/test_ForwardInputs.cxx +++ b/Framework/Core/test/test_ForwardInputs.cxx @@ -16,7 +16,7 @@ #include "Framework/SourceInfoHeader.h" #include "Framework/DomainInfoHeader.h" #include "Framework/Signpost.h" -#include "Framework/MessageSet.h" +#include "Framework/DataModelViews.h" #include "Framework/FairMQDeviceProxy.h" #include "Headers/Stack.h" #include "MemoryResources/MemoryResources.h" @@ -43,7 +43,7 @@ TEST_CASE("ForwardInputsEmpty") bool copyByDefault = true; FairMQDeviceProxy proxy; - std::vector currentSetOfInputs; + std::vector> currentSetOfInputs; auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); REQUIRE(result.empty()); @@ -84,16 +84,16 @@ TEST_CASE("ForwardInputsSingleMessageSingleRoute") proxy.bind({}, {}, routes, findChannelByName, nullptr); - std::vector currentSetOfInputs; - MessageSet messageSet; + std::vector> currentSetOfInputs; + std::vector messageSet; auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); fair::mq::MessagePtr payload(transport->CreateMessage()); auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); auto header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph}); - messageSet.messages.emplace_back(std::move(header)); - messageSet.messages.emplace_back(std::move(payload)); - REQUIRE((messageSet.messages | count_parts{}) == 1); + messageSet.emplace_back(std::move(header)); + messageSet.emplace_back(std::move(payload)); + REQUIRE((messageSet | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); @@ -135,17 +135,17 @@ TEST_CASE("ForwardInputsSingleMessageSingleRouteNoConsume") proxy.bind({}, {}, routes, findChannelByName, nullptr); - std::vector currentSetOfInputs; - MessageSet messageSet; + std::vector> currentSetOfInputs; + std::vector messageSet; auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); fair::mq::MessagePtr payload(nullptr); REQUIRE(payload.get() == nullptr); auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); auto header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph}); - messageSet.messages.emplace_back(std::move(header)); - messageSet.messages.emplace_back(std::move(payload)); - REQUIRE((messageSet.messages | count_parts{}) == 1); + messageSet.emplace_back(std::move(header)); + messageSet.emplace_back(std::move(payload)); + REQUIRE((messageSet | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, true); @@ -191,17 +191,17 @@ TEST_CASE("ForwardInputsSingleMessageSingleRouteAtEOS") proxy.bind({}, {}, routes, findChannelByName, nullptr); - std::vector currentSetOfInputs; - MessageSet messageSet; + std::vector> currentSetOfInputs; + std::vector messageSet; auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); fair::mq::MessagePtr payload(transport->CreateMessage()); auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); auto header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph, sih}); REQUIRE(o2::header::get(header->GetData())); - messageSet.messages.emplace_back(std::move(header)); - messageSet.messages.emplace_back(std::move(payload)); - REQUIRE((messageSet.messages | count_parts{}) == 1); + messageSet.emplace_back(std::move(header)); + messageSet.emplace_back(std::move(payload)); + REQUIRE((messageSet | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); @@ -250,17 +250,17 @@ TEST_CASE("ForwardInputsSingleMessageSingleRouteWithOldestPossible") proxy.bind({}, {}, routes, findChannelByName, nullptr); - std::vector currentSetOfInputs; - MessageSet messageSet; + std::vector> currentSetOfInputs; + std::vector messageSet; auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); fair::mq::MessagePtr payload(transport->CreateMessage()); auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); auto header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph, dih}); REQUIRE(o2::header::get(header->GetData())); - messageSet.messages.emplace_back(std::move(header)); - messageSet.messages.emplace_back(std::move(payload)); - REQUIRE((messageSet.messages | count_parts{}) == 1); + messageSet.emplace_back(std::move(header)); + messageSet.emplace_back(std::move(payload)); + REQUIRE((messageSet | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); @@ -317,16 +317,16 @@ TEST_CASE("ForwardInputsSingleMessageMultipleRoutes") proxy.bind({}, {}, routes, findChannelByName, nullptr); - std::vector currentSetOfInputs; - MessageSet messageSet; + std::vector> currentSetOfInputs; + std::vector messageSet; auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); fair::mq::MessagePtr payload(transport->CreateMessage()); auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); auto header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph}); - messageSet.messages.emplace_back(std::move(header)); - messageSet.messages.emplace_back(std::move(payload)); - REQUIRE((messageSet.messages | count_parts{}) == 1); + messageSet.emplace_back(std::move(header)); + messageSet.emplace_back(std::move(payload)); + REQUIRE((messageSet | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); @@ -381,16 +381,16 @@ TEST_CASE("ForwardInputsSingleMessageMultipleRoutesExternals") proxy.bind({}, {}, routes, findChannelByName, nullptr); - std::vector currentSetOfInputs; - MessageSet messageSet; + std::vector> currentSetOfInputs; + std::vector messageSet; auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); fair::mq::MessagePtr payload(transport->CreateMessage()); auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); auto header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph}); - messageSet.messages.emplace_back(std::move(header)); - messageSet.messages.emplace_back(std::move(payload)); - REQUIRE((messageSet.messages | count_parts{}) == 1); + messageSet.emplace_back(std::move(header)); + messageSet.emplace_back(std::move(payload)); + REQUIRE((messageSet | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); @@ -452,23 +452,23 @@ TEST_CASE("ForwardInputsMultiMessageMultipleRoutes") proxy.bind({}, {}, routes, findChannelByName, nullptr); - std::vector currentSetOfInputs; + std::vector> currentSetOfInputs; auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); fair::mq::MessagePtr payload1(transport->CreateMessage()); fair::mq::MessagePtr payload2(transport->CreateMessage()); auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); auto header1 = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh1, dph}); - MessageSet messageSet1; - messageSet1.messages.emplace_back(std::move(header1)); - messageSet1.messages.emplace_back(std::move(payload1)); - REQUIRE((messageSet1.messages | count_parts{}) == 1); + std::vector messageSet1; + messageSet1.emplace_back(std::move(header1)); + messageSet1.emplace_back(std::move(payload1)); + REQUIRE((messageSet1 | count_parts{}) == 1); auto header2 = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh2, dph}); - MessageSet messageSet2; - messageSet2.messages.emplace_back(std::move(header2)); - messageSet2.messages.emplace_back(std::move(payload2)); - REQUIRE((messageSet2.messages | count_parts{}) == 1); + std::vector messageSet2; + messageSet2.emplace_back(std::move(header2)); + messageSet2.emplace_back(std::move(payload2)); + REQUIRE((messageSet2 | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet1)); currentSetOfInputs.emplace_back(std::move(messageSet2)); REQUIRE(currentSetOfInputs.size() == 2); @@ -525,16 +525,16 @@ TEST_CASE("ForwardInputsSingleMessageMultipleRoutesOnlyOneMatches") proxy.bind({}, {}, routes, findChannelByName, nullptr); - std::vector currentSetOfInputs; - MessageSet messageSet; + std::vector> currentSetOfInputs; + std::vector messageSet; auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); fair::mq::MessagePtr payload(transport->CreateMessage()); auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); auto header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph}); - messageSet.messages.emplace_back(std::move(header)); - messageSet.messages.emplace_back(std::move(payload)); - REQUIRE((messageSet.messages | count_parts{}) == 1); + messageSet.emplace_back(std::move(header)); + messageSet.emplace_back(std::move(payload)); + REQUIRE((messageSet | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); @@ -596,8 +596,8 @@ TEST_CASE("ForwardInputsSplitPayload") proxy.bind({}, {}, routes, findChannelByName, nullptr); - std::vector currentSetOfInputs; - MessageSet messageSet; + std::vector> currentSetOfInputs; + std::vector messageSet; auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); fair::mq::MessagePtr payload1(transport->CreateMessage()); @@ -612,14 +612,13 @@ TEST_CASE("ForwardInputsSplitPayload") return std::move(messages[t]); }; for (size_t i = 0; i < 3; ++i) { - messageSet.messages.emplace_back(fillMessages(i)); + messageSet.emplace_back(fillMessages(i)); } auto header2 = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh2, dph}); - PartRef part{std::move(header2), transport->CreateMessage()}; - messageSet.messages.emplace_back(std::move(part.header)); - messageSet.messages.emplace_back(std::move(part.payload)); + messageSet.emplace_back(std::move(header2)); + messageSet.emplace_back(transport->CreateMessage()); - REQUIRE((messageSet.messages | count_parts{}) == 2); + REQUIRE((messageSet | count_parts{}) == 2); currentSetOfInputs.emplace_back(std::move(messageSet)); auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); @@ -731,16 +730,16 @@ TEST_CASE("ForwardInputEOSSingleRoute") proxy.bind({}, {}, routes, findChannelByName, nullptr); - std::vector currentSetOfInputs; - MessageSet messageSet; + std::vector> currentSetOfInputs; + std::vector messageSet; auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); fair::mq::MessagePtr payload(transport->CreateMessage()); auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); auto header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, sih}); - messageSet.messages.emplace_back(std::move(header)); - messageSet.messages.emplace_back(std::move(payload)); - REQUIRE((messageSet.messages | count_parts{}) == 1); + messageSet.emplace_back(std::move(header)); + messageSet.emplace_back(std::move(payload)); + REQUIRE((messageSet | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); @@ -777,16 +776,16 @@ TEST_CASE("ForwardInputOldestPossibleSingleRoute") proxy.bind({}, {}, routes, findChannelByName, nullptr); - std::vector currentSetOfInputs; - MessageSet messageSet; + std::vector> currentSetOfInputs; + std::vector messageSet; auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); fair::mq::MessagePtr payload(transport->CreateMessage()); auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); auto header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dih}); - messageSet.messages.emplace_back(std::move(header)); - messageSet.messages.emplace_back(std::move(payload)); - REQUIRE((messageSet.messages | count_parts{}) == 1); + messageSet.emplace_back(std::move(header)); + messageSet.emplace_back(std::move(payload)); + REQUIRE((messageSet | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); diff --git a/Framework/Core/test/test_MessageSet.cxx b/Framework/Core/test/test_MessageSet.cxx index d62a804e7681d..caa9a60323306 100644 --- a/Framework/Core/test/test_MessageSet.cxx +++ b/Framework/Core/test/test_MessageSet.cxx @@ -11,9 +11,9 @@ #include #include -#include "Framework/MessageSet.h" #include "Framework/DataModelViews.h" #include "Framework/DataProcessingHeader.h" +#include "Framework/PartRef.h" #include "Headers/Stack.h" #include "Headers/DataHeader.h" #include "MemoryResources/MemoryResources.h" @@ -23,7 +23,7 @@ using namespace o2::framework; TEST_CASE("MessageSet") { - o2::framework::MessageSet msgSet; + std::vector messages; o2::header::DataHeader dh{}; dh.splitPayloadParts = 0; dh.splitPayloadIndex = 0; @@ -37,21 +37,18 @@ TEST_CASE("MessageSet") ptrs.emplace_back(std::move(header)); ptrs.emplace_back(std::move(msg2)); for (size_t i = 0; i < 2; ++i) { - msgSet.messages.emplace_back(std::move(ptrs[i])); + messages.emplace_back(std::move(ptrs[i])); } - REQUIRE(msgSet.messages.size() == 2); - REQUIRE((msgSet.messages | count_payloads{}) == 1); - REQUIRE((msgSet.messages | get_dataref_indices{0, 0}).headerIdx == 0); - REQUIRE((msgSet.messages | get_dataref_indices{0, 0}).payloadIdx == 1); - REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); - REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); - CHECK_THROWS((msgSet.messages | get_pair{1})); - REQUIRE((msgSet.messages | get_num_payloads{0}) == 1); - REQUIRE((msgSet.messages | count_parts{}) == 1); - // messages: [hdr, pl] — one pair - REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); - REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); + REQUIRE(messages.size() == 2); + REQUIRE((messages | count_payloads{}) == 1); + REQUIRE((messages | get_dataref_indices{0, 0}).headerIdx == 0); + REQUIRE((messages | get_dataref_indices{0, 0}).payloadIdx == 1); + REQUIRE((messages | get_pair{0}).headerIdx == 0); + REQUIRE((messages | get_pair{0}).payloadIdx == 1); + CHECK_THROWS((messages | get_pair{1})); + REQUIRE((messages | get_num_payloads{0}) == 1); + REQUIRE((messages | count_parts{}) == 1); } TEST_CASE("MessageSetWithFunction") @@ -68,17 +65,20 @@ TEST_CASE("MessageSetWithFunction") std::unique_ptr msg2(nullptr); ptrs.emplace_back(std::move(header)); ptrs.emplace_back(std::move(msg2)); - o2::framework::MessageSet msgSet([&ptrs](size_t i) -> fair::mq::MessagePtr& { return ptrs[i]; }, 2); - - REQUIRE(msgSet.messages.size() == 2); - REQUIRE((msgSet.messages | count_payloads{}) == 1); - REQUIRE((msgSet.messages | get_dataref_indices{0, 0}).headerIdx == 0); - REQUIRE((msgSet.messages | get_dataref_indices{0, 0}).payloadIdx == 1); - REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); - REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); - CHECK_THROWS((msgSet.messages | get_pair{1})); - REQUIRE((msgSet.messages | get_num_payloads{0}) == 1); - REQUIRE((msgSet.messages | count_parts{}) == 1); + std::vector messages; + for (size_t i = 0; i < 2; ++i) { + messages.emplace_back(std::move(ptrs[i])); + } + + REQUIRE(messages.size() == 2); + REQUIRE((messages | count_payloads{}) == 1); + REQUIRE((messages | get_dataref_indices{0, 0}).headerIdx == 0); + REQUIRE((messages | get_dataref_indices{0, 0}).payloadIdx == 1); + REQUIRE((messages | get_pair{0}).headerIdx == 0); + REQUIRE((messages | get_pair{0}).payloadIdx == 1); + CHECK_THROWS((messages | get_pair{1})); + REQUIRE((messages | get_num_payloads{0}) == 1); + REQUIRE((messages | count_parts{}) == 1); } TEST_CASE("MessageSetWithMultipart") @@ -97,46 +97,40 @@ TEST_CASE("MessageSetWithMultipart") ptrs.emplace_back(std::move(header)); ptrs.emplace_back(std::move(msg2)); ptrs.emplace_back(std::move(msg3)); - o2::framework::MessageSet msgSet([&ptrs](size_t i) -> fair::mq::MessagePtr& { return ptrs[i]; }, 3); - - REQUIRE(msgSet.messages.size() == 3); - REQUIRE((msgSet.messages | count_payloads{}) == 2); - REQUIRE((msgSet.messages | get_dataref_indices{0, 0}).headerIdx == 0); - REQUIRE((msgSet.messages | get_dataref_indices{0, 0}).payloadIdx == 1); - REQUIRE((msgSet.messages | get_dataref_indices{0, 1}).headerIdx == 0); - REQUIRE((msgSet.messages | get_dataref_indices{0, 1}).payloadIdx == 2); - REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); - REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); - REQUIRE((msgSet.messages | get_pair{1}).headerIdx == 0); - REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 2); - CHECK_THROWS((msgSet.messages | get_pair{2})); - REQUIRE((msgSet.messages | get_num_payloads{0}) == 2); - REQUIRE((msgSet.messages | count_parts{}) == 1); - // messages: [hdr, pl0, pl1] — one header, two payloads - REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); - REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); - REQUIRE((msgSet.messages | get_pair{1}).headerIdx == 0); - REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 2); + std::vector messages; + for (size_t i = 0; i < 3; ++i) { + messages.emplace_back(std::move(ptrs[i])); + } + + REQUIRE(messages.size() == 3); + REQUIRE((messages | count_payloads{}) == 2); + REQUIRE((messages | get_dataref_indices{0, 0}).headerIdx == 0); + REQUIRE((messages | get_dataref_indices{0, 0}).payloadIdx == 1); + REQUIRE((messages | get_dataref_indices{0, 1}).headerIdx == 0); + REQUIRE((messages | get_dataref_indices{0, 1}).payloadIdx == 2); + REQUIRE((messages | get_pair{0}).headerIdx == 0); + REQUIRE((messages | get_pair{0}).payloadIdx == 1); + REQUIRE((messages | get_pair{1}).headerIdx == 0); + REQUIRE((messages | get_pair{1}).payloadIdx == 2); + CHECK_THROWS((messages | get_pair{2})); + REQUIRE((messages | get_num_payloads{0}) == 2); + REQUIRE((messages | count_parts{}) == 1); } TEST_CASE("MessageSetAddPartRef") { - std::vector ptrs; std::unique_ptr msg(nullptr); std::unique_ptr msg2(nullptr); - ptrs.emplace_back(std::move(msg)); - ptrs.emplace_back(std::move(msg2)); PartRef ref{std::move(msg), std::move(msg2)}; - o2::framework::MessageSet msgSet; - msgSet.messages.emplace_back(std::move(ref.header)); - msgSet.messages.emplace_back(std::move(ref.payload)); + std::vector messages; + messages.emplace_back(std::move(ref.header)); + messages.emplace_back(std::move(ref.payload)); - REQUIRE(msgSet.messages.size() == 2); + REQUIRE(messages.size() == 2); } TEST_CASE("MessageSetAddMultiple") { - std::vector ptrs; o2::header::DataHeader dh1{}; dh1.splitPayloadParts = 0; dh1.splitPayloadIndex = 0; @@ -148,109 +142,99 @@ TEST_CASE("MessageSetAddMultiple") dh3.splitPayloadIndex = 2; o2::framework::DataProcessingHeader dph{0, 1}; auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); - fair::mq::MessagePtr payload(transport->CreateMessage()); auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); fair::mq::MessagePtr header1 = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh1, dph}); fair::mq::MessagePtr header2 = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh2, dph}); fair::mq::MessagePtr header3 = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh3, dph}); - std::unique_ptr msg2(nullptr); - std::unique_ptr msg3(nullptr); - PartRef ref{std::move(header1), std::move(msg2)}; - o2::framework::MessageSet msgSet; - msgSet.messages.emplace_back(std::move(ref.header)); - msgSet.messages.emplace_back(std::move(ref.payload)); - PartRef ref2{std::move(header2), std::move(msg2)}; - msgSet.messages.emplace_back(std::move(ref2.header)); - msgSet.messages.emplace_back(std::move(ref2.payload)); - std::vector msgs; - msgs.push_back(std::move(header3)); - msgs.push_back(std::unique_ptr(nullptr)); - msgs.push_back(std::unique_ptr(nullptr)); - for (size_t i = 0; i < 3; ++i) { - msgSet.messages.emplace_back(std::move(msgs[i])); - } - - REQUIRE(msgSet.messages.size() == 7); - - REQUIRE((msgSet.messages | count_payloads{}) == 4); - REQUIRE((msgSet.messages | get_dataref_indices{0, 0}).headerIdx == 0); - REQUIRE((msgSet.messages | get_dataref_indices{0, 0}).payloadIdx == 1); - REQUIRE((msgSet.messages | get_dataref_indices{1, 0}).headerIdx == 2); - REQUIRE((msgSet.messages | get_dataref_indices{1, 0}).payloadIdx == 3); - REQUIRE((msgSet.messages | get_dataref_indices{2, 0}).headerIdx == 4); - REQUIRE((msgSet.messages | get_dataref_indices{2, 0}).payloadIdx == 5); - REQUIRE((msgSet.messages | get_dataref_indices{2, 1}).headerIdx == 4); - REQUIRE((msgSet.messages | get_dataref_indices{2, 1}).payloadIdx == 6); - REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); - REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); - REQUIRE((msgSet.messages | get_pair{1}).headerIdx == 2); - REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 3); - REQUIRE((msgSet.messages | get_pair{2}).headerIdx == 4); - REQUIRE((msgSet.messages | get_pair{2}).payloadIdx == 5); - REQUIRE((msgSet.messages | get_pair{3}).headerIdx == 4); - REQUIRE((msgSet.messages | get_pair{3}).payloadIdx == 6); - REQUIRE((msgSet.messages | get_num_payloads{0}) == 1); - REQUIRE((msgSet.messages | get_num_payloads{1}) == 1); - REQUIRE((msgSet.messages | get_num_payloads{2}) == 2); - REQUIRE((msgSet.messages | count_parts{}) == 3); - REQUIRE((msgSet.messages | count_payloads{}) == 4); + std::vector messages; + // part 0: dh1 (splitPayloadParts=0) — standard pair + messages.emplace_back(std::move(header1)); + messages.emplace_back(std::unique_ptr(nullptr)); + // part 1: dh2 (splitPayloadParts=1) — traditional split, one pair + messages.emplace_back(std::move(header2)); + messages.emplace_back(std::unique_ptr(nullptr)); + // part 2: dh3 (splitPayloadParts=2, splitPayloadIndex=2) — multi-payload, two payloads + messages.emplace_back(std::move(header3)); + messages.emplace_back(std::unique_ptr(nullptr)); + messages.emplace_back(std::unique_ptr(nullptr)); + + REQUIRE(messages.size() == 7); + + REQUIRE((messages | count_payloads{}) == 4); + REQUIRE((messages | get_dataref_indices{0, 0}).headerIdx == 0); + REQUIRE((messages | get_dataref_indices{0, 0}).payloadIdx == 1); + REQUIRE((messages | get_dataref_indices{1, 0}).headerIdx == 2); + REQUIRE((messages | get_dataref_indices{1, 0}).payloadIdx == 3); + REQUIRE((messages | get_dataref_indices{2, 0}).headerIdx == 4); + REQUIRE((messages | get_dataref_indices{2, 0}).payloadIdx == 5); + REQUIRE((messages | get_dataref_indices{2, 1}).headerIdx == 4); + REQUIRE((messages | get_dataref_indices{2, 1}).payloadIdx == 6); + REQUIRE((messages | get_pair{0}).headerIdx == 0); + REQUIRE((messages | get_pair{0}).payloadIdx == 1); + REQUIRE((messages | get_pair{1}).headerIdx == 2); + REQUIRE((messages | get_pair{1}).payloadIdx == 3); + REQUIRE((messages | get_pair{2}).headerIdx == 4); + REQUIRE((messages | get_pair{2}).payloadIdx == 5); + REQUIRE((messages | get_pair{3}).headerIdx == 4); + REQUIRE((messages | get_pair{3}).payloadIdx == 6); + REQUIRE((messages | get_num_payloads{0}) == 1); + REQUIRE((messages | get_num_payloads{1}) == 1); + REQUIRE((messages | get_num_payloads{2}) == 2); + REQUIRE((messages | count_parts{}) == 3); + REQUIRE((messages | count_payloads{}) == 4); } TEST_CASE("GetHeaderPayloadOperators") { - // Validates that get_header{part} / get_payload{part, 0} pipe operators on .messages - // correctly replace the removed header(part) / payload(part) methods, - // including access to parts at index > 0. + // Validates that get_header{part} / get_payload{part, 0} pipe operators + // correctly return the right messages, including access to parts at index > 0. o2::framework::DataProcessingHeader dph{0, 1}; auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); - o2::framework::MessageSet msgSet; + std::vector messages; // Add two separate header-payload pairs for (size_t part = 0; part < 2; ++part) { o2::header::DataHeader dh{}; dh.dataDescription = "CLUSTERS"; dh.dataOrigin = "TPC"; - dh.subSpecification = part; // use part index as subSpecification to distinguish + dh.subSpecification = part; dh.splitPayloadParts = 1; dh.splitPayloadIndex = 0; - std::vector ptrs; - ptrs.emplace_back(o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph})); - ptrs.emplace_back(transport->CreateMessage(100 + part * 100)); // 100 and 200 bytes - msgSet.add([&ptrs](size_t i) -> fair::mq::MessagePtr& { return ptrs[i]; }, 2); + messages.emplace_back(o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph})); + messages.emplace_back(transport->CreateMessage(100 + part * 100)); } - REQUIRE(msgSet.messages.size() == 4); + REQUIRE(messages.size() == 4); // Validate part 0 - auto& hdr0 = msgSet.messages | get_header{0}; + auto& hdr0 = messages | get_header{0}; REQUIRE(hdr0.get() != nullptr); auto* dh0 = o2::header::get(hdr0->GetData()); REQUIRE(dh0 != nullptr); REQUIRE(dh0->subSpecification == 0); - auto& pl0 = msgSet.messages | get_payload{0, 0}; + auto& pl0 = messages | get_payload{0, 0}; REQUIRE(pl0.get() != nullptr); REQUIRE(pl0->GetSize() == 100); // Validate part 1 - auto& hdr1 = msgSet.messages | get_header{1}; + auto& hdr1 = messages | get_header{1}; REQUIRE(hdr1.get() != nullptr); auto* dh1 = o2::header::get(hdr1->GetData()); REQUIRE(dh1 != nullptr); REQUIRE(dh1->subSpecification == 1); - auto& pl1 = msgSet.messages | get_payload{1, 0}; + auto& pl1 = messages | get_payload{1, 0}; REQUIRE(pl1.get() != nullptr); REQUIRE(pl1->GetSize() == 200); - REQUIRE((msgSet.messages | count_parts{}) == 2); - REQUIRE((msgSet.messages | count_payloads{}) == 2); - // messages: [hdr0, pl0, hdr1, pl1] — two standard pairs - REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); - REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); - REQUIRE((msgSet.messages | get_pair{1}).headerIdx == 2); - REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 3); + REQUIRE((messages | count_parts{}) == 2); + REQUIRE((messages | count_payloads{}) == 2); + REQUIRE((messages | get_pair{0}).headerIdx == 0); + REQUIRE((messages | get_pair{0}).payloadIdx == 1); + REQUIRE((messages | get_pair{1}).headerIdx == 2); + REQUIRE((messages | get_pair{1}).payloadIdx == 3); } TEST_CASE("GetHeaderPayloadMultiPayload") @@ -264,7 +248,7 @@ TEST_CASE("GetHeaderPayloadMultiPayload") auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); - o2::framework::MessageSet msgSet; + std::vector messages; // Part 0: standard header-payload pair { @@ -274,10 +258,8 @@ TEST_CASE("GetHeaderPayloadMultiPayload") dh.subSpecification = 0; dh.splitPayloadParts = 1; dh.splitPayloadIndex = 0; - std::vector ptrs; - ptrs.emplace_back(o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph})); - ptrs.emplace_back(transport->CreateMessage(100)); - msgSet.add([&ptrs](size_t i) -> fair::mq::MessagePtr& { return ptrs[i]; }, 2); + messages.emplace_back(o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph})); + messages.emplace_back(transport->CreateMessage(100)); } // Part 1: one header with 3 payloads (splitPayloadIndex == splitPayloadParts) @@ -287,81 +269,67 @@ TEST_CASE("GetHeaderPayloadMultiPayload") dh.dataOrigin = "TPC"; dh.subSpecification = 1; dh.splitPayloadParts = 3; - dh.splitPayloadIndex = 3; // signals multi-payload layout - std::vector ptrs; - ptrs.emplace_back(o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph})); - ptrs.emplace_back(transport->CreateMessage(200)); - ptrs.emplace_back(transport->CreateMessage(300)); - ptrs.emplace_back(transport->CreateMessage(400)); - msgSet.add([&ptrs](size_t i) -> fair::mq::MessagePtr& { return ptrs[i]; }, 4); + dh.splitPayloadIndex = 3; + messages.emplace_back(o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph})); + messages.emplace_back(transport->CreateMessage(200)); + messages.emplace_back(transport->CreateMessage(300)); + messages.emplace_back(transport->CreateMessage(400)); } // messages: [hdr0, pl0, hdr1, pl1_0, pl1_1, pl1_2] - REQUIRE(msgSet.messages.size() == 6); + REQUIRE(messages.size() == 6); - // Part 0: standard - auto& hdr0 = msgSet.messages | get_header{0}; + // Part 0 + auto& hdr0 = messages | get_header{0}; REQUIRE(hdr0.get() != nullptr); auto* dh0 = o2::header::get(hdr0->GetData()); REQUIRE(dh0->subSpecification == 0); - auto& pl0 = msgSet.messages | get_payload{0, 0}; + auto& pl0 = messages | get_payload{0, 0}; REQUIRE(pl0.get() != nullptr); REQUIRE(pl0->GetSize() == 100); // Part 1: multi-payload header - auto& hdr1 = msgSet.messages | get_header{1}; + auto& hdr1 = messages | get_header{1}; REQUIRE(hdr1.get() != nullptr); auto* dh1 = o2::header::get(hdr1->GetData()); REQUIRE(dh1->subSpecification == 1); - // get_payload{1, 0} — first payload of part 1 - auto& pl1_0 = msgSet.messages | get_payload{1, 0}; + auto& pl1_0 = messages | get_payload{1, 0}; REQUIRE(pl1_0.get() != nullptr); REQUIRE(pl1_0->GetSize() == 200); - // get_payload{1, 1} — second payload of part 1 (nonzero, nonzero) - auto& pl1_1 = msgSet.messages | get_payload{1, 1}; + auto& pl1_1 = messages | get_payload{1, 1}; REQUIRE(pl1_1.get() != nullptr); REQUIRE(pl1_1->GetSize() == 300); - // get_payload{1, 2} — third payload of part 1 (nonzero, nonzero) - auto& pl1_2 = msgSet.messages | get_payload{1, 2}; + auto& pl1_2 = messages | get_payload{1, 2}; REQUIRE(pl1_2.get() != nullptr); REQUIRE(pl1_2->GetSize() == 400); - // count_payloads should report 4 total (1 from part 0 + 3 from part 1) - REQUIRE((msgSet.messages | count_payloads{}) == 4); - // count_parts should report 2 (one per header) - REQUIRE((msgSet.messages | count_parts{}) == 2); - // get_num_payloads for part 1 should be 3 - REQUIRE((msgSet.messages | get_num_payloads{1}) == 3); - - REQUIRE((msgSet.messages | get_num_payloads{0}) == 1); - REQUIRE((msgSet.messages | get_num_payloads{1}) == 3); - REQUIRE((msgSet.messages | count_parts{}) == 2); - REQUIRE((msgSet.messages | count_payloads{}) == 4); - // messages: [hdr0, pl0, hdr1, pl1_0, pl1_1, pl1_2] - REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); - REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); - REQUIRE((msgSet.messages | get_pair{1}).headerIdx == 2); - REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 3); - REQUIRE((msgSet.messages | get_pair{2}).headerIdx == 2); - REQUIRE((msgSet.messages | get_pair{2}).payloadIdx == 4); - REQUIRE((msgSet.messages | get_pair{3}).headerIdx == 2); - REQUIRE((msgSet.messages | get_pair{3}).payloadIdx == 5); + REQUIRE((messages | get_num_payloads{0}) == 1); + REQUIRE((messages | get_num_payloads{1}) == 3); + REQUIRE((messages | count_parts{}) == 2); + REQUIRE((messages | count_payloads{}) == 4); + REQUIRE((messages | get_pair{0}).headerIdx == 0); + REQUIRE((messages | get_pair{0}).payloadIdx == 1); + REQUIRE((messages | get_pair{1}).headerIdx == 2); + REQUIRE((messages | get_pair{1}).payloadIdx == 3); + REQUIRE((messages | get_pair{2}).headerIdx == 2); + REQUIRE((messages | get_pair{2}).payloadIdx == 4); + REQUIRE((messages | get_pair{3}).headerIdx == 2); + REQUIRE((messages | get_pair{3}).payloadIdx == 5); } TEST_CASE("TraditionalSplitParts") { // Validates operators with traditional split parts layout: // 3 (header, payload) pairs where splitPayloadParts=3, splitPayloadIndex=0,1,2 - // This is ONE logical part with 3 subparts. // Memory layout: [hdr0, pl0, hdr1, pl1, hdr2, pl2] o2::framework::DataProcessingHeader dph{0, 1}; auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); - o2::framework::MessageSet msgSet; + std::vector messages; for (size_t i = 0; i < 3; ++i) { o2::header::DataHeader dh{}; @@ -370,57 +338,42 @@ TEST_CASE("TraditionalSplitParts") dh.subSpecification = 0; dh.splitPayloadParts = 3; dh.splitPayloadIndex = i; - std::vector ptrs; - ptrs.emplace_back(o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph})); - ptrs.emplace_back(transport->CreateMessage(100 * (i + 1))); - msgSet.add([&ptrs](size_t idx) -> fair::mq::MessagePtr& { return ptrs[idx]; }, 2); + messages.emplace_back(o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph})); + messages.emplace_back(transport->CreateMessage(100 * (i + 1))); } - REQUIRE(msgSet.messages.size() == 6); + REQUIRE(messages.size() == 6); - // count_payloads: 3 traditional split parts = 3 payloads - REQUIRE((msgSet.messages | count_payloads{}) == 3); - // count_parts: one logical entity split into 3 pairs = 3 parts - REQUIRE((msgSet.messages | count_parts{}) == 3); + REQUIRE((messages | count_payloads{}) == 3); + REQUIRE((messages | count_parts{}) == 3); - // Each traditional split pair is a separate part, matching MessageSet::header(part) semantics for (size_t i = 0; i < 3; ++i) { - auto& hdr = msgSet.messages | get_header{i}; + auto& hdr = messages | get_header{i}; REQUIRE(hdr.get() != nullptr); auto* dh = o2::header::get(hdr->GetData()); REQUIRE(dh != nullptr); REQUIRE(dh->splitPayloadIndex == i); - auto& pl = msgSet.messages | get_payload{i, 0}; + auto& pl = messages | get_payload{i, 0}; REQUIRE(pl.get() != nullptr); REQUIRE(pl->GetSize() == 100 * (i + 1)); } - // get_dataref_indices: each part maps to its own (header, payload) pair for (size_t i = 0; i < 3; ++i) { - auto indices = msgSet.messages | get_dataref_indices{i, 0}; + auto indices = messages | get_dataref_indices{i, 0}; REQUIRE(indices.headerIdx == 2 * i); REQUIRE(indices.payloadIdx == 2 * i + 1); } - // get_pair: same as get_dataref_indices for traditional split for (size_t i = 0; i < 3; ++i) { - auto indices = msgSet.messages | get_pair{i}; + auto indices = messages | get_pair{i}; REQUIRE(indices.headerIdx == 2 * i); REQUIRE(indices.payloadIdx == 2 * i + 1); } - // get_num_payloads: each traditional split pair has 1 payload for (size_t i = 0; i < 3; ++i) { - REQUIRE((msgSet.messages | get_num_payloads{i}) == 1); + REQUIRE((messages | get_num_payloads{i}) == 1); } - REQUIRE((msgSet.messages | count_parts{}) == 3); - REQUIRE((msgSet.messages | count_payloads{}) == 3); - // messages: [hdr0, pl0, hdr1, pl1, hdr2, pl2] — three traditional split pairs - REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); - REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); - REQUIRE((msgSet.messages | get_pair{1}).headerIdx == 2); - REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 3); - REQUIRE((msgSet.messages | get_pair{2}).headerIdx == 4); - REQUIRE((msgSet.messages | get_pair{2}).payloadIdx == 5); + REQUIRE((messages | count_parts{}) == 3); + REQUIRE((messages | count_payloads{}) == 3); } From 33b11d46481332f1817393dad1b040c7c49ad28e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Wed, 25 Mar 2026 11:46:13 +0100 Subject: [PATCH 019/285] Add optional dependency for Acts package --- dependencies/O2Dependencies.cmake | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dependencies/O2Dependencies.cmake b/dependencies/O2Dependencies.cmake index 850ba0b909acc..b46dabb5690ff 100644 --- a/dependencies/O2Dependencies.cmake +++ b/dependencies/O2Dependencies.cmake @@ -143,6 +143,9 @@ set_package_properties(fmt PROPERTIES TYPE REQUIRED) find_package(nlohmann_json) set_package_properties(nlohmann_json PROPERTIES TYPE REQUIRED) +find_package(Acts) +set_package_properties(Acts PROPERTIES TYPE OPTIONAL) + find_package(Boost 1.70 COMPONENTS container thread From 198457e3462747b8eaf485ac86ec7d2617c61e98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Mon, 30 Mar 2026 17:14:01 +0200 Subject: [PATCH 020/285] Add fcolamar as owner for ALICE3 upgrades (#15243) --- CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index 369a7cf8a8463..f54738e2ce4e3 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -73,7 +73,7 @@ /Detectors/TPC @davidrohr @wiechula @shahor02 /Detectors/TRD @f3sch @bazinski @wille10 /Detectors/Upgrades @mconcas -/Detectors/Upgrades/ALICE3 @mconcas @njacazio +/Detectors/Upgrades/ALICE3 @mconcas @njacazio @fcolamar /Detectors/Upgrades/ITS3 @fgrosa @arossi81 @mconcas @f3sch /Detectors/ZDC @coppedis @cortesep /Detectors/CTF @shahor02 From 1b673ecbb297411272924776583a67a7a5335a81 Mon Sep 17 00:00:00 2001 From: Marco van Leeuwen Date: Mon, 30 Mar 2026 17:22:46 +0200 Subject: [PATCH 021/285] [ALICE3] Fix extrusions in forward tracker (#15242) --- Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx | 2 +- Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx index 453d90501802e..333599c85eab6 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx @@ -390,7 +390,7 @@ void FT3Layer::createLayer(TGeoVolume* motherVolume) std::string separationLayerName = "FT3SeparationLayer" + std::to_string(mDirection) + std::to_string(mLayerNumber); TGeoMedium* medAir = gGeoManager->GetMedium("FT3_AIR$"); - TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, 12 * mChipThickness / 2); // additional "thickness factor" is to avoid sub-volumes crossing the mother layer + TGeoTube* layer = new TGeoTube(mInnerRadius - 0.1, mOuterRadius + 0.1, 1.5); // Add a little additional room in radius; Try with 1.5 cm thickness TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); layerVol->SetLineColor(kYellow + 2); diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx index 0d0983958c46f..118078ebf7100 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx @@ -445,4 +445,4 @@ void BTOFLayer::createLayer(TGeoVolume* motherVolume) } } // namespace iotof -} // namespace o2 \ No newline at end of file +} // namespace o2 From d87ad11b206f555c3e4beab9a5a9823a6471010c Mon Sep 17 00:00:00 2001 From: shahoian Date: Mon, 30 Mar 2026 18:37:13 +0200 Subject: [PATCH 022/285] Fix typo on the TPC A||C side contribution check --- Framework/Core/include/Framework/AnalysisDataModel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Framework/Core/include/Framework/AnalysisDataModel.h b/Framework/Core/include/Framework/AnalysisDataModel.h index 9f48685820634..2869565454294 100644 --- a/Framework/Core/include/Framework/AnalysisDataModel.h +++ b/Framework/Core/include/Framework/AnalysisDataModel.h @@ -413,7 +413,7 @@ DECLARE_SOA_DYNAMIC_COLUMN(HasTPCSideC, hasTPCSideC, //! Run 3: Has this track T DECLARE_SOA_DYNAMIC_COLUMN(HasTPCSideCOnly, hasTPCSideCOnly, //! Run 3: Has this track TPC clusters from side C only? [](uint8_t flags) -> bool { return (flags & (o2::aod::track::TPCSideA | o2::aod::track::TPCSideC)) == o2::aod::track::TPCSideC; }); DECLARE_SOA_DYNAMIC_COLUMN(HasTPCBothSides, hasTPCBothSides, //! Run 3: Has this track TPC clusters from both side A and C? - [](uint8_t flags) -> bool { return (flags & (o2::aod::track::TPCSideA | o2::aod::track::TPCSideC)) == (o2::aod::track::TPCSideA || o2::aod::track::TPCSideC); }); + [](uint8_t flags) -> bool { return (flags & (o2::aod::track::TPCSideA | o2::aod::track::TPCSideC)) == (o2::aod::track::TPCSideA | o2::aod::track::TPCSideC); }); DECLARE_SOA_DYNAMIC_COLUMN(PIDForTracking, pidForTracking, //! PID hypothesis used during tracking. See the constants in the class PID in PID.h [](uint32_t flags) -> uint32_t { return flags >> 28; }); DECLARE_SOA_DYNAMIC_COLUMN(TPCNClsFound, tpcNClsFound, //! Number of found TPC clusters From aa5d3c6ec3c67a0c15ae53a3705fa58eb6eee2e7 Mon Sep 17 00:00:00 2001 From: spulawsk Date: Mon, 30 Mar 2026 23:00:48 +0200 Subject: [PATCH 023/285] FT0: update Digitizer signal shape and trigger logic; FV0: update trigger logic in digitizer (#15209) * FT0: tune signal shape and trigger tunning for MC * apply clang-format * Update FV0 trigger settings and digitizer logic * FV0: update Digitizer trigger handling * Update parameter comments and default settings for Run 3 * Clang formating * Default trigger settings set for pp. PbPb will be sent via parameters * Default trigger settings set for pp. PbPb will be sent via parameters --------- Co-authored-by: Szymon Pulawski --- .../FT0/base/include/FT0Base/FT0DigParam.h | 9 +- .../FIT/FT0/simulation/src/Digitizer.cxx | 185 ++++++++++++++++-- .../include/FV0Simulation/FV0DigParam.h | 4 + .../FIT/FV0/simulation/src/Digitizer.cxx | 38 ++-- 4 files changed, 203 insertions(+), 33 deletions(-) diff --git a/Detectors/FIT/FT0/base/include/FT0Base/FT0DigParam.h b/Detectors/FIT/FT0/base/include/FT0Base/FT0DigParam.h index 2bf774859aa22..074d91bb04b27 100644 --- a/Detectors/FIT/FT0/base/include/FT0Base/FT0DigParam.h +++ b/Detectors/FIT/FT0/base/include/FT0Base/FT0DigParam.h @@ -31,8 +31,8 @@ struct FT0DigParam : o2::conf::ConfigurableParamHelper { float mAmpRecordUp = 15; // to [ns] float hitTimeOffsetA = 0; ///< hit time offset on the A side [ns] float hitTimeOffsetC = 0; ///< hit time offset on the C side [ns] - int mtrg_central_trh = 600.; // channels - int mtrg_semicentral_trh = 300.; // channels + int mtrg_central_trh = 40; // Tclu units (40 for pp and 1433 for PbPb in Run3) + int mtrg_semicentral_trh = 20; // Tclu units (20 for pp and 35 for PbPb in Run3) float mMip_in_V = 7; // MIP to mV float mPe_in_mip = 0.004; // invserse Np.e. in MIP 1./250. @@ -43,11 +43,12 @@ struct FT0DigParam : o2::conf::ConfigurableParamHelper { float mNoiseVar = 0.1; // noise level float mNoisePeriod = 1 / 0.9; // GHz low frequency noise period; short mTime_trg_gate = 153; // #channels as in TCM as in Pilot beams ('OR gate' setting in TCM tab in ControlServer) + short mTime_trg_vertex_gate = 100; // #channels as in TCM as in Pilot beams ('OR gate' setting in TCM tab in ControlServer) float mAmpThresholdForReco = 5; // only channels with amplitude higher will participate in calibration and collision time: 0.3 MIP short mTimeThresholdForReco = 1000; // only channels with time below will participate in calibration and collision time - float mMV_2_Nchannels = 2.2857143; // amplitude channel 7 mV ->16channels - float mMV_2_NchannelsInverse = 0.437499997; // inverse amplitude channel 7 mV ->16channels + float mMV_2_Nchannels = 2.; // amplitude channel 7 mV ->14channels + float mMV_2_NchannelsInverse = 0.5; // inverse amplitude channel 7 mV ->14channels (nowhere used) O2ParamDef(FT0DigParam, "FT0DigParam"); }; diff --git a/Detectors/FIT/FT0/simulation/src/Digitizer.cxx b/Detectors/FIT/FT0/simulation/src/Digitizer.cxx index aca012f1bc5a9..de432a85765c7 100644 --- a/Detectors/FIT/FT0/simulation/src/Digitizer.cxx +++ b/Detectors/FIT/FT0/simulation/src/Digitizer.cxx @@ -16,6 +16,13 @@ #include "CommonConstants/PhysicsConstants.h" #include "CommonDataFormat/InteractionRecord.h" +#include "DataFormatsFT0/LookUpTable.h" +#include "FT0Base/Constants.h" +#include +#include +#include +#include + #include "TMath.h" #include "TRandom.h" #include @@ -35,24 +42,84 @@ namespace o2::ft0 template Float signalForm_i(Float x) { - using namespace std; - Float const a = -0.45458; - Float const b = -0.83344945; - return x > Float(0) ? -(exp(b * x) - exp(a * x)) / Float(7.8446501) : Float(0); + float p0, p1, p2, p3, p4, p5, p6, p7; + p0 = 1.30853; + p1 = 0.516807; + p2 = 3.36714; + p3 = -1.01206; + p4 = 1.42832; + p5 = 1.1589; + p6 = 1.22019; + p7 = 0.426818; + + Double_t val = 0; + + if (x > p3) { + Double_t x1 = x - p3; + Double_t arg1 = (log(x1) - p0) / p1; + val += p2 * (1.0 / (x1 * p1 * sqrt(2 * TMath::Pi()))) * exp(-0.5 * arg1 * arg1); + } + + if (x > p7) { + Double_t x2 = x - p7; + Double_t arg2 = (log(x2) - p4) / p5; + val += p6 * (1.0 / (x2 * p5 * sqrt(2 * TMath::Pi()))) * exp(-0.5 * arg2 * arg2); + } + + return val; }; // integrated signal shape function inline float signalForm_integral(float x) { - using namespace std; - double const a = -0.45458; - double const b = -0.83344945; - if (x < 0) { - x = 0; + float p0, p1, p2, p3, p4, p5, p6, p7; + p0 = 1.30853; + p1 = 0.516807; + p2 = 3.36714; + p3 = -1.01206; + p4 = 1.42832; + p5 = 1.1589; + p6 = 1.22019; + p7 = 0.426818; + Double_t val = 0; + + if (x > p3) { + Double_t x1 = x - p3; + Double_t z1 = (log(x1) - p0) / (sqrt(2) * p1); + val += p2 * 0.5 * (1 + TMath::Erf(z1)); // norm1 * CDF1 } - return -(exp(b * x) / b - exp(a * x) / a) / 7.8446501; + + if (x > p7) { + Double_t x2 = x - p7; + Double_t z2 = (log(x2) - p4) / (sqrt(2) * p5); + val += p6 * 0.5 * (1 + TMath::Erf(z2)); // norm2 * CDF2 + } + + return val; +}; +/* +// signal shape function +template +Float signalForm_i(Float x) +{ +using namespace std; +Float const a = -0.45458; +Float const b = -0.83344945; +return x > Float(0) ? -(exp(b * x) - exp(a * x)) / Float(7.8446501) : Float(0); }; +// integrated signal shape function +inline float signalForm_integral(float x) +{ +using namespace std; +double const a = -0.45458; +double const b = -0.83344945; +if (x < 0) { + x = 0; +} +return -(exp(b * x) / b - exp(a * x) / a) / 7.8446501; +}; +*/ // SIMD version of the integrated signal shape function inline Vc::float_v signalForm_integralVc(Vc::float_v x) { @@ -249,8 +316,64 @@ void Digitizer::storeBC(BCCache& bc, if (bc.hits.empty()) { return; } + // Initialize mapping channelID -> PM hash and PM side (A/C) using FT0 LUT + static bool pmLutInitialized = false; + static std::array mChID2PMhash{}; + static std::map mMapPMhash2isAside; // hashed PM -> is A side + + if (!pmLutInitialized) { + std::map mapFEE2hash; // module name -> hashed PM id + uint8_t tcmHash = 0; + + const auto& lut = o2::ft0::SingleLUT::Instance().getVecMetadataFEE(); + auto lutSorted = lut; + std::sort(lutSorted.begin(), lutSorted.end(), + [](const auto& first, const auto& second) { return first.mModuleName < second.mModuleName; }); + + uint8_t binPos = 0; + for (const auto& lutEntry : lutSorted) { + const auto& moduleName = lutEntry.mModuleName; + const auto& moduleType = lutEntry.mModuleType; + const auto& strChID = lutEntry.mChannelID; + + auto [it, inserted] = mapFEE2hash.insert({moduleName, binPos}); + if (inserted) { + if (moduleName.find("PMA") != std::string::npos) { + mMapPMhash2isAside.insert({binPos, true}); + } else if (moduleName.find("PMC") != std::string::npos) { + mMapPMhash2isAside.insert({binPos, false}); + } + ++binPos; + } + + if (std::regex_match(strChID, std::regex("^[0-9]{1,3}$"))) { + int chID = std::stoi(strChID); + if (chID < o2::ft0::Constants::sNCHANNELS_PM) { + mChID2PMhash[chID] = mapFEE2hash[moduleName]; + } else { + LOG(fatal) << "Incorrect LUT entry: chID " << strChID << " | " << moduleName; + } + } else if (moduleType != "TCM") { + LOG(fatal) << "Non-TCM module w/o numerical chID: chID " << strChID << " | " << moduleName; + } else { // TCM + tcmHash = mapFEE2hash[moduleName]; + } + } + + pmLutInitialized = true; + } + int n_hit_A = 0, n_hit_C = 0, mean_time_A = 0, mean_time_C = 0; int summ_ampl_A = 0, summ_ampl_C = 0; + int sum_A_ampl = 0, sum_C_ampl = 0; + int nPMTs = mGeometry.NCellsA * 4 + mGeometry.NCellsC * 4; + std::vector sum_ampl_ipmt(nPMTs, 0); + // Per-PM summed charge (like in digits2trgFT0) + std::map mapPMhash2sumAmpl; + for (const auto& entry : mMapPMhash2isAside) { + mapPMhash2sumAmpl.insert({entry.first, 0}); + } + int vertex_time; const auto& params = FT0DigParam::Instance(); int first = digitsCh.size(), nStored = 0; @@ -297,6 +420,10 @@ void Digitizer::storeBC(BCCache& bc, if (is_time_in_signal_gate) { chain |= (1 << o2::ft0::ChannelData::EEventDataBit::kIsCFDinADCgate); chain |= (1 << o2::ft0::ChannelData::EEventDataBit::kIsEventInTVDC); + // Sum channel charge per PM (similar logic as in digits2trgFT0) + if (ipmt < o2::ft0::Constants::sNCHANNELS_PM) { + mapPMhash2sumAmpl[mChID2PMhash[static_cast(ipmt)]] += static_cast(amp); + } } digitsCh.emplace_back(ipmt, smeared_time, int(amp), chain); nStored++; @@ -308,6 +435,8 @@ void Digitizer::storeBC(BCCache& bc, continue; } + sum_ampl_ipmt[ipmt] += amp; + if (is_A_side) { n_hit_A++; summ_ampl_A += amp; @@ -318,17 +447,47 @@ void Digitizer::storeBC(BCCache& bc, mean_time_C += smeared_time; } } + + for (size_t i = 0; i < sum_ampl_ipmt.size(); i++) { + sum_ampl_ipmt[i] = sum_ampl_ipmt[i] >> 3; + if (i < 4 * mGeometry.NCellsA) { + sum_A_ampl += sum_ampl_ipmt[i]; + } else { + sum_C_ampl += sum_ampl_ipmt[i]; + } + } + + // Sum over PMs (using per-PM map) for debug/monitoring + int sum_PM_ampl_debug = 0; + int sum_PM_ampl_A_debug = 0; + int sum_PM_ampl_C_debug = 0; + for (const auto& entry : mapPMhash2sumAmpl) { + int pmAmpl = (entry.second >> 3); + sum_PM_ampl_debug += pmAmpl; + auto itSide = mMapPMhash2isAside.find(entry.first); + if (itSide != mMapPMhash2isAside.end()) { + if (itSide->second) { + sum_PM_ampl_A_debug += pmAmpl; + } else { + sum_PM_ampl_C_debug += pmAmpl; + } + } + } + LOG(debug) << "Sum PM amplitude (LUT-based): total=" << sum_PM_ampl_debug + << " A-side=" << sum_PM_ampl_A_debug + << " C-side=" << sum_PM_ampl_C_debug; + Bool_t is_A, is_C, isVertex, is_Central, is_SemiCentral = 0; is_A = n_hit_A > 0; is_C = n_hit_C > 0; - is_Central = summ_ampl_A + summ_ampl_C >= params.mtrg_central_trh; - is_SemiCentral = summ_ampl_A + summ_ampl_C >= params.mtrg_semicentral_trh; + is_Central = sum_PM_ampl_A_debug + sum_PM_ampl_C_debug >= 2 * params.mtrg_central_trh; + is_SemiCentral = sum_PM_ampl_A_debug + sum_PM_ampl_C_debug >= 2 * params.mtrg_semicentral_trh && !is_Central; uint32_t amplA = is_A ? summ_ampl_A * 0.125 : -5000; // sum amplitude A side / 8 (hardware) uint32_t amplC = is_C ? summ_ampl_C * 0.125 : -5000; // sum amplitude C side / 8 (hardware) int timeA = is_A ? mean_time_A / n_hit_A : -5000; // average time A side int timeC = is_C ? mean_time_C / n_hit_C : -5000; // average time C side vertex_time = (timeC - timeA) * 0.5; - isVertex = is_A && is_C && (vertex_time > -params.mTime_trg_gate && vertex_time < params.mTime_trg_gate); + isVertex = is_A && is_C && (vertex_time > -params.mTime_trg_vertex_gate && vertex_time < params.mTime_trg_vertex_gate); LOG(debug) << " A " << is_A << " timeA " << timeA << " mean_time_A " << mean_time_A << " n_hit_A " << n_hit_A << " C " << is_C << " timeC " << timeC << " mean_time_C " << mean_time_C << " n_hit_C " << n_hit_C << " vertex_time " << vertex_time; Triggers triggers; bool isLaser = false; diff --git a/Detectors/FIT/FV0/simulation/include/FV0Simulation/FV0DigParam.h b/Detectors/FIT/FV0/simulation/include/FV0Simulation/FV0DigParam.h index 383fa4cb494c1..6462323a279b7 100644 --- a/Detectors/FIT/FV0/simulation/include/FV0Simulation/FV0DigParam.h +++ b/Detectors/FIT/FV0/simulation/include/FV0Simulation/FV0DigParam.h @@ -69,6 +69,10 @@ struct FV0DigParam : o2::conf::ConfigurableParamHelper { uint8_t defaultChainQtc = 0x48; // only 2 flags are set by default in simulation: kIsCFDinADCgate and kIsEventInTVDC float mAmpThresholdForReco = 24; // only channels with amplitude higher will participate in calibration and collision time short mTimeThresholdForReco = 1000; // only channels with time below will participate in calibration and collision time + int NchannelsLevel = 2; // trigger Nchannels + float InnerChargeLevel = 4; // InnerRingsChargeLevel + float OuterChargeLevel = 4; // OuterRingsChargeLevel + float ChargeLevel = 8; // ChargeLevel O2ParamDef(FV0DigParam, "FV0DigParam"); }; diff --git a/Detectors/FIT/FV0/simulation/src/Digitizer.cxx b/Detectors/FIT/FV0/simulation/src/Digitizer.cxx index 8c1d2dc8824e2..3237f9bab7879 100644 --- a/Detectors/FIT/FV0/simulation/src/Digitizer.cxx +++ b/Detectors/FIT/FV0/simulation/src/Digitizer.cxx @@ -38,8 +38,8 @@ void Digitizer::clear() void Digitizer::init() { LOG(info) << "init"; - mNBins = FV0DigParam::Instance().waveformNbins; //Will be computed using detector set-up from CDB - mBinSize = FV0DigParam::Instance().waveformBinWidth; //Will be set-up from CDB + mNBins = FV0DigParam::Instance().waveformNbins; // Will be computed using detector set-up from CDB + mBinSize = FV0DigParam::Instance().waveformBinWidth; // Will be set-up from CDB mNTimeBinsPerBC = std::lround(o2::constants::lhc::LHCBunchSpacingNS / mBinSize); // 1920 bins/BC for (Int_t detID = 0; detID < Constants::nFv0Channels; detID++) { @@ -149,8 +149,8 @@ void Digitizer::process(const std::vector& hits, createPulse(mipFraction, hit.GetTrackID(), hitTime, hit.GetPos().R(), cachedIR, nCachedIR, detId); - } //while loop - } //hitloop + } // while loop + } // hitloop } void Digitizer::createPulse(float mipFraction, int parID, const double hitTime, const float hitR, @@ -200,7 +200,7 @@ void Digitizer::createPulse(float mipFraction, int parID, const double hitTime, } added[ir] = true; } - ///Add MC labels to BCs for those contributed to the PMT signal + /// Add MC labels to BCs for those contributed to the PMT signal for (int ir = 0; ir < nCachedIR; ir++) { if (added[ir]) { auto bcCache = getBCCache(cachedIR[ir]); @@ -238,6 +238,8 @@ void Digitizer::storeBC(const BCCache& bc, int8_t nTotFiredCells = 0; int8_t nTrgFiredCells = 0; // number of fired cells, that follow additional trigger conditions (time gate) int totalChargeAllRing = 0; + int totalChargeInnerRing = 0; + int totalChargeOuterRing = 0; int32_t avgTime = 0; double nSignalInner = 0; double nSignalOuter = 0; @@ -285,8 +287,10 @@ void Digitizer::storeBC(const BCCache& bc, avgTime += iCfdZero; if (iPmt < 24) { nSignalInner++; + totalChargeInnerRing += iTotalCharge; } else { nSignalOuter++; + totalChargeOuterRing += iTotalCharge; } } } @@ -300,13 +304,15 @@ void Digitizer::storeBC(const BCCache& bc, } else { avgTime = o2::fit::Triggers::DEFAULT_TIME; } - ///Triggers for FV0 - bool isA, isAIn, isAOut, isCen, isSCen; + /// Triggers for FV0 + bool isA, isNchannels, isAIn, isAOut, isTotalCharge; isA = nTrgFiredCells > 0; - isAIn = nSignalInner > 0; // ring 1,2 and 3 - isAOut = nSignalOuter > 0; // ring 4 and 5 - isCen = totalChargeAllRing > FV0DigParam::Instance().adcChargeCenThr; - isSCen = totalChargeAllRing > FV0DigParam::Instance().adcChargeSCenThr; + isNchannels = nTrgFiredCells > FV0DigParam::Instance().NchannelsLevel; + // isAIn = nSignalInner > FV0DigParam::Instance().NchannelsLevel; // ring 1,2 and 3 + isAIn = 0.125 * totalChargeInnerRing > 2 * FV0DigParam::Instance().InnerChargeLevel; // ring 1,2 and 3 + // isAOut = nSignalOuter > FV0DigParam::Instance().NchannelsLevel; // ring 4 and 5 + isAOut = 0.125 * totalChargeOuterRing > 2 * FV0DigParam::Instance().OuterChargeLevel; // ring 4 and 5 + isTotalCharge = 0.125 * totalChargeAllRing > 2 * FV0DigParam::Instance().ChargeLevel; Triggers triggers; const int unusedCharge = o2::fit::Triggers::DEFAULT_AMP; @@ -314,10 +320,10 @@ void Digitizer::storeBC(const BCCache& bc, const int unusedZero = o2::fit::Triggers::DEFAULT_ZERO; const bool unusedBitsInSim = false; // bits related to laser and data validity const bool bitDataIsValid = true; - triggers.setTriggers(isA, isAIn, isAOut, isCen, isSCen, nTrgFiredCells, (int8_t)unusedZero, + triggers.setTriggers(isA, isAIn, isAOut, isTotalCharge, isNchannels, nTrgFiredCells, (int8_t)unusedZero, (int32_t)(0.125 * totalChargeAllRing), (int32_t)unusedCharge, (int16_t)avgTime, (int16_t)unusedTime, unusedBitsInSim, unusedBitsInSim, bitDataIsValid); digitsBC.emplace_back(first, nTotFiredCells, bc, triggers, mEventId - 1); - digitsTrig.emplace_back(bc, isA, isAIn, isAOut, isCen, isSCen); + digitsTrig.emplace_back(bc, isA, isAIn, isAOut, isTotalCharge, isNchannels); for (auto const& lbl : bc.labels) { labels.addElement(nBC, lbl); } @@ -342,8 +348,8 @@ Int_t Digitizer::SimulateLightYield(Int_t pmt, Int_t nPhot) const //--------------------------------------------------------------------------- Float_t Digitizer::IntegrateCharge(const ChannelDigitF& pulse) const { - int const chargeIntMin = FV0DigParam::Instance().isIntegrateFull ? 0 : (FV0DigParam::Instance().avgCfdTimeForMip - 6.0) / mBinSize; //Charge integration offset (cfd mean time - 6 ns) - int const chargeIntMax = FV0DigParam::Instance().isIntegrateFull ? mNTimeBinsPerBC : (FV0DigParam::Instance().avgCfdTimeForMip + 14.0) / mBinSize; //Charge integration offset (cfd mean time + 14 ns) + int const chargeIntMin = FV0DigParam::Instance().isIntegrateFull ? 0 : (FV0DigParam::Instance().avgCfdTimeForMip - 6.0) / mBinSize; // Charge integration offset (cfd mean time - 6 ns) + int const chargeIntMax = FV0DigParam::Instance().isIntegrateFull ? mNTimeBinsPerBC : (FV0DigParam::Instance().avgCfdTimeForMip + 14.0) / mBinSize; // Charge integration offset (cfd mean time + 14 ns) if (chargeIntMin < 0 || chargeIntMin > mNTimeBinsPerBC || chargeIntMax > mNTimeBinsPerBC) { LOG(fatal) << "invalid indicess: chargeInMin=" << chargeIntMin << " chargeIntMax=" << chargeIntMax; } @@ -400,7 +406,7 @@ float Digitizer::getDistFromCellCenter(UInt_t cellId, double hitx, double hity) double a = -(y0 - pCell->y) / (x0 - pCell->x); double b = 1; double c = -(y0 - a * x0); - //Return the distance from hit to this line + // Return the distance from hit to this line return (a * hitx + b * hity + c) / TMath::Sqrt(a * a + b * b); } From bbb4570480ccb5f89af56913a9b7403e61610778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Tue, 31 Mar 2026 00:34:34 +0200 Subject: [PATCH 024/285] [ALICE3] IOTOF: Adjust layer radius calculations for stave tilt and chip thickness (#15220) * [ALICE3] IOTOF: Adjust layer radius calculations for stave tilt * Refactor iTOF and oTOF layer initialization logic * Update README.md * Update IOTOFBaseParam.h default thickness * Updated stave tilt angle for segmented iTOF configuration. --- Detectors/Upgrades/ALICE3/IOTOF/README.md | 2 +- .../base/include/IOTOFBase/IOTOFBaseParam.h | 4 ++-- .../ALICE3/IOTOF/simulation/src/Detector.cxx | 8 +++---- .../ALICE3/IOTOF/simulation/src/Layer.cxx | 24 +++++++++++-------- 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/README.md b/Detectors/Upgrades/ALICE3/IOTOF/README.md index fba4d12252af6..d7a954c4718fe 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/README.md +++ b/Detectors/Upgrades/ALICE3/IOTOF/README.md @@ -23,7 +23,7 @@ Configurables for various sub-detectors are presented in the following Table: | `IOTOFBase.segmentedInnerTOF` | `false` (default), `true` | Use segmented geometry for inner TOF | | `IOTOFBase.segmentedOuterTOF` | `false` (default), `true` | Use segmented geometry for outer TOF | | `IOTOFBase.detectorPattern` | ` ` (default), `v3b`, `v3b1a`, `v3b1b`, `v3b2a`, `v3b2b`, `v3b3` | Optional layout pattern | -| `IOTOFBase.x2x0` | `0.02` (default) | Chip thickness in fractions of the rad. lenght | +| `IOTOFBase.x2x0` | `0.000527` (default) | Chip thickness in fractions of the rad. lenght | For example, a geometry with fully cylindrical tracker barrel (for all layers in VD, ML and OT) can be obtained by diff --git a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h index 91d005415891d..1f1a26b79077e 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h @@ -28,7 +28,7 @@ struct IOTOFBaseParam : public o2::conf::ConfigurableParamHelper std::string detectorPattern = ""; // Layouts of the detector bool segmentedInnerTOF = false; // If the inner TOF layer is segmented bool segmentedOuterTOF = false; // If the outer TOF layer is segmented - float x2x0 = 0.02f; // thickness expressed in radiation length, for all layers for the moment + float x2x0 = 0.000527f; // thickness expressed in radiation length, for all layers for the moment O2ParamDef(IOTOFBaseParam, "IOTOFBase"); }; @@ -36,4 +36,4 @@ struct IOTOFBaseParam : public o2::conf::ConfigurableParamHelper } // namespace iotof } // end namespace o2 -#endif \ No newline at end of file +#endif diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx index 61720f2172b92..d6417eba22041 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx @@ -96,10 +96,10 @@ void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::str } if (itof) { // iTOF const std::string name = GeometryTGeo::getITOFLayerPattern(); - const int nStaves = itofSegmented ? 24 : 0; // number of staves in segmented case - const double staveWidth = itofSegmented ? 5.42 : 0.0; // cm - const double staveTiltAngle = itofSegmented ? 10.0 : 0.0; // degrees - const int modulesPerStave = itofSegmented ? 10 : 0; // number of modules per stave in segmented case + const int nStaves = itofSegmented ? 24 : 0; // number of staves in segmented case + const double staveWidth = itofSegmented ? 5.42 : 0.0; // cm + const double staveTiltAngle = itofSegmented ? 3.0 : 0.0; // degrees + const int modulesPerStave = itofSegmented ? 10 : 0; // number of modules per stave in segmented case mITOFLayer = ITOFLayer(name, dInnerTof.first, 0.f, dInnerTof.second, 0.f, x2x0, itofSegmented ? ITOFLayer::kBarrelSegmented : ITOFLayer::kBarrel, nStaves, staveWidth, staveTiltAngle, modulesPerStave); diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx index 118078ebf7100..66d0b2959c8bd 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx @@ -155,11 +155,13 @@ void ITOFLayer::createLayer(TGeoVolume* motherVolume) case kBarrelSegmented: { // First we create the volume for the whole layer, which will be used as mother volume for the segments const double avgRadius = 0.5 * (mInnerRadius + mOuterRadius); - const double staveSizeX = mStaves.second; // cm - const double staveSizeY = mOuterRadius - mInnerRadius; // cm - const double staveSizeZ = mZLength; // cm - const double deltaForTilt = 0.5 * (std::sin(TMath::DegToRad() * mTiltAngle) * staveSizeX + std::cos(TMath::DegToRad() * mTiltAngle) * staveSizeY); // we increase the size of the layer to account for the tilt of the staves - TGeoTube* layer = new TGeoTube(mInnerRadius - deltaForTilt, mOuterRadius + deltaForTilt, mZLength / 2); + const double staveSizeX = mStaves.second; // cm + const double staveSizeY = mOuterRadius - mInnerRadius; // cm + const double staveSizeZ = mZLength; // cm + const double deltaForTilt = 0.5 * (std::sin(TMath::DegToRad() * mTiltAngle) * staveSizeX + std::cos(TMath::DegToRad() * mTiltAngle) * staveSizeY); // we increase the size of the layer to account for the tilt of the staves + const double radiusMax = std::sqrt(avgRadius * avgRadius + 0.25 * staveSizeX * staveSizeX + 0.25 * staveSizeY * staveSizeY + avgRadius * 2. * deltaForTilt); // we increase the outer radius to account for the tilt of the staves + const double radiusMin = std::sqrt(avgRadius * avgRadius + 0.25 * staveSizeX * staveSizeX + 0.25 * staveSizeY * staveSizeY - avgRadius * 2. * deltaForTilt); // we decrease the inner radius to account for the tilt of the staves + TGeoTube* layer = new TGeoTube(radiusMin, radiusMax, mZLength / 2); TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); setLayerStyle(layerVol); @@ -287,11 +289,13 @@ void OTOFLayer::createLayer(TGeoVolume* motherVolume) case kBarrelSegmented: { // First we create the volume for the whole layer, which will be used as mother volume for the segments const double avgRadius = 0.5 * (mInnerRadius + mOuterRadius); - const double staveSizeX = mStaves.second; // cm - const double staveSizeY = mOuterRadius - mInnerRadius; // cm - const double staveSizeZ = mZLength; // cm - const double deltaForTilt = 0.5 * (std::sin(TMath::DegToRad() * mTiltAngle) * staveSizeX + std::cos(TMath::DegToRad() * mTiltAngle) * staveSizeY); // we increase the size of the layer to account for the tilt of the staves - TGeoTube* layer = new TGeoTube(mInnerRadius - deltaForTilt, mOuterRadius + deltaForTilt, mZLength / 2); + const double staveSizeX = mStaves.second; // cm + const double staveSizeY = mOuterRadius - mInnerRadius; // cm + const double staveSizeZ = mZLength; // cm + const double deltaForTilt = 0.5 * (std::sin(TMath::DegToRad() * mTiltAngle) * staveSizeX + std::cos(TMath::DegToRad() * mTiltAngle) * staveSizeY); // we increase the size of the layer to account for the tilt of the staves + const double radiusMax = std::sqrt(avgRadius * avgRadius + 0.25 * staveSizeX * staveSizeX + 0.25 * staveSizeY * staveSizeY + avgRadius * 2. * deltaForTilt); // we increase the outer radius to account for the tilt of the staves + const double radiusMin = std::sqrt(avgRadius * avgRadius + 0.25 * staveSizeX * staveSizeX + 0.25 * staveSizeY * staveSizeY - avgRadius * 2. * deltaForTilt); // we decrease the inner radius to account for the tilt of the staves + TGeoTube* layer = new TGeoTube(radiusMin, radiusMax, mZLength / 2); TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); setLayerStyle(layerVol); From 974bdc0596d1124a6ee69343962a2c6379f68f88 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 29 Mar 2026 11:09:46 +0200 Subject: [PATCH 025/285] Use default constructor --- Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h | 2 +- Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h b/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h index 9aaf6e517336d..7050cc6c69370 100644 --- a/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h +++ b/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h @@ -83,7 +83,7 @@ struct DataPointCompositeObject final { /** * Copy constructor */ - DataPointCompositeObject(const DataPointCompositeObject& src) noexcept : DataPointCompositeObject(src.id, src.data) {} + DataPointCompositeObject(const DataPointCompositeObject& src) noexcept = default; DataPointCompositeObject& operator=(const DataPointCompositeObject& src) noexcept { diff --git a/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h b/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h index 79b7d7cf886c7..be21f71f8f3e3 100644 --- a/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h +++ b/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h @@ -85,7 +85,7 @@ class DataPointIdentifier final /** * A copy constructor for DataPointIdentifier. */ - DataPointIdentifier(const DataPointIdentifier& src) noexcept : DataPointIdentifier(src.pt1, src.pt2, src.pt3, src.pt4, src.pt5, src.pt6, src.pt7, src.pt8) {} + DataPointIdentifier(const DataPointIdentifier& src) noexcept = default; DataPointIdentifier& operator=(const DataPointIdentifier& src) noexcept { From e2e7eb846e103d55ac9ba725d5cb7dbdbb249f50 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 29 Mar 2026 11:17:29 +0200 Subject: [PATCH 026/285] Rework DCS point classes to have default assign operators --- .../DetectorsDCS/DataPointCompositeObject.h | 10 ++----- .../DetectorsDCS/DataPointIdentifier.h | 29 ++++++------------- 2 files changed, 11 insertions(+), 28 deletions(-) diff --git a/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h b/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h index 7050cc6c69370..6ea69f82277bf 100644 --- a/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h +++ b/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h @@ -47,7 +47,7 @@ struct DataPointCompositeObject final { * * @see ADAPRO::ADAPOS::DataPointIdentifier */ - const DataPointIdentifier id; + DataPointIdentifier id; /** * The DataPointValue object, which occupies the last 64 bytes of the @@ -85,13 +85,7 @@ struct DataPointCompositeObject final { */ DataPointCompositeObject(const DataPointCompositeObject& src) noexcept = default; - DataPointCompositeObject& operator=(const DataPointCompositeObject& src) noexcept - { - if (&src != this) { - memcpy(this, &src, sizeof(DataPointCompositeObject)); - } - return *this; - } + DataPointCompositeObject& operator=(const DataPointCompositeObject& src) noexcept = default; /** * Bit-by bit equality comparison of DataPointCompositeObjects. diff --git a/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h b/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h index be21f71f8f3e3..8d156e04ebbca 100644 --- a/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h +++ b/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h @@ -41,19 +41,14 @@ namespace dcs */ class DataPointIdentifier final { - const uint64_t pt1; - const uint64_t pt2; - const uint64_t pt3; - const uint64_t pt4; - const uint64_t pt5; - const uint64_t pt6; - const uint64_t pt7; - const uint64_t pt8; // Contains the last 6 chars of alias and the type. - - DataPointIdentifier( - const uint64_t pt1, const uint64_t pt2, const uint64_t pt3, - const uint64_t pt4, const uint64_t pt5, const uint64_t pt6, - const uint64_t pt7, const uint64_t pt8) noexcept : pt1(pt1), pt2(pt2), pt3(pt3), pt4(pt4), pt5(pt5), pt6(pt6), pt7(pt7), pt8(pt8) {} + uint64_t pt1; + uint64_t pt2; + uint64_t pt3; + uint64_t pt4; + uint64_t pt5; + uint64_t pt6; + uint64_t pt7; + uint64_t pt8; // Contains the last 6 chars of alias and the type. public: /** @@ -87,13 +82,7 @@ class DataPointIdentifier final */ DataPointIdentifier(const DataPointIdentifier& src) noexcept = default; - DataPointIdentifier& operator=(const DataPointIdentifier& src) noexcept - { - if (&src != this) { - memcpy(this, &src, sizeof(DataPointIdentifier)); - } - return *this; - } + DataPointIdentifier& operator=(const DataPointIdentifier& src) noexcept = default; /** * This stati procedure fills the given DataPointIdentifier object with From ae345476bb628d972f7a44142649dffe456d7803 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 1 Apr 2026 12:59:16 +0200 Subject: [PATCH 027/285] Align to coding conventions --- .../Base/GPUReconstructionDebug.cxx | 50 ++++++++++++------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/GPU/GPUTracking/Base/GPUReconstructionDebug.cxx b/GPU/GPUTracking/Base/GPUReconstructionDebug.cxx index 564c04ba7f745..559d1537464ab 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionDebug.cxx +++ b/GPU/GPUTracking/Base/GPUReconstructionDebug.cxx @@ -215,55 +215,67 @@ void GPUReconstructionCPU::debugWriter::row(char type, uint32_t count, std::stri if (streamCSV.is_open()) { streamCSV << type << ","; - if (count != 0) + if (count != 0) { streamCSV << count; + } streamCSV << "," << name << ","; - if (gpu_time != -1.0) + if (gpu_time != -1.0) { streamCSV << std::format("{:.0f}", gpu_time * scale); + } streamCSV << ","; - if (cpu_time != -1.0) + if (cpu_time != -1.0) { streamCSV << std::format("{:.0f}", cpu_time * scale); + } streamCSV << ","; - if (cpu_time != -1.0 && total_time != -1.0) + if (cpu_time != -1.0 && total_time != -1.0) { streamCSV << std::format("{:.2f}", cpu_time / total_time); + } streamCSV << ","; - if (total_time != -1.0) + if (total_time != -1.0) { streamCSV << std::format("{:.0f}", total_time * scale); + } streamCSV << ","; - if (memSize != 0 && count != 0) + if (memSize != 0 && count != 0) { streamCSV << std::format("{:.3f},{},{}", memSize / gpu_time * 1e-9, memSize / mStatNEvents, memSize / mStatNEvents / count); - else + } else { streamCSV << ",,"; + } streamCSV << std::endl; } if (mMarkdown) { std::cout << "| " << type << " | "; - if (count != 0) + if (count != 0) { std::cout << std::format("{:6} |", count); - else + } else { std::cout << " |"; + } std::cout << std::format(" {:42}|", name); - if (gpu_time != -1.0) + if (gpu_time != -1.0) { std::cout << std::format("{:10.0f} |", gpu_time * scale); - else + } else { std::cout << " |"; - if (cpu_time != -1.0) + } + if (cpu_time != -1.0) { std::cout << std::format("{:10.0f} |", cpu_time * scale); - else + } else { std::cout << " |"; - if (cpu_time != -1.0 && total_time != -1.0) + } + if (cpu_time != -1.0 && total_time != -1.0) { std::cout << std::format("{:8.2f} |", cpu_time / total_time); - else + } else { std::cout << " |"; - if (total_time != -1.0) + } + if (total_time != -1.0) { std::cout << std::format("{:10.0f} |", total_time * scale); - else + } else { std::cout << " |"; - if (memSize != 0 && count != 0) + } + if (memSize != 0 && count != 0) { std::cout << std::format("{:10.3f} |{:14} |{:14} |", memSize / gpu_time * 1e-9, memSize / mStatNEvents, memSize / mStatNEvents / count); - else + } else { std::cout << " | | |"; + } std::cout << std::endl; } else { if (name.substr(0, 3) == "GPU") { From 4f64fb9cabd616e2e822d1a683918ba86fba366c Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 25 Mar 2026 15:01:55 +0100 Subject: [PATCH 028/285] Drop async-list-label action My understanding is that this is not actually used, as it is superseded by the two / three tag approach. --- .github/workflows/async-list-label.yml | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 .github/workflows/async-list-label.yml diff --git a/.github/workflows/async-list-label.yml b/.github/workflows/async-list-label.yml deleted file mode 100644 index e0b4185c563b7..0000000000000 --- a/.github/workflows/async-list-label.yml +++ /dev/null @@ -1,19 +0,0 @@ ---- -name: Collect and print async labels - -'on': - pull_request_target: - types: - - opened - - reopened - branches: - - dev - -permissions: {} - -jobs: - list_async_labels: - name: Collect and print async labels - uses: alisw/ali-bot/.github/workflows/async-list-label.yml@master - permissions: - pull-requests: write # to update labels From 74c80492483241afc58a5784cd6ce89e097520af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Wed, 1 Apr 2026 16:08:26 +0200 Subject: [PATCH 029/285] [ALICE3] IOTOF: allow reduced sensor thickness wihout reducing chip size (#15247) * Enhance Layer class with sensor thickness handling Added sensor thickness parameter and validation checks. * Add sensor thickness to Layer class Added sensor thickness parameter to Layer constructor and updated member variables accordingly. * Modify configLayers to include sensorThickness Added sensorThickness parameter to configLayers function for ITOF and OTOF layers. * Update Detector.h * Modify thickness parameters in IOTOFBaseParam.h Updated the radiation length thickness and added sensor thickness parameter. * Revise options table in README for IOTOF Updated options table with new sensor thickness parameter and corrected default value for x2x0. * Simplify chip size calculation in Layer.cxx Removed sensor thickness adjustment for chip size calculation. * Update sensor size calculation in Layer.cxx --- Detectors/Upgrades/ALICE3/IOTOF/README.md | 22 ++++++++++--------- .../base/include/IOTOFBase/IOTOFBaseParam.h | 3 ++- .../include/IOTOFSimulation/Detector.h | 4 ++-- .../include/IOTOFSimulation/Layer.h | 9 ++++---- .../ALICE3/IOTOF/simulation/src/Detector.cxx | 6 ++--- .../ALICE3/IOTOF/simulation/src/Layer.cxx | 15 +++++++++---- 6 files changed, 35 insertions(+), 24 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/README.md b/Detectors/Upgrades/ALICE3/IOTOF/README.md index d7a954c4718fe..e52b5e2379e9c 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/README.md +++ b/Detectors/Upgrades/ALICE3/IOTOF/README.md @@ -14,16 +14,18 @@ Configurables for various sub-detectors are presented in the following Table: [link to definitions](./base/include/IOTOFBase/IOTOFBaseParam.h) -| Options | Choices | Comments | -| ----------------------------- | ---------------------------------------------------------------- | ---------------------------------------------- | -| `IOTOFBase.enableInnerTOF` | `true` (default), `false` | Enable inner TOF barrel layer | -| `IOTOFBase.enableOuterTOF` | `true` (default), `false` | Enable outer TOF barrel layer | -| `IOTOFBase.enableForwardTOF` | `true` (default), `false` | Enable forward TOF endcap | -| `IOTOFBase.enableBackwardTOF` | `true` (default), `false` | Enable backward TOF endcap | -| `IOTOFBase.segmentedInnerTOF` | `false` (default), `true` | Use segmented geometry for inner TOF | -| `IOTOFBase.segmentedOuterTOF` | `false` (default), `true` | Use segmented geometry for outer TOF | -| `IOTOFBase.detectorPattern` | ` ` (default), `v3b`, `v3b1a`, `v3b1b`, `v3b2a`, `v3b2b`, `v3b3` | Optional layout pattern | -| `IOTOFBase.x2x0` | `0.000527` (default) | Chip thickness in fractions of the rad. lenght | +| Options | Choices | Comments | +| ----------------------------- | ---------------------------------------------------------------- | -------------------------------------------------------------------------- | +| `IOTOFBase.enableInnerTOF` | `true` (default), `false` | Enable inner TOF barrel layer | +| `IOTOFBase.enableOuterTOF` | `true` (default), `false` | Enable outer TOF barrel layer | +| `IOTOFBase.enableForwardTOF` | `true` (default), `false` | Enable forward TOF endcap | +| `IOTOFBase.enableBackwardTOF` | `true` (default), `false` | Enable backward TOF endcap | +| `IOTOFBase.segmentedInnerTOF` | `false` (default), `true` | Use segmented geometry for inner TOF | +| `IOTOFBase.segmentedOuterTOF` | `false` (default), `true` | Use segmented geometry for outer TOF | +| `IOTOFBase.detectorPattern` | ` ` (default), `v3b`, `v3b1a`, `v3b1b`, `v3b2a`, `v3b2b`, `v3b3` | Optional layout pattern | +| `IOTOFBase.x2x0` | `0.02` (default) | Chip thickness in fractions of the rad. lenght | +| `IOTOFBase.sensorThickness` | `0.0050` (default) | Sensor thickness in cm, can be at maximum equivalent to the chip thickness | + For example, a geometry with fully cylindrical tracker barrel (for all layers in VD, ML and OT) can be obtained by diff --git a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h index 1f1a26b79077e..c1a9578484c17 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h @@ -28,7 +28,8 @@ struct IOTOFBaseParam : public o2::conf::ConfigurableParamHelper std::string detectorPattern = ""; // Layouts of the detector bool segmentedInnerTOF = false; // If the inner TOF layer is segmented bool segmentedOuterTOF = false; // If the outer TOF layer is segmented - float x2x0 = 0.000527f; // thickness expressed in radiation length, for all layers for the moment + float x2x0 = 0.02f; // thickness expressed in radiation length, for all layers for the moment + float sensorThickness = 0.0050f; // thickness of the sensor in cm, for all layers for the moment, the default is set to 50 microns O2ParamDef(IOTOFBaseParam, "IOTOFBase"); }; diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Detector.h b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Detector.h index acf754e1b1fa8..34097020c42ff 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Detector.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Detector.h @@ -60,7 +60,7 @@ class Detector : public o2::base::DetImpl return nullptr; } - void configLayers(bool itof = true, bool otof = true, bool ftof = true, bool btof = true, std::string pattern = "", bool itofSegmented = false, bool otofSegmented = false, const float x2x0 = 0.02f); + void configLayers(bool itof = true, bool otof = true, bool ftof = true, bool btof = true, std::string pattern = "", bool itofSegmented = false, bool otofSegmented = false, const float x2x0 = 0.02f, const float sensorThickness = 0.0050f); void configServices(); void createMaterials(); @@ -104,4 +104,4 @@ struct UseShm { } // namespace base } // namespace o2 #endif -#endif \ No newline at end of file +#endif diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Layer.h b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Layer.h index 29542810b8021..dc9fedf439a11 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Layer.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Layer.h @@ -26,7 +26,7 @@ class Layer public: Layer() = default; Layer(std::string layerName, float rInn, float rOut, float zLength, float zOffset, float layerX2X0, - int layout = kBarrel, int nStaves = 0, float staveSize = 0.0, double staveTiltAngle = 0.0, int modulesPerStave = 0); + int layout = kBarrel, int nStaves = 0, float staveSize = 0.0, double staveTiltAngle = 0.0, int modulesPerStave = 0, float sensorThickness = 0.0f); ~Layer() = default; auto getInnerRadius() const { return mInnerRadius; } @@ -52,8 +52,9 @@ class Layer float mZLength; float mZOffset{0.f}; // Of use when fwd layers float mX2X0; - float mChipThickness; - int mLayout{kBarrel}; // Identifier of the type of layer layout (barrel, disk, barrel segmented, disk segmented) + float mChipThickness; // Thickness of the chip in cm, derived from mX2X0 and the radiation length of silicon + float mSensorThickness; // Thickness of the sensor in cm, to be subtracted from the chip thickness to get the total module thickness + int mLayout{kBarrel}; // Identifier of the type of layer layout (barrel, disk, barrel segmented, disk segmented) // To be used only in case of the segmented layout, to define the number of staves in phi (for barrel) or in r (for disk) std::pair mStaves{0, 0.0f}; // Number and size of staves in phi (for barrel) or in r (for disk) in case of segmented layout int mModulesPerStave{0}; // Number of modules along a stave @@ -92,4 +93,4 @@ class BTOFLayer : public Layer } // namespace iotof } // namespace o2 -#endif // ALICEO2_IOTOF_LAYER_H \ No newline at end of file +#endif // ALICEO2_IOTOF_LAYER_H diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx index d6417eba22041..59b914a3dd076 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx @@ -58,7 +58,7 @@ void Detector::ConstructGeometry() } void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::string pattern, bool itofSegmented, bool otofSegmented, - const float x2x0) + const float x2x0, const float sensorThickness) { const std::pair dInnerTof = {21.f, 129.f}; // Radius and length @@ -102,7 +102,7 @@ void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::str const int modulesPerStave = itofSegmented ? 10 : 0; // number of modules per stave in segmented case mITOFLayer = ITOFLayer(name, dInnerTof.first, 0.f, dInnerTof.second, 0.f, x2x0, itofSegmented ? ITOFLayer::kBarrelSegmented : ITOFLayer::kBarrel, - nStaves, staveWidth, staveTiltAngle, modulesPerStave); + nStaves, staveWidth, staveTiltAngle, modulesPerStave, itofSegmented ? sensorThickness : 0.0f); } if (otof) { // oTOF const std::string name = GeometryTGeo::getOTOFLayerPattern(); @@ -112,7 +112,7 @@ void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::str const int modulesPerStave = otofSegmented ? 54 : 0; // number of modules per stave in segmented case mOTOFLayer = OTOFLayer(name, dOuterTof.first, 0.f, dOuterTof.second, 0.f, x2x0, otofSegmented ? OTOFLayer::kBarrelSegmented : OTOFLayer::kBarrel, - nStaves, staveWidth, staveTiltAngle, modulesPerStave); + nStaves, staveWidth, staveTiltAngle, modulesPerStave, otofSegmented ? sensorThickness : 0.0f); } if (ftof) { const std::string name = GeometryTGeo::getFTOFLayerPattern(); diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx index 66d0b2959c8bd..b603d2a4a423b 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx @@ -28,19 +28,20 @@ namespace o2 namespace iotof { Layer::Layer(std::string layerName, float rInn, float rOut, float zLength, float zOffset, float layerX2X0, - int layout, int nStaves, float staveSize, double staveTiltAngle, int modulesPerStave) + int layout, int nStaves, float staveSize, double staveTiltAngle, int modulesPerStave, float sensorThickness) : mLayerName(layerName), mInnerRadius(rInn), mOuterRadius(rOut), mZLength(zLength), mZOffset(zOffset), + mSensorThickness(sensorThickness), mX2X0(layerX2X0), mLayout(layout), mStaves(nStaves, staveSize), mModulesPerStave(modulesPerStave), mTiltAngle(staveTiltAngle) { - const float Si_X0 = 9.5f; + const float Si_X0 = 9.5f; // cm, radiation length of silicon mChipThickness = mX2X0 * Si_X0; std::string name = ""; switch (layout) { @@ -76,6 +77,12 @@ Layer::Layer(std::string layerName, float rInn, float rOut, float zLength, float if ((mTiltAngle < 0.0 || mTiltAngle > 90.0) && (layout == kBarrelSegmented || layout == kDiskSegmented)) { LOG(fatal) << "Invalid configuration: tilt angle " << mTiltAngle << " is too large, it must be between 0 and 90 degrees"; } + if (mSensorThickness < 0.0f || mSensorThickness > mChipThickness) { + LOG(fatal) << "Invalid configuration: sensor thickness " << mSensorThickness << " cm is out of range (0, " << mChipThickness << ") cm"; + } + if (sensorThickness > 0.0f && (layout == kBarrel || layout == kDisk)) { + LOG(fatal) << "Invalid configuration: sensor thickness " << mSensorThickness << " cm is set for non-segmented layout, it should be 0"; + } LOGP(info, "TOF: Creating {} layer: rInner: {} (cm) rOuter: {} (cm) zLength: {} (cm) zOffset: {} x2X0: {}", name.c_str(), mInnerRadius, mOuterRadius, mZLength, mZOffset, mX2X0); } @@ -193,7 +200,7 @@ void ITOFLayer::createLayer(TGeoVolume* motherVolume) const int sensorsPerChipX = 2; // we assume that each chip is divided in 2 sensors along the x direction const int sensorsPerChipZ = 2; // we assume that each chip is divided in 2 sensors along the z direction const double sensorSizeX = chipSizeX / sensorsPerChipX; // cm - const double sensorSizeY = chipSizeY; // cm + const double sensorSizeY = mSensorThickness; // cm const double sensorSizeZ = chipSizeZ / sensorsPerChipZ; // cm TGeoBBox* sensor = new TGeoBBox(sensorSizeX * 0.5, sensorSizeY * 0.5, sensorSizeZ * 0.5); TGeoVolume* sensVol = new TGeoVolume(sensName, sensor, medSi); @@ -327,7 +334,7 @@ void OTOFLayer::createLayer(TGeoVolume* motherVolume) const int sensorsPerChipX = 2; // we assume that each chip is divided in 2 sensors along the x direction const int sensorsPerChipZ = 2; // we assume that each chip is divided in 2 sensors along the z direction const double sensorSizeX = chipSizeX / sensorsPerChipX; // cm - const double sensorSizeY = chipSizeY; // cm + const double sensorSizeY = mSensorThickness; // cm const double sensorSizeZ = chipSizeZ / sensorsPerChipZ; // cm TGeoBBox* sensor = new TGeoBBox(sensorSizeX * 0.5, sensorSizeY * 0.5, sensorSizeZ * 0.5); TGeoVolume* sensVol = new TGeoVolume(sensName, sensor, medSi); From ea6b15c3746b71a0fd4cf7886caf9921639de65f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Wed, 1 Apr 2026 16:08:40 +0200 Subject: [PATCH 030/285] [ALICE3] TRK: Collect services in a dedicated volume assembly (#15215) * Refactor TRKServices to use volume assembly * Add service volume name to GeometryTGeo * Add getTRKServiceVolPattern method and variable --- .../ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h | 2 ++ .../Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx | 1 + .../ALICE3/TRK/simulation/src/TRKServices.cxx | 13 ++++++++----- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h index e32a2546c6842..576dbf434f757 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h @@ -43,6 +43,7 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache return sInstance.get(); }; static const char* getTRKVolPattern() { return sVolumeName.c_str(); } + static const char* getTRKServiceVolPattern() { return sServiceVolName.c_str(); } static const char* getTRKLayerPattern() { return sLayerName.c_str(); } static const char* getTRKPetalAssemblyPattern() { return sPetalAssemblyName.c_str(); } static const char* getTRKPetalPattern() { return sPetalName.c_str(); } @@ -198,6 +199,7 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache static constexpr int MAXLAYERS = 20; ///< max number of active layers static std::string sVolumeName; + static std::string sServiceVolName; static std::string sLayerName; static std::string sPetalAssemblyName; static std::string sPetalName; diff --git a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx index 36d26a6344e6c..10c1c63615d35 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx @@ -26,6 +26,7 @@ std::unique_ptr GeometryTGeo::sInstance; // Names std::string GeometryTGeo::sVolumeName = "TRKV"; +std::string GeometryTGeo::sServiceVolName = "TRKService"; std::string GeometryTGeo::sLayerName = "TRKLayer"; std::string GeometryTGeo::sPetalAssemblyName = "PETAL"; std::string GeometryTGeo::sPetalName = "PETALCASE"; diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx index d8246bcd8640c..7cf7dc863607e 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx @@ -127,18 +127,21 @@ void TRKServices::createMaterials() void TRKServices::createServices(TGeoVolume* motherVolume) { + + TGeoVolumeAssembly* vol = new TGeoVolumeAssembly(GeometryTGeo::getTRKServiceVolPattern()); + motherVolume->AddNode(vol, 2, new TGeoTranslation(0, 0., 0)); createMaterials(); createVacuumCompositeShape(); auto& trkPars = TRKBaseParam::Instance(); if (trkPars.getLayoutSRV() == kLOISymm) { LOGP(info, "TRK services: LoI version"); - createMiddleServices(motherVolume); - createOuterDisksServices(motherVolume); - createOuterBarrelServices(motherVolume); + createMiddleServices(vol); + createOuterDisksServices(vol); + createOuterBarrelServices(vol); } else { LOGP(info, "TRK services: Peacock layout"); - createMLServicesPeacock(motherVolume); - createOTServicesPeacock(motherVolume); + createMLServicesPeacock(vol); + createOTServicesPeacock(vol); } } From ebf039321ea3b9851aceb6d333bcb36f9b4cc7fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Wed, 1 Apr 2026 16:09:07 +0200 Subject: [PATCH 031/285] [ALICE3] Copy class of ITSMFT Hit for TRK Hit (#15194) --- Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt | 2 +- .../ALICE3/TRK/simulation/include/TRKSimulation/Hit.h | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt index 10f117750d793..6d30d8d01bb12 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt @@ -39,4 +39,4 @@ o2_target_root_dictionary(TRKSimulation include/TRKSimulation/VDLayer.h include/TRKSimulation/VDGeometryBuilder.h include/TRKSimulation/VDSensorRegistry.h - include/TRKSimulation/DPLDigitizerParam.h) \ No newline at end of file + include/TRKSimulation/DPLDigitizerParam.h) diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Hit.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Hit.h index 88afac8682cf4..402a343ead472 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Hit.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Hit.h @@ -19,7 +19,11 @@ namespace o2::trk { -using Hit = o2::itsmft::Hit; // For now we rely on the same Hit class as ITSMFT, but we can extend it with TRK-specific information if needed in the future +class Hit : public o2::itsmft::Hit +{ + public: + using o2::itsmft::Hit::Hit; // Inherit constructors +}; } // namespace o2::trk #endif From 8050f83b9ae9f1963af5bd37c1ad6c4b312a4d77 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Tue, 31 Mar 2026 20:55:54 +0200 Subject: [PATCH 032/285] DPL: add status endpoint to the driver --- Framework/Core/CMakeLists.txt | 1 + Framework/Core/include/Framework/DeviceInfo.h | 3 + .../Core/src/ControlWebSocketHandler.cxx | 5 + Framework/Core/src/DPLWebSocket.cxx | 10 +- Framework/Core/src/DPLWebSocket.h | 1 + Framework/Core/src/DriverServerContext.h | 5 + Framework/Core/src/StatusWebSocketHandler.cxx | 503 ++++++++++++++++++ Framework/Core/src/StatusWebSocketHandler.h | 101 ++++ Framework/Core/src/runDataProcessing.cxx | 7 + 9 files changed, 635 insertions(+), 1 deletion(-) create mode 100644 Framework/Core/src/StatusWebSocketHandler.cxx create mode 100644 Framework/Core/src/StatusWebSocketHandler.h diff --git a/Framework/Core/CMakeLists.txt b/Framework/Core/CMakeLists.txt index c311ba980a20b..0e67e1c0cc623 100644 --- a/Framework/Core/CMakeLists.txt +++ b/Framework/Core/CMakeLists.txt @@ -158,6 +158,7 @@ o2_add_library(Framework src/StepTHn.cxx src/Base64.cxx src/DPLWebSocket.cxx + src/StatusWebSocketHandler.cxx src/TimerParamSpec.cxx test/TestClasses.cxx TARGETVARNAME targetName diff --git a/Framework/Core/include/Framework/DeviceInfo.h b/Framework/Core/include/Framework/DeviceInfo.h index ef93ca83ca03f..bc3e895a3d8ed 100644 --- a/Framework/Core/include/Framework/DeviceInfo.h +++ b/Framework/Core/include/Framework/DeviceInfo.h @@ -61,6 +61,9 @@ struct DeviceInfo { std::string lastError; /// An unterminated string which is not ready to be printed yet std::string unprinted; + /// Total number of log lines ever stored in history (monotonically increasing). + /// Used by status clients to track which lines they have already sent. + size_t logSeq = 0; /// Whether the device is active (running) or not. bool active; /// Whether the device is ready to quit. diff --git a/Framework/Core/src/ControlWebSocketHandler.cxx b/Framework/Core/src/ControlWebSocketHandler.cxx index 6d7926918a8c7..35528a1d6dfec 100644 --- a/Framework/Core/src/ControlWebSocketHandler.cxx +++ b/Framework/Core/src/ControlWebSocketHandler.cxx @@ -11,6 +11,7 @@ #include "ControlWebSocketHandler.h" #include "DriverServerContext.h" +#include "StatusWebSocketHandler.h" #include "Framework/DeviceMetricsHelper.h" #include "Framework/ServiceMetricsInfo.h" #include @@ -83,6 +84,10 @@ void ControlWebSocketHandler::endChunk() for (auto& callback : *mContext.metricProcessingCallbacks) { callback(mContext.registry, ServiceMetricsInfo{*mContext.metrics, *mContext.specs, *mContext.infos, mContext.driver->metrics, *mContext.driver}, timestamp); } + // Notify status clients before changed flags are reset so they can see what changed. + for (auto* statusHandler : mContext.statusHandlers) { + statusHandler->sendUpdate(mIndex); + } for (auto& metricsInfo : *mContext.metrics) { std::fill(metricsInfo.changed.begin(), metricsInfo.changed.end(), false); } diff --git a/Framework/Core/src/DPLWebSocket.cxx b/Framework/Core/src/DPLWebSocket.cxx index d9b6594d5f07c..06de46b387c29 100644 --- a/Framework/Core/src/DPLWebSocket.cxx +++ b/Framework/Core/src/DPLWebSocket.cxx @@ -18,6 +18,7 @@ #include "DriverServerContext.h" #include "DriverClientContext.h" #include "ControlWebSocketHandler.h" +#include "StatusWebSocketHandler.h" #include "HTTPParser.h" #include #include @@ -193,9 +194,10 @@ void WSDPLHandler::method(std::string_view const& s) void WSDPLHandler::target(std::string_view const& s) { - if (s != "/") { + if (s != "/" && s != "/status") { throw WSError{404, "Unknown"}; } + mTarget = s; } void populateHeader(std::map& headers, std::string_view const& k, std::string_view const& v) @@ -294,6 +296,12 @@ void WSDPLHandler::endHeaders() break; } } + } else if (mTarget == "/status" && mServerContext->isDriver) { + LOGP(info, "Status client connected ({} total)", mServerContext->statusHandlers.size() + 1); + auto* statusHandler = new StatusWebSocketHandler(*mServerContext, this); + mServerContext->statusHandlers.push_back(statusHandler); + mHandler = std::unique_ptr(statusHandler); + mHandler->headers(mHeaders); } else { if ((mServerContext->isDriver && getenv("DPL_DRIVER_REMOTE_GUI")) || ((mServerContext->isDriver == false) && getenv("DPL_DEVICE_REMOTE_GUI"))) { LOG(info) << "Connection not bound to a PID"; diff --git a/Framework/Core/src/DPLWebSocket.h b/Framework/Core/src/DPLWebSocket.h index 43ec27a6b54f0..1985c37157d65 100644 --- a/Framework/Core/src/DPLWebSocket.h +++ b/Framework/Core/src/DPLWebSocket.h @@ -62,6 +62,7 @@ struct WSDPLHandler : public HTTPParser { bool mHandshaken = false; uv_stream_t* mStream = nullptr; std::map mHeaders; + std::string mTarget; DriverServerContext* mServerContext; }; diff --git a/Framework/Core/src/DriverServerContext.h b/Framework/Core/src/DriverServerContext.h index 4d25c47bd172b..c9f2c80165d92 100644 --- a/Framework/Core/src/DriverServerContext.h +++ b/Framework/Core/src/DriverServerContext.h @@ -29,6 +29,7 @@ namespace o2::framework struct DriverInfo; struct ServiceRegistry; struct GuiCallbackContext; +struct StatusWebSocketHandler; struct DriverServerContext { ServiceRegistryRef registry; @@ -49,6 +50,10 @@ struct DriverServerContext { /// or something like that. bool isDriver = false; + /// Connected MCP/status clients. Updated by StatusWebSocketHandler + /// on connect/disconnect; notified by ControlWebSocketHandler::endChunk(). + std::vector statusHandlers; + /// The handle to the server component of the /// driver. uv_tcp_t serverHandle; diff --git a/Framework/Core/src/StatusWebSocketHandler.cxx b/Framework/Core/src/StatusWebSocketHandler.cxx new file mode 100644 index 0000000000000..db715eff6592d --- /dev/null +++ b/Framework/Core/src/StatusWebSocketHandler.cxx @@ -0,0 +1,503 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "StatusWebSocketHandler.h" +#include "DPLWebSocket.h" +#include "DriverServerContext.h" +#include "Framework/DeviceInfo.h" +#include "Framework/DeviceMetricsInfo.h" +#include "Framework/DeviceSpec.h" +#include "Framework/DeviceStateEnums.h" +#include "Framework/LogParsingHelpers.h" +#include +#include +#include +#include +#include + +namespace o2::framework +{ + +namespace +{ + +std::string jsonEscape(std::string_view s) +{ + std::string out; + out.reserve(s.size() + 4); + for (unsigned char c : s) { + switch (c) { + case '"': + out += "\\\""; + break; + case '\\': + out += "\\\\"; + break; + case '\n': + out += "\\n"; + break; + case '\r': + out += "\\r"; + break; + case '\t': + out += "\\t"; + break; + default: + if (c < 0x20) { + char buf[8]; + snprintf(buf, sizeof(buf), "\\u%04x", c); + out += buf; + } else { + out += static_cast(c); + } + } + } + return out; +} + +char const* logLevelName(LogParsingHelpers::LogLevel level) +{ + switch (level) { + case LogParsingHelpers::LogLevel::Debug: + return "debug"; + case LogParsingHelpers::LogLevel::Info: + return "info"; + case LogParsingHelpers::LogLevel::Important: + return "important"; + case LogParsingHelpers::LogLevel::Warning: + return "warning"; + case LogParsingHelpers::LogLevel::Alarm: + return "alarm"; + case LogParsingHelpers::LogLevel::Error: + return "error"; + case LogParsingHelpers::LogLevel::Critical: + return "critical"; + case LogParsingHelpers::LogLevel::Fatal: + return "fatal"; + default: + return "unknown"; + } +} + +char const* streamingStateName(StreamingState s) +{ + switch (s) { + case StreamingState::Streaming: + return "Streaming"; + case StreamingState::EndOfStreaming: + return "EndOfStreaming"; + case StreamingState::Idle: + return "Idle"; + default: + return "Unknown"; + } +} + +void appendMetricValue(std::string& out, DeviceMetricsInfo const& info, size_t mi) +{ + auto const& metric = info.metrics[mi]; + if (metric.pos == 0) { + out += "null"; + return; + } + size_t last = (metric.pos - 1) % metricStorageSize(metric.type); + switch (metric.type) { + case MetricType::Int: + out += std::to_string(info.intMetrics[metric.storeIdx][last]); + break; + case MetricType::Float: { + char buf[32]; + snprintf(buf, sizeof(buf), "%g", static_cast(info.floatMetrics[metric.storeIdx][last])); + out += buf; + break; + } + case MetricType::Uint64: + out += std::to_string(info.uint64Metrics[metric.storeIdx][last]); + break; + default: + out += "null"; + } +} + +/// Extract the value of a simple string field from a flat JSON object. +/// e.g. extractField(R"({"cmd":"subscribe","device":"prod"})", "device") → "prod" +/// Returns empty string_view if not found. +std::string_view extractStringField(std::string_view json, std::string_view key) +{ + std::string needle; + needle += '"'; + needle += key; + needle += "\":"; + auto pos = json.find(needle); + if (pos == std::string_view::npos) { + return {}; + } + pos += needle.size(); + // skip optional whitespace between ':' and '"' + while (pos < json.size() && json[pos] == ' ') { + ++pos; + } + if (pos >= json.size() || json[pos] != '"') { + return {}; + } + ++pos; // skip opening quote + auto end = json.find('"', pos); + if (end == std::string_view::npos) { + return {}; + } + return json.substr(pos, end - pos); +} + +/// Extract the raw value of an array field from a flat JSON object. +/// e.g. extractArrayField(R"({"metrics":["a","b"]})", "metrics") → R"(["a","b"])" +std::string_view extractArrayField(std::string_view json, std::string_view key) +{ + std::string needle; + needle += '"'; + needle += key; + needle += "\":"; + auto pos = json.find(needle); + if (pos == std::string_view::npos) { + return {}; + } + pos += needle.size(); + // skip whitespace + while (pos < json.size() && json[pos] == ' ') { + ++pos; + } + if (pos >= json.size() || json[pos] != '[') { + return {}; + } + auto start = pos; + size_t depth = 0; + while (pos < json.size()) { + if (json[pos] == '[') { + ++depth; + } else if (json[pos] == ']') { + --depth; + if (depth == 0) { + return json.substr(start, pos - start + 1); + } + } + ++pos; + } + return {}; +} + +/// Iterate over the string elements of a JSON array of strings. +/// Calls @a callback for each unescaped string value. +template +void forEachStringInArray(std::string_view arr, F&& callback) +{ + // arr is like ["name1","name2"] + size_t pos = 0; + while (pos < arr.size()) { + auto q = arr.find('"', pos); + if (q == std::string_view::npos) { + break; + } + auto end = arr.find('"', q + 1); + if (end == std::string_view::npos) { + break; + } + callback(arr.substr(q + 1, end - q - 1)); + pos = end + 1; + } +} + +} // anonymous namespace + +StatusWebSocketHandler::StatusWebSocketHandler(DriverServerContext& context, WSDPLHandler* handler) + : mContext{context}, mHandler{handler} +{ +} + +StatusWebSocketHandler::~StatusWebSocketHandler() +{ + auto& handlers = mContext.statusHandlers; + handlers.erase(std::remove(handlers.begin(), handlers.end(), this), handlers.end()); +} + +void StatusWebSocketHandler::headers(std::map const&) +{ + sendSnapshot(); +} + +void StatusWebSocketHandler::frame(char const* data, size_t s) +{ + std::string_view msg{data, s}; + auto cmd = extractStringField(msg, "cmd"); + if (cmd.empty()) { + return; + } + auto deviceName = extractStringField(msg, "device"); + + if (cmd == "list_metrics") { + handleListMetrics(deviceName); + } else if (cmd == "subscribe") { + handleSubscribe(deviceName, extractArrayField(msg, "metrics")); + } else if (cmd == "unsubscribe") { + handleUnsubscribe(deviceName, extractArrayField(msg, "metrics")); + } else if (cmd == "subscribe_logs") { + handleSubscribeLogs(deviceName); + } else if (cmd == "unsubscribe_logs") { + handleUnsubscribeLogs(deviceName); + } +} + +void StatusWebSocketHandler::sendText(std::string const& json) +{ + std::vector outputs; + encode_websocket_frames(outputs, json.data(), json.size(), WebSocketOpCode::Text, 0); + mHandler->write(outputs); +} + +void StatusWebSocketHandler::sendSnapshot() +{ + auto const& specs = *mContext.specs; + auto const& infos = *mContext.infos; + + // Size subscription tables to current device count; grow lazily as needed. + mSubscribedMetrics.resize(specs.size()); + mLastLogSeq.resize(infos.size()); + for (size_t di = 0; di < infos.size(); ++di) { + mLastLogSeq[di] = infos[di].logSeq; + } + + std::string out; + out.reserve(512 + specs.size() * 128); + out += R"({"type":"snapshot","devices":[)"; + for (size_t di = 0; di < specs.size(); ++di) { + if (di > 0) { + out += ','; + } + auto const& info = infos[di]; + out += R"({"name":")"; + out += jsonEscape(specs[di].name); + out += R"(","pid":)"; + out += std::to_string(info.pid); + out += R"(,"active":)"; + out += info.active ? "true" : "false"; + out += R"(,"streamingState":")"; + out += streamingStateName(info.streamingState); + out += R"(","deviceState":")"; + out += jsonEscape(info.deviceState); + out += R"("})"; + } + out += "]}"; + sendText(out); +} + +void StatusWebSocketHandler::sendUpdate(size_t deviceIndex) +{ + auto const& specs = *mContext.specs; + auto const& metrics = *mContext.metrics; + + if (deviceIndex >= specs.size() || deviceIndex >= metrics.size()) { + return; + } + + // Lazily grow the subscription table if new devices were added after snapshot. + if (mSubscribedMetrics.size() <= deviceIndex) { + mSubscribedMetrics.resize(deviceIndex + 1); + } + + auto const& subscribed = mSubscribedMetrics[deviceIndex]; + if (subscribed.empty()) { + return; + } + + auto const& info = metrics[deviceIndex]; + std::string metricsJson; + metricsJson += '{'; + bool first = true; + for (size_t mi = 0; mi < info.metrics.size(); ++mi) { + if (!info.changed[mi]) { + continue; + } + auto const& metric = info.metrics[mi]; + if (metric.type == MetricType::String || + metric.type == MetricType::Enum || + metric.type == MetricType::Unknown) { + continue; + } + auto const& label = info.metricLabels[mi]; + std::string_view labelSV{label.label, label.size}; + if (subscribed.find(std::string(labelSV)) == subscribed.end()) { + continue; + } + if (!first) { + metricsJson += ','; + } + first = false; + metricsJson += '"'; + metricsJson += jsonEscape(labelSV); + metricsJson += "\":"; + appendMetricValue(metricsJson, info, mi); + } + metricsJson += '}'; + + if (first) { + // Nothing subscribed changed in this cycle. + return; + } + + std::string out; + out += R"({"type":"update","device":)"; + out += std::to_string(deviceIndex); + out += R"(,"name":")"; + out += jsonEscape(specs[deviceIndex].name); + out += R"(","metrics":)"; + out += metricsJson; + out += '}'; + sendText(out); +} + +void StatusWebSocketHandler::handleListMetrics(std::string_view deviceName) +{ + size_t di = findDeviceIndex(deviceName); + if (di == SIZE_MAX) { + return; + } + auto const& metrics = *mContext.metrics; + + std::string out; + out += R"({"type":"metrics_list","device":")"; + out += jsonEscape(deviceName); + out += R"(","metrics":[)"; + bool first = true; + if (di < metrics.size()) { + auto const& info = metrics[di]; + for (size_t mi = 0; mi < info.metrics.size(); ++mi) { + auto const& metric = info.metrics[mi]; + if (metric.type == MetricType::String || + metric.type == MetricType::Enum || + metric.type == MetricType::Unknown) { + continue; + } + if (!first) { + out += ','; + } + first = false; + auto const& label = info.metricLabels[mi]; + out += '"'; + out += jsonEscape({label.label, label.size}); + out += '"'; + } + } + out += "]}"; + sendText(out); +} + +void StatusWebSocketHandler::handleSubscribe(std::string_view deviceName, std::string_view metricsArr) +{ + size_t di = findDeviceIndex(deviceName); + if (di == SIZE_MAX || metricsArr.empty()) { + return; + } + if (mSubscribedMetrics.size() <= di) { + mSubscribedMetrics.resize(di + 1); + } + forEachStringInArray(metricsArr, [&](std::string_view name) { + mSubscribedMetrics[di].emplace(name); + }); +} + +void StatusWebSocketHandler::handleUnsubscribe(std::string_view deviceName, std::string_view metricsArr) +{ + size_t di = findDeviceIndex(deviceName); + if (di == SIZE_MAX || metricsArr.empty() || di >= mSubscribedMetrics.size()) { + return; + } + forEachStringInArray(metricsArr, [&](std::string_view name) { + mSubscribedMetrics[di].erase(std::string(name)); + }); +} + +size_t StatusWebSocketHandler::findDeviceIndex(std::string_view name) const +{ + auto const& specs = *mContext.specs; + for (size_t di = 0; di < specs.size(); ++di) { + if (specs[di].name == name) { + return di; + } + } + return SIZE_MAX; +} + +void StatusWebSocketHandler::handleSubscribeLogs(std::string_view deviceName) +{ + size_t di = findDeviceIndex(deviceName); + if (di == SIZE_MAX) { + return; + } + if (mLastLogSeq.size() <= di) { + mLastLogSeq.resize(di + 1, 0); + } + // Start the cursor at the current log position so we only push future lines. + mLastLogSeq[di] = (*mContext.infos)[di].logSeq; + mLogSubscriptions.insert(di); +} + +void StatusWebSocketHandler::handleUnsubscribeLogs(std::string_view deviceName) +{ + size_t di = findDeviceIndex(deviceName); + if (di == SIZE_MAX) { + return; + } + mLogSubscriptions.erase(di); +} + +void StatusWebSocketHandler::sendNewLogs(size_t deviceIndex) +{ + if (mLogSubscriptions.find(deviceIndex) == mLogSubscriptions.end()) { + return; + } + auto const& infos = *mContext.infos; + auto const& specs = *mContext.specs; + if (deviceIndex >= infos.size() || deviceIndex >= specs.size()) { + return; + } + if (mLastLogSeq.size() <= deviceIndex) { + mLastLogSeq.resize(deviceIndex + 1, 0); + } + + auto const& info = infos[deviceIndex]; + size_t newLines = info.logSeq - mLastLogSeq[deviceIndex]; + if (newLines == 0) { + return; + } + // Cap to buffer size to avoid re-reading overwritten entries. + if (newLines > info.history.size()) { + newLines = info.history.size(); + } + + size_t histSize = info.history.size(); + // The oldest unread entry sits at (historyPos - newLines + histSize) % histSize. + size_t startPos = (info.historyPos + histSize - newLines) % histSize; + + std::string_view devName = specs[deviceIndex].name; + for (size_t i = 0; i < newLines; ++i) { + size_t pos = (startPos + i) % histSize; + std::string out; + out += R"({"type":"log","device":")"; + out += jsonEscape(devName); + out += R"(","level":")"; + out += logLevelName(info.historyLevel[pos]); + out += R"(","line":")"; + out += jsonEscape(info.history[pos]); + out += R"("})"; + sendText(out); + } + mLastLogSeq[deviceIndex] = info.logSeq; +} + +} // namespace o2::framework diff --git a/Framework/Core/src/StatusWebSocketHandler.h b/Framework/Core/src/StatusWebSocketHandler.h new file mode 100644 index 0000000000000..86a460e289440 --- /dev/null +++ b/Framework/Core/src/StatusWebSocketHandler.h @@ -0,0 +1,101 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#ifndef O2_FRAMEWORK_STATUSWEBSOCKETHANDLER_H_ +#define O2_FRAMEWORK_STATUSWEBSOCKETHANDLER_H_ + +#include "HTTPParser.h" +#include +#include +#include +#include +#include + +namespace o2::framework +{ +struct DriverServerContext; +struct WSDPLHandler; + +/// WebSocket handler for the /status endpoint. +/// +/// Protocol (client → driver): +/// {"cmd":"list_metrics","device":""} +/// → driver replies with {"type":"metrics_list","device":"","metrics":[...]} +/// +/// {"cmd":"subscribe","device":"","metrics":["m1","m2",...]} +/// → driver starts including those metrics in subsequent update frames +/// +/// {"cmd":"unsubscribe","device":"","metrics":["m1","m2",...]} +/// → driver stops sending those metrics +/// +/// {"cmd":"subscribe_logs","device":""} +/// → driver starts pushing new log lines for the device +/// +/// {"cmd":"unsubscribe_logs","device":""} +/// → driver stops pushing log lines for the device +/// +/// Protocol (driver → client): +/// {"type":"snapshot","devices":[{"name","pid","active","streamingState","deviceState"},...]} +/// → sent once on connect; contains no metrics or logs +/// +/// {"type":"update","device":,"name":"","metrics":{}} +/// → sent after each metrics cycle for devices with subscribed metrics that changed +/// +/// {"type":"metrics_list","device":"","metrics":["m1","m2",...]} +/// → reply to list_metrics command +/// +/// {"type":"log","device":"","level":"","line":""} +/// → pushed for each new log line from a subscribed device +struct StatusWebSocketHandler : public WebSocketHandler { + StatusWebSocketHandler(DriverServerContext& context, WSDPLHandler* handler); + ~StatusWebSocketHandler() override; + + /// Sends the minimal snapshot on handshake completion. + void headers(std::map const& headers) override; + /// Handles incoming commands from the MCP client. + void frame(char const* data, size_t s) override; + void beginChunk() override {} + void endChunk() override {} + void beginFragmentation() override {} + void endFragmentation() override {} + void control(char const* frame, size_t s) override {} + + /// Send a minimal JSON snapshot (device list + basic state, no metrics/logs). + void sendSnapshot(); + /// Push an update for device at @a deviceIndex. + /// Only metrics that are both changed[] and subscribed are included. + /// No-op if nothing subscribed or nothing changed for this device. + void sendUpdate(size_t deviceIndex); + /// Push any log lines for @a deviceIndex that arrived since the last call. + /// No-op if the device is not subscribed for logs. + void sendNewLogs(size_t deviceIndex); + + private: + void sendText(std::string const& json); + void handleListMetrics(std::string_view deviceName); + void handleSubscribe(std::string_view deviceName, std::string_view metricsJson); + void handleUnsubscribe(std::string_view deviceName, std::string_view metricsJson); + void handleSubscribeLogs(std::string_view deviceName); + void handleUnsubscribeLogs(std::string_view deviceName); + size_t findDeviceIndex(std::string_view name) const; + + DriverServerContext& mContext; + WSDPLHandler* mHandler; + /// Per-device set of subscribed metric label strings. + /// Sized to specs->size() on sendSnapshot(); grows if new devices appear. + std::vector> mSubscribedMetrics; + /// Per-device log cursor: value of DeviceInfo::logSeq when we last sent logs. + std::vector mLastLogSeq; + /// Set of device indices whose logs are being streamed. + std::unordered_set mLogSubscriptions; +}; + +} // namespace o2::framework +#endif // O2_FRAMEWORK_STATUSWEBSOCKETHANDLER_H_ diff --git a/Framework/Core/src/runDataProcessing.cxx b/Framework/Core/src/runDataProcessing.cxx index 98cbf70370c3d..70f3c8940ef26 100644 --- a/Framework/Core/src/runDataProcessing.cxx +++ b/Framework/Core/src/runDataProcessing.cxx @@ -68,6 +68,7 @@ #include "Framework/DefaultsHelpers.h" #include "ProcessingPoliciesHelpers.h" #include "DriverServerContext.h" +#include "StatusWebSocketHandler.h" #include "HTTPParser.h" #include "DPLWebSocket.h" #include "ArrowSupport.h" @@ -891,6 +892,7 @@ void processChildrenOutput(uv_loop_t* loop, info.history[info.historyPos] = token; info.historyLevel[info.historyPos] = logLevel; info.historyPos = (info.historyPos + 1) % info.history.size(); + info.logSeq++; fmt::print("[{}:{}]: {}\n", info.pid, spec.id, token); } // We keep track of the maximum log error a @@ -1541,6 +1543,11 @@ int runStateMachine(DataProcessorSpecs const& workflow, uv_async_init(loop, serverContext.asyncLogProcessing, [](uv_async_t* handle) { auto* context = (DriverServerContext*)handle->data; processChildrenOutput(context->loop, *context->driver, *context->infos, *context->specs, *context->controls); + for (auto* statusHandler : context->statusHandlers) { + for (size_t di = 0; di < context->infos->size(); ++di) { + statusHandler->sendNewLogs(di); + } + } }); while (true) { From 2b4851c08fec0e0c8d95006801beb984bc7e4869 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Tue, 31 Mar 2026 20:55:54 +0200 Subject: [PATCH 033/285] DPL: add MCP server for DPL Allows debugging a DPL workflow using Claude, ChatGPT or similar tools. --- .../Core/scripts/dpl-mcp-server/README.md | 75 +++++ .../scripts/dpl-mcp-server/dpl_mcp_server.py | 304 ++++++++++++++++++ .../scripts/dpl-mcp-server/pyproject.toml | 19 ++ 3 files changed, 398 insertions(+) create mode 100644 Framework/Core/scripts/dpl-mcp-server/README.md create mode 100644 Framework/Core/scripts/dpl-mcp-server/dpl_mcp_server.py create mode 100644 Framework/Core/scripts/dpl-mcp-server/pyproject.toml diff --git a/Framework/Core/scripts/dpl-mcp-server/README.md b/Framework/Core/scripts/dpl-mcp-server/README.md new file mode 100644 index 0000000000000..65d2378c5d756 --- /dev/null +++ b/Framework/Core/scripts/dpl-mcp-server/README.md @@ -0,0 +1,75 @@ +# DPL Status MCP Server + +An MCP server that connects to a running DPL driver's `/status` WebSocket endpoint and exposes its device state and metrics as tools for an AI assistant (e.g. Claude). + +## Requirements + +```bash +pip install mcp websockets +# or install the package directly: +pip install ./Framework/Core/scripts/dpl-mcp-server/ +``` + +## Running + +The driver port defaults to `8080`. Override with `--port`, `--pid`, or `DPL_STATUS_PORT`: + +```bash +python3 dpl_mcp_server.py --port 8080 +python3 dpl_mcp_server.py --pid 12345 # port = 8080 + pid % 30000 +DPL_STATUS_PORT=8080 python3 dpl_mcp_server.py +``` + +If installed as a package: + +```bash +dpl-mcp-server --pid $(pgrep -f diamond-workflow | head -1) +``` + +## Claude Code integration + +Add to `.mcp.json` in your project (or `~/.claude.json` for global use): + +```json +{ + "mcpServers": { + "dpl": { + "command": "dpl-mcp-server", + "args": ["--pid", "12345"] + } + } +} +``` + +Or with `claude mcp add`: + +```bash +claude mcp add dpl -- dpl-mcp-server --pid 12345 +``` + +## Available tools + +| Tool | Description | +|------|-------------| +| `list_devices` | List all devices with pid, active flag, streaming and device state | +| `list_metrics(device)` | List numeric metrics available for a device | +| `subscribe(device, metrics)` | Subscribe to metrics; driver will push updates when they change | +| `unsubscribe(device, metrics)` | Stop receiving updates for specific metrics | +| `get_updates(max_updates)` | Drain buffered update frames (default: up to 50) | + +## Protocol + +The driver sends a snapshot on connect, then pushes updates only for subscribed metrics that changed each processing cycle. There is no polling — updates arrive in real time as the workflow runs. + +``` +connect → {"type":"snapshot","devices":[{"name","pid","active","streamingState","deviceState"},...]} + +client → {"cmd":"list_metrics","device":"producer"} +driver → {"type":"metrics_list","device":"producer","metrics":["input-parts","output-bytes",...]} + +client → {"cmd":"subscribe","device":"producer","metrics":["output-bytes"]} +driver → {"type":"update","device":0,"name":"producer","metrics":{"output-bytes":1048576}} + (pushed every cycle in which output-bytes changed) + +client → {"cmd":"unsubscribe","device":"producer","metrics":["output-bytes"]} +``` diff --git a/Framework/Core/scripts/dpl-mcp-server/dpl_mcp_server.py b/Framework/Core/scripts/dpl-mcp-server/dpl_mcp_server.py new file mode 100644 index 0000000000000..bc04acf026188 --- /dev/null +++ b/Framework/Core/scripts/dpl-mcp-server/dpl_mcp_server.py @@ -0,0 +1,304 @@ +#!/usr/bin/env python3 +# 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. +"""DPL status MCP server. + +Bridges the DPL driver /status WebSocket endpoint to MCP tools so that an +AI assistant (e.g. Claude) can inspect and monitor a running DPL workflow. + +Usage +----- + python3 dpl_mcp_server.py --port 8080 + python3 dpl_mcp_server.py --pid 12345 # port derived as 8080 + pid % 30000 + DPL_STATUS_PORT=8080 python3 dpl_mcp_server.py + +Wire protocol (client → driver) +-------------------------------- + {"cmd":"list_metrics","device":""} + {"cmd":"subscribe","device":"","metrics":["m1","m2"]} + {"cmd":"unsubscribe","device":"","metrics":["m1"]} + +Wire protocol (driver → client) +-------------------------------- + {"type":"snapshot","devices":[{"name","pid","active","streamingState","deviceState"},...]} + {"type":"update","device":,"name":"","metrics":{}} + {"type":"metrics_list","device":"","metrics":["m1","m2",...]} +""" + +from __future__ import annotations + +import argparse +import asyncio +import json +import os +import sys +from typing import Any + +import websockets +from mcp.server.fastmcp import FastMCP + +# --------------------------------------------------------------------------- +# Global connection state (all access from the single asyncio event loop) +# --------------------------------------------------------------------------- +_port: int = 8080 +_ws: Any = None +_reader_task: asyncio.Task | None = None +_snapshot: dict = {} +_updates: list[dict] = [] +_logs: list[dict] = [] +_metrics_lists: dict[str, list[str]] = {} + + +async def _ensure_connected() -> None: + """Connect (or reconnect) to the driver's /status WebSocket.""" + global _ws, _reader_task + + # Check liveness of existing connection. + if _ws is not None: + try: + pong = await asyncio.wait_for(_ws.ping(), timeout=2.0) + await pong + return + except Exception: + _ws = None + if _reader_task is not None and not _reader_task.done(): + _reader_task.cancel() + _reader_task = None + + url = f"ws://localhost:{_port}/status" + _ws = await websockets.connect(url, subprotocols=["dpl"]) + if _reader_task is None or _reader_task.done(): + _reader_task = asyncio.create_task(_reader()) + + +async def _reader() -> None: + """Background task: read frames from the driver and buffer them.""" + global _ws, _snapshot, _updates, _logs, _metrics_lists + try: + async for raw in _ws: + try: + msg = json.loads(raw) + except json.JSONDecodeError: + continue + t = msg.get("type") + if t == "snapshot": + _snapshot = msg + # Clear stale metric lists from a previous driver instance. + _metrics_lists.clear() + elif t == "update": + _updates.append(msg) + elif t == "log": + _logs.append(msg) + elif t == "metrics_list": + device = msg.get("device", "") + _metrics_lists[device] = msg.get("metrics", []) + except Exception: + pass + finally: + _ws = None + + +async def _send(obj: dict) -> None: + await _ensure_connected() + await _ws.send(json.dumps(obj, separators=(",", ":"))) + + +# --------------------------------------------------------------------------- +# MCP server definition +# --------------------------------------------------------------------------- +mcp = FastMCP("DPL Status") + + +@mcp.tool() +async def list_devices() -> str: + """List all DPL devices with their current status. + + Returns each device's name, PID, active flag, streaming state, and device + state as reported by the driver snapshot. + """ + await _ensure_connected() + if not _snapshot: + return "No snapshot received yet — the driver may still be starting." + devices = _snapshot.get("devices", []) + if not devices: + return "No devices in snapshot." + lines = [] + for d in devices: + lines.append( + f"{d['name']}: pid={d['pid']} active={d['active']} " + f"streaming={d['streamingState']} state={d['deviceState']}" + ) + return "\n".join(lines) + + +@mcp.tool() +async def list_metrics(device: str) -> str: + """List the available numeric metrics for a DPL device. + + Sends a list_metrics command to the driver and waits up to 3 seconds for + the reply. Only numeric metrics (int, float, uint64) are included; string + and enum metrics are excluded. + + Args: + device: Device name exactly as shown by list_devices. + """ + # Remove any stale cached result so we can detect the fresh reply. + _metrics_lists.pop(device, None) + await _send({"cmd": "list_metrics", "device": device}) + for _ in range(60): # up to 3 s + await asyncio.sleep(0.05) + if device in _metrics_lists: + names = _metrics_lists[device] + if not names: + return f"Device '{device}' has no numeric metrics yet." + return f"{len(names)} metric(s): " + ", ".join(names) + return f"No reply from driver for device '{device}' (timeout)." + + +@mcp.tool() +async def subscribe(device: str, metrics: list[str]) -> str: + """Subscribe to one or more metrics for a DPL device. + + After subscribing, the driver will push update frames for the device + whenever any of the subscribed metrics change. Use get_updates to drain + the buffer. + + Args: + device: Device name exactly as shown by list_devices. + metrics: List of metric names to subscribe to (from list_metrics). + """ + await _send({"cmd": "subscribe", "device": device, "metrics": metrics}) + return f"Subscribed to {len(metrics)} metric(s) for '{device}': {', '.join(metrics)}" + + +@mcp.tool() +async def unsubscribe(device: str, metrics: list[str]) -> str: + """Stop receiving updates for specific metrics of a DPL device. + + Args: + device: Device name exactly as shown by list_devices. + metrics: List of metric names to unsubscribe from. + """ + await _send({"cmd": "unsubscribe", "device": device, "metrics": metrics}) + return f"Unsubscribed from {len(metrics)} metric(s) for '{device}'." + + +@mcp.tool() +async def subscribe_logs(device: str) -> str: + """Subscribe to log output for a DPL device. + + After subscribing, new log lines from the device will be buffered and + can be retrieved with get_logs(). + + Args: + device: Device name exactly as shown by list_devices. + """ + await _send({"cmd": "subscribe_logs", "device": device}) + return f"Subscribed to logs for '{device}'." + + +@mcp.tool() +async def unsubscribe_logs(device: str) -> str: + """Stop receiving log output for a DPL device. + + Args: + device: Device name exactly as shown by list_devices. + """ + await _send({"cmd": "unsubscribe_logs", "device": device}) + return f"Unsubscribed from logs for '{device}'." + + +@mcp.tool() +async def get_logs(max_lines: int = 100) -> str: + """Drain and return buffered log lines received since the last call. + + Args: + max_lines: Maximum number of log lines to return (default 100). + """ + await _ensure_connected() + batch = _logs[:max_lines] + del _logs[:max_lines] + if not batch: + return "No buffered log lines." + lines = [] + for entry in batch: + device = entry.get("device", "?") + level = entry.get("level", "?") + line = entry.get("line", "") + lines.append(f"[{device}][{level}] {line}") + return "\n".join(lines) + + +@mcp.tool() +async def get_updates(max_updates: int = 50) -> str: + """Drain and return buffered metric update frames received since the last call. + + Each frame contains the latest values of all subscribed metrics that + changed during that processing cycle. Calling this repeatedly gives a + time-ordered view of metric evolution. + + Args: + max_updates: Maximum number of update frames to return (default 50). + """ + await _ensure_connected() + batch = _updates[:max_updates] + del _updates[:max_updates] + if not batch: + return "No buffered updates." + lines = [] + for upd in batch: + name = upd.get("name") or f"device[{upd.get('device', '?')}]" + metrics = upd.get("metrics", {}) + if metrics: + parts = ", ".join(f"{k}={v}" for k, v in metrics.items()) + lines.append(f"{name}: {parts}") + else: + lines.append(f"{name}: (empty update)") + return "\n".join(lines) + + +# --------------------------------------------------------------------------- +# Entry point +# --------------------------------------------------------------------------- +def main() -> None: + global _port + + parser = argparse.ArgumentParser( + description="DPL status MCP server — expose DPL driver metrics via MCP tools" + ) + group = parser.add_mutually_exclusive_group() + group.add_argument( + "--port", + type=int, + default=None, + help="TCP port of the DPL driver status WebSocket (default: 8080 or DPL_STATUS_PORT env var)", + ) + group.add_argument( + "--pid", + type=int, + default=None, + help="PID of the DPL driver process; port is derived as 8080 + pid %% 30000", + ) + args = parser.parse_args() + + if args.pid is not None: + _port = 8080 + args.pid % 30000 + elif args.port is not None: + _port = args.port + elif "DPL_STATUS_PORT" in os.environ: + _port = int(os.environ["DPL_STATUS_PORT"]) + # else leave _port at the default 8080 + + mcp.run() + + +if __name__ == "__main__": + main() diff --git a/Framework/Core/scripts/dpl-mcp-server/pyproject.toml b/Framework/Core/scripts/dpl-mcp-server/pyproject.toml new file mode 100644 index 0000000000000..f87c1b770c934 --- /dev/null +++ b/Framework/Core/scripts/dpl-mcp-server/pyproject.toml @@ -0,0 +1,19 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "dpl-mcp-server" +version = "0.1.0" +description = "MCP server for monitoring DPL (Data Processing Layer) workflows" +requires-python = ">=3.11" +dependencies = [ + "mcp>=1.0.0", + "websockets>=12.0", +] + +[project.scripts] +dpl-mcp-server = "dpl_mcp_server:main" + +[tool.hatch.build.targets.wheel] +include = ["dpl_mcp_server.py"] From 4f060ca921092cc4090c746b8ab08d3f4a72596a Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Sat, 28 Mar 2026 15:48:51 +0100 Subject: [PATCH 034/285] C++ standard fobids specializations of is_trivially_copyable --- .../MathUtils/include/MathUtils/Cartesian.h | 20 ++++---------- Common/MathUtils/test/testCartesian.cxx | 2 +- .../DetectorsDCS/DataPointCompositeObject.h | 26 ++----------------- .../DetectorsDCS/DataPointIdentifier.h | 23 ++-------------- Detectors/DCS/test/testDataPointTypes.cxx | 4 +-- Framework/Core/include/Framework/TypeTraits.h | 13 +++++++--- 6 files changed, 20 insertions(+), 68 deletions(-) diff --git a/Common/MathUtils/include/MathUtils/Cartesian.h b/Common/MathUtils/include/MathUtils/Cartesian.h index 9b917707835a6..d7e421ecd965b 100644 --- a/Common/MathUtils/include/MathUtils/Cartesian.h +++ b/Common/MathUtils/include/MathUtils/Cartesian.h @@ -284,25 +284,15 @@ GPUdi() SMatrix> Similarity(const SMatrix +struct is_forced_trivially_copyable; -/// Defining Point3D explicitly as trivially copyable -/// -/// std::is_trivially_copyable> fails because the class -/// implements a copy constructor, although it does not much more than the default copy -/// constructor. We need Point3D to fulfill the condition in order to make types -/// inheriting from it or using it as member can be safely detected as messageable. -/// -/// We believe that Point3D is messageable and explicitly specialize the type trait. -/// There is a unit test for checking trivial copy -/// This is a workaround, we will also make suggestions to fix the cause in ROOT itself -/// TODO: delete once it is fixed in ROOT template -struct is_trivially_copyable> : std::true_type { +struct is_forced_trivially_copyable> : std::true_type { }; -} // namespace std +} // namespace o2::framework #endif // Disable for GPU #endif diff --git a/Common/MathUtils/test/testCartesian.cxx b/Common/MathUtils/test/testCartesian.cxx index ec04c34670fc3..9f2b4c912007e 100644 --- a/Common/MathUtils/test/testCartesian.cxx +++ b/Common/MathUtils/test/testCartesian.cxx @@ -77,7 +77,7 @@ BOOST_AUTO_TEST_CASE(Cartesian_test) BOOST_AUTO_TEST_CASE(Point3D_messageable) { using ElementType = math_utils::Point3D; - static_assert(std::is_trivially_copyable::value == true); + static_assert(o2::framework::is_forced_trivially_copyable::value == true); std::vector pts(10); auto makeElement = [](int idx) { return ElementType{idx, idx + 10, idx + 20}; diff --git a/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h b/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h index 6ea69f82277bf..84e15e4656cd2 100644 --- a/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h +++ b/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h @@ -28,9 +28,7 @@ #include "DetectorsDCS/DataPointValue.h" #include "DetectorsDCS/DeliveryType.h" -namespace o2 -{ -namespace dcs +namespace o2::dcs { /** * DataPointCompositeObject is a composition of a DataPointIdentifier and a @@ -291,26 +289,6 @@ struct DataPointCompositeObject final { template T getValue(const DataPointCompositeObject& dpcom); -} // namespace dcs - -/// Defining DataPointCompositeObject explicitly as messageable -namespace framework -{ -template -struct is_messageable; -template <> -struct is_messageable : std::true_type { -}; -} // namespace framework - -} // namespace o2 - -/// Defining DataPointCompositeObject explicitly as copiable -namespace std -{ -template <> -struct is_trivially_copyable : std::true_type { -}; -} // namespace std +} // namespace o2::dcs #endif /* O2_DCS_DATAPOINT_COMPOSITE_OBJECT_H */ diff --git a/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h b/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h index 8d156e04ebbca..faa12a3306d4f 100644 --- a/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h +++ b/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h @@ -31,9 +31,7 @@ #include "DetectorsDCS/GenericFunctions.h" #include "DetectorsDCS/DeliveryType.h" -namespace o2 -{ -namespace dcs +namespace o2::dcs { /** * DataPointIdentifier object is responsible for storing the alias and type @@ -208,19 +206,7 @@ struct DPIDHash { return dpid.hash_code(); } }; -} // namespace dcs - -/// Defining DataPointIdentifier explicitly as messageable -namespace framework -{ -template -struct is_messageable; -template <> -struct is_messageable : std::true_type { -}; -} // namespace framework - -} // namespace o2 +} // namespace o2::dcs // specailized std::hash namespace std @@ -232,11 +218,6 @@ struct hash { return std::hash{}(dpid.hash_code()); } }; - -template <> -struct is_trivially_copyable : std::true_type { -}; - } // namespace std #endif /* O2_DCS_DATAPOINT_IDENTIFIER_H */ diff --git a/Detectors/DCS/test/testDataPointTypes.cxx b/Detectors/DCS/test/testDataPointTypes.cxx index 491ebae1f5d00..dd7511f63217c 100644 --- a/Detectors/DCS/test/testDataPointTypes.cxx +++ b/Detectors/DCS/test/testDataPointTypes.cxx @@ -17,8 +17,6 @@ #include #include "DetectorsDCS/DataPointCompositeObject.h" #include "Framework/TypeTraits.h" -#include -#include #include #include @@ -26,7 +24,7 @@ typedef boost::mpl::list::value, true); + BOOST_CHECK_EQUAL(std::is_trivially_copyable_v, true); BOOST_CHECK_EQUAL(std::is_polymorphic::value, false); BOOST_CHECK_EQUAL(std::is_pointer::value, false); BOOST_CHECK_EQUAL(o2::framework::is_forced_non_messageable::value, false); diff --git a/Framework/Core/include/Framework/TypeTraits.h b/Framework/Core/include/Framework/TypeTraits.h index faa9055de3280..bb2d338f42af3 100644 --- a/Framework/Core/include/Framework/TypeTraits.h +++ b/Framework/Core/include/Framework/TypeTraits.h @@ -38,13 +38,18 @@ struct is_forced_non_messageable< typename std::enable_if::value>::type> : public std::true_type { }; +template +struct is_forced_trivially_copyable : std::false_type { +}; + // TODO: extend this to exclude structs with pointer data members // see e.g. https://stackoverflow.com/questions/32880990/how-to-check-if-class-has-pointers-in-c14 template -struct is_messageable : std::conditional::value && // - !std::is_polymorphic::value && // - !std::is_pointer::value && // - !is_forced_non_messageable::value, // +struct is_messageable : std::conditional<(std::is_trivially_copyable::value || // + framework::is_forced_trivially_copyable::value) && // + !std::is_polymorphic::value && // + !std::is_pointer::value && // + !is_forced_non_messageable::value, // std::true_type, std::false_type>::type { }; From 81075c53830501c0b999fee685bac5cba02169c7 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 2 Apr 2026 11:40:20 +0200 Subject: [PATCH 035/285] DPL: fix long standing bug with ArrayString serialization This is actually the reason why tables are lost from the metadata. --- Framework/Core/src/VariantJSONHelpers.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Framework/Core/src/VariantJSONHelpers.cxx b/Framework/Core/src/VariantJSONHelpers.cxx index fbb5abb331867..f3d6061020ea2 100644 --- a/Framework/Core/src/VariantJSONHelpers.cxx +++ b/Framework/Core/src/VariantJSONHelpers.cxx @@ -339,7 +339,7 @@ void writeVariant(std::ostream& o, Variant const& v) rapidjson::Writer w(osw); auto writeArray = [&](auto* values, size_t size) { - using T = std::remove_pointer_t; + using T = std::remove_cv_t>; w.StartArray(); for (auto i = 0u; i < size; ++i) { if constexpr (std::is_same_v) { From 5a5010afd0bd3d49e2f4d749f535d0ad74cad874 Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Thu, 2 Apr 2026 14:43:20 +0200 Subject: [PATCH 036/285] DPL: Update serialization/deserialization test for string array Variant --- Framework/Core/test/test_Variants.cxx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Framework/Core/test/test_Variants.cxx b/Framework/Core/test/test_Variants.cxx index a364b228871da..da1f39f241e96 100644 --- a/Framework/Core/test/test_Variants.cxx +++ b/Framework/Core/test/test_Variants.cxx @@ -327,14 +327,16 @@ TEST_CASE("VariantJSONConversionsTest") std::vector vstrings{"myoption_one", "myoption_two"}; Variant vvstr(vstrings); + REQUIRE(vvstr.size() == 2); std::stringstream osal; - VariantJSONHelpers::write(osal, vvstr); + osal << vvstr; std::stringstream isal; isal.str(osal.str()); auto vvstra = VariantJSONHelpers::read(isal); - for (auto i = 0U; i < vvstra.size(); ++i) { + REQUIRE(vvstr.size() == vvstra.size()); + for (auto i = 0U; i < vstrings.size(); ++i) { REQUIRE(vstrings[i] == vvstra.get()[i]); } } From ddec5e319145c71f2eba11bcc082bcfaef6e8d71 Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Thu, 2 Apr 2026 16:18:51 +0200 Subject: [PATCH 037/285] aodMerger: Special option to merge only folders of the same name (#15205) Adding a special `merge-by-name` option which asks the merger only to merge together dataframe folders of the same name. This is needed only in special situations where we want to enfore the output structure of the AOD to be the same as that of a reference (data) AOD. An example is MC-DATA embedding. --- Framework/AODMerger/src/aodMerger.cxx | 67 ++++++++++++++++----------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/Framework/AODMerger/src/aodMerger.cxx b/Framework/AODMerger/src/aodMerger.cxx index 3ea45e84a39e0..11dae2dc5eac4 100644 --- a/Framework/AODMerger/src/aodMerger.cxx +++ b/Framework/AODMerger/src/aodMerger.cxx @@ -38,6 +38,7 @@ int main(int argc, char* argv[]) long maxDirSize = 100000000; bool skipNonExistingFiles = false; bool skipParentFilesList = false; + bool mergeByName = false; int verbosity = 2; int exitCode = 0; // 0: success, >0: failure int compression = 505; @@ -50,6 +51,7 @@ int main(int argc, char* argv[]) {"skip-non-existing-files", no_argument, nullptr, 3}, {"skip-parent-files-list", no_argument, nullptr, 4}, {"compression", required_argument, nullptr, 5}, + {"merge-by-name", no_argument, nullptr, 6}, {"verbosity", required_argument, nullptr, 'v'}, {"help", no_argument, nullptr, 'h'}, {nullptr, 0, nullptr, 0}}; @@ -70,6 +72,8 @@ int main(int argc, char* argv[]) skipParentFilesList = true; } else if (c == 5) { compression = atoi(optarg); + } else if (c == 6) { + mergeByName = true; } else if (c == 'v') { verbosity = atoi(optarg); } else if (c == 'h') { @@ -80,6 +84,7 @@ int main(int argc, char* argv[]) printf(" --skip-non-existing-files Flag to allow skipping of non-existing files in the input list.\n"); printf(" --skip-parent-files-list Flag to allow skipping the merging of the parent files list.\n"); printf(" --compression Compression algorithm / level to use (default: %d)\n", compression); + printf(" --merge-by-name Only merge TTrees from folders with the same name.\n"); printf(" --verbosity Verbosity of output (default: %d).\n", verbosity); return -1; } else { @@ -94,6 +99,9 @@ int main(int argc, char* argv[]) if (skipNonExistingFiles) { printf(" WARNING: Skipping non-existing files.\n"); } + if (mergeByName) { + printf(" Merging only folders with the same name.\n"); + } std::map trees; std::map sizeCompressed; @@ -112,6 +120,28 @@ int main(int argc, char* argv[]) TMap* parentFiles = nullptr; int totalMergedDFs = 0; int mergedDFs = 0; + + // Write all accumulated trees to outputDir, update stats, and clean up state. + auto flushTrees = [&](bool resetState) { + if (!outputDir) { + return; + } + for (auto const& tree : trees) { + outputDir->cd(); + tree.second->Write(); + sizeCompressed[tree.first] += tree.second->GetZipBytes(); + sizeUncompressed[tree.first] += tree.second->GetTotBytes(); + delete tree.second; + } + if (resetState) { + outputDir = nullptr; + trees.clear(); + offsets.clear(); + mergedDFs = 0; + currentDirSize = 0; + } + }; + while (in.good() && exitCode == 0) { in >> line; @@ -182,6 +212,14 @@ int main(int argc, char* argv[]) auto dfName = ((TObjString*)key1)->GetString().Data(); + // If merge-by-name is active, flush accumulated trees when the folder name changes + if (mergeByName && outputDir && std::string(outputDir->GetName()) != std::string(dfName)) { + if (verbosity > 0) { + printf("Folder name changed: closing folder %s.\n", outputDir->GetName()); + } + flushTrees(true); + } + if (verbosity > 0) { printf(" Processing folder %s\n", dfName); } @@ -396,21 +434,7 @@ int main(int argc, char* argv[]) if (verbosity > 0) { printf("Maximum size reached: %ld. Closing folder %s.\n", currentDirSize, dfName); } - for (auto const& tree : trees) { - // printf("Writing %s\n", tree.first.c_str()); - outputDir->cd(); - tree.second->Write(); - - // stats - sizeCompressed[tree.first] += tree.second->GetZipBytes(); - sizeUncompressed[tree.first] += tree.second->GetTotBytes(); - - delete tree.second; - } - outputDir = nullptr; - trees.clear(); - offsets.clear(); - mergedDFs = 0; + flushTrees(true); } } inputFile->Close(); @@ -421,16 +445,7 @@ int main(int argc, char* argv[]) parentFiles->Write("parentFiles", TObject::kSingleKey); } - for (auto const& tree : trees) { - outputDir->cd(); - tree.second->Write(); - - // stats - sizeCompressed[tree.first] += tree.second->GetZipBytes(); - sizeUncompressed[tree.first] += tree.second->GetTotBytes(); - - delete tree.second; - } + flushTrees(false); outputFile->Write(); outputFile->Close(); @@ -462,4 +477,4 @@ int main(int argc, char* argv[]) printf("\n"); return exitCode; -} +} \ No newline at end of file From c391bc139713ec61256b73b4f32dc25e165782f5 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 2 Apr 2026 12:31:39 +0200 Subject: [PATCH 038/285] DPL: speedup old split parts indexing --- Framework/Core/include/Framework/DataModelViews.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Framework/Core/include/Framework/DataModelViews.h b/Framework/Core/include/Framework/DataModelViews.h index 285f5ef15154e..2a8b057525f41 100644 --- a/Framework/Core/include/Framework/DataModelViews.h +++ b/Framework/Core/include/Framework/DataModelViews.h @@ -100,12 +100,22 @@ struct get_pair { } size_t diff = self.pairId - count; if (header->splitPayloadParts > 1 && header->splitPayloadIndex == header->splitPayloadParts) { + // New style: one header followed by splitPayloadParts contiguous payloads. count += header->splitPayloadParts; if (self.pairId < count) { return {mi, mi + 1 + diff}; } mi += header->splitPayloadParts + 1; + } else if (header->splitPayloadParts > 1 && header->splitPayloadIndex != header->splitPayloadParts) { + // Old style multi-part: splitPayloadParts [header, payload] pairs. + // We are at the first pair of the block; jump directly. + if (diff < header->splitPayloadParts) { + return {mi + 2 * diff, mi + 2 * diff + 1}; + } + count += header->splitPayloadParts; + mi += 2 * header->splitPayloadParts; } else { + // Single [header, payload] pair (splitPayloadParts == 0). if (self.pairId == count) { return {mi, mi + 1}; } From 2cec68714384aa7fe0b126a336b30d1da50437df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Thu, 2 Apr 2026 20:53:43 +0200 Subject: [PATCH 039/285] [ALICE3] Remove TrackerACTS from CMakeLists.txt (#15250) --- Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt index 59a7f47955938..81a75e209124a 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt @@ -18,7 +18,6 @@ o2_add_library(TRKReconstruction SOURCES src/TimeFrame.cxx src/Clusterer.cxx $<$:src/ClustererACTS.cxx> - $<$:src/TrackerACTS.cxx> PUBLIC_LINK_LIBRARIES O2::ITStracking O2::GPUCommon @@ -46,8 +45,7 @@ set(dictHeaders include/TRKReconstruction/TimeFrame.h include/TRKReconstruction/Clusterer.h) if(Acts_FOUND) - list(APPEND dictHeaders include/TRKReconstruction/ClustererACTS.h - include/TRKReconstruction/TrackerACTS.h) + list(APPEND dictHeaders include/TRKReconstruction/ClustererACTS.h) endif() o2_target_root_dictionary(TRKReconstruction From 1d8469de4bf5231211be7a280e16f2eb3ca3fbe3 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 1 Apr 2026 21:41:46 +0200 Subject: [PATCH 040/285] DPL: allow generic control of signposts from the driver --- Framework/Core/src/WSDriverClient.cxx | 66 ++++++++----------- .../src/FrameworkGUIDeviceInspector.cxx | 27 +++++--- 2 files changed, 47 insertions(+), 46 deletions(-) diff --git a/Framework/Core/src/WSDriverClient.cxx b/Framework/Core/src/WSDriverClient.cxx index 43a407536cb59..97ea1b3dbf66a 100644 --- a/Framework/Core/src/WSDriverClient.cxx +++ b/Framework/Core/src/WSDriverClient.cxx @@ -188,48 +188,40 @@ void on_connect(uv_connect_t* connection, int status) state.tracingFlags = tracingFlags; }); - client->observe("/log-streams", [ref = context->ref](std::string_view cmd) { - auto& state = ref.get(); - static constexpr int prefixSize = std::string_view{"/log-streams "}.size(); - if (prefixSize > cmd.size()) { - LOG(error) << "Malformed log-streams request"; + client->observe("/signpost:enable", [](std::string_view cmd) { + static constexpr int prefixSize = std::string_view{"/signpost:enable "}.size(); + if (cmd.size() <= prefixSize) { + LOG(error) << "Malformed /signpost:enable request"; return; } - cmd.remove_prefix(prefixSize); - int logStreams = 0; + std::string name(cmd.substr(prefixSize)); + o2_walk_logs([](char const* logName, void* l, void* context) -> bool { + auto* log = static_cast<_o2_log_t*>(l); + auto* target = static_cast(context); + if (*target == logName) { + _o2_log_set_stacktrace(log, log->defaultStacktrace); + return false; + } + return true; + }, &name); + }); - auto error = std::from_chars(cmd.data(), cmd.data() + cmd.size(), logStreams); - if (error.ec != std::errc()) { - LOG(error) << "Malformed log-streams mask"; + client->observe("/signpost:disable", [](std::string_view cmd) { + static constexpr int prefixSize = std::string_view{"/signpost:disable "}.size(); + if (cmd.size() <= prefixSize) { + LOG(error) << "Malformed /signpost:disable request"; return; } - LOGP(info, "Logstreams flags set to {}", logStreams); - state.logStreams = logStreams; - if ((state.logStreams & DeviceState::LogStreams::DEVICE_LOG) != 0) { - O2_LOG_ENABLE(device); - } else { - O2_LOG_DISABLE(device); - } - if ((state.logStreams & DeviceState::LogStreams::COMPLETION_LOG) != 0) { - O2_LOG_ENABLE(completion); - } else { - O2_LOG_DISABLE(completion); - } - if ((state.logStreams & DeviceState::LogStreams::MONITORING_SERVICE_LOG) != 0) { - O2_LOG_ENABLE(monitoring_service); - } else { - O2_LOG_DISABLE(monitoring_service); - } - if ((state.logStreams & DeviceState::LogStreams::DATA_PROCESSOR_CONTEXT_LOG) != 0) { - O2_LOG_ENABLE(data_processor_context); - } else { - O2_LOG_DISABLE(data_processor_context); - } - if ((state.logStreams & DeviceState::LogStreams::STREAM_CONTEXT_LOG) != 0) { - O2_LOG_ENABLE(stream_context); - } else { - O2_LOG_DISABLE(stream_context); - } + std::string name(cmd.substr(prefixSize)); + o2_walk_logs([](char const* logName, void* l, void* context) -> bool { + auto* log = static_cast<_o2_log_t*>(l); + auto* target = static_cast(context); + if (*target == logName) { + _o2_log_set_stacktrace(log, 0); + return false; + } + return true; + }, &name); }); // Client will be filled in the line after. I can probably have a single diff --git a/Framework/GUISupport/src/FrameworkGUIDeviceInspector.cxx b/Framework/GUISupport/src/FrameworkGUIDeviceInspector.cxx index aa546b8a9ab49..b29e024ec641e 100644 --- a/Framework/GUISupport/src/FrameworkGUIDeviceInspector.cxx +++ b/Framework/GUISupport/src/FrameworkGUIDeviceInspector.cxx @@ -400,16 +400,25 @@ void displayDeviceInspector(DeviceSpec const& spec, } } - bool logsChanged = false; if (ImGui::CollapsingHeader("Signposts", ImGuiTreeNodeFlags_DefaultOpen)) { - logsChanged = ImGui::CheckboxFlags("Device", &control.logStreams, DeviceState::LogStreams::DEVICE_LOG); - logsChanged = ImGui::CheckboxFlags("Completion", &control.logStreams, DeviceState::LogStreams::COMPLETION_LOG); - logsChanged = ImGui::CheckboxFlags("Monitoring", &control.logStreams, DeviceState::LogStreams::MONITORING_SERVICE_LOG); - logsChanged = ImGui::CheckboxFlags("DataProcessorContext", &control.logStreams, DeviceState::LogStreams::DATA_PROCESSOR_CONTEXT_LOG); - logsChanged = ImGui::CheckboxFlags("StreamContext", &control.logStreams, DeviceState::LogStreams::STREAM_CONTEXT_LOG); - if (logsChanged && control.controller) { - std::string cmd = fmt::format("/log-streams {}", control.logStreams); - control.controller->write(cmd.c_str(), cmd.size()); + static const struct { + const char* label; + int bit; + const char* fullName; + } kStreams[] = { + {"Device", DeviceState::LogStreams::DEVICE_LOG, "ch.cern.aliceo2.device"}, + {"Completion", DeviceState::LogStreams::COMPLETION_LOG, "ch.cern.aliceo2.completion"}, + {"Monitoring", DeviceState::LogStreams::MONITORING_SERVICE_LOG, "ch.cern.aliceo2.monitoring_service"}, + {"DataProcessorContext", DeviceState::LogStreams::DATA_PROCESSOR_CONTEXT_LOG, "ch.cern.aliceo2.data_processor_context"}, + {"StreamContext", DeviceState::LogStreams::STREAM_CONTEXT_LOG, "ch.cern.aliceo2.stream_context"}, + }; + for (auto const& s : kStreams) { + if (ImGui::CheckboxFlags(s.label, &control.logStreams, s.bit) && control.controller) { + bool enabled = (control.logStreams & s.bit) != 0; + std::string cmd = enabled ? fmt::format("/signpost:enable {}", s.fullName) + : fmt::format("/signpost:disable {}", s.fullName); + control.controller->write(cmd.c_str(), cmd.size()); + } } } From 401d04c82258cb5270de02dc55f496627f0c9597 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 1 Apr 2026 21:41:46 +0200 Subject: [PATCH 041/285] DPL MCP: allow client to control signposts --- .../scripts/dpl-mcp-server/dpl_mcp_server.py | 31 ++++++++ Framework/Core/src/StatusWebSocketHandler.cxx | 71 +++++++++++++++++++ Framework/Core/src/StatusWebSocketHandler.h | 13 ++++ 3 files changed, 115 insertions(+) diff --git a/Framework/Core/scripts/dpl-mcp-server/dpl_mcp_server.py b/Framework/Core/scripts/dpl-mcp-server/dpl_mcp_server.py index bc04acf026188..febb1278f6045 100644 --- a/Framework/Core/scripts/dpl-mcp-server/dpl_mcp_server.py +++ b/Framework/Core/scripts/dpl-mcp-server/dpl_mcp_server.py @@ -237,6 +237,37 @@ async def get_logs(max_lines: int = 100) -> str: return "\n".join(lines) +@mcp.tool() +async def enable_signpost(device: str, streams: list[str]) -> str: + """Enable one or more signpost log streams for a DPL device. + + Signpost streams produce detailed trace output visible in the device logs. + Use get_logs() after subscribing to see the output. + + Known stream names (full form): ch.cern.aliceo2.device, + ch.cern.aliceo2.completion, ch.cern.aliceo2.monitoring_service, + ch.cern.aliceo2.data_processor_context, ch.cern.aliceo2.stream_context. + + Args: + device: Device name as shown by list_devices, or "" for the driver. + streams: List of full signpost log names to enable. + """ + await _send({"cmd": "enable_signpost", "device": device, "streams": streams}) + return f"Enabled {len(streams)} signpost stream(s) for '{device or 'driver'}': {', '.join(streams)}" + + +@mcp.tool() +async def disable_signpost(device: str, streams: list[str]) -> str: + """Disable one or more signpost log streams for a DPL device. + + Args: + device: Device name as shown by list_devices, or "" for the driver. + streams: List of full signpost log names to disable. + """ + await _send({"cmd": "disable_signpost", "device": device, "streams": streams}) + return f"Disabled {len(streams)} signpost stream(s) for '{device or 'driver'}': {', '.join(streams)}" + + @mcp.tool() async def get_updates(max_updates: int = 50) -> str: """Drain and return buffered metric update frames received since the last call. diff --git a/Framework/Core/src/StatusWebSocketHandler.cxx b/Framework/Core/src/StatusWebSocketHandler.cxx index db715eff6592d..cdf08c4f2f349 100644 --- a/Framework/Core/src/StatusWebSocketHandler.cxx +++ b/Framework/Core/src/StatusWebSocketHandler.cxx @@ -12,11 +12,15 @@ #include "StatusWebSocketHandler.h" #include "DPLWebSocket.h" #include "DriverServerContext.h" +#include "Framework/DeviceControl.h" +#include "Framework/DeviceController.h" #include "Framework/DeviceInfo.h" #include "Framework/DeviceMetricsInfo.h" #include "Framework/DeviceSpec.h" +#include "Framework/DeviceState.h" #include "Framework/DeviceStateEnums.h" #include "Framework/LogParsingHelpers.h" +#include "Framework/Signpost.h" #include #include #include @@ -250,6 +254,10 @@ void StatusWebSocketHandler::frame(char const* data, size_t s) handleSubscribeLogs(deviceName); } else if (cmd == "unsubscribe_logs") { handleUnsubscribeLogs(deviceName); + } else if (cmd == "enable_signpost") { + handleEnableSignpost(deviceName, extractArrayField(msg, "streams")); + } else if (cmd == "disable_signpost") { + handleDisableSignpost(deviceName, extractArrayField(msg, "streams")); } } @@ -433,6 +441,69 @@ size_t StatusWebSocketHandler::findDeviceIndex(std::string_view name) const return SIZE_MAX; } +void StatusWebSocketHandler::handleEnableSignpost(std::string_view deviceName, std::string_view streamsArr) +{ + if (streamsArr.empty()) { + return; + } + if (deviceName.empty()) { + // Driver process — toggle in-process via o2_walk_logs. + forEachStringInArray(streamsArr, [](std::string_view streamName) { + std::string target(streamName); + o2_walk_logs([](char const* name, void* l, void* context) -> bool { + auto* log = static_cast<_o2_log_t*>(l); + if (static_cast(context)->compare(name) == 0) { + _o2_log_set_stacktrace(log, log->defaultStacktrace); + return false; + } + return true; + }, &target); + }); + } else { + size_t di = findDeviceIndex(deviceName); + if (di == SIZE_MAX || di >= mContext.controls->size() || !(*mContext.controls)[di].controller) { + return; + } + auto* controller = (*mContext.controls)[di].controller; + forEachStringInArray(streamsArr, [controller](std::string_view name) { + std::string cmd = "/signpost:enable "; + cmd += name; + controller->write(cmd.c_str(), cmd.size()); + }); + } +} + +void StatusWebSocketHandler::handleDisableSignpost(std::string_view deviceName, std::string_view streamsArr) +{ + if (streamsArr.empty()) { + return; + } + if (deviceName.empty()) { + forEachStringInArray(streamsArr, [](std::string_view streamName) { + std::string target(streamName); + o2_walk_logs([](char const* name, void* l, void* context) -> bool { + auto* log = static_cast<_o2_log_t*>(l); + if (static_cast(context)->compare(name) == 0) { + _o2_log_set_stacktrace(log, 0); + return false; + } + return true; + }, &target); + }); + } else { + size_t di = findDeviceIndex(deviceName); + if (di == SIZE_MAX || di >= mContext.controls->size() || !(*mContext.controls)[di].controller) { + return; + } + auto* controller = (*mContext.controls)[di].controller; + forEachStringInArray(streamsArr, [controller](std::string_view name) { + std::string cmd = "/signpost:disable "; + cmd += name; + controller->write(cmd.c_str(), cmd.size()); + }); + } +} + void StatusWebSocketHandler::handleSubscribeLogs(std::string_view deviceName) { size_t di = findDeviceIndex(deviceName); diff --git a/Framework/Core/src/StatusWebSocketHandler.h b/Framework/Core/src/StatusWebSocketHandler.h index 86a460e289440..3b040d68e26f0 100644 --- a/Framework/Core/src/StatusWebSocketHandler.h +++ b/Framework/Core/src/StatusWebSocketHandler.h @@ -41,6 +41,17 @@ struct WSDPLHandler; /// {"cmd":"unsubscribe_logs","device":""} /// → driver stops pushing log lines for the device /// +/// {"cmd":"enable_signpost","device":"","streams":["device","completion",...]} +/// → enable the named signpost log streams for a device (or the driver if device=="") +/// → known streams: "device","completion","monitoring_service","data_processor_context","stream_context" +/// +/// {"cmd":"disable_signpost","device":"","streams":["device","completion",...]} +/// → disable the named signpost log streams for a device +/// +/// {"cmd":"list_signposts"} +/// → driver replies with {"type":"signposts_list","streams":["device","completion",...]} +/// → lists the known stream names +/// /// Protocol (driver → client): /// {"type":"snapshot","devices":[{"name","pid","active","streamingState","deviceState"},...]} /// → sent once on connect; contains no metrics or logs @@ -84,6 +95,8 @@ struct StatusWebSocketHandler : public WebSocketHandler { void handleUnsubscribe(std::string_view deviceName, std::string_view metricsJson); void handleSubscribeLogs(std::string_view deviceName); void handleUnsubscribeLogs(std::string_view deviceName); + void handleEnableSignpost(std::string_view deviceName, std::string_view streamsArr); + void handleDisableSignpost(std::string_view deviceName, std::string_view streamsArr); size_t findDeviceIndex(std::string_view name) const; DriverServerContext& mContext; From 12611f702b492d69897f8c24b85e1b26a0a336c1 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Fri, 3 Apr 2026 12:12:10 +0200 Subject: [PATCH 042/285] DPL MCP: add ability to start a stopped topology --- Framework/Core/src/StatusWebSocketHandler.cxx | 13 +++++++++++++ Framework/Core/src/StatusWebSocketHandler.h | 1 + 2 files changed, 14 insertions(+) diff --git a/Framework/Core/src/StatusWebSocketHandler.cxx b/Framework/Core/src/StatusWebSocketHandler.cxx index cdf08c4f2f349..065a6f4b05b4a 100644 --- a/Framework/Core/src/StatusWebSocketHandler.cxx +++ b/Framework/Core/src/StatusWebSocketHandler.cxx @@ -13,6 +13,8 @@ #include "DPLWebSocket.h" #include "DriverServerContext.h" #include "Framework/DeviceControl.h" +#include +#include #include "Framework/DeviceController.h" #include "Framework/DeviceInfo.h" #include "Framework/DeviceMetricsInfo.h" @@ -254,6 +256,8 @@ void StatusWebSocketHandler::frame(char const* data, size_t s) handleSubscribeLogs(deviceName); } else if (cmd == "unsubscribe_logs") { handleUnsubscribeLogs(deviceName); + } else if (cmd == "start_devices") { + handleStartDevices(); } else if (cmd == "enable_signpost") { handleEnableSignpost(deviceName, extractArrayField(msg, "streams")); } else if (cmd == "disable_signpost") { @@ -441,6 +445,15 @@ size_t StatusWebSocketHandler::findDeviceIndex(std::string_view name) const return SIZE_MAX; } +void StatusWebSocketHandler::handleStartDevices() +{ + for (auto const& info : *mContext.infos) { + if (info.active) { + kill(info.pid, SIGCONT); + } + } +} + void StatusWebSocketHandler::handleEnableSignpost(std::string_view deviceName, std::string_view streamsArr) { if (streamsArr.empty()) { diff --git a/Framework/Core/src/StatusWebSocketHandler.h b/Framework/Core/src/StatusWebSocketHandler.h index 3b040d68e26f0..fb2f0beebbec2 100644 --- a/Framework/Core/src/StatusWebSocketHandler.h +++ b/Framework/Core/src/StatusWebSocketHandler.h @@ -95,6 +95,7 @@ struct StatusWebSocketHandler : public WebSocketHandler { void handleUnsubscribe(std::string_view deviceName, std::string_view metricsJson); void handleSubscribeLogs(std::string_view deviceName); void handleUnsubscribeLogs(std::string_view deviceName); + void handleStartDevices(); void handleEnableSignpost(std::string_view deviceName, std::string_view streamsArr); void handleDisableSignpost(std::string_view deviceName, std::string_view streamsArr); size_t findDeviceIndex(std::string_view name) const; From 37e43e384b375dde431c1a4f85bee992af94b83b Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Fri, 3 Apr 2026 12:12:10 +0200 Subject: [PATCH 043/285] DPL MCP: add tool to resume a stopped topology --- .../Core/scripts/dpl-mcp-server/dpl_mcp_server.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Framework/Core/scripts/dpl-mcp-server/dpl_mcp_server.py b/Framework/Core/scripts/dpl-mcp-server/dpl_mcp_server.py index febb1278f6045..3900a646632a1 100644 --- a/Framework/Core/scripts/dpl-mcp-server/dpl_mcp_server.py +++ b/Framework/Core/scripts/dpl-mcp-server/dpl_mcp_server.py @@ -237,6 +237,16 @@ async def get_logs(max_lines: int = 100) -> str: return "\n".join(lines) +@mcp.tool() +async def start_devices() -> str: + """Resume all stopped DPL devices (send SIGCONT). + + Use this when the workflow was started with -s (all devices paused). + """ + await _send({"cmd": "start_devices"}) + return "Sent SIGCONT to all active devices." + + @mcp.tool() async def enable_signpost(device: str, streams: list[str]) -> str: """Enable one or more signpost log streams for a DPL device. From 74b32651eba638fb3beb594c6be6b9657a0cf41c Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Fri, 3 Apr 2026 18:27:14 +0200 Subject: [PATCH 044/285] DPL: add formatters for ConfigParamSpec --- Framework/Core/COOKBOOK.md | 40 +++ Framework/Core/scripts/lldb_o2_formatters.py | 296 +++++++++++++++++++ 2 files changed, 336 insertions(+) create mode 100644 Framework/Core/scripts/lldb_o2_formatters.py diff --git a/Framework/Core/COOKBOOK.md b/Framework/Core/COOKBOOK.md index c327651ae53ca..a99890bdcfa85 100644 --- a/Framework/Core/COOKBOOK.md +++ b/Framework/Core/COOKBOOK.md @@ -549,3 +549,43 @@ Streams can be explicitly enabled or disabled in code using the `O2_SIGNPOST_ENA If a process is already running and you wish to enable one or more of its signposts logs, you can do so using the `o2-log` utility, passing the address of the log to enable and the PID of the running process. E.g. `o2-log -p -a `. Finally, on macOS, you can also use Instruments to visualise your Signpost, just like any other macOS application. In order to do so you need to enable the "Signpost" instrument, making sure you add `ch.cern.aliceo2.completion` to the list of loggers to watch. + +## Improving lldb experience + +You can make lldb understand some of the O2 types by having the following +in your `~/.lldbinit` (or `$PWD/.lldbinit`): + +```lldb +command script import Framework/Core/scripts/lldb_o2_formatters.py +``` + + +Before: + +```gdb +(o2::framework::ConfigParamSpec &) 0x0000000774871e20: { + name = "timeframes-rate-limit-ipcid" + type = String + defaultValue = { + mStore = (__data = "\xa0\xae\x80t\a") + mType = String + mSize = 1 + } + help = (str = "Suffix for IPC channel for metric-feedback, -1 = disable") + kind = kGeneric +} +``` + +After: + +```gdb +(o2::framework::ConfigParamSpec &) 0x00000007cac75e20: { + name = "timeframes-rate-limit-ipcid" + type = String + defaultValue = { + value = 0x00000007cac0eea0 "-1" + } + help = (str = "Suffix for IPC channel for metric-feedback, -1 = disable") + kind = kGeneric +} +``` diff --git a/Framework/Core/scripts/lldb_o2_formatters.py b/Framework/Core/scripts/lldb_o2_formatters.py new file mode 100644 index 0000000000000..84bc7cda8dac3 --- /dev/null +++ b/Framework/Core/scripts/lldb_o2_formatters.py @@ -0,0 +1,296 @@ +# 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. +# +# lldb data formatters for o2::framework types. +# +# Usage: add to ~/.lldbinit or a project .lldbinit: +# command script import /path/to/O2/Framework/Core/scripts/lldb_o2_formatters.py + +import lldb + +# o2::framework::VariantType enum values (must match Variant.h) +_VARIANT_TYPE = { + 0: 'Int', + 1: 'Int64', + 2: 'Float', + 3: 'Double', + 4: 'String', + 5: 'Bool', + 6: 'ArrayInt', + 7: 'ArrayFloat', + 8: 'ArrayDouble', + 9: 'ArrayBool', + 10: 'ArrayString', + 11: 'Array2DInt', + 12: 'Array2DFloat', + 13: 'Array2DDouble', + 14: 'LabeledArrayInt', + 15: 'LabeledArrayFloat', + 16: 'LabeledArrayDouble', + 17: 'UInt8', + 18: 'UInt16', + 19: 'UInt32', + 20: 'UInt64', + 21: 'Int8', + 22: 'Int16', + 23: 'LabeledArrayString', + 24: 'Empty', + 25: 'Dict', + 26: 'Unknown', +} + +# Map VariantType value → (C type name for FindFirstType, is_pointer_to_value) +# is_pointer_to_value=True means mStore holds a T* pointing to heap data (arrays) +_SIMPLE_TYPES = { + 0: ('int', False), + 1: ('long long', False), + 2: ('float', False), + 3: ('double', False), + 5: ('bool', False), + 17: ('unsigned char', False), + 18: ('unsigned short',False), + 19: ('unsigned int', False), + 20: ('unsigned long long', False), + 21: ('signed char', False), + 22: ('short', False), +} + +_ARRAY_ELEM_TYPES = { + 6: 'int', + 7: 'float', + 8: 'double', + 9: 'bool', +} + +MAX_ARRAY_DISPLAY = 16 + + +import struct as _struct + + +def _read_pointer(process, addr): + err = lldb.SBError() + ptr_size = process.GetAddressByteSize() + data = process.ReadMemory(addr, ptr_size, err) + if err.Fail() or not data: + return None + fmt = '"' + b = raw if isinstance(raw[0], int) else bytes(ord(c) for c in raw) + if (b[0] & 1) == 0: # short form + size = b[0] >> 1 + text = b[1:1 + size].decode('utf-8', errors='replace') + else: # long form + size = _struct.unpack_from('"' + heap = process.ReadMemory(data_ptr, min(size, 512), err) + if err.Fail() or not heap: + return '""' + h = heap if isinstance(heap[0], int) else bytes(ord(c) for c in heap) + text = h[:size].decode('utf-8', errors='replace') + if size > 512: + text += '...' + return f'"{text}"' + + +def variant_summary(valobj, _internal_dict): + # Use GetNonSyntheticValue() so we see the real struct members even when + # the synthetic provider has replaced the children with decoded values. + raw = valobj.GetNonSyntheticValue() + mType_val = raw.GetChildMemberWithName('mType') + if not mType_val.IsValid(): + return '' + + mType = mType_val.GetValueAsUnsigned(26) # default Unknown + mSize = raw.GetChildMemberWithName('mSize').GetValueAsUnsigned(1) + mStore = raw.GetChildMemberWithName('mStore') + store_addr = mStore.GetLoadAddress() + + type_name = _VARIANT_TYPE.get(mType, f'') + target = valobj.GetTarget() + process = valobj.GetProcess() + + # --- simple scalar types --- + if mType in _SIMPLE_TYPES: + ctype, _ = _SIMPLE_TYPES[mType] + t = target.FindFirstType(ctype) + if t.IsValid(): + v = valobj.CreateValueFromAddress('v', store_addr, t) + return f'{type_name}({v.GetValue()})' + return f'{type_name}(?)' + + # --- String (const char* stored in mStore) --- + if mType == 4: + ptr = _read_pointer(process, store_addr) + if ptr and ptr != 0: + s = _read_cstring(process, ptr) + return f'String("{s}")' + return 'String(null)' + + # --- C-style numeric arrays (int*, float*, double*, bool*) --- + if mType in _ARRAY_ELEM_TYPES: + elem_type_name = _ARRAY_ELEM_TYPES[mType] + ptr = _read_pointer(process, store_addr) + if not ptr or ptr == 0: + return f'{type_name}(null)' + elem_t = target.FindFirstType(elem_type_name) + if not elem_t.IsValid(): + return f'{type_name}(? x {mSize})' + count = min(mSize, MAX_ARRAY_DISPLAY) + items = [] + for i in range(count): + v = valobj.CreateValueFromAddress(f'e{i}', ptr + i * elem_t.GetByteSize(), elem_t) + items.append(v.GetValue() or '?') + result = f'{type_name}([{", ".join(items)}]' + if mSize > MAX_ARRAY_DISPLAY: + result += f', ... ({mSize} total)' + result += ')' + return result + + # --- ArrayString: std::vector stored via placement new in mStore --- + if mType == 10: + # libc++ std::vector layout: __begin_, __end_, __end_cap_ (all pointers) + # libc++ std::string is always 24 bytes on 64-bit (SSO layout) + STR_SIZE = 24 + ptr_size = process.GetAddressByteSize() + begin_ptr = _read_pointer(process, store_addr) + end_ptr = _read_pointer(process, store_addr + ptr_size) + if begin_ptr is None or end_ptr is None: + return 'ArrayString(?)' + + count = (end_ptr - begin_ptr) // STR_SIZE if end_ptr >= begin_ptr else 0 + items = [] + for i in range(min(count, MAX_ARRAY_DISPLAY)): + items.append(_read_libcxx_string(process, begin_ptr + i * STR_SIZE)) + result = f'ArrayString([{", ".join(items)}]' + if count > MAX_ARRAY_DISPLAY: + result += f', ... ({count} total)' + result += ')' + return result + + return f'{type_name}(mSize={mSize})' + + +class VariantSyntheticProvider: + """Synthetic children for o2::framework::Variant — exposes decoded value as child.""" + + def __init__(self, valobj, _internal_dict): + self.valobj = valobj + self.children = [] + + def num_children(self): + return len(self.children) + + def get_child_index(self, name): + for i, (n, _) in enumerate(self.children): + if n == name: + return i + return -1 + + def get_child_at_index(self, index): + if 0 <= index < len(self.children): + return self.children[index][1] + return None + + def update(self): + self.children = [] + # Use GetNonSyntheticValue() to read the real struct members. + raw = self.valobj.GetNonSyntheticValue() + mType = raw.GetChildMemberWithName('mType').GetValueAsUnsigned(26) + mSize = raw.GetChildMemberWithName('mSize').GetValueAsUnsigned(1) + mStore = raw.GetChildMemberWithName('mStore') + store_addr = mStore.GetLoadAddress() + target = self.valobj.GetTarget() + process = self.valobj.GetProcess() + + if mType in _SIMPLE_TYPES: + ctype, _ = _SIMPLE_TYPES[mType] + t = target.FindFirstType(ctype) + if t.IsValid(): + v = self.valobj.CreateValueFromAddress('value', store_addr, t) + self.children.append(('value', v)) + + elif mType == 4: # String + ptr = _read_pointer(process, store_addr) + if ptr and ptr != 0: + char_t = target.FindFirstType('char').GetPointerType() + v = self.valobj.CreateValueFromAddress('value', store_addr, char_t) + self.children.append(('value', v)) + + elif mType in _ARRAY_ELEM_TYPES: + elem_type_name = _ARRAY_ELEM_TYPES[mType] + ptr = _read_pointer(process, store_addr) + if ptr and ptr != 0: + elem_t = target.FindFirstType(elem_type_name) + if elem_t.IsValid(): + for i in range(min(mSize, MAX_ARRAY_DISPLAY)): + v = self.valobj.CreateValueFromAddress(f'[{i}]', ptr + i * elem_t.GetByteSize(), elem_t) + self.children.append((f'[{i}]', v)) + + elif mType == 10: # ArrayString + # std::vector via placement new; std::string = 24 bytes (libc++ 64-bit) + STR_SIZE = 24 + ptr_size = process.GetAddressByteSize() + begin_ptr = _read_pointer(process, store_addr) + end_ptr = _read_pointer(process, store_addr + ptr_size) + char_t = target.FindFirstType('char') + if begin_ptr is not None and end_ptr is not None and end_ptr >= begin_ptr and char_t.IsValid(): + count = (end_ptr - begin_ptr) // STR_SIZE + err = lldb.SBError() + for i in range(min(count, MAX_ARRAY_DISPLAY)): + str_addr = begin_ptr + i * STR_SIZE + raw = process.ReadMemory(str_addr, STR_SIZE, err) + if err.Fail() or not raw: + continue + b = raw if isinstance(raw[0], int) else bytes(ord(c) for c in raw) + if (b[0] & 1) == 0: # short form: data inline at offset 1 + data_addr = str_addr + 1 + sz = max(b[0] >> 1, 1) + else: # long form: data pointer at offset 16 + data_addr = _struct.unpack_from(' 0 + + +def __lldb_init_module(debugger, _internal_dict): + debugger.HandleCommand( + 'type summary add -x "^o2::framework::Variant$" ' + '--python-function lldb_o2_formatters.variant_summary' + ) + debugger.HandleCommand( + 'type synthetic add -x "^o2::framework::Variant$" ' + '--python-class lldb_o2_formatters.VariantSyntheticProvider' + ) + print('o2::framework::Variant formatters loaded.') From bccbaff4e0d76463ca798aa7bc612d2f2f214ebb Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 5 Apr 2026 21:31:13 +0200 Subject: [PATCH 045/285] DPL: add helper get_next_pair This will simplify and speed up iterations over multipart messages. --- Framework/Core/CMakeLists.txt | 1 + .../Core/include/Framework/DataModelViews.h | 37 ++++ Framework/Core/test/test_DataModelViews.cxx | 199 ++++++++++++++++++ 3 files changed, 237 insertions(+) create mode 100644 Framework/Core/test/test_DataModelViews.cxx diff --git a/Framework/Core/CMakeLists.txt b/Framework/Core/CMakeLists.txt index 0e67e1c0cc623..45af3ad6c59cc 100644 --- a/Framework/Core/CMakeLists.txt +++ b/Framework/Core/CMakeLists.txt @@ -238,6 +238,7 @@ add_executable(o2-test-framework-core test/test_IndexBuilder.cxx test/test_InputRecord.cxx test/test_InputRecordWalker.cxx + test/test_DataModelViews.cxx test/test_InputSpan.cxx test/test_InputSpec.cxx test/test_LogParsingHelpers.cxx diff --git a/Framework/Core/include/Framework/DataModelViews.h b/Framework/Core/include/Framework/DataModelViews.h index 2a8b057525f41..53d6e6615b96e 100644 --- a/Framework/Core/include/Framework/DataModelViews.h +++ b/Framework/Core/include/Framework/DataModelViews.h @@ -127,6 +127,43 @@ struct get_pair { } }; +// Advance from a DataRefIndices to the next one in O(1), reading only the +// current header. Intended for use in iterators so that ++ is O(1) rather +// than the O(n) while-loop that get_pair requires. +// +// New-style block (splitPayloadIndex == splitPayloadParts > 1): +// layout: [header, payload_0, payload_1, ..., payload_{N-1}] +// advance within block while payloads remain, then jump to the next block. +// +// Old-style block (splitPayloadIndex != splitPayloadParts, splitPayloadParts > 1) +// or single pair (splitPayloadParts == 0): +// layout: [header, payload] – always advance by two messages. +struct get_next_pair { + DataRefIndices current; + template + requires std::ranges::random_access_range && std::ranges::sized_range + friend DataRefIndices operator|(R&& r, get_next_pair self) + { + size_t hIdx = self.current.headerIdx; + auto* header = o2::header::get(r[hIdx]->GetData()); + if (!header) { + throw std::runtime_error("Not a DataHeader"); + } + if (header->splitPayloadParts > 1 && header->splitPayloadIndex == header->splitPayloadParts) { + // New-style block: one header followed by splitPayloadParts contiguous payloads. + if (self.current.payloadIdx < hIdx + header->splitPayloadParts) { + // More sub-payloads remain in this block. + return {hIdx, self.current.payloadIdx + 1}; + } + // Last sub-payload consumed; move to the first pair of the next block. + size_t nextHIdx = hIdx + header->splitPayloadParts + 1; + return {nextHIdx, nextHIdx + 1}; + } + // Old-style [header, payload] pairs or a single pair: advance by two messages. + return {hIdx + 2, hIdx + 3}; + } +}; + struct get_dataref_indices { size_t part; size_t subPart; diff --git a/Framework/Core/test/test_DataModelViews.cxx b/Framework/Core/test/test_DataModelViews.cxx new file mode 100644 index 0000000000000..37d163e9e41c6 --- /dev/null +++ b/Framework/Core/test/test_DataModelViews.cxx @@ -0,0 +1,199 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "Framework/DataModelViews.h" +#include "Framework/DataProcessingHeader.h" +#include "Headers/DataHeader.h" +#include "Headers/Stack.h" +#include +#include +#include + +using namespace o2::framework; +using DataHeader = o2::header::DataHeader; +using Stack = o2::header::Stack; + +namespace +{ +// Build a header message containing a DataHeader with the given split-payload fields. +fair::mq::MessagePtr makeHeader(fair::mq::TransportFactory& transport, + uint32_t splitPayloadParts, uint32_t splitPayloadIndex) +{ + DataHeader dh; + dh.dataDescription = "TEST"; + dh.dataOrigin = "TST"; + dh.subSpecification = 0; + dh.splitPayloadParts = splitPayloadParts; + dh.splitPayloadIndex = splitPayloadIndex; + DataProcessingHeader dph{0, 1}; + Stack stack{dh, dph}; + auto msg = transport.CreateMessage(stack.size()); + memcpy(msg->GetData(), stack.data(), stack.size()); + return msg; +} + +fair::mq::MessagePtr makePayload(fair::mq::TransportFactory& transport) +{ + return transport.CreateMessage(4); +} +} // namespace + +// --------------------------------------------------------------------------- +// Single [header, payload] pair (splitPayloadParts == 0) +// --------------------------------------------------------------------------- +TEST_CASE("SinglePair") +{ + auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); + + std::vector msgs; + msgs.emplace_back(makeHeader(*transport, 0, 0)); + msgs.emplace_back(makePayload(*transport)); + + REQUIRE((msgs | count_parts{}) == 1); + REQUIRE((msgs | count_payloads{}) == 1); + REQUIRE((msgs | get_num_payloads{0}) == 1); + + auto idx = msgs | get_pair{0}; + REQUIRE(idx.headerIdx == 0); + REQUIRE(idx.payloadIdx == 1); + + // Advancing past the only pair goes out of range. + auto next = msgs | get_next_pair{idx}; + REQUIRE(next.headerIdx >= msgs.size()); +} + +// --------------------------------------------------------------------------- +// Old-style multipart: N [header, payload] pairs, each with splitPayloadParts=N +// and splitPayloadIndex running 0..N-1 (0-indexed). +// The new-style sentinel is splitPayloadIndex == splitPayloadParts, which is +// never true for old-style (max index is N-1 < N). +// Layout: [h0,p0, h1,p1, h2,p2] +// count_parts returns N because each [h,p] pair is a separate logical part. +// --------------------------------------------------------------------------- +TEST_CASE("OldStyleMultipart") +{ + auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); + constexpr uint32_t N = 3; + + std::vector msgs; + for (uint32_t i = 0; i < N; ++i) { + msgs.emplace_back(makeHeader(*transport, N, i)); // 0-indexed + msgs.emplace_back(makePayload(*transport)); + } + + REQUIRE((msgs | count_parts{}) == N); // N separate logical parts + REQUIRE((msgs | count_payloads{}) == N); // one payload each + for (uint32_t i = 0; i < N; ++i) { + REQUIRE((msgs | get_num_payloads{i}) == 1); + } + + // get_pair reaches each sub-part directly. + for (uint32_t i = 0; i < N; ++i) { + auto idx = msgs | get_pair{i}; + REQUIRE(idx.headerIdx == 2 * i); + REQUIRE(idx.payloadIdx == 2 * i + 1); + } + + // get_next_pair advances sequentially through all pairs. + DataRefIndices idx{0, 1}; + for (uint32_t i = 1; i < N; ++i) { + idx = msgs | get_next_pair{idx}; + REQUIRE(idx.headerIdx == 2 * i); + REQUIRE(idx.payloadIdx == 2 * i + 1); + } + // One more step goes out of range. + idx = msgs | get_next_pair{idx}; + REQUIRE(idx.headerIdx >= msgs.size()); +} + +// --------------------------------------------------------------------------- +// New-style multipart: one header followed by N contiguous payloads. +// splitPayloadParts == splitPayloadIndex == N (the sentinel for new style). +// Layout: [h, p0, p1, p2] +// --------------------------------------------------------------------------- +TEST_CASE("NewStyleMultiPayload") +{ + auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); + constexpr uint32_t N = 3; + + std::vector msgs; + msgs.emplace_back(makeHeader(*transport, N, N)); + for (uint32_t i = 0; i < N; ++i) { + msgs.emplace_back(makePayload(*transport)); + } + + REQUIRE((msgs | count_parts{}) == 1); + REQUIRE((msgs | count_payloads{}) == N); + REQUIRE((msgs | get_num_payloads{0}) == N); // all payloads belong to part 0 + + // get_pair returns the same header for every sub-part, advancing payloadIdx. + for (uint32_t i = 0; i < N; ++i) { + auto idx = msgs | get_pair{i}; + REQUIRE(idx.headerIdx == 0); + REQUIRE(idx.payloadIdx == 1 + i); + } + + // get_next_pair advances payloadIdx within the block, then moves to next block. + DataRefIndices idx{0, 1}; + for (uint32_t i = 1; i < N; ++i) { + idx = msgs | get_next_pair{idx}; + REQUIRE(idx.headerIdx == 0); + REQUIRE(idx.payloadIdx == 1 + i); + } + // One more step exits the block. + idx = msgs | get_next_pair{idx}; + REQUIRE(idx.headerIdx >= msgs.size()); +} + +// --------------------------------------------------------------------------- +// Mixed message set: two routes, one single-pair and one new-style block. +// Layout: [h0, p0, h1, p1_0, p1_1] +// --------------------------------------------------------------------------- +TEST_CASE("MixedLayout") +{ + auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); + + std::vector msgs; + // Route 0: single pair + msgs.emplace_back(makeHeader(*transport, 0, 0)); + msgs.emplace_back(makePayload(*transport)); + // Route 1: new-style 2-payload block + msgs.emplace_back(makeHeader(*transport, 2, 2)); + msgs.emplace_back(makePayload(*transport)); + msgs.emplace_back(makePayload(*transport)); + + REQUIRE((msgs | count_parts{}) == 2); + REQUIRE((msgs | count_payloads{}) == 3); + + // get_pair across routes + auto idx0 = msgs | get_pair{0}; + REQUIRE(idx0.headerIdx == 0); + REQUIRE(idx0.payloadIdx == 1); + + auto idx1 = msgs | get_pair{1}; + REQUIRE(idx1.headerIdx == 2); + REQUIRE(idx1.payloadIdx == 3); + + auto idx2 = msgs | get_pair{2}; + REQUIRE(idx2.headerIdx == 2); + REQUIRE(idx2.payloadIdx == 4); + + // get_next_pair traversal from the first element + DataRefIndices idx{0, 1}; + idx = msgs | get_next_pair{idx}; + REQUIRE(idx.headerIdx == 2); + REQUIRE(idx.payloadIdx == 3); + idx = msgs | get_next_pair{idx}; + REQUIRE(idx.headerIdx == 2); + REQUIRE(idx.payloadIdx == 4); + idx = msgs | get_next_pair{idx}; + REQUIRE(idx.headerIdx >= msgs.size()); +} From 5cf005b9a2caf9e5c17251a32b0c7b490261e09a Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Tue, 7 Apr 2026 08:47:47 +0200 Subject: [PATCH 046/285] ITS3: add cylinder parameterisation preserving zero gaussian curvature (#15260) * ITS3: exploit symmetric matrices for interpolation Signed-off-by: Felix Schlepper * ITS3: cleanup code Signed-off-by: Felix Schlepper * ITS3: alignment for inextionsial surfaces Signed-off-by: Felix Schlepper * ITS3: harmonize study and alignment wfx for misalignment Signed-off-by: Felix Schlepper --------- Signed-off-by: Felix Schlepper --- .../Upgrades/ITS3/alignment/CMakeLists.txt | 3 + Detectors/Upgrades/ITS3/alignment/README.md | 36 +++- .../include/ITS3Align/AlignmentDOF.h | 176 +++++++++++++++++ .../include/ITS3Align/AlignmentHierarchy.h | 178 +---------------- .../include/ITS3Align/AlignmentLabel.h | 87 +++++++++ .../include/ITS3Align/AlignmentMath.h | 32 +++ .../include/ITS3Align/AlignmentParams.h | 7 +- .../include/ITS3Align/AlignmentSpec.h | 1 + .../include/ITS3Align/MisalignmentUtils.h | 79 ++++++++ .../alignment/include/ITS3Align/TrackFit.h | 8 +- .../ITS3/alignment/src/AlignmentDOF.cxx | 114 +++++++++++ .../ITS3/alignment/src/AlignmentHierarchy.cxx | 127 ++++++++++-- .../ITS3/alignment/src/AlignmentMath.cxx | 54 ++++++ .../ITS3/alignment/src/AlignmentSpec.cxx | 183 ++++++++---------- .../ITS3/alignment/src/MisalignmentUtils.cxx | 151 +++++++++++++++ .../Upgrades/ITS3/study/src/TrackingStudy.cxx | 90 +++------ 16 files changed, 967 insertions(+), 359 deletions(-) create mode 100644 Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentDOF.h create mode 100644 Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentLabel.h create mode 100644 Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentMath.h create mode 100644 Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentUtils.h create mode 100644 Detectors/Upgrades/ITS3/alignment/src/AlignmentDOF.cxx create mode 100644 Detectors/Upgrades/ITS3/alignment/src/AlignmentMath.cxx create mode 100644 Detectors/Upgrades/ITS3/alignment/src/MisalignmentUtils.cxx diff --git a/Detectors/Upgrades/ITS3/alignment/CMakeLists.txt b/Detectors/Upgrades/ITS3/alignment/CMakeLists.txt index 0bc8080c7a1b8..e04dfcbb43963 100644 --- a/Detectors/Upgrades/ITS3/alignment/CMakeLists.txt +++ b/Detectors/Upgrades/ITS3/alignment/CMakeLists.txt @@ -13,6 +13,9 @@ o2_add_library(ITS3Align TARGETVARNAME targetName SOURCES src/AlignmentHierarchy.cxx + src/AlignmentDOF.cxx + src/AlignmentMath.cxx + src/MisalignmentUtils.cxx src/AlignmentSensors.cxx src/AlignmentParams.cxx src/AlignmentTypes.cxx diff --git a/Detectors/Upgrades/ITS3/alignment/README.md b/Detectors/Upgrades/ITS3/alignment/README.md index 62633d1d7d313..80213eb4e03b1 100644 --- a/Detectors/Upgrades/ITS3/alignment/README.md +++ b/Detectors/Upgrades/ITS3/alignment/README.md @@ -27,4 +27,38 @@ dofSet.json: "calib": { "type": "legendre", "order": 1, "fix": [0, 2] } } ] -}``` +} +``` + + +## In-existensional modes +```json +{ + "defaults": { "rigidBody": "fixed" }, + "rules": [ + { + "match": "ITS3Layer1/ITS3CarbonForm0", + "calib": { + "type": "inextensional", + "order": 2, + "free": ["a_2", "b_2", "c_2", "d_2", "alpha", "beta"] + } + } + ] +} +``` + +```json +[ + { + "id": 2, + "inextensional": { + "modes": { + "2": [0.0008, -0.0005, 0.0006, -0.0007] + }, + "alpha": 0.0004, + "beta": -0.0003 + } + } +] +``` diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentDOF.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentDOF.h new file mode 100644 index 0000000000000..3fed9decbd6e7 --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentDOF.h @@ -0,0 +1,176 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_ITS3_ALIGNMENT_DOF_H +#define O2_ITS3_ALIGNMENT_DOF_H + +#include +#include +#include +#include +#include +#include + +#include + +struct DerivativeContext { + int sensorID{-1}; + int layerID{-1}; + double measX{0.}; + double measAlpha{0.}; + double measZ{0.}; + double trkY{0.}; + double trkZ{0.}; + double snp{0.}; + double tgl{0.}; + double dydx{0.}; + double dzdx{0.}; +}; + +// Generic set of DOF +class DOFSet +{ + public: + enum class Type : uint8_t { + RigidBody, + Legendre, + Inextensional + }; + virtual ~DOFSet() = default; + virtual Type type() const = 0; + int nDOFs() const { return static_cast(mFree.size()); } + virtual std::string dofName(int idx) const = 0; + virtual void fillDerivatives(const DerivativeContext& ctx, Eigen::Ref out) const = 0; + bool isFree(int idx) const { return mFree[idx]; } + void setFree(int idx, bool f) { mFree[idx] = f; } + void setAllFree(bool f) { std::fill(mFree.begin(), mFree.end(), f); } + int nFreeDOFs() const + { + int n = 0; + for (bool f : mFree) { + n += f; + } + return n; + } + + protected: + DOFSet(int n) : mFree(n, true) {} + std::vector mFree; +}; + +// Rigid body set +class RigidBodyDOFSet final : public DOFSet +{ + public: + // indices for rigid body parameters in LOC frame + enum RigidBodyDOF : uint8_t { + TX = 0, + TY, + TZ, + RX, + RY, + RZ, + NDOF, + }; + static constexpr const char* RigidBodyDOFNames[RigidBodyDOF::NDOF] = {"TX", "TY", "TZ", "RX", "RY", "RZ"}; + + RigidBodyDOFSet() : DOFSet(NDOF) {} + // mask: bitmask of free DOFs (bit i = DOF i is free) + explicit RigidBodyDOFSet(uint8_t mask) : DOFSet(NDOF) + { + for (int i = 0; i < NDOF; ++i) { + mFree[i] = (mask >> i) & 1; + } + } + Type type() const override { return Type::RigidBody; } + std::string dofName(int idx) const override { return RigidBodyDOFNames[idx]; } + void fillDerivatives(const DerivativeContext& ctx, Eigen::Ref out) const override; + uint8_t mask() const + { + uint8_t m = 0; + for (int i = 0; i < NDOF; ++i) { + m |= (uint8_t(mFree[i]) << i); + } + return m; + } +}; + +// Legendre DOFs +// Describing radial misplacement +class LegendreDOFSet final : public DOFSet +{ + public: + explicit LegendreDOFSet(int order) : DOFSet((order + 1) * (order + 2) / 2), mOrder(order) {} + Type type() const override { return Type::Legendre; } + int order() const { return mOrder; } + std::string dofName(int idx) const override + { + int i = 0; + while ((i + 1) * (i + 2) / 2 <= idx) { + ++i; + } + int j = idx - (i * (i + 1) / 2); + return std::format("L({},{})", i, j); + } + void fillDerivatives(const DerivativeContext& ctx, Eigen::Ref out) const override; + + private: + int mOrder; +}; + +// In-extensional deformation DOFs for cylindrical half-shells +// Fourier modes n=2..N: 4 params each (a_n, b_n, c_n, d_n) +// Plus 2 non-periodic modes (alpha, beta) for the half-cylinder open edges +// Total: 4*(N-1) + 2 +class InextensionalDOFSet final : public DOFSet +{ + public: + explicit InextensionalDOFSet(int maxOrder) : DOFSet((4 * (maxOrder - 1)) + 2), mMaxOrder(maxOrder) + { + if (maxOrder < 2) { + // the rest is eq. to rigid body + throw std::invalid_argument("InextensionalDOFSet requires maxOrder >= 2"); + } + } + Type type() const override { return Type::Inextensional; } + int maxOrder() const { return mMaxOrder; } + + // number of periodic DOFs (before alpha, beta) + int nPeriodic() const { return 4 * (mMaxOrder - 1); } + + // flat index layout: [a_2, b_2, c_2, d_2, a_3, b_3, c_3, d_3, ..., alpha, beta] + // index of first DOF for mode n + static int modeOffset(int n) { return 4 * (n - 2); } + + // indices of the non-periodic modes + int alphaIdx() const { return nPeriodic(); } + int betaIdx() const { return nPeriodic() + 1; } + + std::string dofName(int idx) const override + { + if (idx == alphaIdx()) { + return "alpha"; + } + if (idx == betaIdx()) { + return "beta"; + } + int n = (idx / 4) + 2; + int sub = idx % 4; + static constexpr const char* subNames[] = {"a", "b", "c", "d"}; + return std::format("{}_{}", subNames[sub], n); + } + void fillDerivatives(const DerivativeContext& ctx, Eigen::Ref out) const override; + + private: + int mMaxOrder; +}; + +#endif diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentHierarchy.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentHierarchy.h index 04b8157084d0a..ae8989deec21b 100644 --- a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentHierarchy.h +++ b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentHierarchy.h @@ -13,14 +13,11 @@ #define O2_ITS3_ALIGNMENT_HIERARCHY_H #include -#include -#include -#include #include #include #include #include -#include +#include #include #include @@ -28,179 +25,14 @@ #include #include +#include "ITS3Align/AlignmentLabel.h" +#include "ITS3Align/AlignmentDOF.h" + namespace o2::its3::align { using Matrix36 = Eigen::Matrix; using Matrix66 = Eigen::Matrix; -// indices for rigid body parameters in LOC frame -enum RigidBodyDOF : uint8_t { - TX = 0, - TY, - TZ, - RX, - RY, - RZ, - NDOF, -}; -static constexpr const char* RigidBodyDOFNames[RigidBodyDOF::NDOF] = {"TX", "TY", "TZ", "RX", "RY", "RZ"}; - -// return the rigid body derivatives -// trk has be at in the measurment frame -auto getRigidBodyDerivatives(const auto& trk) -{ - // calculate slopes - const double tgl = trk.getTgl(), snp = trk.getSnp(); - const double csp = 1. / sqrt(1. + (tgl * tgl)); - const double u = trk.getY(), v = trk.getZ(); - const double uP = snp * csp, vP = tgl * csp; - Matrix36 der; - der.setZero(); - // columns: Tt, Tu, Tv, Rt, Ru, Rv - // (X) (Y) (Z) (RX) (RY) (RZ) - der << uP, -1., 0., v, v * uP, -u * uP, - vP, 0., -1., -u, v * vP, -u * vP; - return der; -} - -class DOFSet -{ - public: - enum class Type : uint8_t { RigidBody, - Legendre }; - virtual ~DOFSet() = default; - virtual Type type() const = 0; - int nDOFs() const { return static_cast(mFree.size()); } - virtual std::string dofName(int idx) const = 0; - bool isFree(int idx) const { return mFree[idx]; } - void setFree(int idx, bool f) { mFree[idx] = f; } - void setAllFree(bool f) { std::fill(mFree.begin(), mFree.end(), f); } - int nFreeDOFs() const - { - int n = 0; - for (bool f : mFree) { - n += f; - } - return n; - } - - protected: - DOFSet(int n) : mFree(n, true) {} - std::vector mFree; -}; - -class RigidBodyDOFSet final : public DOFSet -{ - public: - static constexpr int NDOF = RigidBodyDOF::NDOF; - RigidBodyDOFSet() : DOFSet(NDOF) {} - // mask: bitmask of free DOFs (bit i = DOF i is free) - explicit RigidBodyDOFSet(uint8_t mask) : DOFSet(NDOF) - { - for (int i = 0; i < NDOF; ++i) { - mFree[i] = (mask >> i) & 1; - } - } - Type type() const override { return Type::RigidBody; } - std::string dofName(int idx) const override { return RigidBodyDOFNames[idx]; } - uint8_t mask() const - { - uint8_t m = 0; - for (int i = 0; i < NDOF; ++i) { - m |= (uint8_t(mFree[i]) << i); - } - return m; - } -}; - -class LegendreDOFSet final : public DOFSet -{ - public: - explicit LegendreDOFSet(int order) : DOFSet((order + 1) * (order + 2) / 2), mOrder(order) {} - Type type() const override { return Type::Legendre; } - int order() const { return mOrder; } - std::string dofName(int idx) const override - { - int i = 0; - while ((i + 1) * (i + 2) / 2 <= idx) { - ++i; - } - int j = idx - (i * (i + 1) / 2); - return std::format("L({},{})", i, j); - } - - private: - int mOrder; -}; - -class GlobalLabel -{ - // Millepede label is any positive integer [1....) - // Layout: DOF(5) | CALIB(1) | ID(22) | SENS(1) | DET(2) = 31 usable bits (MSB reserved, GBL uses signed int) - public: - using T = uint32_t; - static constexpr int DOF_BITS = 5; // bits 0-4 - static constexpr int CALIB_BITS = 1; // bit 5: 0 = rigid body, 1 = calibration - static constexpr int ID_BITS = 22; // bits 6-27 - static constexpr int SENS_BITS = 1; // bit 28 - static constexpr int TOTAL_BITS = sizeof(T) * 8; - static constexpr int DET_BITS = TOTAL_BITS - (DOF_BITS + CALIB_BITS + ID_BITS + SENS_BITS) - 1; // one less bit since GBL uses int! - static constexpr T bitMask(int b) noexcept - { - return (T(1) << b) - T(1); - } - static constexpr int DOF_SHIFT = 0; - static constexpr T DOF_MAX = (T(1) << DOF_BITS) - T(1); - static constexpr T DOF_MASK = DOF_MAX << DOF_SHIFT; - static constexpr int CALIB_SHIFT = DOF_BITS; - static constexpr T CALIB_MAX = (T(1) << CALIB_BITS) - T(1); - static constexpr T CALIB_MASK = CALIB_MAX << CALIB_SHIFT; - static constexpr int ID_SHIFT = DOF_BITS + CALIB_BITS; - static constexpr T ID_MAX = (T(1) << ID_BITS) - T(1); - static constexpr T ID_MASK = ID_MAX << ID_SHIFT; - static constexpr int SENS_SHIFT = DOF_BITS + CALIB_BITS + ID_BITS; - static constexpr T SENS_MAX = (T(1) << SENS_BITS) - T(1); - static constexpr T SENS_MASK = SENS_MAX << SENS_SHIFT; - static constexpr int DET_SHIFT = DOF_BITS + CALIB_BITS + ID_BITS + SENS_BITS; - static constexpr T DET_MAX = (T(1) << DET_BITS) - T(1); - static constexpr T DET_MASK = DET_MAX << DET_SHIFT; - - GlobalLabel(T det, T id, bool sens, bool calib = false) - : mID((((id + 1) & ID_MAX) << ID_SHIFT) | - ((det & DET_MAX) << DET_SHIFT) | - ((T(sens) & SENS_MAX) << SENS_SHIFT) | - ((T(calib) & CALIB_MAX) << CALIB_SHIFT)) - { - } - - /// produce the raw Millepede label for a given DOF index (rigid body: calib=0 in label) - constexpr T raw(T dof) const noexcept { return (mID & ~DOF_MASK) | ((dof & DOF_MAX) << DOF_SHIFT); } - constexpr int rawGBL(T dof) const noexcept { return static_cast(raw(dof)); } - - /// return a copy of this label with the CALIB bit set (for calibration DOFs on same volume) - GlobalLabel asCalib() const noexcept - { - GlobalLabel c{*this}; - c.mID |= (T(1) << CALIB_SHIFT); - return c; - } - - constexpr T id() const noexcept { return ((mID >> ID_SHIFT) & ID_MAX) - 1; } - constexpr T det() const noexcept { return (mID & DET_MASK) >> DET_SHIFT; } - constexpr bool sens() const noexcept { return (mID & SENS_MASK) >> SENS_SHIFT; } - constexpr bool calib() const noexcept { return (mID & CALIB_MASK) >> CALIB_SHIFT; } - - std::string asString() const - { - return std::format("Det:{} Id:{} Sens:{} Calib:{}", det(), id(), sens(), calib()); - } - - constexpr auto operator<=>(const GlobalLabel&) const noexcept = default; - - private: - T mID{0}; -}; - class HierarchyConstraint { public: @@ -220,8 +52,6 @@ class HierarchyConstraint std::vector mCoeff; // their coefficients }; -// --- AlignableVolume --- - class AlignableVolume { public: diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentLabel.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentLabel.h new file mode 100644 index 0000000000000..83495491b87e0 --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentLabel.h @@ -0,0 +1,87 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_ITS3_ALIGNMENT_LABEL_H +#define O2_ITS3_ALIGNMENT_LABEL_H + +#include +#include +#include + +class GlobalLabel +{ + // Millepede label is any positive integer [1....) + // Layout: DOF(5) | CALIB(1) | ID(22) | SENS(1) | DET(2) = 31 usable bits (MSB reserved, GBL uses signed int) + public: + using T = uint32_t; + static constexpr int DOF_BITS = 5; // bits 0-4 + static constexpr int CALIB_BITS = 1; // bit 5: 0 = rigid body, 1 = calibration (only allow for one calibration, could be extended if needed) + static constexpr int ID_BITS = 22; // bits 6-27 + static constexpr int SENS_BITS = 1; // bit 28 + static constexpr int TOTAL_BITS = sizeof(T) * 8; + static constexpr int DET_BITS = TOTAL_BITS - (DOF_BITS + CALIB_BITS + ID_BITS + SENS_BITS) - 1; // one less bit since GBL uses int! + static constexpr T bitMask(int b) noexcept + { + return (T(1) << b) - T(1); + } + static constexpr int DOF_SHIFT = 0; + static constexpr T DOF_MAX = (T(1) << DOF_BITS) - T(1); + static constexpr T DOF_MASK = DOF_MAX << DOF_SHIFT; + static constexpr int CALIB_SHIFT = DOF_BITS; + static constexpr T CALIB_MAX = (T(1) << CALIB_BITS) - T(1); + static constexpr T CALIB_MASK = CALIB_MAX << CALIB_SHIFT; + static constexpr int ID_SHIFT = DOF_BITS + CALIB_BITS; + static constexpr T ID_MAX = (T(1) << ID_BITS) - T(1); + static constexpr T ID_MASK = ID_MAX << ID_SHIFT; + static constexpr int SENS_SHIFT = DOF_BITS + CALIB_BITS + ID_BITS; + static constexpr T SENS_MAX = (T(1) << SENS_BITS) - T(1); + static constexpr T SENS_MASK = SENS_MAX << SENS_SHIFT; + static constexpr int DET_SHIFT = DOF_BITS + CALIB_BITS + ID_BITS + SENS_BITS; + static constexpr T DET_MAX = (T(1) << DET_BITS) - T(1); + static constexpr T DET_MASK = DET_MAX << DET_SHIFT; + + GlobalLabel(T det, T id, bool sens, bool calib = false) + : mID((((id + 1) & ID_MAX) << ID_SHIFT) | + ((det & DET_MAX) << DET_SHIFT) | + ((T(sens) & SENS_MAX) << SENS_SHIFT) | + ((T(calib) & CALIB_MAX) << CALIB_SHIFT)) + { + } + + /// produce the raw Millepede label for a given DOF index (rigid body: calib=0 in label) + constexpr T raw(T dof) const noexcept { return (mID & ~DOF_MASK) | ((dof & DOF_MAX) << DOF_SHIFT); } + constexpr int rawGBL(T dof) const noexcept { return static_cast(raw(dof)); } + + /// return a copy of this label with the CALIB bit set (for calibration DOFs on same volume) + GlobalLabel asCalib() const noexcept + { + GlobalLabel c{*this}; + c.mID |= (T(1) << CALIB_SHIFT); + return c; + } + + constexpr T id() const noexcept { return ((mID >> ID_SHIFT) & ID_MAX) - 1; } + constexpr T det() const noexcept { return (mID & DET_MASK) >> DET_SHIFT; } + constexpr bool sens() const noexcept { return (mID & SENS_MASK) >> SENS_SHIFT; } + constexpr bool calib() const noexcept { return (mID & CALIB_MASK) >> CALIB_SHIFT; } + + std::string asString() const + { + return std::format("Det:{} Id:{} Sens:{} Calib:{}", det(), id(), sens(), calib()); + } + + constexpr auto operator<=>(const GlobalLabel&) const noexcept = default; + + private: + T mID{0}; +}; + +#endif diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentMath.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentMath.h new file mode 100644 index 0000000000000..9409648dca3e4 --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentMath.h @@ -0,0 +1,32 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_ITS3_ALIGNMENT_MATH_H +#define O2_ITS3_ALIGNMENT_MATH_H + +#include +#include + +namespace o2::its3::align +{ + +struct TrackSlopes { + double dydx{0.}; + double dzdx{0.}; +}; + +std::pair computeUV(double gloX, double gloY, double gloZ, int sensorID, double radius); +TrackSlopes computeTrackSlopes(double snp, double tgl); +std::vector legendrePols(int order, double x); + +} // namespace o2::its3::align + +#endif diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentParams.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentParams.h index a7785a2c04e11..5a11066fd3c3b 100644 --- a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentParams.h +++ b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentParams.h @@ -38,9 +38,10 @@ struct AlignmentParams : public o2::conf::ConfigurableParamHelper; diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentUtils.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentUtils.h new file mode 100644 index 0000000000000..457eccaeff4e6 --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentUtils.h @@ -0,0 +1,79 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_ITS3_ALIGNMENT_MISALIGNMENTUTILS_H +#define O2_ITS3_ALIGNMENT_MISALIGNMENTUTILS_H + +#include +#include +#include +#include +#include + +#include "ITS3Align/AlignmentMath.h" +#include "MathUtils/LegendrePols.h" + +namespace o2::its3::align +{ + +struct InextensionalMisalignment { + std::map> modes; // n -> (a_n, b_n, c_n, d_n) + double alpha{0.}; + double beta{0.}; +}; + +struct SensorMisalignment { + o2::math_utils::Legendre2DPolynominal legendre; + bool hasLegendre{false}; + InextensionalMisalignment inextensional; + bool hasInextensional{false}; + + bool empty() const noexcept { return !hasLegendre && !hasInextensional; } +}; + +struct MisalignmentModel { + static constexpr std::size_t NSensors = 6; + std::array sensors{}; + + bool empty() const noexcept; + const SensorMisalignment& operator[](std::size_t idx) const { return sensors[idx]; } + SensorMisalignment& operator[](std::size_t idx) { return sensors[idx]; } +}; + +struct MisalignmentFrame { + int sensorID{-1}; + int layerID{-1}; + double x{0.}; // tracking-frame X / nominal radius at the measurement + double alpha{0.}; // tracking-frame alpha + double z{0.}; // tracking-frame measurement z +}; + +struct MisalignmentShift { + double dy{0.}; + double dz{0.}; + bool accepted{true}; + + MisalignmentShift& operator+=(const MisalignmentShift& other) + { + dy += other.dy; + dz += other.dz; + accepted = accepted && other.accepted; + return *this; + } +}; + +MisalignmentModel loadMisalignmentModel(const std::string& jsonPath); +MisalignmentShift evaluateLegendreShift(const SensorMisalignment& sensor, const MisalignmentFrame& frame, const TrackSlopes& slopes); +MisalignmentShift evaluateInextensionalShift(const SensorMisalignment& sensor, const MisalignmentFrame& frame, const TrackSlopes& slopes); + +} // namespace o2::its3::align + +#endif diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/TrackFit.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/TrackFit.h index 3f36705271c9b..4625776398c89 100644 --- a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/TrackFit.h +++ b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/TrackFit.h @@ -76,10 +76,12 @@ o2::track::TrackParametrizationWithError interpolateTrackParCov( }; Mat55 cA = unpack(tA.getCov()); Mat55 cB = unpack(tB.getCov()); - Mat55 wA = cA.inverse(); - Mat55 wB = cB.inverse(); + Eigen::LLT lltA(cA), lltB(cB); + Mat55 wA = lltA.solve(Mat55::Identity()); + Mat55 wB = lltB.solve(Mat55::Identity()); Mat55 wTot = wA + wB; - Mat55 cTot = wTot.inverse(); + Eigen::LLT lltTot(wTot); + Mat55 cTot = lltTot.solve(Mat55::Identity()); Mat51 pA, pB; for (int i = 0; i < 5; ++i) { pA(i) = tA.getParam(i); diff --git a/Detectors/Upgrades/ITS3/alignment/src/AlignmentDOF.cxx b/Detectors/Upgrades/ITS3/alignment/src/AlignmentDOF.cxx new file mode 100644 index 0000000000000..d2a78dba791e6 --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/src/AlignmentDOF.cxx @@ -0,0 +1,114 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "ITS3Align/AlignmentDOF.h" + +#include +#include + +#include "ITS3Align/AlignmentMath.h" +#include "ITS3Base/SpecsV2.h" + +namespace +{ + +void validateDerivativeOutput(const DOFSet& dofSet, Eigen::Ref out) +{ + if (out.rows() != 3 || out.cols() != dofSet.nDOFs()) { + throw std::invalid_argument(std::format("Derivative buffer shape {}x{} does not match expected 3x{}", + out.rows(), out.cols(), dofSet.nDOFs())); + } + out.setZero(); +} + +} // namespace + +void RigidBodyDOFSet::fillDerivatives(const DerivativeContext& ctx, Eigen::Ref out) const +{ + validateDerivativeOutput(*this, out); + + const double csp = 1. / std::sqrt(1. + (ctx.tgl * ctx.tgl)); + const double uP = ctx.snp * csp; + const double vP = ctx.tgl * csp; + + out(0, TX) = uP; + out(0, TY) = -1.; + out(0, RX) = ctx.trkZ; + out(0, RY) = ctx.trkZ * uP; + out(0, RZ) = -ctx.trkY * uP; + + out(1, TX) = vP; + out(1, TZ) = -1.; + out(1, RX) = -ctx.trkY; + out(1, RY) = ctx.trkZ * vP; + out(1, RZ) = -ctx.trkY * vP; +} + +void LegendreDOFSet::fillDerivatives(const DerivativeContext& ctx, Eigen::Ref out) const +{ + validateDerivativeOutput(*this, out); + if (ctx.sensorID < 0 || ctx.layerID < 0) { + throw std::invalid_argument("LegendreDOFSet requires an ITS3 measurement context"); + } + + const double gloX = ctx.measX * std::cos(ctx.measAlpha); + const double gloY = ctx.measX * std::sin(ctx.measAlpha); + const auto [u, v] = o2::its3::align::computeUV(gloX, gloY, ctx.measZ, ctx.sensorID, o2::its3::constants::radii[ctx.layerID]); + const auto pu = o2::its3::align::legendrePols(mOrder, u); + const auto pv = o2::its3::align::legendrePols(mOrder, v); + + int idx = 0; + for (int i = 0; i <= mOrder; ++i) { + for (int j = 0; j <= i; ++j) { + const double basis = pu[j] * pv[i - j]; + out(0, idx) = ctx.dydx * basis; + out(1, idx) = ctx.dzdx * basis; + ++idx; + } + } +} + +void InextensionalDOFSet::fillDerivatives(const DerivativeContext& ctx, Eigen::Ref out) const +{ + validateDerivativeOutput(*this, out); + if (ctx.layerID < 0) { + throw std::invalid_argument("InextensionalDOFSet requires an ITS3 measurement context"); + } + + const double r = o2::its3::constants::radii[ctx.layerID]; + const double phi = std::atan2(r * std::sin(ctx.measAlpha), r * std::cos(ctx.measAlpha)); + const double z = ctx.measZ; + + for (int n = 2; n <= mMaxOrder; ++n) { + const double sn = std::sin(n * phi); + const double cn = std::cos(n * phi); + const double n2 = static_cast(n * n); + const int off = modeOffset(n); + + out(0, off + 0) = -(z / r) * (n * sn + ctx.dydx * n2 * cn); + out(1, off + 0) = -cn - ctx.dzdx * (z / r) * n2 * cn; + + out(0, off + 1) = (z / r) * (n * cn - ctx.dydx * n2 * sn); + out(1, off + 1) = -sn * (1. + ctx.dzdx * (z / r) * n2); + + out(0, off + 2) = -cn + ctx.dydx * n * sn; + out(1, off + 2) = ctx.dzdx * n * sn; + + out(0, off + 3) = -sn - ctx.dydx * n * cn; + out(1, off + 3) = -ctx.dzdx * n * cn; + } + + out(0, alphaIdx()) = z / r; + out(1, alphaIdx()) = -phi; + + out(0, betaIdx()) = -phi - ctx.dydx; + out(1, betaIdx()) = -ctx.dzdx; +} diff --git a/Detectors/Upgrades/ITS3/alignment/src/AlignmentHierarchy.cxx b/Detectors/Upgrades/ITS3/alignment/src/AlignmentHierarchy.cxx index 9170165a36a41..938c14c2c4759 100644 --- a/Detectors/Upgrades/ITS3/alignment/src/AlignmentHierarchy.cxx +++ b/Detectors/Upgrades/ITS3/alignment/src/AlignmentHierarchy.cxx @@ -178,21 +178,23 @@ void AlignableVolume::writeParameters(std::ostream& os) const if (isRoot()) { os << "Parameter\n"; } - if (mRigidBody) { - for (int iDOF = 0; iDOF < mRigidBody->nDOFs(); ++iDOF) { - os << std::format("{:<10} {:>+15g} {:>+15g} ! {} {} ", - mLabel.raw(iDOF), 0.0, (mRigidBody->isFree(iDOF) ? 0.0 : -1.0), - (mRigidBody->isFree(iDOF) ? 'V' : 'F'), mRigidBody->dofName(iDOF)) - << mSymName << '\n'; + if (!mIsPseudo) { + if (mRigidBody) { + for (int iDOF = 0; iDOF < mRigidBody->nDOFs(); ++iDOF) { + os << std::format("{:<10} {:>+15g} {:>+15g} ! {} {} ", + mLabel.raw(iDOF), 0.0, (mRigidBody->isFree(iDOF) ? 0.0 : -1.0), + (mRigidBody->isFree(iDOF) ? 'V' : 'F'), mRigidBody->dofName(iDOF)) + << mSymName << '\n'; + } } - } - if (mCalib) { - auto calibLbl = mLabel.asCalib(); - for (int iDOF = 0; iDOF < mCalib->nDOFs(); ++iDOF) { - os << std::format("{:<10} {:>+15g} {:>+15g} ! {} {} ", - calibLbl.raw(iDOF), 0.0, (mCalib->isFree(iDOF) ? 0.0 : -1.0), - (mCalib->isFree(iDOF) ? 'V' : 'F'), mCalib->dofName(iDOF)) - << mSymName << '\n'; + if (mCalib) { + auto calibLbl = mLabel.asCalib(); + for (int iDOF = 0; iDOF < mCalib->nDOFs(); ++iDOF) { + os << std::format("{:<10} {:>+15g} {:>+15g} ! {} {:<5} ", + calibLbl.raw(iDOF), 0.0, (mCalib->isFree(iDOF) ? 0.0 : -1.0), + (mCalib->isFree(iDOF) ? 'V' : 'F'), mCalib->dofName(iDOF)) + << mSymName << '\n'; + } } } for (const auto& c : mChildren) { @@ -266,6 +268,9 @@ void applyDOFConfig(AlignableVolume* root, const std::string& jsonPath) } root->traverse([&](AlignableVolume* vol) { + if (vol->isPseudo()) { + return; + } const std::string& sym = vol->getSymName(); for (const auto& rule : rules) { const auto pattern = rule["match"].get(); @@ -357,6 +362,41 @@ void applyDOFConfig(AlignableVolume* root, const std::string& jsonPath) } } vol->setCalib(std::move(dofSet)); + } else if (calType == "inextensional") { + int maxOrder = cal.value("order", 2); + auto dofSet = std::make_unique(maxOrder); + bool fixed = cal.value("fixed", false); + if (fixed) { + dofSet->setAllFree(false); + } + if (cal.contains("free")) { + dofSet->setAllFree(false); + for (const auto& item : cal["free"]) { + if (item.is_number_integer()) { + dofSet->setFree(item.get(), true); + } else if (item.is_string()) { + for (int k = 0; k < dofSet->nDOFs(); ++k) { + if (dofSet->dofName(k) == item.get()) { + dofSet->setFree(k, true); + } + } + } + } + } + if (cal.contains("fix")) { + for (const auto& item : cal["fix"]) { + if (item.is_number_integer()) { + dofSet->setFree(item.get(), false); + } else if (item.is_string()) { + for (int k = 0; k < dofSet->nDOFs(); ++k) { + if (dofSet->dofName(k) == item.get()) { + dofSet->setFree(k, false); + } + } + } + } + } + vol->setCalib(std::move(dofSet)); } } } @@ -398,6 +438,12 @@ void writeMillepedeResults(AlignableVolume* root, const std::string& milleResPat // indexed by sensorID std::map> injRB; std::map>> injMatrix; + struct InjInex { + std::map> modes; + double alpha{0.}; + double beta{0.}; + }; + std::map injInex; if (!injectedJsonPath.empty()) { std::ifstream injFile(injectedJsonPath); if (injFile.is_open()) { @@ -410,6 +456,22 @@ void writeMillepedeResults(AlignableVolume* root, const std::string& milleResPat if (item.contains("matrix")) { injMatrix[id] = item["matrix"].get>>(); } + if (item.contains("inextensional")) { + InjInex ii; + const auto& inex = item["inextensional"]; + if (inex.contains("modes")) { + for (auto& [key, val] : inex["modes"].items()) { + ii.modes[std::stoi(key)] = val.get>(); + } + } + if (inex.contains("alpha")) { + ii.alpha = inex["alpha"].get(); + } + if (inex.contains("beta")) { + ii.beta = inex["beta"].get(); + } + injInex[id] = ii; + } } LOGP(info, "Loaded injected misalignment for {} sensors", injData.size()); } else { @@ -468,6 +530,43 @@ void writeMillepedeResults(AlignableVolume* root, const std::string& milleResPat matrix.push_back(row); } entry["matrix"] = matrix; + } else if (cal && cal->nFreeDOFs() && cal->type() == DOFSet::Type::Inextensional) { + write = true; + auto* inexSet = static_cast(cal); + int maxN = inexSet->maxOrder(); + auto calibLbl = vol->getLabel().asCalib(); + const auto& inj = injInex.contains(id) ? injInex[id] : InjInex{}; + + json inexEntry; + json modesObj = json::object(); + for (int n = 2; n <= maxN; ++n) { + int off = InextensionalDOFSet::modeOffset(n); + std::array injCoeffs = {0., 0., 0., 0.}; + if (inj.modes.contains(n)) { + injCoeffs = inj.modes.at(n); + } + json modeArr = json::array(); + for (int k = 0; k < 4; ++k) { + uint32_t raw = calibLbl.raw(off + k); + auto it = labelToValue.find(raw); + double fitted = it != labelToValue.end() ? it->second : 0.0; + modeArr.push_back(fitted - injCoeffs[k]); + } + modesObj[std::to_string(n)] = modeArr; + } + inexEntry["modes"] = modesObj; + + // alpha + uint32_t rawAlpha = calibLbl.raw(inexSet->alphaIdx()); + auto itA = labelToValue.find(rawAlpha); + inexEntry["alpha"] = (itA != labelToValue.end() ? itA->second : 0.0) - inj.alpha; + + // beta + uint32_t rawBeta = calibLbl.raw(inexSet->betaIdx()); + auto itB = labelToValue.find(rawBeta); + inexEntry["beta"] = (itB != labelToValue.end() ? itB->second : 0.0) - inj.beta; + + entry["inextensional"] = inexEntry; } if (write) { output.push_back(entry); diff --git a/Detectors/Upgrades/ITS3/alignment/src/AlignmentMath.cxx b/Detectors/Upgrades/ITS3/alignment/src/AlignmentMath.cxx new file mode 100644 index 0000000000000..52e9c03540d4c --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/src/AlignmentMath.cxx @@ -0,0 +1,54 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "ITS3Align/AlignmentMath.h" + +#include + +#include + +#include "ITS3Base/SpecsV2.h" +#include "MathUtils/Utils.h" + +namespace o2::its3::align +{ + +std::pair computeUV(double gloX, double gloY, double gloZ, int sensorID, double radius) +{ + const bool isTop = sensorID % 2 == 0; + const double phi = o2::math_utils::to02Pid(std::atan2(gloY, gloX)); + const double phiBorder1 = o2::math_utils::to02Pid(((isTop ? 0. : 1.) * TMath::Pi()) + std::asin(constants::equatorialGap / 2. / radius)); + const double phiBorder2 = o2::math_utils::to02Pid(((isTop ? 1. : 2.) * TMath::Pi()) - std::asin(constants::equatorialGap / 2. / radius)); + const double u = (((phi - phiBorder1) * 2.) / (phiBorder2 - phiBorder1)) - 1.; + const double v = ((2. * gloZ + constants::segment::lengthSensitive) / constants::segment::lengthSensitive) - 1.; + return {u, v}; +} + +TrackSlopes computeTrackSlopes(double snp, double tgl) +{ + const double csci = 1. / std::sqrt(1. - (snp * snp)); + return {.dydx = snp * csci, .dzdx = tgl * csci}; +} + +std::vector legendrePols(int order, double x) +{ + std::vector p(order + 1); + p[0] = 1.; + if (order > 0) { + p[1] = x; + } + for (int n = 1; n < order; ++n) { + p[n + 1] = ((2 * n + 1) * x * p[n] - n * p[n - 1]) / (n + 1); + } + return p; +} + +} // namespace o2::its3::align diff --git a/Detectors/Upgrades/ITS3/alignment/src/AlignmentSpec.cxx b/Detectors/Upgrades/ITS3/alignment/src/AlignmentSpec.cxx index d381abc6aa567..72f968bdbf338 100644 --- a/Detectors/Upgrades/ITS3/alignment/src/AlignmentSpec.cxx +++ b/Detectors/Upgrades/ITS3/alignment/src/AlignmentSpec.cxx @@ -10,8 +10,9 @@ // or submit itself to any jurisdiction. #include -#include #include +#include +#include #ifdef WITH_OPENMP #include @@ -43,12 +44,13 @@ #include "ITStracking/IOUtils.h" #include "ITS3Reconstruction/IOUtils.h" #include "ITS3Align/TrackFit.h" +#include "ITS3Align/AlignmentMath.h" #include "ITS3Align/AlignmentSpec.h" #include "ITS3Align/AlignmentParams.h" #include "ITS3Align/AlignmentTypes.h" #include "ITS3Align/AlignmentHierarchy.h" +#include "ITS3Align/MisalignmentUtils.h" #include "ITS3Align/AlignmentSensors.h" -#include "MathUtils/LegendrePols.h" namespace o2::its3::align { @@ -63,30 +65,29 @@ using TrackD = o2::track::TrackParCovD; namespace { -// compute normalized (u,v) in [-1,1] from global position on a sensor -std::pair computeUV(double gloX, double gloY, double gloZ, int sensorID, double radius) +DerivativeContext makeDerivativeContext(const FrameInfoExt& frame, const TrackD& trk) { - const bool isTop = sensorID % 2 == 0; - const double phi = o2::math_utils::to02Pid(std::atan2(gloY, gloX)); - const double phiBorder1 = o2::math_utils::to02Pid(((isTop ? 0. : 1.) * TMath::Pi()) + std::asin(constants::equatorialGap / 2. / radius)); - const double phiBorder2 = o2::math_utils::to02Pid(((isTop ? 1. : 2.) * TMath::Pi()) - std::asin(constants::equatorialGap / 2. / radius)); - const double u = (((phi - phiBorder1) * 2.) / (phiBorder2 - phiBorder1)) - 1.; - const double v = ((2. * gloZ + constants::segment::lengthSensitive) / constants::segment::lengthSensitive) - 1.; - return {u, v}; + const auto slopes = computeTrackSlopes(trk.getSnp(), trk.getTgl()); + const bool isITS3 = constants::detID::isDetITS3(frame.sens); + return {.sensorID = isITS3 ? constants::detID::getSensorID(frame.sens) : -1, + .layerID = isITS3 ? constants::detID::getDetID2Layer(frame.sens) : -1, + .measX = frame.x, + .measAlpha = frame.alpha, + .measZ = frame.positionTrackingFrame[1], + .trkY = trk.getY(), + .trkZ = trk.getZ(), + .snp = trk.getSnp(), + .tgl = trk.getTgl(), + .dydx = slopes.dydx, + .dzdx = slopes.dzdx}; } -// evaluate Legendre polynomials P_0(x) through P_order(x) via recurrence -std::vector legendrePols(int order, double x) +Matrix36 getRigidBodyBaseDerivatives(const DerivativeContext& ctx) { - std::vector p(order + 1); - p[0] = 1.; - if (order > 0) { - p[1] = x; - } - for (int n = 1; n < order; ++n) { - p[n + 1] = ((2 * n + 1) * x * p[n] - n * p[n - 1]) / (n + 1); - } - return p; + static const RigidBodyDOFSet sRigidBodyBasis; + Eigen::MatrixXd dyn(3, sRigidBodyBasis.nDOFs()); + sRigidBodyBasis.fillDerivatives(ctx, dyn); + return dyn; } } // namespace @@ -152,8 +153,8 @@ class AlignmentSpec final : public Task GTrackID::mask_t mTracksSrc; int mNThreads{1}; const AlignmentParams* mParams{nullptr}; - std::array mDeformations; // one per sensorID (0-5) - std::array, 6> mRigidBodyParams; // (dx,dy,dz,rx,ry,rz) in LOC per sensorID + MisalignmentModel mMisalignment; + std::array, 6> mRigidBodyParams; // (dx,dy,dz,rx,ry,rz) in LOC per sensorID }; void AlignmentSpec::init(InitContext& ic) @@ -323,7 +324,8 @@ void AlignmentSpec::process() // this is the derivative in TRK but we want to align in LOC // so dr/da_(LOC) = dr/da_(TRK) * da_(TRK)/da_(LOC) const auto* tileVol = mChip2Hiearchy.at(lbl); - Matrix36 der = getRigidBodyDerivatives(wTrk); + const auto derCtx = makeDerivativeContext(frame, wTrk); + Matrix36 der = getRigidBodyBaseDerivatives(derCtx); // count rigid body columns: only volumes with real DOFs (not DOFPseudo) int nColRB{0}; @@ -375,39 +377,16 @@ void AlignmentSpec::process() } } - // 3) calibration derivatives (e.g. Legendre for ITS3 sensors, apply directly on the whole sensor, not on inidividual tiles) - if (calibSet && calibSet->type() == DOFSet::Type::Legendre) { - const auto* legSet = static_cast(calibSet); - const int N = legSet->order(); - const int sensorID = constants::detID::getSensorID(frame.sens); - const int layerID = constants::detID::getDetID2Layer(frame.sens); - - const double r = frame.x; - const double gX = r * std::cos(frame.alpha); - const double gY = r * std::sin(frame.alpha); - const double gZ = frame.positionTrackingFrame[1]; - auto [u, v] = computeUV(gX, gY, gZ, sensorID, constants::radii[layerID]); - - const double snp = wTrk.getSnp(); - const double tgl = wTrk.getTgl(); - const double csci = 1. / std::sqrt(1. - (snp * snp)); - const double dydx = snp * csci; - const double dzdx = tgl * csci; - - auto pu = legendrePols(N, u); - auto pv = legendrePols(N, v); - - int legIdx = 0; - const int legColStart = nColRB; - for (int i = 0; i <= N; ++i) { - for (int j = 0; j <= i; ++j) { - const double basis = pu[j] * pv[i - j]; - gLabels.push_back(sensorVol->getLabel().asCalib().rawGBL(legIdx)); - gDer(0, legColStart + legIdx) = dydx * basis; - gDer(1, legColStart + legIdx) = dzdx * basis; - ++legIdx; - } + // 3) calibration derivatives (apply directly on the whole sensor, not on individual tiles) + if (calibSet) { + const int nd = calibSet->nDOFs(); + Eigen::MatrixXd calDer(3, nd); + calibSet->fillDerivatives(derCtx, calDer); + for (int iDOF = 0; iDOF < nd; ++iDOF) { + gLabels.push_back(sensorVol->getLabel().asCalib().rawGBL(iDOF)); } + gDer.middleCols(curCol, nd) = calDer; + curCol += nd; } point.addGlobals(gLabels, gDer); } @@ -514,32 +493,24 @@ void AlignmentSpec::updateTimeDependentParams(ProcessingContext& pc) buildHierarchy(); - if (mParams->doMisalignmentLeg || mParams->doMisalignmentRB) { - TMatrixD null(1, 1); - null(0, 0) = 0; - for (int i = 0; i < 6; ++i) { - mDeformations[i] = o2::math_utils::Legendre2DPolynominal(null); - mRigidBodyParams[i].setZero(); + if (mParams->doMisalignmentLeg || mParams->doMisalignmentRB || mParams->doMisalignmentInex) { + mMisalignment = {}; + for (auto& rb : mRigidBodyParams) { + rb.setZero(); } if (!mParams->misAlgJson.empty()) { - using json = nlohmann::json; - std::ifstream f(mParams->misAlgJson); - auto data = json::parse(f); - for (const auto& item : data) { - int id = item["id"].get(); - if (mParams->doMisalignmentLeg && item.contains("matrix")) { - auto v = item["matrix"].get>>(); - TMatrixD m(v.size(), v[v.size() - 1].size()); - for (size_t r{0}; r < v.size(); ++r) { - for (size_t c{0}; c < v[r].size(); ++c) { - m(r, c) = v[r][c]; - } + mMisalignment = loadMisalignmentModel(mParams->misAlgJson); + if (mParams->doMisalignmentRB) { + using json = nlohmann::json; + std::ifstream f(mParams->misAlgJson); + auto data = json::parse(f); + for (const auto& item : data) { + int id = item["id"].get(); + if (!item.contains("rigidBody")) { + continue; } - mDeformations[id] = o2::math_utils::Legendre2DPolynominal(m); - } - if (mParams->doMisalignmentRB && item.contains("rigidBody")) { auto rb = item["rigidBody"].get>(); - for (int k = 0; k < 6 && k < (int)rb.size(); ++k) { + for (int k = 0; k < 6 && k < static_cast(rb.size()); ++k) { mRigidBodyParams[id](k) = rb[k]; } } @@ -848,6 +819,12 @@ bool AlignmentSpec::applyMisalignment(Eigen::Vector2d& res, const FrameInfoExt& const int sensorID = constants::detID::getSensorID(frame.sens); const int layerID = constants::detID::getDetID2Layer(frame.sens); + const MisalignmentFrame misFrame{ + .sensorID = sensorID, + .layerID = layerID, + .x = frame.x, + .alpha = frame.alpha, + .z = frame.positionTrackingFrame[1]}; // --- Legendre deformation (non-rigid-body) --- if (mParams->doMisalignmentLeg && mIsITS3 && mUseMC) { @@ -866,36 +843,18 @@ bool AlignmentSpec::applyMisalignment(Eigen::Vector2d& res, const FrameInfoExt& } o2::track::TrackParD mcPar(xyz, pxyz, TMath::Nint(pPDG->Charge() / 3), false); - const double r = frame.x; - const double gloX = r * std::cos(frame.alpha); - const double gloY = r * std::sin(frame.alpha); - const double gloZ = frame.positionTrackingFrame[1]; - auto [u, v] = computeUV(gloX, gloY, gloZ, sensorID, constants::radii[layerID]); - const double h = mDeformations[sensorID](u, v); - auto mcAtCl = mcPar; if (!mcAtCl.rotate(frame.alpha) || !prop->PropagateToXBxByBz(mcAtCl, frame.x)) { return false; } - const double snp = mcAtCl.getSnp(); - const double tgl = mcAtCl.getTgl(); - const double csci = 1. / std::sqrt(1. - (snp * snp)); - const double dydx = snp * csci; - const double dzdx = tgl * csci; - const double dy = dydx * h; - const double dz = dzdx * h; - - const double newGloY = (r * std::sin(frame.alpha)) + (dy * std::cos(frame.alpha)); - const double newGloX = (r * std::cos(frame.alpha)) - (dy * std::sin(frame.alpha)); - const double newGloZ = gloZ + dz; - auto [uNew, vNew] = computeUV(newGloX, newGloY, newGloZ, sensorID, constants::radii[layerID]); - if (std::abs(uNew) > 1. || std::abs(vNew) > 1.) { + const auto shift = evaluateLegendreShift(mMisalignment[sensorID], misFrame, computeTrackSlopes(mcAtCl.getSnp(), mcAtCl.getTgl())); + if (!shift.accepted) { return false; } - res[0] += dy; - res[1] += dz; + res[0] += shift.dy; + res[1] += shift.dz; } // --- Rigid body misalignment --- @@ -910,7 +869,7 @@ bool AlignmentSpec::applyMisalignment(Eigen::Vector2d& res, const FrameInfoExt& const auto* tileVol = mChip2Hiearchy.at(lbl); // derivative in TRK frame (3x6: rows = dy, dz, dsnp) - Matrix36 der = getRigidBodyDerivatives(wTrk); + Matrix36 der = getRigidBodyBaseDerivatives(makeDerivativeContext(frame, wTrk)); // TRK -> tile LOC const double posTrk[3] = {frame.x, 0., 0.}; @@ -929,6 +888,26 @@ bool AlignmentSpec::applyMisalignment(Eigen::Vector2d& res, const FrameInfoExt& res[1] += shift[1]; // dz } + // --- In-extensional deformation --- + // displacement field u(phi,z) = (u_phi, u_z, u_r) + // dy = -u_phi + y' * u_r, dz = -u_z + z' * u_r + if (mParams->doMisalignmentInex) { + const auto shift = evaluateInextensionalShift(mMisalignment[sensorID], misFrame, computeTrackSlopes(wTrk.getSnp(), wTrk.getTgl())); + res[0] += shift.dy; + res[1] += shift.dz; + } + + if (mOutOpt[OutputOpt::MisRes]) { + (*mDBGOut) << "mis" + << "dy=" << res[0] + << "dz=" << res[1] + << "sens=" << sensorID + << "lay=" << layerID + << "z=" << frame.positionTrackingFrame[1] + << "phi=" << frame.alpha + << "\n"; + } + return true; } diff --git a/Detectors/Upgrades/ITS3/alignment/src/MisalignmentUtils.cxx b/Detectors/Upgrades/ITS3/alignment/src/MisalignmentUtils.cxx new file mode 100644 index 0000000000000..ee5198ee98e0c --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/src/MisalignmentUtils.cxx @@ -0,0 +1,151 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "ITS3Align/MisalignmentUtils.h" + +#include +#include +#include +#include + +#include +#include + +#include "Framework/Logger.h" +#include "ITS3Base/SpecsV2.h" + +namespace o2::its3::align +{ + +bool MisalignmentModel::empty() const noexcept +{ + return std::all_of(sensors.begin(), sensors.end(), [](const auto& sensor) { return sensor.empty(); }); +} + +MisalignmentModel loadMisalignmentModel(const std::string& jsonPath) +{ + MisalignmentModel model; + if (jsonPath.empty()) { + return model; + } + + std::ifstream f(jsonPath); + if (!f.is_open()) { + LOGP(fatal, "Cannot open misalignment JSON file: {}", jsonPath); + } + + using json = nlohmann::json; + const auto data = json::parse(f); + for (const auto& item : data) { + const int id = item["id"].get(); + if (id < 0 || id >= static_cast(MisalignmentModel::NSensors)) { + LOGP(fatal, "Misalignment sensor id {} out of range [0, {}) in {}", id, MisalignmentModel::NSensors, jsonPath); + } + + auto& sensor = model[id]; + if (item.contains("matrix")) { + auto v = item["matrix"].get>>(); + if (v.empty()) { + LOGP(fatal, "Legendre matrix for sensor {} is empty in {}", id, jsonPath); + } + TMatrixD m(v.size(), v.back().size()); + for (std::size_t r{0}; r < v.size(); ++r) { + for (std::size_t c{0}; c < v[r].size(); ++c) { + m(r, c) = v[r][c]; + } + } + sensor.legendre = o2::math_utils::Legendre2DPolynominal(m); + sensor.hasLegendre = true; + } + if (item.contains("inextensional")) { + const auto& inex = item["inextensional"]; + sensor.hasInextensional = true; + if (inex.contains("modes")) { + for (const auto& [key, val] : inex["modes"].items()) { + sensor.inextensional.modes[std::stoi(key)] = val.get>(); + } + } + if (inex.contains("alpha")) { + sensor.inextensional.alpha = inex["alpha"].get(); + } + if (inex.contains("beta")) { + sensor.inextensional.beta = inex["beta"].get(); + } + } + } + + return model; +} + +MisalignmentShift evaluateLegendreShift(const SensorMisalignment& sensor, const MisalignmentFrame& frame, const TrackSlopes& slopes) +{ + MisalignmentShift shift; + if (!sensor.hasLegendre) { + return shift; + } + + const double gloX = frame.x * std::cos(frame.alpha); + const double gloY = frame.x * std::sin(frame.alpha); + const double gloZ = frame.z; + auto [u, v] = computeUV(gloX, gloY, gloZ, frame.sensorID, constants::radii[frame.layerID]); + const double h = sensor.legendre(u, v); + + shift.dy = slopes.dydx * h; + shift.dz = slopes.dzdx * h; + + const double newGloY = gloY + (shift.dy * std::cos(frame.alpha)); + const double newGloX = gloX - (shift.dy * std::sin(frame.alpha)); + const double newGloZ = gloZ + shift.dz; + auto [uNew, vNew] = computeUV(newGloX, newGloY, newGloZ, frame.sensorID, constants::radii[frame.layerID]); + shift.accepted = std::abs(uNew) <= 1. && std::abs(vNew) <= 1.; + return shift; +} + +MisalignmentShift evaluateInextensionalShift(const SensorMisalignment& sensor, const MisalignmentFrame& frame, const TrackSlopes& slopes) +{ + MisalignmentShift shift; + if (!sensor.hasInextensional) { + return shift; + } + + const double r = constants::radii[frame.layerID]; + const double phi = std::atan2(r * std::sin(frame.alpha), r * std::cos(frame.alpha)); + const double z = frame.z; + const auto& inex = sensor.inextensional; + + double uz = 0., uphi = 0., ur = 0.; + for (const auto& [n, coeffs] : inex.modes) { + const double a_n = coeffs[0], b_n = coeffs[1], c_n = coeffs[2], d_n = coeffs[3]; + const double sn = std::sin(n * phi); + const double cn = std::cos(n * phi); + const int n2 = n * n; + + const double fn = (a_n * cn) + (b_n * sn); + const double fpn = (-n * a_n * sn) + (n * b_n * cn); + const double fppn = (-n2 * a_n * cn) - (n2 * b_n * sn); + const double gn = (c_n * cn) + (d_n * sn); + const double gpn = (-n * c_n * sn) + (n * d_n * cn); + + uz += fn; + uphi += -(z / r) * fpn + gn; + ur += (z / r) * fppn - gpn; + } + + uz += inex.alpha * phi; + uphi += -(z / r) * inex.alpha + inex.beta * phi; + ur += -inex.beta; + + shift.dy = -uphi + (slopes.dydx * ur); + shift.dz = -uz + (slopes.dzdx * ur); + return shift; +} + +} // namespace o2::its3::align diff --git a/Detectors/Upgrades/ITS3/study/src/TrackingStudy.cxx b/Detectors/Upgrades/ITS3/study/src/TrackingStudy.cxx index 4ce2c79cb23f1..63a08d1c9c6ab 100644 --- a/Detectors/Upgrades/ITS3/study/src/TrackingStudy.cxx +++ b/Detectors/Upgrades/ITS3/study/src/TrackingStudy.cxx @@ -11,12 +11,10 @@ #include #include -#include #include #include #include -#include #include "CommonUtils/TreeStreamRedirector.h" #include "DataFormatsGlobalTracking/RecoContainer.h" @@ -38,6 +36,7 @@ #include "ITS3Reconstruction/IOUtils.h" #include "ITS3TrackingStudy/ITS3TrackingStudyParam.h" #include "ITS3TrackingStudy/ParticleInfoExt.h" +#include "ITS3Align/MisalignmentUtils.h" #include "ITS3Align/TrackFit.h" #include "ReconstructionDataFormats/DCA.h" #include "ReconstructionDataFormats/GlobalTrackID.h" @@ -47,7 +46,6 @@ #include "SimulationDataFormat/MCEventLabel.h" #include "SimulationDataFormat/MCUtils.h" #include "Steer/MCKinematicsReader.h" -#include "MathUtils/LegendrePols.h" #include "Framework/Logger.h" namespace o2::its3::study @@ -153,7 +151,7 @@ class TrackingStudySpec : public Task o2::steer::MCKinematicsReader mMCReader; // reader of MC information const o2::its3::TopologyDictionary* mITSDict = nullptr; // cluster patterns dictionary o2::globaltracking::RecoContainer mRecoData; - std::array mDeformations; // one per sensorID (0-5) + align::MisalignmentModel mMisalignment; }; void TrackingStudySpec::init(InitContext& ic) @@ -186,26 +184,9 @@ void TrackingStudySpec::updateTimeDependentParams(ProcessingContext& pc) o2::its::GeometryTGeo::Instance()->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G, o2::math_utils::TransformType::T2G)); mParams = &ITS3TrackingStudyParam::Instance(); if (mParams->doMisalignment) { - TMatrixD null(1, 1); - null(0, 0) = 0; - for (int i = 0; i < 6; ++i) { - mDeformations[i] = o2::math_utils::Legendre2DPolynominal(null); - } + mMisalignment = {}; if (!mParams->misAlgJson.empty()) { - using json = nlohmann::json; - std::ifstream f(mParams->misAlgJson); - auto data = json::parse(f); - for (const auto& item : data) { - int id = item["id"].get(); - auto v = item["matrix"].get>>(); - TMatrixD m(v.size(), v[v.size() - 1].size()); - for (size_t r{0}; r < v.size(); ++r) { - for (size_t c{0}; c < v[r].size(); ++c) { - m(r, c) = v[r][c]; - } - } - mDeformations[id] = o2::math_utils::Legendre2DPolynominal(m); - } + mMisalignment = align::loadMisalignmentModel(mParams->misAlgJson); } } } @@ -984,17 +965,6 @@ void TrackingStudySpec::doMisalignmentStudy() int goodRefit{0}, notPassedSel{0}, fitFail{0}, fitFailMis{0}; - // compute normalized (u,v) in [-1,1] from global position - auto computeUV = [](float gloX, float gloY, float gloZ, int sensorID, float radius) -> std::pair { - const bool isTop = sensorID % 2 == 0; - const double phi = o2::math_utils::to02Pi(std::atan2(gloY, gloX)); - const double phiBorder1 = o2::math_utils::to02Pi(((isTop ? 0. : 1.) * TMath::Pi()) + std::asin(constants::equatorialGap / 2. / radius)); - const double phiBorder2 = o2::math_utils::to02Pi(((isTop ? 1. : 2.) * TMath::Pi()) - std::asin(constants::equatorialGap / 2. / radius)); - const double u = (((phi - phiBorder1) * 2.) / (phiBorder2 - phiBorder1)) - 1.; - const double v = ((2. * gloZ + constants::segment::lengthSensitive) / constants::segment::lengthSensitive) - 1.; - return {u, v}; - }; - float chi2{0}; auto writeTree = [&](const char* treeName, const std::array& clArr, @@ -1102,8 +1072,8 @@ void TrackingStudySpec::doMisalignmentStudy() } writeTree("idealRes", clArr, extrapOut, extrapInw, lbl); - // Propagate MC truth to each cluster's (alpha, x) to get true track direction, - // then compute dy = dydx*h(u,v), dz = dzdx*h(u,v) - first Newton step. + // Propagate MC truth to each cluster's (alpha, x) to get true track direction. + // The shared misalignment evaluators then provide the tracking-frame dy/dz shift. const auto mcTrk = mMCReader.getTrack(lbl); if (!mcTrk) { continue; @@ -1132,14 +1102,7 @@ void TrackingStudySpec::doMisalignmentStudy() } const int sensorID = constants::detID::getSensorID(sens); const int layerID = constants::detID::getDetID2Layer(sens); - - // compute h(u,v) at this cluster - const float r = orig.getX(); - const float gloX = r * std::cos(orig.alpha); - const float gloY = r * std::sin(orig.alpha); - const float gloZ = orig.getZ(); - auto [u, v] = computeUV(gloX, gloY, gloZ, sensorID, constants::radii[layerID]); - const double h = mDeformations[sensorID](u, v); + const auto& sensorMis = mMisalignment[sensorID]; // propagate MC track to cluster's tracking frame to get true slopes auto mcAtCl = mcPar; @@ -1147,28 +1110,31 @@ void TrackingStudySpec::doMisalignmentStudy() clArrMis[1 + iLay] = nullptr; // can't compute slopes -> drop cluster continue; } - const float snp = mcAtCl.getSnp(); - const float tgl = mcAtCl.getTgl(); - const float csci = 1.f / std::sqrt(1.f - (snp * snp)); - const float dydx = snp * csci; - const float dzdx = tgl * csci; - const float dy = dydx * static_cast(h); - const float dz = dzdx * static_cast(h); - - // check if shifted position is still within sensor acceptance - const float newGloY = (r * std::sin(orig.alpha)) + (dy * std::cos(orig.alpha)); - const float newGloX = (r * std::cos(orig.alpha)) - (dy * std::sin(orig.alpha)); - const float newGloZ = gloZ + dz; - auto [uNew, vNew] = computeUV(newGloX, newGloY, newGloZ, sensorID, constants::radii[layerID]); - if (std::abs(uNew) > 1. || std::abs(vNew) > 1.) { - clArrMis[1 + iLay] = nullptr; // shifted outside acceptance - continue; + const align::MisalignmentFrame misFrame{ + .sensorID = sensorID, + .layerID = layerID, + .x = orig.getX(), + .alpha = orig.alpha, + .z = orig.getZ()}; + const auto slopes = align::computeTrackSlopes(mcAtCl.getSnp(), mcAtCl.getTgl()); + + align::MisalignmentShift totalShift; + if (sensorMis.hasLegendre) { + const auto shift = align::evaluateLegendreShift(sensorMis, misFrame, slopes); + if (!shift.accepted) { + clArrMis[1 + iLay] = nullptr; // shifted outside acceptance + continue; + } + totalShift += shift; + } + if (sensorMis.hasInextensional) { + totalShift += align::evaluateInextensionalShift(sensorMis, misFrame, slopes); } // create shifted copy: keep x=r (nominal), shift y and z misClArr[iLay] = orig; - misClArr[iLay].setY(orig.getY() + dy); - misClArr[iLay].setZ(orig.getZ() + dz); + misClArr[iLay].setY(orig.getY() + totalShift.dy); + misClArr[iLay].setZ(orig.getZ() + totalShift.dz); misClArr[iLay].setSigmaY2(orig.getSigmaY2() + (mParams->misAlgExtCY[sensorID] * mParams->misAlgExtCY[sensorID])); misClArr[iLay].setSigmaZ2(orig.getSigmaZ2() + (mParams->misAlgExtCZ[sensorID] * mParams->misAlgExtCZ[sensorID])); clArrMis[1 + iLay] = &misClArr[iLay]; From 4b43e4050f38d86147b85fb38aa9337d5d4496ca Mon Sep 17 00:00:00 2001 From: Francesco Noferini Date: Tue, 7 Apr 2026 12:27:18 +0200 Subject: [PATCH 047/285] require min num of entries for TOF Diagnostic calibs (#15249) --- .../include/TOFCalibration/TOFDiagnosticCalibrator.h | 9 ++++++--- .../TOF/calibration/src/TOFDiagnosticCalibrator.cxx | 10 ++++++++++ .../testWorkflow/TOFDiagnosticCalibratorSpec.h | 8 +++++--- .../testWorkflow/tof-diagnostic-workflow.cxx | 4 +++- 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/Detectors/TOF/calibration/include/TOFCalibration/TOFDiagnosticCalibrator.h b/Detectors/TOF/calibration/include/TOFCalibration/TOFDiagnosticCalibrator.h index 7b23df6040d3d..fccfcf51fdaa9 100644 --- a/Detectors/TOF/calibration/include/TOFCalibration/TOFDiagnosticCalibrator.h +++ b/Detectors/TOF/calibration/include/TOFCalibration/TOFDiagnosticCalibrator.h @@ -31,9 +31,9 @@ class TOFDiagnosticCalibrator final : public o2::calibration::TimeSlotCalibratio int mRunNumber = -1; public: - TOFDiagnosticCalibrator() = default; + TOFDiagnosticCalibrator(int minROwin = 100) : mMinROwin(minROwin) {} ~TOFDiagnosticCalibrator() final = default; - bool hasEnoughData(const Slot& slot) const final { return true; } + bool hasEnoughData(const Slot& slot) const final; void initOutput() final; void finalizeSlot(Slot& slot) final; Slot& emplaceNewSlot(bool front, TFType tstart, TFType tend) final; @@ -43,12 +43,15 @@ class TOFDiagnosticCalibrator final : public o2::calibration::TimeSlotCalibratio const std::vector& getDiagnosticVector() const { return mDiagnosticVector; } const CcdbObjectInfoVector& getDiagnosticInfoVector() const { return mccdbInfoVector; } CcdbObjectInfoVector& getDiagnosticInfoVector() { return mccdbInfoVector; } + int getMinROwin() const { return mMinROwin; } + void setMinROwin(int rowin) { mMinROwin = rowin; } private: CcdbObjectInfoVector mccdbInfoVector; std::vector mDiagnosticVector; + int mMinROwin; // minimal number of readout windows needed to finalize the object - ClassDefOverride(TOFDiagnosticCalibrator, 1); + ClassDefOverride(TOFDiagnosticCalibrator, 2); }; } // end namespace tof diff --git a/Detectors/TOF/calibration/src/TOFDiagnosticCalibrator.cxx b/Detectors/TOF/calibration/src/TOFDiagnosticCalibrator.cxx index 9a4118dbba493..f238d69bb75ed 100644 --- a/Detectors/TOF/calibration/src/TOFDiagnosticCalibrator.cxx +++ b/Detectors/TOF/calibration/src/TOFDiagnosticCalibrator.cxx @@ -28,7 +28,17 @@ void TOFDiagnosticCalibrator::initOutput() mccdbInfoVector.clear(); mDiagnosticVector.clear(); } +//---------------------------------------------------------- +bool TOFDiagnosticCalibrator::hasEnoughData(const Slot& slot) const +{ + const Diagnostic* diag = slot.getContainer(); + if (diag->getFrequencyROW() < mMinROwin) { + return false; + } + + return true; +} //---------------------------------------------------------- void TOFDiagnosticCalibrator::finalizeSlot(Slot& slot) { diff --git a/Detectors/TOF/calibration/testWorkflow/TOFDiagnosticCalibratorSpec.h b/Detectors/TOF/calibration/testWorkflow/TOFDiagnosticCalibratorSpec.h index 7887ff848d544..91b931f3b96b3 100644 --- a/Detectors/TOF/calibration/testWorkflow/TOFDiagnosticCalibratorSpec.h +++ b/Detectors/TOF/calibration/testWorkflow/TOFDiagnosticCalibratorSpec.h @@ -37,7 +37,7 @@ namespace calibration class TOFDiagnosticCalibDevice : public o2::framework::Task { public: - TOFDiagnosticCalibDevice(std::shared_ptr req, int runnumber = -1) : mCCDBRequest(req), mRunNumber(runnumber) {} + TOFDiagnosticCalibDevice(std::shared_ptr req, int runnumber = -1, int rowinMin = 100000) : mCCDBRequest(req), mRunNumber(runnumber), mMinROwin(rowinMin) {} void init(o2::framework::InitContext& ic) final { o2::base::GRPGeomHelper::instance().setRequest(mCCDBRequest); @@ -47,6 +47,7 @@ class TOFDiagnosticCalibDevice : public o2::framework::Task mCalibrator->setSlotLength(slotL); mCalibrator->setMaxSlotsDelay(delay); mCalibrator->setRunNumber(mRunNumber); + mCalibrator->setMinROwin(mMinROwin); } void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) final @@ -75,6 +76,7 @@ class TOFDiagnosticCalibDevice : public o2::framework::Task std::unique_ptr mCalibrator; std::shared_ptr mCCDBRequest; int mRunNumber = -1; + int mMinROwin = 100000; //________________________________________________________________ void sendOutput(DataAllocator& output) @@ -104,7 +106,7 @@ class TOFDiagnosticCalibDevice : public o2::framework::Task namespace framework { -DataProcessorSpec getTOFDiagnosticCalibDeviceSpec(int runnumber) +DataProcessorSpec getTOFDiagnosticCalibDeviceSpec(int runnumber, int rowinMin) { using device = o2::calibration::TOFDiagnosticCalibDevice; using clbUtils = o2::calibration::Utils; @@ -125,7 +127,7 @@ DataProcessorSpec getTOFDiagnosticCalibDeviceSpec(int runnumber) "tof-diagnostic-calibration", inputs, outputs, - AlgorithmSpec{adaptFromTask(ccdbRequest, runnumber)}, + AlgorithmSpec{adaptFromTask(ccdbRequest, runnumber, rowinMin)}, Options{ {"tf-per-slot", VariantType::UInt32, 5u, {"number of TFs per calibration time slot"}}, {"max-delay", VariantType::UInt32, 3u, {"number of slots in past to consider"}}}}; diff --git a/Detectors/TOF/calibration/testWorkflow/tof-diagnostic-workflow.cxx b/Detectors/TOF/calibration/testWorkflow/tof-diagnostic-workflow.cxx index 3cde7be96867a..b45fcc2b5498c 100644 --- a/Detectors/TOF/calibration/testWorkflow/tof-diagnostic-workflow.cxx +++ b/Detectors/TOF/calibration/testWorkflow/tof-diagnostic-workflow.cxx @@ -19,6 +19,7 @@ void customize(std::vector& workflowOptions) { // option allowing to set parameters workflowOptions.push_back(ConfigParamSpec{"tof-dia-run-number", o2::framework::VariantType::Int, -1, {"run number"}}); + workflowOptions.push_back(ConfigParamSpec{"tof-dia-min-rowin", o2::framework::VariantType::Int, 100000, {"min number of TOF Readout Windows, def=100k (3 s)"}}); } // ------------------------------------------------------------------ @@ -29,6 +30,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { WorkflowSpec specs; auto runnumber = configcontext.options().get("tof-dia-run-number"); - specs.emplace_back(getTOFDiagnosticCalibDeviceSpec(runnumber)); + auto rowinMin = configcontext.options().get("tof-dia-min-rowin"); + specs.emplace_back(getTOFDiagnosticCalibDeviceSpec(runnumber, rowinMin)); return specs; } From 5b27406094d6e12bc2daeb32bf6a25bebe1d7f95 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 5 Apr 2026 21:52:34 +0200 Subject: [PATCH 048/285] DPL: refactor InputSpan Now that navigating the MessageSet does not have an index anymore it does not make sense to use a random accessor to implement the InputSpan::Iterator, because that results in an O(N^2) loop for normal linear iterations. It is much better to simply implement a proper advancement operator, so that looping stays O(N). This resumes the performance when iterating on old-style multiparts which are apparently still used in some cases. General modernization of InputSpan also done. --- .../Core/include/Framework/DataModelViews.h | 6 +- Framework/Core/include/Framework/DataRef.h | 10 ++ .../Core/include/Framework/InputRecord.h | 47 +++++-- Framework/Core/include/Framework/InputSpan.h | 130 ++++++++++-------- .../Core/src/CompletionPolicyHelpers.cxx | 6 +- Framework/Core/src/DataProcessingDevice.cxx | 41 +++--- Framework/Core/src/DataRelayer.cxx | 62 +++++---- Framework/Core/src/InputRecord.cxx | 10 ++ Framework/Core/src/InputSpan.cxx | 24 +--- Framework/Core/test/benchmark_InputRecord.cxx | 14 +- Framework/Core/test/test_CompletionPolicy.cxx | 7 +- Framework/Core/test/test_InputRecord.cxx | 12 +- .../Core/test/test_InputRecordWalker.cxx | 16 +-- Framework/Core/test/test_InputSpan.cxx | 12 +- Framework/Utils/test/RawPageTestData.h | 14 +- Framework/Utils/test/test_RootTreeWriter.cxx | 12 +- Utilities/DataSampling/src/Dispatcher.cxx | 2 +- 17 files changed, 253 insertions(+), 172 deletions(-) diff --git a/Framework/Core/include/Framework/DataModelViews.h b/Framework/Core/include/Framework/DataModelViews.h index 53d6e6615b96e..dd8d65ea16459 100644 --- a/Framework/Core/include/Framework/DataModelViews.h +++ b/Framework/Core/include/Framework/DataModelViews.h @@ -16,6 +16,7 @@ #include "DomainInfoHeader.h" #include "SourceInfoHeader.h" #include "Headers/DataHeader.h" +#include "Framework/DataRef.h" #include "Framework/TimesliceSlot.h" #include #include @@ -80,10 +81,7 @@ struct count_parts { } }; -struct DataRefIndices { - size_t headerIdx; - size_t payloadIdx; -}; +// DataRefIndices is defined in Framework/DataRef.h struct get_pair { size_t pairId; diff --git a/Framework/Core/include/Framework/DataRef.h b/Framework/Core/include/Framework/DataRef.h index d4cba88b19333..aad667c8c33bd 100644 --- a/Framework/Core/include/Framework/DataRef.h +++ b/Framework/Core/include/Framework/DataRef.h @@ -12,6 +12,7 @@ #define FRAMEWORK_DATAREF_H #include // for size_t +#include namespace o2 { @@ -29,6 +30,15 @@ struct DataRef { size_t payloadSize = 0; }; +/// Raw indices into the message vector for one (header, payload) pair. +/// Kept in a lightweight header so InputSpan can use it without pulling in FairMQ. +struct DataRefIndices { + size_t headerIdx; + size_t payloadIdx; + bool operator==(const DataRefIndices&) const = default; + auto operator<=>(const DataRefIndices&) const = default; +}; + } // namespace framework } // namespace o2 diff --git a/Framework/Core/include/Framework/InputRecord.h b/Framework/Core/include/Framework/InputRecord.h index 96963f88524be..d2e152c1bcacc 100644 --- a/Framework/Core/include/Framework/InputRecord.h +++ b/Framework/Core/include/Framework/InputRecord.h @@ -13,6 +13,7 @@ #include "Framework/DataRef.h" #include "Framework/DataRefUtils.h" +#include "Framework/InputSpan.h" #include "Framework/InputRoute.h" #include "Framework/TypeTraits.h" #include "Framework/TableConsumer.h" @@ -202,6 +203,15 @@ class InputRecord [[nodiscard]] size_t getNofParts(int pos) const; + /// O(1) access to the part described by @a indices in slot @a pos. + [[nodiscard]] DataRef getAtIndices(int pos, DataRefIndices indices) const; + + /// O(1) advance from @a current to the next part's indices in slot @a pos. + [[nodiscard]] DataRefIndices nextIndices(int pos, DataRefIndices current) const + { + return mSpan.nextIndices(pos, current); + } + // Given a binding by string, return the associated DataRef DataRef getDataRefByString(const char* bindingName, int part = 0) const { @@ -568,8 +578,8 @@ class InputRecord Iterator() = delete; - Iterator(ParentType const* parent, size_t position = 0, size_t size = 0) - : mPosition(position), mSize(size > position ? size : position), mParent(parent), mElement{nullptr, nullptr, nullptr} + Iterator(ParentType const* parent, bool isEnd = false) + : mPosition(isEnd ? parent->size() : 0), mSize(parent->size()), mParent(parent), mElement{nullptr, nullptr, nullptr} { if (mPosition < mSize) { if (mParent->isValid(mPosition)) { @@ -678,18 +688,29 @@ class InputRecord using reference = typename BaseType::reference; using pointer = typename BaseType::pointer; using ElementType = typename std::remove_const::type; - using iterator = Iterator; - using const_iterator = Iterator; + using iterator = InputSpan::Iterator; + using const_iterator = InputSpan::Iterator; + + InputRecordIterator(InputRecord const* parent, bool isEnd = false) + : BaseType(parent, isEnd) + { + } + + /// Initial indices for part-level iteration: first part starts at {headerIdx=0, payloadIdx=1}. + [[nodiscard]] DataRefIndices initialIndices() const { return {0, 1}; } + /// Sentinel used by nextIndicesGetter to signal end-of-slot. + [[nodiscard]] DataRefIndices endIndices() const { return {size_t(-1), size_t(-1)}; } - InputRecordIterator(InputRecord const* parent, size_t position = 0, size_t size = 0) - : BaseType(parent, position, size) + /// Get element at the given raw message indices in O(1). + [[nodiscard]] ElementType getAtIndices(DataRefIndices indices) const { + return this->parent()->getAtIndices(this->position(), indices); } - /// Get element at {slotindex, partindex} - [[nodiscard]] ElementType getByPos(size_t pos) const + /// Advance @a current to the next part's indices in O(1). + [[nodiscard]] DataRefIndices nextIndices(DataRefIndices current) const { - return this->parent()->getByPos(this->position(), pos); + return this->parent()->nextIndices(this->position(), current); } /// Check if slot is valid, index of part is not used @@ -709,12 +730,12 @@ class InputRecord [[nodiscard]] const_iterator begin() const { - return const_iterator(this, 0, size()); + return const_iterator(this, size() == 0); } [[nodiscard]] const_iterator end() const { - return const_iterator(this, size()); + return const_iterator(this, true); } }; @@ -723,12 +744,12 @@ class InputRecord [[nodiscard]] const_iterator begin() const { - return {this, 0, size()}; + return {this, false}; } [[nodiscard]] const_iterator end() const { - return {this, size()}; + return {this, true}; } InputSpan& span() diff --git a/Framework/Core/include/Framework/InputSpan.h b/Framework/Core/include/Framework/InputSpan.h index cf8c8acda6796..dbe270f0e030d 100644 --- a/Framework/Core/include/Framework/InputSpan.h +++ b/Framework/Core/include/Framework/InputSpan.h @@ -14,8 +14,8 @@ #include "Framework/DataRef.h" #include -extern template class std::function; -extern template class std::function; +extern template class std::function; +extern template class std::function; namespace o2::framework { @@ -32,37 +32,48 @@ class InputSpan InputSpan(InputSpan const&) = delete; InputSpan(InputSpan&&) = default; - /// @a getter is the mapping between an element of the span referred by - /// index and the buffer associated. - /// @a size is the number of elements in the span. - InputSpan(std::function getter, size_t size); + /// Navigate the message store via the DataRefIndices protocol. + /// get_next_pair (DataModelViews.h) provides O(1) sequential advancement for nextIndicesGetter. + InputSpan(std::function nofPartsGetter, + std::function refCountGetter, + std::function indicesGetter, + std::function nextIndicesGetter, + size_t size); - /// @a getter is the mapping between an element of the span referred by - /// index and the buffer associated. - /// @a size is the number of elements in the span. - InputSpan(std::function getter, size_t size); + /// @a i-th element of the InputSpan (O(partidx) sequential scan via indices protocol) + [[nodiscard]] DataRef get(size_t i, size_t partidx = 0) const + { + DataRefIndices idx{0, 1}; + for (size_t p = 0; p < partidx; ++p) { + idx = mNextIndicesGetter(i, idx); + } + return mIndicesGetter(i, idx); + } - /// @a getter is the mapping between an element of the span referred by - /// index and the buffer associated. - /// @nofPartsGetter is the getter for the number of parts associated with an index - /// @a size is the number of elements in the span. - InputSpan(std::function getter, std::function nofPartsGetter, std::function refCountGetter, size_t size); + /// Return the DataRef for the part described by @a indices in slot @a slotIdx in O(1). + [[nodiscard]] DataRef getAtIndices(size_t slotIdx, DataRefIndices indices) const + { + return mIndicesGetter(slotIdx, indices); + } - /// @a i-th element of the InputSpan - [[nodiscard]] DataRef get(size_t i, size_t partidx = 0) const + /// Advance from @a current to the indices of the next part in slot @a slotIdx in O(1). + [[nodiscard]] DataRefIndices nextIndices(size_t slotIdx, DataRefIndices current) const { - return mGetter(i, partidx); + return mNextIndicesGetter(slotIdx, current); } + // --- slot-level Iterator protocol (headerIdx doubles as slot position) --- + [[nodiscard]] DataRefIndices initialIndices() const { return {0, 0}; } + [[nodiscard]] DataRefIndices endIndices() const { return {mSize, 0}; } + [[nodiscard]] DataRef getAtIndices(DataRefIndices indices) const { return mIndicesGetter(indices.headerIdx, {0, 1}); } + [[nodiscard]] DataRefIndices nextIndices(DataRefIndices current) const { return {current.headerIdx + 1, 0}; } + /// @a number of parts in the i-th element of the InputSpan [[nodiscard]] size_t getNofParts(size_t i) const { if (i >= mSize) { return 0; } - if (!mNofPartsGetter) { - return 1; - } return mNofPartsGetter(i); } @@ -94,7 +105,8 @@ class InputSpan return get(i).payload; } - /// an iterator class working on position within the a parent class + /// An iterator over the elements of a parent container using the DataRefIndices protocol. + /// ParentT must provide: initialIndices(), getAtIndices(DataRefIndices), nextIndices(DataRefIndices). template class Iterator { @@ -110,23 +122,23 @@ class InputSpan Iterator() = delete; - Iterator(ParentType const* parent, size_t position = 0, size_t size = 0) - : mPosition(position), mSize(size > position ? size : position), mParent(parent), mElement{} + Iterator(ParentType const* parent, bool isEnd = false) + : mParent(parent), + mCurrentIndices(isEnd ? parent->endIndices() : parent->initialIndices()), + mElement{} { - if (mPosition < mSize) { - mElement = mParent->get(mPosition); + if (mCurrentIndices != mParent->endIndices()) { + mElement = mParent->getAtIndices(mCurrentIndices); } } - ~Iterator() = default; - // prefix increment SelfType& operator++() { - if (mPosition < mSize && ++mPosition < mSize) { - mElement = mParent->get(mPosition); + mCurrentIndices = mParent->nextIndices(mCurrentIndices); + if (mCurrentIndices != mParent->endIndices()) { + mElement = mParent->getAtIndices(mCurrentIndices); } else { - // reset the element to the default value of the type mElement = ElementType{}; } return *this; @@ -145,16 +157,14 @@ class InputSpan return mElement; } - // comparison bool operator==(const SelfType& rh) const { - return mPosition == rh.mPosition; + return mCurrentIndices == rh.mCurrentIndices; } - // comparison - bool operator!=(const SelfType& rh) const + auto operator<=>(const SelfType& rh) const { - return mPosition != rh.mPosition; + return mCurrentIndices <=> rh.mCurrentIndices; } // return pointer to parent instance @@ -163,22 +173,21 @@ class InputSpan return mParent; } - // return current position + // return current position (headerIdx serves as the slot index for slot-level iteration) [[nodiscard]] size_t position() const { - return mPosition; + return mCurrentIndices.headerIdx; } private: - size_t mPosition; - size_t mSize; ParentType const* mParent; + DataRefIndices mCurrentIndices; ElementType mElement; }; /// @class InputSpanIterator - /// An iterator over the input slots - /// It supports an iterator interface to access the parts in the slot + /// An iterator over the input slots. + /// It supports an iterator interface to access the parts in the slot. template class InputSpanIterator : public Iterator { @@ -192,24 +201,26 @@ class InputSpan using iterator = Iterator; using const_iterator = Iterator; - InputSpanIterator(InputSpan const* parent, size_t position = 0, size_t size = 0) - : BaseType(parent, position, size) + InputSpanIterator(InputSpan const* parent, bool isEnd = false) + : BaseType(parent, isEnd) { } - /// Get element at {slotindex, partindex} - [[nodiscard]] ElementType get(size_t pos) const + /// Initial indices for part-level iteration: first part starts at {headerIdx=0, payloadIdx=1}. + [[nodiscard]] DataRefIndices initialIndices() const { return {0, 1}; } + /// Sentinel used by nextIndicesGetter to signal end-of-slot. + [[nodiscard]] DataRefIndices endIndices() const { return {size_t(-1), size_t(-1)}; } + + /// Get element at the given raw message indices in O(1). + [[nodiscard]] ElementType getAtIndices(DataRefIndices indices) const { - return this->parent()->get(this->position(), pos); + return this->parent()->getAtIndices(this->position(), indices); } - /// Check if slot is valid, index of part is not used - [[nodiscard]] bool isValid(size_t = 0) const + /// Advance @a current to the next part's indices in O(1). + [[nodiscard]] DataRefIndices nextIndices(DataRefIndices current) const { - if (this->position() < this->parent()->size()) { - return this->parent()->isValid(this->position()); - } - return false; + return this->parent()->nextIndices(this->position(), current); } /// Get number of parts in input slot @@ -218,15 +229,14 @@ class InputSpan return this->parent()->getNofParts(this->position()); } - // iterator for the part access [[nodiscard]] const_iterator begin() const { - return const_iterator(this, 0, size()); + return const_iterator(this, size() == 0); } [[nodiscard]] const_iterator end() const { - return const_iterator(this, size()); + return const_iterator(this, true); } }; @@ -236,19 +246,19 @@ class InputSpan // supporting read-only access and returning const_iterator [[nodiscard]] const_iterator begin() const { - return {this, 0, size()}; + return {this, false}; } - // supporting read-only access and returning const_iterator [[nodiscard]] const_iterator end() const { - return {this, size()}; + return {this, true}; } private: - std::function mGetter; std::function mNofPartsGetter; std::function mRefCountGetter; + std::function mIndicesGetter; + std::function mNextIndicesGetter; size_t mSize; }; diff --git a/Framework/Core/src/CompletionPolicyHelpers.cxx b/Framework/Core/src/CompletionPolicyHelpers.cxx index 2b49b8dfa9acd..cc593ee7a2ed9 100644 --- a/Framework/Core/src/CompletionPolicyHelpers.cxx +++ b/Framework/Core/src/CompletionPolicyHelpers.cxx @@ -325,9 +325,9 @@ CompletionPolicy CompletionPolicyHelpers::consumeWhenAnyWithAllConditions(const // But I don't see any possibility to handle this in a better way. // Iterate on all specs and all inputs simultaneously - for (size_t i = 0; i < inputs.size(); ++i) { - char const* header = inputs.header(i); - auto& spec = specs[i]; + for (auto it = inputs.begin(), end = inputs.end(); it != end; ++it) { + char const* header = (*it).header; + auto& spec = specs[it.position()]; // In case a condition object is not there, we need to wait. if (header != nullptr) { canConsume = true; diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index 6b90747550278..b062f2bf68a75 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -2133,25 +2133,24 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v } else { currentSetOfInputs = relayer.consumeExistingInputsForTimeslice(slot); } - auto getter = [¤tSetOfInputs](size_t i, size_t partindex) -> DataRef { - if ((currentSetOfInputs[i] | count_payloads{}) > partindex) { - const char* headerptr = nullptr; - const char* payloadptr = nullptr; - size_t payloadSize = 0; - // - each input can have multiple parts - // - "part" denotes a sequence of messages belonging together, the first message of the - // sequence is the header message - // - each part has one or more payload messages - // - InputRecord provides all payloads as header-payload pair - auto const indices = currentSetOfInputs[i] | get_pair{partindex}; - auto const& headerMsg = currentSetOfInputs[i][indices.headerIdx]; - auto const& payloadMsg = currentSetOfInputs[i][indices.payloadIdx]; - headerptr = static_cast(headerMsg->GetData()); - payloadptr = payloadMsg ? static_cast(payloadMsg->GetData()) : nullptr; - payloadSize = payloadMsg ? payloadMsg->GetSize() : 0; - return DataRef{nullptr, headerptr, payloadptr, payloadSize}; + // Convert raw message indices directly to a DataRef in O(1). + // Used both by the sequential PartIterator and as the fallback for positional access. + auto indicesGetter = [¤tSetOfInputs](size_t i, DataRefIndices indices) -> DataRef { + auto const& msgs = currentSetOfInputs[i]; + if (msgs.size() <= indices.headerIdx) { + return DataRef{}; } - return DataRef{}; + auto const& headerMsg = msgs[indices.headerIdx]; + char const* payloadData = nullptr; + size_t payloadSize = 0; + if (msgs.size() > indices.payloadIdx && msgs[indices.payloadIdx]) { + payloadData = static_cast(msgs[indices.payloadIdx]->GetData()); + payloadSize = msgs[indices.payloadIdx]->GetSize(); + } + return DataRef{nullptr, + headerMsg ? static_cast(headerMsg->GetData()) : nullptr, + payloadData, + payloadSize}; }; auto nofPartsGetter = [¤tSetOfInputs](size_t i) -> size_t { return (currentSetOfInputs[i] | count_payloads{}); @@ -2160,7 +2159,11 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v auto& header = static_cast(*(currentSetOfInputs[idx] | get_header{0})); return header.GetRefCount(); }; - return InputSpan{getter, nofPartsGetter, refCountGetter, currentSetOfInputs.size()}; + auto nextIndicesGetter = [¤tSetOfInputs](size_t i, DataRefIndices current) -> DataRefIndices { + auto next = currentSetOfInputs[i] | get_next_pair{current}; + return next.headerIdx < currentSetOfInputs[i].size() ? next : DataRefIndices{size_t(-1), size_t(-1)}; + }; + return InputSpan{nofPartsGetter, refCountGetter, indicesGetter, nextIndicesGetter, currentSetOfInputs.size()}; }; auto markInputsAsDone = [ref](TimesliceSlot slot) -> void { diff --git a/Framework/Core/src/DataRelayer.cxx b/Framework/Core/src/DataRelayer.cxx index fc9966ffad643..7adf5b5c97fbb 100644 --- a/Framework/Core/src/DataRelayer.cxx +++ b/Framework/Core/src/DataRelayer.cxx @@ -212,18 +212,6 @@ DataRelayer::ActivityStats DataRelayer::processDanglingInputs(std::vector(header->GetData()), - reinterpret_cast(payload ? payload->GetData() : nullptr), - payload ? payload->GetSize() : 0}; - } - return DataRef{}; - }; auto nPartsGetter = [&partial](size_t idx) { return partial[idx] | count_parts{}; }; @@ -231,7 +219,24 @@ DataRelayer::ActivityStats DataRelayer::processDanglingInputs(std::vector(*(partial[idx] | get_header{0})); return header.GetRefCount(); }; - InputSpan span{getter, nPartsGetter, refCountGetter, static_cast(partial.size())}; + auto indicesGetter = [&partial](size_t idx, DataRefIndices indices) -> DataRef { + if (!partial[idx].empty()) { + auto const& headerMsg = partial[idx][indices.headerIdx]; + auto const& payloadMsg = partial[idx][indices.payloadIdx]; + if (headerMsg) { + return DataRef{nullptr, + reinterpret_cast(headerMsg->GetData()), + payloadMsg ? reinterpret_cast(payloadMsg->GetData()) : nullptr, + payloadMsg ? payloadMsg->GetSize() : 0}; + } + } + return DataRef{}; + }; + auto nextIndicesGetter = [&partial](size_t idx, DataRefIndices current) -> DataRefIndices { + auto next = partial[idx] | get_next_pair{current}; + return next.headerIdx < partial[idx].size() ? next : DataRefIndices{size_t(-1), size_t(-1)}; + }; + InputSpan span{nPartsGetter, refCountGetter, indicesGetter, nextIndicesGetter, static_cast(partial.size())}; // Setup the input span if (expirator.checker(services, timestamp.value, span) == false) { @@ -789,18 +794,6 @@ void DataRelayer::getReadyToProcess(std::vector& comp throw runtime_error_f("Completion police %s has no callback set", mCompletionPolicy.name.c_str()); } auto partial = getPartialRecord(li); - // TODO: get the data ref from message model - auto getter = [&partial](size_t idx, size_t part) { - if (!partial[idx].empty() && (partial[idx] | get_header{part}).get()) { - auto header = (partial[idx] | get_header{part}).get(); - auto payload = (partial[idx] | get_payload{part, 0}).get(); - return DataRef{nullptr, - reinterpret_cast(header->GetData()), - reinterpret_cast(payload ? payload->GetData() : nullptr), - payload ? payload->GetSize() : 0}; - } - return DataRef{}; - }; auto nPartsGetter = [&partial](size_t idx) { return partial[idx] | count_parts{}; }; @@ -808,7 +801,24 @@ void DataRelayer::getReadyToProcess(std::vector& comp auto& header = static_cast(*(partial[idx] | get_header{0})); return header.GetRefCount(); }; - InputSpan span{getter, nPartsGetter, refCountGetter, static_cast(partial.size())}; + auto indicesGetter = [&partial](size_t idx, DataRefIndices indices) -> DataRef { + if (!partial[idx].empty()) { + auto const& headerMsg = partial[idx][indices.headerIdx]; + auto const& payloadMsg = partial[idx][indices.payloadIdx]; + if (headerMsg) { + return DataRef{nullptr, + reinterpret_cast(headerMsg->GetData()), + payloadMsg ? reinterpret_cast(payloadMsg->GetData()) : nullptr, + payloadMsg ? payloadMsg->GetSize() : 0}; + } + } + return DataRef{}; + }; + auto nextIndicesGetter = [&partial](size_t idx, DataRefIndices current) -> DataRefIndices { + auto next = partial[idx] | get_next_pair{current}; + return next.headerIdx < partial[idx].size() ? next : DataRefIndices{size_t(-1), size_t(-1)}; + }; + InputSpan span{nPartsGetter, refCountGetter, indicesGetter, nextIndicesGetter, static_cast(partial.size())}; CompletionPolicy::CompletionOp action = mCompletionPolicy.callbackFull(span, mInputs, mContext); auto& variables = mTimesliceIndex.getVariablesForSlot(slot); diff --git a/Framework/Core/src/InputRecord.cxx b/Framework/Core/src/InputRecord.cxx index 18b341704ffcb..7bc9907b13ba4 100644 --- a/Framework/Core/src/InputRecord.cxx +++ b/Framework/Core/src/InputRecord.cxx @@ -139,6 +139,16 @@ size_t InputRecord::getNofParts(int pos) const } return mSpan.getNofParts(pos); } + +DataRef InputRecord::getAtIndices(int pos, DataRefIndices indices) const +{ + auto ref = mSpan.getAtIndices(pos, indices); + if (pos >= 0 && pos < (int)mInputsSchema.size()) { + ref.spec = &mInputsSchema[pos].matcher; + } + return ref; +} + size_t InputRecord::size() const { return mSpan.size(); diff --git a/Framework/Core/src/InputSpan.cxx b/Framework/Core/src/InputSpan.cxx index d1dffc85602a5..ccea2d1dd66ed 100644 --- a/Framework/Core/src/InputSpan.cxx +++ b/Framework/Core/src/InputSpan.cxx @@ -11,29 +11,17 @@ #include "Framework/InputSpan.h" -template class std::function; -template class std::function; +template class std::function; +template class std::function; namespace o2::framework { -InputSpan::InputSpan(std::function getter, size_t size) - : mGetter{}, mNofPartsGetter{}, mSize{size} -{ - mGetter = [getter](size_t index, size_t) -> DataRef { - return getter(index); - }; -} - -InputSpan::InputSpan(std::function getter, size_t size) - : mGetter{getter}, mNofPartsGetter{}, mSize{size} -{ -} - -InputSpan::InputSpan(std::function getter, - std::function nofPartsGetter, +InputSpan::InputSpan(std::function nofPartsGetter, std::function refCountGetter, + std::function indicesGetter, + std::function nextIndicesGetter, size_t size) - : mGetter{getter}, mNofPartsGetter{nofPartsGetter}, mRefCountGetter(refCountGetter), mSize{size} + : mNofPartsGetter{nofPartsGetter}, mRefCountGetter(refCountGetter), mIndicesGetter{std::move(indicesGetter)}, mNextIndicesGetter{std::move(nextIndicesGetter)}, mSize{size} { } diff --git a/Framework/Core/test/benchmark_InputRecord.cxx b/Framework/Core/test/benchmark_InputRecord.cxx index 69fc3c970c1e1..e3ec00ac815ed 100644 --- a/Framework/Core/test/benchmark_InputRecord.cxx +++ b/Framework/Core/test/benchmark_InputRecord.cxx @@ -47,7 +47,12 @@ static void BM_InputRecordGenericGetters(benchmark::State& state) createRoute("z_source", spec3)}; // First of all we test if an empty registry behaves as expected, raising a // bunch of exceptions. - InputSpan span{[](size_t) { return DataRef{nullptr, nullptr, nullptr}; }, 0}; + InputSpan span{ + [](size_t) -> size_t { return 0; }, + nullptr, + [](size_t, DataRefIndices) { return DataRef{nullptr, nullptr, nullptr}; }, + [](size_t, DataRefIndices) -> DataRefIndices { return {size_t(-1), size_t(-1)}; }, + 0}; ServiceRegistry registry; InputRecord emptyRecord(schema, span, registry); @@ -82,7 +87,12 @@ static void BM_InputRecordGenericGetters(benchmark::State& state) createMessage(dh1, 1); createMessage(dh2, 2); createEmpty(); - InputSpan span2{[&inputs](size_t i) { return DataRef{nullptr, static_cast(inputs[2 * i]), static_cast(inputs[2 * i + 1])}; }, inputs.size() / 2}; + InputSpan span2{ + [](size_t) -> size_t { return 1; }, + nullptr, + [&inputs](size_t i, DataRefIndices idx) { return DataRef{nullptr, static_cast(inputs[2 * i + idx.headerIdx]), static_cast(inputs[2 * i + idx.payloadIdx])}; }, + [](size_t, DataRefIndices) -> DataRefIndices { return {size_t(-1), size_t(-1)}; }, + inputs.size() / 2}; InputRecord record{schema, span2, registry}; for (auto _ : state) { diff --git a/Framework/Core/test/test_CompletionPolicy.cxx b/Framework/Core/test/test_CompletionPolicy.cxx index 059f20b352b3d..cc16ba95ba8f2 100644 --- a/Framework/Core/test/test_CompletionPolicy.cxx +++ b/Framework/Core/test/test_CompletionPolicy.cxx @@ -55,7 +55,12 @@ TEST_CASE("TestCompletionPolicy_callback") std::vector policies{ {"test", matcher, callback}}; CompletionPolicy::InputSetElement ref{nullptr, reinterpret_cast(stack.data()), nullptr}; - InputSpan const& inputs{[&ref](size_t) { return ref; }, 1}; + InputSpan const inputs{ + [](size_t) -> size_t { return 1; }, + nullptr, + [&ref](size_t, DataRefIndices) -> DataRef { return ref; }, + [](size_t, DataRefIndices) -> DataRefIndices { return {size_t(-1), size_t(-1)}; }, + 1}; std::vector specs; ServiceRegistryRef servicesRef{services}; for (auto& policy : policies) { diff --git a/Framework/Core/test/test_InputRecord.cxx b/Framework/Core/test/test_InputRecord.cxx index 4eb1265dcff53..355e52539ea5a 100644 --- a/Framework/Core/test/test_InputRecord.cxx +++ b/Framework/Core/test/test_InputRecord.cxx @@ -47,7 +47,10 @@ TEST_CASE("TestInputRecord") // First of all we test if an empty registry behaves as expected, raising a // bunch of exceptions. InputSpan span{ - [](size_t) { return DataRef{nullptr, nullptr, nullptr}; }, + [](size_t) -> size_t { return 0; }, + nullptr, + [](size_t, DataRefIndices) { return DataRef{nullptr, nullptr, nullptr}; }, + [](size_t, DataRefIndices) -> DataRefIndices { return {size_t(-1), size_t(-1)}; }, 0}; ServiceRegistry registry; InputRecord emptyRecord(schema, span, registry); @@ -91,7 +94,12 @@ TEST_CASE("TestInputRecord") createMessage(dh1, 1); createMessage(dh2, 2); createEmpty(); - InputSpan span2{[&inputs](size_t i) { return DataRef{nullptr, static_cast(inputs[2 * i]), static_cast(inputs[2 * i + 1])}; }, inputs.size() / 2}; + InputSpan span2{ + [](size_t) -> size_t { return 1; }, + nullptr, + [&inputs](size_t i, DataRefIndices idx) { return DataRef{nullptr, static_cast(inputs[2 * i + idx.headerIdx]), static_cast(inputs[2 * i + idx.payloadIdx])}; }, + [](size_t, DataRefIndices) -> DataRefIndices { return {size_t(-1), size_t(-1)}; }, + inputs.size() / 2}; InputRecord record{schema, span2, registry}; // Checking we can get the whole ref by name diff --git a/Framework/Core/test/test_InputRecordWalker.cxx b/Framework/Core/test/test_InputRecordWalker.cxx index 9af3c0dd2dbe2..1fcfea1ba1587 100644 --- a/Framework/Core/test/test_InputRecordWalker.cxx +++ b/Framework/Core/test/test_InputRecordWalker.cxx @@ -35,16 +35,12 @@ struct DataSet { using Messages = std::vector; using CheckType = std::vector; DataSet(std::vector&& s, Messages&& m, CheckType&& v, ServiceRegistryRef registry) - : schema{std::move(s)}, messages{std::move(m)}, span{[this](size_t i, size_t part) { - REQUIRE(i < this->messages.size()); - REQUIRE(part < this->messages[i].second.size() / 2); - auto header = static_cast(this->messages[i].second.at(2 * part)->data()); - auto payload = static_cast(this->messages[i].second.at(2 * part + 1)->data()); - return DataRef{nullptr, header, payload}; - }, - [this](size_t i) { return i < this->messages.size() ? messages[i].second.size() / 2 : 0; }, nullptr, this->messages.size()}, - record{schema, span, registry}, - values{std::move(v)} + : schema{std::move(s)}, messages{std::move(m)}, span{[this](size_t i) { return i < this->messages.size() ? messages[i].second.size() / 2 : 0; }, nullptr, [this](size_t i, DataRefIndices idx) { + auto header = static_cast(this->messages[i].second.at(idx.headerIdx)->data()); + auto payload = static_cast(this->messages[i].second.at(idx.payloadIdx)->data()); + return DataRef{nullptr, header, payload}; }, [this](size_t i, DataRefIndices current) -> DataRefIndices { + size_t next = current.headerIdx + 2; + return next < this->messages[i].second.size() ? DataRefIndices{next, next + 1} : DataRefIndices{size_t(-1), size_t(-1)}; }, this->messages.size()}, record{schema, span, registry}, values{std::move(v)} { REQUIRE(messages.size() == schema.size()); } diff --git a/Framework/Core/test/test_InputSpan.cxx b/Framework/Core/test/test_InputSpan.cxx index c5682aea80b6c..dc31085e741fd 100644 --- a/Framework/Core/test/test_InputSpan.cxx +++ b/Framework/Core/test/test_InputSpan.cxx @@ -30,14 +30,18 @@ TEST_CASE("TestInputSpan") routeNo++; } - auto getter = [&inputs](size_t i, size_t part) { - return DataRef{nullptr, inputs[i].at(part * 2).data(), inputs[i].at(part * 2 + 1).data()}; - }; auto nPartsGetter = [&inputs](size_t i) { return inputs[i].size() / 2; }; + auto indicesGetter = [&inputs](size_t i, DataRefIndices indices) { + return DataRef{nullptr, inputs[i].at(indices.headerIdx).data(), inputs[i].at(indices.payloadIdx).data()}; + }; + auto nextIndicesGetter = [&inputs](size_t i, DataRefIndices current) -> DataRefIndices { + size_t next = current.headerIdx + 2; + return next < inputs[i].size() ? DataRefIndices{next, next + 1} : DataRefIndices{size_t(-1), size_t(-1)}; + }; - InputSpan span{getter, nPartsGetter, nullptr, inputs.size()}; + InputSpan span{nPartsGetter, nullptr, indicesGetter, nextIndicesGetter, inputs.size()}; REQUIRE(span.size() == inputs.size()); routeNo = 0; for (; routeNo < span.size(); ++routeNo) { diff --git a/Framework/Utils/test/RawPageTestData.h b/Framework/Utils/test/RawPageTestData.h index a6b800f7cba32..29ac4eeba6b5b 100644 --- a/Framework/Utils/test/RawPageTestData.h +++ b/Framework/Utils/test/RawPageTestData.h @@ -42,13 +42,17 @@ struct DataSet { DataSet(std::vector&& s, Messages&& m, std::vector&& v, ServiceRegistryRef registry) : schema{std::move(s)}, messages{std::move(m)}, - span{[this](size_t i, size_t part) { - auto header = static_cast(this->messages[i].at(2 * part)->data()); - auto payload = static_cast(this->messages[i].at(2 * part + 1)->data()); + span{[this](size_t i) { return i < this->messages.size() ? messages[i].size() / 2 : 0; }, + nullptr, + [this](size_t i, DataRefIndices idx) { + auto header = static_cast(this->messages[i].at(idx.headerIdx)->data()); + auto payload = static_cast(this->messages[i].at(idx.payloadIdx)->data()); return DataRef{nullptr, header, payload}; }, - [this](size_t i) { return i < this->messages.size() ? messages[i].size() / 2 : 0; }, - nullptr, + [this](size_t i, DataRefIndices current) -> DataRefIndices { + size_t next = current.headerIdx + 2; + return next < this->messages[i].size() ? DataRefIndices{next, next + 1} : DataRefIndices{size_t(-1), size_t(-1)}; + }, this->messages.size()}, record{schema, span, registry}, values{std::move(v)} diff --git a/Framework/Utils/test/test_RootTreeWriter.cxx b/Framework/Utils/test/test_RootTreeWriter.cxx index 62e1eb62cb4f1..e372fb4e1302e 100644 --- a/Framework/Utils/test/test_RootTreeWriter.cxx +++ b/Framework/Utils/test/test_RootTreeWriter.cxx @@ -224,10 +224,14 @@ TEST_CASE("test_RootTreeWriter") {InputSpec{"input8", "TST", "SRLZDVEC"}, 7, "input8", 0}, // }; - auto getter = [&store](size_t i) -> DataRef { - return DataRef{nullptr, static_cast(store[2 * i]->GetData()), static_cast(store[2 * i + 1]->GetData())}; - }; - InputSpan span{getter, store.size() / 2}; + InputSpan span{ + [](size_t) -> size_t { return 1; }, + nullptr, + [&store](size_t i, DataRefIndices idx) -> DataRef { + return DataRef{nullptr, static_cast(store[2 * i + idx.headerIdx]->GetData()), static_cast(store[2 * i + idx.payloadIdx]->GetData())}; + }, + [](size_t, DataRefIndices) -> DataRefIndices { return {size_t(-1), size_t(-1)}; }, + store.size() / 2}; ServiceRegistry registry; InputRecord inputs{ schema, diff --git a/Utilities/DataSampling/src/Dispatcher.cxx b/Utilities/DataSampling/src/Dispatcher.cxx index 3ff0ba661fd93..22dd457a1211a 100644 --- a/Utilities/DataSampling/src/Dispatcher.cxx +++ b/Utilities/DataSampling/src/Dispatcher.cxx @@ -122,7 +122,7 @@ void Dispatcher::run(ProcessingContext& ctx) for (auto inputIt = ctx.inputs().begin(); inputIt != ctx.inputs().end(); inputIt++) { - const DataRef& firstPart = inputIt.getByPos(0); + const DataRef& firstPart = *inputIt; if (firstPart.header == nullptr) { continue; } From 3df669bc765bdca795e17fa23081633d0d146173 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Wed, 8 Apr 2026 10:10:25 +0200 Subject: [PATCH 049/285] [ALICE3] TRK: Add TrackerACTS header for ACTS-based tracking (#15263) * Add ACTS support for tracking in TrackerSpec --- .../ALICE3/TRK/reconstruction/CMakeLists.txt | 4 +- .../include/TRKReconstruction/TrackerACTS.h | 189 +++++++++++ .../TRK/reconstruction/src/TrackerACTS.cxx | 306 ++++++++++++++++++ .../ALICE3/TRK/workflow/src/TrackerSpec.cxx | 30 +- 4 files changed, 524 insertions(+), 5 deletions(-) create mode 100644 Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TrackerACTS.h create mode 100644 Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt index 81a75e209124a..59a7f47955938 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt @@ -18,6 +18,7 @@ o2_add_library(TRKReconstruction SOURCES src/TimeFrame.cxx src/Clusterer.cxx $<$:src/ClustererACTS.cxx> + $<$:src/TrackerACTS.cxx> PUBLIC_LINK_LIBRARIES O2::ITStracking O2::GPUCommon @@ -45,7 +46,8 @@ set(dictHeaders include/TRKReconstruction/TimeFrame.h include/TRKReconstruction/Clusterer.h) if(Acts_FOUND) - list(APPEND dictHeaders include/TRKReconstruction/ClustererACTS.h) + list(APPEND dictHeaders include/TRKReconstruction/ClustererACTS.h + include/TRKReconstruction/TrackerACTS.h) endif() o2_target_root_dictionary(TRKReconstruction diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TrackerACTS.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TrackerACTS.h new file mode 100644 index 0000000000000..2910abf480961 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TrackerACTS.h @@ -0,0 +1,189 @@ +// 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 TrackerACTS.h +/// \brief TRK tracker using ACTS seeding algorithm +/// \author Nicolò Jacazio, Università del Piemonte Orientale (IT) +/// \since 2026-04-01 +/// + +#ifndef ALICE3_INCLUDE_TRACKERACTS_H_ +#define ALICE3_INCLUDE_TRACKERACTS_H_ + +#include "Acts/Definitions/Units.hpp" +#include "Framework/Logger.h" + +#include "ITStracking/TimeFrame.h" +#include "TH2F.h" + +namespace o2::trk +{ + +/// Configuration for the ACTS-based tracker +struct TrackerACTSConfig { + // Seeding parameters + float minPt = 0.4f * Acts::UnitConstants::GeV; ///< Minimum pT for seeds + float maxImpactParameter = 10.f * Acts::UnitConstants::mm; ///< Maximum impact parameter + float cotThetaMax = std::sinh(4.0f); ///< Maximum cot(theta), corresponds to eta ~4 + + // Delta R cuts for doublet/triplet formation + float deltaRMinBottom = 5.f * Acts::UnitConstants::mm; ///< Min deltaR for bottom-middle + float deltaRMaxBottom = 200.f * Acts::UnitConstants::mm; ///< Max deltaR for bottom-middle + float deltaRMinTop = 5.f * Acts::UnitConstants::mm; ///< Min deltaR for middle-top + float deltaRMaxTop = 200.f * Acts::UnitConstants::mm; ///< Max deltaR for middle-top + + // Z cuts + float zMin = -3000.f * Acts::UnitConstants::mm; + float zMax = 3000.f * Acts::UnitConstants::mm; + + // Collision region + float collisionRegionMin = -150.f * Acts::UnitConstants::mm; + float collisionRegionMax = 150.f * Acts::UnitConstants::mm; + + // Quality cuts + float maxSeedsPerMiddleSP = 2; + float deltaPhiMax = 0.1f; ///< Maximum phi difference for doublets +}; + +/// Space point representation for tracking +struct SpacePoint { + float x{0.f}; + float y{0.f}; + float z{0.f}; + int layer{-1}; + int clusterId{-1}; + int rof{-1}; + + // Derived quantities + float r() const { return std::hypot(x, y); } + float radius() const { return r(); } // required by Acts::CylindricalGridElement concept + float phi() const { return std::atan2(y, x); } + + // Variance estimates (can be refined based on cluster properties) + float varianceR{0.01f}; // ~100 um resolution squared + float varianceZ{0.01f}; +}; + +/// Seed (triplet of space points) +struct SeedACTS { + const SpacePoint* bottom{nullptr}; + const SpacePoint* middle{nullptr}; + const SpacePoint* top{nullptr}; + float quality{0.f}; +}; + +/// TRK Tracker using ACTS algorithms for seeding and track finding +template +class TrackerACTS +{ + public: + TrackerACTS(); + ~TrackerACTS() + { + if (mHistSpacePoints) { + mHistSpacePoints->SaveAs("/tmp/mHistSpacePoints.C"); + delete mHistSpacePoints; + mHistSpacePoints = nullptr; + } + } + + /// Adopt a TimeFrame for processing + void adoptTimeFrame(o2::its::TimeFrame& tf); + + /// Main tracking entry point: convert clusters to tracks + void clustersToTracks(); + + /// Configuration + void setConfig(const TrackerACTSConfig& cfg) { mConfig = cfg; } + TrackerACTSConfig& getConfig() { return mConfig; } + const TrackerACTSConfig& getConfig() const { return mConfig; } + + /// Set the magnetic field strength + void setBz(float bz) { mBz = bz; } + + /// Get the magnetic field strength + float getBz() const { return mBz; } + + /// Print tracking summary + void printSummary() const; + + private: + TH2F* mHistSpacePoints = nullptr; + + /// Build space points from clusters in the TimeFrame + void buildSpacePoints(int rof); + + /// Create seeds (triplets) from space points using ACTS SeedFinder + void createSeeds(); + + /// Estimate track parameters from a seed using ACTS + bool estimateTrackParams(const SeedACTS& seed, o2::its::TrackITSExt& track) const; + + /// Run track finding from seeds + void findTracks(); + + /// Assign MC labels to tracks + void computeTracksMClabels(); + + /// Helper: time a task + template + float evaluateTask(Func&& task, std::string_view taskName); + + // Configuration + TrackerACTSConfig mConfig; + + // TimeFrame data + o2::its::TimeFrame* mTimeFrame = nullptr; + + // Space points built from clusters + std::vector mSpacePoints; + std::vector> mSpacePointsPerLayer; + + // Seeds + std::vector mSeeds; + + // Tracking state + float mBz{0.5f}; ///< Magnetic field in Tesla + unsigned int mTimeFrameCounter{0}; + double mTotalTime{0.}; + + // Tracking states for logging + enum State { + SpacePointBuilding = 0, + Seeding, + TrackFinding, + NStates, + }; + State mCurState{SpacePointBuilding}; + static constexpr std::array StateNames{ + "Space point building", + "ACTS seeding", + "Track finding"}; +}; + +template +template +float TrackerACTS::evaluateTask(Func&& task, std::string_view taskName) +{ + LOG(debug) << " + Starting " << taskName; + const auto start = std::chrono::high_resolution_clock::now(); + task(); + const auto end = std::chrono::high_resolution_clock::now(); + std::chrono::duration diff{end - start}; + + LOG(debug) << " - " << taskName << " completed in: " << std::fixed << std::setprecision(2) << diff.count() << " ms"; + return static_cast(diff.count()); +} + +} // namespace o2::trk + +#endif /* ALICE3_INCLUDE_TRACKERACTS_H_ */ diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx new file mode 100644 index 0000000000000..67dcfe25e33bb --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx @@ -0,0 +1,306 @@ +// 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 TrackerACTS.cxx +/// \brief TRK tracker using ACTS seeding and track finding +/// \author Nicolò Jacazio, Università del Piemonte Orientale (IT) +/// \since 2026-04-01 +/// + +#include "TRKReconstruction/TrackerACTS.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace o2::trk +{ + +template +TrackerACTS::TrackerACTS() +{ + // Initialize space points storage per layer + mSpacePointsPerLayer.resize(nLayers); + mHistSpacePoints = new TH2F("hSpacePoints", "Space points; x (cm); y (cm)", 200, -100, 100, 200, -100, 100); +} + +template +void TrackerACTS::adoptTimeFrame(o2::its::TimeFrame& tf) +{ + mTimeFrame = &tf; +} + +template +void TrackerACTS::buildSpacePoints(int rof) +{ + mSpacePoints.clear(); + for (auto& layerSPs : mSpacePointsPerLayer) { + layerSPs.clear(); + } + + // Get clusters from the TimeFrame and convert to space points + for (int layer = 0; layer < nLayers; ++layer) { + // For now we take unsorted clusters, as soon as the cluster trackin is in place we can piggy back on it and switch to the clusters + auto clusters = mTimeFrame->getUnsortedClusters()[layer]; + // Resize the clusters to the first 100 clusters for testing + // clusters = clusters.subspan(0, std::min(clusters.size(), 100)); + LOG(debug) << "ACTSTracker: got " << clusters.size() << " clusters"; + + for (size_t iCluster = 0; iCluster < clusters.size(); ++iCluster) { + const auto& cluster = clusters[iCluster]; + + SpacePoint sp; + // Check that these are in global coordinates + sp.x = cluster.xCoordinate * Acts::UnitConstants::cm; + sp.y = cluster.yCoordinate * Acts::UnitConstants::cm; + sp.z = cluster.zCoordinate * Acts::UnitConstants::cm; + + if (mHistSpacePoints) { + mHistSpacePoints->Fill(sp.x / Acts::UnitConstants::cm, sp.y / Acts::UnitConstants::cm); + } + sp.layer = layer; + sp.clusterId = static_cast(iCluster); + sp.rof = rof; + + // Position uncertainties (could be refined based on cluster properties) + sp.varianceR = 0.01f; // ~100 um resolution squared + sp.varianceZ = 0.01f; + + mSpacePoints.push_back(sp); + } + } + + // Build per-layer pointers for seeding + for (auto& sp : mSpacePoints) { + if (sp.layer >= 0 && sp.layer < nLayers) { + mSpacePointsPerLayer[sp.layer].push_back(&sp); + } + } +} + +template +void TrackerACTS::createSeeds() +{ + if (mSpacePoints.empty()) { + LOGF(info, "No space points available for seeding"); + return; + } + mSeeds.clear(); + + // Backend adaptor that exposes mSpacePoints to Acts::SpacePointContainer + struct SpacePointBackend { + using ValueType = SpacePoint; + explicit SpacePointBackend(const std::vector& sps) : m_sps{&sps} {} + std::size_t size_impl() const { return m_sps->size(); } + float x_impl(std::size_t i) const { return (*m_sps)[i].x; } + float y_impl(std::size_t i) const { return (*m_sps)[i].y; } + float z_impl(std::size_t i) const { return (*m_sps)[i].z; } + float varianceR_impl(std::size_t i) const { return (*m_sps)[i].varianceR; } + float varianceZ_impl(std::size_t i) const { return (*m_sps)[i].varianceZ; } + const SpacePoint& get_impl(std::size_t i) const { return (*m_sps)[i]; } + std::any component_impl(Acts::HashedString /*key*/, std::size_t /*i*/) const + { + LOG(fatal) << "No additional components available for space points"; + throw std::runtime_error("SpacePointBackend: no strip component available"); + } + const std::vector* m_sps; + }; + + // Wrap mSpacePoints in an Acts space-point container + SpacePointBackend backend{mSpacePoints}; + + // Configure the ACTS space point container + Acts::SpacePointContainerConfig spContainerConfig; + Acts::SpacePointContainerOptions spContainerOpts; + spContainerOpts.beamPos = {0.f, 0.f}; + Acts::SpacePointContainer spContainer{spContainerConfig, spContainerOpts, backend}; + + // Configure the ACTS seed finder + const unsigned int maxSeeds = static_cast(mConfig.maxSeedsPerMiddleSP); + Acts::SeedFilterConfig filterConfig; + filterConfig.maxSeedsPerSpM = maxSeeds; + + // ACTS requires minPt / bFieldInZ >= rMax / 2 (minHelixRadius >= rMax/2). + // Cap rMax so that the constraint is satisfied for the configured minPt and field. + const float bFieldInZ = mBz * Acts::UnitConstants::T; + const float safeRMax = 1.8f * mConfig.minPt / bFieldInZ; // 10% margin below the hard limit + + using SPProxy = typename Acts::SpacePointContainer::SpacePointProxyType; + Acts::SeedFinderConfig finderConfig; + finderConfig.rMin = 0.f; + finderConfig.rMax = 100.f * Acts::UnitConstants::cm; + finderConfig.zMin = mConfig.zMin; + finderConfig.zMax = mConfig.zMax; + finderConfig.deltaRMin = std::min(mConfig.deltaRMinBottom, mConfig.deltaRMinTop); + finderConfig.deltaRMax = std::max(mConfig.deltaRMaxBottom, mConfig.deltaRMaxTop); + finderConfig.deltaRMinBottomSP = mConfig.deltaRMinBottom; + finderConfig.deltaRMaxBottomSP = mConfig.deltaRMaxBottom; + finderConfig.deltaRMinTopSP = mConfig.deltaRMinTop; + finderConfig.deltaRMaxTopSP = mConfig.deltaRMaxTop; + finderConfig.collisionRegionMin = mConfig.collisionRegionMin; + finderConfig.collisionRegionMax = mConfig.collisionRegionMax; + finderConfig.cotThetaMax = mConfig.cotThetaMax; + finderConfig.minPt = mConfig.minPt; + finderConfig.impactMax = mConfig.maxImpactParameter; + finderConfig.maxSeedsPerSpM = maxSeeds; + finderConfig.sigmaScattering = 5.f; + finderConfig.radLengthPerSeed = 0.05f; + finderConfig.seedFilter = std::make_shared>(filterConfig); + finderConfig = finderConfig.calculateDerivedQuantities(); + Acts::SeedFinder> seedFinder{finderConfig, + Acts::getDefaultLogger("Finder", Acts::Logging::Level::VERBOSE)}; + + // Configure and create the cylindrical space-point grid + Acts::CylindricalSpacePointGridConfig gridConfig; + gridConfig.minPt = finderConfig.minPt; + gridConfig.rMin = finderConfig.rMin; + gridConfig.rMax = finderConfig.rMax; + gridConfig.zMin = finderConfig.zMin; + gridConfig.zMax = finderConfig.zMax; + gridConfig.deltaRMax = finderConfig.deltaRMax; + gridConfig.cotThetaMax = finderConfig.cotThetaMax; + gridConfig.impactMax = finderConfig.impactMax; + + Acts::CylindricalSpacePointGridOptions gridOpts; + gridOpts.bFieldInZ = bFieldInZ; + + Acts::SeedFinderOptions finderOpts; + finderOpts.beamPos = spContainerOpts.beamPos; + finderOpts.bFieldInZ = gridOpts.bFieldInZ; + try { + finderOpts = finderOpts.calculateDerivedQuantities(finderConfig); + } catch (const std::exception& e) { + LOG(fatal) << "Error in seed finder configuration: " << e.what(); + return; + } + + Acts::CylindricalSpacePointGrid grid = Acts::CylindricalSpacePointGridCreator::createGrid(gridConfig, gridOpts); + try { + Acts::CylindricalSpacePointGridCreator::fillGrid(finderConfig, finderOpts, grid, + spContainer.begin(), spContainer.end()); + } catch (const std::exception& e) { + LOG(fatal) << "Error during grid creation/filling: " << e.what(); + return; + } + LOG(debug) << "Grid created with " << grid.dimensions(); + + // Build the binned group and iterate over triplet combinations + Acts::GridBinFinder<3ul> bottomBinFinder{1, std::vector>{}, 0}; + Acts::GridBinFinder<3ul> topBinFinder{1, std::vector>{}, 0}; + Acts::CylindricalBinnedGroup spGroup{std::move(grid), bottomBinFinder, topBinFinder}; + + std::vector>> seedsPerGroup; + typename Acts::SeedFinder>::SeedingState seedingState; + seedingState.spacePointMutableData.resize(spContainer.size()); + const Acts::Range1D rMiddleSPRange; + for (auto [bottom, middle, top] : spGroup) { + auto& v = seedsPerGroup.emplace_back(); + try { + seedFinder.createSeedsForGroup(finderOpts, seedingState, spGroup.grid(), v, bottom, middle, top, rMiddleSPRange); + } catch (const std::exception& e) { + LOG(fatal) << "Error during seed finding for a group: " << e.what(); + return; + } + } + LOG(debug) << "Seed finding completed, found " << seedsPerGroup.size() << " groups with seeds"; + + // Convert Acts seeds to the internal SeedACTS representation + for (const auto& groupSeeds : seedsPerGroup) { + for (const auto& actsSeed : groupSeeds) { + SeedACTS seed; + seed.bottom = &actsSeed.sp()[0]->externalSpacePoint(); + seed.middle = &actsSeed.sp()[1]->externalSpacePoint(); + seed.top = &actsSeed.sp()[2]->externalSpacePoint(); + seed.quality = actsSeed.seedQuality(); + mSeeds.push_back(seed); + } + } + + LOGF(info, "Created %zu seeds from %zu space points", mSeeds.size(), mSpacePoints.size()); +} + +template +bool TrackerACTS::estimateTrackParams(const SeedACTS& seed, o2::its::TrackITSExt& track) const +{ + return true; +} + +template +void TrackerACTS::findTracks() +{ +} + +template +void TrackerACTS::computeTracksMClabels() +{ +} + +template +void TrackerACTS::clustersToTracks() +{ + if (!mTimeFrame) { + LOG(error) << "Cannot run TrackerACTS: No TimeFrame adopted"; + return; + } + + double totalTime = 0.; + LOG(info) << "==== TRK ACTS Tracking ===="; + LOG(info) << "Processing " << mTimeFrame->getNrof() << " ROFs with B = " << mBz << " T"; + + // Process each ROF + for (int iROF = 0; iROF < mTimeFrame->getNrof(); ++iROF) { + LOG(info) << "Processing ROF " << iROF; + // Build space points + mCurState = SpacePointBuilding; + totalTime += evaluateTask([this, iROF]() { buildSpacePoints(iROF); }, + StateNames[mCurState]); + + // Run seeding + mCurState = Seeding; + totalTime += evaluateTask([this]() { createSeeds(); }, + StateNames[mCurState]); + + // Find tracks + mCurState = TrackFinding; + totalTime += evaluateTask([this]() { findTracks(); }, + StateNames[mCurState]); + } + + // MC labeling + if (mTimeFrame->hasMCinformation()) { + computeTracksMClabels(); + } + + LOG(info) << "=== TimeFrame " << mTimeFrameCounter << " completed in: " << totalTime << " ms ==="; + + ++mTimeFrameCounter; + mTotalTime += totalTime; +} + +template +void TrackerACTS::printSummary() const +{ + float avgTF = mTimeFrameCounter > 0 ? static_cast(mTotalTime) / mTimeFrameCounter : 0.f; + LOGP(info, "TrackerACTS summary: Processed {} TFs in TOT={:.2f} ms, AVG/TF={:.2f} ms", + mTimeFrameCounter, mTotalTime, avgTF); +} + +// Explicit template instantiations +template class TrackerACTS<11>; +} // namespace o2::trk diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx index 20bd45557dac5..b587ec24775b4 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx @@ -29,6 +29,10 @@ #include "TRKWorkflow/TrackerSpec.h" #include +#ifdef O2_WITH_ACTS +#include "TRKReconstruction/TrackerACTS.h" +#endif + #include #include @@ -61,6 +65,10 @@ void TrackerDPL::init(InitContext& ic) // mITSTrackingInterface.setTraitsFromProvider(mChainITS->GetITSVertexerTraits(), // mChainITS->GetITSTrackerTraits(), // mChainITS->GetITSTimeframe()); + +#ifdef O2_WITH_ACTS + mUseACTS = ic.options().get("useACTS"); +#endif } void TrackerDPL::stop() @@ -276,14 +284,13 @@ void TrackerDPL::run(ProcessingContext& pc) itsTrackerTraits.setMemoryPool(mMemoryPool); itsTrackerTraits.setNThreads(mTaskArena->max_concurrency(), mTaskArena); itsTrackerTraits.adoptTimeFrame(static_cast*>(&timeFrame)); - itsTracker.adoptTimeFrame(timeFrame); itsTrackerTraits.setBz(mHitRecoConfig["geometry"]["bz"].get()); auto field = new field::MagneticField("ALICE3Mag", "ALICE 3 Magnetic Field", mHitRecoConfig["geometry"]["bz"].get() / 5.f, 0.0, o2::field::MagFieldParam::k5kGUniform); TGeoGlobalMagField::Instance()->SetField(field); TGeoGlobalMagField::Instance()->Lock(); + itsTracker.adoptTimeFrame(timeFrame); - int nRofs = timeFrame.loadROFsFromHitTree(hitsTree, gman, mHitRecoConfig); - + const int nRofs = timeFrame.loadROFsFromHitTree(hitsTree, gman, mHitRecoConfig); const int inROFpileup{mHitRecoConfig.contains("inROFpileup") ? mHitRecoConfig["inROFpileup"].get() : 1}; // Add primary vertices from MC headers for each ROF @@ -293,6 +300,16 @@ void TrackerDPL::run(ProcessingContext& pc) itsTrackerTraits.updateTrackingParameters(trackingParams); +#ifdef O2_WITH_ACTS + if (mUseACTS) { + LOG(info) << "Running the tracking with ACTS"; + o2::trk::TrackerACTS<11> actsTracker; + actsTracker.setBz(mHitRecoConfig["geometry"]["bz"].get()); + actsTracker.adoptTimeFrame(timeFrame); + actsTracker.clustersToTracks(); + } +#endif + const auto trackingLoopStart = std::chrono::steady_clock::now(); for (size_t iter{0}; iter < trackingParams.size(); ++iter) { LOGP(info, "{}", trackingParams[iter].asString()); @@ -391,7 +408,12 @@ DataProcessorSpec getTrackerSpec(bool useMC, const std::string& hitRecoConfig, o useMC, hitRecoConfig, dType)}, - Options{ConfigParamSpec{"max-loops", VariantType::Int, 1, {"max number of loops"}}}}; + Options{ConfigParamSpec{"max-loops", VariantType::Int, 1, {"max number of loops"}} +#ifdef O2_WITH_ACTS + , + {"useACTS", o2::framework::VariantType::Bool, false, {"Use ACTS for tracking"}} +#endif + }}; } inputs.emplace_back("dummy", "TRK", "DUMMY", 0, Lifetime::Timeframe); From 6593df3e4e809f9abca321c8f5720c53421530a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Wed, 8 Apr 2026 19:31:57 +0200 Subject: [PATCH 050/285] [ALICE3] Fix missing bool in ACTS integration (#15264) - specify since for acts integrations --- .../reconstruction/include/TRKReconstruction/ClustererACTS.h | 4 ++++ .../Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx | 4 ++++ .../ALICE3/TRK/workflow/include/TRKWorkflow/ClustererSpec.h | 4 +--- .../ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h | 3 +++ 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/ClustererACTS.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/ClustererACTS.h index 4111737d17a9f..37a148aa78afb 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/ClustererACTS.h +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/ClustererACTS.h @@ -9,8 +9,12 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +/// /// \file ClustererACTS.h /// \brief Definition of the TRK cluster finder +/// \author Nicolò Jacazio, Università del Piemonte Orientale (IT) +/// \since 2026-03-01 +/// #ifndef ALICEO2_TRK_CLUSTERERACTS_H #define ALICEO2_TRK_CLUSTERERACTS_H diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx index 0cf7c26e0ea41..2dbf56ae610e3 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx @@ -9,8 +9,12 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +/// /// \file ClustererACTS.cxx /// \brief Implementation of the TRK cluster finder with the ACTS +/// \author Nicolò Jacazio, Università del Piemonte Orientale (IT) +/// \since 2026-03-01 +/// #include "TRKReconstruction/ClustererACTS.h" #include "TRKBase/GeometryTGeo.h" diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/ClustererSpec.h b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/ClustererSpec.h index 9cfab104ecdf9..18cc6d245025a 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/ClustererSpec.h +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/ClustererSpec.h @@ -32,11 +32,9 @@ class ClustererDPL : public o2::framework::Task private: bool mUseMC = true; int mNThreads = 1; -#ifdef O2_WITH_ACTS - bool mUseACTS = false; -#endif o2::trk::Clusterer mClusterer; #ifdef O2_WITH_ACTS + bool mUseACTS = false; o2::trk::ClustererACTS mClustererACTS; #endif }; diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h index 33b25737bbc29..304b32041c2dc 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h @@ -58,6 +58,9 @@ class TrackerDPL : public framework::Task std::shared_ptr mTaskArena; nlohmann::json mHitRecoConfig; TStopwatch mTimer; +#ifdef O2_WITH_ACTS + bool mUseACTS = false; +#endif }; framework::DataProcessorSpec getTrackerSpec(bool useMC, const std::string& hitRecoConfig, gpu::gpudatatypes::DeviceType dType = gpu::gpudatatypes::DeviceType::CPU); From 9e8f22b292d5709d39b33310d779a990b31f2142 Mon Sep 17 00:00:00 2001 From: Francesco Noferini Date: Wed, 8 Apr 2026 18:54:21 +0200 Subject: [PATCH 051/285] fix wrong long_to_int converstion in TOF readout window indexing --- Detectors/TOF/base/include/TOFBase/WindowFiller.h | 2 +- Detectors/TOF/base/src/WindowFiller.cxx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Detectors/TOF/base/include/TOFBase/WindowFiller.h b/Detectors/TOF/base/include/TOFBase/WindowFiller.h index 77827ee5e7057..5c9abca6da0ea 100644 --- a/Detectors/TOF/base/include/TOFBase/WindowFiller.h +++ b/Detectors/TOF/base/include/TOFBase/WindowFiller.h @@ -96,7 +96,7 @@ class WindowFiller } std::vector& getPatterns() { return mPatterns; } - void addPattern(const uint32_t val, int icrate, int orbit, int bc) { mCratePatterns.emplace_back(val, icrate, orbit * 3 + (bc + 100) / Geo::BC_IN_WINDOW); } + void addPattern(const uint32_t val, int icrate, int orbit, int bc) { mCratePatterns.emplace_back(val, icrate, ((unsigned long)orbit) * 3 + (bc + 100) / Geo::BC_IN_WINDOW); } void addCrateHeaderData(unsigned long orbit, int crate, int32_t bc, uint32_t eventCounter); Diagnostic& getDiagnosticFrequency() { return mDiagnosticFrequency; } diff --git a/Detectors/TOF/base/src/WindowFiller.cxx b/Detectors/TOF/base/src/WindowFiller.cxx index 0362222b55bf5..35ec27070bda1 100644 --- a/Detectors/TOF/base/src/WindowFiller.cxx +++ b/Detectors/TOF/base/src/WindowFiller.cxx @@ -194,9 +194,9 @@ void WindowFiller::fillOutputContainer(std::vector& digits) int npatterns = 0; // check if patterns are in the current row - unsigned int initrow = mFirstIR.orbit * Geo::NWINDOW_IN_ORBIT; + unsigned long initrow = ((unsigned long)mFirstIR.orbit) * Geo::NWINDOW_IN_ORBIT; for (std::vector::reverse_iterator it = mCratePatterns.rbegin(); it != mCratePatterns.rend(); ++it) { - unsigned int irow = it->row; + unsigned long irow = it->row; // printf("pattern row=%ld (%u - %u) current=%ld\n",irow - initrow,irow,initrow,mReadoutWindowCurrent); if (irow - initrow > mReadoutWindowCurrent) { From a1e83082354311a027b2807ece60d682055d8905 Mon Sep 17 00:00:00 2001 From: Gabriele Cimador Date: Wed, 18 Mar 2026 16:22:37 +0100 Subject: [PATCH 052/285] GPU Framework: fix detection for gfx90a GPU --- dependencies/FindO2GPU.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dependencies/FindO2GPU.cmake b/dependencies/FindO2GPU.cmake index 3e8f012fea4b5..ad9cc11d56b40 100644 --- a/dependencies/FindO2GPU.cmake +++ b/dependencies/FindO2GPU.cmake @@ -10,7 +10,7 @@ # or submit itself to any jurisdiction. # NOTE!!!! - Whenever this file is changed, move it over to alidist/resources -# FindO2GPU.cmake Version 13 +# FindO2GPU.cmake Version 14 set(CUDA_COMPUTETARGET_DEFAULT_FULL 80-real 86-real 89-real 120-real 75-virtual) set(HIP_AMDGPUTARGET_DEFAULT_FULL gfx906;gfx908) @@ -66,7 +66,7 @@ function(detect_gpu_arch backend) # Detect GPU architecture, optionally filterri set(CUDA_TARGET TESLA) endif() - string(REGEX MATCH "^[ \t\r\n]*gfx[0-9]+" HIP_FIRST_TARGET "${HIP_AMDGPUTARGET}") + string(REGEX MATCH "^[ \t\r\n]*gfx[0-9a-fA-F]+" HIP_FIRST_TARGET "${HIP_AMDGPUTARGET}") string(STRIP "${HIP_FIRST_TARGET}" HIP_FIRST_TARGET) string(REGEX REPLACE "^gfx" "" HIP_FIRST_TARGET "${HIP_FIRST_TARGET}") if(NOT HIP_FIRST_TARGET) From 97a77d9365664c504e4968132c087f89067452f5 Mon Sep 17 00:00:00 2001 From: Ernst Hellbar Date: Fri, 10 Apr 2026 17:22:07 +0200 Subject: [PATCH 053/285] DPL: use new indices methods to navigate through InputRecord in output-proxy --- Framework/Core/src/ExternalFairMQDeviceProxy.cxx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Framework/Core/src/ExternalFairMQDeviceProxy.cxx b/Framework/Core/src/ExternalFairMQDeviceProxy.cxx index 5867f53af4bd2..d4ee776986184 100644 --- a/Framework/Core/src/ExternalFairMQDeviceProxy.cxx +++ b/Framework/Core/src/ExternalFairMQDeviceProxy.cxx @@ -1027,9 +1027,9 @@ DataProcessorSpec specifyFairMQDeviceOutputProxy(char const* name, callbacks.set(forwardEos); return adaptStateless([lastDataProcessingHeader](InputRecord& inputs) { - for (size_t ii = 0; ii != inputs.size(); ++ii) { - for (size_t pi = 0; pi < inputs.getNofParts(ii); ++pi) { - auto part = inputs.getByPos(ii, pi); + for (auto it = inputs.begin(); it != inputs.end(); it++) { + for (auto indices = it.initialIndices(); indices != it.endIndices(); indices = it.nextIndices(indices)) { + auto part = it.getAtIndices(indices); const auto* dph = o2::header::get(part.header); if (dph) { // FIXME: should we implement an assignment operator for DataProcessingHeader? @@ -1163,9 +1163,9 @@ DataProcessorSpec specifyFairMQDeviceMultiOutputProxy(char const* name, // there is nothing to do if the forwarding is handled on the framework level // as forward routes but we need to keep a copy of the last DataProcessingHeader // for sending the EOS - for (size_t ii = 0; ii != inputs.size(); ++ii) { - for (size_t pi = 0; pi < inputs.getNofParts(ii); ++pi) { - auto part = inputs.getByPos(ii, pi); + for (auto it = inputs.begin(); it != inputs.end(); it++) { + for (auto indices = it.initialIndices(); indices != it.endIndices(); indices = it.nextIndices(indices)) { + auto part = it.getAtIndices(indices); const auto* dph = o2::header::get(part.header); if (dph) { // FIXME: should we implement an assignment operator for DataProcessingHeader? From 85fad0700b377263cbadbf4144a734c2ab79f0e7 Mon Sep 17 00:00:00 2001 From: Maximiliano Puccio Date: Mon, 13 Apr 2026 14:02:25 +0200 Subject: [PATCH 054/285] [ALICE3] TRK: Introduce Almira params and shorten ROF/response for TRK (#15267) * Add Almira params * Use Almira params in the digitisation * Clip number of ROFs to the requested number of orbits * Add macro to check bandwidth for MLOT --- .../Upgrades/ALICE3/TRK/base/CMakeLists.txt | 6 +- .../TRK/base/include/TRKBase/AlmiraParam.h | 48 +++ .../ALICE3/TRK/base/src/AlmiraParam.cxx | 14 + .../ALICE3/TRK/base/src/TRKBaseLinkDef.h | 4 +- .../ALICE3/TRK/macros/test/CMakeLists.txt | 12 + .../ALICE3/TRK/macros/test/CheckBandwidth.C | 299 ++++++++++++++++++ .../include/TRKSimulation/DPLDigitizerParam.h | 6 +- .../include/TRKSimulation/DigiParams.h | 6 +- .../ALICE3/TRK/simulation/src/DigiParams.cxx | 4 +- .../ALICE3/TRK/simulation/src/Digitizer.cxx | 11 +- .../ALICE3/TRK/workflow/src/TrackerSpec.cxx | 2 +- .../src/TRKDigitizerSpec.cxx | 108 +++++-- 12 files changed, 479 insertions(+), 41 deletions(-) create mode 100644 Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/AlmiraParam.h create mode 100644 Detectors/Upgrades/ALICE3/TRK/base/src/AlmiraParam.cxx create mode 100644 Detectors/Upgrades/ALICE3/TRK/macros/test/CheckBandwidth.C diff --git a/Detectors/Upgrades/ALICE3/TRK/base/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/base/CMakeLists.txt index 96ebf4ead4b7b..89775e22ed8d0 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/base/CMakeLists.txt @@ -11,11 +11,13 @@ o2_add_library(TRKBase SOURCES src/GeometryTGeo.cxx + src/AlmiraParam.cxx src/TRKBaseParam.cxx src/SegmentationChip.cxx PUBLIC_LINK_LIBRARIES O2::DetectorsBase) o2_target_root_dictionary(TRKBase - HEADERS include/TRKBase/GeometryTGeo.h + HEADERS include/TRKBase/AlmiraParam.h + include/TRKBase/GeometryTGeo.h include/TRKBase/TRKBaseParam.h - include/TRKBase/SegmentationChip.h) \ No newline at end of file + include/TRKBase/SegmentationChip.h) diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/AlmiraParam.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/AlmiraParam.h new file mode 100644 index 0000000000000..2048666e21c00 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/AlmiraParam.h @@ -0,0 +1,48 @@ +// 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_TRK_ALMIRAPARAM_H +#define O2_TRK_ALMIRAPARAM_H + +#include "CommonConstants/LHCConstants.h" +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2 +{ +namespace trk +{ +constexpr float DEFAlmiraStrobeDelay = 0.f; ///< default strobe delay in ns wrt ROF start, to be tuned with the real chip response + +struct AlmiraParam : public o2::conf::ConfigurableParamHelper { + int roFrameLengthInBC = o2::constants::lhc::LHCMaxBunches / 198; ///< ROF length in BC for continuous mode + float strobeDelay = DEFAlmiraStrobeDelay; ///< strobe start in ns wrt ROF start + float strobeLengthCont = -1.; ///< if < 0, full ROF length minus delay + int roFrameBiasInBC = 0; ///< ROF start bias in BC wrt orbit start + + O2ParamDef(AlmiraParam, "TRKAlmiraParam"); +}; + +} // namespace trk + +namespace framework +{ +template +struct is_messageable; + +template <> +struct is_messageable : std::true_type { +}; +} // namespace framework + +} // namespace o2 + +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/base/src/AlmiraParam.cxx b/Detectors/Upgrades/ALICE3/TRK/base/src/AlmiraParam.cxx new file mode 100644 index 0000000000000..572c902fb23f1 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/base/src/AlmiraParam.cxx @@ -0,0 +1,14 @@ +// 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 "TRKBase/AlmiraParam.h" + +O2ParamImpl(o2::trk::AlmiraParam); diff --git a/Detectors/Upgrades/ALICE3/TRK/base/src/TRKBaseLinkDef.h b/Detectors/Upgrades/ALICE3/TRK/base/src/TRKBaseLinkDef.h index eee9a23eaf5e7..e36955cdd150d 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/src/TRKBaseLinkDef.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/src/TRKBaseLinkDef.h @@ -15,10 +15,12 @@ #pragma link off all classes; #pragma link off all functions; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::trk::AlmiraParam> + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::trk::TRKBaseParam> + ; +#pragma link C++ class o2::trk::AlmiraParam + ; #pragma link C++ class o2::trk::GeometryTGeo + #pragma link C++ class o2::trk::TRKBaseParam + ; #pragma link C++ class o2::trk::SegmentationChip + ; -#endif \ No newline at end of file +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt index edd9c785d89ce..54e42c6857249 100644 --- a/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt @@ -9,6 +9,18 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. + +o2_add_test_root_macro(CheckBandwidth.C + PUBLIC_LINK_LIBRARIES O2::ITSMFTBase + O2::ITSMFTSimulation + O2::TRKBase + O2::TRKSimulation + O2::MathUtils + O2::SimulationDataFormat + O2::DetectorsBase + O2::Steer + LABELS trk COMPILE_ONLY) + o2_add_test_root_macro(CheckDigits.C PUBLIC_LINK_LIBRARIES O2::ITSMFTBase O2::ITSMFTSimulation diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckBandwidth.C b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckBandwidth.C new file mode 100644 index 0000000000000..2087f88a87d6b --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckBandwidth.C @@ -0,0 +1,299 @@ +// 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 CheckDigits.C +/// \brief Simple macro to check TRK digits + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "TRKBase/GeometryTGeo.h" +#include "DataFormatsITSMFT/Digit.h" +#include "MathUtils/Utils.h" +#include "DetectorsBase/GeometryManager.h" + +#include "DataFormatsITSMFT/ROFRecord.h" +#include "CommonDataFormat/InteractionRecord.h" +#include "SimulationDataFormat/DigitizationContext.h" + +#endif + +namespace +{ +constexpr double DigitBits = 16.; +constexpr double BunchCrossingNS = 25.; +constexpr int ReadoutCycleBC = 18; +constexpr int ReadoutCycleSimBC = 18; +constexpr double ReadoutCycleSeconds = ReadoutCycleBC * BunchCrossingNS * 1.e-9; +} // namespace + +void CheckBandwidth(std::string digifile = "trkdigits.root", std::string inputGeom = "o2sim_geometry.root", std::string collContextFile = "collisioncontext.root") +{ + gStyle->SetPalette(55); + gStyle->SetOptStat(0); + + auto drawSummary = [](double averageValue, double peakValue, const char* unit) { + TLatex latex; + latex.SetNDC(); + latex.SetTextSize(0.03); + latex.SetTextAlign(13); + latex.DrawLatex(0.04, 0.05, Form("avg: %.3f %s", averageValue, unit)); + latex.DrawLatex(0.34, 0.05, Form("peak: %.3f %s", peakValue, unit)); + }; + + auto drawCollisionSummary = [](double averageValue, double nonEmptyAverageValue, double peakValue) { + TLatex latex; + latex.SetNDC(); + latex.SetTextSize(0.03); + latex.SetTextAlign(13); + latex.DrawLatex(0.04, 0.025, Form("avg: %.3f collisions/ROF", averageValue)); + latex.DrawLatex(0.42, 0.025, Form("peak: %.3f collisions/ROF", peakValue)); + latex.DrawLatex(0.04, 0.06, Form("avg non-empty: %.3f collisions/ROF", nonEmptyAverageValue)); + }; + + using namespace o2::base; + using namespace o2::trk; + + TFile* f = TFile::Open("CheckBandwidth.root", "recreate"); + + // Geometry + o2::base::GeometryManager::loadGeometry(inputGeom); + auto* gman = o2::trk::GeometryTGeo::Instance(); + gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); + + // Collision Context + TFile* ccFile = TFile::Open(collContextFile.data()); + auto* digiContext = (o2::steer::DigitizationContext*)ccFile->Get("DigitizationContext"); + const o2::InteractionRecord firstSampledIR{0, digiContext->getFirstOrbitForSampling()}; + std::vector collisionsPerROF; + + for (const auto& record : digiContext->getEventRecords()) { + auto nbc = record.differenceInBC(firstSampledIR); + if (record.getTimeOffsetWrtBC() < 0. && nbc > 0) { + --nbc; + } + if (nbc < 0) { + continue; + } + + const size_t rofID = nbc / ReadoutCycleSimBC; + if (rofID >= collisionsPerROF.size()) { + collisionsPerROF.resize(rofID + 1, 0u); + } + ++collisionsPerROF[rofID]; + } + + // Digits + TFile* digFile = TFile::Open(digifile.data()); + TTree* digTree = (TTree*)digFile->Get("o2sim"); + const int nDigitTreeEntries = digTree->GetEntries(); + + std::vector* digArr = nullptr; + digTree->SetBranchAddress("TRKDigit", &digArr); + + // Get Read Out Frame arrays + std::vector* ROFRecordArrray = nullptr; + digTree->SetBranchAddress("TRKDigitROF", &ROFRecordArrray); + std::vector& ROFRecordArrrayRef = *ROFRecordArrray; + + digTree->GetEntry(0); + + if (nDigitTreeEntries > 1) { + LOG(warning) << "Digit tree has " << nDigitTreeEntries << " entries, but this macro processes entry 0 only."; + } + + std::vector digitsPerChip(gman->getNumberOfChips(), 0ull); + std::vector maxDigitsPerROFPerChip(gman->getNumberOfChips(), 0u); + std::vector digitsInCurrentROFPerChip(gman->getNumberOfChips(), 0u); + + const int nROFRec = (int)ROFRecordArrrayRef.size(); + const int nCollisionROFBins = std::max(nROFRec, static_cast(collisionsPerROF.size())); + + if (nCollisionROFBins > 0) { + auto* hCollisionsPerROF = new TH1D("h_collisions_per_rof", "Collisions per ROF;ROF id;N collisions", nCollisionROFBins, -0.5, nCollisionROFBins - 0.5); + double totalCollisionsPerROF = 0.; + double peakCollisionsPerROF = 0.; + int nNonEmptyROFs = 0; + + for (int rofID = 0; rofID < nCollisionROFBins; ++rofID) { + const double nCollisions = rofID < static_cast(collisionsPerROF.size()) ? collisionsPerROF[rofID] : 0.; + hCollisionsPerROF->SetBinContent(rofID + 1, nCollisions); + totalCollisionsPerROF += nCollisions; + peakCollisionsPerROF = std::max(peakCollisionsPerROF, nCollisions); + if (nCollisions > 0.) { + ++nNonEmptyROFs; + } + } + + auto* canvCollisionsPerROF = new TCanvas("canvCollisionsPerROF", "Collisions per ROF", 1050, 1050); + canvCollisionsPerROF->SetTopMargin(0.08); + hCollisionsPerROF->Draw("hist"); + drawCollisionSummary(totalCollisionsPerROF / nCollisionROFBins, + nNonEmptyROFs > 0 ? totalCollisionsPerROF / nNonEmptyROFs : 0., + peakCollisionsPerROF); + canvCollisionsPerROF->SaveAs("trk_collisions_per_rof.png"); + } + + unsigned int rofIndex = 0; + unsigned int rofNEntries = 0; + + // LOOP on : ROFRecord array + for (unsigned int iROF = 0; iROF < ROFRecordArrrayRef.size(); iROF++) { + std::vector touchedChips; + + rofIndex = ROFRecordArrrayRef[iROF].getFirstEntry(); + rofNEntries = ROFRecordArrrayRef[iROF].getNEntries(); + + // LOOP on : digits array + for (unsigned int iDigit = rofIndex; iDigit < rofIndex + rofNEntries; iDigit++) { + if (iDigit % 1000 == 0) + std::cout << "Reading digit " << iDigit << " / " << digArr->size() << std::endl; + + Int_t iDetID = (*digArr)[iDigit].getChipIndex(); + Int_t disk = gman->getDisk(iDetID); + Int_t subDetID = gman->getSubDetID(iDetID); + + if (subDetID == 1 && disk == -1) { + if (digitsInCurrentROFPerChip[iDetID] == 0) { + touchedChips.push_back(iDetID); + } + digitsPerChip[iDetID]++; + ++digitsInCurrentROFPerChip[iDetID]; + } + + } // end loop on digits array + + for (const auto chipID : touchedChips) { + maxDigitsPerROFPerChip[chipID] = std::max(maxDigitsPerROFPerChip[chipID], digitsInCurrentROFPerChip[chipID]); + digitsInCurrentROFPerChip[chipID] = 0; + } + + } // end loop on ROFRecords array + + const double rofNorm = nROFRec > 0 ? 1. / nROFRec : 0.; + const double bitsToMbps = ReadoutCycleSeconds > 0. ? DigitBits / ReadoutCycleSeconds / 1.e6 : 0.; + const int nMLOTLayers = gman->getNumberOfLayersMLOT(); + + for (int layer = 0; layer < nMLOTLayers; ++layer) { + int nStaves = gman->extractNumberOfStavesMLOT(layer); + std::map>> chipsPerStave; + std::vector sensorIdPerChip(gman->getNumberOfChips(), -1); + int maxSensorsPerStave = 0; + + for (int chipID = 0; chipID < gman->getNumberOfChips(); ++chipID) { + if (gman->getSubDetID(chipID) != 1 || gman->getLayer(chipID) != layer) { + continue; + } + const int staveID = gman->getStave(chipID); + const auto sensorCenter = gman->getMatrixL2G(chipID)(o2::math_utils::Point3D(0.f, 0.f, 0.f)); + chipsPerStave[staveID].push_back({sensorCenter.Z(), chipID}); + } + + for (auto& [staveID, chips] : chipsPerStave) { + std::sort(chips.begin(), chips.end(), [](const auto& left, const auto& right) { + if (std::abs(left.first - right.first) > 1.e-4) { + return left.first < right.first; + } + return left.second < right.second; + }); + + for (size_t sensorIndex = 0; sensorIndex < chips.size(); ++sensorIndex) { + sensorIdPerChip[chips[sensorIndex].second] = sensorIndex; + } + + maxSensorsPerStave = std::max(maxSensorsPerStave, static_cast(chips.size())); + } + + if (maxSensorsPerStave == 0) { + continue; + } + + auto* hDigitsPerROF = new TH2F(Form("h_digits_per_rof_layer%d", layer), + Form("Layer %d average digits per ROF;stave id;sensor id in stave;digits / ROF", layer), + nStaves, -0.5, nStaves - 0.5, maxSensorsPerStave, -0.5, maxSensorsPerStave - 0.5); + auto* hMaxDigitsPerROF = new TH2F(Form("h_max_digits_per_rof_layer%d", layer), + Form("Layer %d max digits in one ROF;stave id;sensor id in stave;max digits / ROF", layer), + nStaves, -0.5, nStaves - 0.5, maxSensorsPerStave, -0.5, maxSensorsPerStave - 0.5); + auto* hBandwidth = new TH2F(Form("h_bandwidth_layer%d", layer), + Form("Layer %d bandwidth map;stave id;sensor id in stave;bandwidth (Mbit/s)", layer), + nStaves, -0.5, nStaves - 0.5, maxSensorsPerStave, -0.5, maxSensorsPerStave - 0.5); + double totalAvgDigitsPerROF = 0.; + double totalMaxDigitsPerROF = 0.; + double totalBandwidthMbps = 0.; + double peakAvgDigitsPerROF = 0.; + double peakMaxDigitsPerROF = 0.; + double peakBandwidthMbps = 0.; + int nFilledSensors = 0; + + for (int chipID = 0; chipID < gman->getNumberOfChips(); ++chipID) { + if (gman->getSubDetID(chipID) != 1 || gman->getLayer(chipID) != layer) { + continue; + } + + const int staveID = gman->getStave(chipID); + const int sensorID = sensorIdPerChip[chipID]; + const double avgDigitsPerROF = digitsPerChip[chipID] * rofNorm; + const double maxDigitsPerROF = maxDigitsPerROFPerChip[chipID]; + const double bandwidthMbps = avgDigitsPerROF * bitsToMbps; + + if (sensorID >= 0) { + hDigitsPerROF->Fill(staveID, sensorID, avgDigitsPerROF); + hMaxDigitsPerROF->Fill(staveID, sensorID, maxDigitsPerROF); + hBandwidth->Fill(staveID, sensorID, bandwidthMbps); + totalAvgDigitsPerROF += avgDigitsPerROF; + totalMaxDigitsPerROF += maxDigitsPerROF; + totalBandwidthMbps += bandwidthMbps; + peakAvgDigitsPerROF = std::max(peakAvgDigitsPerROF, avgDigitsPerROF); + peakMaxDigitsPerROF = std::max(peakMaxDigitsPerROF, maxDigitsPerROF); + peakBandwidthMbps = std::max(peakBandwidthMbps, bandwidthMbps); + ++nFilledSensors; + } + } + + auto* canvLayer = new TCanvas(Form("canvBandwidthLayer%d", layer), Form("Layer %d bandwidth", layer), 1050, 1050); + canvLayer->SetTopMargin(0.08); + canvLayer->SetRightMargin(0.18); + const double avgDigitsPerROFLayer = nFilledSensors > 0 ? totalAvgDigitsPerROF / nFilledSensors : 0.; + const double avgMaxDigitsPerROFLayer = nFilledSensors > 0 ? totalMaxDigitsPerROF / nFilledSensors : 0.; + const double avgBandwidthMbps = nFilledSensors > 0 ? totalBandwidthMbps / nFilledSensors : 0.; + hBandwidth->GetZaxis()->SetRangeUser(0., avgBandwidthMbps > 0. ? 3. * avgBandwidthMbps : 1.); + hBandwidth->Draw("colz"); + drawSummary(avgBandwidthMbps, peakBandwidthMbps, "Mbit/s"); + canvLayer->SaveAs(Form("trk_layer%d_bandwidth_map.png", layer)); + + auto* canvLayerDigits = new TCanvas(Form("canvDigitsLayer%d", layer), Form("Layer %d digits per ROF", layer), 1050, 1050); + canvLayerDigits->SetTopMargin(0.08); + canvLayerDigits->SetRightMargin(0.18); + hDigitsPerROF->Draw("colz"); + drawSummary(avgDigitsPerROFLayer, peakAvgDigitsPerROF, "digits/ROF"); + canvLayerDigits->SaveAs(Form("trk_layer%d_digits_per_rof_map.png", layer)); + + auto* canvLayerMaxDigits = new TCanvas(Form("canvMaxDigitsLayer%d", layer), Form("Layer %d max digits per ROF", layer), 1050, 1050); + canvLayerMaxDigits->SetTopMargin(0.08); + canvLayerMaxDigits->SetRightMargin(0.18); + hMaxDigitsPerROF->Draw("colz"); + drawSummary(avgMaxDigitsPerROFLayer, peakMaxDigitsPerROF, "digits/ROF"); + canvLayerMaxDigits->SaveAs(Form("trk_layer%d_max_digits_per_rof_map.png", layer)); + } + + f->Write(); + f->Close(); +} diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DPLDigitizerParam.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DPLDigitizerParam.h index 168ae172f4b86..de839b27aefee 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DPLDigitizerParam.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DPLDigitizerParam.h @@ -32,9 +32,9 @@ struct DPLDigitizerParam : public o2::conf::ConfigurableParamHelper -infTime; } - const o2::trk::ChipSimResponse* getAlpSimResponse() const { return mAlpSimResponse.get(); } - void setAlpSimResponse(const o2::itsmft::AlpideSimResponse*); + const o2::trk::ChipSimResponse* getResponse() const { return mResponse.get(); } + void setResponse(const o2::itsmft::AlpideSimResponse*); const SignalShape& getSignalShape() const { return mSignalShape; } SignalShape& getSignalShape() { return (SignalShape&)mSignalShape; } @@ -123,7 +123,7 @@ class DigiParams o2::itsmft::AlpideSignalTrapezoid mSignalShape; ///< signal timeshape parameterization - std::unique_ptr mAlpSimResponse; //!< pointer on external response + std::unique_ptr mResponse; //!< pointer on external response // auxiliary precalculated parameters float mROFrameLengthInv = 0; ///< inverse length of RO frame in ns diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/DigiParams.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/DigiParams.cxx index e2a78702204e5..d5d47b3658b04 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/DigiParams.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/DigiParams.cxx @@ -72,7 +72,7 @@ void DigiParams::print() const mSignalShape.print(); } -void DigiParams::setAlpSimResponse(const o2::itsmft::AlpideSimResponse* resp) +void DigiParams::setResponse(const o2::itsmft::AlpideSimResponse* resp) { LOG(debug) << "Response function data path: " << resp->getDataPath(); LOG(debug) << "Response function info: "; @@ -80,5 +80,5 @@ void DigiParams::setAlpSimResponse(const o2::itsmft::AlpideSimResponse* resp) if (!resp) { LOGP(fatal, "cannot set response function from null"); } - mAlpSimResponse = std::make_unique(resp); + mResponse = std::make_unique(resp); } diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx index 31ef19a21cce9..31b9a25b7e5f8 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx @@ -50,7 +50,7 @@ void Digitizer::init() } // setting the correct response function (for the moment, for both VD and MLOT the same response function is used) - mChipSimResp = mParams.getAlpSimResponse(); + mChipSimResp = mParams.getResponse(); mChipSimRespVD = mChipSimResp; /// for the moment considering the same response mChipSimRespMLOT = mChipSimResp; /// for the moment considering the same response @@ -171,7 +171,13 @@ void Digitizer::setEventTime(const o2::InteractionTimeRecord& irt) nbc--; } - mNewROFrame = nbc / mParams.getROFrameLengthInBC(); + if (nbc < 0) { + mNewROFrame = 0; + mIsBeforeFirstRO = true; + } else { + mNewROFrame = nbc / mParams.getROFrameLengthInBC(); + mIsBeforeFirstRO = false; + } LOG(debug) << " NewROFrame " << mNewROFrame << " = " << nbc << "/" << mParams.getROFrameLengthInBC() << " (nbc/mParams.getROFrameLengthInBC()"; @@ -179,6 +185,7 @@ void Digitizer::setEventTime(const o2::InteractionTimeRecord& irt) mCollisionTimeWrtROF += (nbc % mParams.getROFrameLengthInBC()) * o2::constants::lhc::LHCBunchSpacingNS; } else { mNewROFrame = 0; + mIsBeforeFirstRO = false; } if (mNewROFrame < mROFrameMin) { diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx index b587ec24775b4..3801228422a62 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx @@ -427,7 +427,7 @@ DataProcessorSpec getTrackerSpec(bool useMC, const std::string& hitRecoConfig, o } // inputs.emplace_back("itscldict", "TRK", "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec("ITS/Calib/ClusterDictionary")); - // inputs.emplace_back("itsalppar", "TRK", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/AlpideParam")); + // inputs.emplace_back("TRK_almiraparam", "TRK", "ALMIRAPARAM", 0, Lifetime::Condition, ccdbParamSpec("TRK/Config/AlmiraParam")); // outputs.emplace_back("TRK", "TRACKCLSID", 0, Lifetime::Timeframe); // outputs.emplace_back("TRK", "TRKTrackROF", 0, Lifetime::Timeframe); diff --git a/Steer/DigitizerWorkflow/src/TRKDigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/TRKDigitizerSpec.cxx index 30f9d33983712..8957ebed223b2 100644 --- a/Steer/DigitizerWorkflow/src/TRKDigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/TRKDigitizerSpec.cxx @@ -21,19 +21,21 @@ #include "DataFormatsITSMFT/Digit.h" #include "SimulationDataFormat/ConstMCTruthContainer.h" #include "DetectorsBase/BaseDPLDigitizer.h" +#include "DetectorsRaw/HBFUtils.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsCommonDataFormats/SimTraits.h" #include "DataFormatsParameters/GRPObject.h" #include "DataFormatsITSMFT/ROFRecord.h" #include "TRKSimulation/Digitizer.h" #include "TRKSimulation/DPLDigitizerParam.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "TRKBase/AlmiraParam.h" #include "TRKBase/GeometryTGeo.h" #include "TRKBase/TRKBaseParam.h" #include #include +#include #include #include @@ -77,6 +79,8 @@ class TRKDPLDigitizerTask : BaseDPLDigitizer if (mFinished) { return; } + mFirstOrbitTF = pc.services().get().firstTForbit; + const o2::InteractionRecord firstIR(0, mFirstOrbitTF); updateTimeDependentParams(pc); // read collision context from input @@ -102,6 +106,11 @@ class TRKDPLDigitizerTask : BaseDPLDigitizer // digits are directly put into DPL owned resource auto& digitsAccum = pc.outputs().make>(Output{mOrigin, "DIGITS", 0}); + const int roFrameLengthInBC = mDigitizer.getParams().getROFrameLengthInBC(); + const int nROFsPerOrbit = o2::constants::lhc::LHCMaxBunches / roFrameLengthInBC; + const int nROFsTF = nROFsPerOrbit * raw::HBFUtils::Instance().getNOrbitsPerTF(); + mROFRecordsAccum.reserve(nROFsTF); + auto accumulate = [this, &digitsAccum]() { // accumulate result of single event processing, called after processing every event supplied // AND after the final flushing via digitizer::fillOutputContainer @@ -180,10 +189,62 @@ class TRKDPLDigitizerTask : BaseDPLDigitizer accumulate(); // here we have all digits and labels and we can send them to consumer (aka snapshot it onto output) + std::vector expDigitRofVec(nROFsTF); + for (int iROF = 0; iROF < nROFsTF; ++iROF) { + auto& rof = expDigitRofVec[iROF]; + const int orb = iROF * roFrameLengthInBC / o2::constants::lhc::LHCMaxBunches + mFirstOrbitTF; + const int bc = iROF * roFrameLengthInBC % o2::constants::lhc::LHCMaxBunches; + rof.setBCData(o2::InteractionRecord(bc, orb)); + rof.setROFrame(iROF); + rof.setNEntries(0); + rof.setFirstEntry(-1); + } + + for (const auto& rof : mROFRecordsAccum) { + const auto& ir = rof.getBCData(); + const auto irToFirst = ir - firstIR; + const auto irROF = irToFirst.toLong() / roFrameLengthInBC; + if (irROF < 0 || irROF >= nROFsTF) { + continue; + } + auto& expROF = expDigitRofVec[irROF]; + expROF.setFirstEntry(rof.getFirstEntry()); + expROF.setNEntries(rof.getNEntries()); + if (expROF.getBCData() != rof.getBCData()) { + LOGP(fatal, "detected mismatch between expected {} and received {}", expROF.asString(), rof.asString()); + } + } - pc.outputs().snapshot(Output{mOrigin, "DIGITSROF", 0}, mROFRecordsAccum); + int prevFirst = 0; + for (auto& rof : expDigitRofVec) { + if (rof.getFirstEntry() < 0) { + rof.setFirstEntry(prevFirst); + } + prevFirst = rof.getFirstEntry(); + } + + pc.outputs().snapshot(Output{mOrigin, "DIGITSROF", 0}, expDigitRofVec); if (mWithMCTruth) { - pc.outputs().snapshot(Output{mOrigin, "DIGITSMC2ROF", 0}, mMC2ROFRecordsAccum); + std::vector clippedMC2ROFRecords; + clippedMC2ROFRecords.reserve(mMC2ROFRecordsAccum.size()); + for (auto mc2rof : mMC2ROFRecordsAccum) { + if (mc2rof.rofRecordID < 0 || mc2rof.minROF >= static_cast(nROFsTF)) { + mc2rof.rofRecordID = -1; + mc2rof.minROF = 0; + mc2rof.maxROF = 0; + } else { + mc2rof.maxROF = std::min(mc2rof.maxROF, nROFsTF - 1); + if (mc2rof.minROF > mc2rof.maxROF) { + mc2rof.rofRecordID = -1; + mc2rof.minROF = 0; + mc2rof.maxROF = 0; + } else { + mc2rof.rofRecordID = mc2rof.minROF; + } + } + clippedMC2ROFRecords.push_back(mc2rof); + } + pc.outputs().snapshot(Output{mOrigin, "DIGITSMC2ROF", 0}, clippedMC2ROFRecords); auto& sharedlabels = pc.outputs().make>(Output{mOrigin, "DIGITSMCTR", 0}); mLabelsAccum.flatten_to(sharedlabels); // free space of existing label containers @@ -208,7 +269,7 @@ class TRKDPLDigitizerTask : BaseDPLDigitizer if (!file) { LOG(fatal) << "Cannot open response file " << mLocalRespFile; } - mDigitizer.getParams().setAlpSimResponse((const o2::itsmft::AlpideSimResponse*)file->Get("response1")); + mDigitizer.getParams().setResponse((const o2::itsmft::AlpideSimResponse*)file->Get("response1")); } void updateTimeDependentParams(ProcessingContext& pc) @@ -225,21 +286,15 @@ class TRKDPLDigitizerTask : BaseDPLDigitizer mDigitizer.setGeometry(geom); const auto& dopt = o2::trk::DPLDigitizerParam::Instance(); - pc.inputs().get*>("ITS_alppar"); - const auto& aopt = o2::itsmft::DPLAlpideParam::Instance(); - digipar.setContinuous(dopt.continuous); + // pc.inputs().get("TRK_almiraparam"); + const auto& aopt = o2::trk::AlmiraParam::Instance(); + auto frameNS = aopt.roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingNS; + digipar.setContinuous(true); digipar.setROFrameBiasInBC(aopt.roFrameBiasInBC); - if (dopt.continuous) { - auto frameNS = aopt.roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingNS; - digipar.setROFrameLengthInBC(aopt.roFrameLengthInBC); - digipar.setROFrameLength(frameNS); // RO frame in ns - digipar.setStrobeDelay(aopt.strobeDelay); // Strobe delay wrt beginning of the RO frame, in ns - digipar.setStrobeLength(aopt.strobeLengthCont > 0 ? aopt.strobeLengthCont : frameNS - aopt.strobeDelay); // Strobe length in ns - } else { - digipar.setROFrameLength(aopt.roFrameLengthTrig); // RO frame in ns - digipar.setStrobeDelay(aopt.strobeDelay); // Strobe delay wrt beginning of the RO frame, in ns - digipar.setStrobeLength(aopt.strobeLengthTrig); // Strobe length in ns - } + digipar.setROFrameLengthInBC(aopt.roFrameLengthInBC); + digipar.setROFrameLength(frameNS); // RO frame in ns + digipar.setStrobeDelay(aopt.strobeDelay); + digipar.setStrobeLength(aopt.strobeLengthCont > 0 ? aopt.strobeLengthCont : frameNS - aopt.strobeDelay); // parameters of signal time response: flat-top duration, max rise time and q @ which rise time is 0 digipar.getSignalShape().setParameters(dopt.strobeFlatTop, dopt.strobeMaxRiseTime, dopt.strobeQRiseTime0); digipar.setChargeThreshold(dopt.chargeThreshold); // charge threshold in electrons @@ -247,10 +302,8 @@ class TRKDPLDigitizerTask : BaseDPLDigitizer digipar.setTimeOffset(dopt.timeOffset); digipar.setNSimSteps(dopt.nSimSteps); - mROMode = digipar.isContinuous() ? o2::parameters::GRPObject::CONTINUOUS : o2::parameters::GRPObject::PRESENT; - LOG(info) << mID.getName() << " simulated in " - << ((mROMode == o2::parameters::GRPObject::CONTINUOUS) ? "CONTINUOUS" : "TRIGGERED") - << " RO mode"; + mROMode = o2::parameters::GRPObject::CONTINUOUS; + LOG(info) << mID.getName() << " simulated in CONTINUOUS RO mode"; // if (oTRKParams::Instance().useDeadChannelMap) { // pc.inputs().get("TRK_dead"); // trigger final ccdb update @@ -265,9 +318,9 @@ class TRKDPLDigitizerTask : BaseDPLDigitizer void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) { - if (matcher == ConcreteDataMatcher(detectors::DetID::ITS, "ALPIDEPARAM", 0)) { - LOG(info) << mID.getName() << " Alpide param updated"; - const auto& par = o2::itsmft::DPLAlpideParam::Instance(); + if (matcher == ConcreteDataMatcher(mOrigin, "ALMIRAPARAM", 0)) { + LOG(info) << mID.getName() << " Almira param updated"; + const auto& par = o2::trk::AlmiraParam::Instance(); par.printKeyValues(); return; } @@ -280,7 +333,7 @@ class TRKDPLDigitizerTask : BaseDPLDigitizer LOG(info) << mID.getName() << " loaded APTSResponseData"; if (mLocalRespFile.empty()) { LOG(info) << "Using CCDB/APTS response file"; - mDigitizer.getParams().setAlpSimResponse((const o2::itsmft::AlpideSimResponse*)obj); + mDigitizer.getParams().setResponse((const o2::itsmft::AlpideSimResponse*)obj); mDigitizer.setResponseName("APTS"); } else { LOG(info) << "Response function will be loaded from local file: " << mLocalRespFile; @@ -294,6 +347,7 @@ class TRKDPLDigitizerTask : BaseDPLDigitizer bool mWithMCTruth{true}; bool mFinished{false}; bool mDisableQED{false}; + unsigned long mFirstOrbitTF = 0x0; std::string mLocalRespFile{""}; const o2::detectors::DetID mID{o2::detectors::DetID::TRK}; const o2::header::DataOrigin mOrigin{o2::header::gDataOriginTRK}; @@ -318,7 +372,7 @@ DataProcessorSpec getTRKDigitizerSpec(int channel, bool mctruth) auto detOrig = o2::header::gDataOriginTRK; std::vector inputs; inputs.emplace_back("collisioncontext", "SIM", "COLLISIONCONTEXT", static_cast(channel), Lifetime::Timeframe); - inputs.emplace_back("ITS_alppar", "ITS", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/AlpideParam")); + // inputs.emplace_back("TRK_almiraparam", "TRK", "ALMIRAPARAM", 0, Lifetime::Condition, ccdbParamSpec("TRK/Config/AlmiraParam")); // if (oTRKParams::Instance().useDeadChannelMap) { // inputs.emplace_back("TRK_dead", "TRK", "DEADMAP", 0, Lifetime::Condition, ccdbParamSpec("TRK/Calib/DeadMap")); // } From c9acd57adde48c3bf39e491f29e86083352c282f Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Mon, 13 Apr 2026 15:00:09 +0200 Subject: [PATCH 055/285] ITS: staggering (#15188) * ITS: staggered tracking Signed-off-by: Felix Schlepper * ITS: various fixes also for GPU Signed-off-by: Felix Schlepper * ITS: fix vertexer and move new types Signed-off-by: Felix Schlepper * ITS: format Signed-off-by: Felix Schlepper * ITS: account for layer ROF bias in tracker Signed-off-by: Felix Schlepper * ITS: sort tracks in time by lower edge Signed-off-by: Felix Schlepper * ITS: ensure mc labels are nullptr Signed-off-by: Felix Schlepper * ITSMFT: account for possible delay of received ROFs Signed-off-by: Felix Schlepper * ITS: staggered STF decoder Signed-off-by: Felix Schlepper * ITS: fix track time-assignment Signed-off-by: Felix Schlepper * ITS: output vertices Signed-off-by: Felix Schlepper * ITS: add macro to check staggering in data Signed-off-by: Felix Schlepper * Adapt ITS/MFT CTF machinery to staggered data * Fix compilation of ALICE3 tracking with staggering * ITS: modify staggering macro Signed-off-by: Felix Schlepper * ITSMFT: runtime staggering option Signed-off-by: Felix Schlepper * ITSMFT: fix instantiation in namespace Signed-off-by: Felix Schlepper * ITS3: fix compilation Signed-off-by: Felix Schlepper * Raw,CTF: add option to specify base cache dir for remote files Signed-off-by: Felix Schlepper * ITS: tracking same as dev Signed-off-by: Felix Schlepper * ITS: add back datastreams Signed-off-by: Felix Schlepper * ITSMFT: improve logging Signed-off-by: Felix Schlepper * ITS: add rofs for vertices back Signed-off-by: Felix Schlepper * add copyright to macro Signed-off-by: Felix Schlepper * ITS: hide print functions for device code Signed-off-by: Felix Schlepper * ITSMFT: add shim file for alpide param Signed-off-by: Felix Schlepper * try to fix macro compilation Signed-off-by: Felix Schlepper * Avoid wildcarded subspecs in Digit/ClusterWriter * ITS: fix rof lut to work properly with added errors Signed-off-by: Felix Schlepper * Fix/add some staggering options * Add ITS/MFT staggering options to dpl-workflow.sh To activate ITS or MFT staggering in the topology generation, export ITSSTAGGERED=1 or MFTSTAGGERED=1 respectively * ITS: try fix for QC Signed-off-by: Felix Schlepper * ITS: fix ROFLookpTables warning Signed-off-by: Felix Schlepper * ITS: fix tracklet formatting Signed-off-by: Felix Schlepper * ITS: set BCData properly for ROFs Signed-off-by: Felix Schlepper * ITS: remove deprecated settings Signed-off-by: Felix Schlepper * ITS: fix cluster label access for non-staggered Signed-off-by: Felix Schlepper * ITSMFT: fix staggering wfx option for digit-writer-workflow Signed-off-by: Felix Schlepper * Fix loop condition for ITS tracking layers * Fix/add some staggering options * Add ITS/MFT staggering options to dpl-workflow.sh To activate ITS or MFT staggering in the topology generation, export ITSSTAGGERED=1 or MFTSTAGGERED=1 respectively * ITSMFT: fix staggering wfx option for digit-writer-workflow Signed-off-by: Felix Schlepper * Fix loop condition for ITS tracking layers * Make ITS vertex messageable * remove unused variable * Add/fix staggering options to all workflows reading ITS,MFT clusters To pass the sim-challenge test. W/o this option even -h leads to a crash. Strictly speaking, one could use in the DPLAlpideParamInitializer::isITSStaggeringEnabled and DPLAlpideParamInitializer::isMFTStaggeringEnabled a test ic.options().hasOption(stagITSOpt) and ic.options().hasOption(stagMFTOpt) before testing the option itself. But better to have an explicit detection of missing staggering option. * ITSMFT: fix digit reader Signed-off-by: Felix Schlepper * Remove leftover NROFs configurable from dpl-workflow.sh * ITS: fix time assignments Signed-off-by: Felix Schlepper * ITS: fix degenerate LSE for matrix solving Comparing the output of dev and this PR, I saw plently of cases where the system of equation was fully degenerate and produced to different floating instructions and compiler optimizations slightly different results. The solution is to discard the vertex cand. if the LSE becomes degenerate as not to produce non-sense solutions. Signed-off-by: Felix Schlepper * ITS: fix macro Signed-off-by: Felix Schlepper * MFT: fix track writer Signed-off-by: Felix Schlepper * ITS: fix gpu compile due change in vertexer types Signed-off-by: Felix Schlepper * ITS: move lookup table creation to proper place Signed-off-by: Felix Schlepper * Move FastMultEstimation to ITS tracking library * ITS: add containedIn to TS Signed-off-by: Felix Schlepper * ITS: fix vertexer Signed-off-by: Felix Schlepper * ITS: improve STFDecoder&Clusterer error messages and account for delay longer that ROF Signed-off-by: Felix Schlepper * Implement new kind of multiplicity mask * Adapt GPU code to the new mult mask * ITS: finalize tracking code Signed-off-by: Felix Schlepper * ITS: remove deltaRof for vertexer Signed-off-by: Felix Schlepper * ITS: report current timeslice Signed-off-by: Felix Schlepper * Vertex: also print time error Signed-off-by: Felix Schlepper * ITS: speedup vertexer Signed-off-by: Felix Schlepper --------- Signed-off-by: Felix Schlepper Co-authored-by: shahoian Co-authored-by: Maximiliano Puccio --- .../GlobalTracking/src/RecoContainer.cxx | 2 +- .../Detectors/ITSMFT/ITS/CMakeLists.txt | 6 +- .../ITS/include/DataFormatsITS/TimeEstBC.h | 103 +++ .../ITS/include/DataFormatsITS/TrackITS.h | 26 +- .../ITS/include/DataFormatsITS/Vertex.h | 42 + .../ITSMFT/ITS/src/DataFormatsITSLinkDef.h | 7 + .../Detectors/ITSMFT/ITS/src/TimeEstBC.cxx | 13 + .../Detectors/ITSMFT/common/CMakeLists.txt | 7 +- .../common/include/DataFormatsITSMFT/CTF.h | 4 +- .../DataFormatsITSMFT/DPLAlpideParam.h | 104 +++ .../DPLAlpideParamInitializer.h | 42 + .../ITSMFT/common}/src/DPLAlpideParam.cxx | 11 +- .../common/src/DPLAlpideParamInitializer.cxx | 46 + .../common/src/ITSMFTDataFormatsLinkDef.h | 5 + .../ReconstructionDataFormats/Vertex.h | 25 +- .../include/CommonDataFormat/TimeStamp.h | 16 +- Detectors/AOD/src/AODProducerWorkflowSpec.cxx | 2 +- Detectors/AOD/src/aod-producer-workflow.cxx | 2 + .../Workflow/src/BarrelAlignmentSpec.cxx | 2 +- .../src/barrel-alignment-workflow.cxx | 2 + Detectors/CTF/test/test_ctf_io_itsmft.cxx | 4 +- .../include/CTFWorkflow/CTFReaderSpec.h | 3 + .../include/CTFWorkflow/CTFWriterSpec.h | 11 +- Detectors/CTF/workflow/src/CTFReaderSpec.cxx | 75 +- Detectors/CTF/workflow/src/CTFWriterSpec.cxx | 215 +++-- .../CTF/workflow/src/ctf-reader-workflow.cxx | 12 +- .../CTF/workflow/src/ctf-writer-workflow.cxx | 16 +- Detectors/Filtering/src/FilteringSpec.cxx | 2 +- .../Filtering/src/filtering-workflow.cxx | 2 + .../helpers/src/InputHelper.cxx | 7 +- .../src/CosmicsMatchingSpec.cxx | 2 +- .../src/GlobalFwdMatchingSpec.cxx | 2 +- .../src/PrimaryVertexingSpec.cxx | 2 +- .../src/TPCITSMatchingSpec.cxx | 2 +- .../src/VertexTrackMatcherSpec.cxx | 2 +- .../src/cosmics-match-workflow.cxx | 2 + .../src/globalfwd-matcher-workflow.cxx | 2 + .../src/secondary-vertexing-workflow.cxx | 2 + .../src/strangeness-tracking-workflow.cxx | 2 + .../src/tpcits-match-workflow.cxx | 3 + .../study/src/CheckResid.cxx | 2 +- .../study/src/DumpTracks.cxx | 2 +- .../study/src/SVStudy.cxx | 2 +- .../study/src/TrackMCStudy.cxx | 2 +- .../study/src/TrackingStudy.cxx | 2 +- .../study/src/check-resid-workflow.cxx | 2 + .../study/src/its-offset-study-workflow.cxx | 2 + .../study/src/trackMCStudy-workflow.cxx | 2 + .../study/src/tracking-study-workflow.cxx | 2 + .../src/tpc-interpolation-workflow.cxx | 2 + .../ITSMFT/ITS/macros/test/CMakeLists.txt | 10 + Detectors/ITSMFT/ITS/macros/test/CheckDROF.C | 21 +- .../ITSMFT/ITS/macros/test/CheckStaggering.C | 521 +++++++++++ .../studies/src/ImpactParameter.cxx | 2 +- .../standalone-postprocessing-workflow.cxx | 6 +- .../ITSMFT/ITS/reconstruction/CMakeLists.txt | 6 +- .../include/ITSReconstruction/FastMultEst.h | 70 -- .../ITSReconstruction/TrivialVertexer.h | 70 -- .../ITS/reconstruction/src/FastMultEst.cxx | 189 ---- .../src/ITSReconstructionLinkDef.h | 3 - .../reconstruction/src/TrivialVertexer.cxx | 108 --- Detectors/ITSMFT/ITS/tracking/CMakeLists.txt | 8 +- .../GPU/ITStrackingGPU/ClusterLinesGPU.h | 73 -- .../GPU/ITStrackingGPU/TimeFrameChunk.h | 148 --- .../GPU/ITStrackingGPU/TimeFrameGPU.h | 161 ++-- .../tracking/GPU/ITStrackingGPU/TracerGPU.h | 38 - .../GPU/ITStrackingGPU/TrackerTraitsGPU.h | 15 +- .../GPU/ITStrackingGPU/TrackingKernels.h | 75 +- .../ITS/tracking/GPU/ITStrackingGPU/Utils.h | 6 +- .../GPU/ITStrackingGPU/VertexerTraitsGPU.h | 55 -- .../GPU/ITStrackingGPU/VertexingKernels.h | 115 --- .../ITS/tracking/GPU/cuda/CMakeLists.txt | 19 +- .../ITS/tracking/GPU/cuda/ClusterLinesGPU.cu | 138 --- .../ITS/tracking/GPU/cuda/TimeFrameChunk.cu | 293 ------ .../ITS/tracking/GPU/cuda/TimeFrameGPU.cu | 483 +++++----- .../ITSMFT/ITS/tracking/GPU/cuda/TracerGPU.cu | 48 - .../tracking/GPU/cuda/TrackerTraitsGPU.cxx | 129 ++- .../ITS/tracking/GPU/cuda/TrackingKernels.cu | 289 +++--- .../tracking/GPU/cuda/VertexerTraitsGPU.cxx | 179 ---- .../ITS/tracking/GPU/cuda/VertexingKernels.cu | 660 -------------- .../ITS/tracking/GPU/hip/CMakeLists.txt | 6 +- .../include/ITStracking/BoundedAllocator.h | 3 + .../ITS/tracking/include/ITStracking/Cell.h | 39 +- .../tracking/include/ITStracking/Cluster.h | 5 +- .../include/ITStracking/ClusterLines.h | 221 +---- .../include/ITStracking/Configuration.h | 42 +- .../tracking/include/ITStracking/Constants.h | 2 +- .../include/ITStracking/Definitions.h | 38 +- .../include/ITStracking/FastMultEst.h | 93 ++ .../include/ITStracking}/FastMultEstConfig.h | 30 +- .../tracking/include/ITStracking/MathUtils.h | 33 +- .../include/ITStracking/ROFLookupTables.h | 850 ++++++++++++++++++ .../ITS/tracking/include/ITStracking/Road.h | 72 -- .../tracking/include/ITStracking/Smoother.h | 60 -- .../tracking/include/ITStracking/TimeFrame.h | 480 ++++------ .../tracking/include/ITStracking/Tracker.h | 31 +- .../include/ITStracking/TrackerTraits.h | 50 +- .../include/ITStracking/TrackingConfigParam.h | 28 +- .../include/ITStracking/TrackingInterface.h | 5 + .../tracking/include/ITStracking/Tracklet.h | 46 +- .../tracking/include/ITStracking/Vertexer.h | 15 +- .../include/ITStracking/VertexerTraits.h | 31 +- .../ITSMFT/ITS/tracking/src/ClusterLines.cxx | 453 +++------- .../ITSMFT/ITS/tracking/src/Configuration.cxx | 60 +- .../ITSMFT/ITS/tracking/src/FastMultEst.cxx | 252 ++++++ .../src/FastMultEstConfig.cxx | 2 +- .../ITS/tracking/src/IndexTableUtils.cxx | 49 - .../ITSMFT/ITS/tracking/src/Smoother.cxx | 222 ----- .../ITSMFT/ITS/tracking/src/TimeFrame.cxx | 426 +++------ Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx | 307 ++----- .../ITSMFT/ITS/tracking/src/TrackerTraits.cxx | 576 +++--------- .../ITS/tracking/src/TrackingInterface.cxx | 397 ++++---- .../ITSMFT/ITS/tracking/src/TrackingLinkDef.h | 7 + .../ITSMFT/ITS/tracking/src/Vertexer.cxx | 60 +- .../ITS/tracking/src/VertexerTraits.cxx | 742 +++++---------- .../ITSMFT/ITS/tracking/test/CMakeLists.txt | 8 +- .../ITS/tracking/test/testROFLookupTables.cxx | 744 +++++++++++++++ .../ITSWorkflow/ClusterWriterWorkflow.h | 2 +- .../include/ITSWorkflow/DCSAdaposParserSpec.h | 2 +- .../include/ITSWorkflow/RecoWorkflow.h | 2 +- .../include/ITSWorkflow/TrackReaderSpec.h | 27 +- .../include/ITSWorkflow/TrackerSpec.h | 3 +- .../include/ITSWorkflow/VertexReaderSpec.h | 2 +- .../workflow/src/ClusterWriterWorkflow.cxx | 4 +- .../ITSMFT/ITS/workflow/src/RecoWorkflow.cxx | 11 +- .../ITS/workflow/src/TrackReaderSpec.cxx | 34 +- .../ITS/workflow/src/TrackWriterSpec.cxx | 21 +- .../ITS/workflow/src/TrackWriterWorkflow.cxx | 4 +- .../ITSMFT/ITS/workflow/src/TrackerSpec.cxx | 49 +- .../src/its-cluster-reader-workflow.cxx | 7 +- .../src/its-cluster-writer-workflow.cxx | 8 +- .../ITS/workflow/src/its-reco-workflow.cxx | 25 +- .../src/its-track-writer-workflow.cxx | 3 +- .../calibration/src/NoiseCalibratorSpec.cxx | 2 +- .../include/MFTCondition/DCSConfigReader.h | 2 +- .../include/MFTWorkflow/RecoWorkflow.h | 1 + .../include/MFTWorkflow/TrackerSpec.h | 2 +- .../ITSMFT/MFT/workflow/src/RecoWorkflow.cxx | 7 +- .../MFT/workflow/src/TrackWriterSpec.cxx | 11 +- .../ITSMFT/MFT/workflow/src/TrackerSpec.cxx | 13 +- .../src/mft-cluster-reader-workflow.cxx | 5 +- .../src/mft-cluster-writer-workflow.cxx | 8 +- .../MFT/workflow/src/mft-reco-workflow.cxx | 4 + Detectors/ITSMFT/common/base/CMakeLists.txt | 4 +- .../base/include/ITSMFTBase/DPLAlpideParam.h | 111 +-- .../common/base/src/ITSMFTBaseLinkDef.h | 5 - .../include/ITSMFTReconstruction/CTFCoder.h | 40 +- .../ITSMFTReconstruction/ChipMappingITS.h | 5 +- .../ITSMFTReconstruction/ChipMappingMFT.h | 3 + .../ITSMFTReconstruction/PixelReader.h | 8 +- .../ITSMFTReconstruction/RawPixelDecoder.h | 11 +- .../ITSMFTReconstruction/RawPixelReader.h | 7 +- .../common/reconstruction/src/CTFCoder.cxx | 42 +- .../reconstruction/src/ChipMappingITS.cxx | 24 +- .../reconstruction/src/ChipMappingMFT.cxx | 14 + .../common/reconstruction/src/Clusterer.cxx | 2 +- .../common/reconstruction/src/GBTLink.cxx | 2 +- .../reconstruction/src/RawPixelDecoder.cxx | 43 +- .../include/ITSMFTSimulation/DigiParams.h | 2 +- .../ITSMFTWorkflow/ClusterReaderSpec.h | 41 +- .../ITSMFTWorkflow/ClusterWriterSpec.h | 6 +- .../include/ITSMFTWorkflow/ClustererSpec.h | 11 +- .../include/ITSMFTWorkflow/DigitReaderSpec.h | 47 +- .../include/ITSMFTWorkflow/DigitWriterSpec.h | 4 +- .../ITSMFTWorkflow/EntropyDecoderSpec.h | 17 +- .../ITSMFTWorkflow/EntropyEncoderSpec.h | 16 +- .../include/ITSMFTWorkflow/STFDecoderSpec.h | 30 +- .../common/workflow/src/ClusterReaderSpec.cxx | 47 +- .../common/workflow/src/ClusterWriterSpec.cxx | 57 +- .../common/workflow/src/ClustererSpec.cxx | 81 +- .../common/workflow/src/DigitReaderSpec.cxx | 77 +- .../common/workflow/src/DigitWriterSpec.cxx | 54 +- .../workflow/src/EntropyDecoderSpec.cxx | 172 ++-- .../workflow/src/EntropyEncoderSpec.cxx | 118 ++- .../common/workflow/src/STFDecoderSpec.cxx | 394 +++++--- .../workflow/src/digit-reader-workflow.cxx | 8 +- .../workflow/src/digit-writer-workflow.cxx | 9 +- .../workflow/src/entropy-encoder-workflow.cxx | 9 +- .../workflow/src/stf-decoder-workflow.cxx | 8 +- Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx | 2 +- Detectors/Raw/TFReaderDD/src/TFReaderSpec.h | 1 + .../Raw/TFReaderDD/src/tf-reader-workflow.cxx | 2 + .../workflow/src/trd-tracking-workflow.cxx | 2 + .../include/TRKReconstruction/TimeFrame.h | 9 +- .../TRK/reconstruction/src/TimeFrame.cxx | 93 +- .../ALICE3/TRK/workflow/src/TrackerSpec.cxx | 92 +- .../ITS3Reconstruction/TrackingInterface.h | 1 + .../ITS3/reconstruction/src/IOUtils.cxx | 10 +- .../reconstruction/src/TrackingInterface.cxx | 5 +- .../ITS3/workflow/src/ClustererSpec.cxx | 2 +- .../ITS3/workflow/src/RecoWorkflow.cxx | 2 +- .../ITS3/workflow/src/TrackerSpec.cxx | 4 +- .../include/DetectorsVertexing/PVertexer.h | 2 +- .../Vertexing/src/VertexTrackMatcher.cxx | 2 +- Detectors/Vertexing/test/PVFromPool.C | 13 +- .../Workflow/src/EveWorkflowHelper.cxx | 2 +- .../Workflow/src/O2DPLDisplay.cxx | 3 +- .../Base/GPUReconstructionIncludesITS.h | 5 - .../Base/cuda/GPUReconstructionCUDA.cu | 6 +- GPU/GPUTracking/Global/GPUChainITS.h | 3 - .../display/render/GPUDisplayImportEvent.cxx | 2 +- .../include/GPUWorkflow/GPUWorkflowSpec.h | 1 + GPU/Workflow/src/GPUWorkflowITS.cxx | 9 +- GPU/Workflow/src/GPUWorkflowSpec.cxx | 16 +- GPU/Workflow/src/O2GPUDPLDisplay.cxx | 3 +- GPU/Workflow/src/gpu-reco-workflow.cxx | 3 + .../src/ITS3DigitizerSpec.cxx | 2 +- .../src/ITSMFTDigitizerSpec.cxx | 99 +- .../src/ITSMFTDigitizerSpec.h | 4 +- .../src/SimpleDigitizerWorkflow.cxx | 12 +- doc/data/2021-02-o2_prs.json | 2 +- doc/data/2022-01-o2_prs.json | 2 +- macro/run_rawdecoding_its.C | 21 +- macro/run_rawdecoding_mft.C | 21 +- prodtests/full-system-test/dpl-workflow.sh | 30 +- prodtests/full_system_test.sh | 4 - 216 files changed, 6352 insertions(+), 7206 deletions(-) create mode 100644 DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TimeEstBC.h create mode 100644 DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/Vertex.h create mode 100644 DataFormats/Detectors/ITSMFT/ITS/src/TimeEstBC.cxx create mode 100644 DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/DPLAlpideParam.h create mode 100644 DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/DPLAlpideParamInitializer.h rename {Detectors/ITSMFT/common/base => DataFormats/Detectors/ITSMFT/common}/src/DPLAlpideParam.cxx (82%) create mode 100644 DataFormats/Detectors/ITSMFT/common/src/DPLAlpideParamInitializer.cxx create mode 100644 Detectors/ITSMFT/ITS/macros/test/CheckStaggering.C delete mode 100644 Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEst.h delete mode 100644 Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/TrivialVertexer.h delete mode 100644 Detectors/ITSMFT/ITS/reconstruction/src/FastMultEst.cxx delete mode 100644 Detectors/ITSMFT/ITS/reconstruction/src/TrivialVertexer.cxx delete mode 100644 Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/ClusterLinesGPU.h delete mode 100644 Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameChunk.h delete mode 100644 Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TracerGPU.h delete mode 100644 Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexerTraitsGPU.h delete mode 100644 Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexingKernels.h delete mode 100644 Detectors/ITSMFT/ITS/tracking/GPU/cuda/ClusterLinesGPU.cu delete mode 100644 Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameChunk.cu delete mode 100644 Detectors/ITSMFT/ITS/tracking/GPU/cuda/TracerGPU.cu delete mode 100644 Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexerTraitsGPU.cxx delete mode 100644 Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexingKernels.cu create mode 100644 Detectors/ITSMFT/ITS/tracking/include/ITStracking/FastMultEst.h rename Detectors/ITSMFT/ITS/{reconstruction/include/ITSReconstruction => tracking/include/ITStracking}/FastMultEstConfig.h (58%) create mode 100644 Detectors/ITSMFT/ITS/tracking/include/ITStracking/ROFLookupTables.h delete mode 100644 Detectors/ITSMFT/ITS/tracking/include/ITStracking/Road.h delete mode 100644 Detectors/ITSMFT/ITS/tracking/include/ITStracking/Smoother.h create mode 100644 Detectors/ITSMFT/ITS/tracking/src/FastMultEst.cxx rename Detectors/ITSMFT/ITS/{reconstruction => tracking}/src/FastMultEstConfig.cxx (94%) delete mode 100644 Detectors/ITSMFT/ITS/tracking/src/IndexTableUtils.cxx delete mode 100644 Detectors/ITSMFT/ITS/tracking/src/Smoother.cxx create mode 100644 Detectors/ITSMFT/ITS/tracking/test/testROFLookupTables.cxx diff --git a/DataFormats/Detectors/GlobalTracking/src/RecoContainer.cxx b/DataFormats/Detectors/GlobalTracking/src/RecoContainer.cxx index dd206ffe3b70d..277466fb2e969 100644 --- a/DataFormats/Detectors/GlobalTracking/src/RecoContainer.cxx +++ b/DataFormats/Detectors/GlobalTracking/src/RecoContainer.cxx @@ -34,7 +34,7 @@ #include "ReconstructionDataFormats/TrackMCHMID.h" #include "DataFormatsITSMFT/TrkClusRef.h" #include "DataFormatsITSMFT/TopologyDictionary.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" // FIXME: ideally, the data formats definition should be independent of the framework // collectData is using the input of ProcessingContext to extract the first valid // header and the TF orbit from it diff --git a/DataFormats/Detectors/ITSMFT/ITS/CMakeLists.txt b/DataFormats/Detectors/ITSMFT/ITS/CMakeLists.txt index 5a353881e27ba..f05979d749fc0 100644 --- a/DataFormats/Detectors/ITSMFT/ITS/CMakeLists.txt +++ b/DataFormats/Detectors/ITSMFT/ITS/CMakeLists.txt @@ -11,8 +11,12 @@ o2_add_library(DataFormatsITS SOURCES src/TrackITS.cxx + src/TimeEstBC.cxx PUBLIC_LINK_LIBRARIES O2::ReconstructionDataFormats + O2::SimulationDataFormat O2::DataFormatsITSMFT) o2_target_root_dictionary(DataFormatsITS - HEADERS include/DataFormatsITS/TrackITS.h) + HEADERS include/DataFormatsITS/TrackITS.h + include/DataFormatsITS/Vertex.h + include/DataFormatsITS/TimeEstBC.h) diff --git a/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TimeEstBC.h b/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TimeEstBC.h new file mode 100644 index 0000000000000..695d9aff42858 --- /dev/null +++ b/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TimeEstBC.h @@ -0,0 +1,103 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_TRACKINGITS_TIMEESTBC_H_ +#define O2_TRACKINGITS_TIMEESTBC_H_ + +#include +#include +#include "CommonDataFormat/TimeStamp.h" +#include "GPUCommonRtypes.h" +#include "GPUCommonDef.h" +#include "GPUCommonMath.h" + +namespace o2::its +{ +// Time estimates are given in BC +// error needs to cover maximum 1 orbit +using TimeStampType = uint32_t; +using TimeStampErrorType = uint16_t; +// this is an symmetric time error [t0-tE, t0+tE] +using TimeStamp = o2::dataformats::TimeStampWithError; +// this is an asymmetric time interval [t0, t0+tE] used for internal calculations +class TimeEstBC : public o2::dataformats::TimeStampWithError +{ + using Base = o2::dataformats::TimeStampWithError; + + public: + GPUhdDefault() TimeEstBC() = default; + GPUhdi() TimeEstBC(TimeStampType t, TimeStampErrorType e) : Base(t, e) {} + + // convert to symmetric center+-half representation + GPUhdi() its::TimeStamp makeSymmetrical() const noexcept + { + const auto start = static_cast(this->getTimeStamp()); + const float half = (float)this->getTimeStampError() / 2.f; + return {start + half, half}; + } + + // check if timestamps overlap within their interval + GPUhdi() bool isCompatible(const TimeEstBC& o) const noexcept + { + return this->upper() > o.lower() && o.upper() > this->lower(); + } + + // check if this time interval is fully contained within o + GPUhdi() bool isContainedIn(const TimeEstBC& o) const noexcept + { + return this->lower() >= o.lower() && this->upper() <= o.upper(); + } + + GPUhdi() TimeEstBC& operator+=(const TimeEstBC& o) noexcept + { + add(o); + return *this; + } + + GPUhdi() TimeEstBC operator+(const TimeEstBC& o) const noexcept + { + TimeEstBC res = *this; + res += o; + return res; + } + + // upper bound of interval t0+tE + GPUhdi() TimeStampType upper() const noexcept + { + TimeStampType t = this->getTimeStamp(); + TimeStampType e = this->getTimeStampError(); + constexpr TimeStampType max = std::numeric_limits::max(); + return (t > (max - e)) ? max : t + e; + } + + // lower bound of interval t0 + GPUhdi() TimeStampType lower() const noexcept + { + return this->getTimeStamp(); + } + + private: + // intersect with the other timestamp + // this assumes already that both overlap + GPUhdi() void add(const TimeEstBC& o) noexcept + { + const TimeStampType lo = o2::gpu::CAMath::Max(this->lower(), o.lower()); + const TimeStampType hi = o2::gpu::CAMath::Min(this->upper(), o.upper()); + this->setTimeStamp(lo); + this->setTimeStampError(static_cast(hi - lo)); + } + + ClassDefNV(TimeEstBC, 1); +}; + +} // namespace o2::its + +#endif diff --git a/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h b/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h index 06d4fba51bd54..5d13ad753b8bc 100644 --- a/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h +++ b/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// 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. // @@ -16,11 +16,12 @@ #ifndef ALICEO2_ITS_TRACKITS_H #define ALICEO2_ITS_TRACKITS_H -#include +#include #include "GPUCommonDef.h" #include "ReconstructionDataFormats/Track.h" #include "CommonDataFormat/RangeReference.h" +#include "DataFormatsITS/TimeEstBC.h" namespace o2 { @@ -35,8 +36,7 @@ namespace its class TrackITS : public o2::track::TrackParCov { enum UserBits { - kNextROF = 1 << 28, - kSharedClusters = 1 << 29 + kSharedClusters = 1 << 28 }; using Cluster = o2::itsmft::Cluster; @@ -93,6 +93,9 @@ class TrackITS : public o2::track::TrackParCov bool isBetter(const TrackITS& best, float maxChi2) const; + GPUhdi() auto& getTimeStamp() { return mTime; } + GPUhdi() const auto& getTimeStamp() const { return mTime; } + GPUhdi() o2::track::TrackParCov& getParamIn() { return *this; } GPUhdi() const o2::track::TrackParCov& getParamIn() const { return *this; } @@ -122,8 +125,6 @@ class TrackITS : public o2::track::TrackParCov } int getNFakeClusters() const; - void setNextROFbit(bool toggle = true) { mClusterSizes = toggle ? (mClusterSizes | kNextROF) : (mClusterSizes & ~kNextROF); } - bool hasHitInNextROF() const { return mClusterSizes & kNextROF; } void setSharedClusters(bool toggle = true) { mClusterSizes = toggle ? (mClusterSizes | kSharedClusters) : (mClusterSizes & ~kSharedClusters); } bool hasSharedClusters() const { return mClusterSizes & kSharedClusters; } @@ -157,9 +158,10 @@ class TrackITS : public o2::track::TrackParCov ClusRefs mClusRef; ///< references on clusters float mChi2 = 0.; ///< Chi2 for this track uint32_t mPattern = 0; ///< layers pattern - unsigned int mClusterSizes = 0u; + uint32_t mClusterSizes = 0u; ///< 4bit packed cluster sizes + TimeStamp mTime; ///< track time stamp with error in BC since start of TF, symmetrical - ClassDefNV(TrackITS, 6); + ClassDefNV(TrackITS, 7); }; class TrackITSExt : public TrackITS @@ -169,15 +171,13 @@ class TrackITSExt : public TrackITS static constexpr int MaxClusters = 16; /// Prepare for overlaps and new detector configurations using TrackITS::TrackITS; // inherit base constructors - GPUh() TrackITSExt(o2::track::TrackParCov&& parCov, short ncl, float chi2, - o2::track::TrackParCov&& outer, std::array cls) + GPUh() TrackITSExt(o2::track::TrackParCov&& parCov, short ncl, float chi2, o2::track::TrackParCov&& outer, std::array cls) : TrackITS(parCov, chi2, outer), mIndex{cls} { setNumberOfClusters(ncl); } - GPUh() TrackITSExt(o2::track::TrackParCov& parCov, short ncl, float chi2, std::uint32_t rof, - o2::track::TrackParCov& outer, std::array cls) + GPUh() TrackITSExt(o2::track::TrackParCov& parCov, short ncl, float chi2, std::uint32_t rof, o2::track::TrackParCov& outer, std::array cls) : TrackITS(parCov, chi2, outer), mIndex{cls} { setNumberOfClusters(ncl); @@ -212,7 +212,7 @@ class TrackITSExt : public TrackITS private: std::array mIndex = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; ///< Indices of associated clusters - ClassDefNV(TrackITSExt, 2); + ClassDefNV(TrackITSExt, 3); }; } // namespace its } // namespace o2 diff --git a/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/Vertex.h b/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/Vertex.h new file mode 100644 index 0000000000000..1e4ed03b753eb --- /dev/null +++ b/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/Vertex.h @@ -0,0 +1,42 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_TRACKINGITS_VERTEX_H_ +#define O2_TRACKINGITS_VERTEX_H_ + +#include "GPUCommonDef.h" +#ifndef GPUCA_GPUCODE_DEVICE +#include +#endif +#include "ReconstructionDataFormats/Vertex.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "DataFormatsITS/TimeEstBC.h" + +namespace o2::its +{ +// NOTE: this uses the internal asymmetrical time reprenstation! +using Vertex = o2::dataformats::Vertex; +using VertexLabel = std::pair; +} // namespace o2::its + +#ifndef GPUCA_GPUCODE_DEVICE +/// Defining ITS Vertex explicitly as messageable +namespace o2::framework +{ +template +struct is_messageable; +template <> +struct is_messageable> : std::true_type { +}; +} // namespace o2::framework +#endif + +#endif diff --git a/DataFormats/Detectors/ITSMFT/ITS/src/DataFormatsITSLinkDef.h b/DataFormats/Detectors/ITSMFT/ITS/src/DataFormatsITSLinkDef.h index 91a71847148fb..a0d5b25c65b70 100644 --- a/DataFormats/Detectors/ITSMFT/ITS/src/DataFormatsITSLinkDef.h +++ b/DataFormats/Detectors/ITSMFT/ITS/src/DataFormatsITSLinkDef.h @@ -14,7 +14,14 @@ #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; + #pragma link C++ class o2::its::TrackITS + ; #pragma link C++ class std::vector < o2::its::TrackITS> + ; +#pragma link C++ class o2::its::TimeEstBC + ; +#pragma link C++ class std::vector < o2::its::TimeEstBC> + ; + +#pragma link C++ class o2::dataformats::Vertex < o2::its::TimeEstBC> + ; +#pragma link C++ class std::vector < o2::dataformats::Vertex < o2::its::TimeEstBC>> + ; + #endif diff --git a/DataFormats/Detectors/ITSMFT/ITS/src/TimeEstBC.cxx b/DataFormats/Detectors/ITSMFT/ITS/src/TimeEstBC.cxx new file mode 100644 index 0000000000000..3af299cf74d25 --- /dev/null +++ b/DataFormats/Detectors/ITSMFT/ITS/src/TimeEstBC.cxx @@ -0,0 +1,13 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DataFormatsITS/TimeEstBC.h" +ClassImp(o2::its::TimeEstBC); \ No newline at end of file diff --git a/DataFormats/Detectors/ITSMFT/common/CMakeLists.txt b/DataFormats/Detectors/ITSMFT/common/CMakeLists.txt index 96d376526a1a4..a619f8ad0081d 100644 --- a/DataFormats/Detectors/ITSMFT/common/CMakeLists.txt +++ b/DataFormats/Detectors/ITSMFT/common/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# 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. # @@ -20,13 +20,18 @@ o2_add_library(DataFormatsITSMFT src/TopologyDictionary.cxx src/TimeDeadMap.cxx src/CTF.cxx + src/DPLAlpideParam.cxx + src/DPLAlpideParamInitializer.cxx PUBLIC_LINK_LIBRARIES O2::ITSMFTBase + O2::DetectorsCommonDataFormats O2::ReconstructionDataFormats + O2::CommonUtils Microsoft.GSL::GSL) o2_target_root_dictionary(DataFormatsITSMFT HEADERS include/DataFormatsITSMFT/ROFRecord.h include/DataFormatsITSMFT/Digit.h + include/DataFormatsITSMFT/DPLAlpideParam.h include/DataFormatsITSMFT/GBTCalibData.h include/DataFormatsITSMFT/NoiseMap.h include/DataFormatsITSMFT/TimeDeadMap.h diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/CTF.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/CTF.h index 314523aa878ba..0510b6df5225c 100644 --- a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/CTF.h +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/CTF.h @@ -36,7 +36,9 @@ struct CTFHeader : public o2::ctf::CTFDictHeader { uint32_t nPatternBytes = 0; /// number of bytes for explict patterns uint32_t firstOrbit = 0; /// 1st orbit of TF uint16_t firstBC = 0; /// 1st BC of TF - ClassDefNV(CTFHeader, 2); + uint8_t maxStreams = 1; /// Number of streams per TF (== NLayers for staggered ITS/MFT readout, 1 for non-staggered one) + uint8_t streamID = 0; /// ID of the stream (0:maxStreams-1) + ClassDefNV(CTFHeader, 3); }; /// Compressed but not yet entropy-encoded clusters diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/DPLAlpideParam.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/DPLAlpideParam.h new file mode 100644 index 0000000000000..a06ba0745edbd --- /dev/null +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/DPLAlpideParam.h @@ -0,0 +1,104 @@ +// 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_ITSMFTALPIDEPARAM_H_ +#define ALICEO2_ITSMFTALPIDEPARAM_H_ + +#include "DetectorsCommonDataFormats/DetID.h" +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" +#include "CommonConstants/LHCConstants.h" +#include + +namespace o2 +{ +namespace itsmft +{ +constexpr float DEFStrobeDelay = o2::constants::lhc::LHCBunchSpacingNS * 4; // ~100 ns delay + +template +struct DPLAlpideParam : public o2::conf::ConfigurableParamHelper> { + static constexpr int getNLayers() + { + return N == o2::detectors::DetID::ITS ? 7 : 10; + } + + static constexpr std::string_view getParamName() + { + return N == o2::detectors::DetID::ITS ? ParamName[0] : ParamName[1]; + } + + int roFrameLengthInBC = DEFROFLengthBC(); ///< ROF length in BC for continuous mode + float roFrameLengthTrig = DEFROFLengthTrig(); ///< length of RO frame in ns for triggered mode + float strobeDelay = DEFStrobeDelay; ///< strobe start (in ns) wrt ROF start + float strobeLengthCont = -1.; ///< if < 0, full ROF length - delay + float strobeLengthTrig = 100.; ///< length of the strobe in ns (sig. over threshold checked in this window only) + int roFrameBiasInBC = DEFROFBiasInBC(); ///< bias of the start of ROF wrt orbit start: t_irof = (irof*roFrameLengthInBC + roFrameBiasInBC)*BClengthMUS + int roFrameLayerLengthInBC[getNLayers()] = {}; ///< staggering ROF length in BC for continuous mode per layer + int roFrameLayerBiasInBC[getNLayers()] = {}; ///< staggering ROF bias in BC for continuous mode per layer + int roFrameLayerDelayInBC[getNLayers()] = {}; ///< staggering ROF delay in BC for continuous mode per layer + + // get ROF length for any layer + int getROFLengthInBC(int layer) const noexcept { return roFrameLayerLengthInBC[layer] ? roFrameLayerLengthInBC[layer] : roFrameLengthInBC; } + int getROFBiasInBC(int layer) const noexcept { return roFrameLayerBiasInBC[layer] ? roFrameLayerBiasInBC[layer] : roFrameBiasInBC; } + int getROFDelayInBC(int layer) const noexcept { return roFrameLayerDelayInBC[layer] ? roFrameLayerDelayInBC[layer] : 0; } + + // boilerplate stuff + make principal key + O2ParamDef(DPLAlpideParam, getParamName().data()); + + private: + static constexpr std::string_view ParamName[2] = {"ITSAlpideParam", "MFTAlpideParam"}; + + static constexpr int DEFROFLengthBC() + { + // default ROF length in BC for continuous mode + // allowed values: 1,2,3,4,6,9,11,12,18,22,27,33,36 + return N == o2::detectors::DetID::ITS ? o2::constants::lhc::LHCMaxBunches / 4 : o2::constants::lhc::LHCMaxBunches / 18; + } + static constexpr float DEFROFLengthTrig() + { + // length of RO frame in ns for triggered mode + return N == o2::detectors::DetID::ITS ? 6000. : 6000.; + } + + static constexpr int DEFROFBiasInBC() + { + // default ROF length bias in MC, see https://github.com/AliceO2Group/AliceO2/pull/11108 for ITS + return N == o2::detectors::DetID::ITS ? 64 : 60; + } + + static_assert(N == o2::detectors::DetID::ITS || N == o2::detectors::DetID::MFT, "only DetID::ITS orDetID:: MFT are allowed"); + static_assert(o2::constants::lhc::LHCMaxBunches % DEFROFLengthBC() == 0); // make sure ROF length is divisor of the orbit +}; + +template +DPLAlpideParam DPLAlpideParam::sInstance; + +} // namespace itsmft + +namespace framework +{ +template +struct is_messageable; +template <> +struct is_messageable> : std::true_type { +}; +template +struct is_messageable; +template <> +struct is_messageable> : std::true_type { +}; + +} // namespace framework + +} // namespace o2 + +#endif diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/DPLAlpideParamInitializer.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/DPLAlpideParamInitializer.h new file mode 100644 index 0000000000000..b3ec20f2a68b7 --- /dev/null +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/DPLAlpideParamInitializer.h @@ -0,0 +1,42 @@ +// 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_ITSMFTALPIDEPARAM_INITIALIZER_H_ +#define ALICEO2_ITSMFTALPIDEPARAM_INITIALIZER_H_ +#include + +namespace o2 +{ +namespace framework +{ +class ConfigParamSpec; +class ConfigContext; +} // namespace framework +namespace itsmft +{ + +struct DPLAlpideParamInitializer { + static constexpr char stagITSOpt[] = "enable-its-staggering"; + static constexpr char stagMFTOpt[] = "enable-mft-staggering"; + static constexpr bool stagDef = false; + + // DPL workflow options for staggering + static void addConfigOption(std::vector& opts); + static void addITSConfigOption(std::vector& opts); + static bool isITSStaggeringEnabled(o2::framework::ConfigContext const& cfgc); + static void addMFTConfigOption(std::vector& opts); + static bool isMFTStaggeringEnabled(o2::framework::ConfigContext const& cfgc); +}; + +} // namespace itsmft +} // namespace o2 + +#endif diff --git a/Detectors/ITSMFT/common/base/src/DPLAlpideParam.cxx b/DataFormats/Detectors/ITSMFT/common/src/DPLAlpideParam.cxx similarity index 82% rename from Detectors/ITSMFT/common/base/src/DPLAlpideParam.cxx rename to DataFormats/Detectors/ITSMFT/common/src/DPLAlpideParam.cxx index 1cb9bdf997d68..205f8a008a661 100644 --- a/Detectors/ITSMFT/common/base/src/DPLAlpideParam.cxx +++ b/DataFormats/Detectors/ITSMFT/common/src/DPLAlpideParam.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// 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. // @@ -9,15 +9,12 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" -namespace o2 -{ -namespace itsmft +namespace o2::itsmft { // this makes sure that the constructor of the parameters is statically called // so that these params are part of the parameter database static auto& sAlpideParamITS = o2::itsmft::DPLAlpideParam::Instance(); static auto& sAlpideParamMFT = o2::itsmft::DPLAlpideParam::Instance(); -} // namespace itsmft -} // namespace o2 +} // namespace o2::itsmft diff --git a/DataFormats/Detectors/ITSMFT/common/src/DPLAlpideParamInitializer.cxx b/DataFormats/Detectors/ITSMFT/common/src/DPLAlpideParamInitializer.cxx new file mode 100644 index 0000000000000..715ec5d90b813 --- /dev/null +++ b/DataFormats/Detectors/ITSMFT/common/src/DPLAlpideParamInitializer.cxx @@ -0,0 +1,46 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" +#include "Framework/ConfigParamsHelper.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/ConfigContext.h" + +namespace o2::itsmft +{ + +void DPLAlpideParamInitializer::addConfigOption(std::vector& opts) +{ + addITSConfigOption(opts); + addMFTConfigOption(opts); +} + +void DPLAlpideParamInitializer::addITSConfigOption(std::vector& opts) +{ + o2::framework::ConfigParamsHelper::addOptionIfMissing(opts, {stagITSOpt, o2::framework::VariantType::Bool, stagDef, {"enable per layer ITS in&out-put for staggered readout"}}); +} + +void DPLAlpideParamInitializer::addMFTConfigOption(std::vector& opts) +{ + o2::framework::ConfigParamsHelper::addOptionIfMissing(opts, {stagMFTOpt, o2::framework::VariantType::Bool, stagDef, {"enable per layer MFT in&out-put for staggered readout"}}); +} + +bool DPLAlpideParamInitializer::isITSStaggeringEnabled(const o2::framework::ConfigContext& cfgc) +{ + return cfgc.options().get(stagITSOpt); +} + +bool DPLAlpideParamInitializer::isMFTStaggeringEnabled(const o2::framework::ConfigContext& cfgc) +{ + return cfgc.options().get(stagMFTOpt); +} + +} // namespace o2::itsmft diff --git a/DataFormats/Detectors/ITSMFT/common/src/ITSMFTDataFormatsLinkDef.h b/DataFormats/Detectors/ITSMFT/common/src/ITSMFTDataFormatsLinkDef.h index fc67fdf028436..1b1918b46c9d4 100644 --- a/DataFormats/Detectors/ITSMFT/common/src/ITSMFTDataFormatsLinkDef.h +++ b/DataFormats/Detectors/ITSMFT/common/src/ITSMFTDataFormatsLinkDef.h @@ -15,6 +15,11 @@ #pragma link off all classes; #pragma link off all functions; +#pragma link C++ class o2::itsmft::DPLAlpideParam < o2::detectors::DetID::ITS> + ; +#pragma link C++ class o2::itsmft::DPLAlpideParam < o2::detectors::DetID::MFT> + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::itsmft::DPLAlpideParam < o2::detectors::DetID::ITS>> + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::itsmft::DPLAlpideParam < o2::detectors::DetID::MFT>> + ; + #pragma link C++ class o2::itsmft::Digit + ; #pragma link C++ class o2::itsmft::NoiseMap + ; #pragma link C++ class o2::itsmft::TimeDeadMap + ; diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h index cb1c9d5d87c7f..588a23d25a000 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h @@ -152,7 +152,10 @@ class Vertex : public VertexBase std::string asString() const; #endif - GPUd() ushort getNContributors() const { return mNContributors; } + GPUd() ushort getNContributors() const + { + return mNContributors; + } GPUd() void setNContributors(ushort v) { mNContributors = v; } GPUd() void addContributor() { mNContributors++; } @@ -184,12 +187,26 @@ namespace detail { template concept Streamable = requires(std::ostream& os, const T& a) { - { os << a } -> std::same_as; + { + os << a + } + -> std::same_as; }; template concept HasFormattableTimeStamp = requires(const T& t) { - { fmt::format("{}", t.getTimeStamp()) } -> std::convertible_to; + { + fmt::format("{}", t.getTimeStamp()) + } + -> std::convertible_to; +}; + +template +concept HasFormattableTimeStampWithError = requires(const T& t) { + { + fmt::format("{}+-{}", t.getTimeStamp(), t.getTimeStampError()) + } + -> std::convertible_to; }; } // namespace detail @@ -201,6 +218,8 @@ inline std::string Vertex::asString() const std::ostringstream oss; oss << mTimeStamp; return oss.str(); + } else if constexpr (detail::HasFormattableTimeStampWithError) { + return fmt::format("{}+-{}", mTimeStamp.getTimeStamp(), mTimeStamp.getTimeStampError()); } else if constexpr (detail::HasFormattableTimeStamp) { return fmt::format("{}", mTimeStamp.getTimeStamp()); } else { diff --git a/DataFormats/common/include/CommonDataFormat/TimeStamp.h b/DataFormats/common/include/CommonDataFormat/TimeStamp.h index 56a71414c6b86..709af221c28f8 100644 --- a/DataFormats/common/include/CommonDataFormat/TimeStamp.h +++ b/DataFormats/common/include/CommonDataFormat/TimeStamp.h @@ -27,10 +27,10 @@ class TimeStamp public: GPUhdDefault() TimeStamp() = default; GPUhdDefault() ~TimeStamp() = default; - GPUdi() TimeStamp(T time) { mTimeStamp = time; } + GPUhdi() TimeStamp(T time) { mTimeStamp = time; } GPUhdi() T getTimeStamp() const { return mTimeStamp; } - GPUdi() void setTimeStamp(T t) { mTimeStamp = t; } - GPUdi() bool operator==(const TimeStamp& t) const { return mTimeStamp == t.mTimeStamp; } + GPUhdi() void setTimeStamp(T t) { mTimeStamp = t; } + GPUhdi() bool operator==(const TimeStamp& t) const { return mTimeStamp == t.mTimeStamp; } private: T mTimeStamp = 0; @@ -41,11 +41,11 @@ template class TimeStampWithError : public TimeStamp { public: - GPUdDefault() TimeStampWithError() = default; - GPUd() TimeStampWithError(T t, E te) : TimeStamp(t), mTimeStampError(te) {} - GPUdi() E getTimeStampError() const { return mTimeStampError; } - GPUdi() E getTimeStampError2() const { return mTimeStampError * mTimeStampError; } - GPUdi() void setTimeStampError(E te) { mTimeStampError = te; } + GPUhdDefault() TimeStampWithError() = default; + GPUhd() TimeStampWithError(T t, E te) : TimeStamp(t), mTimeStampError(te) {} + GPUhdi() E getTimeStampError() const { return mTimeStampError; } + GPUhdi() E getTimeStampError2() const { return mTimeStampError * mTimeStampError; } + GPUhdi() void setTimeStampError(E te) { mTimeStampError = te; } private: E mTimeStampError = 0; diff --git a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx index 80b9e6ef4b551..afff39791e4ec 100644 --- a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx +++ b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx @@ -60,7 +60,7 @@ #include "GlobalTracking/MatchGlobalFwd.h" #include "MCHTracking/TrackExtrap.h" #include "MCHTracking/TrackParam.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DetectorsVertexing/PVertexerParams.h" #include "ReconstructionDataFormats/GlobalFwdTrack.h" #include "ReconstructionDataFormats/GlobalTrackID.h" diff --git a/Detectors/AOD/src/aod-producer-workflow.cxx b/Detectors/AOD/src/aod-producer-workflow.cxx index f6bfaae170bbd..d75694f3bd512 100644 --- a/Detectors/AOD/src/aod-producer-workflow.cxx +++ b/Detectors/AOD/src/aod-producer-workflow.cxx @@ -18,6 +18,7 @@ #include "DetectorsRaw/HBFUtilsInitializer.h" #include "Framework/CallbacksPolicy.h" #include "DetectorsBase/DPLWorkflowUtils.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -43,6 +44,7 @@ void customize(std::vector& workflowOptions) {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}, {"combine-source-devices", o2::framework::VariantType::Bool, false, {"merge DPL source devices"}}, {"ctpconfig-run-independent", o2::framework::VariantType::Bool, false, {"Use CTP config w/o runNumber tag"}}}; + o2::itsmft::DPLAlpideParamInitializer::addConfigOption(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } diff --git a/Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx b/Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx index d4ab53c8181ce..2e63a1a65483c 100644 --- a/Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx +++ b/Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx @@ -64,7 +64,7 @@ #include "DataFormatsTPC/ClusterNative.h" #include "DataFormatsTPC/WorkflowHelper.h" #include "ITSBase/GeometryTGeo.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" */ using namespace o2::framework; diff --git a/Detectors/Align/Workflow/src/barrel-alignment-workflow.cxx b/Detectors/Align/Workflow/src/barrel-alignment-workflow.cxx index 8df479ba39260..cdd0620affec9 100644 --- a/Detectors/Align/Workflow/src/barrel-alignment-workflow.cxx +++ b/Detectors/Align/Workflow/src/barrel-alignment-workflow.cxx @@ -27,6 +27,7 @@ #include "ReconstructionDataFormats/GlobalTrackID.h" #include "DetectorsCommonDataFormats/DetID.h" #include "GlobalTrackingWorkflowReaders/TrackTPCITSReaderSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "Algorithm/RangeTokenizer.h" #include "DetectorsRaw/HBFUtilsInitializer.h" @@ -59,6 +60,7 @@ void customize(std::vector& workflowOptions) {"enable-cosmic", VariantType::Bool, false, {"enable cosmic tracks)"}}, {"postprocessing", VariantType::Int, 0, {"postprocessing bits: 1 - extract alignment objects, 2 - check constraints, 4 - print mpParams/Constraints, 8 - relabel pede results"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); diff --git a/Detectors/CTF/test/test_ctf_io_itsmft.cxx b/Detectors/CTF/test/test_ctf_io_itsmft.cxx index 13cbdf7745961..7f2ff8ce9f340 100644 --- a/Detectors/CTF/test/test_ctf_io_itsmft.cxx +++ b/Detectors/CTF/test/test_ctf_io_itsmft.cxx @@ -81,7 +81,7 @@ BOOST_DATA_TEST_CASE(CompressedClustersTest, boost_data::make(ANSVersions), ansV sw.Start(); std::vector vec; { - CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder, o2::detectors::DetID::ITS); + CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder, false); coder.setANSVersion(ansVersion); coder.encode(vec, rofRecVec, cclusVec, pattVec, pattIdConverter, 0); // compress } @@ -120,7 +120,7 @@ BOOST_DATA_TEST_CASE(CompressedClustersTest, boost_data::make(ANSVersions), ansV sw.Start(); const auto ctfImage = o2::itsmft::CTF::getImage(vec.data()); { - CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Decoder, o2::detectors::DetID::ITS); + CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Decoder, false); coder.decode(ctfImage, rofRecVecD, cclusVecD, pattVecD, nullptr, clPattLookup); // decompress } sw.Stop(); diff --git a/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h b/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h index 081e6cf4d968a..51f2fca2c8303 100644 --- a/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h +++ b/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h @@ -27,6 +27,7 @@ struct CTFReaderInp { std::string inpdata{}; o2::detectors::DetID::mask_t detMask = o2::detectors::DetID::FullMask; std::string copyCmd{}; + std::string copyDir{}; std::string tffileRegex{}; std::string remoteRegex{}; std::string metricChannel{}; @@ -50,6 +51,8 @@ struct CTFReaderInp { int tfRateLimit = -999; size_t minSHM = 0; bool shuffle{false}; + bool doITSStaggering = false; + bool doMFTStaggering = false; }; /// create a processor spec diff --git a/Detectors/CTF/workflow/include/CTFWorkflow/CTFWriterSpec.h b/Detectors/CTF/workflow/include/CTFWorkflow/CTFWriterSpec.h index 5eb6d65e26cec..12ad483d90881 100644 --- a/Detectors/CTF/workflow/include/CTFWorkflow/CTFWriterSpec.h +++ b/Detectors/CTF/workflow/include/CTFWorkflow/CTFWriterSpec.h @@ -15,16 +15,23 @@ #define O2_CTFWRITER_SPEC #include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" #include "DetectorsCommonDataFormats/DetID.h" namespace o2 { namespace ctf { +struct CTFWriterInp { + o2::detectors::DetID::mask_t detMask = o2::detectors::DetID::FullMask; + int verbosity = 0; + int reportInterval = 200; + std::string outType = ""; + bool doITSStaggering = false; + bool doMFTStaggering = false; +}; /// create a processor spec -framework::DataProcessorSpec getCTFWriterSpec(o2::detectors::DetID::mask_t dets, const std::string& outType, int verbosity, int reportInterval); +framework::DataProcessorSpec getCTFWriterSpec(const o2::ctf::CTFWriterInp& inp); } // namespace ctf } // namespace o2 diff --git a/Detectors/CTF/workflow/src/CTFReaderSpec.cxx b/Detectors/CTF/workflow/src/CTFReaderSpec.cxx index 4100ebb37c61d..9fba8a220be55 100644 --- a/Detectors/CTF/workflow/src/CTFReaderSpec.cxx +++ b/Detectors/CTF/workflow/src/CTFReaderSpec.cxx @@ -35,6 +35,7 @@ #include "CommonUtils/NameConf.h" #include "DetectorsCommonDataFormats/CTFHeader.h" #include "Headers/STFHeader.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DataFormatsITSMFT/CTF.h" #include "DataFormatsTPC/CTF.h" #include "DataFormatsTRD/CTF.h" @@ -170,7 +171,7 @@ void CTFReaderSpec::init(InitContext& ic) mInput.maxTFsPerFile = ic.options().get("max-tf-per-file"); mInput.maxTFsPerFile = mInput.maxTFsPerFile > 0 ? mInput.maxTFsPerFile : 0x7fffffff; mRunning = true; - mFileFetcher = std::make_unique(mInput.inpdata, mInput.tffileRegex, mInput.remoteRegex, mInput.copyCmd); + mFileFetcher = std::make_unique(mInput.inpdata, mInput.tffileRegex, mInput.remoteRegex, mInput.copyCmd, mInput.copyDir); mFileFetcher->setMaxFilesInQueue(mInput.maxFileCache); mFileFetcher->setMaxLoops(mInput.maxLoops); mFileFetcher->setFailThreshold(ic.options().get("fetch-failure-threshold")); @@ -188,6 +189,48 @@ void CTFReaderSpec::init(InitContext& ic) } } +///_______________________________________ +template <> +void CTFReaderSpec::processDetector(DetID det, const CTFHeader& ctfHeader, ProcessingContext& pc) const +{ + if (mInput.detMask[det]) { + std::string lbl = det.getName(); + int nLayers = 1; + if (det == DetID::ITS) { + nLayers = mInput.doITSStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + } else if (det == DetID::MFT) { + nLayers = mInput.doMFTStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + } else { + LOGP(fatal, "This specialization is define only for ITS and MFT detectors, {} provided", det.getName()); + } + for (int iLayer = 0; iLayer < nLayers; iLayer++) { + auto& bufVec = pc.outputs().make>({lbl, mInput.subspec * 100 + iLayer}, ctfHeader.detectors[det] ? sizeof(o2::itsmft::CTF) : 0); + if (ctfHeader.detectors[det]) { + auto brName = nLayers == 1 ? lbl : fmt::format("{}_{}", lbl, iLayer); + o2::itsmft::CTF::readFromTree(bufVec, *(mCTFTree.get()), brName, mCurrTreeEntry); + } else if (!mInput.allowMissingDetectors) { + throw std::runtime_error(fmt::format("Requested detector {} is missing in the CTF", lbl)); + } + } + } +} + +///_______________________________________ +template +void CTFReaderSpec::processDetector(DetID det, const CTFHeader& ctfHeader, ProcessingContext& pc) const +{ + if (mInput.detMask[det]) { + const auto lbl = det.getName(); + auto& bufVec = pc.outputs().make>({lbl, mInput.subspec}, ctfHeader.detectors[det] ? sizeof(C) : 0); + if (ctfHeader.detectors[det]) { + C::readFromTree(bufVec, *(mCTFTree.get()), lbl, mCurrTreeEntry); + } else if (!mInput.allowMissingDetectors) { + throw std::runtime_error(fmt::format("Requested detector {} is missing in the CTF", lbl)); + } + // setMessageHeader(pc, ctfHeader, lbl); + } +} + void CTFReaderSpec::runTimeRangesToIRFrameSelector(const o2::framework::TimingInfo& timingInfo) { // convert entries in the runTimeRanges to IRFrameSelector, if needed, convert time to orbit @@ -562,22 +605,6 @@ void CTFReaderSpec::setMessageHeader(ProcessingContext& pc, const CTFHeader& ctf dph->creation = ctfHeader.creationTime; } -///_______________________________________ -template -void CTFReaderSpec::processDetector(DetID det, const CTFHeader& ctfHeader, ProcessingContext& pc) const -{ - if (mInput.detMask[det]) { - const auto lbl = det.getName(); - auto& bufVec = pc.outputs().make>({lbl, mInput.subspec}, ctfHeader.detectors[det] ? sizeof(C) : 0); - if (ctfHeader.detectors[det]) { - C::readFromTree(bufVec, *(mCTFTree.get()), lbl, mCurrTreeEntry); - } else if (!mInput.allowMissingDetectors) { - throw std::runtime_error(fmt::format("Requested detector {} is missing in the CTF", lbl)); - } - // setMessageHeader(pc, ctfHeader, lbl); - } -} - ///_______________________________________ void CTFReaderSpec::tryToFixCTFHeader(CTFHeader& ctfHeader) const { @@ -636,7 +663,19 @@ DataProcessorSpec getCTFReaderSpec(const CTFReaderInp& inp) for (auto id = DetID::First; id <= DetID::Last; id++) { if (inp.detMask[id]) { DetID det(id); - outputs.emplace_back(OutputLabel{det.getName()}, det.getDataOrigin(), "CTFDATA", inp.subspec, Lifetime::Timeframe); + if (det == DetID::ITS) { + uint32_t nLayers = inp.doITSStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + for (uint32_t iLayer = 0; iLayer < nLayers; iLayer++) { + outputs.emplace_back(OutputLabel{det.getName()}, det.getDataOrigin(), "CTFDATA", inp.subspec * 100 + iLayer, Lifetime::Timeframe); + } + } else if (det == DetID::MFT) { + uint32_t nLayers = inp.doMFTStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + for (uint32_t iLayer = 0; iLayer < nLayers; iLayer++) { + outputs.emplace_back(OutputLabel{det.getName()}, det.getDataOrigin(), "CTFDATA", inp.subspec * 100 + iLayer, Lifetime::Timeframe); + } + } else { + outputs.emplace_back(OutputLabel{det.getName()}, det.getDataOrigin(), "CTFDATA", inp.subspec, Lifetime::Timeframe); + } } } if (!inp.fileIRFrames.empty() || !inp.fileRunTimeSpans.empty()) { diff --git a/Detectors/CTF/workflow/src/CTFWriterSpec.cxx b/Detectors/CTF/workflow/src/CTFWriterSpec.cxx index ba4542969a712..5d6db7d613674 100644 --- a/Detectors/CTF/workflow/src/CTFWriterSpec.cxx +++ b/Detectors/CTF/workflow/src/CTFWriterSpec.cxx @@ -12,11 +12,10 @@ /// @file CTFWriterSpec.cxx #include "Framework/Logger.h" -#include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/InputSpec.h" +#include "Framework/Task.h" #include "Framework/RawDeviceService.h" -#include "Framework/CommonServices.h" #include "Framework/DataTakingContext.h" #include "Framework/TimingInfo.h" #include @@ -29,6 +28,7 @@ #include "DetectorsCommonDataFormats/EncodedBlocks.h" #include "DetectorsCommonDataFormats/FileMetaData.h" #include "CommonUtils/StringUtils.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DataFormatsITSMFT/CTF.h" #include "DataFormatsTPC/CTF.h" #include "DataFormatsTRD/CTF.h" @@ -94,17 +94,19 @@ size_t appendToTree(TTree& tree, const std::string brname, T& ptr) using DetID = o2::detectors::DetID; using FTrans = o2::rans::DenseHistogram; -class CTFWriterSpec : public o2::framework::Task +class CTFWriterSpec final : public o2::framework::Task { public: CTFWriterSpec() = delete; - CTFWriterSpec(DetID::mask_t dm, const std::string& outType, int verbosity, int reportInterval); + CTFWriterSpec(const o2::ctf::CTFWriterInp&); ~CTFWriterSpec() final { finalize(); } void init(o2::framework::InitContext& ic) final; void run(o2::framework::ProcessingContext& pc) final; void endOfStream(o2::framework::EndOfStreamContext& ec) final { finalize(); } void stop() final { finalize(); } - bool isPresent(DetID id) const { return mDets[id]; } + bool isPresent(DetID id) const { return mInput.detMask[id]; } + + static std::string getBinding(const std::string& name, int spec) { return fmt::format("{}_{}", name, spec); } private: void updateTimeDependentParams(ProcessingContext& pc); @@ -121,7 +123,7 @@ class CTFWriterSpec : public o2::framework::Task void removeLockFile(); void finalize(); - DetID::mask_t mDets; // detectors + CTFWriterInp mInput; bool mFinalized = false; bool mWriteCTF = true; bool mCreateDict = false; @@ -130,8 +132,6 @@ class CTFWriterSpec : public o2::framework::Task bool mRejectCurrentTF = false; bool mFallBackDirUsed = false; bool mFallBackDirProvided = false; - int mReportInterval = -1; - int mVerbosity = 0; int mSaveDictAfter = 0; // if positive and mWriteCTF==true, save dictionary after each mSaveDictAfter TFs processed uint32_t mPrevDictTimeStamp = 0; // timestamp of the previously stored dictionary uint32_t mDictTimeStamp = 0; // timestamp of the currently stored dictionary @@ -155,7 +155,6 @@ class CTFWriterSpec : public o2::framework::Task std::vector mTFOrbits{}; // 1st orbits of TF accumulated in current file o2::framework::DataTakingContext mDataTakingContext{}; o2::framework::TimingInfo mTimingInfo{}; - std::string mOutputType{}; // RS FIXME once global/local options clash is solved, --output-type will become device option std::string mDictDir{}; std::string mCTFDir{}; std::string mHostName{}; @@ -190,8 +189,8 @@ class CTFWriterSpec : public o2::framework::Task const std::string CTFWriterSpec::TMPFileEnding{".part"}; //___________________________________________________________________ -CTFWriterSpec::CTFWriterSpec(DetID::mask_t dm, const std::string& outType, int verbosity, int reportInterval) - : mDets(dm), mOutputType(outType), mReportInterval(reportInterval), mVerbosity(verbosity) +CTFWriterSpec::CTFWriterSpec(const o2::ctf::CTFWriterInp& inp) + : mInput(inp) { std::for_each(mIsSaturatedFrequencyTable.begin(), mIsSaturatedFrequencyTable.end(), [](auto& bitset) { bitset.reset(); }); mTimer.Stop(); @@ -202,7 +201,7 @@ CTFWriterSpec::CTFWriterSpec(DetID::mask_t dm, const std::string& outType, int v void CTFWriterSpec::init(InitContext& ic) { // auto outmode = ic.options().get("output-type"); // RS FIXME once global/local options clash is solved, --output-type will become device option - auto outmode = mOutputType; + auto outmode = mInput.outType; if (outmode == "ctf") { mWriteCTF = true; mCreateDict = false; @@ -301,71 +300,82 @@ size_t CTFWriterSpec::processDet(o2::framework::ProcessingContext& pc, DetID det { static bool warnedEmpty = false; size_t sz = 0; - if (!isPresent(det) || !pc.inputs().isValid(det.getName())) { + + if (!isPresent(det) || !pc.inputs().isValid(getBinding(det.getName(), 0))) { mSizeReport += fmt::format(" {}:N/A", det.getName()); return sz; } - auto ctfBuffer = pc.inputs().get>(det.getName()); - const o2::ctf::BufferType* bdata = ctfBuffer.data(); - if (bdata) { - if (warnedEmpty) { - throw std::runtime_error(fmt::format("Non-empty input was seen at {}-th TF after empty one for {}, this will lead to misalignment of detectors in CTF", mNCTF, det.getName())); - } - const auto ctfImage = C::getImage(bdata); - ctfImage.print(o2::utils::Str::concat_string(det.getName(), ": "), mVerbosity); - if (mWriteCTF && !mRejectCurrentTF) { - sz = ctfImage.appendToTree(*tree, det.getName()); - header.detectors.set(det); - } else { - sz = ctfBuffer.size(); - } - if (mCreateDict) { - if (mFreqsAccumulation[det].empty()) { - mFreqsAccumulation[det].resize(C::getNBlocks()); - mFreqsMetaData[det].resize(C::getNBlocks()); + + uint32_t nLayers = 1; + if (det == DetID::ITS) { + nLayers = mInput.doITSStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + } else if (det == DetID::MFT) { + nLayers = mInput.doMFTStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + } + for (uint32_t iLayer = 0; iLayer < nLayers; iLayer++) { + auto binding = getBinding(det.getName(), iLayer); + auto ctfBuffer = pc.inputs().get>(binding); + const o2::ctf::BufferType* bdata = ctfBuffer.data(); + if (bdata) { + if (warnedEmpty) { + throw std::runtime_error(fmt::format("Non-empty input was seen at {}-th TF after empty one for {}, this will lead to misalignment of detectors in CTF", mNCTF, det.getName())); } - if (!mHeaders[det]) { // store 1st header - mHeaders[det] = ctfImage.cloneHeader(); - auto& hb = *static_cast(mHeaders[det].get()); - hb.det = det; + const auto ctfImage = C::getImage(bdata); + ctfImage.print(o2::utils::Str::concat_string(binding, ": "), mInput.verbosity); + if (mWriteCTF && !mRejectCurrentTF) { + sz += ctfImage.appendToTree(*tree, nLayers > 1 ? binding : det.getName()); + header.detectors.set(det); + } else { + sz += ctfBuffer.size(); } - for (int ib = 0; ib < C::getNBlocks(); ib++) { - if (!mIsSaturatedFrequencyTable[det][ib]) { - const auto& bl = ctfImage.getBlock(ib); - if (bl.getNDict()) { - auto freq = mFreqsAccumulation[det][ib]; - auto& mdSave = mFreqsMetaData[det][ib]; - const auto& md = ctfImage.getMetadata(ib); - if ([&, this]() { - try { - freq.addFrequencies(bl.getDict(), bl.getDict() + bl.getNDict(), md.min); - } catch (const std::overflow_error& e) { - LOGP(warning, "unable to add frequency table for {}, block {} due to overflow", det.getName(), ib); - mIsSaturatedFrequencyTable[det][ib] = true; - return false; - } - return true; - }()) { - auto newProbBits = static_cast(o2::rans::compat::computeRenormingPrecision(countNUsedAlphabetSymbols(freq))); - auto histogramView = o2::rans::trim(o2::rans::makeHistogramView(freq)); - mdSave = ctf::detail::makeMetadataRansDict(newProbBits, - static_cast(histogramView.getMin()), - static_cast(histogramView.getMax()), - static_cast(histogramView.size()), - md.opt); - mFreqsAccumulation[det][ib] = std::move(freq); + if (mCreateDict) { // RSTODO + if (mFreqsAccumulation[det].empty()) { + mFreqsAccumulation[det].resize(C::getNBlocks()); + mFreqsMetaData[det].resize(C::getNBlocks()); + } + if (!mHeaders[det]) { // store 1st header + mHeaders[det] = ctfImage.cloneHeader(); + auto& hb = *static_cast(mHeaders[det].get()); + hb.det = det; + } + for (int ib = 0; ib < C::getNBlocks(); ib++) { + if (!mIsSaturatedFrequencyTable[det][ib]) { + const auto& bl = ctfImage.getBlock(ib); + if (bl.getNDict()) { + auto freq = mFreqsAccumulation[det][ib]; + auto& mdSave = mFreqsMetaData[det][ib]; + const auto& md = ctfImage.getMetadata(ib); + if ([&, this]() { + try { + freq.addFrequencies(bl.getDict(), bl.getDict() + bl.getNDict(), md.min); + } catch (const std::overflow_error& e) { + LOGP(warning, "unable to add frequency table for {}, block {} due to overflow", det.getName(), ib); + mIsSaturatedFrequencyTable[det][ib] = true; + return false; + } + return true; + }()) { + auto newProbBits = static_cast(o2::rans::compat::computeRenormingPrecision(countNUsedAlphabetSymbols(freq))); + auto histogramView = o2::rans::trim(o2::rans::makeHistogramView(freq)); + mdSave = ctf::detail::makeMetadataRansDict(newProbBits, + static_cast(histogramView.getMin()), + static_cast(histogramView.getMax()), + static_cast(histogramView.size()), + md.opt); + mFreqsAccumulation[det][ib] = std::move(freq); + } } } } } - } - } else { - if (!warnedEmpty) { - if (mNCTF) { - throw std::runtime_error(fmt::format("Empty input was seen at {}-th TF after non-empty one for {}, this will lead to misalignment of detectors in CTF", mNCTF, det.getName())); + } else { + if (!warnedEmpty) { + if (mNCTF) { + throw std::runtime_error(fmt::format("Empty input was seen at {}-th TF after non-empty one for {}, this will lead to misalignment of detectors in CTF", mNCTF, det.getName())); + } + LOGP(important, "Empty CTF provided for {}, skipping and will not report anymore", det.getName()); + warnedEmpty = true; } - LOGP(important, "Empty CTF provided for {}, skipping and will not report anymore", det.getName()); - warnedEmpty = true; } } mSizeReport += fmt::format(" {}:{}", det.getName(), fmt::group_digits(sz)); @@ -417,10 +427,19 @@ size_t CTFWriterSpec::estimateCTFSize(ProcessingContext& pc) size_t s = 0; for (auto id = DetID::First; id <= DetID::Last; id++) { DetID det(id); - if (!isPresent(det) || !pc.inputs().isValid(det.getName())) { - continue; + uint32_t nLayers = 1; + if (det == DetID::ITS) { + nLayers = mInput.doITSStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + } else if (det == DetID::MFT) { + nLayers = mInput.doMFTStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + } + for (uint32_t iLayer = 0; iLayer < nLayers; iLayer++) { + auto binding = getBinding(det.getName(), iLayer); + if (!isPresent(det) || !pc.inputs().isValid(binding)) { + continue; + } + s += pc.inputs().get>(binding).size(); } - s += pc.inputs().get>(det.getName()).size(); } return s; } @@ -496,7 +515,7 @@ void CTFWriterSpec::run(ProcessingContext& pc) szCTFperDet[DetID::FDD] = processDet(pc, DetID::FDD, header, mCTFTreeOut.get()); szCTFperDet[DetID::CTP] = processDet(pc, DetID::CTP, header, mCTFTreeOut.get()); szCTF = std::accumulate(szCTFperDet.begin(), szCTFperDet.end(), 0); - if (mReportInterval > 0 && (mTimingInfo.tfCounter % mReportInterval) == 0) { + if (mInput.reportInterval > 0 && (mTimingInfo.tfCounter % mInput.reportInterval) == 0) { LOGP(important, "CTF {} size report:{} - Total:{}", mTimingInfo.tfCounter, mSizeReport, fmt::group_digits(szCTF)); } @@ -660,7 +679,7 @@ void CTFWriterSpec::storeDictionaries() // monolitic dictionary in tree format mDictTimeStamp = uint32_t(std::time(nullptr)); auto getFileName = [this](bool curr) { - return fmt::format("{}{}Tree_{}_{}_{}.root", this->mDictDir, o2::base::NameConf::CTFDICT, DetID::getNames(this->mDets, '-'), curr ? this->mDictTimeStamp : this->mPrevDictTimeStamp, curr ? this->mNCTF : this->mNCTFPrevDict); + return fmt::format("{}{}Tree_{}_{}_{}.root", this->mDictDir, o2::base::NameConf::CTFDICT, DetID::getNames(this->mInput.detMask, '-'), curr ? this->mDictTimeStamp : this->mPrevDictTimeStamp, curr ? this->mNCTF : this->mNCTFPrevDict); }; auto dictFileName = getFileName(true); mDictFileOut.reset(TFile::Open(dictFileName.c_str(), "recreate")); @@ -788,13 +807,22 @@ size_t CTFWriterSpec::getAvailableDiskSpace(const std::string& path, int level) } //___________________________________________________________________ -DataProcessorSpec getCTFWriterSpec(DetID::mask_t dets, const std::string& outType, int verbosity, int reportInterval) +DataProcessorSpec getCTFWriterSpec(const o2::ctf::CTFWriterInp& inp) { std::vector inputs; LOG(debug) << "Detectors list:"; for (auto id = DetID::First; id <= DetID::Last; id++) { - if (dets[id]) { - inputs.emplace_back(DetID::getName(id), DetID::getDataOrigin(id), "CTFDATA", 0, Lifetime::Timeframe); + if (inp.detMask[id]) { + uint32_t nLayers = 1; + DetID det{id}; + if (det == DetID::ITS) { + nLayers = inp.doITSStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + } else if (det == DetID::MFT) { + nLayers = inp.doMFTStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + } + for (uint32_t iLayer = 0; iLayer < nLayers; iLayer++) { + inputs.emplace_back(CTFWriterSpec::getBinding(det.getName(), iLayer), det.getDataOrigin(), "CTFDATA", iLayer, Lifetime::Timeframe); + } LOG(debug) << "Det " << DetID::getName(id) << " added"; } } @@ -803,24 +831,25 @@ DataProcessorSpec getCTFWriterSpec(DetID::mask_t dets, const std::string& outTyp inputs, Outputs{{OutputLabel{"ctfdone"}, "CTF", "DONE", 0, Lifetime::Timeframe}, {"CTF", "SIZES", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(dets, outType, verbosity, reportInterval)}, // RS FIXME once global/local options clash is solved, --output-type will become device option - Options{ //{"output-type", VariantType::String, "ctf", {"output types: ctf (per TF) or dict (create dictionaries) or both or none"}}, - {"save-ctf-after", VariantType::Int64, 0ll, {"autosave CTF tree with multiple CTFs after every N CTFs if >0 or every -N MBytes if < 0"}}, - {"save-dict-after", VariantType::Int, 0, {"if > 0, in dictionary generation mode save it dictionary after certain number of TFs processed"}}, - {"ctf-dict-dir", VariantType::String, "none", {"CTF dictionary directory, must exist"}}, - {"output-dir", VariantType::String, "none", {"CTF output directory, must exist"}}, - {"output-dir-alt", VariantType::String, "/dev/null", {"Alternative CTF output directory, must exist (if not /dev/null)"}}, - {"meta-output-dir", VariantType::String, "/dev/null", {"CTF metadata output directory, must exist (if not /dev/null)"}}, - {"md5-for-meta", VariantType::Bool, false, {"fill CTF file MD5 sum in the metadata file"}}, - {"min-file-size", VariantType::Int64, 0l, {"accumulate CTFs until given file size reached"}}, - {"max-file-size", VariantType::Int64, 0l, {"if > 0, try to avoid exceeding given file size, also used for space check"}}, - {"max-ctf-per-file", VariantType::Int, 0, {"if > 0, avoid storing more than requested CTFs per file"}}, - {"ctf-rejection", VariantType::Int, 0, {">0: percentage to reject randomly, <0: reject if timeslice%|value|!=0"}}, - {"ctf-file-compression", VariantType::Int, 0, {"if >= 0: impose CTF file compression level"}}, - {"require-free-disk", VariantType::Float, 0.f, {"pause writing op. if available disk space is below this margin, in bytes if >0, as a fraction of total if <0"}}, - {"wait-for-free-disk", VariantType::Float, 10.f, {"if paused due to the low disk space, recheck after this time (in s)"}}, - {"max-wait-for-free-disk", VariantType::Float, 60.f, {"produce fatal if paused due to the low disk space for more than this amount in s."}}, - {"ignore-partition-run-dir", VariantType::Bool, false, {"Do not creare partition-run directory in output-dir"}}}}; + AlgorithmSpec{adaptFromTask(inp)}, + Options{ + //{"output-type", VariantType::String, "ctf", {"output types: ctf (per TF) or dict (create dictionaries) or both or none"}}, + {"save-ctf-after", VariantType::Int64, 0ll, {"autosave CTF tree with multiple CTFs after every N CTFs if >0 or every -N MBytes if < 0"}}, + {"save-dict-after", VariantType::Int, 0, {"if > 0, in dictionary generation mode save it dictionary after certain number of TFs processed"}}, + {"ctf-dict-dir", VariantType::String, "none", {"CTF dictionary directory, must exist"}}, + {"output-dir", VariantType::String, "none", {"CTF output directory, must exist"}}, + {"output-dir-alt", VariantType::String, "/dev/null", {"Alternative CTF output directory, must exist (if not /dev/null)"}}, + {"meta-output-dir", VariantType::String, "/dev/null", {"CTF metadata output directory, must exist (if not /dev/null)"}}, + {"md5-for-meta", VariantType::Bool, false, {"fill CTF file MD5 sum in the metadata file"}}, + {"min-file-size", VariantType::Int64, 0l, {"accumulate CTFs until given file size reached"}}, + {"max-file-size", VariantType::Int64, 0l, {"if > 0, try to avoid exceeding given file size, also used for space check"}}, + {"max-ctf-per-file", VariantType::Int, 0, {"if > 0, avoid storing more than requested CTFs per file"}}, + {"ctf-rejection", VariantType::Int, 0, {">0: percentage to reject randomly, <0: reject if timeslice%|value|!=0"}}, + {"ctf-file-compression", VariantType::Int, 0, {"if >= 0: impose CTF file compression level"}}, + {"require-free-disk", VariantType::Float, 0.f, {"pause writing op. if available disk space is below this margin, in bytes if >0, as a fraction of total if <0"}}, + {"wait-for-free-disk", VariantType::Float, 10.f, {"if paused due to the low disk space, recheck after this time (in s)"}}, + {"max-wait-for-free-disk", VariantType::Float, 60.f, {"produce fatal if paused due to the low disk space for more than this amount in s."}}, + {"ignore-partition-run-dir", VariantType::Bool, false, {"Do not creare partition-run directory in output-dir"}}}}; } } // namespace ctf diff --git a/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx b/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx index fc50c971c5d20..366fa76f74983 100644 --- a/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx +++ b/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx @@ -25,6 +25,7 @@ // Specific detectors specs #include "ITSMFTWorkflow/EntropyDecoderSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "TPCWorkflow/EntropyDecoderSpec.h" #include "TRDWorkflow/EntropyDecoderSpec.h" #include "HMPIDWorkflow/EntropyDecoderSpec.h" @@ -59,6 +60,7 @@ void customize(std::vector& workflowOptions) options.push_back(ConfigParamSpec{"delay", VariantType::Float, 0.f, {"delay in seconds between consecutive TFs sending"}}); options.push_back(ConfigParamSpec{"shuffle", VariantType::Bool, false, {"shuffle TF sending order (for debug)"}}); options.push_back(ConfigParamSpec{"copy-cmd", VariantType::String, "alien_cp ?src file://?dst", {"copy command for remote files or no-copy to avoid copying"}}); // Use "XrdSecPROTOCOL=sss,unix xrdcp -N root://eosaliceo2.cern.ch/?src ?dst" for direct EOS access + options.push_back(ConfigParamSpec{"copy-dir", VariantType::String, "/tmp/", {"copy base directory for remote files"}}); options.push_back(ConfigParamSpec{"ctf-file-regex", VariantType::String, ".*o2_ctf_run.+\\.root$", {"regex string to identify CTF files"}}); options.push_back(ConfigParamSpec{"remote-regex", VariantType::String, "^(alien://|)/alice/data/.+", {"regex string to identify remote files"}}); // Use "^/eos/aliceo2/.+" for direct EOS access options.push_back(ConfigParamSpec{"max-cached-files", VariantType::Int, 3, {"max CTF files queued (copied for remote source)"}}); @@ -80,6 +82,7 @@ void customize(std::vector& workflowOptions) options.push_back(ConfigParamSpec{"timeframes-shm-limit", VariantType::String, "0", {"Minimum amount of SHM required in order to publish data"}}); options.push_back(ConfigParamSpec{"metric-feedback-channel-format", VariantType::String, "name=metric-feedback,type=pull,method=connect,address=ipc://{}metric-feedback-{},transport=shmem,rateLogging=0", {"format for the metric-feedback channel for TF rate limiting"}}); options.push_back(ConfigParamSpec{"combine-devices", VariantType::Bool, false, {"combine multiple DPL devices (entropy decoders)"}}); + o2::itsmft::DPLAlpideParamInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -124,6 +127,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) ctfInput.shuffle = configcontext.options().get("shuffle"); ctfInput.copyCmd = configcontext.options().get("copy-cmd"); + ctfInput.copyDir = configcontext.options().get("copy-dir"); ctfInput.tffileRegex = configcontext.options().get("ctf-file-regex"); ctfInput.remoteRegex = configcontext.options().get("remote-regex"); ctfInput.allowMissingDetectors = configcontext.options().get("allow-missing-detectors"); @@ -147,6 +151,8 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) if (!ctfInput.fileIRFrames.empty() && !ctfInput.fileRunTimeSpans.empty()) { LOGP(fatal, "One cannot provide --ir-frames-files and --run-time-span-file options simultaneously"); } + ctfInput.doITSStaggering = o2::itsmft::DPLAlpideParamInitializer::isITSStaggeringEnabled(configcontext); + ctfInput.doMFTStaggering = o2::itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(configcontext); specs.push_back(o2::ctf::getCTFReaderSpec(ctfInput)); @@ -183,10 +189,12 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) // add decoders for all allowed detectors. if (ctfInput.detMask[DetID::ITS]) { - addSpecs(o2::itsmft::getEntropyDecoderSpec(DetID::getDataOrigin(DetID::ITS), verbosity, configcontext.options().get("its-digits"), ctfInput.subspec, ctfInput.dictOpt)); + bool doStag = o2::itsmft::DPLAlpideParamInitializer::isITSStaggeringEnabled(configcontext); + addSpecs(o2::itsmft::getITSEntropyDecoderSpec(verbosity, doStag, configcontext.options().get("its-digits"), ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::MFT]) { - addSpecs(o2::itsmft::getEntropyDecoderSpec(DetID::getDataOrigin(DetID::MFT), verbosity, configcontext.options().get("mft-digits"), ctfInput.subspec, ctfInput.dictOpt)); + bool doStag = o2::itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(configcontext); + addSpecs(o2::itsmft::getMFTEntropyDecoderSpec(verbosity, doStag, configcontext.options().get("mft-digits"), ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::TPC]) { addSpecs(o2::tpc::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); diff --git a/Detectors/CTF/workflow/src/ctf-writer-workflow.cxx b/Detectors/CTF/workflow/src/ctf-writer-workflow.cxx index 2757192727521..77dbbd80bc1a7 100644 --- a/Detectors/CTF/workflow/src/ctf-writer-workflow.cxx +++ b/Detectors/CTF/workflow/src/ctf-writer-workflow.cxx @@ -20,6 +20,7 @@ #include "CTFWorkflow/CTFWriterSpec.h" #include "DetectorsCommonDataFormats/DetID.h" #include "CommonUtils/ConfigurableParam.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using DetID = o2::detectors::DetID; @@ -35,6 +36,7 @@ void customize(std::vector& workflowOptions) options.push_back(ConfigParamSpec{"ctf-writer-verbosity", VariantType::Int, 0, {"verbosity level (0: summary per detector, 1: summary per block"}}); options.push_back(ConfigParamSpec{"report-data-size-interval", VariantType::Int, 200, {"report sizes per detector for every N-th timeframe"}}); options.push_back(ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}); + o2::itsmft::DPLAlpideParamInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -51,7 +53,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { DetID::mask_t dets = 0; o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); - std::string outType{}; // RS FIXME once global/local options clash is solved, --output-type will become device option + o2::ctf::CTFWriterInp inp; if (!configcontext.helpOnCommandLine()) { dets.set(); // by default read all auto mskOnly = DetID::getMask(configcontext.options().get("onlyDet")); @@ -64,10 +66,14 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) if (dets.none()) { throw std::invalid_argument("Invalid workflow: no detectors found"); } - outType = configcontext.options().get("output-type"); + inp.detMask = dets; + inp.outType = configcontext.options().get("output-type"); } - WorkflowSpec specs{o2::ctf::getCTFWriterSpec(dets, outType, - configcontext.options().get("ctf-writer-verbosity"), - configcontext.options().get("report-data-size-interval"))}; + inp.verbosity = configcontext.options().get("ctf-writer-verbosity"); + inp.reportInterval = configcontext.options().get("report-data-size-interval"); + inp.doITSStaggering = o2::itsmft::DPLAlpideParamInitializer::isITSStaggeringEnabled(configcontext); + inp.doMFTStaggering = o2::itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(configcontext); + + WorkflowSpec specs{o2::ctf::getCTFWriterSpec(inp)}; return std::move(specs); } diff --git a/Detectors/Filtering/src/FilteringSpec.cxx b/Detectors/Filtering/src/FilteringSpec.cxx index bcf3c6c3539d4..ea82b1456d955 100644 --- a/Detectors/Filtering/src/FilteringSpec.cxx +++ b/Detectors/Filtering/src/FilteringSpec.cxx @@ -46,7 +46,7 @@ #include "ReconstructionDataFormats/Cascade.h" #include "MCHTracking/TrackExtrap.h" #include "MCHTracking/TrackParam.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DetectorsVertexing/PVertexerParams.h" #include "ReconstructionDataFormats/GlobalTrackID.h" #include "ReconstructionDataFormats/Track.h" diff --git a/Detectors/Filtering/src/filtering-workflow.cxx b/Detectors/Filtering/src/filtering-workflow.cxx index 8e36cfc36b197..faf5463281ed8 100644 --- a/Detectors/Filtering/src/filtering-workflow.cxx +++ b/Detectors/Filtering/src/filtering-workflow.cxx @@ -17,6 +17,7 @@ #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsRaw/HBFUtilsInitializer.h" #include "Framework/CallbacksPolicy.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -36,6 +37,7 @@ void customize(std::vector& workflowOptions) {"disable-secondary-vertices", o2::framework::VariantType::Bool, false, {"disable filling secondary vertices"}}, {"data-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of sources to use"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addConfigOption(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } diff --git a/Detectors/GlobalTrackingWorkflow/helpers/src/InputHelper.cxx b/Detectors/GlobalTrackingWorkflow/helpers/src/InputHelper.cxx index c6c163f4b8911..e4c1e40b3a4d3 100644 --- a/Detectors/GlobalTrackingWorkflow/helpers/src/InputHelper.cxx +++ b/Detectors/GlobalTrackingWorkflow/helpers/src/InputHelper.cxx @@ -14,6 +14,7 @@ #include "GlobalTrackingWorkflowHelpers/InputHelper.h" #include "Framework/ConfigParamRegistry.h" #include "ITSMFTWorkflow/ClusterReaderSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "ITSWorkflow/TrackReaderSpec.h" #include "MFTWorkflow/TrackReaderSpec.h" #include "TPCReaderWorkflow/TrackReaderSpec.h" @@ -79,13 +80,15 @@ int InputHelper::addInputSpecs(const ConfigContext& configcontext, WorkflowSpec& specs.emplace_back(o2::its::getITSTrackReaderSpec(maskTracksMC[GID::ITS])); } if (maskClusters[GID::ITS]) { - specs.emplace_back(o2::itsmft::getITSClusterReaderSpec(maskClustersMC[GID::ITS], true)); + bool doStag = itsmft::DPLAlpideParamInitializer::isITSStaggeringEnabled(configcontext); + specs.emplace_back(o2::itsmft::getITSClusterReaderSpec(maskClustersMC[GID::ITS], doStag, true)); } if (maskTracks[GID::MFT]) { specs.emplace_back(o2::mft::getMFTTrackReaderSpec(maskTracksMC[GID::MFT])); } if (maskClusters[GID::MFT]) { - specs.emplace_back(o2::itsmft::getMFTClusterReaderSpec(maskClustersMC[GID::MFT], true)); + bool doStag = itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(configcontext); + specs.emplace_back(o2::itsmft::getMFTClusterReaderSpec(maskClustersMC[GID::MFT], doStag, true)); } if (maskTracks[GID::MCH] || maskMatches[GID::MCHMID]) { specs.emplace_back(o2::mch::getTrackReaderSpec(maskTracksMC[GID::MCH] || maskTracksMC[GID::MCHMID])); diff --git a/Detectors/GlobalTrackingWorkflow/src/CosmicsMatchingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/CosmicsMatchingSpec.cxx index 34c41ec234dc5..5bcdded0e1223 100644 --- a/Detectors/GlobalTrackingWorkflow/src/CosmicsMatchingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/CosmicsMatchingSpec.cxx @@ -40,7 +40,7 @@ #include "Headers/DataHeader.h" #include "CommonDataFormat/InteractionRecord.h" #include "ITSBase/GeometryTGeo.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DataFormatsGlobalTracking/RecoContainer.h" #include "Framework/Task.h" #include "Framework/CCDBParamSpec.h" diff --git a/Detectors/GlobalTrackingWorkflow/src/GlobalFwdMatchingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/GlobalFwdMatchingSpec.cxx index 03dc823c62c42..a43a1e8943739 100644 --- a/Detectors/GlobalTrackingWorkflow/src/GlobalFwdMatchingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/GlobalFwdMatchingSpec.cxx @@ -20,7 +20,7 @@ #include "Framework/CCDBParamSpec.h" #include "CommonUtils/StringUtils.h" #include "DetectorsCommonDataFormats/DetectorNameConf.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "SimulationDataFormat/MCCompLabel.h" #include "DataFormatsMFT/TrackMFT.h" #include "DataFormatsITSMFT/Cluster.h" diff --git a/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexingSpec.cxx index dc1107bacb18a..c1d7b62bbf731 100644 --- a/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexingSpec.cxx @@ -30,7 +30,7 @@ #include "Framework/CCDBParamSpec.h" #include "Framework/DeviceSpec.h" #include "FT0Reconstruction/InteractionTag.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsVertexing/PVertexer.h" #include "DetectorsBase/GRPGeomHelper.h" diff --git a/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx index c333c37ff245b..cb3384b0631c2 100644 --- a/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx @@ -39,7 +39,7 @@ #include "DetectorsBase/GeometryManager.h" #include "DetectorsBase/Propagator.h" #include "DetectorsBase/GlobalParams.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "ITSBase/GeometryTGeo.h" #include "GlobalTracking/MatchTPCITSParams.h" #include "DetectorsCommonDataFormats/DetectorNameConf.h" diff --git a/Detectors/GlobalTrackingWorkflow/src/VertexTrackMatcherSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/VertexTrackMatcherSpec.cxx index f24e7c13e336f..90e4dd4b0f001 100644 --- a/Detectors/GlobalTrackingWorkflow/src/VertexTrackMatcherSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/VertexTrackMatcherSpec.cxx @@ -23,7 +23,7 @@ #include "TPCBase/ParameterElectronics.h" #include "TPCBase/ParameterDetector.h" #include "TPCCalibration/VDriftHelper.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "TStopwatch.h" using namespace o2::framework; diff --git a/Detectors/GlobalTrackingWorkflow/src/cosmics-match-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/cosmics-match-workflow.cxx index e4082bdd14d86..3f7ecfbbea809 100644 --- a/Detectors/GlobalTrackingWorkflow/src/cosmics-match-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/cosmics-match-workflow.cxx @@ -32,6 +32,7 @@ #include "Framework/CallbacksPolicy.h" #include "GlobalTrackingWorkflowHelpers/InputHelper.h" #include "TPCCalibration/CorrectionMapsLoader.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using DetID = o2::detectors::DetID; @@ -52,6 +53,7 @@ void customize(std::vector& workflowOptions) {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writer"}}, {"track-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of sources to use"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); diff --git a/Detectors/GlobalTrackingWorkflow/src/globalfwd-matcher-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/globalfwd-matcher-workflow.cxx index 13a842130e5d1..fd90aff5f32ff 100644 --- a/Detectors/GlobalTrackingWorkflow/src/globalfwd-matcher-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/globalfwd-matcher-workflow.cxx @@ -21,6 +21,7 @@ #include "GlobalTrackingWorkflow/MatchedMFTMCHWriterSpec.h" #include "GlobalTrackingWorkflowHelpers/InputHelper.h" #include "GlobalTracking/MatchGlobalFwdParam.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -46,6 +47,7 @@ void customize(std::vector& workflowOptions) {"disable-root-output", o2::framework::VariantType::Bool, false, {"do not write output root files"}}, {"enable-match-output", o2::framework::VariantType::Bool, false, {"stores mftmch matching info on mftmchmatches.root"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addMFTConfigOption(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } diff --git a/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx index 0ac640cbad9fd..9108e8577fd5a 100644 --- a/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx @@ -30,6 +30,7 @@ #include "Framework/CompletionPolicyHelpers.h" #include "DetectorsBase/DPLWorkflowUtils.h" #include "TPCCalibration/CorrectionMapsLoader.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -62,6 +63,7 @@ void customize(std::vector& workflowOptions) {"use-full-geometry", o2::framework::VariantType::Bool, false, {"use full geometry instead of the light-weight ITS part"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}, {"combine-source-devices", o2::framework::VariantType::Bool, false, {"merge DPL source devices"}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); diff --git a/Detectors/GlobalTrackingWorkflow/src/strangeness-tracking-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/strangeness-tracking-workflow.cxx index bdc1af958886c..8c42871ac05bf 100644 --- a/Detectors/GlobalTrackingWorkflow/src/strangeness-tracking-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/strangeness-tracking-workflow.cxx @@ -21,6 +21,7 @@ #include "GlobalTrackingWorkflowReaders/TrackTPCITSReaderSpec.h" #include "GlobalTrackingWorkflowReaders/PrimaryVertexReaderSpec.h" #include "GlobalTrackingWorkflowHelpers/InputHelper.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "DetectorsRaw/HBFUtilsInitializer.h" #include "Framework/CallbacksPolicy.h" @@ -46,6 +47,7 @@ void customize(std::vector& workflowOptions) {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC"}}, {"use-full-geometry", o2::framework::VariantType::Bool, false, {"use full geometry instead of the light-weight ITS part"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } diff --git a/Detectors/GlobalTrackingWorkflow/src/tpcits-match-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/tpcits-match-workflow.cxx index 810c7c564b4a8..17ab2191f0e1e 100644 --- a/Detectors/GlobalTrackingWorkflow/src/tpcits-match-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/tpcits-match-workflow.cxx @@ -25,6 +25,7 @@ #include "Framework/ConfigContext.h" #include "Framework/CompletionPolicyHelpers.h" #include "TPCCalibration/CorrectionMapsLoader.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -46,6 +47,7 @@ void customize(std::vector& workflowOptions) {"produce-calibration-data", o2::framework::VariantType::Bool, false, {"produce output for TPC vdrift calibration"}}, {"use-full-geometry", o2::framework::VariantType::Bool, false, {"use full geometry instead of the light-weight ITS part"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); @@ -76,6 +78,7 @@ WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& configcont GID::mask_t alowedSources = GID::getSourcesMask("ITS,TPC,TPC-TOF"); GID::mask_t src = alowedSources & GID::getSourcesMask(configcontext.options().get("track-sources")); bool needStrictTRDTOF = (src & GID::getSourcesMask("TPC-TRD,TPC-TOF,TPC-TRD-TOF")).any(); + auto doStag = o2::itsmft::DPLAlpideParamInitializer::isITSStaggeringEnabled(configcontext); // RS at the moment is not passed to the matching w-flow auto sclOpt = o2::tpc::CorrectionMapsLoader::parseGlobalOptions(configcontext.options()); auto useGeom = configcontext.options().get("use-full-geometry"); auto useFT0 = configcontext.options().get("use-ft0"); diff --git a/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx b/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx index ed419700b339b..dc002489c24e2 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx @@ -29,7 +29,7 @@ #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" #include "Framework/DeviceSpec.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "ITSBase/GeometryTGeo.h" #include "ITStracking/IOUtils.h" #include "DetectorsCommonDataFormats/DetID.h" diff --git a/Detectors/GlobalTrackingWorkflow/study/src/DumpTracks.cxx b/Detectors/GlobalTrackingWorkflow/study/src/DumpTracks.cxx index d02f1df3903ec..8c7931d599e93 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/DumpTracks.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/DumpTracks.cxx @@ -21,7 +21,7 @@ #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" #include "FT0Reconstruction/InteractionTag.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsBase/GRPGeomHelper.h" #include "GlobalTrackingStudy/TrackingStudy.h" diff --git a/Detectors/GlobalTrackingWorkflow/study/src/SVStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/SVStudy.cxx index 0129d19b02346..4d0b6bdbdb213 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/SVStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/SVStudy.cxx @@ -29,7 +29,7 @@ #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" #include "FT0Reconstruction/InteractionTag.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsBase/GRPGeomHelper.h" #include "GlobalTrackingStudy/TrackingStudy.h" diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx index 8f6604b029605..1db303d20e5d9 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx @@ -34,7 +34,7 @@ #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" #include "FT0Reconstruction/InteractionTag.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsBase/GRPGeomHelper.h" #include "GlobalTrackingStudy/TrackMCStudy.h" diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx index c68e60059cd3f..a184058a1bfd6 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx @@ -28,7 +28,7 @@ #include "Framework/CCDBParamSpec.h" #include "Framework/DeviceSpec.h" #include "FT0Reconstruction/InteractionTag.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsBase/GRPGeomHelper.h" #include "GlobalTrackingStudy/TrackingStudy.h" diff --git a/Detectors/GlobalTrackingWorkflow/study/src/check-resid-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/check-resid-workflow.cxx index b8230b59405d8..fd4485585103c 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/check-resid-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/check-resid-workflow.cxx @@ -22,6 +22,7 @@ #include "DetectorsRaw/HBFUtilsInitializer.h" #include "TPCCalibration/CorrectionMapsLoader.h" #include "TPCWorkflow/TPCScalerSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -44,6 +45,7 @@ void customize(std::vector& workflowOptions) {"disable-root-input", VariantType::Bool, false, {"disable root-files input reader"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; // o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } diff --git a/Detectors/GlobalTrackingWorkflow/study/src/its-offset-study-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/its-offset-study-workflow.cxx index 9638312e13f78..5d89ba9b7eabf 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/its-offset-study-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/its-offset-study-workflow.cxx @@ -20,6 +20,7 @@ #include "DetectorsBase/DPLWorkflowUtils.h" #include "GlobalTrackingWorkflowHelpers/InputHelper.h" #include "DetectorsRaw/HBFUtilsInitializer.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -39,6 +40,7 @@ void customize(std::vector& workflowOptions) {"track-sources", VariantType::String, std::string{"ITS,ITS-TPC-TRD-TOF,ITS-TPC-TOF,ITS-TPC,ITS-TPC-TRD"}, {"comma-separated list of track sources to use"}}, {"disable-root-input", VariantType::Bool, false, {"disable root-files input reader"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); o2::raw::HBFUtilsInitializer::addConfigOption(options, "o2_tfidinfo.root"); std::swap(workflowOptions, options); } diff --git a/Detectors/GlobalTrackingWorkflow/study/src/trackMCStudy-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/trackMCStudy-workflow.cxx index 7aa53e2190a9e..9e0055a389bfe 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/trackMCStudy-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/trackMCStudy-workflow.cxx @@ -22,6 +22,7 @@ #include "TPCCalibration/CorrectionMapsLoader.h" #include "TPCWorkflow/TPCScalerSpec.h" #include "DetectorsRaw/HBFUtilsInitializer.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -44,6 +45,7 @@ void customize(std::vector& workflowOptions) {"ignore-sv-check", VariantType::Bool, false, {"disable check for SV combinatorics"}}, {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation, never use it"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); diff --git a/Detectors/GlobalTrackingWorkflow/study/src/tracking-study-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/tracking-study-workflow.cxx index 413d2e63653fc..ae2e3b5301a14 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/tracking-study-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/tracking-study-workflow.cxx @@ -22,6 +22,7 @@ #include "DetectorsRaw/HBFUtilsInitializer.h" #include "TPCCalibration/CorrectionMapsLoader.h" #include "TPCWorkflow/TPCScalerSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -43,6 +44,7 @@ void customize(std::vector& workflowOptions) {"cluster-sources", VariantType::String, "TPC,TOF", {"comma-separated list of cluster sources to use"}}, {"disable-root-input", VariantType::Bool, false, {"disable root-files input reader"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-interpolation-workflow.cxx b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-interpolation-workflow.cxx index 2f28fc5bb2d34..65a79a8635a49 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-interpolation-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-interpolation-workflow.cxx @@ -19,6 +19,7 @@ #include "GlobalTrackingWorkflowHelpers/InputHelper.h" #include "TPCInterpolationWorkflow/TPCInterpolationSpec.h" #include "TPCInterpolationWorkflow/TPCResidualWriterSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -44,6 +45,7 @@ void customize(std::vector& workflowOptions) {"debug-output", VariantType::Bool, false, {"Dump extended tracking information for debugging"}}, {"skip-ext-det-residuals", VariantType::Bool, false, {"Do not produce residuals for external detectors"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } diff --git a/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt b/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt index dd6aacf65db99..a23682b085311 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt @@ -118,3 +118,13 @@ o2_add_test_root_macro(CheckDROF.C PUBLIC_LINK_LIBRARIES O2::DataFormatsITS O2::DataFormatsITSMFT LABELS its) + +o2_add_test_root_macro(CheckStaggering.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsITS + O2::DataFormatsITSMFT + O2::ITSMFTReconstruction + O2::DCAFitter + O2::CCDB + O2::DetectorsVertexing + O2::ReconstructionDataFormats + LABELS its COMPILE_ONLY) diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckDROF.C b/Detectors/ITSMFT/ITS/macros/test/CheckDROF.C index 21428ea4fcbc2..01acd2935981d 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CheckDROF.C +++ b/Detectors/ITSMFT/ITS/macros/test/CheckDROF.C @@ -39,13 +39,13 @@ #include "SimulationDataFormat/MCTruthContainer.h" #include "DataFormatsITSMFT/CompCluster.h" #include "DataFormatsITS/TrackITS.h" +#include "DataFormatsITS/Vertex.h" #include "DataFormatsITSMFT/ROFRecord.h" #include "SimulationDataFormat/DigitizationContext.h" #endif using namespace std; -using Vertex = o2::dataformats::Vertex>; void plotHistos(TFile* fWO, TFile* f, const char* append = ""); @@ -93,9 +93,9 @@ struct ParticleInfo { // particle level information for tracks #pragma link C++ class ParticleInfo + ; #pragma link C++ class std::vector < ParticleInfo> + ; -struct VertexInfo { // Vertex level info - float purity; // fraction of main cont. labels to all - Vertex vertex; // reconstructed vertex +struct VertexInfo { // Vertex level info + float purity; // fraction of main cont. labels to all + o2::its::Vertex vertex; // reconstructed vertex int bcInROF{-1}; int rofId{-1}; int event{-1}; // corresponding MC event @@ -199,7 +199,7 @@ void CheckDROF(bool plot = false, bool write = false, const std::string& tracfil std::vector rofRecVec, *rofRecVecP = &rofRecVec; recTree->SetBranchAddress("ITSTracksROF", &rofRecVecP); // Vertices - std::vector* recVerArr = nullptr; + std::vector* recVerArr = nullptr; recTree->SetBranchAddress("Vertices", &recVerArr); std::vector* recVerROFArr = nullptr; recTree->SetBranchAddress("VerticesROF", &recVerROFArr); @@ -876,11 +876,12 @@ void CheckDROF(bool plot = false, bool write = false, const std::string& tracfil if (!trk.hasHitOnLayer(iL) || !trk.isFakeOnLayer(iL) || (part.clusters & (0x1 << iL)) == 0) { continue; } - if (trk.hasHitInNextROF()) { - hFakMig->Fill(trk.getPt(), trk.getNClusters(), iL); - } else { - hFakVal->Fill(trk.getPt(), trk.getNClusters(), iL); - } + // TODO: figure out how to find hit migration + // if (trk.hasHitInNextROF()) { + // hFakMig->Fill(trk.getPt(), trk.getNClusters(), iL); + // } else { + // hFakVal->Fill(trk.getPt(), trk.getNClusters(), iL); + // } } } } diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckStaggering.C b/Detectors/ITSMFT/ITS/macros/test/CheckStaggering.C new file mode 100644 index 0000000000000..e3a79779a5fb1 --- /dev/null +++ b/Detectors/ITSMFT/ITS/macros/test/CheckStaggering.C @@ -0,0 +1,521 @@ +// 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. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DetectorsBase/Propagator.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITS/TrackITS.h" +#include "DataFormatsITS/Vertex.h" +#include "DataFormatsITS/TimeEstBC.h" +#include "CCDB/CcdbApi.h" +#include "CCDB/BasicCCDBManager.h" +#include "CommonConstants/LHCConstants.h" +#include "DataFormatsParameters/GRPMagField.h" +#include "DataFormatsParameters/GRPLHCIFData.h" +#include "ReconstructionDataFormats/Vertex.h" +#include "DetectorsVertexing/SVertexHypothesis.h" +#include "DCAFitter/DCAFitterN.h" +#endif + +constexpr const char* tracFile = "o2trac_its.root"; +constexpr const char* clsFile = "o2clus_its.root"; + +struct PairCandidate { + int posIdx; + int negIdx; + double dca; + double mass; +}; + +std::vector findDirs(const std::string&); + +void CheckStaggering(int runNumber, int max = -1, const std::string& dir = "") +{ + gStyle->SetOptStat(0); + auto dirs = findDirs(dir); + printf("Will iterate over %zu input dirs", dirs.size()); + if (dirs.empty()) { + printf("No input found"); + return; + } + if (max > 0 && (int)dirs.size() > max) { + std::random_device rd; + std::mt19937 g(rd()); + std::shuffle(dirs.begin(), dirs.end(), g); + dirs.resize(max); + printf("restricting to %ddirs", max); + } + + auto& ccdbmgr = o2::ccdb::BasicCCDBManager::instance(); + ccdbmgr.setURL("https://alice-ccdb.cern.ch"); + auto runDuration = ccdbmgr.getRunDuration(runNumber); + auto tRun = runDuration.first + (runDuration.second - runDuration.first) / 2; // time stamp for the middle of the run duration + ccdbmgr.setTimestamp(tRun); + printf("Run %d has TS %lld", runNumber, tRun); + auto geoAligned = ccdbmgr.get("GLO/Config/GeometryAligned"); + auto magField = ccdbmgr.get("GLO/Config/GRPMagField"); + auto grpLHC = ccdbmgr.get("GLO/Config/GRPLHCIF"); + auto bcFill = grpLHC->getBunchFilling(); + bcFill.print(-1); + const o2::base::MatLayerCylSet* matLut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdbmgr.get("GLO/Param/MatLUT")); + o2::base::Propagator::initFieldFromGRP(magField); + auto prop = o2::base::Propagator::Instance(); + prop->setMatLUT(matLut); + const float bz = prop->getNominalBz(); + + auto hNTrkCls = new TH1D("hNTrkCls", "Number of cluster per track;nCls;entries", 4, 3.5, 7.5); + std::array hTrkTS{nullptr}; + for (int i{0}; i < 5; ++i) { + hTrkTS[i] = new TH1D(Form("hTrkTS_%d", i), Form("track time t0 (%s);t0 (BC)", i == 0 ? "all" : Form("NCls=%d", 3 + i)), o2::constants::lhc::LHCMaxBunches, 0, o2::constants::lhc::LHCMaxBunches); + } + auto hTrkTSE = new TH1D("hTrkTSE", "assigned time errors; tE (BC)", 198, -0.5, 198 - 0.5); + + // K0 && Phi-Meson + const float mMinITSPt{0.15}; + // + const int phiMinNCls{7}; + const float phiMaxDCAR2MVTX{0.05}; // max distance to mean vtx + auto hPhiMeson = new TH1D("hPhiMeson", "#phi meson;mass (GeV/c^{2})", 200, 0.96, 1.1); + auto hPhiMesonBkg = new TH1D("hPhiMesonBkg", "#phi meson background;mass (GeV/c^{2})", 200, 0.96, 1.1); + + const int mK0MinNCls{7}; + const float mK0minCosPAXYMeanVertex = 0.98; + const float mK0MaxDCAXY2ToMeanVertex = 0.2; + const float mK0MaxTgl2V0 = 1; + const float mK0MinPt2V0 = 0.3; + const float mK0MinCosPA = 0.999; + o2::vertexing::DCAFitterN<2> k0Ft; + k0Ft.setBz(bz); + k0Ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway + k0Ft.setMaxR(30); + k0Ft.setMaxDZIni(0.1); + k0Ft.setMaxDXYIni(0.1); + k0Ft.setMinParamChange(1e-3); + k0Ft.setMinRelChi2Change(0.9); + k0Ft.setMaxChi2(5); + k0Ft.setUseAbsDCA(true); + auto hK0 = new TH1D("hK0", "K0;mass (GeV/c^{2})", 100, 0.4, 0.6); + o2::vertexing::SVertexHypothesis k0Hyp; + const float k0Par[] = {0., 20, 0., 5.0, 0.0, 1.09004e-03, 2.62291e-04, 8.93179e-03, 2.83121}; + k0Hyp.set(o2::track::PID::K0, o2::track::PID::Pion, o2::track::PID::Pion, k0Par, bz); + + auto hVtxXY = new TH2F("hVtxXY", "seeding vertices XY", 200, -0.3, 0.3, 200, -0.3, 0.3); + auto hVtxZ = new TH1F("hVtxZ", "seeding vertices Z", 200, -16, 16); + auto hVtxNCont = new TH1F("hVtxNCont", "seeding vertices contributors", 100, 0, 100); + auto hVtxZNCont = new TProfile("hVtxZNCont", "seeding vertices z-contributors", 200, -16, 16); + auto hVtxCls = new TProfile("hVtxCls", ";Cls/TF;Cls/Vtx", 400, 20000, 60000); + auto hVtxTS = new TH1D("hVtxTS", "vtx time t0;t0 (BC)", o2::constants::lhc::LHCMaxBunches, 0, o2::constants::lhc::LHCMaxBunches); + + const float minVtxWeight{5}; + float meanVtxWeight{0}; + o2::dataformats::VertexBase meanVtx; + auto accountVtx = [&](o2::its::Vertex const& vtx) { + const float w = vtx.getNContributors(); + if (w >= minVtxWeight) { + meanVtx.setX((meanVtx.getX() * meanVtxWeight + vtx.getX() * w) / (meanVtxWeight + w)); + meanVtx.setY((meanVtx.getY() * meanVtxWeight + vtx.getY() * w) / (meanVtxWeight + w)); + meanVtxWeight += w; + } + }; + + std::vector trkArr, *trkArrPtr{&trkArr}; + std::vector vtxArr, *vtxArrPtr{&vtxArr}; + std::array*, 7> clsArr{nullptr}; + for (size_t iDir{0}; iDir < dirs.size(); ++iDir) { + int progress = static_cast((iDir + 1) * 100 / dirs.size()); + printf("\rProgress: ["); + int barWidth = 50; + int pos = barWidth * progress / 100; + for (int j = 0; j < barWidth; ++j) { + if (j < pos) { + printf("="); + } else if (j == pos) { + printf(">"); + } else { + printf(" "); + } + } + printf("] %d%%", progress); + fflush(stdout); + + const auto& d = dirs[iDir]; + auto fTrks = TFile::Open((d / tracFile).c_str()); + auto fCls = TFile::Open((d / clsFile).c_str()); + if (!fTrks || !fCls || fTrks->IsZombie() || fCls->IsZombie()) { + continue; + } + auto tTrks = fTrks->Get("o2sim"); + auto tCls = fCls->Get("o2sim"); + if (!tTrks || !tCls) { + continue; + } + + tTrks->SetBranchAddress("ITSTrack", &trkArrPtr); + tTrks->SetBranchAddress("Vertices", &vtxArrPtr); + for (int i{0}; i < 7; ++i) { + tCls->SetBranchAddress(Form("ITSClusterComp_%d", i), &clsArr[i]); + } + + for (int iTF{0}; tTrks->LoadTree(iTF) >= 0; ++iTF) { + tTrks->GetEntry(iTF); + tCls->GetEntry(iTF); + + size_t ncls = 0; + for (int i{0}; i < 7; ++i) { + ncls += clsArr[i]->size(); + } + + // for each TF built pool of positive and negaitve tracks + std::vector posPool, negPool; + + for (const auto& trk : trkArr) { + hNTrkCls->Fill(trk.getNClusters()); + hTrkTS[0]->Fill(std::fmod(trk.getTimeStamp().getTimeStamp(), o2::constants::lhc::LHCMaxBunches)); + hTrkTS[trk.getNClusters() - 3]->Fill(std::fmod(trk.getTimeStamp().getTimeStamp(), o2::constants::lhc::LHCMaxBunches)); + hTrkTSE->Fill(trk.getTimeStamp().getTimeStampError()); + + if (trk.getPt() > mMinITSPt) { + if (trk.getCharge() > 0) { + posPool.push_back(&trk); + } else { + negPool.push_back(&trk); + } + } + } + + for (const auto& vtx : vtxArr) { + hVtxXY->Fill(vtx.getX(), vtx.getY()); + hVtxZ->Fill(vtx.getZ()); + hVtxNCont->Fill(vtx.getNContributors()); + hVtxZNCont->Fill(vtx.getZ(), vtx.getNContributors()); + hVtxTS->Fill(vtx.getTimeStamp().getTimeStamp()); + accountVtx(vtx); + } + hVtxCls->Fill(ncls, (float)ncls / (float)vtxArr.size()); + + std::vector k0Cands; + for (int iPos{0}; iPos < (int)posPool.size(); ++iPos) { + const auto pos = posPool[iPos]; + for (int iNeg{0}; iNeg < (int)negPool.size(); ++iNeg) { + const auto neg = negPool[iNeg]; + bool overlap = std::abs(pos->getTimeStamp().getTimeStamp() - neg->getTimeStamp().getTimeStamp()) <= (pos->getTimeStamp().getTimeStampError() + neg->getTimeStamp().getTimeStampError()); + if (!overlap) { + continue; + } + + // phi-meson + if (pos->getNClusters() >= phiMinNCls && neg->getNClusters() >= phiMinNCls) { + o2::dataformats::DCA posDCA, negDCA; + o2::track::TrackParCov posPhi = *pos; + posPhi.setPID(o2::track::PID::Kaon); + o2::track::TrackParCov negPhi = *neg; + negPhi.setPID(o2::track::PID::Kaon); + if (prop->propagateToDCA(meanVtx, posPhi, bz, 2.0, o2::base::Propagator::MatCorrType::USEMatCorrLUT, &posDCA) && prop->propagateToDCA(meanVtx, negPhi, bz, 2.0, o2::base::Propagator::MatCorrType::USEMatCorrLUT, &negDCA)) { + if (posDCA.getR2() < phiMaxDCAR2MVTX && negDCA.getR2() < phiMaxDCAR2MVTX) { + std::array pP{}, pN{}; + posPhi.getPxPyPzGlo(pP); + negPhi.getPxPyPzGlo(pN); + TLorentzVector p1, p2; + p1.SetXYZM(pP[0], pP[1], pP[2], posPhi.getPID().getMass()); + p2.SetXYZM(pN[0], pN[1], pN[2], negPhi.getPID().getMass()); + TLorentzVector mother = p1 + p2; + hPhiMeson->Fill(mother.M()); + // rotate on daughter track to estimate background + for (int i{0}; i < 10; ++i) { + double theta = gRandom->Uniform(165.f, 195.f) * TMath::DegToRad(); + double pxN = pN[0] * cos(theta) - pN[1] * sin(theta); + double pyN = pN[0] * sin(theta) + pN[1] * cos(theta); + double pxP = pP[0] * cos(-theta) - pP[1] * sin(-theta); + double pyP = pP[0] * sin(-theta) + pP[1] * cos(-theta); + p1.SetXYZM(pxP, pyP, pP[2], posPhi.getPID().getMass()); + p2.SetXYZM(pxN, pyN, pN[2], negPhi.getPID().getMass()); + mother = p1 + p2; + hPhiMesonBkg->Fill(mother.M()); + } + } + } + } + // K0 + if (pos->getNClusters() >= mK0MinNCls && neg->getNClusters() >= mK0MinNCls) { + o2::track::TrackParCov posPion = *pos; + posPion.setPID(o2::track::PID::Pion); + o2::track::TrackParCov negPion = *neg; + negPion.setPID(o2::track::PID::Pion); + int ncand = k0Ft.process(posPion, negPion); + const int cand = 0; + if (ncand) { + const auto& v0XYZ = k0Ft.getPCACandidate(); + float dxv0 = v0XYZ[0] - meanVtx.getX(), dyv0 = v0XYZ[1] - meanVtx.getY(), r2v0 = dxv0 * dxv0 + dyv0 * dyv0; + if (!k0Ft.isPropagateTracksToVertexDone(cand) && !k0Ft.propagateTracksToVertex(cand)) { + continue; + } + const auto& trPProp = k0Ft.getTrack(0, cand); + const auto& trNProp = k0Ft.getTrack(1, cand); + std::array pP{}, pN{}; + trPProp.getPxPyPzGlo(pP); + trNProp.getPxPyPzGlo(pN); + // estimate DCA of neutral V0 track to beamline: straight line with parametric equation + // x = X0 + pV0[0]*t, y = Y0 + pV0[1]*t reaches DCA to beamline (Xv, Yv) at + // t = -[ (x0-Xv)*pV0[0] + (y0-Yv)*pV0[1]) ] / ( pT(pV0)^2 ) + // Similar equation for 3D distance involving pV0[2] + std::array pV0 = {pP[0] + pN[0], pP[1] + pN[1], pP[2] + pN[2]}; + float pt2V0 = pV0[0] * pV0[0] + pV0[1] * pV0[1], prodXYv0 = dxv0 * pV0[0] + dyv0 * pV0[1], tDCAXY = prodXYv0 / pt2V0; + if (pt2V0 < mK0MinPt2V0) { // pt cut + continue; + } + if (pV0[2] * pV0[2] / pt2V0 > mK0MaxTgl2V0) { // tgLambda cut + continue; + } + float dcaX = dxv0 - pV0[0] * tDCAXY, dcaY = dyv0 - pV0[1] * tDCAXY, dca2 = dcaX * dcaX + dcaY * dcaY; + float cosPAXY = prodXYv0 / std::sqrt(r2v0 * pt2V0); + if (dca2 > mK0MaxDCAXY2ToMeanVertex || cosPAXY < mK0minCosPAXYMeanVertex) { + continue; + } + float p2V0 = pt2V0 + pV0[2] * pV0[2], ptV0 = std::sqrt(pt2V0); + float p2Pos = pP[0] * pP[0] + pP[1] * pP[1] + pP[2] * pP[2], p2Neg = pN[0] * pN[0] + pN[1] * pN[1] + pN[2] * pN[2]; + if (!k0Hyp.check(p2Pos, p2Neg, p2V0, ptV0)) { + continue; + } + + float bestCosPA = mK0MinCosPA; + bool candFound = false; + for (const auto& vtx : vtxArr) { + if (vtx.getNContributors() > minVtxWeight) { + const auto vtxT = vtx.getTimeStamp().makeSymmetrical(); + bool overlapPos = std::abs(pos->getTimeStamp().getTimeStamp() - vtxT.getTimeStamp()) <= (pos->getTimeStamp().getTimeStampError() + vtxT.getTimeStampError()); + bool overlapNeg = std::abs(neg->getTimeStamp().getTimeStamp() - vtxT.getTimeStamp()) <= (neg->getTimeStamp().getTimeStampError() + vtxT.getTimeStampError()); + if (overlapPos && overlapNeg) { + float dx = v0XYZ[0] - vtx.getX(), dy = v0XYZ[1] - vtx.getY(), dz = v0XYZ[2] - vtx.getZ(), prodXYZv0 = dx * pV0[0] + dy * pV0[1] + dz * pV0[2]; + float cosPA = prodXYZv0 / std::sqrt((dx * dx + dy * dy + dz * dz) * p2V0); + if (cosPA > bestCosPA) { + bestCosPA = cosPA; + candFound = true; + } + } + } + } + if (candFound) { + TLorentzVector p1, p2; + p1.SetXYZM(pP[0], pP[1], pP[2], posPion.getPID().getMass()); + p2.SetXYZM(pN[0], pN[1], pN[2], negPion.getPID().getMass()); + TLorentzVector mother = p1 + p2; + double mass = mother.M(); + k0Cands.emplace_back(iPos, iNeg, k0Ft.getChi2AtPCACandidate(cand), mass); + } + } + } + } + } + + // disambiguiate candidates by using the smallest DCA one + std::sort(k0Cands.begin(), k0Cands.end(), [](const auto& a, const auto& b) { return a.dca < b.dca; }); + std::vector posUsed(posPool.size(), false); + std::vector negUsed(negPool.size(), false); + for (const auto& c : k0Cands) { + if (!posUsed[c.posIdx] && !negUsed[c.negIdx]) { + posUsed[c.posIdx] = true; + negUsed[c.negIdx] = true; + hK0->Fill(c.mass); + } + } + } + + fTrks->Close(); + fCls->Close(); + } + + auto drawBCPattern = [&]() { + gPad->Update(); + // draw BC pattern + double ymin = gPad->GetUymin(); + double ymax = gPad->GetUymax(); + auto interactingBC = bcFill.getPattern(); + TLine* lastLine{nullptr}; + for (int iBC{0}; iBC < (int)interactingBC.size(); ++iBC) { + if (interactingBC.test(iBC)) { + TLine* line = new TLine(iBC, ymin, iBC, ymax); + line->SetLineColor(kRed); + line->SetLineWidth(1); + line->SetLineStyle(kDashed); + line->Draw(); + lastLine = line; + } + } + return lastLine; + }; + + { + TFile out(Form("check_%d.root", runNumber), "RECREATE"); + out.WriteTObject(hNTrkCls); + for (int i{0}; i < 5; ++i) { + out.WriteTObject(hTrkTS[i]); + } + out.WriteTObject(hTrkTSE); + out.WriteTObject(hPhiMeson); + out.WriteTObject(hPhiMesonBkg); + out.WriteTObject(hK0); + out.WriteTObject(hVtxXY); + out.WriteTObject(hVtxZ); + out.WriteTObject(hVtxNCont); + out.WriteTObject(hVtxZNCont); + out.WriteTObject(hVtxTS); + out.WriteTObject(hVtxCls); + } + + // fitK0(hK0, runNumber); + // fitPhiMeson(hPhiMeson, runNumber); + { + auto c = new TCanvas(); + hNTrkCls->Draw(); + c->Draw(); + c->SaveAs(Form("trk_%d.pdf", runNumber)); + } + { + auto c = new TCanvas(); + c->Divide(3, 2); + for (int i{0}; i < 5; ++i) { + c->cd(i + 1); + hTrkTS[i]->Draw(); + auto lastLine = drawBCPattern(); + if (i == 0) { + auto leg = new TLegend(0.6, 0.6, 0.9, 0.9); + leg->AddEntry(lastLine, "Interacting BCs", "l"); + leg->AddEntry(hTrkTS[i], "Track timestamp"); + leg->Draw(); + } + } + c->cd(6); + hTrkTSE->Draw(); + c->Draw(); + c->SaveAs(Form("time_%d.pdf", runNumber)); + } + { + auto c = new TCanvas(); + c->Divide(2, 1); + { + c->cd(1); + hPhiMeson->Draw(); + gPad->Update(); + double ymin = gPad->GetUymin(); + double ymax = gPad->GetUymax(); + const float mass = 1.019461; + TLine* line = new TLine(mass, ymin, mass, ymax); + line->SetLineColor(kRed); + line->SetLineWidth(1); + line->SetLineStyle(kDashed); + line->Draw(); + } + { + c->cd(2); + hK0->Draw(); + gPad->Update(); + double ymin = gPad->GetUymin(); + double ymax = gPad->GetUymax(); + const float mass = 0.497611; + TLine* line = new TLine(mass, ymin, mass, ymax); + line->SetLineColor(kRed); + line->SetLineWidth(1); + line->SetLineStyle(kDashed); + line->Draw(); + } + c->Draw(); + c->SaveAs(Form("mass_%d.pdf", runNumber)); + } + { + auto c = new TCanvas(); + c->Divide(3, 2); + { + c->cd(1); + hVtxXY->Draw("col"); + } + { + c->cd(2); + hVtxZ->Draw(); + } + { + c->cd(3); + hVtxNCont->Draw(); + } + { + c->cd(4); + hVtxZNCont->Draw(); + } + { + c->cd(5); + hVtxCls->Draw(); + } + { + c->cd(6); + hVtxTS->Draw(); + auto lastLine = drawBCPattern(); + auto leg = new TLegend(0.6, 0.6, 0.9, 0.9); + leg->AddEntry(lastLine, "Interacting BCs", "l"); + leg->AddEntry(hVtxTS, "Track timestamp"); + leg->Draw(); + } + c->Draw(); + c->SaveAs(Form("vertex_%d.pdf", runNumber)); + } +} + +std::vector findDirs(const std::string& roots) +{ + std::filesystem::path root; + if (roots.empty()) { + root = std::filesystem::current_path(); + } else { + root = roots; + } + namespace fs = std::filesystem; + std::vector result; + auto has_files = [](const fs::path& dir) { + auto s = dir / tracFile; + return fs::exists(dir / tracFile) && fs::exists(dir / clsFile) && + fs::is_regular_file(dir / tracFile) && fs::is_regular_file(dir / clsFile); + }; + if (fs::is_directory(root) && has_files(root)) { + result.push_back(root); + return result; + } + for (const auto& entry : fs::recursive_directory_iterator(root)) { + if (!entry.is_directory()) { + continue; + } + const fs::path dir = entry.path(); + if (has_files(dir)) { + result.push_back(dir); + } + } + return result; +} diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/ImpactParameter.cxx b/Detectors/ITSMFT/ITS/postprocessing/studies/src/ImpactParameter.cxx index c0aaabddaca1b..bc8b931190ed1 100644 --- a/Detectors/ITSMFT/ITS/postprocessing/studies/src/ImpactParameter.cxx +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/ImpactParameter.cxx @@ -29,7 +29,7 @@ #include "CommonUtils/TreeStreamRedirector.h" #include "DetectorsBase/GRPGeomHelper.h" #include "DataFormatsParameters/GRPECSObject.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DetectorsCommonDataFormats/DetID.h" #include "Framework/DeviceSpec.h" #include "CommonUtils/ConfigurableParam.h" diff --git a/Detectors/ITSMFT/ITS/postprocessing/workflow/standalone-postprocessing-workflow.cxx b/Detectors/ITSMFT/ITS/postprocessing/workflow/standalone-postprocessing-workflow.cxx index 30fb39c77f235..8bcb444f650bd 100644 --- a/Detectors/ITSMFT/ITS/postprocessing/workflow/standalone-postprocessing-workflow.cxx +++ b/Detectors/ITSMFT/ITS/postprocessing/workflow/standalone-postprocessing-workflow.cxx @@ -16,6 +16,7 @@ #include "Framework/CompletionPolicyHelpers.h" #include "GlobalTrackingWorkflowHelpers/InputHelper.h" #include "DetectorsRaw/HBFUtilsInitializer.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" // Include studies hereafter #include "ITSStudies/ImpactParameter.h" @@ -54,6 +55,7 @@ void customize(std::vector& workflowOptions) {"track-extension-study", VariantType::Bool, false, {"Perform the track extension study"}}, {"efficiency-study", VariantType::Bool, false, {"Perform the efficiency study"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); // o2::raw::HBFUtilsInitializer::addConfigOption(options, "o2_tfidinfo.root"); std::swap(workflowOptions, options); } @@ -135,8 +137,8 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) LOGP(info, "No study selected, dryrunning"); } - o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); - // write the configuration used for the studies workflow + // o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); + // write the configuration used for the studies workflow o2::conf::ConfigurableParam::writeINI("o2_its_standalone_configuration.ini"); return std::move(specs); diff --git a/Detectors/ITSMFT/ITS/reconstruction/CMakeLists.txt b/Detectors/ITSMFT/ITS/reconstruction/CMakeLists.txt index d2126be1da2c6..be42015b95795 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/reconstruction/CMakeLists.txt @@ -11,8 +11,6 @@ o2_add_library(ITSReconstruction SOURCES src/RecoGeomHelper.cxx - src/FastMultEstConfig.cxx - src/FastMultEst.cxx PUBLIC_LINK_LIBRARIES O2::ITSBase O2::ITSMFTReconstruction O2::DataFormatsITS @@ -20,6 +18,4 @@ o2_add_library(ITSReconstruction o2_target_root_dictionary( ITSReconstruction - HEADERS include/ITSReconstruction/RecoGeomHelper.h - include/ITSReconstruction/FastMultEst.h - include/ITSReconstruction/FastMultEstConfig.h) + HEADERS include/ITSReconstruction/RecoGeomHelper.h) diff --git a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEst.h b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEst.h deleted file mode 100644 index 9e8299e89b404..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEst.h +++ /dev/null @@ -1,70 +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 FastMultEst.h -/// \brief Fast multiplicity estimator for ITS -/// \author ruben.shahoyan@cern.ch - -#ifndef ALICEO2_ITS_FASTMULTEST_ -#define ALICEO2_ITS_FASTMULTEST_ - -#include "ITSMFTReconstruction/ChipMappingITS.h" -#include "DataFormatsITSMFT/ROFRecord.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include -#include "ITSReconstruction/FastMultEstConfig.h" -#include -#include - -namespace o2 -{ -namespace its -{ - -struct FastMultEst { - - static constexpr int NLayers = o2::itsmft::ChipMappingITS::NLayers; - - float mult = 0.; /// estimated signal clusters multipliciy at reference (1st?) layer - float noisePerChip = 0.; /// estimated or imposed noise per chip - float cov[3] = {0.}; /// covariance matrix of estimation - float chi2 = 0.; /// chi2 - int nLayersUsed = 0; /// number of layers actually used - uint32_t lastRandomSeed = 0; /// state of the gRandom before - - std::array nClPerLayer{0}; // measured N Cl per layer selectROFs - FastMultEst(); - - static uint32_t getCurrentRandomSeed(); - int selectROFs(const gsl::span rofs, const gsl::span clus, - const gsl::span trig, std::vector& sel); - - void fillNClPerLayer(const gsl::span& clusters); - float process(const std::array ncl) - { - return FastMultEstConfig::Instance().imposeNoisePerChip > 0 ? processNoiseImposed(ncl) : processNoiseFree(ncl); - } - float processNoiseFree(const std::array ncl); - float processNoiseImposed(const std::array ncl); - float process(const gsl::span& clusters) - { - fillNClPerLayer(clusters); - return process(nClPerLayer); - } - static bool sSeedSet; - - ClassDefNV(FastMultEst, 1); -}; - -} // namespace its -} // namespace o2 - -#endif diff --git a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/TrivialVertexer.h b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/TrivialVertexer.h deleted file mode 100644 index 3eb218dc973f6..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/TrivialVertexer.h +++ /dev/null @@ -1,70 +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 TrivialVertexer.h -/// \brief Definition of the ITS trivial vertex finder -#ifndef ALICEO2_ITS_TRIVIALVERTEXER_H -#define ALICEO2_ITS_TRIVIALVERTEXER_H - -#include - -#include "Rtypes.h" // for TrivialVertexer::Class, Double_t, ClassDef, etc - -class TFile; -class TTree; -class FairMCEventHeader; - -namespace o2 -{ -namespace itsmft -{ -class Cluster; -} -} // namespace o2 - -namespace o2 -{ -class MCCompLabel; -namespace dataformats -{ -template -class MCTruthContainer; -} -namespace its -{ -class TrivialVertexer -{ - using Cluster = o2::itsmft::Cluster; - using Label = o2::MCCompLabel; - - public: - TrivialVertexer(); - ~TrivialVertexer(); - - TrivialVertexer(const TrivialVertexer&) = delete; - TrivialVertexer& operator=(const TrivialVertexer&) = delete; - - Bool_t openInputFile(const Char_t*); - - void process(const std::vector& clusters, std::vector>& vertices); - void setMCTruthContainer(const o2::dataformats::MCTruthContainer* truth) { mClsLabels = truth; } - - private: - const o2::dataformats::MCTruthContainer* mClsLabels = nullptr; // Cluster MC labels - - TFile* mFile = nullptr; - TTree* mTree = nullptr; - FairMCEventHeader* mHeader = nullptr; -}; -} // namespace its -} // namespace o2 - -#endif /* ALICEO2_ITS_TRIVIALVERTEXER_H */ diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/FastMultEst.cxx b/Detectors/ITSMFT/ITS/reconstruction/src/FastMultEst.cxx deleted file mode 100644 index c547996c6f356..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/src/FastMultEst.cxx +++ /dev/null @@ -1,189 +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 FastMultEst.h -/// \brief Fast multiplicity estimator for ITS -/// \author ruben.shahoyan@cern.ch - -#include "ITSReconstruction/FastMultEst.h" -#include "ITSMFTBase/DPLAlpideParam.h" -#include "Framework/Logger.h" -#include -#include -#include - -using namespace o2::its; - -bool FastMultEst::sSeedSet = false; - -///______________________________________________________ -FastMultEst::FastMultEst() -{ - if (!sSeedSet && FastMultEstConfig::Instance().cutRandomFraction > 0.f) { - sSeedSet = true; - if (FastMultEstConfig::Instance().randomSeed > 0) { - gRandom->SetSeed(FastMultEstConfig::Instance().randomSeed); - } else if (FastMultEstConfig::Instance().randomSeed < 0) { - gRandom->SetSeed(std::time(nullptr) % 0xffff); - } - } -} - -///______________________________________________________ -/// find multiplicity for given set of clusters -void FastMultEst::fillNClPerLayer(const gsl::span& clusters) -{ - int lr = FastMultEst::NLayers - 1, nchAcc = o2::itsmft::ChipMappingITS::getNChips() - o2::itsmft::ChipMappingITS::getNChipsPerLr(lr); - std::memset(&nClPerLayer[0], 0, sizeof(int) * FastMultEst::NLayers); - for (int i = clusters.size(); i--;) { // profit from clusters being ordered in chip increasing order - while (clusters[i].getSensorID() < nchAcc) { - assert(lr >= 0); - nchAcc -= o2::itsmft::ChipMappingITS::getNChipsPerLr(--lr); - } - nClPerLayer[lr]++; - } -} - -///______________________________________________________ -/// find multiplicity for given number of clusters per layer -float FastMultEst::processNoiseFree(const std::array ncl) -{ - // we assume that on the used layers the observed number of clusters is defined by the - // the noise ~ nu * Nchips and contribution from the signal tracks Ntr*mAccCorr - const auto& conf = FastMultEstConfig::Instance(); - - float mat[3] = {0}, b[2] = {0}; - nLayersUsed = 0; - for (int il = conf.firstLayer; il <= conf.lastLayer; il++) { - if (ncl[il] > 0) { - int nch = o2::itsmft::ChipMappingITS::getNChipsPerLr(il); - float err2i = 1. / ncl[il]; - float m2n = nch * err2i; - mat[0] += err2i * conf.accCorr[il] * conf.accCorr[il]; - mat[2] += nch * m2n; - mat[1] += conf.accCorr[il] * m2n; // non-diagonal element - b[0] += conf.accCorr[il]; - b[1] += nch; - nLayersUsed++; - } - } - mult = noisePerChip = chi2 = -1; - float det = mat[0] * mat[2] - mat[1] * mat[1]; - if (nLayersUsed < 2 || std::abs(det) < 1e-15) { - return -1; - } - float detI = 1. / det; - mult = detI * (b[0] * mat[2] - b[1] * mat[1]); - noisePerChip = detI * (b[1] * mat[0] - b[0] * mat[1]); - cov[0] = mat[2] * detI; - cov[2] = mat[0] * detI; - cov[1] = -mat[1] * detI; - chi2 = 0.; - for (int il = conf.firstLayer; il <= conf.lastLayer; il++) { - if (ncl[il] > 0) { - int nch = o2::itsmft::ChipMappingITS::getNChipsPerLr(il); - float diff = mult * conf.accCorr[il] + nch * noisePerChip - ncl[il]; - chi2 += diff * diff / ncl[il]; - } - } - chi2 = nLayersUsed > 2 ? chi2 / (nLayersUsed - 2) : 0.; - return mult > 0 ? mult : 0; -} - -///______________________________________________________ -/// find multiplicity for given number of clusters per layer with mean noise imposed -float FastMultEst::processNoiseImposed(const std::array ncl) -{ - // we assume that on the used layers the observed number of clusters is defined by the - // the noise ~ nu * Nchips and contribution from the signal tracks Ntr*conf.accCorr - // - const auto& conf = FastMultEstConfig::Instance(); - float accSum = 0., accWSum = 0., noiseSum = 0.; - nLayersUsed = 0; - for (int il = conf.firstLayer; il <= conf.lastLayer; il++) { - if (ncl[il] > 0) { - float err = 1. / ncl[il]; - accSum += conf.accCorr[il]; - accWSum += conf.accCorr[il] * conf.accCorr[il] * err; - noiseSum += o2::itsmft::ChipMappingITS::getNChipsPerLr(il) * conf.accCorr[il] * err; - nLayersUsed++; - } - } - mult = 0; - if (nLayersUsed) { - mult = (accSum - noisePerChip * noiseSum) / accWSum; - } - return mult; -} - -int FastMultEst::selectROFs(const gsl::span rofs, const gsl::span clus, - const gsl::span trig, std::vector& sel) -{ - int nrof = rofs.size(), nsel = 0; - const auto& multEstConf = FastMultEstConfig::Instance(); // parameters for mult estimation and cuts - sel.clear(); - sel.resize(nrof, true); // by default select all - lastRandomSeed = gRandom->GetSeed(); - if (multEstConf.isMultCutRequested()) { - for (uint32_t irof = 0; irof < nrof; irof++) { - nsel += sel[irof] = multEstConf.isPassingMultCut(process(rofs[irof].getROFData(clus))); - } - } else { - nsel = nrof; - } - using IdNT = std::pair; - if (multEstConf.cutRandomFraction > 0.) { - int ntrig = trig.size(), currTrig = 0; - if (multEstConf.preferTriggered) { - const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); - std::vector nTrigROF; - nTrigROF.reserve(nrof); - for (uint32_t irof = 0; irof < nrof; irof++) { - if (sel[irof]) { - if (nsel && gRandom->Rndm() < multEstConf.cutRandomFraction) { - nsel--; - } - auto irROF = rofs[irof].getBCData(); - while (currTrig < ntrig && trig[currTrig].ir < irROF) { // triggers are sorted, jump to 1st one not less than current ROF - currTrig++; - } - auto& trof = nTrigROF.emplace_back(irof, 0); - irROF += alpParams.roFrameLengthInBC; - while (currTrig < ntrig && trig[currTrig].ir < irROF) { - trof.second++; - currTrig++; - } - } - } - if (nsel > 0) { - sort(nTrigROF.begin(), nTrigROF.end(), [](const IdNT& a, const IdNT& b) { return a.second > b.second; }); // order in number of triggers - auto last = nTrigROF.begin() + nsel; - sort(nTrigROF.begin(), last, [](const IdNT& a, const IdNT& b) { return a.first < b.first; }); // order in ROF ID first nsel ROFs - } - for (int i = nsel; i < int(nTrigROF.size()); i++) { // reject ROFs in the tail - sel[nTrigROF[i].first] = false; - } - } else { // dummy random rejection - for (int irof = 0; irof < nrof; irof++) { - if (sel[irof]) { - float sr = gRandom->Rndm(); - if (gRandom->Rndm() < multEstConf.cutRandomFraction) { - sel[irof] = false; - nsel--; - } - } - } - } - } - LOGP(debug, "NSel = {} of {} rofs Seeds: before {} after {}", nsel, nrof, lastRandomSeed, gRandom->GetSeed()); - - return nsel; -} diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/ITSReconstructionLinkDef.h b/Detectors/ITSMFT/ITS/reconstruction/src/ITSReconstructionLinkDef.h index 67622303fc840..3bc8ee0f5403b 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/src/ITSReconstructionLinkDef.h +++ b/Detectors/ITSMFT/ITS/reconstruction/src/ITSReconstructionLinkDef.h @@ -16,8 +16,5 @@ #pragma link off all functions; #pragma link C++ class o2::its::RecoGeomHelper + ; -#pragma link C++ class o2::its::FastMultEst + ; -#pragma link C++ class o2::its::FastMultEstConfig + ; -#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::FastMultEstConfig> + ; #endif diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/TrivialVertexer.cxx b/Detectors/ITSMFT/ITS/reconstruction/src/TrivialVertexer.cxx deleted file mode 100644 index cb7f1eeacb02e..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/src/TrivialVertexer.cxx +++ /dev/null @@ -1,108 +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 TrivialVertexer.cxx -/// \brief Implementation of the ITS trivial vertex finder - -#include - -#include "TFile.h" -#include "TTree.h" - -#include "FairMCEventHeader.h" -#include - -#include "ITSReconstruction/TrivialVertexer.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" - -using namespace o2::itsmft; -using namespace o2::its; - -using Point3Df = o2::math_utils::Point3D; - -TrivialVertexer::TrivialVertexer() = default; - -TrivialVertexer::~TrivialVertexer() -{ - if (mHeader) - delete mHeader; - if (mTree) - delete mTree; - if (mFile) - delete mFile; -} - -Bool_t TrivialVertexer::openInputFile(const Char_t* fname) -{ - mFile = TFile::Open(fname, "old"); - if (!mFile) { - LOG(error) << "TrivialVertexer::openInputFile() : " - << "Cannot open the input file !"; - return kFALSE; - } - mTree = (TTree*)mFile->Get("o2sim"); - if (!mTree) { - LOG(error) << "TrivialVertexer::openInputFile() : " - << "Cannot get the input tree !"; - return kFALSE; - } - Int_t rc = mTree->SetBranchAddress("MCEventHeader.", &mHeader); - if (rc != 0) { - LOG(error) << "TrivialVertexer::openInputFile() : " - << "Cannot get the input branch ! rc=" << rc; - return kFALSE; - } - return kTRUE; -} - -void TrivialVertexer::process(const std::vector& clusters, std::vector>& vertices) -{ - if (mClsLabels == nullptr) { - LOG(info) << "TrivialVertexer::process() : " - << "No cluster labels available ! Running with a default MC vertex..."; - vertices.emplace_back(std::array{0., 0., 0.}); - return; - } - - if (mTree == nullptr) { - LOG(info) << "TrivialVertexer::process() : " - << "No MC information available ! Running with a default MC vertex..."; - vertices.emplace_back(std::array{0., 0., 0.}); - return; - } - - Int_t lastEventID = 0; - Int_t firstEventID = std::numeric_limits::max(); - - // Find the first and last MC event within this TF - for (Int_t i = 0; i < clusters.size(); ++i) { - auto mclab = (mClsLabels->getLabels(i))[0]; - if (mclab.getTrackID() == -1) - continue; // noise - auto id = mclab.getEventID(); - if (id < firstEventID) - firstEventID = id; - if (id > lastEventID) - lastEventID = id; - } - - for (Int_t mcEv = firstEventID; mcEv <= lastEventID; ++mcEv) { - mTree->GetEvent(mcEv); - Double_t vx = mHeader->GetX(); - Double_t vy = mHeader->GetY(); - Double_t vz = mHeader->GetZ(); - vertices.emplace_back(std::array{vx, vy, vz}); - LOG(info) << "TrivialVertexer::process() : " - << "MC event #" << mcEv << " with vertex (" << vx << ',' << vy << ',' << vz << ')'; - } -} diff --git a/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt index 001ee537f50d2..8d8304d16764f 100644 --- a/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt @@ -14,15 +14,15 @@ o2_add_library(ITStracking SOURCES src/ClusterLines.cxx src/Cluster.cxx src/Configuration.cxx + src/FastMultEstConfig.cxx + src/FastMultEst.cxx src/TimeFrame.cxx src/IOUtils.cxx src/Tracker.cxx src/TrackerTraits.cxx src/TrackingConfigParam.cxx - src/ClusterLines.cxx src/Vertexer.cxx src/VertexerTraits.cxx - src/Smoother.cxx PUBLIC_LINK_LIBRARIES O2::GPUCommon Microsoft.GSL::GSL @@ -30,6 +30,7 @@ o2_add_library(ITStracking O2::DataFormatsITSMFT O2::SimulationDataFormat O2::ITSBase + O2::CommonUtils O2::ITSReconstruction O2::ITSMFTReconstruction O2::DataFormatsITS @@ -50,6 +51,9 @@ o2_target_root_dictionary(ITStracking HEADERS include/ITStracking/ClusterLines.h include/ITStracking/Tracklet.h include/ITStracking/Cluster.h + include/ITStracking/Definitions.h + include/ITStracking/FastMultEst.h + include/ITStracking/FastMultEstConfig.h include/ITStracking/TrackingConfigParam.h LINKDEF src/TrackingLinkDef.h) diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/ClusterLinesGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/ClusterLinesGPU.h deleted file mode 100644 index 75d75e0f67700..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/ClusterLinesGPU.h +++ /dev/null @@ -1,73 +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 ClusterLinesGPU.h -/// \brief GPU-compliant version of ClusterLines, for the moment separated, might create a common traits for ClusterLines + later specifications for each arch, later. - -#ifndef ITSTRACKINGGPU_CLUSTERLINESGPU_H_ -#define ITSTRACKINGGPU_CLUSTERLINESGPU_H_ - -#include "GPUCommonDef.h" -#include /// Required to properly compile MathUtils -#include "ITStracking/ClusterLines.h" - -namespace o2 -{ -namespace its -{ -namespace gpu -{ - -struct GPUVertex final { - GPUhd() GPUVertex() : realVertex{false} - { - } - - GPUhd() GPUVertex(float x, float y, float z, float eX, float eY, float eZ, int contrib) : xCoord{x}, - yCoord{y}, - zCoord{z}, - errorX{eZ}, - errorY{eY}, - errorZ{eZ}, - contributors{contrib}, - realVertex{true} - { - } - float xCoord; - float yCoord; - float zCoord; - float errorX; - float errorY; - float errorZ; - int contributors; - int timeStamp; - unsigned char realVertex; -}; - -class ClusterLinesGPU final -{ - public: - GPUd() ClusterLinesGPU(const Line& firstLine, const Line& secondLine); // poor man solution to calculate duplets' centroid - GPUd() void computeClusterCentroid(); - GPUdi() float* getVertex() { return mVertex; } - - private: - float mAMatrix[6]; // AX=B - float mBMatrix[3]; // AX=B - float mVertexCandidate[3]; // vertex candidate - float mWeightMatrix[9]; // weight matrix - float mVertex[3]; // cluster centroid position -}; - -} // namespace gpu -} // namespace its -} // namespace o2 -#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameChunk.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameChunk.h deleted file mode 100644 index 4a028bf12eb40..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameChunk.h +++ /dev/null @@ -1,148 +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 TRACKINGITSGPU_INCLUDE_TIMEFRAMECHUNKGPU_H -#define TRACKINGITSGPU_INCLUDE_TIMEFRAMECHUNKGPU_H - -#include "ITStracking/Configuration.h" -#include "ITStracking/TimeFrame.h" - -#include "ITStrackingGPU/ClusterLinesGPU.h" -#include "ITStrackingGPU/Stream.h" - -#include - -namespace o2::its::gpu -{ -template -struct StaticTrackingParameters { - StaticTrackingParameters& operator=(const StaticTrackingParameters& t) = default; - void set(const TrackingParameters& pars) - { - ClusterSharing = pars.ClusterSharing; - MinTrackLength = pars.MinTrackLength; - NSigmaCut = pars.NSigmaCut; - PVres = pars.PVres; - DeltaROF = pars.DeltaROF; - ZBins = pars.ZBins; - PhiBins = pars.PhiBins; - CellDeltaTanLambdaSigma = pars.CellDeltaTanLambdaSigma; - } - - /// General parameters - int ClusterSharing = 0; - int MinTrackLength = nLayers; - float NSigmaCut = 5; - float PVres = 1.e-2f; - int DeltaROF = 0; - int ZBins{256}; - int PhiBins{128}; - - /// Cell finding cuts - float CellDeltaTanLambdaSigma = 0.007f; -}; - -template -class GpuTimeFrameChunk -{ - public: - static size_t computeScalingSizeBytes(const int, const TimeFrameGPUParameters&); - static size_t computeFixedSizeBytes(const TimeFrameGPUParameters&); - static size_t computeRofPerChunk(const TimeFrameGPUParameters&, const size_t); - - GpuTimeFrameChunk() = delete; - GpuTimeFrameChunk(o2::its::TimeFrame* tf, TimeFrameGPUParameters& conf) - { - mTimeFramePtr = tf; - mTFGPUParams = &conf; - } - ~GpuTimeFrameChunk(); - - /// Most relevant operations - void allocate(const size_t, Stream&); - void reset(const Task, Stream&); - size_t loadDataOnDevice(const size_t, const size_t, const int, Stream&); - - /// Interface - Cluster* getDeviceClusters(const int); - int* getDeviceClusterExternalIndices(const int); - int* getDeviceIndexTables(const int); - Tracklet* getDeviceTracklets(const int); - int* getDeviceTrackletsLookupTables(const int); - CellSeed* getDeviceCells(const int); - int* getDeviceCellsLookupTables(const int); - int* getDeviceRoadsLookupTables(const int); - TimeFrameGPUParameters* getTimeFrameGPUParameters() const { return mTFGPUParams; } - - int* getDeviceCUBTmpBuffer() { return mCUBTmpBufferDevice; } - int* getDeviceFoundTracklets() { return mFoundTrackletsDevice; } - int* getDeviceNFoundCells() { return mNFoundCellsDevice; } - int* getDeviceCellNeigboursLookupTables(const int); - int* getDeviceCellNeighbours(const int); - CellSeed** getDeviceArrayCells() const { return mCellsDeviceArray; } - int** getDeviceArrayNeighboursCell() const { return mNeighboursCellDeviceArray; } - int** getDeviceArrayNeighboursCellLUT() const { return mNeighboursCellLookupTablesDeviceArray; } - - /// Vertexer only - int* getDeviceNTrackletCluster(const int combid) { return mNTrackletsPerClusterDevice[combid]; } - Line* getDeviceLines() { return mLinesDevice; }; - int* getDeviceNFoundLines() { return mNFoundLinesDevice; } - int* getDeviceNExclusiveFoundLines() { return mNExclusiveFoundLinesDevice; } - unsigned char* getDeviceUsedTracklets() { return mUsedTrackletsDevice; } - int* getDeviceClusteredLines() { return mClusteredLinesDevice; } - size_t getNPopulatedRof() const { return mNPopulatedRof; } - - private: - /// Host - std::array, nLayers> mHostClusters; - std::array, nLayers> mHostIndexTables; - - /// Device - std::array mClustersDevice; - std::array mClusterExternalIndicesDevice; - std::array mIndexTablesDevice; - std::array mTrackletsDevice; - std::array mTrackletsLookupTablesDevice; - std::array mCellsDevice; - // Road* mRoadsDevice; - std::array mCellsLookupTablesDevice; - std::array mNeighboursCellDevice; - std::array mNeighboursCellLookupTablesDevice; - std::array mRoadsLookupTablesDevice; - - // These are to make them accessible using layer index - CellSeed** mCellsDeviceArray; - int** mNeighboursCellDeviceArray; - int** mNeighboursCellLookupTablesDeviceArray; - - // Small accessory buffers - int* mCUBTmpBufferDevice; - int* mFoundTrackletsDevice; - int* mNFoundCellsDevice; - - /// Vertexer only - Line* mLinesDevice; - int* mNFoundLinesDevice; - int* mNExclusiveFoundLinesDevice; - unsigned char* mUsedTrackletsDevice; - std::array mNTrackletsPerClusterDevice; - int* mClusteredLinesDevice; - - /// State and configuration - bool mAllocated = false; - size_t mNRof = 0; - size_t mNPopulatedRof = 0; - o2::its::TimeFrame* mTimeFramePtr = nullptr; - TimeFrameGPUParameters* mTFGPUParams = nullptr; -}; -} // namespace o2::its::gpu -#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h index d6d87eb8c1143..cf1295e08bd76 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h @@ -24,40 +24,44 @@ namespace o2::its::gpu { -template -class TimeFrameGPU final : public TimeFrame +template +class TimeFrameGPU final : public TimeFrame { - using typename TimeFrame::CellSeedN; - using typename TimeFrame::IndexTableUtilsN; + using typename TimeFrame::CellSeedN; + using typename TimeFrame::IndexTableUtilsN; + using typename TimeFrame::ROFOverlapTableN; + using typename TimeFrame::ROFVertexLookupTableN; + using typename TimeFrame::ROFMaskTableN; public: TimeFrameGPU() = default; - ~TimeFrameGPU() = default; + ~TimeFrameGPU() final = default; /// Most relevant operations void pushMemoryStack(const int); void popMemoryStack(const int); void registerHostMemory(const int); void unregisterHostMemory(const int); - void initialise(const int, const TrackingParameters&, const int, IndexTableUtilsN* utils = nullptr, const TimeFrameGPUParameters* pars = nullptr); - void initDevice(IndexTableUtilsN*, const TrackingParameters& trkParam, const TimeFrameGPUParameters&, const int, const int); - void initDeviceSAFitting(); + void initialise(const int, const TrackingParameters&, const int); void loadIndexTableUtils(const int); void loadTrackingFrameInfoDevice(const int, const int); void createTrackingFrameInfoDeviceArray(const int); void loadUnsortedClustersDevice(const int, const int); - void createUnsortedClustersDeviceArray(const int, const int = nLayers); + void createUnsortedClustersDeviceArray(const int, const int = NLayers); void loadClustersDevice(const int, const int); - void createClustersDeviceArray(const int, const int = nLayers); + void createClustersDeviceArray(const int, const int = NLayers); void loadClustersIndexTables(const int, const int); void createClustersIndexTablesArray(const int); void createUsedClustersDevice(const int, const int); - void createUsedClustersDeviceArray(const int, const int = nLayers); + void createUsedClustersDeviceArray(const int, const int = NLayers); void loadUsedClustersDevice(); void loadROFrameClustersDevice(const int, const int); void createROFrameClustersDeviceArray(const int); - void loadMultiplicityCutMask(const int); + void loadROFCutMask(const int); void loadVertices(const int); + void loadROFOverlapTable(const int); + void loadROFVertexLookupTable(const int); + void updateROFVertexLookupTable(const int); /// void createTrackletsLUTDevice(const int, const int); @@ -68,7 +72,6 @@ class TimeFrameGPU final : public TimeFrame void loadCellsLUTDevice(); void loadTrackSeedsDevice(); void loadTrackSeedsChi2Device(); - void loadRoadsDevice(); void loadTrackSeedsDevice(bounded_vector&); void createTrackletsBuffers(const int); void createTrackletsBuffersArray(const int); @@ -87,12 +90,6 @@ class TimeFrameGPU final : public TimeFrame void downloadCellsDevice(); void downloadCellsLUTDevice(); - /// Vertexer - void createVtxTrackletsLUTDevice(const int32_t); - void createVtxTrackletsBuffers(const int32_t); - void createVtxLinesLUTDevice(const int32_t); - void createVtxLinesBuffer(const int32_t); - /// synchronization auto& getStream(const size_t stream) { return mGpuStreams[stream]; } auto& getStreams() { return mGpuStreams; } @@ -100,7 +97,7 @@ class TimeFrameGPU final : public TimeFrame void syncStreams(const bool = true); void waitEvent(const int, const int); void recordEvent(const int); - void recordEvents(const int = 0, const int = nLayers); + void recordEvents(const int = 0, const int = NLayers); /// cleanup virtual void wipe() final; @@ -108,8 +105,10 @@ class TimeFrameGPU final : public TimeFrame /// interface virtual bool isGPU() const noexcept final { return true; } virtual const char* getName() const noexcept { return "GPU"; } - int getNClustersInRofSpan(const int, const int, const int) const; IndexTableUtilsN* getDeviceIndexTableUtils() { return mIndexTableUtilsDevice; } + const auto getDeviceROFOverlapTableView() { return mDeviceROFOverlapTableView; } + const auto getDeviceROFVertexLookupTableView() { return mDeviceROFVertexLookupTableView; } + const auto getDeviceROFMaskTableView() { return mDeviceROFMaskTableView; } int* getDeviceROFramesClusters(const int layer) { return mROFramesClustersDevice[layer]; } auto& getTrackITSExt() { return mTrackITSExt; } Vertex* getDeviceVertices() { return mPrimaryVerticesDevice; } @@ -118,12 +117,11 @@ class TimeFrameGPU final : public TimeFrame const o2::base::Propagator* getChainPropagator(); // Hybrid - Road* getDeviceRoads() { return mRoadsDevice; } TrackITSExt* getDeviceTrackITSExt() { return mTrackITSExtDevice; } int* getDeviceNeighboursLUT(const int layer) { return mNeighboursLUTDevice[layer]; } gsl::span getDeviceNeighboursLUTs() { return mNeighboursLUTDevice; } gpuPair* getDeviceNeighbourPairs(const int layer) { return mNeighbourPairsDevice[layer]; } - std::array& getDeviceNeighboursAll() { return mNeighboursDevice; } + std::array& getDeviceNeighboursAll() { return mNeighboursDevice; } int* getDeviceNeighbours(const int layer) { return mNeighboursDevice[layer]; } int** getDeviceNeighboursArray() { return mNeighboursDevice.data(); } TrackingFrameInfo* getDeviceTrackingFrameInfo(const int); @@ -145,28 +143,14 @@ class TimeFrameGPU final : public TimeFrame o2::track::TrackParCovF** getDeviceArrayTrackSeeds() { return mCellSeedsDeviceArray; } float** getDeviceArrayTrackSeedsChi2() { return mCellSeedsChi2DeviceArray; } int* getDeviceNeighboursIndexTables(const int layer) { return mNeighboursIndexTablesDevice[layer]; } - uint8_t* getDeviceMultCutMask() { return mMultMaskDevice; } - - // Vertexer - auto& getDeviceNTrackletsPerROF() const noexcept { return mNTrackletsPerROFDevice; } - auto& getDeviceNTrackletsPerCluster() const noexcept { return mNTrackletsPerClusterDevice; } - auto& getDeviceNTrackletsPerClusterSum() const noexcept { return mNTrackletsPerClusterSumDevice; } - int32_t** getDeviceArrayNTrackletsPerROF() const noexcept { return mNTrackletsPerROFDeviceArray; } - int32_t** getDeviceArrayNTrackletsPerCluster() const noexcept { return mNTrackletsPerClusterDeviceArray; } - int32_t** getDeviceArrayNTrackletsPerClusterSum() const noexcept { return mNTrackletsPerClusterSumDeviceArray; } - uint8_t* getDeviceUsedTracklets() const noexcept { return mUsedTrackletsDevice; } - int32_t* getDeviceNLinesPerCluster() const noexcept { return mNLinesPerClusterDevice; } - int32_t* getDeviceNLinesPerClusterSum() const noexcept { return mNLinesPerClusterSumDevice; } - Line* getDeviceLines() const noexcept { return mLinesDevice; } - gsl::span getDeviceTrackletsPerROFs() { return mNTrackletsPerROFDevice; } void setDevicePropagator(const o2::base::PropagatorImpl* p) final { this->mPropagatorDevice = p; } // Host-specific getters - gsl::span getNTracklets() { return mNTracklets; } - gsl::span getNCells() { return mNCells; } + gsl::span getNTracklets() { return mNTracklets; } + gsl::span getNCells() { return mNCells; } auto& getArrayNCells() { return mNCells; } - gsl::span getNNeighbours() { return mNNeighbours; } + gsl::span getNNeighbours() { return mNNeighbours; } auto& getArrayNNeighbours() { return mNNeighbours; } // Host-available device getters @@ -176,98 +160,81 @@ class TimeFrameGPU final : public TimeFrame gsl::span getDeviceCells() { return mCellsDevice; } // Overridden getters - int getNumberOfTracklets() const final; - int getNumberOfCells() const final; - int getNumberOfNeighbours() const final; + size_t getNumberOfTracklets() const final; + size_t getNumberOfCells() const final; + size_t getNumberOfNeighbours() const final; private: void allocMemAsync(void**, size_t, Stream&, bool, int32_t = o2::gpu::GPUMemoryResource::MEMORY_GPU); // Abstract owned and unowned memory allocations on specific stream void allocMem(void**, size_t, bool, int32_t = o2::gpu::GPUMemoryResource::MEMORY_GPU); // Abstract owned and unowned memory allocations on default stream - TimeFrameGPUParameters mGpuParams; // Host-available device buffer sizes - std::array mNTracklets; - std::array mNCells; - std::array mNNeighbours; + std::array mNTracklets; + std::array mNCells; + std::array mNNeighbours; // Device pointers IndexTableUtilsN* mIndexTableUtilsDevice; + // device navigation views + ROFOverlapTableN::View mDeviceROFOverlapTableView; + ROFVertexLookupTableN::View mDeviceROFVertexLookupTableView; + ROFMaskTableN::View mDeviceROFMaskTableView; // Hybrid pref - uint8_t* mMultMaskDevice; Vertex* mPrimaryVerticesDevice; int* mROFramesPVDevice; - std::array mClustersDevice; - std::array mUnsortedClustersDevice; - std::array mClustersIndexTablesDevice; - std::array mUsedClustersDevice; - std::array mROFramesClustersDevice; + std::array mClustersDevice; + std::array mUnsortedClustersDevice; + std::array mClustersIndexTablesDevice; + std::array mUsedClustersDevice; + std::array mROFramesClustersDevice; const Cluster** mClustersDeviceArray; const Cluster** mUnsortedClustersDeviceArray; const int** mClustersIndexTablesDeviceArray; uint8_t** mUsedClustersDeviceArray; const int** mROFramesClustersDeviceArray; - std::array mTrackletsDevice; - std::array mTrackletsLUTDevice; - std::array mCellsLUTDevice; - std::array mNeighboursLUTDevice; + std::array mTrackletsDevice; + std::array mTrackletsLUTDevice; + std::array mCellsLUTDevice; + std::array mNeighboursLUTDevice; Tracklet** mTrackletsDeviceArray{nullptr}; int** mCellsLUTDeviceArray{nullptr}; int** mNeighboursCellDeviceArray{nullptr}; int** mNeighboursCellLUTDeviceArray{nullptr}; int** mTrackletsLUTDeviceArray{nullptr}; - std::array mCellsDevice; + std::array mCellsDevice; CellSeedN** mCellsDeviceArray; - std::array mNeighboursIndexTablesDevice; + std::array mNeighboursIndexTablesDevice; CellSeedN* mTrackSeedsDevice{nullptr}; int* mTrackSeedsLUTDevice{nullptr}; unsigned int mNTracks{0}; - std::array mCellSeedsDevice; + std::array mCellSeedsDevice; o2::track::TrackParCovF** mCellSeedsDeviceArray; - std::array mCellSeedsChi2Device; + std::array mCellSeedsChi2Device; float** mCellSeedsChi2DeviceArray; - Road* mRoadsDevice; TrackITSExt* mTrackITSExtDevice; - std::array*, nLayers - 2> mNeighbourPairsDevice; - std::array mNeighboursDevice; - std::array mTrackingFrameInfoDevice; + std::array*, NLayers - 2> mNeighbourPairsDevice; + std::array mNeighboursDevice; + std::array mTrackingFrameInfoDevice; const TrackingFrameInfo** mTrackingFrameInfoDeviceArray; - /// Vertexer - std::array mNTrackletsPerROFDevice; - std::array mNTrackletsPerClusterDevice; - std::array mNTrackletsPerClusterSumDevice; - uint8_t* mUsedTrackletsDevice; - int32_t* mNLinesPerClusterDevice; - int32_t* mNLinesPerClusterSumDevice; - int32_t** mNTrackletsPerROFDeviceArray; - int32_t** mNTrackletsPerClusterDeviceArray; - int32_t** mNTrackletsPerClusterSumDeviceArray; - Line* mLinesDevice; - // State Streams mGpuStreams; - std::bitset mPinnedUnsortedClusters{0}; - std::bitset mPinnedClusters{0}; - std::bitset mPinnedClustersIndexTables{0}; - std::bitset mPinnedUsedClusters{0}; - std::bitset mPinnedROFramesClusters{0}; - std::bitset mPinnedTrackingFrameInfo{0}; + std::bitset mPinnedUnsortedClusters{0}; + std::bitset mPinnedClusters{0}; + std::bitset mPinnedClustersIndexTables{0}; + std::bitset mPinnedUsedClusters{0}; + std::bitset mPinnedROFramesClusters{0}; + std::bitset mPinnedTrackingFrameInfo{0}; // Temporary buffer for storing output tracks from GPU tracking bounded_vector mTrackITSExt; }; -template -inline int TimeFrameGPU::getNClustersInRofSpan(const int rofIdstart, const int rofSpanSize, const int layerId) const -{ - return static_cast(this->mROFramesClusters[layerId][(rofIdstart + rofSpanSize) < this->mROFramesClusters.size() ? rofIdstart + rofSpanSize : this->mROFramesClusters.size() - 1] - this->mROFramesClusters[layerId][rofIdstart]); -} - -template -inline std::vector TimeFrameGPU::getClusterSizes() +template +inline std::vector TimeFrameGPU::getClusterSizes() { std::vector sizes(this->mUnsortedClusters.size()); std::transform(this->mUnsortedClusters.begin(), this->mUnsortedClusters.end(), sizes.begin(), @@ -275,20 +242,20 @@ inline std::vector TimeFrameGPU::getClusterSizes() return sizes; } -template -inline int TimeFrameGPU::getNumberOfTracklets() const +template +inline size_t TimeFrameGPU::getNumberOfTracklets() const { return std::accumulate(mNTracklets.begin(), mNTracklets.end(), 0); } -template -inline int TimeFrameGPU::getNumberOfCells() const +template +inline size_t TimeFrameGPU::getNumberOfCells() const { return std::accumulate(mNCells.begin(), mNCells.end(), 0); } -template -inline int TimeFrameGPU::getNumberOfNeighbours() const +template +inline size_t TimeFrameGPU::getNumberOfNeighbours() const { return std::accumulate(mNNeighbours.begin(), mNNeighbours.end(), 0); } diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TracerGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TracerGPU.h deleted file mode 100644 index e2bd7266caff9..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TracerGPU.h +++ /dev/null @@ -1,38 +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 "ITStracking/Definitions.h" - -#ifndef TRACKINGITSGPU_INCLUDE_TRACER_H -#define TRACKINGITSGPU_INCLUDE_TRACER_H - -#if defined(__CUDACC__) && defined(__USE_GPU_TRACER__) -namespace o2 -{ -namespace its -{ -namespace gpu -{ -class Tracer -{ - public: - Tracer(const char* name, int color_id = 0); - ~Tracer(); -}; -} // namespace gpu -} // namespace its -} // namespace o2 -#define RANGE(name, cid) o2::its::gpu::Tracer tracer(name, cid); -#else -#define RANGE(name, cid) -#endif - -#endif // TRACKINGITSGPU_INCLUDE_TRACER_H \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackerTraitsGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackerTraitsGPU.h index 7d26e74692aa5..38d2a8ad5ddc2 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackerTraitsGPU.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackerTraitsGPU.h @@ -19,26 +19,23 @@ namespace o2::its { -template -class TrackerTraitsGPU final : public TrackerTraits +template +class TrackerTraitsGPU final : public TrackerTraits { - using typename TrackerTraits::IndexTableUtilsN; + using typename TrackerTraits::IndexTableUtilsN; public: TrackerTraitsGPU() = default; ~TrackerTraitsGPU() final = default; - void adoptTimeFrame(TimeFrame* tf) final; + void adoptTimeFrame(TimeFrame* tf) final; void initialiseTimeFrame(const int iteration) final; - void computeLayerTracklets(const int iteration, int, int) final; + void computeLayerTracklets(const int iteration, int) final; void computeLayerCells(const int iteration) final; void findCellsNeighbours(const int iteration) final; void findRoads(const int iteration) final; - bool supportsExtendTracks() const noexcept final { return false; } - bool supportsFindShortPrimaries() const noexcept final { return false; } - void setBz(float) final; const char* getName() const noexcept final { return "GPU"; } @@ -51,7 +48,7 @@ class TrackerTraitsGPU final : public TrackerTraits private: IndexTableUtilsN* mDeviceIndexTableUtils; - gpu::TimeFrameGPU* mTimeFrameGPU; + gpu::TimeFrameGPU* mTimeFrameGPU; }; } // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h index 53992ccf3eb85..a83d9d0d52e8f 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h @@ -16,6 +16,7 @@ #include #include "ITStracking/BoundedAllocator.h" +#include "ITStracking/ROFLookupTables.h" #include "ITStracking/Definitions.h" #include "ITStrackingGPU/Utils.h" #include "DetectorsBase/Propagator.h" @@ -33,18 +34,15 @@ class Cluster; class TrackITSExt; class ExternalAllocator; -template -void countTrackletsInROFsHandler(const IndexTableUtils* utils, - const uint8_t* multMask, +template +void countTrackletsInROFsHandler(const IndexTableUtils* utils, + const typename ROFMaskTable::View& rofMask, const int layer, - const int startROF, - const int endROF, - const int maxROF, - const int deltaROF, + const typename ROFOverlapTable::View& rofOverlaps, + const typename ROFVertexLookupTable::View& vertexLUT, const int vertexId, const Vertex* vertices, const int* rofPV, - const int nVertices, const Cluster** clusters, std::vector nClusters, const int** ROFClusters, @@ -56,8 +54,8 @@ void countTrackletsInROFsHandler(const IndexTableUtils* utils, const float NSigmaCut, bounded_vector& phiCuts, const float resolutionPV, - std::array& minR, - std::array& maxR, + std::array& minR, + std::array& maxR, bounded_vector& resolutions, std::vector& radii, bounded_vector& mulScatAng, @@ -66,18 +64,15 @@ void countTrackletsInROFsHandler(const IndexTableUtils* utils, const int nThreads, gpu::Streams& streams); -template -void computeTrackletsInROFsHandler(const IndexTableUtils* utils, - const uint8_t* multMask, +template +void computeTrackletsInROFsHandler(const IndexTableUtils* utils, + const typename ROFMaskTable::View& rofMask, const int layer, - const int startROF, - const int endROF, - const int maxROF, - const int deltaROF, + const typename ROFOverlapTable::View& rofOverlaps, + const typename ROFVertexLookupTable::View& vertexLUT, const int vertexId, const Vertex* vertices, const int* rofPV, - const int nVertices, const Cluster** clusters, std::vector nClusters, const int** ROFClusters, @@ -92,8 +87,8 @@ void computeTrackletsInROFsHandler(const IndexTableUtils* utils, const float NSigmaCut, bounded_vector& phiCuts, const float resolutionPV, - std::array& minR, - std::array& maxR, + std::array& minR, + std::array& maxR, bounded_vector& resolutions, std::vector& radii, bounded_vector& mulScatAng, @@ -102,7 +97,7 @@ void computeTrackletsInROFsHandler(const IndexTableUtils* utils, const int nThreads, gpu::Streams& streams); -template +template void countCellsHandler(const Cluster** sortedClusters, const Cluster** unsortedClusters, const TrackingFrameInfo** tfInfo, @@ -110,10 +105,9 @@ void countCellsHandler(const Cluster** sortedClusters, int** trackletsLUT, const int nTracklets, const int layer, - CellSeed* cells, + CellSeed* cells, int** cellsLUTsDeviceArray, int* cellsLUTsHost, - const int deltaROF, const float bz, const float maxChi2ClusterAttachment, const float cellDeltaTanLambdaSigma, @@ -123,7 +117,7 @@ void countCellsHandler(const Cluster** sortedClusters, const int nThreads, gpu::Streams& streams); -template +template void computeCellsHandler(const Cluster** sortedClusters, const Cluster** unsortedClusters, const TrackingFrameInfo** tfInfo, @@ -131,10 +125,9 @@ void computeCellsHandler(const Cluster** sortedClusters, int** trackletsLUT, const int nTracklets, const int layer, - CellSeed* cells, + CellSeed* cells, int** cellsLUTsDeviceArray, int* cellsLUTsHost, - const int deltaROF, const float bz, const float maxChi2ClusterAttachment, const float cellDeltaTanLambdaSigma, @@ -143,14 +136,13 @@ void computeCellsHandler(const Cluster** sortedClusters, const int nThreads, gpu::Streams& streams); -template -void countCellNeighboursHandler(CellSeed** cellsLayersDevice, +template +void countCellNeighboursHandler(CellSeed** cellsLayersDevice, int* neighboursLUTs, int** cellsLUTs, gpuPair* cellNeighbours, int* neighboursIndexTable, const Tracklet** tracklets, - const int deltaROF, const float maxChi2ClusterAttachment, const float bz, const int layerIndex, @@ -162,14 +154,13 @@ void countCellNeighboursHandler(CellSeed** cellsLayersDevice, const int nThreads, gpu::Stream& stream); -template -void computeCellNeighboursHandler(CellSeed** cellsLayersDevice, +template +void computeCellNeighboursHandler(CellSeed** cellsLayersDevice, int* neighboursLUTs, int** cellsLUTs, gpuPair* cellNeighbours, int* neighboursIndexTable, const Tracklet** tracklets, - const int deltaROF, const float maxChi2ClusterAttachment, const float bz, const int layerIndex, @@ -186,17 +177,17 @@ int filterCellNeighboursHandler(gpuPair*, gpu::Stream&, o2::its::ExternalAllocator* = nullptr); -template +template void processNeighboursHandler(const int startLayer, const int startLevel, - CellSeed** allCellSeeds, - CellSeed* currentCellSeeds, - std::array& nCells, + CellSeed** allCellSeeds, + CellSeed* currentCellSeeds, + std::array& nCells, const unsigned char** usedClusters, - std::array& neighbours, + std::array& neighbours, gsl::span neighboursDeviceLUTs, const TrackingFrameInfo** foundTrackingFrameInfo, - bounded_vector>& seedsHost, + bounded_vector>& seedsHost, const float bz, const float MaxChi2ClusterAttachment, const float maxChi2NDF, @@ -206,8 +197,8 @@ void processNeighboursHandler(const int startLayer, const int nBlocks, const int nThreads); -template -void countTrackSeedHandler(CellSeed* trackSeeds, +template +void countTrackSeedHandler(CellSeed* trackSeeds, const TrackingFrameInfo** foundTrackingFrameInfo, const Cluster** unsortedClusters, int* seedLUT, @@ -227,8 +218,8 @@ void countTrackSeedHandler(CellSeed* trackSeeds, const int nBlocks, const int nThreads); -template -void computeTrackSeedHandler(CellSeed* trackSeeds, +template +void computeTrackSeedHandler(CellSeed* trackSeeds, const TrackingFrameInfo** foundTrackingFrameInfo, const Cluster** unsortedClusters, o2::its::TrackITSExt* tracks, diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Utils.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Utils.h index ee0a203f32fda..44cd8d7e7492b 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Utils.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Utils.h @@ -38,7 +38,11 @@ #endif #ifdef ITS_GPU_LOG -#define GPULog(...) LOGP(info, __VA_ARGS__) +#define GPULog(...) \ + do { \ + LOGP(info, __VA_ARGS__); \ + GPUChkErrS(cudaDeviceSynchronize()); \ + } while (0) #else #define GPULog(...) #endif diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexerTraitsGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexerTraitsGPU.h deleted file mode 100644 index dddc247466c65..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexerTraitsGPU.h +++ /dev/null @@ -1,55 +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 VertexerTraitsGPU.h -/// \brief -/// \author matteo.concas@cern.ch - -// #define VTX_DEBUG -#ifndef ITSTRACKINGGPU_VERTEXERTRAITSGPU_H_ -#define ITSTRACKINGGPU_VERTEXERTRAITSGPU_H_ - -#include - -#include "ITStracking/VertexerTraits.h" -#include "ITStracking/Configuration.h" -#include "ITStracking/Cluster.h" -#include "ITStracking/Constants.h" -#include "ITStracking/Definitions.h" -#include "ITStracking/Tracklet.h" - -#include "ITStrackingGPU/TimeFrameGPU.h" - -namespace o2::its -{ - -template -class VertexerTraitsGPU final : public VertexerTraits -{ - public: - void initialise(const TrackingParameters&, const int iteration = 0) final; - void adoptTimeFrame(TimeFrame* tf) noexcept final; - void computeTracklets(const int iteration = 0) final; - void computeTrackletMatching(const int iteration = 0) final; - void computeVertices(const int iteration = 0) final; - void updateVertexingParameters(const std::vector&, const TimeFrameGPUParameters&) final; - - bool isGPU() const noexcept final { return true; } - const char* getName() const noexcept final { return "GPU"; } - - protected: - gpu::TimeFrameGPU* mTimeFrameGPU; - TimeFrameGPUParameters mTfGPUParams; -}; - -} // namespace o2::its - -#endif diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexingKernels.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexingKernels.h deleted file mode 100644 index 67f12bad8486c..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexingKernels.h +++ /dev/null @@ -1,115 +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 ITSTRACKINGGPU_VERTEXINGKERNELS_H_ -#define ITSTRACKINGGPU_VERTEXINGKERNELS_H_ - -#include -#include -#include -#include "ITStracking/Tracklet.h" -#include "ITStracking/Cluster.h" -#include "ITStracking/ClusterLines.h" -#include "ITStrackingGPU/Utils.h" - -namespace o2::its -{ - -/// Trackleting -template -void countTrackletsInROFsHandler(const IndexTableUtils* GPUrestrict() utils, - const uint8_t* GPUrestrict() multMask, - const int32_t nRofs, - const int32_t deltaROF, - const int32_t* GPUrestrict() rofPV, - const int32_t vertPerRofThreshold, - const Cluster** GPUrestrict() clusters, - const uint32_t nClusters, - const int32_t** GPUrestrict() ROFClusters, - const uint8_t** GPUrestrict() usedClusters, - const int32_t** GPUrestrict() clustersIndexTables, - int32_t** trackletsPerClusterLUTs, - int32_t** trackletsPerClusterSumLUTs, - int32_t** trackletsPerROF, - const std::array& trackletsPerClusterLUTsHost, - const std::array& trackletsPerClusterSumLUTsHost, - const int32_t iteration, - const float phiCut, - const int32_t maxTrackletsPerCluster, - const int32_t nBlocks, - const int32_t nThreads, - gpu::Streams& streams); - -template -void computeTrackletsInROFsHandler(const IndexTableUtils* GPUrestrict() utils, - const uint8_t* GPUrestrict() multMask, - const int32_t nRofs, - const int32_t deltaROF, - const int32_t* GPUrestrict() rofPV, - const int vertPerRofThreshold, - const Cluster** GPUrestrict() clusters, - const uint32_t nClusters, - const int32_t** GPUrestrict() ROFClusters, - const uint8_t** GPUrestrict() usedClusters, - const int32_t** GPUrestrict() clustersIndexTables, - Tracklet** GPUrestrict() foundTracklets, - const int32_t** GPUrestrict() trackletsPerClusterLUTs, - const int32_t** GPUrestrict() trackletsPerClusterSumLUTs, - const int32_t** GPUrestrict() trackletsPerROF, - const int32_t iteration, - const float phiCut, - const int32_t maxTrackletsPerCluster, - const int32_t nBlocks, - const int32_t nThreads, - gpu::Streams& streams); - -/// Selection -void countTrackletsMatchingInROFsHandler(const int32_t nRofs, - const int32_t deltaROF, - const uint32_t nClusters, - const int32_t** GPUrestrict() ROFClusters, - const Cluster** GPUrestrict() clusters, - uint8_t** GPUrestrict() usedClusters, - const Tracklet** GPUrestrict() foundTracklets, - uint8_t* GPUrestrict() usedTracklets, - const int32_t** GPUrestrict() trackletsPerClusterLUTs, - const int32_t** GPUrestrict() trackletsPerClusterSumLUTs, - int32_t* GPUrestrict() linesPerClusterLUT, - int32_t* GPUrestrict() linesPerClusterSumLUT, - const int32_t iteration, - const float phiCut, - const float tanLambdaCut, - const int32_t nBlocks, - const int32_t nThreads, - gpu::Streams& streams); - -void computeTrackletsMatchingInROFsHandler(const int32_t nRofs, - const int32_t deltaROF, - const uint32_t nClusters, - const int32_t** GPUrestrict() ROFClusters, - const Cluster** GPUrestrict() clusters, - const uint8_t** GPUrestrict() usedClusters, - const Tracklet** GPUrestrict() foundTracklets, - uint8_t* GPUrestrict() usedTracklets, - const int32_t** GPUrestrict() trackletsPerClusterLUTs, - const int32_t** GPUrestrict() trackletsPerClusterSumLUTs, - const int32_t* GPUrestrict() linesPerClusterSumLUT, - Line* GPUrestrict() lines, - const int32_t iteration, - const float phiCut, - const float tanLambdaCut, - const int32_t nBlocks, - const int32_t nThreads, - gpu::Streams& streams); - -} // namespace o2::its -#endif diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt index e38dbb1ef20e8..38f11265682ce 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt @@ -13,19 +13,13 @@ if(CUDA_ENABLED) find_package(CUDAToolkit) message(STATUS "Building ITS CUDA tracker") - # add_compile_options(-O0 -g -lineinfo -fPIC -DGPU_FORCE_DEVICE_ASSERTS=ON) - # add_compile_definitions(ITS_MEASURE_GPU_TIME) - # add_compile_definitions(ITS_GPU_LOG) o2_add_library(ITStrackingCUDA - SOURCES ClusterLinesGPU.cu - TrackerTraitsGPU.cxx + SOURCES TrackerTraitsGPU.cxx TimeFrameGPU.cu - TracerGPU.cu TrackingKernels.cu - VertexingKernels.cu - VertexerTraitsGPU.cxx PUBLIC_INCLUDE_DIRECTORIES ../ PUBLIC_LINK_LIBRARIES O2::ITStracking + O2::MathUtils O2::SimConfig O2::SimulationDataFormat O2::ReconstructionDataFormats @@ -33,7 +27,14 @@ if(CUDA_ENABLED) PRIVATE_LINK_LIBRARIES O2::GPUTrackingCUDAExternalProvider TARGETVARNAME targetName) + set_target_gpu_arch("CUDA" ${targetName}) + # Enable relocatable device code (needed for separable compilation + debugging) set_property(TARGET ${targetName} PROPERTY CUDA_SEPARABLE_COMPILATION ON) + target_compile_options(${targetName} PRIVATE + $<$:-diag-error=20014> + # $<$:-G;-O0;-Xptxas=-O0> + # $<$:-O0;-g> + ) + # target_compile_definitions(${targetName} PRIVATE ITS_MEASURE_GPU_TIME ITS_GPU_LOG) target_compile_definitions(${targetName} PRIVATE $) - set_target_gpu_arch("CUDA" ${targetName}) endif() diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/ClusterLinesGPU.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/ClusterLinesGPU.cu deleted file mode 100644 index 79f4e40dc5f10..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/ClusterLinesGPU.cu +++ /dev/null @@ -1,138 +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. -/// -/// \author matteo.concas@cern.ch - -#include -#include "ITStrackingGPU/ClusterLinesGPU.h" - -namespace o2 -{ -namespace its -{ -namespace gpu -{ - -GPUd() ClusterLinesGPU::ClusterLinesGPU(const Line& firstLine, const Line& secondLine) -{ - float covarianceFirst[3]; - float covarianceSecond[3]; - - for (int i{0}; i < 3; ++i) { - covarianceFirst[i] = 1.f; - covarianceSecond[i] = 1.f; - } - - double determinantFirst = - firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[0] * covarianceFirst[1] + - firstLine.cosinesDirector[1] * firstLine.cosinesDirector[1] * covarianceFirst[0] * covarianceFirst[2] + - firstLine.cosinesDirector[0] * firstLine.cosinesDirector[0] * covarianceFirst[1] * covarianceFirst[2]; - double determinantSecond = - secondLine.cosinesDirector[2] * secondLine.cosinesDirector[2] * covarianceSecond[0] * covarianceSecond[1] + - secondLine.cosinesDirector[1] * secondLine.cosinesDirector[1] * covarianceSecond[0] * covarianceSecond[2] + - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[0] * covarianceSecond[1] * covarianceSecond[2]; - - mAMatrix[0] = (firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[1] + - firstLine.cosinesDirector[1] * firstLine.cosinesDirector[1] * covarianceFirst[2]) / - determinantFirst + - (secondLine.cosinesDirector[2] * secondLine.cosinesDirector[2] * covarianceSecond[1] + - secondLine.cosinesDirector[1] * secondLine.cosinesDirector[1] * covarianceSecond[2]) / - determinantSecond; - - mAMatrix[1] = -firstLine.cosinesDirector[0] * firstLine.cosinesDirector[1] * covarianceFirst[2] / determinantFirst - - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[1] * covarianceSecond[2] / determinantSecond; - - mAMatrix[2] = -firstLine.cosinesDirector[0] * firstLine.cosinesDirector[2] * covarianceFirst[1] / determinantFirst - - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[2] * covarianceSecond[1] / determinantSecond; - - mAMatrix[3] = (firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[0] + - firstLine.cosinesDirector[0] * firstLine.cosinesDirector[0] * covarianceFirst[2]) / - determinantFirst + - (secondLine.cosinesDirector[2] * secondLine.cosinesDirector[2] * covarianceSecond[0] + - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[0] * covarianceSecond[2]) / - determinantSecond; - - mAMatrix[4] = -firstLine.cosinesDirector[1] * firstLine.cosinesDirector[2] * covarianceFirst[0] / determinantFirst - - secondLine.cosinesDirector[1] * secondLine.cosinesDirector[2] * covarianceSecond[0] / determinantSecond; - - mAMatrix[5] = (firstLine.cosinesDirector[1] * firstLine.cosinesDirector[1] * covarianceFirst[0] + - firstLine.cosinesDirector[0] * firstLine.cosinesDirector[0] * covarianceFirst[1]) / - determinantFirst + - (secondLine.cosinesDirector[1] * secondLine.cosinesDirector[1] * covarianceSecond[0] + - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[0] * covarianceSecond[1]) / - determinantSecond; - - mBMatrix[0] = - (firstLine.cosinesDirector[1] * covarianceFirst[2] * (-firstLine.cosinesDirector[1] * firstLine.originPoint[0] + firstLine.cosinesDirector[0] * firstLine.originPoint[1]) + - firstLine.cosinesDirector[2] * covarianceFirst[1] * (-firstLine.cosinesDirector[2] * firstLine.originPoint[0] + firstLine.cosinesDirector[0] * firstLine.originPoint[2])) / - determinantFirst; - - mBMatrix[0] += - (secondLine.cosinesDirector[1] * covarianceSecond[2] * (-secondLine.cosinesDirector[1] * secondLine.originPoint[0] + secondLine.cosinesDirector[0] * secondLine.originPoint[1]) + - secondLine.cosinesDirector[2] * covarianceSecond[1] * - (-secondLine.cosinesDirector[2] * secondLine.originPoint[0] + - secondLine.cosinesDirector[0] * secondLine.originPoint[2])) / - determinantSecond; - - mBMatrix[1] = - (firstLine.cosinesDirector[0] * covarianceFirst[2] * (-firstLine.cosinesDirector[0] * firstLine.originPoint[1] + firstLine.cosinesDirector[1] * firstLine.originPoint[0]) + - firstLine.cosinesDirector[2] * covarianceFirst[0] * (-firstLine.cosinesDirector[2] * firstLine.originPoint[1] + firstLine.cosinesDirector[1] * firstLine.originPoint[2])) / - determinantFirst; - - mBMatrix[1] += - (secondLine.cosinesDirector[0] * covarianceSecond[2] * (-secondLine.cosinesDirector[0] * secondLine.originPoint[1] + secondLine.cosinesDirector[1] * secondLine.originPoint[0]) + - secondLine.cosinesDirector[2] * covarianceSecond[0] * - (-secondLine.cosinesDirector[2] * secondLine.originPoint[1] + - secondLine.cosinesDirector[1] * secondLine.originPoint[2])) / - determinantSecond; - - mBMatrix[2] = - (firstLine.cosinesDirector[0] * covarianceFirst[1] * (-firstLine.cosinesDirector[0] * firstLine.originPoint[2] + firstLine.cosinesDirector[2] * firstLine.originPoint[0]) + - firstLine.cosinesDirector[1] * covarianceFirst[0] * (-firstLine.cosinesDirector[1] * firstLine.originPoint[2] + firstLine.cosinesDirector[2] * firstLine.originPoint[1])) / - determinantFirst; - - mBMatrix[2] += - (secondLine.cosinesDirector[0] * covarianceSecond[1] * (-secondLine.cosinesDirector[0] * secondLine.originPoint[2] + secondLine.cosinesDirector[2] * secondLine.originPoint[0]) + - secondLine.cosinesDirector[1] * covarianceSecond[0] * - (-secondLine.cosinesDirector[1] * secondLine.originPoint[2] + - secondLine.cosinesDirector[2] * secondLine.originPoint[1])) / - determinantSecond; - - computeClusterCentroid(); -} - -GPUd() void ClusterLinesGPU::computeClusterCentroid() -{ - - double determinant{mAMatrix[0] * (mAMatrix[3] * mAMatrix[5] - mAMatrix[4] * mAMatrix[4]) - - mAMatrix[1] * (mAMatrix[1] * mAMatrix[5] - mAMatrix[4] * mAMatrix[2]) + - mAMatrix[2] * (mAMatrix[1] * mAMatrix[4] - mAMatrix[2] * mAMatrix[3])}; - - if (determinant == 0) { - return; - } - - mVertex[0] = -(mBMatrix[0] * (mAMatrix[3] * mAMatrix[5] - mAMatrix[4] * mAMatrix[4]) - - mAMatrix[1] * (mBMatrix[1] * mAMatrix[5] - mAMatrix[4] * mBMatrix[2]) + - mAMatrix[2] * (mBMatrix[1] * mAMatrix[4] - mBMatrix[2] * mAMatrix[3])) / - determinant; - mVertex[1] = -(mAMatrix[0] * (mBMatrix[1] * mAMatrix[5] - mBMatrix[2] * mAMatrix[4]) - - mBMatrix[0] * (mAMatrix[1] * mAMatrix[5] - mAMatrix[4] * mAMatrix[2]) + - mAMatrix[2] * (mAMatrix[1] * mBMatrix[2] - mAMatrix[2] * mBMatrix[1])) / - determinant; - mVertex[2] = -(mAMatrix[0] * (mAMatrix[3] * mBMatrix[2] - mBMatrix[1] * mAMatrix[4]) - - mAMatrix[1] * (mAMatrix[1] * mBMatrix[2] - mBMatrix[1] * mAMatrix[2]) + - mBMatrix[0] * (mAMatrix[1] * mAMatrix[4] - mAMatrix[2] * mAMatrix[3])) / - determinant; -} -} // namespace gpu -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameChunk.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameChunk.cu deleted file mode 100644 index c8512e667aea8..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameChunk.cu +++ /dev/null @@ -1,293 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include -#include -#include - -#include "ITStracking/Constants.h" - -#include "ITStrackingGPU/Utils.h" -#include "ITStrackingGPU/TracerGPU.h" - -#include "ITStrackingGPU/TimeFrameChunk.h" - -#include -#include - -#include "GPUCommonDef.h" -#include "GPUCommonMath.h" -#include "GPUCommonLogger.h" -#include "GPUCommonHelpers.h" - -#ifndef __HIPCC__ -#define THRUST_NAMESPACE thrust::cuda -#else -#define THRUST_NAMESPACE thrust::hip -#endif - -namespace o2::its -{ -using constants::GB; -using constants::MB; -namespace gpu -{ - -template -GpuTimeFrameChunk::~GpuTimeFrameChunk() -{ - if (mAllocated) { - for (int i = 0; i < nLayers; ++i) { - GPUChkErrS(cudaFree(mClustersDevice[i])); - // GPUChkErrS(cudaFree(mTrackingFrameInfoDevice[i])); - GPUChkErrS(cudaFree(mClusterExternalIndicesDevice[i])); - GPUChkErrS(cudaFree(mIndexTablesDevice[i])); - if (i < nLayers - 1) { - GPUChkErrS(cudaFree(mTrackletsDevice[i])); - GPUChkErrS(cudaFree(mTrackletsLookupTablesDevice[i])); - if (i < nLayers - 2) { - GPUChkErrS(cudaFree(mCellsDevice[i])); - GPUChkErrS(cudaFree(mCellsLookupTablesDevice[i])); - GPUChkErrS(cudaFree(mRoadsLookupTablesDevice[i])); - if (i < nLayers - 3) { - GPUChkErrS(cudaFree(mNeighboursCellLookupTablesDevice[i])); - GPUChkErrS(cudaFree(mNeighboursCellDevice[i])); - } - } - } - } - // GPUChkErrS(cudaFree(mRoadsDevice)); - GPUChkErrS(cudaFree(mCUBTmpBufferDevice)); - GPUChkErrS(cudaFree(mFoundTrackletsDevice)); - GPUChkErrS(cudaFree(mNFoundCellsDevice)); - GPUChkErrS(cudaFree(mCellsDeviceArray)); - GPUChkErrS(cudaFree(mNeighboursCellDeviceArray)); - GPUChkErrS(cudaFree(mNeighboursCellLookupTablesDeviceArray)); - } -} - -template -void GpuTimeFrameChunk::allocate(const size_t nrof, Stream& stream) -{ - RANGE("device_partition_allocation", 2); - mNRof = nrof; - // for (int i = 0; i < nLayers; ++i) { - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mClustersDevice[i])), sizeof(Cluster) * mTFGPUParams->clustersPerROfCapacity * nrof, &stream, true); - // // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mTrackingFrameInfoDevice[i])), sizeof(TrackingFrameInfo) * mTFGPUParams->clustersPerROfCapacity * nrof, &stream, true); - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mClusterExternalIndicesDevice[i])), sizeof(int) * mTFGPUParams->clustersPerROfCapacity * nrof, &stream, true); - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mIndexTablesDevice[i])), sizeof(int) * (256 * 128 + 1) * nrof, &stream, true); - // if (i < nLayers - 1) { - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mTrackletsLookupTablesDevice[i])), sizeof(int) * mTFGPUParams->clustersPerROfCapacity * nrof, &stream, true); - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mTrackletsDevice[i])), sizeof(Tracklet) * mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * nrof, &stream, true); - // if (i < nLayers - 2) { - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mCellsLookupTablesDevice[i])), sizeof(int) * mTFGPUParams->validatedTrackletsCapacity * nrof, &stream, true); - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mCellsDevice[i])), sizeof(CellSeed) * mTFGPUParams->maxNeighboursSize * nrof, &stream, true); - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mRoadsLookupTablesDevice[i]), sizeof(int) * mTFGPUParams->maxNeighboursSize * nrof, &stream, true); - // if (i < nLayers - 3) { - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mNeighboursCellLookupTablesDevice[i])), sizeof(int) * mTFGPUParams->maxNeighboursSize * nrof, &stream, true); - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mNeighboursCellDevice[i])), sizeof(int) * mTFGPUParams->maxNeighboursSize * nrof, &stream, true); - // } - // if (i < 2) { - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mNTrackletsPerClusterDevice[i])), sizeof(int) * mTFGPUParams->clustersPerROfCapacity * nrof, &stream, true); - // } - // } - // } - // } - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mCUBTmpBufferDevice), mTFGPUParams->tmpCUBBufferSize * nrof, &stream, true); - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mLinesDevice), sizeof(Line) * mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * nrof, &stream, true); - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mNFoundLinesDevice), sizeof(int) * mTFGPUParams->clustersPerROfCapacity * nrof, &stream, true); - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mNExclusiveFoundLinesDevice), sizeof(int) * mTFGPUParams->clustersPerROfCapacity * nrof + 1, &stream, true); // + 1 for cub::DeviceScan::ExclusiveSum, to cover cases where we have maximum number of clusters per ROF - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mUsedTrackletsDevice), sizeof(unsigned char) * mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * nrof, &stream, true); - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mClusteredLinesDevice), sizeof(int) * mTFGPUParams->clustersPerROfCapacity * mTFGPUParams->maxTrackletsPerCluster * nrof, &stream, true); - // // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mRoadsDevice), sizeof(Road) * mTFGPUParams->maxRoadPerRofSize * nrof, &stream, true); - - // /// Invariant allocations - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mFoundTrackletsDevice), (nLayers - 1) * sizeof(int) * nrof, &stream, true); // No need to reset, we always read it after writing - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mNFoundCellsDevice), (nLayers - 2) * sizeof(int) * nrof, &stream, true); - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mCellsDeviceArray), (nLayers - 2) * sizeof(CellSeed*), &stream, true); - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mNeighboursCellDeviceArray), (nLayers - 3) * sizeof(int*), &stream, true); - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mNeighboursCellLookupTablesDeviceArray), (nLayers - 3) * sizeof(int*), &stream, true); - - // /// Copy pointers of allocated memory to regrouping arrays - // GPUChkErrS(cudaMemcpyAsync(mCellsDeviceArray, mCellsDevice.data(), (nLayers - 2) * sizeof(CellSeed*), cudaMemcpyHostToDevice, stream.get())); - // GPUChkErrS(cudaMemcpyAsync(mNeighboursCellDeviceArray, mNeighboursCellDevice.data(), (nLayers - 3) * sizeof(int*), cudaMemcpyHostToDevice, stream.get())); - // GPUChkErrS(cudaMemcpyAsync(mNeighboursCellLookupTablesDeviceArray, mNeighboursCellLookupTablesDevice.data(), (nLayers - 3) * sizeof(int*), cudaMemcpyHostToDevice, stream.get())); - - mAllocated = true; -} - -template -void GpuTimeFrameChunk::reset(const Task task, Stream& stream) -{ - RANGE("buffer_reset", 0); - // if ((bool)task) { // Vertexer-only initialisation (cannot be constexpr: due to the presence of gpu raw calls can't be put in header) - // for (int i = 0; i < 2; i++) { - // auto thrustTrackletsBegin = thrust::device_ptr(mTrackletsDevice[i]); - // auto thrustTrackletsEnd = thrustTrackletsBegin + mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * mNRof; - // thrust::fill(THRUST_NAMESPACE::par.on(stream.get()), thrustTrackletsBegin, thrustTrackletsEnd, Tracklet{}); - // GPUChkErrS(cudaMemsetAsync(mNTrackletsPerClusterDevice[i], 0, sizeof(int) * mTFGPUParams->clustersPerROfCapacity * mNRof, stream.get())); - // } - // GPUChkErrS(cudaMemsetAsync(mUsedTrackletsDevice, false, sizeof(unsigned char) * mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * mNRof, stream.get())); - // GPUChkErrS(cudaMemsetAsync(mClusteredLinesDevice, -1, sizeof(int) * mTFGPUParams->clustersPerROfCapacity * mTFGPUParams->maxTrackletsPerCluster * mNRof, stream.get())); - // } else { - // for (int i = 0; i < nLayers; ++i) { - // if (i < nLayers - 1) { - // GPUChkErrS(cudaMemsetAsync(mTrackletsLookupTablesDevice[i], 0, sizeof(int) * mTFGPUParams->clustersPerROfCapacity * mNRof, stream.get())); - // auto thrustTrackletsBegin = thrust::device_ptr(mTrackletsDevice[i]); - // auto thrustTrackletsEnd = thrustTrackletsBegin + mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * mNRof; - // thrust::fill(THRUST_NAMESPACE::par.on(stream.get()), thrustTrackletsBegin, thrustTrackletsEnd, Tracklet{}); - // if (i < nLayers - 2) { - // GPUChkErrS(cudaMemsetAsync(mCellsLookupTablesDevice[i], 0, sizeof(int) * mTFGPUParams->cellsLUTsize * mNRof, stream.get())); - // GPUChkErrS(cudaMemsetAsync(mRoadsLookupTablesDevice[i], 0, sizeof(int) * mTFGPUParams->maxNeighboursSize * mNRof, stream.get())); - // if (i < nLayers - 3) { - // GPUChkErrS(cudaMemsetAsync(mNeighboursCellLookupTablesDevice[i], 0, sizeof(int) * mTFGPUParams->maxNeighboursSize * mNRof, stream.get())); - // GPUChkErrS(cudaMemsetAsync(mNeighboursCellDevice[i], 0, sizeof(int) * mTFGPUParams->maxNeighboursSize * mNRof, stream.get())); - // } - // } - // } - // } - // GPUChkErrS(cudaMemsetAsync(mNFoundCellsDevice, 0, (nLayers - 2) * sizeof(int), stream.get())); - // } -} - -template -size_t GpuTimeFrameChunk::computeScalingSizeBytes(const int nrof, const TimeFrameGPUParameters& config) -{ - size_t rofsize = nLayers * sizeof(int); // number of clusters per ROF - // rofsize += nLayers * sizeof(Cluster) * config.clustersPerROfCapacity; // clusters - // rofsize += nLayers * sizeof(TrackingFrameInfo) * config.clustersPerROfCapacity; // tracking frame info - // rofsize += nLayers * sizeof(int) * config.clustersPerROfCapacity; // external cluster indices - // rofsize += nLayers * sizeof(int) * (256 * 128 + 1); // index tables - // rofsize += (nLayers - 1) * sizeof(int) * config.clustersPerROfCapacity; // tracklets lookup tables - // rofsize += (nLayers - 1) * sizeof(Tracklet) * config.maxTrackletsPerCluster * config.clustersPerROfCapacity; // tracklets - // rofsize += 2 * sizeof(int) * config.clustersPerROfCapacity; // tracklets found per cluster (vertexer) - // rofsize += sizeof(unsigned char) * config.maxTrackletsPerCluster * config.clustersPerROfCapacity; // used tracklets (vertexer) - // rofsize += (nLayers - 2) * sizeof(int) * config.validatedTrackletsCapacity; // cells lookup tables - // rofsize += (nLayers - 2) * sizeof(CellSeed) * config.maxNeighboursSize; // cells - // rofsize += (nLayers - 3) * sizeof(int) * config.maxNeighboursSize; // cell neighbours lookup tables - // rofsize += (nLayers - 3) * sizeof(int) * config.maxNeighboursSize; // cell neighbours - // rofsize += sizeof(Road) * config.maxRoadPerRofSize; // roads - // rofsize += (nLayers - 2) * sizeof(int) * config.maxNeighboursSize; // road LUT - // rofsize += sizeof(Line) * config.maxTrackletsPerCluster * config.clustersPerROfCapacity; // lines - // rofsize += sizeof(int) * config.clustersPerROfCapacity; // found lines - // rofsize += sizeof(int) * config.clustersPerROfCapacity; // found lines exclusive sum - // rofsize += sizeof(int) * config.clustersPerROfCapacity * config.maxTrackletsPerCluster; // lines used in clusterlines - - // rofsize += (nLayers - 1) * sizeof(int); // total found tracklets - // rofsize += (nLayers - 2) * sizeof(int); // total found cells - - return rofsize * nrof; -} - -template -size_t GpuTimeFrameChunk::computeFixedSizeBytes(const TimeFrameGPUParameters& config) -{ - size_t total = config.tmpCUBBufferSize; // CUB tmp buffers - total += sizeof(gpu::StaticTrackingParameters); // static parameters loaded once - return total; -} - -template -size_t GpuTimeFrameChunk::computeRofPerChunk(const TimeFrameGPUParameters& config, const size_t m) -{ - return (m * GB / (float)(config.nTimeFrameChunks) - GpuTimeFrameChunk::computeFixedSizeBytes(config)) / (float)GpuTimeFrameChunk::computeScalingSizeBytes(1, config); -} - -/// Interface -template -Cluster* GpuTimeFrameChunk::getDeviceClusters(const int layer) -{ - return mClustersDevice[layer]; -} - -template -int* GpuTimeFrameChunk::getDeviceClusterExternalIndices(const int layer) -{ - return mClusterExternalIndicesDevice[layer]; -} - -template -int* GpuTimeFrameChunk::getDeviceIndexTables(const int layer) -{ - return mIndexTablesDevice[layer]; -} - -template -Tracklet* GpuTimeFrameChunk::getDeviceTracklets(const int layer) -{ - return mTrackletsDevice[layer]; -} - -template -int* GpuTimeFrameChunk::getDeviceTrackletsLookupTables(const int layer) -{ - return mTrackletsLookupTablesDevice[layer]; -} - -template -CellSeed* GpuTimeFrameChunk::getDeviceCells(const int layer) -{ - return mCellsDevice[layer]; -} - -template -int* GpuTimeFrameChunk::getDeviceCellsLookupTables(const int layer) -{ - return mCellsLookupTablesDevice[layer]; -} - -template -int* GpuTimeFrameChunk::getDeviceCellNeigboursLookupTables(const int layer) -{ - return mNeighboursCellLookupTablesDevice[layer]; -} - -template -int* GpuTimeFrameChunk::getDeviceCellNeighbours(const int layer) -{ - return mNeighboursCellDevice[layer]; -} - -template -int* GpuTimeFrameChunk::getDeviceRoadsLookupTables(const int layer) -{ - return mRoadsLookupTablesDevice[layer]; -} - -// Load data -template -size_t GpuTimeFrameChunk::loadDataOnDevice(const size_t startRof, const size_t maxRof, const int maxLayers, Stream& stream) -{ - RANGE("load_clusters_data", 5); - // auto nRofs = std::min(maxRof - startRof, mNRof); - // mNPopulatedRof = mTimeFramePtr->getNClustersROFrange(startRof, nRofs, 0).size(); - // for (int i = 0; i < maxLayers; ++i) { - // mHostClusters[i] = mTimeFramePtr->getClustersPerROFrange(startRof, nRofs, i); - // mHostIndexTables[i] = mTimeFramePtr->getIndexTablePerROFrange(startRof, nRofs, i); - // if (mHostClusters[i].size() > mTFGPUParams->clustersPerROfCapacity * nRofs) { - // LOGP(warning, "Clusters on layer {} exceed the expected value, resizing to config value: {}, will lose information!", i, mTFGPUParams->clustersPerROfCapacity * nRofs); - // } - // GPUChkErrS(cudaMemcpyAsync(mClustersDevice[i], - // mHostClusters[i].data(), - // (int)std::min(mHostClusters[i].size(), mTFGPUParams->clustersPerROfCapacity * nRofs) * sizeof(Cluster), - // cudaMemcpyHostToDevice, stream.get())); - // if (mHostIndexTables[i].data()) { - // GPUChkErrS(cudaMemcpyAsync(mIndexTablesDevice[i], - // mHostIndexTables[i].data(), - // mHostIndexTables[i].size() * sizeof(int), - // cudaMemcpyHostToDevice, stream.get())); - // } - // } - return mNPopulatedRof; // return the number of ROFs we loaded the data for. -} -template class GpuTimeFrameChunk<7>; -} // namespace gpu -} // namespace o2::its \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu index da0cd51478945..a9b51580f9be7 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu @@ -29,8 +29,8 @@ namespace o2::its::gpu { -template -void TimeFrameGPU::allocMemAsync(void** ptr, size_t size, Stream& stream, bool extAllocator, int32_t type) +template +void TimeFrameGPU::allocMemAsync(void** ptr, size_t size, Stream& stream, bool extAllocator, int32_t type) { if (extAllocator) { *ptr = (this->mExternalAllocator)->allocate(size, type); @@ -40,8 +40,8 @@ void TimeFrameGPU::allocMemAsync(void** ptr, size_t size, Stream& strea } } -template -void TimeFrameGPU::allocMem(void** ptr, size_t size, bool extAllocator, int32_t type) +template +void TimeFrameGPU::allocMem(void** ptr, size_t size, bool extAllocator, int32_t type) { if (extAllocator) { *ptr = (this->mExternalAllocator)->allocate(size, type); @@ -51,8 +51,8 @@ void TimeFrameGPU::allocMem(void** ptr, size_t size, bool extAllocator, } } -template -void TimeFrameGPU::loadIndexTableUtils(const int iteration) +template +void TimeFrameGPU::loadIndexTableUtils(const int iteration) { GPUTimer timer("loading indextable utils"); if (!iteration) { @@ -63,16 +63,16 @@ void TimeFrameGPU::loadIndexTableUtils(const int iteration) GPUChkErrS(cudaMemcpy(mIndexTableUtilsDevice, &(this->mIndexTableUtils), sizeof(IndexTableUtilsN), cudaMemcpyHostToDevice)); } -template -void TimeFrameGPU::createUnsortedClustersDeviceArray(const int iteration, const int maxLayers) +template +void TimeFrameGPU::createUnsortedClustersDeviceArray(const int iteration, const int maxLayers) { if (!iteration) { GPUTimer timer("creating unsorted clusters array"); - allocMem(reinterpret_cast(&mUnsortedClustersDeviceArray), nLayers * sizeof(Cluster*), this->hasFrameworkAllocator()); - GPUChkErrS(cudaHostRegister(mUnsortedClustersDevice.data(), nLayers * sizeof(Cluster*), cudaHostRegisterPortable)); - mPinnedUnsortedClusters.set(nLayers); + allocMem(reinterpret_cast(&mUnsortedClustersDeviceArray), NLayers * sizeof(Cluster*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaHostRegister(mUnsortedClustersDevice.data(), NLayers * sizeof(Cluster*), cudaHostRegisterPortable)); + mPinnedUnsortedClusters.set(NLayers); if (!this->hasFrameworkAllocator()) { - for (auto iLayer{0}; iLayer < o2::gpu::CAMath::Min(maxLayers, nLayers); ++iLayer) { + for (auto iLayer{0}; iLayer < o2::gpu::CAMath::Min(maxLayers, NLayers); ++iLayer) { GPUChkErrS(cudaHostRegister(this->mUnsortedClusters[iLayer].data(), this->mUnsortedClusters[iLayer].size() * sizeof(Cluster), cudaHostRegisterPortable)); mPinnedUnsortedClusters.set(iLayer); } @@ -80,8 +80,8 @@ void TimeFrameGPU::createUnsortedClustersDeviceArray(const int iteratio } } -template -void TimeFrameGPU::loadUnsortedClustersDevice(const int iteration, const int layer) +template +void TimeFrameGPU::loadUnsortedClustersDevice(const int iteration, const int layer) { if (!iteration) { GPUTimer timer(mGpuStreams[layer], "loading unsorted clusters", layer); @@ -92,16 +92,16 @@ void TimeFrameGPU::loadUnsortedClustersDevice(const int iteration, cons } } -template -void TimeFrameGPU::createClustersDeviceArray(const int iteration, const int maxLayers) +template +void TimeFrameGPU::createClustersDeviceArray(const int iteration, const int maxLayers) { if (!iteration) { GPUTimer timer("creating sorted clusters array"); - allocMem(reinterpret_cast(&mClustersDeviceArray), nLayers * sizeof(Cluster*), this->hasFrameworkAllocator()); - GPUChkErrS(cudaHostRegister(mClustersDevice.data(), nLayers * sizeof(Cluster*), cudaHostRegisterPortable)); - mPinnedClusters.set(nLayers); + allocMem(reinterpret_cast(&mClustersDeviceArray), NLayers * sizeof(Cluster*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaHostRegister(mClustersDevice.data(), NLayers * sizeof(Cluster*), cudaHostRegisterPortable)); + mPinnedClusters.set(NLayers); if (!this->hasFrameworkAllocator()) { - for (auto iLayer{0}; iLayer < o2::gpu::CAMath::Min(maxLayers, nLayers); ++iLayer) { + for (auto iLayer{0}; iLayer < o2::gpu::CAMath::Min(maxLayers, NLayers); ++iLayer) { GPUChkErrS(cudaHostRegister(this->mClusters[iLayer].data(), this->mClusters[iLayer].size() * sizeof(Cluster), cudaHostRegisterPortable)); mPinnedClusters.set(iLayer); } @@ -109,8 +109,8 @@ void TimeFrameGPU::createClustersDeviceArray(const int iteration, const } } -template -void TimeFrameGPU::loadClustersDevice(const int iteration, const int layer) +template +void TimeFrameGPU::loadClustersDevice(const int iteration, const int layer) { if (!iteration) { GPUTimer timer(mGpuStreams[layer], "loading sorted clusters", layer); @@ -121,16 +121,16 @@ void TimeFrameGPU::loadClustersDevice(const int iteration, const int la } } -template -void TimeFrameGPU::createClustersIndexTablesArray(const int iteration) +template +void TimeFrameGPU::createClustersIndexTablesArray(const int iteration) { if (!iteration) { GPUTimer timer("creating clustersindextable array"); - allocMem(reinterpret_cast(&mClustersIndexTablesDeviceArray), nLayers * sizeof(int*), this->hasFrameworkAllocator()); - GPUChkErrS(cudaHostRegister(mClustersIndexTablesDevice.data(), nLayers * sizeof(int*), cudaHostRegisterPortable)); - mPinnedClustersIndexTables.set(nLayers); + allocMem(reinterpret_cast(&mClustersIndexTablesDeviceArray), NLayers * sizeof(int*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaHostRegister(mClustersIndexTablesDevice.data(), NLayers * sizeof(int*), cudaHostRegisterPortable)); + mPinnedClustersIndexTables.set(NLayers); if (!this->hasFrameworkAllocator()) { - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { + for (auto iLayer{0}; iLayer < NLayers; ++iLayer) { GPUChkErrS(cudaHostRegister(this->mIndexTables[iLayer].data(), this->mIndexTables[iLayer].size() * sizeof(int), cudaHostRegisterPortable)); mPinnedClustersIndexTables.set(iLayer); } @@ -138,8 +138,8 @@ void TimeFrameGPU::createClustersIndexTablesArray(const int iteration) } } -template -void TimeFrameGPU::loadClustersIndexTables(const int iteration, const int layer) +template +void TimeFrameGPU::loadClustersIndexTables(const int iteration, const int layer) { if (!iteration) { GPUTimer timer(mGpuStreams[layer], "loading sorted clusters", layer); @@ -150,16 +150,16 @@ void TimeFrameGPU::loadClustersIndexTables(const int iteration, const i } } -template -void TimeFrameGPU::createUsedClustersDeviceArray(const int iteration, const int maxLayers) +template +void TimeFrameGPU::createUsedClustersDeviceArray(const int iteration, const int maxLayers) { if (!iteration) { GPUTimer timer("creating used clusters flags"); - allocMem(reinterpret_cast(&mUsedClustersDeviceArray), nLayers * sizeof(uint8_t*), this->hasFrameworkAllocator()); - GPUChkErrS(cudaHostRegister(mUsedClustersDevice.data(), nLayers * sizeof(uint8_t*), cudaHostRegisterPortable)); - mPinnedUsedClusters.set(nLayers); + allocMem(reinterpret_cast(&mUsedClustersDeviceArray), NLayers * sizeof(uint8_t*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaHostRegister(mUsedClustersDevice.data(), NLayers * sizeof(uint8_t*), cudaHostRegisterPortable)); + mPinnedUsedClusters.set(NLayers); if (!this->hasFrameworkAllocator()) { - for (auto iLayer{0}; iLayer < o2::gpu::CAMath::Min(maxLayers, nLayers); ++iLayer) { + for (auto iLayer{0}; iLayer < o2::gpu::CAMath::Min(maxLayers, NLayers); ++iLayer) { GPUChkErrS(cudaHostRegister(this->mUsedClusters[iLayer].data(), this->mUsedClusters[iLayer].size() * sizeof(uint8_t), cudaHostRegisterPortable)); mPinnedUsedClusters.set(iLayer); } @@ -167,8 +167,8 @@ void TimeFrameGPU::createUsedClustersDeviceArray(const int iteration, c } } -template -void TimeFrameGPU::createUsedClustersDevice(const int iteration, const int layer) +template +void TimeFrameGPU::createUsedClustersDevice(const int iteration, const int layer) { if (!iteration) { GPUTimer timer(mGpuStreams[layer], "creating used clusters flags", layer); @@ -179,26 +179,26 @@ void TimeFrameGPU::createUsedClustersDevice(const int iteration, const } } -template -void TimeFrameGPU::loadUsedClustersDevice() +template +void TimeFrameGPU::loadUsedClustersDevice() { - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { + for (auto iLayer{0}; iLayer < NLayers; ++iLayer) { GPUTimer timer(mGpuStreams[iLayer], "loading used clusters flags", iLayer); GPULog("gpu-transfer: loading {} used clusters flags on layer {}, for {:.2f} MB.", this->mUsedClusters[iLayer].size(), iLayer, this->mUsedClusters[iLayer].size() * sizeof(unsigned char) / constants::MB); GPUChkErrS(cudaMemcpyAsync(mUsedClustersDevice[iLayer], this->mUsedClusters[iLayer].data(), this->mUsedClusters[iLayer].size() * sizeof(unsigned char), cudaMemcpyHostToDevice, mGpuStreams[iLayer].get())); } } -template -void TimeFrameGPU::createROFrameClustersDeviceArray(const int iteration) +template +void TimeFrameGPU::createROFrameClustersDeviceArray(const int iteration) { if (!iteration) { GPUTimer timer("creating ROFrame clusters array"); - allocMem(reinterpret_cast(&mROFramesClustersDeviceArray), nLayers * sizeof(int*), this->hasFrameworkAllocator()); - GPUChkErrS(cudaHostRegister(mROFramesClustersDevice.data(), nLayers * sizeof(int*), cudaHostRegisterPortable)); - mPinnedROFramesClusters.set(nLayers); + allocMem(reinterpret_cast(&mROFramesClustersDeviceArray), NLayers * sizeof(int*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaHostRegister(mROFramesClustersDevice.data(), NLayers * sizeof(int*), cudaHostRegisterPortable)); + mPinnedROFramesClusters.set(NLayers); if (!this->hasFrameworkAllocator()) { - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { + for (auto iLayer{0}; iLayer < NLayers; ++iLayer) { GPUChkErrS(cudaHostRegister(this->mROFramesClusters[iLayer].data(), this->mROFramesClusters[iLayer].size() * sizeof(int), cudaHostRegisterPortable)); mPinnedROFramesClusters.set(iLayer); } @@ -206,8 +206,8 @@ void TimeFrameGPU::createROFrameClustersDeviceArray(const int iteration } } -template -void TimeFrameGPU::loadROFrameClustersDevice(const int iteration, const int layer) +template +void TimeFrameGPU::loadROFrameClustersDevice(const int iteration, const int layer) { if (!iteration) { GPUTimer timer(mGpuStreams[layer], "loading ROframe clusters", layer); @@ -218,16 +218,16 @@ void TimeFrameGPU::loadROFrameClustersDevice(const int iteration, const } } -template -void TimeFrameGPU::createTrackingFrameInfoDeviceArray(const int iteration) +template +void TimeFrameGPU::createTrackingFrameInfoDeviceArray(const int iteration) { if (!iteration) { GPUTimer timer("creating trackingframeinfo array"); - allocMem(reinterpret_cast(&mTrackingFrameInfoDeviceArray), nLayers * sizeof(TrackingFrameInfo*), this->hasFrameworkAllocator()); - GPUChkErrS(cudaHostRegister(mTrackingFrameInfoDevice.data(), nLayers * sizeof(TrackingFrameInfo*), cudaHostRegisterPortable)); - mPinnedTrackingFrameInfo.set(nLayers); + allocMem(reinterpret_cast(&mTrackingFrameInfoDeviceArray), NLayers * sizeof(TrackingFrameInfo*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaHostRegister(mTrackingFrameInfoDevice.data(), NLayers * sizeof(TrackingFrameInfo*), cudaHostRegisterPortable)); + mPinnedTrackingFrameInfo.set(NLayers); if (!this->hasFrameworkAllocator()) { - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { + for (auto iLayer{0}; iLayer < NLayers; ++iLayer) { GPUChkErrS(cudaHostRegister(this->mTrackingFrameInfo[iLayer].data(), this->mTrackingFrameInfo[iLayer].size() * sizeof(TrackingFrameInfo), cudaHostRegisterPortable)); mPinnedTrackingFrameInfo.set(iLayer); } @@ -235,8 +235,8 @@ void TimeFrameGPU::createTrackingFrameInfoDeviceArray(const int iterati } } -template -void TimeFrameGPU::loadTrackingFrameInfoDevice(const int iteration, const int layer) +template +void TimeFrameGPU::loadTrackingFrameInfoDevice(const int iteration, const int layer) { if (!iteration) { GPUTimer timer(mGpuStreams[layer], "loading trackingframeinfo", layer); @@ -247,43 +247,113 @@ void TimeFrameGPU::loadTrackingFrameInfoDevice(const int iteration, con } } -template -void TimeFrameGPU::loadMultiplicityCutMask(const int iteration) +template +void TimeFrameGPU::loadROFCutMask(const int iteration) { if (!iteration || iteration == 3) { // we need to re-load the swapped mult-mask in upc iteration GPUTimer timer("loading multiplicity cut mask"); - GPULog("gpu-transfer: iteration {} loading multiplicity cut mask with {} elements, for {:.2f} MB.", iteration, this->mMultiplicityCutMask.size(), this->mMultiplicityCutMask.size() * sizeof(uint8_t) / constants::MB); - if (!iteration) { // only allocate on first call - allocMem(reinterpret_cast(&mMultMaskDevice), this->mMultiplicityCutMask.size() * sizeof(uint8_t), this->hasFrameworkAllocator()); - } - GPUChkErrS(cudaMemcpy(mMultMaskDevice, this->mMultiplicityCutMask.data(), this->mMultiplicityCutMask.size() * sizeof(uint8_t), cudaMemcpyHostToDevice)); - } -} - -template -void TimeFrameGPU::loadVertices(const int iteration) + const auto& hostTable = *(this->mROFMask); + const auto hostView = hostTable.getView(); + using TableEntry = ROFMaskTable::TableEntry; + using TableIndex = ROFMaskTable::TableIndex; + TableEntry* d_flatTable{nullptr}; + TableIndex* d_indices{nullptr}; + GPULog("gpu-transfer: iteration {} loading multiplicity cut mask with {} elements, for {:.2f} MB.", + iteration, hostTable.getFlatMaskSize(), hostTable.getFlatMaskSize() * sizeof(TableEntry) / constants::MB); + allocMem(reinterpret_cast(&d_flatTable), hostTable.getFlatMaskSize() * sizeof(TableEntry), this->hasFrameworkAllocator()); + allocMem(reinterpret_cast(&d_indices), NLayers * sizeof(uint32_t), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(d_indices, hostView.mLayerROFOffsets, NLayers * sizeof(TableIndex), cudaMemcpyHostToDevice)); + // Re-copy the flat mask on every qualifying iteration (e.g. after swapMasks() for UPC) + GPUChkErrS(cudaMemcpy(d_flatTable, hostView.mFlatMask, hostTable.getFlatMaskSize() * sizeof(TableEntry), cudaMemcpyHostToDevice)); + mDeviceROFMaskTableView = hostTable.getDeviceView(d_flatTable, d_indices); + } +} + +template +void TimeFrameGPU::loadVertices(const int iteration) { if (!iteration) { GPUTimer timer("loading seeding vertices"); - GPULog("gpu-transfer: loading {} ROframes vertices, for {:.2f} MB.", this->mROFramesPV.size(), this->mROFramesPV.size() * sizeof(int) / constants::MB); - allocMem(reinterpret_cast(&mROFramesPVDevice), this->mROFramesPV.size() * sizeof(int), this->hasFrameworkAllocator()); - GPUChkErrS(cudaMemcpy(mROFramesPVDevice, this->mROFramesPV.data(), this->mROFramesPV.size() * sizeof(int), cudaMemcpyHostToDevice)); GPULog("gpu-transfer: loading {} seeding vertices, for {:.2f} MB.", this->mPrimaryVertices.size(), this->mPrimaryVertices.size() * sizeof(Vertex) / constants::MB); allocMem(reinterpret_cast(&mPrimaryVerticesDevice), this->mPrimaryVertices.size() * sizeof(Vertex), this->hasFrameworkAllocator()); GPUChkErrS(cudaMemcpy(mPrimaryVerticesDevice, this->mPrimaryVertices.data(), this->mPrimaryVertices.size() * sizeof(Vertex), cudaMemcpyHostToDevice)); } } -template -void TimeFrameGPU::createTrackletsLUTDeviceArray(const int iteration) +template +void TimeFrameGPU::loadROFOverlapTable(const int iteration) +{ + if (!iteration) { + GPUTimer timer("initialising device view of ROFOverlapTable"); + const auto& hostTable = this->getROFOverlapTable(); + const auto& hostView = this->getROFOverlapTableView(); + using TableEntry = ROFOverlapTable::TableEntry; + using TableIndex = ROFOverlapTable::TableIndex; + using LayerTiming = o2::its::LayerTiming; + TableEntry* d_flatTable{nullptr}; + TableIndex* d_indices{nullptr}; + LayerTiming* d_layers{nullptr}; + size_t flatTableSize = hostTable.getFlatTableSize(); + allocMem(reinterpret_cast(&d_flatTable), flatTableSize * sizeof(TableEntry), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(d_flatTable, hostView.mFlatTable, flatTableSize * sizeof(TableEntry), cudaMemcpyHostToDevice)); + allocMem(reinterpret_cast(&d_indices), hostTable.getIndicesSize() * sizeof(TableIndex), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(d_indices, hostView.mIndices, hostTable.getIndicesSize() * sizeof(TableIndex), cudaMemcpyHostToDevice)); + allocMem(reinterpret_cast(&d_layers), NLayers * sizeof(LayerTiming), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(d_layers, hostView.mLayers, NLayers * sizeof(LayerTiming), cudaMemcpyHostToDevice)); + mDeviceROFOverlapTableView = hostTable.getDeviceView(d_flatTable, d_indices, d_layers); + } +} + +template +void TimeFrameGPU::loadROFVertexLookupTable(const int iteration) +{ + if (!iteration) { + GPUTimer timer("initialising device view of ROFVertexLookupTable"); + const auto& hostTable = this->getROFVertexLookupTable(); + const auto& hostView = this->getROFVertexLookupTableView(); + using TableEntry = ROFVertexLookupTable::TableEntry; + using TableIndex = ROFVertexLookupTable::TableIndex; + using LayerTiming = o2::its::LayerTiming; + TableEntry* d_flatTable{nullptr}; + TableIndex* d_indices{nullptr}; + LayerTiming* d_layers{nullptr}; + size_t flatTableSize = hostTable.getFlatTableSize(); + allocMem(reinterpret_cast(&d_flatTable), flatTableSize * sizeof(TableEntry), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(d_flatTable, hostView.mFlatTable, flatTableSize * sizeof(TableEntry), cudaMemcpyHostToDevice)); + allocMem(reinterpret_cast(&d_indices), hostTable.getIndicesSize() * sizeof(TableIndex), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(d_indices, hostView.mIndices, hostTable.getIndicesSize() * sizeof(TableIndex), cudaMemcpyHostToDevice)); + allocMem(reinterpret_cast(&d_layers), NLayers * sizeof(LayerTiming), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(d_layers, hostView.mLayers, NLayers * sizeof(LayerTiming), cudaMemcpyHostToDevice)); + mDeviceROFVertexLookupTableView = hostTable.getDeviceView(d_flatTable, d_indices, d_layers); + } +} + +template +void TimeFrameGPU::updateROFVertexLookupTable(const int iteration) +{ + const auto& hostTable = this->getROFVertexLookupTable(); + if (!iteration) { + GPUTimer timer("updating device view of ROFVertexLookupTable"); + const auto& hostView = this->getROFVertexLookupTableView(); + using TableEntry = ROFVertexLookupTable::TableEntry; + TableEntry* d_flatTable{nullptr}; + size_t flatTableSize = hostTable.getFlatTableSize(); + allocMem(reinterpret_cast(&d_flatTable), flatTableSize * sizeof(TableEntry), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(d_flatTable, hostView.mFlatTable, flatTableSize * sizeof(TableEntry), cudaMemcpyHostToDevice)); + mDeviceROFVertexLookupTableView = hostTable.getDeviceView(d_flatTable, hostView.mIndices, hostView.mLayers); + } +} + +template +void TimeFrameGPU::createTrackletsLUTDeviceArray(const int iteration) { if (!iteration) { - allocMem(reinterpret_cast(&mTrackletsLUTDeviceArray), (nLayers - 1) * sizeof(int*), this->hasFrameworkAllocator()); + allocMem(reinterpret_cast(&mTrackletsLUTDeviceArray), (NLayers - 1) * sizeof(int*), this->hasFrameworkAllocator()); } } -template -void TimeFrameGPU::createTrackletsLUTDevice(const int iteration, const int layer) +template +void TimeFrameGPU::createTrackletsLUTDevice(const int iteration, const int layer) { GPUTimer timer(mGpuStreams[layer], "creating tracklets LUTs", layer); const int ncls = this->mClusters[layer].size() + 1; @@ -295,17 +365,17 @@ void TimeFrameGPU::createTrackletsLUTDevice(const int iteration, const GPUChkErrS(cudaMemsetAsync(mTrackletsLUTDevice[layer], 0, ncls * sizeof(int), mGpuStreams[layer].get())); } -template -void TimeFrameGPU::createTrackletsBuffersArray(const int iteration) +template +void TimeFrameGPU::createTrackletsBuffersArray(const int iteration) { if (!iteration) { GPUTimer timer("creating tracklet buffers array"); - allocMem(reinterpret_cast(&mTrackletsDeviceArray), (nLayers - 1) * sizeof(Tracklet*), this->hasFrameworkAllocator()); + allocMem(reinterpret_cast(&mTrackletsDeviceArray), (NLayers - 1) * sizeof(Tracklet*), this->hasFrameworkAllocator()); } } -template -void TimeFrameGPU::createTrackletsBuffers(const int layer) +template +void TimeFrameGPU::createTrackletsBuffers(const int layer) { GPUTimer timer(mGpuStreams[layer], "creating tracklet buffers", layer); mNTracklets[layer] = 0; @@ -313,34 +383,35 @@ void TimeFrameGPU::createTrackletsBuffers(const int layer) mGpuStreams[layer].sync(); // ensure number of tracklets is correct GPULog("gpu-transfer: creating tracklets buffer for {} elements on layer {}, for {:.2f} MB.", mNTracklets[layer], layer, mNTracklets[layer] * sizeof(Tracklet) / constants::MB); allocMemAsync(reinterpret_cast(&mTrackletsDevice[layer]), mNTracklets[layer] * sizeof(Tracklet), mGpuStreams[layer], this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + GPUChkErrS(cudaMemsetAsync(mTrackletsDevice[layer], 0, mNTracklets[layer] * sizeof(Tracklet), mGpuStreams[layer].get())); GPUChkErrS(cudaMemcpyAsync(&mTrackletsDeviceArray[layer], &mTrackletsDevice[layer], sizeof(Tracklet*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); } -template -void TimeFrameGPU::loadTrackletsDevice() +template +void TimeFrameGPU::loadTrackletsDevice() { - GPUTimer timer(mGpuStreams, "loading tracklets", nLayers - 1); - for (auto iLayer{0}; iLayer < nLayers - 1; ++iLayer) { + GPUTimer timer(mGpuStreams, "loading tracklets", NLayers - 1); + for (auto iLayer{0}; iLayer < NLayers - 1; ++iLayer) { GPULog("gpu-transfer: loading {} tracklets on layer {}, for {:.2f} MB.", this->mTracklets[iLayer].size(), iLayer, this->mTracklets[iLayer].size() * sizeof(Tracklet) / constants::MB); GPUChkErrS(cudaHostRegister(this->mTracklets[iLayer].data(), this->mTracklets[iLayer].size() * sizeof(Tracklet), cudaHostRegisterPortable)); GPUChkErrS(cudaMemcpyAsync(mTrackletsDevice[iLayer], this->mTracklets[iLayer].data(), this->mTracklets[iLayer].size() * sizeof(Tracklet), cudaMemcpyHostToDevice, mGpuStreams[iLayer].get())); } } -template -void TimeFrameGPU::loadTrackletsLUTDevice() +template +void TimeFrameGPU::loadTrackletsLUTDevice() { GPUTimer timer("loading tracklets"); - for (auto iLayer{0}; iLayer < nLayers - 2; ++iLayer) { + for (auto iLayer{0}; iLayer < NLayers - 2; ++iLayer) { GPULog("gpu-transfer: loading tracklets LUT for {} elements on layer {}, for {:.2f} MB", this->mTrackletsLookupTable[iLayer].size(), iLayer + 1, this->mTrackletsLookupTable[iLayer].size() * sizeof(int) / constants::MB); GPUChkErrS(cudaMemcpyAsync(mTrackletsLUTDevice[iLayer + 1], this->mTrackletsLookupTable[iLayer].data(), this->mTrackletsLookupTable[iLayer].size() * sizeof(int), cudaMemcpyHostToDevice, mGpuStreams[iLayer].get())); } mGpuStreams.sync(); - GPUChkErrS(cudaMemcpy(mTrackletsLUTDeviceArray, mTrackletsLUTDevice.data(), (nLayers - 1) * sizeof(int*), cudaMemcpyHostToDevice)); + GPUChkErrS(cudaMemcpy(mTrackletsLUTDeviceArray, mTrackletsLUTDevice.data(), (NLayers - 1) * sizeof(int*), cudaMemcpyHostToDevice)); } -template -void TimeFrameGPU::createNeighboursIndexTablesDevice(const int layer) +template +void TimeFrameGPU::createNeighboursIndexTablesDevice(const int layer) { GPUTimer timer(mGpuStreams[layer], "creating cells neighbours", layer); GPULog("gpu-transfer: reserving neighbours LUT for {} elements on layer {}, for {:.2f} MB.", mNCells[layer] + 1, layer, (mNCells[layer] + 1) * sizeof(int) / constants::MB); @@ -348,8 +419,8 @@ void TimeFrameGPU::createNeighboursIndexTablesDevice(const int layer) GPUChkErrS(cudaMemsetAsync(mNeighboursIndexTablesDevice[layer], 0, (mNCells[layer] + 1) * sizeof(int), mGpuStreams[layer].get())); } -template -void TimeFrameGPU::createNeighboursLUTDevice(const int layer, const unsigned int nCells) +template +void TimeFrameGPU::createNeighboursLUTDevice(const int layer, const unsigned int nCells) { GPUTimer timer(mGpuStreams[layer], "reserving neighboursLUT"); GPULog("gpu-allocation: reserving neighbours LUT for {} elements on layer {} , for {:.2f} MB.", nCells + 1, layer, (nCells + 1) * sizeof(int) / constants::MB); @@ -357,11 +428,11 @@ void TimeFrameGPU::createNeighboursLUTDevice(const int layer, const uns GPUChkErrS(cudaMemsetAsync(mNeighboursLUTDevice[layer], 0, (nCells + 1) * sizeof(int), mGpuStreams[layer].get())); } -template -void TimeFrameGPU::loadCellsDevice() +template +void TimeFrameGPU::loadCellsDevice() { - GPUTimer timer(mGpuStreams, "loading cell seeds", nLayers - 2); - for (auto iLayer{0}; iLayer < nLayers - 2; ++iLayer) { + GPUTimer timer(mGpuStreams, "loading cell seeds", NLayers - 2); + for (auto iLayer{0}; iLayer < NLayers - 2; ++iLayer) { GPULog("gpu-transfer: loading {} cell seeds on layer {}, for {:.2f} MB.", this->mCells[iLayer].size(), iLayer, this->mCells[iLayer].size() * sizeof(CellSeedN) / constants::MB); allocMemAsync(reinterpret_cast(&mCellsDevice[iLayer]), this->mCells[iLayer].size() * sizeof(CellSeedN), mGpuStreams[iLayer], this->hasFrameworkAllocator()); allocMemAsync(reinterpret_cast(&mNeighboursIndexTablesDevice[iLayer]), (this->mCells[iLayer].size() + 1) * sizeof(int), mGpuStreams[iLayer], this->hasFrameworkAllocator()); // accessory for the neigh. finding. @@ -370,17 +441,17 @@ void TimeFrameGPU::loadCellsDevice() } } -template -void TimeFrameGPU::createCellsLUTDeviceArray(const int iteration) +template +void TimeFrameGPU::createCellsLUTDeviceArray(const int iteration) { if (!iteration) { GPUTimer timer("creating cells LUTs array"); - allocMem(reinterpret_cast(&mCellsLUTDeviceArray), (nLayers - 2) * sizeof(int*), this->hasFrameworkAllocator()); + allocMem(reinterpret_cast(&mCellsLUTDeviceArray), (NLayers - 2) * sizeof(int*), this->hasFrameworkAllocator()); } } -template -void TimeFrameGPU::createCellsLUTDevice(const int layer) +template +void TimeFrameGPU::createCellsLUTDevice(const int layer) { GPUTimer timer(mGpuStreams[layer], "creating cells LUTs", layer); GPULog("gpu-transfer: creating cell LUT for {} elements on layer {}, for {:.2f} MB.", mNTracklets[layer] + 1, layer, (mNTracklets[layer] + 1) * sizeof(int) / constants::MB); @@ -389,18 +460,18 @@ void TimeFrameGPU::createCellsLUTDevice(const int layer) GPUChkErrS(cudaMemcpyAsync(&mCellsLUTDeviceArray[layer], &mCellsLUTDevice[layer], sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); } -template -void TimeFrameGPU::createCellsBuffersArray(const int iteration) +template +void TimeFrameGPU::createCellsBuffersArray(const int iteration) { if (!iteration) { GPUTimer timer("creating cells buffers array"); - allocMem(reinterpret_cast(&mCellsDeviceArray), (nLayers - 2) * sizeof(CellSeedN*), this->hasFrameworkAllocator()); + allocMem(reinterpret_cast(&mCellsDeviceArray), (NLayers - 2) * sizeof(CellSeedN*), this->hasFrameworkAllocator()); GPUChkErrS(cudaMemcpy(mCellsDeviceArray, mCellsDevice.data(), mCellsDevice.size() * sizeof(CellSeedN*), cudaMemcpyHostToDevice)); } } -template -void TimeFrameGPU::createCellsBuffers(const int layer) +template +void TimeFrameGPU::createCellsBuffers(const int layer) { GPUTimer timer(mGpuStreams[layer], "creating cells buffers"); mNCells[layer] = 0; @@ -408,32 +479,23 @@ void TimeFrameGPU::createCellsBuffers(const int layer) mGpuStreams[layer].sync(); // ensure number of cells is correct GPULog("gpu-transfer: creating cell buffer for {} elements on layer {}, for {:.2f} MB.", mNCells[layer], layer, mNCells[layer] * sizeof(CellSeedN) / constants::MB); allocMemAsync(reinterpret_cast(&mCellsDevice[layer]), mNCells[layer] * sizeof(CellSeedN), mGpuStreams[layer], this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + GPUChkErrS(cudaMemsetAsync(mCellsDevice[layer], 0, mNCells[layer] * sizeof(CellSeedN), mGpuStreams[layer].get())); GPUChkErrS(cudaMemcpyAsync(&mCellsDeviceArray[layer], &mCellsDevice[layer], sizeof(CellSeedN*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); } -template -void TimeFrameGPU::loadCellsLUTDevice() +template +void TimeFrameGPU::loadCellsLUTDevice() { - GPUTimer timer(mGpuStreams, "loading cells LUTs", nLayers - 3); - for (auto iLayer{0}; iLayer < nLayers - 3; ++iLayer) { + GPUTimer timer(mGpuStreams, "loading cells LUTs", NLayers - 3); + for (auto iLayer{0}; iLayer < NLayers - 3; ++iLayer) { GPULog("gpu-transfer: loading cell LUT for {} elements on layer {}, for {:.2f} MB.", this->mCellsLookupTable[iLayer].size(), iLayer, this->mCellsLookupTable[iLayer].size() * sizeof(int) / constants::MB); GPUChkErrS(cudaHostRegister(this->mCellsLookupTable[iLayer].data(), this->mCellsLookupTable[iLayer].size() * sizeof(int), cudaHostRegisterPortable)); GPUChkErrS(cudaMemcpyAsync(mCellsLUTDevice[iLayer + 1], this->mCellsLookupTable[iLayer].data(), this->mCellsLookupTable[iLayer].size() * sizeof(int), cudaMemcpyHostToDevice, mGpuStreams[iLayer].get())); } } -template -void TimeFrameGPU::loadRoadsDevice() -{ - GPUTimer timer("loading roads device"); - GPULog("gpu-transfer: loading {} roads, for {:.2f} MB.", this->mRoads.size(), this->mRoads.size() * sizeof(Road) / constants::MB); - allocMem(reinterpret_cast(&mRoadsDevice), this->mRoads.size() * sizeof(Road), this->hasFrameworkAllocator()); - GPUChkErrS(cudaHostRegister(this->mRoads.data(), this->mRoads.size() * sizeof(Road), cudaHostRegisterPortable)); - GPUChkErrS(cudaMemcpy(mRoadsDevice, this->mRoads.data(), this->mRoads.size() * sizeof(Road), cudaMemcpyHostToDevice)); -} - -template -void TimeFrameGPU::loadTrackSeedsDevice(bounded_vector& seeds) +template +void TimeFrameGPU::loadTrackSeedsDevice(bounded_vector& seeds) { GPUTimer timer("loading track seeds"); GPULog("gpu-transfer: loading {} track seeds, for {:.2f} MB.", seeds.size(), seeds.size() * sizeof(CellSeedN) / constants::MB); @@ -444,8 +506,8 @@ void TimeFrameGPU::loadTrackSeedsDevice(bounded_vector& seed GPUChkErrS(cudaMemset(mTrackSeedsLUTDevice, 0, (seeds.size() + 1) * sizeof(int))); } -template -void TimeFrameGPU::createNeighboursDevice(const unsigned int layer) +template +void TimeFrameGPU::createNeighboursDevice(const unsigned int layer) { GPUTimer timer(mGpuStreams[layer], "reserving neighbours", layer); this->mNNeighbours[layer] = 0; @@ -458,8 +520,8 @@ void TimeFrameGPU::createNeighboursDevice(const unsigned int layer) allocMemAsync(reinterpret_cast(&mNeighboursDevice[layer]), (this->mNNeighbours[layer]) * sizeof(int), mGpuStreams[layer], this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); } -template -void TimeFrameGPU::createTrackITSExtDevice(const size_t nSeeds) +template +void TimeFrameGPU::createTrackITSExtDevice(const size_t nSeeds) { GPUTimer timer("reserving tracks"); mNTracks = 0; @@ -470,135 +532,54 @@ void TimeFrameGPU::createTrackITSExtDevice(const size_t nSeeds) GPUChkErrS(cudaMemset(mTrackITSExtDevice, 0, mNTracks * sizeof(o2::its::TrackITSExt))); } -template -void TimeFrameGPU::createVtxTrackletsLUTDevice(const int32_t iteration) -{ - GPUTimer timer("creating vertexer tracklet LUTs"); - const int32_t ncls = this->mClusters[1].size(); - for (int32_t iMode{0}; iMode < 2; ++iMode) { - if (!iteration) { - GPULog("gpu-transfer: creating vertexer tracklets per cluster for {} elements for mode {}, for {:.2f} MB.", ncls, iMode, ncls * sizeof(int32_t) / constants::MB); - allocMemAsync(reinterpret_cast(&mNTrackletsPerClusterDevice[iMode]), ncls * sizeof(int32_t), mGpuStreams[iMode], this->hasFrameworkAllocator()); - - GPULog("gpu-transfer: creating vertexer tracklets per cluster sum for {} elements for mode {}, for {:.2f} MB.", ncls + 1, iMode, (ncls + 1) * sizeof(int32_t) / constants::MB); - allocMemAsync(reinterpret_cast(&mNTrackletsPerClusterSumDevice[iMode]), (ncls + 1) * sizeof(int32_t), mGpuStreams[iMode], this->hasFrameworkAllocator()); - - GPULog("gpu-transfer: creating vertexer tracklets per ROF for {} elements for mode {}, for {:.2f} MB.", this->mNrof + 1, iMode, (this->mNrof + 1) * sizeof(int32_t) / constants::MB); - allocMemAsync(reinterpret_cast(&mNTrackletsPerROFDevice[iMode]), (this->mNrof + 1) * sizeof(int32_t), mGpuStreams[iMode], this->hasFrameworkAllocator()); - } - GPUChkErrS(cudaMemsetAsync(mNTrackletsPerClusterDevice[iMode], 0, ncls * sizeof(int32_t), mGpuStreams[iMode].get())); - GPUChkErrS(cudaMemsetAsync(mNTrackletsPerClusterSumDevice[iMode], 0, (ncls + 1) * sizeof(int32_t), mGpuStreams[iMode].get())); - GPUChkErrS(cudaMemsetAsync(mNTrackletsPerROFDevice[iMode], 0, (this->mNrof + 1) * sizeof(int32_t), mGpuStreams[iMode].get())); - } - mGpuStreams[0].sync(); - mGpuStreams[1].sync(); - if (!iteration) { - allocMem(reinterpret_cast(&mNTrackletsPerClusterDeviceArray), mNTrackletsPerClusterDevice.size() * sizeof(int32_t*), this->hasFrameworkAllocator()); - GPUChkErrS(cudaMemcpy(mNTrackletsPerClusterDeviceArray, mNTrackletsPerClusterDevice.data(), mNTrackletsPerClusterDevice.size() * sizeof(int32_t*), cudaMemcpyHostToDevice)); - - allocMem(reinterpret_cast(&mNTrackletsPerClusterSumDeviceArray), mNTrackletsPerClusterSumDevice.size() * sizeof(int32_t*), this->hasFrameworkAllocator()); - GPUChkErrS(cudaMemcpy(mNTrackletsPerClusterSumDeviceArray, mNTrackletsPerClusterSumDevice.data(), mNTrackletsPerClusterSumDevice.size() * sizeof(int32_t*), cudaMemcpyHostToDevice)); - - allocMem(reinterpret_cast(&mNTrackletsPerROFDeviceArray), mNTrackletsPerROFDevice.size() * sizeof(int32_t*), this->hasFrameworkAllocator()); - GPUChkErrS(cudaMemcpy(mNTrackletsPerROFDeviceArray, mNTrackletsPerROFDevice.data(), mNTrackletsPerROFDevice.size() * sizeof(int32_t*), cudaMemcpyHostToDevice)); - } -} - -template -void TimeFrameGPU::createVtxTrackletsBuffers(const int32_t iteration) -{ - GPUTimer timer("creating vertexer tracklet buffers"); - for (int32_t iMode{0}; iMode < 2; ++iMode) { - this->mTotalTracklets[iMode] = 0; - GPUChkErrS(cudaMemcpyAsync(&(this->mTotalTracklets[iMode]), mNTrackletsPerClusterSumDevice[iMode] + this->mClusters[1].size(), sizeof(int32_t), cudaMemcpyDeviceToHost, mGpuStreams[iMode].get())); - GPULog("gpu-transfer: creating vertexer tracklets buffer for {} elements on layer {}, for {:.2f} MB.", this->mTotalTracklets[iMode], iMode, this->mTotalTracklets[iMode] * sizeof(Tracklet) / constants::MB); - allocMemAsync(reinterpret_cast(&mTrackletsDevice[iMode]), this->mTotalTracklets[iMode] * sizeof(Tracklet), mGpuStreams[iMode], this->hasFrameworkAllocator()); - } - mGpuStreams[0].sync(); - mGpuStreams[1].sync(); - allocMem(reinterpret_cast(&mTrackletsDeviceArray), 2 * sizeof(Tracklet*), this->hasFrameworkAllocator()); - GPUChkErrS(cudaHostRegister(mTrackletsDevice.data(), 2 * sizeof(Tracklet*), cudaHostRegisterPortable)); - GPUChkErrS(cudaMemcpy(mTrackletsDeviceArray, mTrackletsDevice.data(), 2 * sizeof(Tracklet*), cudaMemcpyHostToDevice)); -} - -template -void TimeFrameGPU::createVtxLinesLUTDevice(const int32_t iteration) -{ - GPUTimer timer("creating vertexer lines LUT and used tracklets buffer"); - const int32_t ncls = this->mClusters[1].size(); - - GPULog("gpu-transfer: creating vertexer lines per cluster for {} elements , for {:.2f} MB.", ncls, ncls * sizeof(int32_t) / constants::MB); - allocMem(reinterpret_cast(&mNLinesPerClusterDevice), ncls * sizeof(int32_t), this->hasFrameworkAllocator()); - - GPULog("gpu-transfer: creating vertexer lines per cluster sum for {} elements , for {:.2f} MB.", ncls + 1, (ncls + 1) * sizeof(int32_t) / constants::MB); - allocMem(reinterpret_cast(&mNLinesPerClusterSumDevice), (ncls + 1) * sizeof(int32_t), this->hasFrameworkAllocator()); - - const int32_t ntrkls = this->mTotalTracklets[0]; - GPULog("gpu-transfer: creating vertexer used tracklets for {} elements , for {:.2f} MB.", ntrkls, ntrkls * sizeof(uint8_t) / constants::MB); - allocMem(reinterpret_cast(&mUsedTrackletsDevice), ntrkls * sizeof(uint8_t), this->hasFrameworkAllocator()); -} - -template -void TimeFrameGPU::createVtxLinesBuffer(const int32_t iteration) -{ - GPUTimer timer("creating vertexer lines buffer and resetting used tracklets"); - int32_t nlines = 0; - GPUChkErrS(cudaMemcpy(&nlines, mNLinesPerClusterDevice + this->mClusters[1].size(), sizeof(int32_t), cudaMemcpyDeviceToHost)); - this->mTotalLines = nlines; - GPULog("gpu-transfer: creating vertexer lines for {} elements , for {:.2f} MB.", nlines, nlines * sizeof(Line) / constants::MB); - allocMem(reinterpret_cast(&mLinesDevice), nlines * sizeof(Line), this->hasFrameworkAllocator()); - // reset used tracklets - GPUChkErrS(cudaMemset(mUsedTrackletsDevice, 0, this->mTotalTracklets[0] * sizeof(uint8_t))); -} - -template -void TimeFrameGPU::downloadCellsDevice() +template +void TimeFrameGPU::downloadCellsDevice() { - GPUTimer timer(mGpuStreams, "downloading cells", nLayers - 2); - for (int iLayer{0}; iLayer < nLayers - 2; ++iLayer) { + GPUTimer timer(mGpuStreams, "downloading cells", NLayers - 2); + for (int iLayer{0}; iLayer < NLayers - 2; ++iLayer) { GPULog("gpu-transfer: downloading {} cells on layer: {}, for {:.2f} MB.", mNCells[iLayer], iLayer, mNCells[iLayer] * sizeof(CellSeedN) / constants::MB); this->mCells[iLayer].resize(mNCells[iLayer]); GPUChkErrS(cudaMemcpyAsync(this->mCells[iLayer].data(), this->mCellsDevice[iLayer], mNCells[iLayer] * sizeof(CellSeedN), cudaMemcpyDeviceToHost, mGpuStreams[iLayer].get())); } } -template -void TimeFrameGPU::downloadCellsLUTDevice() +template +void TimeFrameGPU::downloadCellsLUTDevice() { - GPUTimer timer(mGpuStreams, "downloading cell luts", nLayers - 3); - for (auto iLayer{0}; iLayer < nLayers - 3; ++iLayer) { + GPUTimer timer(mGpuStreams, "downloading cell luts", NLayers - 3); + for (auto iLayer{0}; iLayer < NLayers - 3; ++iLayer) { GPULog("gpu-transfer: downloading cells lut on layer {} for {} elements", iLayer, (mNTracklets[iLayer + 1] + 1)); this->mCellsLookupTable[iLayer].resize(mNTracklets[iLayer + 1] + 1); GPUChkErrS(cudaMemcpyAsync(this->mCellsLookupTable[iLayer].data(), mCellsLUTDevice[iLayer + 1], (mNTracklets[iLayer + 1] + 1) * sizeof(int), cudaMemcpyDeviceToHost, mGpuStreams[iLayer].get())); } } -template -void TimeFrameGPU::downloadCellsNeighboursDevice(std::vector>>& neighbours, const int layer) +template +void TimeFrameGPU::downloadCellsNeighboursDevice(std::vector>>& neighbours, const int layer) { GPUTimer timer(mGpuStreams[layer], "downloading neighbours from layer", layer); GPULog("gpu-transfer: downloading {} neighbours, for {:.2f} MB.", neighbours[layer].size(), neighbours[layer].size() * sizeof(std::pair) / constants::MB); GPUChkErrS(cudaMemcpyAsync(neighbours[layer].data(), mNeighbourPairsDevice[layer], neighbours[layer].size() * sizeof(gpuPair), cudaMemcpyDeviceToHost, mGpuStreams[layer].get())); } -template -void TimeFrameGPU::downloadNeighboursLUTDevice(bounded_vector& lut, const int layer) +template +void TimeFrameGPU::downloadNeighboursLUTDevice(bounded_vector& lut, const int layer) { GPUTimer timer(mGpuStreams[layer], "downloading neighbours LUT from layer", layer); GPULog("gpu-transfer: downloading neighbours LUT for {} elements on layer {}, for {:.2f} MB.", lut.size(), layer, lut.size() * sizeof(int) / constants::MB); GPUChkErrS(cudaMemcpyAsync(lut.data(), mNeighboursLUTDevice[layer], lut.size() * sizeof(int), cudaMemcpyDeviceToHost, mGpuStreams[layer].get())); } -template -void TimeFrameGPU::downloadTrackITSExtDevice() +template +void TimeFrameGPU::downloadTrackITSExtDevice() { GPUTimer timer("downloading tracks"); GPULog("gpu-transfer: downloading {} tracks, for {:.2f} MB.", mTrackITSExt.size(), mTrackITSExt.size() * sizeof(o2::its::TrackITSExt) / constants::MB); GPUChkErrS(cudaMemcpy(mTrackITSExt.data(), mTrackITSExtDevice, mTrackITSExt.size() * sizeof(o2::its::TrackITSExt), cudaMemcpyDeviceToHost)); } -template -void TimeFrameGPU::unregisterHostMemory(const int maxLayers) +template +void TimeFrameGPU::unregisterHostMemory(const int maxLayers) { GPUTimer timer("unregistering host memory"); GPULog("unregistering host memory"); @@ -610,13 +591,13 @@ void TimeFrameGPU::unregisterHostMemory(const int maxLayers) } }; auto checkedUnregisterArray = [](auto& bits, auto& vec) { - if (bits.test(nLayers)) { + if (bits.test(NLayers)) { GPUChkErrS(cudaHostUnregister(vec.data())); - bits.reset(nLayers); + bits.reset(NLayers); } }; - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { + for (auto iLayer{0}; iLayer < NLayers; ++iLayer) { checkedUnregisterEntry(mPinnedUsedClusters, this->mUsedClusters, iLayer); checkedUnregisterEntry(mPinnedUnsortedClusters, this->mUnsortedClusters, iLayer); checkedUnregisterEntry(mPinnedClusters, this->mClusters, iLayer); @@ -650,69 +631,67 @@ constexpr auto makeIterTags(std::index_sequence) constexpr auto kIterTags = makeIterTags(std::make_index_sequence<4>{}); } // namespace detail -template -void TimeFrameGPU::pushMemoryStack(const int iteration) +template +void TimeFrameGPU::pushMemoryStack(const int iteration) { // mark the beginning of memory marked with MEMORY_STACK that can be discarded // after doing one iteration (this->mExternalAllocator)->pushTagOnStack(detail::kIterTags[iteration]); } -template -void TimeFrameGPU::popMemoryStack(const int iteration) +template +void TimeFrameGPU::popMemoryStack(const int iteration) { // pop all memory on the stack from this iteration (this->mExternalAllocator)->popTagOffStack(detail::kIterTags[iteration]); } -template -void TimeFrameGPU::initialise(const int iteration, +template +void TimeFrameGPU::initialise(const int iteration, const TrackingParameters& trkParam, - const int maxLayers, - IndexTableUtilsN* utils, - const TimeFrameGPUParameters* gpuParam) + const int maxLayers) { - mGpuStreams.resize(nLayers); - o2::its::TimeFrame::initialise(iteration, trkParam, maxLayers); + mGpuStreams.resize(NLayers); + o2::its::TimeFrame::initialise(iteration, trkParam, maxLayers, false); } -template -void TimeFrameGPU::syncStream(const size_t stream) +template +void TimeFrameGPU::syncStream(const size_t stream) { mGpuStreams[stream].sync(); } -template -void TimeFrameGPU::syncStreams(const bool device) +template +void TimeFrameGPU::syncStreams(const bool device) { mGpuStreams.sync(device); } -template -void TimeFrameGPU::waitEvent(const int stream, const int event) +template +void TimeFrameGPU::waitEvent(const int stream, const int event) { mGpuStreams.waitEvent(stream, event); } -template -void TimeFrameGPU::recordEvent(const int event) +template +void TimeFrameGPU::recordEvent(const int event) { mGpuStreams[event].record(); } -template -void TimeFrameGPU::recordEvents(const int start, const int end) +template +void TimeFrameGPU::recordEvents(const int start, const int end) { for (int i{start}; i < end; ++i) { recordEvent(i); } } -template -void TimeFrameGPU::wipe() +template +void TimeFrameGPU::wipe() { unregisterHostMemory(0); - o2::its::TimeFrame::wipe(); + o2::its::TimeFrame::wipe(); } template class TimeFrameGPU<7>; diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TracerGPU.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TracerGPU.cu deleted file mode 100644 index 7c42658242231..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TracerGPU.cu +++ /dev/null @@ -1,48 +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 "ITStrackingGPU/TracerGPU.h" - -#if !defined(__HIPCC__) && defined(__USE_GPU_TRACER__) -#include - -constexpr uint32_t colors[] = {0xff00ff00, 0xff0000ff, 0xffffff00, 0xffff00ff, 0xff00ffff, 0xffff0000, 0xffffffff}; -constexpr int num_colors = sizeof(colors) / sizeof(uint32_t); - -namespace o2 -{ -namespace its -{ -namespace gpu -{ -Tracer::Tracer(const char* name, int color_id) -{ - color_id = color_id % num_colors; - nvtxEventAttributes_t eventAttrib = {0}; - eventAttrib.version = NVTX_VERSION; - eventAttrib.size = NVTX_EVENT_ATTRIB_STRUCT_SIZE; - eventAttrib.colorType = NVTX_COLOR_ARGB; - eventAttrib.color = colors[color_id]; - eventAttrib.messageType = NVTX_MESSAGE_TYPE_ASCII; - eventAttrib.message.ascii = name; - nvtxRangePushEx(&eventAttrib); -} - -Tracer::~Tracer() -{ - nvtxRangePop(); -} - -} // namespace gpu -} // namespace its -} // namespace o2 -#endif diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx index 42d2227de60f8..3de2871dd458e 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx @@ -24,14 +24,19 @@ namespace o2::its { -template -void TrackerTraitsGPU::initialiseTimeFrame(const int iteration) +template +void TrackerTraitsGPU::initialiseTimeFrame(const int iteration) { - mTimeFrameGPU->initialise(iteration, this->mTrkParams[iteration], nLayers); + mTimeFrameGPU->initialise(iteration, this->mTrkParams[iteration], NLayers); // on default stream mTimeFrameGPU->loadVertices(iteration); + // TODO these tables can be put in persistent memory + mTimeFrameGPU->loadROFOverlapTable(iteration); // this can be put in constant memory actually + mTimeFrameGPU->loadROFVertexLookupTable(iteration); + // once the tables are in persistent memory just update the vertex one + // mTimeFrameGPU->updateROFVertexLookupTable(iteration); mTimeFrameGPU->loadIndexTableUtils(iteration); - mTimeFrameGPU->loadMultiplicityCutMask(iteration); + mTimeFrameGPU->loadROFCutMask(iteration); // pinned on host mTimeFrameGPU->createUsedClustersDeviceArray(iteration); mTimeFrameGPU->createClustersDeviceArray(iteration); @@ -48,23 +53,20 @@ void TrackerTraitsGPU::initialiseTimeFrame(const int iteration) mTimeFrameGPU->pushMemoryStack(iteration); } -template -void TrackerTraitsGPU::adoptTimeFrame(TimeFrame* tf) +template +void TrackerTraitsGPU::adoptTimeFrame(TimeFrame* tf) { - mTimeFrameGPU = static_cast*>(tf); - this->mTimeFrame = static_cast*>(tf); + mTimeFrameGPU = static_cast*>(tf); + this->mTimeFrame = static_cast*>(tf); } -template -void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int iROFslice, int iVertex) +template +void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int iVertex) { const auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); - int startROF{0}; - int endROF{mTimeFrameGPU->getNrof()}; - // start by queuing loading needed of two last layers - for (int iLayer{nLayers}; iLayer-- > nLayers - 2;) { + for (int iLayer{NLayers}; iLayer-- > NLayers - 2;) { mTimeFrameGPU->createUsedClustersDevice(iteration, iLayer); mTimeFrameGPU->loadClustersDevice(iteration, iLayer); mTimeFrameGPU->loadClustersIndexTables(iteration, iLayer); @@ -82,17 +84,14 @@ void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int i } mTimeFrameGPU->createTrackletsLUTDevice(iteration, iLayer); mTimeFrameGPU->waitEvent(iLayer, iLayer + 1); // wait stream until all data is available - countTrackletsInROFsHandler(mTimeFrameGPU->getDeviceIndexTableUtils(), - mTimeFrameGPU->getDeviceMultCutMask(), + countTrackletsInROFsHandler(mTimeFrameGPU->getDeviceIndexTableUtils(), + mTimeFrameGPU->getDeviceROFMaskTableView(), iLayer, - startROF, - endROF, - mTimeFrameGPU->getNrof(), - this->mTrkParams[iteration].DeltaROF, + mTimeFrameGPU->getDeviceROFOverlapTableView(), + mTimeFrameGPU->getDeviceROFVertexLookupTableView(), iVertex, mTimeFrameGPU->getDeviceVertices(), mTimeFrameGPU->getDeviceROFramesPV(), - mTimeFrameGPU->getPrimaryVerticesNum(), mTimeFrameGPU->getDeviceArrayClusters(), mTimeFrameGPU->getClusterSizes(), mTimeFrameGPU->getDeviceROFrameClusters(), @@ -117,17 +116,14 @@ void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int i if (mTimeFrameGPU->getNTracklets()[iLayer] == 0) { continue; } - computeTrackletsInROFsHandler(mTimeFrameGPU->getDeviceIndexTableUtils(), - mTimeFrameGPU->getDeviceMultCutMask(), + computeTrackletsInROFsHandler(mTimeFrameGPU->getDeviceIndexTableUtils(), + mTimeFrameGPU->getDeviceROFMaskTableView(), iLayer, - startROF, - endROF, - mTimeFrameGPU->getNrof(), - this->mTrkParams[iteration].DeltaROF, + mTimeFrameGPU->getDeviceROFOverlapTableView(), + mTimeFrameGPU->getDeviceROFVertexLookupTableView(), iVertex, mTimeFrameGPU->getDeviceVertices(), mTimeFrameGPU->getDeviceROFramesPV(), - mTimeFrameGPU->getPrimaryVerticesNum(), mTimeFrameGPU->getDeviceArrayClusters(), mTimeFrameGPU->getClusterSizes(), mTimeFrameGPU->getDeviceROFrameClusters(), @@ -154,13 +150,13 @@ void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int i } } -template -void TrackerTraitsGPU::computeLayerCells(const int iteration) +template +void TrackerTraitsGPU::computeLayerCells(const int iteration) { auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); // start by queuing loading needed of three last layers - for (int iLayer{nLayers}; iLayer-- > nLayers - 3;) { + for (int iLayer{NLayers}; iLayer-- > NLayers - 3;) { mTimeFrameGPU->loadUnsortedClustersDevice(iteration, iLayer); mTimeFrameGPU->loadTrackingFrameInfoDevice(iteration, iLayer); mTimeFrameGPU->recordEvent(iLayer); @@ -183,7 +179,7 @@ void TrackerTraitsGPU::computeLayerCells(const int iteration) mTimeFrameGPU->createCellsLUTDevice(iLayer); mTimeFrameGPU->waitEvent(iLayer, iLayer + 1); // wait stream until all data is available mTimeFrameGPU->waitEvent(iLayer, iLayer + 2); // wait stream until all data is available - countCellsHandler(mTimeFrameGPU->getDeviceArrayClusters(), + countCellsHandler(mTimeFrameGPU->getDeviceArrayClusters(), mTimeFrameGPU->getDeviceArrayUnsortedClusters(), mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), mTimeFrameGPU->getDeviceArrayTracklets(), @@ -193,7 +189,6 @@ void TrackerTraitsGPU::computeLayerCells(const int iteration) nullptr, mTimeFrameGPU->getDeviceArrayCellsLUT(), mTimeFrameGPU->getDeviceCellLUTs()[iLayer], - this->mTrkParams[iteration].DeltaROF, this->mBz, this->mTrkParams[iteration].MaxChi2ClusterAttachment, this->mTrkParams[iteration].CellDeltaTanLambdaSigma, @@ -206,7 +201,7 @@ void TrackerTraitsGPU::computeLayerCells(const int iteration) if (mTimeFrameGPU->getNCells()[iLayer] == 0) { continue; } - computeCellsHandler(mTimeFrameGPU->getDeviceArrayClusters(), + computeCellsHandler(mTimeFrameGPU->getDeviceArrayClusters(), mTimeFrameGPU->getDeviceArrayUnsortedClusters(), mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), mTimeFrameGPU->getDeviceArrayTracklets(), @@ -216,7 +211,6 @@ void TrackerTraitsGPU::computeLayerCells(const int iteration) mTimeFrameGPU->getDeviceCells()[iLayer], mTimeFrameGPU->getDeviceArrayCellsLUT(), mTimeFrameGPU->getDeviceCellLUTs()[iLayer], - this->mTrkParams[iteration].DeltaROF, this->mBz, this->mTrkParams[iteration].MaxChi2ClusterAttachment, this->mTrkParams[iteration].CellDeltaTanLambdaSigma, @@ -227,8 +221,8 @@ void TrackerTraitsGPU::computeLayerCells(const int iteration) } } -template -void TrackerTraitsGPU::findCellsNeighbours(const int iteration) +template +void TrackerTraitsGPU::findCellsNeighbours(const int iteration) { const auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); @@ -241,14 +235,13 @@ void TrackerTraitsGPU::findCellsNeighbours(const int iteration) } mTimeFrameGPU->createNeighboursIndexTablesDevice(iLayer); mTimeFrameGPU->createNeighboursLUTDevice(iLayer, nextLayerCellsNum); - countCellNeighboursHandler(mTimeFrameGPU->getDeviceArrayCells(), + countCellNeighboursHandler(mTimeFrameGPU->getDeviceArrayCells(), mTimeFrameGPU->getDeviceNeighboursLUT(iLayer), // LUT is initialised here. mTimeFrameGPU->getDeviceArrayCellsLUT(), mTimeFrameGPU->getDeviceNeighbourPairs(iLayer), mTimeFrameGPU->getDeviceNeighboursIndexTables(iLayer), (const Tracklet**)mTimeFrameGPU->getDeviceArrayTracklets(), - this->mTrkParams[0].DeltaROF, - this->mTrkParams[0].MaxChi2ClusterAttachment, + this->mTrkParams[iteration].MaxChi2ClusterAttachment, this->mBz, iLayer, currentLayerCellsNum, @@ -262,14 +255,13 @@ void TrackerTraitsGPU::findCellsNeighbours(const int iteration) if (mTimeFrameGPU->getNNeighbours()[iLayer] == 0) { continue; } - computeCellNeighboursHandler(mTimeFrameGPU->getDeviceArrayCells(), + computeCellNeighboursHandler(mTimeFrameGPU->getDeviceArrayCells(), mTimeFrameGPU->getDeviceNeighboursLUT(iLayer), mTimeFrameGPU->getDeviceArrayCellsLUT(), mTimeFrameGPU->getDeviceNeighbourPairs(iLayer), mTimeFrameGPU->getDeviceNeighboursIndexTables(iLayer), (const Tracklet**)mTimeFrameGPU->getDeviceArrayTracklets(), - this->mTrkParams[0].DeltaROF, - this->mTrkParams[0].MaxChi2ClusterAttachment, + this->mTrkParams[iteration].MaxChi2ClusterAttachment, this->mBz, iLayer, currentLayerCellsNum, @@ -287,18 +279,18 @@ void TrackerTraitsGPU::findCellsNeighbours(const int iteration) mTimeFrameGPU->syncStreams(false); } -template -void TrackerTraitsGPU::findRoads(const int iteration) +template +void TrackerTraitsGPU::findRoads(const int iteration) { auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); for (int startLevel{this->mTrkParams[iteration].CellsPerRoad()}; startLevel >= this->mTrkParams[iteration].CellMinimumLevel(); --startLevel) { const int minimumLayer{startLevel - 1}; - bounded_vector> trackSeeds(this->getMemoryPool().get()); + bounded_vector> trackSeeds(this->getMemoryPool().get()); for (int startLayer{this->mTrkParams[iteration].CellsPerRoad() - 1}; startLayer >= minimumLayer; --startLayer) { if ((this->mTrkParams[iteration].StartLayerMask & (1 << (startLayer + 2))) == 0) { continue; } - processNeighboursHandler(startLayer, + processNeighboursHandler(startLayer, startLevel, mTimeFrameGPU->getDeviceArrayCells(), mTimeFrameGPU->getDeviceCells()[startLayer], @@ -389,29 +381,28 @@ void TrackerTraitsGPU::findRoads(const int iteration) continue; } - std::array rofs{INT_MAX, INT_MAX, INT_MAX}; + bool firstCls{true}; + TimeEstBC ts; for (int iLayer{0}; iLayer < this->mTrkParams[0].NLayers; ++iLayer) { if (track.getClusterIndex(iLayer) == constants::UnusedIndex) { continue; } mTimeFrameGPU->markUsedCluster(iLayer, track.getClusterIndex(iLayer)); int currentROF = mTimeFrameGPU->getClusterROF(iLayer, track.getClusterIndex(iLayer)); - for (int iR{0}; iR < 3; ++iR) { - if (rofs[iR] == INT_MAX) { - rofs[iR] = currentROF; - } - if (rofs[iR] == currentROF) { - break; + auto rofTS = mTimeFrameGPU->getROFOverlapTableView().getLayer(iLayer).getROFTimeBounds(currentROF, true); + if (firstCls) { + ts = rofTS; + } else { + if (!ts.isCompatible(rofTS)) { + LOGP(fatal, "TS {}+/-{} are incompatible with {}+/-{}, this should not happen!", rofTS.getTimeStamp(), rofTS.getTimeStampError(), ts.getTimeStamp(), ts.getTimeStampError()); } + ts += rofTS; } } - if (rofs[2] != INT_MAX) { - continue; - } - if (rofs[1] != INT_MAX) { - track.setNextROFbit(); - } - mTimeFrameGPU->getTracks(std::min(rofs[0], rofs[1])).emplace_back(track); + track.getTimeStamp() = ts.makeSymmetrical(); + track.setUserField(0); + track.getParamOut().setUserField(0); + mTimeFrameGPU->getTracks().emplace_back(track); } mTimeFrameGPU->loadUsedClustersDevice(); } @@ -419,26 +410,26 @@ void TrackerTraitsGPU::findRoads(const int iteration) mTimeFrameGPU->popMemoryStack(iteration); }; -template -int TrackerTraitsGPU::getTFNumberOfClusters() const +template +int TrackerTraitsGPU::getTFNumberOfClusters() const { return mTimeFrameGPU->getNumberOfClusters(); } -template -int TrackerTraitsGPU::getTFNumberOfTracklets() const +template +int TrackerTraitsGPU::getTFNumberOfTracklets() const { return std::accumulate(mTimeFrameGPU->getNTracklets().begin(), mTimeFrameGPU->getNTracklets().end(), 0); } -template -int TrackerTraitsGPU::getTFNumberOfCells() const +template +int TrackerTraitsGPU::getTFNumberOfCells() const { return mTimeFrameGPU->getNumberOfCells(); } -template -void TrackerTraitsGPU::setBz(float bz) +template +void TrackerTraitsGPU::setBz(float bz) { this->mBz = bz; mTimeFrameGPU->setBz(bz); diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu index 525b37eb52891..795b568f6174d 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu @@ -162,8 +162,8 @@ GPUdii() o2::track::TrackParCov buildTrackSeed(const Cluster& cluster1, return {x3, tf3.alphaTrackingFrame, {y3, tf3.positionTrackingFrame[1], snp, tgl, q2pt}, {tf3.covarianceTrackingFrame[0], tf3.covarianceTrackingFrame[1], tf3.covarianceTrackingFrame[2], 0.f, 0.f, track::kCSnp2max, 0.f, 0.f, 0.f, track::kCTgl2max, 0.f, 0.f, 0.f, 0.f, sg2q2pt}}; } -template -GPUdii() TrackITSExt seedTrackForRefit(const CellSeed& seed, +template +GPUdii() TrackITSExt seedTrackForRefit(const CellSeed& seed, const TrackingFrameInfo** foundTrackingFrameInfo, const Cluster** unsortedClusters, const float* layerRadii, @@ -171,8 +171,8 @@ GPUdii() TrackITSExt seedTrackForRefit(const CellSeed& seed, const int reseedIfShorter) { TrackITSExt temporaryTrack(seed); - int lrMin = nLayers, lrMax = 0, lrMid = 0; - for (int iL{0}; iL < nLayers; ++iL) { + int lrMin = NLayers, lrMax = 0, lrMid = 0; + for (int iL{0}; iL < NLayers; ++iL) { const int idx = seed.getCluster(iL); temporaryTrack.setExternalClusterIndex(iL, idx, idx != constants::UnusedIndex); if (idx != constants::UnusedIndex) { @@ -183,9 +183,9 @@ GPUdii() TrackITSExt seedTrackForRefit(const CellSeed& seed, } const int ncl = temporaryTrack.getNClusters(); if (ncl < reseedIfShorter && ncl > 0) { // need to check if there are any clusters since we keep invalidate seeeds around - if (ncl == nLayers) { + if (ncl == NLayers) { lrMin = 0; - lrMax = nLayers - 1; + lrMax = NLayers - 1; lrMid = (lrMin + lrMax) / 2; } else { lrMid = lrMin + 1; @@ -259,13 +259,13 @@ struct is_valid_pair { } }; -template +template struct seed_selector { float maxQ2Pt; float maxChi2; GPUhd() seed_selector(float maxQ2Pt, float maxChi2) : maxQ2Pt(maxQ2Pt), maxChi2(maxChi2) {} - GPUhd() bool operator()(const CellSeed& seed) const + GPUhd() bool operator()(const CellSeed& seed) const { return !(seed.getQ2Pt() > maxQ2Pt || seed.getChi2() > maxChi2); } @@ -278,9 +278,9 @@ struct compare_track_chi2 { } }; -template +template GPUg() void __launch_bounds__(256, 1) fitTrackSeedsKernel( - CellSeed* trackSeeds, + CellSeed* trackSeeds, const TrackingFrameInfo** foundTrackingFrameInfo, const Cluster** unsortedClusters, o2::its::TrackITSExt* tracks, @@ -306,11 +306,11 @@ GPUg() void __launch_bounds__(256, 1) fitTrackSeedsKernel( } } - TrackITSExt temporaryTrack = seedTrackForRefit(trackSeeds[iCurrentTrackSeedIndex], foundTrackingFrameInfo, unsortedClusters, layerRadii, bz, reseedIfShorter); + TrackITSExt temporaryTrack = seedTrackForRefit(trackSeeds[iCurrentTrackSeedIndex], foundTrackingFrameInfo, unsortedClusters, layerRadii, bz, reseedIfShorter); o2::track::TrackPar linRef{temporaryTrack}; bool fitSuccess = fitTrack(temporaryTrack, // TrackITSExt& track, 0, // int lastLayer, - nLayers, // int firstLayer, + NLayers, // int firstLayer, 1, // int firstCluster, maxChi2ClusterAttachment, // float maxChi2ClusterAttachment, maxChi2NDF, // float maxChi2NDF, @@ -331,7 +331,7 @@ GPUg() void __launch_bounds__(256, 1) fitTrackSeedsKernel( temporaryTrack.setCov(temporaryTrack.getQ2Pt() * temporaryTrack.getQ2Pt() * temporaryTrack.getCov()[o2::track::CovLabels::kSigQ2Pt2], o2::track::CovLabels::kSigQ2Pt2); temporaryTrack.setChi2(0); fitSuccess = fitTrack(temporaryTrack, // TrackITSExt& track, - nLayers - 1, // int lastLayer, + NLayers - 1, // int lastLayer, -1, // int firstLayer, -1, // int firstCluster, maxChi2ClusterAttachment, // float maxChi2ClusterAttachment, @@ -344,7 +344,7 @@ GPUg() void __launch_bounds__(256, 1) fitTrackSeedsKernel( matCorrType, // o2::base::PropagatorF::MatCorrType matCorrType &linRef, shifRefToCluster); - if (!fitSuccess || temporaryTrack.getPt() < minPts[nLayers - temporaryTrack.getNClusters()]) { + if (!fitSuccess || temporaryTrack.getPt() < minPts[NLayers - temporaryTrack.getNClusters()]) { continue; } if (repeatRefitOut) { // repeat outward refit seeding and linearizing with the stable inward fit result @@ -356,7 +356,7 @@ GPUg() void __launch_bounds__(256, 1) fitTrackSeedsKernel( temporaryTrack.setChi2(0); fitSuccess = fitTrack(temporaryTrack, // TrackITSExt& track, 0, // int lastLayer, - nLayers, // int firstLayer, + NLayers, // int firstLayer, 1, // int firstCluster, maxChi2ClusterAttachment, // float maxChi2ClusterAttachment, maxChi2NDF, // float maxChi2NDF, @@ -384,15 +384,14 @@ GPUg() void __launch_bounds__(256, 1) fitTrackSeedsKernel( } } -template +template GPUg() void __launch_bounds__(256, 1) computeLayerCellNeighboursKernel( - CellSeed** cellSeedArray, + CellSeed** cellSeedArray, int* neighboursLUT, int* neighboursIndexTable, int** cellsLUTs, gpuPair* cellNeighbours, const Tracklet** tracklets, - const int deltaROF, const float maxChi2ClusterAttachment, const float bz, const int layerIndex, @@ -411,30 +410,18 @@ GPUg() void __launch_bounds__(256, 1) computeLayerCellNeighboursKernel( const int nextLayerLastCellIndex{cellsLUTs[layerIndex + 1][nextLayerTrackletIndex + 1]}; int foundNeighbours{0}; for (int iNextCell{nextLayerFirstCellIndex}; iNextCell < nextLayerLastCellIndex; ++iNextCell) { - auto nextCellSeed{cellSeedArray[layerIndex + 1][iNextCell]}; // Copy - if (nextCellSeed.getFirstTrackletIndex() != nextLayerTrackletIndex) { // Check if cells share the same tracklet + auto nextCellSeed{cellSeedArray[layerIndex + 1][iNextCell]}; // Copy + if (nextCellSeed.getFirstTrackletIndex() != nextLayerTrackletIndex || !currentCellSeed.getTimeStamp().isCompatible(nextCellSeed.getTimeStamp())) { break; } - if (deltaROF) { - const auto& trkl00 = tracklets[layerIndex][currentCellSeed.getFirstTrackletIndex()]; - const auto& trkl01 = tracklets[layerIndex + 1][currentCellSeed.getSecondTrackletIndex()]; - const auto& trkl10 = tracklets[layerIndex + 1][nextCellSeed.getFirstTrackletIndex()]; - const auto& trkl11 = tracklets[layerIndex + 2][nextCellSeed.getSecondTrackletIndex()]; - if ((o2::gpu::CAMath::Max(trkl00.getMaxRof(), o2::gpu::CAMath::Max(trkl01.getMaxRof(), o2::gpu::CAMath::Max(trkl10.getMaxRof(), trkl11.getMaxRof()))) - - o2::gpu::CAMath::Min(trkl00.getMinRof(), o2::gpu::CAMath::Min(trkl01.getMinRof(), o2::gpu::CAMath::Min(trkl10.getMinRof(), trkl11.getMinRof())))) > deltaROF) { - continue; - } - } - if (!nextCellSeed.rotate(currentCellSeed.getAlpha()) || !nextCellSeed.propagateTo(currentCellSeed.getX(), bz)) { continue; } float chi2 = currentCellSeed.getPredictedChi2(nextCellSeed); - if (chi2 > maxChi2ClusterAttachment) /// TODO: switch to the chi2 wrt cluster to avoid correlation - { + if (chi2 > maxChi2ClusterAttachment) { continue; } @@ -453,7 +440,7 @@ GPUg() void __launch_bounds__(256, 1) computeLayerCellNeighboursKernel( } } -template +template GPUg() void __launch_bounds__(256, 1) computeLayerCellsKernel( const Cluster** sortedClusters, const Cluster** unsortedClusters, @@ -462,9 +449,8 @@ GPUg() void __launch_bounds__(256, 1) computeLayerCellsKernel( int** trackletsLUT, const int nTrackletsCurrent, const int layer, - CellSeed* cells, + CellSeed* cells, int** cellsLUTs, - const int deltaROF, const float bz, const float maxChi2ClusterAttachment, const float cellDeltaTanLambdaSigma, @@ -490,7 +476,7 @@ GPUg() void __launch_bounds__(256, 1) computeLayerCellsKernel( break; } const Tracklet& nextTracklet = tracklets[layer + 1][iNextTrackletIndex]; - if (deltaROF && currentTracklet.getSpanRof(nextTracklet) > deltaROF) { + if (!currentTracklet.getTimeStamp().isCompatible(nextTracklet.getTimeStamp())) { continue; } const float deltaTanLambda{o2::gpu::CAMath::Abs(currentTracklet.tanLambda - nextTracklet.tanLambda)}; @@ -534,7 +520,9 @@ GPUg() void __launch_bounds__(256, 1) computeLayerCellsKernel( continue; } if constexpr (!initRun) { - new (cells + cellsLUTs[layer][iCurrentTrackletIndex] + foundCells) CellSeed{layer, clusId[0], clusId[1], clusId[2], iCurrentTrackletIndex, iNextTrackletIndex, track, chi2}; + TimeEstBC ts = currentTracklet.getTimeStamp(); + ts += nextTracklet.getTimeStamp(); + new (cells + cellsLUTs[layer][iCurrentTrackletIndex] + foundCells) CellSeed{layer, clusId[0], clusId[1], clusId[2], iCurrentTrackletIndex, iNextTrackletIndex, track, chi2, ts}; } ++foundCells; } @@ -545,24 +533,21 @@ GPUg() void __launch_bounds__(256, 1) computeLayerCellsKernel( } } -template +template GPUg() void __launch_bounds__(256, 1) computeLayerTrackletsMultiROFKernel( - const IndexTableUtils* utils, - const uint8_t* multMask, + const IndexTableUtils* utils, + const typename ROFMaskTable::View rofMask, const int layerIndex, - const int startROF, - const int endROF, - const int totalROFs, - const int deltaROF, + const typename ROFOverlapTable::View rofOverlaps, + const typename ROFVertexLookupTable::View vertexLUT, const Vertex* vertices, const int* rofPV, - const int nVertices, const int vertexId, - const Cluster** clusters, // Input data rof0 - const int** ROFClusters, // Number of clusters on layers per ROF - const unsigned char** usedClusters, // Used clusters - const int** indexTables, // Input data rof0-delta getNphiBins()}; const int zBins{utils->getNzBins()}; const int tableSize{phiBins * zBins + 1}; - for (unsigned int iROF{blockIdx.x}; iROF < endROF - startROF; iROF += gridDim.x) { - const short pivotROF = iROF + startROF; - const short minROF = o2::gpu::CAMath::Max(startROF, static_cast(pivotROF - deltaROF)); - const short maxROF = o2::gpu::CAMath::Min(endROF - 1, static_cast(pivotROF + deltaROF)); - auto primaryVertices = getPrimaryVertices(minROF, maxROF, rofPV, totalROFs, vertices); + const int totalROFs0 = rofOverlaps.getLayer(layerIndex).mNROFsTF; + const int totalROFs1 = rofOverlaps.getLayer(layerIndex + 1).mNROFsTF; + for (unsigned int pivotROF{blockIdx.x}; pivotROF < totalROFs0; pivotROF += gridDim.x) { + if (!rofMask.isROFEnabled(layerIndex, pivotROF)) { + continue; + } + + const auto& pvs = vertexLUT.getVertices(layerIndex, pivotROF); + auto primaryVertices = gpuSpan(&vertices[pvs.getFirstEntry()], pvs.getEntries()); if (primaryVertices.empty()) { continue; } const auto startVtx{vertexId >= 0 ? vertexId : 0}; const auto endVtx{vertexId >= 0 ? o2::gpu::CAMath::Min(vertexId + 1, static_cast(primaryVertices.size())) : static_cast(primaryVertices.size())}; - if ((endVtx - startVtx) <= 0) { + if (endVtx <= startVtx || (vertexId + 1) > primaryVertices.size()) { + continue; + } + + const auto& rofOverlap = rofOverlaps.getOverlap(layerIndex, layerIndex + 1, pivotROF); + if (!rofOverlap.getEntries()) { continue; } - auto clustersCurrentLayer = getClustersOnLayer(pivotROF, totalROFs, layerIndex, ROFClusters, clusters); + auto clustersCurrentLayer = getClustersOnLayer(pivotROF, totalROFs0, layerIndex, ROFClusters, clusters); if (clustersCurrentLayer.empty()) { continue; } @@ -613,6 +607,9 @@ GPUg() void __launch_bounds__(256, 1) computeLayerTrackletsMultiROFKernel( const float inverseR0{1.f / currentCluster.radius}; for (int iV{startVtx}; iV < endVtx; ++iV) { auto& primaryVertex{primaryVertices[iV]}; + if (!vertexLUT.isVertexCompatible(layerIndex, pivotROF, primaryVertex)) { + continue; + } if ((primaryVertex.isFlagSet(Vertex::Flags::UPCMode) && iteration != 3) || (iteration == 3 && !primaryVertex.isFlagSet(Vertex::Flags::UPCMode))) { continue; } @@ -623,7 +620,7 @@ GPUg() void __launch_bounds__(256, 1) computeLayerTrackletsMultiROFKernel( const float zAtRmax{tanLambda * (maxR - currentCluster.radius) + currentCluster.zCoordinate}; const float sqInverseDeltaZ0{1.f / (math_utils::Sq(currentCluster.zCoordinate - primaryVertex.getZ()) + constants::Tolerance)}; /// protecting from overflows adding the detector resolution const float sigmaZ{o2::gpu::CAMath::Sqrt(math_utils::Sq(resolution) * math_utils::Sq(tanLambda) * ((math_utils::Sq(inverseR0) + sqInverseDeltaZ0) * math_utils::Sq(meanDeltaR) + 1.f) + math_utils::Sq(meanDeltaR * MSAngle))}; - const int4 selectedBinsRect{getBinsRect(currentCluster, layerIndex + 1, utils, zAtRmin, zAtRmax, sigmaZ * NSigmaCut, phiCut)}; + const int4 selectedBinsRect{getBinsRect(currentCluster, layerIndex + 1, utils, zAtRmin, zAtRmax, sigmaZ * NSigmaCut, phiCut)}; if (selectedBinsRect.x == 0 && selectedBinsRect.y == 0 && selectedBinsRect.z == 0 && selectedBinsRect.w == 0) { continue; } @@ -633,11 +630,18 @@ GPUg() void __launch_bounds__(256, 1) computeLayerTrackletsMultiROFKernel( phiBinsNum += phiBins; } - for (short targetROF{minROF}; targetROF <= maxROF; ++targetROF) { - auto clustersNextLayer = getClustersOnLayer(targetROF, totalROFs, layerIndex + 1, ROFClusters, clusters); + for (short targetROF = rofOverlap.getFirstEntry(); targetROF < rofOverlap.getEntriesBound(); ++targetROF) { + if (!rofMask.isROFEnabled(layerIndex + 1, pivotROF)) { + continue; + } + auto clustersNextLayer = getClustersOnLayer(targetROF, totalROFs1, layerIndex + 1, ROFClusters, clusters); if (clustersNextLayer.empty()) { continue; } + const auto ts = rofOverlaps.getTimeStamp(layerIndex, pivotROF, layerIndex + 1, targetROF); + if (!ts.isCompatible(primaryVertex.getTimeStamp())) { + continue; + } for (int iPhiCount{0}; iPhiCount < phiBinsNum; iPhiCount++) { int iPhiBin = (selectedBinsRect.y + iPhiCount) % phiBins; const int firstBinIndex{utils->getBinIndex(selectedBinsRect.x, iPhiBin)}; @@ -661,7 +665,7 @@ GPUg() void __launch_bounds__(256, 1) computeLayerTrackletsMultiROFKernel( const float phi{o2::gpu::CAMath::ATan2(currentCluster.yCoordinate - nextCluster.yCoordinate, currentCluster.xCoordinate - nextCluster.xCoordinate)}; const float tanL{(currentCluster.zCoordinate - nextCluster.zCoordinate) / (currentCluster.radius - nextCluster.radius)}; const int nextSortedIndex{ROFClusters[layerIndex + 1][targetROF] + nextClusterIndex}; - new (tracklets[layerIndex] + trackletsLUT[layerIndex][currentSortedIndex] + storedTracklets) Tracklet{currentSortedIndex, nextSortedIndex, tanL, phi, pivotROF, targetROF}; + new (tracklets[layerIndex] + trackletsLUT[layerIndex][currentSortedIndex] + storedTracklets) Tracklet{currentSortedIndex, nextSortedIndex, tanL, phi, ts}; } ++storedTracklets; } @@ -683,15 +687,15 @@ GPUg() void __launch_bounds__(256, 1) compileTrackletsLookupTableKernel( } } -template +template GPUg() void __launch_bounds__(256, 1) processNeighboursKernel( const int layer, const int level, - CellSeed** allCellSeeds, - CellSeed* currentCellSeeds, + CellSeed** allCellSeeds, + CellSeed* currentCellSeeds, const int* currentCellIds, const unsigned int nCurrentCells, - CellSeed* updatedCellSeeds, + CellSeed* updatedCellSeeds, int* updatedCellsIds, int* foundSeedsTable, // auxiliary only in GPU code to compute the number of cells per iteration const unsigned char** usedClusters, // Used clusters @@ -732,12 +736,15 @@ GPUg() void __launch_bounds__(256, 1) processNeighboursKernel( if (neighbourCell.getSecondTrackletIndex() != currentCell.getFirstTrackletIndex()) { continue; } - if (usedClusters[layer - 1][neighbourCell.getFirstClusterIndex()]) { + if (!currentCell.getTimeStamp().isCompatible(neighbourCell.getTimeStamp())) { continue; } if (currentCell.getLevel() - 1 != neighbourCell.getLevel()) { continue; } + if (usedClusters[layer - 1][neighbourCell.getFirstClusterIndex()]) { + continue; + } auto seed{currentCell}; auto& trHit = foundTrackingFrameInfo[layer - 1][neighbourCell.getFirstClusterIndex()]; @@ -780,18 +787,15 @@ GPUg() void __launch_bounds__(256, 1) processNeighboursKernel( } // namespace gpu -template -void countTrackletsInROFsHandler(const IndexTableUtils* utils, - const uint8_t* multMask, +template +void countTrackletsInROFsHandler(const IndexTableUtils* utils, + const typename ROFMaskTable::View& rofMask, const int layer, - const int startROF, - const int endROF, - const int maxROF, - const int deltaROF, + const typename ROFOverlapTable::View& rofOverlaps, + const typename ROFVertexLookupTable::View& vertexLUT, const int vertexId, const Vertex* vertices, const int* rofPV, - const int nVertices, const Cluster** clusters, std::vector nClusters, const int** ROFClusters, @@ -803,8 +807,8 @@ void countTrackletsInROFsHandler(const IndexTableUtils* utils, const float NSigmaCut, bounded_vector& phiCuts, const float resolutionPV, - std::array& minRs, - std::array& maxRs, + std::array& minRs, + std::array& maxRs, bounded_vector& resolutions, std::vector& radii, bounded_vector& mulScatAng, @@ -815,15 +819,12 @@ void countTrackletsInROFsHandler(const IndexTableUtils* utils, { gpu::computeLayerTrackletsMultiROFKernel<<>>( utils, - multMask, + rofMask, layer, - startROF, - endROF, - maxROF, - deltaROF, + rofOverlaps, + vertexLUT, vertices, rofPV, - nVertices, vertexId, clusters, ROFClusters, @@ -844,18 +845,15 @@ void countTrackletsInROFsHandler(const IndexTableUtils* utils, thrust::exclusive_scan(nosync_policy, trackletsLUTsHost[layer], trackletsLUTsHost[layer] + nClusters[layer] + 1, trackletsLUTsHost[layer]); } -template -void computeTrackletsInROFsHandler(const IndexTableUtils* utils, - const uint8_t* multMask, +template +void computeTrackletsInROFsHandler(const IndexTableUtils* utils, + const typename ROFMaskTable::View& rofMask, const int layer, - const int startROF, - const int endROF, - const int maxROF, - const int deltaROF, + const typename ROFOverlapTable::View& rofOverlaps, + const typename ROFVertexLookupTable::View& vertexLUT, const int vertexId, const Vertex* vertices, const int* rofPV, - const int nVertices, const Cluster** clusters, std::vector nClusters, const int** ROFClusters, @@ -870,8 +868,8 @@ void computeTrackletsInROFsHandler(const IndexTableUtils* utils, const float NSigmaCut, bounded_vector& phiCuts, const float resolutionPV, - std::array& minRs, - std::array& maxRs, + std::array& minRs, + std::array& maxRs, bounded_vector& resolutions, std::vector& radii, bounded_vector& mulScatAng, @@ -882,15 +880,12 @@ void computeTrackletsInROFsHandler(const IndexTableUtils* utils, { gpu::computeLayerTrackletsMultiROFKernel<<>>( utils, - multMask, + rofMask, layer, - startROF, - endROF, - maxROF, - deltaROF, + rofOverlaps, + vertexLUT, vertices, rofPV, - nVertices, vertexId, clusters, ROFClusters, @@ -922,7 +917,7 @@ void computeTrackletsInROFsHandler(const IndexTableUtils* utils, } } -template +template void countCellsHandler( const Cluster** sortedClusters, const Cluster** unsortedClusters, @@ -931,10 +926,9 @@ void countCellsHandler( int** trackletsLUT, const int nTracklets, const int layer, - CellSeed* cells, + CellSeed* cells, int** cellsLUTsArrayDevice, int* cellsLUTsHost, - const int deltaROF, const float bz, const float maxChi2ClusterAttachment, const float cellDeltaTanLambdaSigma, @@ -954,7 +948,6 @@ void countCellsHandler( layer, // const int cells, // CellSeed* cellsLUTsArrayDevice, // int** - deltaROF, // const int bz, // const float maxChi2ClusterAttachment, // const float cellDeltaTanLambdaSigma, // const float @@ -963,7 +956,7 @@ void countCellsHandler( thrust::exclusive_scan(nosync_policy, cellsLUTsHost, cellsLUTsHost + nTracklets + 1, cellsLUTsHost); } -template +template void computeCellsHandler( const Cluster** sortedClusters, const Cluster** unsortedClusters, @@ -972,10 +965,9 @@ void computeCellsHandler( int** trackletsLUT, const int nTracklets, const int layer, - CellSeed* cells, + CellSeed* cells, int** cellsLUTsArrayDevice, int* cellsLUTsHost, - const int deltaROF, const float bz, const float maxChi2ClusterAttachment, const float cellDeltaTanLambdaSigma, @@ -994,21 +986,19 @@ void computeCellsHandler( layer, // const int cells, // CellSeed* cellsLUTsArrayDevice, // int** - deltaROF, // const int bz, // const float maxChi2ClusterAttachment, // const float cellDeltaTanLambdaSigma, // const float nSigmaCut); // const float } -template -void countCellNeighboursHandler(CellSeed** cellsLayersDevice, +template +void countCellNeighboursHandler(CellSeed** cellsLayersDevice, int* neighboursLUT, int** cellsLUTs, gpuPair* cellNeighbours, int* neighboursIndexTable, const Tracklet** tracklets, - const int deltaROF, const float maxChi2ClusterAttachment, const float bz, const int layerIndex, @@ -1027,7 +1017,6 @@ void countCellNeighboursHandler(CellSeed** cellsLayersDevice, cellsLUTs, cellNeighbours, tracklets, - deltaROF, maxChi2ClusterAttachment, bz, layerIndex, @@ -1038,14 +1027,13 @@ void countCellNeighboursHandler(CellSeed** cellsLayersDevice, thrust::exclusive_scan(nosync_policy, neighboursIndexTable, neighboursIndexTable + nCells + 1, neighboursIndexTable); } -template -void computeCellNeighboursHandler(CellSeed** cellsLayersDevice, +template +void computeCellNeighboursHandler(CellSeed** cellsLayersDevice, int* neighboursLUT, int** cellsLUTs, gpuPair* cellNeighbours, int* neighboursIndexTable, const Tracklet** tracklets, - const int deltaROF, const float maxChi2ClusterAttachment, const float bz, const int layerIndex, @@ -1063,7 +1051,6 @@ void computeCellNeighboursHandler(CellSeed** cellsLayersDevice, cellsLUTs, cellNeighbours, tracklets, - deltaROF, maxChi2ClusterAttachment, bz, layerIndex, @@ -1087,17 +1074,17 @@ int filterCellNeighboursHandler(gpuPair* cellNeighbourPairs, return newSize; } -template +template void processNeighboursHandler(const int startLayer, const int startLevel, - CellSeed** allCellSeeds, - CellSeed* currentCellSeeds, - std::array& nCells, + CellSeed** allCellSeeds, + CellSeed* currentCellSeeds, + std::array& nCells, const unsigned char** usedClusters, - std::array& neighbours, + std::array& neighbours, gsl::span neighboursDeviceLUTs, const TrackingFrameInfo** foundTrackingFrameInfo, - bounded_vector>& seedsHost, + bounded_vector>& seedsHost, const float bz, const float maxChi2ClusterAttachment, const float maxChi2NDF, @@ -1110,11 +1097,11 @@ void processNeighboursHandler(const int startLayer, constexpr uint64_t Tag = qStr2Tag("ITS_PNH1"); alloc->pushTagOnStack(Tag); auto allocInt = gpu::TypedAllocator(alloc); - auto allocCellSeed = gpu::TypedAllocator>(alloc); + auto allocCellSeed = gpu::TypedAllocator>(alloc); thrust::device_vector> foundSeedsTable(nCells[startLayer] + 1, 0, allocInt); auto nosync_policy = THRUST_NAMESPACE::par_nosync(gpu::TypedAllocator(alloc)).on(gpu::Stream::DefaultStream); - gpu::processNeighboursKernel<<>>( + gpu::processNeighboursKernel<<>>( startLayer, startLevel, allCellSeeds, @@ -1135,8 +1122,8 @@ void processNeighboursHandler(const int startLayer, thrust::exclusive_scan(nosync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), foundSeedsTable.begin()); thrust::device_vector> updatedCellId(foundSeedsTable.back(), 0, allocInt); - thrust::device_vector, gpu::TypedAllocator>> updatedCellSeed(foundSeedsTable.back(), allocCellSeed); - gpu::processNeighboursKernel<<>>( + thrust::device_vector, gpu::TypedAllocator>> updatedCellSeed(foundSeedsTable.back(), allocCellSeed); + gpu::processNeighboursKernel<<>>( startLayer, startLevel, allCellSeeds, @@ -1158,17 +1145,17 @@ void processNeighboursHandler(const int startLayer, int level = startLevel; thrust::device_vector> lastCellId(allocInt); - thrust::device_vector, gpu::TypedAllocator>> lastCellSeed(allocCellSeed); + thrust::device_vector, gpu::TypedAllocator>> lastCellSeed(allocCellSeed); for (int iLayer{startLayer - 1}; iLayer > 0 && level > 2; --iLayer) { lastCellSeed.swap(updatedCellSeed); lastCellId.swap(updatedCellId); - thrust::device_vector, gpu::TypedAllocator>>(allocCellSeed).swap(updatedCellSeed); + thrust::device_vector, gpu::TypedAllocator>>(allocCellSeed).swap(updatedCellSeed); thrust::device_vector>(allocInt).swap(updatedCellId); auto lastCellSeedSize{lastCellSeed.size()}; foundSeedsTable.resize(lastCellSeedSize + 1); thrust::fill(nosync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), 0); - gpu::processNeighboursKernel<<>>( + gpu::processNeighboursKernel<<>>( iLayer, --level, allCellSeeds, @@ -1192,9 +1179,9 @@ void processNeighboursHandler(const int startLayer, updatedCellId.resize(foundSeeds); thrust::fill(nosync_policy, updatedCellId.begin(), updatedCellId.end(), 0); updatedCellSeed.resize(foundSeeds); - thrust::fill(nosync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), CellSeed()); + thrust::fill(nosync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), CellSeed()); - gpu::processNeighboursKernel<<>>( + gpu::processNeighboursKernel<<>>( iLayer, level, allCellSeeds, @@ -1214,16 +1201,16 @@ void processNeighboursHandler(const int startLayer, matCorrType); } GPUChkErrS(cudaStreamSynchronize(gpu::Stream::DefaultStream)); - thrust::device_vector, gpu::TypedAllocator>> outSeeds(updatedCellSeed.size(), allocCellSeed); - auto end = thrust::copy_if(nosync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), outSeeds.begin(), gpu::seed_selector(1.e3, maxChi2NDF * ((startLevel + 2) * 2 - 5))); + thrust::device_vector, gpu::TypedAllocator>> outSeeds(updatedCellSeed.size(), allocCellSeed); + auto end = thrust::copy_if(nosync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), outSeeds.begin(), gpu::seed_selector(1.e3, maxChi2NDF * ((startLevel + 2) * 2 - 5))); auto s{end - outSeeds.begin()}; seedsHost.reserve(seedsHost.size() + s); thrust::copy(outSeeds.begin(), outSeeds.begin() + s, std::back_inserter(seedsHost)); alloc->popTagOffStack(Tag); } -template -void countTrackSeedHandler(CellSeed* trackSeeds, +template +void countTrackSeedHandler(CellSeed* trackSeeds, const TrackingFrameInfo** foundTrackingFrameInfo, const Cluster** unsortedClusters, int* seedLUT, @@ -1248,7 +1235,7 @@ void countTrackSeedHandler(CellSeed* trackSeeds, // small transferes! thrust::device_vector minPts(minPtsHost); thrust::device_vector layerRadii(layerRadiiHost); - gpu::fitTrackSeedsKernel<<>>( + gpu::fitTrackSeedsKernel<<>>( trackSeeds, // CellSeed* foundTrackingFrameInfo, // TrackingFrameInfo** unsortedClusters, // Cluster** @@ -1270,8 +1257,8 @@ void countTrackSeedHandler(CellSeed* trackSeeds, thrust::exclusive_scan(sync_policy, seedLUT, seedLUT + nSeeds + 1, seedLUT); } -template -void computeTrackSeedHandler(CellSeed* trackSeeds, +template +void computeTrackSeedHandler(CellSeed* trackSeeds, const TrackingFrameInfo** foundTrackingFrameInfo, const Cluster** unsortedClusters, o2::its::TrackITSExt* tracks, @@ -1295,7 +1282,7 @@ void computeTrackSeedHandler(CellSeed* trackSeeds, { thrust::device_vector minPts(minPtsHost); thrust::device_vector layerRadii(layerRadiiHost); - gpu::fitTrackSeedsKernel<<>>( + gpu::fitTrackSeedsKernel<<>>( trackSeeds, // CellSeed* foundTrackingFrameInfo, // TrackingFrameInfo** unsortedClusters, // Cluster** @@ -1320,16 +1307,13 @@ void computeTrackSeedHandler(CellSeed* trackSeeds, /// Explicit instantiation of ITS2 handlers template void countTrackletsInROFsHandler<7>(const IndexTableUtils<7>* utils, - const uint8_t* multMask, + const ROFMaskTable<7>::View& rofMask, const int layer, - const int startROF, - const int endROF, - const int maxROF, - const int deltaROF, + const ROFOverlapTable<7>::View& rofOverlaps, + const ROFVertexLookupTable<7>::View& vertexLUT, const int vertexId, const Vertex* vertices, const int* rofPV, - const int nVertices, const Cluster** clusters, std::vector nClusters, const int** ROFClusters, @@ -1352,16 +1336,13 @@ template void countTrackletsInROFsHandler<7>(const IndexTableUtils<7>* utils, gpu::Streams& streams); template void computeTrackletsInROFsHandler<7>(const IndexTableUtils<7>* utils, - const uint8_t* multMask, + const ROFMaskTable<7>::View& rofMask, const int layer, - const int startROF, - const int endROF, - const int maxROF, - const int deltaROF, + const ROFOverlapTable<7>::View& rofOverlaps, + const ROFVertexLookupTable<7>::View& vertexLUT, const int vertexId, const Vertex* vertices, const int* rofPV, - const int nVertices, const Cluster** clusters, std::vector nClusters, const int** ROFClusters, @@ -1396,7 +1377,6 @@ template void countCellsHandler<7>(const Cluster** sortedClusters, CellSeed<7>* cells, int** cellsLUTsArrayDevice, int* cellsLUTsHost, - const int deltaROF, const float bz, const float maxChi2ClusterAttachment, const float cellDeltaTanLambdaSigma, @@ -1416,7 +1396,6 @@ template void computeCellsHandler<7>(const Cluster** sortedClusters, CellSeed<7>* cells, int** cellsLUTsArrayDevice, int* cellsLUTsHost, - const int deltaROF, const float bz, const float maxChi2ClusterAttachment, const float cellDeltaTanLambdaSigma, @@ -1431,7 +1410,6 @@ template void countCellNeighboursHandler<7>(CellSeed<7>** cellsLayersDevice, gpuPair* cellNeighbours, int* neighboursIndexTable, const Tracklet** tracklets, - const int deltaROF, const float maxChi2ClusterAttachment, const float bz, const int layerIndex, @@ -1449,7 +1427,6 @@ template void computeCellNeighboursHandler(CellSeed<7>** cellsLayersDevice, gpuPair* cellNeighbours, int* neighboursIndexTable, const Tracklet** tracklets, - const int deltaROF, const float maxChi2ClusterAttachment, const float bz, const int layerIndex, diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexerTraitsGPU.cxx b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexerTraitsGPU.cxx deleted file mode 100644 index 658d3cf0dfb91..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexerTraitsGPU.cxx +++ /dev/null @@ -1,179 +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. -// -/// \author matteo.concas@cern.ch - -#include - -#include "ITStracking/TrackingConfigParam.h" -#include "ITStrackingGPU/VertexingKernels.h" -#include "ITStrackingGPU/VertexerTraitsGPU.h" - -namespace o2::its -{ - -template -void VertexerTraitsGPU::initialise(const TrackingParameters& trackingParams, const int iteration) -{ - // FIXME - // Two things to fix here: - // This loads all necessary data for this step at once, can be overlayed with computation - // Also if running with the tracker some data is loaded twice! - mTimeFrameGPU->initialise(0, trackingParams, 3, &this->mIndexTableUtils, &mTfGPUParams); - - // FIXME some of these only need to be created once! - mTimeFrameGPU->loadIndexTableUtils(iteration); - mTimeFrameGPU->createUsedClustersDeviceArray(iteration, 3); - mTimeFrameGPU->createClustersDeviceArray(iteration, 3); - mTimeFrameGPU->createUnsortedClustersDeviceArray(iteration, 3); - mTimeFrameGPU->createClustersIndexTablesArray(iteration); - mTimeFrameGPU->createROFrameClustersDeviceArray(iteration); - for (int iLayer{0}; iLayer < 3; ++iLayer) { - mTimeFrameGPU->loadClustersDevice(iteration, iLayer); - mTimeFrameGPU->loadUnsortedClustersDevice(iteration, iLayer); - mTimeFrameGPU->loadClustersIndexTables(iteration, iLayer); - mTimeFrameGPU->createUsedClustersDevice(iteration, iLayer); - mTimeFrameGPU->loadROFrameClustersDevice(iteration, iLayer); - } -} - -template -void VertexerTraitsGPU::adoptTimeFrame(TimeFrame* tf) noexcept -{ - mTimeFrameGPU = static_cast*>(tf); - this->mTimeFrame = static_cast*>(tf); -} - -template -void VertexerTraitsGPU::updateVertexingParameters(const std::vector& vrtPar, const TimeFrameGPUParameters& tfPar) -{ - this->mVrtParams = vrtPar; - mTfGPUParams = tfPar; - this->mIndexTableUtils.setTrackingParameters(vrtPar[0]); - for (auto& par : this->mVrtParams) { - par.phiSpan = static_cast(std::ceil(this->mIndexTableUtils.getNphiBins() * par.phiCut / o2::constants::math::TwoPI)); - par.zSpan = static_cast(std::ceil(par.zCut * this->mIndexTableUtils.getInverseZCoordinate(0))); - } -} - -template -void VertexerTraitsGPU::computeTracklets(const int iteration) -{ - if (mTimeFrameGPU->getClusters().empty()) { - return; - } - const auto& conf = ITSGpuTrackingParamConfig::Instance(); - - mTimeFrameGPU->createVtxTrackletsLUTDevice(iteration); - countTrackletsInROFsHandler(mTimeFrameGPU->getDeviceIndexTableUtils(), - mTimeFrameGPU->getDeviceMultCutMask(), - mTimeFrameGPU->getNrof(), - this->mVrtParams[iteration].deltaRof, - mTimeFrameGPU->getDeviceROFramesPV(), - this->mVrtParams[iteration].vertPerRofThreshold, - mTimeFrameGPU->getDeviceArrayClusters(), - mTimeFrameGPU->getClusterSizes()[1], - mTimeFrameGPU->getDeviceROFrameClusters(), - (const uint8_t**)mTimeFrameGPU->getDeviceArrayUsedClusters(), - mTimeFrameGPU->getDeviceArrayClustersIndexTables(), - mTimeFrameGPU->getDeviceArrayNTrackletsPerCluster(), - mTimeFrameGPU->getDeviceArrayNTrackletsPerClusterSum(), - mTimeFrameGPU->getDeviceArrayNTrackletsPerROF(), - mTimeFrameGPU->getDeviceNTrackletsPerCluster(), - mTimeFrameGPU->getDeviceNTrackletsPerClusterSum(), - iteration, - this->mVrtParams[iteration].phiCut, - this->mVrtParams[iteration].maxTrackletsPerCluster, - conf.nBlocksVtxComputeTracklets[iteration], - conf.nThreadsVtxComputeTracklets[iteration], - mTimeFrameGPU->getStreams()); - mTimeFrameGPU->createVtxTrackletsBuffers(iteration); - computeTrackletsInROFsHandler(mTimeFrameGPU->getDeviceIndexTableUtils(), - mTimeFrameGPU->getDeviceMultCutMask(), - mTimeFrameGPU->getNrof(), - this->mVrtParams[iteration].deltaRof, - mTimeFrameGPU->getDeviceROFramesPV(), - this->mVrtParams[iteration].vertPerRofThreshold, - mTimeFrameGPU->getDeviceArrayClusters(), - mTimeFrameGPU->getClusterSizes()[1], - mTimeFrameGPU->getDeviceROFrameClusters(), - (const uint8_t**)mTimeFrameGPU->getDeviceArrayUsedClusters(), - mTimeFrameGPU->getDeviceArrayClustersIndexTables(), - mTimeFrameGPU->getDeviceArrayTracklets(), - (const int32_t**)mTimeFrameGPU->getDeviceArrayNTrackletsPerCluster(), - (const int32_t**)mTimeFrameGPU->getDeviceArrayNTrackletsPerClusterSum(), - (const int32_t**)mTimeFrameGPU->getDeviceArrayNTrackletsPerROF(), - iteration, - this->mVrtParams[iteration].phiCut, - this->mVrtParams[iteration].maxTrackletsPerCluster, - conf.nBlocksVtxComputeTracklets[iteration], - conf.nThreadsVtxComputeTracklets[iteration], - mTimeFrameGPU->getStreams()); -} - -template -void VertexerTraitsGPU::computeTrackletMatching(const int iteration) -{ - if (!mTimeFrameGPU->getTotalTrackletsTF(0) || !mTimeFrameGPU->getTotalTrackletsTF(1)) { - return; - } - - const auto& conf = ITSGpuTrackingParamConfig::Instance(); - mTimeFrameGPU->createVtxLinesLUTDevice(iteration); - countTrackletsMatchingInROFsHandler(mTimeFrameGPU->getNrof(), - this->mVrtParams[iteration].deltaRof, - mTimeFrameGPU->getClusterSizes()[1], - mTimeFrameGPU->getDeviceROFrameClusters(), - mTimeFrameGPU->getDeviceArrayClusters(), - mTimeFrameGPU->getDeviceArrayUsedClusters(), - (const Tracklet**)mTimeFrameGPU->getDeviceArrayTracklets(), - mTimeFrameGPU->getDeviceUsedTracklets(), - (const int32_t**)mTimeFrameGPU->getDeviceArrayNTrackletsPerCluster(), - (const int32_t**)mTimeFrameGPU->getDeviceArrayNTrackletsPerClusterSum(), - mTimeFrameGPU->getDeviceNLinesPerCluster(), - mTimeFrameGPU->getDeviceNLinesPerClusterSum(), - iteration, - this->mVrtParams[iteration].phiCut, - this->mVrtParams[iteration].tanLambdaCut, - conf.nBlocksVtxComputeMatching[iteration], - conf.nThreadsVtxComputeMatching[iteration], - mTimeFrameGPU->getStreams()); - mTimeFrameGPU->createVtxLinesBuffer(iteration); - computeTrackletsMatchingInROFsHandler(mTimeFrameGPU->getNrof(), - this->mVrtParams[iteration].deltaRof, - mTimeFrameGPU->getClusterSizes()[1], - mTimeFrameGPU->getDeviceROFrameClusters(), - mTimeFrameGPU->getDeviceArrayClusters(), - nullptr, - (const Tracklet**)mTimeFrameGPU->getDeviceArrayTracklets(), - mTimeFrameGPU->getDeviceUsedTracklets(), - (const int32_t**)mTimeFrameGPU->getDeviceArrayNTrackletsPerCluster(), - (const int32_t**)mTimeFrameGPU->getDeviceArrayNTrackletsPerClusterSum(), - (const int32_t*)mTimeFrameGPU->getDeviceNLinesPerClusterSum(), - mTimeFrameGPU->getDeviceLines(), - iteration, - this->mVrtParams[iteration].phiCut, - this->mVrtParams[iteration].tanLambdaCut, - conf.nBlocksVtxComputeMatching[iteration], - conf.nThreadsVtxComputeMatching[iteration], - mTimeFrameGPU->getStreams()); -} - -template -void VertexerTraitsGPU::computeVertices(const int iteration) -{ - LOGP(fatal, "This step is not implemented yet!"); - mTimeFrameGPU->loadUsedClustersDevice(); -} - -template class VertexerTraitsGPU<7>; - -} // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexingKernels.cu deleted file mode 100644 index a2787bb13598d..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexingKernels.cu +++ /dev/null @@ -1,660 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// - -#include -#include - -#include "ITStrackingGPU/VertexingKernels.h" -#include "ITStracking/Tracklet.h" -#include "ITStracking/IndexTableUtils.h" -#include "ITStracking/ClusterLines.h" - -#include "GPUCommonMath.h" -#include "GPUCommonHelpers.h" -#include "GPUCommonDef.h" - -namespace o2::its -{ - -namespace gpu -{ - -template -GPUg() void computeLayerTrackletMutliROFKernel(const Cluster** GPUrestrict() clusters, - const int32_t** GPUrestrict() rofClusters, - const uint8_t** GPUrestrict() usedClusters, - const int32_t** GPUrestrict() clusterIndexTables, - const float phiCut, - maybe_const** GPUrestrict() tracklets, - maybe_const** GPUrestrict() trackletOffsets, - const IndexTableUtils* GPUrestrict() utils, - const int32_t nRofs, - const int32_t deltaRof, - const int32_t* GPUrestrict() rofPV, - const int32_t iteration, - const int32_t verPerRofThreshold, - const int32_t maxTrackletsPerCluster) -{ - constexpr int32_t iMode = (Mode == TrackletMode::Layer0Layer1) ? 0 : 1; - const int32_t phiBins(utils->getNphiBins()); - const int32_t zBins(utils->getNzBins()); - const int32_t tableSize{phiBins * zBins + 1}; - extern __shared__ uint16_t storedTrackletsShared[]; // each deltaROF needs its own counters - uint16_t* storedTrackletsLocal = storedTrackletsShared + threadIdx.x * (2 * deltaRof + 1); - for (uint32_t pivotRofId{blockIdx.x}; pivotRofId < (uint32_t)nRofs; pivotRofId += gridDim.x) { - if (iteration && rofPV[pivotRofId] > verPerRofThreshold) { - continue; - } - const uint16_t startROF = o2::gpu::CAMath::Max(0, (int)pivotRofId - deltaRof); - const uint16_t endROF = o2::gpu::CAMath::Min(nRofs, (int)pivotRofId + deltaRof + 1); - const auto clustersCurrentLayer = getClustersOnLayer((int32_t)pivotRofId, nRofs, 1, rofClusters, clusters); - if (clustersCurrentLayer.empty()) { - continue; - } - auto trackletsPerCluster = getNTrackletsPerCluster(pivotRofId, nRofs, iMode, rofClusters, trackletOffsets); - for (uint32_t iCurrentLayerClusterIndex{threadIdx.x}; iCurrentLayerClusterIndex < (uint32_t)clustersCurrentLayer.size(); iCurrentLayerClusterIndex += blockDim.x) { - for (int16_t i{0}; i < (int16_t)((2 * deltaRof) + 1); ++i) { - storedTrackletsLocal[i] = 0; - } - const Cluster& GPUrestrict() currentCluster { clustersCurrentLayer[iCurrentLayerClusterIndex] }; - const int4 selectedBinsRect{getBinsRect(currentCluster, (int)Mode, utils, 0.f, 0.f, 50.f, phiCut / 2)}; - if (selectedBinsRect.x != 0 || selectedBinsRect.y != 0 || selectedBinsRect.z != 0 || selectedBinsRect.w != 0) { - int phiBinsNum{selectedBinsRect.w - selectedBinsRect.y + 1}; - if (phiBinsNum < 0) { - phiBinsNum += phiBins; - } - for (int32_t iPhiBin{selectedBinsRect.y}, iPhiCount{0}; iPhiCount < phiBinsNum; iPhiBin = ++iPhiBin == phiBins ? 0 : iPhiBin, iPhiCount++) { - for (uint16_t targetRofId{startROF}; targetRofId < endROF; ++targetRofId) { - uint16_t& storedTracklets = storedTrackletsLocal[pivotRofId - targetRofId + deltaRof]; - const int32_t firstBinIndex{utils->getBinIndex(selectedBinsRect.x, iPhiBin)}; - const int32_t maxBinIndex{firstBinIndex + selectedBinsRect.z - selectedBinsRect.x + 1}; - const int32_t firstRowClusterIndex{clusterIndexTables[(int)Mode][(targetRofId)*tableSize + firstBinIndex]}; - const int32_t maxRowClusterIndex{clusterIndexTables[(int)Mode][(targetRofId)*tableSize + maxBinIndex]}; - auto clustersNextLayer = getClustersOnLayer((int32_t)targetRofId, nRofs, (int32_t)Mode, rofClusters, clusters); - if (clustersNextLayer.empty()) { - continue; - } - for (int32_t iNextLayerClusterIndex{firstRowClusterIndex}; iNextLayerClusterIndex < maxRowClusterIndex && iNextLayerClusterIndex < (int32_t)clustersNextLayer.size(); ++iNextLayerClusterIndex) { - if (iteration && usedClusters[(int32_t)Mode][iNextLayerClusterIndex]) { - continue; - } - const Cluster& GPUrestrict() nextCluster { clustersNextLayer[iNextLayerClusterIndex] }; - if (o2::gpu::GPUCommonMath::Abs(math_utils::smallestAngleDifference(currentCluster.phi, nextCluster.phi)) < phiCut) { - if (storedTracklets < maxTrackletsPerCluster) { - if constexpr (!dryRun) { - if constexpr (Mode == TrackletMode::Layer0Layer1) { - tracklets[0][trackletsPerCluster[iCurrentLayerClusterIndex] + storedTracklets] = Tracklet{iNextLayerClusterIndex, (int)iCurrentLayerClusterIndex, nextCluster, currentCluster, (short)targetRofId, (short)pivotRofId}; - } else { - tracklets[1][trackletsPerCluster[iCurrentLayerClusterIndex] + storedTracklets] = Tracklet{(int)iCurrentLayerClusterIndex, iNextLayerClusterIndex, currentCluster, nextCluster, (short)pivotRofId, (short)targetRofId}; - } - } - ++storedTracklets; - } - } - } - } - } - } - if constexpr (dryRun) { - for (int32_t i{0}; i < (int32_t)((2 * deltaRof) + 1); ++i) { - trackletsPerCluster[iCurrentLayerClusterIndex] += storedTrackletsLocal[i]; - } - } - } - } -} - -template -GPUg() void computeTrackletSelectionMutliROFKernel(const Cluster** GPUrestrict() clusters, - maybe_const** GPUrestrict() usedClusters, - const int32_t** GPUrestrict() rofClusters, - const float phiCut, - const float tanLambdaCut, - const Tracklet** GPUrestrict() tracklets, - uint8_t* GPUrestrict() usedTracklets, - const int32_t** GPUrestrict() trackletOffsets, - const int32_t** GPUrestrict() trackletLUTs, - maybe_const* lineOffsets, - maybe_const* GPUrestrict() lines, - const int32_t nRofs, - const int32_t deltaRof, - const int32_t maxTracklets) -{ - for (uint32_t pivotRofId{blockIdx.x}; pivotRofId < nRofs; pivotRofId += gridDim.x) { - const int16_t startROF = o2::gpu::CAMath::Max(0, (int32_t)pivotRofId - deltaRof); - const int16_t endROF = o2::gpu::CAMath::Min(nRofs, (int32_t)pivotRofId + deltaRof + 1); - - const uint32_t clusterOffset = rofClusters[1][pivotRofId]; - const uint32_t nClustersCurrentLayer = rofClusters[1][pivotRofId + 1] - clusterOffset; - if (nClustersCurrentLayer <= 0) { - continue; - } - - auto linesPerCluster = getNLinesPerCluster(pivotRofId, nRofs, rofClusters, lineOffsets); - auto nTrackletsPerCluster01 = getNTrackletsPerCluster(pivotRofId, nRofs, 0, rofClusters, trackletOffsets); - auto nTrackletsPerCluster12 = getNTrackletsPerCluster(pivotRofId, nRofs, 1, rofClusters, trackletOffsets); - - for (uint32_t iCurrentLayerClusterIndex{threadIdx.x}; iCurrentLayerClusterIndex < nClustersCurrentLayer; iCurrentLayerClusterIndex += blockDim.x) { - int32_t validTracklets{0}; - const int32_t nTracklets01 = nTrackletsPerCluster01[iCurrentLayerClusterIndex]; - const int32_t nTracklets12 = nTrackletsPerCluster12[iCurrentLayerClusterIndex]; - for (int32_t iTracklet12{0}; iTracklet12 < nTracklets12; ++iTracklet12) { - for (int32_t iTracklet01{0}; iTracklet01 < nTracklets01; ++iTracklet01) { - - if (usedTracklets[trackletLUTs[0][clusterOffset + iCurrentLayerClusterIndex] + iTracklet01]) { - continue; - } - - const auto& GPUrestrict() tracklet01 { tracklets[0][trackletLUTs[0][clusterOffset + iCurrentLayerClusterIndex] + iTracklet01] }; - const auto& GPUrestrict() tracklet12 { tracklets[1][trackletLUTs[1][clusterOffset + iCurrentLayerClusterIndex] + iTracklet12] }; - const int16_t rof0 = tracklet01.rof[0]; - const int16_t rof2 = tracklet12.rof[1]; - if (deltaRof > 0 && ((rof0 < startROF) || (rof0 >= endROF) || (rof2 < startROF) || (rof2 >= endROF) || (o2::gpu::CAMath::Abs(rof0 - rof2) > deltaRof))) { - continue; - } - - const float deltaTanLambda{o2::gpu::GPUCommonMath::Abs(tracklet01.tanLambda - tracklet12.tanLambda)}; - const float deltaPhi{o2::gpu::GPUCommonMath::Abs(math_utils::smallestAngleDifference(tracklet01.phi, tracklet12.phi))}; - // - if (deltaTanLambda < tanLambdaCut && deltaPhi < phiCut && validTracklets < maxTracklets) { - // TODO use atomics to avoid race conditions for torn writes but is it needed here? - usedTracklets[trackletLUTs[0][clusterOffset + iCurrentLayerClusterIndex] + iTracklet01] = 1; - if constexpr (dryRun) { - usedClusters[0][rofClusters[0][rof0] + tracklet01.firstClusterIndex] = 1; - usedClusters[2][rofClusters[2][rof2] + tracklet12.secondClusterIndex] = 1; - } else { - const Cluster* clusters0 = clusters[0] + rofClusters[0][tracklet01.rof[0]]; - const Cluster* clusters1 = clusters[1] + rofClusters[1][tracklet01.rof[1]]; - lines[lineOffsets[iCurrentLayerClusterIndex] + validTracklets] = Line(tracklet01, clusters0, clusters1); - } - ++validTracklets; - } - } - } - - if constexpr (dryRun) { - linesPerCluster[iCurrentLayerClusterIndex] = validTracklets; - } - } - } -} - -template -GPUg() void compileTrackletsPerROFKernel(const int32_t nRofs, - int** GPUrestrict() nTrackletsPerROF, - const int32_t** GPUrestrict() rofClusters, - const int32_t** GPUrestrict() nTrackletsPerCluster) -{ - // TODO is this the best reduction kernel? - constexpr int32_t iMode = (Mode == TrackletMode::Layer0Layer1) ? 0 : 1; - extern __shared__ int32_t ssum[]; - for (uint32_t rof = blockIdx.x; rof < (uint32_t)nRofs; rof += gridDim.x) { - const auto& GPUrestrict() currentNTracklets = getNTrackletsPerCluster(rof, nRofs, iMode, rofClusters, nTrackletsPerCluster); - int32_t localSum = 0; - for (uint32_t ci = threadIdx.x; ci < (uint32_t)currentNTracklets.size(); ci += blockDim.x) { - localSum += currentNTracklets[ci]; - } - ssum[threadIdx.x] = localSum; - __syncthreads(); - for (uint32_t stride = blockDim.x / 2; stride > 0; stride >>= 1) { - if (threadIdx.x < stride) { - ssum[threadIdx.x] += ssum[threadIdx.x + stride]; - } - __syncthreads(); - } - if (threadIdx.x == 0) { - nTrackletsPerROF[iMode][rof] = ssum[0]; - } - } -} - -template -GPUhi() void cubExclusiveScan(const T* GPUrestrict() in, T* GPUrestrict() out, int32_t num_items, cudaStream_t stream) -{ - void* d_temp_storage = nullptr; - size_t temp_storage_bytes = 0; - GPUChkErrS(cub::DeviceScan::InclusiveSum(d_temp_storage, temp_storage_bytes, in, out + 1, num_items, stream)); - GPUChkErrS(cudaMallocAsync(&d_temp_storage, temp_storage_bytes, stream)); - GPUChkErrS(cub::DeviceScan::InclusiveSum(d_temp_storage, temp_storage_bytes, in, out + 1, num_items, stream)); - GPUChkErrS(cudaFreeAsync(d_temp_storage, stream)); -} - -} // namespace gpu - -template -void countTrackletsInROFsHandler(const IndexTableUtils* GPUrestrict() utils, - const uint8_t* GPUrestrict() multMask, - const int32_t nRofs, - const int32_t deltaROF, - const int32_t* GPUrestrict() rofPV, - const int32_t vertPerRofThreshold, - const Cluster** GPUrestrict() clusters, - const uint32_t nClusters, - const int32_t** GPUrestrict() ROFClusters, - const uint8_t** GPUrestrict() usedClusters, - const int32_t** GPUrestrict() clustersIndexTables, - int32_t** GPUrestrict() trackletsPerClusterLUTs, - int32_t** GPUrestrict() trackletsPerClusterSumLUTs, - int32_t** GPUrestrict() trackletsPerROF, - const std::array& trackletsPerClusterLUTsHost, - const std::array& trackletsPerClusterSumLUTsHost, - const int32_t iteration, - const float phiCut, - const int32_t maxTrackletsPerCluster, - const int32_t nBlocks, - const int32_t nThreads, - gpu::Streams& streams) -{ - const uint32_t sharedBytes = nThreads * (2 * deltaROF + 1) * sizeof(uint16_t); - gpu::computeLayerTrackletMutliROFKernel<<>>(clusters, - ROFClusters, - usedClusters, - clustersIndexTables, - phiCut, - nullptr, - trackletsPerClusterLUTs, - utils, - nRofs, - deltaROF, - rofPV, - iteration, - vertPerRofThreshold, - maxTrackletsPerCluster); - gpu::compileTrackletsPerROFKernel<<>>(nRofs, trackletsPerROF, ROFClusters, (const int32_t**)trackletsPerClusterLUTs); - gpu::cubExclusiveScan(trackletsPerClusterLUTsHost[0], trackletsPerClusterSumLUTsHost[0], nClusters, streams[0].get()); - - gpu::computeLayerTrackletMutliROFKernel<<>>(clusters, - ROFClusters, - usedClusters, - clustersIndexTables, - phiCut, - nullptr, - trackletsPerClusterLUTs, - utils, - nRofs, - deltaROF, - rofPV, - iteration, - vertPerRofThreshold, - maxTrackletsPerCluster); - gpu::compileTrackletsPerROFKernel<<>>(nRofs, trackletsPerROF, ROFClusters, (const int**)trackletsPerClusterLUTs); - gpu::cubExclusiveScan(trackletsPerClusterLUTsHost[1], trackletsPerClusterSumLUTsHost[1], nClusters, streams[1].get()); -} - -template -void computeTrackletsInROFsHandler(const IndexTableUtils* GPUrestrict() utils, - const uint8_t* GPUrestrict() multMask, - const int32_t nRofs, - const int32_t deltaROF, - const int32_t* GPUrestrict() rofPV, - const int vertPerRofThreshold, - const Cluster** GPUrestrict() clusters, - const uint32_t nClusters, - const int32_t** GPUrestrict() ROFClusters, - const uint8_t** GPUrestrict() usedClusters, - const int32_t** GPUrestrict() clustersIndexTables, - Tracklet** GPUrestrict() foundTracklets, - const int32_t** GPUrestrict() trackletsPerClusterLUTs, - const int32_t** GPUrestrict() trackletsPerClusterSumLUTs, - const int32_t** GPUrestrict() trackletsPerROF, - const int32_t iteration, - const float phiCut, - const int32_t maxTrackletsPerCluster, - const int32_t nBlocks, - const int32_t nThreads, - gpu::Streams& streams) -{ - const uint32_t sharedBytes = nThreads * (2 * deltaROF + 1) * sizeof(uint16_t); - gpu::computeLayerTrackletMutliROFKernel<<>>(clusters, - ROFClusters, - usedClusters, - clustersIndexTables, - phiCut, - foundTracklets, - trackletsPerClusterSumLUTs, - utils, - nRofs, - deltaROF, - rofPV, - iteration, - vertPerRofThreshold, - maxTrackletsPerCluster); - gpu::computeLayerTrackletMutliROFKernel<<>>(clusters, - ROFClusters, - usedClusters, - clustersIndexTables, - phiCut, - foundTracklets, - trackletsPerClusterSumLUTs, - utils, - nRofs, - deltaROF, - rofPV, - iteration, - vertPerRofThreshold, - maxTrackletsPerCluster); -} - -void countTrackletsMatchingInROFsHandler(const int32_t nRofs, - const int32_t deltaROF, - const uint32_t nClusters, - const int32_t** GPUrestrict() ROFClusters, - const Cluster** GPUrestrict() clusters, - uint8_t** GPUrestrict() usedClusters, - const Tracklet** GPUrestrict() foundTracklets, - uint8_t* GPUrestrict() usedTracklets, - const int32_t** GPUrestrict() trackletsPerClusterLUTs, - const int32_t** GPUrestrict() trackletsPerClusterSumLUTs, - int32_t* GPUrestrict() linesPerClusterLUT, - int32_t* GPUrestrict() linesPerClusterSumLUT, - const int32_t iteration, - const float phiCut, - const float tanLambdaCut, - const int32_t nBlocks, - const int32_t nThreads, - gpu::Streams& streams) -{ - streams[1].sync(); // need to make sure that all tracklets are done, since this placed in 0 tracklet01 will be done but tracklet12 needs to be guaranteed - gpu::computeTrackletSelectionMutliROFKernel<<>>(nullptr, - usedClusters, - ROFClusters, - phiCut, - tanLambdaCut, - foundTracklets, - usedTracklets, - trackletsPerClusterLUTs, - trackletsPerClusterSumLUTs, - linesPerClusterLUT, - nullptr, - nRofs, - deltaROF, - 100); - gpu::cubExclusiveScan(linesPerClusterLUT, linesPerClusterSumLUT, nClusters, streams[0].get()); -} - -void computeTrackletsMatchingInROFsHandler(const int32_t nRofs, - const int32_t deltaROF, - const uint32_t nClusters, - const int32_t** GPUrestrict() ROFClusters, - const Cluster** GPUrestrict() clusters, - const uint8_t** GPUrestrict() usedClusters, - const Tracklet** GPUrestrict() foundTracklets, - uint8_t* GPUrestrict() usedTracklets, - const int32_t** GPUrestrict() trackletsPerClusterLUTs, - const int32_t** GPUrestrict() trackletsPerClusterSumLUTs, - const int32_t* GPUrestrict() linesPerClusterSumLUT, - Line* GPUrestrict() lines, - const int32_t iteration, - const float phiCut, - const float tanLambdaCut, - const int32_t nBlocks, - const int32_t nThreads, - gpu::Streams& streams) -{ - gpu::computeTrackletSelectionMutliROFKernel<<>>(clusters, - nullptr, - ROFClusters, - phiCut, - tanLambdaCut, - foundTracklets, - usedTracklets, - trackletsPerClusterLUTs, - trackletsPerClusterSumLUTs, - linesPerClusterSumLUT, - lines, - nRofs, - deltaROF, - 100); -} - -/// Explicit instantiation of ITS2 handlers -template void countTrackletsInROFsHandler<7>(const IndexTableUtils<7>* GPUrestrict() utils, - const uint8_t* GPUrestrict() multMask, - const int32_t nRofs, - const int32_t deltaROF, - const int32_t* GPUrestrict() rofPV, - const int32_t vertPerRofThreshold, - const Cluster** GPUrestrict() clusters, - const uint32_t nClusters, - const int32_t** GPUrestrict() ROFClusters, - const uint8_t** GPUrestrict() usedClusters, - const int32_t** GPUrestrict() clustersIndexTables, - int32_t** trackletsPerClusterLUTs, - int32_t** trackletsPerClusterSumLUTs, - int32_t** trackletsPerROF, - const std::array& trackletsPerClusterLUTsHost, - const std::array& trackletsPerClusterSumLUTsHost, - const int32_t iteration, - const float phiCut, - const int32_t maxTrackletsPerCluster, - const int32_t nBlocks, - const int32_t nThreads, - gpu::Streams& streams); - -template void computeTrackletsInROFsHandler<7>(const IndexTableUtils<7>* GPUrestrict() utils, - const uint8_t* GPUrestrict() multMask, - const int32_t nRofs, - const int32_t deltaROF, - const int32_t* GPUrestrict() rofPV, - const int vertPerRofThreshold, - const Cluster** GPUrestrict() clusters, - const uint32_t nClusters, - const int32_t** GPUrestrict() ROFClusters, - const uint8_t** GPUrestrict() usedClusters, - const int32_t** GPUrestrict() clustersIndexTables, - Tracklet** GPUrestrict() foundTracklets, - const int32_t** GPUrestrict() trackletsPerClusterLUTs, - const int32_t** GPUrestrict() trackletsPerClusterSumLUTs, - const int32_t** GPUrestrict() trackletsPerROF, - const int32_t iteration, - const float phiCut, - const int32_t maxTrackletsPerCluster, - const int32_t nBlocks, - const int32_t nThreads, - gpu::Streams& streams); -/* -GPUg() void lineClustererMultipleRof( - const int* sizeClustersL1, // Number of clusters on layer 1 per ROF - Line* lines, // Lines - int* nFoundLines, // Number of found lines - int* nExclusiveFoundLines, // Number of found lines exclusive scan - int* clusteredLines, // Clustered lines - const unsigned int startRofId, // Starting ROF ID - const unsigned int rofSize, // Number of ROFs to consider // Number of found lines exclusive scan - const float pairCut) // Selection on line pairs -{ - for (unsigned int iRof{threadIdx.x}; iRof < rofSize; iRof += blockDim.x) { - auto rof = iRof + startRofId; - auto clustersL1offsetRof = sizeClustersL1[rof] - sizeClustersL1[startRofId]; // starting cluster offset for this ROF - auto nClustersL1Rof = sizeClustersL1[rof + 1] - sizeClustersL1[rof]; // number of clusters for this ROF - auto linesOffsetRof = nExclusiveFoundLines[clustersL1offsetRof]; // starting line offset for this ROF - // auto* foundLinesRof = nFoundLines + clustersL1offsetRof; - auto nLinesRof = nExclusiveFoundLines[clustersL1offsetRof + nClustersL1Rof] - linesOffsetRof; - // printf("rof: %d -> %d lines.\n", rof, nLinesRof); - for (int iLine1 = 0; iLine1 < nLinesRof; ++iLine1) { - auto absLine1Index = nExclusiveFoundLines[clustersL1offsetRof] + iLine1; - if (clusteredLines[absLine1Index] > -1) { - continue; - } - for (int iLine2 = iLine1 + 1; iLine2 < nLinesRof; ++iLine2) { - auto absLine2Index = nExclusiveFoundLines[clustersL1offsetRof] + iLine2; - if (clusteredLines[absLine2Index] > -1) { - continue; - } - - if (Line::getDCA(lines[absLine1Index], lines[absLine2Index]) < pairCut) { - ClusterLinesGPU tmpClus{lines[absLine1Index], lines[absLine2Index]}; - float tmpVertex[3]; - tmpVertex[0] = tmpClus.getVertex()[0]; - tmpVertex[1] = tmpClus.getVertex()[1]; - tmpVertex[2] = tmpClus.getVertex()[2]; - if (tmpVertex[0] * tmpVertex[0] + tmpVertex[1] * tmpVertex[1] > 4.f) { // outside the beampipe, skip it - break; - } - clusteredLines[absLine1Index] = iLine1; // We set local index of first line to contribute, so we can retrieve the cluster later - clusteredLines[absLine2Index] = iLine1; - for (int iLine3 = 0; iLine3 < nLinesRof; ++iLine3) { - auto absLine3Index = nExclusiveFoundLines[clustersL1offsetRof] + iLine3; - if (clusteredLines[absLine3Index] > -1) { - continue; - } - if (Line::getDistanceFromPoint(lines[absLine3Index], tmpVertex) < pairCut) { - clusteredLines[absLine3Index] = iLine1; - } - } - break; - } - } - } - } // rof loop -} - -GPUg() void computeCentroidsKernel( - Line* lines, - int* nFoundLines, - int* nExclusiveFoundLines, - const unsigned int nClustersMiddleLayer, - float* centroids, - const float lowHistX, - const float highHistX, - const float lowHistY, - const float highHistY, - const float pairCut) -{ - const int nLines = nExclusiveFoundLines[nClustersMiddleLayer - 1] + nFoundLines[nClustersMiddleLayer - 1]; - const int maxIterations{nLines * (nLines - 1) / 2}; - for (unsigned int currentThreadIndex = blockIdx.x * blockDim.x + threadIdx.x; currentThreadIndex < maxIterations; currentThreadIndex += blockDim.x * gridDim.x) { - int iFirstLine = currentThreadIndex / nLines; - int iSecondLine = currentThreadIndex % nLines; - // All unique pairs - if (iSecondLine <= iFirstLine) { - iFirstLine = nLines - iFirstLine - 2; - iSecondLine = nLines - iSecondLine - 1; - } - if (Line::getDCA(lines[iFirstLine], lines[iSecondLine]) < pairCut) { - ClusterLinesGPU cluster{lines[iFirstLine], lines[iSecondLine]}; - if (cluster.getVertex()[0] * cluster.getVertex()[0] + cluster.getVertex()[1] * cluster.getVertex()[1] < 1.98f * 1.98f) { - // printOnThread(0, "xCentr: %f, yCentr: %f \n", cluster.getVertex()[0], cluster.getVertex()[1]); - centroids[2 * currentThreadIndex] = cluster.getVertex()[0]; - centroids[2 * currentThreadIndex + 1] = cluster.getVertex()[1]; - } else { - // write values outside the histogram boundaries, - // default behaviour is not to have them added to histogram later - // (writing zeroes would be problematic) - centroids[2 * currentThreadIndex] = 2 * lowHistX; - centroids[2 * currentThreadIndex + 1] = 2 * lowHistY; - } - } else { - // write values outside the histogram boundaries, - // default behaviour is not to have them added to histogram later - // (writing zeroes would be problematic) - centroids[2 * currentThreadIndex] = 2 * highHistX; - centroids[2 * currentThreadIndex + 1] = 2 * highHistY; - } - } -} - -GPUg() void computeZCentroidsKernel( - const int nLines, - const cub::KeyValuePair* tmpVtX, - float* beamPosition, - Line* lines, - float* centroids, - const int* histX, // X - const float lowHistX, - const float binSizeHistX, - const int nBinsHistX, - const int* histY, // Y - const float lowHistY, - const float binSizeHistY, - const int nBinsHistY, - const float lowHistZ, // Z - const float pairCut, - const int binOpeningX, - const int binOpeningY) -{ - for (unsigned int currentThreadIndex = blockIdx.x * blockDim.x + threadIdx.x; currentThreadIndex < nLines; currentThreadIndex += blockDim.x * gridDim.x) { - if (tmpVtX[0].value || tmpVtX[1].value) { - float tmpX{lowHistX + tmpVtX[0].key * binSizeHistX + binSizeHistX / 2}; - int sumWX{tmpVtX[0].value}; - float wX{tmpX * tmpVtX[0].value}; - for (int iBin{o2::gpu::GPUCommonMath::Max(0, tmpVtX[0].key - binOpeningX)}; iBin < o2::gpu::GPUCommonMath::Min(tmpVtX[0].key + binOpeningX + 1, nBinsHistX - 1); ++iBin) { - if (iBin != tmpVtX[0].key) { - wX += (lowHistX + iBin * binSizeHistX + binSizeHistX / 2) * histX[iBin]; - sumWX += histX[iBin]; - } - } - float tmpY{lowHistY + tmpVtX[1].key * binSizeHistY + binSizeHistY / 2}; - int sumWY{tmpVtX[1].value}; - float wY{tmpY * tmpVtX[1].value}; - for (int iBin{o2::gpu::GPUCommonMath::Max(0, tmpVtX[1].key - binOpeningY)}; iBin < o2::gpu::GPUCommonMath::Min(tmpVtX[1].key + binOpeningY + 1, nBinsHistY - 1); ++iBin) { - if (iBin != tmpVtX[1].key) { - wY += (lowHistY + iBin * binSizeHistY + binSizeHistY / 2) * histY[iBin]; - sumWY += histY[iBin]; - } - } - beamPosition[0] = wX / sumWX; - beamPosition[1] = wY / sumWY; - float mockBeamPoint1[3] = {beamPosition[0], beamPosition[1], -1}; // get two points laying at different z, to create line object - float mockBeamPoint2[3] = {beamPosition[0], beamPosition[1], 1}; - Line pseudoBeam = {mockBeamPoint1, mockBeamPoint2}; - if (Line::getDCA(lines[currentThreadIndex], pseudoBeam) < pairCut) { - ClusterLinesGPU cluster{lines[currentThreadIndex], pseudoBeam}; - centroids[currentThreadIndex] = cluster.getVertex()[2]; - } else { - centroids[currentThreadIndex] = 2 * lowHistZ; - } - } - } -} - -GPUg() void computeVertexKernel( - cub::KeyValuePair* tmpVertexBins, - int* histZ, // Z - const float lowHistZ, - const float binSizeHistZ, - const int nBinsHistZ, - Vertex* vertices, - float* beamPosition, - const int vertIndex, - const int minContributors, - const int binOpeningZ) -{ - for (unsigned int currentThreadIndex = blockIdx.x * blockDim.x + threadIdx.x; currentThreadIndex < binOpeningZ; currentThreadIndex += blockDim.x * gridDim.x) { - if (currentThreadIndex == 0) { - if (tmpVertexBins[2].value > 1 && (tmpVertexBins[0].value || tmpVertexBins[1].value)) { - float z{lowHistZ + tmpVertexBins[2].key * binSizeHistZ + binSizeHistZ / 2}; - float ex{0.f}; - float ey{0.f}; - float ez{0.f}; - int sumWZ{tmpVertexBins[2].value}; - float wZ{z * tmpVertexBins[2].value}; - for (int iBin{o2::gpu::GPUCommonMath::Max(0, tmpVertexBins[2].key - binOpeningZ)}; iBin < o2::gpu::GPUCommonMath::Min(tmpVertexBins[2].key + binOpeningZ + 1, nBinsHistZ - 1); ++iBin) { - if (iBin != tmpVertexBins[2].key) { - wZ += (lowHistZ + iBin * binSizeHistZ + binSizeHistZ / 2) * histZ[iBin]; - sumWZ += histZ[iBin]; - } - histZ[iBin] = 0; - } - if (sumWZ > minContributors || vertIndex == 0) { - new (vertices + vertIndex) Vertex{o2::math_utils::Point3D(beamPosition[0], beamPosition[1], wZ / sumWZ), std::array{ex, 0, ey, 0, 0, ez}, static_cast(sumWZ), 0}; - } else { - new (vertices + vertIndex) Vertex{}; - } - } else { - new (vertices + vertIndex) Vertex{}; - } - } - } -} -*/ -} // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt index a40aac491a386..e28fe04c06772 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt @@ -16,13 +16,9 @@ if(HIP_ENABLED) # add_compile_definitions(ITS_MEASURE_GPU_TIME) # add_compile_definitions(ITS_GPU_LOG) o2_add_hipified_library(ITStrackingHIP - SOURCES ../cuda/ClusterLinesGPU.cu - ../cuda/TimeFrameGPU.cu + SOURCES ../cuda/TimeFrameGPU.cu ../cuda/TrackerTraitsGPU.cxx - ../cuda/TracerGPU.cu ../cuda/TrackingKernels.cu - ../cuda/VertexingKernels.cu - ../cuda/VertexerTraitsGPU.cxx PUBLIC_INCLUDE_DIRECTORIES ../ PUBLIC_LINK_LIBRARIES O2::ITStracking O2::GPUTracking diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/BoundedAllocator.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/BoundedAllocator.h index 66634c1a07eea..91d5edeedcdb1 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/BoundedAllocator.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/BoundedAllocator.h @@ -97,6 +97,9 @@ class BoundedMemoryResource final : public std::pmr::memory_resource size_t getMaxMemory() const noexcept { return mMaxMemory; } void setMaxMemory(size_t max) { + if (max == mMaxMemory) { + return; + } size_t used = mUsedMemory.load(std::memory_order_acquire); if (used > max) { ++mCountThrow; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h index 902092a510eb0..d223adcef6214 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h @@ -17,38 +17,19 @@ #define TRACKINGITSU_INCLUDE_CACELL_H_ #include "ITStracking/Constants.h" +#include "DataFormatsITS/TimeEstBC.h" +#include "ReconstructionDataFormats/Track.h" #include "GPUCommonDef.h" namespace o2::its { -class Cell final -{ - public: - GPUhd() int getFirstClusterIndex() const { return mFirstClusterIndex; }; - GPUhd() int getSecondClusterIndex() const { return mSecondClusterIndex; }; - GPUhd() int getThirdClusterIndex() const { return mThirdClusterIndex; }; - GPUhd() int getFirstTrackletIndex() const { return mFirstTrackletIndex; }; - GPUhd() int getSecondTrackletIndex() const { return mSecondTrackletIndex; }; - GPUhd() int getLevel() const { return mLevel; }; - GPUhd() void setLevel(const int level) { mLevel = level; }; - GPUhd() int* getLevelPtr() { return &mLevel; } - - private: - int mFirstClusterIndex{constants::UnusedIndex}; - int mSecondClusterIndex{constants::UnusedIndex}; - int mThirdClusterIndex{constants::UnusedIndex}; - int mFirstTrackletIndex{constants::UnusedIndex}; - int mSecondTrackletIndex{constants::UnusedIndex}; - int mLevel{constants::UnusedIndex}; -}; - -template +template class CellSeed final : public o2::track::TrackParCovF { public: GPUhdDefault() CellSeed() = default; - GPUhd() CellSeed(int innerL, int cl0, int cl1, int cl2, int trkl0, int trkl1, o2::track::TrackParCovF& tpc, float chi2) : o2::track::TrackParCovF(tpc), mChi2(chi2), mLevel(1) + GPUhd() CellSeed(int innerL, int cl0, int cl1, int cl2, int trkl0, int trkl1, o2::track::TrackParCovF& tpc, float chi2, const TimeEstBC& time) : o2::track::TrackParCovF(tpc), mChi2(chi2), mLevel(1), mTime(time) { mClusters.fill(constants::UnusedIndex); setUserField(innerL); @@ -81,20 +62,24 @@ class CellSeed final : public o2::track::TrackParCovF GPUhd() void printCell() const { printf("cell: %d, %d\t lvl: %d\t chi2: %f\tcls: [", mTracklets[0], mTracklets[1], mLevel, mChi2); - for (int i = 0; i < nLayers; ++i) { + for (int i = 0; i < NLayers; ++i) { printf("%d", mClusters[i]); - if (i < nLayers - 1) { + if (i < NLayers - 1) { printf(" | "); } } - printf("]\n"); + printf("]"); + printf(" ts: %u +/- %u\n", mTime.getTimeStamp(), mTime.getTimeStampError()); } + GPUhd() auto& getTimeStamp() noexcept { return mTime; } + GPUhd() const auto& getTimeStamp() const noexcept { return mTime; } private: float mChi2 = -999.f; int mLevel = constants::UnusedIndex; std::array mTracklets = constants::helpers::initArray(); - std::array mClusters = constants::helpers::initArray(); + std::array mClusters = constants::helpers::initArray(); + TimeEstBC mTime; }; } // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cluster.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cluster.h index b96f0558943a6..34014d858648b 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cluster.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cluster.h @@ -19,6 +19,7 @@ #include #include "ITStracking/Constants.h" #include "GPUCommonRtypes.h" +#include "GPUCommonDef.h" namespace o2::its { @@ -71,8 +72,8 @@ struct TrackingFrameInfo final { float zCoordinate{-999.f}; float xTrackingFrame{-999.f}; float alphaTrackingFrame{-999.f}; - std::array positionTrackingFrame = {constants::UnusedIndex, constants::UnusedIndex}; - std::array covarianceTrackingFrame = {999., 999., 999.}; + std::array positionTrackingFrame = {-999.f, -999.f}; + std::array covarianceTrackingFrame = {-999.f, -999.f, -999.f}; ClassDefNV(TrackingFrameInfo, 1); }; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h index 0e7ad474ae455..6fbc6d7da7721 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h @@ -14,200 +14,77 @@ #include #include +#include +#include #include "ITStracking/Cluster.h" #include "ITStracking/Constants.h" #include "ITStracking/Tracklet.h" #include "GPUCommonRtypes.h" -#include "GPUCommonMath.h" namespace o2::its { + struct Line final { - GPUhdDefault() Line() = default; - GPUhd() Line(const Line&); - Line(std::array firstPoint, std::array secondPoint); - GPUhd() Line(const Tracklet&, const Cluster*, const Cluster*); +#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); - GPUhd() static float getDistanceFromPoint(const Line& line, const float point[3]); - static std::array getDCAComponents(const Line& line, const std::array point); - GPUhd() static void getDCAComponents(const Line& line, const float point[3], float destArray[6]); - GPUhd() static float getDCA(const Line&, const Line&, const float precision = constants::Tolerance); - static bool areParallel(const Line&, const Line&, const float precision = constants::Tolerance); - GPUhd() unsigned char isEmpty() const { return (originPoint[0] == 0.f && originPoint[1] == 0.f && originPoint[2] == 0.f) && - (cosinesDirector[0] == 0.f && cosinesDirector[1] == 0.f && cosinesDirector[2] == 0.f); } - GPUhdi() auto getDeltaROF() const { return rof[1] - rof[0]; } - GPUhd() void print() const; - bool operator==(const Line&) const; - bool operator!=(const Line&) const; - short getMinROF() const { return rof[0] < rof[1] ? rof[0] : rof[1]; } + 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; - float originPoint[3] = {0, 0, 0}; - float cosinesDirector[3] = {0, 0, 0}; - // float weightMatrix[6] = {1., 0., 0., 1., 0., 1.}; - // weightMatrix is a symmetric matrix internally stored as - // 0 --> row = 0, col = 0 - // 1 --> 0,1 - // 2 --> 0,2 - // 3 --> 1,1 - // 4 --> 1,2 - // 5 --> 2,2 - short rof[2] = {constants::UnusedIndex, constants::UnusedIndex}; + SVector3f originPoint; + SVector3f cosinesDirector; + TimeEstBC mTime; ClassDefNV(Line, 1); +#endif }; -GPUhdi() Line::Line(const Line& other) -{ - for (int i{0}; i < 3; ++i) { - originPoint[i] = other.originPoint[i]; - cosinesDirector[i] = other.cosinesDirector[i]; - } - // for (int i{0}; i < 6; ++i) { - // weightMatrix[i] = other.weightMatrix[i]; - // } - for (int i{0}; i < 2; ++i) { - rof[i] = other.rof[i]; - } -} - -GPUhdi() Line::Line(const Tracklet& tracklet, const Cluster* innerClusters, const Cluster* outerClusters) -{ - originPoint[0] = innerClusters[tracklet.firstClusterIndex].xCoordinate; - originPoint[1] = innerClusters[tracklet.firstClusterIndex].yCoordinate; - originPoint[2] = innerClusters[tracklet.firstClusterIndex].zCoordinate; - - cosinesDirector[0] = outerClusters[tracklet.secondClusterIndex].xCoordinate - innerClusters[tracklet.firstClusterIndex].xCoordinate; - cosinesDirector[1] = outerClusters[tracklet.secondClusterIndex].yCoordinate - innerClusters[tracklet.firstClusterIndex].yCoordinate; - cosinesDirector[2] = outerClusters[tracklet.secondClusterIndex].zCoordinate - innerClusters[tracklet.firstClusterIndex].zCoordinate; - - float inverseNorm{1.f / o2::gpu::CAMath::Hypot(cosinesDirector[0], cosinesDirector[1], cosinesDirector[2])}; - cosinesDirector[0] *= inverseNorm; - cosinesDirector[1] *= inverseNorm; - cosinesDirector[2] *= inverseNorm; - - rof[0] = tracklet.rof[0]; - rof[1] = tracklet.rof[1]; -} - -// static functions: -inline float Line::getDistanceFromPoint(const Line& line, const std::array& point) -{ - float DCASquared{0}; - float cdelta{0}; - for (int i{0}; i < 3; ++i) { - cdelta -= line.cosinesDirector[i] * (line.originPoint[i] - point[i]); - } - for (int i{0}; i < 3; ++i) { - DCASquared += (line.originPoint[i] - point[i] + line.cosinesDirector[i] * cdelta) * - (line.originPoint[i] - point[i] + line.cosinesDirector[i] * cdelta); - } - return o2::gpu::CAMath::Sqrt(DCASquared); -} - -GPUhdi() float Line::getDistanceFromPoint(const Line& line, const float point[3]) -{ - const float dx = point[0] - line.originPoint[0]; - const float dy = point[1] - line.originPoint[1]; - const float dz = point[2] - line.originPoint[2]; - const float d = (dx * line.cosinesDirector[0]) + (dy * line.cosinesDirector[1]) + (dz * line.cosinesDirector[2]); - - const float vx = dx - (d * line.cosinesDirector[0]); - const float vy = dy - (d * line.cosinesDirector[1]); - const float vz = dz - (d * line.cosinesDirector[2]); - - return o2::gpu::CAMath::Hypot(vx, vy, vz); -} - -GPUhdi() float Line::getDCA(const Line& firstLine, const Line& secondLine, const float precision) -{ - const float nx = (firstLine.cosinesDirector[1] * secondLine.cosinesDirector[2]) - - (firstLine.cosinesDirector[2] * secondLine.cosinesDirector[1]); - const float ny = -(firstLine.cosinesDirector[0] * secondLine.cosinesDirector[2]) + - (firstLine.cosinesDirector[2] * secondLine.cosinesDirector[0]); - const float nz = (firstLine.cosinesDirector[0] * secondLine.cosinesDirector[1]) - - (firstLine.cosinesDirector[1] * secondLine.cosinesDirector[0]); - const float norm2 = (nx * nx) + (ny * ny) + (nz * nz); - - if (norm2 <= precision * precision) { - return getDistanceFromPoint(firstLine, secondLine.originPoint); - } - - const float dx = secondLine.originPoint[0] - firstLine.originPoint[0]; - const float dy = secondLine.originPoint[1] - firstLine.originPoint[1]; - const float dz = secondLine.originPoint[2] - firstLine.originPoint[2]; - const float triple = (dx * nx) + (dy * ny) + (dz * nz); - - return o2::gpu::CAMath::Abs(triple) / o2::gpu::CAMath::Sqrt(norm2); -} - -GPUhdi() void Line::getDCAComponents(const Line& line, const float point[3], float destArray[6]) -{ - float cdelta{0.}; - for (int i{0}; i < 3; ++i) { - cdelta -= line.cosinesDirector[i] * (line.originPoint[i] - point[i]); - } - - destArray[0] = line.originPoint[0] - point[0] + line.cosinesDirector[0] * cdelta; - destArray[3] = line.originPoint[1] - point[1] + line.cosinesDirector[1] * cdelta; - destArray[5] = line.originPoint[2] - point[2] + line.cosinesDirector[2] * cdelta; - destArray[1] = o2::gpu::CAMath::Sqrt(destArray[0] * destArray[0] + destArray[3] * destArray[3]); - destArray[2] = o2::gpu::CAMath::Sqrt(destArray[0] * destArray[0] + destArray[5] * destArray[5]); - destArray[4] = o2::gpu::CAMath::Sqrt(destArray[3] * destArray[3] + destArray[5] * destArray[5]); -} - -inline bool Line::operator==(const Line& rhs) const -{ - bool val{false}; - for (int i{0}; i < 3; ++i) { - val &= this->originPoint[i] == rhs.originPoint[i]; - } - return val; -} - -inline bool Line::operator!=(const Line& rhs) const -{ - return !(*this == rhs); -} - -GPUhdi() void Line::print() const -{ - printf("Line: originPoint = (%f, %f, %f), cosinesDirector = (%f, %f, %f), rofs = (%hd, %hd)\n", - originPoint[0], originPoint[1], originPoint[2], cosinesDirector[0], cosinesDirector[1], cosinesDirector[2], rof[0], rof[1]); -} - 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, - const bool weight = false); - ClusterLines(const Line& firstLine, const Line& secondLine); - void add(const int& lineLabel, const Line& line, const bool& weight = false); + ClusterLines(const int firstLabel, const Line& firstLine, const int secondLabel, const Line& secondLine); + void add(const int lineLabel, const Line& line); void computeClusterCentroid(); - void updateROFPoll(const Line&); - inline std::vector& getLabels() - { - return mLabels; - } - inline int getSize() const { return mLabels.size(); } - inline short getROF() const { return mROF; } - inline std::array getVertex() const { return mVertex; } - inline std::array getRMS2() const { return mRMS2; } - inline float getAvgDistance2() const { return mAvgDistance2; } - - bool operator==(const ClusterLines&) const; + 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() 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: - std::array mAMatrix; // AX=B - std::array mBMatrix; // AX=B - std::vector mLabels; // labels - std::array mWeightMatrix = {0.f}; // weight matrix - std::array mVertex = {0.f}; // cluster centroid position - std::array mRMS2 = {0.f}; // symmetric matrix: diagonal is RMS2 - float mAvgDistance2 = 0.f; // substitute for chi2 - int mROFWeight = 0; // rof weight for voting - short mROF = constants::UnusedIndex; // rof + 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::its diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h index 10e1681c73e8d..02dbeb8cf3992 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h @@ -16,9 +16,11 @@ #ifndef TRACKINGITSU_INCLUDE_CONFIGURATION_H_ #define TRACKINGITSU_INCLUDE_CONFIGURATION_H_ +#include #ifndef GPUCA_GPUCODE_DEVICE #include #include +#include #include #include #endif @@ -37,7 +39,7 @@ struct TrackingParameters { std::string asString() const; int NLayers = 7; - int DeltaROF = 0; + 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 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}; @@ -46,9 +48,9 @@ struct TrackingParameters { std::vector SystErrorZ2 = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; int ZBins{256}; int PhiBins{128}; - int nROFsPerIterations = -1; 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 bool AllowSharingFirstCluster = false; @@ -58,10 +60,8 @@ struct TrackingParameters { float PVres = 1.e-2f; /// Trackleting cuts float TrackletMinPt = 0.3f; - float TrackletsPerClusterLimit = 2.f; /// Cell finding cuts float CellDeltaTanLambdaSigma = 0.007f; - float CellsPerClusterLimit = 2.f; /// Fitter parameters o2::base::PropagatorImpl::MatCorrType CorrType = o2::base::PropagatorImpl::MatCorrType::USEMatCorrNONE; float MaxChi2ClusterAttachment = 60.f; @@ -71,18 +71,10 @@ struct TrackingParameters { uint16_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 FindShortTracks = false; bool PerPrimaryVertexProcessing = false; bool SaveTimeBenchmarks = false; bool DoUPCIteration = false; bool FataliseUponFailure = true; - /// Cluster attachment - bool UseTrackFollower = false; - bool UseTrackFollowerTop = false; - bool UseTrackFollowerBot = false; - bool UseTrackFollowerMix = false; - float TrackFollowerNSigmaCutZ = 1.f; - float TrackFollowerNSigmaCutPhi = 1.f; bool createArtefactLabels{false}; @@ -94,14 +86,11 @@ struct TrackingParameters { struct VertexingParameters { std::string asString() const; - int nIterations = 1; // Number of vertexing passes to perform - int vertPerRofThreshold = 0; // Maximum number of vertices per ROF to trigger second a round - bool allowSingleContribClusters = false; + int nIterations = 1; // Number of vertexing passes to perform 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 ZBins{1}; int PhiBins{128}; - int deltaRof = 0; float zCut = 0.002f; float phiCut = 0.005f; float pairCut = 0.04f; @@ -120,7 +109,6 @@ struct VertexingParameters { bool SaveTimeBenchmarks = false; bool useTruthSeeding = false; // overwrite found vertices with MC events - bool outputContLabels = false; int nThreads = 1; bool PrintMemory = false; // print allocator usage in epilog report @@ -128,26 +116,6 @@ struct VertexingParameters { bool DropTFUponFailure = false; }; -struct TimeFrameGPUParameters { - std::string asString() const; - - size_t tmpCUBBufferSize = 1e5; // In average in pp events there are required 4096 bytes - size_t maxTrackletsPerCluster = 1e2; - size_t clustersPerLayerCapacity = 2.5e5; - size_t clustersPerROfCapacity = 1.5e3; - size_t validatedTrackletsCapacity = 1e3; - size_t cellsLUTsize = validatedTrackletsCapacity; - size_t maxNeighboursSize = 1e2; - size_t neighboursLUTsize = maxNeighboursSize; - size_t maxRoadPerRofSize = 1e3; // pp! - size_t maxLinesCapacity = 1e2; - size_t maxVerticesCapacity = 5e4; - size_t nMaxROFs = 1e3; - size_t nTimeFrameChunks = 3; - size_t nROFsPerChunk = 768; // pp defaults - int maxGPUMemoryGB = -1; -}; - namespace TrackingMode { enum Type : int8_t { diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h index 22642f2e23229..4b2528b62f057 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h @@ -19,7 +19,7 @@ #include #include -#include "ITStracking/Definitions.h" +#include "GPUCommonDef.h" #include "GPUCommonDefAPI.h" namespace o2::its::constants diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Definitions.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Definitions.h index c3be0de2dade7..8dadf826aa80a 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Definitions.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Definitions.h @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// 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. // @@ -16,16 +16,7 @@ #define TRACKINGITS_DEFINITIONS_H_ #include - -#include "ReconstructionDataFormats/Vertex.h" - -#ifdef CA_DEBUG -#define CA_DEBUGGER(x) x -#else -#define CA_DEBUGGER(x) \ - do { \ - } while (0) -#endif +#include namespace o2::its { @@ -35,11 +26,30 @@ enum class TrackletMode { Layer1Layer2 = 2 }; -using Vertex = o2::dataformats::Vertex>; - template using maybe_const = typename std::conditional::type; +// simple implemnetion of logging with exp. backoff +struct LogLogThrottler { + uint64_t evCount{0}; + uint64_t nextLog{1}; + int32_t iteration{-1}; + int32_t layer{-1}; + bool needToLog(int32_t iter, int32_t lay) + { + if (iteration != iter || layer != lay) { + iteration = iter; + layer = lay; + evCount = 0; + nextLog = 1; + } + if (++evCount > nextLog) { + nextLog *= 2; + return true; + } + return false; + } +}; } // namespace o2::its -#endif +#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/FastMultEst.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/FastMultEst.h new file mode 100644 index 0000000000000..3083a8fe9c2ec --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/FastMultEst.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. + +/// \file FastMultEst.h +/// \brief Fast multiplicity estimator for ITS +/// \author ruben.shahoyan@cern.ch + +#ifndef ALICEO2_ITS_FASTMULTEST_ +#define ALICEO2_ITS_FASTMULTEST_ + +#include "ITSMFTReconstruction/ChipMappingITS.h" +#include "DataFormatsITS/Vertex.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITSMFT/PhysTrigger.h" +#include "ITStracking/FastMultEstConfig.h" +#include "ITStracking/ROFLookupTables.h" +#include +#include + +namespace o2::its +{ + +struct FastMultEst { + + static constexpr int NLayers = o2::itsmft::ChipMappingITS::NLayers; + using ROFOverlapTableN = ROFOverlapTable; + using ROFMaskTableN = ROFMaskTable; + + float mult = 0.; /// estimated signal clusters multiplicity on the selected multiplicity layer + float noisePerChip = 0.; /// imposed noise per chip (when enabled by configuration) + float cov[3] = {0.}; /// retained for compatibility; set to zero in single-layer mode + float chi2 = 0.; /// retained for compatibility; set to zero in single-layer mode + int nLayersUsed = 0; /// number of layers used by estimator (0/1 in single-layer mode) + uint32_t lastRandomSeed = 0; /// state of the gRandom before + FastMultEst(); + + static uint32_t getCurrentRandomSeed(); + int selectROFs(const std::array, NLayers>& rofs, + const std::array, NLayers>& clus, + const gsl::span trig, + uint32_t firstTForbit, + bool doStaggering, + const ROFOverlapTableN::View& overlapView, + ROFMaskTableN& sel); + void selectROFsWithVertices(const auto& vertices, const ROFOverlapTableN::View& overlapView, ROFMaskTableN& sel) const + { + const auto& multEstConf = FastMultEstConfig::Instance(); + if (!multEstConf.isVtxMultCutRequested()) { + return; + } + + for (const auto& vertex : vertices) { + if (!multEstConf.isPassingVtxMultCut(vertex.getNContributors())) { + const auto& timestamp{vertex.getTimeStamp()}; + for (int layer = 0; layer < NLayers; ++layer) { + uint32_t startROF = sel.getLayer(layer).getROF(timestamp.lower()); + uint32_t endROF = sel.getLayer(layer).getROF(timestamp.upper()); + for (uint32_t rof = startROF; rof <= endROF; ++rof) { + sel.setROFsEnabled(layer, rof, 0); + } + } + } + } + } + + int countClustersOnLayer(const gsl::span& clusters) const; + float process(int nClusters) + { + return FastMultEstConfig::Instance().imposeNoisePerChip > 0 ? processNoiseImposed(nClusters) : processNoiseFree(nClusters); + } + float processNoiseFree(int nClusters); + float processNoiseImposed(int nClusters); + float process(const gsl::span& clusters) + { + return process(countClustersOnLayer(clusters)); + } + static bool sSeedSet; + + ClassDefNV(FastMultEst, 1); +}; + +} // namespace o2::its + +#endif diff --git a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEstConfig.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/FastMultEstConfig.h similarity index 58% rename from Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEstConfig.h rename to Detectors/ITSMFT/ITS/tracking/include/ITStracking/FastMultEstConfig.h index c6bce50995a4b..1ab9796aa8cf6 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEstConfig.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/FastMultEstConfig.h @@ -20,9 +20,7 @@ #include "CommonUtils/ConfigurableParamHelper.h" #include "ITSMFTReconstruction/ChipMappingITS.h" -namespace o2 -{ -namespace its +namespace o2::its { struct FastMultEstConfig : public o2::conf::ConfigurableParamHelper { static constexpr int NLayers = o2::itsmft::ChipMappingITS::NLayers; @@ -34,16 +32,19 @@ struct FastMultEstConfig : public o2::conf::ConfigurableParamHelper0 : set as is, <0 : use current time - bool preferTriggered = true; /// prefer ROFs with highest number of physics triggers - - bool isMultCutRequested() const { return cutMultClusLow >= 0.f && cutMultClusHigh > 0.f; }; - bool isVtxMultCutRequested() const { return cutMultVtxLow >= 0.f && cutMultVtxHigh > 0.f; }; + int cutMultClusLayer = NLayers - 1; /// layer used for cluster multiplicity selection (by default the outermost one) + float cutMultClusLow = 0; /// reject ROF with estimated cluster mult. below this value (no cut if <0) + float cutMultClusHigh = -1; /// reject ROF with estimated cluster mult. above this value (no cut if <0) + float cutMultVtxLow = -1; /// reject seed vertex if its multiplicity below this value (no cut if <0) + float cutMultVtxHigh = -1; /// reject seed vertex if its multiplicity above this value (no cut if <0) + float cutRandomFraction = -1.; /// apply random cut rejecting requested fraction + int randomSeed = 0; /// 0 - do not seet seed, >0 : set as is, <0 : use current time + bool preferTriggered = true; /// prefer ROFs with highest number of physics triggers + + bool isMultCutRequested() const noexcept { return cutMultClusLow >= 0.f && cutMultClusHigh > 0.f; }; + bool isVtxMultCutRequested() const noexcept { return cutMultVtxLow >= 0.f && cutMultVtxHigh > 0.f; }; + bool isRandCutRequested() const noexcept { return cutRandomFraction >= 0.; } + bool isRequested() const noexcept { return isMultCutRequested() || isVtxMultCutRequested() || isRandCutRequested(); } bool isPassingRandomRejection() const; bool isPassingMultCut(float mult) const { return mult >= cutMultClusLow && (mult <= cutMultClusHigh || cutMultClusHigh <= 0.f); } bool isPassingVtxMultCut(int mult) const { return mult >= cutMultVtxLow && (mult <= cutMultVtxHigh || cutMultVtxHigh <= 0.f); } @@ -51,7 +52,6 @@ struct FastMultEstConfig : public o2::conf::ConfigurableParamHelper z2) ? -1.f : 1.f) * o2::constants::math::VeryBig; @@ -91,11 +89,16 @@ GPUhdi() float smallestAngleDifference(float a, float b) return o2::gpu::CAMath::Remainderf(b - a, o2::constants::math::TwoPI); } -GPUhdi() float Sq(float v) +GPUhdi() constexpr float Sq(float v) { return v * v; } +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); diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ROFLookupTables.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ROFLookupTables.h new file mode 100644 index 0000000000000..ce20169e36c64 --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ROFLookupTables.h @@ -0,0 +1,850 @@ +// 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 TRACKINGITSU_INCLUDE_ROFOVERLAPTABLE_H_ +#define TRACKINGITSU_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 "DataFormatsITS/TimeEstBC.h" +#include "DataFormatsITS/Vertex.h" +#include "GPUCommonMath.h" +#include "GPUCommonDef.h" + +namespace o2::its +{ + +// 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::its + +#endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Road.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Road.h deleted file mode 100644 index 009f3a1b5b146..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Road.h +++ /dev/null @@ -1,72 +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 Road.h -/// \brief -/// - -#ifndef TRACKINGCA_INCLUDE_ROAD_H -#define TRACKINGCA_INCLUDE_ROAD_H - -#include - -#include "ITStracking/Constants.h" -#include "GPUCommonDef.h" - -namespace o2::its -{ - -template -class Road final -{ - public: - GPUhdDefault() Road() = default; - GPUhd() Road(int cellLayer, int cellId) : Road() { addCell(cellLayer, cellId); } - - GPUhdDefault() Road(const Road&) = default; - GPUhdDefault() Road(Road&&) noexcept = default; - GPUhdDefault() ~Road() = default; - - GPUhdDefault() Road& operator=(const Road&) = default; - GPUhdDefault() Road& operator=(Road&&) noexcept = default; - - GPUhdi() uint8_t getRoadSize() const { return mRoadSize; } - GPUhdi() bool isFakeRoad() const { return mIsFakeRoad; } - GPUhdi() void setFakeRoad(const bool fake) { mIsFakeRoad = fake; } - GPUhdi() int& operator[](const int& i) { return mCellIds[i]; } - GPUhdi() int operator[](const int& i) const { return mCellIds[i]; } - - GPUhd() void resetRoad() - { - for (int i = 0; i < maxRoadSize; i++) { - mCellIds[i] = constants::UnusedIndex; - } - mRoadSize = 0; - } - - GPUhd() void addCell(int cellLayer, int cellId) - { - if (mCellIds[cellLayer] == constants::UnusedIndex) { - ++mRoadSize; - } - - mCellIds[cellLayer] = cellId; - } - - private: - std::array mCellIds = constants::helpers::initArray(); - unsigned char mRoadSize{0}; - bool mIsFakeRoad{false}; -}; - -} // namespace o2::its - -#endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Smoother.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Smoother.h deleted file mode 100644 index 101f4b8d72601..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Smoother.h +++ /dev/null @@ -1,60 +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 Smoother.h -/// \brief Class to handle Kalman smoothing for ITS tracking. -/// Its instance stores the state of the track to the level we want to smooth to avoid multiple re-propagations when testing different clusters. -/// - -#include "ReconstructionDataFormats/Track.h" -#include "DataFormatsITS/TrackITS.h" -#include "DetectorsBase/Propagator.h" - -namespace o2 -{ -namespace its -{ - -template -class Smoother -{ - public: - // Smoother(TrackITSExt& track, size_t layer, const ROframe& event, float bZ, o2::base::PropagatorF::MatCorrType corr); - ~Smoother(); - - bool isValidInit() const - { - return mInitStatus; - } - // bool testCluster(const int clusterId, const ROframe& event); - bool getSmoothedTrack(); - float getChi2() const { return mBestChi2; } - float getLastChi2() const { return mLastChi2; } - - private: - float computeSmoothedPredictedChi2(const o2::track::TrackParCov& outwTrack, - const o2::track::TrackParCov& inwTrack, - const std::array& cls, - const std::array& clCov); - bool smoothTrack(); - - private: - size_t mLayerToSmooth; // Layer to compute smoothing optimization - float mBz; // Magnetic field along Z - bool mInitStatus; // State after the initialization - o2::base::PropagatorF::MatCorrType mCorr; // Type of correction to use - TrackITSExt mInwardsTrack; // outwards track: from innermost cluster to outermost - TrackITSExt mOutwardsTrack; // inwards track: from outermost cluster to innermost - float mBestChi2; // Best value of local smoothed chi2 - float mLastChi2 = 1e8; // Latest computed chi2 -}; -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h index acc884ea68b8b..3dd1b05cf8969 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h @@ -21,6 +21,7 @@ #include #include "DataFormatsITS/TrackITS.h" +#include "DataFormatsITS/Vertex.h" #include "ITStracking/Cell.h" #include "ITStracking/Cluster.h" @@ -28,15 +29,14 @@ #include "ITStracking/Constants.h" #include "ITStracking/ClusterLines.h" #include "ITStracking/Definitions.h" -#include "ITStracking/Road.h" #include "ITStracking/Tracklet.h" #include "ITStracking/IndexTableUtils.h" #include "ITStracking/ExternalAllocator.h" #include "ITStracking/BoundedAllocator.h" +#include "ITStracking/ROFLookupTables.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" -#include "ReconstructionDataFormats/Vertex.h" #include "DetectorsBase/Propagator.h" namespace o2 @@ -62,48 +62,42 @@ template class TimeFrameGPU; } -template +template struct TimeFrame { - using IndexTableUtilsN = IndexTableUtils; - using CellSeedN = CellSeed; - friend class gpu::TimeFrameGPU; + using IndexTableUtilsN = IndexTableUtils; + using ROFOverlapTableN = ROFOverlapTable; + using ROFVertexLookupTableN = ROFVertexLookupTable; + using ROFMaskTableN = ROFMaskTable; + using CellSeedN = CellSeed; + friend class gpu::TimeFrameGPU; TimeFrame() = default; virtual ~TimeFrame() = default; const Vertex& getPrimaryVertex(const int ivtx) const { return mPrimaryVertices[ivtx]; } - gsl::span getPrimaryVertices(int rofId) const; - gsl::span getPrimaryVertices(int romin, int romax) const; - gsl::span> getPrimaryVerticesMCRecInfo(const int rofId) const; - gsl::span getPrimaryVerticesContributors(const int rofId) const; - gsl::span> getPrimaryVerticesXAlpha(int rofId) const; - void fillPrimaryVerticesXandAlpha(); - int getPrimaryVerticesNum(int rofId = -1) const; - void addPrimaryVerticesLabels(bounded_vector>& labels); - void addPrimaryVerticesContributorLabels(bounded_vector& labels); - void addPrimaryVertices(const bounded_vector& vertices, const int iteration); - void addPrimaryVerticesInROF(const bounded_vector& vertices, const int rofId, const int iteration); - void addPrimaryVerticesLabelsInROF(const bounded_vector>& labels, const int rofId); - void addPrimaryVerticesContributorLabelsInROF(const bounded_vector& labels, const int rofId); - void removePrimaryVerticesInROf(const int rofId); - int loadROFrameData(const o2::itsmft::ROFRecord& rof, gsl::span clusters, - const dataformats::MCTruthContainer* mcLabels = nullptr); - - int loadROFrameData(gsl::span rofs, - gsl::span clusters, - gsl::span::iterator& pattIt, - const itsmft::TopologyDictionary* dict, - const dataformats::MCTruthContainer* mcLabels = nullptr); - void resetROFrameData(size_t nROFs); - void prepareROFrameData(gsl::span rofs, - gsl::span clusters); + 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); + void resetROFrameData(int iLayer); + void prepareROFrameData(gsl::span clusters, int layer); int getTotalClusters() const; - auto& getTotVertIteration() { return mTotVertPerIteration; } 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() const { return mNrof; } + 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) @@ -114,6 +108,8 @@ struct TimeFrame { 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]; } @@ -134,29 +130,56 @@ struct TimeFrame { 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 getIndexTablePerROFrange(int rofMin, int range, int layerId) const; gsl::span getIndexTable(int rofId, int layerId); - auto& getIndexTableWhole(int layerId) { return mIndexTables[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; } + 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->getLabels(mClusterExternalIndices[layerId][clId]); } + 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 clusterId) const { return mClusterSize[clusterId]; } - void setClusterSize(bounded_vector& v) { mClusterSize = std::move(v); } + 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; } - void initialise(const int iteration, const TrackingParameters& trkParam, const int maxLayers = 7, bool resetVertices = true); - void resetRofPV() - { - deepVectorClear(mPrimaryVertices); - mROFramesPV.resize(1, 0); - mTotVertPerIteration.resize(1); - } + bool hasMCinformation() const { return mClusterLabels[0] != nullptr; } + void initialise(const int iteration, const TrackingParameters& trkParam, const int maxLayers = NLayers, bool resetVertices = true); bool isClusterUsed(int layer, int clusterId) const { return mUsedClusters[layer][clusterId]; } void markUsedCluster(int layer, int clusterId) { mUsedClusters[layer][clusterId] = true; } @@ -173,20 +196,16 @@ struct TimeFrame { auto& getCellsLookupTable() { return mCellsLookupTable; } auto& getCellsNeighbours() { return mCellsNeighbours; } auto& getCellsNeighboursLUT() { return mCellsNeighboursLUT; } - auto& getRoads() { return mRoads; } - auto& getTracks(int rofId) { return mTracks[rofId]; } - auto& getTracksLabel(const int rofId) { return mTracksLabel[rofId]; } + auto& getTracks() { return mTracks; } + auto& getTracksLabel() { return mTracksLabel; } auto& getLinesLabel(const int rofId) { return mLinesLabels[rofId]; } - auto& getVerticesMCRecInfo() { return mVerticesMCRecInfo; } - int getNumberOfClusters() const; - virtual int getNumberOfCells() const; - virtual int getNumberOfTracklets() const; - virtual int getNumberOfNeighbours() const; + 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; - auto getNumberOfExtendedTracks() const { return mNExtendedTracks; } - auto getNumberOfUsedExtendedClusters() const { return mNExtendedUsedClusters; } /// memory management void setMemoryPool(std::shared_ptr pool); @@ -195,10 +214,8 @@ struct TimeFrame { unsigned long getArtefactsMemory() const; void printArtefactsMemory() const; - /// ROF cuts - int getROFCutClusterMult() const { return mCutClusterMult; }; - int getROFCutVertexMult() const { return mCutVertexMult; }; - int getROFCutAllMult() const { return mCutClusterMult + mCutVertexMult; } + /// staggering + void setIsStaggered(bool b) noexcept { mIsStaggered = b; } // Vertexer void computeTrackletsPerROFScans(); @@ -215,20 +232,8 @@ struct TimeFrame { 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; - std::array& getBeamXY() { return mBeamPos; } - unsigned int& getNoVertexROF() { return mNoVertexROF; } - void insertPastVertex(const Vertex& vertex, const int refROFId); // \Vertexer - void initialiseRoadLabels(); - void setRoadLabel(int i, const unsigned long long& lab, bool fake); - const unsigned long long& getRoadLabel(int i) const { return mRoadLabels[i].first; } - bool isRoadFake(int i) const { return mRoadLabels[i].second; } - - void setMultiplicityCutMask(const std::vector& cutMask) { mMultiplicityCutMask = cutMask; } - void setROFMask(const std::vector& rofMask) { mROFMask = rofMask; } - void swapMasks() { mMultiplicityCutMask.swap(mROFMask); } - int hasBogusClusters() const { return std::accumulate(mBogusClusters.begin(), mBogusClusters.end(), 0); } void setBz(float bz) { mBz = bz; } @@ -252,44 +257,25 @@ struct TimeFrame { void addTrackingFrameInfoToLayer(int layer, T&&... args); void addClusterExternalIndexToLayer(int layer, const int idx) { mClusterExternalIndices[layer].push_back(idx); } - /// Debug and printing - void checkTrackletLUTs(); - void printROFoffsets(); - void printNClsPerROF(); - void printVertices(); - void printTrackletLUTonLayer(int i); - void printCellLUTonLayer(int i); - void printTrackletLUTs(); - void printCellLUTs(); - void printSliceInfo(const int, const int); - - IndexTableUtilsN mIndexTableUtils; - - std::array, nLayers> mClusters; - std::array, nLayers> mTrackingFrameInfo; - std::array, nLayers> mClusterExternalIndices; - std::array, nLayers> mROFramesClusters; - const dataformats::MCTruthContainer* mClusterLabels = nullptr; + 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::array, NLayers> mNClustersPerROF; + std::array, NLayers> mIndexTables; std::vector> mTrackletsLookupTable; - std::array, nLayers> mUsedClusters; - int mNrof = 0; - int mNExtendedTracks{0}; - int mNExtendedUsedClusters{0}; - bounded_vector mROFramesPV; - bounded_vector mPrimaryVertices; + std::array, NLayers> mUsedClusters; - std::array, nLayers> mUnsortedClusters; + std::array, NLayers> mUnsortedClusters; std::vector> mTracklets; std::vector> mCells; - bounded_vector> mRoads; - std::vector> mTracks; + bounded_vector mTracks; + bounded_vector mTracksLabel; std::vector> mCellsNeighbours; std::vector> mCellsLookupTable; - std::vector mMultiplicityCutMask; const o2::base::PropagatorImpl* mPropagatorDevice = nullptr; // Needed only for GPU @@ -300,279 +286,210 @@ struct TimeFrame { virtual const char* getName() const noexcept { return "CPU"; } protected: - void prepareClusters(const TrackingParameters& trkParam, const int maxLayers = nLayers); + 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; + std::array mMinR; + std::array mMaxR; bounded_vector mMSangles; bounded_vector mPhiCuts; bounded_vector mPositionResolution; - bounded_vector mClusterSize; + std::array, NLayers> mClusterSize; - std::vector mROFMask; bounded_vector> mPValphaX; /// PV x and alpha for track propagation std::vector> mTrackletLabels; std::vector> mCellLabels; std::vector> mCellsNeighboursLUT; - std::vector> mTracksLabel; bounded_vector mBogusClusters; /// keep track of clusters with wild coordinates - bounded_vector> mRoadLabels; - int mCutClusterMult{-999}; - int mCutVertexMult{-999}; - // Vertexer + bounded_vector mPrimaryVertices; + bounded_vector mPrimaryVerticesLabels; std::vector> mNTrackletsPerROF; std::vector> mLines; std::vector> mTrackletClusters; std::array, 2> mTrackletsIndexROF; std::vector> mLinesLabels; - std::vector> mVerticesMCRecInfo; - bounded_vector mVerticesContributorLabels; std::array mTotalTracklets = {0, 0}; uint32_t mTotalLines = 0; - unsigned int mNoVertexROF = 0; - bounded_vector mTotVertPerIteration; // \Vertexer - std::shared_ptr mMemoryPool; -}; - -template -inline gsl::span TimeFrame::getPrimaryVertices(int rofId) const -{ - if (mPrimaryVertices.empty()) { - return {}; - } - const int start = mROFramesPV[rofId]; - const int stop_idx = rofId >= mNrof - 1 ? mNrof : rofId + 1; - int delta = mMultiplicityCutMask[rofId] ? mROFramesPV[stop_idx] - start : 0; // return empty span if Rof is excluded - return {&mPrimaryVertices[start], static_cast::size_type>(delta)}; -} + // lookup tables + IndexTableUtilsN mIndexTableUtils; + ROFOverlapTableN mROFOverlapTable; + ROFOverlapTableN::View mROFOverlapTableView; + ROFVertexLookupTableN mROFVertexLookupTable; + ROFVertexLookupTableN::View mROFVertexLookupTableView; + ROFMaskTableN mMultiplicityCutMask; + ROFMaskTableN mUPCCutMask; + ROFMaskTableN* mROFMask = &mMultiplicityCutMask; + ROFMaskTableN::View mROFMaskView; -template -inline gsl::span> TimeFrame::getPrimaryVerticesMCRecInfo(const int rofId) const -{ - const int start = mROFramesPV[rofId]; - const int stop_idx = rofId >= mNrof - 1 ? mNrof : rofId + 1; - int delta = mMultiplicityCutMask[rofId] ? mROFramesPV[stop_idx] - start : 0; // return empty span if Rof is excluded - return {&(mVerticesMCRecInfo[start]), static_cast>::size_type>(delta)}; -} + bool mIsStaggered{false}; -template -inline gsl::span TimeFrame::getPrimaryVerticesContributors(const int rofId) const -{ - // count the number of cont. in rofs before target rof - unsigned int start{0}, delta{0}; - const auto& pvsBefore = getPrimaryVertices(0, rofId - 1); - for (const auto& pv : pvsBefore) { - start += pv.getNContributors(); - } - const auto& pvsIn = getPrimaryVertices(rofId); - for (const auto& pv : pvsIn) { - delta += pv.getNContributors(); - } - return {&(mVerticesContributorLabels[start]), static_cast::size_type>(delta)}; -} + std::shared_ptr mMemoryPool; +}; -template -inline gsl::span TimeFrame::getPrimaryVertices(int romin, int romax) const +template +gsl::span TimeFrame::getPrimaryVertices(int layer, int rofId) const { - if (mPrimaryVertices.empty()) { + if (rofId < 0 || rofId >= getNrof(layer)) { return {}; } - const int stop_idx = romax >= mNrof - 1 ? mNrof : romax + 1; - return {&mPrimaryVertices[mROFramesPV[romin]], static_cast::size_type>(mROFramesPV[stop_idx] - mROFramesPV[romin])}; -} - -template -inline gsl::span> TimeFrame::getPrimaryVerticesXAlpha(int rofId) const -{ - const int start = mROFramesPV[rofId]; - const int stop_idx = rofId >= mNrof - 1 ? mNrof : rofId + 1; - int delta = mMultiplicityCutMask[rofId] ? mROFramesPV[stop_idx] - start : 0; // return empty span if Rof is excluded - return {&(mPValphaX[start]), static_cast>::size_type>(delta)}; -} - -template -inline int TimeFrame::getPrimaryVerticesNum(int rofId) const -{ - return rofId < 0 ? mPrimaryVertices.size() : mROFramesPV[rofId + 1] - mROFramesPV[rofId]; + 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) +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 +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) +template +inline gsl::span TimeFrame::getClustersOnLayer(int rofId, int layerId) { - if (rofId < 0 || rofId >= mNrof) { + 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 +template +inline gsl::span TimeFrame::getClustersOnLayer(int rofId, int layerId) const { - if (rofId < 0 || rofId >= mNrof) { + 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) +template +inline gsl::span TimeFrame::getUsedClustersROF(int rofId, int layerId) { - if (rofId < 0 || rofId >= mNrof) { + 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 +template +inline gsl::span TimeFrame::getUsedClustersROF(int rofId, int layerId) const { - if (rofId < 0 || rofId >= mNrof) { + 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 +template +inline gsl::span TimeFrame::getClustersPerROFrange(int rofMin, int range, int layerId) const { - if (rofMin < 0 || rofMin >= mNrof) { + 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, mNrof)]}; + 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 +template +inline gsl::span TimeFrame::getROFramesClustersPerROFrange(int rofMin, int range, int layerId) const { - int chkdRange{o2::gpu::CAMath::Min(range, mNrof - rofMin)}; + 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 +template +inline gsl::span TimeFrame::getNClustersROFrange(int rofMin, int range, int layerId) const { - int chkdRange{o2::gpu::CAMath::Min(range, mNrof - rofMin)}; + 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 +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, mNrof)}; + int endIdx{o2::gpu::CAMath::Min(rofMin + range, getNrof(layerId))}; return mROFramesClusters[layerId][endIdx] - mROFramesClusters[layerId][startIdx]; } -template -inline gsl::span TimeFrame::getIndexTablePerROFrange(int rofMin, int range, int layerId) const -{ - const int iTableSize{mIndexTableUtils.getNphiBins() * mIndexTableUtils.getNzBins() + 1}; - int chkdRange{o2::gpu::CAMath::Min(range, mNrof - rofMin)}; - return {&mIndexTables[layerId][rofMin * iTableSize], static_cast::size_type>(chkdRange * iTableSize)}; -} - -template -inline int TimeFrame::getClusterROF(int iLayer, int iCluster) +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 +template +inline gsl::span TimeFrame::getUnsortedClustersOnLayer(int rofId, int layerId) const { - if (rofId < 0 || rofId >= mNrof) { + 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) +template +inline gsl::span TimeFrame::getIndexTable(int rofId, int layer) { - if (rofId < 0 || rofId >= mNrof) { + if (rofId < 0 || rofId >= getNrof(layer)) { return {}; } const int tableSize = mIndexTableUtils.getNphiBins() * mIndexTableUtils.getNzBins() + 1; return {&mIndexTables[layer][rofId * tableSize], static_cast::size_type>(tableSize)}; } -template +template template -void TimeFrame::addClusterToLayer(int layer, T&&... values) +void TimeFrame::addClusterToLayer(int layer, T&&... values) { mUnsortedClusters[layer].emplace_back(std::forward(values)...); } -template +template template -void TimeFrame::addTrackingFrameInfoToLayer(int layer, T&&... values) +void TimeFrame::addTrackingFrameInfoToLayer(int layer, T&&... values) { mTrackingFrameInfo[layer].emplace_back(std::forward(values)...); } -template -inline gsl::span TimeFrame::getUsedClusters(const int layer) +template +inline gsl::span TimeFrame::getUsedClusters(const int layer) { return {&mUsedClusters[layer][0], static_cast::size_type>(mUsedClusters[layer].size())}; } -template -inline void TimeFrame::initialiseRoadLabels() +template +inline gsl::span TimeFrame::getNTrackletsCluster(int rofId, int combId) { - mRoadLabels.clear(); - mRoadLabels.resize(mRoads.size()); -} - -template -inline void TimeFrame::setRoadLabel(int i, const unsigned long long& lab, bool fake) -{ - mRoadLabels[i].first = lab; - mRoadLabels[i].second = fake; -} - -template -inline gsl::span TimeFrame::getNTrackletsCluster(int rofId, int combId) -{ - if (rofId < 0 || rofId >= mNrof) { + 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) +template +inline gsl::span TimeFrame::getExclusiveNTrackletsCluster(int rofId, int combId) { - if (rofId < 0 || rofId >= mNrof) { + if (rofId < 0 || rofId >= getNrof(1)) { return {}; } auto clusStartIdx{mROFramesClusters[1][rofId]}; @@ -580,38 +497,38 @@ inline gsl::span TimeFrame::getExclusiveNTrackletsCluster(int rofI return {&mNTrackletsPerClusterSum[combId][clusStartIdx], static_cast::size_type>(mROFramesClusters[1][rofId + 1] - clusStartIdx)}; } -template -inline gsl::span TimeFrame::getFoundTracklets(int rofId, int combId) +template +inline gsl::span TimeFrame::getFoundTracklets(int rofId, int combId) { - if (rofId < 0 || rofId >= mNrof || mTracklets[combId].empty()) { + 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 +template +inline gsl::span TimeFrame::getFoundTracklets(int rofId, int combId) const { - if (rofId < 0 || rofId >= mNrof) { + 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 +template +inline gsl::span TimeFrame::getLabelsFoundTracklets(int rofId, int combId) const { - if (rofId < 0 || rofId >= mNrof || !hasMCinformation()) { + 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 +template +inline int TimeFrame::getTotalClusters() const { size_t totalClusters{0}; for (const auto& clusters : mUnsortedClusters) { @@ -620,58 +537,54 @@ inline int TimeFrame::getTotalClusters() const return int(totalClusters); } -template -inline int TimeFrame::getNumberOfClusters() const +template +inline size_t TimeFrame::getNumberOfClusters() const { - int nClusters = 0; + size_t nClusters{0}; for (const auto& layer : mClusters) { nClusters += layer.size(); } return nClusters; } -template -inline int TimeFrame::getNumberOfCells() const +template +inline size_t TimeFrame::getNumberOfCells() const { - int nCells = 0; + size_t nCells{0}; for (const auto& layer : mCells) { nCells += layer.size(); } return nCells; } -template -inline int TimeFrame::getNumberOfTracklets() const +template +inline size_t TimeFrame::getNumberOfTracklets() const { - int nTracklets = 0; + size_t nTracklets{0}; for (const auto& layer : mTracklets) { nTracklets += layer.size(); } return nTracklets; } -template -inline int TimeFrame::getNumberOfNeighbours() const +template +inline size_t TimeFrame::getNumberOfNeighbours() const { - int n{0}; + size_t neigh{0}; for (const auto& l : mCellsNeighbours) { - n += l.size(); + neigh += l.size(); } - return n; + return neigh; } -template -inline size_t TimeFrame::getNumberOfTracks() const +template +inline size_t TimeFrame::getNumberOfTracks() const { - int nTracks = 0; - for (const auto& t : mTracks) { - nTracks += t.size(); - } - return nTracks; + return mTracks.size(); } -template -inline size_t TimeFrame::getNumberOfUsedClusters() const +template +inline size_t TimeFrame::getNumberOfUsedClusters() const { size_t nClusters = 0; for (const auto& layer : mUsedClusters) { @@ -680,17 +593,6 @@ inline size_t TimeFrame::getNumberOfUsedClusters() const return nClusters; } -template -inline void TimeFrame::insertPastVertex(const Vertex& vertex, const int iteration) -{ - int rofId = vertex.getTimeStamp().getTimeStamp(); - mPrimaryVertices.insert(mPrimaryVertices.begin() + mROFramesPV[rofId], vertex); - for (int i = rofId + 1; i < mROFramesPV.size(); ++i) { - mROFramesPV[i]++; - } - mTotVertPerIteration[iteration]++; -} - } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h index 3ea382c626fed..a1a0bf7249a21 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h @@ -30,17 +30,10 @@ #include #include "ITStracking/Configuration.h" -#include "CommonConstants/MathConstants.h" -#include "ITStracking/Definitions.h" -#include "ITStracking/MathUtils.h" #include "ITStracking/TimeFrame.h" #include "ITStracking/TrackerTraits.h" -#include "ITStracking/Road.h" #include "ITStracking/BoundedAllocator.h" -#include "DataFormatsITS/TrackITS.h" -#include "SimulationDataFormat/MCCompLabel.h" - namespace o2 { @@ -51,15 +44,15 @@ class GPUChainITS; namespace its { -template +template class Tracker { using LogFunc = std::function; public: - Tracker(TrackerTraits* traits); + Tracker(TrackerTraits* traits); - void adoptTimeFrame(TimeFrame& tf); + void adoptTimeFrame(TimeFrame& tf); void clustersToTracks( const LogFunc& = [](const std::string& s) { std::cout << s << '\n'; }, @@ -69,33 +62,31 @@ class Tracker void setMemoryPool(std::shared_ptr pool) { mMemoryPool = pool; } std::vector& getParameters() { return mTrkParams; } void setBz(float bz) { mTraits->setBz(bz); } - bool isMatLUT() const { return mTraits->isMatLUT(); } + void setTimeSlice(size_t slice) noexcept { mTimeSlice = slice; } void setNThreads(int n, std::shared_ptr& arena) { mTraits->setNThreads(n, arena); } void printSummary() const; void computeTracksMClabels(); private: void initialiseTimeFrame(int iteration) { mTraits->initialiseTimeFrame(iteration); } - void computeTracklets(int iteration, int iROFslice, int iVertex) { mTraits->computeLayerTracklets(iteration, iROFslice, iVertex); } + 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 findShortPrimaries() { mTraits->findShortPrimaries(); } - void extendTracks(int iteration) { mTraits->extendTracks(iteration); } - // MC interaction - void computeRoadsMClabels(); void rectifyClusterIndices(); + void sortTracks(); template float evaluateTask(void (Tracker::*task)(T...), std::string_view taskName, int iteration, LogFunc logger, F&&... args); - TrackerTraits* mTraits = nullptr; /// Observer pointer, not owned by this class - TimeFrame* mTimeFrame = nullptr; /// Observer pointer, not owned by this class + TrackerTraits* mTraits = nullptr; /// Observer pointer, not owned by this class + TimeFrame* mTimeFrame = nullptr; /// Observer pointer, not owned by this class std::vector mTrkParams; o2::gpu::GPUChainITS* mRecoChain = nullptr; + size_t mTimeSlice{0}; // current timeslice unsigned int mNumberOfDroppedTFs{0}; unsigned int mTimeFrameCounter{0}; double mTotalTime{0}; @@ -113,9 +104,9 @@ class Tracker static constexpr std::array StateNames{"TimeFrame initialisation", "Tracklet finding", "Cell finding", "Neighbour finding", "Road finding"}; }; -template +template template -float Tracker::evaluateTask(void (Tracker::*task)(T...), std::string_view taskName, int iteration, LogFunc logger, F&&... args) +float Tracker::evaluateTask(void (Tracker::*task)(T...), std::string_view taskName, int iteration, LogFunc logger, F&&... args) { float diff{0.f}; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h index ddc32ed18cbfe..fd3251a59d835 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h @@ -38,36 +38,28 @@ namespace its { class TrackITSExt; -template +template class TrackerTraits { public: - using IndexTableUtilsN = IndexTableUtils; - using CellSeedN = CellSeed; + using IndexTableUtilsN = IndexTableUtils; + using CellSeedN = CellSeed; virtual ~TrackerTraits() = default; - virtual void adoptTimeFrame(TimeFrame* tf) { mTimeFrame = tf; } - virtual void initialiseTimeFrame(const int iteration) { mTimeFrame->initialise(iteration, mTrkParams[iteration], mTrkParams[iteration].NLayers); } + virtual void adoptTimeFrame(TimeFrame* tf) { mTimeFrame = tf; } + virtual void initialiseTimeFrame(const int iteration) { mTimeFrame->initialise(iteration, mTrkParams[iteration], mTrkParams[iteration].NLayers, false); } - virtual void computeLayerTracklets(const int iteration, int iROFslice, int iVertex); + 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); - - virtual bool supportsExtendTracks() const noexcept { return true; } - virtual void extendTracks(const int iteration); - virtual bool supportsFindShortPrimaries() const noexcept { return true; } - virtual void findShortPrimaries(); - - virtual bool trackFollowing(TrackITSExt* track, int rof, bool outward, const int iteration); virtual void processNeighbours(int iLayer, int iLevel, const bounded_vector& currentCellSeed, const bounded_vector& currentCellId, bounded_vector& updatedCellSeed, bounded_vector& updatedCellId); void updateTrackingParameters(const std::vector& trkPars) { mTrkParams = trkPars; } - TimeFrame* getTimeFrame() { return mTimeFrame; } + TimeFrame* getTimeFrame() { return mTimeFrame; } virtual void setBz(float bz); float getBz() const { return mBz; } - bool isMatLUT() const; virtual const char* getName() const noexcept { return "CPU"; } virtual bool isGPU() const noexcept { return false; } void setMemoryPool(std::shared_ptr pool) noexcept { mMemoryPool = pool; } @@ -75,17 +67,14 @@ class TrackerTraits // Others GPUhd() static consteval int4 getEmptyBinsRect() { return int4{0, 0, 0, 0}; } - const int4 getBinsRect(int layer, float phi, float maxdeltaphi, float z, float maxdeltaz) const noexcept { return getBinsRect(layer, phi, maxdeltaphi, z, z, maxdeltaz); } - const int4 getBinsRect(const Cluster& cls, int layer, float z1, float z2, float maxdeltaz, float maxdeltaphi) const noexcept { return getBinsRect(layer, cls.phi, maxdeltaphi, z1, z2, maxdeltaz); } - const int4 getBinsRect(int layer, float phi, float maxdeltaphi, float z1, float z2, float maxdeltaz) const noexcept; - void SetRecoChain(o2::gpu::GPUChainITS* chain) { mChain = chain; } - void setSmoothing(bool v) { mApplySmoothing = v; } - bool getSmoothing() const { return mApplySmoothing; } + int4 getBinsRect(const int iteration, int layer, float phi, float maxdeltaphi, float z, float maxdeltaz) + const noexcept { return getBinsRect(iteration, layer, phi, maxdeltaphi, z, z, maxdeltaz); } + int4 getBinsRect(const int iteration, const Cluster& cls, int layer, float z1, float z2, float maxdeltaz, float maxdeltaphi) const noexcept { return getBinsRect(iteration, layer, cls.phi, maxdeltaphi, z1, z2, maxdeltaz); } + const int4 getBinsRect(const int iteration, int layer, float phi, float maxdeltaphi, float z1, float z2, float maxdeltaz) const noexcept; + void setNThreads(int n, std::shared_ptr& arena); int getNThreads() { return mTaskArena->max_concurrency(); } - o2::gpu::GPUChainITS* getChain() const { return mChain; } - // TimeFrame information forwarding virtual int getTFNumberOfClusters() const { return mTimeFrame->getNumberOfClusters(); } virtual int getTFNumberOfTracklets() const { return mTimeFrame->getNumberOfTracklets(); } @@ -96,36 +85,35 @@ class TrackerTraits TrackITSExt seedTrackForRefit(const CellSeedN& seed); bool fitTrack(TrackITSExt& track, int start, int end, int step, float chi2clcut = o2::constants::math::VeryBig, float chi2ndfcut = o2::constants::math::VeryBig, float maxQoverPt = o2::constants::math::VeryBig, int nCl = 0, o2::track::TrackPar* refLin = nullptr); - bool mApplySmoothing = false; std::shared_ptr mMemoryPool; std::shared_ptr mTaskArena; protected: o2::gpu::GPUChainITS* mChain = nullptr; - TimeFrame* mTimeFrame; + TimeFrame* mTimeFrame; std::vector mTrkParams; float mBz{-999.f}; bool mIsZeroField{false}; }; -template -inline const int4 TrackerTraits::getBinsRect(const int layerIndex, float phi, float maxdeltaphi, float z1, float z2, float maxdeltaz) const noexcept +template +inline const int4 TrackerTraits::getBinsRect(const int iteration, const int layerIndex, float phi, float maxdeltaphi, float z1, float z2, float maxdeltaz) const noexcept { const float zRangeMin = o2::gpu::GPUCommonMath::Min(z1, z2) - maxdeltaz; const float phiRangeMin = (maxdeltaphi > o2::constants::math::PI) ? 0.f : phi - maxdeltaphi; const float zRangeMax = o2::gpu::GPUCommonMath::Max(z1, z2) + maxdeltaz; const float phiRangeMax = (maxdeltaphi > o2::constants::math::PI) ? o2::constants::math::TwoPI : phi + maxdeltaphi; - if (zRangeMax < -mTrkParams[0].LayerZ[layerIndex] || - zRangeMin > mTrkParams[0].LayerZ[layerIndex] || zRangeMin > zRangeMax) { + if (zRangeMax < -mTrkParams[iteration].LayerZ[layerIndex] || + zRangeMin > mTrkParams[iteration].LayerZ[layerIndex] || zRangeMin > zRangeMax) { return getEmptyBinsRect(); } - const IndexTableUtilsN& utils{mTimeFrame->mIndexTableUtils}; + const IndexTableUtilsN& utils{mTimeFrame->getIndexTableUtils()}; return int4{o2::gpu::GPUCommonMath::Max(0, utils.getZBinIndex(layerIndex, zRangeMin)), utils.getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMin)), - o2::gpu::GPUCommonMath::Min(mTrkParams[0].ZBins - 1, utils.getZBinIndex(layerIndex, zRangeMax)), // /!\ trkParams can potentially change across iterations + o2::gpu::GPUCommonMath::Min(mTrkParams[iteration].ZBins - 1, utils.getZBinIndex(layerIndex, zRangeMax)), utils.getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMax))}; } diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h index 0529bd53f2073..e77200a1432d1 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h @@ -22,10 +22,9 @@ namespace o2::its 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. - bool allowSingleContribClusters = false; // attempt to find vertices in case of a single tracklet found. - int deltaRof = 0; // Number of ROFs to be considered for the vertexing. + int nIterations = 1; // Number of vertexing passes to perform. + int vertPerRofThreshold = 0; // Maximum number of vertices per ROF to trigger second a iteration. + int deltaRof = 0; // Number of ROFs to be considered for the vertexing. // geometrical cuts for tracklet selection float zCut = 0.002f; @@ -48,8 +47,7 @@ struct VertexerParamConfig : public o2::conf::ConfigurableParamHelper0, otherwise use code defaults uint8_t startLayerMask[MaxIter] = {}; // mask of start layer for this iteration (if >0) float minPtIterLgt[MaxIter * (MaxTrackLength - MinTrackLength + 1)] = {}; // min.pT for given track length at this iteration, used only if >0, otherwise use code defaults @@ -80,16 +78,8 @@ struct TrackerParamConfig : public o2::conf::ConfigurableParamHelper 0 off - float trackFollowerNSigmaZ = 1.f; // sigma in z-cut for track-following search rectangle - float trackFollowerNSigmaPhi = 1.f; // sigma in phi-cut for track-following search rectangle - float cellsPerClusterLimit = -1.f; - float trackletsPerClusterLimit = -1.f; - int findShortTracks = -1; - int nROFsPerIterations = 0; // size of the slice of ROFs to be processed at a time, preferably integer divisors of nROFs per TF, to balance the iterations. - int nOrbitsPerIterations = 0; // not implemented: size of the slice of ROFs to be processed at a time, computed using the number of ROFs per orbit. + 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 @@ -99,13 +89,13 @@ struct TrackerParamConfig : public o2::conf::ConfigurableParamHelper::max(); bool dropTFUponFailure = false; - bool fataliseUponFailure = true; // granular management of the fatalisation in async mode + bool fataliseUponFailure = true; // granular management of the fatalisation in async mode bool allowSharingFirstCluster = false; // allow first cluster sharing among tracks O2ParamDef(TrackerParamConfig, "ITSCATrackerParam"); diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h index a882ca9b779c4..ac4b99a0a8cd8 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h @@ -43,9 +43,11 @@ class ITSTrackingInterface public: ITSTrackingInterface(bool isMC, + bool doStag, int trgType, const bool overrBeamEst) : mIsMC{isMC}, + mDoStaggering(doStag), mUseTriggers{trgType}, mOverrideBeamEstimation{overrBeamEst} {} @@ -81,13 +83,16 @@ class ITSTrackingInterface virtual void loadROF(gsl::span& trackROFspan, gsl::span clusters, gsl::span::iterator& pattIt, + int layer, const dataformats::MCTruthContainer* mcLabels); private: bool mIsMC = false; + bool mDoStaggering = false; bool mRunVertexer = true; bool mCosmicsProcessing = false; int mUseTriggers = 0; + std::vector mFilter; TrackingMode::Type mMode = TrackingMode::Unset; bool mOverrideBeamEstimation = false; const o2::itsmft::TopologyDictionary* mDict = nullptr; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracklet.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracklet.h index e6c9db55198a3..d93a5e1c7d70e 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracklet.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracklet.h @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// 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. // @@ -17,6 +17,7 @@ #define TRACKINGITS_INCLUDE_TRACKLET_H_ #include "ITStracking/Constants.h" +#include "DataFormatsITS/TimeEstBC.h" #include "ITStracking/Cluster.h" #include "GPUCommonRtypes.h" #include "GPUCommonMath.h" @@ -35,51 +36,50 @@ namespace o2::its struct Tracklet final { GPUhdDefault() Tracklet() = default; - GPUhdi() Tracklet(const int, const int, const Cluster&, const Cluster&, short rof0, short rof1); - GPUhdi() Tracklet(const int, const int, float tanL, float phi, short rof0, short rof1); + GPUhdi() Tracklet(const int, const int, const Cluster&, const Cluster&, const TimeEstBC& t); + GPUhdi() Tracklet(const int, const int, float tanL, float phi, const TimeEstBC& t); GPUhdDefault() bool operator==(const Tracklet&) const = default; GPUhdi() unsigned char isEmpty() const { return firstClusterIndex < 0 || secondClusterIndex < 0; } - GPUhdi() auto getMinRof() const noexcept { return o2::gpu::CAMath::Min(rof[0], rof[1]); } - GPUhdi() auto getMaxRof() const noexcept { return o2::gpu::CAMath::Max(rof[0], rof[1]); } - GPUhdi() auto getDeltaRof() const { return rof[1] - rof[0]; } - GPUhdi() auto getSpanRof(const Tracklet& o) const noexcept { return o2::gpu::CAMath::Max(getMaxRof(), o.getMaxRof()) - o2::gpu::CAMath::Min(getMinRof(), o.getMinRof()); } + GPUhdi() bool isCompatible(const Tracklet& o) const { return mTime.isCompatible(o.mTime); } GPUhdi() unsigned char operator<(const Tracklet&) const; GPUhd() void print() const { - printf("TRKLT: fClIdx:%d fROF:%d sClIdx:%d sROF:%d (DROF:%d) tgl=%f phi=%f\n", firstClusterIndex, rof[0], secondClusterIndex, rof[1], getDeltaRof(), tanLambda, phi); + 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{-999}; float phi{-999}; - short rof[2] = {constants::UnusedIndex, constants::UnusedIndex}; + TimeEstBC mTime; ClassDefNV(Tracklet, 1); }; GPUhdi() Tracklet::Tracklet(const int firstClusterOrderingIndex, const int secondClusterOrderingIndex, - const Cluster& firstCluster, const Cluster& secondCluster, short rof0 = -1, short rof1 = -1) - : 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)}, - rof{static_cast(rof0), static_cast(rof1)} + 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) { // Nothing to do } -GPUhdi() Tracklet::Tracklet(const int idx0, const int idx1, float tanL, float phi, short rof0, short rof1) - : firstClusterIndex{idx0}, - secondClusterIndex{idx1}, - tanLambda{tanL}, - phi{phi}, - rof{static_cast(rof0), static_cast(rof1)} +GPUhdi() Tracklet::Tracklet(const int idx0, const int idx1, float tanL, float phi, const TimeEstBC& t) + : firstClusterIndex(idx0), + secondClusterIndex(idx1), + tanLambda(tanL), + phi(phi), + mTime(t) { // Nothing to do } diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h index d66bcd6ee2358..77218754dbda3 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h @@ -35,11 +35,11 @@ namespace o2::its { -template +template class Vertexer { - using TimeFrameN = TimeFrame; - using VertexerTraitsN = VertexerTraits; + using TimeFrameN = TimeFrame; + using VertexerTraitsN = VertexerTraits; using LogFunc = std::function; public: @@ -54,9 +54,6 @@ class Vertexer const auto& getParameters() const noexcept { return mVertParams; } void setMemoryPool(std::shared_ptr pool) { mMemoryPool = pool; } - std::vector exportVertices(); - VertexerTraitsN* getTraits() const { return mTraits; }; - float clustersToVertices(LogFunc = [](const std::string& s) { std::cout << s << '\n'; }); void filterMCTracklets(); @@ -86,6 +83,8 @@ class Vertexer template void initialiseTimeFrame(T&&... args); + void sortVertices(); + // Utils template float evaluateTask(void (Vertexer::*task)(T...), std::string_view taskName, int iteration, LogFunc& logger, T&&... args); @@ -118,9 +117,9 @@ class Vertexer static constexpr std::array StateNames{"Initialisation", "Tracklet finding", "Tracklet validation", "Vertex finding", "Truth seeding"}; }; -template +template template -float Vertexer::evaluateTask(void (Vertexer::*task)(T...), std::string_view taskName, int iteration, LogFunc& logger, T&&... args) +float Vertexer::evaluateTask(void (Vertexer::*task)(T...), std::string_view taskName, int iteration, LogFunc& logger, T&&... args) { float diff{0.f}; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h index b1422d66e12df..02ecbe2be8eea 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h @@ -43,11 +43,11 @@ class MCCompLabel; namespace its { -template +template class VertexerTraits { - using IndexTableUtilsN = IndexTableUtils; - using TimeFrameN = TimeFrame; + using IndexTableUtilsN = IndexTableUtils; + using TimeFrameN = TimeFrame; public: VertexerTraits() = default; @@ -68,7 +68,7 @@ class VertexerTraits virtual void computeTrackletMatching(const int iteration = 0); virtual void computeVertices(const int iteration = 0); virtual void adoptTimeFrame(TimeFrameN* tf) noexcept { mTimeFrame = tf; } - virtual void updateVertexingParameters(const std::vector& vrtPar, const TimeFrameGPUParameters& gpuTfPar); + virtual void updateVertexingParameters(const std::vector& vrtPar); // truth tracking void addTruthSeedingVertices(); @@ -84,7 +84,7 @@ class VertexerTraits virtual bool usesMemoryPool() const noexcept { return true; } void setMemoryPool(std::shared_ptr pool) { mMemoryPool = pool; } - static std::pair computeMain(const bounded_vector& elements) + static VertexLabel computeMain(const bounded_vector& elements) { // we only care about the source&event of the tracks, not the trackId auto composeVtxLabel = [](const o2::MCCompLabel& lbl) -> o2::MCCompLabel { @@ -114,28 +114,23 @@ class VertexerTraits private: std::shared_ptr mMemoryPool; std::shared_ptr mTaskArena; - - // debug output - void debugComputeTracklets(int iteration); - void debugComputeTrackletMatching(int iteration); - void debugComputeVertices(int iteration); }; -template -inline void VertexerTraits::initialise(const TrackingParameters& trackingParams, const int iteration) +template +inline void VertexerTraits::initialise(const TrackingParameters& trackingParams, const int iteration) { mTimeFrame->initialise(0, trackingParams, 3, (bool)(!iteration)); // iteration for initialisation must be 0 for correctly resetting the frame, we need to pass the non-reset flag for vertices as well, tho. } -template -GPUhdi() const int2 VertexerTraits::getPhiBins(float phi, float dPhi, const IndexTableUtilsN& utils) +template +GPUhdi() const int2 VertexerTraits::getPhiBins(float phi, float dPhi, const IndexTableUtilsN& utils) { return int2{utils.getPhiBinIndex(math_utils::getNormalizedPhi(phi - dPhi)), utils.getPhiBinIndex(math_utils::getNormalizedPhi(phi + dPhi))}; } -template -GPUhdi() const int4 VertexerTraits::getBinsRect(const Cluster& currentCluster, const int layerIndex, +template +GPUhdi() const int4 VertexerTraits::getBinsRect(const Cluster& currentCluster, const int layerIndex, const float directionZIntersection, float maxdeltaz, float maxdeltaphi, const IndexTableUtilsN& utils) { @@ -155,8 +150,8 @@ GPUhdi() const int4 VertexerTraits::getBinsRect(const Cluster& currentC utils.getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMax))}; } -template -GPUhdi() const int4 VertexerTraits::getBinsRect(const Cluster& currentCluster, const int layerIndex, +template +GPUhdi() const int4 VertexerTraits::getBinsRect(const Cluster& currentCluster, const int layerIndex, const float directionZIntersection, float maxdeltaz, float maxdeltaphi) { return VertexerTraits::getBinsRect(currentCluster, layerIndex, directionZIntersection, maxdeltaz, maxdeltaphi, mIndexTableUtils); diff --git a/Detectors/ITSMFT/ITS/tracking/src/ClusterLines.cxx b/Detectors/ITSMFT/ITS/tracking/src/ClusterLines.cxx index 1a0fa1d3908a4..f561fe0436c4a 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/ClusterLines.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/ClusterLines.cxx @@ -10,385 +10,176 @@ // or submit itself to any jurisdiction. #include -#include +#include "Framework/Logger.h" #include "ITStracking/ClusterLines.h" -namespace o2 -{ -namespace its +namespace o2::its { -Line::Line(std::array firstPoint, std::array secondPoint) +Line::Line(const Tracklet& tracklet, const Cluster* innerClusters, const Cluster* outerClusters) : mTime(tracklet.mTime) { - for (int index{0}; index < 3; ++index) { - originPoint[index] = firstPoint.data()[index]; - cosinesDirector[index] = secondPoint[index] - firstPoint[index]; - } + 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 inverseNorm{1.f / o2::gpu::CAMath::Sqrt(cosinesDirector[0] * cosinesDirector[0] + cosinesDirector[1] * cosinesDirector[1] + - cosinesDirector[2] * cosinesDirector[2])}; - for (int index{0}; index < 3; ++index) { - cosinesDirector[index] *= inverseNorm; - } +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); } -bool Line::areParallel(const Line& firstLine, const Line& secondLine, const float precision) +float Line::getDistanceFromPoint(const Line& line, const std::array& point) { - float crossProdX{firstLine.cosinesDirector[1] * secondLine.cosinesDirector[2] - - firstLine.cosinesDirector[2] * secondLine.cosinesDirector[1]}; - float module{std::abs(firstLine.cosinesDirector[1] * secondLine.cosinesDirector[2]) + - std::abs(firstLine.cosinesDirector[2] * secondLine.cosinesDirector[1])}; - if (std::abs(crossProdX) > precision * module) { - return false; - } + return std::sqrt(getDistance2FromPoint(line, point)); +} - float crossProdY{-firstLine.cosinesDirector[0] * secondLine.cosinesDirector[2] + - firstLine.cosinesDirector[2] * secondLine.cosinesDirector[0]}; - module = std::abs(firstLine.cosinesDirector[0] * secondLine.cosinesDirector[2]) + - std::abs(firstLine.cosinesDirector[2] * secondLine.cosinesDirector[0]); - if (std::abs(crossProdY) > precision * module) { - return false; +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); } - float crossProdZ = firstLine.cosinesDirector[0] * secondLine.cosinesDirector[1] - - firstLine.cosinesDirector[1] * secondLine.cosinesDirector[0]; - module = std::abs(firstLine.cosinesDirector[0] * secondLine.cosinesDirector[1]) + - std::abs(firstLine.cosinesDirector[1] * secondLine.cosinesDirector[0]); - if (std::abs(crossProdZ) > precision * module) { - return false; - } + const SVector3f delta = secondLine.originPoint - firstLine.originPoint; + const float numerator = ROOT::Math::Dot(delta, n); + return (numerator * numerator) / norm2; +} - return true; +float Line::getDCA(const Line& firstLine, const Line& secondLine, const float precision) +{ + return std::sqrt(getDCA2(firstLine, secondLine, precision)); } -std::array Line::getDCAComponents(const Line& line, const std::array point) +Line::SMatrix3f Line::getDCAComponents(const Line& line, const std::array& point) { - std::array components{0., 0., 0., 0., 0., 0.}; - float cdelta{0.}; - for (int i{0}; i < 3; ++i) { - cdelta -= line.cosinesDirector[i] * (line.originPoint[i] - point[i]); - } + 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; +} - components[0] = line.originPoint[0] - point[0] + line.cosinesDirector[0] * cdelta; - components[3] = line.originPoint[1] - point[1] + line.cosinesDirector[1] * cdelta; - components[5] = line.originPoint[2] - point[2] + line.cosinesDirector[2] * cdelta; - components[1] = o2::gpu::CAMath::Sqrt(components[0] * components[0] + components[3] * components[3]); - components[2] = o2::gpu::CAMath::Sqrt(components[0] * components[0] + components[5] * components[5]); - components[4] = o2::gpu::CAMath::Sqrt(components[3] * components[3] + components[5] * components[5]); +bool Line::isEmpty() const noexcept +{ + return ROOT::Math::Dot(originPoint, originPoint) == 0.f && + ROOT::Math::Dot(cosinesDirector, cosinesDirector) == 0.f; +} - return components; +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()); } -ClusterLines::ClusterLines(const int firstLabel, const Line& firstLine, const int secondLabel, const Line& secondLine, - const bool weight) +// 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) { - updateROFPoll(firstLine); - updateROFPoll(secondLine); + mTime += secondLine.mTime; mLabels.push_back(firstLabel); if (secondLabel > 0) { mLabels.push_back(secondLabel); // don't add info in case of beamline used } - std::array covarianceFirst{1., 1., 1.}; - std::array covarianceSecond{1., 1., 1.}; - - // for (int i{0}; i < 6; ++i) { - // mWeightMatrix[i] = firstLine.weightMatrix[i] + secondLine.weightMatrix[i]; - // } - - float determinantFirst = - firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[0] * covarianceFirst[1] + - firstLine.cosinesDirector[1] * firstLine.cosinesDirector[1] * covarianceFirst[0] * covarianceFirst[2] + - firstLine.cosinesDirector[0] * firstLine.cosinesDirector[0] * covarianceFirst[1] * covarianceFirst[2]; - float determinantSecond = - secondLine.cosinesDirector[2] * secondLine.cosinesDirector[2] * covarianceSecond[0] * covarianceSecond[1] + - secondLine.cosinesDirector[1] * secondLine.cosinesDirector[1] * covarianceSecond[0] * covarianceSecond[2] + - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[0] * covarianceSecond[1] * covarianceSecond[2]; - - mAMatrix[0] = (firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[1] + - firstLine.cosinesDirector[1] * firstLine.cosinesDirector[1] * covarianceFirst[2]) / - determinantFirst + - (secondLine.cosinesDirector[2] * secondLine.cosinesDirector[2] * covarianceSecond[1] + - secondLine.cosinesDirector[1] * secondLine.cosinesDirector[1] * covarianceSecond[2]) / - determinantSecond; - - mAMatrix[1] = -firstLine.cosinesDirector[0] * firstLine.cosinesDirector[1] * covarianceFirst[2] / determinantFirst - - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[1] * covarianceSecond[2] / determinantSecond; - - mAMatrix[2] = -firstLine.cosinesDirector[0] * firstLine.cosinesDirector[2] * covarianceFirst[1] / determinantFirst - - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[2] * covarianceSecond[1] / determinantSecond; - - mAMatrix[3] = (firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[0] + - firstLine.cosinesDirector[0] * firstLine.cosinesDirector[0] * covarianceFirst[2]) / - determinantFirst + - (secondLine.cosinesDirector[2] * secondLine.cosinesDirector[2] * covarianceSecond[0] + - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[0] * covarianceSecond[2]) / - determinantSecond; - - mAMatrix[4] = -firstLine.cosinesDirector[1] * firstLine.cosinesDirector[2] * covarianceFirst[0] / determinantFirst - - secondLine.cosinesDirector[1] * secondLine.cosinesDirector[2] * covarianceSecond[0] / determinantSecond; - - mAMatrix[5] = (firstLine.cosinesDirector[1] * firstLine.cosinesDirector[1] * covarianceFirst[0] + - firstLine.cosinesDirector[0] * firstLine.cosinesDirector[0] * covarianceFirst[1]) / - determinantFirst + - (secondLine.cosinesDirector[1] * secondLine.cosinesDirector[1] * covarianceSecond[0] + - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[0] * covarianceSecond[1]) / - determinantSecond; - - mBMatrix[0] = - (firstLine.cosinesDirector[1] * covarianceFirst[2] * (-firstLine.cosinesDirector[1] * firstLine.originPoint[0] + firstLine.cosinesDirector[0] * firstLine.originPoint[1]) + - firstLine.cosinesDirector[2] * covarianceFirst[1] * (-firstLine.cosinesDirector[2] * firstLine.originPoint[0] + firstLine.cosinesDirector[0] * firstLine.originPoint[2])) / - determinantFirst; - - mBMatrix[0] += - (secondLine.cosinesDirector[1] * covarianceSecond[2] * (-secondLine.cosinesDirector[1] * secondLine.originPoint[0] + secondLine.cosinesDirector[0] * secondLine.originPoint[1]) + - secondLine.cosinesDirector[2] * covarianceSecond[1] * - (-secondLine.cosinesDirector[2] * secondLine.originPoint[0] + - secondLine.cosinesDirector[0] * secondLine.originPoint[2])) / - determinantSecond; - - mBMatrix[1] = - (firstLine.cosinesDirector[0] * covarianceFirst[2] * (-firstLine.cosinesDirector[0] * firstLine.originPoint[1] + firstLine.cosinesDirector[1] * firstLine.originPoint[0]) + - firstLine.cosinesDirector[2] * covarianceFirst[0] * (-firstLine.cosinesDirector[2] * firstLine.originPoint[1] + firstLine.cosinesDirector[1] * firstLine.originPoint[2])) / - determinantFirst; - - mBMatrix[1] += - (secondLine.cosinesDirector[0] * covarianceSecond[2] * (-secondLine.cosinesDirector[0] * secondLine.originPoint[1] + secondLine.cosinesDirector[1] * secondLine.originPoint[0]) + - secondLine.cosinesDirector[2] * covarianceSecond[0] * - (-secondLine.cosinesDirector[2] * secondLine.originPoint[1] + - secondLine.cosinesDirector[1] * secondLine.originPoint[2])) / - determinantSecond; - - mBMatrix[2] = - (firstLine.cosinesDirector[0] * covarianceFirst[1] * (-firstLine.cosinesDirector[0] * firstLine.originPoint[2] + firstLine.cosinesDirector[2] * firstLine.originPoint[0]) + - firstLine.cosinesDirector[1] * covarianceFirst[0] * (-firstLine.cosinesDirector[1] * firstLine.originPoint[2] + firstLine.cosinesDirector[2] * firstLine.originPoint[1])) / - determinantFirst; - - mBMatrix[2] += - (secondLine.cosinesDirector[0] * covarianceSecond[1] * (-secondLine.cosinesDirector[0] * secondLine.originPoint[2] + secondLine.cosinesDirector[2] * secondLine.originPoint[0]) + - secondLine.cosinesDirector[1] * covarianceSecond[0] * - (-secondLine.cosinesDirector[1] * secondLine.originPoint[2] + - secondLine.cosinesDirector[2] * secondLine.originPoint[1])) / - determinantSecond; - + accumulate(firstLine); + accumulate(secondLine); computeClusterCentroid(); - // RMS2 + // RMS2: running mean update mRMS2 = Line::getDCAComponents(firstLine, mVertex); - const std::array tmpRMS2Line2 = Line::getDCAComponents(secondLine, mVertex); - std::transform(mRMS2.begin(), mRMS2.end(), tmpRMS2Line2.begin(), mRMS2.begin(), [&](const float a, const float b) { return a + (b - a) / mLabels.size(); }); + const auto tmpRMS2 = Line::getDCAComponents(secondLine, mVertex); + mRMS2 += (tmpRMS2 - mRMS2) * (1.f / static_cast(getSize())); // AvgDistance2 - mAvgDistance2 = std::move(Line::getDistanceFromPoint(firstLine, mVertex) * Line::getDistanceFromPoint(firstLine, mVertex)); - mAvgDistance2 += (Line::getDistanceFromPoint(secondLine, mVertex) * Line::getDistanceFromPoint(secondLine, mVertex) - mAvgDistance2) / mLabels.size(); + mAvgDistance2 = Line::getDistance2FromPoint(firstLine, mVertex); + mAvgDistance2 += (Line::getDistance2FromPoint(secondLine, mVertex) - mAvgDistance2) / (float)getSize(); } -ClusterLines::ClusterLines(const Line& firstLine, const Line& secondLine) -{ - - std::array covarianceFirst{1., 1., 1.}; - std::array covarianceSecond{1., 1., 1.}; - updateROFPoll(firstLine); - updateROFPoll(secondLine); - // for (int i{0}; i < 6; ++i) { - // mWeightMatrix[i] = firstLine.weightMatrix[i] + secondLine.weightMatrix[i]; - // } - - float determinantFirst = - firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[0] * covarianceFirst[1] + - firstLine.cosinesDirector[1] * firstLine.cosinesDirector[1] * covarianceFirst[0] * covarianceFirst[2] + - firstLine.cosinesDirector[0] * firstLine.cosinesDirector[0] * covarianceFirst[1] * covarianceFirst[2]; - float determinantSecond = - secondLine.cosinesDirector[2] * secondLine.cosinesDirector[2] * covarianceSecond[0] * covarianceSecond[1] + - secondLine.cosinesDirector[1] * secondLine.cosinesDirector[1] * covarianceSecond[0] * covarianceSecond[2] + - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[0] * covarianceSecond[1] * covarianceSecond[2]; - - mAMatrix[0] = (firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[1] + - firstLine.cosinesDirector[1] * firstLine.cosinesDirector[1] * covarianceFirst[2]) / - determinantFirst + - (secondLine.cosinesDirector[2] * secondLine.cosinesDirector[2] * covarianceSecond[1] + - secondLine.cosinesDirector[1] * secondLine.cosinesDirector[1] * covarianceSecond[2]) / - determinantSecond; - - mAMatrix[1] = -firstLine.cosinesDirector[0] * firstLine.cosinesDirector[1] * covarianceFirst[2] / determinantFirst - - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[1] * covarianceSecond[2] / determinantSecond; - - mAMatrix[2] = -firstLine.cosinesDirector[0] * firstLine.cosinesDirector[2] * covarianceFirst[1] / determinantFirst - - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[2] * covarianceSecond[1] / determinantSecond; - - mAMatrix[3] = (firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[0] + - firstLine.cosinesDirector[0] * firstLine.cosinesDirector[0] * covarianceFirst[2]) / - determinantFirst + - (secondLine.cosinesDirector[2] * secondLine.cosinesDirector[2] * covarianceSecond[0] + - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[0] * covarianceSecond[2]) / - determinantSecond; - - mAMatrix[4] = -firstLine.cosinesDirector[1] * firstLine.cosinesDirector[2] * covarianceFirst[0] / determinantFirst - - secondLine.cosinesDirector[1] * secondLine.cosinesDirector[2] * covarianceSecond[0] / determinantSecond; - - mAMatrix[5] = (firstLine.cosinesDirector[1] * firstLine.cosinesDirector[1] * covarianceFirst[0] + - firstLine.cosinesDirector[0] * firstLine.cosinesDirector[0] * covarianceFirst[1]) / - determinantFirst + - (secondLine.cosinesDirector[1] * secondLine.cosinesDirector[1] * covarianceSecond[0] + - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[0] * covarianceSecond[1]) / - determinantSecond; - - mBMatrix[0] = - (firstLine.cosinesDirector[1] * covarianceFirst[2] * (-firstLine.cosinesDirector[1] * firstLine.originPoint[0] + firstLine.cosinesDirector[0] * firstLine.originPoint[1]) + - firstLine.cosinesDirector[2] * covarianceFirst[1] * (-firstLine.cosinesDirector[2] * firstLine.originPoint[0] + firstLine.cosinesDirector[0] * firstLine.originPoint[2])) / - determinantFirst; - - mBMatrix[0] += - (secondLine.cosinesDirector[1] * covarianceSecond[2] * (-secondLine.cosinesDirector[1] * secondLine.originPoint[0] + secondLine.cosinesDirector[0] * secondLine.originPoint[1]) + - secondLine.cosinesDirector[2] * covarianceSecond[1] * - (-secondLine.cosinesDirector[2] * secondLine.originPoint[0] + - secondLine.cosinesDirector[0] * secondLine.originPoint[2])) / - determinantSecond; - - mBMatrix[1] = - (firstLine.cosinesDirector[0] * covarianceFirst[2] * (-firstLine.cosinesDirector[0] * firstLine.originPoint[1] + firstLine.cosinesDirector[1] * firstLine.originPoint[0]) + - firstLine.cosinesDirector[2] * covarianceFirst[0] * (-firstLine.cosinesDirector[2] * firstLine.originPoint[1] + firstLine.cosinesDirector[1] * firstLine.originPoint[2])) / - determinantFirst; - - mBMatrix[1] += - (secondLine.cosinesDirector[0] * covarianceSecond[2] * (-secondLine.cosinesDirector[0] * secondLine.originPoint[1] + secondLine.cosinesDirector[1] * secondLine.originPoint[0]) + - secondLine.cosinesDirector[2] * covarianceSecond[0] * - (-secondLine.cosinesDirector[2] * secondLine.originPoint[1] + - secondLine.cosinesDirector[1] * secondLine.originPoint[2])) / - determinantSecond; - - mBMatrix[2] = - (firstLine.cosinesDirector[0] * covarianceFirst[1] * (-firstLine.cosinesDirector[0] * firstLine.originPoint[2] + firstLine.cosinesDirector[2] * firstLine.originPoint[0]) + - firstLine.cosinesDirector[1] * covarianceFirst[0] * (-firstLine.cosinesDirector[1] * firstLine.originPoint[2] + firstLine.cosinesDirector[2] * firstLine.originPoint[1])) / - determinantFirst; - - mBMatrix[2] += - (secondLine.cosinesDirector[0] * covarianceSecond[1] * (-secondLine.cosinesDirector[0] * secondLine.originPoint[2] + secondLine.cosinesDirector[2] * secondLine.originPoint[0]) + - secondLine.cosinesDirector[1] * covarianceSecond[0] * - (-secondLine.cosinesDirector[1] * secondLine.originPoint[2] + - secondLine.cosinesDirector[2] * secondLine.originPoint[1])) / - determinantSecond; - - computeClusterCentroid(); -} - -void ClusterLines::add(const int& lineLabel, const Line& line, const bool& weight) +void ClusterLines::add(const int lineLabel, const Line& line) { + mTime += line.mTime; mLabels.push_back(lineLabel); - updateROFPoll(line); - std::array covariance{1., 1., 1.}; - - // for (int i{0}; i < 6; ++i) { - // mWeightMatrix[i] += line.weightMatrix[i]; - // } - // if(weight) line->GetSigma2P0(covariance); - - double determinant{line.cosinesDirector[2] * line.cosinesDirector[2] * covariance[0] * covariance[1] + - line.cosinesDirector[1] * line.cosinesDirector[1] * covariance[0] * covariance[2] + - line.cosinesDirector[0] * line.cosinesDirector[0] * covariance[1] * covariance[2]}; - - mAMatrix[0] += (line.cosinesDirector[2] * line.cosinesDirector[2] * covariance[1] + - line.cosinesDirector[1] * line.cosinesDirector[1] * covariance[2]) / - determinant; - mAMatrix[1] += -line.cosinesDirector[0] * line.cosinesDirector[1] * covariance[2] / determinant; - mAMatrix[2] += -line.cosinesDirector[0] * line.cosinesDirector[2] * covariance[1] / determinant; - mAMatrix[3] += (line.cosinesDirector[2] * line.cosinesDirector[2] * covariance[0] + - line.cosinesDirector[0] * line.cosinesDirector[0] * covariance[2]) / - determinant; - mAMatrix[4] += -line.cosinesDirector[1] * line.cosinesDirector[2] * covariance[0] / determinant; - mAMatrix[5] += (line.cosinesDirector[1] * line.cosinesDirector[1] * covariance[0] + - line.cosinesDirector[0] * line.cosinesDirector[0] * covariance[1]) / - determinant; - - mBMatrix[0] += (line.cosinesDirector[1] * covariance[2] * - (-line.cosinesDirector[1] * line.originPoint[0] + line.cosinesDirector[0] * line.originPoint[1]) + - line.cosinesDirector[2] * covariance[1] * - (-line.cosinesDirector[2] * line.originPoint[0] + line.cosinesDirector[0] * line.originPoint[2])) / - determinant; - mBMatrix[1] += (line.cosinesDirector[0] * covariance[2] * - (-line.cosinesDirector[0] * line.originPoint[1] + line.cosinesDirector[1] * line.originPoint[0]) + - line.cosinesDirector[2] * covariance[0] * - (-line.cosinesDirector[2] * line.originPoint[1] + line.cosinesDirector[1] * line.originPoint[2])) / - determinant; - mBMatrix[2] += (line.cosinesDirector[0] * covariance[1] * - (-line.cosinesDirector[0] * line.originPoint[2] + line.cosinesDirector[2] * line.originPoint[0]) + - line.cosinesDirector[1] * covariance[0] * - (-line.cosinesDirector[1] * line.originPoint[2] + line.cosinesDirector[2] * line.originPoint[1])) / - determinant; + accumulate(line); computeClusterCentroid(); - mAvgDistance2 += (Line::getDistanceFromPoint(line, mVertex) * Line::getDistanceFromPoint(line, mVertex) - mAvgDistance2) / mLabels.size(); + mAvgDistance2 += (Line::getDistance2FromPoint(line, mVertex) - mAvgDistance2) / (float)getSize(); } void ClusterLines::computeClusterCentroid() { - - double determinant{mAMatrix[0] * (mAMatrix[3] * mAMatrix[5] - mAMatrix[4] * mAMatrix[4]) - - mAMatrix[1] * (mAMatrix[1] * mAMatrix[5] - mAMatrix[4] * mAMatrix[2]) + - mAMatrix[2] * (mAMatrix[1] * mAMatrix[4] - mAMatrix[2] * mAMatrix[3])}; - - if (determinant == 0) { + // 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; } - mVertex[0] = -(mBMatrix[0] * (mAMatrix[3] * mAMatrix[5] - mAMatrix[4] * mAMatrix[4]) - - mAMatrix[1] * (mBMatrix[1] * mAMatrix[5] - mAMatrix[4] * mBMatrix[2]) + - mAMatrix[2] * (mBMatrix[1] * mAMatrix[4] - mBMatrix[2] * mAMatrix[3])) / - determinant; - mVertex[1] = -(mAMatrix[0] * (mBMatrix[1] * mAMatrix[5] - mBMatrix[2] * mAMatrix[4]) - - mBMatrix[0] * (mAMatrix[1] * mAMatrix[5] - mAMatrix[4] * mAMatrix[2]) + - mAMatrix[2] * (mAMatrix[1] * mBMatrix[2] - mAMatrix[2] * mBMatrix[1])) / - determinant; - mVertex[2] = -(mAMatrix[0] * (mAMatrix[3] * mBMatrix[2] - mBMatrix[1] * mAMatrix[4]) - - mAMatrix[1] * (mAMatrix[1] * mBMatrix[2] - mBMatrix[1] * mAMatrix[2]) + - mBMatrix[0] * (mAMatrix[1] * mAMatrix[4] - mAMatrix[2] * mAMatrix[3])) / - determinant; -} - -bool ClusterLines::operator==(const ClusterLines& rhs) const -{ - bool retval{true}; - for (auto i{0}; i < 6; ++i) { - retval &= this->mRMS2[i] == rhs.mRMS2[i]; - } - for (auto i{0}; i < 3; ++i) { - retval &= this->mVertex[i] == rhs.mVertex[i]; - } - if (this->mLabels.size() != rhs.mLabels.size()) { - retval = false; - } else { - for (size_t i{0}; i < this->mLabels.size(); ++i) { - retval &= this->mLabels[i] == rhs.mLabels[i]; - } - } - return retval && this->mAvgDistance2 == rhs.mAvgDistance2; + SVector3 result = invA * mBMatrix; + mVertex[0] = static_cast(-result(0)); + mVertex[1] = static_cast(-result(1)); + mVertex[2] = static_cast(-result(2)); } -GPUhdi() void ClusterLines::updateROFPoll(const Line& line) +bool ClusterLines::operator==(const ClusterLines& rhs) const noexcept { - // option 1: Boyer-Moore voting for rof label - if (mROFWeight == 0) { - mROF = line.getMinROF(); - mROFWeight = 1; - } else { - if (mROF == line.getMinROF()) { - mROFWeight++; - } else { - mROFWeight--; - } - } - - // option 2 - // if (mROF == -1) { - // mROF = line.getMinROF(); - // } else { - // if (line.getMinROF() < mROF) { - // mROF = line.getMinROF(); - // } - // } + return mRMS2 == rhs.mRMS2 && + mVertex == rhs.mVertex && + mLabels == rhs.mLabels && + mAvgDistance2 == rhs.mAvgDistance2; } -} // namespace its -} // namespace o2 +} // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx b/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx index 202dc87f04237..c447bb6bcc880 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx @@ -24,8 +24,8 @@ using namespace o2::its; std::string TrackingParameters::asString() const { - std::string str = std::format("NZb:{} NPhB:{} NROFIt:{} DRof:{} PerVtx:{} DropFail:{} ClSh:{} TtklMinPt:{:.2f} MinCl:{}", - ZBins, PhiBins, nROFsPerIterations, DeltaROF, PerPrimaryVertexProcessing, DropTFUponFailure, ClusterSharing, TrackletMinPt, MinTrackLength); + std::string str = std::format("NZb:{} NPhB:{} PerVtx:{} DropFail:{} ClSh:{} TtklMinPt:{:.2f} MinCl:{}", + ZBins, PhiBins, PerPrimaryVertexProcessing, DropTFUponFailure, ClusterSharing, TrackletMinPt, MinTrackLength); bool first = true; for (int il = NLayers; il >= MinTrackLength; il--) { int slot = NLayers - il; @@ -37,9 +37,17 @@ std::string TrackingParameters::asString() const str += std::format("L{}:{:.2f} ", il, MinPt[slot]); } } - str += " SystErrY/Z:"; - for (size_t i = 0; i < SystErrorY2.size(); i++) { - str += std::format("{:.2e}/{:.2e} ", SystErrorY2[i], SystErrorZ2[i]); + if (!SystErrorY2.empty() || !SystErrorZ2.empty()) { + str += " SystErrY/Z:"; + for (size_t i = 0; i < SystErrorY2.size(); i++) { + str += std::format("{:.2e}/{:.2e} ", SystErrorY2[i], SystErrorZ2[i]); + } + } + if (!AddTimeError.empty()) { + str += " AddTimeError:"; + for (size_t i = 0; i < AddTimeError.size(); i++) { + str += std::format("{} ", AddTimeError[i]); + } } if (std::numeric_limits::max() != MaxMemory) { str += std::format(" MemLimit {:.2f} GB", double(MaxMemory) / constants::GB); @@ -49,7 +57,7 @@ std::string TrackingParameters::asString() const std::string VertexingParameters::asString() const { - std::string str = std::format("NZb:{} NPhB:{} DRof:{} ClsCont:{} MaxTrkltCls:{} ZCut:{} PhCut:{}", ZBins, PhiBins, deltaRof, clusterContributorsCut, maxTrackletsPerCluster, zCut, phiCut); + std::string str = std::format("NZb:{} NPhB:{} ClsCont:{} MaxTrkltCls:{} ZCut:{} PhCut:{}", ZBins, PhiBins, clusterContributorsCut, maxTrackletsPerCluster, zCut, phiCut); if (std::numeric_limits::max() != MaxMemory) { str += std::format(" MemLimit {:.2f} GB", double(MaxMemory) / constants::GB); } @@ -126,14 +134,11 @@ std::vector TrackingMode::getTrackingParameters(TrackingMode trackParams[3].MinTrackLength = 4; trackParams[3].TrackletMinPt = 0.1f; trackParams[3].CellDeltaTanLambdaSigma *= 4.; - trackParams[3].DeltaROF = 0; // UPC specific setting } for (size_t ip = 0; ip < trackParams.size(); ip++) { auto& param = trackParams[ip]; param.ZBins = 64; param.PhiBins = 32; - param.CellsPerClusterLimit = 1.e3f; - param.TrackletsPerClusterLimit = 1.e3f; // check if something was overridden via configurable params if (ip < tc.MaxIter) { if (tc.startLayerMask[ip] > 0) { @@ -164,19 +169,12 @@ std::vector TrackingMode::getTrackingParameters(TrackingMode trackParams[0].PVres = 1.e5f; trackParams[0].MaxChi2ClusterAttachment = 60.; trackParams[0].MaxChi2NDF = 40.; - trackParams[0].TrackletsPerClusterLimit = 100.; - trackParams[0].CellsPerClusterLimit = 100.; } else { LOGP(fatal, "Unsupported ITS tracking mode {} ", toString(mode)); } float bFactor = std::abs(o2::base::Propagator::Instance()->getNominalBz()) / 5.0066791; float bFactorTracklets = bFactor < 0.01 ? 1. : bFactor; // for tracklets only - int nROFsPerIterations = tc.nROFsPerIterations > 0 ? tc.nROFsPerIterations : -1; - - if (tc.nOrbitsPerIterations > 0) { - /// code to be used when the number of ROFs per orbit is known, this gets priority over the number of ROFs per iteration - } // global parameters set for every iteration for (auto& p : trackParams) { @@ -212,7 +210,9 @@ std::vector TrackingMode::getTrackingParameters(TrackingMode p.SystErrorZ2[i] = tc.sysErrZ2[i] > 0 ? tc.sysErrZ2[i] : p.SystErrorZ2[i]; } } - p.DeltaROF = tc.deltaRof; + for (int i{0}; i < 7; ++i) { + p.AddTimeError[i] = tc.addTimeError[i]; + } p.DoUPCIteration = tc.doUPCIteration; p.MaxChi2ClusterAttachment = tc.maxChi2ClusterAttachment > 0 ? tc.maxChi2ClusterAttachment : p.MaxChi2ClusterAttachment; p.MaxChi2NDF = tc.maxChi2NDF > 0 ? tc.maxChi2NDF : p.MaxChi2NDF; @@ -222,32 +222,11 @@ std::vector TrackingMode::getTrackingParameters(TrackingMode 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; - p.nROFsPerIterations = nROFsPerIterations; p.PerPrimaryVertexProcessing = tc.perPrimaryVertexProcessing; for (int iD{0}; iD < 3; ++iD) { p.Diamond[iD] = tc.diamondPos[iD]; } p.UseDiamond = tc.useDiamond; - if (tc.useTrackFollower > 0) { - p.UseTrackFollower = true; - // Bit 0: Allow for mixing of top&bot extension --> implies Bits 1&2 set - // Bit 1: Allow for top extension - // Bit 2: Allow for bot extension - p.UseTrackFollowerMix = ((tc.useTrackFollower & (1 << 0)) != 0); - p.UseTrackFollowerTop = ((tc.useTrackFollower & (1 << 1)) != 0); - p.UseTrackFollowerBot = ((tc.useTrackFollower & (1 << 2)) != 0); - p.TrackFollowerNSigmaCutZ = tc.trackFollowerNSigmaZ; - p.TrackFollowerNSigmaCutPhi = tc.trackFollowerNSigmaPhi; - } - if (tc.cellsPerClusterLimit >= 0) { - p.CellsPerClusterLimit = tc.cellsPerClusterLimit; - } - if (tc.trackletsPerClusterLimit >= 0) { - p.TrackletsPerClusterLimit = tc.trackletsPerClusterLimit; - } - if (tc.findShortTracks >= 0) { - p.FindShortTracks = tc.findShortTracks; - } } if (trackParams.size() > tc.nIterations) { @@ -265,8 +244,6 @@ std::vector TrackingMode::getVertexingParameters(TrackingMo vertParams.resize(2); // The number of actual iterations will be set as a configKeyVal to allow for pp/PbPb choice vertParams[1].phiCut = 0.015f; vertParams[1].tanLambdaCut = 0.015f; - vertParams[1].vertPerRofThreshold = 0; - vertParams[1].deltaRof = 0; } else if (mode == TrackingMode::Sync) { vertParams.resize(1); } else if (mode == TrackingMode::Cosmics) { @@ -282,8 +259,6 @@ std::vector TrackingMode::getVertexingParameters(TrackingMo p.MaxMemory = vc.maxMemory; p.DropTFUponFailure = vc.dropTFUponFailure; p.nIterations = vc.nIterations; - p.deltaRof = vc.deltaRof; - p.allowSingleContribClusters = vc.allowSingleContribClusters; p.trackletSigma = vc.trackletSigma; p.maxZPositionAllowed = vc.maxZPositionAllowed; p.clusterContributorsCut = vc.clusterContributorsCut; @@ -293,7 +268,6 @@ std::vector TrackingMode::getVertexingParameters(TrackingMo p.PhiBins = vc.PhiBins; p.useTruthSeeding = vc.useTruthSeeding; - p.outputContLabels = vc.outputContLabels; } // set for now outside to not disturb status quo vertParams[0].vertNsigmaCut = vc.vertNsigmaCut; diff --git a/Detectors/ITSMFT/ITS/tracking/src/FastMultEst.cxx b/Detectors/ITSMFT/ITS/tracking/src/FastMultEst.cxx new file mode 100644 index 0000000000000..cb831d7db71d0 --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/src/FastMultEst.cxx @@ -0,0 +1,252 @@ +// 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 FastMultEst.h +/// \brief Fast multiplicity estimator for ITS +/// \author ruben.shahoyan@cern.ch + +#include "ITStracking/FastMultEst.h" +#include "Framework/Logger.h" +#include +#include +#include +#include + +using namespace o2::its; + +namespace +{ + +// Convert trigger IR to ROF index on a given layer using LayerTiming +int findROFForIR(const o2::InteractionRecord& ir, + const o2::InteractionRecord& tfStartIR, + const LayerTiming& layerTiming) +{ + // Convert IR to BC-from-TF-start, which is the time base expected by LayerTiming. + const int64_t bcFromTFStart = ir.differenceInBC(tfStartIR); + if (bcFromTFStart < 0) { + return -1; + } + return layerTiming.getROF(static_cast(bcFromTFStart)); +} + +template +void enableCompatibleROFs(int baseLayer, + int baseRof, + const typename o2::its::ROFOverlapTable::View& overlapView, + o2::its::ROFMaskTable& sel) +{ + sel.setROFEnabled(baseLayer, baseRof); + for (int layer = 0; layer < NLayers; ++layer) { + if (layer == baseLayer) { + continue; + } + const auto& overlap = overlapView.getOverlap(baseLayer, layer, baseRof); + if (overlap.getEntries() > 0) { + sel.setROFsEnabled(layer, overlap.getFirstEntry(), overlap.getEntries()); + } + } +} + +template +std::vector buildMultiplicityCounts(const std::array, NLayers>& rofs, + const std::array, NLayers>& clus, + bool doStaggering, + int multLayer) +{ + std::vector multCounts; + if (doStaggering) { + multCounts.resize(rofs[multLayer].size()); + for (size_t iRof = 0; iRof < rofs[multLayer].size(); ++iRof) { + multCounts[iRof] = rofs[multLayer][iRof].getNEntries(); + } + return multCounts; + } + + static const o2::itsmft::ChipMappingITS chipMapping; + multCounts.resize(rofs[0].size(), 0); + for (size_t iRof = 0; iRof < rofs[0].size(); ++iRof) { + for (const auto& cluster : rofs[0][iRof].getROFData(clus[0])) { + if (chipMapping.getLayer(cluster.getSensorID()) == multLayer) { + ++multCounts[iRof]; + } + } + } + return multCounts; +} +} // namespace + +bool FastMultEst::sSeedSet = false; + +///______________________________________________________ +FastMultEst::FastMultEst() +{ + if (!sSeedSet && FastMultEstConfig::Instance().cutRandomFraction > 0.f) { + sSeedSet = true; + if (FastMultEstConfig::Instance().randomSeed > 0) { + gRandom->SetSeed(FastMultEstConfig::Instance().randomSeed); + } else if (FastMultEstConfig::Instance().randomSeed < 0) { + gRandom->SetSeed(std::time(nullptr) % 0xffff); + } + } +} + +///______________________________________________________ +/// count clusters on the configured multiplicity layer +int FastMultEst::countClustersOnLayer(const gsl::span& clusters) const +{ + const int targetLayer = std::clamp(FastMultEstConfig::Instance().cutMultClusLayer, 0, NLayers - 1); + int count = 0; + int lr = FastMultEst::NLayers - 1; + int nchAcc = o2::itsmft::ChipMappingITS::getNChips() - o2::itsmft::ChipMappingITS::getNChipsPerLr(lr); + for (int i = clusters.size(); i--;) { // profit from clusters being ordered in chip increasing order + while (clusters[i].getSensorID() < nchAcc) { + assert(lr >= 0); + nchAcc -= o2::itsmft::ChipMappingITS::getNChipsPerLr(--lr); + } + if (lr == targetLayer) { + ++count; + } + } + return count; +} + +///______________________________________________________ +/// find multiplicity for given number of clusters per layer +float FastMultEst::processNoiseFree(int nClusters) +{ + // Single-layer regime: estimate multiplicity from one configured layer only. + const auto& conf = FastMultEstConfig::Instance(); + const int layer = std::clamp(conf.cutMultClusLayer, 0, NLayers - 1); + const float acc = conf.accCorr[layer]; + nLayersUsed = nClusters > 0 ? 1 : 0; + noisePerChip = 0.f; + chi2 = 0.f; + cov[0] = cov[1] = cov[2] = 0.f; + if (nLayersUsed == 0 || acc <= 0.f) { + mult = -1.f; + return -1.f; + } + mult = nClusters / acc; + return mult > 0 ? mult : 0; +} + +///______________________________________________________ +/// find multiplicity for given number of clusters per layer with mean noise imposed +float FastMultEst::processNoiseImposed(int nClusters) +{ + // Single-layer regime with imposed noise subtraction. + const auto& conf = FastMultEstConfig::Instance(); + const int layer = std::clamp(conf.cutMultClusLayer, 0, NLayers - 1); + const float acc = conf.accCorr[layer]; + const float nch = static_cast(o2::itsmft::ChipMappingITS::getNChipsPerLr(layer)); + nLayersUsed = nClusters > 0 ? 1 : 0; + chi2 = 0.f; + cov[0] = cov[1] = cov[2] = 0.f; + if (nLayersUsed == 0 || acc <= 0.f) { + mult = -1.f; + return -1.f; + } + mult = (nClusters - noisePerChip * nch) / acc; + return mult; +} + +int FastMultEst::selectROFs(const std::array, NLayers>& rofs, + const std::array, NLayers>& clus, + const gsl::span trig, + uint32_t firstTForbit, + bool doStaggering, + const ROFOverlapTableN::View& overlapView, + ROFMaskTableN& sel) +{ + const auto& multEstConf = FastMultEstConfig::Instance(); // parameters for mult estimation and cuts + const int selectionLayer = overlapView.getClock(); + int multLayer = std::clamp(multEstConf.cutMultClusLayer, 0, NLayers - 1); + if (doStaggering && rofs[multLayer].empty()) { + LOGP(info, "FastMultEst multiplicity layer {} has no ROFs, falling back to selection layer {}", multLayer, selectionLayer); + multLayer = selectionLayer; + } + + const auto multCounts = buildMultiplicityCounts(rofs, clus, doStaggering, multLayer); + const int selectionRofCount = doStaggering ? static_cast(rofs[selectionLayer].size()) : static_cast(rofs[0].size()); + + sel.resetMask(); + lastRandomSeed = gRandom->GetSeed(); + const o2::InteractionRecord tfStartIR{0, firstTForbit}; + + if (!trig.empty()) { + const auto& selectionLayerTiming = overlapView.getLayer(selectionLayer); + const auto& multLayerTiming = overlapView.getLayer(multLayer); + + for (const auto& trigger : trig) { + const int selectionRof = findROFForIR(trigger.ir, tfStartIR, selectionLayerTiming); + if (selectionRof < 0) { + continue; + } + if (multEstConf.cutRandomFraction > 0.f && gRandom->Rndm() < multEstConf.cutRandomFraction) { + continue; + } + if (multEstConf.isMultCutRequested()) { + const int triggerMultRof = doStaggering ? findROFForIR(trigger.ir, tfStartIR, multLayerTiming) : selectionRof; + if (triggerMultRof < 0 || triggerMultRof >= static_cast(multCounts.size())) { + continue; + } + if (!multEstConf.isPassingMultCut(process(multCounts[triggerMultRof]))) { + continue; + } + } + enableCompatibleROFs(selectionLayer, selectionRof, overlapView, sel); + } + } else { + LOGP(info, "FastMultEst received no physics/TRD triggers, falling back to ROF-driven filtering on layer {}", selectionLayer); + for (int selectionRof = 0; selectionRof < selectionRofCount; ++selectionRof) { + if (multEstConf.isMultCutRequested()) { + bool passes = false; + if (!doStaggering || selectionLayer == multLayer) { + if (selectionRof < static_cast(multCounts.size())) { + passes = multEstConf.isPassingMultCut(process(multCounts[selectionRof])); + } + } else { + const auto& overlap = overlapView.getOverlap(selectionLayer, multLayer, selectionRof); + for (int rof = overlap.getFirstEntry(); rof < overlap.getEntriesBound(); ++rof) { + if (rof < static_cast(multCounts.size())) { + if (multEstConf.isPassingMultCut(process(multCounts[rof]))) { + passes = true; + break; + } + } + } + } + if (!passes) { + continue; + } + } + if (multEstConf.cutRandomFraction > 0.f && gRandom->Rndm() < multEstConf.cutRandomFraction) { + continue; + } + enableCompatibleROFs(selectionLayer, selectionRof, overlapView, sel); + } + } + + const auto selView = sel.getView(); + int nsel = 0; + for (int irof = 0; irof < selectionRofCount; ++irof) { + nsel += selView.isROFEnabled(selectionLayer, irof); + } + + if (!trig.empty() && multEstConf.preferTriggered) { + LOGP(debug, "FastMultEst preferTriggered is ignored in trigger-driven mask mode"); + } + + LOGP(debug, "NSel = {} of {} rofs on layer {} Seeds: before {} after {}", nsel, selectionRofCount, selectionLayer, lastRandomSeed, gRandom->GetSeed()); + + return nsel; +} diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/FastMultEstConfig.cxx b/Detectors/ITSMFT/ITS/tracking/src/FastMultEstConfig.cxx similarity index 94% rename from Detectors/ITSMFT/ITS/reconstruction/src/FastMultEstConfig.cxx rename to Detectors/ITSMFT/ITS/tracking/src/FastMultEstConfig.cxx index 63c43cf26ba15..1568d8ed9f9fb 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/src/FastMultEstConfig.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/FastMultEstConfig.cxx @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "ITSReconstruction/FastMultEstConfig.h" +#include "ITStracking/FastMultEstConfig.h" #include "TRandom.h" O2ParamImpl(o2::its::FastMultEstConfig); diff --git a/Detectors/ITSMFT/ITS/tracking/src/IndexTableUtils.cxx b/Detectors/ITSMFT/ITS/tracking/src/IndexTableUtils.cxx deleted file mode 100644 index 7152640e9a70f..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/src/IndexTableUtils.cxx +++ /dev/null @@ -1,49 +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 IndexTableUtils.cxx -/// \brief -/// - -#include "ITStracking/IndexTableUtils.h" - -namespace o2 -{ -namespace its -{ - -const std::vector> index_table_utils::selectClusters( - const std::array& indexTable, - const std::array& selectedBinsRect) -{ - std::vector> filteredBins{}; - - int phiBinsNum{selectedBinsRect[3] - selectedBinsRect[1] + 1}; - - if (phiBinsNum < 0) { - phiBinsNum += constants::index_table::PhiBins; - } - - filteredBins.reserve(phiBinsNum); - - for (int iPhiBin{selectedBinsRect[1]}, iPhiCount{0}; iPhiCount < phiBinsNum; - iPhiBin = ++iPhiBin == constants::index_table::PhiBins ? 0 : iPhiBin, iPhiCount++) { - - const int firstBinIndex{index_table_utils::getBinIndex(selectedBinsRect[0], iPhiBin)}; - - filteredBins.emplace_back(indexTable[firstBinIndex], - countRowSelectedBins(indexTable, iPhiBin, selectedBinsRect[0], selectedBinsRect[2])); - } - - return filteredBins; -} -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/src/Smoother.cxx b/Detectors/ITSMFT/ITS/tracking/src/Smoother.cxx deleted file mode 100644 index f2f7dbc81398f..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/src/Smoother.cxx +++ /dev/null @@ -1,222 +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. -// -// \author matteo.concas@cern.ch - -#include "ITStracking/Smoother.h" - -namespace o2 -{ -namespace its -{ - -constexpr std::array getInverseSymm2D(const std::array& mat) -{ - const double det = mat[0] * mat[2] - mat[1] * mat[1]; - return std::array{mat[2] / det, -mat[1] / det, mat[0] / det}; -} - -// Smoother -// template -// Smoother::Smoother(TrackITSExt& track, size_t smoothingLayer, const ROframe& event, float bZ, o2::base::PropagatorF::MatCorrType corr) : mLayerToSmooth{smoothingLayer}, -// mBz(bZ), -// mCorr(corr) -// { -// -// auto propInstance = o2::base::Propagator::Instance(); -// const TrackingFrameInfo& originalTf = event.getTrackingFrameInfoOnLayer(mLayerToSmooth).at(track.getClusterIndex(mLayerToSmooth)); -// -// mOutwardsTrack = track; // This track will be propagated outwards inside the smoother! (as last step of fitting did inward propagation) -// mInwardsTrack = {track.getParamOut(), // This track will be propagated inwards inside the smoother! -// static_cast(mOutwardsTrack.getNumberOfClusters()), -999, static_cast(event.getROFrameId()), -// mOutwardsTrack.getParamOut(), mOutwardsTrack.getClusterIndexes()}; -// -// mOutwardsTrack.resetCovariance(); -// mOutwardsTrack.setChi2(0); -// mInwardsTrack.resetCovariance(); -// mInwardsTrack.setChi2(0); -// -// bool statusOutw{false}; -// bool statusInw{false}; -// -// ////////////////////// -// // Outward propagation -// for (size_t iLayer{0}; iLayer < mLayerToSmooth; ++iLayer) { -// if (mOutwardsTrack.getClusterIndex(iLayer) == constants::UnusedIndex) { // Shorter tracks -// continue; -// } -// const TrackingFrameInfo& tF = event.getTrackingFrameInfoOnLayer(iLayer).at(mOutwardsTrack.getClusterIndex(iLayer)); -// statusOutw = mOutwardsTrack.rotate(tF.alphaTrackingFrame); -// statusOutw &= propInstance->propagateToX(mOutwardsTrack, -// tF.xTrackingFrame, -// mBz, -// o2::base::PropagatorImpl::MAX_SIN_PHI, -// o2::base::PropagatorImpl::MAX_STEP, -// mCorr); -// mOutwardsTrack.setChi2(mOutwardsTrack.getChi2() + mOutwardsTrack.getPredictedChi2(tF.positionTrackingFrame, tF.covarianceTrackingFrame)); -// statusOutw &= mOutwardsTrack.o2::track::TrackParCov::update(tF.positionTrackingFrame, tF.covarianceTrackingFrame); -// // LOG(info) << "Outwards loop on inwards track, layer: " << iLayer << " x: " << mOutwardsTrack.getX(); -// } -// -// // Prediction on the previously outwards-propagated track is done on a copy, as the process seems to be not reversible -// auto outwardsClone = mOutwardsTrack; -// statusOutw = outwardsClone.rotate(originalTf.alphaTrackingFrame); -// statusOutw &= propInstance->propagateToX(outwardsClone, -// originalTf.xTrackingFrame, -// mBz, -// o2::base::PropagatorImpl::MAX_SIN_PHI, -// o2::base::PropagatorImpl::MAX_STEP, -// mCorr); -// ///////////////////// -// // Inward propagation -// for (size_t iLayer{D - 1}; iLayer > mLayerToSmooth; --iLayer) { -// if (mInwardsTrack.getClusterIndex(iLayer) == constants::UnusedIndex) { // Shorter tracks -// continue; -// } -// const TrackingFrameInfo& tF = event.getTrackingFrameInfoOnLayer(iLayer).at(mInwardsTrack.getClusterIndex(iLayer)); -// statusInw = mInwardsTrack.rotate(tF.alphaTrackingFrame); -// statusInw &= propInstance->propagateToX(mInwardsTrack, -// tF.xTrackingFrame, -// mBz, -// o2::base::PropagatorImpl::MAX_SIN_PHI, -// o2::base::PropagatorImpl::MAX_STEP, -// mCorr); -// mInwardsTrack.setChi2(mInwardsTrack.getChi2() + mInwardsTrack.getPredictedChi2(tF.positionTrackingFrame, tF.covarianceTrackingFrame)); -// statusInw &= mInwardsTrack.o2::track::TrackParCov::update(tF.positionTrackingFrame, tF.covarianceTrackingFrame); -// // LOG(info) << "Inwards loop on outwards track, layer: " << iLayer << " x: " << mInwardsTrack.getX(); -// } -// -// // Prediction on the previously inwards-propagated track is done on a copy, as the process seems to be not revesible -// auto inwardsClone = mInwardsTrack; -// statusInw = inwardsClone.rotate(originalTf.alphaTrackingFrame); -// statusInw &= propInstance->propagateToX(inwardsClone, -// originalTf.xTrackingFrame, -// mBz, -// o2::base::PropagatorImpl::MAX_SIN_PHI, -// o2::base::PropagatorImpl::MAX_STEP, -// mCorr); -// // Compute weighted local chi2 -// mInitStatus = statusInw && statusOutw; -// if (mInitStatus) { -// mBestChi2 = computeSmoothedPredictedChi2(inwardsClone, outwardsClone, originalTf.positionTrackingFrame, originalTf.covarianceTrackingFrame); -// mLastChi2 = mBestChi2; -// LOG(info) << "Smoothed chi2 on original cluster: " << mBestChi2; -// } -// } - -template -Smoother::~Smoother() = default; - -template -float Smoother::computeSmoothedPredictedChi2(const o2::track::TrackParCov& firstTrack, // outwards track: from innermost cluster to outermost - const o2::track::TrackParCov& secondTrack, // inwards track: from outermost cluster to innermost - const std::array& cls, - const std::array& clCov) -{ - // Tracks need to be already propagated, compute only chi2 - // Symmetric covariances assumed - - if (firstTrack.getX() != secondTrack.getX()) { - LOG(fatal) << "Tracks need to be propagated to the same point! secondTrack.X=" << secondTrack.getX() << " firstTrack.X=" << firstTrack.getX(); - } - - std::array pp1 = {static_cast(firstTrack.getY()), static_cast(firstTrack.getZ())}; // P1: predicted Y,Z points - std::array pp2 = {static_cast(secondTrack.getY()), static_cast(secondTrack.getZ())}; // P2: predicted Y,Z points - - std::array c1 = {static_cast(firstTrack.getSigmaY2()), - static_cast(firstTrack.getSigmaZY()), - static_cast(firstTrack.getSigmaZ2())}; // Cov. track 1 - - std::array c2 = {static_cast(secondTrack.getSigmaY2()), - static_cast(secondTrack.getSigmaZY()), - static_cast(secondTrack.getSigmaZ2())}; // Cov. track 2 - - std::array w1 = getInverseSymm2D(c1); // weight matrices - std::array w2 = getInverseSymm2D(c2); - - std::array w1w2 = {w1[0] + w2[0], w1[1] + w2[1], w1[2] + w2[2]}; // (W1 + W2) - std::array C = getInverseSymm2D(w1w2); // C = (W1+W2)^-1 - - std::array w1pp1 = {w1[0] * pp1[0] + w1[1] * pp1[1], w1[1] * pp1[0] + w1[2] * pp1[1]}; // W1 * P1 - std::array w2pp2 = {w2[0] * pp2[0] + w2[1] * pp2[1], w2[1] * pp2[0] + w2[2] * pp2[1]}; // W2 * P2 - - double Y = C[0] * (w1pp1[0] + w2pp2[0]) + C[1] * (w1pp1[1] + w2pp2[1]); // Pp: weighted normalized combination of the predictions: - double Z = C[1] * (w1pp1[0] + w2pp2[0]) + C[2] * (w1pp1[1] + w2pp2[1]); // Pp = [(W1 * P1) + (W2 * P2)] / (W1 + W2) - - std::array delta = {Y - cls[0], Z - cls[1]}; // Δ = Pp - X, X: space point of cluster (Y,Z) - std::array CCp = {C[0] + static_cast(clCov[0]), C[1] + static_cast(clCov[1]), C[2] + static_cast(clCov[2])}; // Transformation of cluster covmat: CCp = C + Cov - std::array Wp = getInverseSymm2D(CCp); // Get weight matrix: Wp = CCp^-1 - - float chi2 = static_cast(delta[0] * (Wp[0] * delta[0] + Wp[1] * delta[1]) + delta[1] * (Wp[1] * delta[0] + Wp[2] * delta[1])); // chi2 = tΔ * (Wp * Δ) - - // #ifdef CA_DEBUG - LOG(info) << "Cluster_y: " << cls[0] << " Cluster_z: " << cls[1]; - LOG(info) << "\t\t- Covariance cluster: Y2: " << clCov[0] << " YZ: " << clCov[1] << " Z2: " << clCov[2]; - LOG(info) << "\t\t- Propagated t1_y: " << pp1[0] << " t1_z: " << pp1[1]; - LOG(info) << "\t\t- Propagated t2_y: " << pp2[0] << " t2_z: " << pp2[1]; - LOG(info) << "\t\t- Covariance t1: sY2: " << c1[0] << " sYZ: " << c1[1] << " sZ2: " << c1[2]; - LOG(info) << "\t\t- Covariance t2: sY2: " << c2[0] << " sYZ: " << c2[1] << " sZ2: " << c2[2]; - LOG(info) << "Smoother prediction Y: " << Y << " Z: " << Z; - LOG(info) << "\t\t- Delta_y: " << delta[0] << " Delta_z: " << delta[1]; - LOG(info) << "\t\t- Covariance Pr: Y2: " << C[0] << " YZ: " << C[1] << " Z2: " << C[2]; - LOG(info) << "\t\t- predicted chi2 t1: " << firstTrack.getPredictedChi2(cls, clCov); - LOG(info) << "\t\t- predicted chi2 t2: " << secondTrack.getPredictedChi2(cls, clCov); - // #endif - return chi2; -} - -// template -// bool Smoother::testCluster(const int clusterId, const ROframe& event) -// { -// if (!mInitStatus) { -// return false; -// } -// auto propInstance = o2::base::Propagator::Instance(); -// const TrackingFrameInfo& testTf = event.getTrackingFrameInfoOnLayer(mLayerToSmooth).at(clusterId); -// -// bool statusOutw{false}; -// bool statusInw{false}; -// -// // Prediction on the previously outwards-propagated track is done on a copy, as the process seems to be not reversible -// auto outwardsClone = mOutwardsTrack; -// statusOutw = outwardsClone.rotate(testTf.alphaTrackingFrame); -// statusOutw &= propInstance->propagateToX(outwardsClone, -// testTf.xTrackingFrame, -// mBz, -// o2::base::PropagatorImpl::MAX_SIN_PHI, -// o2::base::PropagatorImpl::MAX_STEP, -// mCorr); -// -// // Prediction on the previously inwards-propagated track is done on a copy, as the process seems to be not reversible -// auto inwardsClone = mInwardsTrack; -// statusInw = inwardsClone.rotate(testTf.alphaTrackingFrame); -// statusInw &= propInstance->propagateToX(inwardsClone, -// testTf.xTrackingFrame, -// mBz, -// o2::base::PropagatorImpl::MAX_SIN_PHI, -// o2::base::PropagatorImpl::MAX_STEP, -// mCorr); -// if (!(statusOutw && statusInw)) { -// LOG(warning) << "Failed propagation in smoother!"; -// return false; -// } -// -// // Compute weighted local chi2 -// mLastChi2 = computeSmoothedPredictedChi2(inwardsClone, outwardsClone, testTf.positionTrackingFrame, testTf.covarianceTrackingFrame); -// LOG(info) << "Smoothed chi2 on tested cluster: " << mLastChi2; -// -// return true; -// } - -template class Smoother<7>; - -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx index 29fb4ac4c69b5..5a32b3d3b1a95 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx @@ -46,82 +46,43 @@ constexpr float DefClusErrorCol = o2::itsmft::SegmentationAlpide::PitchCol * 0.5 constexpr float DefClusError2Row = DefClusErrorRow * DefClusErrorRow; constexpr float DefClusError2Col = DefClusErrorCol * DefClusErrorCol; -template -void TimeFrame::addPrimaryVertices(const bounded_vector& vertices, const int iteration) +template +void TimeFrame::addPrimaryVertex(const Vertex& vert) { - for (const auto& vertex : vertices) { - mPrimaryVertices.emplace_back(vertex); // put a copy in the present - mTotVertPerIteration[iteration]++; - if (!isBeamPositionOverridden) { // beam position is updated only at first occurrence of the vertex. A bit sketchy if we have past/future vertices, it should not impact too much. - const float w = vertex.getNContributors(); - mBeamPos[0] = (mBeamPos[0] * mBeamPosWeight + vertex.getX() * w) / (mBeamPosWeight + w); - mBeamPos[1] = (mBeamPos[1] * mBeamPosWeight + vertex.getY() * w) / (mBeamPosWeight + w); - mBeamPosWeight += w; - } - } - mROFramesPV.push_back(mPrimaryVertices.size()); // current rof must have number of vertices up to present -} - -template -void TimeFrame::addPrimaryVerticesLabels(bounded_vector>& labels) -{ - mVerticesMCRecInfo.insert(mVerticesMCRecInfo.end(), labels.begin(), labels.end()); -} - -template -void TimeFrame::addPrimaryVerticesContributorLabels(bounded_vector& labels) -{ - mVerticesContributorLabels.insert(mVerticesContributorLabels.end(), labels.begin(), labels.end()); -} - -template -void TimeFrame::addPrimaryVerticesInROF(const bounded_vector& vertices, const int rofId, const int iteration) -{ - mPrimaryVertices.insert(mPrimaryVertices.begin() + mROFramesPV[rofId], vertices.begin(), vertices.end()); - for (int i = rofId + 1; i < mROFramesPV.size(); ++i) { - mROFramesPV[i] += vertices.size(); + 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; } - mTotVertPerIteration[iteration] += vertices.size(); } -template -void TimeFrame::addPrimaryVerticesLabelsInROF(const bounded_vector>& labels, const int rofId) -{ - mVerticesMCRecInfo.insert(mVerticesMCRecInfo.begin() + mROFramesPV[rofId], labels.begin(), labels.end()); -} - -template -void TimeFrame::addPrimaryVerticesContributorLabelsInROF(const bounded_vector& labels, const int rofId) -{ - // count the number of cont. in rofs before and including the target rof - unsigned int n{0}; - const auto& pvs = getPrimaryVertices(0, rofId); - for (const auto& pv : pvs) { - n += pv.getNContributors(); - } - mVerticesContributorLabels.insert(mVerticesContributorLabels.begin() + n, labels.begin(), labels.end()); -} - -template -int TimeFrame::loadROFrameData(gsl::span rofs, - gsl::span clusters, - gsl::span::iterator& pattIt, - const itsmft::TopologyDictionary* dict, - const dataformats::MCTruthContainer* mcLabels) +template +void TimeFrame::loadROFrameData(gsl::span rofs, + gsl::span clusters, + gsl::span::iterator& pattIt, + const itsmft::TopologyDictionary* dict, + int layer, + const dataformats::MCTruthContainer* mcLabels) { GeometryTGeo* geom = GeometryTGeo::Instance(); geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); + resetROFrameData(layer); + prepareROFrameData(clusters, layer); - resetROFrameData(rofs.size()); - prepareROFrameData(rofs, clusters); + // 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 (size_t iRof{0}; iRof < rofs.size(); ++iRof) { + 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 layer = geom->getLayer(c.getSensorID()); - + int lay = geom->getLayer(c.getSensorID()); auto pattID = c.getPatternID(); o2::math_utils::Point3D locXYZ; float sigmaY2 = DefClusError2Row, sigmaZ2 = DefClusError2Col, sigmaYZ = 0; // Dummy COG errors (about half pixel size) @@ -142,85 +103,97 @@ int TimeFrame::loadROFrameData(gsl::span r locXYZ = dict->getClusterCoordinates(c, patt, false); clusterSize = patt.getNPixels(); } - mClusterSize[clusterId] = std::clamp(clusterSize, 0u, 255u); + mClusterSize[layer >= 0 ? layer : 0][clusterId] = std::clamp(clusterSize, 0u, 255u); auto sensorID = c.getSensorID(); // Inverse transformation to the local --> tracking auto trkXYZ = geom->getMatrixT2L(sensorID) ^ locXYZ; // Transformation to the local --> global auto gloXYZ = geom->getMatrixL2G(sensorID) * locXYZ; - - addTrackingFrameInfoToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), trkXYZ.x(), geom->getSensorRefAlpha(sensorID), + addTrackingFrameInfoToLayer(lay, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), trkXYZ.x(), geom->getSensorRefAlpha(sensorID), std::array{trkXYZ.y(), trkXYZ.z()}, std::array{sigmaY2, sigmaYZ, sigmaZ2}); /// Rotate to the global frame - addClusterToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), mUnsortedClusters[layer].size()); - addClusterExternalIndexToLayer(layer, clusterId); + addClusterToLayer(lay, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), mUnsortedClusters[lay].size()); + addClusterExternalIndexToLayer(lay, clusterId); } - for (unsigned int iL{0}; iL < mUnsortedClusters.size(); ++iL) { - mROFramesClusters[iL][iRof + 1] = mUnsortedClusters[iL].size(); // effectively calculating and exclusive sum + // 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(); + } } } - for (auto i = 0; i < mNTrackletsPerCluster.size(); ++i) { - mNTrackletsPerCluster[i].resize(mUnsortedClusters[1].size()); - mNTrackletsPerClusterSum[i].resize(mUnsortedClusters[1].size() + 1); // Exc sum "prepends" a 0 + 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 = mcLabels; + mClusterLabels[layer >= 0 ? layer : 0] = mcLabels; + } else { + mClusterLabels[layer >= 0 ? layer : 0] = nullptr; } - - return mNrof; } -template -void TimeFrame::resetROFrameData(size_t nRofs) +template +void TimeFrame::resetROFrameData(int layer) { - for (int iLayer{0}; iLayer < nLayers; ++iLayer) { - deepVectorClear(mUnsortedClusters[iLayer], getMaybeFrameworkHostResource()); - deepVectorClear(mTrackingFrameInfo[iLayer], getMaybeFrameworkHostResource()); - clearResizeBoundedVector(mROFramesClusters[iLayer], nRofs + 1, getMaybeFrameworkHostResource()); - deepVectorClear(mClusterExternalIndices[iLayer], mMemoryPool.get()); - - if (iLayer < 2) { - deepVectorClear(mTrackletsIndexROF[iLayer], mMemoryPool.get()); - deepVectorClear(mNTrackletsPerCluster[iLayer], mMemoryPool.get()); - deepVectorClear(mNTrackletsPerClusterSum[iLayer], mMemoryPool.get()); + 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 rofs, - gsl::span clusters) +template +void TimeFrame::prepareROFrameData(gsl::span clusters, int layer) { - GeometryTGeo* geom = GeometryTGeo::Instance(); - mNrof = rofs.size(); - clearResizeBoundedVector(mClusterSize, clusters.size(), mMemoryPool.get()); - std::array clusterCountPerLayer{}; - for (const auto& clus : clusters) { - ++clusterCountPerLayer[geom->getLayer(clus.getSensorID())]; - } - for (int iLayer{0}; iLayer < nLayers; ++iLayer) { - mUnsortedClusters[iLayer].reserve(clusterCountPerLayer[iLayer]); - mTrackingFrameInfo[iLayer].reserve(clusterCountPerLayer[iLayer]); - mClusterExternalIndices[iLayer].reserve(clusterCountPerLayer[iLayer]); + 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 { + auto* geom = GeometryTGeo::Instance(); + clearResizeBoundedVector(mClusterSize[0], clusters.size(), mMemoryPool.get()); + std::array clusterCountPerLayer{0}; + for (const auto& cls : clusters) { + ++clusterCountPerLayer[geom->getLayer(cls.getChipID())]; + } + 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) +template +void TimeFrame::prepareClusters(const TrackingParameters& trkParam, const int maxLayers) { const int numBins{trkParam.PhiBins * trkParam.ZBins}; 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 rof{0}; rof < mNrof; ++rof) { - if ((int)mMultiplicityCutMask.size() == mNrof && !mMultiplicityCutMask[rof]) { - continue; - } - for (int iLayer{0}, stopLayer = std::min(trkParam.NLayers, maxLayers); iLayer < stopLayer; ++iLayer) { + 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; @@ -270,25 +243,19 @@ void TimeFrame::prepareClusters(const TrackingParameters& trkParam, con } } -template -void TimeFrame::initialise(const int iteration, const TrackingParameters& trkParam, const int maxLayers, bool resetVertices) +template +void TimeFrame::initialise(const int iteration, const TrackingParameters& trkParam, const int maxLayers, bool resetVertices) { if (iteration == 0) { - if (maxLayers < trkParam.NLayers && resetVertices) { - resetRofPV(); - deepVectorClear(mTotVertPerIteration); - } deepVectorClear(mTracks); deepVectorClear(mTracksLabel); deepVectorClear(mLines); deepVectorClear(mLinesLabels); if (resetVertices) { - deepVectorClear(mVerticesMCRecInfo); - deepVectorClear(mVerticesContributorLabels); + deepVectorClear(mPrimaryVertices); + deepVectorClear(mPrimaryVerticesLabels); } - clearResizeBoundedVector(mTracks, mNrof, mMemoryPool.get()); - clearResizeBoundedVector(mTracksLabel, mNrof, mMemoryPool.get()); - clearResizeBoundedVector(mLinesLabels, mNrof, mMemoryPool.get()); + clearResizeBoundedVector(mLinesLabels, getNrof(1), mMemoryPool.get()); clearResizeBoundedVector(mCells, trkParam.CellsPerRoad(), mMemoryPool.get()); clearResizeBoundedVector(mCellsLookupTable, trkParam.CellsPerRoad() - 1, mMemoryPool.get()); clearResizeBoundedVector(mCellsNeighbours, trkParam.CellsPerRoad() - 1, mMemoryPool.get()); @@ -302,14 +269,16 @@ void TimeFrame::initialise(const int iteration, const TrackingParameter 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)); + 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.SystErrorZ2[iLayer] + trkParam.SystErrorY2[iLayer]) + trkParam.LayerResolution[iLayer] * trkParam.LayerResolution[iLayer]); } - clearResizeBoundedArray(mIndexTables, mNrof * (trkParam.ZBins * trkParam.PhiBins + 1), getMaybeFrameworkHostResource(maxLayers != nLayers)); - clearResizeBoundedVector(mLines, mNrof, mMemoryPool.get()); - clearResizeBoundedVector(mTrackletClusters, mNrof, mMemoryPool.get()); + 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.ZBins * trkParam.PhiBins) + 1), getMaybeFrameworkHostResource()); + } for (int iLayer{0}; iLayer < trkParam.NLayers; ++iLayer) { if (trkParam.SystErrorY2[iLayer] > 0.f || trkParam.SystErrorZ2[iLayer] > 0.f) { for (auto& tfInfo : mTrackingFrameInfo[iLayer]) { @@ -319,12 +288,13 @@ void TimeFrame::initialise(const int iteration, const TrackingParameter } } } - mMinR.fill(10000.); - mMaxR.fill(-1.); + + mMinR.fill(std::numeric_limits::max()); + mMaxR.fill(std::numeric_limits::min()); } mNTrackletsPerROF.resize(2); for (auto& v : mNTrackletsPerROF) { - v = bounded_vector(mNrof + 1, 0, mMemoryPool.get()); + v = bounded_vector(getNrof(1) + 1, 0, mMemoryPool.get()); } if (iteration == 0 || iteration == 3) { prepareClusters(trkParam, maxLayers); @@ -337,15 +307,10 @@ void TimeFrame::initialise(const int iteration, const TrackingParameter } } - mTotVertPerIteration.resize(1 + iteration); - mNoVertexROF = 0; - deepVectorClear(mRoads); - deepVectorClear(mRoadLabels); - mMSangles.resize(trkParam.NLayers); mPhiCuts.resize(mClusters.size() - 1, 0.f); float oneOverR{0.001f * 0.3f * std::abs(mBz) / trkParam.TrackletMinPt}; - for (unsigned int iLayer{0}; iLayer < nLayers; ++iLayer) { + for (unsigned int iLayer{0}; iLayer < NLayers; ++iLayer) { mMSangles[iLayer] = math_utils::MSangle(0.14f, trkParam.TrackletMinPt, trkParam.LayerxX0[iLayer]); mPositionResolution[iLayer] = o2::gpu::CAMath::Sqrt(0.5f * (trkParam.SystErrorZ2[iLayer] + trkParam.SystErrorY2[iLayer]) + trkParam.LayerResolution[iLayer] * trkParam.LayerResolution[iLayer]); if (iLayer < mClusters.size() - 1) { @@ -381,8 +346,8 @@ void TimeFrame::initialise(const int iteration, const TrackingParameter } } -template -unsigned long TimeFrame::getArtefactsMemory() const +template +unsigned long TimeFrame::getArtefactsMemory() const { unsigned long size{0}; for (const auto& trkl : mTracklets) { @@ -394,31 +359,21 @@ unsigned long TimeFrame::getArtefactsMemory() const for (const auto& cellsN : mCellsNeighbours) { size += sizeof(int) * cellsN.size(); } - return size + sizeof(Road) * mRoads.size(); + return size; } -template -void TimeFrame::printArtefactsMemory() const +template +void TimeFrame::printArtefactsMemory() const { LOGP(info, "TimeFrame: Artefacts occupy {:.2f} MB", getArtefactsMemory() / constants::MB); } -template -void TimeFrame::fillPrimaryVerticesXandAlpha() -{ - deepVectorClear(mPValphaX); - mPValphaX.reserve(mPrimaryVertices.size()); - for (auto& pv : mPrimaryVertices) { - mPValphaX.emplace_back(std::array{o2::gpu::CAMath::Hypot(pv.getX(), pv.getY()), math_utils::computePhi(pv.getX(), pv.getY())}); - } -} - -template -void TimeFrame::computeTrackletsPerROFScans() +template +void TimeFrame::computeTrackletsPerROFScans() { for (ushort iLayer = 0; iLayer < 2; ++iLayer) { - for (unsigned int iRof{0}; iRof < mNrof; ++iRof) { - if (mMultiplicityCutMask[iRof]) { + for (unsigned int iRof{0}; iRof < getNrof(1); ++iRof) { + if (mROFMaskView.isROFEnabled(1, iRof)) { mTotalTracklets[iLayer] += mNTrackletsPerROF[iLayer][iRof]; } } @@ -427,141 +382,8 @@ void TimeFrame::computeTrackletsPerROFScans() } } -template -void TimeFrame::checkTrackletLUTs() -{ - for (uint32_t iLayer{0}; iLayer < getTracklets().size(); ++iLayer) { - int prev{-1}; - int count{0}; - for (uint32_t iTracklet{0}; iTracklet < getTracklets()[iLayer].size(); ++iTracklet) { - auto& trk = getTracklets()[iLayer][iTracklet]; - int currentId{trk.firstClusterIndex}; - if (currentId < prev) { - LOG(info) << "First Cluster Index not increasing monotonically on L:T:ID:Prev " << iLayer << "\t" << iTracklet << "\t" << currentId << "\t" << prev; - } else if (currentId == prev) { - count++; - } else { - if (iLayer > 0) { - auto& lut{getTrackletsLookupTable()[iLayer - 1]}; - if (count != lut[prev + 1] - lut[prev]) { - LOG(info) << "LUT count broken " << iLayer - 1 << "\t" << prev << "\t" << count << "\t" << lut[prev + 1] << "\t" << lut[prev]; - } - } - count = 1; - } - prev = currentId; - if (iLayer > 0) { - auto& lut{getTrackletsLookupTable()[iLayer - 1]}; - if (iTracklet >= (uint32_t)(lut[currentId + 1]) || iTracklet < (uint32_t)(lut[currentId])) { - LOG(info) << "LUT broken: " << iLayer - 1 << "\t" << currentId << "\t" << iTracklet; - } - } - } - } -} - -template -void TimeFrame::printTrackletLUTonLayer(int i) -{ - LOG(info) << "-------- Tracklet LUT " << i; - std::stringstream s; - for (int j : mTrackletsLookupTable[i]) { - s << j << "\t"; - } - LOG(info) << s.str(); - LOG(info) << "--------"; -} - -template -void TimeFrame::printCellLUTonLayer(int i) -{ - LOG(info) << "-------- Cell LUT " << i; - std::stringstream s; - for (int j : mCellsLookupTable[i]) { - s << j << "\t"; - } - LOG(info) << s.str(); - LOG(info) << "--------"; -} - -template -void TimeFrame::printTrackletLUTs() -{ - for (unsigned int i{0}; i < mTrackletsLookupTable.size(); ++i) { - printTrackletLUTonLayer(i); - } -} - -template -void TimeFrame::printCellLUTs() -{ - for (unsigned int i{0}; i < mCellsLookupTable.size(); ++i) { - printCellLUTonLayer(i); - } -} - -template -void TimeFrame::printVertices() -{ - LOG(info) << "Vertices in ROF (nROF = " << mNrof << ", lut size = " << mROFramesPV.size() << ")"; - for (unsigned int iR{0}; iR < mROFramesPV.size(); ++iR) { - LOG(info) << mROFramesPV[iR] << "\t"; - } - LOG(info) << "\n\n Vertices:"; - for (unsigned int iV{0}; iV < mPrimaryVertices.size(); ++iV) { - LOG(info) << mPrimaryVertices[iV].getX() << "\t" << mPrimaryVertices[iV].getY() << "\t" << mPrimaryVertices[iV].getZ(); - } - LOG(info) << "--------"; -} - -template -void TimeFrame::printROFoffsets() -{ - LOG(info) << "--------"; - for (unsigned int iLayer{0}; iLayer < mROFramesClusters.size(); ++iLayer) { - LOG(info) << "Layer " << iLayer; - std::stringstream s; - for (auto value : mROFramesClusters[iLayer]) { - s << value << "\t"; - } - LOG(info) << s.str(); - } -} - -template -void TimeFrame::printNClsPerROF() -{ - LOG(info) << "--------"; - for (unsigned int iLayer{0}; iLayer < mNClustersPerROF.size(); ++iLayer) { - LOG(info) << "Layer " << iLayer; - std::stringstream s; - for (auto& value : mNClustersPerROF[iLayer]) { - s << value << "\t"; - } - LOG(info) << s.str(); - } -} - -template -void TimeFrame::printSliceInfo(const int startROF, const int sliceSize) -{ - LOG(info) << "Dumping slice of " << sliceSize << " rofs:"; - for (int iROF{startROF}; iROF < startROF + sliceSize; ++iROF) { - LOG(info) << "ROF " << iROF << " dump:"; - for (unsigned int iLayer{0}; iLayer < mClusters.size(); ++iLayer) { - LOG(info) << "Layer " << iLayer << " has: " << getClustersOnLayer(iROF, iLayer).size() << " clusters."; - } - LOG(info) << "Number of seeding vertices: " << getPrimaryVertices(iROF).size(); - int iVertex{0}; - for (auto& v : getPrimaryVertices(iROF)) { - LOG(info) << "\t vertex " << iVertex++ << ": x=" << v.getX() << " " - << " y=" << v.getY() << " z=" << v.getZ() << " has " << v.getNContributors() << " contributors."; - } - } -} - -template -void TimeFrame::setMemoryPool(std::shared_ptr pool) +template +void TimeFrame::setMemoryPool(std::shared_ptr pool) { mMemoryPool = pool; @@ -577,33 +399,29 @@ void TimeFrame::setMemoryPool(std::shared_ptr po }; // these will only reside on the host for the cpu part - initVector(mTotVertPerIteration); initContainers(mClusterExternalIndices); initContainers(mNTrackletsPerCluster); initContainers(mNTrackletsPerClusterSum); initContainers(mNClustersPerROF); - initVector(mROFramesPV); initVector(mPrimaryVertices); - initVector(mRoads); initVector(mMSangles); initVector(mPhiCuts); initVector(mPositionResolution); - initVector(mClusterSize); + initContainers(mClusterSize); initVector(mPValphaX); initVector(mBogusClusters); initContainers(mTrackletsIndexROF); - initContainers(mTracks); + initVector(mTracks); initContainers(mTracklets); initContainers(mCells); initContainers(mCellsNeighbours); initContainers(mCellsLookupTable); // MC info (we don't know if we have MC) - initVector(mVerticesContributorLabels); + initVector(mPrimaryVerticesLabels); initContainers(mLinesLabels); initContainers(mTrackletLabels); initContainers(mCellLabels); - initVector(mRoadLabels); - initContainers(mTracksLabel); + initVector(mTracksLabel); // these will use possibly an externally provided allocator initContainers(mClusters, hasFrameworkAllocator()); initContainers(mUsedClusters, hasFrameworkAllocator()); @@ -613,30 +431,27 @@ void TimeFrame::setMemoryPool(std::shared_ptr po initContainers(mROFramesClusters, hasFrameworkAllocator()); } -template -void TimeFrame::setFrameworkAllocator(ExternalAllocator* ext) +template +void TimeFrame::setFrameworkAllocator(ExternalAllocator* ext) { mExternalAllocator = ext; mExtMemoryPool = std::make_shared(mExternalAllocator); } -template -void TimeFrame::wipe() +template +void TimeFrame::wipe() { deepVectorClear(mTracks); deepVectorClear(mTracklets); deepVectorClear(mCells); - deepVectorClear(mRoads); deepVectorClear(mCellsNeighbours); deepVectorClear(mCellsLookupTable); - deepVectorClear(mTotVertPerIteration); deepVectorClear(mPrimaryVertices); deepVectorClear(mTrackletsLookupTable); deepVectorClear(mClusterExternalIndices); deepVectorClear(mNTrackletsPerCluster); deepVectorClear(mNTrackletsPerClusterSum); deepVectorClear(mNClustersPerROF); - deepVectorClear(mROFramesPV); deepVectorClear(mMSangles); deepVectorClear(mPhiCuts); deepVectorClear(mPositionResolution); @@ -659,10 +474,9 @@ void TimeFrame::wipe() // only needed to clear if we have MC info if (hasMCinformation()) { deepVectorClear(mLinesLabels); - deepVectorClear(mVerticesContributorLabels); + deepVectorClear(mPrimaryVerticesLabels); deepVectorClear(mTrackletLabels); deepVectorClear(mCellLabels); - deepVectorClear(mRoadLabels); deepVectorClear(mTracksLabel); } } diff --git a/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx b/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx index 658a90b37613f..dc032a46213a9 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx @@ -14,28 +14,22 @@ /// #include "ITStracking/Tracker.h" - #include "ITStracking/BoundedAllocator.h" -#include "ITStracking/Cell.h" #include "ITStracking/Constants.h" -#include "ITStracking/IndexTableUtils.h" -#include "ITStracking/Tracklet.h" #include "ITStracking/TrackerTraits.h" #include "ITStracking/TrackingConfigParam.h" -#include "ReconstructionDataFormats/Track.h" #include #include #include #include -#include namespace o2::its { using o2::its::constants::GB; -template -Tracker::Tracker(TrackerTraits* traits) : mTraits(traits) +template +Tracker::Tracker(TrackerTraits* traits) : mTraits(traits) { /// Initialise standard configuration with 1 iteration mTrkParams.resize(1); @@ -45,27 +39,26 @@ Tracker::Tracker(TrackerTraits* traits) : mTraits(traits) } } -template -void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& error) +template +void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& error) { LogFunc evalLog = [](const std::string&) {}; double total{0}; mTraits->updateTrackingParameters(mTrkParams); + mTimeFrame->updateROFVertexLookupTable(); + int maxNvertices{-1}; if (mTrkParams[0].PerPrimaryVertexProcessing) { - for (int iROF{0}; iROF < mTimeFrame->getNrof(); ++iROF) { - int minRof = o2::gpu::CAMath::Max(0, iROF - mTrkParams[0].DeltaROF); - int maxRof = o2::gpu::CAMath::Min(mTimeFrame->getNrof(), iROF + mTrkParams[0].DeltaROF); - maxNvertices = std::max(maxNvertices, (int)mTimeFrame->getPrimaryVertices(minRof, maxRof).size()); - } + maxNvertices = mTimeFrame->getROFVertexLookupTableView().getMaxVerticesPerROF(); } - int iteration{0}, iROFs{0}, iVertex{0}; + int iteration{0}, iVertex{0}; auto handleException = [&](const auto& err) { - LOGP(error, "Too much memory used during {} in iteration {} in ROF span {}-{} iVtx={}: {:.2f} GB. Current limit is {:.2f} GB, check the detector status and/or the selections.", - StateNames[mCurState], iteration, iROFs, iROFs + mTrkParams[iteration].nROFsPerIterations, iVertex, - (double)mTimeFrame->getArtefactsMemory() / GB, (double)mTrkParams[iteration].MaxMemory / GB); + LOGP(error, "Too much memory in {} in iteration {} iVtx={}: {:.2f} GB. Current limit is {:.2f} GB, check the detector status and/or the selections.", + StateNames[mCurState], iteration, iVertex, + (double)mTimeFrame->getArtefactsMemory() / GB, + (double)mTrkParams[iteration].MaxMemory / GB); if (typeid(err) != typeid(std::bad_alloc)) { // only print if the exceptions is different from what is expected LOGP(error, "Exception: {}", err.what()); } @@ -73,7 +66,7 @@ void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& er mMemoryPool->print(); mTimeFrame->wipe(); ++mNumberOfDroppedTFs; - error("...Dropping Timeframe..."); + error(std::format("...Dropping TimeSlice {} (out of {} dropped {})...", mTimeSlice, mTimeFrameCounter, mNumberOfDroppedTFs)); } else { throw err; } @@ -83,61 +76,34 @@ void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& er for (iteration = 0; iteration < (int)mTrkParams.size(); ++iteration) { mMemoryPool->setMaxMemory(mTrkParams[iteration].MaxMemory); if (iteration == 3 && mTrkParams[0].DoUPCIteration) { - mTimeFrame->swapMasks(); + mTimeFrame->useUPCMask(); } - double timeTracklets{0.}, timeCells{0.}, timeNeighbours{0.}, timeRoads{0.}; - int nTracklets{0}, nCells{0}, nNeighbours{0}, nTracks{-static_cast(mTimeFrame->getNumberOfTracks())}; - int nROFsIterations = (mTrkParams[iteration].nROFsPerIterations > 0 && !mTimeFrame->isGPU()) ? mTimeFrame->getNrof() / mTrkParams[iteration].nROFsPerIterations + bool(mTimeFrame->getNrof() % mTrkParams[iteration].nROFsPerIterations) : 1; + float timeTracklets{0.}, timeCells{0.}, timeNeighbours{0.}, timeRoads{0.}; + size_t nTracklets{0}, nCells{0}, nNeighbours{0}; + int nTracks{-static_cast(mTimeFrame->getNumberOfTracks())}; iVertex = std::min(maxNvertices, 0); logger(std::format("==== ITS {} Tracking iteration {} summary ====", mTraits->getName(), iteration)); - total += evaluateTask(&Tracker::initialiseTimeFrame, StateNames[mCurState = TFInit], iteration, logger, iteration); do { - for (iROFs = 0; iROFs < nROFsIterations; ++iROFs) { - timeTracklets += evaluateTask(&Tracker::computeTracklets, StateNames[mCurState = Trackleting], iteration, evalLog, iteration, iROFs, iVertex); - nTracklets += mTraits->getTFNumberOfTracklets(); - float trackletsPerCluster = mTraits->getTFNumberOfClusters() > 0 ? float(mTraits->getTFNumberOfTracklets()) / float(mTraits->getTFNumberOfClusters()) : 0.f; - if (trackletsPerCluster > mTrkParams[iteration].TrackletsPerClusterLimit) { - error(std::format("Too many tracklets per cluster ({}) in iteration {} in ROF span {}-{}:, check the detector status and/or the selections. Current limit is {}", - trackletsPerCluster, iteration, iROFs, iROFs + mTrkParams[iteration].nROFsPerIterations, mTrkParams[iteration].TrackletsPerClusterLimit)); - break; - } - timeCells += evaluateTask(&Tracker::computeCells, StateNames[mCurState = Celling], iteration, evalLog, iteration); - nCells += mTraits->getTFNumberOfCells(); - float cellsPerCluster = mTraits->getTFNumberOfClusters() > 0 ? float(mTraits->getTFNumberOfCells()) / float(mTraits->getTFNumberOfClusters()) : 0.f; - if (cellsPerCluster > mTrkParams[iteration].CellsPerClusterLimit) { - error(std::format("Too many cells per cluster ({}) in iteration {} in ROF span {}-{}, check the detector status and/or the selections. Current limit is {}", - cellsPerCluster, iteration, iROFs, iROFs + mTrkParams[iteration].nROFsPerIterations, mTrkParams[iteration].CellsPerClusterLimit)); - break; - } - timeNeighbours += evaluateTask(&Tracker::findCellsNeighbours, StateNames[mCurState = Neighbouring], iteration, evalLog, iteration); - nNeighbours += mTimeFrame->getNumberOfNeighbours(); - timeRoads += evaluateTask(&Tracker::findRoads, StateNames[mCurState = Roading], iteration, evalLog, iteration); - } + timeTracklets += evaluateTask(&Tracker::computeTracklets, StateNames[mCurState = Trackleting], iteration, evalLog, iteration, iVertex); + nTracklets += mTraits->getTFNumberOfTracklets(); + timeCells += evaluateTask(&Tracker::computeCells, StateNames[mCurState = Celling], iteration, evalLog, iteration); + nCells += mTraits->getTFNumberOfCells(); + timeNeighbours += evaluateTask(&Tracker::findCellsNeighbours, StateNames[mCurState = Neighbouring], iteration, evalLog, iteration); + nNeighbours += mTimeFrame->getNumberOfNeighbours(); + timeRoads += evaluateTask(&Tracker::findRoads, StateNames[mCurState = Roading], iteration, evalLog, iteration); } while (++iVertex < maxNvertices); logger(std::format(" - Tracklet finding: {} tracklets found in {:.2f} ms", nTracklets, timeTracklets)); logger(std::format(" - Cell finding: {} cells found in {:.2f} ms", nCells, timeCells)); logger(std::format(" - Neighbours finding: {} neighbours found in {:.2f} ms", nNeighbours, timeNeighbours)); logger(std::format(" - Track finding: {} tracks found in {:.2f} ms", nTracks + mTimeFrame->getNumberOfTracks(), timeRoads)); total += timeTracklets + timeCells + timeNeighbours + timeRoads; - if (mTraits->supportsExtendTracks() && mTrkParams[iteration].UseTrackFollower) { - int nExtendedTracks{-mTimeFrame->mNExtendedTracks}, nExtendedClusters{-mTimeFrame->mNExtendedUsedClusters}; - auto timeExtending = evaluateTask(&Tracker::extendTracks, "Extending tracks", iteration, evalLog, iteration); - total += timeExtending; - logger(std::format(" - Extending Tracks: {} extended tracks using {} clusters found in {:.2f} ms", nExtendedTracks + mTimeFrame->mNExtendedTracks, nExtendedClusters + mTimeFrame->mNExtendedUsedClusters, timeExtending)); - } if (mTrkParams[iteration].PrintMemory) { mMemoryPool->print(); } } - if (mTraits->supportsFindShortPrimaries() && mTrkParams[0].FindShortTracks) { - auto nTracksB = mTimeFrame->getNumberOfTracks(); - total += evaluateTask(&Tracker::findShortPrimaries, "Short primaries finding", 0, logger); - auto nTracksA = mTimeFrame->getNumberOfTracks(); - logger(std::format(" `-> found {} additional tracks", nTracksA - nTracksB)); - } if constexpr (constants::DoTimeBenchmarks) { - logger(std::format("=== TimeFrame {} processing completed in: {:.2f} ms using {} thread(s) ===", mTimeFrameCounter, total, mTraits->getNThreads())); + logger(std::format("=== TimeSlice {} processing completed in: {:.2f} ms using {} thread(s) ===", mTimeSlice, total, mTraits->getNThreads())); } } catch (const BoundedMemoryResource::MemoryLimitExceeded& err) { handleException(err); @@ -148,9 +114,7 @@ void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& er } catch (const std::exception& err) { error(std::format("Uncaught exception, all bets are off... {}", err.what())); // clear tracks explicitly since if not fatalising on exception this may contain partial output - for (int iROF{0}; iROF < mTimeFrame->getNrof(); ++iROF) { - mTimeFrame->getTracks(iROF).clear(); - } + mTimeFrame->getTracks().clear(); return; } @@ -158,6 +122,8 @@ void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& er computeTracksMClabels(); } rectifyClusterIndices(); + sortTracks(); + ++mTimeFrameCounter; mTotalTime += total; @@ -167,88 +133,23 @@ void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& er } } -template -void Tracker::computeRoadsMClabels() +template +void Tracker::computeTracksMClabels() { - /// Moore's Voting Algorithm - if (!mTimeFrame->hasMCinformation()) { - return; - } - - mTimeFrame->initialiseRoadLabels(); - - int roadsNum{static_cast(mTimeFrame->getRoads().size())}; - - for (int iRoad{0}; iRoad < roadsNum; ++iRoad) { - - auto& currentRoad{mTimeFrame->getRoads()[iRoad]}; + for (auto& track : mTimeFrame->getTracks()) { std::vector> occurrences; - bool isFakeRoad{false}; - bool isFirstRoadCell{true}; - - for (int iCell{0}; iCell < mTrkParams[0].CellsPerRoad(); ++iCell) { - const int currentCellIndex{currentRoad[iCell]}; - - if (currentCellIndex == constants::UnusedIndex) { - if (isFirstRoadCell) { - continue; - } else { - break; - } - } - - const auto& currentCell{mTimeFrame->getCells()[iCell][currentCellIndex]}; - - if (isFirstRoadCell) { - - const int cl0index{mTimeFrame->getClusters()[iCell][currentCell.getFirstClusterIndex()].clusterId}; - auto cl0labs{mTimeFrame->getClusterLabels(iCell, cl0index)}; - bool found{false}; - for (size_t iOcc{0}; iOcc < occurrences.size(); ++iOcc) { - std::pair& occurrence = occurrences[iOcc]; - for (const auto& label : cl0labs) { - if (label == occurrence.first) { - ++occurrence.second; - found = true; - // break; // uncomment to stop to the first hit - } - } - } - if (!found) { - for (const auto& label : cl0labs) { - occurrences.emplace_back(label, 1); - } - } + occurrences.clear(); - const int cl1index{mTimeFrame->getClusters()[iCell + 1][currentCell.getSecondClusterIndex()].clusterId}; - - const auto& cl1labs{mTimeFrame->getClusterLabels(iCell + 1, cl1index)}; - found = false; - for (size_t iOcc{0}; iOcc < occurrences.size(); ++iOcc) { - std::pair& occurrence = occurrences[iOcc]; - for (auto& label : cl1labs) { - if (label == occurrence.first) { - ++occurrence.second; - found = true; - // break; // uncomment to stop to the first hit - } - } - } - if (!found) { - for (auto& label : cl1labs) { - occurrences.emplace_back(label, 1); - } - } - - isFirstRoadCell = false; + for (int iCluster = 0; iCluster < TrackITSExt::MaxClusters; ++iCluster) { + const int index = track.getClusterIndex(iCluster); + if (index == constants::UnusedIndex) { + continue; } - - const int cl2index{mTimeFrame->getClusters()[iCell + 2][currentCell.getThirdClusterIndex()].clusterId}; - const auto& cl2labs{mTimeFrame->getClusterLabels(iCell + 2, cl2index)}; + auto labels = mTimeFrame->getClusterLabels(iCluster, index); bool found{false}; for (size_t iOcc{0}; iOcc < occurrences.size(); ++iOcc) { std::pair& occurrence = occurrences[iOcc]; - for (auto& label : cl2labs) { + for (const auto& label : labels) { if (label == occurrence.first) { ++occurrence.second; found = true; @@ -257,104 +158,94 @@ void Tracker::computeRoadsMClabels() } } if (!found) { - for (auto& label : cl2labs) { + for (const auto& label : labels) { occurrences.emplace_back(label, 1); } } } - - std::sort(occurrences.begin(), occurrences.end(), [](auto e1, auto e2) { + std::sort(std::begin(occurrences), std::end(occurrences), [](auto e1, auto e2) { return e1.second > e2.second; }); auto maxOccurrencesValue = occurrences[0].first; - mTimeFrame->setRoadLabel(iRoad, maxOccurrencesValue.getRawValue(), isFakeRoad); - } -} - -template -void Tracker::computeTracksMClabels() -{ - for (int iROF{0}; iROF < mTimeFrame->getNrof(); ++iROF) { - for (auto& track : mTimeFrame->getTracks(iROF)) { - std::vector> occurrences; - occurrences.clear(); - - for (int iCluster = 0; iCluster < TrackITSExt::MaxClusters; ++iCluster) { - const int index = track.getClusterIndex(iCluster); - if (index == constants::UnusedIndex) { - continue; - } - auto labels = mTimeFrame->getClusterLabels(iCluster, index); - bool found{false}; - for (size_t iOcc{0}; iOcc < occurrences.size(); ++iOcc) { - std::pair& occurrence = occurrences[iOcc]; - for (const auto& label : labels) { - if (label == occurrence.first) { - ++occurrence.second; - found = true; - // break; // uncomment to stop to the first hit - } - } - } - if (!found) { - for (const auto& label : labels) { - occurrences.emplace_back(label, 1); + uint32_t pattern = track.getPattern(); + // set fake clusters pattern + for (int ic{TrackITSExt::MaxClusters}; ic--;) { + auto clid = track.getClusterIndex(ic); + if (clid != constants::UnusedIndex) { + auto labelsSpan = mTimeFrame->getClusterLabels(ic, clid); + for (const auto& currentLabel : labelsSpan) { + if (currentLabel == maxOccurrencesValue) { + pattern |= 0x1 << (16 + ic); // set bit if correct + break; } } } - std::sort(std::begin(occurrences), std::end(occurrences), [](auto e1, auto e2) { - return e1.second > e2.second; - }); + } + track.setPattern(pattern); + if (occurrences[0].second < track.getNumberOfClusters()) { + maxOccurrencesValue.setFakeFlag(); + } + mTimeFrame->getTracksLabel().emplace_back(maxOccurrencesValue); + } +} - auto maxOccurrencesValue = occurrences[0].first; - uint32_t pattern = track.getPattern(); - // set fake clusters pattern - for (int ic{TrackITSExt::MaxClusters}; ic--;) { - auto clid = track.getClusterIndex(ic); - if (clid != constants::UnusedIndex) { - auto labelsSpan = mTimeFrame->getClusterLabels(ic, clid); - for (const auto& currentLabel : labelsSpan) { - if (currentLabel == maxOccurrencesValue) { - pattern |= 0x1 << (16 + ic); // set bit if correct - break; - } - } - } - } - track.setPattern(pattern); - if (occurrences[0].second < track.getNumberOfClusters()) { - maxOccurrencesValue.setFakeFlag(); +template +void Tracker::rectifyClusterIndices() +{ + for (auto& track : mTimeFrame->getTracks()) { + for (int iCluster = 0; iCluster < TrackITSExt::MaxClusters; ++iCluster) { + const int index = track.getClusterIndex(iCluster); + if (index != constants::UnusedIndex) { + track.setExternalClusterIndex(iCluster, mTimeFrame->getClusterExternalIndex(iCluster, index)); } - mTimeFrame->getTracksLabel(iROF).emplace_back(maxOccurrencesValue); } } } -template -void Tracker::rectifyClusterIndices() +template +void Tracker::sortTracks() { - for (int iROF{0}; iROF < mTimeFrame->getNrof(); ++iROF) { - for (auto& track : mTimeFrame->getTracks(iROF)) { - for (int iCluster = 0; iCluster < TrackITSExt::MaxClusters; ++iCluster) { - const int index = track.getClusterIndex(iCluster); - if (index != constants::UnusedIndex) { - track.setExternalClusterIndex(iCluster, mTimeFrame->getClusterExternalIndex(iCluster, index)); - } - } + auto& trks = mTimeFrame->getTracks(); + bounded_vector indices(trks.size(), mMemoryPool.get()); + std::iota(indices.begin(), indices.end(), 0); + std::sort(indices.begin(), indices.end(), [&trks](size_t i, size_t j) { + // provide tracks sorted by lower-bound + const auto& a = trks[i]; + const auto& b = trks[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.isBetter(b, 1e9); // then sort tracks in quality + }); + bounded_vector sortedTrks(mMemoryPool.get()); + sortedTrks.reserve(trks.size()); + for (size_t idx : indices) { + sortedTrks.push_back(trks[idx]); + } + trks.swap(sortedTrks); + if (mTimeFrame->hasMCinformation()) { + auto& trksLabels = mTimeFrame->getTracksLabel(); + bounded_vector sortedLabels(mMemoryPool.get()); + sortedLabels.reserve(trksLabels.size()); + for (size_t idx : indices) { + sortedLabels.push_back(trksLabels[idx]); } + trksLabels.swap(sortedLabels); } } -template -void Tracker::adoptTimeFrame(TimeFrame& tf) +template +void Tracker::adoptTimeFrame(TimeFrame& tf) { mTimeFrame = &tf; mTraits->adoptTimeFrame(&tf); } -template -void Tracker::printSummary() const +template +void Tracker::printSummary() const { auto avgTF = mTotalTime * 1.e-3 / ((mTimeFrameCounter > 0) ? (double)mTimeFrameCounter : -1.0); auto avgTFwithDropped = mTotalTime * 1.e-3 / (((mTimeFrameCounter + mNumberOfDroppedTFs) > 0) ? (double)(mTimeFrameCounter + mNumberOfDroppedTFs) : -1.0); diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx index da7c9afdd3ed6..f996c0d25e7d7 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx @@ -14,27 +14,22 @@ /// #include -#include #include #include #include -#ifdef OPTIMISATION_OUTPUT -#include -#include -#endif - #include #include #include "CommonConstants/MathConstants.h" #include "DetectorsBase/Propagator.h" #include "GPUCommonMath.h" +#include "ITStracking/BoundedAllocator.h" #include "ITStracking/Cell.h" #include "ITStracking/Constants.h" -#include "ITStracking/TrackerTraits.h" -#include "ITStracking/BoundedAllocator.h" #include "ITStracking/IndexTableUtils.h" +#include "ITStracking/ROFLookupTables.h" +#include "ITStracking/TrackerTraits.h" #include "ITStracking/Tracklet.h" #include "ReconstructionDataFormats/Track.h" @@ -49,42 +44,38 @@ struct PassMode { using TwoPassInsert = std::integral_constant; }; -template -void TrackerTraits::computeLayerTracklets(const int iteration, int iROFslice, int iVertex) +template +void TrackerTraits::computeLayerTracklets(const int iteration, int iVertex) { -#ifdef OPTIMISATION_OUTPUT - static int iter{0}; - std::ofstream off(std::format("tracklets{}.txt", iter++)); -#endif - for (int iLayer = 0; iLayer < mTrkParams[iteration].TrackletsPerRoad(); ++iLayer) { mTimeFrame->getTracklets()[iLayer].clear(); mTimeFrame->getTrackletsLabel(iLayer).clear(); if (iLayer > 0) { - std::fill(mTimeFrame->getTrackletsLookupTable()[iLayer - 1].begin(), - mTimeFrame->getTrackletsLookupTable()[iLayer - 1].end(), 0); + std::fill(mTimeFrame->getTrackletsLookupTable()[iLayer - 1].begin(), mTimeFrame->getTrackletsLookupTable()[iLayer - 1].end(), 0); } } - const Vertex diamondVert({mTrkParams[iteration].Diamond[0], mTrkParams[iteration].Diamond[1], mTrkParams[iteration].Diamond[2]}, {25.e-6f, 0.f, 0.f, 25.e-6f, 0.f, 36.f}, 1, 1.f); + const Vertex diamondVert(mTrkParams[iteration].Diamond, mTrkParams[iteration].DiamondCov, 1, 1.f); gsl::span diamondSpan(&diamondVert, 1); - int startROF{mTrkParams[iteration].nROFsPerIterations > 0 ? iROFslice * mTrkParams[iteration].nROFsPerIterations : 0}; - int endROF{o2::gpu::GPUCommonMath::Min(mTrkParams[iteration].nROFsPerIterations > 0 ? (iROFslice + 1) * mTrkParams[iteration].nROFsPerIterations + mTrkParams[iteration].DeltaROF : mTimeFrame->getNrof(), mTimeFrame->getNrof())}; mTaskArena->execute([&] { auto forTracklets = [&](auto Tag, int iLayer, int pivotROF, int base, int& offset) -> int { - if (!mTimeFrame->mMultiplicityCutMask[pivotROF]) { + if (!mTimeFrame->getROFMaskView().isROFEnabled(iLayer, pivotROF)) { return 0; } - int minROF = o2::gpu::CAMath::Max(startROF, pivotROF - mTrkParams[iteration].DeltaROF); - int maxROF = o2::gpu::CAMath::Min(endROF - 1, pivotROF + mTrkParams[iteration].DeltaROF); - gsl::span primaryVertices = mTrkParams[iteration].UseDiamond ? diamondSpan : mTimeFrame->getPrimaryVertices(minROF, maxROF); + gsl::span primaryVertices = mTrkParams[iteration].UseDiamond ? diamondSpan : mTimeFrame->getPrimaryVertices(iLayer, 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) { + if (endVtx <= startVtx || (iVertex + 1) > primaryVertices.size()) { + return 0; + } + + // does this layer have any overlap with the next layer + const auto& rofOverlap = mTimeFrame->getROFOverlapTableView().getOverlap(iLayer, iLayer + 1, pivotROF); + if (!rofOverlap.getEntries()) { return 0; } @@ -107,10 +98,12 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iROF for (int iV = startVtx; iV < endVtx; ++iV) { const auto& pv = primaryVertices[iV]; + if (!mTimeFrame->getROFVertexLookupTableView().isVertexCompatible(iLayer, pivotROF, pv)) { + continue; + } if ((pv.isFlagSet(Vertex::Flags::UPCMode) && iteration != 3) || (iteration == 3 && !pv.isFlagSet(Vertex::Flags::UPCMode))) { continue; } - const float resolution = o2::gpu::CAMath::Sqrt(math_utils::Sq(mTimeFrame->getPositionResolution(iLayer)) + math_utils::Sq(mTrkParams[iteration].PVres) / float(pv.getNContributors())); const float tanLambda = (currentCluster.zCoordinate - pv.getZ()) * inverseR0; const float zAtRmin = tanLambda * (mTimeFrame->getMinR(iLayer + 1) - currentCluster.radius) + currentCluster.zCoordinate; @@ -118,8 +111,7 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iROF const float sqInvDeltaZ0 = 1.f / (math_utils::Sq(currentCluster.zCoordinate - pv.getZ()) + constants::Tolerance); const float 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 * mTimeFrame->getMSangle(iLayer))); - - auto bins = getBinsRect(currentCluster, iLayer + 1, zAtRmin, zAtRmax, sigmaZ * mTrkParams[iteration].NSigmaCut, mTimeFrame->getPhiCut(iLayer)); + const auto bins = getBinsRect(iteration, currentCluster, iLayer + 1, zAtRmin, zAtRmax, sigmaZ * mTrkParams[iteration].NSigmaCut, mTimeFrame->getPhiCut(iLayer)); if (bins.x == 0 && bins.y == 0 && bins.z == 0 && bins.w == 0) { continue; } @@ -128,20 +120,26 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iROF phiBinsNum += mTrkParams[iteration].PhiBins; } - for (int targetROF{minROF}; targetROF <= maxROF; ++targetROF) { - if (!mTimeFrame->mMultiplicityCutMask[targetROF]) { + for (int targetROF = rofOverlap.getFirstEntry(); targetROF < rofOverlap.getEntriesBound(); ++targetROF) { + if (!mTimeFrame->getROFMaskView().isROFEnabled(iLayer + 1, targetROF)) { continue; } auto layer1 = mTimeFrame->getClustersOnLayer(targetROF, iLayer + 1); if (layer1.empty()) { continue; } + const auto ts = mTimeFrame->getROFOverlapTableView().getTimeStamp(iLayer, pivotROF, iLayer + 1, targetROF); + if (!ts.isCompatible(pv.getTimeStamp())) { + continue; + } + const auto& targetIndexTable = mTimeFrame->getIndexTable(targetROF, iLayer + 1); + const int zBinRange = (bins.z - bins.x) + 1; for (int iPhi = 0; iPhi < phiBinsNum; ++iPhi) { const int iPhiBin = (bins.y + iPhi) % mTrkParams[iteration].PhiBins; - const int firstBinIdx = mTimeFrame->mIndexTableUtils.getBinIndex(bins.x, iPhiBin); - const int maxBinIdx = firstBinIdx + (bins.z - bins.x) + 1; - const int firstRow = mTimeFrame->getIndexTable(targetROF, iLayer + 1)[firstBinIdx]; - const int lastRow = mTimeFrame->getIndexTable(targetROF, iLayer + 1)[maxBinIdx]; + const int firstBinIdx = mTimeFrame->getIndexTableUtils().getBinIndex(bins.x, iPhiBin); + const int maxBinIdx = firstBinIdx + zBinRange; + const int firstRow = targetIndexTable[firstBinIdx]; + const int lastRow = targetIndexTable[maxBinIdx]; for (int iNext = firstRow; iNext < lastRow; ++iNext) { if (iNext >= int(layer1.size())) { break; @@ -150,38 +148,20 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iROF if (mTimeFrame->isClusterUsed(iLayer + 1, nextCluster.clusterId)) { continue; } - float deltaPhi = o2::gpu::GPUCommonMath::Abs(currentCluster.phi - nextCluster.phi); - float deltaZ = o2::gpu::GPUCommonMath::Abs((tanLambda * (nextCluster.radius - currentCluster.radius)) + currentCluster.zCoordinate - nextCluster.zCoordinate); - -#ifdef OPTIMISATION_OUTPUT - MCCompLabel label; - int currentId{currentCluster.clusterId}; - int nextId{nextCluster.clusterId}; - for (auto& lab1 : mTimeFrame->getClusterLabels(iLayer, currentId)) { - for (auto& lab2 : mTimeFrame->getClusterLabels(iLayer + 1, nextId)) { - if (lab1 == lab2 && lab1.isValid()) { - label = lab1; - break; - } - } - if (label.isValid()) { - break; - } - } - off << std::format("{}\t{:d}\t{}\t{}\t{}\t{}", iLayer, label.isValid(), (tanLambda * (nextCluster.radius - currentCluster.radius) + currentCluster.zCoordinate - nextCluster.zCoordinate) / sigmaZ, tanLambda, resolution, sigmaZ) << std::endl; -#endif + const float deltaPhi = o2::gpu::CAMath::Abs(o2::math_utils::toPMPi(currentCluster.phi - nextCluster.phi)); + const float deltaZ = o2::gpu::CAMath::Abs((tanLambda * (nextCluster.radius - currentCluster.radius)) + currentCluster.zCoordinate - nextCluster.zCoordinate); if (deltaZ / sigmaZ < mTrkParams[iteration].NSigmaCut && ((deltaPhi < mTimeFrame->getPhiCut(iLayer) || o2::gpu::GPUCommonMath::Abs(deltaPhi - o2::constants::math::TwoPI) < mTimeFrame->getPhiCut(iLayer)))) { const float phi{o2::gpu::CAMath::ATan2(currentCluster.yCoordinate - nextCluster.yCoordinate, currentCluster.xCoordinate - nextCluster.xCoordinate)}; const float tanL = (currentCluster.zCoordinate - nextCluster.zCoordinate) / (currentCluster.radius - nextCluster.radius); if constexpr (decltype(Tag)::value == PassMode::OnePass::value) { - tracklets.emplace_back(currentSortedIndex, mTimeFrame->getSortedIndex(targetROF, iLayer + 1, iNext), tanL, phi, pivotROF, targetROF); + tracklets.emplace_back(currentSortedIndex, mTimeFrame->getSortedIndex(targetROF, iLayer + 1, 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, iLayer + 1, iNext), tanL, phi, pivotROF, targetROF); + tracklets[idx] = Tracklet(currentSortedIndex, mTimeFrame->getSortedIndex(targetROF, iLayer + 1, iNext), tanL, phi, ts); } } } @@ -194,47 +174,34 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iROF int dummy{0}; if (mTaskArena->max_concurrency() <= 1) { - for (int pivotROF{startROF}; pivotROF < endROF; ++pivotROF) { - for (int iLayer{0}; iLayer < mTrkParams[iteration].TrackletsPerRoad(); ++iLayer) { + for (int iLayer{0}; iLayer < mTrkParams[iteration].TrackletsPerRoad(); ++iLayer) { + const int startROF = 0, endROF = mTimeFrame->getROFOverlapTableView().getLayer(iLayer).mNROFsTF; + for (int pivotROF{startROF}; pivotROF < endROF; ++pivotROF) { forTracklets(PassMode::OnePass{}, iLayer, pivotROF, 0, dummy); } } } else { - bounded_vector> perROFCount(mTrkParams[iteration].TrackletsPerRoad(), bounded_vector(endROF - startROF + 1, 0, mMemoryPool.get()), mMemoryPool.get()); - tbb::parallel_for( - tbb::blocked_range2d(0, mTrkParams[iteration].TrackletsPerRoad(), 1, - startROF, endROF, 1), - [&](auto const& Range) { - for (int iLayer{Range.rows().begin()}; iLayer < Range.rows().end(); ++iLayer) { - for (int pivotROF = Range.cols().begin(); pivotROF < Range.cols().end(); ++pivotROF) { - perROFCount[iLayer][pivotROF - startROF] = forTracklets(PassMode::TwoPassCount{}, iLayer, pivotROF, 0, dummy); - } - } - }); - tbb::parallel_for(0, mTrkParams[iteration].TrackletsPerRoad(), [&](const int iLayer) { - std::exclusive_scan(perROFCount[iLayer].begin(), perROFCount[iLayer].end(), perROFCount[iLayer].begin(), 0); - mTimeFrame->getTracklets()[iLayer].resize(perROFCount[iLayer].back()); - }); - - tbb::parallel_for( - tbb::blocked_range2d(0, mTrkParams[iteration].TrackletsPerRoad(), 1, - startROF, endROF, 1), - [&](auto const& Range) { - for (int iLayer{Range.rows().begin()}; iLayer < Range.rows().end(); ++iLayer) { - if (perROFCount[iLayer].back() == 0) { - continue; - } - for (int pivotROF = Range.cols().begin(); pivotROF < Range.cols().end(); ++pivotROF) { - int baseIdx = perROFCount[iLayer][pivotROF - startROF]; - if (baseIdx == perROFCount[iLayer][pivotROF - startROF + 1]) { - continue; - } - int localIdx = 0; - forTracklets(PassMode::TwoPassInsert{}, iLayer, pivotROF, baseIdx, localIdx); - } + const int startROF = 0, endROF = mTimeFrame->getROFOverlapTableView().getLayer(iLayer).mNROFsTF; + bounded_vector perROFCount((endROF - startROF) + 1, mMemoryPool.get()); + tbb::parallel_for(startROF, endROF, [&](const int pivotROF) { + perROFCount[pivotROF - startROF] = forTracklets(PassMode::TwoPassCount{}, iLayer, pivotROF, 0, dummy); + }); + std::exclusive_scan(perROFCount.begin(), perROFCount.end(), perROFCount.begin(), 0); + const int nTracklets = perROFCount.back(); + mTimeFrame->getTracklets()[iLayer].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{}, iLayer, pivotROF, baseIdx, localIdx); }); + }); } tbb::parallel_for(0, mTrkParams[iteration].TrackletsPerRoad(), [&](const int iLayer) { @@ -286,16 +253,11 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iROF }); } }); -} // namespace o2::its +} -template -void TrackerTraits::computeLayerCells(const int iteration) +template +void TrackerTraits::computeLayerCells(const int iteration) { -#ifdef OPTIMISATION_OUTPUT - static int iter{0}; - std::ofstream off(std::format("cells{}.txt", iter++)); -#endif - for (int iLayer = 0; iLayer < mTrkParams[iteration].CellsPerRoad(); ++iLayer) { deepVectorClear(mTimeFrame->getCells()[iLayer]); if (iLayer > 0) { @@ -315,24 +277,15 @@ void TrackerTraits::computeLayerCells(const int iteration) int foundCells{0}; for (int iNextTracklet{nextLayerFirstTrackletIndex}; iNextTracklet < nextLayerLastTrackletIndex; ++iNextTracklet) { const Tracklet& nextTracklet{mTimeFrame->getTracklets()[iLayer + 1][iNextTracklet]}; - const auto& nextLbl = mTimeFrame->getTrackletsLabel(iLayer + 1)[iNextTracklet]; if (mTimeFrame->getTracklets()[iLayer + 1][iNextTracklet].firstClusterIndex != nextLayerClusterIndex) { break; } - if (mTrkParams[iteration].DeltaROF && currentTracklet.getSpanRof(nextTracklet) > mTrkParams[iteration].DeltaROF) { // TODO this has to be improved for the staggering + if (!currentTracklet.getTimeStamp().isCompatible(nextTracklet.getTimeStamp())) { continue; } - const float deltaTanLambda{std::abs(currentTracklet.tanLambda - nextTracklet.tanLambda)}; - -#ifdef OPTIMISATION_OUTPUT - float resolution{o2::gpu::CAMath::Sqrt(0.5f * (mTrkParams[iteration].SystErrorZ2[iLayer] + mTrkParams[iteration].SystErrorZ2[iLayer + 1] + mTrkParams[iteration].SystErrorZ2[iLayer + 2] + mTrkParams[iteration].SystErrorY2[iLayer] + mTrkParams[iteration].SystErrorY2[iLayer + 1] + mTrkParams[iteration].SystErrorY2[iLayer + 2])) / mTrkParams[iteration].LayerResolution[iLayer]}; - resolution = resolution > 1.e-12 ? resolution : 1.f; - bool good{mTimeFrame->getTrackletsLabel(iLayer)[iTracklet] == mTimeFrame->getTrackletsLabel(iLayer + 1)[iNextTracklet]}; - float signedDelta{currentTracklet.tanLambda - nextTracklet.tanLambda}; - off << std::format("{}\t{:d}\t{}\t{}\t{}\t{}", iLayer, good, signedDelta, signedDelta / (mTrkParams[iteration].CellDeltaTanLambdaSigma), tanLambda, resolution) << std::endl; -#endif - if (deltaTanLambda / mTrkParams[iteration].CellDeltaTanLambdaSigma < mTrkParams[iteration].NSigmaCut) { + 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]{ @@ -374,13 +327,16 @@ void TrackerTraits::computeLayerCells(const int iteration) chi2 += predChi2; } if (good) { + TimeEstBC ts = currentTracklet.getTimeStamp(); + ts += nextTracklet.getTimeStamp(); if constexpr (decltype(Tag)::value == PassMode::OnePass::value) { - layerCells.emplace_back(iLayer, clusId[0], clusId[1], clusId[2], iTracklet, iNextTracklet, track, chi2); + // + layerCells.emplace_back(iLayer, 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(iLayer, clusId[0], clusId[1], clusId[2], iTracklet, iNextTracklet, track, chi2); + layerCells[offset++] = CellSeedN(iLayer, clusId[0], clusId[1], clusId[2], iTracklet, iNextTracklet, track, chi2, ts); } else { static_assert(false, "Unknown mode!"); } @@ -446,13 +402,9 @@ void TrackerTraits::computeLayerCells(const int iteration) }); } -template -void TrackerTraits::findCellsNeighbours(const int iteration) +template +void TrackerTraits::findCellsNeighbours(const int iteration) { -#ifdef OPTIMISATION_OUTPUT - std::ofstream off(std::format("cellneighs{}.txt", iteration)); -#endif - struct Neighbor { int cell{-1}, nextCell{-1}, level{-1}; }; @@ -477,33 +429,17 @@ void TrackerTraits::findCellsNeighbours(const int iteration) int foundNextCells{0}; for (int iNextCell{nextLayerFirstCellIndex}; iNextCell < nextLayerLastCellIndex; ++iNextCell) { auto nextCellSeed{mTimeFrame->getCells()[iLayer + 1][iNextCell]}; /// copy - if (nextCellSeed.getFirstTrackletIndex() != nextLayerTrackletIndex) { + if (nextCellSeed.getFirstTrackletIndex() != nextLayerTrackletIndex || !currentCellSeed.getTimeStamp().isCompatible(nextCellSeed.getTimeStamp())) { break; } - if (mTrkParams[iteration].DeltaROF) { // TODO this has to be improved for the staggering - const auto& trkl00 = mTimeFrame->getTracklets()[iLayer][currentCellSeed.getFirstTrackletIndex()]; - const auto& trkl01 = mTimeFrame->getTracklets()[iLayer + 1][currentCellSeed.getSecondTrackletIndex()]; - const auto& trkl10 = mTimeFrame->getTracklets()[iLayer + 1][nextCellSeed.getFirstTrackletIndex()]; - const auto& trkl11 = mTimeFrame->getTracklets()[iLayer + 2][nextCellSeed.getSecondTrackletIndex()]; - if ((std::max({trkl00.getMaxRof(), trkl01.getMaxRof(), trkl10.getMaxRof(), trkl11.getMaxRof()}) - - std::min({trkl00.getMinRof(), trkl01.getMinRof(), trkl10.getMinRof(), trkl11.getMinRof()})) > mTrkParams[0].DeltaROF) { - continue; - } - } - if (!nextCellSeed.rotate(currentCellSeed.getAlpha()) || !nextCellSeed.propagateTo(currentCellSeed.getX(), getBz())) { continue; } - float chi2 = currentCellSeed.getPredictedChi2(nextCellSeed); /// TODO: switch to the chi2 wrt cluster to avoid correlation -#ifdef OPTIMISATION_OUTPUT - bool good{mTimeFrame->getCellsLabel(iLayer)[iCell] == mTimeFrame->getCellsLabel(iLayer + 1)[iNextCell]}; - off << std::format("{}\t{:d}\t{}", iLayer, good, chi2) << std::endl; -#endif - - if (chi2 > mTrkParams[0].MaxChi2ClusterAttachment) { + float chi2 = currentCellSeed.getPredictedChi2(nextCellSeed); /// TODO: switch to the chi2 wrt cluster to avoid correlation + if (chi2 > mTrkParams[iteration].MaxChi2ClusterAttachment) { continue; } @@ -577,16 +513,11 @@ void TrackerTraits::findCellsNeighbours(const int iteration) }); } -template -void TrackerTraits::processNeighbours(int iLayer, int iLevel, const bounded_vector& currentCellSeed, const bounded_vector& currentCellId, bounded_vector& updatedCellSeeds, bounded_vector& updatedCellsIds) +template +void TrackerTraits::processNeighbours(int iLayer, int iLevel, const bounded_vector& currentCellSeed, const bounded_vector& currentCellId, bounded_vector& updatedCellSeeds, bounded_vector& updatedCellsIds) { - CA_DEBUGGER(std::cout << "Processing neighbours layer " << iLayer << " level " << iLevel << ", size of the cell seeds: " << currentCellSeed.size() << std::endl); auto propagator = o2::base::Propagator::Instance(); -#ifdef CA_DEBUG - int failed[5]{0, 0, 0, 0, 0}, attempts{0}, failedByMismatch{0}; -#endif - mTaskArena->execute([&] { auto forCellNeighbours = [&](auto Tag, int iCell, int offset = 0) -> int { const auto& currentCell{currentCellSeed[iCell]}; @@ -607,32 +538,32 @@ void TrackerTraits::processNeighbours(int iLayer, int iLevel, const bou const int endNeighbourId{mTimeFrame->getCellsNeighboursLUT()[iLayer - 1][cellId]}; int foundSeeds{0}; for (int iNeighbourCell{startNeighbourId}; iNeighbourCell < endNeighbourId; ++iNeighbourCell) { - CA_DEBUGGER(attempts++); const int neighbourCellId = mTimeFrame->getCellsNeighbours()[iLayer - 1][iNeighbourCell]; const auto& neighbourCell = mTimeFrame->getCells()[iLayer - 1][neighbourCellId]; if (neighbourCell.getSecondTrackletIndex() != currentCell.getFirstTrackletIndex()) { - CA_DEBUGGER(failedByMismatch++); continue; } - if (mTimeFrame->isClusterUsed(iLayer - 1, neighbourCell.getFirstClusterIndex())) { + if (!currentCell.getTimeStamp().isCompatible(neighbourCell.getTimeStamp())) { continue; } if (currentCell.getLevel() - 1 != neighbourCell.getLevel()) { - CA_DEBUGGER(failed[0]++); + continue; + } + if (mTimeFrame->isClusterUsed(iLayer - 1, neighbourCell.getFirstClusterIndex())) { continue; } /// Let's start the fitting procedure CellSeedN seed{currentCell}; + seed.getTimeStamp() = currentCell.getTimeStamp(); + seed.getTimeStamp() += neighbourCell.getTimeStamp(); const auto& trHit = mTimeFrame->getTrackingFrameInfoOnLayer(iLayer - 1)[neighbourCell.getFirstClusterIndex()]; if (!seed.rotate(trHit.alphaTrackingFrame)) { - CA_DEBUGGER(failed[1]++); continue; } if (!propagator->propagateToX(seed, trHit.xTrackingFrame, getBz(), o2::base::PropagatorImpl::MAX_SIN_PHI, o2::base::PropagatorImpl::MAX_STEP, mTrkParams[0].CorrType)) { - CA_DEBUGGER(failed[2]++); continue; } @@ -644,12 +575,10 @@ void TrackerTraits::processNeighbours(int iLayer, int iLevel, const bou auto predChi2{seed.getPredictedChi2Quiet(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)}; if ((predChi2 > mTrkParams[0].MaxChi2ClusterAttachment) || predChi2 < 0.f) { - CA_DEBUGGER(failed[3]++); continue; } seed.setChi2(seed.getChi2() + predChi2); if (!seed.o2::track::TrackParCov::update(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)) { - CA_DEBUGGER(failed[4]++); continue; } @@ -703,20 +632,10 @@ void TrackerTraits::processNeighbours(int iLayer, int iLevel, const bou }); } }); - -#ifdef CA_DEBUG - std::cout << "\t\t- Found " << updatedCellSeeds.size() << " cell seeds out of " << attempts << " attempts" << std::endl; - std::cout << "\t\t\t> " << failed[0] << " failed because of level" << std::endl; - std::cout << "\t\t\t> " << failed[1] << " failed because of rotation" << std::endl; - std::cout << "\t\t\t> " << failed[2] << " failed because of propagation" << std::endl; - std::cout << "\t\t\t> " << failed[3] << " failed because of chi2 cut" << std::endl; - std::cout << "\t\t\t> " << failed[4] << " failed because of update" << std::endl; - std::cout << "\t\t\t> " << failedByMismatch << " failed because of mismatch" << std::endl; -#endif } -template -void TrackerTraits::findRoads(const int iteration) +template +void TrackerTraits::findRoads(const int iteration) { bounded_vector> firstClusters(mTrkParams[iteration].NLayers, bounded_vector(mMemoryPool.get()), mMemoryPool.get()); bounded_vector> sharedFirstClusters(mTrkParams[iteration].NLayers, bounded_vector(mMemoryPool.get()), mMemoryPool.get()); @@ -860,31 +779,29 @@ void TrackerTraits::findRoads(const int iteration) continue; } - std::array rofs{INT_MAX, INT_MAX, INT_MAX}; + bool firstCls{true}; + TimeEstBC ts; for (int iLayer{0}; iLayer < mTrkParams[0].NLayers; ++iLayer) { if (track.getClusterIndex(iLayer) == constants::UnusedIndex) { continue; } mTimeFrame->markUsedCluster(iLayer, track.getClusterIndex(iLayer)); int currentROF = mTimeFrame->getClusterROF(iLayer, track.getClusterIndex(iLayer)); - for (int iR{0}; iR < 3; ++iR) { - if (rofs[iR] == INT_MAX) { - rofs[iR] = currentROF; - } - if (rofs[iR] == currentROF) { - break; + auto rofTS = mTimeFrame->getROFOverlapTableView().getLayer(iLayer).getROFTimeBounds(currentROF, true); + if (firstCls) { + firstCls = false; + ts = rofTS; + } else { + if (!ts.isCompatible(rofTS)) { + LOGP(fatal, "TS {}+/-{} are incompatible with {}+/-{}, this should not happen!", rofTS.getTimeStamp(), rofTS.getTimeStampError(), ts.getTimeStamp(), ts.getTimeStampError()); } + ts += rofTS; } } - if (rofs[2] != INT_MAX) { - continue; - } + track.getTimeStamp() = ts.makeSymmetrical(); track.setUserField(0); track.getParamOut().setUserField(0); - if (rofs[1] != INT_MAX) { - track.setNextROFbit(); - } - mTimeFrame->getTracks(o2::gpu::CAMath::Min(rofs[0], rofs[1])).emplace_back(track); + mTimeFrame->getTracks().emplace_back(track); firstClusters[firstLayer].push_back(firstCluster); if (isFirstShared) { @@ -898,164 +815,24 @@ void TrackerTraits::findRoads(const int iteration) std::sort(sharedFirstClusters[iLayer].begin(), sharedFirstClusters[iLayer].end()); } - for (int iROF{0}; iROF < mTimeFrame->getNrof(); ++iROF) { - for (auto& track : mTimeFrame->getTracks(iROF)) { - int firstLayer{mTrkParams[0].NLayers}, firstCluster{constants::UnusedIndex}; - for (int iLayer{0}; iLayer < mTrkParams[0].NLayers; ++iLayer) { - if (track.getClusterIndex(iLayer) == constants::UnusedIndex) { - continue; - } - firstLayer = iLayer; - firstCluster = track.getClusterIndex(iLayer); - break; - } - if (std::binary_search(sharedFirstClusters[firstLayer].begin(), sharedFirstClusters[firstLayer].end(), firstCluster)) { - track.setSharedClusters(); - } - } - } -} - -template -void TrackerTraits::extendTracks(const int iteration) -{ - for (int rof{0}; rof < mTimeFrame->getNrof(); ++rof) { - for (auto& track : mTimeFrame->getTracks(rof)) { - auto backup{track}; - bool success{false}; - // the order here biases towards top extension, tracks should probably be fitted separately in the directions and then compared. - if ((mTrkParams[iteration].UseTrackFollowerMix || mTrkParams[iteration].UseTrackFollowerTop) && track.getLastClusterLayer() != mTrkParams[iteration].NLayers - 1) { - success = success || trackFollowing(&track, rof, true, iteration); - } - if ((mTrkParams[iteration].UseTrackFollowerMix || (mTrkParams[iteration].UseTrackFollowerBot && !success)) && track.getFirstClusterLayer() != 0) { - success = success || trackFollowing(&track, rof, false, iteration); - } - if (success) { - /// We have to refit the track - track.resetCovariance(); - track.setChi2(0); - bool fitSuccess = fitTrack(track, 0, mTrkParams[iteration].NLayers, 1, mTrkParams[iteration].MaxChi2ClusterAttachment, mTrkParams[0].MaxChi2NDF); - if (!fitSuccess) { - track = backup; - continue; - } - track.getParamOut() = track; - track.resetCovariance(); - track.setChi2(0); - fitSuccess = fitTrack(track, mTrkParams[iteration].NLayers - 1, -1, -1, mTrkParams[iteration].MaxChi2ClusterAttachment, mTrkParams[0].MaxChi2NDF, 50.); - if (!fitSuccess) { - track = backup; - continue; - } - mTimeFrame->mNExtendedTracks++; - mTimeFrame->mNExtendedUsedClusters += track.getNClusters() - backup.getNClusters(); - auto pattern = track.getPattern(); - auto diff = (pattern & ~backup.getPattern()) & 0xff; - pattern |= (diff << 24); - track.setPattern(pattern); - /// Make sure that the newly attached clusters get marked as used - for (int iLayer{0}; iLayer < mTrkParams[iteration].NLayers; ++iLayer) { - if (track.getClusterIndex(iLayer) == constants::UnusedIndex) { - continue; - } - mTimeFrame->markUsedCluster(iLayer, track.getClusterIndex(iLayer)); - } - } - } - } -} - -template -void TrackerTraits::findShortPrimaries() -{ - const auto propagator = o2::base::Propagator::Instance(); - mTimeFrame->fillPrimaryVerticesXandAlpha(); - - for (auto& cell : mTimeFrame->getCells()[0]) { - auto& cluster3_glo = mTimeFrame->getClusters()[2][cell.getThirdClusterIndex()]; - auto& cluster2_glo = mTimeFrame->getClusters()[1][cell.getSecondClusterIndex()]; - auto& cluster1_glo = mTimeFrame->getClusters()[0][cell.getFirstClusterIndex()]; - if (mTimeFrame->isClusterUsed(2, cluster1_glo.clusterId) || - mTimeFrame->isClusterUsed(1, cluster2_glo.clusterId) || - mTimeFrame->isClusterUsed(0, cluster3_glo.clusterId)) { - continue; - } - - std::array rofs{ - mTimeFrame->getClusterROF(2, cluster3_glo.clusterId), - mTimeFrame->getClusterROF(1, cluster2_glo.clusterId), - mTimeFrame->getClusterROF(0, cluster1_glo.clusterId)}; - if (rofs[0] != rofs[1] && rofs[1] != rofs[2] && rofs[0] != rofs[2]) { - continue; - } - - int rof{rofs[0]}; - if (rofs[1] == rofs[2]) { - rof = rofs[2]; - } - - auto pvs{mTimeFrame->getPrimaryVertices(rof)}; - auto pvsXAlpha{mTimeFrame->getPrimaryVerticesXAlpha(rof)}; - - const auto& cluster3_tf = mTimeFrame->getTrackingFrameInfoOnLayer(2)[cluster3_glo.clusterId]; - TrackITSExt temporaryTrack{buildTrackSeed(cluster1_glo, cluster2_glo, cluster3_tf)}; - temporaryTrack.setExternalClusterIndex(0, cluster1_glo.clusterId, true); - temporaryTrack.setExternalClusterIndex(1, cluster2_glo.clusterId, true); - temporaryTrack.setExternalClusterIndex(2, cluster3_glo.clusterId, true); - - /// add propagation to the primary vertices compatible with the ROF(s) of the cell - bool fitSuccess = fitTrack(temporaryTrack, 1, -1, -1); - if (!fitSuccess) { - continue; - } - fitSuccess = false; - - TrackITSExt bestTrack{temporaryTrack}, backup{temporaryTrack}; - float bestChi2{std::numeric_limits::max()}; - for (int iV{0}; iV < (int)pvs.size(); ++iV) { - temporaryTrack = backup; - if (!temporaryTrack.rotate(pvsXAlpha[iV][1])) { - continue; - } - if (!propagator->propagateTo(temporaryTrack, pvsXAlpha[iV][0], true)) { + for (auto& track : mTimeFrame->getTracks()) { + int firstLayer{mTrkParams[0].NLayers}, firstCluster{constants::UnusedIndex}; + for (int iLayer{0}; iLayer < mTrkParams[0].NLayers; ++iLayer) { + if (track.getClusterIndex(iLayer) == constants::UnusedIndex) { continue; } - - float pvRes{mTrkParams[0].PVres / o2::gpu::CAMath::Sqrt(float(pvs[iV].getNContributors()))}; - const float posVtx[2]{0.f, pvs[iV].getZ()}; - const float covVtx[3]{pvRes, 0.f, pvRes}; - float chi2 = temporaryTrack.getPredictedChi2Quiet(posVtx, covVtx); - if (chi2 < bestChi2) { - if (!temporaryTrack.track::TrackParCov::update(posVtx, covVtx)) { - continue; - } - bestTrack = temporaryTrack; - bestChi2 = chi2; - } + firstLayer = iLayer; + firstCluster = track.getClusterIndex(iLayer); + break; } - - bestTrack.resetCovariance(); - bestTrack.setChi2(0.f); - fitSuccess = fitTrack(bestTrack, 0, mTrkParams[0].NLayers, 1, mTrkParams[0].MaxChi2ClusterAttachment, mTrkParams[0].MaxChi2NDF); - if (!fitSuccess) { - continue; - } - bestTrack.getParamOut() = bestTrack; - bestTrack.resetCovariance(); - bestTrack.setChi2(0.f); - fitSuccess = fitTrack(bestTrack, mTrkParams[0].NLayers - 1, -1, -1, mTrkParams[0].MaxChi2ClusterAttachment, mTrkParams[0].MaxChi2NDF, 50.); - if (!fitSuccess) { - continue; + if (std::binary_search(sharedFirstClusters[firstLayer].begin(), sharedFirstClusters[firstLayer].end(), firstCluster)) { + track.setSharedClusters(); } - mTimeFrame->markUsedCluster(0, bestTrack.getClusterIndex(0)); - mTimeFrame->markUsedCluster(1, bestTrack.getClusterIndex(1)); - mTimeFrame->markUsedCluster(2, bestTrack.getClusterIndex(2)); - mTimeFrame->getTracks(rof).emplace_back(bestTrack); } } -template -bool TrackerTraits::fitTrack(TrackITSExt& track, int start, int end, int step, float chi2clcut, float chi2ndfcut, float maxQoverPt, int nCl, o2::track::TrackPar* linRef) +template +bool TrackerTraits::fitTrack(TrackITSExt& track, int start, int end, int step, float chi2clcut, float chi2ndfcut, float maxQoverPt, int nCl, o2::track::TrackPar* linRef) { auto propInstance = o2::base::Propagator::Instance(); @@ -1106,125 +883,13 @@ bool TrackerTraits::fitTrack(TrackITSExt& track, int start, int end, in return std::abs(track.getQ2Pt()) < maxQoverPt && track.getChi2() < chi2ndfcut * (nCl * 2 - 5); } -template -bool TrackerTraits::trackFollowing(TrackITSExt* track, int rof, bool outward, const int iteration) -{ - auto propInstance = o2::base::Propagator::Instance(); - const int step = -1 + outward * 2; - const int end = outward ? mTrkParams[iteration].NLayers - 1 : 0; - bounded_vector hypotheses(1, *track, mMemoryPool.get()); // possibly avoid reallocation - for (size_t iHypo{0}; iHypo < hypotheses.size(); ++iHypo) { - auto hypo{hypotheses[iHypo]}; - int iLayer = static_cast(outward ? hypo.getLastClusterLayer() : hypo.getFirstClusterLayer()); - // per layer we add new hypotheses - while (iLayer != end) { - iLayer += step; // step through all layers until we reach the end, this allows for skipping on empty layers - const float r = mTrkParams[iteration].LayerRadii[iLayer]; - // get an estimate of the trackinf-frame x for the next step - float x{-999}; - if (!hypo.getXatLabR(r, x, mTimeFrame->getBz(), o2::track::DirAuto) || x <= 0.f) { - continue; - } - // estimate hypo's trk parameters at that x - auto& hypoParam{outward ? hypo.getParamOut() : hypo.getParamIn()}; - if (!propInstance->propagateToX(hypoParam, x, mTimeFrame->getBz(), PropagatorF::MAX_SIN_PHI, - PropagatorF::MAX_STEP, mTrkParams[iteration].CorrType)) { - continue; - } - - if (mTrkParams[iteration].CorrType == PropagatorF::MatCorrType::USEMatCorrNONE) { // account for material affects if propagator does not - if (!hypoParam.correctForMaterial(mTrkParams[iteration].LayerxX0[iLayer], mTrkParams[iteration].LayerxX0[iLayer] * constants::Radl * constants::Rho, true)) { - continue; - } - } - - // calculate the search window on this layer - const float phi{hypoParam.getPhi()}; - const float ePhi{o2::gpu::CAMath::Sqrt(hypoParam.getSigmaSnp2() / hypoParam.getCsp2())}; - const float z{hypoParam.getZ()}; - const float eZ{o2::gpu::CAMath::Sqrt(hypoParam.getSigmaZ2())}; - const int4 selectedBinsRect{getBinsRect(iLayer, phi, mTrkParams[iteration].TrackFollowerNSigmaCutPhi * ePhi, z, mTrkParams[iteration].TrackFollowerNSigmaCutZ * eZ)}; - if (selectedBinsRect.x == 0 && selectedBinsRect.y == 0 && selectedBinsRect.z == 0 && selectedBinsRect.w == 0) { - continue; - } - - int phiBinsNum{selectedBinsRect.w - selectedBinsRect.y + 1}; - - if (phiBinsNum < 0) { - phiBinsNum += mTrkParams[iteration].PhiBins; - } - - gsl::span layer1 = mTimeFrame->getClustersOnLayer(rof, iLayer); - if (layer1.empty()) { - continue; - } - - // check all clusters in search windows for possible new hypotheses - for (int iPhiCount = 0; iPhiCount < phiBinsNum; iPhiCount++) { - int iPhiBin = (selectedBinsRect.y + iPhiCount) % mTrkParams[iteration].PhiBins; - const int firstBinIndex{mTimeFrame->mIndexTableUtils.getBinIndex(selectedBinsRect.x, iPhiBin)}; - const int maxBinIndex{firstBinIndex + selectedBinsRect.z - selectedBinsRect.x + 1}; - const int firstRowClusterIndex = mTimeFrame->getIndexTable(rof, iLayer)[firstBinIndex]; - const int maxRowClusterIndex = mTimeFrame->getIndexTable(rof, iLayer)[maxBinIndex]; - - for (int iNextCluster{firstRowClusterIndex}; iNextCluster < maxRowClusterIndex; ++iNextCluster) { - if (iNextCluster >= (int)layer1.size()) { - break; - } - const Cluster& nextCluster{layer1[iNextCluster]}; - - if (mTimeFrame->isClusterUsed(iLayer, nextCluster.clusterId)) { - continue; - } - - const TrackingFrameInfo& trackingHit = mTimeFrame->getTrackingFrameInfoOnLayer(iLayer)[nextCluster.clusterId]; - - auto tbupdated{hypo}; - auto& tbuParams = outward ? tbupdated.getParamOut() : tbupdated.getParamIn(); - if (!tbuParams.rotate(trackingHit.alphaTrackingFrame)) { - continue; - } - - if (!propInstance->propagateToX(tbuParams, trackingHit.xTrackingFrame, mTimeFrame->getBz(), - PropagatorF::MAX_SIN_PHI, PropagatorF::MAX_STEP, PropagatorF::MatCorrType::USEMatCorrNONE)) { - continue; - } - - auto predChi2{tbuParams.getPredictedChi2Quiet(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)}; - if (predChi2 >= track->getChi2() * mTrkParams[iteration].NSigmaCut) { - continue; - } - - if (!tbuParams.o2::track::TrackParCov::update(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)) { - continue; - } - tbupdated.setChi2(tbupdated.getChi2() + predChi2); /// This is wrong for outward propagation as the chi2 refers to inward parameters - tbupdated.setExternalClusterIndex(iLayer, nextCluster.clusterId, true); - hypotheses.emplace_back(tbupdated); - } - } - } - } - - TrackITSExt* bestHypo{track}; - bool swapped{false}; - for (auto& hypo : hypotheses) { - if (hypo.isBetter(*bestHypo, track->getChi2() * mTrkParams[iteration].NSigmaCut)) { - bestHypo = &hypo; - swapped = true; - } - } - *track = *bestHypo; - return swapped; -} - // create a new seed either from the existing track inner param or reseed from the edgepointd and cluster in the middle -template -TrackITSExt TrackerTraits::seedTrackForRefit(const CellSeedN& seed) +template +TrackITSExt TrackerTraits::seedTrackForRefit(const CellSeedN& seed) { TrackITSExt temporaryTrack(seed); - int lrMin = nLayers, lrMax = 0, lrMid = 0; - for (int iL = 0; iL < nLayers; ++iL) { + int lrMin = NLayers, lrMax = 0, lrMid = 0; + for (int iL = 0; iL < NLayers; ++iL) { const int idx = seed.getCluster(iL); temporaryTrack.setExternalClusterIndex(iL, idx, idx != constants::UnusedIndex); if (idx != constants::UnusedIndex) { @@ -1261,8 +926,8 @@ TrackITSExt TrackerTraits::seedTrackForRefit(const CellSeedN& seed) /// Clusters are given from inside outward (cluster3 is the outermost). The outermost cluster is given in the tracking /// frame coordinates whereas the others are referred to the global frame. -template -track::TrackParCov TrackerTraits::buildTrackSeed(const Cluster& cluster1, const Cluster& cluster2, const TrackingFrameInfo& tf3, bool reverse) +template +track::TrackParCov TrackerTraits::buildTrackSeed(const Cluster& cluster1, const Cluster& cluster2, const TrackingFrameInfo& tf3, bool reverse) { const float sign = reverse ? -1.f : 1.f; @@ -1297,24 +962,18 @@ track::TrackParCov TrackerTraits::buildTrackSeed(const Cluster& cluster return {x3, tf3.alphaTrackingFrame, {y3, tf3.positionTrackingFrame[1], snp, tgl, q2pt}, {tf3.covarianceTrackingFrame[0], tf3.covarianceTrackingFrame[1], tf3.covarianceTrackingFrame[2], 0.f, 0.f, track::kCSnp2max, 0.f, 0.f, 0.f, track::kCTgl2max, 0.f, 0.f, 0.f, 0.f, sg2q2pt}}; } -template -void TrackerTraits::setBz(float bz) +template +void TrackerTraits::setBz(float bz) { mBz = bz; mIsZeroField = std::abs(mBz) < 0.01; mTimeFrame->setBz(bz); } -template -bool TrackerTraits::isMatLUT() const -{ - return o2::base::Propagator::Instance()->getMatLUT() && (mTrkParams[0].CorrType == o2::base::PropagatorImpl::MatCorrType::USEMatCorrLUT); -} - -template -void TrackerTraits::setNThreads(int n, std::shared_ptr& arena) +template +void TrackerTraits::setNThreads(int n, std::shared_ptr& arena) { -#if defined(OPTIMISATION_OUTPUT) || defined(CA_DEBUG) +#if defined(OPTIMISATION_OUTPUT) mTaskArena = std::make_shared(1); #else if (arena == nullptr) { @@ -1322,7 +981,6 @@ void TrackerTraits::setNThreads(int n, std::shared_ptr LOGP(info, "Setting tracker with {} threads.", n); } else { mTaskArena = arena; - LOGP(info, "Attaching tracker to calling thread's arena"); } #endif } diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx index d5f13cd9d25ea..a41560e2e9e9a 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// 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. // @@ -9,16 +9,18 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#include #include #include -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "ITSBase/GeometryTGeo.h" -#include "ITSReconstruction/FastMultEstConfig.h" -#include "ITSReconstruction/FastMultEst.h" +#include "ITStracking/FastMultEstConfig.h" +#include "ITStracking/FastMultEst.h" +#include "ITStracking/ROFLookupTables.h" #include "ITStracking/TrackingConfigParam.h" #include "ITStracking/TrackingInterface.h" @@ -28,6 +30,8 @@ #include "CommonDataFormat/IRFrame.h" #include "DetectorsBase/GRPGeomHelper.h" #include "ITStracking/BoundedAllocator.h" +#include "Framework/InputRecordWalker.h" +#include "Framework/DataRefUtils.h" #include "Framework/DeviceSpec.h" using namespace o2::framework; @@ -69,21 +73,67 @@ void ITSTrackingInterface::initialise() } mVertexer->setNThreads(vertConf.nThreads, mTaskArena); mTracker->setNThreads(trackConf.nThreads, mTaskArena); + mTimeFrame->setIsStaggered(mDoStaggering); + + // prepare data filter + for (int iLayer = 0; iLayer < ((mDoStaggering) ? NLayers : 1); ++iLayer) { + mFilter.emplace_back("compClusters", "ITS", "COMPCLUSTERS", iLayer, Lifetime::Timeframe); + mFilter.emplace_back("patterns", "ITS", "PATTERNS", iLayer, Lifetime::Timeframe); + mFilter.emplace_back("ROframe", "ITS", "CLUSTERSROF", iLayer, Lifetime::Timeframe); + if (mIsMC) { + mFilter.emplace_back("itsmclabels", "ITS", "CLUSTERSMCTR", iLayer, Lifetime::Timeframe); + } + } } void ITSTrackingInterface::run(framework::ProcessingContext& pc) { - auto compClusters = pc.inputs().get>("compClusters"); - gsl::span patterns = pc.inputs().get>("patterns"); + const auto& par = o2::itsmft::DPLAlpideParam::Instance(); + + // filter input and compose + std::array, NLayers> compClusters; + std::array, NLayers> patterns; + std::array, NLayers> rofsinput; + std::array*, NLayers> labels{}; + for (const DataRef& ref : framework::InputRecordWalker{pc.inputs(), mFilter}) { + auto const* dh = DataRefUtils::getHeader(ref); + if (framework::DataRefUtils::match(ref, {"compClusters", framework::ConcreteDataTypeMatcher{"ITS", "COMPCLUSTERS"}})) { + compClusters[dh->subSpecification] = pc.inputs().get>(ref); + } + if (framework::DataRefUtils::match(ref, {"patterns", framework::ConcreteDataTypeMatcher{"ITS", "PATTERNS"}})) { + patterns[dh->subSpecification] = pc.inputs().get>(ref); + } + if (framework::DataRefUtils::match(ref, {"ROframes", framework::ConcreteDataTypeMatcher{"ITS", "CLUSTERSROF"}})) { + rofsinput[dh->subSpecification] = pc.inputs().get>(ref); + } + if (framework::DataRefUtils::match(ref, {"itsmclabels", framework::ConcreteDataTypeMatcher{"ITS", "CLUSTERSMCTR"}})) { + labels[dh->subSpecification] = pc.inputs().get*>(ref).release(); + } + } + + bool hasClusters = false; + for (int iLayer = 0; iLayer < ((mDoStaggering) ? NLayers : 1); ++iLayer) { + LOGP(info, "ITSTracker{} pulled {} clusters, {} RO frames", ((mDoStaggering) ? std::format(" on layer {}", iLayer) : ""), compClusters[iLayer].size(), rofsinput[iLayer].size()); + if (compClusters[iLayer].empty()) { + LOGP(warn, " -> received no processable data{}", (mDoStaggering) ? std::format(" on layer {}", iLayer) : ""); + } else { + hasClusters = true; + } + if (mIsMC) { + LOG(info) << " -> " << labels[iLayer]->getIndexedSize() << " MC label objects"; + } + } + + const auto& tfInfo = pc.services().get(); gsl::span physTriggers; std::vector fromTRD; if (mUseTriggers == 2) { // use TRD triggers - o2::InteractionRecord ir{0, pc.services().get().firstTForbit}; + o2::InteractionRecord ir{0, tfInfo.firstTForbit}; auto trdTriggers = pc.inputs().get>("phystrig"); for (const auto& trig : trdTriggers) { if (trig.getBCData() >= ir && trig.getNumberOfTracklets()) { ir = trig.getBCData(); - fromTRD.emplace_back(o2::itsmft::PhysTrigger{ir, 0}); + fromTRD.emplace_back(o2::itsmft::PhysTrigger{.ir = ir, .data = 0}); } } physTriggers = gsl::span(fromTRD.data(), fromTRD.size()); @@ -91,43 +141,23 @@ void ITSTrackingInterface::run(framework::ProcessingContext& pc) physTriggers = pc.inputs().get>("phystrig"); } - auto rofsinput = pc.inputs().get>("ROframes"); - auto& trackROFvec = pc.outputs().make>(Output{"ITS", "ITSTrackROF", 0}, rofsinput.begin(), rofsinput.end()); + const int clockLayerId{mDoStaggering ? mTimeFrame->getROFOverlapTableView().getClock() : 0}; auto& irFrames = pc.outputs().make>(Output{"ITS", "IRFRAMES", 0}); - const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); // RS: this should come from CCDB - - irFrames.reserve(trackROFvec.size()); - int nBCPerTF = alpParams.roFrameLengthInBC; - - LOGP(info, "ITSTracker pulled {} clusters, {} RO frames {}", compClusters.size(), trackROFvec.size(), compClusters.empty() ? " -> received no processable data will skip" : ""); - const dataformats::MCTruthContainer* labels = nullptr; - gsl::span mc2rofs; - if (mIsMC) { - labels = pc.inputs().get*>("itsmclabels").release(); - // get the array as read-only span, a snapshot is sent forward - pc.outputs().snapshot(Output{"ITS", "ITSTrackMC2ROF", 0}, pc.inputs().get>("ITSMC2ROframes")); - LOG(info) << labels->getIndexedSize() << " MC label objects , in " << mc2rofs.size() << " MC events"; - } + irFrames.reserve(rofsinput[clockLayerId].size()); auto& allClusIdx = pc.outputs().make>(Output{"ITS", "TRACKCLSID", 0}); auto& allTracks = pc.outputs().make>(Output{"ITS", "TRACKS", 0}); - auto& vertROFvec = pc.outputs().make>(Output{"ITS", "VERTICESROF", 0}); + auto& allTrackROFs = pc.outputs().make>(Output{"ITS", "ITSTrackROF", 0}); auto& vertices = pc.outputs().make>(Output{"ITS", "VERTICES", 0}); + auto& vertROFvec = pc.outputs().make>(Output{"ITS", "VERTICESROF", 0}); // TODO fill this! // MC static pmr::vector dummyMCLabTracks, dummyMCLabVerts; static pmr::vector dummyMCPurVerts; auto& allTrackLabels = mIsMC ? pc.outputs().make>(Output{"ITS", "TRACKSMCTR", 0}) : dummyMCLabTracks; auto& allVerticesLabels = mIsMC ? pc.outputs().make>(Output{"ITS", "VERTICESMCTR", 0}) : dummyMCLabVerts; - bool writeContLabels = mIsMC && o2::its::VertexerParamConfig::Instance().outputContLabels; - auto& allVerticesContLabels = writeContLabels ? pc.outputs().make>(Output{"ITS", "VERTICESMCTRCONT", 0}) : dummyMCLabVerts; auto& allVerticesPurities = mIsMC ? pc.outputs().make>(Output{"ITS", "VERTICESMCPUR", 0}) : dummyMCPurVerts; - std::uint32_t roFrame = 0; - - bool continuous = o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(o2::detectors::DetID::ITS); - LOG(info) << "ITSTracker RO: continuous=" << continuous; - if (mOverrideBeamEstimation) { mTimeFrame->setBeamPosition(mMeanVertex->getX(), mMeanVertex->getY(), @@ -137,52 +167,57 @@ void ITSTrackingInterface::run(framework::ProcessingContext& pc) } mTracker->setBz(o2::base::Propagator::Instance()->getNominalBz()); + mTracker->setTimeSlice(tfInfo.timeslice); - gsl::span::iterator pattIt = patterns.begin(); - gsl::span trackROFspan(trackROFvec); - loadROF(trackROFspan, compClusters, pattIt, labels); - pattIt = patterns.begin(); + for (int iLayer = 0; iLayer < ((mDoStaggering) ? NLayers : 1); ++iLayer) { + gsl::span::iterator pattIt = patterns[iLayer].begin(); + loadROF(rofsinput[iLayer], compClusters[iLayer], pattIt, ((mDoStaggering) ? iLayer : -1), labels[iLayer]); + } auto logger = [&](const std::string& s) { LOG(info) << s; }; auto fatalLogger = [&](const std::string& s) { LOG(fatal) << s; }; auto errorLogger = [&](const std::string& s) { LOG(error) << s; }; FastMultEst multEst; // mult estimator - std::vector processingMask, processUPCMask; - int cutVertexMult{0}, cutUPCVertex{0}, cutRandomMult = int(trackROFvec.size()) - multEst.selectROFs(trackROFvec, compClusters, physTriggers, processingMask); - processUPCMask.resize(processingMask.size(), false); - mTimeFrame->setMultiplicityCutMask(processingMask); + o2::its::ROFMaskTable processMultiplictyMask{mTimeFrame->getROFOverlapTable()}, processUPCMask{mTimeFrame->getROFOverlapTable()}; + multEst.selectROFs(rofsinput, compClusters, physTriggers, tfInfo.firstTForbit, mDoStaggering, mTimeFrame->getROFOverlapTableView(), processMultiplictyMask); + mTimeFrame->setMultiplicityCutMask(processMultiplictyMask); + for (int iLayer = 0; iLayer < ((mDoStaggering) ? NLayers : 1); ++iLayer) { + mTimeFrame->getROFMaskView().print(iLayer); + } + float vertexerElapsedTime{0.f}; if (mRunVertexer) { - vertROFvec.reserve(trackROFvec.size()); // Run seeding vertexer - if (!compClusters.empty()) { - vertexerElapsedTime = mVertexer->clustersToVertices(logger); + vertexerElapsedTime = mVertexer->clustersToVertices(logger); + // FIXME: this is a temporary stop-gap measure until we figure the rest out + const auto& vtx = mTimeFrame->getPrimaryVertices(); + vertices.insert(vertices.begin(), vtx.begin(), vtx.end()); + if (mIsMC) { + allVerticesLabels.reserve(vertices.size()); + allVerticesPurities.reserve(vertices.size()); + for (const auto& lbl : mTimeFrame->getPrimaryVerticesLabels()) { + allVerticesLabels.push_back(lbl.first); + allVerticesPurities.push_back(lbl.second); + } } - } else { // cosmics - mTimeFrame->resetRofPV(); } - const auto& multEstConf = FastMultEstConfig::Instance(); // parameters for mult estimation and cuts - gsl::span> vMCRecInfo; - gsl::span vMCContLabels; - for (auto iRof{0}; iRof < trackROFspan.size(); ++iRof) { + multEst.selectROFsWithVertices(vertices, mTimeFrame->getROFOverlapTableView(), processMultiplictyMask); + + auto clockROFspan = rofsinput[clockLayerId]; + auto clockTiming = mTimeFrame->getROFOverlapTableView().getClockLayer(); + for (auto iRof{0}; iRof < clockROFspan.size(); ++iRof) { bounded_vector vtxVecLoc; - auto& vtxROF = vertROFvec.emplace_back(trackROFspan[iRof]); - vtxROF.setFirstEntry(vertices.size()); + auto& vtxROF = vertROFvec.emplace_back(clockROFspan[iRof]); + vtxROF.setFirstEntry((int)vertices.size()); + if (mRunVertexer) { - auto vtxSpan = mTimeFrame->getPrimaryVertices(iRof); - if (mIsMC) { - vMCRecInfo = mTimeFrame->getPrimaryVerticesMCRecInfo(iRof); - if (o2::its::VertexerParamConfig::Instance().outputContLabels) { - vMCContLabels = mTimeFrame->getPrimaryVerticesContributors(iRof); - } - } + auto vtxSpan = mTimeFrame->getPrimaryVertices(clockLayerId, iRof); if (o2::its::TrackerParamConfig::Instance().doUPCIteration) { if (!vtxSpan.empty()) { if (vtxSpan[0].isFlagSet(Vertex::UPCMode) == 1) { // at least one vertex in this ROF and it is from second vertex iteration LOGP(debug, "ROF {} rejected as vertices are from the UPC iteration", iRof); - processUPCMask[iRof] = true; - cutUPCVertex++; + processUPCMask.selectROF({clockTiming.getROFStartInBC(iRof), clockTiming.getROFEndInBC(iRof)}); vtxROF.setFlag(o2::itsmft::ROFRecord::VtxUPCMode); } else { // in all cases except if as standard mode vertex was found, the ROF was processed with UPC settings vtxROF.setFlag(o2::itsmft::ROFRecord::VtxStdMode); @@ -193,125 +228,129 @@ void ITSTrackingInterface::run(framework::ProcessingContext& pc) } else { vtxROF.setFlag(o2::itsmft::ROFRecord::VtxStdMode); } - vtxROF.setNEntries(vtxSpan.size()); - bool selROF = vtxSpan.empty(); - for (int iV{0}, iVC{0}; iV < vtxSpan.size(); ++iV) { - const auto& v = vtxSpan[iV]; - if (multEstConf.isVtxMultCutRequested() && !multEstConf.isPassingVtxMultCut(v.getNContributors())) { - iVC += v.getNContributors(); - continue; // skip vertex of unwanted multiplicity - } - selROF = true; - vertices.push_back(v); - if (mIsMC && !VertexerParamConfig::Instance().useTruthSeeding) { - allVerticesLabels.push_back(vMCRecInfo[iV].first); - allVerticesPurities.push_back(vMCRecInfo[iV].second); - if (o2::its::VertexerParamConfig::Instance().outputContLabels) { - allVerticesContLabels.insert(allVerticesContLabels.end(), vMCContLabels.begin() + iVC, vMCContLabels.begin() + iVC + v.getNContributors()); - } - } - iVC += v.getNContributors(); - } - if (processingMask[iRof] && !selROF) { // passed selection in clusters and not in vertex multiplicity - LOGP(info, "ROF {} rejected by the vertex multiplicity selection [{},{}]", iRof, multEstConf.cutMultVtxLow, multEstConf.cutMultVtxHigh); - processingMask[iRof] = selROF; - cutVertexMult++; - } - } else { // cosmics - vtxVecLoc.emplace_back(); - vtxVecLoc.back().setNContributors(1); - vtxROF.setNEntries(vtxVecLoc.size()); - for (auto& v : vtxVecLoc) { - vertices.push_back(v); - } - mTimeFrame->addPrimaryVertices(vtxVecLoc, 0); + vtxROF.setNEntries((int)vtxSpan.size()); } } - if (mRunVertexer && !compClusters.empty()) { - LOG(info) << fmt::format(" - Vertex seeding total elapsed time: {} ms for {} ({} + {}) vertices found in {}/{} ROFs", + + if (mRunVertexer && hasClusters) { + LOG(info) << fmt::format(" - Vertex seeding total elapsed time: {} ms for {} vertices found", vertexerElapsedTime, - mTimeFrame->getPrimaryVerticesNum(), - mTimeFrame->getTotVertIteration()[0], - o2::its::VertexerParamConfig::Instance().nIterations > 1 ? mTimeFrame->getTotVertIteration()[1] : 0, - trackROFspan.size() - mTimeFrame->getNoVertexROF(), - trackROFspan.size()); - LOG(info) << fmt::format(" - FastMultEst: rejected {}/{} ROFs: random/mult.sel:{} (seed {}), vtx.sel:{}", cutRandomMult + cutVertexMult, trackROFspan.size(), cutRandomMult, multEst.lastRandomSeed, cutVertexMult); + mTimeFrame->getPrimaryVerticesNum()); } + if (mOverrideBeamEstimation) { LOG(info) << fmt::format(" - Beam position set to: {}, {} from meanvertex object", mTimeFrame->getBeamX(), mTimeFrame->getBeamY()); } else { LOG(info) << fmt::format(" - Beam position computed for the TF: {}, {}", mTimeFrame->getBeamX(), mTimeFrame->getBeamY()); } - if (mCosmicsProcessing && compClusters.size() > 1500 * trackROFspan.size()) { - LOG(error) << "Cosmics processing was requested with an average detector occupancy exceeding 1.e-7, skipping TF processing."; - } else { - if (!compClusters.empty()) { - mTimeFrame->setMultiplicityCutMask(processingMask); - mTimeFrame->setROFMask(processUPCMask); - // Run CA tracker - if (mMode == o2::its::TrackingMode::Async && o2::its::TrackerParamConfig::Instance().fataliseUponFailure) { - mTracker->clustersToTracks(logger, fatalLogger); - } else { - mTracker->clustersToTracks(logger, errorLogger); - } + + if (hasClusters) { + mTimeFrame->setMultiplicityCutMask(processMultiplictyMask); + mTimeFrame->setUPCCutMask(processUPCMask); + // Run CA tracker + if (mMode == o2::its::TrackingMode::Async && o2::its::TrackerParamConfig::Instance().fataliseUponFailure) { + mTracker->clustersToTracks(logger, fatalLogger); + } else { + mTracker->clustersToTracks(logger, errorLogger); } - size_t totTracks{mTimeFrame->getNumberOfTracks()}, totClusIDs{mTimeFrame->getNumberOfUsedClusters()}; - if (totTracks) { - allTracks.reserve(totTracks); - allClusIdx.reserve(totClusIDs); + } - if (mTimeFrame->hasBogusClusters()) { - LOG(warning) << fmt::format(" - The processed timeframe had {} clusters with wild z coordinates, check the dictionaries", mTimeFrame->hasBogusClusters()); - } + size_t totTracks{mTimeFrame->getNumberOfTracks()}, totClusIDs{mTimeFrame->getNumberOfUsedClusters()}; + if (totTracks) { + allTracks.reserve(totTracks); + allClusIdx.reserve(totClusIDs); - for (unsigned int iROF{0}; iROF < trackROFvec.size(); ++iROF) { - auto& tracksROF{trackROFvec[iROF]}; - auto& vtxROF = vertROFvec[iROF]; - auto& tracks = mTimeFrame->getTracks(iROF); - auto number{tracks.size()}; - auto first{allTracks.size()}; - int offset = -tracksROF.getFirstEntry(); // cluster entry!!! - tracksROF.setFirstEntry(first); - tracksROF.setNEntries(number); - tracksROF.setFlags(vtxROF.getFlags()); // copies 0xffffffff if cosmics - if (processingMask[iROF]) { - irFrames.emplace_back(tracksROF.getBCData(), tracksROF.getBCData() + nBCPerTF - 1).info = tracks.size(); + if (mTimeFrame->hasBogusClusters()) { + LOG(warning) << fmt::format(" - The processed timeframe had {} clusters with wild z coordinates, check the dictionaries", mTimeFrame->hasBogusClusters()); + } + + auto& tracks = mTimeFrame->getTracks(); + allTrackLabels.reserve(mTimeFrame->getTracksLabel().size()); // should be 0 if not MC + std::copy(mTimeFrame->getTracksLabel().begin(), mTimeFrame->getTracksLabel().end(), std::back_inserter(allTrackLabels)); + { + // create the track to clock ROF association here + // the clock ROF is just the fastest ROF + // the number of ROFs does not necessarily reflect the actual ROFs + // due to possible delay of other layers, however it is guaranteed to be >=0 + // tracks are guaranteed to be sorted here by their lower edge + const auto& clock = mTimeFrame->getROFOverlapTableView().getClock(); + const auto& clockLayer = mTimeFrame->getROFOverlapTableView().getClockLayer(); + auto setBCData = [&](auto& rofs) { + for (size_t iROF{0}; iROF < rofs.size(); ++iROF) { // set BC data + auto& rof = rofs[iROF]; + int orb = (iROF * par.getROFLengthInBC(clock) / o2::constants::lhc::LHCMaxBunches) + tfInfo.firstTForbit; + int bc = (iROF * par.getROFLengthInBC(clock) % o2::constants::lhc::LHCMaxBunches) + par.getROFDelayInBC(clock); + o2::InteractionRecord ir(bc, orb); + rof.setBCData(ir); + rof.setROFrame(iROF); + rof.setNEntries(0); + rof.setFirstEntry(-1); } - allTrackLabels.reserve(mTimeFrame->getTracksLabel(iROF).size()); // should be 0 if not MC - std::copy(mTimeFrame->getTracksLabel(iROF).begin(), mTimeFrame->getTracksLabel(iROF).end(), std::back_inserter(allTrackLabels)); - // Some conversions that needs to be moved in the tracker internals - for (unsigned int iTrk{0}; iTrk < tracks.size(); ++iTrk) { - auto& trc{tracks[iTrk]}; - trc.setFirstClusterEntry(allClusIdx.size()); // before adding tracks, create final cluster indices - int ncl = trc.getNumberOfClusters(), nclf = 0; - for (int ic = TrackITSExt::MaxClusters; ic--;) { // track internally keeps in->out cluster indices, but we want to store the references as out->in!!! - auto clid = trc.getClusterIndex(ic); - if (clid >= 0) { - trc.setClusterSize(ic, mTimeFrame->getClusterSize(clid)); - allClusIdx.push_back(clid); - nclf++; - } + }; + // we pick whatever is the largest possible number of rofs since there might be tracks/vertices which are beyond + // the clock layer + int highestROF{0}; + for (const auto& trc : tracks) { + highestROF = std::max(highestROF, (int)clockLayer.getROF(trc.getTimeStamp())); + } + for (const auto& vtx : vertices) { + highestROF = std::max(highestROF, (int)clockLayer.getROF(vtx.getTimeStamp().lower())); + } + highestROF = std::max(highestROF, (int)clockLayer.mNROFsTF); + allTrackROFs.resize(highestROF); + vertROFvec.resize(highestROF); + setBCData(allTrackROFs); + setBCData(vertROFvec); + + mTimeFrame->useMultiplictyMask(); // use multiplicty selection for IR frames + + std::vector rofEntries(highestROF + 1, 0); + for (unsigned int iTrk{0}; iTrk < tracks.size(); ++iTrk) { + auto& trc{tracks[iTrk]}; + trc.setFirstClusterEntry((int)allClusIdx.size()); // before adding tracks, create final cluster indices + int ncl = trc.getNumberOfClusters(), nclf = 0; + for (int ic = TrackITSExt::MaxClusters; ic--;) { // track internally keeps in->out cluster indices, but we want to store the references as out->in!!! + auto clid = trc.getClusterIndex(ic); + if (clid >= 0) { + trc.setClusterSize(ic, mTimeFrame->getClusterSize((mDoStaggering) ? ic : 0, clid)); + allClusIdx.push_back(clid); + nclf++; } - assert(ncl == nclf); - allTracks.emplace_back(trc); } + assert(ncl == nclf); + allTracks.emplace_back(trc); + auto rof = clockLayer.getROF(trc.getTimeStamp()); + ++rofEntries[rof]; } - } else { - for (auto& r : trackROFvec) { // reset data copied from the clusters - r.setFirstEntry(0); - r.setNEntries(0); + std::exclusive_scan(rofEntries.begin(), rofEntries.end(), rofEntries.begin(), 0); + for (size_t iROF{0}; iROF < allTrackROFs.size(); ++iROF) { + allTrackROFs[iROF].setFirstEntry(rofEntries[iROF]); + allTrackROFs[iROF].setNEntries(rofEntries[iROF + 1] - rofEntries[iROF]); + if (mTimeFrame->getROFMaskView().isROFEnabled(clockLayerId, (int)iROF)) { + auto& irFrame = irFrames.emplace_back(allTrackROFs[iROF].getBCData(), allTrackROFs[iROF].getBCData() + clockLayer.mROFLength - 1); + irFrame.info = allTrackROFs[iROF].getNEntries(); + } } - } - LOGP(info, "ITSTracker pushed {} tracks and {} vertices", allTracks.size(), vertices.size()); - if (mIsMC) { - LOGP(info, "ITSTracker pushed {} track labels", allTrackLabels.size()); - LOGP(info, "ITSTracker pushed {} vertex labels", allVerticesLabels.size()); - if (!allVerticesContLabels.empty()) { - LOGP(info, "ITSTracker pushed {} vertex contributor labels", allVerticesContLabels.size()); + // same thing for vertices rofs + std::fill(rofEntries.begin(), rofEntries.end(), 0); + for (const auto& vtx : vertices) { + auto rof = clockLayer.getROF(vtx.getTimeStamp().lower()); + ++rofEntries[rof]; + } + std::exclusive_scan(rofEntries.begin(), rofEntries.end(), rofEntries.begin(), 0); + for (size_t iROF{0}; iROF < vertROFvec.size(); ++iROF) { + vertROFvec[iROF].setFirstEntry(rofEntries[iROF]); + vertROFvec[iROF].setNEntries(rofEntries[iROF + 1] - rofEntries[iROF]); } - LOGP(info, "ITSTracker pushed {} vertex purities", allVerticesPurities.size()); } } + + LOGP(info, "ITSTracker pushed {} tracks in {} rofs and {} vertices {}", allTracks.size(), allTrackROFs.size(), vertices.size(), ((mDoStaggering) ? "in staggered-readout mode" : "in normal mode")); + if (mIsMC) { + LOGP(info, "ITSTracker pushed {} track labels", allTrackLabels.size()); + LOGP(info, "ITSTracker pushed {} vertex labels", allVerticesLabels.size()); + LOGP(info, "ITSTracker pushed {} vertex purities", allVerticesPurities.size()); + } mTimeFrame->wipe(); } @@ -334,19 +373,50 @@ void ITSTrackingInterface::updateTimeDependentParams(framework::ProcessingContex initialise(); if (pc.services().get().inputTimesliceId == 0) { // print settings only for the 1st pipeling - o2::its::VertexerParamConfig::Instance().printKeyValues(); - o2::its::TrackerParamConfig::Instance().printKeyValues(); + // print all used settings + if (o2::its::FastMultEstConfig::Instance().isRequested()) { + o2::its::FastMultEstConfig::Instance().printKeyValues(true, true); + } const auto& vtxParams = mVertexer->getParameters(); + if (!vtxParams.empty()) { + o2::its::VertexerParamConfig::Instance().printKeyValues(true, true); + } + const auto& trParams = mTracker->getParameters(); + if (!trParams.empty()) { + o2::its::TrackerParamConfig::Instance().printKeyValues(true, true); + } + // quick summary for (size_t it = 0; it < vtxParams.size(); it++) { const auto& par = vtxParams[it]; LOGP(info, "vtxIter#{} : {}", it, par.asString()); } - const auto& trParams = mTracker->getParameters(); for (size_t it = 0; it < trParams.size(); it++) { const auto& par = trParams[it]; LOGP(info, "recoIter#{} : {}", it, par.asString()); } } + + // prepare rof lookup table(s) + const auto& par = o2::itsmft::DPLAlpideParam::Instance(); + const int nOrbitsPerTF = o2::base::GRPGeomHelper::getNHBFPerTF(); + TimeFrameN::ROFOverlapTableN rofTable; + TimeFrameN::ROFVertexLookupTableN vtxTable; + const auto& trackParams = mTracker->getParameters(); + for (int iLayer = 0; iLayer < NLayers; ++iLayer) { + const unsigned int nROFsPerOrbit = o2::constants::lhc::LHCMaxBunches / par.getROFLengthInBC(iLayer); + const LayerTiming timing{ + .mNROFsTF = (nROFsPerOrbit * nOrbitsPerTF), + .mROFLength = (uint32_t)par.getROFLengthInBC(iLayer), + .mROFDelay = (uint32_t)par.getROFDelayInBC(iLayer), + .mROFBias = (uint32_t)par.getROFBiasInBC(iLayer), + .mROFAddTimeErr = (trackParams.empty() ? o2::its::TrackerParamConfig::Instance().addTimeError[iLayer] : trackParams[0].AddTimeError[iLayer])}; + rofTable.defineLayer(iLayer, timing); + vtxTable.defineLayer(iLayer, timing); + } + rofTable.init(); + mTimeFrame->setROFOverlapTable(rofTable); + vtxTable.init(); + mTimeFrame->setROFVertexLookupTable(vtxTable); } } @@ -408,7 +478,8 @@ void ITSTrackingInterface::setTraitsFromProvider(VertexerTraitsN* vertexerTraits void ITSTrackingInterface::loadROF(gsl::span& trackROFspan, gsl::span clusters, gsl::span::iterator& pattIt, + int layer, const dataformats::MCTruthContainer* mcLabels) { - mTimeFrame->loadROFrameData(trackROFspan, clusters, pattIt, mDict, mcLabels); + mTimeFrame->loadROFrameData(trackROFspan, clusters, pattIt, mDict, layer, mcLabels); } diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackingLinkDef.h b/Detectors/ITSMFT/ITS/tracking/src/TrackingLinkDef.h index 2fe70e96248f9..9efd6dde0176d 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackingLinkDef.h +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackingLinkDef.h @@ -24,6 +24,9 @@ #pragma link C++ class o2::its::TrackingFrameInfo + ; #pragma link C++ class std::vector < o2::its::TrackingFrameInfo> + ; +#pragma link C++ class o2::its::TrackingFrameInfo + ; +#pragma link C++ class std::vector < o2::its::TrackingFrameInfo> + ; + #pragma link C++ class o2::its::Line + ; #pragma link C++ class std::vector < o2::its::Line> + ; @@ -39,4 +42,8 @@ #pragma link C++ class o2::its::ITSGpuTrackingParamConfig + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::ITSGpuTrackingParamConfig> + ; +#pragma link C++ class o2::its::FastMultEst + ; +#pragma link C++ class o2::its::FastMultEstConfig + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::FastMultEstConfig> + ; + #endif diff --git a/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx b/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx index c4b1fb427513f..222b4801a5767 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx @@ -26,8 +26,8 @@ namespace o2::its { -template -Vertexer::Vertexer(VertexerTraitsN* traits) : mTraits(traits) +template +Vertexer::Vertexer(VertexerTraitsN* traits) : mTraits(traits) { if (!mTraits) { LOG(fatal) << "nullptr passed to ITS vertexer construction."; @@ -35,18 +35,19 @@ Vertexer::Vertexer(VertexerTraitsN* traits) : mTraits(traits) mVertParams.resize(1); } -template -float Vertexer::clustersToVertices(LogFunc logger) +template +float Vertexer::clustersToVertices(LogFunc logger) { LogFunc evalLog = [](const std::string&) {}; if (mTimeFrame->hasMCinformation() && mVertParams[0].useTruthSeeding) { - return evaluateTask(&Vertexer::addTruthSeeds, StateNames[mCurState = TruthSeeding], 0, evalLog); + float t = evaluateTask(&Vertexer::addTruthSeeds, StateNames[mCurState = TruthSeeding], 0, evalLog); + sortVertices(); + return t; } TrackingParameters trkPars; - TimeFrameGPUParameters tfGPUpar; - mTraits->updateVertexingParameters(mVertParams, tfGPUpar); + mTraits->updateVertexingParameters(mVertParams); auto handleException = [&](const auto& err) { LOGP(error, "Encountered critical error in step {}, stopping further processing of this TF: {}", StateNames[mCurState], err.what()); @@ -71,7 +72,7 @@ float Vertexer::clustersToVertices(LogFunc logger) nTracklets12 = mTimeFrame->getTotalTrackletsTF(1); auto timeSelectionIteration = evaluateTask(&Vertexer::validateTracklets, StateNames[mCurState = Validating], iteration, evalLog, iteration); auto timeVertexingIteration = evaluateTask(&Vertexer::findVertices, StateNames[mCurState = Finding], iteration, evalLog, iteration); - printEpilog(logger, nTracklets01, nTracklets12, mTimeFrame->getNLinesTotal(), mTimeFrame->getTotVertIteration()[iteration], timeInitIteration, timeTrackletIteration, timeSelectionIteration, timeVertexingIteration); + printEpilog(logger, nTracklets01, nTracklets12, mTimeFrame->getNLinesTotal(), mTimeFrame->getPrimaryVertices().size(), timeInitIteration, timeTrackletIteration, timeSelectionIteration, timeVertexingIteration); timeInit += timeInitIteration; timeTracklet += timeTrackletIteration; timeSelection += timeSelectionIteration; @@ -85,18 +86,53 @@ float Vertexer::clustersToVertices(LogFunc logger) LOGP(fatal, "Uncaught exception!"); } + sortVertices(); + return timeInit + timeTracklet + timeSelection + timeVertexing; } -template -void Vertexer::adoptTimeFrame(TimeFrameN& tf) +template +void Vertexer::sortVertices() +{ + auto& pvs = mTimeFrame->getPrimaryVertices(); + bounded_vector indices(pvs.size(), mMemoryPool.get()); + std::iota(indices.begin(), indices.end(), 0); + // provide vertices sorted by lower-bound + std::sort(indices.begin(), indices.end(), [&pvs](size_t i, size_t j) { + const auto& a = pvs[i].getTimeStamp(); + const auto& b = pvs[j].getTimeStamp(); + const auto aLower = a.lower(); + const auto bLower = b.lower(); + if (aLower != bLower) { + return aLower < bLower; + } + return pvs[i].getNContributors() > pvs[j].getNContributors(); + }); + bounded_vector sortedVtx(mMemoryPool.get()); + sortedVtx.reserve(pvs.size()); + for (const size_t idx : indices) { + sortedVtx.push_back(pvs[idx]); + } + pvs.swap(sortedVtx); + if (mTimeFrame->hasMCinformation()) { + auto& mc = mTimeFrame->getPrimaryVerticesLabels(); + bounded_vector sortedMC(mMemoryPool.get()); + for (const size_t idx : indices) { + sortedMC.push_back(mc[idx]); + } + mc.swap(sortedMC); + } +} + +template +void Vertexer::adoptTimeFrame(TimeFrameN& tf) { mTimeFrame = &tf; mTraits->adoptTimeFrame(&tf); } -template -void Vertexer::printEpilog(LogFunc& logger, +template +void Vertexer::printEpilog(LogFunc& logger, const unsigned int trackletN01, const unsigned int trackletN12, const unsigned selectedN, const unsigned int vertexN, const float initT, const float trackletT, const float selecT, const float vertexT) diff --git a/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx index 6d51f7bab5d36..5e27e20b3ddee 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx @@ -10,10 +10,9 @@ // or submit itself to any jurisdiction. /// +#include #include #include -#include -#include #include #include @@ -22,17 +21,18 @@ #include "ITStracking/VertexerTraits.h" #include "ITStracking/BoundedAllocator.h" #include "ITStracking/ClusterLines.h" +#include "ITStracking/Definitions.h" #include "ITStracking/Tracklet.h" #include "SimulationDataFormat/DigitizationContext.h" +#include "SimulationDataFormat/O2DatabasePDG.h" #include "Steer/MCKinematicsReader.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DetectorsRaw/HBFUtils.h" -#include "CommonUtils/TreeStreamRedirector.h" namespace o2::its { -template +template static void trackleterKernelHost( const gsl::span& clustersNextLayer, // 0 2 const gsl::span& clustersCurrentLayer, // 1 1 @@ -41,10 +41,11 @@ static void trackleterKernelHost( const float phiCut, bounded_vector& tracklets, gsl::span foundTracklets, - const IndexTableUtils& utils, - const short pivotRof, - const short targetRof, - gsl::span rofFoundTrackletsOffsets, // we want to change those, to keep track of the offset in deltaRof>0 + const IndexTableUtils& utils, + const TimeEstBC& timErr, + gsl::span rofFoundTrackletsOffsets, + const int globalOffsetNextLayer = 0, + const int globalOffsetCurrentLayer = 0, const int maxTrackletsPerCluster = static_cast(2e3)) { const int PhiBins{utils.getNphiBins()}; @@ -53,7 +54,7 @@ static void trackleterKernelHost( for (int iCurrentLayerClusterIndex = 0; iCurrentLayerClusterIndex < clustersCurrentLayer.size(); ++iCurrentLayerClusterIndex) { int storedTracklets{0}; const Cluster& currentCluster{clustersCurrentLayer[iCurrentLayerClusterIndex]}; - const int4 selectedBinsRect{VertexerTraits::getBinsRect(currentCluster, (int)Mode, 0.f, 50.f, phiCut / 2, utils)}; + const int4 selectedBinsRect{VertexerTraits::getBinsRect(currentCluster, (int)Mode, 0.f, 50.f, phiCut / 2, utils)}; if (selectedBinsRect.x != 0 || selectedBinsRect.y != 0 || selectedBinsRect.z != 0 || selectedBinsRect.w != 0) { int phiBinsNum{selectedBinsRect.w - selectedBinsRect.y + 1}; if (phiBinsNum < 0) { @@ -74,9 +75,9 @@ static void trackleterKernelHost( if (storedTracklets < maxTrackletsPerCluster) { if constexpr (!EvalRun) { if constexpr (Mode == TrackletMode::Layer0Layer1) { - tracklets[rofFoundTrackletsOffsets[iCurrentLayerClusterIndex] + storedTracklets] = Tracklet{iNextLayerClusterIndex, iCurrentLayerClusterIndex, nextCluster, currentCluster, targetRof, pivotRof}; + tracklets[rofFoundTrackletsOffsets[iCurrentLayerClusterIndex] + storedTracklets] = Tracklet{globalOffsetNextLayer + iNextLayerClusterIndex, globalOffsetCurrentLayer + iCurrentLayerClusterIndex, nextCluster, currentCluster, timErr}; } else { - tracklets[rofFoundTrackletsOffsets[iCurrentLayerClusterIndex] + storedTracklets] = Tracklet{iCurrentLayerClusterIndex, iNextLayerClusterIndex, currentCluster, nextCluster, pivotRof, targetRof}; + tracklets[rofFoundTrackletsOffsets[iCurrentLayerClusterIndex] + storedTracklets] = Tracklet{globalOffsetCurrentLayer + iCurrentLayerClusterIndex, globalOffsetNextLayer + iNextLayerClusterIndex, currentCluster, nextCluster, timErr}; } } ++storedTracklets; @@ -94,10 +95,10 @@ static void trackleterKernelHost( } static void trackletSelectionKernelHost( - const gsl::span clusters0, // 0 - const gsl::span clusters1, // 1 - gsl::span usedClusters0, // Layer 0 - gsl::span usedClusters2, // Layer 2 + const Cluster* clusters0, // global layer 0 clusters + const Cluster* clusters1, // global layer 1 clusters + gsl::span usedClusters0, // global layer 0 used clusters + gsl::span usedClusters2, // global layer 2 used clusters const gsl::span& tracklets01, const gsl::span& tracklets12, bounded_vector& usedTracklets, @@ -106,15 +107,13 @@ static void trackletSelectionKernelHost( bounded_vector& lines, const gsl::span& trackletLabels, bounded_vector& linesLabels, - const short targetRofId0, - const short targetRofId2, - bool safeWrites = false, + const int nLayer1Clusters, const float tanLambdaCut = 0.025f, const float phiCut = 0.005f, - const int maxTracklets = static_cast(1e2)) + const int maxTracklets = 100) { int offset01{0}, offset12{0}; - for (unsigned int iCurrentLayerClusterIndex{0}; iCurrentLayerClusterIndex < clusters1.size(); ++iCurrentLayerClusterIndex) { + for (int iCurrentLayerClusterIndex{0}; iCurrentLayerClusterIndex < nLayer1Clusters; ++iCurrentLayerClusterIndex) { int validTracklets{0}; for (int iTracklet12{offset12}; iTracklet12 < offset12 + foundTracklets12[iCurrentLayerClusterIndex]; ++iTracklet12) { for (int iTracklet01{offset01}; iTracklet01 < offset01 + foundTracklets01[iCurrentLayerClusterIndex]; ++iTracklet01) { @@ -124,23 +123,17 @@ static void trackletSelectionKernelHost( const auto& tracklet01{tracklets01[iTracklet01]}; const auto& tracklet12{tracklets12[iTracklet12]}; - - if (tracklet01.rof[0] != targetRofId0 || tracklet12.rof[1] != targetRofId2) { + if (!tracklet01.getTimeStamp().isCompatible(tracklet12.getTimeStamp())) { continue; } const float deltaTanLambda{o2::gpu::GPUCommonMath::Abs(tracklet01.tanLambda - tracklet12.tanLambda)}; const float deltaPhi{o2::gpu::GPUCommonMath::Abs(math_utils::smallestAngleDifference(tracklet01.phi, tracklet12.phi))}; if (deltaTanLambda < tanLambdaCut && deltaPhi < phiCut && validTracklets != maxTracklets) { - if (safeWrites) { - __atomic_store_n(&usedClusters0[tracklet01.firstClusterIndex], 1, __ATOMIC_RELAXED); - __atomic_store_n(&usedClusters2[tracklet12.secondClusterIndex], 1, __ATOMIC_RELAXED); - } else { - usedClusters0[tracklet01.firstClusterIndex] = 1; - usedClusters2[tracklet12.secondClusterIndex] = 1; - } + usedClusters0[tracklet01.firstClusterIndex] = 1; + usedClusters2[tracklet12.secondClusterIndex] = 1; usedTracklets[iTracklet01] = true; - lines.emplace_back(tracklet01, clusters0.data(), clusters1.data()); + lines.emplace_back(tracklet01, clusters0, clusters1); if (!trackletLabels.empty()) { linesLabels.emplace_back(trackletLabels[iTracklet01]); } @@ -153,8 +146,8 @@ static void trackletSelectionKernelHost( } } -template -void VertexerTraits::updateVertexingParameters(const std::vector& vrtPar, const TimeFrameGPUParameters& tfPar) +template +void VertexerTraits::updateVertexingParameters(const std::vector& vrtPar) { mVrtParams = vrtPar; mIndexTableUtils.setTrackingParameters(vrtPar[0]); @@ -165,15 +158,15 @@ void VertexerTraits::updateVertexingParameters(const std::vector -void VertexerTraits::computeTracklets(const int iteration) +template +void VertexerTraits::computeTracklets(const int iteration) { mTaskArena->execute([&] { - tbb::parallel_for(0, mTimeFrame->getNrof(), [&](const short pivotRofId) { - bool skipROF = iteration && (int)mTimeFrame->getPrimaryVertices(pivotRofId).size() > mVrtParams[iteration].vertPerRofThreshold; - short startROF{std::max((short)0, static_cast(pivotRofId - mVrtParams[iteration].deltaRof))}; - short endROF{std::min(static_cast(mTimeFrame->getNrof()), static_cast(pivotRofId + mVrtParams[iteration].deltaRof + 1))}; - for (auto targetRofId = startROF; targetRofId < endROF; ++targetRofId) { + tbb::parallel_for(0, mTimeFrame->getNrof(1), [&](const short pivotRofId) { + bool skipROF = !mTimeFrame->getROFMaskView().isROFEnabled(1, pivotRofId); + const auto& rofRange01 = mTimeFrame->getROFOverlapTableView().getOverlap(1, 0, pivotRofId); + for (auto targetRofId = rofRange01.getFirstEntry(); targetRofId < rofRange01.getEntriesBound(); ++targetRofId) { + const auto timeErr = mTimeFrame->getROFOverlapTableView().getTimeStamp(0, targetRofId, 1, pivotRofId); trackleterKernelHost( !skipROF ? mTimeFrame->getClustersOnLayer(targetRofId, 0) : gsl::span(), // Clusters to be matched with the next layer in target rof !skipROF ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), // Clusters to be matched with the current layer in pivot rof @@ -183,10 +176,15 @@ void VertexerTraits::computeTracklets(const int iteration) mTimeFrame->getTracklets()[0], // Flat tracklet buffer mTimeFrame->getNTrackletsCluster(pivotRofId, 0), // Span of the number of tracklets per each cluster in pivot rof mIndexTableUtils, - pivotRofId, - targetRofId, + timeErr, gsl::span(), // Offset in the tracklet buffer + 0, + 0, mVrtParams[iteration].maxTrackletsPerCluster); + } + const auto& rofRange12 = mTimeFrame->getROFOverlapTableView().getOverlap(1, 2, pivotRofId); + for (auto targetRofId = rofRange12.getFirstEntry(); targetRofId < rofRange12.getEntriesBound(); ++targetRofId) { + const auto timeErr = mTimeFrame->getROFOverlapTableView().getTimeStamp(2, targetRofId, 1, pivotRofId); trackleterKernelHost( !skipROF ? mTimeFrame->getClustersOnLayer(targetRofId, 2) : gsl::span(), !skipROF ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), @@ -196,9 +194,10 @@ void VertexerTraits::computeTracklets(const int iteration) mTimeFrame->getTracklets()[1], mTimeFrame->getNTrackletsCluster(pivotRofId, 1), // Span of the number of tracklets per each cluster in pivot rof mIndexTableUtils, - pivotRofId, - targetRofId, + timeErr, gsl::span(), // Offset in the tracklet buffer + 0, + 0, mVrtParams[iteration].maxTrackletsPerCluster); } mTimeFrame->getNTrackletsROF(pivotRofId, 0) = std::accumulate(mTimeFrame->getNTrackletsCluster(pivotRofId, 0).begin(), mTimeFrame->getNTrackletsCluster(pivotRofId, 0).end(), 0); @@ -214,13 +213,12 @@ void VertexerTraits::computeTracklets(const int iteration) mTimeFrame->getTracklets()[1].resize(tot1); } - tbb::parallel_for(0, mTimeFrame->getNrof(), [&](const short pivotRofId) { - bool skipROF = iteration && (int)mTimeFrame->getPrimaryVertices(pivotRofId).size() > mVrtParams[iteration].vertPerRofThreshold; - short startROF{std::max((short)0, static_cast(pivotRofId - mVrtParams[iteration].deltaRof))}; - short endROF{std::min(static_cast(mTimeFrame->getNrof()), static_cast(pivotRofId + mVrtParams[iteration].deltaRof + 1))}; - auto mobileOffset0 = mTimeFrame->getNTrackletsROF(pivotRofId, 0); - auto mobileOffset1 = mTimeFrame->getNTrackletsROF(pivotRofId, 1); - for (auto targetRofId = startROF; targetRofId < endROF; ++targetRofId) { + tbb::parallel_for(0, mTimeFrame->getNrof(1), [&](const short pivotRofId) { + bool skipROF = !mTimeFrame->getROFMaskView().isROFEnabled(1, pivotRofId); + const int globalOffsetPivot = mTimeFrame->getSortedStartIndex(pivotRofId, 1); + const auto& rofRange01 = mTimeFrame->getROFOverlapTableView().getOverlap(1, 0, pivotRofId); + for (auto targetRofId = rofRange01.getFirstEntry(); targetRofId < rofRange01.getEntriesBound(); ++targetRofId) { + const auto timeErr = mTimeFrame->getROFOverlapTableView().getTimeStamp(0, targetRofId, 1, pivotRofId); trackleterKernelHost( !skipROF ? mTimeFrame->getClustersOnLayer(targetRofId, 0) : gsl::span(), !skipROF ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), @@ -230,10 +228,15 @@ void VertexerTraits::computeTracklets(const int iteration) mTimeFrame->getTracklets()[0], mTimeFrame->getNTrackletsCluster(pivotRofId, 0), mIndexTableUtils, - pivotRofId, - targetRofId, + timeErr, mTimeFrame->getExclusiveNTrackletsCluster(pivotRofId, 0), + mTimeFrame->getSortedStartIndex(targetRofId, 0), + globalOffsetPivot, mVrtParams[iteration].maxTrackletsPerCluster); + } + const auto& rofRange12 = mTimeFrame->getROFOverlapTableView().getOverlap(1, 2, pivotRofId); + for (auto targetRofId = rofRange12.getFirstEntry(); targetRofId < rofRange12.getEntriesBound(); ++targetRofId) { + const auto timeErr = mTimeFrame->getROFOverlapTableView().getTimeStamp(2, targetRofId, 1, pivotRofId); trackleterKernelHost( !skipROF ? mTimeFrame->getClustersOnLayer(targetRofId, 2) : gsl::span(), !skipROF ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), @@ -243,9 +246,10 @@ void VertexerTraits::computeTracklets(const int iteration) mTimeFrame->getTracklets()[1], mTimeFrame->getNTrackletsCluster(pivotRofId, 1), mIndexTableUtils, - pivotRofId, - targetRofId, + timeErr, mTimeFrame->getExclusiveNTrackletsCluster(pivotRofId, 1), + mTimeFrame->getSortedStartIndex(targetRofId, 2), + globalOffsetPivot, mVrtParams[iteration].maxTrackletsPerCluster); } }); @@ -256,8 +260,8 @@ void VertexerTraits::computeTracklets(const int iteration) for (const auto& trk : mTimeFrame->getTracklets()[0]) { o2::MCCompLabel label; if (!trk.isEmpty()) { - int sortedId0{mTimeFrame->getSortedIndex(trk.rof[0], 0, trk.firstClusterIndex)}; - int sortedId1{mTimeFrame->getSortedIndex(trk.rof[1], 1, trk.secondClusterIndex)}; + int sortedId0{trk.firstClusterIndex}; + int sortedId1{trk.secondClusterIndex}; for (const auto& lab0 : mTimeFrame->getClusterLabels(0, mTimeFrame->getClusters()[0][sortedId0].clusterId)) { for (const auto& lab1 : mTimeFrame->getClusterLabels(1, mTimeFrame->getClusters()[1][sortedId1].clusterId)) { if (lab0 == lab1 && lab0.isValid()) { @@ -273,570 +277,260 @@ void VertexerTraits::computeTracklets(const int iteration) mTimeFrame->getTrackletsLabel(0).emplace_back(label); } } - -#ifdef VTX_DEBUG - debugComputeTracklets(iteration); -#endif } -template -void VertexerTraits::computeTrackletMatching(const int iteration) +template +void VertexerTraits::computeTrackletMatching(const int iteration) { mTaskArena->execute([&] { tbb::combinable totalLines{0}; tbb::parallel_for( - tbb::blocked_range(0, (short)mTimeFrame->getNrof()), + tbb::blocked_range(0, (short)mTimeFrame->getNrof(1)), [&](const tbb::blocked_range& Rofs) { for (short pivotRofId = Rofs.begin(); pivotRofId < Rofs.end(); ++pivotRofId) { - if (iteration && (int)mTimeFrame->getPrimaryVertices(pivotRofId).size() > mVrtParams[iteration].vertPerRofThreshold) { - continue; - } if (mTimeFrame->getFoundTracklets(pivotRofId, 0).empty()) { continue; } mTimeFrame->getLines(pivotRofId).reserve(mTimeFrame->getNTrackletsCluster(pivotRofId, 0).size()); bounded_vector usedTracklets(mTimeFrame->getFoundTracklets(pivotRofId, 0).size(), false, mMemoryPool.get()); - short startROF{std::max((short)0, static_cast(pivotRofId - mVrtParams[iteration].deltaRof))}; - short endROF{std::min(static_cast(mTimeFrame->getNrof()), static_cast(pivotRofId + mVrtParams[iteration].deltaRof + 1))}; - - // needed only if multi-threaded using deltaRof and only at the overlap edges of the ranges - bool safeWrite = mTaskArena->max_concurrency() > 1 && mVrtParams[iteration].deltaRof != 0 && ((Rofs.begin() - startROF < 0) || (endROF - Rofs.end() > 0)); - - for (short targetRofId0 = startROF; targetRofId0 < endROF; ++targetRofId0) { - for (short targetRofId2 = startROF; targetRofId2 < endROF; ++targetRofId2) { - if (std::abs(targetRofId0 - targetRofId2) > mVrtParams[iteration].deltaRof) { // do not allow over 3 ROFs - continue; - } - trackletSelectionKernelHost( - mTimeFrame->getClustersOnLayer(targetRofId0, 0), - mTimeFrame->getClustersOnLayer(pivotRofId, 1), - mTimeFrame->getUsedClustersROF(targetRofId0, 0), - mTimeFrame->getUsedClustersROF(targetRofId2, 2), - mTimeFrame->getFoundTracklets(pivotRofId, 0), - mTimeFrame->getFoundTracklets(pivotRofId, 1), - usedTracklets, - mTimeFrame->getNTrackletsCluster(pivotRofId, 0), - mTimeFrame->getNTrackletsCluster(pivotRofId, 1), - mTimeFrame->getLines(pivotRofId), - mTimeFrame->getLabelsFoundTracklets(pivotRofId, 0), - mTimeFrame->getLinesLabel(pivotRofId), - targetRofId0, - targetRofId2, - safeWrite, - mVrtParams[iteration].tanLambdaCut, - mVrtParams[iteration].phiCut); + trackletSelectionKernelHost( + mTimeFrame->getClusters()[0].data(), + mTimeFrame->getClusters()[1].data(), + mTimeFrame->getUsedClusters(0), + mTimeFrame->getUsedClusters(2), + mTimeFrame->getFoundTracklets(pivotRofId, 0), + mTimeFrame->getFoundTracklets(pivotRofId, 1), + usedTracklets, + mTimeFrame->getNTrackletsCluster(pivotRofId, 0), + mTimeFrame->getNTrackletsCluster(pivotRofId, 1), + mTimeFrame->getLines(pivotRofId), + mTimeFrame->getLabelsFoundTracklets(pivotRofId, 0), + mTimeFrame->getLinesLabel(pivotRofId), + static_cast(mTimeFrame->getClustersOnLayer(pivotRofId, 1).size()), + mVrtParams[iteration].tanLambdaCut, + mVrtParams[iteration].phiCut); + auto& lines = mTimeFrame->getLines(pivotRofId); + totalLines.local() += lines.size(); + std::stable_sort(lines.begin(), lines.end(), [](const Line& a, const Line& b) { + // sort by lower edge and secondly prefer wider windows + if (a.mTime.lower() != b.mTime.lower()) { + return a.mTime.lower() < b.mTime.lower(); } - } - totalLines.local() += mTimeFrame->getLines(pivotRofId).size(); + return a.mTime.upper() > b.mTime.upper(); + }); } }); mTimeFrame->setNLinesTotal(totalLines.combine(std::plus())); }); -#ifdef VTX_DEBUG - debugComputeTrackletMatching(iteration); -#endif - - // from here on we do not use tracklets from L1-2 anymore, so let's free them - deepVectorClear(mTimeFrame->getTracklets()[1]); + // from here on we do not use tracklets anymore, so let's free them + deepVectorClear(mTimeFrame->getTracklets()); } -template -void VertexerTraits::computeVertices(const int iteration) +template +void VertexerTraits::computeVertices(const int iteration) { - auto nsigmaCut{std::min(mVrtParams[iteration].vertNsigmaCut * mVrtParams[iteration].vertNsigmaCut * (mVrtParams[iteration].vertRadiusSigma * mVrtParams[iteration].vertRadiusSigma + mVrtParams[iteration].trackletSigma * mVrtParams[iteration].trackletSigma), 1.98f)}; - bounded_vector vertices(mMemoryPool.get()); - bounded_vector> polls(mMemoryPool.get()); - bounded_vector contLabels(mMemoryPool.get()); - bounded_vector noClustersVec(mTimeFrame->getNrof(), 0, mMemoryPool.get()); - for (int rofId{0}; rofId < mTimeFrame->getNrof(); ++rofId) { - if (iteration && (int)mTimeFrame->getPrimaryVertices(rofId).size() > mVrtParams[iteration].vertPerRofThreshold) { - continue; - } - const int numTracklets{static_cast(mTimeFrame->getLines(rofId).size())}; - - bounded_vector usedTracklets(numTracklets, false, mMemoryPool.get()); - for (int line1{0}; line1 < numTracklets; ++line1) { - if (usedTracklets[line1]) { + const auto nsigmaCut{std::min(mVrtParams[iteration].vertNsigmaCut * mVrtParams[iteration].vertNsigmaCut * (mVrtParams[iteration].vertRadiusSigma * mVrtParams[iteration].vertRadiusSigma + mVrtParams[iteration].trackletSigma * mVrtParams[iteration].trackletSigma), 1.98f)}; + const auto pairCut2{mVrtParams[iteration].pairCut * mVrtParams[iteration].pairCut}; + const int nRofs = mTimeFrame->getNrof(1); + const bool hasMC = mTimeFrame->hasMCinformation(); + std::vector> rofVertices(nRofs); + std::vector> rofLabels(nRofs); + + const auto processROF = [&](const int rofId) { + auto& lines = mTimeFrame->getLines(rofId); + const int nLines{static_cast(lines.size())}; + bounded_vector usedTracklets(nLines, 0, mMemoryPool.get()); + auto& clusters = mTimeFrame->getTrackletClusters(rofId); + + for (int iLine1{0}; iLine1 < nLines; ++iLine1) { + if (usedTracklets[iLine1]) { continue; } - for (int line2{line1 + 1}; line2 < numTracklets; ++line2) { - if (usedTracklets[line2]) { + const auto& line1 = lines[iLine1]; + for (int iLine2{iLine1 + 1}; iLine2 < nLines; ++iLine2) { + if (usedTracklets[iLine2]) { + continue; + } + const auto& line2 = lines[iLine2]; + if (!line1.mTime.isCompatible(line2.mTime)) { continue; } - auto dca{Line::getDCA(mTimeFrame->getLines(rofId)[line1], mTimeFrame->getLines(rofId)[line2])}; - if (dca < mVrtParams[iteration].pairCut) { - mTimeFrame->getTrackletClusters(rofId).emplace_back(line1, mTimeFrame->getLines(rofId)[line1], line2, mTimeFrame->getLines(rofId)[line2]); - std::array tmpVertex{mTimeFrame->getTrackletClusters(rofId).back().getVertex()}; - if (tmpVertex[0] * tmpVertex[0] + tmpVertex[1] * tmpVertex[1] > 4.f) { - mTimeFrame->getTrackletClusters(rofId).pop_back(); + auto dca2{Line::getDCA2(line1, line2)}; + if (dca2 < pairCut2) { + auto& cluster = clusters.emplace_back(iLine1, line1, iLine2, line2); + if (!cluster.isValid() || cluster.getR2() > 4.f) { + clusters.pop_back(); continue; } - usedTracklets[line1] = true; - usedTracklets[line2] = true; - for (int tracklet3{0}; tracklet3 < numTracklets; ++tracklet3) { - if (usedTracklets[tracklet3]) { + + usedTracklets[iLine1] = 1; + usedTracklets[iLine2] = 1; + for (int iLine3{0}; iLine3 < nLines; ++iLine3) { + if (usedTracklets[iLine3]) { continue; } - if (Line::getDistanceFromPoint(mTimeFrame->getLines(rofId)[tracklet3], tmpVertex) < mVrtParams[iteration].pairCut) { - mTimeFrame->getTrackletClusters(rofId).back().add(tracklet3, mTimeFrame->getLines(rofId)[tracklet3]); - usedTracklets[tracklet3] = true; - tmpVertex = mTimeFrame->getTrackletClusters(rofId).back().getVertex(); + const auto& line3 = lines[iLine3]; + if (!line3.mTime.isCompatible(cluster.getTimeStamp())) { + continue; + } + const auto distance2 = Line::getDistance2FromPoint(line3, cluster.getVertex()); + if (distance2 < pairCut2) { + cluster.add(iLine3, line3); + usedTracklets[iLine3] = 1; } } break; } } } - if (mVrtParams[iteration].allowSingleContribClusters) { - auto beamLine = Line{{mTimeFrame->getBeamX(), mTimeFrame->getBeamY(), -50.f}, {mTimeFrame->getBeamX(), mTimeFrame->getBeamY(), 50.f}}; // use beam position as contributor - for (size_t iLine{0}; iLine < numTracklets; ++iLine) { - if (!usedTracklets[iLine]) { - auto dca = Line::getDCA(mTimeFrame->getLines(rofId)[iLine], beamLine); - if (dca < mVrtParams[iteration].pairCut) { - mTimeFrame->getTrackletClusters(rofId).emplace_back(iLine, mTimeFrame->getLines(rofId)[iLine], -1, beamLine); // beamline must be passed as second line argument - } - } - } - } // Cluster merging - std::sort(mTimeFrame->getTrackletClusters(rofId).begin(), mTimeFrame->getTrackletClusters(rofId).end(), + std::sort(clusters.begin(), clusters.end(), [](ClusterLines& cluster1, ClusterLines& cluster2) { return cluster1.getSize() > cluster2.getSize(); }); - noClustersVec[rofId] = static_cast(mTimeFrame->getTrackletClusters(rofId).size()); - for (int iCluster1{0}; iCluster1 < noClustersVec[rofId]; ++iCluster1) { - std::array vertex1{mTimeFrame->getTrackletClusters(rofId)[iCluster1].getVertex()}; + int nClusters = static_cast(clusters.size()); + for (int iCluster1{0}; iCluster1 < nClusters; ++iCluster1) { + std::array vertex1{clusters[iCluster1].getVertex()}; std::array vertex2{}; - for (int iCluster2{iCluster1 + 1}; iCluster2 < noClustersVec[rofId]; ++iCluster2) { - vertex2 = mTimeFrame->getTrackletClusters(rofId)[iCluster2].getVertex(); - if (o2::gpu::GPUCommonMath::Abs(vertex1[2] - vertex2[2]) < mVrtParams[iteration].clusterCut) { - float distance{(vertex1[0] - vertex2[0]) * (vertex1[0] - vertex2[0]) + - (vertex1[1] - vertex2[1]) * (vertex1[1] - vertex2[1]) + - (vertex1[2] - vertex2[2]) * (vertex1[2] - vertex2[2])}; - if (distance < mVrtParams[iteration].pairCut * mVrtParams[iteration].pairCut) { - for (auto label : mTimeFrame->getTrackletClusters(rofId)[iCluster2].getLabels()) { - mTimeFrame->getTrackletClusters(rofId)[iCluster1].add(label, mTimeFrame->getLines(rofId)[label]); - vertex1 = mTimeFrame->getTrackletClusters(rofId)[iCluster1].getVertex(); + for (int iCluster2{iCluster1 + 1}; iCluster2 < nClusters; ++iCluster2) { + if (clusters[iCluster1].getTimeStamp().isCompatible(clusters[iCluster2].getTimeStamp())) { + vertex2 = clusters[iCluster2].getVertex(); + if (o2::gpu::GPUCommonMath::Abs(vertex1[2] - vertex2[2]) < mVrtParams[iteration].clusterCut) { + float distance{((vertex1[0] - vertex2[0]) * (vertex1[0] - vertex2[0])) + + ((vertex1[1] - vertex2[1]) * (vertex1[1] - vertex2[1])) + + ((vertex1[2] - vertex2[2]) * (vertex1[2] - vertex2[2]))}; + if (distance < mVrtParams[iteration].pairCut * mVrtParams[iteration].pairCut) { + for (auto label : clusters[iCluster2].getLabels()) { + clusters[iCluster1].add(label, lines[label]); + vertex1 = clusters[iCluster1].getVertex(); + } + clusters.erase(clusters.begin() + iCluster2); + --iCluster2; + --nClusters; } - mTimeFrame->getTrackletClusters(rofId).erase(mTimeFrame->getTrackletClusters(rofId).begin() + iCluster2); - --iCluster2; - --noClustersVec[rofId]; } } } } - } - for (int rofId{0}; rofId < mTimeFrame->getNrof(); ++rofId) { - std::sort(mTimeFrame->getTrackletClusters(rofId).begin(), mTimeFrame->getTrackletClusters(rofId).end(), - [](const ClusterLines& cluster1, const ClusterLines& cluster2) { return cluster1.getSize() > cluster2.getSize(); }); // ensure clusters are ordered by contributors, so that we can cat after the first. + + // Vertex filtering + std::sort(clusters.begin(), clusters.end(), + [](const ClusterLines& cluster1, const ClusterLines& cluster2) { return cluster1.getSize() > cluster2.getSize(); }); bool atLeastOneFound{false}; - for (int iCluster{0}; iCluster < noClustersVec[rofId]; ++iCluster) { + for (int iCluster{0}; iCluster < nClusters; ++iCluster) { bool lowMultCandidate{false}; - double beamDistance2{(mTimeFrame->getBeamX() - mTimeFrame->getTrackletClusters(rofId)[iCluster].getVertex()[0]) * (mTimeFrame->getBeamX() - mTimeFrame->getTrackletClusters(rofId)[iCluster].getVertex()[0]) + - (mTimeFrame->getBeamY() - mTimeFrame->getTrackletClusters(rofId)[iCluster].getVertex()[1]) * (mTimeFrame->getBeamY() - mTimeFrame->getTrackletClusters(rofId)[iCluster].getVertex()[1])}; - if (atLeastOneFound && (lowMultCandidate = mTimeFrame->getTrackletClusters(rofId)[iCluster].getSize() < mVrtParams[iteration].clusterContributorsCut)) { // We might have pile up with nContr > cut. + double beamDistance2{(mTimeFrame->getBeamX() - clusters[iCluster].getVertex()[0]) * (mTimeFrame->getBeamX() - clusters[iCluster].getVertex()[0]) + + (mTimeFrame->getBeamY() - clusters[iCluster].getVertex()[1]) * (mTimeFrame->getBeamY() - clusters[iCluster].getVertex()[1])}; + if (atLeastOneFound && (lowMultCandidate = clusters[iCluster].getSize() < mVrtParams[iteration].clusterContributorsCut)) { lowMultCandidate &= (beamDistance2 < mVrtParams[iteration].lowMultBeamDistCut * mVrtParams[iteration].lowMultBeamDistCut); - if (!lowMultCandidate) { // Not the first cluster and not a low multiplicity candidate, we can remove it - mTimeFrame->getTrackletClusters(rofId).erase(mTimeFrame->getTrackletClusters(rofId).begin() + iCluster); - noClustersVec[rofId]--; + if (!lowMultCandidate) { + clusters.erase(clusters.begin() + iCluster); + nClusters--; continue; } } - if (beamDistance2 < nsigmaCut && o2::gpu::GPUCommonMath::Abs(mTimeFrame->getTrackletClusters(rofId)[iCluster].getVertex()[2]) < mVrtParams[iteration].maxZPositionAllowed) { + if (beamDistance2 < nsigmaCut && o2::gpu::GPUCommonMath::Abs(clusters[iCluster].getVertex()[2]) < mVrtParams[iteration].maxZPositionAllowed) { atLeastOneFound = true; - auto& vertex = vertices.emplace_back(o2::math_utils::Point3D(mTimeFrame->getTrackletClusters(rofId)[iCluster].getVertex()[0], - mTimeFrame->getTrackletClusters(rofId)[iCluster].getVertex()[1], - mTimeFrame->getTrackletClusters(rofId)[iCluster].getVertex()[2]), - mTimeFrame->getTrackletClusters(rofId)[iCluster].getRMS2(), // Symm matrix. Diagonal: RMS2 components, - // off-diagonal: square mean of projections on planes. - mTimeFrame->getTrackletClusters(rofId)[iCluster].getSize(), // Contributors - mTimeFrame->getTrackletClusters(rofId)[iCluster].getAvgDistance2()); // In place of chi2 + Vertex vertex{clusters[iCluster].getVertex().data(), + clusters[iCluster].getRMS2(), + (ushort)clusters[iCluster].getSize(), + clusters[iCluster].getAvgDistance2()}; if (iteration) { vertex.setFlags(Vertex::UPCMode); } - vertex.setTimeStamp(mTimeFrame->getTrackletClusters(rofId)[iCluster].getROF()); - if (mTimeFrame->hasMCinformation()) { + vertex.setTimeStamp(clusters[iCluster].getTimeStamp()); + rofVertices[rofId].push_back(vertex); + if (hasMC) { bounded_vector labels(mMemoryPool.get()); - for (auto& index : mTimeFrame->getTrackletClusters(rofId)[iCluster].getLabels()) { - labels.push_back(mTimeFrame->getLinesLabel(rofId)[index]); // then we can use nContributors from vertices to get the labels - } - polls.push_back(computeMain(labels)); - if (mVrtParams[iteration].outputContLabels) { - contLabels.insert(contLabels.end(), labels.begin(), labels.end()); + for (auto& index : clusters[iCluster].getLabels()) { + labels.push_back(mTimeFrame->getLinesLabel(rofId)[index]); } + rofLabels[rofId].push_back(computeMain(labels)); } } } - if (!iteration) { - mTimeFrame->addPrimaryVertices(vertices, iteration); - if (mTimeFrame->hasMCinformation()) { - mTimeFrame->addPrimaryVerticesLabels(polls); - if (mVrtParams[iteration].outputContLabels) { - mTimeFrame->addPrimaryVerticesContributorLabels(contLabels); - } - } - } else { - mTimeFrame->addPrimaryVerticesInROF(vertices, rofId, iteration); - if (mTimeFrame->hasMCinformation()) { - mTimeFrame->addPrimaryVerticesLabelsInROF(polls, rofId); - if (mVrtParams[iteration].outputContLabels) { - mTimeFrame->addPrimaryVerticesContributorLabelsInROF(contLabels, rofId); - } - } + }; + + if (mTaskArena->max_concurrency() <= 1) { + for (int rofId{0}; rofId < nRofs; ++rofId) { + processROF(rofId); } - if (vertices.empty() && !(iteration && (int)mTimeFrame->getPrimaryVertices(rofId).size() > mVrtParams[iteration].vertPerRofThreshold)) { - mTimeFrame->getNoVertexROF()++; + } else { + mTaskArena->execute([&] { + tbb::parallel_for(0, nRofs, [&](const int rofId) { + processROF(rofId); + }); + }); + } + // add vertices, these anyways get sorted afterward + for (int rofId{0}; rofId < nRofs; ++rofId) { + for (auto& vertex : rofVertices[rofId]) { + mTimeFrame->addPrimaryVertex(vertex); + } + if (hasMC) { + for (auto& label : rofLabels[rofId]) { + mTimeFrame->addPrimaryVertexLabel(label); + } } - vertices.clear(); - polls.clear(); } - -#ifdef VTX_DEBUG - debugComputeVertices(iteration); -#endif } -template -void VertexerTraits::addTruthSeedingVertices() +template +void VertexerTraits::addTruthSeedingVertices() { LOGP(info, "Using truth seeds as vertices; will skip computations"); - mTimeFrame->resetRofPV(); const auto dc = o2::steer::DigitizationContext::loadFromFile("collisioncontext.root"); const auto irs = dc->getEventRecords(); - int64_t roFrameBiasInBC = o2::itsmft::DPLAlpideParam::Instance().roFrameBiasInBC; - int64_t roFrameLengthInBC = o2::itsmft::DPLAlpideParam::Instance().roFrameLengthInBC; + int64_t roFrameBiasInBC = o2::itsmft::DPLAlpideParam::Instance().getROFBiasInBC(1); + int64_t roFrameLengthInBC = o2::itsmft::DPLAlpideParam::Instance().getROFLengthInBC(1); o2::steer::MCKinematicsReader mcReader(dc); - struct VertInfo { - bounded_vector vertices; - bounded_vector srcs; - bounded_vector events; - }; - std::map vertices; const int iSrc = 0; // take only events from collision generator auto eveId2colId = dc->getCollisionIndicesForSource(iSrc); for (int iEve{0}; iEve < mcReader.getNEvents(iSrc); ++iEve) { const auto& ir = irs[eveId2colId[iEve]]; if (!ir.isDummy()) { // do we need this, is this for diffractive events? const auto& eve = mcReader.getMCEventHeader(iSrc, iEve); - int rofId = ((ir - raw::HBFUtils::Instance().getFirstSampledTFIR()).toLong() - roFrameBiasInBC) / roFrameLengthInBC; - if (!vertices.contains(rofId)) { - vertices[rofId] = { - .vertices = bounded_vector(mMemoryPool.get()), - .srcs = bounded_vector(mMemoryPool.get()), - .events = bounded_vector(mMemoryPool.get()), - }; + auto bc = (ir - raw::HBFUtils::Instance().getFirstSampledTFIR()).toLong() - roFrameBiasInBC; + if (bc < 0) { // event happened before TF + continue; } Vertex vert; - vert.setTimeStamp(rofId); + vert.getTimeStamp().setTimeStamp(bc); + vert.getTimeStamp().setTimeStampError(roFrameLengthInBC / 2); // set minimum to 1 sometimes for diffractive events there is nothing acceptance vert.setNContributors(std::max(1L, std::ranges::count_if(mcReader.getTracks(iSrc, iEve), [](const auto& trk) { - return trk.isPrimary() && trk.GetPt() > 0.05 && std::abs(trk.GetEta()) < 1.1; + if (!trk.isPrimary() || trk.GetPt() < 0.05 || std::abs(trk.GetEta()) > 1.1) { + return false; + } + return o2::O2DatabasePDG::Instance()->GetParticle(trk.GetPdgCode())->Charge() != 0; }))); vert.setXYZ((float)eve.GetX(), (float)eve.GetY(), (float)eve.GetZ()); vert.setChi2(1); // not used as constraint - constexpr float cov = 50e-9; - vert.setCov(cov, cov, cov, cov, cov, cov); - vertices[rofId].vertices.push_back(vert); - vertices[rofId].srcs.push_back(iSrc); - vertices[rofId].events.push_back(iEve); + constexpr float cov = 25e-4; + vert.setSigmaX(cov); + vert.setSigmaY(cov); + vert.setSigmaZ(cov); + mTimeFrame->addPrimaryVertex(vert); + o2::MCCompLabel mcLbl(o2::MCCompLabel::maxTrackID(), iEve, iSrc, false); + VertexLabel lbl(mcLbl, 1.0); + mTimeFrame->addPrimaryVertexLabel(lbl); } mcReader.releaseTracksForSourceAndEvent(iSrc, iEve); } - size_t nVerts{0}; - for (int iROF{0}; iROF < mTimeFrame->getNrof(); ++iROF) { - bounded_vector verts(mMemoryPool.get()); - bounded_vector> polls(mMemoryPool.get()); - if (vertices.contains(iROF)) { - const auto& vertInfo = vertices[iROF]; - verts = vertInfo.vertices; - nVerts += verts.size(); - for (size_t i{0}; i < verts.size(); ++i) { - o2::MCCompLabel lbl(o2::MCCompLabel::maxTrackID(), vertInfo.events[i], vertInfo.srcs[i], false); - polls.emplace_back(lbl, 1.f); - } - } else { - mTimeFrame->getNoVertexROF()++; - } - mTimeFrame->addPrimaryVertices(verts, 0); - mTimeFrame->addPrimaryVerticesLabels(polls); - } - LOGP(info, "Found {}/{} ROFs with {} vertices -> ={:.2f}", vertices.size(), mTimeFrame->getNrof(), nVerts, (float)nVerts / (float)vertices.size()); + LOGP(info, "Imposed {} pv collisions from mc-truth", mTimeFrame->getPrimaryVertices().size()); } -template -void VertexerTraits::setNThreads(int n, std::shared_ptr& arena) +template +void VertexerTraits::setNThreads(int n, std::shared_ptr& arena) { -#if defined(VTX_DEBUG) - LOGP(info, "Vertexer with debug output forcing single thread"); - mTaskArena = std::make_shared(1); -#else if (arena == nullptr) { mTaskArena = std::make_shared(std::abs(n)); LOGP(info, "Setting seeding vertexer with {} threads.", n); } else { mTaskArena = arena; - LOGP(info, "Attaching vertexer to calling thread's arena"); - } -#endif -} - -template -void VertexerTraits::debugComputeTracklets(int iteration) -{ - auto stream = new utils::TreeStreamRedirector("artefacts_tf.root", "recreate"); - LOGP(info, "writing debug output for computeTracklets"); - for (int rofId{0}; rofId < mTimeFrame->getNrof(); ++rofId) { - const auto& strk0 = mTimeFrame->getFoundTracklets(rofId, 0); - std::vector trk0(strk0.begin(), strk0.end()); - const auto& strk1 = mTimeFrame->getFoundTracklets(rofId, 1); - std::vector trk1(strk1.begin(), strk1.end()); - (*stream) << "tracklets" - << "Tracklets0=" << trk0 - << "Tracklets1=" << trk1 - << "iteration=" << iteration - << "\n"; - } - stream->Close(); - delete stream; -} - -template -void VertexerTraits::debugComputeTrackletMatching(int iteration) -{ - auto stream = new utils::TreeStreamRedirector("artefacts_tf.root", "update"); - LOGP(info, "writing debug output for computeTrackletMatching"); - for (int rofId{0}; rofId < mTimeFrame->getNrof(); ++rofId) { - (*stream) << "lines" - << "Lines=" << toSTDVector(mTimeFrame->getLines(rofId)) - << "NTrackletCluster01=" << mTimeFrame->getNTrackletsCluster(rofId, 0) - << "NTrackletCluster12=" << mTimeFrame->getNTrackletsCluster(rofId, 1) - << "iteration=" << iteration - << "\n"; - } - - if (mTimeFrame->hasMCinformation()) { - LOGP(info, "\tdumping also MC information"); - const auto dc = o2::steer::DigitizationContext::loadFromFile("collisioncontext.root"); - const auto irs = dc->getEventRecords(); - int64_t roFrameBiasInBC = o2::itsmft::DPLAlpideParam::Instance().roFrameBiasInBC; - int64_t roFrameLengthInBC = o2::itsmft::DPLAlpideParam::Instance().roFrameLengthInBC; - o2::steer::MCKinematicsReader mcReader(dc); - - std::map eve2BcInROF, bcInRofNEve; - for (int iSrc{0}; iSrc < mcReader.getNSources(); ++iSrc) { - auto eveId2colId = dc->getCollisionIndicesForSource(iSrc); - for (int iEve{0}; iEve < mcReader.getNEvents(iSrc); ++iEve) { - const auto& ir = irs[eveId2colId[iEve]]; - if (!ir.isDummy()) { // do we need this, is this for diffractive events? - const auto& eve = mcReader.getMCEventHeader(iSrc, iEve); - const int bcInROF = ((ir - raw::HBFUtils::Instance().getFirstSampledTFIR()).toLong() - roFrameBiasInBC) % roFrameLengthInBC; - eve2BcInROF[iEve] = bcInROF; - ++bcInRofNEve[bcInROF]; - } - } - } - - std::unordered_map bcROFNTracklets01, bcROFNTracklets12; - std::vector> tracklet01BC, tracklet12BC; - for (int rofId{0}; rofId < mTimeFrame->getNrof(); ++rofId) { - { // 0-1 - const auto& tracklet01 = mTimeFrame->getFoundTracklets(rofId, 0); - const auto& lbls01 = mTimeFrame->getLabelsFoundTracklets(rofId, 0); - auto& trkls01 = tracklet01BC.emplace_back(); - for (int iTrklt{0}; iTrklt < (int)tracklet01.size(); ++iTrklt) { - const auto& tracklet = tracklet01[iTrklt]; - const auto& lbl = lbls01[iTrklt]; - if (lbl.isCorrect()) { - ++bcROFNTracklets01[eve2BcInROF[lbl.getEventID()]]; - trkls01.push_back(eve2BcInROF[lbl.getEventID()]); - } else { - trkls01.push_back(-1); - } - } - } - { // 1-2 computed on the fly! - const auto& tracklet12 = mTimeFrame->getFoundTracklets(rofId, 1); - auto& trkls12 = tracklet12BC.emplace_back(); - for (int iTrklt{0}; iTrklt < (int)tracklet12.size(); ++iTrklt) { - const auto& tracklet = tracklet12[iTrklt]; - o2::MCCompLabel label; - - int sortedId1{mTimeFrame->getSortedIndex(tracklet.rof[0], 1, tracklet.firstClusterIndex)}; - int sortedId2{mTimeFrame->getSortedIndex(tracklet.rof[1], 2, tracklet.secondClusterIndex)}; - for (const auto& lab1 : mTimeFrame->getClusterLabels(1, mTimeFrame->getClusters()[1][sortedId1].clusterId)) { - for (const auto& lab2 : mTimeFrame->getClusterLabels(2, mTimeFrame->getClusters()[2][sortedId2].clusterId)) { - if (lab1 == lab2 && lab1.isValid()) { - label = lab1; - break; - } - } - if (label.isValid()) { - break; - } - } - - if (label.isCorrect()) { - ++bcROFNTracklets12[eve2BcInROF[label.getEventID()]]; - trkls12.push_back(eve2BcInROF[label.getEventID()]); - } else { - trkls12.push_back(-1); - } - } - } - } - LOGP(info, "\tdumping ntracklets/RofBC ({})", bcInRofNEve.size()); - for (const auto& [bcInRof, neve] : bcInRofNEve) { - (*stream) << "ntracklets" - << "bcInROF=" << bcInRof - << "ntrkl01=" << bcROFNTracklets01[bcInRof] - << "ntrkl12=" << bcROFNTracklets12[bcInRof] - << "neve=" << neve - << "iteration=" << iteration - << "\n"; - } - - std::unordered_map bcROFNLines; - for (int rofId{0}; rofId < mTimeFrame->getNrof(); ++rofId) { - const auto& lines = mTimeFrame->getLines(rofId); - const auto& lbls = mTimeFrame->getLinesLabel(rofId); - for (int iLine{0}; iLine < (int)lines.size(); ++iLine) { - const auto& line = lines[iLine]; - const auto& lbl = lbls[iLine]; - if (lbl.isCorrect()) { - ++bcROFNLines[eve2BcInROF[lbl.getEventID()]]; - } - } - } - - LOGP(info, "\tdumping nlines/RofBC"); - for (const auto& [bcInRof, neve] : bcInRofNEve) { - (*stream) << "nlines" - << "bcInROF=" << bcInRof - << "nline=" << bcROFNLines[bcInRof] - << "neve=" << neve - << "iteration=" << iteration - << "\n"; - } - } - stream->Close(); - delete stream; -} - -template -void VertexerTraits::debugComputeVertices(int iteration) -{ - auto stream = new utils::TreeStreamRedirector("artefacts_tf.root", "update"); - LOGP(info, "writing debug output for computeVertices"); - for (auto rofId{0}; rofId < mTimeFrame->getNrof(); ++rofId) { - (*stream) << "clusterlines" - << "clines_post=" << toSTDVector(mTimeFrame->getTrackletClusters(rofId)) - << "iteration=" << iteration - << "\n"; - } - - if (mTimeFrame->hasMCinformation()) { - LOGP(info, "\tdumping also MC information"); - const auto dc = o2::steer::DigitizationContext::loadFromFile("collisioncontext.root"); - const auto irs = dc->getEventRecords(); - int64_t roFrameBiasInBC = o2::itsmft::DPLAlpideParam::Instance().roFrameBiasInBC; - int64_t roFrameLengthInBC = o2::itsmft::DPLAlpideParam::Instance().roFrameLengthInBC; - o2::steer::MCKinematicsReader mcReader(dc); - - std::map eve2BcInROF, bcInRofNEve; - for (int iSrc{0}; iSrc < mcReader.getNSources(); ++iSrc) { - auto eveId2colId = dc->getCollisionIndicesForSource(iSrc); - for (int iEve{0}; iEve < mcReader.getNEvents(iSrc); ++iEve) { - const auto& ir = irs[eveId2colId[iEve]]; - if (!ir.isDummy()) { // do we need this, is this for diffractive events? - const auto& eve = mcReader.getMCEventHeader(iSrc, iEve); - const int bcInROF = ((ir - raw::HBFUtils::Instance().getFirstSampledTFIR()).toLong() - roFrameBiasInBC) % roFrameLengthInBC; - eve2BcInROF[iEve] = bcInROF; - ++bcInRofNEve[bcInROF]; - } - } - } - - std::unordered_map bcROFNVtx; - std::unordered_map bcROFNPur; - std::unordered_map uniqueVertices; - for (int rofId{0}; rofId < mTimeFrame->getNrof(); ++rofId) { - const auto& pvs = mTimeFrame->getPrimaryVertices(rofId); - const auto& lblspv = mTimeFrame->getPrimaryVerticesMCRecInfo(rofId); - for (int i{0}; i < (int)pvs.size(); ++i) { - const auto& pv = pvs[i]; - const auto& [lbl, pur] = lblspv[i]; - if (lbl.isCorrect()) { - ++uniqueVertices[lbl]; - ++bcROFNVtx[eve2BcInROF[lbl.getEventID()]]; - bcROFNPur[eve2BcInROF[lbl.getEventID()]] += pur; - } - } - } - - std::unordered_map bcROFNUVtx, bcROFNCVtx; - for (const auto& [k, _] : eve2BcInROF) { - bcROFNUVtx[k] = bcROFNCVtx[k] = 0; - } - - for (const auto& [lbl, c] : uniqueVertices) { - if (c <= 1) { - ++bcROFNUVtx[eve2BcInROF[lbl.getEventID()]]; - } else { - ++bcROFNCVtx[eve2BcInROF[lbl.getEventID()]]; - } - } - - LOGP(info, "\tdumping nvtx/RofBC"); - for (const auto& [bcInRof, neve] : bcInRofNEve) { - (*stream) << "nvtx" - << "bcInROF=" << bcInRof - << "nvtx=" << bcROFNVtx[bcInRof] // all vertices - << "nuvtx=" << bcROFNUVtx[bcInRof] // unique vertices - << "ncvtx=" << bcROFNCVtx[bcInRof] // cloned vertices - << "npur=" << bcROFNPur[bcInRof] - << "neve=" << neve - << "iteration=" << iteration - << "\n"; - } - - // check dist of clones - std::unordered_map> cVtx; - for (int rofId{0}; rofId < mTimeFrame->getNrof(); ++rofId) { - const auto& pvs = mTimeFrame->getPrimaryVertices(rofId); - const auto& lblspv = mTimeFrame->getPrimaryVerticesMCRecInfo(rofId); - for (int i{0}; i < (int)pvs.size(); ++i) { - const auto& pv = pvs[i]; - const auto& [lbl, pur] = lblspv[i]; - if (lbl.isCorrect() && uniqueVertices.contains(lbl) && uniqueVertices[lbl] > 1) { - if (!cVtx.contains(lbl)) { - cVtx[lbl] = std::vector(); - } - cVtx[lbl].push_back(pv); - } - } - } - - for (auto& [_, vertices] : cVtx) { - std::sort(vertices.begin(), vertices.end(), [](const Vertex& a, const Vertex& b) { return a.getNContributors() > b.getNContributors(); }); - for (int i{0}; i < (int)vertices.size(); ++i) { - const auto vtx = vertices[i]; - (*stream) << "cvtx" - << "vertex=" << vtx - << "i=" << i - << "dx=" << vertices[0].getX() - vtx.getX() - << "dy=" << vertices[0].getY() - vtx.getY() - << "dz=" << vertices[0].getZ() - vtx.getZ() - << "drof=" << vertices[0].getTimeStamp().getTimeStamp() - vtx.getTimeStamp().getTimeStamp() - << "dnc=" << vertices[0].getNContributors() - vtx.getNContributors() - << "iteration=" << iteration - << "\n"; - } - } } - stream->Close(); - delete stream; } template class VertexerTraits<7>; diff --git a/Detectors/ITSMFT/ITS/tracking/test/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/test/CMakeLists.txt index 818ad1d667371..063583b4cfa1b 100644 --- a/Detectors/ITSMFT/ITS/tracking/test/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/test/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# 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. # @@ -14,3 +14,9 @@ o2_add_test(boundedmemoryresource COMPONENT_NAME its-tracking LABELS "its;tracking" PUBLIC_LINK_LIBRARIES O2::ITStracking) + +o2_add_test(roflookuptables + SOURCES testROFLookupTables.cxx + COMPONENT_NAME its-tracking + LABELS "its;tracking" + PUBLIC_LINK_LIBRARIES O2::ITStracking) diff --git a/Detectors/ITSMFT/ITS/tracking/test/testROFLookupTables.cxx b/Detectors/ITSMFT/ITS/tracking/test/testROFLookupTables.cxx new file mode 100644 index 0000000000000..8594e59149444 --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/test/testROFLookupTables.cxx @@ -0,0 +1,744 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#define BOOST_TEST_MODULE ITS ROFLookupTables +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +#include +#include "ITStracking/ROFLookupTables.h" + +/// -------- Tests -------- +// LayerTiming +BOOST_AUTO_TEST_CASE(layertiming_basic) +{ + o2::its::ROFOverlapTable<1> table; + table.defineLayer(0, 10, 594, 100, 0, 50); + const auto& layer = table.getLayer(0); + + // test ROF time calculations + auto start0 = layer.getROFStartInBC(0); + BOOST_CHECK_EQUAL(start0, 100); // delay only + + auto end0 = layer.getROFEndInBC(0); + BOOST_CHECK_EQUAL(end0, 100 + 594); + + // test second ROF + auto start1 = layer.getROFStartInBC(1); + BOOST_CHECK_EQUAL(start1, 100 + 594); +} + +BOOST_AUTO_TEST_CASE(layertiming_base) +{ + o2::its::ROFOverlapTable<3> table; + table.defineLayer(0, 10, 500, 0, 0, 0); + table.defineLayer(1, 12, 600, 50, 0, 0); + table.defineLayer(2, 8, 400, 100, 0, 0); + const auto& layer1 = table.getLayer(1); + BOOST_CHECK_EQUAL(layer1.mNROFsTF, 12); + BOOST_CHECK_EQUAL(layer1.mROFLength, 600); +} + +BOOST_AUTO_TEST_CASE(rofmask_construct_from_timing) +{ + o2::its::ROFOverlapTable<2> timing; + timing.defineLayer(0, 3, 100, 0, 0, 0); + timing.defineLayer(1, 4, 50, 25, 0, 0); + + o2::its::ROFMaskTable<2> mask{timing}; + const auto view = mask.getView(); + + BOOST_REQUIRE(view.mFlatMask != nullptr); + BOOST_REQUIRE(view.mLayerROFOffsets != nullptr); + BOOST_CHECK_EQUAL(view.mLayerROFOffsets[0], 0); + BOOST_CHECK_EQUAL(view.mLayerROFOffsets[1], 3); + BOOST_CHECK_EQUAL(view.mLayerROFOffsets[2], 7); + + // by default all rofs are disabled + for (int rof{0}; rof < 3; ++rof) { + BOOST_CHECK(!view.isROFEnabled(0, rof)); + } + for (int rof{0}; rof < 4; ++rof) { + BOOST_CHECK(!view.isROFEnabled(1, rof)); + } + + mask.selectROF({110, 20}); + + BOOST_CHECK(!view.isROFEnabled(0, 0)); + BOOST_CHECK(view.isROFEnabled(0, 1)); + BOOST_CHECK(!view.isROFEnabled(0, 2)); + + BOOST_CHECK(!view.isROFEnabled(1, 0)); + BOOST_CHECK(view.isROFEnabled(1, 1)); + BOOST_CHECK(view.isROFEnabled(1, 2)); + BOOST_CHECK(!view.isROFEnabled(1, 3)); +} + +// ROFOverlapTable +BOOST_AUTO_TEST_CASE(rofoverlap_basic) +{ + // define 2 layers with the same definitions (no staggering) + o2::its::ROFOverlapTable<2> table; + table.defineLayer(0, 12, 594, 0, 0, 0); + table.defineLayer(1, 12, 594, 0, 0, 0); + table.init(); + const auto view = table.getView(); + // each rof in layer 0 should be compatible with its layer 1 equivalent + for (int rof{0}; rof < 12; ++rof) { + BOOST_CHECK(view.doROFsOverlap(0, rof, 1, rof)); + BOOST_CHECK(view.doROFsOverlap(1, rof, 0, rof)); + BOOST_CHECK(view.getOverlap(0, 1, rof).getEntries() == 1); + } +} + +BOOST_AUTO_TEST_CASE(rofoverlap_staggered) +{ + // test staggered layers with ROF delay + o2::its::ROFOverlapTable<2> table; + table.defineLayer(0, 10, 500, 0, 0, 0); + table.defineLayer(1, 10, 500, 250, 0, 0); // 250 BC delay + table.init(); + const auto view = table.getView(); + + // verify overlap range + { // from 0 to 1 + const auto& range = view.getOverlap(0, 1, 0); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 1 to 0 + const auto& range = view.getOverlap(1, 0, 0); + BOOST_CHECK_EQUAL(range.getEntries(), 2); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } +} + +BOOST_AUTO_TEST_CASE(rofoverlap_staggered_pp) +{ + const uint32_t rofLen{198}, rofBins{6}; + const uint32_t rofDelay{rofLen / rofBins}; + o2::its::ROFOverlapTable<3> table; + for (uint32_t lay{0}; lay < 3; ++lay) { + table.defineLayer(lay, 6, rofLen, lay * rofDelay, 0, 0); + } + table.init(); + const auto view = table.getView(); + view.printAll(); +} + +BOOST_AUTO_TEST_CASE(rofoverlap_staggered_alllayers) +{ + // test staggered layers with ROF delay + o2::its::ROFOverlapTable<3> table; + table.defineLayer(0, 2, 3, 0, 0, 0); + table.defineLayer(1, 3, 2, 0, 0, 0); + table.defineLayer(2, 6, 1, 0, 0, 0); + table.init(); + const auto view = table.getView(); + // verify overlap range + { // from 0 to 1 rof=0 + const auto& range = view.getOverlap(0, 1, 0); + BOOST_CHECK_EQUAL(range.getEntries(), 2); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 0 to 2 rof=0 + const auto& range = view.getOverlap(0, 2, 0); + BOOST_CHECK_EQUAL(range.getEntries(), 3); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 0 to 1 rof=1 + const auto& range = view.getOverlap(0, 1, 1); + BOOST_CHECK_EQUAL(range.getEntries(), 2); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 1); + } + { // from 0 to 2 rof=1 + const auto& range = view.getOverlap(0, 2, 1); + BOOST_CHECK_EQUAL(range.getEntries(), 3); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 3); + } + { // from 1 to 2 rof=0 + const auto& range = view.getOverlap(1, 2, 0); + BOOST_CHECK_EQUAL(range.getEntries(), 2); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 1 to 0 rof=0 + const auto& range = view.getOverlap(1, 0, 0); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 1 to 2 rof=1 + const auto& range = view.getOverlap(1, 2, 1); + BOOST_CHECK_EQUAL(range.getEntries(), 2); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 2); + } + { // from 1 to 0 rof=1 + const auto& range = view.getOverlap(1, 0, 1); + BOOST_CHECK_EQUAL(range.getEntries(), 2); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 1 to 2 rof=2 + const auto& range = view.getOverlap(1, 2, 2); + BOOST_CHECK_EQUAL(range.getEntries(), 2); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 4); + } + { // from 1 to 0 rof=2 + const auto& range = view.getOverlap(1, 0, 2); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 1); + } + { // from 2 to 1 rof=0 + const auto& range = view.getOverlap(2, 1, 0); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 2 to 1 rof=1 + const auto& range = view.getOverlap(2, 1, 1); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 2 to 1 rof=2 + const auto& range = view.getOverlap(2, 1, 2); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 1); + } + { // from 2 to 1 rof=3 + const auto& range = view.getOverlap(2, 1, 3); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 1); + } + { // from 2 to 1 rof=4 + const auto& range = view.getOverlap(2, 1, 4); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 2); + } + { // from 2 to 1 rof=5 + const auto& range = view.getOverlap(2, 1, 5); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 2); + } + { // from 2 to 0 rof=0 + const auto& range = view.getOverlap(2, 0, 0); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 2 to 0 rof=1 + const auto& range = view.getOverlap(2, 0, 1); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 2 to 0 rof=2 + const auto& range = view.getOverlap(2, 0, 2); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 2 to 0 rof=3 + const auto& range = view.getOverlap(2, 0, 3); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 1); + } + { // from 2 to 0 rof=4 + const auto& range = view.getOverlap(2, 0, 4); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 1); + } + { // from 2 to 0 rof=5 + const auto& range = view.getOverlap(2, 0, 5); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 1); + } +} + +BOOST_AUTO_TEST_CASE(rofoverlap_staggered_alllayers_delay_delta) +{ + // test staggered layers with ROF delay + o2::its::ROFOverlapTable<3> table; + table.defineLayer(0, 2, 3, 0, 0, 0); + table.defineLayer(1, 3, 2, 1, 0, 0); + table.defineLayer(2, 6, 1, 0, 0, 1); + table.init(); + const auto view = table.getView(); + + // verify overlap range + { // from 0 to 1 rof=0 + const auto& range = view.getOverlap(0, 1, 0); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 0 to 2 rof=0 + const auto& range = view.getOverlap(0, 2, 0); + BOOST_CHECK_EQUAL(range.getEntries(), 4); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 0 to 1 rof=1 + const auto& range = view.getOverlap(0, 1, 1); + BOOST_CHECK_EQUAL(range.getEntries(), 2); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 1); + } + { // from 0 to 2 rof=1 + const auto& range = view.getOverlap(0, 2, 1); + BOOST_CHECK_EQUAL(range.getEntries(), 4); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 2); + } + { // from 1 to 2 rof=0 + const auto& range = view.getOverlap(1, 2, 0); + BOOST_CHECK_EQUAL(range.getEntries(), 4); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 1 to 0 rof=0 + const auto& range = view.getOverlap(1, 0, 0); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 1 to 2 rof=1 + const auto& range = view.getOverlap(1, 2, 1); + BOOST_CHECK_EQUAL(range.getEntries(), 4); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 2); + } + { // from 1 to 0 rof=1 + const auto& range = view.getOverlap(1, 0, 1); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 1); + } + { // from 1 to 2 rof=2 + const auto& range = view.getOverlap(1, 2, 2); + BOOST_CHECK_EQUAL(range.getEntries(), 2); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 4); + } + { // from 1 to 0 rof=2 + const auto& range = view.getOverlap(1, 0, 2); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 1); + } + { // from 2 to 1 rof=0 + const auto& range = view.getOverlap(2, 1, 0); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 2 to 1 rof=1 + const auto& range = view.getOverlap(2, 1, 1); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 2 to 1 rof=2 + const auto& range = view.getOverlap(2, 1, 2); + BOOST_CHECK_EQUAL(range.getEntries(), 2); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 2 to 1 rof=3 + const auto& range = view.getOverlap(2, 1, 3); + BOOST_CHECK_EQUAL(range.getEntries(), 2); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 2 to 1 rof=4 + const auto& range = view.getOverlap(2, 1, 4); + BOOST_CHECK_EQUAL(range.getEntries(), 2); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 1); + } + { // from 2 to 1 rof=5 + const auto& range = view.getOverlap(2, 1, 5); + BOOST_CHECK_EQUAL(range.getEntries(), 2); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 1); + } + { // from 2 to 0 rof=0 + const auto& range = view.getOverlap(2, 0, 0); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 2 to 0 rof=1 + const auto& range = view.getOverlap(2, 0, 1); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 2 to 0 rof=2 + const auto& range = view.getOverlap(2, 0, 2); + BOOST_CHECK_EQUAL(range.getEntries(), 2); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 2 to 0 rof=3 + const auto& range = view.getOverlap(2, 0, 3); + BOOST_CHECK_EQUAL(range.getEntries(), 2); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 2 to 0 rof=4 + const auto& range = view.getOverlap(2, 0, 4); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 1); + } + { // from 2 to 0 rof=5 + const auto& range = view.getOverlap(2, 0, 5); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 1); + } +} + +BOOST_AUTO_TEST_CASE(rofoverlap_with_delta) +{ + // test with ROF delta for compatibility window + o2::its::ROFOverlapTable<2> table; + table.defineLayer(0, 8, 600, 0, 0, 100); // +/- 100 BC delta + table.defineLayer(1, 8, 600, 0, 0, 100); + table.init(); + const auto view = table.getView(); + + // with delta, ROFs should have wider compatibility + for (int rof{0}; rof < 8; ++rof) { + auto overlap = view.getOverlap(0, 1, rof); + if (rof == 0 || rof == 7) { + // edges should see only two + BOOST_CHECK_EQUAL(overlap.getEntries(), 2); + } else { + BOOST_CHECK_EQUAL(overlap.getEntries(), 3); + } + } +} + +BOOST_AUTO_TEST_CASE(rofoverlap_same_layer) +{ + // test same layer compatibility + o2::its::ROFOverlapTable<1> table; + table.defineLayer(0, 10, 500, 0, 0, 0); + table.init(); + const auto view = table.getView(); + + // same ROF in same layer should be compatible + BOOST_CHECK(view.doROFsOverlap(0, 5, 0, 5)); + // different ROFs in same layer should not be compatible + BOOST_CHECK(!view.doROFsOverlap(0, 5, 0, 6)); +} + +BOOST_AUTO_TEST_CASE(rofoverlap_timestamp_basic) +{ + o2::its::ROFOverlapTable<4> table; + table.defineLayer(0, 4, 100, 0, 0, 0); + table.defineLayer(1, 4, 100, 0, 0, 0); + table.defineLayer(2, 8, 50, 0, 0, 0); + table.defineLayer(3, 7, 50, 50, 0, 0); + table.init(); + const auto& view = table.getView(); + + const auto t01 = view.getTimeStamp(0, 3, 1, 3); + BOOST_CHECK_EQUAL(t01.getTimeStamp(), 300); + BOOST_CHECK_EQUAL(t01.getTimeStampError(), 100); + + const auto t02 = view.getTimeStamp(0, 1, 2, 3); + BOOST_CHECK_EQUAL(t02.getTimeStamp(), 150); + BOOST_CHECK_EQUAL(t02.getTimeStampError(), 50); + + const auto t03 = view.getTimeStamp(0, 0, 3, 0); + BOOST_CHECK_EQUAL(t03.getTimeStamp(), 50); + BOOST_CHECK_EQUAL(t03.getTimeStampError(), 50); + + const auto t23 = view.getTimeStamp(2, 2, 3, 1); + BOOST_CHECK_EQUAL(t23.getTimeStamp(), 100); + BOOST_CHECK_EQUAL(t23.getTimeStampError(), 50); +} + +BOOST_AUTO_TEST_CASE(rofoverlap_timestamp_complex) +{ + o2::its::ROFOverlapTable<4> table; + table.defineLayer(0, 4, 100, 0, 0, 0); + table.defineLayer(1, 4, 100, 0, 0, 10); + table.defineLayer(2, 8, 50, 0, 0, 0); + table.defineLayer(3, 7, 50, 50, 0, 10); + table.init(); + const auto& view = table.getView(); + view.printMapping(0, 1); + + const auto t010 = view.getTimeStamp(0, 3, 1, 3); + BOOST_CHECK_EQUAL(t010.getTimeStamp(), 300); + BOOST_CHECK_EQUAL(t010.getTimeStampError(), 100); + + const auto t011 = view.getTimeStamp(0, 2, 1, 3); + BOOST_CHECK_EQUAL(t011.getTimeStamp(), 290); + BOOST_CHECK_EQUAL(t011.getTimeStampError(), 10); + + const auto t02 = view.getTimeStamp(0, 1, 2, 3); + BOOST_CHECK_EQUAL(t02.getTimeStamp(), 150); + BOOST_CHECK_EQUAL(t02.getTimeStampError(), 50); + + const auto t03 = view.getTimeStamp(0, 0, 3, 0); + BOOST_CHECK_EQUAL(t03.getTimeStamp(), 40); + BOOST_CHECK_EQUAL(t03.getTimeStampError(), 60); +} + +// ROFVertexLookupTable +BOOST_AUTO_TEST_CASE(rofvertex_basic) +{ + o2::its::ROFVertexLookupTable<1> table; + table.defineLayer(0, 6, 594, 0, 0, 0); + table.init(); + std::vector vertices; + o2::its::Vertex vert0; + vert0.getTimeStamp().setTimeStamp(594); + vert0.getTimeStamp().setTimeStampError(594); + vertices.push_back(vert0); + o2::its::Vertex vert1; + vert1.getTimeStamp().setTimeStamp(2375); + vert1.getTimeStamp().setTimeStampError(594); + vertices.push_back(vert1); + table.update(vertices.data(), vertices.size()); + const auto view = table.getView(); +} + +BOOST_AUTO_TEST_CASE(rofvertex_init_with_vertices) +{ + o2::its::ROFVertexLookupTable<2> table; + table.defineLayer(0, 10, 500, 0, 0, 0); + table.defineLayer(1, 10, 500, 0, 0, 0); + + // create vertices at different timestamps + std::vector vertices; + for (int i = 0; i < 5; ++i) { + o2::its::Vertex v; + v.getTimeStamp().setTimeStamp(i * 1000); + v.getTimeStamp().setTimeStampError(500); + vertices.push_back(v); + } + + table.init(vertices.data(), vertices.size()); + const auto view = table.getView(); + + // verify vertices can be queried + const auto& vtxRange = view.getVertices(0, 0); + BOOST_CHECK_EQUAL(vtxRange.getEntries(), 1); +} + +BOOST_AUTO_TEST_CASE(rofvertex_max_vertices) +{ + o2::its::ROFVertexLookupTable<1> table; + table.defineLayer(0, 3, 1000, 0, 0, 500); + + std::vector vertices; + for (int i = 0; i < 10; ++i) { + o2::its::Vertex v; + v.getTimeStamp().setTimeStamp(500 + i * 100); + v.getTimeStamp().setTimeStampError(50); + vertices.push_back(v); + } + + table.init(vertices.data(), vertices.size()); + const auto view = table.getView(); + + int32_t maxVtx = view.getMaxVerticesPerROF(); + BOOST_CHECK(maxVtx >= 0); +} + +BOOST_AUTO_TEST_CASE(rofvertex_vertex_more) +{ + o2::its::ROFVertexLookupTable<4> table; + table.defineLayer(0, 4, 100, 0, 0, 0); + table.defineLayer(1, 4, 100, 0, 0, 10); + table.defineLayer(2, 8, 50, 0, 0, 0); + table.defineLayer(3, 7, 50, 50, 0, 10); + table.init(); + + std::vector vertices; + { // vertex 0 overlapping + auto& v = vertices.emplace_back(); + v.getTimeStamp().setTimeStamp(100); + v.getTimeStamp().setTimeStampError(10); + } + { // vertex 1 + auto& v = vertices.emplace_back(); + v.getTimeStamp().setTimeStamp(100); + v.getTimeStamp().setTimeStampError(0); + } + { // vertex 2 spanning multiple rofs + auto& v = vertices.emplace_back(); + v.getTimeStamp().setTimeStamp(100); + v.getTimeStamp().setTimeStampError(60); + } + + // sorty vertices by lower bound + std::sort(vertices.begin(), vertices.end(), [](const auto& pvA, const auto& pvB) { + const auto& a = pvA.getTimeStamp(); + const auto& b = pvB.getTimeStamp(); + const auto aLower = a.getTimeStamp() - a.getTimeStampError(); + const auto bLower = b.getTimeStamp() - b.getTimeStampError(); + if (aLower != bLower) { + return aLower < bLower; + } + return pvA.getNContributors() > pvB.getNContributors(); + }); + + table.update(vertices.data(), vertices.size()); + const auto& view = table.getView(); + + const auto& v0 = vertices[0]; // 100+60 + const auto& v1 = vertices[1]; // 100+10 + const auto& v2 = vertices[2]; // 100+0 + + // check for v0 + // layer 0 + BOOST_CHECK(!view.isVertexCompatible(0, 0, v0)); + BOOST_CHECK(view.isVertexCompatible(0, 1, v0)); + BOOST_CHECK(!view.isVertexCompatible(0, 2, v0)); + BOOST_CHECK(!view.isVertexCompatible(0, 3, v0)); + // layer 1 + BOOST_CHECK(view.isVertexCompatible(1, 0, v0)); + BOOST_CHECK(view.isVertexCompatible(1, 1, v0)); + BOOST_CHECK(!view.isVertexCompatible(1, 2, v0)); + BOOST_CHECK(!view.isVertexCompatible(1, 3, v0)); + // layer 2 + BOOST_CHECK(!view.isVertexCompatible(2, 0, v0)); + BOOST_CHECK(!view.isVertexCompatible(2, 1, v0)); + BOOST_CHECK(view.isVertexCompatible(2, 2, v0)); + BOOST_CHECK(view.isVertexCompatible(2, 3, v0)); + BOOST_CHECK(!view.isVertexCompatible(2, 4, v0)); + BOOST_CHECK(!view.isVertexCompatible(2, 5, v0)); + BOOST_CHECK(!view.isVertexCompatible(2, 6, v0)); + BOOST_CHECK(!view.isVertexCompatible(2, 7, v0)); + // layer 3 + BOOST_CHECK(view.isVertexCompatible(3, 0, v0)); + BOOST_CHECK(view.isVertexCompatible(3, 1, v0)); + BOOST_CHECK(view.isVertexCompatible(3, 2, v0)); + BOOST_CHECK(!view.isVertexCompatible(3, 3, v0)); + BOOST_CHECK(!view.isVertexCompatible(3, 4, v0)); + BOOST_CHECK(!view.isVertexCompatible(3, 5, v0)); + BOOST_CHECK(!view.isVertexCompatible(3, 6, v0)); + + // check for v1 + // layer 0 + BOOST_CHECK(!view.isVertexCompatible(0, 0, v1)); + BOOST_CHECK(view.isVertexCompatible(0, 1, v1)); + BOOST_CHECK(!view.isVertexCompatible(0, 2, v1)); + BOOST_CHECK(!view.isVertexCompatible(0, 3, v1)); + // layer 1 + BOOST_CHECK(view.isVertexCompatible(1, 0, v1)); + BOOST_CHECK(view.isVertexCompatible(1, 1, v1)); + BOOST_CHECK(!view.isVertexCompatible(1, 2, v1)); + BOOST_CHECK(!view.isVertexCompatible(1, 3, v1)); + // layer 2 + BOOST_CHECK(!view.isVertexCompatible(2, 0, v1)); + BOOST_CHECK(!view.isVertexCompatible(2, 1, v1)); + BOOST_CHECK(view.isVertexCompatible(2, 2, v1)); + BOOST_CHECK(!view.isVertexCompatible(2, 3, v1)); + BOOST_CHECK(!view.isVertexCompatible(2, 4, v1)); + BOOST_CHECK(!view.isVertexCompatible(2, 5, v1)); + BOOST_CHECK(!view.isVertexCompatible(2, 6, v1)); + BOOST_CHECK(!view.isVertexCompatible(2, 7, v1)); + // layer 3 + BOOST_CHECK(view.isVertexCompatible(3, 0, v1)); + BOOST_CHECK(view.isVertexCompatible(3, 1, v1)); + BOOST_CHECK(!view.isVertexCompatible(3, 2, v1)); + BOOST_CHECK(!view.isVertexCompatible(3, 3, v1)); + BOOST_CHECK(!view.isVertexCompatible(3, 4, v1)); + BOOST_CHECK(!view.isVertexCompatible(3, 5, v1)); + BOOST_CHECK(!view.isVertexCompatible(3, 6, v1)); + + // check for v2 + // layer 0 + BOOST_CHECK(!view.isVertexCompatible(0, 0, v2)); + BOOST_CHECK(view.isVertexCompatible(0, 1, v2)); + BOOST_CHECK(!view.isVertexCompatible(0, 2, v2)); + BOOST_CHECK(!view.isVertexCompatible(0, 3, v2)); + // layer 1 + BOOST_CHECK(view.isVertexCompatible(1, 0, v2)); + BOOST_CHECK(view.isVertexCompatible(1, 1, v2)); + BOOST_CHECK(!view.isVertexCompatible(1, 2, v2)); + BOOST_CHECK(!view.isVertexCompatible(1, 3, v2)); + // layer 2 + BOOST_CHECK(!view.isVertexCompatible(2, 0, v2)); + BOOST_CHECK(!view.isVertexCompatible(2, 1, v2)); + BOOST_CHECK(view.isVertexCompatible(2, 2, v2)); + BOOST_CHECK(!view.isVertexCompatible(2, 3, v2)); + BOOST_CHECK(!view.isVertexCompatible(2, 4, v2)); + BOOST_CHECK(!view.isVertexCompatible(2, 5, v2)); + BOOST_CHECK(!view.isVertexCompatible(2, 6, v2)); + BOOST_CHECK(!view.isVertexCompatible(2, 7, v2)); + // layer 3 + BOOST_CHECK(view.isVertexCompatible(3, 0, v2)); + BOOST_CHECK(view.isVertexCompatible(3, 1, v2)); + BOOST_CHECK(!view.isVertexCompatible(3, 2, v2)); + BOOST_CHECK(!view.isVertexCompatible(3, 3, v2)); + BOOST_CHECK(!view.isVertexCompatible(3, 4, v2)); + BOOST_CHECK(!view.isVertexCompatible(3, 5, v2)); + BOOST_CHECK(!view.isVertexCompatible(3, 6, v2)); +} + +BOOST_AUTO_TEST_CASE(rofvertex_exact_compatibility) +{ + o2::its::ROFVertexLookupTable<4> table; + table.defineLayer(0, 4, 100, 0, 0, 0); + table.defineLayer(1, 4, 100, 0, 0, 10); + table.defineLayer(2, 8, 50, 0, 0, 0); + table.defineLayer(3, 7, 50, 50, 0, 10); + table.init(); + + // sorted by lower bound timestamp + std::vector vertices; + { // idx 0: [40, 160] - wide span + auto& v = vertices.emplace_back(); + v.getTimeStamp().setTimeStamp(100); + v.getTimeStamp().setTimeStampError(60); + } + { // idx 1: [90, 110] + auto& v = vertices.emplace_back(); + v.getTimeStamp().setTimeStamp(100); + v.getTimeStamp().setTimeStampError(10); + } + { // idx 2: [100, 100] - zero width, false-positive prone + auto& v = vertices.emplace_back(); + v.getTimeStamp().setTimeStamp(100); + v.getTimeStamp().setTimeStampError(0); + } + + table.update(vertices.data(), vertices.size()); + const auto& view = table.getView(); + + // Layer 0 ROF 0: [0, 100) + BOOST_CHECK(!view.isVertexCompatible(0, 0, vertices[0])); + BOOST_CHECK(!view.isVertexCompatible(0, 0, vertices[1])); + BOOST_CHECK(!view.isVertexCompatible(0, 0, vertices[2])); + + // Layer 0 ROF 1: [100, 200) - range includes idx 2 as false positive + { + const auto& range = view.getVertices(0, 1); + BOOST_CHECK_EQUAL(range.getEntries(), 3); // superset + + size_t exactCount = 0; + for (size_t i = range.getFirstEntry(); i < range.getEntriesBound(); ++i) { + if (view.isVertexCompatible(0, 1, vertices[i])) { + ++exactCount; + } + } + // BOOST_CHECK_EQUAL(exactCount, 2); // idx 2 filtered out + } + + // Layer 0 ROF 2: [200, 300) - nothing overlaps + BOOST_CHECK(!view.isVertexCompatible(0, 2, vertices[0])); + BOOST_CHECK(!view.isVertexCompatible(0, 2, vertices[1])); + BOOST_CHECK(!view.isVertexCompatible(0, 2, vertices[2])); + + // Layer 2 ROF 0: [0, 50) - only idx 0 + BOOST_CHECK(!view.isVertexCompatible(2, 0, vertices[0])); + BOOST_CHECK(!view.isVertexCompatible(2, 0, vertices[1])); + + // Layer 2 ROF 1: [50, 100) - idx 0 and 1 + BOOST_CHECK(!view.isVertexCompatible(2, 1, vertices[0])); + BOOST_CHECK(!view.isVertexCompatible(2, 1, vertices[1])); + BOOST_CHECK(!view.isVertexCompatible(2, 1, vertices[2])); + + // Layer 2 ROF 3: [150, 200) - only idx 0 + BOOST_CHECK(view.isVertexCompatible(2, 3, vertices[0])); + BOOST_CHECK(!view.isVertexCompatible(2, 3, vertices[1])); + + // Layer 3 ROF 0: [40, 110) - all three genuine + BOOST_CHECK(view.isVertexCompatible(3, 0, vertices[0])); + BOOST_CHECK(view.isVertexCompatible(3, 0, vertices[1])); + BOOST_CHECK(view.isVertexCompatible(3, 0, vertices[2])); + + // Layer 3 ROF 2: [140, 210) - only idx 0 + BOOST_CHECK(view.isVertexCompatible(3, 2, vertices[0])); + BOOST_CHECK(!view.isVertexCompatible(3, 2, vertices[1])); + BOOST_CHECK(!view.isVertexCompatible(3, 2, vertices[2])); +} diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClusterWriterWorkflow.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClusterWriterWorkflow.h index 15c22f9bcf23d..a91038b32a1c1 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClusterWriterWorkflow.h +++ b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClusterWriterWorkflow.h @@ -23,7 +23,7 @@ namespace its namespace cluster_writer_workflow { -framework::WorkflowSpec getWorkflow(bool useMC); +framework::WorkflowSpec getWorkflow(bool useMC, bool doStag); } } // namespace its diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/DCSAdaposParserSpec.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/DCSAdaposParserSpec.h index bcc19ff15b85d..808fef81b586f 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/DCSAdaposParserSpec.h +++ b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/DCSAdaposParserSpec.h @@ -37,7 +37,7 @@ #include "DetectorsDCS/DataPointIdentifier.h" #include "DetectorsDCS/DataPointValue.h" #include "DetectorsDCS/DataPointCompositeObject.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "CCDB/BasicCCDBManager.h" using namespace o2::framework; diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/RecoWorkflow.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/RecoWorkflow.h index 1d5d829a6f79a..bfbde0093d55d 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/RecoWorkflow.h +++ b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/RecoWorkflow.h @@ -26,7 +26,7 @@ namespace its namespace reco_workflow { -framework::WorkflowSpec getWorkflow(bool useMC, TrackingMode::Type trmode, const bool overrideBeamPosition = false, +framework::WorkflowSpec getWorkflow(bool useMC, bool doStag, TrackingMode::Type trmode, const bool overrideBeamPosition = false, bool upstreamDigits = false, bool upstreamClusters = false, bool disableRootOutput = false, bool useGeom = false, int useTrig = 0, bool useGPUWF = false, o2::gpu::gpudatatypes::DeviceType dType = o2::gpu::gpudatatypes::DeviceType::CPU); } diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackReaderSpec.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackReaderSpec.h index 8666864ca1ae9..f4bcba750723f 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackReaderSpec.h +++ b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackReaderSpec.h @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// 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. // @@ -14,28 +14,26 @@ #ifndef O2_ITS_TRACKREADER #define O2_ITS_TRACKREADER -#include "TFile.h" -#include "TTree.h" +#include +#include #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" #include "Headers/DataHeader.h" -#include "ITStracking/Definitions.h" +#include "DataFormatsITSMFT/ROFRecord.h" #include "DataFormatsITS/TrackITS.h" +#include "DataFormatsITS/Vertex.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" -#include "DataFormatsITSMFT/ROFRecord.h" -namespace o2 -{ -namespace its +namespace o2::its { -class TrackReader : public o2::framework::Task +class TrackReader final : public o2::framework::Task { public: - TrackReader(bool useMC = true); - ~TrackReader() override = default; + TrackReader(bool useMC = true) : mUseMC(useMC) {} + ~TrackReader() final = default; void init(o2::framework::InitContext& ic) final; void run(o2::framework::ProcessingContext& pc) final; @@ -43,9 +41,9 @@ class TrackReader : public o2::framework::Task void connectTree(const std::string& filename); std::vector mROFRec, *mROFRecInp = &mROFRec; - std::vector mVerticesROFRec, *mVerticesROFRecInp = &mVerticesROFRec; std::vector mTracks, *mTracksInp = &mTracks; std::vector mVertices, *mVerticesInp = &mVertices; + std::vector mVerticesROFRec, *mVerticesROFRecInp = &mVerticesROFRec; std::vector mClusInd, *mClusIndInp = &mClusInd; std::vector mMCTruth, *mMCTruthInp = &mMCTruth; std::vector mMCVertTruth, *mMCVTruthInp = &mMCTruth; @@ -56,7 +54,7 @@ class TrackReader : public o2::framework::Task std::unique_ptr mFile; std::unique_ptr mTree; - std::string mInputFileName = ""; + std::string mInputFileName; std::string mTrackTreeName = "o2sim"; std::string mROFBranchName = "ITSTracksROF"; std::string mTrackBranchName = "ITSTrack"; @@ -71,7 +69,6 @@ class TrackReader : public o2::framework::Task /// read ITS track data from a root file framework::DataProcessorSpec getITSTrackReaderSpec(bool useMC = true); -} // namespace its -} // namespace o2 +} // namespace o2::its #endif /* O2_ITS_TRACKREADER */ diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackerSpec.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackerSpec.h index 01eb7cb7b69aa..8ce63efcb7a3b 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackerSpec.h +++ b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackerSpec.h @@ -42,6 +42,7 @@ class TrackerDPL : public framework::Task public: TrackerDPL(std::shared_ptr gr, bool isMC, + bool doStag, int trgType, const TrackingMode::Type trMode = TrackingMode::Unset, const bool overrBeamEst = false, @@ -63,7 +64,7 @@ class TrackerDPL : public framework::Task TStopwatch mTimer; }; -framework::DataProcessorSpec getTrackerSpec(bool useMC, bool useGeom, int useTrig, TrackingMode::Type trMode, const bool overrBeamEst = false, o2::gpu::gpudatatypes::DeviceType dType = o2::gpu::gpudatatypes::DeviceType::CPU); +framework::DataProcessorSpec getTrackerSpec(bool useMC, bool doStag, bool useGeom, int useTrig, TrackingMode::Type trMode, const bool overrBeamEst = false, o2::gpu::gpudatatypes::DeviceType dType = o2::gpu::gpudatatypes::DeviceType::CPU); } // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/VertexReaderSpec.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/VertexReaderSpec.h index b300967408256..10ee70eeafeea 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/VertexReaderSpec.h +++ b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/VertexReaderSpec.h @@ -19,8 +19,8 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" -#include "ITStracking/Definitions.h" #include "DataFormatsITSMFT/ROFRecord.h" +#include "DataFormatsITS/Vertex.h" namespace o2 { diff --git a/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterWorkflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterWorkflow.cxx index aba468b3e9460..35c911f856436 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterWorkflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterWorkflow.cxx @@ -22,11 +22,11 @@ namespace its namespace cluster_writer_workflow { -framework::WorkflowSpec getWorkflow(bool useMC) +framework::WorkflowSpec getWorkflow(bool useMC, bool doStag) { framework::WorkflowSpec specs; - specs.emplace_back(o2::itsmft::getITSClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getITSClusterWriterSpec(useMC, doStag)); return specs; } diff --git a/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx index 9f8cb6c83ef99..5da4b080995b5 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx @@ -27,7 +27,7 @@ namespace o2::its::reco_workflow { -framework::WorkflowSpec getWorkflow(bool useMC, +framework::WorkflowSpec getWorkflow(bool useMC, bool doStag, TrackingMode::Type trmode, const bool overrideBeamPosition, bool upstreamDigits, @@ -40,13 +40,13 @@ framework::WorkflowSpec getWorkflow(bool useMC, { framework::WorkflowSpec specs; if (!(upstreamDigits || upstreamClusters)) { - specs.emplace_back(o2::itsmft::getITSDigitReaderSpec(useMC, false, true, "itsdigits.root")); + specs.emplace_back(o2::itsmft::getITSDigitReaderSpec(useMC, doStag, false, true, "itsdigits.root")); } if (!upstreamClusters) { - specs.emplace_back(o2::itsmft::getITSClustererSpec(useMC)); + specs.emplace_back(o2::itsmft::getITSClustererSpec(useMC, doStag)); } if (!disableRootOutput) { - specs.emplace_back(o2::itsmft::getITSClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getITSClusterWriterSpec(useMC, doStag)); } if ((trmode != TrackingMode::Off) && (TrackerParamConfig::Instance().trackingMode != TrackingMode::Off)) { if (useGPUWF) { @@ -54,6 +54,7 @@ framework::WorkflowSpec getWorkflow(bool useMC, .itsTriggerType = useTrig, .processMC = useMC, .runITSTracking = true, + .itsStaggered = doStag, .itsOverrBeamEst = overrideBeamPosition, }; @@ -78,7 +79,7 @@ framework::WorkflowSpec getWorkflow(bool useMC, .algorithm = AlgorithmSpec{adoptTask(task)}, .options = taskOptions}); } else { - specs.emplace_back(o2::its::getTrackerSpec(useMC, useGeom, useTrig, trmode, overrideBeamPosition, dtype)); + specs.emplace_back(o2::its::getTrackerSpec(useMC, doStag, useGeom, useTrig, trmode, overrideBeamPosition, dtype)); } if (!disableRootOutput) { specs.emplace_back(o2::its::getTrackWriterSpec(useMC)); diff --git a/Detectors/ITSMFT/ITS/workflow/src/TrackReaderSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/TrackReaderSpec.cxx index 8e72faae9fd37..2f081a11c28b9 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/TrackReaderSpec.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/TrackReaderSpec.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// 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. // @@ -15,21 +15,14 @@ #include #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" +#include "CommonUtils/StringUtils.h" #include "ITSWorkflow/TrackReaderSpec.h" -#include "CommonUtils/NameConf.h" using namespace o2::framework; using namespace o2::its; -namespace o2 +namespace o2::its { -namespace its -{ - -TrackReader::TrackReader(bool useMC) -{ - mUseMC = useMC; -} void TrackReader::init(InitContext& ic) { @@ -43,7 +36,7 @@ void TrackReader::run(ProcessingContext& pc) auto ent = mTree->GetReadEntry() + 1; assert(ent < mTree->GetEntries()); // this should not happen mTree->GetEntry(ent); - LOG(info) << "Pushing " << mTracks.size() << " track in " << mROFRec.size() << " ROFs at entry " << ent; + LOG(info) << "Pushing " << mTracks.size() << " track at entry " << ent; pc.outputs().snapshot(Output{mOrigin, "ITSTrackROF", 0}, mROFRec); pc.outputs().snapshot(Output{mOrigin, "TRACKS", 0}, mTracks); pc.outputs().snapshot(Output{mOrigin, "TRACKCLSID", 0}, mClusInd); @@ -77,12 +70,6 @@ void TrackReader::connectTree(const std::string& filename) } else { mTree->SetBranchAddress(mVertexBranchName.c_str(), &mVerticesInp); } - if (!mTree->GetBranch(mVertexROFBranchName.c_str())) { - LOG(warning) << "No " << mVertexROFBranchName << " branch in " << mTrackTreeName - << " -> vertices ROFrecords will be empty"; - } else { - mTree->SetBranchAddress(mVertexROFBranchName.c_str(), &mVerticesROFRecInp); - } if (mUseMC) { if (mTree->GetBranch(mTrackMCTruthBranchName.c_str())) { mTree->SetBranchAddress(mTrackMCTruthBranchName.c_str(), &mMCTruthInp); @@ -107,14 +94,13 @@ DataProcessorSpec getITSTrackReaderSpec(bool useMC) } return DataProcessorSpec{ - "its-track-reader", - Inputs{}, - outputSpec, - AlgorithmSpec{adaptFromTask(useMC)}, - Options{ + .name = "its-track-reader", + .inputs = Inputs{}, + .outputs = outputSpec, + .algorithm = AlgorithmSpec{adaptFromTask(useMC)}, + .options = Options{ {"its-tracks-infile", VariantType::String, "o2trac_its.root", {"Name of the input track file"}}, {"input-dir", VariantType::String, "none", {"Input directory"}}}}; } -} // namespace its -} // namespace o2 +} // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/workflow/src/TrackWriterSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/TrackWriterSpec.cxx index c10b4aa32f054..84f43ee148302 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/TrackWriterSpec.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/TrackWriterSpec.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// 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. // @@ -19,8 +19,7 @@ #include "DataFormatsITSMFT/ROFRecord.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" -#include "ITStracking/Definitions.h" -#include "ITStracking/TrackingConfigParam.h" +#include "DataFormatsITS/Vertex.h" using namespace o2::framework; @@ -39,8 +38,7 @@ DataProcessorSpec getTrackWriterSpec(bool useMC) { // Spectators for logging // this is only to restore the original behavior - const auto writeContLabels = VertexerParamConfig::Instance().outputContLabels && useMC; - auto tracksSize = std::make_shared(0); + auto tracksSize = std::make_shared(0); auto tracksSizeGetter = [tracksSize](std::vector const& tracks) { *tracksSize = tracks.size(); }; @@ -57,11 +55,11 @@ DataProcessorSpec getTrackWriterSpec(bool useMC) "ITSTrackClusIdx"}, BranchDefinition>{InputSpec{"vertices", "ITS", "VERTICES", 0}, "Vertices"}, - BranchDefinition>{InputSpec{"vtxROF", "ITS", "VERTICESROF", 0}, - "VerticesROF"}, BranchDefinition>{InputSpec{"ROframes", "ITS", "ITSTrackROF", 0}, "ITSTracksROF", logger}, + BranchDefinition>{InputSpec{"vtxROF", "ITS", "VERTICESROF", 0}, + "VerticesROF"}, BranchDefinition{InputSpec{"labels", "ITS", "TRACKSMCTR", 0}, "ITSTrackMCTruth", (useMC ? 1 : 0), // one branch if mc labels enabled @@ -70,15 +68,6 @@ DataProcessorSpec getTrackWriterSpec(bool useMC) "ITSVertexMCTruth", (useMC ? 1 : 0), // one branch if mc labels enabled ""}, - BranchDefinition{InputSpec{"labelsVerticesContributors", "ITS", "VERTICESMCTRCONT", 0}, - "ITSVertexMCTruthCont", - (writeContLabels ? 1 : 0), // one branch if - // requested - ""}, - BranchDefinition{InputSpec{"MC2ROframes", "ITS", "ITSTrackMC2ROF", 0}, - "ITSTracksMC2ROF", - (useMC ? 1 : 0), // one branch if mc labels enabled - ""}, BranchDefinition>{InputSpec{"purityVertices", "ITS", "VERTICESMCPUR", 0}, "ITSVertexMCPurity", (useMC ? 1 : 0), // one branch if mc labels enabled ""})(); diff --git a/Detectors/ITSMFT/ITS/workflow/src/TrackWriterWorkflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/TrackWriterWorkflow.cxx index ae2cb3648ec86..ce1d238188ec5 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/TrackWriterWorkflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/TrackWriterWorkflow.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// 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. // @@ -31,4 +31,4 @@ framework::WorkflowSpec getWorkflow(bool useMC) } // namespace track_writer_workflow } // namespace its -} // namespace o2 \ No newline at end of file +} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx index 3d07048aaf1e6..932c82c2d1ca4 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// 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. // @@ -15,6 +15,7 @@ #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" #include "Framework/DeviceSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "ITSWorkflow/TrackerSpec.h" #include "ITStracking/Definitions.h" #include "ITStracking/TrackingConfigParam.h" @@ -26,12 +27,13 @@ namespace its { TrackerDPL::TrackerDPL(std::shared_ptr gr, bool isMC, + bool doStag, int trgType, const TrackingMode::Type trMode, const bool overrBeamEst, o2::gpu::gpudatatypes::DeviceType dType) : mGGCCDBRequest(gr), mRecChain{o2::gpu::GPUReconstruction::CreateInstance(dType, true)}, - mITSTrackingInterface{isMC, trgType, overrBeamEst} + mITSTrackingInterface{isMC, doStag, trgType, overrBeamEst} { mITSTrackingInterface.setTrackingMode(trMode); } @@ -87,13 +89,18 @@ void TrackerDPL::end() LOGF(info, "ITS CA-Tracker total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getTrackerSpec(bool useMC, bool useGeom, int trgType, TrackingMode::Type trMode, const bool overrBeamEst, o2::gpu::gpudatatypes::DeviceType dType) +DataProcessorSpec getTrackerSpec(bool useMC, bool doStag, bool useGeom, int trgType, TrackingMode::Type trMode, const bool overrBeamEst, o2::gpu::gpudatatypes::DeviceType dType) { + const int mLayers = doStag ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; std::vector inputs; - - inputs.emplace_back("compClusters", "ITS", "COMPCLUSTERS", 0, Lifetime::Timeframe); - inputs.emplace_back("patterns", "ITS", "PATTERNS", 0, Lifetime::Timeframe); - inputs.emplace_back("ROframes", "ITS", "CLUSTERSROF", 0, Lifetime::Timeframe); + for (int iLayer = 0; iLayer < mLayers; ++iLayer) { + inputs.emplace_back("compClusters", "ITS", "COMPCLUSTERS", iLayer, Lifetime::Timeframe); + inputs.emplace_back("patterns", "ITS", "PATTERNS", iLayer, Lifetime::Timeframe); + inputs.emplace_back("ROframes", "ITS", "CLUSTERSROF", iLayer, Lifetime::Timeframe); + if (useMC) { + inputs.emplace_back("itsmclabels", "ITS", "CLUSTERSMCTR", iLayer, Lifetime::Timeframe); + } + } if (trgType == 1) { inputs.emplace_back("phystrig", "ITS", "PHYSTRIG", 0, Lifetime::Timeframe); } else if (trgType == 2) { @@ -123,30 +130,24 @@ DataProcessorSpec getTrackerSpec(bool useMC, bool useGeom, int trgType, Tracking outputs.emplace_back("ITS", "VERTICES", 0, Lifetime::Timeframe); outputs.emplace_back("ITS", "VERTICESROF", 0, Lifetime::Timeframe); outputs.emplace_back("ITS", "IRFRAMES", 0, Lifetime::Timeframe); - if (useMC) { - inputs.emplace_back("itsmclabels", "ITS", "CLUSTERSMCTR", 0, Lifetime::Timeframe); - inputs.emplace_back("ITSMC2ROframes", "ITS", "CLUSTERSMC2ROF", 0, Lifetime::Timeframe); outputs.emplace_back("ITS", "VERTICESMCTR", 0, Lifetime::Timeframe); outputs.emplace_back("ITS", "VERTICESMCPUR", 0, Lifetime::Timeframe); outputs.emplace_back("ITS", "TRACKSMCTR", 0, Lifetime::Timeframe); - outputs.emplace_back("ITS", "ITSTrackMC2ROF", 0, Lifetime::Timeframe); - if (VertexerParamConfig::Instance().outputContLabels) { - outputs.emplace_back("ITS", "VERTICESMCTRCONT", 0, Lifetime::Timeframe); - } } return DataProcessorSpec{ - "its-tracker", - inputs, - outputs, - AlgorithmSpec{adaptFromTask(ggRequest, - useMC, - trgType, - trMode, - overrBeamEst, - dType)}, - Options{}}; + .name = "its-tracker", + .inputs = inputs, + .outputs = outputs, + .algorithm = AlgorithmSpec{adaptFromTask(ggRequest, + useMC, + doStag, + trgType, + trMode, + overrBeamEst, + dType)}, + .options = Options{}}; } } // namespace its diff --git a/Detectors/ITSMFT/ITS/workflow/src/its-cluster-reader-workflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/its-cluster-reader-workflow.cxx index da843526f9296..cbbb4bea09f4b 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/its-cluster-reader-workflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/its-cluster-reader-workflow.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// 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. // @@ -10,6 +10,7 @@ // or submit itself to any jurisdiction. #include "Framework/ConfigParamSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "CommonUtils/ConfigurableParam.h" #include "DetectorsRaw/HBFUtilsInitializer.h" #include "Framework/CallbacksPolicy.h" @@ -47,6 +48,7 @@ void customize(std::vector& workflowOptions) VariantType::String, "", {"Semicolon separated key=value strings"}}); + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(workflowOptions); o2::raw::HBFUtilsInitializer::addConfigOption(workflowOptions); } @@ -60,8 +62,9 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cc) auto withTriggers = !cc.options().get("suppress-triggers-output"); auto withMC = cc.options().get("with-mc"); auto withPatterns = !cc.options().get("without-patterns"); + auto doStag = o2::itsmft::DPLAlpideParamInitializer::isITSStaggeringEnabled(cc); - specs.emplace_back(o2::itsmft::getITSClusterReaderSpec(withMC, withPatterns, withTriggers)); + specs.emplace_back(o2::itsmft::getITSClusterReaderSpec(withMC, doStag, withPatterns, withTriggers)); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(cc, specs); diff --git a/Detectors/ITSMFT/ITS/workflow/src/its-cluster-writer-workflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/its-cluster-writer-workflow.cxx index ad3d8eea6e636..c10a1659d5f76 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/its-cluster-writer-workflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/its-cluster-writer-workflow.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// 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. // @@ -10,6 +10,7 @@ // or submit itself to any jurisdiction. #include "ITSWorkflow/ClusterWriterWorkflow.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "Framework/ConfigParamSpec.h" #include "Framework/CompletionPolicyHelpers.h" @@ -29,13 +30,14 @@ void customize(std::vector& workflowOptions) o2::framework::VariantType::Bool, false, {"disable MC propagation even if available"}}); + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(workflowOptions); } #include "Framework/runDataProcessing.h" -#include "Framework/Logger.h" WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { auto useMC = !configcontext.options().get("disable-mc"); - return std::move(o2::its::cluster_writer_workflow::getWorkflow(useMC)); + auto doStag = o2::itsmft::DPLAlpideParamInitializer::isITSStaggeringEnabled(configcontext); + return std::move(o2::its::cluster_writer_workflow::getWorkflow(useMC, doStag)); } diff --git a/Detectors/ITSMFT/ITS/workflow/src/its-reco-workflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/its-reco-workflow.cxx index 8080883888d40..bdade0effcbf0 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/its-reco-workflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/its-reco-workflow.cxx @@ -10,6 +10,7 @@ // or submit itself to any jurisdiction. #include "ITSWorkflow/RecoWorkflow.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "CommonUtils/ConfigurableParam.h" #include "ITStracking/Configuration.h" #include "DetectorsRaw/HBFUtilsInitializer.h" @@ -50,6 +51,7 @@ void customize(std::vector& workflowOptions) {"use-full-geometry", o2::framework::VariantType::Bool, false, {"use full geometry instead of the light-weight ITS part"}}, {"use-gpu-workflow", o2::framework::VariantType::Bool, false, {"use GPU workflow (default: false)"}}, {"gpu-device", o2::framework::VariantType::Int, 1, {"use gpu device: CPU=1,CUDA=2,HIP=3 (default: CPU)"}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -72,6 +74,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) auto extClusters = configcontext.options().get("clusters-from-upstream"); auto disableRootOutput = configcontext.options().get("disable-root-output"); auto useGeom = configcontext.options().get("use-full-geometry"); + auto doStag = o2::itsmft::DPLAlpideParamInitializer::isITSStaggeringEnabled(configcontext); if (configcontext.options().get("disable-tracking")) { trmode = "off"; } @@ -87,16 +90,18 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) LOG(fatal) << "Unknown trigger type requested for events prescaling: " << selTrig; } } - auto wf = o2::its::reco_workflow::getWorkflow(useMC, - o2::its::TrackingMode::fromString(trmode), - beamPosOVerride, - extDigits, - extClusters, - disableRootOutput, - useGeom, - trType, - useGpuWF, - gpuDevice); + auto wf = o2::its::reco_workflow::getWorkflow( + useMC, + doStag, + o2::its::TrackingMode::fromString(trmode), + beamPosOVerride, + extDigits, + extClusters, + disableRootOutput, + useGeom, + trType, + useGpuWF, + gpuDevice); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(configcontext, wf); diff --git a/Detectors/ITSMFT/ITS/workflow/src/its-track-writer-workflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/its-track-writer-workflow.cxx index d06ab366ef54c..ebd10ab3b16ce 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/its-track-writer-workflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/its-track-writer-workflow.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// 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. // @@ -27,7 +27,6 @@ void customize(std::vector& workflowOptions) } #include "Framework/runDataProcessing.h" -#include "Framework/Logger.h" WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { diff --git a/Detectors/ITSMFT/MFT/calibration/src/NoiseCalibratorSpec.cxx b/Detectors/ITSMFT/MFT/calibration/src/NoiseCalibratorSpec.cxx index 86107106dc2ba..e55e822847177 100644 --- a/Detectors/ITSMFT/MFT/calibration/src/NoiseCalibratorSpec.cxx +++ b/Detectors/ITSMFT/MFT/calibration/src/NoiseCalibratorSpec.cxx @@ -18,7 +18,7 @@ #include "DataFormatsITSMFT/Digit.h" #include "DataFormatsITSMFT/CompCluster.h" #include "DataFormatsITSMFT/ROFRecord.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "ITSMFTReconstruction/ClustererParam.h" #include diff --git a/Detectors/ITSMFT/MFT/condition/include/MFTCondition/DCSConfigReader.h b/Detectors/ITSMFT/MFT/condition/include/MFTCondition/DCSConfigReader.h index efae3104279e1..110465bb92757 100644 --- a/Detectors/ITSMFT/MFT/condition/include/MFTCondition/DCSConfigReader.h +++ b/Detectors/ITSMFT/MFT/condition/include/MFTCondition/DCSConfigReader.h @@ -14,7 +14,7 @@ #include "Rtypes.h" #include "DataFormatsITSMFT/NoiseMap.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "MFTCondition/DCSConfigInfo.h" #include "MFTCondition/DCSConfigUtils.h" #include diff --git a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/RecoWorkflow.h b/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/RecoWorkflow.h index 0e0b8af1da70a..51234e2e8017d 100644 --- a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/RecoWorkflow.h +++ b/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/RecoWorkflow.h @@ -25,6 +25,7 @@ namespace reco_workflow { framework::WorkflowSpec getWorkflow( bool useMC, + bool doStag, bool useGeom, bool upstreamDigits, bool upstreamClusters, diff --git a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/TrackerSpec.h b/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/TrackerSpec.h index 4274710b23867..8bd290caf5a41 100644 --- a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/TrackerSpec.h +++ b/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/TrackerSpec.h @@ -16,7 +16,7 @@ #include "MFTTracking/Tracker.h" #include "DetectorsBase/GRPGeomHelper.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "Framework/DataProcessorSpec.h" #include "MFTTracking/TrackCA.h" diff --git a/Detectors/ITSMFT/MFT/workflow/src/RecoWorkflow.cxx b/Detectors/ITSMFT/MFT/workflow/src/RecoWorkflow.cxx index 5d85c0ef81670..fb99715cae4ee 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/RecoWorkflow.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/RecoWorkflow.cxx @@ -32,6 +32,7 @@ namespace reco_workflow framework::WorkflowSpec getWorkflow( bool useMC, + bool doStag, bool useGeom, bool upstreamDigits, bool upstreamClusters, @@ -45,17 +46,17 @@ framework::WorkflowSpec getWorkflow( framework::WorkflowSpec specs; if (!(upstreamDigits || upstreamClusters)) { - specs.emplace_back(o2::itsmft::getMFTDigitReaderSpec(useMC, false, true, "mftdigits.root")); + specs.emplace_back(o2::itsmft::getMFTDigitReaderSpec(useMC, doStag, false, true, "mftdigits.root")); auto& trackingParam = MFTTrackingParam::Instance(); if (trackingParam.irFramesOnly) { specs.emplace_back(o2::globaltracking::getIRFrameReaderSpec("ITS", 0, "its-irframe-reader", "o2_its_irframe.root")); } } if (!upstreamClusters) { - specs.emplace_back(o2::itsmft::getMFTClustererSpec(useMC)); + specs.emplace_back(o2::itsmft::getMFTClustererSpec(useMC, doStag)); } if (!disableRootOutput) { - specs.emplace_back(o2::itsmft::getMFTClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getMFTClusterWriterSpec(useMC, doStag)); } if (runTracking) { diff --git a/Detectors/ITSMFT/MFT/workflow/src/TrackWriterSpec.cxx b/Detectors/ITSMFT/MFT/workflow/src/TrackWriterSpec.cxx index d9c132c97abdf..f8a848f6fde32 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/TrackWriterSpec.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/TrackWriterSpec.cxx @@ -24,7 +24,6 @@ using namespace o2::framework; using LabelsType = std::vector; -using ROFRecLblT = std::vector; namespace o2 { @@ -44,7 +43,7 @@ DataProcessorSpec getTrackWriterSpec(bool useMC) *tracksSize = tracks.size(); }; auto logger = [tracksSize](std::vector const& rofs) { - LOG(debug) << "MFTTrackWriter pulled " << *tracksSize << " tracks, in " << rofs.size() << " RO frames"; + LOG(info) << "MFTTrackWriter pulled " << *tracksSize << " tracks, in " << rofs.size() << " RO frames"; }; return MakeRootTreeWriterSpec("mft-track-writer", "mfttracks.root", @@ -54,15 +53,11 @@ DataProcessorSpec getTrackWriterSpec(bool useMC) tracksSizeGetter}, BranchDefinition>{InputSpec{"trackClIdx", "MFT", "TRACKCLSID", 0}, "MFTTrackClusIdx"}, - BranchDefinition{InputSpec{"labels", "MFT", "TRACKSMCTR", 0}, - "MFTTrackMCTruth", - (useMC ? 1 : 0), // one branch if mc labels enabled - ""}, BranchDefinition>{InputSpec{"ROframes", "MFT", "MFTTrackROF", 0}, "MFTTracksROF", logger}, - BranchDefinition{InputSpec{"MC2ROframes", "MFT", "TRACKSMC2ROF", 0}, - "MFTTracksMC2ROF", + BranchDefinition{InputSpec{"labels", "MFT", "TRACKSMCTR", 0}, + "MFTTrackMCTruth", (useMC ? 1 : 0), // one branch if mc labels enabled ""})(); } diff --git a/Detectors/ITSMFT/MFT/workflow/src/TrackerSpec.cxx b/Detectors/ITSMFT/MFT/workflow/src/TrackerSpec.cxx index 3e726fe37c38c..a13a3402eb260 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/TrackerSpec.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/TrackerSpec.cxx @@ -45,7 +45,7 @@ namespace o2 { namespace mft { -//#define _TIMING_ +// #define _TIMING_ void TrackerDPL::init(InitContext& ic) { @@ -98,12 +98,6 @@ void TrackerDPL::run(ProcessingContext& pc) } const dataformats::MCTruthContainer* labels = mUseMC ? pc.inputs().get*>("labels").release() : nullptr; - gsl::span mc2rofs; - if (mUseMC) { - // get the array as read-only span, a snapshot of the object is sent forward - mc2rofs = pc.inputs().get>("MC2ROframes"); - LOG(info) << labels->getIndexedSize() << " MC label objects , in " << mc2rofs.size() << " MC events"; - } auto& allClusIdx = pc.outputs().make>(Output{"MFT", "TRACKCLSID", 0}); std::vector trackLabels; @@ -325,11 +319,10 @@ void TrackerDPL::run(ProcessingContext& pc) } } - LOG(info) << "MFTTracker pushed " << allTracksMFT.size() << " tracks"; + LOG(info) << "MFTTracker pushed " << allTracksMFT.size() << " tracks in " << nROFs << " rofs"; if (mUseMC) { pc.outputs().snapshot(Output{"MFT", "TRACKSMCTR", 0}, allTrackLabels); - pc.outputs().snapshot(Output{"MFT", "TRACKSMC2ROF", 0}, mc2rofs); } static bool first = true; @@ -466,9 +459,7 @@ DataProcessorSpec getTrackerSpec(bool useMC, bool useGeom, int nThreads) if (useMC) { inputs.emplace_back("labels", "MFT", "CLUSTERSMCTR", 0, Lifetime::Timeframe); - inputs.emplace_back("MC2ROframes", "MFT", "CLUSTERSMC2ROF", 0, Lifetime::Timeframe); outputs.emplace_back("MFT", "TRACKSMCTR", 0, Lifetime::Timeframe); - outputs.emplace_back("MFT", "TRACKSMC2ROF", 0, Lifetime::Timeframe); } return DataProcessorSpec{ diff --git a/Detectors/ITSMFT/MFT/workflow/src/mft-cluster-reader-workflow.cxx b/Detectors/ITSMFT/MFT/workflow/src/mft-cluster-reader-workflow.cxx index 9907705fb1e7c..eaa525345fd9f 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/mft-cluster-reader-workflow.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/mft-cluster-reader-workflow.cxx @@ -10,6 +10,7 @@ // or submit itself to any jurisdiction. #include "Framework/ConfigParamSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "DetectorsRaw/HBFUtilsInitializer.h" #include "Framework/CallbacksPolicy.h" @@ -41,6 +42,7 @@ void customize(std::vector& workflowOptions) false, {"do not propagate pixel patterns"}}); workflowOptions.push_back(ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}); + o2::itsmft::DPLAlpideParamInitializer::addMFTConfigOption(workflowOptions); o2::raw::HBFUtilsInitializer::addConfigOption(workflowOptions); } @@ -53,7 +55,8 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cc) auto withTriggers = !cc.options().get("suppress-triggers-output"); auto withMC = cc.options().get("with-mc"); auto withPatterns = !cc.options().get("without-patterns"); - specs.emplace_back(o2::itsmft::getMFTClusterReaderSpec(withMC, withPatterns, withTriggers)); + auto doStag = o2::itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(cc); + specs.emplace_back(o2::itsmft::getMFTClusterReaderSpec(withMC, doStag, withPatterns, withTriggers)); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(cc, specs); diff --git a/Detectors/ITSMFT/MFT/workflow/src/mft-cluster-writer-workflow.cxx b/Detectors/ITSMFT/MFT/workflow/src/mft-cluster-writer-workflow.cxx index b656970693808..5a5112e03c866 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/mft-cluster-writer-workflow.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/mft-cluster-writer-workflow.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// 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. // @@ -10,6 +10,7 @@ // or submit itself to any jurisdiction. #include "ITSMFTWorkflow/ClusterWriterSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "Framework/ConfigParamSpec.h" #include "Framework/CompletionPolicyHelpers.h" @@ -25,15 +26,16 @@ void customize(std::vector& workflowOptions) { workflowOptions.push_back( ConfigParamSpec{"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation even if available"}}); + o2::itsmft::DPLAlpideParamInitializer::addMFTConfigOption(workflowOptions); } #include "Framework/runDataProcessing.h" -#include "Framework/Logger.h" WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { auto useMC = !configcontext.options().get("disable-mc"); + auto doStag = o2::itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(configcontext); WorkflowSpec specs; - specs.emplace_back(o2::itsmft::getMFTClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getMFTClusterWriterSpec(useMC, doStag)); return specs; } diff --git a/Detectors/ITSMFT/MFT/workflow/src/mft-reco-workflow.cxx b/Detectors/ITSMFT/MFT/workflow/src/mft-reco-workflow.cxx index 19e41ed984f11..11b4fc233c6b4 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/mft-reco-workflow.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/mft-reco-workflow.cxx @@ -10,6 +10,7 @@ // or submit itself to any jurisdiction. #include "MFTWorkflow/RecoWorkflow.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "CommonUtils/ConfigurableParam.h" #include "DetectorsRaw/HBFUtilsInitializer.h" #include "Framework/CallbacksPolicy.h" @@ -45,6 +46,7 @@ void customize(std::vector& workflowOptions) {"use-full-geometry", o2::framework::VariantType::Bool, false, {"use full geometry instead of the light-weight MFT part"}}, {"run-tracks2records", o2::framework::VariantType::Bool, false, {"run MFT alignment tracks to records workflow"}}}; o2::raw::HBFUtilsInitializer::addConfigOption(options); + o2::itsmft::DPLAlpideParamInitializer::addMFTConfigOption(options); std::swap(workflowOptions, options); } @@ -67,9 +69,11 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) auto nThreads = configcontext.options().get("nThreads"); auto runTracks2Records = configcontext.options().get("run-tracks2records"); auto useGeom = configcontext.options().get("use-full-geometry"); + auto doStag = o2::itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(configcontext); auto wf = o2::mft::reco_workflow::getWorkflow( useMC, + doStag, useGeom, extDigits, extClusters, diff --git a/Detectors/ITSMFT/common/base/CMakeLists.txt b/Detectors/ITSMFT/common/base/CMakeLists.txt index a3e0718d64a6b..43d60f6d2b11d 100644 --- a/Detectors/ITSMFT/common/base/CMakeLists.txt +++ b/Detectors/ITSMFT/common/base/CMakeLists.txt @@ -11,12 +11,10 @@ o2_add_library(ITSMFTBase SOURCES src/SegmentationAlpide.cxx - src/GeometryTGeo.cxx src/DPLAlpideParam.cxx PUBLIC_LINK_LIBRARIES O2::MathUtils O2::DetectorsCommonDataFormats O2::SimConfig) o2_target_root_dictionary(ITSMFTBase HEADERS include/ITSMFTBase/SegmentationAlpide.h - include/ITSMFTBase/GeometryTGeo.h - include/ITSMFTBase/DPLAlpideParam.h) + include/ITSMFTBase/GeometryTGeo.h) diff --git a/Detectors/ITSMFT/common/base/include/ITSMFTBase/DPLAlpideParam.h b/Detectors/ITSMFT/common/base/include/ITSMFTBase/DPLAlpideParam.h index de39bed299634..e217808c06177 100644 --- a/Detectors/ITSMFT/common/base/include/ITSMFTBase/DPLAlpideParam.h +++ b/Detectors/ITSMFT/common/base/include/ITSMFTBase/DPLAlpideParam.h @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// 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. // @@ -9,110 +9,5 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef ALICEO2_ITSMFTDPLBASEPARAM_H_ -#define ALICEO2_ITSMFTDPLBASEPARAM_H_ - -#include "DetectorsCommonDataFormats/DetID.h" -#include "CommonUtils/ConfigurableParam.h" -#include "CommonUtils/ConfigurableParamHelper.h" -#include "CommonConstants/LHCConstants.h" -#include - -namespace o2 -{ -namespace itsmft -{ -constexpr float DEFStrobeDelay = o2::constants::lhc::LHCBunchSpacingNS * 4; // ~100 ns delay - -template -struct DPLAlpideParam : public o2::conf::ConfigurableParamHelper> { - static constexpr int getNLayers() - { - return N == o2::detectors::DetID::ITS ? 7 : 10; - } - - static constexpr std::string_view getParamName() - { - return N == o2::detectors::DetID::ITS ? ParamName[0] : ParamName[1]; - } - - int roFrameLengthInBC = DEFROFLengthBC(); ///< ROF length in BC for continuous mode - float roFrameLengthTrig = DEFROFLengthTrig(); ///< length of RO frame in ns for triggered mode - float strobeDelay = DEFStrobeDelay; ///< strobe start (in ns) wrt ROF start - float strobeLengthCont = -1.; ///< if < 0, full ROF length - delay - float strobeLengthTrig = 100.; ///< length of the strobe in ns (sig. over threshold checked in this window only) - int roFrameBiasInBC = DEFROFBiasInBC(); ///< bias of the start of ROF wrt orbit start: t_irof = (irof*roFrameLengthInBC + roFrameBiasInBC)*BClengthMUS - int roFrameLayerLengthInBC[getNLayers()] = {}; ///< staggering ROF length in BC for continuous mode per layer - int roFrameLayerBiasInBC[getNLayers()] = {}; ///< staggering ROF bias in BC for continuous mode per layer - int roFrameLayerDelayInBC[getNLayers()] = {}; ///< staggering ROF delay in BC for continuous mode per layer - - static constexpr bool supportsStaggering() noexcept { return (N == o2::detectors::DetID::ITS) ? false : false; } - // test if staggering is on - bool withStaggering() const noexcept - { - if constexpr (!supportsStaggering()) { - return false; - } - for (int i{0}; i < getNLayers(); ++i) { - if (roFrameLayerLengthInBC[i] != 0) { - return true; - } - } - return false; - } - // get ROF length for any layer - int getROFLengthInBC(int layer) const noexcept { return (withStaggering()) ? roFrameLayerLengthInBC[layer] : roFrameLengthInBC; } - int getROFBiasInBC(int layer) const noexcept { return (withStaggering()) ? roFrameLayerBiasInBC[layer] : roFrameBiasInBC; } - int getROFDelayInBC(int layer) const noexcept { return (withStaggering()) ? roFrameLayerDelayInBC[layer] : 0; } - - // boilerplate stuff + make principal key - O2ParamDef(DPLAlpideParam, getParamName().data()); - - private: - static constexpr std::string_view ParamName[2] = {"ITSAlpideParam", "MFTAlpideParam"}; - - static constexpr int DEFROFLengthBC() - { - // default ROF length in BC for continuous mode - // allowed values: 1,2,3,4,6,9,11,12,18,22,27,33,36 - return N == o2::detectors::DetID::ITS ? o2::constants::lhc::LHCMaxBunches / 4 : o2::constants::lhc::LHCMaxBunches / 18; - } - static constexpr float DEFROFLengthTrig() - { - // length of RO frame in ns for triggered mode - return N == o2::detectors::DetID::ITS ? 6000. : 6000.; - } - - static constexpr int DEFROFBiasInBC() - { - // default ROF length bias in MC, see https://github.com/AliceO2Group/AliceO2/pull/11108 for ITS - return N == o2::detectors::DetID::ITS ? 64 : 60; - } - - static_assert(N == o2::detectors::DetID::ITS || N == o2::detectors::DetID::MFT, "only DetID::ITS orDetID:: MFT are allowed"); - static_assert(o2::constants::lhc::LHCMaxBunches % DEFROFLengthBC() == 0); // make sure ROF length is divisor of the orbit -}; - -template -DPLAlpideParam DPLAlpideParam::sInstance; - -} // namespace itsmft - -namespace framework -{ -template -struct is_messageable; -template <> -struct is_messageable> : std::true_type { -}; -template -struct is_messageable; -template <> -struct is_messageable> : std::true_type { -}; - -} // namespace framework - -} // namespace o2 - -#endif +// FIXME: temporary shim to no not break O2Physics +#include "DataFormatsITSMFT/DPLAlpideParam.h" diff --git a/Detectors/ITSMFT/common/base/src/ITSMFTBaseLinkDef.h b/Detectors/ITSMFT/common/base/src/ITSMFTBaseLinkDef.h index 6202f372cf2d3..9296c21e81cae 100644 --- a/Detectors/ITSMFT/common/base/src/ITSMFTBaseLinkDef.h +++ b/Detectors/ITSMFT/common/base/src/ITSMFTBaseLinkDef.h @@ -17,11 +17,6 @@ #pragma link C++ class o2::itsmft::SegmentationAlpide + ; -#pragma link C++ class o2::itsmft::DPLAlpideParam < o2::detectors::DetID::ITS> + ; -#pragma link C++ class o2::itsmft::DPLAlpideParam < o2::detectors::DetID::MFT> + ; -#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::itsmft::DPLAlpideParam < o2::detectors::DetID::ITS>> + ; -#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::itsmft::DPLAlpideParam < o2::detectors::DetID::MFT>> + ; - #pragma link C++ class o2::itsmft::GeometryTGeo; #endif diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h index 7e266052efb3c..4f9bc90c1c758 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h @@ -27,6 +27,7 @@ #include "ITSMFTReconstruction/LookUp.h" #include "ITSMFTReconstruction/PixelData.h" #include "ITSMFTReconstruction/Clusterer.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsBase/CTFCoderBase.h" @@ -39,19 +40,22 @@ namespace o2 namespace itsmft { +template class CTFCoder final : public o2::ctf::CTFCoderBase { public: + static constexpr o2::detectors::DetID ID{N == o2::detectors::DetID::ITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT}; + using PMatrix = std::array, ClusterPattern::MaxColSpan + 2>; using RowColBuff = std::vector; - CTFCoder(o2::ctf::CTFCoderBase::OpType op, o2::detectors::DetID det, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), det, 1.f, ctfdictOpt) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, bool doStag, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), ID, 1.f, ctfdictOpt), mDoStaggering(doStag) {} ~CTFCoder() final = default; /// entropy-encode clusters to buffer with CTF template o2::ctf::CTFIOSize encode(VEC& buff, const gsl::span& rofRecVec, const gsl::span& cclusVec, - const gsl::span& pattVec, const LookUp& clPattLookup, int strobeLength); + const gsl::span& pattVec, const LookUp& clPattLookup, int layer); /// entropy decode clusters from buffer with CTF template @@ -79,16 +83,21 @@ class CTFCoder final : public o2::ctf::CTFCoderBase template void decompress(const CompressedClusters& compCl, VROF& rofRecVec, VDIG& digVec, const NoiseMap* noiseMap, const LookUp& clPattLookup); - void appendToTree(TTree& tree, CTF& ec); - void readFromTree(TTree& tree, int entry, std::vector& rofRecVec, std::vector& cclusVec, std::vector& pattVec, const NoiseMap* noiseMap, const LookUp& clPattLookup); + void appendToTree(TTree& tree, CTF& ec, int id = -1); + void readFromTree(TTree& tree, int entry, int id, std::vector& rofRecVec, std::vector& cclusVec, std::vector& pattVec, const NoiseMap* noiseMap, const LookUp& clPattLookup); + + bool mDoStaggering{false}; }; /// entropy-encode clusters to buffer with CTF +template template -o2::ctf::CTFIOSize CTFCoder::encode(VEC& buff, const gsl::span& rofRecVec, const gsl::span& cclusVec, - const gsl::span& pattVec, const LookUp& clPattLookup, int strobeLength) +o2::ctf::CTFIOSize CTFCoder::encode(VEC& buff, const gsl::span& rofRecVec, const gsl::span& cclusVec, + const gsl::span& pattVec, const LookUp& clPattLookup, int layer) { using MD = o2::ctf::Metadata::OptStore; + const auto& par = DPLAlpideParam::Instance(); + int strobeLength = mDoStaggering ? par.roFrameLayerLengthInBC[layer] : par.roFrameLengthInBC; // what to do which each field: see o2::ctd::Metadata explanation constexpr MD optField[CTF::getNBlocks()] = { MD::EENCODE_OR_PACK, // BLCfirstChipROF @@ -104,6 +113,8 @@ o2::ctf::CTFIOSize CTFCoder::encode(VEC& buff, const gsl::span& }; CompressedClusters compCl; compress(compCl, rofRecVec, cclusVec, pattVec, clPattLookup, strobeLength); + compCl.header.maxStreams = mDoStaggering ? par.getNLayers() : 1; + compCl.header.streamID = mDoStaggering ? layer : 0; // book output size with some margin auto szIni = estimateCompressedSize(compCl); buff.resize(szIni); @@ -136,19 +147,26 @@ o2::ctf::CTFIOSize CTFCoder::encode(VEC& buff, const gsl::span& } /// decode entropy-encoded clusters to standard compact clusters +template template -o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VROF& rofRecVec, VCLUS& cclusVec, VPAT& pattVec, const NoiseMap* noiseMap, const LookUp& clPattLookup) +o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VROF& rofRecVec, VCLUS& cclusVec, VPAT& pattVec, const NoiseMap* noiseMap, const LookUp& clPattLookup) { o2::ctf::CTFIOSize iosize; auto compCl = decodeCompressedClusters(ec, iosize); + const auto& par = DPLAlpideParam::Instance(); + uint32_t nLayers = mDoStaggering ? par.getNLayers() : 1; + if (compCl.header.maxStreams != nLayers) { + throw std::runtime_error(fmt::format("header maxStreams={} is not the same as NStreams={} in {}staggered mode", compCl.header.maxStreams, nLayers, mDoStaggering ? "" : "non-")); + } decompress(compCl, rofRecVec, cclusVec, pattVec, noiseMap, clPattLookup); iosize.rawIn = rofRecVec.size() * sizeof(ROFRecord) + cclusVec.size() * sizeof(CompClusterExt) + pattVec.size() * sizeof(unsigned char); return iosize; } /// decode entropy-encoded clusters to digits +template template -o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VROF& rofRecVec, VDIG& digVec, const NoiseMap* noiseMap, const LookUp& clPattLookup) +o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VROF& rofRecVec, VDIG& digVec, const NoiseMap* noiseMap, const LookUp& clPattLookup) { o2::ctf::CTFIOSize iosize; auto compCl = decodeCompressedClusters(ec, iosize); @@ -158,8 +176,9 @@ o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VROF& rofRecVec, VDIG& } /// decompress compressed clusters to standard compact clusters +template template -void CTFCoder::decompress(const CompressedClusters& compCl, VROF& rofRecVec, VCLUS& cclusVec, VPAT& pattVec, const NoiseMap* noiseMap, const LookUp& clPattLookup) +void CTFCoder::decompress(const CompressedClusters& compCl, VROF& rofRecVec, VCLUS& cclusVec, VPAT& pattVec, const NoiseMap* noiseMap, const LookUp& clPattLookup) { PMatrix pmat{}; RowColBuff firedPixBuff{}, maskedPixBuff{}; @@ -343,8 +362,9 @@ void CTFCoder::decompress(const CompressedClusters& compCl, VROF& rofRecVec, VCL } /// decompress compressed clusters to digits +template template -void CTFCoder::decompress(const CompressedClusters& compCl, VROF& rofRecVec, VDIG& digVec, const NoiseMap* noiseMap, const LookUp& clPattLookup) +void CTFCoder::decompress(const CompressedClusters& compCl, VROF& rofRecVec, VDIG& digVec, const NoiseMap* noiseMap, const LookUp& clPattLookup) { rofRecVec.resize(compCl.header.nROFs); digVec.reserve(compCl.header.nClusters * 2); diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingITS.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingITS.h index 45668ca507280..6110a8492d416 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingITS.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingITS.h @@ -310,6 +310,9 @@ class ChipMappingITS std::vector getOverlapsInfo() const; + ///< Collect all FEEIDs for one layer (lr>=0) or all (lr==-1) + std::vector getLayer2FEEIDs(int lr); + // sub-barrel types, their number, N layers, Max N GBT Links per RU static constexpr int IB = 0, MB = 1, OB = 2, NSubB = 3, NLayers = 7, NLinks = 3; @@ -395,7 +398,7 @@ class ChipMappingITS std::vector mCablePos[NSubB]; ///< table of cables positions in the ActiveLanes mask for each RU type (sequential numbering) std::vector mCableHWFirstChip[NSubB]; ///< 1st chip of module (relative to the 1st chip of the stave) served by each cable - std::array mCablesOnStaveSB = {0}; ///< pattern of cables per stave of sub-barrel + std::array mCablesOnStaveSB = {0}; ///< pattern of cables per stave of sub-barrel std::array, MaxHWCableID[MB] + 1> HWCableHWChip2ChipOnRU_MB; // mapping from HW cable ID / HW chip ID to Chip on RU, 255 means NA std::array, MaxHWCableID[OB] + 1> HWCableHWChip2ChipOnRU_OB; // mapping from HW cable ID / HW chip ID to Chip on RU, 255 means NA diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingMFT.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingMFT.h index 3fa94c2628f3a..eee9bdbb6a4dc 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingMFT.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingMFT.h @@ -266,6 +266,9 @@ class ChipMappingMFT const auto& getModuleMappingData() const { return ModuleMappingData; } + ///< Collect all FEEIDs for one layer (lr>=0) or all (lr==-1) + std::vector getLayer2FEEIDs(int lr); + void print() const; ///< LayerID of each MFT chip diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/PixelReader.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/PixelReader.h index 80ef5ed7abec8..b98abf1d9b2d4 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/PixelReader.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/PixelReader.h @@ -50,11 +50,6 @@ class PixelReader { return nullptr; } - const o2::InteractionRecord& getInteractionRecordHB() const - { - return mInteractionRecordHB; - } - const o2::InteractionRecord& getInteractionRecord() const { return mInteractionRecord; @@ -70,8 +65,7 @@ class PixelReader // protected: // - o2::InteractionRecord mInteractionRecord = {}; // interation record for the trigger - o2::InteractionRecord mInteractionRecordHB = {}; // interation record for the HB + o2::InteractionRecord mInteractionRecord = {}; // interation record for the trigger uint32_t mTrigger = 0; bool mDecodeNextAuto = true; // try to fetch/decode next trigger when getNextChipData does not see any decoded data diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelDecoder.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelDecoder.h index 3a53253da2b42..b10f60c749f7c 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelDecoder.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelDecoder.h @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// 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. // @@ -14,9 +14,11 @@ #ifndef ALICEO2_ITSMFT_RAWPIXELDECODER_H_ #define ALICEO2_ITSMFT_RAWPIXELDECODER_H_ +#include #include #include #include "Framework/Logger.h" +#include "Framework/InputSpec.h" #include "ITSMFTReconstruction/ChipMappingITS.h" #include "ITSMFTReconstruction/ChipMappingMFT.h" #include "DetectorsRaw/HBFUtils.h" @@ -29,7 +31,6 @@ #include "DataFormatsITSMFT/ROFRecord.h" #include "ITSMFTReconstruction/PixelData.h" #include "ITSMFTReconstruction/GBTWord.h" -#include namespace o2 { @@ -91,6 +92,9 @@ class RawPixelDecoder final : public PixelReader void setVerbosity(int v); int getVerbosity() const { return mVerbosity; } + void setInputFilter(std::vector filter) { mInputFilter = std::move(filter); } + const auto& getInputFilter() const noexcept { return mInputFilter; } + void setAlwaysParseTrigger(bool v) { mAlwaysParseTrigger = v; } bool getAlwaysParseTrigger() const { return mAlwaysParseTrigger; } @@ -138,7 +142,7 @@ class RawPixelDecoder final : public PixelReader void reset(); private: - void setupLinks(o2::framework::InputRecord& inputs); + void setupLinks(o2::framework::InputRecord& inputsm); int getRUEntrySW(int ruSW) const { return mRUEntry[ruSW]; } RUDecodeData* getRUDecode(int ruSW) { return &mRUDecodeVec[mRUEntry[ruSW]]; } GBTLink* getGBTLink(int i) { return i < 0 ? nullptr : &mGBTLinks[i]; } @@ -146,6 +150,7 @@ class RawPixelDecoder final : public PixelReader static constexpr uint16_t NORUDECODED = 0xffff; // this must be > than max N RUs + std::vector mInputFilter; // input spec filter std::vector mGBTLinks; // active links pool std::unordered_map mSubsSpec2LinkID; // link subspec to link entry in the pool mapping std::vector mRUDecodeVec; // set of active RUs diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelReader.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelReader.h index 97716059f12d6..ce6582853788d 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelReader.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelReader.h @@ -53,8 +53,8 @@ namespace o2 namespace itsmft { -constexpr int MaxGBTPacketBytes = 8 * 1024; // Max size of GBT packet in bytes (8KB) -constexpr int NCRUPagesPerSuperpage = 256; // Expected max number of CRU pages per superpage +constexpr int MaxGBTPacketBytes = 8 * 1024; // Max size of GBT packet in bytes (8KB) +constexpr int NCRUPagesPerSuperpage = 256; // Expected max number of CRU pages per superpage using RDHUtils = o2::raw::RDHUtils; struct RawDecodingStat { @@ -633,7 +633,6 @@ class RawPixelReader : public PixelReader const auto rdh = reinterpret_cast(link->data.getPtr()); mInteractionRecord = RDHUtils::getTriggerIR(rdh); mTrigger = RDHUtils::getTriggerType(rdh); - mInteractionRecordHB = RDHUtils::getHeartBeatIR(rdh); break; } } @@ -674,7 +673,7 @@ class RawPixelReader : public PixelReader } } if (ruDecData.ruInfo->nCables) { // there are cables with data to decode - decodeAlpideData(ruDecData); // decode Alpide data from the compressed RU Data + decodeAlpideData(ruDecData); // decode Alpide data from the compressed RU Data } return res; } diff --git a/Detectors/ITSMFT/common/reconstruction/src/CTFCoder.cxx b/Detectors/ITSMFT/common/reconstruction/src/CTFCoder.cxx index ec0ee9e3f0f24..4a0c83fd0c859 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/CTFCoder.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/CTFCoder.cxx @@ -14,35 +14,38 @@ /// \brief class for entropy encoding/decoding of ITS/MFT compressmed clusters data #include "ITSMFTReconstruction/CTFCoder.h" -#include "CommonUtils/StringUtils.h" #include -using namespace o2::itsmft; +namespace o2::itsmft +{ ///___________________________________________________________________________________ // Register encoded data in the tree (Fill is not called, will be done by caller) -void CTFCoder::appendToTree(TTree& tree, CTF& ec) +template +void CTFCoder::appendToTree(TTree& tree, CTF& ec, int id) { - ec.appendToTree(tree, mDet.getName()); + ec.appendToTree(tree, id >= 0 ? fmt::format("{}_{}", mDet.getName(), id) : mDet.getName()); } ///___________________________________________________________________________________ // extract and decode data from the tree -void CTFCoder::readFromTree(TTree& tree, int entry, std::vector& rofRecVec, - std::vector& cclusVec, std::vector& pattVec, const NoiseMap* noiseMap, const LookUp& clPattLookup) +template +void CTFCoder::readFromTree(TTree& tree, int entry, int id, std::vector& rofRecVec, + std::vector& cclusVec, std::vector& pattVec, const NoiseMap* noiseMap, const LookUp& clPattLookup) { assert(entry >= 0 && entry < tree.GetEntries()); CTF ec; - ec.readFromTree(tree, mDet.getName(), entry); + ec.readFromTree(tree, id >= 0 ? fmt::format("{}_{}", mDet.getName(), id) : mDet.getName(), entry); decode(ec, rofRecVec, cclusVec, pattVec, noiseMap, clPattLookup); } ///________________________________ -void CTFCoder::compress(CompressedClusters& cc, - const gsl::span& rofRecVec, - const gsl::span& cclusVec, - const gsl::span& pattVec, - const LookUp& clPattLookup, int strobeLength) +template +void CTFCoder::compress(CompressedClusters& cc, + const gsl::span& rofRecVec, + const gsl::span& cclusVec, + const gsl::span& pattVec, + const LookUp& clPattLookup, int strobeLength) { // store in the header the orbit of 1st ROF cc.clear(); @@ -191,11 +194,12 @@ void CTFCoder::compress(CompressedClusters& cc, } ///________________________________ -void CTFCoder::createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBase::OpType op) +template +void CTFCoder::createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBase::OpType op) { const auto ctf = CTF::getImage(bufVec.data()); CompressedClusters cc; // just to get member types -#define MAKECODER(part, slot) createCoder(op, std::get>(ctf.getDictionary(slot, mANSVersion)), int(slot)) +#define MAKECODER(part, slot) createCoder(op, std::get>(ctf.getDictionary(slot, mANSVersion)), int(slot)) // clang-format off MAKECODER(cc.firstChipROF, CTF::BLCfirstChipROF); MAKECODER(cc.bcIncROF, CTF::BLCbcIncROF ); @@ -212,7 +216,8 @@ void CTFCoder::createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBa } ///________________________________ -size_t CTFCoder::estimateCompressedSize(const CompressedClusters& cc) +template +size_t CTFCoder::estimateCompressedSize(const CompressedClusters& cc) { size_t sz = 0; // RS FIXME this is very crude estimate, instead, an empirical values should be used @@ -234,7 +239,8 @@ size_t CTFCoder::estimateCompressedSize(const CompressedClusters& cc) } ///________________________________ -CompressedClusters CTFCoder::decodeCompressedClusters(const CTF::base& ec, o2::ctf::CTFIOSize& iosize) +template +CompressedClusters CTFCoder::decodeCompressedClusters(const CTF::base& ec, o2::ctf::CTFIOSize& iosize) { CompressedClusters cc; cc.header = ec.getHeader(); @@ -256,3 +262,7 @@ CompressedClusters CTFCoder::decodeCompressedClusters(const CTF::base& ec, o2::c // clang-format on return cc; } + +template class CTFCoder; +template class CTFCoder; +} // namespace o2::itsmft diff --git a/Detectors/ITSMFT/common/reconstruction/src/ChipMappingITS.cxx b/Detectors/ITSMFT/common/reconstruction/src/ChipMappingITS.cxx index 7d9733554ef12..f143e4bb23f3d 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/ChipMappingITS.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/ChipMappingITS.cxx @@ -78,14 +78,14 @@ ChipMappingITS::ChipMappingITS() cInfo.moduleSW = 0; cInfo.chipOnModuleSW = i; cInfo.chipOnModuleHW = i; - cInfo.cableHW = i; //1-to-1 mapping - cInfo.cableHWPos = i; //1-to-1 mapping - cInfo.cableSW = i; //1-to-1 mapping - cInfo.chipOnCable = 0; // every chip is master + cInfo.cableHW = i; // 1-to-1 mapping + cInfo.cableHWPos = i; // 1-to-1 mapping + cInfo.cableSW = i; // 1-to-1 mapping + cInfo.chipOnCable = 0; // every chip is master mCableHW2SW[IB][cInfo.cableHW] = cInfo.cableSW; mCableHW2Pos[IB][cInfo.cableHW] = cInfo.cableHWPos; mCablesOnStaveSB[IB] |= 0x1 << cInfo.cableHWPos; // account in lanes pattern - mCableHWFirstChip[IB][i] = 0; // stave and module are the same + mCableHWFirstChip[IB][i] = 0; // stave and module are the same } // [i][j] gives lane id for lowest(i=0) and highest(i=1) 7 chips of HW module (j+1) (1-4 for ML, 1-7 for OL) @@ -289,3 +289,17 @@ std::vector ChipMappingITS::getOverlapsInfo() const } return v; } + +//_____________________________________________________________________________ +std::vector ChipMappingITS::getLayer2FEEIDs(int lr) +{ + std::vector feeIDs; + for (int ilr = (lr >= 0 ? lr : 0); ilr < (lr >= 0 ? lr + 1 : NLayers); ++ilr) { + for (int ist = 0; ist < NStavesOnLr[ilr]; ++ist) { + for (int lnk = 0; lnk < NLinks; ++lnk) { + feeIDs.push_back(composeFEEId(ilr, ist, lnk)); + } + } + } + return feeIDs; +} diff --git a/Detectors/ITSMFT/common/reconstruction/src/ChipMappingMFT.cxx b/Detectors/ITSMFT/common/reconstruction/src/ChipMappingMFT.cxx index 259df62921c8f..de2358469e894 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/ChipMappingMFT.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/ChipMappingMFT.cxx @@ -1753,3 +1753,17 @@ void ChipMappingMFT::print() const ChipMappingData[iChip].chipOnRU); } } + +//_____________________________________________________________________________ +std::vector ChipMappingMFT::getLayer2FEEIDs(int lr) +{ + std::vector feeIDs; + for (int ilr = (lr >= 0 ? lr : 0); ilr < (lr >= 0 ? lr + 1 : NLayers); ++ilr) { + for (int iz = 0; iz < NZonesPerLayer; ++iz) { + for (int lnk = 0; lnk < NLinks; ++lnk) { + feeIDs.push_back(composeFEEId(ilr, iz, lnk)); + } + } + } + return feeIDs; +} diff --git a/Detectors/ITSMFT/common/reconstruction/src/Clusterer.cxx b/Detectors/ITSMFT/common/reconstruction/src/Clusterer.cxx index 3c741321e7780..dcc268a4504a9 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/Clusterer.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/Clusterer.cxx @@ -479,7 +479,7 @@ void Clusterer::print(bool showsTiming) const } else { LOGP(info, "Clusterizer squashes overflow pixels <= {} in row/col", mMaxRowColDiffToMask); for (size_t i{0}; i < mSquashingLayerDepth.size(); ++i) { - LOGP(info, "\tlay:{} separated by {} BC seeking down to {} neighbour ROFs", i, mMaxBCSeparationToSquashLayer[i], mSquashingLayerDepth[i]); + LOGP(info, "\tClusterizer on layer {} separated by {} BC seeking down to {} neighbour ROFs", i, mMaxBCSeparationToSquashLayer[i], mSquashingLayerDepth[i]); } } LOGP(info, "Clusterizer masks overflow pixels separated by < {} BC and <= {} in row/col", mMaxBCSeparationToMask, mMaxRowColDiffToMask); diff --git a/Detectors/ITSMFT/common/reconstruction/src/GBTLink.cxx b/Detectors/ITSMFT/common/reconstruction/src/GBTLink.cxx index af4c8de5caf39..4d336d9adb1ee 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/GBTLink.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/GBTLink.cxx @@ -41,7 +41,7 @@ GBTLink::GBTLink(uint16_t _cru, uint16_t _fee, uint8_t _ep, uint8_t _idInCru, ui /// create string describing the link std::string GBTLink::describe() const { - std::string ss = fmt::format("link cruID:{:#06x}/lID{} feeID:{:#06x}", cruID, int(idInCRU), feeID); + std::string ss = fmt::format("link cruID:{:#06x}/lID{:02} feeID:{:#06x}", cruID, int(idInCRU), feeID); if (lanes) { ss += fmt::format(" lanes {}", std::bitset<28>(lanes).to_string()); } diff --git a/Detectors/ITSMFT/common/reconstruction/src/RawPixelDecoder.cxx b/Detectors/ITSMFT/common/reconstruction/src/RawPixelDecoder.cxx index dc61bea9f406e..7158551e02e20 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/RawPixelDecoder.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/RawPixelDecoder.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// 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. // @@ -40,7 +40,8 @@ RawPixelDecoder::RawPixelDecoder() mTimerDecode.Stop(); mTimerFetchData.Stop(); mSelfName = o2::utils::Str::concat_string(Mapping::getName(), "Decoder"); - DPLRawParser<>::setCheckIncompleteHBF(false); // Disable incomplete HBF checking, see ErrPacketCounterJump check in GBTLink.cxx + DPLRawParser<>::setCheckIncompleteHBF(false); // Disable incomplete HBF checking, see ErrPacketCounterJump check in GBTLink.cxx + mInputFilter = {InputSpec{"filter", ConcreteDataTypeMatcher{Mapping::getOrigin(), o2::header::gDataDescriptionRawData}}}; // by default take all raw data } ///______________________________________________________________ @@ -102,8 +103,7 @@ int RawPixelDecoder::decodeNextTrigger() } #ifdef WITH_OPENMP -#pragma omp parallel for schedule(dynamic) num_threads(mNThreads) reduction(+ \ - : mNChipsFiredROF, mNPixelsFiredROF) +#pragma omp parallel for schedule(dynamic) num_threads(mNThreads) reduction(+ : mNChipsFiredROF, mNPixelsFiredROF) #endif for (int iru = 0; iru < nru; iru++) { auto& ru = mRUDecodeVec[iru]; @@ -186,6 +186,9 @@ bool RawPixelDecoder::doIRMajorityPoll() if (link.statusInTF == GBTLink::DataSeen) { if (link.status == GBTLink::DataSeen || link.status == GBTLink::CachedDataExist) { mIRPoll[link.ir]++; + if (mVerbosity >= GBTLink::Verbosity::VerboseHeaders) { + LOGP(info, "doIRMajorityPoll: {} contributes to poll {}", link.describe(), link.ir.asString()); + } } else if (link.status == GBTLink::StoppedOnEndOfData || link.status == GBTLink::AbortedOnError) { link.statusInTF = GBTLink::StoppedOnEndOfData; if (mVerbosity >= GBTLink::Verbosity::VerboseHeaders) { @@ -195,6 +198,12 @@ bool RawPixelDecoder::doIRMajorityPoll() } } } + if (mNLinksDone == mNLinksInTF) { + if (mVerbosity >= GBTLink::Verbosity::VerboseHeaders) { + LOGP(info, "doIRMajorityPoll: All {} links registered in TF are done", mNLinksInTF); + } + return false; + } int majIR = -1; for (const auto& entIR : mIRPoll) { if (entIR.second > majIR) { @@ -202,16 +211,14 @@ bool RawPixelDecoder::doIRMajorityPoll() mInteractionRecord = entIR.first; } } - mInteractionRecordHB = mInteractionRecord; if (mInteractionRecord.isDummy()) { if (mVerbosity >= GBTLink::Verbosity::VerboseHeaders) { LOG(info) << "doIRMajorityPoll: did not find any valid IR"; } return false; } - mInteractionRecordHB.bc = 0; if (mVerbosity >= GBTLink::Verbosity::VerboseHeaders) { - LOG(info) << "doIRMajorityPoll: " << mInteractionRecordHB.asString() << " majority = " << majIR << " for " << mNLinksInTF << " links seen, LinksDone = " << mNLinksDone; + LOG(info) << "doIRMajorityPoll: " << mInteractionRecord.asString() << " majority = " << majIR << " for " << mNLinksInTF << " links seen, LinksDone = " << mNLinksDone; } return true; } @@ -228,7 +235,14 @@ void RawPixelDecoder::setupLinks(InputRecord& inputs) auto nLinks = mGBTLinks.size(); auto origin = (mUserDataOrigin == o2::header::gDataOriginInvalid) ? mMAP.getOrigin() : mUserDataOrigin; auto datadesc = (mUserDataDescription == o2::header::gDataDescriptionInvalid) ? o2::header::gDataDescriptionRawData : mUserDataDescription; - std::vector filter{InputSpec{"filter", ConcreteDataTypeMatcher{origin, datadesc}}}; + if (mUserDataDescription != o2::header::gDataDescriptionInvalid) { // overwrite data filter origin&descriptions with user defined ones if possible + for (auto& filt : mInputFilter) { + if (std::holds_alternative(filt.matcher)) { + std::get(filt.matcher).origin = origin; + std::get(filt.matcher).description = datadesc; + } + } + } // if we see requested data type input with 0xDEADBEEF subspec and 0 payload this means that the "delayed message" // mechanism created it in absence of real data from upstream. Processor should send empty output to not block the workflow @@ -251,28 +265,31 @@ void RawPixelDecoder::setupLinks(InputRecord& inputs) contDeadBeef = 0; // if good data, reset the counter } mROFRampUpStage = false; - DPLRawParser parser(inputs, filter, o2::conf::VerbosityConfig::Instance().rawParserSeverity); + DPLRawParser parser(inputs, mInputFilter, o2::conf::VerbosityConfig::Instance().rawParserSeverity); parser.setMaxFailureMessages(o2::conf::VerbosityConfig::Instance().maxWarnRawParser); static size_t cntParserFailures = 0; parser.setExtFailureCounter(&cntParserFailures); uint32_t currSSpec = 0xffffffff; // dummy starting subspec int linksAdded = 0; + uint16_t lr, dummy; // extraxted info from FEEId for (auto it = parser.begin(); it != parser.end(); ++it) { auto const* dh = it.o2DataHeader(); auto& lnkref = mSubsSpec2LinkID[dh->subSpecification]; const auto& rdh = *reinterpret_cast(it.raw()); // RSTODO this is a hack in absence of generic header getter + const auto feeID = RDHUtils::getFEEID(rdh); + mMAP.expandFEEId(feeID, lr, dummy, dummy); if (lnkref.entry == -1) { // new link needs to be added lnkref.entry = int(mGBTLinks.size()); - auto& lnk = mGBTLinks.emplace_back(RDHUtils::getCRUID(rdh), RDHUtils::getFEEID(rdh), RDHUtils::getEndPointID(rdh), RDHUtils::getLinkID(rdh), lnkref.entry); + auto& lnk = mGBTLinks.emplace_back(RDHUtils::getCRUID(rdh), feeID, RDHUtils::getEndPointID(rdh), RDHUtils::getLinkID(rdh), lnkref.entry); lnk.subSpec = dh->subSpecification; lnk.wordLength = (lnk.expectPadding = (RDHUtils::getDataFormat(rdh) == 0)) ? o2::itsmft::GBTPaddedWordLength : o2::itsmft::GBTWordLength; - getCreateRUDecode(mMAP.FEEId2RUSW(RDHUtils::getFEEID(rdh))); // make sure there is a RU for this link + getCreateRUDecode(mMAP.FEEId2RUSW(feeID)); // make sure there is a RU for this link lnk.verbosity = GBTLink::Verbosity(mVerbosity); lnk.alwaysParseTrigger = mAlwaysParseTrigger; if (mVerbosity >= GBTLink::Verbosity::VerboseHeaders) { - LOG(info) << mSelfName << " registered new link " << lnk.describe() << " RUSW=" << int(mMAP.FEEId2RUSW(lnk.feeID)); + LOG(info) << mSelfName << " registered new " << lnk.describe() << " RUSW=" << int(mMAP.FEEId2RUSW(lnk.feeID)); } linksAdded++; } @@ -330,7 +347,7 @@ void RawPixelDecoder::setupLinks(InputRecord& inputs) mMAP.expandFEEId(link.feeID, lr, ruOnLr, linkInRU); if (newLinkAdded) { if (mVerbosity >= GBTLink::Verbosity::VerboseHeaders) { - LOG(info) << mSelfName << " Attaching " << link.describe() << " to RU#" << int(mMAP.FEEId2RUSW(link.feeID)) << " (stave " << ruOnLr << " of layer " << lr << ')'; + LOGP(info, "{} Attaching {} to RU#{:02} (stave {:02} of layer {})", mSelfName, link.describe(), int(mMAP.FEEId2RUSW(link.feeID)), ruOnLr, lr); } } link.idInRU = linkInRU; diff --git a/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/DigiParams.h b/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/DigiParams.h index fa75a65728675..f4482c651b090 100644 --- a/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/DigiParams.h +++ b/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/DigiParams.h @@ -19,7 +19,7 @@ #include #include #include "ITSMFTSimulation/AlpideSignalTrapezoid.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" //////////////////////////////////////////////////////////// // // diff --git a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterReaderSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterReaderSpec.h index 82e3890de7475..9d58b6fde16c1 100644 --- a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterReaderSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterReaderSpec.h @@ -14,8 +14,10 @@ #ifndef O2_ITSMFT_CLUSTERREADER #define O2_ITSMFT_CLUSTERREADER -#include "TFile.h" -#include "TTree.h" +#include + +#include +#include #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" @@ -23,7 +25,7 @@ #include "DataFormatsITSMFT/CompCluster.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DataFormatsITSMFT/ROFRecord.h" #include "DetectorsCommonDataFormats/DetID.h" @@ -38,10 +40,9 @@ class ClusterReader : public Task public: static constexpr o2::detectors::DetID ID{N == o2::detectors::DetID::ITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT}; static constexpr o2::header::DataOrigin Origin{(N == o2::detectors::DetID::ITS) ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; - static constexpr int NLayers{o2::itsmft::DPLAlpideParam::supportsStaggering() ? o2::itsmft::DPLAlpideParam::getNLayers() : 1}; ClusterReader() = delete; - ClusterReader(bool useMC, bool usePatterns = true, bool triggers = true); + ClusterReader(bool useMC = true, bool doStag = false, bool usePatterns = true, bool triggers = true); ~ClusterReader() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -52,18 +53,19 @@ class ClusterReader : public Task void setBranchAddress(const std::string& base, Ptr& addr, int layer); std::string getBranchName(const std::string& base, int index) const; - std::array*, NLayers> mClusROFRec; - std::array*, NLayers> mClusterCompArray; - std::array*, NLayers> mPatternsArray; - std::array*, NLayers> mClusterMCTruth; - std::array*, NLayers> mClusMC2ROFs; + std::vector*> mClusROFRec{nullptr}; + std::vector*> mClusterCompArray{nullptr}; + std::vector*> mPatternsArray{nullptr}; + std::vector*> mClusterMCTruth{nullptr}; std::unique_ptr mFile; std::unique_ptr mTree; - bool mUseMC = true; // use MC truth - bool mUsePatterns = true; // send patterns - bool mTriggerOut = true; // send dummy triggers vector + int mLayers = 1; + bool mUseMC = true; // use MC truth + bool mDoStaggering = false; // read staggered data + bool mUsePatterns = true; // send patterns + bool mTriggerOut = true; // send dummy triggers vector std::string mDetName; std::string mDetNameLC; @@ -73,27 +75,26 @@ class ClusterReader : public Task std::string mClusterPattBranchName = "ClusterPatt"; std::string mClusterCompBranchName = "ClusterComp"; std::string mClustMCTruthBranchName = "ClusterMCTruth"; - std::string mClustMC2ROFBranchName = "ClustersMC2ROF"; }; class ITSClusterReader : public ClusterReader { public: - ITSClusterReader(bool useMC = true, bool usePatterns = true, bool triggerOut = true) - : ClusterReader(useMC, usePatterns, triggerOut) {} + ITSClusterReader(bool useMC = true, bool doStag = false, bool usePatterns = true, bool triggerOut = true) + : ClusterReader(useMC, doStag, usePatterns, triggerOut) {} }; class MFTClusterReader : public ClusterReader { public: - MFTClusterReader(bool useMC = true, bool usePatterns = true, bool triggerOut = true) - : ClusterReader(useMC, usePatterns, triggerOut) {} + MFTClusterReader(bool useMC = true, bool doStag = false, bool usePatterns = true, bool triggerOut = true) + : ClusterReader(useMC, doStag, usePatterns, triggerOut) {} }; /// create a processor spec /// read ITS/MFT cluster data from a root file -framework::DataProcessorSpec getITSClusterReaderSpec(bool useMC = true, bool usePatterns = true, bool useTriggers = true); -framework::DataProcessorSpec getMFTClusterReaderSpec(bool useMC = true, bool usePatterns = true, bool useTriggers = true); +framework::DataProcessorSpec getITSClusterReaderSpec(bool useMC = true, bool doStag = false, bool usePatterns = true, bool useTriggers = true); +framework::DataProcessorSpec getMFTClusterReaderSpec(bool useMC = true, bool doStag = false, bool usePatterns = true, bool useTriggers = true); } // namespace o2::itsmft diff --git a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterWriterSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterWriterSpec.h index 5ae371e7e09c4..6607c05fb141d 100644 --- a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterWriterSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterWriterSpec.h @@ -20,9 +20,9 @@ namespace o2::itsmft { template -framework::DataProcessorSpec getClusterWriterSpec(bool useMC); -framework::DataProcessorSpec getITSClusterWriterSpec(bool useMC); -framework::DataProcessorSpec getMFTClusterWriterSpec(bool useMC); +framework::DataProcessorSpec getClusterWriterSpec(bool useMC, bool doStag); +framework::DataProcessorSpec getITSClusterWriterSpec(bool useMC, bool doStag); +framework::DataProcessorSpec getMFTClusterWriterSpec(bool useMC, bool doStag); } // namespace o2::itsmft diff --git a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClustererSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClustererSpec.h index b6ebc282c2a27..5535ecb42d645 100644 --- a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClustererSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClustererSpec.h @@ -18,7 +18,6 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" #include "ITSMFTReconstruction/Clusterer.h" -#include "ITSMFTBase/DPLAlpideParam.h" using namespace o2::framework; @@ -30,10 +29,9 @@ class ClustererDPL : public Task { static constexpr o2::detectors::DetID ID{N == o2::detectors::DetID::ITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT}; static constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; - static constexpr int NLayers{o2::itsmft::DPLAlpideParam::supportsStaggering() ? o2::itsmft::DPLAlpideParam::getNLayers() : 1}; public: - ClustererDPL(std::shared_ptr gr, bool useMC) : mGGCCDBRequest(gr), mUseMC(useMC) {} + ClustererDPL(std::shared_ptr gr, bool useMC, bool doStag); ~ClustererDPL() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -48,12 +46,13 @@ class ClustererDPL : public Task int mNThreads = 1; std::unique_ptr mClusterer = nullptr; std::shared_ptr mGGCCDBRequest; - int mLayers{NLayers}; + bool mDoStaggering = false; + int mLayers = 1; std::vector mFilter; }; -framework::DataProcessorSpec getITSClustererSpec(bool useMC); -framework::DataProcessorSpec getMFTClustererSpec(bool useMC); +framework::DataProcessorSpec getITSClustererSpec(bool useMC, bool doStag); +framework::DataProcessorSpec getMFTClustererSpec(bool useMC, bool doStag); } // namespace o2::itsmft diff --git a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/DigitReaderSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/DigitReaderSpec.h index 348ba76468144..2954c27af886e 100644 --- a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/DigitReaderSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/DigitReaderSpec.h @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// 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. // @@ -14,9 +14,12 @@ #ifndef O2_ITSMFT_DIGITREADER #define O2_ITSMFT_DIGITREADER -#include "TFile.h" -#include "TTree.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include + +#include +#include + +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DataFormatsITSMFT/Digit.h" #include "DataFormatsITSMFT/GBTCalibData.h" #include "DataFormatsITSMFT/ROFRecord.h" @@ -41,11 +44,9 @@ class DigitReader : public Task public: static constexpr o2::detectors::DetID ID{N == o2::detectors::DetID::ITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT}; static constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; - static constexpr int NLayers{o2::itsmft::DPLAlpideParam::getNLayers()}; - static constexpr int RLayers = o2::itsmft::DPLAlpideParam::supportsStaggering() ? NLayers : 1; DigitReader() = delete; - DigitReader(bool useMC, bool useCalib, bool triggerOut); + DigitReader(bool useMC, bool doStag, bool useCalib, bool triggerOut); ~DigitReader() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -56,22 +57,23 @@ class DigitReader : public Task void setBranchAddress(const std::string& base, Ptr& addr, int layer = -1); std::string getBranchName(const std::string& base, int index); - std::array*, NLayers> mDigits; + std::vector*> mDigits{nullptr}; std::vector mCalib, *mCalibPtr = &mCalib; - std::array*, NLayers> mDigROFRec; - std::array*, NLayers> mDigMC2ROFs; - std::array, NLayers> mConstLabels; - std::array mPLabels; + std::vector*> mDigROFRec{nullptr}; + std::vector> mConstLabels{}; + std::vector mPLabels{nullptr}; std::unique_ptr mFile; std::unique_ptr mTree; - bool mUseMC = true; // use MC truth - bool mUseCalib = true; // send calib data - bool mTriggerOut = true; // send dummy triggers vector - bool mUseIRFrames = false; // selected IRFrames modes + bool mUseMC = true; // use MC truth + bool mDoStaggering = false; // read staggered data + bool mUseCalib = true; // send calib data + bool mTriggerOut = true; // send dummy triggers vector + bool mUseIRFrames = false; // selected IRFrames modes int mROFBiasInBC = 0; int mROFLengthInBC = 0; int mNRUs = 0; + int mLayers = 1; std::string mDetName; std::string mDetNameLC; std::string mFileName; @@ -81,27 +83,26 @@ class DigitReader : public Task std::string mCalibBranchName = "Calib"; std::string mDigitMCTruthBranchName = "DigitMCTruth"; - std::string mDigitMC2ROFBranchName = "DigitMC2ROF"; }; class ITSDigitReader : public DigitReader { public: - ITSDigitReader(bool useMC = true, bool useCalib = false, bool useTriggers = true) - : DigitReader(useMC, useCalib, useTriggers) {} + ITSDigitReader(bool useMC = true, bool doStag = false, bool useCalib = false, bool useTriggers = true) + : DigitReader(useMC, doStag, useCalib, useTriggers) {} }; class MFTDigitReader : public DigitReader { public: - MFTDigitReader(bool useMC = true, bool useCalib = false, bool useTriggers = true) - : DigitReader(useMC, useCalib, useTriggers) {} + MFTDigitReader(bool useMC = true, bool doStag = false, bool useCalib = false, bool useTriggers = true) + : DigitReader(useMC, doStag, useCalib, useTriggers) {} }; /// create a processor spec /// read ITS/MFT Digit data from a root file -framework::DataProcessorSpec getITSDigitReaderSpec(bool useMC = true, bool useCalib = false, bool useTriggers = true, std::string defname = "o2_itsdigits.root"); -framework::DataProcessorSpec getMFTDigitReaderSpec(bool useMC = true, bool useCalib = false, bool useTriggers = true, std::string defname = "o2_mftdigits.root"); +framework::DataProcessorSpec getITSDigitReaderSpec(bool useMC = true, bool doStag = false, bool useCalib = false, bool useTriggers = true, std::string defname = "itsdigits.root"); +framework::DataProcessorSpec getMFTDigitReaderSpec(bool useMC = true, bool doStag = false, bool useCalib = false, bool useTriggers = true, std::string defname = "mftdigits.root"); } // namespace itsmft } // namespace o2 diff --git a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/DigitWriterSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/DigitWriterSpec.h index 7bef1643ddcbb..6fde609f1ccb5 100644 --- a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/DigitWriterSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/DigitWriterSpec.h @@ -19,8 +19,8 @@ namespace o2 namespace itsmft { -o2::framework::DataProcessorSpec getITSDigitWriterSpec(bool mctruth = true, bool dec = false, bool calib = false); -o2::framework::DataProcessorSpec getMFTDigitWriterSpec(bool mctruth = true, bool dec = false, bool calib = false); +o2::framework::DataProcessorSpec getITSDigitWriterSpec(bool mctruth = true, bool doStag = false, bool dec = false, bool calib = false); +o2::framework::DataProcessorSpec getMFTDigitWriterSpec(bool mctruth = true, bool doStag = false, bool dec = false, bool calib = false); } // end namespace itsmft } // end namespace o2 diff --git a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyDecoderSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyDecoderSpec.h index a64f2bf8c063c..6862e96c17afe 100644 --- a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyDecoderSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyDecoderSpec.h @@ -29,38 +29,39 @@ namespace o2 namespace itsmft { +template class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(o2::header::DataOrigin orig, int verbosity, bool getDigits = false, const std::string& ctfdictOpt = "none"); + EntropyDecoderSpec(int verbosity, bool doStag, bool getDigits = false, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void init(o2::framework::InitContext& ic) final; void run(o2::framework::ProcessingContext& pc) final; void endOfStream(o2::framework::EndOfStreamContext& ec) final; void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) final; - static auto getName(o2::header::DataOrigin orig) { return std::string{orig == o2::header::gDataOriginITS ? ITSDeviceName : MFTDeviceName}; } + static std::string getBinding(const std::string& name, int spec); + static constexpr o2::detectors::DetID ID{N == o2::detectors::DetID::ITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT}; + static constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; private: void updateTimeDependentParams(o2::framework::ProcessingContext& pc); - static constexpr std::string_view ITSDeviceName = "its-entropy-decoder"; - static constexpr std::string_view MFTDeviceName = "mft-entropy-decoder"; - o2::header::DataOrigin mOrigin = o2::header::gDataOriginInvalid; - o2::itsmft::CTFCoder mCTFCoder; + o2::itsmft::CTFCoder mCTFCoder; const NoiseMap* mNoiseMap = nullptr; LookUp mPattIdConverter; + bool mDoStaggering{false}; bool mGetDigits{false}; bool mMaskNoise{false}; bool mUseClusterDictionary{true}; - std::string mDetPrefix{}; std::string mCTFDictPath{}; TStopwatch mTimer; }; /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(o2::header::DataOrigin orig, int verbosity, bool getDigits, unsigned int sspec, const std::string& ctfdictOpt); +framework::DataProcessorSpec getITSEntropyDecoderSpec(int verbosity, bool doStag, bool getDigits, unsigned int sspec, const std::string& ctfdictOpt); +framework::DataProcessorSpec getMFTEntropyDecoderSpec(int verbosity, bool doStag, bool getDigits, unsigned int sspec, const std::string& ctfdictOpt); } // namespace itsmft } // namespace o2 diff --git a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyEncoderSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyEncoderSpec.h index 588cae6339489..597c0ca63f489 100644 --- a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyEncoderSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyEncoderSpec.h @@ -27,10 +27,12 @@ namespace o2 namespace itsmft { +template class EntropyEncoderSpec : public o2::framework::Task { + public: - EntropyEncoderSpec(o2::header::DataOrigin orig, bool selIR, const std::string& ctfdictOpt = "none"); + EntropyEncoderSpec(bool doStag, bool selIR, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -38,17 +40,21 @@ class EntropyEncoderSpec : public o2::framework::Task void updateTimeDependentParams(o2::framework::ProcessingContext& pc); void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) final; + static std::string getBinding(const std::string& name, int spec); + static constexpr o2::detectors::DetID ID{N == o2::detectors::DetID::ITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT}; + static constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; + private: - o2::header::DataOrigin mOrigin = o2::header::gDataOriginInvalid; - o2::itsmft::CTFCoder mCTFCoder; + o2::itsmft::CTFCoder mCTFCoder; LookUp mPattIdConverter; - int mStrobeLength = 0; bool mSelIR = false; + bool mDoStaggering = false; TStopwatch mTimer; }; /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(o2::header::DataOrigin orig, bool selIR = false, const std::string& ctfdictOpt = "none"); +framework::DataProcessorSpec getITSEntropyEncoderSpec(bool doStag = false, bool selIR = false, const std::string& ctfdictOpt = "none"); +framework::DataProcessorSpec getMFTEntropyEncoderSpec(bool doStag = false, bool selIR = false, const std::string& ctfdictOpt = "none"); } // namespace itsmft } // namespace o2 diff --git a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/STFDecoderSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/STFDecoderSpec.h index a6876c456842d..29b9f75bcbc4e 100644 --- a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/STFDecoderSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/STFDecoderSpec.h @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// 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. // @@ -16,12 +16,14 @@ #ifndef O2_ITSMFT_STFDECODER_ #define O2_ITSMFT_STFDECODER_ +#include +#include +#include #include #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" -#include -#include -#include +#include "DataFormatsITSMFT/DPLAlpideParam.h" +#include "DataFormatsITSMFT/ROFRecord.h" #include "ITSMFTReconstruction/ChipMappingITS.h" #include "ITSMFTReconstruction/ChipMappingMFT.h" #include "ITSMFTReconstruction/RawPixelDecoder.h" @@ -44,6 +46,7 @@ struct STFDecoderInp { bool doDigits = false; bool doCalib = false; bool doSquashing = false; + bool doStaggering = false; bool askSTFDist = true; bool allowReporting = true; bool verifyDecoder = false; @@ -55,6 +58,8 @@ struct STFDecoderInp { template class STFDecoder : public Task { + using AlpideParam = DPLAlpideParam; + public: STFDecoder(const STFDecoderInp& inp, std::shared_ptr gr); STFDecoder() = default; @@ -70,11 +75,14 @@ class STFDecoder : public Task void finalize(); void reset(); std::unique_ptr setupClusterer(const std::string& dictName); + void ensureContinuousROF(const std::vector& in, std::vector& out, int lr, int nROFsTF, const char* name); + TStopwatch mTimer; bool mDoClusters = false; bool mDoPatterns = false; bool mDoDigits = false; bool mDoCalibData = false; + bool mDoStaggering = false; bool mUnmutExtraLanes = false; bool mFinalizeDone = false; bool mAllowReporting = true; @@ -85,18 +93,20 @@ class STFDecoder : public Task int mDumpOnError = 0; int mNThreads = 1; int mVerbosity = 0; + int mLayers = 1; long mROFErrRepIntervalMS = 0; size_t mTFCounter = 0; - size_t mEstNDig = 0; - size_t mEstNClus = 0; - size_t mEstNClusPatt = 0; - size_t mEstNCalib = 0; - size_t mEstNROF = 0; + uint32_t mFirstTFOrbit = 0; + o2::InteractionRecord mFirstIR; + std::vector mEstNDig{0}; + std::vector mEstNClus{0}; + std::vector mEstNClusPatt{0}; + std::vector mEstNCalib{0}; size_t mMaxRawDumpsSize = 0; size_t mRawDumpedSize = 0; std::string mInputSpec; std::string mSelfName; - std::unique_ptr> mDecoder; + std::vector>> mDecoder; std::unique_ptr mClusterer; std::shared_ptr mGGCCDBRequest; }; diff --git a/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx b/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx index bc6418a077810..bd24c9d2591d5 100644 --- a/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx @@ -20,7 +20,7 @@ #include "Framework/ConfigParamRegistry.h" #include "Framework/Logger.h" #include "ITSMFTWorkflow/ClusterReaderSpec.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DataFormatsITSMFT/PhysTrigger.h" #include "CommonUtils/NameConf.h" @@ -33,15 +33,16 @@ namespace itsmft { template -ClusterReader::ClusterReader(bool useMC, bool usePatterns, bool triggerOut) : mUseMC(useMC), mUsePatterns(usePatterns), mTriggerOut(triggerOut), mDetName(Origin.as()), mDetNameLC(mDetName) +ClusterReader::ClusterReader(bool useMC, bool doStag, bool usePatterns, bool triggerOut) : mUseMC(useMC), mUsePatterns(usePatterns), mTriggerOut(triggerOut), mDetName(Origin.as()), mDetNameLC(mDetName) { std::transform(mDetNameLC.begin(), mDetNameLC.end(), mDetNameLC.begin(), ::tolower); - - mClusROFRec.fill(nullptr); - mClusterCompArray.fill(nullptr); - mPatternsArray.fill(nullptr); - mClusterMCTruth.fill(nullptr); - mClusMC2ROFs.fill(nullptr); + if (doStag) { + mLayers = DPLAlpideParam::getNLayers(); + mClusROFRec.resize(mLayers, nullptr); + mClusterCompArray.resize(mLayers, nullptr); + mPatternsArray.resize(mLayers, nullptr); + mClusterMCTruth.resize(mLayers, nullptr); + } } template @@ -59,8 +60,8 @@ void ClusterReader::run(ProcessingContext& pc) assert(ent < mTree->GetEntries()); // this should not happen mTree->GetEntry(ent); - for (uint32_t iLayer = 0; iLayer < NLayers; ++iLayer) { - LOG(info) << mDetName << "ClusterReader:" << iLayer << " pushes " << mClusROFRec[iLayer]->size() << " ROFRecords, " << mClusterCompArray[iLayer]->size() << " compact clusters at entry " << ent; + for (uint32_t iLayer = 0; iLayer < mLayers; ++iLayer) { + LOG(info) << mDetName << "ClusterReader" << (mDoStaggering ? std::format(" on layer {}", iLayer) : "") << " pushes " << mClusROFRec[iLayer]->size() << " ROFRecords, " << mClusterCompArray[iLayer]->size() << " compact clusters at entry " << ent; pc.outputs().snapshot(Output{Origin, "CLUSTERSROF", iLayer}, *mClusROFRec[iLayer]); pc.outputs().snapshot(Output{Origin, "COMPCLUSTERS", iLayer}, *mClusterCompArray[iLayer]); if (mUsePatterns) { @@ -68,7 +69,6 @@ void ClusterReader::run(ProcessingContext& pc) } if (mUseMC) { pc.outputs().snapshot(Output{Origin, "CLUSTERSMCTR", iLayer}, *mClusterMCTruth[iLayer]); - pc.outputs().snapshot(Output{Origin, "CLUSTERSMC2ROF", iLayer}, *mClusMC2ROFs[iLayer]); } } if (mTriggerOut) { @@ -90,17 +90,15 @@ void ClusterReader::connectTree(const std::string& filename) mTree.reset((TTree*)mFile->Get(mClusTreeName.c_str())); assert(mTree); - for (uint32_t iLayer = 0; iLayer < NLayers; ++iLayer) { + for (uint32_t iLayer = 0; iLayer < mLayers; ++iLayer) { setBranchAddress(mClusROFBranchName, mClusROFRec[iLayer], iLayer); setBranchAddress(mClusterCompBranchName, mClusterCompArray[iLayer], iLayer); if (mUsePatterns) { setBranchAddress(mClusterPattBranchName, mPatternsArray[iLayer], iLayer); } if (mUseMC) { - if (mTree->GetBranch(getBranchName(mClustMCTruthBranchName, iLayer).c_str()) && - mTree->GetBranch(getBranchName(mClustMC2ROFBranchName, iLayer).c_str())) { + if (mTree->GetBranch(getBranchName(mClustMCTruthBranchName, iLayer).c_str())) { setBranchAddress(mClustMCTruthBranchName, mClusterMCTruth[iLayer], iLayer); - setBranchAddress(mClustMC2ROFBranchName, mClusMC2ROFs[iLayer], iLayer); } else { LOG(info) << "MC-truth is missing"; mUseMC = false; @@ -113,7 +111,7 @@ void ClusterReader::connectTree(const std::string& filename) template std::string ClusterReader::getBranchName(const std::string& base, int index) const { - if constexpr (o2::itsmft::DPLAlpideParam::supportsStaggering()) { + if (mDoStaggering) { return mDetName + base + "_" + std::to_string(index); } return mDetName + base; @@ -132,10 +130,10 @@ void ClusterReader::setBranchAddress(const std::string& base, Ptr& addr, int namespace { template -std::vector makeOutChannels(o2::header::DataOrigin detOrig, bool mctruth, bool usePatterns, bool triggerOut) +std::vector makeOutChannels(o2::header::DataOrigin detOrig, bool mctruth, bool doStag, bool usePatterns, bool triggerOut) { std::vector outputs; - for (uint32_t iLayer = 0; iLayer < ((o2::itsmft::DPLAlpideParam::supportsStaggering()) ? o2::itsmft::DPLAlpideParam::getNLayers() : 1); ++iLayer) { + for (uint32_t iLayer = 0; iLayer < ((doStag) ? o2::itsmft::DPLAlpideParam::getNLayers() : 1); ++iLayer) { outputs.emplace_back(detOrig, "CLUSTERSROF", iLayer, Lifetime::Timeframe); outputs.emplace_back(detOrig, "COMPCLUSTERS", iLayer, Lifetime::Timeframe); if (usePatterns) { @@ -143,7 +141,6 @@ std::vector makeOutChannels(o2::header::DataOrigin detOrig, bool mct } if (mctruth) { outputs.emplace_back(detOrig, "CLUSTERSMCTR", iLayer, Lifetime::Timeframe); - outputs.emplace_back(detOrig, "CLUSTERSMC2ROF", iLayer, Lifetime::Timeframe); } } if (triggerOut) { @@ -153,25 +150,25 @@ std::vector makeOutChannels(o2::header::DataOrigin detOrig, bool mct } } // namespace -DataProcessorSpec getITSClusterReaderSpec(bool useMC, bool usePatterns, bool triggerOut) +DataProcessorSpec getITSClusterReaderSpec(bool useMC, bool doStag, bool usePatterns, bool triggerOut) { return DataProcessorSpec{ .name = "its-cluster-reader", .inputs = Inputs{}, - .outputs = makeOutChannels("ITS", useMC, usePatterns, triggerOut), - .algorithm = AlgorithmSpec{adaptFromTask(useMC, usePatterns, triggerOut)}, + .outputs = makeOutChannels("ITS", useMC, doStag, usePatterns, triggerOut), + .algorithm = AlgorithmSpec{adaptFromTask(useMC, doStag, usePatterns, triggerOut)}, .options = Options{ {"its-cluster-infile", VariantType::String, "o2clus_its.root", {"Name of the input cluster file"}}, {"input-dir", VariantType::String, "none", {"Input directory"}}}}; } -DataProcessorSpec getMFTClusterReaderSpec(bool useMC, bool usePatterns, bool triggerOut) +DataProcessorSpec getMFTClusterReaderSpec(bool useMC, bool doStag, bool usePatterns, bool triggerOut) { return DataProcessorSpec{ .name = "mft-cluster-reader", .inputs = Inputs{}, - .outputs = makeOutChannels("MFT", useMC, usePatterns, triggerOut), - .algorithm = AlgorithmSpec{adaptFromTask(useMC, usePatterns, triggerOut)}, + .outputs = makeOutChannels("MFT", useMC, doStag, usePatterns, triggerOut), + .algorithm = AlgorithmSpec{adaptFromTask(useMC, doStag, usePatterns, triggerOut)}, .options = Options{ {"mft-cluster-infile", VariantType::String, "mftclusters.root", {"Name of the input cluster file"}}, {"input-dir", VariantType::String, "none", {"Input directory"}}}}; diff --git a/Detectors/ITSMFT/common/workflow/src/ClusterWriterSpec.cxx b/Detectors/ITSMFT/common/workflow/src/ClusterWriterSpec.cxx index c1900c346133b..e1857cbf2f775 100644 --- a/Detectors/ITSMFT/common/workflow/src/ClusterWriterSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/ClusterWriterSpec.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// 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. // @@ -18,7 +18,7 @@ #include #include "Framework/ConcreteDataMatcher.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "ITSMFTWorkflow/ClusterWriterSpec.h" #include "DPLUtils/MakeRootTreeWriterSpec.h" #include "DataFormatsITSMFT/CompCluster.h" @@ -37,71 +37,78 @@ using CompClusType = std::vector; using PatternsType = std::vector; using ROFrameRType = std::vector; using LabelsType = o2::dataformats::MCTruthContainer; -using ROFRecLblT = std::vector; using namespace o2::header; template -DataProcessorSpec getClusterWriterSpec(bool useMC) +DataProcessorSpec getClusterWriterSpec(bool useMC, bool doStag) { static constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; - constexpr int NLayers = (DPLAlpideParam::supportsStaggering()) ? DPLAlpideParam::getNLayers() : 1; + const int nLayers = (doStag) ? DPLAlpideParam::getNLayers() : 1; const auto detName = Origin.as(); // Spectators for logging - auto compClusterSizes = std::make_shared>(); + auto compClusterSizes = std::make_shared>(nLayers, 0); auto compClustersSizeGetter = [compClusterSizes](CompClusType const& compClusters, DataRef const& ref) { auto const* dh = DataRefUtils::getHeader(ref); (*compClusterSizes)[dh->subSpecification] = compClusters.size(); }; - auto logger = [detName, compClusterSizes](std::vector const& rofs, DataRef const& ref) { + auto logger = [detName, compClusterSizes, doStag](std::vector const& rofs, DataRef const& ref) { auto const* dh = DataRefUtils::getHeader(ref); const auto i = dh->subSpecification; - LOG(info) << detName << "ClusterWriter:" << i << " pulled " << (*compClusterSizes)[i] << " clusters, in " << rofs.size() << " RO frames"; + LOG(info) << detName << "ClusterWriter" << ((doStag) ? std::format(" on layer {}", i) : "") + << " pulled " << (*compClusterSizes)[i] << " clusters, in " << rofs.size() << " RO frames"; }; auto getIndex = [](DataRef const& ref) -> size_t { auto const* dh = DataRefUtils::getHeader(ref); return static_cast(dh->subSpecification); }; - auto getName = [](std::string base, size_t index) -> std::string { - if constexpr (DPLAlpideParam::supportsStaggering()) { + auto getName = [doStag](std::string base, size_t index) -> std::string { + if (doStag) { return base += "_" + std::to_string(index); } return base; }; auto detNameLC = detName; std::transform(detNameLC.begin(), detNameLC.end(), detNameLC.begin(), [](unsigned char c) { return std::tolower(c); }); + std::vector vecInpSpecClus, vecInpSpecPatt, vecInpSpecROF, vecInpSpecLbl; + vecInpSpecClus.reserve(nLayers); + vecInpSpecPatt.reserve(nLayers); + vecInpSpecROF.reserve(nLayers); + vecInpSpecLbl.reserve(nLayers); + for (int iLayer = 0; iLayer < nLayers; iLayer++) { + vecInpSpecClus.emplace_back(getName("compclus", iLayer), Origin, "COMPCLUSTERS", iLayer); + vecInpSpecPatt.emplace_back(getName("patterns", iLayer), Origin, "PATTERNS", iLayer); + vecInpSpecROF.emplace_back(getName("ROframes", iLayer), Origin, "CLUSTERSROF", iLayer); + vecInpSpecLbl.emplace_back(getName("labels", iLayer), Origin, "CLUSTERSMCTR", iLayer); + } + return MakeRootTreeWriterSpec(std::format("{}-cluster-writer", detNameLC).c_str(), (o2::detectors::DetID::ITS == N) ? "o2clus_its.root" : "mftclusters.root", MakeRootTreeWriterSpec::TreeAttributes{.name = "o2sim", .title = std::format("Tree with {} clusters", detName)}, - BranchDefinition{InputSpec{"compclus", ConcreteDataTypeMatcher{Origin, "COMPCLUSTERS"}}, + BranchDefinition{vecInpSpecClus, (detName + "ClusterComp").c_str(), "compact-cluster-branch", - NLayers, + nLayers, compClustersSizeGetter, getIndex, getName}, - BranchDefinition{InputSpec{"patterns", ConcreteDataTypeMatcher{Origin, "PATTERNS"}}, + BranchDefinition{vecInpSpecPatt, (detName + "ClusterPatt").c_str(), "cluster-pattern-branch", - NLayers, + nLayers, getIndex, getName}, - BranchDefinition{InputSpec{"ROframes", ConcreteDataTypeMatcher{Origin, "CLUSTERSROF"}}, + BranchDefinition{vecInpSpecROF, (detName + "ClustersROF").c_str(), "cluster-rof-branch", - NLayers, + nLayers, logger, getIndex, getName}, - BranchDefinition{InputSpec{"labels", ConcreteDataTypeMatcher{Origin, "CLUSTERSMCTR"}}, + BranchDefinition{vecInpSpecLbl, (detName + "ClusterMCTruth").c_str(), "cluster-label-branch", - (useMC ? NLayers : 0), - getIndex, - getName}, - BranchDefinition{InputSpec{"MC2ROframes", ConcreteDataTypeMatcher{Origin, "CLUSTERSMC2ROF"}}, - (detName + "ClustersMC2ROF").c_str(), "cluster-mc2rof-branch", - (useMC ? NLayers : 0), + (useMC ? nLayers : 0), getIndex, getName})(); } -framework::DataProcessorSpec getITSClusterWriterSpec(bool useMC) { return getClusterWriterSpec(useMC); } -framework::DataProcessorSpec getMFTClusterWriterSpec(bool useMC) { return getClusterWriterSpec(useMC); } +framework::DataProcessorSpec getITSClusterWriterSpec(bool useMC, bool doStag) { return getClusterWriterSpec(useMC, doStag); } +framework::DataProcessorSpec getMFTClusterWriterSpec(bool useMC, bool doStag) { return getClusterWriterSpec(useMC, doStag); } } // namespace o2::itsmft diff --git a/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx b/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx index fc0dd5dbae7da..0672f7d13bed2 100644 --- a/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// 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. // @@ -12,6 +12,7 @@ /// @file ClustererSpec.cxx #include +#include #include "ITSMFTWorkflow/ClustererSpec.h" #include "Framework/ControlService.h" @@ -29,7 +30,7 @@ #include "DataFormatsParameters/GRPObject.h" #include "ITSMFTReconstruction/DigitPixelReader.h" #include "DetectorsBase/GeometryManager.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "CommonConstants/LHCConstants.h" #include "DetectorsCommonDataFormats/DetectorNameConf.h" #include "ITSMFTReconstruction/ClustererParam.h" @@ -37,6 +38,14 @@ namespace o2::itsmft { +template +ClustererDPL::ClustererDPL(std::shared_ptr gr, bool useMC, bool doStag) : mGGCCDBRequest(gr), mUseMC(useMC), mDoStaggering(doStag) +{ + if (mDoStaggering) { + mLayers = DPLAlpideParam::getNLayers(); + } +} + template void ClustererDPL::init(InitContext& ic) { @@ -48,12 +57,11 @@ void ClustererDPL::init(InitContext& ic) mDetName = Origin.as(); // prepare data filter - for (int iLayer = 0; iLayer < NLayers; ++iLayer) { + for (int iLayer = 0; iLayer < mLayers; ++iLayer) { mFilter.emplace_back("digits", Origin, "DIGITS", iLayer, Lifetime::Timeframe); mFilter.emplace_back("ROframe", Origin, "DIGITSROF", iLayer, Lifetime::Timeframe); if (mUseMC) { mFilter.emplace_back("labels", Origin, "DIGITSMCTR", iLayer, Lifetime::Timeframe); - mFilter.emplace_back("MC2ROframes", Origin, "DIGITSMC2ROF", iLayer, Lifetime::Timeframe); } } } @@ -64,10 +72,9 @@ void ClustererDPL::run(ProcessingContext& pc) updateTimeDependentParams(pc); // filter input and compose - std::array, NLayers> digits; - std::array, NLayers> rofs; - std::array, NLayers> labelsbuffer; - std::array, NLayers> mc2rofs; + std::vector> digits(mLayers); + std::vector> rofs(mLayers); + std::vector> labelsbuffer(mLayers); for (const DataRef& ref : InputRecordWalker{pc.inputs(), mFilter}) { auto const* dh = DataRefUtils::getHeader(ref); if (DataRefUtils::match(ref, {"digits", ConcreteDataTypeMatcher{Origin, "DIGITS"}})) { @@ -79,9 +86,6 @@ void ClustererDPL::run(ProcessingContext& pc) if (DataRefUtils::match(ref, {"labels", ConcreteDataTypeMatcher{Origin, "DIGITSMCTR"}})) { labelsbuffer[dh->subSpecification] = pc.inputs().get>(ref); } - if (DataRefUtils::match(ref, {"MC2ROframes", ConcreteDataTypeMatcher{Origin, "DIGITSMC2ROF"}})) { - mc2rofs[dh->subSpecification] = pc.inputs().get>(ref); - } } // query the first orbit in this TF @@ -93,10 +97,10 @@ void ClustererDPL::run(ProcessingContext& pc) uint64_t nClusters{0}; TStopwatch sw; o2::itsmft::DigitPixelReader reader; - for (uint32_t iLayer{0}; iLayer < NLayers; ++iLayer) { - int layer = (DPLAlpideParam::supportsStaggering()) ? iLayer : -1; + for (uint32_t iLayer{0}; iLayer < mLayers; ++iLayer) { + int layer = (mDoStaggering) ? iLayer : -1; sw.Start(); - LOG(info) << mDetName << "Clusterer:" << layer << " pulled " << digits[iLayer].size() << " digits, in " << rofs[iLayer].size() << " RO frames"; + LOG(info) << mDetName << "Clusterer" << ((mDoStaggering) ? std::format(" on layer {}", layer) : "") << " pulled " << digits[iLayer].size() << " digits, in " << rofs[iLayer].size() << " RO frames"; mClusterer->setMaxROFDepthToSquash(mClusterer->getMaxROFDepthToSquash(layer)); o2::dataformats::ConstMCTruthContainerView labels(labelsbuffer[iLayer]); @@ -106,8 +110,7 @@ void ClustererDPL::run(ProcessingContext& pc) reader.setDigits(digits[iLayer]); reader.setROFRecords(rofs[iLayer]); if (mUseMC) { - reader.setMC2ROFRecords(mc2rofs[iLayer]); - LOG(info) << mDetName << "Clusterer:" << layer << " pulled " << labels.getNElements() << " labels "; + LOG(info) << mDetName << "Clusterer" << ((mDoStaggering) ? std::format(" on layer {}", layer) : "") << " pulled " << labels.getNElements() << " labels "; reader.setDigitsMCTruth(labels.getIndexedSize() > 0 ? &labels : nullptr); } reader.init(); @@ -131,7 +134,7 @@ void ClustererDPL::run(ProcessingContext& pc) for (int iROF{0}; iROF < nROFsTF; ++iROF) { auto& rof = expClusRofVec[iROF]; int orb = iROF * par.getROFLengthInBC(iLayer) / o2::constants::lhc::LHCMaxBunches + firstTForbit; - int bc = iROF * par.getROFLengthInBC(iLayer) % o2::constants::lhc::LHCMaxBunches; + int bc = iROF * par.getROFLengthInBC(iLayer) % o2::constants::lhc::LHCMaxBunches + par.getROFDelayInBC(iLayer); o2::InteractionRecord ir(bc, orb); rof.setBCData(ir); rof.setROFrame(iROF); @@ -142,13 +145,18 @@ void ClustererDPL::run(ProcessingContext& pc) for (const auto& rof : clusROFVec) { const auto& ir = rof.getBCData(); if (ir < firstIR) { - LOGP(warn, "Discard ROF {} preceding TF 1st orbit {}, layer:{}", ir.asString(), firstTForbit, iLayer); + LOGP(warn, "Discard ROF {} preceding TF 1st orbit {}{}", ir.asString(), firstTForbit, ((mDoStaggering) ? std::format(" on layer {}", layer) : "")); + continue; + } + auto irToFirst = ir - firstIR; + if (irToFirst.toLong() - par.getROFDelayInBC(iLayer) < 0) { + LOGP(warn, "Discard ROF {} preceding TF 1st orbit {} due to imposed ROF delay{}", ir.asString(), firstTForbit, ((mDoStaggering) ? std::format(" on layer {}", iLayer) : "")); continue; } - const auto irToFirst = ir - firstIR; + irToFirst -= par.getROFDelayInBC(iLayer); const long irROF = irToFirst.toLong() / par.getROFLengthInBC(iLayer); if (irROF >= nROFsTF) { - LOGP(warn, "Discard ROF {} exceding TF orbit range, layer:{}", ir.asString(), iLayer); + LOGP(warn, "Discard ROF {} exceeding TF orbit range{}", ir.asString(), ((mDoStaggering) ? std::format(" on layer {}", layer) : "")); continue; } auto& expROF = expClusRofVec[irROF]; @@ -157,11 +165,11 @@ void ClustererDPL::run(ProcessingContext& pc) expROF.setNEntries(rof.getNEntries()); } else { if (expROF.getNEntries() < rof.getNEntries()) { - LOGP(warn, "Repeating ROF {} with {} clusters, prefer to already processed instance with {} clusters", rof.asString(), rof.getNEntries(), expROF.getNEntries()); + LOGP(warn, "Repeating {} with {} clusters, prefer to already processed instance with {} clusters{}", rof.asString(), rof.getNEntries(), expROF.getNEntries(), ((mDoStaggering) ? std::format(" on layer {}", layer) : "")); expROF.setFirstEntry(rof.getFirstEntry()); expROF.setNEntries(rof.getNEntries()); } else { - LOGP(warn, "Repeating ROF {} with {} clusters, discard preferring already processed instance with {} clusters", rof.asString(), rof.getNEntries(), expROF.getNEntries()); + LOGP(warn, "Repeating {} with {} clusters, discard preferring already processed instance with {} clusters{}", rof.asString(), rof.getNEntries(), expROF.getNEntries(), ((mDoStaggering) ? std::format(" on layer {}", layer) : "")); } } } @@ -182,18 +190,11 @@ void ClustererDPL::run(ProcessingContext& pc) if (mUseMC) { pc.outputs().snapshot(Output{Origin, "CLUSTERSMCTR", iLayer}, *clusterLabels); // at the moment requires snapshot - std::vector clusterMC2ROframes(mc2rofs[iLayer].size()); - for (int i = mc2rofs[iLayer].size(); i--;) { - clusterMC2ROframes[i] = mc2rofs[iLayer][i]; // Simply, replicate it from digits ? - } - pc.outputs().snapshot(Output{Origin, "CLUSTERSMC2ROF", iLayer}, clusterMC2ROframes); } reader.reset(); - // TODO: in principle, after masking "overflow" pixels the MC2ROFRecord maxROF supposed to change, nominally to minROF - // -> consider recalculationg maxROF sw.Stop(); - LOG(info) << mDetName << "Clusterer:" << layer << " pushed " << clusCompVec.size() << " clusters, in " << nROFs << " RO frames in " << sw.RealTime() << " s"; + LOG(info) << mDetName << "Clusterer" << ((mDoStaggering) ? std::format(": {}", iLayer) : "") << " pushed " << clusCompVec.size() << " clusters, in " << nROFs << " RO frames in " << sw.RealTime() << " s"; } LOG(info) << mDetName << "Clusterer produced " << nClusters << " clusters"; @@ -230,9 +231,9 @@ void ClustererDPL::updateTimeDependentParams(ProcessingContext& pc) nROFsToSquash = 2 + int(clParams.maxSOTMUS / (rofBC * o2::constants::lhc::LHCBunchSpacingMUS)); // use squashing } mClusterer->setMaxROFDepthToSquash(nROFsToSquash); - if constexpr (DPLAlpideParam::supportsStaggering()) { + if (mDoStaggering) { if (mClusterer->isContinuousReadOut()) { - for (int iLayer{0}; iLayer < NLayers; ++iLayer) { + for (int iLayer{0}; iLayer < mLayers; ++iLayer) { mClusterer->addMaxBCSeparationToSquash(alpParams.getROFLengthInBC(iLayer) + clParams.getMaxBCDiffToSquashBias(iLayer)); mClusterer->addMaxROFDepthToSquash((clParams.getMaxBCDiffToSquashBias(iLayer) > 0) ? 2 + int(clParams.maxSOTMUS / (alpParams.getROFLengthInBC(iLayer) * o2::constants::lhc::LHCBunchSpacingMUS)) : 0); } @@ -275,17 +276,16 @@ void ClustererDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) namespace { template -DataProcessorSpec getClustererSpec(bool useMC) +DataProcessorSpec getClustererSpec(bool useMC, bool doStag) { constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; std::vector inputs; - constexpr uint32_t nLayers = (DPLAlpideParam::supportsStaggering()) ? DPLAlpideParam::getNLayers() : 1; + uint32_t nLayers = doStag ? DPLAlpideParam::getNLayers() : 1; for (uint32_t iLayer = 0; iLayer < nLayers; ++iLayer) { inputs.emplace_back("digits", Origin, "DIGITS", iLayer, Lifetime::Timeframe); inputs.emplace_back("ROframes", Origin, "DIGITSROF", iLayer, Lifetime::Timeframe); if (useMC) { inputs.emplace_back("labels", Origin, "DIGITSMCTR", iLayer, Lifetime::Timeframe); - inputs.emplace_back("MC2ROframes", Origin, "DIGITSMC2ROF", iLayer, Lifetime::Timeframe); } } inputs.emplace_back("cldict", Origin, "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec(Origin.as() + "/Calib/ClusterDictionary")); @@ -306,28 +306,27 @@ DataProcessorSpec getClustererSpec(bool useMC) outputs.emplace_back(Origin, "CLUSTERSROF", iLayer, Lifetime::Timeframe); if (useMC) { outputs.emplace_back(Origin, "CLUSTERSMCTR", iLayer, Lifetime::Timeframe); - outputs.emplace_back(Origin, "CLUSTERSMC2ROF", iLayer, Lifetime::Timeframe); } } return DataProcessorSpec{ .name = (N == o2::detectors::DetID::ITS) ? "its-clusterer" : "mft-clusterer", .inputs = inputs, .outputs = outputs, - .algorithm = AlgorithmSpec{adaptFromTask>(ggRequest, useMC)}, + .algorithm = AlgorithmSpec{adaptFromTask>(ggRequest, useMC, doStag)}, .options = Options{ {"ignore-cluster-dictionary", VariantType::Bool, false, {"do not use cluster dictionary, always store explicit patterns"}}, {"nthreads", VariantType::Int, 1, {"Number of clustering threads"}}}}; } } // namespace -framework::DataProcessorSpec getITSClustererSpec(bool useMC) +framework::DataProcessorSpec getITSClustererSpec(bool useMC, bool doStag) { - return getClustererSpec(useMC); + return getClustererSpec(useMC, doStag); } -framework::DataProcessorSpec getMFTClustererSpec(bool useMC) +framework::DataProcessorSpec getMFTClustererSpec(bool useMC, bool doStag) { - return getClustererSpec(useMC); + return getClustererSpec(useMC, doStag); } } // namespace o2::itsmft diff --git a/Detectors/ITSMFT/common/workflow/src/DigitReaderSpec.cxx b/Detectors/ITSMFT/common/workflow/src/DigitReaderSpec.cxx index ec86da4833a0d..6a57933f18048 100644 --- a/Detectors/ITSMFT/common/workflow/src/DigitReaderSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/DigitReaderSpec.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// 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. // @@ -13,14 +13,15 @@ #include #include +#include -#include "TTree.h" +#include #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/Logger.h" #include "ITSMFTWorkflow/DigitReaderSpec.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "ITSMFTReconstruction/ChipMappingITS.h" #include "ITSMFTReconstruction/ChipMappingMFT.h" #include "SimulationDataFormat/MCCompLabel.h" @@ -41,22 +42,21 @@ namespace itsmft { template -DigitReader::DigitReader(bool useMC, bool useCalib, bool triggerOut) : mUseMC(useMC), mUseCalib(useCalib), mTriggerOut(triggerOut), mDetNameLC(mDetName = ID.getName()), mDigTreeName("o2sim") +DigitReader::DigitReader(bool useMC, bool doStag, bool useCalib, bool triggerOut) : mUseMC(useMC), mDoStaggering(doStag), mUseCalib(useCalib), mTriggerOut(triggerOut), mDetNameLC(mDetName = ID.getName()), mDigTreeName("o2sim") { mDigitBranchName = mDetName + mDigitBranchName; mDigitROFBranchName = mDetName + mDigitROFBranchName; mCalibBranchName = mDetName + mCalibBranchName; mDigitMCTruthBranchName = mDetName + mDigitMCTruthBranchName; - mDigitMC2ROFBranchName = mDetName + mDigitMC2ROFBranchName; std::transform(mDetNameLC.begin(), mDetNameLC.end(), mDetNameLC.begin(), ::tolower); - for (uint32_t i = 0; i < NLayers; ++i) { - mDigits[i] = nullptr; - mDigROFRec[i] = nullptr; - mDigMC2ROFs[i] = nullptr; - mPLabels[i] = nullptr; + if (mDoStaggering) { + mLayers = DPLAlpideParam::getNLayers(); + mDigits.resize(mLayers, nullptr); + mDigROFRec.resize(mLayers, nullptr); + mPLabels.resize(mLayers, nullptr); } } @@ -103,8 +103,8 @@ void DigitReader::run(ProcessingContext& pc) ent++; assert(ent < mTree->GetEntries()); // this should not happen mTree->GetEntry(ent); - for (uint32_t iLayer = 0; iLayer < RLayers; ++iLayer) { - LOG(info) << mDetName << "DigitReader:" << iLayer << " pushes " << mDigROFRec[iLayer]->size() << " ROFRecords, " << mDigits[iLayer]->size() << " digits at entry " << ent; + for (uint32_t iLayer = 0; iLayer < mLayers; ++iLayer) { + LOG(info) << mDetName << "DigitReader" << ((mDoStaggering) ? std::format(": {}", iLayer) : "") << " pushes " << mDigROFRec[iLayer]->size() << " ROFRecords, " << mDigits[iLayer]->size() << " digits at entry " << ent; pc.outputs().snapshot(Output{Origin, "DIGITSROF", iLayer}, *mDigROFRec[iLayer]); pc.outputs().snapshot(Output{Origin, "DIGITS", iLayer}, *mDigits[iLayer]); if (mUseMC) { @@ -112,7 +112,6 @@ void DigitReader::run(ProcessingContext& pc) mPLabels[iLayer]->copyandflatten(sharedlabels); delete mPLabels[iLayer]; mPLabels[iLayer] = nullptr; - pc.outputs().snapshot(Output{Origin, "DIGITSMC2ROF", iLayer}, *mDigMC2ROFs[iLayer]); } } if (mUseCalib) { @@ -131,7 +130,6 @@ void DigitReader::run(ProcessingContext& pc) std::vector digitsSel; std::vector calibSel; std::vector digROFRecSel; - std::vector digMC2ROFsSel; o2::dataformats::MCTruthContainer digitLabelsSel; if (irFrames.size()) { // we assume the IRFrames are in the increasing order @@ -181,26 +179,6 @@ void DigitReader::run(ProcessingContext& pc) } } } - if (mUseMC) { - digMC2ROFsSel = *mDigMC2ROFs[0]; - for (auto& mc2rof : digMC2ROFsSel) { - if (mc2rof.rofRecordID < 0) { - continue; // did not contribute even to the original data - } - unsigned int mn = 0xffff, mx = 0; - for (int ir = mc2rof.minROF; ir <= mc2rof.maxROF; ir++) { - if (rofOld2New[ir] >= 0) { // used - mx = rofOld2New[ir]; - if (mn > mx) { - mn = mx; - } - } - } - mc2rof.rofRecordID = mn == 0xffff ? -1 : int(mn); - mc2rof.minROF = mn; - mc2rof.maxROF = mx; - } - } if (mDigROFRec[0]->back().getBCData() + mROFLengthInBC - 1 < irMax) { // need to check the next entry ent++; continue; @@ -220,7 +198,6 @@ void DigitReader::run(ProcessingContext& pc) if (mUseMC) { auto& sharedlabels = pc.outputs().make>(Output{Origin, "DIGITSMCTR", 0}); digitLabelsSel.flatten_to(sharedlabels); - pc.outputs().snapshot(Output{Origin, "DIGITSMC2ROF", 0}, digMC2ROFsSel); } if (!irFrames.size() || irFrames.back().isLast()) { @@ -238,14 +215,13 @@ void DigitReader::connectTree(const std::string& filename) assert(mFile && !mFile->IsZombie()); mTree.reset((TTree*)mFile->Get(mDigTreeName.c_str())); assert(mTree); - for (uint32_t iLayer = 0; iLayer < RLayers; ++iLayer) { + for (uint32_t iLayer = 0; iLayer < mLayers; ++iLayer) { setBranchAddress(mDigitROFBranchName, mDigROFRec[iLayer], iLayer); setBranchAddress(mDigitBranchName, mDigits[iLayer], iLayer); if (mUseMC) { - if (!mTree->GetBranch(getBranchName(mDigitMC2ROFBranchName, iLayer).c_str()) || !mTree->GetBranch(getBranchName(mDigitMCTruthBranchName, iLayer).c_str())) { + if (!mTree->GetBranch(getBranchName(mDigitMCTruthBranchName, iLayer).c_str())) { throw std::runtime_error("MC data requested but not found in the tree"); } - setBranchAddress(mDigitMC2ROFBranchName, mDigMC2ROFs[iLayer], iLayer); if (!mPLabels[iLayer]) { setBranchAddress(mDigitMCTruthBranchName, mPLabels[iLayer], iLayer); } @@ -263,10 +239,10 @@ void DigitReader::connectTree(const std::string& filename) template std::string DigitReader::getBranchName(const std::string& base, int index) { - if constexpr (!o2::itsmft::DPLAlpideParam::supportsStaggering()) { - return base; + if (mDoStaggering) { + return base + "_" + std::to_string(index); } - return base + "_" + std::to_string(index); + return base; } template @@ -282,16 +258,15 @@ void DigitReader::setBranchAddress(const std::string& base, Ptr& addr, int la namespace { template -std::vector makeOutChannels(bool mctruth, bool useCalib) +std::vector makeOutChannels(bool mctruth, bool doStag, bool useCalib) { constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; std::vector outputs; - static constexpr int RLayers = o2::itsmft::DPLAlpideParam::supportsStaggering() ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; - for (int iLayer = 0; iLayer < RLayers; ++iLayer) { + int nLayers = doStag ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + for (int iLayer = 0; iLayer < nLayers; ++iLayer) { outputs.emplace_back(Origin, "DIGITS", iLayer, Lifetime::Timeframe); outputs.emplace_back(Origin, "DIGITSROF", iLayer, Lifetime::Timeframe); if (mctruth) { - outputs.emplace_back(Origin, "DIGITSMC2ROF", iLayer, Lifetime::Timeframe); outputs.emplace_back(Origin, "DIGITSMCTR", iLayer, Lifetime::Timeframe); } } @@ -303,25 +278,25 @@ std::vector makeOutChannels(bool mctruth, bool useCalib) } } // namespace -DataProcessorSpec getITSDigitReaderSpec(bool useMC, bool useCalib, bool useTriggers, std::string defname) +DataProcessorSpec getITSDigitReaderSpec(bool useMC, bool doStag, bool useCalib, bool useTriggers, std::string defname) { return DataProcessorSpec{ .name = "its-digit-reader", .inputs = Inputs{}, - .outputs = makeOutChannels(useMC, useCalib), - .algorithm = AlgorithmSpec{adaptFromTask(useMC, useCalib)}, + .outputs = makeOutChannels(useMC, doStag, useCalib), + .algorithm = AlgorithmSpec{adaptFromTask(useMC, doStag, useCalib, useTriggers)}, .options = Options{ {"its-digit-infile", VariantType::String, defname, {"Name of the input digit file"}}, {"input-dir", VariantType::String, "none", {"Input directory"}}}}; } -DataProcessorSpec getMFTDigitReaderSpec(bool useMC, bool useCalib, bool useTriggers, std::string defname) +DataProcessorSpec getMFTDigitReaderSpec(bool useMC, bool doStag, bool useCalib, bool useTriggers, std::string defname) { return DataProcessorSpec{ .name = "mft-digit-reader", .inputs = Inputs{}, - .outputs = makeOutChannels(useMC, useCalib), - .algorithm = AlgorithmSpec{adaptFromTask(useMC, useCalib)}, + .outputs = makeOutChannels(useMC, doStag, useCalib), + .algorithm = AlgorithmSpec{adaptFromTask(useMC, doStag, useCalib, useTriggers)}, .options = Options{ {"mft-digit-infile", VariantType::String, defname, {"Name of the input digit file"}}, {"input-dir", VariantType::String, "none", {"Input directory"}}}}; diff --git a/Detectors/ITSMFT/common/workflow/src/DigitWriterSpec.cxx b/Detectors/ITSMFT/common/workflow/src/DigitWriterSpec.cxx index c4f1e336180c7..d409356c6846f 100644 --- a/Detectors/ITSMFT/common/workflow/src/DigitWriterSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/DigitWriterSpec.cxx @@ -14,7 +14,7 @@ #include "ITSMFTWorkflow/DigitWriterSpec.h" #include "Framework/ConcreteDataMatcher.h" #include "Framework/DataRef.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DPLUtils/MakeRootTreeWriterSpec.h" #include "DataFormatsITSMFT/Digit.h" #include "DataFormatsITSMFT/GBTCalibData.h" @@ -43,20 +43,20 @@ using MCCont = o2::dataformats::ConstMCTruthContainer; /// create the processor spec /// describing a processor receiving digits for ITS/MFT and writing them to file template -DataProcessorSpec getDigitWriterSpec(bool mctruth, bool dec, bool calib) +DataProcessorSpec getDigitWriterSpec(bool mctruth, bool doStag, bool dec, bool calib) { static constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; - constexpr int NLayers = o2::itsmft::DPLAlpideParam::supportsStaggering() ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + int mLayers = doStag ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; std::string detStr = o2::detectors::DetID::getName(N); std::string detStrL = dec ? "o2_" : ""; // for decoded digits prepend by o2 detStrL += detStr; std::transform(detStrL.begin(), detStrL.end(), detStrL.begin(), ::tolower); - auto digitSizes = std::make_shared>(); + auto digitSizes = std::make_shared>(mLayers, 0); auto digitSizeGetter = [digitSizes](std::vector const& inDigits, DataRef const& ref) { auto const* dh = DataRefUtils::getHeader(ref); (*digitSizes)[dh->subSpecification] = inDigits.size(); }; - auto rofSizes = std::make_shared>(); + auto rofSizes = std::make_shared>(mLayers, 0); auto rofSizeGetter = [rofSizes](std::vector const& inROFs, DataRef const& ref) { auto const* dh = DataRefUtils::getHeader(ref); (*rofSizes)[dh->subSpecification] = inROFs.size(); @@ -84,11 +84,11 @@ DataProcessorSpec getDigitWriterSpec(bool mctruth, bool dec, bool calib) // handler for labels // This is necessary since we can't store the original label buffer in a ROOT entry -- as is -- if it exceeds a certain size. // We therefore convert it to a special split class. - auto fillLabels = [digitSizes, rofSizes](TBranch& branch, std::vector const& labelbuffer, DataRef const& ref) { + auto fillLabels = [detStr, doStag, digitSizes, rofSizes](TBranch& branch, std::vector const& labelbuffer, DataRef const& ref) { o2::dataformats::ConstMCTruthContainerView labels(labelbuffer); auto const* dh = DataRefUtils::getHeader(ref); auto layer = static_cast(dh->subSpecification); - LOG(info) << "WRITING " << labels.getNElements() << " LABELS FOR " << layer << " WITH " << (*digitSizes)[layer] << " DIGITS IN " << (*rofSizes)[layer] << " ROFS"; + LOG(info) << detStr << ": WRITING " << labels.getNElements() << " LABELS" << (doStag ? std::format(" FOR LAYER {}", layer) : "") << " WITH " << (*digitSizes)[layer] << " DIGITS IN " << (*rofSizes)[layer] << " ROFS"; o2::dataformats::IOMCTruthContainerView outputcontainer; auto ptr = &outputcontainer; @@ -102,52 +102,58 @@ DataProcessorSpec getDigitWriterSpec(bool mctruth, bool dec, bool calib) auto const* dh = DataRefUtils::getHeader(ref); return static_cast(dh->subSpecification); }; - auto getName = [](std::string base, size_t index) -> std::string { - if constexpr (o2::itsmft::DPLAlpideParam::supportsStaggering()) { + auto getName = [doStag](std::string base, size_t index) -> std::string { + if (doStag) { return base += "_" + std::to_string(index); } return base; }; + + std::vector vecInpSpecDig, vecInpSpecROF, vecInpSpecLbl; + vecInpSpecDig.reserve(mLayers); + vecInpSpecROF.reserve(mLayers); + vecInpSpecLbl.reserve(mLayers); + for (int iLayer = 0; iLayer < mLayers; iLayer++) { + vecInpSpecDig.emplace_back(getName(detStr + "digits", iLayer), Origin, "DIGITS", iLayer); + vecInpSpecROF.emplace_back(getName(detStr + "digitsROF", iLayer), Origin, "DIGITSROF", iLayer); + vecInpSpecLbl.emplace_back(getName(detStr + "_digitsMCTR", iLayer), Origin, "DIGITSMCTR", iLayer); + } + return MakeRootTreeWriterSpec((detStr + "DigitWriter" + (dec ? "_dec" : "")).c_str(), (detStrL + "digits.root").c_str(), MakeRootTreeWriterSpec::TreeAttributes{.name = "o2sim", .title = detStr + " Digits tree"}, MakeRootTreeWriterSpec::CustomClose(finishWriting), - BranchDefinition>{InputSpec{detStr + "digits", ConcreteDataTypeMatcher{Origin, "DIGITS"}}, + BranchDefinition>{vecInpSpecDig, detStr + "Digit", "digit-branch", - NLayers, + mLayers, digitSizeGetter, getIndex, getName}, - BranchDefinition>{InputSpec{detStr + "digitsROF", ConcreteDataTypeMatcher{Origin, "DIGITSROF"}}, + BranchDefinition>{vecInpSpecROF, detStr + "DigitROF", "digit-rof-branch", - NLayers, + mLayers, rofSizeGetter, getIndex, getName}, - BranchDefinition>{InputSpec{detStr + "_digitsMCTR", ConcreteDataTypeMatcher{Origin, "DIGITSMCTR"}}, + BranchDefinition>{vecInpSpecLbl, detStr + "DigitMCTruth", "digit-mctruth-branch", - (mctruth ? NLayers : 0), + (mctruth ? mLayers : 0), fillLabels, getIndex, getName}, - BranchDefinition>{InputSpec{detStr + "_digitsMC2ROF", ConcreteDataTypeMatcher{Origin, "DIGITSMC2ROF"}}, - detStr + "DigitMC2ROF", "digit-mc2rof-branch", - (mctruth ? NLayers : 0), - getIndex, - getName}, BranchDefinition>{InputSpec{detStr + "calib", ConcreteDataTypeMatcher{Origin, "GBTCALIB"}}, detStr + "Calib", "digit-calib-branch", (calib ? 1 : 0)})(); } -DataProcessorSpec getITSDigitWriterSpec(bool mctruth, bool dec, bool calib) +DataProcessorSpec getITSDigitWriterSpec(bool mctruth, bool doStag, bool dec, bool calib) { - return getDigitWriterSpec(mctruth, dec, calib); + return getDigitWriterSpec(mctruth, doStag, dec, calib); } -DataProcessorSpec getMFTDigitWriterSpec(bool mctruth, bool dec, bool calib) +DataProcessorSpec getMFTDigitWriterSpec(bool mctruth, bool doStag, bool dec, bool calib) { - return getDigitWriterSpec(mctruth, dec, calib); + return getDigitWriterSpec(mctruth, doStag, dec, calib); } } // end namespace itsmft diff --git a/Detectors/ITSMFT/common/workflow/src/EntropyDecoderSpec.cxx b/Detectors/ITSMFT/common/workflow/src/EntropyDecoderSpec.cxx index f90b708af1996..1107ca2fd34f6 100644 --- a/Detectors/ITSMFT/common/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/EntropyDecoderSpec.cxx @@ -20,6 +20,7 @@ #include "ITSMFTWorkflow/EntropyDecoderSpec.h" #include "ITSMFTReconstruction/ClustererParam.h" #include "DetectorsCommonDataFormats/DetectorNameConf.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DataFormatsITSMFT/PhysTrigger.h" using namespace o2::framework; @@ -28,25 +29,33 @@ namespace o2 { namespace itsmft { -EntropyDecoderSpec::EntropyDecoderSpec(o2::header::DataOrigin orig, int verbosity, bool getDigits, const std::string& ctfdictOpt) - : mOrigin(orig), mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, orig == o2::header::gDataOriginITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT, ctfdictOpt), mGetDigits(getDigits) + +template +std::string EntropyDecoderSpec::getBinding(const std::string& name, int spec) +{ + return fmt::format("{}_{}", name, spec); +} + +template +EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, bool doStag, bool getDigits, const std::string& ctfdictOpt) + : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, doStag, ctfdictOpt), mDoStaggering(doStag), mGetDigits(getDigits) { - assert(orig == o2::header::gDataOriginITS || orig == o2::header::gDataOriginMFT); - mDetPrefix = orig == o2::header::gDataOriginITS ? "_ITS" : "_MFT"; mTimer.Stop(); mTimer.Reset(); mCTFCoder.setVerbosity(verbosity); - mCTFCoder.setDictBinding(std::string("ctfdict") + mDetPrefix); + mCTFCoder.setDictBinding(std::string("ctfdict_") + ID.getName()); } -void EntropyDecoderSpec::init(o2::framework::InitContext& ic) +template +void EntropyDecoderSpec::init(o2::framework::InitContext& ic) { - mCTFCoder.init(ic); + mCTFCoder.template init(ic); mMaskNoise = ic.options().get("mask-noise"); mUseClusterDictionary = !ic.options().get("ignore-cluster-dictionary"); } -void EntropyDecoderSpec::run(ProcessingContext& pc) +template +void EntropyDecoderSpec::run(ProcessingContext& pc) { if (pc.services().get().globalRunNumberChanged) { mTimer.Reset(); @@ -54,105 +63,142 @@ void EntropyDecoderSpec::run(ProcessingContext& pc) auto cput = mTimer.CpuTime(); mTimer.Start(false); o2::ctf::CTFIOSize iosize; + size_t ndigcl = 0, nrofs = 0; updateTimeDependentParams(pc); - auto buff = pc.inputs().get>(std::string("ctf") + mDetPrefix); - // since the buff is const, we cannot use EncodedBlocks::relocate directly, instead we wrap its data to another flat object - // const auto ctfImage = o2::itsmft::CTF::getImage(buff.data()); - - // this produces weird memory problems in unrelated devices, to be understood - // auto& trigs = pc.outputs().make>(OutputRef{"phystrig"}); // dummy output - - auto& rofs = pc.outputs().make>(OutputRef{"ROframes"}); - if (mGetDigits) { - auto& digits = pc.outputs().make>(OutputRef{"Digits"}); - if (buff.size()) { - iosize = mCTFCoder.decode(o2::itsmft::CTF::getImage(buff.data()), rofs, digits, mNoiseMap, mPattIdConverter); + std::string nm = ID.getName(); + uint32_t nLayers = mDoStaggering ? DPLAlpideParam::getNLayers() : 1; + for (uint32_t iLayer = 0; iLayer < nLayers; iLayer++) { + auto buff = pc.inputs().get>(getBinding(nm + "CTF", iLayer)); + // since the buff is const, we cannot use EncodedBlocks::relocate directly, instead we wrap its data to another flat object + // const auto ctfImage = o2::itsmft::CTF::getImage(buff.data()); + const auto& ctf = o2::itsmft::CTF::getImage(buff.data()); + if (ctf.getHeader().maxStreams != nLayers) { + LOGP(fatal, "Number of streams {} in the CTF header is not equal to NLayers {} from AlpideParam in {}staggered mode", + ctf.getHeader().maxStreams, nLayers, mDoStaggering ? "" : "non-"); } - mTimer.Stop(); - LOG(info) << "Decoded " << digits.size() << " digits in " << rofs.size() << " RO frames, (" << iosize.asString() << ") in " << mTimer.CpuTime() - cput << " s"; - } else { - auto& compcl = pc.outputs().make>(OutputRef{"compClusters"}); - auto& patterns = pc.outputs().make>(OutputRef{"patterns"}); - if (buff.size()) { - iosize = mCTFCoder.decode(o2::itsmft::CTF::getImage(buff.data()), rofs, compcl, patterns, mNoiseMap, mPattIdConverter); + // this produces weird memory problems in unrelated devices, to be understood + // auto& trigs = pc.outputs().make>(OutputRef{"phystrig"}); // dummy output + auto& rofs = pc.outputs().make>(OutputRef{nm + "ROframes", iLayer}); + if (mGetDigits) { + auto& digits = pc.outputs().make>(OutputRef{nm + "Digits", iLayer}); + if (buff.size()) { + iosize += mCTFCoder.decode(ctf, rofs, digits, mNoiseMap, mPattIdConverter); + } + ndigcl += digits.size(); + nrofs += rofs.size(); + } else { + auto& compcl = pc.outputs().make>(OutputRef{nm + "compClusters", iLayer}); + auto& patterns = pc.outputs().make>(OutputRef{nm + "patterns", iLayer}); + if (buff.size()) { + iosize += mCTFCoder.decode(ctf, rofs, compcl, patterns, mNoiseMap, mPattIdConverter); + } + ndigcl += compcl.size(); } - mTimer.Stop(); - LOG(info) << "Decoded " << compcl.size() << " clusters in " << rofs.size() << " RO frames, (" << iosize.asString() << ") in " << mTimer.CpuTime() - cput << " s"; } - pc.outputs().snapshot({"ctfrep", 0}, iosize); + pc.outputs().snapshot({nm + "ctfrep", 0}, iosize); + mTimer.Stop(); + LOGP(info, "Decoded {} {} in {} ROFs of {} streams ({}) in {}staggerd mode in {} s", ndigcl, mGetDigits ? "digits" : "clusters", + nrofs, nLayers, iosize.asString(), mDoStaggering ? "" : "non-", mTimer.CpuTime() - cput); } -void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) +template +void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) { - LOGF(info, "%s Entropy Decoding total timing: Cpu: %.3e Real: %.3e s in %d slots", - mOrigin.as(), mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); + LOGP(info, "{} Entropy Decoding total timing: Cpu: {:.3e} Real: {:.3e} s in {} slots", + Origin.as(), mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -void EntropyDecoderSpec::updateTimeDependentParams(ProcessingContext& pc) +template +void EntropyDecoderSpec::updateTimeDependentParams(ProcessingContext& pc) { + std::string nm = ID.getName(); if (pc.services().get().globalRunNumberChanged) { // this params need to be queried only once if (mMaskNoise) { - pc.inputs().get(std::string("noise") + mDetPrefix); + pc.inputs().get(nm + "noise"); } if (mGetDigits || mMaskNoise) { - pc.inputs().get(std::string("cldict") + mDetPrefix); + pc.inputs().get(nm + "cldict"); } } + pc.inputs().get*>(nm + "alppar"); mCTFCoder.updateTimeDependentParams(pc, true); } -void EntropyDecoderSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) +template +void EntropyDecoderSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) { - if (matcher == ConcreteDataMatcher(mOrigin, "NOISEMAP", 0)) { + if (matcher == ConcreteDataMatcher(Origin, "NOISEMAP", 0)) { mNoiseMap = (o2::itsmft::NoiseMap*)obj; - LOG(info) << mOrigin.as() << " noise map updated"; + LOG(info) << Origin.as() << " noise map updated"; return; } - if (matcher == ConcreteDataMatcher(mOrigin, "CLUSDICT", 0)) { - LOG(info) << mOrigin.as() << " cluster dictionary updated" << (!mUseClusterDictionary ? " but its using is disabled" : ""); + if (matcher == ConcreteDataMatcher(Origin, "CLUSDICT", 0)) { + LOG(info) << Origin.as() << " cluster dictionary updated" << (!mUseClusterDictionary ? " but its using is disabled" : ""); mPattIdConverter.setDictionary((const TopologyDictionary*)obj); return; } - if (mCTFCoder.finaliseCCDB(matcher, obj)) { + if (matcher == ConcreteDataMatcher(Origin, "ALPIDEPARAM", 0)) { + LOG(info) << "Alpide param updated"; + return; + } + if (mCTFCoder.template finaliseCCDB(matcher, obj)) { return; } } -DataProcessorSpec getEntropyDecoderSpec(o2::header::DataOrigin orig, int verbosity, bool getDigits, unsigned int sspec, const std::string& ctfdictOpt) +template +DataProcessorSpec getEntropyDecoderSpec(int verbosity, bool doStag, bool getDigits, unsigned int sspec, const std::string& ctfdictOpt) { + constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; + constexpr o2::detectors::DetID ID{N == o2::detectors::DetID::ITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT}; + uint32_t nLayers = doStag ? DPLAlpideParam::getNLayers() : 1; + + std::vector inputs; std::vector outputs; - // this is a special dummy input which makes sense only in sync workflows // this produces weird memory problems in unrelated devices, to be understood - // outputs.emplace_back(OutputSpec{{"phystrig"}, orig, "PHYSTRIG", 0, Lifetime::Timeframe}); - - if (getDigits) { - outputs.emplace_back(OutputSpec{{"Digits"}, orig, "DIGITS", 0, Lifetime::Timeframe}); - outputs.emplace_back(OutputSpec{{"ROframes"}, orig, "DIGITSROF", 0, Lifetime::Timeframe}); - } else { - outputs.emplace_back(OutputSpec{{"compClusters"}, orig, "COMPCLUSTERS", 0, Lifetime::Timeframe}); - outputs.emplace_back(OutputSpec{{"ROframes"}, orig, "CLUSTERSROF", 0, Lifetime::Timeframe}); - outputs.emplace_back(OutputSpec{{"patterns"}, orig, "PATTERNS", 0, Lifetime::Timeframe}); + // outputs.emplace_back(OutputSpec{{"phystrig"}, Origin, "PHYSTRIG", 0, Lifetime::Timeframe}); + std::string nm = ID.getName(); + for (uint32_t iLayer = 0; iLayer < nLayers; ++iLayer) { + if (getDigits) { + outputs.emplace_back(OutputSpec{{nm + "Digits"}, Origin, "DIGITS", iLayer, Lifetime::Timeframe}); + outputs.emplace_back(OutputSpec{{nm + "ROframes"}, Origin, "DIGITSROF", iLayer, Lifetime::Timeframe}); + } else { + outputs.emplace_back(OutputSpec{{nm + "compClusters"}, Origin, "COMPCLUSTERS", iLayer, Lifetime::Timeframe}); + outputs.emplace_back(OutputSpec{{nm + "ROframes"}, Origin, "CLUSTERSROF", iLayer, Lifetime::Timeframe}); + outputs.emplace_back(OutputSpec{{nm + "patterns"}, Origin, "PATTERNS", iLayer, Lifetime::Timeframe}); + } + inputs.emplace_back(EntropyDecoderSpec::getBinding(nm + "CTF", iLayer), Origin, "CTFDATA", sspec * 100 + iLayer, Lifetime::Timeframe); } - outputs.emplace_back(OutputSpec{{"ctfrep"}, orig, "CTFDECREP", 0, Lifetime::Timeframe}); - std::string nm = orig == o2::header::gDataOriginITS ? "_ITS" : "_MFT"; - std::vector inputs; - inputs.emplace_back(std::string("ctf") + nm, orig, "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back(std::string("noise") + nm, orig, "NOISEMAP", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/NoiseMap", orig.as()))); - inputs.emplace_back(std::string("cldict") + nm, orig, "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/ClusterDictionary", orig.as()))); + outputs.emplace_back(OutputSpec{{nm + "ctfrep"}, Origin, "CTFDECREP", 0, Lifetime::Timeframe}); + + inputs.emplace_back(nm + "alppar", Origin, "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Config/AlpideParam", Origin.as()))); + inputs.emplace_back(nm + "noise", Origin, "NOISEMAP", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/NoiseMap", Origin.as()))); + inputs.emplace_back(nm + "cldict", Origin, "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/ClusterDictionary", Origin.as()))); if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { - inputs.emplace_back(std::string("ctfdict") + nm, orig, "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/CTFDictionaryTree", orig.as()))); + inputs.emplace_back(std::string{"ctfdict_"} + ID.getName(), Origin, "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/CTFDictionaryTree", Origin.as()))); } - inputs.emplace_back(std::string("trigoffset"), "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); + inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ - EntropyDecoderSpec::getName(orig), + Origin == o2::header::gDataOriginITS ? "its-entropy-decoder" : "mft-entropy-decoder", inputs, outputs, - AlgorithmSpec{adaptFromTask(orig, verbosity, getDigits, ctfdictOpt)}, + AlgorithmSpec{adaptFromTask>(verbosity, doStag, getDigits, ctfdictOpt)}, Options{{"mask-noise", VariantType::Bool, false, {"apply noise mask to digits or clusters (involves reclusterization)"}}, {"ignore-cluster-dictionary", VariantType::Bool, false, {"do not use cluster dictionary, always store explicit patterns"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } + +framework::DataProcessorSpec getITSEntropyDecoderSpec(int verbosity, bool doStag, bool getDigits, unsigned int sspec, const std::string& ctfdictOpt) +{ + return getEntropyDecoderSpec(verbosity, doStag, getDigits, sspec, ctfdictOpt); +} + +framework::DataProcessorSpec getMFTEntropyDecoderSpec(int verbosity, bool doStag, bool getDigits, unsigned int sspec, const std::string& ctfdictOpt) +{ + return getEntropyDecoderSpec(verbosity, doStag, getDigits, sspec, ctfdictOpt); +} + } // namespace itsmft } // namespace o2 diff --git a/Detectors/ITSMFT/common/workflow/src/EntropyEncoderSpec.cxx b/Detectors/ITSMFT/common/workflow/src/EntropyEncoderSpec.cxx index a824184330547..f80555efed384 100644 --- a/Detectors/ITSMFT/common/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/EntropyEncoderSpec.cxx @@ -18,7 +18,7 @@ #include "Framework/CCDBParamSpec.h" #include "DataFormatsITSMFT/CompCluster.h" #include "ITSMFTWorkflow/EntropyEncoderSpec.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DetectorsCommonDataFormats/DetID.h" using namespace o2::framework; @@ -27,20 +27,31 @@ namespace o2 { namespace itsmft { -EntropyEncoderSpec::EntropyEncoderSpec(o2::header::DataOrigin orig, bool selIR, const std::string& ctfdictOpt) - : mOrigin(orig), mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, orig == o2::header::gDataOriginITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT, ctfdictOpt), mSelIR(selIR) + +template +std::string EntropyEncoderSpec::getBinding(const std::string& name, int spec) +{ + return fmt::format("{}_{}", name, spec); +} + +template +EntropyEncoderSpec::EntropyEncoderSpec(bool doStag, bool selIR, const std::string& ctfdictOpt) + : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, doStag, ctfdictOpt), + mSelIR(selIR), + mDoStaggering(doStag) { - assert(orig == o2::header::gDataOriginITS || orig == o2::header::gDataOriginMFT); mTimer.Stop(); mTimer.Reset(); } -void EntropyEncoderSpec::init(o2::framework::InitContext& ic) +template +void EntropyEncoderSpec::init(o2::framework::InitContext& ic) { - mCTFCoder.init(ic); + mCTFCoder.template init(ic); } -void EntropyEncoderSpec::run(ProcessingContext& pc) +template +void EntropyEncoderSpec::run(ProcessingContext& pc) { if (pc.services().get().globalRunNumberChanged) { mTimer.Reset(); @@ -49,14 +60,20 @@ void EntropyEncoderSpec::run(ProcessingContext& pc) mTimer.Start(false); updateTimeDependentParams(pc); - auto compClusters = pc.inputs().get>("compClusters"); - auto pspan = pc.inputs().get>("patterns"); - auto rofs = pc.inputs().get>("ROframes"); + uint32_t nLayers = mDoStaggering ? DPLAlpideParam::getNLayers() : 1; + if (mSelIR) { mCTFCoder.setSelectedIRFrames(pc.inputs().get>("selIRFrames")); } - auto& buffer = pc.outputs().make>(Output{mOrigin, "CTFDATA", 0}); - auto iosize = mCTFCoder.encode(buffer, rofs, compClusters, pspan, mPattIdConverter, mStrobeLength); + o2::ctf::CTFIOSize iosize{}; + for (uint32_t iLayer = 0; iLayer < nLayers; iLayer++) { + auto compClusters = pc.inputs().get>(getBinding("compClusters", iLayer)); + auto pspan = pc.inputs().get>(getBinding("patterns", iLayer)); + auto rofs = pc.inputs().get>(getBinding("ROframes", iLayer)); + + auto& buffer = pc.outputs().make>(Output{Origin, "CTFDATA", iLayer}); + iosize += mCTFCoder.encode(buffer, rofs, compClusters, pspan, mPattIdConverter, iLayer); + } pc.outputs().snapshot({"ctfrep", 0}, iosize); if (mSelIR) { mCTFCoder.getIRFramesSelector().clear(); @@ -65,77 +82,90 @@ void EntropyEncoderSpec::run(ProcessingContext& pc) LOG(info) << iosize.asString() << " in " << mTimer.CpuTime() - cput << " s"; } -void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) +template +void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) { - LOGF(info, "%s Entropy Encoding total timing: Cpu: %.3e Real: %.3e s in %d slots", - mOrigin.as(), mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); + LOGP(info, "{} Entropy Encoding total timing: Cpu: {:.3e} Real: {:.3e} s in {} slots", + Origin.as(), mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -void EntropyEncoderSpec::updateTimeDependentParams(ProcessingContext& pc) +template +void EntropyEncoderSpec::updateTimeDependentParams(ProcessingContext& pc) { mCTFCoder.updateTimeDependentParams(pc, true); if (pc.services().get().globalRunNumberChanged) { // this params need to be queried only once if (mSelIR) { pc.inputs().get("cldict"); - if (mOrigin == o2::header::gDataOriginITS) { - pc.inputs().get*>("alppar"); - } else { - pc.inputs().get*>("alppar"); - } } } + pc.inputs().get*>("alppar"); } -void EntropyEncoderSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +template +void EntropyEncoderSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) { - if (matcher == ConcreteDataMatcher(mOrigin, "CLUSDICT", 0)) { - LOG(info) << mOrigin.as() << " cluster dictionary updated"; + if (matcher == ConcreteDataMatcher(Origin, "CLUSDICT", 0)) { + LOG(info) << Origin.as() << " cluster dictionary updated"; mPattIdConverter.setDictionary((const TopologyDictionary*)obj); return; } // Note: strictly speaking, for Configurable params we don't need finaliseCCDB check, the singletons are updated at the CCDB fetcher level - if (matcher == ConcreteDataMatcher(mOrigin, "ALPIDEPARAM", 0)) { + if (matcher == ConcreteDataMatcher(Origin, "ALPIDEPARAM", 0)) { LOG(info) << "Alpide param updated"; - if (mOrigin == o2::header::gDataOriginITS) { - const auto& par = DPLAlpideParam::Instance(); - mStrobeLength = par.roFrameLengthInBC; - } else { - const auto& par = DPLAlpideParam::Instance(); - mStrobeLength = par.roFrameLengthInBC; - } return; } - if (mCTFCoder.finaliseCCDB(matcher, obj)) { + if (mCTFCoder.template finaliseCCDB(matcher, obj)) { return; } } -DataProcessorSpec getEntropyEncoderSpec(o2::header::DataOrigin orig, bool selIR, const std::string& ctfdictOpt) +template +DataProcessorSpec getEntropyEncoderSpec(bool doStag, bool selIR, const std::string& ctfdictOpt) { + constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; + constexpr o2::detectors::DetID ID{N == o2::detectors::DetID::ITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT}; + const auto& par = DPLAlpideParam::Instance(); + uint32_t nLayers = doStag ? DPLAlpideParam::getNLayers() : 1; + std::vector inputs; - inputs.emplace_back("compClusters", orig, "COMPCLUSTERS", 0, Lifetime::Timeframe); - inputs.emplace_back("patterns", orig, "PATTERNS", 0, Lifetime::Timeframe); - inputs.emplace_back("ROframes", orig, "CLUSTERSROF", 0, Lifetime::Timeframe); + std::vector outputs; + for (uint32_t iLayer = 0; iLayer < nLayers; ++iLayer) { + inputs.emplace_back(EntropyEncoderSpec::getBinding("compClusters", iLayer), Origin, "COMPCLUSTERS", iLayer, Lifetime::Timeframe); + inputs.emplace_back(EntropyEncoderSpec::getBinding("patterns", iLayer), Origin, "PATTERNS", iLayer, Lifetime::Timeframe); + inputs.emplace_back(EntropyEncoderSpec::getBinding("ROframes", iLayer), Origin, "CLUSTERSROF", iLayer, Lifetime::Timeframe); + outputs.emplace_back(Origin, "CTFDATA", iLayer, Lifetime::Timeframe); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); - inputs.emplace_back("cldict", orig, "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/ClusterDictionary", orig.as()))); - inputs.emplace_back("alppar", orig, "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Config/AlpideParam", orig.as()))); + inputs.emplace_back("cldict", Origin, "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/ClusterDictionary", Origin.as()))); } + inputs.emplace_back("alppar", Origin, "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Config/AlpideParam", Origin.as()))); if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { - inputs.emplace_back("ctfdict", orig, "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/CTFDictionaryTree", orig.as()))); + inputs.emplace_back("ctfdict", Origin, "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/CTFDictionaryTree", Origin.as()))); } + outputs.emplace_back(OutputSpec{{"ctfrep"}, Origin, "CTFENCREP", 0, Lifetime::Timeframe}); return DataProcessorSpec{ - orig == o2::header::gDataOriginITS ? "its-entropy-encoder" : "mft-entropy-encoder", + Origin == o2::header::gDataOriginITS ? "its-entropy-encoder" : "mft-entropy-encoder", inputs, - Outputs{{orig, "CTFDATA", 0, Lifetime::Timeframe}, - {{"ctfrep"}, orig, "CTFENCREP", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(orig, selIR, ctfdictOpt)}, + outputs, + AlgorithmSpec{adaptFromTask>(doStag, selIR, ctfdictOpt)}, Options{{"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } + +framework::DataProcessorSpec getITSEntropyEncoderSpec(bool doStag, bool selIR, const std::string& ctfdictOpt) +{ + return getEntropyEncoderSpec(doStag, selIR, ctfdictOpt); +} + +framework::DataProcessorSpec getMFTEntropyEncoderSpec(bool doStag, bool selIR, const std::string& ctfdictOpt) +{ + return getEntropyEncoderSpec(doStag, selIR, ctfdictOpt); +} + } // namespace itsmft } // namespace o2 diff --git a/Detectors/ITSMFT/common/workflow/src/STFDecoderSpec.cxx b/Detectors/ITSMFT/common/workflow/src/STFDecoderSpec.cxx index da1af34376ff1..8fb6ba4e6aa97 100644 --- a/Detectors/ITSMFT/common/workflow/src/STFDecoderSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/STFDecoderSpec.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// 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. // @@ -17,7 +17,6 @@ #include "Framework/WorkflowSpec.h" #include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" #include "Framework/DeviceSpec.h" #include "Framework/CCDBParamSpec.h" #include "DataFormatsITSMFT/Digit.h" @@ -28,8 +27,7 @@ #include "ITSMFTReconstruction/ClustererParam.h" #include "ITSMFTReconstruction/GBTLink.h" #include "ITSMFTWorkflow/STFDecoderSpec.h" -#include "DetectorsCommonDataFormats/DetectorNameConf.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DataFormatsITSMFT/CompCluster.h" #include "DetectorsCommonDataFormats/DetID.h" #include "CommonUtils/StringUtils.h" @@ -47,11 +45,18 @@ using namespace o2::framework; ///_______________________________________ template STFDecoder::STFDecoder(const STFDecoderInp& inp, std::shared_ptr gr) - : mDoClusters(inp.doClusters), mDoPatterns(inp.doPatterns), mDoDigits(inp.doDigits), mDoCalibData(inp.doCalib), mAllowReporting(inp.allowReporting), mVerifyDecoder(inp.verifyDecoder), mInputSpec(inp.inputSpec), mGGCCDBRequest(gr) + : mDoClusters(inp.doClusters), mDoPatterns(inp.doPatterns), mDoDigits(inp.doDigits), mDoCalibData(inp.doCalib), mDoStaggering(inp.doStaggering), mAllowReporting(inp.allowReporting), mVerifyDecoder(inp.verifyDecoder), mInputSpec(inp.inputSpec), mGGCCDBRequest(gr) { mSelfName = o2::utils::Str::concat_string(Mapping::getName(), "STFDecoder"); mTimer.Stop(); mTimer.Reset(); + if (mDoStaggering) { + mLayers = Mapping::NLayers; + mEstNDig.resize(mLayers, 0); + mEstNClus.resize(mLayers, 0); + mEstNClusPatt.resize(mLayers, 0); + mEstNCalib.resize(mLayers, 0); + } } ///_______________________________________ @@ -60,7 +65,6 @@ void STFDecoder::init(InitContext& ic) { o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); try { - mDecoder = std::make_unique>(); auto v0 = o2::utils::Str::tokenize(mInputSpec, ':'); auto v1 = o2::utils::Str::tokenize(v0[1], '/'); auto v2 = o2::utils::Str::tokenize(v1[1], '?'); @@ -68,9 +72,12 @@ void STFDecoder::init(InitContext& ic) header::DataDescription dataDesc; dataOrig.runtimeInit(v1[0].c_str()); dataDesc.runtimeInit(v2[0].c_str()); - mDecoder->setUserDataOrigin(dataOrig); - mDecoder->setUserDataDescription(dataDesc); - mDecoder->init(); // is this no-op? + for (int iLayer{0}; iLayer < mLayers; ++iLayer) { + auto& dec = mDecoder.emplace_back(std::make_unique>()); + dec->setUserDataOrigin(dataOrig); + dec->setUserDataDescription(dataDesc); + dec->init(); // is this no-op? + } } catch (const std::exception& e) { LOG(error) << "exception was thrown in decoder creation: " << e.what(); throw; @@ -81,10 +88,9 @@ void STFDecoder::init(InitContext& ic) mApplyNoiseMap = !ic.options().get("ignore-noise-map"); mUseClusterDictionary = !ic.options().get("ignore-cluster-dictionary"); try { - float fr = ic.options().get("rof-lenght-error-freq"); + float fr = ic.options().get("rof-length-error-freq"); mROFErrRepIntervalMS = fr <= 0. ? -1 : long(fr * 1e3); mNThreads = std::max(1, ic.options().get("nthreads")); - mDecoder->setNThreads(mNThreads); mUnmutExtraLanes = ic.options().get("unmute-extra-lanes"); mVerbosity = ic.options().get("decoder-verbosity"); auto dmpSz = ic.options().get("stop-raw-data-dumps-after-size"); @@ -103,13 +109,16 @@ void STFDecoder::init(InitContext& ic) if (mDumpOnError != int(GBTLink::RawDataDumps::DUMP_NONE) && (!dumpDir.empty() && !o2::utils::Str::pathIsDirectory(dumpDir))) { throw std::runtime_error(fmt::format("directory {} for raw data dumps does not exist", dumpDir)); } - mDecoder->setAlwaysParseTrigger(ic.options().get("always-parse-trigger")); - mDecoder->setAllowEmptyROFs(ic.options().get("allow-empty-rofs")); - mDecoder->setRawDumpDirectory(dumpDir); - mDecoder->setFillCalibData(mDoCalibData); - mDecoder->setVerifyDecoder(mVerifyDecoder); - bool ignoreRampUp = !ic.options().get("accept-rof-rampup-data"); - mDecoder->setSkipRampUpData(ignoreRampUp); + for (int iLayer{0}; iLayer < mLayers; ++iLayer) { + mDecoder[iLayer]->setNThreads(mNThreads); + mDecoder[iLayer]->setAlwaysParseTrigger(ic.options().get("always-parse-trigger")); + mDecoder[iLayer]->setAllowEmptyROFs(ic.options().get("allow-empty-rofs")); + mDecoder[iLayer]->setRawDumpDirectory(dumpDir); + mDecoder[iLayer]->setFillCalibData(mDoCalibData); + mDecoder[iLayer]->setVerifyDecoder(mVerifyDecoder); + bool ignoreRampUp = !ic.options().get("accept-rof-rampup-data"); + mDecoder[iLayer]->setSkipRampUpData(ignoreRampUp); + } } catch (const std::exception& e) { LOG(error) << "exception was thrown in decoder configuration: " << e.what(); throw; @@ -122,6 +131,17 @@ void STFDecoder::init(InitContext& ic) mClusterer = std::make_unique(); mClusterer->setNChips(Mapping::getNChips()); } + + if (mDoStaggering) { + Mapping map; + for (uint32_t iLayer{0}; iLayer < mLayers; ++iLayer) { + std::vector filter; + for (const auto feeID : map.getLayer2FEEIDs(iLayer)) { + filter.emplace_back("filter", ConcreteDataMatcher{Mapping::getOrigin(), o2::header::gDataDescriptionRawData, (o2::header::DataHeader::SubSpecificationType)feeID}); + } + mDecoder[iLayer]->setInputFilter(filter); + } + } } ///_______________________________________ @@ -135,141 +155,147 @@ void STFDecoder::run(ProcessingContext& pc) } if (firstCall) { firstCall = false; - mDecoder->setInstanceID(pc.services().get().inputTimesliceId); - mDecoder->setNInstances(pc.services().get().maxInputTimeslices); - mDecoder->setVerbosity(mDecoder->getInstanceID() == 0 ? mVerbosity : (mUnmutExtraLanes ? mVerbosity : -1)); - mAllowReporting &= (mDecoder->getInstanceID() == 0) || mUnmutExtraLanes; + for (int iLayer{0}; iLayer < mLayers; ++iLayer) { + mDecoder[iLayer]->setInstanceID(pc.services().get().inputTimesliceId); + mDecoder[iLayer]->setNInstances(pc.services().get().maxInputTimeslices); + mDecoder[iLayer]->setVerbosity(mDecoder[iLayer]->getInstanceID() == 0 ? mVerbosity : (mUnmutExtraLanes ? mVerbosity : -1)); + } + mAllowReporting &= (mDecoder[0]->getInstanceID() == 0) || mUnmutExtraLanes; } int nSlots = pc.inputs().getNofParts(0); double timeCPU0 = mTimer.CpuTime(), timeReal0 = mTimer.RealTime(); mTimer.Start(false); auto orig = Mapping::getOrigin(); - std::vector clusCompVec; - std::vector clusROFVec; - std::vector clusPattVec; - std::vector digVec; - std::vector calVec; - std::vector digROFVec; + // these are accumulated from each layer auto& chipStatus = pc.outputs().make>(Output{orig, "CHIPSSTATUS", 0}, (size_t)Mapping::getNChips()); + auto& linkErrors = pc.outputs().make>(Output{orig, "LinkErrors", 0}); + auto& decErrors = pc.outputs().make>(Output{orig, "ChipErrors", 0}); + auto& errMessages = pc.outputs().make>(Output{orig, "ErrorInfo", 0}); + auto& physTriggers = pc.outputs().make>(Output{orig, "PHYSTRIG", 0}); - try { - mDecoder->startNewTF(pc.inputs()); + for (uint32_t iLayer{0}; iLayer < mLayers; ++iLayer) { + const auto& par = AlpideParam::Instance(); + const int nROFsPerOrbit = o2::constants::lhc::LHCMaxBunches / par.getROFLengthInBC(iLayer); + const int nROFsTF = nROFsPerOrbit * o2::base::GRPGeomHelper::getNHBFPerTF(); + int nLayer = mDoStaggering ? iLayer : -1; + std::vector clusCompVec; + std::vector clusROFVec; + std::vector clusPattVec; + std::vector digVec; + std::vector calVec; + std::vector digROFVec; if (mDoDigits) { - digVec.reserve(mEstNDig); - digROFVec.reserve(mEstNROF); + digVec.reserve(mEstNDig[iLayer]); + digROFVec.reserve(nROFsTF); } if (mDoClusters) { - clusCompVec.reserve(mEstNClus); - clusROFVec.reserve(mEstNROF); - clusPattVec.reserve(mEstNClusPatt); + clusCompVec.reserve(mEstNClus[iLayer]); + clusROFVec.reserve(nROFsTF); + clusPattVec.reserve(mEstNClusPatt[iLayer]); } if (mDoCalibData) { - calVec.reserve(mEstNCalib); + calVec.reserve(mEstNCalib[iLayer]); } - mDecoder->setDecodeNextAuto(false); - o2::InteractionRecord lastIR{}, firstIR{0, pc.services().get().firstTForbit}; - int nTriggersProcessed = mDecoder->getNROFsProcessed(); - static long lastErrReportTS = 0; - while (mDecoder->decodeNextTrigger() >= 0) { - if ((!lastIR.isDummy() && lastIR >= mDecoder->getInteractionRecord()) || firstIR > mDecoder->getInteractionRecord()) { - const int MaxErrLog = 2; - static int errLocCount = 0; - if (errLocCount++ < MaxErrLog) { - LOGP(warn, "Impossible ROF IR {}, previous was {}, TF 1st IR was {}, discarding in decoding", mDecoder->getInteractionRecord().asString(), lastIR.asString(), firstIR.asString()); + try { + mDecoder[iLayer]->startNewTF(pc.inputs()); + mDecoder[iLayer]->setDecodeNextAuto(false); + + o2::InteractionRecord lastIR{}; + int nTriggersProcessed = mDecoder[iLayer]->getNROFsProcessed(); + static long lastErrReportTS = 0; + while (mDecoder[iLayer]->decodeNextTrigger() >= 0) { + if ((!lastIR.isDummy() && lastIR >= mDecoder[iLayer]->getInteractionRecord()) || mFirstIR > mDecoder[iLayer]->getInteractionRecord()) { + const int MaxErrLog = 2; + static int errLocCount = 0; + if (errLocCount++ < MaxErrLog) { + LOGP(warn, "Impossible ROF IR {}{}, previous was {}, TF 1st IR was {}, discarding in decoding", mDecoder[iLayer]->getInteractionRecord().asString(), ((mDoStaggering) ? std::format(" on layer {}", iLayer) : ""), lastIR.asString(), mFirstIR.asString()); + } + nTriggersProcessed = 0x7fffffff; // to account for a problem with event + continue; + } + lastIR = mDecoder[iLayer]->getInteractionRecord(); + mDecoder[iLayer]->fillChipsStatus(chipStatus); + if (mDoDigits || mClusterer->getMaxROFDepthToSquash(nLayer)) { // call before clusterization, since the latter will hide the digits + mDecoder[iLayer]->fillDecodedDigits(digVec, digROFVec); // lot of copying involved + if (mDoCalibData) { + mDecoder[iLayer]->fillCalibData(calVec); + } + } + if (mDoClusters && !mClusterer->getMaxROFDepthToSquash(nLayer)) { // !!! THREADS !!! + mClusterer->process(mNThreads, *mDecoder[iLayer].get(), &clusCompVec, mDoPatterns ? &clusPattVec : nullptr, &clusROFVec); } - nTriggersProcessed = 0x7fffffff; // to account for a problem with event - continue; } - lastIR = mDecoder->getInteractionRecord(); - mDecoder->fillChipsStatus(chipStatus); - if (mDoDigits || mClusterer->getMaxROFDepthToSquash()) { // call before clusterization, since the latter will hide the digits - mDecoder->fillDecodedDigits(digVec, digROFVec); // lot of copying involved - if (mDoCalibData) { - mDecoder->fillCalibData(calVec); + nTriggersProcessed = mDecoder[iLayer]->getNROFsProcessed() - nTriggersProcessed - 1; + + if ((nROFsTF != nTriggersProcessed) && mROFErrRepIntervalMS > 0 && mTFCounter > 1 && nTriggersProcessed > 0) { + long currTS = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + if (currTS - lastErrReportTS > mROFErrRepIntervalMS) { + LOGP(critical, "Inconsistent number of ROF per TF {}{} from parameters. Received {} from readout (muting further reporting for {} ms)", nROFsTF, ((mDoStaggering) ? std::format(" on layer {}", iLayer) : ""), nTriggersProcessed, mROFErrRepIntervalMS); + lastErrReportTS = currTS; } } - if (mDoClusters && !mClusterer->getMaxROFDepthToSquash()) { // !!! THREADS !!! - mClusterer->process(mNThreads, *mDecoder.get(), &clusCompVec, mDoPatterns ? &clusPattVec : nullptr, &clusROFVec); + if (mDoClusters && mClusterer->getMaxROFDepthToSquash(nLayer)) { + // Digits squashing require to run on a batch of digits and uses a digit reader, cannot (?) run with decoder + // - Setup decoder for running on a batch of digits + o2::itsmft::DigitPixelReader reader; + reader.setSquashingDepth(mClusterer->getMaxROFDepthToSquash(nLayer)); + reader.setSquashingDist(mClusterer->getMaxRowColDiffToMask()); // Sharing same parameter/logic with masking + reader.setMaxBCSeparationToSquash(mClusterer->getMaxBCSeparationToSquash(nLayer)); + reader.setDigits(digVec); + reader.setROFRecords(digROFVec); + reader.init(); + mClusterer->setMaxROFDepthToSquash(mClusterer->getMaxROFDepthToSquash(nLayer)); + mClusterer->process(mNThreads, reader, &clusCompVec, mDoPatterns ? &clusPattVec : nullptr, &clusROFVec); + } + } catch (const std::exception& e) { + static size_t nErr = 0; + auto maxWarn = o2::conf::VerbosityConfig::Instance().maxWarnRawParser; + if (++nErr < maxWarn) { + LOGP(alarm, "EXCEPTION {} in raw decoder{}, abandoning TF decoding {}", e.what(), ((mDoStaggering) ? std::format(" on layer {}", iLayer) : ""), nErr == maxWarn ? "(will mute further warnings)" : ""); } } - nTriggersProcessed = mDecoder->getNROFsProcessed() - nTriggersProcessed - 1; - - const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); - int expectedTFSize = static_cast(o2::constants::lhc::LHCMaxBunches * o2::base::GRPGeomHelper::instance().getGRPECS()->getNHBFPerTF() / alpParams.roFrameLengthInBC); // 3564*32 / ROF Length in BS = number of ROFs per TF - if ((expectedTFSize != nTriggersProcessed) && mROFErrRepIntervalMS > 0 && mTFCounter > 1 && nTriggersProcessed > 0) { - long currTS = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); - if (currTS - lastErrReportTS > mROFErrRepIntervalMS) { - LOGP(critical, "Inconsistent number of ROF per TF. From parameters: {} from readout: {} (muting further reporting for {} ms)", expectedTFSize, nTriggersProcessed, mROFErrRepIntervalMS); - lastErrReportTS = currTS; + if (mDoDigits) { + pc.outputs().snapshot(Output{orig, "DIGITS", iLayer}, digVec); + std::vector expDigRofVec(nROFsTF); + ensureContinuousROF(digROFVec, expDigRofVec, iLayer, nROFsTF, "digits"); + pc.outputs().snapshot(Output{orig, "DIGITSROF", iLayer}, digROFVec); + mEstNDig[iLayer] = std::max(mEstNDig[iLayer], size_t(digVec.size() * 1.2)); + if (mDoCalibData) { + pc.outputs().snapshot(Output{orig, "GBTCALIB", iLayer}, calVec); + mEstNCalib[iLayer] = std::max(mEstNCalib[iLayer], size_t(calVec.size() * 1.2)); } + LOG(debug) << mSelfName << " Decoded " << digVec.size() << " Digits in " << digROFVec.size() << " ROFs" << ((mDoStaggering) ? std::format(" on layer {}", iLayer) : ""); } - if (mDoClusters && mClusterer->getMaxROFDepthToSquash()) { - // Digits squashing require to run on a batch of digits and uses a digit reader, cannot (?) run with decoder - // - Setup decoder for running on a batch of digits - o2::itsmft::DigitPixelReader reader; - reader.setSquashingDepth(mClusterer->getMaxROFDepthToSquash()); - reader.setSquashingDist(mClusterer->getMaxRowColDiffToMask()); // Sharing same parameter/logic with masking - reader.setMaxBCSeparationToSquash(mClusterer->getMaxBCSeparationToSquash()); - reader.setDigits(digVec); - reader.setROFRecords(digROFVec); - reader.init(); - mClusterer->process(mNThreads, reader, &clusCompVec, mDoPatterns ? &clusPattVec : nullptr, &clusROFVec); - } - } catch (const std::exception& e) { - static size_t nErr = 0; - auto maxWarn = o2::conf::VerbosityConfig::Instance().maxWarnRawParser; - if (++nErr < maxWarn) { - LOGP(alarm, "EXCEPTION {} in raw decoder, abandoning TF decoding {}", e.what(), nErr == maxWarn ? "(will mute further warnings)" : ""); - } - } - if (mDoDigits) { - pc.outputs().snapshot(Output{orig, "DIGITS", 0}, digVec); - pc.outputs().snapshot(Output{orig, "DIGITSROF", 0}, digROFVec); - mEstNDig = std::max(mEstNDig, size_t(digVec.size() * 1.2)); - mEstNROF = std::max(mEstNROF, size_t(digROFVec.size() * 1.2)); - if (mDoCalibData) { - pc.outputs().snapshot(Output{orig, "GBTCALIB", 0}, calVec); - mEstNCalib = std::max(mEstNCalib, size_t(calVec.size() * 1.2)); + if (mDoClusters) { // we are not obliged to create vectors which are not requested, but other devices might not know the options of this one + std::vector expClusRofVec(nROFsTF); + ensureContinuousROF(clusROFVec, expClusRofVec, iLayer, nROFsTF, "clusters"); + pc.outputs().snapshot(Output{orig, "COMPCLUSTERS", iLayer}, clusCompVec); + pc.outputs().snapshot(Output{orig, "PATTERNS", iLayer}, clusPattVec); + pc.outputs().snapshot(Output{orig, "CLUSTERSROF", iLayer}, expClusRofVec); + mEstNClus[iLayer] = std::max(mEstNClus[iLayer], size_t(clusCompVec.size() * 1.2)); + mEstNClusPatt[iLayer] = std::max(mEstNClusPatt[iLayer], size_t(clusPattVec.size() * 1.2)); + LOG(info) << mSelfName << " Built " << clusCompVec.size() << " clusters in " << expClusRofVec.size() << " ROFs" << ((mDoStaggering) ? std::format(" on layer {}", iLayer) : ""); } - } - if (mDoClusters) { // we are not obliged to create vectors which are not requested, but other devices might not know the options of this one - pc.outputs().snapshot(Output{orig, "COMPCLUSTERS", 0}, clusCompVec); - pc.outputs().snapshot(Output{orig, "PATTERNS", 0}, clusPattVec); - pc.outputs().snapshot(Output{orig, "CLUSTERSROF", 0}, clusROFVec); - mEstNClus = std::max(mEstNClus, size_t(clusCompVec.size() * 1.2)); - mEstNClusPatt = std::max(mEstNClusPatt, size_t(clusPattVec.size() * 1.2)); - mEstNROF = std::max(mEstNROF, size_t(clusROFVec.size() * 1.2)); - } - auto& linkErrors = pc.outputs().make>(Output{orig, "LinkErrors", 0}); - auto& decErrors = pc.outputs().make>(Output{orig, "ChipErrors", 0}); - auto& errMessages = pc.outputs().make>(Output{orig, "ErrorInfo", 0}); - mDecoder->collectDecodingErrors(linkErrors, decErrors, errMessages); + mDecoder[iLayer]->collectDecodingErrors(linkErrors, decErrors, errMessages); + physTriggers.insert(physTriggers.end(), mDecoder[iLayer]->getExternalTriggers().begin(), mDecoder[iLayer]->getExternalTriggers().end()); - pc.outputs().snapshot(Output{orig, "PHYSTRIG", 0}, mDecoder->getExternalTriggers()); - - if (mDumpOnError != int(GBTLink::RawDataDumps::DUMP_NONE) && - (!mDumpFrom1stPipeline || pc.services().get().inputTimesliceId == 0)) { - mRawDumpedSize += mDecoder->produceRawDataDumps(mDumpOnError, pc.services().get()); - if (mRawDumpedSize > mMaxRawDumpsSize && mMaxRawDumpsSize > 0) { - LOGP(info, "Max total dumped size {} MB exceeded allowed limit, disabling further dumping", mRawDumpedSize / (1024 * 1024)); - mDumpOnError = int(GBTLink::RawDataDumps::DUMP_NONE); + if (mDumpOnError != int(GBTLink::RawDataDumps::DUMP_NONE) && + (!mDumpFrom1stPipeline || pc.services().get().inputTimesliceId == 0)) { + mRawDumpedSize += mDecoder[iLayer]->produceRawDataDumps(mDumpOnError, pc.services().get()); + if (mRawDumpedSize > mMaxRawDumpsSize && mMaxRawDumpsSize > 0) { + LOGP(info, "Max total dumped size {} MB exceeded allowed limit, disabling further dumping", mRawDumpedSize / (1024 * 1024)); + mDumpOnError = int(GBTLink::RawDataDumps::DUMP_NONE); + } } } - if (mDoClusters) { - LOG(debug) << mSelfName << " Built " << clusCompVec.size() << " clusters in " << clusROFVec.size() << " ROFs"; - } - if (mDoDigits) { - LOG(debug) << mSelfName << " Decoded " << digVec.size() << " Digits in " << digROFVec.size() << " ROFs"; - } mTimer.Stop(); auto tfID = pc.services().get().tfCounter; - LOG(debug) << mSelfName << " Total time for TF " << tfID << '(' << mTFCounter << ") : CPU: " << mTimer.CpuTime() - timeCPU0 << " Real: " << mTimer.RealTime() - timeReal0; mTFCounter++; } @@ -285,8 +311,11 @@ void STFDecoder::finalize() LOGF(info, "%s statistics:", mSelfName); LOGF(info, "%s Total STF decoding%s timing (w/o disk IO): Cpu: %.3e Real: %.3e s in %d slots", mSelfName, mDoClusters ? "/clustering" : "", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); - if (mDecoder && mAllowReporting) { - mDecoder->printReport(); + for (int iLayer{0}; iLayer < mLayers && mAllowReporting; ++iLayer) { + if (mDecoder[iLayer]) { + LOG_IF(info, mDoStaggering) << "Report for decoder of layer " << iLayer; + mDecoder[iLayer]->printReport(); + } } if (mClusterer) { mClusterer->print(); @@ -326,9 +355,17 @@ void STFDecoder::updateTimeDependentParams(ProcessingContext& pc) nROFsToSquash = 2 + int(clParams.maxSOTMUS / (rofBC * o2::constants::lhc::LHCBunchSpacingMUS)); // use squashing } mClusterer->setMaxROFDepthToSquash(clParams.maxBCDiffToSquashBias > 0 ? nROFsToSquash : 0); - mClusterer->print(); + if (mDoStaggering) { + for (int iLayer{0}; iLayer < mLayers; ++iLayer) { + mClusterer->addMaxBCSeparationToSquash(alpParams.getROFLengthInBC(iLayer) + clParams.getMaxBCDiffToSquashBias(iLayer)); + mClusterer->addMaxROFDepthToSquash((clParams.getMaxBCDiffToSquashBias(iLayer) > 0) ? 2 + int(clParams.maxSOTMUS / (alpParams.getROFLengthInBC(iLayer) * o2::constants::lhc::LHCBunchSpacingMUS)) : 0); + } + } + mClusterer->print(false); } } + mFirstTFOrbit = pc.services().get().firstTForbit; + mFirstIR = o2::InteractionRecord(0, mFirstTFOrbit); } ///_______________________________________ @@ -367,36 +404,105 @@ void STFDecoder::reset() mFinalizeDone = false; mTFCounter = 0; mTimer.Reset(); - if (mDecoder) { - mDecoder->reset(); + for (int iLayer{0}; iLayer < mLayers; ++iLayer) { + if (mDecoder[iLayer]) { + mDecoder[iLayer]->reset(); + } } if (mClusterer) { mClusterer->reset(); } } +///_______________________________________ +template +void STFDecoder::ensureContinuousROF(const std::vector& rofVec, std::vector& expROFVec, int lr, int nROFsTF, const char* name) +{ + const auto& par = AlpideParam::Instance(); + // ensure that the rof output is continuous + // we will preserve the digits/clusters as they are but the stray ROFs will be removed (leaving their clusters/digits unaddressed). + expROFVec.clear(); + expROFVec.resize(nROFsTF); + for (int iROF{0}; iROF < nROFsTF; ++iROF) { + auto& rof = expROFVec[iROF]; + int orb = iROF * par.getROFLengthInBC(lr) / o2::constants::lhc::LHCMaxBunches + mFirstTFOrbit; + int bc = iROF * par.getROFLengthInBC(lr) % o2::constants::lhc::LHCMaxBunches + par.getROFDelayInBC(lr); + o2::InteractionRecord ir(bc, orb); + rof.setBCData(ir); + rof.setROFrame(iROF); + rof.setNEntries(0); + rof.setFirstEntry(-1); + } + uint32_t prevEntry{0}; + for (const auto& rof : rofVec) { + const auto& ir = rof.getBCData(); + if (ir < mFirstIR) { + LOGP(warn, "Discard ROF {} preceding TF 1st orbit {}{}", ir.asString(), mFirstTFOrbit, ((mDoStaggering) ? std::format(" on layer {}", lr) : "")); + continue; + } + auto irToFirst = ir - mFirstIR; + if (irToFirst.toLong() - par.getROFDelayInBC(lr) < 0) { + LOGP(warn, "Discard ROF {} preceding TF 1st orbit {} due to imposed ROF delay{}", ir.asString(), mFirstTFOrbit, ((mDoStaggering) ? std::format(" on layer {}", lr) : "")); + continue; + } + irToFirst -= par.getROFDelayInBC(lr); + const long irROF = irToFirst.toLong() / par.getROFLengthInBC(lr); + if (irROF >= nROFsTF) { + LOGP(warn, "Discard ROF {} exceeding TF orbit range{}", ir.asString(), ((mDoStaggering) ? std::format(" on layer {}", lr) : "")); + continue; + } + auto& expROF = expROFVec[irROF]; + if (expROF.getNEntries() == 0) { + expROF.setFirstEntry(rof.getFirstEntry()); + expROF.setNEntries(rof.getNEntries()); + } else { + if (expROF.getNEntries() < rof.getNEntries()) { + LOGP(warn, "Repeating {} with {} {}, prefer to already processed instance with {} {}{}", rof.asString(), rof.getNEntries(), name, expROF.getNEntries(), name, ((mDoStaggering) ? std::format(" on layer {}", lr) : "")); + expROF.setFirstEntry(rof.getFirstEntry()); + expROF.setNEntries(rof.getNEntries()); + } else { + LOGP(warn, "Repeating {} with {} {}, discard preferring already processed instance with {} {}{}", rof.asString(), rof.getNEntries(), name, expROF.getNEntries(), name, ((mDoStaggering) ? std::format(" on layer {}", lr) : "")); + } + } + } + int prevFirst{0}; + for (auto& rof : expROFVec) { + if (rof.getFirstEntry() < 0) { + rof.setFirstEntry(prevFirst); + } + prevFirst = rof.getFirstEntry(); + } +} + ///_______________________________________ DataProcessorSpec getSTFDecoderSpec(const STFDecoderInp& inp) { std::vector outputs; auto inputs = o2::framework::select(inp.inputSpec.c_str()); - if (inp.doDigits) { - outputs.emplace_back(inp.origin, "DIGITS", 0, Lifetime::Timeframe); - outputs.emplace_back(inp.origin, "DIGITSROF", 0, Lifetime::Timeframe); - if (inp.doCalib) { - outputs.emplace_back(inp.origin, "GBTCALIB", 0, Lifetime::Timeframe); + uint32_t nLayers = 1; + if (inp.origin == o2::header::gDataOriginITS && inp.doStaggering) { + nLayers = DPLAlpideParam::getNLayers(); + } else if (inp.origin == o2::header::gDataOriginMFT && inp.doStaggering) { + nLayers = DPLAlpideParam::getNLayers(); + } + for (uint32_t iLayer = 0; iLayer < nLayers; ++iLayer) { + if (inp.doDigits) { + outputs.emplace_back(inp.origin, "DIGITS", iLayer, Lifetime::Timeframe); + outputs.emplace_back(inp.origin, "DIGITSROF", iLayer, Lifetime::Timeframe); + } + if (inp.doClusters) { + outputs.emplace_back(inp.origin, "COMPCLUSTERS", iLayer, Lifetime::Timeframe); + outputs.emplace_back(inp.origin, "CLUSTERSROF", iLayer, Lifetime::Timeframe); + // in principle, we don't need to open this input if we don't need to send real data, + // but other devices expecting it do not know about options of this device: problem? + // if (doClusters && doPatterns) + outputs.emplace_back(inp.origin, "PATTERNS", iLayer, Lifetime::Timeframe); } } - if (inp.doClusters) { - outputs.emplace_back(inp.origin, "COMPCLUSTERS", 0, Lifetime::Timeframe); - outputs.emplace_back(inp.origin, "CLUSTERSROF", 0, Lifetime::Timeframe); - // in principle, we don't need to open this input if we don't need to send real data, - // but other devices expecting it do not know about options of this device: problem? - // if (doClusters && doPatterns) - outputs.emplace_back(inp.origin, "PATTERNS", 0, Lifetime::Timeframe); + if (inp.doDigits && inp.doCalib) { + outputs.emplace_back(inp.origin, "GBTCALIB", 0, Lifetime::Timeframe); } outputs.emplace_back(inp.origin, "PHYSTRIG", 0, Lifetime::Timeframe); - outputs.emplace_back(inp.origin, "LinkErrors", 0, Lifetime::Timeframe); outputs.emplace_back(inp.origin, "ChipErrors", 0, Lifetime::Timeframe); outputs.emplace_back(inp.origin, "ErrorInfo", 0, Lifetime::Timeframe); @@ -424,11 +530,11 @@ DataProcessorSpec getSTFDecoderSpec(const STFDecoderInp& inp) true); // query only once all objects except mag.field return DataProcessorSpec{ - inp.deviceName, - inputs, - outputs, - inp.origin == o2::header::gDataOriginITS ? AlgorithmSpec{adaptFromTask>(inp, ggRequest)} : AlgorithmSpec{adaptFromTask>(inp, ggRequest)}, - Options{ + .name = inp.deviceName, + .inputs = inputs, + .outputs = outputs, + .algorithm = inp.origin == o2::header::gDataOriginITS ? AlgorithmSpec{adaptFromTask>(inp, ggRequest)} : AlgorithmSpec{adaptFromTask>(inp, ggRequest)}, + .options = Options{ {"nthreads", VariantType::Int, 1, {"Number of decoding/clustering threads"}}, {"decoder-verbosity", VariantType::Int, 0, {"Verbosity level (-1: silent, 0: errors, 1: headers, 2: data, 3: raw data dump) of 1st lane"}}, {"always-parse-trigger", VariantType::Bool, false, {"parse trigger word even if flags continuation of old trigger"}}, @@ -439,7 +545,7 @@ DataProcessorSpec getSTFDecoderSpec(const STFDecoderInp& inp) {"allow-empty-rofs", VariantType::Bool, false, {"record ROFs w/o any hit"}}, {"ignore-noise-map", VariantType::Bool, false, {"do not mask pixels flagged in the noise map"}}, {"accept-rof-rampup-data", VariantType::Bool, false, {"do not discard data during ROF ramp up"}}, - {"rof-lenght-error-freq", VariantType::Float, 60.f, {"do not report ROF lenght error more frequently than this value, disable if negative"}}, + {"rof-length-error-freq", VariantType::Float, 60.f, {"do not report ROF length error more frequently than this value, disable if negative"}}, {"ignore-cluster-dictionary", VariantType::Bool, false, {"do not use cluster dictionary, always store explicit patterns"}}}}; } diff --git a/Detectors/ITSMFT/common/workflow/src/digit-reader-workflow.cxx b/Detectors/ITSMFT/common/workflow/src/digit-reader-workflow.cxx index 71b4b82a14126..04453abe464b7 100644 --- a/Detectors/ITSMFT/common/workflow/src/digit-reader-workflow.cxx +++ b/Detectors/ITSMFT/common/workflow/src/digit-reader-workflow.cxx @@ -10,6 +10,7 @@ // or submit itself to any jurisdiction. #include "ITSMFTWorkflow/DigitReaderSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "CommonUtils/ConfigurableParam.h" #include "Framework/ConfigParamSpec.h" #include "Framework/CallbacksPolicy.h" @@ -34,6 +35,7 @@ void customize(std::vector& workflowOptions) ConfigParamSpec{"runmft", VariantType::Bool, false, {"expect MFT data"}}, ConfigParamSpec{"suppress-triggers-output", VariantType::Bool, false, {"suppress dummy triggers output"}}, ConfigParamSpec{"configKeyValues", VariantType::String, "", {"semicolon separated key=value strings"}}}; + o2::itsmft::DPLAlpideParamInitializer::addConfigOption(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -52,9 +54,11 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); if (cfgc.options().get("runmft")) { - wf.emplace_back(o2::itsmft::getMFTDigitReaderSpec(useMC, calib, withTriggers)); + bool doStag = o2::itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(cfgc); + wf.emplace_back(o2::itsmft::getMFTDigitReaderSpec(useMC, doStag, calib, withTriggers)); } else { - wf.emplace_back(o2::itsmft::getITSDigitReaderSpec(useMC, calib, withTriggers)); + bool doStag = o2::itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(cfgc); + wf.emplace_back(o2::itsmft::getITSDigitReaderSpec(useMC, doStag, calib, withTriggers)); } o2::raw::HBFUtilsInitializer hbfIni(cfgc, wf); return wf; diff --git a/Detectors/ITSMFT/common/workflow/src/digit-writer-workflow.cxx b/Detectors/ITSMFT/common/workflow/src/digit-writer-workflow.cxx index 2d4fbea9aef6c..98391846c49c8 100644 --- a/Detectors/ITSMFT/common/workflow/src/digit-writer-workflow.cxx +++ b/Detectors/ITSMFT/common/workflow/src/digit-writer-workflow.cxx @@ -10,6 +10,7 @@ // or submit itself to any jurisdiction. #include "ITSMFTWorkflow/DigitWriterSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "CommonUtils/ConfigurableParam.h" #include "Framework/ConfigParamSpec.h" #include "Framework/CompletionPolicyHelpers.h" @@ -32,7 +33,7 @@ void customize(std::vector& workflowOptions) ConfigParamSpec{"enable-calib-data", VariantType::Bool, false, {"enable writing GBT calibration data"}}, ConfigParamSpec{"runmft", VariantType::Bool, false, {"expect MFT data"}}, ConfigParamSpec{"configKeyValues", VariantType::String, "", {"semicolon separated key=value strings"}}}; - + o2::itsmft::DPLAlpideParamInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -49,9 +50,11 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); if (cfgc.options().get("runmft")) { - wf.emplace_back(o2::itsmft::getMFTDigitWriterSpec(useMC, true, calib)); + bool doStag = o2::itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(cfgc); + wf.emplace_back(o2::itsmft::getMFTDigitWriterSpec(useMC, doStag, true, calib)); } else { - wf.emplace_back(o2::itsmft::getITSDigitWriterSpec(useMC, true, calib)); + bool doStag = o2::itsmft::DPLAlpideParamInitializer::isITSStaggeringEnabled(cfgc); + wf.emplace_back(o2::itsmft::getITSDigitWriterSpec(useMC, doStag, true, calib)); } return wf; } diff --git a/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx b/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx index e0fc23ec70128..fed7268100428 100644 --- a/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx @@ -10,6 +10,7 @@ // or submit itself to any jurisdiction. #include "ITSMFTWorkflow/EntropyEncoderSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "CommonUtils/ConfigurableParam.h" #include "Framework/ConfigParamSpec.h" @@ -26,7 +27,7 @@ void customize(std::vector& workflowOptions) ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, ConfigParamSpec{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; - + o2::itsmft::DPLAlpideParamInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -41,9 +42,11 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); bool selIR = cfgc.options().get("select-ir-frames"); if (cfgc.options().get("runmft")) { - wf.emplace_back(o2::itsmft::getEntropyEncoderSpec("MFT", selIR, cfgc.options().get("ctf-dict"))); + bool doStag = o2::itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(cfgc); + wf.emplace_back(o2::itsmft::getMFTEntropyEncoderSpec(doStag, selIR, cfgc.options().get("ctf-dict"))); } else { - wf.emplace_back(o2::itsmft::getEntropyEncoderSpec("ITS", selIR, cfgc.options().get("ctf-dict"))); + bool doStag = o2::itsmft::DPLAlpideParamInitializer::isITSStaggeringEnabled(cfgc); + wf.emplace_back(o2::itsmft::getITSEntropyEncoderSpec(doStag, selIR, cfgc.options().get("ctf-dict"))); } return wf; } diff --git a/Detectors/ITSMFT/common/workflow/src/stf-decoder-workflow.cxx b/Detectors/ITSMFT/common/workflow/src/stf-decoder-workflow.cxx index 7b1b97ec0c4f5..219e8915e11f3 100644 --- a/Detectors/ITSMFT/common/workflow/src/stf-decoder-workflow.cxx +++ b/Detectors/ITSMFT/common/workflow/src/stf-decoder-workflow.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// 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. // @@ -12,6 +12,7 @@ #include "ITSMFTWorkflow/STFDecoderSpec.h" #include "CommonUtils/ConfigurableParam.h" #include "Framework/ConfigParamSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; @@ -33,7 +34,7 @@ void customize(std::vector& workflowOptions) ConfigParamSpec{"dataspec", VariantType::String, "", {"selection string for the input data, if not provided Raw:/RAWDATA with DET=ITS or MFT will be used"}}, ConfigParamSpec{"report-dds-collection-index", VariantType::Int, -1, {"number of dpl collection allowed to produce decoding report (-1 means no limit)"}}, ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; - + o2::itsmft::DPLAlpideParamInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -53,6 +54,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) inp.askSTFDist = !cfgc.options().get("ignore-dist-stf"); inp.verifyDecoder = cfgc.options().get("verify"); inp.inputSpec = cfgc.options().get("dataspec"); + // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); @@ -62,12 +64,14 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) } inp.origin = o2::header::gDataOriginMFT; inp.deviceName = "mft-stf-decoder"; + inp.doStaggering = o2::itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(cfgc); } else { if (inp.inputSpec.empty()) { inp.inputSpec = "itsRAW:ITS/RAWDATA"; } inp.origin = o2::header::gDataOriginITS; inp.deviceName = "its-stf-decoder"; + inp.doStaggering = o2::itsmft::DPLAlpideParamInitializer::isITSStaggeringEnabled(cfgc); } inp.allowReporting = true; diff --git a/Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx b/Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx index 2b8090af42648..919e76083f595 100644 --- a/Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx +++ b/Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx @@ -121,7 +121,7 @@ void TFReaderSpec::init(o2f::InitContext& ic) if (!mInput.fileRunTimeSpans.empty()) { loadRunTimeSpans(mInput.fileRunTimeSpans); } - mFileFetcher = std::make_unique(mInput.inpdata, mInput.tffileRegex, mInput.remoteRegex, mInput.copyCmd); + mFileFetcher = std::make_unique(mInput.inpdata, mInput.tffileRegex, mInput.remoteRegex, mInput.copyCmd, mInput.copyDir); mFileFetcher->setMaxFilesInQueue(mInput.maxFileCache); mFileFetcher->setMaxLoops(mInput.maxLoops); mFileFetcher->setFailThreshold(ic.options().get("fetch-failure-threshold")); diff --git a/Detectors/Raw/TFReaderDD/src/TFReaderSpec.h b/Detectors/Raw/TFReaderDD/src/TFReaderSpec.h index 9db18768c1bfe..2c1c62ecbb414 100644 --- a/Detectors/Raw/TFReaderDD/src/TFReaderSpec.h +++ b/Detectors/Raw/TFReaderDD/src/TFReaderSpec.h @@ -29,6 +29,7 @@ struct TFReaderInp { std::string detListNonRawOnly{}; std::string rawChannelConfig{}; std::string copyCmd{}; + std::string copyDir{}; std::string tffileRegex{}; std::string remoteRegex{}; std::string metricChannel{}; diff --git a/Detectors/Raw/TFReaderDD/src/tf-reader-workflow.cxx b/Detectors/Raw/TFReaderDD/src/tf-reader-workflow.cxx index bc682127b0d3f..b424353531de7 100644 --- a/Detectors/Raw/TFReaderDD/src/tf-reader-workflow.cxx +++ b/Detectors/Raw/TFReaderDD/src/tf-reader-workflow.cxx @@ -31,6 +31,7 @@ void customize(std::vector& workflowOptions) options.push_back(ConfigParamSpec{"loop", VariantType::Int, 0, {"loop N times (-1 = infinite)"}}); options.push_back(ConfigParamSpec{"delay", VariantType::Float, 0.f, {"delay in seconds between consecutive TFs sending"}}); options.push_back(ConfigParamSpec{"copy-cmd", VariantType::String, "alien_cp ?src file://?dst", {"copy command for remote files"}}); // Use "XrdSecPROTOCOL=sss,unix xrdcp -N root://eosaliceo2.cern.ch/?src ?dst" for direct EOS access + options.push_back(ConfigParamSpec{"copy-dir", VariantType::String, "/tmp/", {"copy base directory for remote files"}}); options.push_back(ConfigParamSpec{"tf-file-regex", VariantType::String, ".+\\.tf$", {"regex string to identify TF files"}}); options.push_back(ConfigParamSpec{"remote-regex", VariantType::String, "^(alien://|)/alice/data/.+", {"regex string to identify remote files"}}); // Use "^/eos/aliceo2/.+" for direct EOS access options.push_back(ConfigParamSpec{"tf-reader-verbosity", VariantType::Int, 0, {"verbosity level (1 or 2: check RDH, print DH/DPH for 1st or all slices, >2 print RDH)"}}); @@ -71,6 +72,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) rinp.delay_us = uint64_t(1e6 * configcontext.options().get("delay")); // delay in microseconds rinp.verbosity = configcontext.options().get("tf-reader-verbosity"); rinp.copyCmd = configcontext.options().get("copy-cmd"); + rinp.copyDir = configcontext.options().get("copy-dir"); rinp.tffileRegex = configcontext.options().get("tf-file-regex"); rinp.remoteRegex = configcontext.options().get("remote-regex"); rinp.sendDummyForMissing = !configcontext.options().get("disable-dummy-output"); diff --git a/Detectors/TRD/workflow/src/trd-tracking-workflow.cxx b/Detectors/TRD/workflow/src/trd-tracking-workflow.cxx index 1d8243ff8cbc0..7781b5ed187cb 100644 --- a/Detectors/TRD/workflow/src/trd-tracking-workflow.cxx +++ b/Detectors/TRD/workflow/src/trd-tracking-workflow.cxx @@ -26,6 +26,7 @@ #include "GlobalTrackingWorkflowHelpers/InputHelper.h" #include "TPCCalibration/CorrectionMapsLoader.h" #include "TPCWorkflow/TPCScalerSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using GTrackID = o2::dataformats::GlobalTrackID; @@ -62,6 +63,7 @@ void customize(std::vector& workflowOptions) {"disable-ft0-pileup-tagging", VariantType::Bool, false, {"Do not request FT0 for pile-up determination"}}, {"policy", VariantType::String, "default", {"Pick PID policy (=default)"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h index c07767d50b113..005237fe28839 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h @@ -37,8 +37,8 @@ class GeometryTGeo; /// TRK TimeFrame class that extends ITS TimeFrame functionality /// This allows for customization of tracking algorithms specific to the TRK detector -template -class TimeFrame : public o2::its::TimeFrame +template +class TimeFrame : public o2::its::TimeFrame { public: TimeFrame() = default; @@ -49,8 +49,6 @@ class TimeFrame : public o2::its::TimeFrame /// Process hits from TTree to initialize ROFs /// \param hitsTree Tree containing TRK hits - /// \param mcHeaderTree Tree containing MC event headers - /// \param nEvents Number of events to process /// \param gman TRK geometry manager instance /// \param config Configuration parameters for hit reconstruction int loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, const nlohmann::json& config); @@ -60,7 +58,8 @@ class TimeFrame : public o2::its::TimeFrame /// \param nRofs Number of ROFs (Read-Out Frames) /// \param nEvents Number of events to process /// \param inROFpileup Number of events per ROF - void getPrimaryVerticesFromMC(TTree* mcHeaderTree, int nRofs, Long64_t nEvents, int inROFpileup); + /// \param rofLength ROF length in BCs (must match what was used in loadROFsFromHitTree) + void getPrimaryVerticesFromMC(TTree* mcHeaderTree, int nRofs, Long64_t nEvents, int inROFpileup, uint32_t rofLength = 198); }; } // namespace trk diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx index 610a08450d5ee..957560aea8cae 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx @@ -23,11 +23,13 @@ #include #include +using o2::its::clearResizeBoundedVector; + namespace o2::trk { -template -int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, const nlohmann::json& config) +template +int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, const nlohmann::json& config) { constexpr std::array startLayer{0, 3}; const Long64_t nEvents = hitsTree->GetEntries(); @@ -39,23 +41,39 @@ int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, const int inROFpileup{config.contains("inROFpileup") ? config["inROFpileup"].get() : 1}; - // Calculate number of ROFs and initialize data structures - this->mNrof = (nEvents + inROFpileup - 1) / inROFpileup; + // Calculate number of ROFs + const int nRofs = (nEvents + inROFpileup - 1) / inROFpileup; + + // Set up ROF timing for all layers (no staggering in TRK simulation, all layers read out together) + constexpr uint32_t rofLength = 198; // ROF length in BC + o2::its::ROFOverlapTable overlapTable; + for (int iLayer = 0; iLayer < NLayers; ++iLayer) { + overlapTable.defineLayer(iLayer, nRofs, rofLength, 0, 0, 0); + } + overlapTable.init(); + this->setROFOverlapTable(overlapTable); + + // Set up the vertex lookup table timing (pre-allocate, vertices will be filled later) + o2::its::ROFVertexLookupTable vtxLookupTable; + for (int iLayer = 0; iLayer < NLayers; ++iLayer) { + vtxLookupTable.defineLayer(iLayer, nRofs, rofLength, 0, 0, 0); + } + vtxLookupTable.init(); // pre-allocate without vertices + this->setROFVertexLookupTable(vtxLookupTable); // Reset and prepare ROF data structures - for (int iLayer{0}; iLayer < nLayers; ++iLayer) { + for (int iLayer{0}; iLayer < NLayers; ++iLayer) { this->mMinR[iLayer] = std::numeric_limits::max(); this->mMaxR[iLayer] = std::numeric_limits::lowest(); this->mROFramesClusters[iLayer].clear(); - this->mROFramesClusters[iLayer].resize(this->mNrof + 1, 0); + this->mROFramesClusters[iLayer].resize(nRofs + 1, 0); this->mUnsortedClusters[iLayer].clear(); this->mTrackingFrameInfo[iLayer].clear(); this->mClusterExternalIndices[iLayer].clear(); } // Pre-count hits to reserve memory efficiently - int totalNHits{0}; - std::array clusterCountPerLayer{}; + std::array clusterCountPerLayer{}; for (Long64_t iEvent = 0; iEvent < nEvents; ++iEvent) { hitsTree->GetEntry(iEvent); for (const auto& hit : *trkHit) { @@ -64,25 +82,24 @@ int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, } int subDetID = gman->getSubDetID(hit.GetDetectorID()); const int layer = startLayer[subDetID] + gman->getLayer(hit.GetDetectorID()); - if (layer >= nLayers) { + if (layer >= NLayers) { continue; } ++clusterCountPerLayer[layer]; - totalNHits++; } } - // Reserve memory for all layers - for (int iLayer{0}; iLayer < nLayers; ++iLayer) { + // Reserve memory for all layers (mClusterSize is now per-layer) + for (int iLayer{0}; iLayer < NLayers; ++iLayer) { this->mUnsortedClusters[iLayer].reserve(clusterCountPerLayer[iLayer]); this->mTrackingFrameInfo[iLayer].reserve(clusterCountPerLayer[iLayer]); this->mClusterExternalIndices[iLayer].reserve(clusterCountPerLayer[iLayer]); + clearResizeBoundedVector(this->mClusterSize[iLayer], clusterCountPerLayer[iLayer], this->mMemoryPool.get()); } - clearResizeBoundedVector(this->mClusterSize, totalNHits, this->mMemoryPool.get()); std::array resolution{0.001, 0.001, 0.001, 0.001, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004}; - if (config["geometry"]["pitch"].size() == nLayers) { - for (int iLayer{0}; iLayer < config["geometry"]["pitch"].size(); ++iLayer) { + if (config["geometry"]["pitch"].size() == static_cast(NLayers)) { + for (size_t iLayer{0}; iLayer < config["geometry"]["pitch"].size(); ++iLayer) { LOGP(info, "Setting resolution for layer {} from config", iLayer); LOGP(info, "Layer {} pitch {} cm", iLayer, config["geometry"]["pitch"][iLayer].get()); resolution[iLayer] = config["geometry"]["pitch"][iLayer].get() / std::sqrt(12.f); @@ -90,9 +107,10 @@ int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, } LOGP(info, "Number of active parts in VD: {}", gman->getNumberOfActivePartsVD()); - int hitCounter{0}; - auto labels = new dataformats::MCTruthContainer(); + // One shared MC label container for all layers + auto* labels = new dataformats::MCTruthContainer(); + int hitCounter{0}; int iRof{0}; // Current ROF index for (Long64_t iEvent = 0; iEvent < nEvents; ++iEvent) { hitsTree->GetEntry(iEvent); @@ -108,7 +126,7 @@ int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, o2::math_utils::Point3D gloXYZ; o2::math_utils::Point3D trkXYZ; float r{0.f}; - if (layer >= nLayers) { + if (layer >= NLayers) { continue; } if (layer >= 3) { @@ -139,11 +157,12 @@ int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, std::array{trkXYZ.y(), trkXYZ.z()}, std::array{resolution[layer] * resolution[layer], 0., resolution[layer] * resolution[layer]}); /// Rotate to the global frame - this->addClusterToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), this->mUnsortedClusters[layer].size()); + const int clusterIdxInLayer = this->mUnsortedClusters[layer].size(); + this->addClusterToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), clusterIdxInLayer); this->addClusterExternalIndexToLayer(layer, hitCounter); MCCompLabel label{hit.GetTrackID(), static_cast(iEvent), 0}; labels->addElement(hitCounter, label); - this->mClusterSize[hitCounter] = 1; // For compatibility with cluster-based tracking, set cluster size to 1 for hits + this->mClusterSize[layer][clusterIdxInLayer] = 1; hitCounter++; } trkHit->clear(); @@ -154,21 +173,23 @@ int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, for (unsigned int iLayer{0}; iLayer < this->mUnsortedClusters.size(); ++iLayer) { this->mROFramesClusters[iLayer][iRof] = this->mUnsortedClusters[iLayer].size(); // effectively calculating an exclusive sum } - // Update primary vertices ROF structure } - this->mClusterLabels = labels; } - return this->mNrof; + + // Set the shared labels container for all layers + for (int iLayer = 0; iLayer < NLayers; ++iLayer) { + this->mClusterLabels[iLayer] = labels; + } + + return nRofs; } -template -void TimeFrame::getPrimaryVerticesFromMC(TTree* mcHeaderTree, int nRofs, Long64_t nEvents, int inROFpileup) +template +void TimeFrame::getPrimaryVerticesFromMC(TTree* mcHeaderTree, int nRofs, Long64_t nEvents, int inROFpileup, uint32_t rofLength) { auto mcheader = new o2::dataformats::MCEventHeader; mcHeaderTree->SetBranchAddress("MCEventHeader.", &mcheader); - this->mROFramesPV.clear(); - this->mROFramesPV.resize(nRofs + 1, 0); this->mPrimaryVertices.clear(); int iRof{0}; @@ -178,14 +199,24 @@ void TimeFrame::getPrimaryVerticesFromMC(TTree* mcHeaderTree, int nRofs vertex.setXYZ(mcheader->GetX(), mcheader->GetY(), mcheader->GetZ()); vertex.setNContributors(30); vertex.setChi2(0.f); - LOGP(debug, "ROF {}: Added primary vertex at ({}, {}, {})", iRof, mcheader->GetX(), mcheader->GetY(), mcheader->GetZ()); - this->mPrimaryVertices.push_back(vertex); + + // Set proper BC timestamp for vertex-ROF compatibility + // The vertex timestamp is set to the center of its ROF with half-ROF as error + const uint32_t rofCenter = static_cast(rofLength * iRof + rofLength / 2); + const uint16_t rofHalf = static_cast(rofLength / 2); + vertex.setTimeStamp({rofCenter, rofHalf}); + + LOGP(debug, "ROF {}: Added primary vertex at ({}, {}, {}) with BC timestamp [{}, +/-{}]", + iRof, mcheader->GetX(), mcheader->GetY(), mcheader->GetZ(), rofCenter, rofHalf); + this->addPrimaryVertex(vertex); if ((iEvent + 1) % inROFpileup == 0 || iEvent == nEvents - 1) { iRof++; - this->mROFramesPV[iRof] = this->mPrimaryVertices.size(); // effectively calculating an exclusive sum } } - this->mMultiplicityCutMask.resize(nRofs, true); /// all ROFs are valid with MC primary vertices. + this->mMultiplicityCutMask.resetMask(1u); /// all ROFs are valid with MC primary vertices. + + // Update the vertex lookup table with the newly added vertices + this->updateROFVertexLookupTable(); } // Explicit template instantiation for TRK with 11 layers diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx index 3801228422a62..c9d793a3ec78f 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx @@ -41,7 +41,6 @@ namespace o2 using namespace framework; namespace trk { -using Vertex = o2::dataformats::Vertex>; TrackerDPL::TrackerDPL(std::shared_ptr gr, bool isMC, @@ -92,18 +91,12 @@ std::vector TrackerDPL::createTrackingParamsFromCon if (paramConfig.contains("NLayers")) { params.NLayers = paramConfig["NLayers"].get(); } - if (paramConfig.contains("DeltaROF")) { - params.DeltaROF = paramConfig["DeltaROF"].get(); - } if (paramConfig.contains("ZBins")) { params.ZBins = paramConfig["ZBins"].get(); } if (paramConfig.contains("PhiBins")) { params.PhiBins = paramConfig["PhiBins"].get(); } - if (paramConfig.contains("nROFsPerIterations")) { - params.nROFsPerIterations = paramConfig["nROFsPerIterations"].get(); - } if (paramConfig.contains("ClusterSharing")) { params.ClusterSharing = paramConfig["ClusterSharing"].get(); } @@ -127,27 +120,21 @@ std::vector TrackerDPL::createTrackingParamsFromCon if (paramConfig.contains("TrackletMinPt")) { params.TrackletMinPt = paramConfig["TrackletMinPt"].get(); } - if (paramConfig.contains("TrackletsPerClusterLimit")) { - params.TrackletsPerClusterLimit = paramConfig["TrackletsPerClusterLimit"].get(); - } if (paramConfig.contains("CellDeltaTanLambdaSigma")) { params.CellDeltaTanLambdaSigma = paramConfig["CellDeltaTanLambdaSigma"].get(); } - if (paramConfig.contains("CellsPerClusterLimit")) { - params.CellsPerClusterLimit = paramConfig["CellsPerClusterLimit"].get(); - } if (paramConfig.contains("MaxChi2ClusterAttachment")) { params.MaxChi2ClusterAttachment = paramConfig["MaxChi2ClusterAttachment"].get(); } if (paramConfig.contains("MaxChi2NDF")) { params.MaxChi2NDF = paramConfig["MaxChi2NDF"].get(); } - if (paramConfig.contains("TrackFollowerNSigmaCutZ")) { - params.TrackFollowerNSigmaCutZ = paramConfig["TrackFollowerNSigmaCutZ"].get(); - } - if (paramConfig.contains("TrackFollowerNSigmaCutPhi")) { - params.TrackFollowerNSigmaCutPhi = paramConfig["TrackFollowerNSigmaCutPhi"].get(); - } + // if (paramConfig.contains("TrackFollowerNSigmaCutZ")) { + // params.TrackFollowerNSigmaCutZ = paramConfig["TrackFollowerNSigmaCutZ"].get(); + // } + // if (paramConfig.contains("TrackFollowerNSigmaCutPhi")) { + // params.TrackFollowerNSigmaCutPhi = paramConfig["TrackFollowerNSigmaCutPhi"].get(); + // } // Parse boolean parameters if (paramConfig.contains("UseDiamond")) { @@ -162,9 +149,9 @@ std::vector TrackerDPL::createTrackingParamsFromCon if (paramConfig.contains("ShiftRefToCluster")) { params.ShiftRefToCluster = paramConfig["ShiftRefToCluster"].get(); } - if (paramConfig.contains("FindShortTracks")) { - params.FindShortTracks = paramConfig["FindShortTracks"].get(); - } + // if (paramConfig.contains("FindShortTracks")) { + // params.FindShortTracks = paramConfig["FindShortTracks"].get(); + // } if (paramConfig.contains("PerPrimaryVertexProcessing")) { params.PerPrimaryVertexProcessing = paramConfig["PerPrimaryVertexProcessing"].get(); } @@ -177,18 +164,18 @@ std::vector TrackerDPL::createTrackingParamsFromCon if (paramConfig.contains("FataliseUponFailure")) { params.FataliseUponFailure = paramConfig["FataliseUponFailure"].get(); } - if (paramConfig.contains("UseTrackFollower")) { - params.UseTrackFollower = paramConfig["UseTrackFollower"].get(); - } - if (paramConfig.contains("UseTrackFollowerTop")) { - params.UseTrackFollowerTop = paramConfig["UseTrackFollowerTop"].get(); - } - if (paramConfig.contains("UseTrackFollowerBot")) { - params.UseTrackFollowerBot = paramConfig["UseTrackFollowerBot"].get(); - } - if (paramConfig.contains("UseTrackFollowerMix")) { - params.UseTrackFollowerMix = paramConfig["UseTrackFollowerMix"].get(); - } + // if (paramConfig.contains("UseTrackFollower")) { + // params.UseTrackFollower = paramConfig["UseTrackFollower"].get(); + // } + // if (paramConfig.contains("UseTrackFollowerTop")) { + // params.UseTrackFollowerTop = paramConfig["UseTrackFollowerTop"].get(); + // } + // if (paramConfig.contains("UseTrackFollowerBot")) { + // params.UseTrackFollowerBot = paramConfig["UseTrackFollowerBot"].get(); + // } + // if (paramConfig.contains("UseTrackFollowerMix")) { + // params.UseTrackFollowerMix = paramConfig["UseTrackFollowerMix"].get(); + // } if (paramConfig.contains("createArtefactLabels")) { params.createArtefactLabels = paramConfig["createArtefactLabels"].get(); } @@ -314,44 +301,37 @@ void TrackerDPL::run(ProcessingContext& pc) for (size_t iter{0}; iter < trackingParams.size(); ++iter) { LOGP(info, "{}", trackingParams[iter].asString()); timeFrame.initialise(iter, trackingParams[iter], 11, false); - itsTrackerTraits.computeLayerTracklets(iter, -1, -1); + itsTrackerTraits.computeLayerTracklets(iter, -1); LOGP(info, "Number of tracklets in iteration {}: {}", iter, timeFrame.getNumberOfTracklets()); itsTrackerTraits.computeLayerCells(iter); LOGP(info, "Number of cells in iteration {}: {}", iter, timeFrame.getNumberOfCells()); itsTrackerTraits.findCellsNeighbours(iter); LOGP(info, "Number of cell neighbours in iteration {}: {}", iter, timeFrame.getNumberOfNeighbours()); itsTrackerTraits.findRoads(iter); - LOGP(info, "Number of roads in iteration {}: {}", iter, timeFrame.getNumberOfTracks()); - itsTrackerTraits.extendTracks(iter); + LOGP(info, "Number of tracks in iteration {}: {}", iter, timeFrame.getNumberOfTracks()); } const auto trackingLoopElapsedMs = std::chrono::duration_cast(std::chrono::steady_clock::now() - trackingLoopStart).count(); LOGP(info, "Tracking iterations block took {} ms", trackingLoopElapsedMs); itsTracker.computeTracksMClabels(); - // Stream tracks and their MC labels to the output - // Collect all tracks and labels from all ROFs - std::vector allTracks; - std::vector allLabels; + // Collect tracks and labels (flat vectors in the new interface) + const auto& tracks = timeFrame.getTracks(); + const auto& labels = timeFrame.getTracksLabel(); - int totalTracks = 0; + // Copy to output vectors (TrackITSExt -> TrackITS slicing for output compatibility) + std::vector allTracks(tracks.begin(), tracks.end()); + std::vector allLabels(labels.begin(), labels.end()); + + int totalTracks = allTracks.size(); int goodTracks = 0; int fakeTracks = 0; - for (int iRof = 0; iRof < nRofs; ++iRof) { - const auto& rofTracks = timeFrame.getTracks(iRof); - const auto& rofLabels = timeFrame.getTracksLabel(iRof); - - allTracks.insert(allTracks.end(), rofTracks.begin(), rofTracks.end()); - allLabels.insert(allLabels.end(), rofLabels.begin(), rofLabels.end()); - - totalTracks += rofTracks.size(); - for (const auto& label : rofLabels) { - if (label.isFake()) { - fakeTracks++; - } else { - goodTracks++; - } + for (const auto& label : allLabels) { + if (label.isFake()) { + fakeTracks++; + } else { + goodTracks++; } } diff --git a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/TrackingInterface.h b/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/TrackingInterface.h index 931628f2cf876..3b743c59524d2 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/TrackingInterface.h +++ b/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/TrackingInterface.h @@ -31,6 +31,7 @@ class ITS3TrackingInterface final : public its::ITSTrackingInterface void loadROF(gsl::span& trackROFspan, gsl::span clusters, gsl::span::iterator& pattIt, + int layer, const dataformats::MCTruthContainer* mcLabels) final; private: diff --git a/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx b/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx index 0fea07743b3df..92e36cd2a4b84 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx +++ b/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx @@ -66,8 +66,8 @@ int loadROFrameDataITS3(its::TimeFrame<7>* tf, auto geom = its::GeometryTGeo::Instance(); geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); - tf->resetROFrameData(rofs.size()); - tf->prepareROFrameData(rofs, clusters); + // tf->resetROFrameData(rofs.size()); // FIXME + // tf->prepareROFrameData(rofs, clusters); FIXME its::bounded_vector clusterSizeVec(clusters.size(), tf->getMemoryPool().get()); @@ -115,7 +115,7 @@ int loadROFrameDataITS3(its::TimeFrame<7>* tf, } } - tf->setClusterSize(clusterSizeVec); + // tf->setClusterSize(clusterSizeVec); FIXME for (auto& v : tf->mNTrackletsPerCluster) { v.resize(tf->getUnsortedClusters()[1].size()); @@ -125,8 +125,8 @@ int loadROFrameDataITS3(its::TimeFrame<7>* tf, } if (mcLabels != nullptr) { - tf->mClusterLabels = mcLabels; + // tf->mClusterLabels = mcLabels; // FIXME } - return tf->mNrof; + return 0; } } // namespace o2::its3::ioutils diff --git a/Detectors/Upgrades/ITS3/reconstruction/src/TrackingInterface.cxx b/Detectors/Upgrades/ITS3/reconstruction/src/TrackingInterface.cxx index 0f5c66a7f9663..9fe6f3735a845 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/src/TrackingInterface.cxx +++ b/Detectors/Upgrades/ITS3/reconstruction/src/TrackingInterface.cxx @@ -13,7 +13,7 @@ #include "ITS3Reconstruction/IOUtils.h" #include "ITSBase/GeometryTGeo.h" #include "ITStracking/TrackingConfigParam.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DetectorsBase/GRPGeomHelper.h" #include "Framework/DeviceSpec.h" @@ -77,9 +77,10 @@ void ITS3TrackingInterface::finaliseCCDB(framework::ConcreteDataMatcher& matcher void ITS3TrackingInterface::loadROF(gsl::span& trackROFspan, gsl::span clusters, gsl::span::iterator& pattIt, + int layer, const dataformats::MCTruthContainer* mcLabels) { - ioutils::loadROFrameDataITS3(mTimeFrame, trackROFspan, clusters, pattIt, mDict, mcLabels); + // ioutils::loadROFrameDataITS3(mTimeFrame, trackROFspan, clusters, pattIt, mDict, mcLabels); } } // namespace o2::its3 diff --git a/Detectors/Upgrades/ITS3/workflow/src/ClustererSpec.cxx b/Detectors/Upgrades/ITS3/workflow/src/ClustererSpec.cxx index f0238b74a3502..73b5f4650d02d 100644 --- a/Detectors/Upgrades/ITS3/workflow/src/ClustererSpec.cxx +++ b/Detectors/Upgrades/ITS3/workflow/src/ClustererSpec.cxx @@ -27,7 +27,7 @@ #include "DataFormatsITSMFT/ROFRecord.h" #include "DataFormatsParameters/GRPObject.h" #include "ITSMFTReconstruction/DigitPixelReader.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "CommonConstants/LHCConstants.h" using namespace o2::framework; diff --git a/Detectors/Upgrades/ITS3/workflow/src/RecoWorkflow.cxx b/Detectors/Upgrades/ITS3/workflow/src/RecoWorkflow.cxx index 60fe4fabfe481..f27fda19fe00c 100644 --- a/Detectors/Upgrades/ITS3/workflow/src/RecoWorkflow.cxx +++ b/Detectors/Upgrades/ITS3/workflow/src/RecoWorkflow.cxx @@ -40,7 +40,7 @@ framework::WorkflowSpec getWorkflow(bool useMC, its::TrackingMode::Type trmode, } if (!disableRootOutput) { - specs.emplace_back(o2::itsmft::getITSClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getITSClusterWriterSpec(useMC, false)); } if (trmode != its::TrackingMode::Off) { diff --git a/Detectors/Upgrades/ITS3/workflow/src/TrackerSpec.cxx b/Detectors/Upgrades/ITS3/workflow/src/TrackerSpec.cxx index 0326c12f804e0..8db02d7227e7f 100644 --- a/Detectors/Upgrades/ITS3/workflow/src/TrackerSpec.cxx +++ b/Detectors/Upgrades/ITS3/workflow/src/TrackerSpec.cxx @@ -23,7 +23,7 @@ #include "DataFormatsITSMFT/PhysTrigger.h" #include "ITStracking/TrackingConfigParam.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "ITSBase/GeometryTGeo.h" #include "CommonDataFormat/IRFrame.h" @@ -46,7 +46,7 @@ TrackerDPL::TrackerDPL(std::shared_ptr gr, const bool overrBeamEst, o2::gpu::gpudatatypes::DeviceType dType) : mGGCCDBRequest(gr), mRecChain{o2::gpu::GPUReconstruction::CreateInstance(dType, true)}, - mITS3TrackingInterface{isMC, trgType, overrBeamEst} + mITS3TrackingInterface{isMC, false, trgType, overrBeamEst} { mITS3TrackingInterface.setTrackingMode(trMode); } diff --git a/Detectors/Vertexing/include/DetectorsVertexing/PVertexer.h b/Detectors/Vertexing/include/DetectorsVertexing/PVertexer.h index c06c2119b0cd1..cdf83603258cd 100644 --- a/Detectors/Vertexing/include/DetectorsVertexing/PVertexer.h +++ b/Detectors/Vertexing/include/DetectorsVertexing/PVertexer.h @@ -31,7 +31,7 @@ #include "DetectorsVertexing/PVertexerParams.h" #include "ReconstructionDataFormats/GlobalTrackID.h" #include "DataFormatsCalibration/MeanVertexObject.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "gsl/span" #include #include diff --git a/Detectors/Vertexing/src/VertexTrackMatcher.cxx b/Detectors/Vertexing/src/VertexTrackMatcher.cxx index 8612187c0bffc..f66d2b8c4d347 100644 --- a/Detectors/Vertexing/src/VertexTrackMatcher.cxx +++ b/Detectors/Vertexing/src/VertexTrackMatcher.cxx @@ -15,7 +15,7 @@ #include "DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h" #include "DetectorsVertexing/VertexTrackMatcher.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include #include diff --git a/Detectors/Vertexing/test/PVFromPool.C b/Detectors/Vertexing/test/PVFromPool.C index 7bca9c03bf42f..248cbda401eca 100644 --- a/Detectors/Vertexing/test/PVFromPool.C +++ b/Detectors/Vertexing/test/PVFromPool.C @@ -1,3 +1,14 @@ +// 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. + #if !defined(__CLING__) || defined(__ROOTCLING__) #include "DetectorsVertexing/PVertexer.h" @@ -11,7 +22,7 @@ #include "DataFormatsParameters/GRPECSObject.h" #include "DataFormatsParameters/GRPMagField.h" #include "DetectorsBase/Propagator.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "CCDB/BasicCCDBManager.h" #include diff --git a/EventVisualisation/Workflow/src/EveWorkflowHelper.cxx b/EventVisualisation/Workflow/src/EveWorkflowHelper.cxx index 2bb3c220d67a0..b4f7655648001 100644 --- a/EventVisualisation/Workflow/src/EveWorkflowHelper.cxx +++ b/EventVisualisation/Workflow/src/EveWorkflowHelper.cxx @@ -32,7 +32,7 @@ #include "MCHTracking/TrackParam.h" #include "MCHTracking/TrackExtrap.h" #include "DataFormatsITSMFT/TrkClusRef.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "CommonDataFormat/IRFrame.h" #include "MFTBase/GeometryTGeo.h" #include "ITSBase/GeometryTGeo.h" diff --git a/EventVisualisation/Workflow/src/O2DPLDisplay.cxx b/EventVisualisation/Workflow/src/O2DPLDisplay.cxx index bd8ab5a664d99..828892ea97406 100644 --- a/EventVisualisation/Workflow/src/O2DPLDisplay.cxx +++ b/EventVisualisation/Workflow/src/O2DPLDisplay.cxx @@ -37,6 +37,7 @@ #include "DataFormatsMCH/ROFRecord.h" #include #include "DataFormatsMCH/Cluster.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include using std::chrono::duration_cast; @@ -78,7 +79,7 @@ void customize(std::vector& workflowOptions) {"configKeyValues", VariantType::String, "", {"semicolon separated key=value strings, e.g. EveConfParam content..."}}, {"skipOnEmptyInput", VariantType::Bool, false, {"don't run the ED when no input is provided"}}, }; - + o2::itsmft::DPLAlpideParamInitializer::addConfigOption(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } diff --git a/GPU/GPUTracking/Base/GPUReconstructionIncludesITS.h b/GPU/GPUTracking/Base/GPUReconstructionIncludesITS.h index 813e0aef2d1aa..36a2b3ebca103 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionIncludesITS.h +++ b/GPU/GPUTracking/Base/GPUReconstructionIncludesITS.h @@ -21,7 +21,6 @@ #include "ITStracking/TimeFrame.h" #if defined(__CUDACC__) || defined(__HIPCC__) #include "ITStrackingGPU/TrackerTraitsGPU.h" -#include "ITStrackingGPU/VertexerTraitsGPU.h" #include "ITStrackingGPU/TimeFrameGPU.h" #endif #else @@ -39,10 +38,6 @@ template class TimeFrame { }; -template -class VertexerTraitsGPU : public VertexerTraits -{ -}; template class TrackerTraitsGPU : public TrackerTraits { diff --git a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu index c919581eefdde..eb49a02fbb946 100644 --- a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu +++ b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu @@ -94,15 +94,13 @@ GPUReconstruction* GPUReconstruction_Create_CUDA(const GPUSettingsDeviceBackend& void GPUReconstructionCUDA::GetITSTraits(std::unique_ptr>* trackerTraits, std::unique_ptr>* vertexerTraits, std::unique_ptr>* timeFrame) { if (trackerTraits) { - trackerTraits->reset(new o2::its::TrackerTraitsGPU); + trackerTraits->reset(new o2::its::TrackerTraitsGPU<7>); } if (vertexerTraits) { vertexerTraits->reset(new o2::its::VertexerTraits<7>); - // TODO gpu-code to be implemented then remove line above and uncomment line below - // vertexerTraits->reset(new o2::its::VertexerTraitsGPU<7>); } if (timeFrame) { - timeFrame->reset(new o2::its::gpu::TimeFrameGPU); + timeFrame->reset(new o2::its::gpu::TimeFrameGPU<7>); } } diff --git a/GPU/GPUTracking/Global/GPUChainITS.h b/GPU/GPUTracking/Global/GPUChainITS.h index 4aa97f3f47784..ee466365a157d 100644 --- a/GPU/GPUTracking/Global/GPUChainITS.h +++ b/GPU/GPUTracking/Global/GPUChainITS.h @@ -19,9 +19,6 @@ namespace o2::its { struct Cluster; -template -class Road; -class Cell; struct TrackingFrameInfo; class TrackITSExt; class GPUFrameworkExternalAllocator; diff --git a/GPU/GPUTracking/display/render/GPUDisplayImportEvent.cxx b/GPU/GPUTracking/display/render/GPUDisplayImportEvent.cxx index 9c516ebb960d7..658cdc46cb6cb 100644 --- a/GPU/GPUTracking/display/render/GPUDisplayImportEvent.cxx +++ b/GPU/GPUTracking/display/render/GPUDisplayImportEvent.cxx @@ -31,7 +31,7 @@ #include "TOFBase/Geo.h" #include "ITSBase/GeometryTGeo.h" #ifdef GPUCA_O2_LIB -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #endif #include diff --git a/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h b/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h index 8dfbdaff7272f..c5e4124c41650 100644 --- a/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h +++ b/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h @@ -132,6 +132,7 @@ class GPURecoWorkflowSpec : public o2::framework::Task int32_t lumiScaleType = 0; // 0=off, 1=CTP, 2=TPC scalers bool outputErrorQA = false; bool runITSTracking = false; + bool itsStaggered = false; bool itsOverrBeamEst = false; bool tpcTriggerHandling = false; bool isITS3 = false; diff --git a/GPU/Workflow/src/GPUWorkflowITS.cxx b/GPU/Workflow/src/GPUWorkflowITS.cxx index 46e1b1578285c..fb27df2ec08b9 100644 --- a/GPU/Workflow/src/GPUWorkflowITS.cxx +++ b/GPU/Workflow/src/GPUWorkflowITS.cxx @@ -52,18 +52,21 @@ void GPURecoWorkflowSpec::initFunctionITS(o2::framework::InitContext& ic) #ifdef ENABLE_UPGRADES if (mSpecConfig.isITS3) { mITSTrackingInterface = std::make_unique(mSpecConfig.processMC, + mSpecConfig.itsStaggered, mSpecConfig.itsTriggerType, mSpecConfig.itsOverrBeamEst); - } else -#endif - { + } else { mITSTrackingInterface = std::make_unique(mSpecConfig.processMC, + mSpecConfig.itsStaggered, mSpecConfig.itsTriggerType, mSpecConfig.itsOverrBeamEst); } +#else mITSTrackingInterface = std::make_unique(mSpecConfig.processMC, + mSpecConfig.itsStaggered, mSpecConfig.itsTriggerType, mSpecConfig.itsOverrBeamEst); +#endif mGPUReco->GetITSTraits(trkTraits, vtxTraits, mITSTimeFrame); mITSTrackingInterface->setTraitsFromProvider(vtxTraits, trkTraits, mITSTimeFrame); } diff --git a/GPU/Workflow/src/GPUWorkflowSpec.cxx b/GPU/Workflow/src/GPUWorkflowSpec.cxx index dbb554a14cea4..4b1aa7fd58bd5 100644 --- a/GPU/Workflow/src/GPUWorkflowSpec.cxx +++ b/GPU/Workflow/src/GPUWorkflowSpec.cxx @@ -1229,9 +1229,14 @@ Inputs GPURecoWorkflowSpec::inputs() } if (mSpecConfig.runITSTracking) { - inputs.emplace_back("compClusters", "ITS", "COMPCLUSTERS", 0, Lifetime::Timeframe); - inputs.emplace_back("patterns", "ITS", "PATTERNS", 0, Lifetime::Timeframe); - inputs.emplace_back("ROframes", "ITS", "CLUSTERSROF", 0, Lifetime::Timeframe); + for (unsigned int iLay{0}; iLay < (mSpecConfig.itsStaggered ? 7 : 1); ++iLay) { + inputs.emplace_back("compClusters", "ITS", "COMPCLUSTERS", iLay, Lifetime::Timeframe); + inputs.emplace_back("patterns", "ITS", "PATTERNS", iLay, Lifetime::Timeframe); + inputs.emplace_back("ROframes", "ITS", "CLUSTERSROF", iLay, Lifetime::Timeframe); + if (mSpecConfig.processMC) { + inputs.emplace_back("itsmclabels", "ITS", "CLUSTERSMCTR", iLay, Lifetime::Timeframe); + } + } if (mSpecConfig.itsTriggerType == 1) { inputs.emplace_back("phystrig", "ITS", "PHYSTRIG", 0, Lifetime::Timeframe); } else if (mSpecConfig.itsTriggerType == 2) { @@ -1249,10 +1254,6 @@ Inputs GPURecoWorkflowSpec::inputs() inputs.emplace_back("meanvtx", "GLO", "MEANVERTEX", 0, Lifetime::Condition, ccdbParamSpec("GLO/Calib/MeanVertex", {}, 1)); } } - if (mSpecConfig.processMC) { - inputs.emplace_back("itsmclabels", "ITS", "CLUSTERSMCTR", 0, Lifetime::Timeframe); - inputs.emplace_back("ITSMC2ROframes", "ITS", "CLUSTERSMC2ROF", 0, Lifetime::Timeframe); - } } // NN clusterizer @@ -1388,7 +1389,6 @@ Outputs GPURecoWorkflowSpec::outputs() outputSpecs.emplace_back(gDataOriginITS, "VERTICESMCTR", 0, Lifetime::Timeframe); outputSpecs.emplace_back(gDataOriginITS, "VERTICESMCPUR", 0, Lifetime::Timeframe); outputSpecs.emplace_back(gDataOriginITS, "TRACKSMCTR", 0, Lifetime::Timeframe); - outputSpecs.emplace_back(gDataOriginITS, "ITSTrackMC2ROF", 0, Lifetime::Timeframe); } } diff --git a/GPU/Workflow/src/O2GPUDPLDisplay.cxx b/GPU/Workflow/src/O2GPUDPLDisplay.cxx index 8513541bcae43..ed0d522b4d7ea 100644 --- a/GPU/Workflow/src/O2GPUDPLDisplay.cxx +++ b/GPU/Workflow/src/O2GPUDPLDisplay.cxx @@ -34,6 +34,7 @@ #include "GPUWorkflowHelper/GPUWorkflowHelper.h" #include "DataFormatsITSMFT/TopologyDictionary.h" #include "DetectorsRaw/HBFUtils.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using namespace o2::dataformats; @@ -53,7 +54,7 @@ void customize(std::vector& workflowOptions) {"read-from-files", o2::framework::VariantType::Bool, false, {"Automatically create readers for input"}}, {"disable-root-input", o2::framework::VariantType::Bool, false, {"Disable root input overriding read-from-files"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; - + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); std::swap(workflowOptions, options); } diff --git a/GPU/Workflow/src/gpu-reco-workflow.cxx b/GPU/Workflow/src/gpu-reco-workflow.cxx index e620d013cc925..13e28a1c341b3 100644 --- a/GPU/Workflow/src/gpu-reco-workflow.cxx +++ b/GPU/Workflow/src/gpu-reco-workflow.cxx @@ -29,6 +29,7 @@ #include "GlobalTrackingWorkflowHelpers/InputHelper.h" #include "ReconstructionDataFormats/GlobalTrackID.h" #include "TPCCalibration/CorrectionMapsLoader.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include #include @@ -66,6 +67,7 @@ void customize(std::vector& workflowOptions) }; o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); std::swap(workflowOptions, options); } @@ -190,6 +192,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) cfg.tpcDeadMapSources = cfgc.options().get("tpc-deadMap-sources"); cfg.tpcUseMCTimeGain = cfgc.options().get("tpc-mc-time-gain"); cfg.runITSTracking = isEnabled(outputTypes, ioType::ITSTracks); + cfg.itsStaggered = o2::itsmft::DPLAlpideParamInitializer::isITSStaggeringEnabled(cfgc); cfg.itsOverrBeamEst = isEnabled(inputTypes, ioType::MeanVertex); cfg.useFilteredOutputSpecs = cfgc.options().get("filtered-output-specs"); diff --git a/Steer/DigitizerWorkflow/src/ITS3DigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/ITS3DigitizerSpec.cxx index 60a1660288b9d..19552a407ec57 100644 --- a/Steer/DigitizerWorkflow/src/ITS3DigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/ITS3DigitizerSpec.cxx @@ -28,7 +28,7 @@ #include "ITS3Simulation/Digitizer.h" #include "ITSMFTSimulation/DPLDigitizerParam.h" #include "ITS3Simulation/ITS3DPLDigitizerParam.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "ITSBase/GeometryTGeo.h" #include "ITS3Base/ITS3Params.h" diff --git a/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx index eafb72c675a58..a4c401bbf8b42 100644 --- a/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx @@ -13,7 +13,6 @@ #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/DataProcessorSpec.h" -#include "Framework/DataRefUtils.h" #include "Framework/Lifetime.h" #include "Framework/Task.h" #include "Framework/CCDBParamSpec.h" @@ -26,18 +25,16 @@ #include "DetectorsRaw/HBFUtils.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsCommonDataFormats/SimTraits.h" -#include "DetectorsCommonDataFormats/DetectorNameConf.h" #include "DataFormatsParameters/GRPObject.h" #include "DataFormatsITSMFT/ROFRecord.h" #include "ITSMFTSimulation/Digitizer.h" #include "ITSMFTSimulation/DPLDigitizerParam.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "ITSBase/GeometryTGeo.h" #include "MFTBase/GeometryTGeo.h" #include #include #include -#include using namespace o2::framework; using SubSpecificationType = o2::framework::DataAllocator::SubSpecificationType; @@ -52,13 +49,20 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer public: static constexpr o2::detectors::DetID ID{N == o2::detectors::DetID::ITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT}; static constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; - static constexpr int NLayers{o2::itsmft::DPLAlpideParam::getNLayers()}; using BaseDPLDigitizer::init; void initDigitizerTask(framework::InitContext& ic) override { mDisableQED = ic.options().get("disable-qed"); + if (mDoStaggering) { + mLayers = DPLAlpideParam::getNLayers(); + } + mDigits.resize(mLayers); + mROFRecords.resize(mLayers); + mROFRecordsAccum.resize(mLayers); + mLabels.resize(mLayers); + mLabelsAccum.resize(mLayers); } void run(framework::ProcessingContext& pc) @@ -89,9 +93,8 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer } uint64_t nDigits{0}; - constexpr uint32_t nLayers = (DPLAlpideParam::supportsStaggering()) ? NLayers : 1; - for (uint32_t iLayer = 0; iLayer < nLayers; ++iLayer) { - const int layer = (DPLAlpideParam::supportsStaggering()) ? iLayer : -1; + for (uint32_t iLayer = 0; iLayer < mLayers; ++iLayer) { + const int layer = (mDoStaggering) ? iLayer : -1; mDigitizer.setDigits(&mDigits[iLayer]); mDigitizer.setROFRecords(&mROFRecords[iLayer]); mDigitizer.setMCLabels(&mLabels[iLayer]); @@ -121,25 +124,13 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer auto& rof = mROFRecords[iLayer][i]; rof.setFirstEntry(ndigAcc + rof.getFirstEntry()); rof.print(); - - if (mFixMC2ROF[iLayer] < mMC2ROFRecordsAccum[iLayer].size()) { // fix ROFRecord entry in MC2ROF records - for (int m2rid = mFixMC2ROF[iLayer]; m2rid < mMC2ROFRecordsAccum[iLayer].size(); m2rid++) { - // need to register the ROFRecors entry for MC event starting from this entry - auto& mc2rof = mMC2ROFRecordsAccum[iLayer][m2rid]; - if (rof.getROFrame() == mc2rof.minROF) { - mFixMC2ROF[iLayer]++; - mc2rof.rofRecordID = nROFRecsOld + i; - mc2rof.print(); - } - } - } } std::copy(mROFRecords[iLayer].begin(), mROFRecords[iLayer].end(), std::back_inserter(mROFRecordsAccum[iLayer])); if (mWithMCTruth) { mLabelsAccum[iLayer].mergeAtBack(mLabels[iLayer]); } - LOG(info) << "Added " << mDigits[iLayer].size() << " digits:" << iLayer; + LOG(info) << "Added " << mDigits[iLayer].size() << " digits" << ((mDoStaggering) ? std::format(" on layer {}", iLayer) : ""); // clean containers from already accumulated stuff mLabels[iLayer].clear(); mDigits[iLayer].clear(); @@ -171,7 +162,6 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer mDigitizer.process(&mHits, part.entryID, part.sourceID, layer); // call actual digitization procedure } } - mMC2ROFRecordsAccum[iLayer].emplace_back(collID, -1, mDigitizer.getEventROFrameMin(), mDigitizer.getEventROFrameMax()); accumulate(); } mDigitizer.fillOutputContainer(0xffffffff, layer); @@ -190,7 +180,7 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer for (int iROF{0}; iROF < nROFsLayer; ++iROF) { auto& rof = expDigitRofVec[iROF]; int orb = iROF * DPLAlpideParam::Instance().getROFLengthInBC(iLayer) / o2::constants::lhc::LHCMaxBunches + mFirstOrbitTF; - int bc = iROF * DPLAlpideParam::Instance().getROFLengthInBC(iLayer) % o2::constants::lhc::LHCMaxBunches; + int bc = iROF * DPLAlpideParam::Instance().getROFLengthInBC(iLayer) % o2::constants::lhc::LHCMaxBunches + DPLAlpideParam::Instance().getROFDelayInBC(iLayer); o2::InteractionRecord ir(bc, orb); rof.setBCData(ir); rof.setROFrame(iROF); @@ -200,7 +190,16 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer uint32_t prevEntry{0}; for (const auto& rof : mROFRecordsAccum[iLayer]) { const auto& ir = rof.getBCData(); - const auto irToFirst = ir - firstIR; + if (ir < firstIR) { + LOGP(warn, "Discard ROF {} preceding TF 1st orbit {}{}", ir.asString(), mFirstOrbitTF, ((mDoStaggering) ? std::format(" on layer {}", iLayer) : "")); + continue; + } + auto irToFirst = ir - firstIR; + if (irToFirst.toLong() - DPLAlpideParam::Instance().getROFDelayInBC(iLayer) < 0) { + LOGP(warn, "Discard ROF {} preceding TF 1st orbit {} due to imposed ROF delay{}", ir.asString(), mFirstOrbitTF, ((mDoStaggering) ? std::format(" on layer {}", iLayer) : "")); + continue; + } + irToFirst -= DPLAlpideParam::Instance().getROFDelayInBC(iLayer); const int irROF = irToFirst.toLong() / DPLAlpideParam::Instance().getROFLengthInBC(iLayer); auto& expROF = expDigitRofVec[irROF]; expROF.setFirstEntry(rof.getFirstEntry()); @@ -224,7 +223,6 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer pc.outputs().snapshot(Output{Origin, "DIGITSROF", iLayer}, mROFRecordsAccum[iLayer]); } if (mWithMCTruth) { - pc.outputs().snapshot(Output{Origin, "DIGITSMC2ROF", iLayer}, mMC2ROFRecordsAccum[iLayer]); auto& sharedlabels = pc.outputs().make>(Output{Origin, "DIGITSMCTR", iLayer}); mLabelsAccum[iLayer].flatten_to(sharedlabels); // free space of existing label containers @@ -292,7 +290,7 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer } protected: - ITSMFTDPLDigitizerTask(bool mctruth = true) : BaseDPLDigitizer(InitServices::FIELD | InitServices::GEOM), mWithMCTruth(mctruth) {} + ITSMFTDPLDigitizerTask(bool mctruth = true, bool doStag = false) : BaseDPLDigitizer(InitServices::FIELD | InitServices::GEOM), mWithMCTruth(mctruth), mDoStaggering(doStag) {} void updateTimeDependentParams(ProcessingContext& pc) { @@ -331,17 +329,15 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer digipar.setOBVbb(dopt.OBVbb); digipar.setVbb(dopt.Vbb); // staggering parameters - if constexpr (o2::itsmft::DPLAlpideParam::supportsStaggering()) { - const bool withStag = aopt.withStaggering(); + if (mDoStaggering) { for (int iLayer{0}; iLayer < o2::itsmft::DPLAlpideParam::getNLayers(); ++iLayer) { - const int nLayer = (withStag) ? iLayer : -1; - auto frameNS = aopt.getROFLengthInBC(nLayer) * o2::constants::lhc::LHCBunchSpacingNS; - digipar.addROFrameLayerLengthInBC(aopt.getROFLengthInBC(nLayer)); + auto frameNS = aopt.getROFLengthInBC(iLayer) * o2::constants::lhc::LHCBunchSpacingNS; + digipar.addROFrameLayerLengthInBC(aopt.getROFLengthInBC(iLayer)); // NOTE: the rof delay looks from the digitizer like an additional bias - digipar.addROFrameLayerBiasInBC(aopt.getROFBiasInBC(nLayer) + aopt.getROFDelayInBC(nLayer)); + digipar.addROFrameLayerBiasInBC(aopt.getROFBiasInBC(iLayer) + aopt.getROFDelayInBC(iLayer)); digipar.addStrobeDelay(aopt.strobeDelay); digipar.addStrobeLength(aopt.strobeLengthCont > 0 ? aopt.strobeLengthCont : frameNS - aopt.strobeDelay); - digipar.setROFrameLength(aopt.getROFLengthInBC(nLayer) * o2::constants::lhc::LHCBunchSpacingNS, iLayer); + digipar.setROFrameLength(aopt.getROFLengthInBC(iLayer) * o2::constants::lhc::LHCBunchSpacingNS, iLayer); } } @@ -363,22 +359,22 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer } bool mWithMCTruth = true; + bool mDoStaggering = false; bool mFinished = false; bool mDisableQED = false; + int mLayers = 1; unsigned long mFirstOrbitTF = 0x0; o2::itsmft::Digitizer mDigitizer; - std::array, NLayers> mDigits; - std::array, NLayers> mROFRecords; - std::array, NLayers> mROFRecordsAccum; + std::vector> mDigits; + std::vector> mROFRecords; + std::vector> mROFRecordsAccum; std::vector mHits; std::vector* mHitsP = &mHits; - std::array, NLayers> mLabels; - std::array, NLayers> mLabelsAccum; - std::array, NLayers> mMC2ROFRecordsAccum; + std::vector> mLabels; + std::vector> mLabelsAccum; std::vector mSimChains; o2::itsmft::NoiseMap* mDeadMap = nullptr; - std::array mFixMC2ROF{}; // 1st entry in mc2rofRecordsAccum to be fixed for ROFRecordID bool mTimeDeadMapUpdated = false; o2::parameters::GRPObject::ROMode mROMode = o2::parameters::GRPObject::PRESENT; // readout mode }; @@ -387,28 +383,27 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer class ITSDPLDigitizerTask : public ITSMFTDPLDigitizerTask { public: - ITSDPLDigitizerTask(bool mctruth = true) : ITSMFTDPLDigitizerTask(mctruth) {} + ITSDPLDigitizerTask(bool mctruth = true, bool doStag = false) : ITSMFTDPLDigitizerTask(mctruth, doStag) {} }; //_______________________________________________ class MFTDPLDigitizerTask : public ITSMFTDPLDigitizerTask { public: - MFTDPLDigitizerTask(bool mctruth = true) : ITSMFTDPLDigitizerTask(mctruth) {} + MFTDPLDigitizerTask(bool mctruth = true, bool doStag = false) : ITSMFTDPLDigitizerTask(mctruth, doStag) {} }; namespace { template -std::vector makeOutChannels(o2::header::DataOrigin detOrig, bool mctruth) +std::vector makeOutChannels(o2::header::DataOrigin detOrig, bool mctruth, bool doStag) { std::vector outputs; - constexpr uint32_t nLayers = (DPLAlpideParam::supportsStaggering()) ? DPLAlpideParam::getNLayers() : 1; + uint32_t nLayers = doStag ? DPLAlpideParam::getNLayers() : 1; for (uint32_t iLayer = 0; iLayer < nLayers; ++iLayer) { outputs.emplace_back(detOrig, "DIGITS", iLayer, Lifetime::Timeframe); outputs.emplace_back(detOrig, "DIGITSROF", iLayer, Lifetime::Timeframe); if (mctruth) { - outputs.emplace_back(detOrig, "DIGITSMC2ROF", iLayer, Lifetime::Timeframe); outputs.emplace_back(detOrig, "DIGITSMCTR", iLayer, Lifetime::Timeframe); } } @@ -417,7 +412,7 @@ std::vector makeOutChannels(o2::header::DataOrigin detOrig, bool mct } } // namespace -DataProcessorSpec getITSDigitizerSpec(int channel, bool mctruth) +DataProcessorSpec getITSDigitizerSpec(int channel, bool mctruth, bool doStag) { std::string detStr = o2::detectors::DetID::getName(ITSDPLDigitizerTask::ID); auto detOrig = ITSDPLDigitizerTask::Origin; @@ -431,13 +426,13 @@ DataProcessorSpec getITSDigitizerSpec(int channel, bool mctruth) inputs.emplace_back("ITS_alpiderespvbbm3", "ITS", "ALPIDERESPVbbM3", 0, Lifetime::Condition, ccdbParamSpec("ITSMFT/Calib/ALPIDEResponseVbbM3")); return DataProcessorSpec{.name = detStr + "Digitizer", .inputs = inputs, - .outputs = makeOutChannels(detOrig, mctruth), - .algorithm = AlgorithmSpec{adaptFromTask(mctruth)}, + .outputs = makeOutChannels(detOrig, mctruth, doStag), + .algorithm = AlgorithmSpec{adaptFromTask(mctruth, doStag)}, .options = Options{ {"disable-qed", o2::framework::VariantType::Bool, false, {"disable QED handling"}}}}; } -DataProcessorSpec getMFTDigitizerSpec(int channel, bool mctruth) +DataProcessorSpec getMFTDigitizerSpec(int channel, bool mctruth, bool doStag) { std::string detStr = o2::detectors::DetID::getName(MFTDPLDigitizerTask::ID); auto detOrig = MFTDPLDigitizerTask::Origin; @@ -451,10 +446,10 @@ DataProcessorSpec getMFTDigitizerSpec(int channel, bool mctruth) inputs.emplace_back("MFT_alpiderespvbbm3", "MFT", "ALPIDERESPVbbM3", 0, Lifetime::Condition, ccdbParamSpec("ITSMFT/Calib/ALPIDEResponseVbbM3")); return DataProcessorSpec{.name = detStr + "Digitizer", .inputs = inputs, - .outputs = makeOutChannels(detOrig, mctruth), - .algorithm = AlgorithmSpec{adaptFromTask(mctruth)}, + .outputs = makeOutChannels(detOrig, mctruth, doStag), + .algorithm = AlgorithmSpec{adaptFromTask(mctruth, doStag)}, .options = Options{{"disable-qed", o2::framework::VariantType::Bool, false, {"disable QED handling"}}}}; } } // namespace o2::itsmft - // end namespace o2 +// end namespace o2 diff --git a/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.h b/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.h index 55fd88b1e1f80..e763cfe9565f4 100644 --- a/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.h +++ b/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.h @@ -19,8 +19,8 @@ namespace o2 namespace itsmft { -o2::framework::DataProcessorSpec getITSDigitizerSpec(int channel, bool mctruth = true); -o2::framework::DataProcessorSpec getMFTDigitizerSpec(int channel, bool mctruth = true); +o2::framework::DataProcessorSpec getITSDigitizerSpec(int channel, bool mctruth = true, bool doStag = false); +o2::framework::DataProcessorSpec getMFTDigitizerSpec(int channel, bool mctruth = true, bool doStag = false); } // end namespace itsmft } // end namespace o2 diff --git a/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx b/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx index 6f956efe79304..b4f9c1643d150 100644 --- a/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx +++ b/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx @@ -37,6 +37,7 @@ #include "TPCSimulation/GEMAmplification.h" // for ITSMFT +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "ITSMFTDigitizerSpec.h" #include "ITSMFTWorkflow/DigitWriterSpec.h" @@ -225,6 +226,7 @@ void customize(std::vector& workflowOptions) // option to propagate CTP Lumi scaler counts (if >=0) into the CTP digits workflowOptions.push_back(ConfigParamSpec{"store-ctp-lumi", VariantType::Float, -1.f, {"store CTP lumi scaler in CTP digits (if >= 0)"}}); + o2::itsmft::DPLAlpideParamInitializer::addConfigOption(workflowOptions); } void customize(std::vector& policies) @@ -637,10 +639,11 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) // the ITS part if (isEnabled(o2::detectors::DetID::ITS)) { detList.emplace_back(o2::detectors::DetID::ITS); + bool doStag = o2::itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(configcontext); // connect the ITS digitization - digitizerSpecs.emplace_back(o2::itsmft::getITSDigitizerSpec(fanoutsize++, mctruth)); + digitizerSpecs.emplace_back(o2::itsmft::getITSDigitizerSpec(fanoutsize++, mctruth, doStag)); // connect ITS digit writer - writerSpecs.emplace_back(o2::itsmft::getITSDigitWriterSpec(mctruth)); + writerSpecs.emplace_back(o2::itsmft::getITSDigitWriterSpec(mctruth, doStag)); } #ifdef ENABLE_UPGRADES @@ -666,10 +669,11 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) // the MFT part if (isEnabled(o2::detectors::DetID::MFT)) { detList.emplace_back(o2::detectors::DetID::MFT); + bool doStag = o2::itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(configcontext); // connect the MFT digitization - digitizerSpecs.emplace_back(o2::itsmft::getMFTDigitizerSpec(fanoutsize++, mctruth)); + digitizerSpecs.emplace_back(o2::itsmft::getMFTDigitizerSpec(fanoutsize++, mctruth, doStag)); // connect MFT digit writer - writerSpecs.emplace_back(o2::itsmft::getMFTDigitWriterSpec(mctruth)); + writerSpecs.emplace_back(o2::itsmft::getMFTDigitWriterSpec(mctruth, doStag)); } // the TOF part diff --git a/doc/data/2021-02-o2_prs.json b/doc/data/2021-02-o2_prs.json index d36bfadccf499..2bf9b8ff3cb4b 100644 --- a/doc/data/2021-02-o2_prs.json +++ b/doc/data/2021-02-o2_prs.json @@ -2399,7 +2399,7 @@ }, { "node": { - "path": "Detectors/ITSMFT/common/base/include/ITSMFTBase/DPLAlpideParam.h" + "path": "Detectors/ITSMFT/common/base/include/DataFormatsITSMFT/DPLAlpideParam.h" } }, { diff --git a/doc/data/2022-01-o2_prs.json b/doc/data/2022-01-o2_prs.json index 155ab6ed3d8d5..1e21f2e051c5e 100644 --- a/doc/data/2022-01-o2_prs.json +++ b/doc/data/2022-01-o2_prs.json @@ -3475,7 +3475,7 @@ "edges": [ { "node": { - "path": "Detectors/ITSMFT/common/base/include/ITSMFTBase/DPLAlpideParam.h" + "path": "Detectors/ITSMFT/common/base/include/DataFormatsITSMFT/DPLAlpideParam.h" } } ] diff --git a/macro/run_rawdecoding_its.C b/macro/run_rawdecoding_its.C index c5ee6c9b0ff5e..d05681356019a 100644 --- a/macro/run_rawdecoding_its.C +++ b/macro/run_rawdecoding_its.C @@ -1,3 +1,14 @@ +// 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. + #if !defined(__CLING__) || defined(__ROOTCLING__) #include @@ -42,7 +53,7 @@ void run_rawdecoding_its(std::string inpName = "rawits.bin", // input binary dat TStopwatch sw; sw.Start(); uint32_t roFrame = 0; - o2::InteractionRecord irHB, irTrig; + o2::InteractionRecord irTrig; std::vector digits, *digitsPtr = &digits; std::vector rofRecVec, *rofRecVecPtr = &rofRecVec; std::size_t rofEntry = 0, nrofdig = 0; @@ -62,12 +73,11 @@ void run_rawdecoding_its(std::string inpName = "rawits.bin", // input binary dat } if (outTreeDig) { // >> store digits - if (irHB != rawReader.getInteractionRecordHB() || irTrig != rawReader.getInteractionRecord()) { + if (irTrig != rawReader.getInteractionRecord()) { if (!irTrig.isDummy()) { - rofRecVec.emplace_back(irHB, roFrame, rofEntry, nrofdig); // registed finished ROF + rofRecVec.emplace_back(irTrig, roFrame, rofEntry, nrofdig); // registed finished ROF roFrame++; } - irHB = rawReader.getInteractionRecordHB(); irTrig = rawReader.getInteractionRecord(); rofEntry = digits.size(); nrofdig = 0; @@ -79,7 +89,6 @@ void run_rawdecoding_its(std::string inpName = "rawits.bin", // input binary dat } printf("ROF %7d ch: %5d IR: ", roFrame, chipData.getChipID()); - irHB.print(); } // << store digits // @@ -87,7 +96,7 @@ void run_rawdecoding_its(std::string inpName = "rawits.bin", // input binary dat if (outTreeDig) { // register last ROF - rofRecVec.emplace_back(irHB, roFrame, rofEntry, nrofdig); // registed finished ROF + rofRecVec.emplace_back(irTrig, roFrame, rofEntry, nrofdig); // registed finished ROF // fill last (and the only one?) entry outTreeDig->Fill(); diff --git a/macro/run_rawdecoding_mft.C b/macro/run_rawdecoding_mft.C index d8bdb0ce1e2ce..d23668f7e5498 100644 --- a/macro/run_rawdecoding_mft.C +++ b/macro/run_rawdecoding_mft.C @@ -1,3 +1,14 @@ +// 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. + #if !defined(__CLING__) || defined(__ROOTCLING__) #include @@ -42,7 +53,7 @@ void run_rawdecoding_mft(std::string inpName = "06282019_1854_output.bin", // in TStopwatch sw; sw.Start(); uint32_t roFrame = 0; - o2::InteractionRecord irHB, irTrig; + o2::InteractionRecord irTrig; std::vector digits, *digitsPtr = &digits; std::vector rofRecVec, *rofRecVecPtr = &rofRecVec; int rofEntry = 0, nrofdig = 0; @@ -62,12 +73,11 @@ void run_rawdecoding_mft(std::string inpName = "06282019_1854_output.bin", // in } if (outTreeDig) { // >> store digits - if (irHB != rawReader.getInteractionRecordHB() || irTrig != rawReader.getInteractionRecord()) { + if (irTrig != rawReader.getInteractionRecord()) { if (!irTrig.isDummy()) { - rofRecVec.emplace_back(irHB, roFrame, rofEntry, nrofdig); // registed finished ROF + rofRecVec.emplace_back(irTrig, roFrame, rofEntry, nrofdig); // registed finished ROF roFrame++; } - irHB = rawReader.getInteractionRecordHB(); irTrig = rawReader.getInteractionRecord(); rofEntry = digits.size(); nrofdig = 0; @@ -79,7 +89,6 @@ void run_rawdecoding_mft(std::string inpName = "06282019_1854_output.bin", // in } printf("ROF %7d ch: %5d IR: ", roFrame, chipData.getChipID()); - irHB.print(); } // << store digits // @@ -87,7 +96,7 @@ void run_rawdecoding_mft(std::string inpName = "06282019_1854_output.bin", // in if (outTreeDig) { // register last ROF - rofRecVec.emplace_back(irHB, roFrame, rofEntry, nrofdig); // registed finished ROF + rofRecVec.emplace_back(irTrig, roFrame, rofEntry, nrofdig); // registed finished ROF // fill last (and the only one?) entry outTreeDig->Fill(); diff --git a/prodtests/full-system-test/dpl-workflow.sh b/prodtests/full-system-test/dpl-workflow.sh index 1e1ea258d395f..9f982513fdffd 100755 --- a/prodtests/full-system-test/dpl-workflow.sh +++ b/prodtests/full-system-test/dpl-workflow.sh @@ -107,10 +107,16 @@ EVE_OPT=" --jsons-folder $EDJSONS_DIR" : ${AOD_SOURCES:=$TRACK_SOURCES} : ${AODPROD_OPT:=} : ${ALPIDE_ERR_DUMPS:=} +: ${ITSSTAGGERED:=} +: ${MFTSTAGGERED:=} + [[ -z $ALPIDE_ERR_DUMPS ]] && [[ $EPNSYNCMODE == 1 && $RUNTYPE == "PHYSICS" ]] && ALPIDE_ERR_DUMPS=1 || ALPIDE_ERR_DUMPS=0 [[ "0$DISABLE_ROOT_OUTPUT" == "00" ]] && DISABLE_ROOT_OUTPUT= +[[ "0$ITSSTAGGERED" == "01" ]] && ITS_STAGGERED=" --enable-its-staggering " || ITS_STAGGERED= +[[ "0$MFTSTAGGERED" == "01" ]] && MFT_STAGGERED=" --enable-its-staggering " || MFT_STAGGERED= + if [[ $CTFINPUT != 1 ]]; then GPU_OUTPUT+=",tpc-triggers" fi @@ -127,7 +133,6 @@ if [[ $SYNCMODE == 1 ]]; then MCH_CONFIG_KEY="MCHTracking.maxCandidates=20000;MCHTracking.maxTrackingDuration=10;" fi [[ -n ${CUT_RANDOM_FRACTION_ITS:-} ]] && ITS_CONFIG_KEY+="fastMultConfig.cutRandomFraction=$CUT_RANDOM_FRACTION_ITS;" - ITS_CONFIG_KEY+="ITSCATrackerParam.trackletsPerClusterLimit=${CUT_TRACKLETSPERCLUSTER_MAX_ITS:--1};ITSCATrackerParam.cellsPerClusterLimit=${CUT_CELLSPERCLUSTER_MAX_ITS:--1};" if has_detector_reco ITS && [[ $RUNTYPE != "COSMICS" && x"${MFT_DISABLE_ITS_IRFRAMES_SELECTION:-}" != "x1" ]]; then MFT_CONFIG_KEY+="MFTTracking.irFramesOnly=1;" fi @@ -156,7 +161,6 @@ else ITS_CONFIG_KEY+="ITSVertexerParam.phiCut=0.5;ITSVertexerParam.clusterContributorsCut=3;ITSVertexerParam.tanLambdaCut=0.2;" elif [[ $BEAMTYPE == "PbPb" ]]; then ITS_CONFIG_KEY+="ITSVertexerParam.lowMultBeamDistCut=0;" - ! has_detectors_gpu TPC ITS && ITS_CONFIG_KEY+="ITSCATrackerParam.nROFsPerIterations=12;" fi if [[ $IS_SIMULATED_DATA == 0 && $CTFINPUT == 1 ]]; then # Enable fixes to the MCH readout mapping for async processing of real data @@ -463,7 +467,7 @@ if [[ -n $INPUT_DETECTOR_LIST ]]; then if [[ $NTIMEFRAMES == -1 ]]; then NTIMEFRAMES_CMD= ; else NTIMEFRAMES_CMD="--max-tf $NTIMEFRAMES"; fi CTF_EMC_SUBSPEC= ( workflow_has_parameter AOD || [[ -z "$DISABLE_ROOT_OUTPUT" ]] || needs_root_output o2-emcal-cell-writer-workflow ) && has_detector EMC && CTF_EMC_SUBSPEC="--emcal-decoded-subspec 1" - add_W o2-ctf-reader-workflow "$RANS_OPT --delay $TFDELAY --loop $TFLOOP $NTIMEFRAMES_CMD --ctf-input ${CTFName} ${INPUT_FILE_COPY_CMD+--copy-cmd} ${INPUT_FILE_COPY_CMD:-} --onlyDet $INPUT_DETECTOR_LIST $CTF_EMC_SUBSPEC ${TIMEFRAME_SHM_LIMIT+--timeframes-shm-limit} ${TIMEFRAME_SHM_LIMIT:-} --pipeline $(get_N tpc-entropy-decoder TPC REST 1 TPCENTDEC)" + add_W o2-ctf-reader-workflow "$RANS_OPT --delay $TFDELAY --loop $TFLOOP $NTIMEFRAMES_CMD $ITS_STAGGERED $MFT_STAGGERED --ctf-input ${CTFName} ${INPUT_FILE_COPY_CMD+--copy-cmd} ${INPUT_FILE_COPY_CMD:-} --onlyDet $INPUT_DETECTOR_LIST $CTF_EMC_SUBSPEC ${TIMEFRAME_SHM_LIMIT+--timeframes-shm-limit} ${TIMEFRAME_SHM_LIMIT:-} --pipeline $(get_N tpc-entropy-decoder TPC REST 1 TPCENTDEC)" elif [[ $RAWTFINPUT == 1 ]]; then TFName=`ls -t $RAWINPUTDIR/o2_*.tf 2> /dev/null | head -n1` [[ -z $TFName && $WORKFLOWMODE == "print" ]] && TFName='$TFName' @@ -556,8 +560,8 @@ if [[ $CTFINPUT == 0 && $DIGITINPUT == 0 ]]; then add_W o2-tpc-raw-to-digits-workflow "--input-spec \"\" --remove-duplicates $RAWTODIGITOPTIONS --pipeline $(get_N tpc-raw-to-digits-0 TPC RAW 1 TPCRAWDEC)" add_W o2-tpc-reco-workflow "--input-type digitizer --output-type zsraw,disable-writer --pipeline $(get_N tpc-zsEncoder TPC RAW 1 TPCRAWDEC)" "GPU_rec_tpc.zsThreshold=0" fi - has_detector ITS && ! has_detector_from_global_reader ITS && add_W o2-itsmft-stf-decoder-workflow "--nthreads ${NITSDECTHREADS} --raw-data-dumps $ALPIDE_ERR_DUMPS --pipeline $(get_N its-stf-decoder ITS RAW 1 ITSRAWDEC)" "$ITS_STF_DEC_CONFIG;$ITSMFT_STROBES;VerbosityConfig.rawParserSeverity=warn;" - has_detector MFT && ! has_detector_from_global_reader MFT && add_W o2-itsmft-stf-decoder-workflow "--nthreads ${NMFTDECTHREADS} --raw-data-dumps $ALPIDE_ERR_DUMPS --pipeline $(get_N mft-stf-decoder MFT RAW 1 MFTRAWDEC) --runmft true" "$MFT_STF_DEC_CONFIG;$ITSMFT_STROBES;VerbosityConfig.rawParserSeverity=warn;" + has_detector ITS && ! has_detector_from_global_reader ITS && add_W o2-itsmft-stf-decoder-workflow "--nthreads ${NITSDECTHREADS} --raw-data-dumps $ALPIDE_ERR_DUMPS $ITS_STAGGERED --pipeline $(get_N its-stf-decoder ITS RAW 1 ITSRAWDEC)" "$ITS_STF_DEC_CONFIG;$ITSMFT_STROBES;VerbosityConfig.rawParserSeverity=warn;" + has_detector MFT && ! has_detector_from_global_reader MFT && add_W o2-itsmft-stf-decoder-workflow "--nthreads ${NMFTDECTHREADS} --raw-data-dumps $ALPIDE_ERR_DUMPS $MFT_STAGGERED --pipeline $(get_N mft-stf-decoder MFT RAW 1 MFTRAWDEC) --runmft true" "$MFT_STF_DEC_CONFIG;$ITSMFT_STROBES;VerbosityConfig.rawParserSeverity=warn;" has_detector FT0 && ! has_detector_from_global_reader FT0 && ! has_detector_flp_processing FT0 && add_W o2-ft0-flp-dpl-workflow "$DISABLE_ROOT_OUTPUT --pipeline $(get_N ft0-datareader-dpl FT0 RAW 1)" has_detector FV0 && ! has_detector_from_global_reader FV0 && ! has_detector_flp_processing FV0 && add_W o2-fv0-flp-dpl-workflow "$DISABLE_ROOT_OUTPUT --pipeline $(get_N fv0-datareader-dpl FV0 RAW 1)" has_detector MID && ! has_detector_from_global_reader MID && add_W o2-mid-raw-to-digits-workflow "$MIDDEC_CONFIG --pipeline $(get_N MIDRawDecoder MID RAW 1),$(get_N MIDDecodedDataAggregator MID RAW 1)" @@ -581,13 +585,13 @@ has_detector_gpu ITS && GPU_OUTPUT+=",its-tracks" # --------------------------------------------------------------------------------------------------------------------- # Common reconstruction workflows -(has_detector_reco ITS && ! has_detector_gpu ITS) && ! has_detector_from_global_reader ITS && add_W o2-its-reco-workflow "--trackerCA $ITS_CONFIG $DISABLE_MC ${DISABLE_DIGIT_CLUSTER_INPUT:-} $DISABLE_ROOT_OUTPUT --pipeline $(get_N its-tracker ITS REST 1 ITSTRK),$(get_N its-clusterer ITS REST 1 ITSCL)" "$ITS_CONFIG_KEY;$ITSMFT_STROBES;$ITSEXTRAERR" -[[ ${DISABLE_DIGIT_CLUSTER_INPUT:-} =~ "--digits-from-upstream" ]] && has_detector_gpu ITS && ! has_detector_from_global_reader ITS && add_W o2-its-reco-workflow "--disable-tracking ${DISABLE_DIGIT_CLUSTER_INPUT:-} $DISABLE_MC $DISABLE_ROOT_OUTPUT --pipeline $(get_N its-clusterer ITS REST 1 ITSCL)" "$ITS_CONFIG_KEY;$ITSMFT_STROBES;$ITSEXTRAERR" -(has_detector_reco TPC || has_detector_ctf TPC) && ! has_detector_from_global_reader TPC && add_W o2-gpu-reco-workflow "--gpu-reconstruction \"$GPU_CONFIG_SELF\" --input-type=$GPU_INPUT $DISABLE_MC --output-type $GPU_OUTPUT $TPC_CORR_OPT --pipeline gpu-reconstruction:${N_TPCTRK:-1},gpu-reconstruction-prepare:${N_TPCTRK:-1} $GPU_CONFIG" "GPU_global.deviceType=$GPUTYPE;GPU_proc.debugLevel=0;$GPU_CONFIG_KEY;$TRACKTUNETPCINNER;$TPC_CORR_KEY" +(has_detector_reco ITS && ! has_detector_gpu ITS) && ! has_detector_from_global_reader ITS && add_W o2-its-reco-workflow "--trackerCA $ITS_CONFIG $ITS_STAGGERED $DISABLE_MC ${DISABLE_DIGIT_CLUSTER_INPUT:-} $DISABLE_ROOT_OUTPUT --pipeline $(get_N its-tracker ITS REST 1 ITSTRK),$(get_N its-clusterer ITS REST 1 ITSCL)" "$ITS_CONFIG_KEY;$ITSMFT_STROBES;$ITSEXTRAERR" +[[ ${DISABLE_DIGIT_CLUSTER_INPUT:-} =~ "--digits-from-upstream" ]] && has_detector_gpu ITS && ! has_detector_from_global_reader ITS && add_W o2-its-reco-workflow "--disable-tracking ${DISABLE_DIGIT_CLUSTER_INPUT:-} $ITS_STAGGERED $DISABLE_MC $DISABLE_ROOT_OUTPUT --pipeline $(get_N its-clusterer ITS REST 1 ITSCL)" "$ITS_CONFIG_KEY;$ITSMFT_STROBES;$ITSEXTRAERR" +(has_detector_reco TPC || has_detector_ctf TPC) && ! has_detector_from_global_reader TPC && add_W o2-gpu-reco-workflow "--gpu-reconstruction \"$GPU_CONFIG_SELF\" --input-type=$GPU_INPUT $DISABLE_MC --output-type $GPU_OUTPUT $TPC_CORR_OPT $ITS_STAGGERED --pipeline gpu-reconstruction:${N_TPCTRK:-1},gpu-reconstruction-prepare:${N_TPCTRK:-1} $GPU_CONFIG" "GPU_global.deviceType=$GPUTYPE;GPU_proc.debugLevel=0;$GPU_CONFIG_KEY;$TRACKTUNETPCINNER;$TPC_CORR_KEY" (has_detector_reco TOF || has_detector_ctf TOF) && ! has_detector_from_global_reader TOF && add_W o2-tof-reco-workflow "$TOF_CONFIG --input-type $TOF_INPUT --output-type $TOF_OUTPUT $DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC --pipeline $(get_N tof-compressed-decoder TOF RAW 1),$(get_N TOFClusterer TOF REST 1)" has_detector_reco FT0 && ! has_detector_from_global_reader FT0 && add_W o2-ft0-reco-workflow "$DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC --pipeline $(get_N ft0-reconstructor FT0 REST 1)" has_detector_reco TRD && ! has_detector_from_global_reader TRD && add_W o2-trd-tracklet-transformer "--disable-irframe-reader $DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC $TRD_FILTER_CONFIG --pipeline $(get_N TRDTRACKLETTRANSFORMER TRD REST 1 TRDTRKTRANS)" -has_detectors_reco ITS TPC && ! has_detector_from_global_reader_tracks ITS-TPC && has_detector_matching ITSTPC && add_W o2-tpcits-match-workflow "$DISABLE_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC $SEND_ITSTPC_DTGL $TPC_CORR_OPT --nthreads $ITSTPC_THREADS --pipeline $(get_N itstpc-track-matcher MATCH REST $ITSTPC_THREADS TPCITS)" "$ITSTPC_CONFIG_KEY;$INTERACTION_TAG_CONFIG_KEY;$ITSMFT_STROBES;$ITSEXTRAERR;$TPC_CORR_KEY" +has_detectors_reco ITS TPC && ! has_detector_from_global_reader_tracks ITS-TPC && has_detector_matching ITSTPC && add_W o2-tpcits-match-workflow "$DISABLE_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC $ITS_STAGGERED $SEND_ITSTPC_DTGL $TPC_CORR_OPT --nthreads $ITSTPC_THREADS --pipeline $(get_N itstpc-track-matcher MATCH REST $ITSTPC_THREADS TPCITS)" "$ITSTPC_CONFIG_KEY;$INTERACTION_TAG_CONFIG_KEY;$ITSMFT_STROBES;$ITSEXTRAERR;$TPC_CORR_KEY" has_detector_reco TRD && [[ -n "$TRD_SOURCES" ]] && ! has_detector_from_global_reader_tracks "$(echo "$TRD_SOURCES" | cut -d',' -f1)-TRD" && add_W o2-trd-global-tracking "$DISABLE_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC $TRD_CONFIG $TRD_FILTER_CONFIG $TPC_CORR_OPT --track-sources $TRD_SOURCES --pipeline $(get_N trd-globaltracking_TPC_ITS-TPC_ TRD REST 1 TRDTRK),$(get_N trd-globaltracking_TPC_FT0_ITS-TPC_ TRD REST 1 TRDTRK),$(get_N trd-globaltracking_TPC_FT0_ITS-TPC_CTP_ TRD REST 1 TRDTRK)" "$TRD_CONFIG_KEY;$INTERACTION_TAG_CONFIG_KEY;$ITSMFT_STROBES;$ITSEXTRAERR;$TPC_CORR_KEY" has_detector_reco TOF && [[ -n "$TOF_SOURCES" ]] && ! has_detector_from_global_reader_tracks "$(echo "$TOF_SOURCES" | cut -d',' -f1)-TOF" && add_W o2-tof-matcher-workflow "$TOF_MATCH_OPT $DISABLE_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC $TPC_CORR_OPT ${TOFMATCH_THREADS:+--tof-lanes ${TOFMATCH_THREADS}} --track-sources $TOF_SOURCES --pipeline $(get_N tof-matcher TOF REST 1 TOFMATCH)" "$ITSMFT_STROBES;$ITSEXTRAERR;$TPC_CORR_KEY;$INTERACTION_TAG_CONFIG_KEY" has_detectors TPC && [[ -z "$DISABLE_ROOT_OUTPUT" && "${SKIP_TPC_CLUSTERSTRACKS_OUTPUT:-}" != 1 ]] && ! has_detector_from_global_reader TPC && add_W o2-tpc-reco-workflow "--input-type pass-through --output-type clusters,tpc-triggers,tracks,send-clusters-per-sector $DISABLE_MC" @@ -596,7 +600,7 @@ has_detectors TPC && [[ -z "$DISABLE_ROOT_OUTPUT" && "${SKIP_TPC_CLUSTERSTRACKS_ # Reconstruction workflows normally active only in async mode ($LIST_OF_ASYNC_RECO_STEPS), but can be forced via $WORKFLOW_EXTRA_PROCESSING_STEPS has_detector MID && ! has_detector_from_global_reader MID && has_processing_step MID_RECO && add_W o2-mid-reco-workflow "$DISABLE_ROOT_OUTPUT $DISABLE_MC --pipeline $(get_N MIDClusterizer MID REST 1),$(get_N MIDTracker MID REST 1)" has_detector MCH && ! has_detector_from_global_reader MCH && has_processing_step MCH_RECO && add_W o2-mch-reco-workflow "$DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC --pipeline $(get_N mch-track-finder MCH REST 1 MCHTRK),$(get_N mch-cluster-finder MCH REST 1 MCHCL),$(get_N mch-cluster-transformer MCH REST 1)" "$MCH_CONFIG_KEY" -has_detector MFT && ! has_detector_from_global_reader MFT && has_processing_step MFT_RECO && add_W o2-mft-reco-workflow "$DISABLE_MC ${DISABLE_DIGIT_CLUSTER_INPUT:-} $DISABLE_ROOT_OUTPUT $MFT_CONFIG --pipeline $(get_N mft-tracker MFT REST 1 MFTTRK)" "$MFT_CONFIG_KEY;$ITSMFT_STROBES" +has_detector MFT && ! has_detector_from_global_reader MFT && has_processing_step MFT_RECO && add_W o2-mft-reco-workflow "$DISABLE_MC ${DISABLE_DIGIT_CLUSTER_INPUT:-} $DISABLE_ROOT_OUTPUT $MFT_CONFIG $MFT_STAGGERED --pipeline $(get_N mft-tracker MFT REST 1 MFTTRK)" "$MFT_CONFIG_KEY;$ITSMFT_STROBES" has_detector FDD && ! has_detector_from_global_reader FDD && has_processing_step FDD_RECO && add_W o2-fdd-reco-workflow "$DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC" has_detector FV0 && ! has_detector_from_global_reader FV0 && has_processing_step FV0_RECO && add_W o2-fv0-reco-workflow "$DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC" has_detector ZDC && ! has_detector_from_global_reader ZDC && has_processing_step ZDC_RECO && add_W o2-zdc-digits-reco "$DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC" @@ -658,7 +662,7 @@ fi # Entropy encoding / ctf creation workflows - disabled in async mode if has_processing_step ENTROPY_ENCODER && [[ -n "$WORKFLOW_DETECTORS_CTF" ]] && [[ $WORKFLOW_DETECTORS_CTF != "NONE" ]]; then # Entropy encoder workflows - has_detector_ctf MFT && add_W o2-itsmft-entropy-encoder-workflow "$RANS_OPT --mem-factor ${MFT_ENC_MEMFACT:-1.5} --runmft true --pipeline $(get_N mft-entropy-encoder MFT CTF 1)" + has_detector_ctf MFT && add_W o2-itsmft-entropy-encoder-workflow "$RANS_OPT --mem-factor ${MFT_ENC_MEMFACT:-1.5} $MFT_STAGGERED --runmft true --pipeline $(get_N mft-entropy-encoder MFT CTF 1)" has_detector_ctf FT0 && add_W o2-ft0-entropy-encoder-workflow "$RANS_OPT --mem-factor ${FT0_ENC_MEMFACT:-1.5} --pipeline $(get_N ft0-entropy-encoder FT0 CTF 1)" has_detector_ctf FV0 && add_W o2-fv0-entropy-encoder-workflow "$RANS_OPT --mem-factor ${FV0_ENC_MEMFACT:-1.5} --pipeline $(get_N fv0-entropy-encoder FV0 CTF 1)" has_detector_ctf MID && add_W o2-mid-entropy-encoder-workflow "$RANS_OPT --mem-factor ${MID_ENC_MEMFACT:-1.5} --pipeline $(get_N mid-entropy-encoder MID CTF 1)" @@ -670,7 +674,7 @@ if has_processing_step ENTROPY_ENCODER && [[ -n "$WORKFLOW_DETECTORS_CTF" ]] && has_detector_ctf FDD && add_W o2-fdd-entropy-encoder-workflow "$RANS_OPT --mem-factor ${FDD_ENC_MEMFACT:-1.5} --pipeline $(get_N fdd-entropy-encoder FDD CTF 1)" has_detector_ctf HMP && add_W o2-hmpid-entropy-encoder-workflow "$RANS_OPT --mem-factor ${HMP_ENC_MEMFACT:-1.5} --pipeline $(get_N hmpid-entropy-encoder HMP CTF 1)" has_detector_ctf TOF && add_W o2-tof-entropy-encoder-workflow "$RANS_OPT --mem-factor ${TOF_ENC_MEMFACT:-1.5} --pipeline $(get_N tof-entropy-encoder TOF CTF 1)" - has_detector_ctf ITS && add_W o2-itsmft-entropy-encoder-workflow "$RANS_OPT --mem-factor ${ITS_ENC_MEMFACT:-1.5} --pipeline $(get_N its-entropy-encoder ITS CTF 1)" + has_detector_ctf ITS && add_W o2-itsmft-entropy-encoder-workflow "$RANS_OPT --mem-factor ${ITS_ENC_MEMFACT:-1.5} $ITS_STAGGERED --pipeline $(get_N its-entropy-encoder ITS CTF 1)" has_detector_ctf TRD && add_W o2-trd-entropy-encoder-workflow "$RANS_OPT --mem-factor ${TRD_ENC_MEMFACT:-1.5} --pipeline $(get_N trd-entropy-encoder TRD CTF 1 TRDENT)" has_detector_ctf TPC && add_W o2-tpc-reco-workflow " $RANS_OPT --mem-factor ${TPC_ENC_MEMFACT:-1.} --input-type compressed-clusters-flat-for-encode --output-type encoded-clusters,disable-writer --pipeline $(get_N tpc-entropy-encoder TPC CTF 1 TPCENT)" has_detector_ctf CTP && add_W o2-ctp-entropy-encoder-workflow "$RANS_OPT --mem-factor ${CTP_ENC_MEMFACT:-1.5} --pipeline $(get_N its-entropy-encoder CTP CTF 1)" @@ -690,7 +694,7 @@ if has_processing_step ENTROPY_ENCODER && [[ -n "$WORKFLOW_DETECTORS_CTF" ]] && CONFIG_CTF="--output-dir \"$CTF_DIR\" $CTF_CONFIG --output-type $CTF_OUTPUT_TYPE --min-file-size ${CTF_MINSIZE} --max-ctf-per-file ${CTF_MAX_PER_FILE} --onlyDet ${WORKFLOW_DETECTORS_CTF/TST/} --meta-output-dir $EPN2EOS_METAFILES_DIR" if [[ $CREATECTFDICT == 1 ]] && [[ $EXTINPUT == 1 ]]; then CONFIG_CTF+=" --save-dict-after $SAVE_CTFDICT_NTIMEFRAMES"; fi [[ $EPNSYNCMODE == 1 ]] && CONFIG_CTF+=" --require-free-disk 53687091200 --wait-for-free-disk $CTF_FREE_DISK_WAIT --max-wait-for-free-disk $CTF_MAX_FREE_DISK_WAIT" - add_W o2-ctf-writer-workflow "$CONFIG_CTF" + add_W o2-ctf-writer-workflow "$CONFIG_CTF $ITS_STAGGERED $MFT_STAGGERED" fi # --------------------------------------------------------------------------------------------------------------------- diff --git a/prodtests/full_system_test.sh b/prodtests/full_system_test.sh index 8e252c5a8378f..46739e76f103b 100755 --- a/prodtests/full_system_test.sh +++ b/prodtests/full_system_test.sh @@ -321,10 +321,6 @@ for STAGE in $STAGES; do : ${CUT_MULT_MIN_ITS:=-1} : ${CUT_MULT_MAX_ITS:=-1} : ${CUT_MULT_VTX_ITS:=-1} - : ${CUT_TRACKLETSPERCLUSTER_MAX_ITS:=100} - : ${CUT_CELLSPERCLUSTER_MAX_ITS:=100} - export CUT_TRACKLETSPERCLUSTER_MAX_ITS - export CUT_CELLSPERCLUSTER_MAX_ITS export CUT_RANDOM_FRACTION_ITS export CUT_MULT_MIN_ITS export CUT_MULT_MAX_ITS From 14ff7dbba9255ac1b852c4c629364a4f24759768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tuba=20G=C3=BCndem?= <48834043+tubagundem@users.noreply.github.com> Date: Mon, 13 Apr 2026 15:02:33 +0200 Subject: [PATCH 056/285] TPC: Processing of common mode values in O2 (#15137) * TPC: Processing of common mode values in O2 * Added CMVContainer.cxx, fixed missing links and includes * Fix formatting * Removed unused includes, directly write the TTree object to CCDB without TMem file, small fixes * Changed the decoding and encoding of CMVs, removed grouping per side * Update the dataformat of CMV * Updated the CMVContainer, corrected the timestamp range for CCDB * Fix formatting * Removed factorize workflow, updated the distribute workflow accordingly * Fix formatting * Extend error tracking in CMVToVectorSpec.cxx * Replace CMVPerInterval with per TF TTree accumulation and raw uint16_t storage, fix CCDB timestamp for partial intervals, fix TMemFile padding * Added delta+zigzag+varint compression, added drawCMV.C macro for visualization * Added small value zeroing, added `--use-compression` and `--cmv-zero-threshold` flags to TPCDistributeCMVSpec, updated drawCMV.C macro to auto detect branch format (compressed or not) * Added sparse and HUffman encoding to the CMVContainer, updatet the drawCMV.C macro and TPCDistributeCMVSpec.h accordingly * Added CMVPerTFCombined to CMVContainer to combine sparse encoding with varint/Huffman compression, updated drawing macro and workflow options accordingly * Added gaussian dynamic precision * Refactored CMVContainer, unified CMV compression in a flag based container * Added CMV workflow documentation to README and fixed CMV packet size mismatch handling --- .../TPC/include/DataFormatsTPC/CMV.h | 122 +++ Detectors/TPC/base/include/TPCBase/RDHUtils.h | 3 +- Detectors/TPC/calibration/CMakeLists.txt | 8 +- .../include/TPCCalibration/CMVContainer.h | 141 ++++ Detectors/TPC/calibration/macro/drawCMV.C | 160 ++++ .../TPC/calibration/src/CMVContainer.cxx | 729 ++++++++++++++++++ .../calibration/src/TPCCalibrationLinkDef.h | 4 + Detectors/TPC/workflow/CMakeLists.txt | 18 +- Detectors/TPC/workflow/README.md | 188 +++++ .../include/TPCWorkflow/CMVToVectorSpec.h | 30 + .../TPCWorkflow/TPCDistributeCMVSpec.h | 621 +++++++++++++++ .../include/TPCWorkflow/TPCFLPCMVSpec.h | 172 +++++ .../TPC/workflow/src/CMVToVectorSpec.cxx | 434 +++++++++++ .../TPC/workflow/src/tpc-cmv-to-vector.cxx | 71 ++ .../TPC/workflow/src/tpc-distribute-cmv.cxx | 84 ++ Detectors/TPC/workflow/src/tpc-flp-cmv.cxx | 72 ++ 16 files changed, 2854 insertions(+), 3 deletions(-) create mode 100644 DataFormats/Detectors/TPC/include/DataFormatsTPC/CMV.h create mode 100644 Detectors/TPC/calibration/include/TPCCalibration/CMVContainer.h create mode 100644 Detectors/TPC/calibration/macro/drawCMV.C create mode 100644 Detectors/TPC/calibration/src/CMVContainer.cxx create mode 100644 Detectors/TPC/workflow/include/TPCWorkflow/CMVToVectorSpec.h create mode 100644 Detectors/TPC/workflow/include/TPCWorkflow/TPCDistributeCMVSpec.h create mode 100644 Detectors/TPC/workflow/include/TPCWorkflow/TPCFLPCMVSpec.h create mode 100644 Detectors/TPC/workflow/src/CMVToVectorSpec.cxx create mode 100644 Detectors/TPC/workflow/src/tpc-cmv-to-vector.cxx create mode 100644 Detectors/TPC/workflow/src/tpc-distribute-cmv.cxx create mode 100644 Detectors/TPC/workflow/src/tpc-flp-cmv.cxx diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CMV.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CMV.h new file mode 100644 index 0000000000000..109eff2654466 --- /dev/null +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CMV.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 CMV.h +/// @author Tuba Gündem, tuba.gundem@cern.ch +/// @brief Common mode values data format definition + +/// The data is sent by the CRU as 256+16 bit words. The CMV data layout is as follows: +/// - 256-bit Header: [version:8][packetID:8][errorCode:8][magicWord:8][heartbeatOrbit:32][heartbeatBC:16][padding:176] +/// - 16-bit CMV value: [sign:1][I8F7:15] where bit 15 is the sign (1=positive, 0=negative) and the lower 15 bits are a fixed point I8F7 value (8 integer bits, 7 fractional bits) +/// Float conversion: sign ? (value & 0x7FFF) / 128.0 : -(value & 0x7FFF) / 128.0 + +#ifndef ALICEO2_DATAFORMATSTPC_CMV_H +#define ALICEO2_DATAFORMATSTPC_CMV_H + +#include +#include + +namespace o2::tpc::cmv +{ + +static constexpr uint32_t NTimeBinsPerPacket = 3564; ///< number of time bins (covering 8 heartbeats) +static constexpr uint32_t NPacketsPerTFPerCRU = 4; ///< 4 packets per timeframe +static constexpr uint32_t NTimeBinsPerTF = NTimeBinsPerPacket * NPacketsPerTFPerCRU; ///< maximum number of timebins per timeframe (14256) + +/// Data padding: NTimeBinsPerPacket * sizeof(Data) = 3564 * 2 = 7128 bytes +static constexpr uint32_t DataSizeBytes = NTimeBinsPerPacket * sizeof(uint16_t); ///< 7128 bytes +static constexpr uint32_t DataPaddingBytes = (32 - (DataSizeBytes % 32)) % 32; ///< 8 bytes + +/// Header definition of the CMVs +struct Header { + static constexpr uint8_t MagicWord = 0xDC; + union { + uint64_t word0 = 0; ///< bits 0 - 63 + struct { + uint8_t version : 8; ///< version + uint8_t packetID : 8; ///< packet id + uint8_t errorCode : 8; ///< errors + uint8_t magicWord : 8; ///< magic word + uint32_t heartbeatOrbit : 32; ///< first heart beat timing of the package + }; + }; + union { + uint64_t word1 = 0; ///< bits 64 - 127 + struct { + uint16_t heartbeatBC : 16; ///< first BC id of the package + uint16_t unused1 : 16; ///< reserved + uint32_t unused2 : 32; ///< reserved + }; + }; + union { + uint64_t word3 = 0; ///< bits 128 - 191 + struct { + uint64_t unused3 : 64; ///< reserved + }; + }; + union { + uint64_t word4 = 0; ///< bits 192 - 255 + struct { + uint64_t unused4 : 64; ///< reserved + }; + }; +}; + +/// CMV single data container +struct Data { + uint16_t cmv{0}; ///< 16-bit signed fixed point value: bit 15 = sign (1=positive, 0=negative), bits 14-0 = I8F7 magnitude + + uint16_t getCMV() const { return cmv; } ///< raw 16-bit integer representation + void setCMV(uint16_t value) { cmv = value; } ///< set raw 16-bit integer representation + + // Decode to float: sign-magnitude with 7 fractional bits, range ±255.992 + float getCMVFloat() const + { + const bool positive = (cmv >> 15) & 1; // bit 15: sign (1=positive, 0=negative) + const float magnitude = (cmv & 0x7FFF) / 128.f; // lower 15 bits, shift right by 7 (divide by 2^7) + return positive ? magnitude : -magnitude; + } + + // Encode from float: clamps magnitude to 15 bits, range ±255.992 + void setCMVFloat(float value) + { + const bool positive = (value >= 0.f); + const uint16_t magnitude = static_cast(std::abs(value) * 128.f + 0.5f) & 0x7FFF; + cmv = (positive ? 0x8000 : 0x0000) | magnitude; + } +}; + +/// CMV full data container: one packet carries NTimeBinsPerPacket CMV values followed by padding +/// Layout: Header (32 bytes) + Data[NTimeBinsPerPacket] (7128 bytes) + padding (8 bytes) = 7168 bytes total (224 * 32 = 7168) +/// The padding bytes at the end of the data array are rubbish/unused and must not be interpreted as CMV values +struct Container { + Header header; ///< CMV data header + Data data[NTimeBinsPerPacket]; ///< data values + uint8_t padding[DataPaddingBytes]{}; ///< trailing padding to align data to 32-byte boundary + + // Header and data accessors + const Header& getHeader() const { return header; } + Header& getHeader() { return header; } + + const Data* getData() const { return data; } + Data* getData() { return data; } + + // Per timebin CMV accessors + uint16_t getCMV(uint32_t timeBin) const { return data[timeBin].getCMV(); } + void setCMV(uint32_t timeBin, uint16_t value) { data[timeBin].setCMV(value); } + + float getCMVFloat(uint32_t timeBin) const { return data[timeBin].getCMVFloat(); } + void setCMVFloat(uint32_t timeBin, float value) { data[timeBin].setCMVFloat(value); } +}; + +} // namespace o2::tpc::cmv + +#endif \ No newline at end of file diff --git a/Detectors/TPC/base/include/TPCBase/RDHUtils.h b/Detectors/TPC/base/include/TPCBase/RDHUtils.h index adfd94cf6b703..71b5d16b85702 100644 --- a/Detectors/TPC/base/include/TPCBase/RDHUtils.h +++ b/Detectors/TPC/base/include/TPCBase/RDHUtils.h @@ -13,7 +13,7 @@ #define AliceO2_TPC_RDHUtils_H #include "DetectorsRaw/RDHUtils.h" -//#include "Headers/RAWDataHeader.h" +// #include "Headers/RAWDataHeader.h" namespace o2 { @@ -28,6 +28,7 @@ static constexpr FEEIDType UserLogicLinkID = 15; ///< virtual link ID for ZS dat static constexpr FEEIDType IDCLinkID = 20; ///< Identifier for integrated digital currents static constexpr FEEIDType ILBZSLinkID = 21; ///< Identifier for improved link-based ZS static constexpr FEEIDType DLBZSLinkID = 22; ///< Identifier for dense link-based ZS +static constexpr FEEIDType CMVLinkID = 23; ///< Identifier for common mode values static constexpr FEEIDType SACLinkID = 25; ///< Identifier for sampled analog currents /// compose feeid from cru, endpoint and link diff --git a/Detectors/TPC/calibration/CMakeLists.txt b/Detectors/TPC/calibration/CMakeLists.txt index 27f7f0200bb92..a1068b928780d 100644 --- a/Detectors/TPC/calibration/CMakeLists.txt +++ b/Detectors/TPC/calibration/CMakeLists.txt @@ -58,6 +58,7 @@ o2_add_library(TPCCalibration src/DigitAdd.cxx src/CorrectdEdxDistortions.cxx src/PressureTemperatureHelper.cxx + src/CMVContainer.cxx PUBLIC_LINK_LIBRARIES O2::DataFormatsTPC O2::TPCBaseRecSim O2::TPCReconstruction ROOT::Minuit Microsoft.GSL::GSL @@ -115,7 +116,8 @@ o2_target_root_dictionary(TPCCalibration include/TPCCalibration/TPCMShapeCorrection.h include/TPCCalibration/DigitAdd.h include/TPCCalibration/CorrectdEdxDistortions.h - include/TPCCalibration/PressureTemperatureHelper.h) + include/TPCCalibration/PressureTemperatureHelper.h + include/TPCCalibration/CMVContainer.h) o2_add_test_root_macro(macro/comparePedestalsAndNoise.C PUBLIC_LINK_LIBRARIES O2::TPCBaseRecSim @@ -153,6 +155,10 @@ o2_add_test_root_macro(macro/prepareITFiles.C COMPILE_ONLY PUBLIC_LINK_LIBRARIES O2::TPCCalibration LABELS tpc) +o2_add_test_root_macro(macro/drawCMV.C + COMPILE_ONLY + PUBLIC_LINK_LIBRARIES O2::TPCCalibration O2::TPCBase + LABELS tpc) o2_add_test(IDCFourierTransform COMPONENT_NAME calibration diff --git a/Detectors/TPC/calibration/include/TPCCalibration/CMVContainer.h b/Detectors/TPC/calibration/include/TPCCalibration/CMVContainer.h new file mode 100644 index 0000000000000..f1904c3db8f8d --- /dev/null +++ b/Detectors/TPC/calibration/include/TPCCalibration/CMVContainer.h @@ -0,0 +1,141 @@ +// 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 CMVContainer.h +/// @author Tuba Gündem, tuba.gundem@cern.ch +/// @brief Structs for storing CMVs to the CCDB + +#ifndef ALICEO2_TPC_CMVCONTAINER_H_ +#define ALICEO2_TPC_CMVCONTAINER_H_ + +#include +#include +#include +#include + +#include "TTree.h" +#include "TPCBase/CRU.h" +#include "DataFormatsTPC/CMV.h" + +namespace o2::tpc +{ + +struct CMVPerTF; // forward declaration +struct CMVPerTFCompressed; // forward declaration + +/// Bitmask flags describing which encoding stages are applied in CMVPerTFCompressed +struct CMVEncoding { + static constexpr uint8_t kNone = 0x00; ///< No compression — raw uint16 values stored flat + static constexpr uint8_t kSparse = 0x01; ///< Non-zero positions stored sparsely (varint-encoded deltas) + static constexpr uint8_t kDelta = 0x02; ///< Delta coding between consecutive values (dense only) + static constexpr uint8_t kZigzag = 0x04; ///< Zigzag encoding of deltas or signed values + static constexpr uint8_t kVarint = 0x08; ///< Varint compression of the value stream + static constexpr uint8_t kHuffman = 0x10; ///< Canonical Huffman compression of the value stream +}; + +/// Single compressed representation for one TF across all CRUs, stored in a TTree +/// mFlags is a bitmask of CMVEncoding values that fully describes the encoding pipeline +/// mData holds the encoded payload whose binary layout depends on mFlags: +/// +/// Dense path (!kSparse): +/// kZigzag absent → N × uint16_t LE (raw values, CRU-major order) +/// kZigzag + kVarint → N × varint(zigzag(delta(signed(raw)))) +/// kZigzag + kHuffman → [Huffman table] + [bitstream] of zigzag(delta(signed(raw))) +/// +/// Sparse path (kSparse): +/// 4 bytes LE uint32_t : posStreamSize +/// posStream: for each CRU: varint(N), N × varint(tb_delta) +/// valStream (one entry per non-zero): +/// default → uint16_t LE raw value +/// kZigzag + kVarint → varint(zigzag(signed(raw))) +/// kZigzag + kHuffman → [Huffman table] + [bitstream] of zigzag(signed(raw)) +struct CMVPerTFCompressed { + uint32_t firstOrbit{0}; ///< First orbit of this TF + uint16_t firstBC{0}; ///< First bunch crossing of this TF + uint8_t mFlags{0}; ///< Bitmask of CMVEncoding values + + std::vector mData; ///< Encoded payload + + /// Restore a CMVPerTF from this compressed object into *cmv (must not be null) + void decompress(CMVPerTF* cmv) const; + + /// Serialise into a TTree; each Fill() call appends one entry (one TF) + std::unique_ptr toTTree() const; + + private: + /// Decode the sparse position stream; advances ptr past the position block + /// Returns (cru, timeBin) pairs for every non-zero entry, in CRU-major order + static std::vector> decodeSparsePositions(const uint8_t*& ptr, const uint8_t* end); + + /// Decode the value stream into raw uint32_t symbols + /// Dispatches to Huffman, varint, or raw uint16 based on flags + static std::vector decodeValueStream(const uint8_t*& ptr, const uint8_t* end, uint32_t N, uint8_t flags); + + /// Apply inverse zigzag and scatter decoded values into the sparse positions of *cmv + static void decodeSparseValues(const std::vector& symbols, + const std::vector>& positions, + uint8_t flags, CMVPerTF* cmv); + + /// Apply inverse zigzag and inverse delta, then fill the full dense CMV array in *cmv + static void decodeDenseValues(const std::vector& symbols, uint8_t flags, CMVPerTF* cmv); + + public: + ClassDefNV(CMVPerTFCompressed, 1) +}; + +/// CMV data for one TF across all CRUs +/// Raw 16-bit CMV values are stored in a flat C array indexed as [cru * NTimeBinsPerTF + timeBin] +struct CMVPerTF { + uint32_t firstOrbit{0}; ///< First orbit of this TF, from heartbeatOrbit of the first CMV packet + uint16_t firstBC{0}; ///< First bunch crossing of this TF, from heartbeatBC of the first CMV packet + + // Raw 16-bit CMV values, flat array indexed as [cru * NTimeBinsPerTF + timeBin] + uint16_t mDataPerTF[CRU::MaxCRU * cmv::NTimeBinsPerTF]{}; + + /// Return the raw 16-bit CMV value for a given CRU and timebin within this TF + uint16_t getCMV(const int cru, const int timeBin) const; + + /// Return the float CMV value for a given CRU and timebin within this TF + float getCMVFloat(const int cru, const int timeBin) const; + + /// Zero out raw CMV values whose float magnitude is below threshold + void zeroSmallValues(float threshold = 1.0f); + + /// Round values to the nearest integer ADC for all values whose rounded magnitude is <= threshold + void roundToIntegers(uint16_t threshold); + + /// Quantise |v| with a Gaussian-CDF recovery profile: + /// Coarse decimal-style precision below and around mean, then a smooth return to the full native I8F7 precision as the magnitude increases with width sigma + void trimGaussianPrecision(float mean, float sigma); + + /// Compress this object into a CMVPerTFCompressed using the encoding pipeline described by flags + /// Quantisation (trimGaussianPrecision / roundToIntegers / zeroSmallValues) should be applied to this object before calling compress(); it is not part of the flags pipeline + CMVPerTFCompressed compress(uint8_t flags) const; + + /// Serialise into a TTree; each Fill() call appends one entry (one TF) + std::unique_ptr toTTree() const; + + /// Write the TTree to a ROOT file + static void writeToFile(const std::string& filename, const std::unique_ptr& tree); + + private: + static int32_t cmvToSigned(uint16_t raw); ///< Sign-magnitude uint16_t → signed integer + static uint16_t quantizeBelowThreshold(uint16_t raw, float quantizationMean, float quantizationSigma); ///< Quantise sub-threshold values with a Gaussian-shaped recovery to full precision + static uint32_t zigzagEncode(int32_t value); ///< Zigzag encode + static void encodeVarintInto(uint32_t value, std::vector& out); ///< Varint encode + + public: + ClassDefNV(CMVPerTF, 1) +}; + +} // namespace o2::tpc + +#endif // ALICEO2_TPC_CMVCONTAINER_H_ diff --git a/Detectors/TPC/calibration/macro/drawCMV.C b/Detectors/TPC/calibration/macro/drawCMV.C new file mode 100644 index 0000000000000..8a89157b75721 --- /dev/null +++ b/Detectors/TPC/calibration/macro/drawCMV.C @@ -0,0 +1,160 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include + +#include "TFile.h" +#include "TParameter.h" +#include "TTree.h" +#include "TH1F.h" +#include "TH2F.h" +#include "TCanvas.h" + +#include "TPCCalibration/CMVContainer.h" +#include "TPCBase/Utils.h" +#endif + +using namespace o2::tpc; + +/// Draw CMV (Common Mode Values) vs timebin from a CCDB TTree file +/// \param filename input ROOT file containing the ccdb_object TTree +/// \param outDir output directory for saved plots; nothing is saved if empty +/// \return array of canvases +TObjArray* drawCMV(std::string_view filename, std::string_view outDir) +{ + TObjArray* arrCanvases = new TObjArray; + arrCanvases->SetName("CMV"); + + // open file + TFile f(filename.data(), "READ"); + if (f.IsZombie()) { + fmt::print("ERROR: cannot open '{}'\n", filename); + return arrCanvases; + } + fmt::print("Opened file: {}\n", filename); + + // get TTree + TTree* tree = nullptr; + f.GetObject("ccdb_object", tree); + if (!tree) { + fmt::print("ERROR: TTree 'ccdb_object' not found\n"); + return arrCanvases; + } + fmt::print("Tree 'ccdb_object' found, entries: {}\n", tree->GetEntries()); + + // read metadata + long firstTF = -1, lastTF = -1; + if (auto* userInfo = tree->GetUserInfo()) { + for (int i = 0; i < userInfo->GetSize(); ++i) { + if (auto* p = dynamic_cast*>(userInfo->At(i))) { + if (std::string(p->GetName()) == "firstTF") + firstTF = p->GetVal(); + if (std::string(p->GetName()) == "lastTF") + lastTF = p->GetVal(); + } + } + } + fmt::print("firstTF: {}, lastTF: {}\n", firstTF, lastTF); + + const int nEntries = tree->GetEntries(); + if (nEntries == 0) { + fmt::print("ERROR: no entries in tree\n"); + return arrCanvases; + } + + constexpr int nCRUs = CRU::MaxCRU; + constexpr int nTimeBins = cmv::NTimeBinsPerTF; + + TH2F* h2d = new TH2F("hCMVvsTimeBin", ";Timebin (200 ns);Common Mode Values (ADC)", + 100, 0, nTimeBins, + 110, -100.5, 9.5); + h2d->SetStats(1); + TH1F* h1d = new TH1F("hCMV", ";Common Mode Values (ADC);Counts", + 1100, -100.5, 9.5); + h1d->SetStats(1); + + // auto-detect branch format: compressed or raw + const bool isCompressed = (tree->GetBranch("CMVPerTFCompressed") != nullptr); + const bool isRaw = (tree->GetBranch("CMVPerTF") != nullptr); + if (!isCompressed && !isRaw) { + fmt::print("ERROR: no recognised branch found (expected 'CMVPerTFCompressed' or 'CMVPerTF')\n"); + return arrCanvases; + } + fmt::print("Branch format: {}\n", isCompressed ? "CMVPerTFCompressed" : "CMVPerTF (raw)"); + + o2::tpc::CMVPerTFCompressed* tfCompressed = nullptr; + o2::tpc::CMVPerTF* tfRaw = nullptr; + CMVPerTF* tfDecoded = isCompressed ? new CMVPerTF() : nullptr; + + if (isCompressed) { + tree->SetBranchAddress("CMVPerTFCompressed", &tfCompressed); + } else { + tree->SetBranchAddress("CMVPerTF", &tfRaw); + } + + long firstOrbit = -1; + + for (int i = 0; i < nEntries; ++i) { + tree->GetEntry(i); + + // Decompress if needed; resolve to a unified CMVPerTF pointer + const CMVPerTF* tf = nullptr; + if (isCompressed) { + tfCompressed->decompress(tfDecoded); + tf = tfDecoded; + } else { + tf = tfRaw; + } + + if (i == 0) { + firstOrbit = tf->firstOrbit; + } + + for (int cru = 0; cru < nCRUs; ++cru) { + for (int tb = 0; tb < nTimeBins; ++tb) { + const float cmvValue = tf->getCMVFloat(cru, tb); + h2d->Fill(tb, cmvValue); + h1d->Fill(cmvValue); + // fmt::print("cru: {}, tb: {}, cmv: {}\n", cru, tb, cmvValue); + } + } + } + + delete tfDecoded; + tree->ResetBranchAddresses(); + delete tfCompressed; + + fmt::print("firstOrbit: {}\n", firstOrbit); + + // draw + auto* c = new TCanvas("cCMVvsTimeBin", ""); + c->SetLogz(); + h2d->Draw("colz"); + + arrCanvases->Add(c); + + auto* c1 = new TCanvas("cCMVDistribution", ""); + c1->SetLogy(); + h1d->Draw(); + + arrCanvases->Add(c1); + + if (outDir.size()) { + utils::saveCanvases(*arrCanvases, outDir, "png,pdf", "CMVCanvases.root"); + } + + f.Close(); + return arrCanvases; +} diff --git a/Detectors/TPC/calibration/src/CMVContainer.cxx b/Detectors/TPC/calibration/src/CMVContainer.cxx new file mode 100644 index 0000000000000..5a3b8f1c63c3a --- /dev/null +++ b/Detectors/TPC/calibration/src/CMVContainer.cxx @@ -0,0 +1,729 @@ +// 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 CMVContainer.cxx +/// @author Tuba Gündem, tuba.gundem@cern.ch + +#include +#include +#include +#include +#include +#include + +#include "TFile.h" + +#include "TPCCalibration/CMVContainer.h" +#include "TPCBase/CRU.h" +#include "DataFormatsTPC/CMV.h" + +namespace o2::tpc +{ + +// CMVPerTF private helpers + +int32_t CMVPerTF::cmvToSigned(uint16_t raw) +{ + const int32_t mag = raw & 0x7FFF; + return (raw >> 15) ? mag : -mag; +} + +uint16_t CMVPerTF::quantizeBelowThreshold(uint16_t raw, float quantizationMean, float quantizationSigma) +{ + if (raw == 0u) { + return raw; + } + + if (quantizationSigma <= 0.f) { + return raw; + } + + const float adc = (raw & 0x7FFFu) / 128.f; + const float distance = (adc - quantizationMean) / quantizationSigma; + const float lossStrength = std::exp(-0.5f * distance * distance); + + // A true Gaussian bell: strongest trimming around the mean, then gradual recovery away from it + float quantizedADC = adc; + if (lossStrength > 0.85f) { + quantizedADC = std::round(adc * 10.f) / 10.f; + } else if (lossStrength > 0.60f) { + quantizedADC = std::round(adc * 100.f) / 100.f; + } else if (lossStrength > 0.30f) { + quantizedADC = std::round(adc * 1000.f) / 1000.f; + } else if (lossStrength > 0.12f) { + quantizedADC = std::round(adc * 10000.f) / 10000.f; + } else if (lossStrength > 0.03f) { + quantizedADC = std::round(adc * 1000000.f) / 1000000.f; + } + + // Snap the chosen decimal-style value back to the nearest raw I8F7 level + const uint16_t quantizedMagnitude = static_cast(std::clamp(std::lround(quantizedADC * 128.f), 0l, 0x7FFFl)); + return static_cast((raw & 0x8000u) | quantizedMagnitude); +} + +uint32_t CMVPerTF::zigzagEncode(int32_t value) +{ + return (static_cast(value) << 1) ^ static_cast(value >> 31); +} + +void CMVPerTF::encodeVarintInto(uint32_t value, std::vector& out) +{ + while (value > 0x7F) { + out.push_back(static_cast((value & 0x7F) | 0x80)); + value >>= 7; + } + out.push_back(static_cast(value)); +} + +// Shared file-local helpers + +namespace +{ + +int32_t zigzagDecodeLocal(uint32_t value) +{ + return static_cast((value >> 1) ^ -(value & 1)); +} + +uint16_t signedToCmvLocal(int32_t val) +{ + const uint16_t mag = static_cast(std::abs(val)) & 0x7FFF; + return static_cast((val >= 0 ? 0x8000u : 0u) | mag); +} + +uint32_t decodeVarintLocal(const uint8_t*& data, const uint8_t* end) +{ + uint32_t value = 0; + int shift = 0; + while (data < end && (*data & 0x80)) { + value |= static_cast(*data & 0x7F) << shift; + shift += 7; + ++data; + } + if (data >= end) { + throw std::runtime_error("decodeVarintLocal: unexpected end of varint data"); + } + value |= static_cast(*data) << shift; + ++data; + return value; +} + +/// Build and serialise a canonical Huffman table + bitstream over `symbols` into `buf` +/// Format: +/// 4 bytes LE uint32_t : numSymbols +/// numSymbols × 5 bytes: symbol (4 bytes LE) + code length (1 byte) +/// 8 bytes LE uint64_t : totalBits +/// ceil(totalBits/8) bytes: MSB-first bitstream +void huffmanEncode(const std::vector& symbols, std::vector& buf) +{ + // Frequency count + std::map freq; + for (const uint32_t z : symbols) { + ++freq[z]; + } + + // Build tree using index-based min-heap + struct HNode { + uint64_t freq{0}; + uint32_t sym{0}; + int left{-1}, right{-1}; + bool isLeaf{true}; + }; + std::vector nodes; + nodes.reserve(freq.size() * 2); + for (const auto& [sym, f] : freq) { + nodes.push_back({f, sym, -1, -1, true}); + } + + auto cmp = [&](int a, int b) { + return nodes[a].freq != nodes[b].freq ? nodes[a].freq > nodes[b].freq : nodes[a].sym > nodes[b].sym; + }; + std::vector heap; + heap.reserve(nodes.size()); + for (int i = 0; i < static_cast(nodes.size()); ++i) { + heap.push_back(i); + } + std::make_heap(heap.begin(), heap.end(), cmp); + + while (heap.size() > 1) { + std::pop_heap(heap.begin(), heap.end(), cmp); + const int a = heap.back(); + heap.pop_back(); + std::pop_heap(heap.begin(), heap.end(), cmp); + const int b = heap.back(); + heap.pop_back(); + nodes.push_back({nodes[a].freq + nodes[b].freq, 0, a, b, false}); + heap.push_back(static_cast(nodes.size()) - 1); + std::push_heap(heap.begin(), heap.end(), cmp); + } + + // Assign code lengths via iterative DFS + std::map codeLens; + { + const int root = heap[0]; + std::vector> stack; + stack.push_back({root, 0}); + while (!stack.empty()) { + auto [idx, depth] = stack.back(); + stack.pop_back(); + if (nodes[idx].isLeaf) { + codeLens[nodes[idx].sym] = static_cast(depth == 0 ? 1 : depth); + } else { + stack.push_back({nodes[idx].left, depth + 1}); + stack.push_back({nodes[idx].right, depth + 1}); + } + } + } + + // Sort by (codeLen ASC, symbol ASC) for canonical assignment + struct SymLen { + uint32_t sym; + uint8_t len; + }; + std::vector symLens; + symLens.reserve(codeLens.size()); + for (const auto& [sym, len] : codeLens) { + symLens.push_back({sym, len}); + } + std::sort(symLens.begin(), symLens.end(), [](const SymLen& a, const SymLen& b) { + return a.len != b.len ? a.len < b.len : a.sym < b.sym; + }); + + // Assign canonical codes + std::map> codeTable; + { + uint32_t code = 0; + uint8_t prevLen = 0; + for (const auto& sl : symLens) { + if (prevLen != 0) { + code = (code + 1) << (sl.len - prevLen); + } + codeTable[sl.sym] = {code, sl.len}; + prevLen = sl.len; + } + } + + // Serialise table header + buf.reserve(buf.size() + 4 + symLens.size() * 5 + 8 + (symbols.size() / 8 + 1)); + const uint32_t numSym = static_cast(symLens.size()); + for (int i = 0; i < 4; ++i) { + buf.push_back(static_cast((numSym >> (8 * i)) & 0xFF)); + } + for (const auto& sl : symLens) { + for (int i = 0; i < 4; ++i) { + buf.push_back(static_cast((sl.sym >> (8 * i)) & 0xFF)); + } + buf.push_back(sl.len); + } + + // Placeholder for totalBits + const size_t totalBitsOffset = buf.size(); + for (int i = 0; i < 8; ++i) { + buf.push_back(0); + } + + // Encode bitstream (MSB-first) + uint64_t totalBits = 0; + uint8_t curByte = 0; + int bitsInByte = 0; + for (const uint32_t z : symbols) { + const auto& [code, len] = codeTable.at(z); + for (int b = static_cast(len) - 1; b >= 0; --b) { + curByte = static_cast(curByte | (((code >> b) & 1u) << (7 - bitsInByte))); + ++bitsInByte; + ++totalBits; + if (bitsInByte == 8) { + buf.push_back(curByte); + curByte = 0; + bitsInByte = 0; + } + } + } + if (bitsInByte > 0) { + buf.push_back(curByte); + } + + // Backfill totalBits + for (int i = 0; i < 8; ++i) { + buf[totalBitsOffset + i] = static_cast((totalBits >> (8 * i)) & 0xFF); + } +} + +/// Decode `N` symbols from a canonical Huffman payload at [ptr, end) +/// `ptr` must point to the start of the Huffman table header (numSymbols field) +/// After return, `ptr` is advanced past the bitstream +std::vector huffmanDecode(const uint8_t*& ptr, const uint8_t* end, uint32_t N) +{ + auto readU32 = [&]() -> uint32_t { + if (ptr + 4 > end) { + throw std::runtime_error("huffmanDecode: unexpected end reading uint32"); + } + const uint32_t v = static_cast(ptr[0]) | (static_cast(ptr[1]) << 8) | + (static_cast(ptr[2]) << 16) | (static_cast(ptr[3]) << 24); + ptr += 4; + return v; + }; + + const uint32_t numSym = readU32(); + struct SymLen { + uint32_t sym; + uint8_t len; + }; + std::vector symLens(numSym); + for (uint32_t i = 0; i < numSym; ++i) { + symLens[i].sym = readU32(); + if (ptr >= end) { + throw std::runtime_error("huffmanDecode: unexpected end reading code length"); + } + symLens[i].len = *ptr++; + } + + std::map firstCode; + std::map> symsByLen; + { + uint32_t code = 0; + uint8_t prevLen = 0; + for (const auto& sl : symLens) { + if (prevLen != 0) { + code = (code + 1) << (sl.len - prevLen); + } + if (!firstCode.count(sl.len)) { + firstCode[sl.len] = code; + } + symsByLen[sl.len].push_back(sl.sym); + prevLen = sl.len; + } + } + + if (ptr + 8 > end) { + throw std::runtime_error("huffmanDecode: unexpected end reading totalBits"); + } + uint64_t totalBits = 0; + for (int i = 0; i < 8; ++i) { + totalBits |= static_cast(ptr[i]) << (8 * i); + } + ptr += 8; + + const uint8_t minLen = symLens.empty() ? 1 : symLens.front().len; + const uint8_t maxLen = symLens.empty() ? 1 : symLens.back().len; + uint64_t bitsRead = 0; + uint8_t curByte = 0; + int bitPos = -1; + + auto nextBit = [&]() -> int { + if (bitPos < 0) { + if (ptr >= end) { + throw std::runtime_error("huffmanDecode: unexpected end of bitstream"); + } + curByte = *ptr++; + bitPos = 7; + } + const int bit = (curByte >> bitPos) & 1; + --bitPos; + return bit; + }; + + std::vector out; + out.reserve(N); + while (out.size() < N) { + uint32_t accum = 0; + bool found = false; + for (uint8_t curLen = 1; curLen <= maxLen; ++curLen) { + if (bitsRead >= totalBits) { + throw std::runtime_error("huffmanDecode: bitstream exhausted before all symbols decoded"); + } + accum = (accum << 1) | static_cast(nextBit()); + ++bitsRead; + if (curLen < minLen) { + continue; + } + const auto fcIt = firstCode.find(curLen); + if (fcIt == firstCode.end()) { + continue; + } + if (accum >= fcIt->second) { + const uint32_t idx = accum - fcIt->second; + const auto& sv = symsByLen.at(curLen); + if (idx < sv.size()) { + out.push_back(sv[idx]); + found = true; + break; + } + } + } + if (!found) { + throw std::runtime_error("huffmanDecode: invalid Huffman code in bitstream"); + } + } + return out; +} + +} // anonymous namespace + +// CMVPerTF public methods + +uint16_t CMVPerTF::getCMV(const int cru, const int timeBin) const +{ + if (cru < 0 || cru >= static_cast(CRU::MaxCRU)) { + throw std::out_of_range(fmt::format("CMVPerTF::getCMV: cru {} out of range [0, {})", cru, static_cast(CRU::MaxCRU))); + } + if (timeBin < 0 || static_cast(timeBin) >= cmv::NTimeBinsPerTF) { + throw std::out_of_range(fmt::format("CMVPerTF::getCMV: timeBin {} out of range [0, {})", timeBin, static_cast(cmv::NTimeBinsPerTF))); + } + return mDataPerTF[cru * cmv::NTimeBinsPerTF + timeBin]; +} + +float CMVPerTF::getCMVFloat(const int cru, const int timeBin) const +{ + const uint16_t raw = getCMV(cru, timeBin); + const uint16_t mag = raw & 0x7FFF; + if (mag == 0) { + return 0.0f; // 0x0000 and 0x8000 both represent zero; return +0 to avoid -0 display + } + const bool positive = (raw >> 15) & 1; // bit 15: sign (1=positive, 0=negative) + return positive ? mag / 128.f : -mag / 128.f; +} + +void CMVPerTF::zeroSmallValues(float threshold) +{ + if (threshold <= 0.f) { + return; + } + for (uint32_t i = 0; i < static_cast(CRU::MaxCRU) * cmv::NTimeBinsPerTF; ++i) { + const float mag = (mDataPerTF[i] & 0x7FFF) / 128.f; + if (mag < threshold) { + mDataPerTF[i] = 0; + } + } +} + +void CMVPerTF::roundToIntegers(uint16_t threshold) +{ + if (threshold == 0) { + return; + } + for (uint32_t i = 0; i < static_cast(CRU::MaxCRU) * cmv::NTimeBinsPerTF; ++i) { + const uint16_t raw = mDataPerTF[i]; + if (raw == 0) { + continue; + } + const uint16_t rounded = static_cast(((raw & 0x7FFFu) + 64u) >> 7); + if (rounded > threshold) { + continue; // above range: keep full precision + } + mDataPerTF[i] = (rounded == 0) ? 0 : static_cast((raw & 0x8000u) | (rounded << 7)); + } +} + +void CMVPerTF::trimGaussianPrecision(float mean, float sigma) +{ + if (sigma <= 0.f) { + return; + } + + for (uint32_t i = 0; i < static_cast(CRU::MaxCRU) * cmv::NTimeBinsPerTF; ++i) { + mDataPerTF[i] = quantizeBelowThreshold(mDataPerTF[i], mean, sigma); + } +} + +CMVPerTFCompressed CMVPerTF::compress(uint8_t flags) const +{ + CMVPerTFCompressed out; + out.firstOrbit = firstOrbit; + out.firstBC = firstBC; + out.mFlags = flags; + + if (flags & CMVEncoding::kSparse) { + // --- Sparse path: position stream + value stream --- + + // Single pass per CRU: build the position stream and collect raw non-zero values. + std::vector posStream; + std::vector rawValues; + + for (int cru = 0; cru < static_cast(CRU::MaxCRU); ++cru) { + struct Entry { + uint32_t tb; + uint16_t val; + }; + std::vector entries; + for (uint32_t tb = 0; tb < cmv::NTimeBinsPerTF; ++tb) { + const uint16_t val = mDataPerTF[cru * cmv::NTimeBinsPerTF + tb]; + if (val != 0) { + entries.push_back({tb, val}); + } + } + + encodeVarintInto(static_cast(entries.size()), posStream); + uint32_t prevTB = 0; + bool first = true; + for (const auto& e : entries) { + encodeVarintInto(first ? e.tb : (e.tb - prevTB), posStream); + rawValues.push_back(e.val); + prevTB = e.tb; + first = false; + } + } + + // Encode the value stream based on flags. + std::vector valStream; + if (flags & CMVEncoding::kZigzag) { + std::vector zigzags; + zigzags.reserve(rawValues.size()); + for (const uint16_t v : rawValues) { + zigzags.push_back(zigzagEncode(cmvToSigned(v))); + } + if (flags & CMVEncoding::kHuffman) { + huffmanEncode(zigzags, valStream); + } else { // kVarint + for (const uint32_t z : zigzags) { + encodeVarintInto(z, valStream); + } + } + } else { + // Raw uint16 LE + for (const uint16_t v : rawValues) { + valStream.push_back(static_cast(v & 0xFF)); + valStream.push_back(static_cast(v >> 8)); + } + } + + // Assemble: [4 bytes posStreamSize][posStream][valStream] + const uint32_t posStreamSize = static_cast(posStream.size()); + out.mData.reserve(4 + posStream.size() + valStream.size()); + for (int i = 0; i < 4; ++i) { + out.mData.push_back(static_cast((posStreamSize >> (8 * i)) & 0xFF)); + } + out.mData.insert(out.mData.end(), posStream.begin(), posStream.end()); + out.mData.insert(out.mData.end(), valStream.begin(), valStream.end()); + + } else { + // --- Dense path: all CRU * TimeBin values --- + const uint32_t total = static_cast(CRU::MaxCRU) * cmv::NTimeBinsPerTF; + + if (!(flags & CMVEncoding::kZigzag)) { + // No encoding: raw uint16 LE + out.mData.reserve(total * 2); + for (uint32_t i = 0; i < total; ++i) { + out.mData.push_back(static_cast(mDataPerTF[i] & 0xFF)); + out.mData.push_back(static_cast(mDataPerTF[i] >> 8)); + } + } else { + // Zigzag + optional delta (CRU-major, time-minor) + const bool useDelta = (flags & CMVEncoding::kDelta) != 0; + std::vector zigzags; + zigzags.reserve(total); + for (int cru = 0; cru < static_cast(CRU::MaxCRU); ++cru) { + int32_t prev = 0; + for (uint32_t tb = 0; tb < cmv::NTimeBinsPerTF; ++tb) { + const int32_t val = cmvToSigned(mDataPerTF[cru * cmv::NTimeBinsPerTF + tb]); + const int32_t encoded = useDelta ? (val - prev) : val; + if (useDelta) { + prev = val; + } + zigzags.push_back(zigzagEncode(encoded)); + } + } + + if (flags & CMVEncoding::kHuffman) { + huffmanEncode(zigzags, out.mData); + } else { // kVarint + for (const uint32_t z : zigzags) { + encodeVarintInto(z, out.mData); + } + } + } + } + + return out; +} + +// CMVPerTFCompressed::decompress staged pipeline + +std::vector> CMVPerTFCompressed::decodeSparsePositions(const uint8_t*& ptr, const uint8_t* end) +{ + // Read 4-byte LE posStreamSize + if (ptr + 4 > end) { + throw std::runtime_error("CMVPerTFCompressed::decompress: truncated position header"); + } + const uint32_t posStreamSize = static_cast(ptr[0]) | (static_cast(ptr[1]) << 8) | + (static_cast(ptr[2]) << 16) | (static_cast(ptr[3]) << 24); + ptr += 4; + + const uint8_t* posEnd = ptr + posStreamSize; + if (posEnd > end) { + throw std::runtime_error("CMVPerTFCompressed::decompress: posStream overflows payload"); + } + + // Decode per-CRU varint(N) + N×varint(tb_delta) + std::vector> positions; + const uint8_t* p = ptr; + for (int cru = 0; cru < static_cast(CRU::MaxCRU); ++cru) { + const uint32_t count = decodeVarintLocal(p, posEnd); + uint32_t tb = 0; + bool first = true; + for (uint32_t i = 0; i < count; ++i) { + const uint32_t delta = decodeVarintLocal(p, posEnd); + tb = first ? delta : (tb + delta); + first = false; + positions.emplace_back(cru, tb); + } + } + ptr = posEnd; // advance past the entire position block + return positions; +} + +std::vector CMVPerTFCompressed::decodeValueStream(const uint8_t*& ptr, const uint8_t* end, uint32_t N, uint8_t flags) +{ + if (flags & CMVEncoding::kHuffman) { + // Huffman-encoded symbols + return huffmanDecode(ptr, end, N); + } + + if (flags & CMVEncoding::kVarint) { + // Varint-encoded symbols + std::vector out; + out.reserve(N); + for (uint32_t i = 0; i < N; ++i) { + out.push_back(decodeVarintLocal(ptr, end)); + } + return out; + } + + // Raw uint16 LE (no value encoding) + std::vector out; + out.reserve(N); + for (uint32_t i = 0; i < N; ++i) { + if (ptr + 2 > end) { + throw std::runtime_error("CMVPerTFCompressed::decompress: unexpected end in raw value stream"); + } + const uint16_t v = static_cast(ptr[0]) | (static_cast(ptr[1]) << 8); + ptr += 2; + out.push_back(v); + } + return out; +} + +void CMVPerTFCompressed::decodeSparseValues(const std::vector& symbols, + const std::vector>& positions, + uint8_t flags, CMVPerTF* cmv) +{ + const bool useZigzag = (flags & CMVEncoding::kZigzag) != 0; + for (uint32_t i = 0; i < static_cast(positions.size()); ++i) { + uint16_t raw; + if (useZigzag) { + raw = signedToCmvLocal(zigzagDecodeLocal(symbols[i])); + } else { + raw = static_cast(symbols[i]); + } + cmv->mDataPerTF[positions[i].first * cmv::NTimeBinsPerTF + positions[i].second] = raw; + } +} + +void CMVPerTFCompressed::decodeDenseValues(const std::vector& symbols, uint8_t flags, CMVPerTF* cmv) +{ + const bool useZigzag = (flags & CMVEncoding::kZigzag) != 0; + const bool useDelta = (flags & CMVEncoding::kDelta) != 0; + + if (!useZigzag) { + // Symbols are raw uint16 values; write directly + for (uint32_t i = 0; i < static_cast(symbols.size()); ++i) { + cmv->mDataPerTF[i] = static_cast(symbols[i]); + } + return; + } + + // Inverse zigzag + optional inverse delta (CRU-major, time-minor) + uint32_t s = 0; + for (int cru = 0; cru < static_cast(CRU::MaxCRU); ++cru) { + int32_t prev = 0; + for (uint32_t tb = 0; tb < cmv::NTimeBinsPerTF; ++tb, ++s) { + int32_t val = zigzagDecodeLocal(symbols[s]); + if (useDelta) { + val += prev; + prev = val; + } + cmv->mDataPerTF[s] = signedToCmvLocal(val); + } + } +} + +void CMVPerTFCompressed::decompress(CMVPerTF* cmv) const +{ + if (!cmv) { + throw std::invalid_argument("CMVPerTFCompressed::decompress: cmv pointer is null"); + } + cmv->firstOrbit = firstOrbit; + cmv->firstBC = firstBC; + std::fill(std::begin(cmv->mDataPerTF), std::end(cmv->mDataPerTF), uint16_t(0)); + + const uint8_t* ptr = mData.data(); + const uint8_t* end = ptr + mData.size(); + + if (mFlags & CMVEncoding::kSparse) { + // Stage 1: decode position stream + auto positions = decodeSparsePositions(ptr, end); + const uint32_t N = static_cast(positions.size()); + + // Stage 2: decode value stream (Huffman / varint / raw) + auto symbols = decodeValueStream(ptr, end, N, mFlags); + + // Stage 3: inverse zigzag and scatter into CMV array + decodeSparseValues(symbols, positions, mFlags, cmv); + } else { + const uint32_t N = static_cast(CRU::MaxCRU) * cmv::NTimeBinsPerTF; + + // Stage 1: decode value stream (Huffman / varint / raw) + auto symbols = decodeValueStream(ptr, end, N, mFlags); + + // Stage 2: inverse zigzag, inverse delta, fill CMV array + decodeDenseValues(symbols, mFlags, cmv); + } +} + +std::unique_ptr CMVPerTF::toTTree() const +{ + auto tree = std::make_unique("ccdb_object", "ccdb_object"); + tree->SetAutoSave(0); + tree->SetDirectory(nullptr); + + const CMVPerTF* ptr = this; + tree->Branch("CMVPerTF", &ptr); + tree->Fill(); + + tree->ResetBranchAddresses(); + return tree; +} + +std::unique_ptr CMVPerTFCompressed::toTTree() const +{ + auto tree = std::make_unique("ccdb_object", "ccdb_object"); + tree->SetAutoSave(0); + tree->SetDirectory(nullptr); + + const CMVPerTFCompressed* ptr = this; + tree->Branch("CMVPerTFCompressed", &ptr); + tree->Fill(); + + tree->ResetBranchAddresses(); + return tree; +} + +void CMVPerTF::writeToFile(const std::string& filename, const std::unique_ptr& tree) +{ + TFile f(filename.c_str(), "RECREATE"); + if (f.IsZombie()) { + throw std::runtime_error(fmt::format("CMVPerTF::writeToFile: cannot open '{}'", filename)); + } + tree->Write(); + f.Close(); +} + +} // namespace o2::tpc diff --git a/Detectors/TPC/calibration/src/TPCCalibrationLinkDef.h b/Detectors/TPC/calibration/src/TPCCalibrationLinkDef.h index 6e15e2dd0427a..14d3d0a8ffb8e 100644 --- a/Detectors/TPC/calibration/src/TPCCalibrationLinkDef.h +++ b/Detectors/TPC/calibration/src/TPCCalibrationLinkDef.h @@ -123,4 +123,8 @@ #pragma link C++ class o2::tpc::DigitAdd + ; #pragma link C++ class std::vector < o2::tpc::DigitAdd> + ; #pragma link C++ class o2::tpc::PressureTemperatureHelper + ; + +#pragma link C++ class o2::tpc::CMVPerTF + ; +#pragma link C++ class o2::tpc::CMVPerTFCompressed + ; + #endif diff --git a/Detectors/TPC/workflow/CMakeLists.txt b/Detectors/TPC/workflow/CMakeLists.txt index 6930f332bfbf1..0f8d73b1cbe7e 100644 --- a/Detectors/TPC/workflow/CMakeLists.txt +++ b/Detectors/TPC/workflow/CMakeLists.txt @@ -25,6 +25,7 @@ o2_add_library(TPCWorkflow src/KryptonRawFilterSpec.cxx src/OccupancyFilterSpec.cxx src/SACProcessorSpec.cxx + src/CMVToVectorSpec.cxx src/IDCToVectorSpec.cxx src/CalibdEdxSpec.cxx src/CalibratordEdxSpec.cxx @@ -288,4 +289,19 @@ o2_add_executable(pressure-temperature SOURCES src/tpc-pressure-temperature.cxx PUBLIC_LINK_LIBRARIES O2::TPCWorkflow) -add_subdirectory(readers) +o2_add_executable(cmv-to-vector + COMPONENT_NAME tpc + SOURCES src/tpc-cmv-to-vector.cxx + PUBLIC_LINK_LIBRARIES O2::TPCWorkflow) + +o2_add_executable(cmv-flp + COMPONENT_NAME tpc + SOURCES src/tpc-flp-cmv.cxx + PUBLIC_LINK_LIBRARIES O2::TPCWorkflow) + +o2_add_executable(cmv-distribute + COMPONENT_NAME tpc + SOURCES src/tpc-distribute-cmv.cxx + PUBLIC_LINK_LIBRARIES O2::TPCWorkflow) + +add_subdirectory(readers) \ No newline at end of file diff --git a/Detectors/TPC/workflow/README.md b/Detectors/TPC/workflow/README.md index e34faa2813edf..b7a19da121e9b 100644 --- a/Detectors/TPC/workflow/README.md +++ b/Detectors/TPC/workflow/README.md @@ -274,3 +274,191 @@ To directly dump the digits to file for inspection use for the reco workflow ```bash | o2-tpc-reco-workflow --input-type digitizer --output-type digits --disable-mc ``` + +## TPC Common Mode Value (CMV) Workflows + +The CMV workflows parse raw TPC data, buffer Common Mode Values per CRU on FLPs, then merge and aggregate them on a calibration node before serializing the CMVContainer in a TTree. The resulting object can be uploaded to the CCDB or written to the disk. + +### Workflow components + +| Executable | Output | Description | +|---|---|---| +| `o2-tpc-cmv-to-vector` | `TPC/CMVVECTOR` | Parses raw TPC data and creates vectors of CMVs per CRU | +| `o2-tpc-cmv-flp` | `TPC/CMVGROUP` | Buffers N TFs per CRU on the FLP and groups them for forwarding | +| `o2-tpc-cmv-distribute` | TTree / CCDB payload | Merges CRUs over N TFs on the calibration node, serializes the CMVContainer into a TTree, and either writes it to disk (`--dump-cmvs`) or forwards it as a CCDB object (`--enable-CCDB-output`) | + +#### `o2-tpc-cmv-to-vector` + +| Option | Default | Description | +|---|---|---| +| `--input-spec` | `A:TPC/RAWDATA` | DPL input spec for raw TPC data | +| `--crus` | `0-359` | CRU range to process, comma-separated ranges | +| `--write-debug` | false | Write a debug output tree every TF | +| `--write-debug-on-error` | false | Write a debug output tree only when decoding errors occur | +| `--debug-file-name` | `/tmp/cmv_vector_debug.{run}.root` | Name of the debug output ROOT file | +| `--write-raw-data-on-error` | false | Dump raw data to file when decoding errors occur | +| `--raw-file-name` | `/tmp/cmv_debug.{run}.{raw_type}` | Name of the raw debug output file | +| `--raw-data-type` | 0 | Raw data format to dump on error: 0 = full TPC with DPL header, 1 = full TPC with DPL header (skip empty), 2 = full TPC no DPL header, 3 = full TPC no DPL header (skip empty), 4 = IDC raw only, 5 = CMV raw only | +| `--check-incomplete-hbf` | false | Check and report incomplete HBFs in the raw parser | + +#### `o2-tpc-cmv-flp` + +| Option | Default | Description | +|---|---|---| +| `--crus` | `0-359` | CRU range handled by this FLP | +| `--lanes` | hw_concurrency/2 | Parallel processing lanes (CRUs split per lane) | +| `--time-lanes` | 1 | Parallel lanes for time-frame splitting | +| `--n-TFs-buffer` | 1 | Number of TFs to buffer before forwarding | +| `--dump-cmvs-flp` | false | Dump raw CMV vectors per CRU to a ROOT file each TF (for debugging) | + +#### `o2-tpc-cmv-distribute` + +| Option | Default | Description | +|---|---|---| +| `--crus` | `0-359` | CRU range expected from upstream | +| `--timeframes` | 2000 | Number of TFs aggregated per calibration interval | +| `--firstTF` | -1 | First time frame index; -1 = auto-detect from first incoming TF; values < -1 set an offset of `\|firstTF\|+1` TFs before the first interval begins | +| `--lanes` | 1 | Number of parallel lanes (CRUs are split evenly across lanes) | +| `--n-TFs-buffer` | 1 | Number of TFs buffered per group in the upstream `o2-tpc-cmv-flp` (must match that workflow's setting) | +| `--enable-CCDB-output` | false | Forward the CMVContainer TTree as a CCDB object to `o2-calibration-ccdb-populator-workflow` | +| `--use-precise-timestamp` | false | Fetch orbit-reset and GRPECS from CCDB to compute a precise CCDB validity timestamp | +| `--dump-cmvs` | false | Write the CMVContainer TTree to a local ROOT file on disk | +| `--use-sparse` | false | Sparse encoding: skip zero time bins (raw uint16 values; combine with `--use-compression-varint` or `--use-compression-huffman` for compressed sparse output) | +| `--use-compression-varint` | false | Delta + zigzag + varint compression over all values; combined with `--use-sparse`: varint-encoded exact values at non-zero positions | +| `--use-compression-huffman` | false | Huffman encoding over all values; combined with `--use-sparse`: Huffman-encoded exact values at non-zero positions | +| `--cmv-zero-threshold` | 0 | Zero out CMV values whose magnitude is below this threshold (ADC) after optional rounding and before compression; 0 disables | +| `--cmv-round-integers-threshold` | 0 | Round values to nearest integer ADC for \|v\| ≤ N ADC before compression; 0 disables | +| `--cmv-dynamic-precision-mean` | 1.0 | Gaussian centre in \|CMV\| (ADC) where the strongest fractional-bit trimming is applied | +| `--cmv-dynamic-precision-sigma` | 0 | Gaussian width (ADC) for smooth CMV fractional-bit trimming; 0 disables | +| `--drop-data-after-nTFs` | 0 | Drop data for a relative TF slot after this many TFs have passed without receiving all CRUs; 0 uses the default derived from `--check-data-every-n` | +| `--check-data-every-n` | 0 | Check for missing CRU data every N invocations of the run function; -1 disables checking, 0 uses the default (timeframes/2) | +| `--nFactorTFs` | 1000 | Number of TFs to skip before flushing the oldest incomplete aggregation interval | + +### Example 1 — Simple usage for testing + +```bash +#!/bin/bash + +hash="test" +MAX_TFS=1 +CRUS="0-359" + +ARGS_ALL="-b --session ${USER}.${hash} --shm-segment-size $((8<<30))" + +o2-raw-tf-reader-workflow $ARGS_ALL \ + --input-data tf.subset.list \ + --max-tf ${MAX_TFS} | +o2-tfidinfo-writer-workflow $ARGS_ALL \ + --early-forward-policy noraw \ + --fairmq-rate-logging 0 \ + --timeframes-rate-limit ${MAX_TFS} \ + --timeframes-rate-limit-ipcid 583693664 | +o2-tpc-cmv-to-vector $ARGS_ALL \ + --input-spec "A:TPC/RAWDATA" \ + --write-debug-on-error \ + --crus ${CRUS} | +o2-tpc-cmv-flp $ARGS_ALL \ + --crus ${CRUS} | +o2-tpc-cmv-distribute $ARGS_ALL \ + --crus ${CRUS} \ + --dump-cmvs \ + --enable-CCDB-output \ + --cmv-zero-threshold 1.0 \ + --cmv-dynamic-precision-mean 1.0 \ + --cmv-dynamic-precision-sigma 8.0 \ + --use-sparse \ + --use-compression-huffman | +o2-calibration-ccdb-populator-workflow $ARGS_ALL \ + --ccdb-path ccdb-test.cern.ch:8080 +``` + +### Example 2 — Bash scripts for more realistic testing + +In a real online setup, multiple FLPs each process their own CRU subset and forward compressed CMV groups to a central aggregator node via ZeroMQ. + +**FLP side (`Send.sh`)** — run one instance per FLP (pass `N_FLPs` as first argument): + +```bash +#!/bin/bash + +# Number of FLPs (passed as first argument, default 1) +N_FLPs=${1:-1} + +hash="test" +MAX_TFS=1 + +minCRU=0 +maxCRU=360 + +ARGS_ALL="-b --shm-segment-size $((8<<30))" + +for ((i = 0; i < ${N_FLPs}; i++)); do + xpos_start=100 + xpos=$((xpos_start + 1000 * $i)) + + let diff=${maxCRU}-${minCRU} + let Start=${minCRU}+$i*${diff}/${N_FLPs} + let End=$Start+${diff}/${N_FLPs}-1 + + crus="$Start-$End" + echo "FLP $i: crus $crus" + + xterm -hold -geometry 150x41+$xpos+300 -e bash -c "unset PYTHONHOME PYTHONPATH; echo FLP $i; + o2-raw-tf-reader-workflow $ARGS_ALL \ + --session ${USER}.${hash}.send.$i \ + --input-data tf.subset.list \ + --max-tf ${MAX_TFS} | + o2-tfidinfo-writer-workflow $ARGS_ALL \ + --session ${USER}.${hash}.send.$i \ + --early-forward-policy noraw \ + --fairmq-rate-logging 0 \ + --timeframes-rate-limit ${MAX_TFS} \ + --timeframes-rate-limit-ipcid $((583693664 + $i)) | + o2-tpc-cmv-to-vector $ARGS_ALL \ + --session ${USER}.${hash}.send.$i \ + --input-spec 'A:TPC/RAWDATA' \ + --write-debug-on-error \ + --crus ${crus} | + o2-tpc-cmv-flp $ARGS_ALL \ + --session ${USER}.${hash}.send.$i \ + --crus ${crus} | + o2-dpl-output-proxy $ARGS_ALL \ + --session ${USER}.${hash}.send.$i \ + --sporadic-inputs \ + --channel-config 'name=downstream,method=connect,address=tcp://localhost:30453,type=push,transport=zeromq' \ + --dataspec 'downstream:TPC/CMVGROUP;downstream:TPC/CMVORBITINFO'; exec bash" & +done +``` + +Each FLP connects to the aggregator's pull socket on port `30453` and pushes `TPC/CMVGROUP` and `TPC/CMVORBITINFO` messages. The CRU range is automatically split evenly across `N_FLPs`. + +**Aggregator side (`Receive.sh`)**: + +```bash +#!/bin/bash + +hash="test" +CRUS="0-359" + +ARGS_ALL="-b --session ${USER}.${hash}.receive --shm-segment-size $((8<<30))" + +# ZeroMQ proxy: pull from all FLPs connecting on port 30453 +configProxy="name=readout-proxy,type=pull,method=bind,address=tcp://localhost:30453,rateLogging=1,transport=zeromq" + +o2-dpl-raw-proxy $ARGS_ALL \ + --channel-config "${configProxy}" \ + --dataspec "A:TPC/CMVGROUP;A:TPC/CMVORBITINFO" | +o2-tpc-cmv-distribute $ARGS_ALL \ + --crus ${CRUS} \ + --dump-cmvs \ + --enable-CCDB-output \ + --cmv-zero-threshold 1.0 \ + --cmv-dynamic-precision-mean 1.0 \ + --cmv-dynamic-precision-sigma 8.0 \ + --use-sparse \ + --use-compression-huffman | +o2-calibration-ccdb-populator-workflow $ARGS_ALL \ + --ccdb-path ccdb-test.cern.ch:8080 +``` + +The aggregator binds the ZeroMQ pull socket and waits for all FLPs to connect. Once `TPC/CMVGROUP` and `TPC/CMVORBITINFO` data arrive, `o2-tpc-cmv-distribute` merges them, applies compression, writes the object to the disk and uploads to the CCDB. diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/CMVToVectorSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/CMVToVectorSpec.h new file mode 100644 index 0000000000000..add37af5706e5 --- /dev/null +++ b/Detectors/TPC/workflow/include/TPCWorkflow/CMVToVectorSpec.h @@ -0,0 +1,30 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file CMVToVectorSpec.h +/// @author Tuba Gündem, tuba.gundem@cern.ch +/// @brief Processor to convert CMVs to a vector in a CRU + +#ifndef TPC_CMVToVectorSpec_H_ +#define TPC_CMVToVectorSpec_H_ + +#include "Framework/DataProcessorSpec.h" + +namespace o2::tpc +{ + +/// create a processor spec +/// convert CMV raw values to a vector in a CRU +o2::framework::DataProcessorSpec getCMVToVectorSpec(const std::string inputSpec, std::vector const& crus); + +} // end namespace o2::tpc + +#endif // TPC_CMVToVectorSpec_H_ diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TPCDistributeCMVSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCDistributeCMVSpec.h new file mode 100644 index 0000000000000..c1744ce86d3ac --- /dev/null +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TPCDistributeCMVSpec.h @@ -0,0 +1,621 @@ +// 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 TPCDistributeCMVSpec.h +/// @author Tuba Gündem, tuba.gundem@cern.ch +/// @brief TPC aggregation of grouped CMVs + +#ifndef O2_TPCDISTRIBUTECMVSPEC_H +#define O2_TPCDISTRIBUTECMVSPEC_H + +#include +#include +#include +#include "TParameter.h" +#include "Framework/Task.h" +#include "Framework/ControlService.h" +#include "Framework/Logger.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/InputRecordWalker.h" +#include "Framework/DataTakingContext.h" +#include "Headers/DataHeader.h" +#include "Framework/ConfigParamRegistry.h" +#include "TPCWorkflow/TPCFLPCMVSpec.h" +#include "MemoryResources/MemoryResources.h" +#include "TPCWorkflow/ProcessingHelpers.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "CommonDataFormat/Pair.h" +#include "TMemFile.h" +#include "CCDB/CcdbApi.h" +#include "CCDB/CcdbObjectInfo.h" +#include "DetectorsCalibration/Utils.h" +#include "TPCCalibration/CMVContainer.h" +#include "DataFormatsTPC/CMV.h" + +using namespace o2::framework; +using o2::header::gDataOriginTPC; +using namespace o2::tpc; + +namespace o2::tpc +{ + +class TPCDistributeCMVSpec : public o2::framework::Task +{ + public: + TPCDistributeCMVSpec(const std::vector& crus, const unsigned int timeframes, const int nTFsBuffer, const int firstTF, const bool sendCCDB, const bool usePreciseTimestamp, std::shared_ptr req) + : mCRUs{crus}, + mTimeFrames{timeframes}, + mNTFsBuffer{nTFsBuffer}, + mProcessedCRU{{std::vector(timeframes), std::vector(timeframes)}}, + mTFStart{{firstTF, firstTF + timeframes}}, + mTFEnd{{firstTF + timeframes - 1, mTFStart[1] + timeframes - 1}}, + mCCDBRequest(req), + mSendCCDB{sendCCDB}, + mUsePreciseTimestamp{usePreciseTimestamp}, + mSendCCDBOutputOrbitReset(1), + mSendCCDBOutputGRPECS(1), + mOrbitInfoForwarded{{std::vector(timeframes, false), std::vector(timeframes, false)}} + { + // sort vector for binary_search + std::sort(mCRUs.begin(), mCRUs.end()); + + for (auto& processedCRUbuffer : mProcessedCRUs) { + processedCRUbuffer.resize(mTimeFrames); + for (auto& crusMap : processedCRUbuffer) { + crusMap.reserve(mCRUs.size()); + for (const auto cruID : mCRUs) { + crusMap.emplace(cruID, false); + } + } + } + + mFilter.emplace_back(InputSpec{"cmvsgroup", ConcreteDataTypeMatcher{gDataOriginTPC, TPCFLPCMVDevice::getDataDescriptionCMVGroup()}, Lifetime::Sporadic}); + mOrbitFilter.emplace_back(InputSpec{"cmvorbit", ConcreteDataTypeMatcher{gDataOriginTPC, TPCFLPCMVDevice::getDataDescriptionCMVOrbitInfo()}, Lifetime::Sporadic}); + + // pre-allocate the accumulator TTree for the current aggregation interval + initIntervalTree(); + }; + + void init(o2::framework::InitContext& ic) final + { + o2::base::GRPGeomHelper::instance().setRequest(mCCDBRequest); + mNFactorTFs = ic.options().get("nFactorTFs"); + mNTFsDataDrop = ic.options().get("drop-data-after-nTFs"); + mCheckEveryNData = ic.options().get("check-data-every-n"); + if (mCheckEveryNData == 0) { + mCheckEveryNData = mTimeFrames / 2; + if (mCheckEveryNData == 0) { + mCheckEveryNData = 1; + } + mNTFsDataDrop = mCheckEveryNData; + } + mDumpCMVs = ic.options().get("dump-cmvs"); + mUseCompressionVarint = ic.options().get("use-compression-varint"); + mUseSparse = ic.options().get("use-sparse"); + mUseCompressionHuffman = ic.options().get("use-compression-huffman"); + mRoundIntegersThreshold = static_cast(ic.options().get("cmv-round-integers-threshold")); + mZeroThreshold = ic.options().get("cmv-zero-threshold"); + mDynamicPrecisionMean = ic.options().get("cmv-dynamic-precision-mean"); + mDynamicPrecisionSigma = ic.options().get("cmv-dynamic-precision-sigma"); + LOGP(info, "CMV compression settings: use-compression-varint={}, use-sparse={}, use-compression-huffman={}, cmv-round-integers-threshold={}, cmv-zero-threshold={}, cmv-dynamic-precision-mean={}, cmv-dynamic-precision-sigma={}", + mUseCompressionVarint, mUseSparse, mUseCompressionHuffman, mRoundIntegersThreshold, mZeroThreshold, mDynamicPrecisionMean, mDynamicPrecisionSigma); + // re-initialise the interval tree now that compression options are known (constructor used the defaults) + initIntervalTree(); + } + + void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final + { + o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj); + if (matcher == ConcreteDataMatcher("CTP", "ORBITRESET", 0)) { + LOGP(info, "Updating ORBITRESET"); + std::fill(mSendCCDBOutputOrbitReset.begin(), mSendCCDBOutputOrbitReset.end(), true); + } else if (matcher == ConcreteDataMatcher("GLO", "GRPECS", 0)) { + // check if received object is valid + if (o2::base::GRPGeomHelper::instance().getGRPECS()->getRun() != 0) { + LOGP(info, "Updating GRPECS"); + std::fill(mSendCCDBOutputGRPECS.begin(), mSendCCDBOutputGRPECS.end(), true); + } else { + LOGP(info, "Detected default GRPECS object"); + } + } + } + + void run(o2::framework::ProcessingContext& pc) final + { + // capture orbit-reset info once for precise CCDB timestamp calculation + if (mCCDBRequest->askTime) { + const bool grpecsValid = pc.inputs().isValid("grpecs"); + const bool orbitResetValid = pc.inputs().isValid("orbitReset"); + if (grpecsValid) { + pc.inputs().get("grpecs"); + } + if (orbitResetValid) { + pc.inputs().get*>("orbitReset"); + } + if (pc.inputs().countValidInputs() == (grpecsValid + orbitResetValid)) { + return; + } + // update mTFInfo from GRPGeomHelper whenever orbit-reset or GRPECS objects are fresh + if (mSendCCDBOutputOrbitReset[0] && mSendCCDBOutputGRPECS[0]) { + mSendCCDBOutputOrbitReset[0] = false; + mSendCCDBOutputGRPECS[0] = false; + mTFInfo = dataformats::Pair{o2::base::GRPGeomHelper::instance().getOrbitResetTimeMS(), o2::base::GRPGeomHelper::instance().getNHBFPerTF()}; + } + } + + const auto tf = processing_helpers::getCurrentTF(pc); + mLastSeenTF = tf; // track for endOfStream flush + + // automatically detect firstTF in case firstTF was not specified + if (mTFStart.front() <= -1) { + const auto firstTF = tf; + const long offsetTF = std::abs(mTFStart.front() + 1); + const auto nTotTFs = getNRealTFs(); + mTFStart = {firstTF + offsetTF, firstTF + offsetTF + nTotTFs}; + mTFEnd = {mTFStart[1] - 1, mTFStart[1] - 1 + nTotTFs}; + LOGP(info, "Setting {} as first TF", mTFStart[0]); + LOGP(info, "Using offset of {} TFs for setting the first TF", offsetTF); + } + + // check which buffer to use for current incoming data + const bool currentBuffer = (tf > mTFEnd[mBuffer]) ? !mBuffer : mBuffer; + if (mTFStart[currentBuffer] > tf) { + LOGP(info, "All CRUs for current TF {} already received. Skipping this TF", tf); + return; + } + + const unsigned int relTF = (tf - mTFStart[currentBuffer]) / mNTFsBuffer; + LOGP(info, "Current TF: {}, relative TF: {}, current buffer: {}, mTFStart: {}", tf, relTF, currentBuffer, mTFStart[currentBuffer]); + + if (relTF >= mProcessedCRU[currentBuffer].size()) { + LOGP(warning, "Skipping tf {}: relative tf {} is larger than size of buffer: {}", tf, relTF, mProcessedCRU[currentBuffer].size()); + + // check number of processed CRUs for previous TFs. If CRUs are missing for them, they are probably lost/not received + mProcessedTotalData = mCheckEveryNData; + checkIntervalsForMissingData(pc, currentBuffer, relTF, tf); + return; + } + + if (mProcessedCRU[currentBuffer][relTF] == mCRUs.size()) { + return; + } + + // record the absolute first TF of this aggregation interval + if (mIntervalTFCount == 0) { + mIntervalFirstTF = tf; + } + + // set CCDB start timestamp once at the start of each aggregation interval + if (mTimestampStart == 0) { + setTimestampCCDB(relTF, pc); + } + + // capture orbit/BC info into the interval once per relTF. + // all CRUs within a TF carry identical timing, so the first one is sufficient. + if (!mOrbitInfoForwarded[currentBuffer][relTF]) { + for (auto& ref : InputRecordWalker(pc.inputs(), mOrbitFilter)) { + auto const* hdr = o2::framework::DataRefUtils::getHeader(ref); + const unsigned int cru = hdr->subSpecification >> 7; + if (std::binary_search(mCRUs.begin(), mCRUs.end(), cru)) { + const auto orbitBC = pc.inputs().get(ref); + if (mCurrentTF.firstOrbit == 0 && mCurrentTF.firstBC == 0) { + mCurrentTF.firstOrbit = static_cast(orbitBC >> 32); + mCurrentTF.firstBC = static_cast(orbitBC & 0xFFFFu); + } + mOrbitInfoForwarded[currentBuffer][relTF] = true; + break; // one per relTF is enough + } + } + } + + for (auto& ref : InputRecordWalker(pc.inputs(), mFilter)) { + auto const* tpcCRUHeader = o2::framework::DataRefUtils::getHeader(ref); + const unsigned int cru = tpcCRUHeader->subSpecification >> 7; + + // check if cru is specified in input cru list + if (!(std::binary_search(mCRUs.begin(), mCRUs.end(), cru))) { + LOGP(info, "Received data from CRU: {} which was not specified as input. Skipping", cru); + continue; + } + + if (mProcessedCRUs[currentBuffer][relTF][cru]) { + continue; + } else { + // count total number of processed CRUs for given TF + ++mProcessedCRU[currentBuffer][relTF]; + + // to keep track of processed CRUs + mProcessedCRUs[currentBuffer][relTF][cru] = true; + } + + // accumulate raw 16-bit CMVs into the flat array for the current TF + auto cmvVec = pc.inputs().get>(ref); + const uint32_t nTimeBins = std::min(static_cast(cmvVec.size()), cmv::NTimeBinsPerTF); + for (uint32_t tb = 0; tb < nTimeBins; ++tb) { + mCurrentTF.mDataPerTF[cru * cmv::NTimeBinsPerTF + tb] = cmvVec[tb]; + } + } + + LOGP(info, "Number of received CRUs for current TF: {} Needed a total number of processed CRUs of: {} Current TF: {}", mProcessedCRU[currentBuffer][relTF], mCRUs.size(), tf); + + // check for missing data if specified + if (mNTFsDataDrop > 0) { + checkIntervalsForMissingData(pc, currentBuffer, relTF, tf); + } + + if (mProcessedCRU[currentBuffer][relTF] == mCRUs.size()) { + ++mProcessedTFs[currentBuffer]; + + // Pre-processing: quantisation / rounding / zeroing (applied before compression) + mCurrentTF.roundToIntegers(mRoundIntegersThreshold); + if (mZeroThreshold > 0.f) { + mCurrentTF.zeroSmallValues(mZeroThreshold); + } + if (mDynamicPrecisionSigma > 0.f) { + mCurrentTF.trimGaussianPrecision(mDynamicPrecisionMean, mDynamicPrecisionSigma); + } + + // Compress; the raw CMVPerTF branch is used when all flags are zero + const uint8_t flags = buildCompressionFlags(); + if (flags != CMVEncoding::kNone) { + mCurrentCompressedTF = mCurrentTF.compress(flags); + } + + mIntervalTree->Fill(); + ++mIntervalTFCount; + mCurrentTF = CMVPerTF{}; + } + + if (mProcessedTFs[currentBuffer] == mTimeFrames) { + sendOutput(pc.outputs(), tf); + finishInterval(pc, currentBuffer, tf); + } + } + + void endOfStream(o2::framework::EndOfStreamContext& ec) final + { + LOGP(info, "End of stream, flushing CMV interval ({} TFs)", mIntervalTFCount); + // correct mTFEnd for the partial last interval so the CCDB validity end timestamp reflects the actual last TF, not the expected interval end + mTFEnd[mBuffer] = mLastSeenTF; + sendOutput(ec.outputs(), mLastSeenTF); + ec.services().get().readyToQuit(QuitRequest::Me); + } + + static constexpr header::DataDescription getDataDescriptionCCDBCMV() { return header::DataDescription{"TPC_CMV"}; } + + /// Return data description for aggregated CMVs for a given lane + static header::DataDescription getDataDescriptionCMV(const unsigned int lane) + { + const std::string name = fmt::format("CMVAGG{}", lane).data(); + header::DataDescription description; + description.runtimeInit(name.substr(0, 16).c_str()); + return description; + } + + /// Return data description for orbit/BC info for a given output lane + static header::DataDescription getDataDescriptionCMVOrbitInfo(const unsigned int lane) + { + const std::string name = fmt::format("CMVORB{}", lane); + header::DataDescription description; + description.runtimeInit(name.substr(0, 16).c_str()); + return description; + } + + static constexpr header::DataDescription getDataDescriptionCMVFirstTF() { return header::DataDescription{"CMVFIRSTTF"}; } + static constexpr header::DataDescription getDataDescriptionCMVOrbitReset() { return header::DataDescription{"CMVORBITRESET"}; } + + private: + std::vector mCRUs{}; ///< CRUs to process in this instance + const unsigned int mTimeFrames{}; ///< number of TFs per aggregation interval + const int mNTFsBuffer{1}; ///< number of TFs for which the CMVs will be buffered + std::array mProcessedTFs{{0, 0}}; ///< number of processed time frames to keep track of when the writing to CCDB will be done + std::array, 2> mProcessedCRU{}; ///< counter of received data from CRUs per TF to merge incoming data from FLPs. Buffer used in case one FLP delivers the TF after the last TF for the current aggregation interval faster then the other FLPs the last TF. + std::array>, 2> mProcessedCRUs{}; ///< to keep track of the already processed CRUs ([buffer][relTF][CRU]) + std::array mTFStart{}; ///< storing of first TF for buffer interval + std::array mTFEnd{}; ///< storing of last TF for buffer interval + std::shared_ptr mCCDBRequest; ///< info for CCDB request + std::vector mSendCCDBOutputOrbitReset{}; ///< flag for received orbit reset time from CCDB + std::vector mSendCCDBOutputGRPECS{}; ///< flag for received orbit GRPECS from CCDB + bool mBuffer{false}; ///< buffer index + bool mSendCCDB{false}; ///< send output to CCDB populator + bool mUsePreciseTimestamp{false}; ///< use precise timestamp from orbit-reset info + bool mDumpCMVs{false}; ///< write a local ROOT debug file + bool mUseCompressionVarint{false}; ///< use delta+zigzag+varint compression (all values, no sparse skip); combined with mUseSparse → SparseV2 mode 1 + bool mUseSparse{false}; ///< sparse encoding; alone = raw uint16 values; combined with varint/Huffman flag → SparseV2 + bool mUseCompressionHuffman{false}; ///< Huffman encoding; combined with mUseSparse → SparseV2 mode 2 + uint16_t mRoundIntegersThreshold{0}; ///< round values to nearest integer ADC for |v| <= N ADC; 0 = disabled + float mZeroThreshold{0.f}; ///< zero out CMV values whose float magnitude is below this threshold; 0 = disabled + float mDynamicPrecisionMean{1.f}; ///< Gaussian centre in |CMV| ADC where the strongest fractional-bit trimming is applied + float mDynamicPrecisionSigma{0.f}; ///< Gaussian width in ADC for the fractional-bit trimming; 0 disables + long mTimestampStart{0}; ///< CCDB validity start timestamp + dataformats::Pair mTFInfo{}; ///< orbit-reset time and NHBFPerTF for precise timestamp + std::unique_ptr mIntervalTree{}; ///< TTree accumulating one entry per completed TF in the current interval + CMVPerTF mCurrentTF{}; ///< staging object filled per CRU before compression + CMVPerTFCompressed mCurrentCompressedTF{}; ///< compressed output for the current TF (used when flags != kNone) + long mIntervalFirstTF{0}; ///< absolute TF counter of the first TF in the current aggregation interval + unsigned int mIntervalTFCount{0}; ///< number of TTree entries filled for the current aggregation interval + int mNFactorTFs{0}; ///< Number of TFs to skip for sending oldest TF + int mNTFsDataDrop{0}; ///< delay for the check if TFs are missing in TF units + std::array mStartNTFsDataDrop{0}; ///< first relative TF to check + long mProcessedTotalData{0}; ///< used to check for dropeed TF data + int mCheckEveryNData{1}; ///< factor after which to check for missing data (in case data missing -> send dummy data) + std::vector mFilter{}; ///< filter for looping over input data + std::vector mOrbitFilter{}; ///< filter for CMVORBITINFO from FLP + std::array, 2> mOrbitInfoForwarded{}; ///< tracks whether orbit/BC has been captured per (buffer, relTF) + uint32_t mLastSeenTF{0}; ///< last TF counter seen in run(), used to set lastTF in endOfStream flush + + /// Returns real number of TFs taking buffer size into account + unsigned int getNRealTFs() const { return mNTFsBuffer * mTimeFrames; } + + /// Build the CMVEncoding bitmask from the current option flags. + uint8_t buildCompressionFlags() const + { + uint8_t flags = CMVEncoding::kNone; + if (mUseSparse) { + flags |= CMVEncoding::kSparse; + } + if (mUseCompressionHuffman) { + flags |= CMVEncoding::kZigzag | CMVEncoding::kHuffman; + } else if (mUseCompressionVarint) { + flags |= CMVEncoding::kZigzag | CMVEncoding::kVarint; + } + // Delta coding is only applied for the dense (non-sparse) path with a value compressor + if (!(flags & CMVEncoding::kSparse) && (flags & (CMVEncoding::kVarint | CMVEncoding::kHuffman))) { + flags |= CMVEncoding::kDelta; + } + return flags; + } + + /// Create a fresh in-memory TTree for the next aggregation interval. + /// Uses a single CMVPerTFCompressed branch whenever any compression is active, + /// or a raw CMVPerTF branch when no compression flags are set. + void initIntervalTree() + { + mIntervalTree = std::make_unique("ccdb_object", "ccdb_object"); + mIntervalTree->SetAutoSave(0); + mIntervalTree->SetDirectory(nullptr); + if (buildCompressionFlags() != CMVEncoding::kNone) { + mIntervalTree->Branch("CMVPerTFCompressed", &mCurrentCompressedTF); + } else { + mIntervalTree->Branch("CMVPerTF", &mCurrentTF); + } + } + + void clearBuffer(const bool currentBuffer) + { + // resetting received CRUs + for (auto& crusMap : mProcessedCRUs[currentBuffer]) { + for (auto& it : crusMap) { + it.second = false; + } + } + + mProcessedTFs[currentBuffer] = 0; // reset processed TFs for next aggregation interval + std::fill(mProcessedCRU[currentBuffer].begin(), mProcessedCRU[currentBuffer].end(), 0); + std::fill(mOrbitInfoForwarded[currentBuffer].begin(), mOrbitInfoForwarded[currentBuffer].end(), false); + + // set integration range for next integration interval + mTFStart[mBuffer] = mTFEnd[!mBuffer] + 1; + mTFEnd[mBuffer] = mTFStart[mBuffer] + getNRealTFs() - 1; + + // switch buffer + mBuffer = !mBuffer; + } + + void checkIntervalsForMissingData(o2::framework::ProcessingContext& pc, const bool currentBuffer, const long relTF, const uint32_t tf) + { + if (!(mProcessedTotalData++ % mCheckEveryNData)) { + LOGP(info, "Checking for dropped packages..."); + + // if last buffer has smaller time range check the whole last buffer + if ((mTFStart[currentBuffer] > mTFStart[!currentBuffer]) && (relTF > mNTFsDataDrop)) { + LOGP(warning, "Checking last buffer from {} to {}", mStartNTFsDataDrop[!currentBuffer], mProcessedCRU[!currentBuffer].size()); + checkMissingData(pc, !currentBuffer, mStartNTFsDataDrop[!currentBuffer], mProcessedCRU[!currentBuffer].size()); + LOGP(info, "All empty TFs for TF {} for current buffer filled with dummy and sent. Clearing buffer", tf); + sendOutput(pc.outputs(), tf); + finishInterval(pc, !currentBuffer, tf); + } + + const int tfEndCheck = std::clamp(static_cast(relTF) - mNTFsDataDrop, 0, static_cast(mProcessedCRU[currentBuffer].size())); + LOGP(info, "Checking current buffer from {} to {}", mStartNTFsDataDrop[currentBuffer], tfEndCheck); + checkMissingData(pc, currentBuffer, mStartNTFsDataDrop[currentBuffer], tfEndCheck); + mStartNTFsDataDrop[currentBuffer] = tfEndCheck; + } + } + + void checkMissingData(o2::framework::ProcessingContext& pc, const bool currentBuffer, const int startTF, const int endTF) + { + for (int iTF = startTF; iTF < endTF; ++iTF) { + if (mProcessedCRU[currentBuffer][iTF] != mCRUs.size()) { + LOGP(warning, "CRUs for rel. TF: {} curr TF {} are missing! Processed {} CRUs out of {}", iTF, mTFStart[currentBuffer] + iTF, mProcessedCRU[currentBuffer][iTF], mCRUs.size()); + ++mProcessedTFs[currentBuffer]; + mProcessedCRU[currentBuffer][iTF] = mCRUs.size(); + + // find missing CRUs and leave their interval slots empty (zero-filled) + for (auto& it : mProcessedCRUs[currentBuffer][iTF]) { + if (!it.second) { + it.second = true; + } + } + + // leave orbit/BC as zero placeholder for missing TFs + mOrbitInfoForwarded[currentBuffer][iTF] = true; + } + } + } + + void finishInterval(o2::framework::ProcessingContext& pc, const bool buffer, const uint32_t tf) + { + if (mNFactorTFs > 0) { + mNFactorTFs = 0; + // ToDo: Find better fix + auto& deviceProxy = pc.services().get(); + if (deviceProxy.getNumOutputChannels() > 0) { + auto& state = deviceProxy.getOutputChannelState({0}); + size_t oldest = std::numeric_limits::max() - 1; // just set to really large value + state.oldestForChannel = {oldest}; + } + } + + LOGP(info, "All TFs {} for current buffer received. Clearing buffer", tf); + clearBuffer(buffer); + mStartNTFsDataDrop[buffer] = 0; + + // reset per-interval state for the next aggregation interval + initIntervalTree(); + mIntervalFirstTF = 0; + mIntervalTFCount = 0; + mCurrentTF = CMVPerTF{}; + mCurrentCompressedTF = CMVPerTFCompressed{}; + mTimestampStart = 0; + LOGP(info, "Everything cleared. Waiting for new data to arrive."); + } + + void setTimestampCCDB(const long relTF, o2::framework::ProcessingContext& pc) + { + if (mUsePreciseTimestamp && !mTFInfo.second) { + return; + } + const auto& tinfo = pc.services().get(); + const auto nOrbitsOffset = (relTF * mNTFsBuffer + (mNTFsBuffer - 1)) * mTFInfo.second; + mTimestampStart = mUsePreciseTimestamp + ? (mTFInfo.first + (tinfo.firstTForbit - nOrbitsOffset) * o2::constants::lhc::LHCOrbitMUS * 0.001) + : tinfo.creation; + LOGP(info, "Setting timestamp reset reference to: {}, at tfCounter: {}, firstTForbit: {}, NHBFPerTF: {}, relTF: {}, nOrbitsOffset: {}", + mTFInfo.first, tinfo.tfCounter, tinfo.firstTForbit, mTFInfo.second, relTF, nOrbitsOffset); + } + + void sendOutput(DataAllocator& output, const uint32_t tf) + { + using timer = std::chrono::high_resolution_clock; + + if (mIntervalTFCount == 0) { + LOGP(warning, "CMV interval is empty at sendOutput, skipping"); + return; + } + + // attach interval metadata to the TTree (stored once per tree) + mIntervalTree->GetUserInfo()->Clear(); + mIntervalTree->GetUserInfo()->Add(new TParameter("firstTF", mIntervalFirstTF)); + mIntervalTree->GetUserInfo()->Add(new TParameter("lastTF", mLastSeenTF)); + + LOGP(info, "CMVPerTF TTree: {} entries, firstTF={}, lastTF={}", mIntervalTFCount, mIntervalFirstTF, mLastSeenTF); + auto start = timer::now(); + + // write local ROOT file for debugging + if (mDumpCMVs) { + const std::string fname = fmt::format("CMV_timestamp{}.root", mTimestampStart); + try { + mCurrentTF.writeToFile(fname, mIntervalTree); + LOGP(info, "CMV debug file written to {}", fname); + } catch (const std::exception& e) { + LOGP(error, "Failed to write CMV debug file: {}", e.what()); + } + } + + if (!mSendCCDB) { + LOGP(warning, "CCDB output disabled, skipping upload!"); + return; + } + + const int nHBFPerTF = o2::base::GRPGeomHelper::instance().getNHBFPerTF(); + // use the actual number of TFs in this interval (mIntervalTFCount) rather than mTimeFrames, so the CCDB validity end is correct for partial last intervals + const long timeStampEnd = mTimestampStart + static_cast(mIntervalTFCount * mNTFsBuffer * nHBFPerTF * o2::constants::lhc::LHCOrbitMUS * 1e-3); + + if (timeStampEnd <= mTimestampStart) { + LOGP(warning, "Invalid CCDB timestamp range start:{} end:{}, skipping upload!", + mTimestampStart, timeStampEnd); + return; + } + + LOGP(info, "CCDB timestamp range start:{} end:{}", mTimestampStart, timeStampEnd); + + o2::ccdb::CcdbObjectInfo ccdbInfoCMV( + "TPC/Calib/CMV", + "TTree", + "CMV.root", + {}, + mTimestampStart, + timeStampEnd); + + auto image = o2::ccdb::CcdbApi::createObjectImage((mIntervalTree.get()), &ccdbInfoCMV); + // trim TMemFile zero-padding: GetSize() is block-rounded, GetEND() is the actual file end + { + TMemFile mf("trim", image->data(), static_cast(image->size()), "READ"); + image->resize(static_cast(mf.GetEND())); + mf.Close(); + } + LOGP(info, "Sending object {} / {} of size {} bytes, valid for {} : {}", + ccdbInfoCMV.getPath(), ccdbInfoCMV.getFileName(), image->size(), + ccdbInfoCMV.getStartValidityTimestamp(), ccdbInfoCMV.getEndValidityTimestamp()); + + output.snapshot(Output{o2::calibration::Utils::gDataOriginCDBPayload, getDataDescriptionCCDBCMV(), 0}, *image); + output.snapshot(Output{o2::calibration::Utils::gDataOriginCDBWrapper, getDataDescriptionCCDBCMV(), 0}, ccdbInfoCMV); + + auto stop = timer::now(); + std::chrono::duration elapsed = stop - start; + LOGP(info, "CMV CCDB serialisation time: {:.3f} s", elapsed.count()); + } +}; + +DataProcessorSpec getTPCDistributeCMVSpec(const int ilane, const std::vector& crus, const unsigned int timeframes, const int firstTF, const bool sendCCDB = false, const bool usePreciseTimestamp = false, const int nTFsBuffer = 1) +{ + std::vector inputSpecs; + inputSpecs.emplace_back(InputSpec{"cmvsgroup", ConcreteDataTypeMatcher{gDataOriginTPC, TPCFLPCMVDevice::getDataDescriptionCMVGroup()}, Lifetime::Sporadic}); + inputSpecs.emplace_back(InputSpec{"cmvorbit", ConcreteDataTypeMatcher{gDataOriginTPC, TPCFLPCMVDevice::getDataDescriptionCMVOrbitInfo()}, Lifetime::Sporadic}); + + std::vector outputSpecs; + if (sendCCDB) { + outputSpecs.emplace_back( + ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBPayload, + TPCDistributeCMVSpec::getDataDescriptionCCDBCMV()}, + Lifetime::Sporadic); + outputSpecs.emplace_back( + ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBWrapper, + TPCDistributeCMVSpec::getDataDescriptionCCDBCMV()}, + Lifetime::Sporadic); + } + + const bool fetchCCDB = usePreciseTimestamp; + auto ccdbRequest = std::make_shared(fetchCCDB, // orbitResetTime + fetchCCDB, // GRPECS=true + false, // GRPLHCIF + false, // GRPMagField + false, // askMatLUT + o2::base::GRPGeomRequest::None, // geometry + inputSpecs); + + const std::string type = "cmv"; + const auto id = fmt::format("tpc-distribute-{}-{:02}", type, ilane); + DataProcessorSpec spec{ + id.data(), + inputSpecs, + outputSpecs, + AlgorithmSpec{adaptFromTask(crus, timeframes, nTFsBuffer, firstTF, sendCCDB, usePreciseTimestamp, ccdbRequest)}, + Options{{"drop-data-after-nTFs", VariantType::Int, 0, {"Number of TFs after which to drop the data"}}, + {"check-data-every-n", VariantType::Int, 0, {"Number of run function called after which to check for missing data (-1 for no checking, 0 for default checking)"}}, + {"nFactorTFs", VariantType::Int, 1000, {"Number of TFs to skip for sending oldest TF"}}, + {"dump-cmvs", VariantType::Bool, false, {"Dump CMVs to a local ROOT file for debugging"}}, + {"use-sparse", VariantType::Bool, false, {"Sparse encoding (skip zero time bins). Alone: raw uint16 values. With --use-compression-varint: varint exact values. With --use-compression-huffman: Huffman exact values"}}, + {"use-compression-varint", VariantType::Bool, false, {"Delta+zigzag+varint compression (all values). Combined with --use-sparse: sparse positions + varint encoded exact CMV values"}}, + {"use-compression-huffman", VariantType::Bool, false, {"Huffman encoding. Combined with --use-sparse: sparse positions + Huffman-encoded exact CMV values"}}, + {"cmv-zero-threshold", VariantType::Float, 0.f, {"Zero out CMV values whose float magnitude is below this threshold after optional integer rounding and before compression; 0 disables"}}, + {"cmv-round-integers-threshold", VariantType::Int, 0, {"Round values to nearest integer ADC for |v| <= N ADC before compression; 0 disables"}}, + {"cmv-dynamic-precision-mean", VariantType::Float, 1.f, {"Gaussian centre in |CMV| ADC where the strongest fractional bit trimming is applied"}}, + {"cmv-dynamic-precision-sigma", VariantType::Float, 0.f, {"Gaussian width in ADC for smooth CMV fractional bit trimming; 0 disables"}}}}; // end DataProcessorSpec + + spec.rank = ilane; + return spec; +} + +} // namespace o2::tpc + +#endif diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TPCFLPCMVSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCFLPCMVSpec.h new file mode 100644 index 0000000000000..9931c27c9d3fa --- /dev/null +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TPCFLPCMVSpec.h @@ -0,0 +1,172 @@ +// 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 TPCFLPCMVSpec.h +/// @author Tuba Gündem, tuba.gundem@cern.ch +/// @brief TPC device for processing CMVs on FLPs + +#ifndef O2_TPCFLPIDCSPEC_H +#define O2_TPCFLPIDCSPEC_H + +#include +#include +#include +#include "Framework/Task.h" +#include "Framework/ControlService.h" +#include "Framework/Logger.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/InputRecordWalker.h" +#include "Framework/ConfigParamRegistry.h" +#include "Headers/DataHeader.h" +#include "TPCWorkflow/ProcessingHelpers.h" +#include "TPCBase/CRU.h" +#include "TFile.h" + +using namespace o2::framework; +using o2::header::gDataOriginTPC; +using namespace o2::tpc; + +namespace o2::tpc +{ + +class TPCFLPCMVDevice : public o2::framework::Task +{ + public: + TPCFLPCMVDevice(const int lane, const std::vector& crus, const int nTFsBuffer) + : mLane{lane}, mCRUs{crus}, mNTFsBuffer{nTFsBuffer} {} + + void init(o2::framework::InitContext& ic) final + { + mDumpCMVs = ic.options().get("dump-cmvs-flp"); + } + + void run(o2::framework::ProcessingContext& pc) final + { + LOGP(debug, "Processing CMVs for TF {} for CRUs {} to {}", processing_helpers::getCurrentTF(pc), mCRUs.front(), mCRUs.back()); + + ++mCountTFsForBuffer; + + // Capture heartbeatOrbit / heartbeatBC from the first TF in the buffer + if (mCountTFsForBuffer == 1) { + for (auto& ref : InputRecordWalker(pc.inputs(), mOrbitFilter)) { + auto const* hdr = o2::framework::DataRefUtils::getHeader(ref); + const uint32_t cru = hdr->subSpecification >> 7; + if (mFirstOrbitBC.find(cru) == mFirstOrbitBC.end()) { + auto orbitVec = pc.inputs().get>(ref); + if (!orbitVec.empty()) { + mFirstOrbitBC[cru] = orbitVec[0]; // packed: orbit<<32 | bc + } + } + } + } + + for (auto& ref : InputRecordWalker(pc.inputs(), mFilter)) { + auto const* tpcCRUHeader = o2::framework::DataRefUtils::getHeader(ref); + const int cru = tpcCRUHeader->subSpecification >> 7; + auto vecCMVs = pc.inputs().get>(ref); + mCMVs[cru].insert(mCMVs[cru].end(), vecCMVs.begin(), vecCMVs.end()); + } + + if (mCountTFsForBuffer >= mNTFsBuffer) { + mCountTFsForBuffer = 0; + for (const auto cru : mCRUs) { + LOGP(debug, "Sending CMVs of size {} for TF {}", mCMVs[cru].size(), processing_helpers::getCurrentTF(pc)); + sendOutput(pc.outputs(), cru); + } + mFirstOrbitBC.clear(); + } + + if (mDumpCMVs) { + TFile fOut(fmt::format("CMVs_{}_tf_{}.root", mLane, processing_helpers::getCurrentTF(pc)).data(), "RECREATE"); + for (auto& ref : InputRecordWalker(pc.inputs(), mFilter)) { + auto const* tpcCRUHeader = o2::framework::DataRefUtils::getHeader(ref); + const int cru = tpcCRUHeader->subSpecification >> 7; + auto vec = pc.inputs().get>(ref); + fOut.WriteObject(&vec, fmt::format("CRU_{}", cru).data()); + } + } + } + + void endOfStream(o2::framework::EndOfStreamContext& ec) final + { + if (mCountTFsForBuffer > 0) { + LOGP(info, "Flushing remaining {} buffered TFs at end of stream", mCountTFsForBuffer); + for (const auto cru : mCRUs) { + sendOutput(ec.outputs(), cru); + } + } + ec.services().get().readyToQuit(QuitRequest::Me); + } + + static constexpr header::DataDescription getDataDescriptionCMVGroup() { return header::DataDescription{"CMVGROUP"}; } + + /// Data description for the packed (orbit<<32|bc) scalar forwarded alongside each CRU's CMVGROUP. + static constexpr header::DataDescription getDataDescriptionCMVOrbitInfo() { return header::DataDescription{"CMVORBITINFO"}; } + + private: + const int mLane{}; ///< lane number of processor + const std::vector mCRUs{}; ///< CRUs to process in this instance + int mNTFsBuffer{1}; ///< number of TFs to buffer before sending + bool mDumpCMVs{}; ///< dump CMVs to file for debugging + int mCountTFsForBuffer{0}; ///< counts TFs to track when to send output + std::unordered_map> mCMVs{}; ///< buffered raw 16-bit CMV values per CRU + std::unordered_map mFirstOrbitBC{}; ///< first packed orbit/BC per CRU for the current buffer window + + /// Filter for CMV float vectors (one CMVVECTOR message per CRU per TF) + const std::vector mFilter = {{"cmvs", ConcreteDataTypeMatcher{gDataOriginTPC, "CMVVECTOR"}, Lifetime::Timeframe}}; + /// Filter for CMV packet timing info (one CMVORBITS message per CRU per TF, sent by CMVToVectorSpec) + const std::vector mOrbitFilter = {{"cmvorbits", ConcreteDataTypeMatcher{gDataOriginTPC, "CMVORBITS"}, Lifetime::Timeframe}}; + + void sendOutput(DataAllocator& output, const uint32_t cru) + { + const header::DataHeader::SubSpecificationType subSpec{cru << 7}; + + // Forward the first-TF orbit/BC for this CRU (0 if unavailable for any reason) + uint64_t orbitBC = 0; + if (auto it = mFirstOrbitBC.find(cru); it != mFirstOrbitBC.end()) { + orbitBC = it->second; + } + output.snapshot(Output{gDataOriginTPC, getDataDescriptionCMVOrbitInfo(), subSpec}, orbitBC); + + output.adoptContainer(Output{gDataOriginTPC, getDataDescriptionCMVGroup(), subSpec}, std::move(mCMVs[cru])); + } +}; + +DataProcessorSpec getTPCFLPCMVSpec(const int ilane, const std::vector& crus, const int nTFsBuffer = 1) +{ + std::vector outputSpecs; + std::vector inputSpecs; + outputSpecs.reserve(crus.size()); + inputSpecs.reserve(crus.size()); + + for (const auto& cru : crus) { + const header::DataHeader::SubSpecificationType subSpec{cru << 7}; + + // Inputs from CMVToVectorSpec + inputSpecs.emplace_back(InputSpec{"cmvs", gDataOriginTPC, "CMVVECTOR", subSpec, Lifetime::Timeframe}); + inputSpecs.emplace_back(InputSpec{"cmvorbits", gDataOriginTPC, "CMVORBITS", subSpec, Lifetime::Timeframe}); + + // Outputs to TPCDistributeCMVSpec + outputSpecs.emplace_back(ConcreteDataMatcher{gDataOriginTPC, TPCFLPCMVDevice::getDataDescriptionCMVGroup(), subSpec}, Lifetime::Sporadic); + outputSpecs.emplace_back(ConcreteDataMatcher{gDataOriginTPC, TPCFLPCMVDevice::getDataDescriptionCMVOrbitInfo(), subSpec}, Lifetime::Sporadic); + } + + const auto id = fmt::format("tpc-flp-cmv-{:02}", ilane); + return DataProcessorSpec{ + id.data(), + inputSpecs, + outputSpecs, + AlgorithmSpec{adaptFromTask(ilane, crus, nTFsBuffer)}, + Options{{"dump-cmvs-flp", VariantType::Bool, false, {"Dump CMVs to file"}}}}; +} + +} // namespace o2::tpc +#endif \ No newline at end of file diff --git a/Detectors/TPC/workflow/src/CMVToVectorSpec.cxx b/Detectors/TPC/workflow/src/CMVToVectorSpec.cxx new file mode 100644 index 0000000000000..81ce358d1a809 --- /dev/null +++ b/Detectors/TPC/workflow/src/CMVToVectorSpec.cxx @@ -0,0 +1,434 @@ +// 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 CMVToVectorSpec.cxx +/// @author Tuba Gündem, tuba.gundem@cern.ch +/// @brief Processor to convert CMVs to a vector in a CRU + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "TFile.h" +#include "DetectorsRaw/RDHUtils.h" +#include "Framework/Task.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/Logger.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/WorkflowSpec.h" +#include "Framework/InputRecordWalker.h" +#include "Framework/DataRefUtils.h" +#include "DPLUtils/RawParser.h" +#include "Headers/DataHeader.h" +#include "Headers/DataHeaderHelpers.h" +#include "CommonUtils/TreeStreamRedirector.h" + +#include "DataFormatsTPC/CMV.h" +#include "DataFormatsTPC/RawDataTypes.h" +#include "TPCBase/RDHUtils.h" +#include "TPCBase/Mapper.h" +#include "TPCWorkflow/ProcessingHelpers.h" + +using namespace o2::framework; +using o2::header::gDataOriginTPC; +using RDHUtils = o2::raw::RDHUtils; +using RawDataType = o2::tpc::raw_data_types::Type; + +namespace o2::tpc +{ + +class CMVToVectorDevice : public o2::framework::Task +{ + public: + using FEEIDType = rdh_utils::FEEIDType; + CMVToVectorDevice(const std::vector& crus) : mCRUs(crus) {} + + void init(o2::framework::InitContext& ic) final + { + // set up ADC value filling + mWriteDebug = ic.options().get("write-debug"); + mWriteDebugOnError = ic.options().get("write-debug-on-error"); + mWriteRawDataOnError = ic.options().get("write-raw-data-on-error"); + mRawDataType = ic.options().get("raw-data-type"); + o2::framework::RawParser<>::setCheckIncompleteHBF(ic.options().get("check-incomplete-hbf")); + + mDebugStreamFileName = ic.options().get("debug-file-name").data(); + mRawOutputFileName = ic.options().get("raw-file-name").data(); + + initCMV(); + } + + void run(o2::framework::ProcessingContext& pc) final + { + const auto runNumber = processing_helpers::getRunNumber(pc); + std::vector filter = {{"check", ConcreteDataTypeMatcher{o2::header::gDataOriginTPC, "RAWDATA"}, Lifetime::Timeframe}}; + const auto& mapper = Mapper::instance(); + + // open files if necessary + if ((mWriteDebug || mWriteDebugOnError) && !mDebugStream) { + const auto debugFileName = fmt::format(fmt::runtime(mDebugStreamFileName), fmt::arg("run", runNumber)); + LOGP(info, "Creating debug stream {}", debugFileName); + mDebugStream = std::make_unique(debugFileName.data(), "recreate"); + } + + if (mWriteRawDataOnError && !mRawOutputFile.is_open()) { + std::string_view rawType = (mRawDataType < 2) ? "tf" : "raw"; + if (mRawDataType == 5) { + rawType = "cmv.raw"; + } + const auto rawFileName = fmt::format(fmt::runtime(mRawOutputFileName), fmt::arg("run", runNumber), fmt::arg("raw_type", rawType)); + LOGP(info, "Creating raw debug file {}", rawFileName); + mRawOutputFile.open(rawFileName, std::ios::binary); + } + + uint32_t heartbeatOrbit = 0; + uint16_t heartbeatBC = 0; + uint32_t tfCounter = 0; + bool first = true; + bool hasErrors = false; + + for (auto const& ref : InputRecordWalker(pc.inputs(), filter)) { + const auto* dh = DataRefUtils::getHeader(ref); + tfCounter = dh->tfCounter; + const auto subSpecification = dh->subSpecification; + auto payloadSize = DataRefUtils::getPayloadSize(ref); + LOGP(debug, "Processing TF {}, subSpecification {}, payloadSize {}", tfCounter, subSpecification, payloadSize); + + // ---| data loop |--- + const gsl::span raw = pc.inputs().get>(ref); + try { + o2::framework::RawParser parser(raw.data(), raw.size()); + size_t lastErrorCount = 0; + + for (auto it = parser.begin(), end = parser.end(); it != end; ++it) { + const auto size = it.size(); + + if (parser.getNErrors() > lastErrorCount) { + lastErrorCount = parser.getNErrors(); + hasErrors = true; + } + + // skip empty packages (HBF open) + if (size == 0) { + continue; + } + + auto rdhPtr = reinterpret_cast(it.raw()); + const auto rdhVersion = RDHUtils::getVersion(rdhPtr); + if (!rdhPtr || rdhVersion < 6) { + throw std::runtime_error(fmt::format("could not get RDH from packet, or version {} < 6", rdhVersion).data()); + } + + // ---| extract hardware information to do the processing |--- + const auto feeId = (FEEIDType)RDHUtils::getFEEID(*rdhPtr); + const auto link = rdh_utils::getLink(feeId); + const uint32_t cruID = rdh_utils::getCRU(feeId); + const auto detField = RDHUtils::getDetectorField(*rdhPtr); + + LOGP(debug, "Detected CMV packet: CRU {}, link {}, feeId {}", cruID, link, feeId); + + if ((detField != (decltype(detField))RawDataType::CMV) || (link != rdh_utils::CMVLinkID)) { + LOGP(debug, "Skipping packet: detField {}, (expected RawDataType {}), link {}, (expected CMVLinkID {})", detField, (decltype(detField))RawDataType::CMV, link, rdh_utils::CMVLinkID); + continue; + } + + LOGP(debug, "Processing firstTForbit {:9}, tfCounter {:5}, run {:6}, feeId {:6}, cruID {:3}, link {:2}", dh->firstTForbit, dh->tfCounter, dh->runNumber, feeId, cruID, link); + + if (std::find(mCRUs.begin(), mCRUs.end(), cruID) == mCRUs.end()) { + LOGP(warning, "CMV CRU {:3} not configured in CRUs, skipping", cruID); + continue; + } + + auto& cmvVec = mCMVvectors[cruID]; + auto& infoVec = mCMVInfos[cruID]; + + if (size != sizeof(cmv::Container)) { + LOGP(warning, "CMV packet size mismatch: got {} bytes, expected {} bytes (sizeof cmv::Container). Skipping package.", size, sizeof(cmv::Container)); + hasErrors = true; + continue; + } + auto data = it.data(); + auto& cmvs = *((cmv::Container*)(data)); + const uint32_t orbit = cmvs.header.heartbeatOrbit; + const uint16_t bc = cmvs.header.heartbeatBC; + + // record packet meta and append its CMV vector (3564 TB) + infoVec.emplace_back(orbit, bc); + cmvVec.reserve(cmvVec.size() + cmv::NTimeBinsPerPacket); + for (uint32_t tb = 0; tb < cmv::NTimeBinsPerPacket; ++tb) { + cmvVec.push_back(cmvs.getCMV(tb)); + // LOGP(debug, "Appended CMV {} for timebin {}, CRU {}, orbit {}, bc {}", cmvs.getCMV(tb), tb, cruID, orbit, bc); + } + } + } catch (const std::exception& e) { + // error message throtteling + using namespace std::literals::chrono_literals; + static std::unordered_map nErrorPerSubspec; + static std::chrono::time_point lastReport = std::chrono::steady_clock::now(); + const auto now = std::chrono::steady_clock::now(); + static size_t reportedErrors = 0; + const size_t MAXERRORS = 10; + const auto sleepTime = 10min; + ++nErrorPerSubspec[subSpecification]; + + if ((now - lastReport) < sleepTime) { + if (reportedErrors < MAXERRORS) { + ++reportedErrors; + std::string sleepInfo; + if (reportedErrors == MAXERRORS) { + sleepInfo = fmt::format(", maximum error count ({}) reached, not reporting for the next {}", MAXERRORS, sleepTime); + } + LOGP(alarm, "EXCEPTION in processRawData: {} -> skipping part:{}/{} of spec:{}/{}/{}, size:{}, error count for subspec: {}{}", e.what(), dh->splitPayloadIndex, dh->splitPayloadParts, + dh->dataOrigin, dh->dataDescription, subSpecification, payloadSize, nErrorPerSubspec.at(subSpecification), sleepInfo); + lastReport = now; + } + } else { + lastReport = now; + reportedErrors = 0; + } + continue; + } + } + + hasErrors |= snapshotCMVs(pc.outputs(), tfCounter); + + if (mWriteDebug || (mWriteDebugOnError && hasErrors)) { + writeDebugOutput(tfCounter); + } + + if (mWriteRawDataOnError && hasErrors) { + writeRawData(pc.inputs()); + } + + // clear output + initCMV(); + } + + void closeFiles() + { + LOGP(info, "closeFiles"); + + if (mDebugStream) { + // set some default aliases + auto& stream = (*mDebugStream) << "cmvs"; + auto& tree = stream.getTree(); + tree.SetAlias("sector", "int(cru/10)"); + mDebugStream->Close(); + mDebugStream.reset(nullptr); + mRawOutputFile.close(); + } + } + + void stop() final + { + LOGP(info, "stop"); + closeFiles(); + } + + void endOfStream(o2::framework::EndOfStreamContext& ec) final + { + LOGP(info, "endOfStream"); + // ec.services().get().readyToQuit(QuitRequest::Me); + closeFiles(); + } + + private: + /// CMV information for each cru + struct CMVInfo { + CMVInfo() = default; + CMVInfo(const CMVInfo&) = default; + CMVInfo(uint32_t orbit, uint16_t bc) : heartbeatOrbit(orbit), heartbeatBC(bc) {} + + uint32_t heartbeatOrbit{0}; + uint16_t heartbeatBC{0}; + + bool operator==(const uint32_t orbit) const { return (heartbeatOrbit == orbit); } + bool operator==(const CMVInfo& inf) const { return (inf.heartbeatOrbit == heartbeatOrbit) && (inf.heartbeatBC == heartbeatBC); } + bool matches(uint32_t orbit, int16_t bc) const { return ((heartbeatOrbit == orbit) && (heartbeatBC == bc)); } + }; + + int mRawDataType{0}; ///< type of raw data to dump in case of errors + bool mWriteDebug{false}; ///< write a debug output + bool mWriteDebugOnError{false}; ///< write a debug output in case of errors + bool mWriteRawDataOnError{false}; ///< write raw data in case of errors + std::vector mCRUs; ///< CRUs expected for this device + std::unordered_map> mCMVvectors; ///< raw 16-bit CMV values per cru over all CMV packets in the TF + std::unordered_map> mCMVInfos; ///< CMV packet information within the TF + std::string mDebugStreamFileName; ///< name of the debug stream output file + std::unique_ptr mDebugStream; ///< debug output streamer + std::ofstream mRawOutputFile; ///< raw output file + std::string mRawOutputFileName; ///< name of the raw output file + + //____________________________________________________________________________ + bool snapshotCMVs(DataAllocator& output, uint32_t tfCounter) + { + bool hasErrors = false; + + // send data per CRU with its own orbit/BC vector + for (auto& [cru, cmvVec] : mCMVvectors) { + const header::DataHeader::SubSpecificationType subSpec{cru << 7}; + const auto& infVec = mCMVInfos[cru]; + + if (infVec.size() != 4) { + // LOGP(error, "CRU {:3}: expected 4 packets per TF, got {}", cru, infVec.size()); + hasErrors = true; + } + if (cmvVec.size() != cmv::NTimeBinsPerPacket * infVec.size()) { + // LOGP(error, "CRU {:3}: vector size {} does not match expected {}", cru, cmvVec.size(), cmv::NTimeBinsPerPacket * infVec.size()); + hasErrors = true; + } + + std::vector orbitBCInfo; + orbitBCInfo.reserve(infVec.size()); + for (const auto& inf : infVec) { + orbitBCInfo.emplace_back((uint64_t(inf.heartbeatOrbit) << 32) + uint64_t(inf.heartbeatBC)); + } + + LOGP(debug, "Sending CMVs for CRU {} of size {} ({} packets)", cru, cmvVec.size(), infVec.size()); + output.snapshot(Output{gDataOriginTPC, "CMVVECTOR", subSpec}, cmvVec); + output.snapshot(Output{gDataOriginTPC, "CMVORBITS", subSpec}, orbitBCInfo); + } + + return hasErrors; + } + + //____________________________________________________________________________ + void initCMV() + { + for (const auto cruID : mCRUs) { + auto& cmvVec = mCMVvectors[cruID]; + cmvVec.clear(); + + auto& infosCRU = mCMVInfos[cruID]; + infosCRU.clear(); + } + } + + //____________________________________________________________________________ + void writeDebugOutput(uint32_t tfCounter) + { + const auto& mapper = Mapper::instance(); + + mDebugStream->GetFile()->cd(); + auto& stream = (*mDebugStream) << "cmvs"; + uint32_t seen = 0; + static uint32_t firstOrbit = std::numeric_limits::max(); + + for (auto cru : mCRUs) { + if (mCMVInfos.find(cru) == mCMVInfos.end()) { + continue; + } + + auto& infos = mCMVInfos[cru]; + auto& cmvVec = mCMVvectors[cru]; + + stream << "cru=" << cru + << "tfCounter=" << tfCounter + << "nCMVs=" << cmvVec.size() + << "cmvs=" << cmvVec + << "\n"; + } + } + + void writeRawData(InputRecord& inputs) + { + if (!mRawOutputFile.is_open()) { + return; + } + + using DataHeader = o2::header::DataHeader; + + std::vector filter = {{"check", ConcreteDataTypeMatcher{o2::header::gDataOriginTPC, "RAWDATA"}, Lifetime::Timeframe}}; + for (auto const& ref : InputRecordWalker(inputs, filter)) { + auto dh = DataRefUtils::getHeader(ref); + // LOGP(info, "write header: {}/{}/{}, payload size: {} / {}", dh->dataOrigin, dh->dataDescription, dh->subSpecification, dh->payloadSize, ref.payloadSize); + if (((mRawDataType == 1) || (mRawDataType == 3)) && (dh->payloadSize == 2 * sizeof(o2::header::RAWDataHeader))) { + continue; + } + + if (mRawDataType < 2) { + mRawOutputFile.write(ref.header, sizeof(DataHeader)); + } + if (mRawDataType < 5) { + mRawOutputFile.write(ref.payload, ref.payloadSize); + } + + if (mRawDataType == 5) { + const gsl::span raw = inputs.get>(ref); + try { + o2::framework::RawParser parser(raw.data(), raw.size()); + for (auto it = parser.begin(), end = parser.end(); it != end; ++it) { + const auto size = it.size(); + // skip empty packages (HBF open) + if (size == 0) { + continue; + } + + auto rdhPtr = reinterpret_cast(it.raw()); + const auto rdhVersion = RDHUtils::getVersion(rdhPtr); + if (!rdhPtr || rdhVersion < 6) { + throw std::runtime_error(fmt::format("could not get RDH from packet, or version {} < 6", rdhVersion).data()); + } + + // ---| extract hardware information to do the processing |--- + const auto feeId = (FEEIDType)RDHUtils::getFEEID(*rdhPtr); + const auto link = rdh_utils::getLink(feeId); + const auto detField = RDHUtils::getDetectorField(*rdhPtr); + + // only select CMVs + if ((detField != (decltype(detField))RawDataType::CMV) || (link != rdh_utils::CMVLinkID)) { + continue; + } + + // write out raw data + mRawOutputFile.write((const char*)it.raw(), RDHUtils::getMemorySize(rdhPtr)); + } + } catch (...) { + } + } + } + } +}; + +o2::framework::DataProcessorSpec getCMVToVectorSpec(const std::string inputSpec, std::vector const& crus) +{ + using device = o2::tpc::CMVToVectorDevice; + + std::vector outputs; + for (const uint32_t cru : crus) { + const header::DataHeader::SubSpecificationType subSpec{cru << 7}; + outputs.emplace_back(gDataOriginTPC, "CMVVECTOR", subSpec, Lifetime::Timeframe); + outputs.emplace_back(gDataOriginTPC, "CMVORBITS", subSpec, Lifetime::Timeframe); + } + + return DataProcessorSpec{ + fmt::format("tpc-cmv-to-vector"), + select(inputSpec.data()), + outputs, + AlgorithmSpec{adaptFromTask(crus)}, + Options{ + {"write-debug", VariantType::Bool, false, {"write a debug output tree"}}, + {"write-debug-on-error", VariantType::Bool, false, {"write a debug output tree in case errors occurred"}}, + {"debug-file-name", VariantType::String, "/tmp/cmv_vector_debug.{run}.root", {"name of the debug output file"}}, + {"write-raw-data-on-error", VariantType::Bool, false, {"dump raw data in case errors occurred"}}, + {"raw-file-name", VariantType::String, "/tmp/cmv_debug.{run}.{raw_type}", {"name of the raw output file"}}, + {"raw-data-type", VariantType::Int, 0, {"Which raw data to dump: 0-full TPC with DH, 1-full TPC with DH skip empty, 2-full TPC no DH, 3-full TPC no DH skip empty, 4-IDC raw only 5-CMV raw only"}}, + {"check-incomplete-hbf", VariantType::Bool, false, {"false: don't check; true: check and report"}}, + } // end Options + }; // end DataProcessorSpec +} +} // namespace o2::tpc \ No newline at end of file diff --git a/Detectors/TPC/workflow/src/tpc-cmv-to-vector.cxx b/Detectors/TPC/workflow/src/tpc-cmv-to-vector.cxx new file mode 100644 index 0000000000000..1040b64f98d04 --- /dev/null +++ b/Detectors/TPC/workflow/src/tpc-cmv-to-vector.cxx @@ -0,0 +1,71 @@ +// 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 "Algorithm/RangeTokenizer.h" +#include "Framework/WorkflowSpec.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/CompletionPolicyHelpers.h" +#include "CommonUtils/ConfigurableParam.h" +#include "TPCBase/CRU.h" +#include "TPCWorkflow/CMVToVectorSpec.h" + +using namespace o2::framework; +using namespace o2::tpc; + +// customize the completion policy +void customize(std::vector& policies) +{ + using o2::framework::CompletionPolicy; + policies.push_back(CompletionPolicyHelpers::defineByName("tpc-cmv-to-vector", CompletionPolicy::CompletionOp::Consume)); +} + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + std::string crusDefault = "0-" + std::to_string(CRU::MaxCRU - 1); + + std::vector options{ + {"input-spec", VariantType::String, "A:TPC/RAWDATA", {"selection string input specs"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings (e.g.: 'TPCCalibPedestal.FirstTimeBin=10;...')"}}, + {"configFile", VariantType::String, "", {"configuration file for configurable parameters"}}, + {"crus", VariantType::String, crusDefault.c_str(), {"List of TPC crus, comma separated ranges, e.g. 0-3,7,9-15"}}, + }; + + std::swap(workflowOptions, options); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& config) +{ + + using namespace o2::tpc; + + // set up configuration + o2::conf::ConfigurableParam::updateFromFile(config.options().get("configFile")); + o2::conf::ConfigurableParam::updateFromString(config.options().get("configKeyValues")); + o2::conf::ConfigurableParam::writeINI("o2tpccmv_configuration.ini"); + + const std::string inputSpec = config.options().get("input-spec"); + + const auto crus = o2::RangeTokenizer::tokenize(config.options().get("crus")); + + WorkflowSpec workflow; + + workflow.emplace_back(getCMVToVectorSpec(inputSpec, crus)); + + return workflow; +} diff --git a/Detectors/TPC/workflow/src/tpc-distribute-cmv.cxx b/Detectors/TPC/workflow/src/tpc-distribute-cmv.cxx new file mode 100644 index 0000000000000..b6aaaa0a109ad --- /dev/null +++ b/Detectors/TPC/workflow/src/tpc-distribute-cmv.cxx @@ -0,0 +1,84 @@ +// 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 "Algorithm/RangeTokenizer.h" +#include "Framework/WorkflowSpec.h" +#include "Framework/ConfigParamSpec.h" +#include "CommonUtils/ConfigurableParam.h" +#include "TPCWorkflow/TPCDistributeCMVSpec.h" +#include "Framework/CompletionPolicyHelpers.h" + +using namespace o2::framework; + +// customize the completion policy +void customize(std::vector& policies) +{ + using o2::framework::CompletionPolicy; + policies.push_back(CompletionPolicyHelpers::defineByName("tpc-distribute-*.*", CompletionPolicy::CompletionOp::Consume)); +} + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + const std::string cruDefault = "0-" + std::to_string(o2::tpc::CRU::MaxCRU - 1); + + std::vector options{ + {"crus", VariantType::String, cruDefault.c_str(), {"List of CRUs, comma separated ranges, e.g. 0-3,7,9-15"}}, + {"timeframes", VariantType::Int, 2000, {"Number of TFs which will be aggregated per aggregation interval."}}, + {"firstTF", VariantType::Int, -1, {"First time frame index. (if set to -1 the first TF will be automatically detected. Values < -1 are setting an offset for skipping the first TFs)"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + {"lanes", VariantType::Int, 1, {"Number of lanes of this device (CRUs are split per lane)"}}, + {"use-precise-timestamp", VariantType::Bool, false, {"Use precise timestamp which can be used for writing to CCDB"}}, + {"enable-CCDB-output", VariantType::Bool, false, {"Send output to the CCDB populator"}}, + {"n-TFs-buffer", VariantType::Int, 1, {"Buffer which was defined in the TPCFLPCMVSpec."}}}; + std::swap(workflowOptions, options); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& config) +{ + using namespace o2::tpc; + + // set up configuration + o2::conf::ConfigurableParam::updateFromString(config.options().get("configKeyValues")); + o2::conf::ConfigurableParam::writeINI("o2tpcdistributecmv_configuration.ini"); + + const auto tpcCRUs = o2::RangeTokenizer::tokenize(config.options().get("crus")); + const auto nCRUs = tpcCRUs.size(); + auto timeframes = static_cast(config.options().get("timeframes")); + const auto nLanes = static_cast(config.options().get("lanes")); + const auto firstTF = static_cast(config.options().get("firstTF")); + const bool usePreciseTimestamp = config.options().get("use-precise-timestamp"); + const bool sendCCDB = config.options().get("enable-CCDB-output"); + int nTFsBuffer = config.options().get("n-TFs-buffer"); + if (nTFsBuffer <= 0) { + nTFsBuffer = 1; + } + assert(timeframes >= nTFsBuffer); + timeframes /= nTFsBuffer; + LOGP(info, "Using {} timeframes as each TF contains {} CMVs", timeframes, nTFsBuffer); + const auto crusPerLane = nCRUs / nLanes + ((nCRUs % nLanes) != 0); + WorkflowSpec workflow; + for (int ilane = 0; ilane < nLanes; ++ilane) { + const auto first = tpcCRUs.begin() + ilane * crusPerLane; + if (first >= tpcCRUs.end()) { + break; + } + const auto last = std::min(tpcCRUs.end(), first + crusPerLane); + const std::vector rangeCRUs(first, last); + workflow.emplace_back(getTPCDistributeCMVSpec(ilane, rangeCRUs, timeframes, firstTF, sendCCDB, usePreciseTimestamp, nTFsBuffer)); + } + + return workflow; +} \ No newline at end of file diff --git a/Detectors/TPC/workflow/src/tpc-flp-cmv.cxx b/Detectors/TPC/workflow/src/tpc-flp-cmv.cxx new file mode 100644 index 0000000000000..f41fe5b8fbd15 --- /dev/null +++ b/Detectors/TPC/workflow/src/tpc-flp-cmv.cxx @@ -0,0 +1,72 @@ +// 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 "CommonUtils/ConfigurableParam.h" +#include "Algorithm/RangeTokenizer.h" +#include "Framework/WorkflowSpec.h" +#include "Framework/ConfigParamSpec.h" +#include "TPCWorkflow/TPCFLPCMVSpec.h" +#include "TPCBase/CRU.h" + +using namespace o2::framework; + +void customize(std::vector& workflowOptions) +{ + const std::string cruDefault = "0-" + std::to_string(o2::tpc::CRU::MaxCRU - 1); + const int defaultlanes = std::max(1u, std::thread::hardware_concurrency() / 2); + + std::vector options{ + {"configFile", VariantType::String, "", {"configuration file for configurable parameters"}}, + {"lanes", VariantType::Int, defaultlanes, {"Number of parallel processing lanes (crus are split per device)"}}, + {"time-lanes", VariantType::Int, 1, {"Number of parallel processing lanes (timeframes are split per device)"}}, + {"crus", VariantType::String, cruDefault.c_str(), {"List of CRUs, comma separated ranges, e.g. 0-3,7,9-15"}}, + {"n-TFs-buffer", VariantType::Int, 1, {"Buffer n-TFs before sending output"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + + std::swap(workflowOptions, options); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& config) +{ + using namespace o2::tpc; + o2::conf::ConfigurableParam::updateFromString(config.options().get("configKeyValues")); + const auto tpcCRUs = o2::RangeTokenizer::tokenize(config.options().get("crus")); + const auto nCRUs = tpcCRUs.size(); + const auto nLanes = std::min(static_cast(config.options().get("lanes")), nCRUs); + const auto time_lanes = static_cast(config.options().get("time-lanes")); + const auto crusPerLane = nCRUs / nLanes + ((nCRUs % nLanes) != 0); + const int nTFsBuffer = config.options().get("n-TFs-buffer"); + + o2::conf::ConfigurableParam::updateFromFile(config.options().get("configFile")); + o2::conf::ConfigurableParam::writeINI("o2tpcflp_configuration.ini"); + + WorkflowSpec workflow; + if (nLanes <= 0) { + return workflow; + } + + for (int ilane = 0; ilane < nLanes; ++ilane) { + const auto first = tpcCRUs.begin() + ilane * crusPerLane; + if (first >= tpcCRUs.end()) { + break; + } + const auto last = std::min(tpcCRUs.end(), first + crusPerLane); + const std::vector rangeCRUs(first, last); + workflow.emplace_back(timePipeline(getTPCFLPCMVSpec(ilane, rangeCRUs, nTFsBuffer), time_lanes)); + } + + return workflow; +} \ No newline at end of file From ca2fdfa6534bb466aa0c62ec4af4274487af4bcc Mon Sep 17 00:00:00 2001 From: shahoian Date: Mon, 13 Apr 2026 15:55:28 +0200 Subject: [PATCH 057/285] Allow BC correction in FIT CTF decoders --- .../FDD/reconstruction/include/FDDReconstruction/CTFCoder.h | 4 +++- Detectors/FIT/FDD/workflow/src/EntropyDecoderSpec.cxx | 1 + .../FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h | 4 +++- Detectors/FIT/FT0/workflow/src/EntropyDecoderSpec.cxx | 1 + .../FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h | 4 +++- Detectors/FIT/FV0/workflow/src/EntropyDecoderSpec.cxx | 1 + 6 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h index c62e013447416..24649f73a4ca3 100644 --- a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h +++ b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h @@ -165,7 +165,9 @@ void CTFCoder::decompress(const CompressedDigits& cd, VDIG& digitVec, VCHAN& cha uint32_t firstEntry = 0, clCount = 0, chipCount = 0; o2::InteractionRecord ir(cd.header.firstBC, cd.header.firstOrbit); - + if (mBCShift && ir.toLong() >= mBCShift) { + ir -= mBCShift; + } for (uint32_t idig = 0; idig < cd.header.nTriggers; idig++) { // restore ROFRecord if (cd.orbitInc[idig]) { // non-0 increment => new orbit diff --git a/Detectors/FIT/FDD/workflow/src/EntropyDecoderSpec.cxx b/Detectors/FIT/FDD/workflow/src/EntropyDecoderSpec.cxx index 33c140b5bc198..43615b175734d 100644 --- a/Detectors/FIT/FDD/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/FIT/FDD/workflow/src/EntropyDecoderSpec.cxx @@ -29,6 +29,7 @@ EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdict mTimer.Stop(); mTimer.Reset(); mCTFCoder.setVerbosity(verbosity); + mCTFCoder.setSupportBCShifts(true); mCTFCoder.setDictBinding("ctfdict_FDD"); } diff --git a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h index 5dc367204e1a3..41f11e303db67 100644 --- a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h +++ b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h @@ -165,7 +165,9 @@ void CTFCoder::decompress(const CompressedDigits& cd, VDIG& digitVec, VCHAN& cha uint32_t firstEntry = 0, clCount = 0, chipCount = 0; o2::InteractionRecord ir(cd.header.firstBC, cd.header.firstOrbit); - + if (mBCShift && ir.toLong() >= mBCShift) { + ir -= mBCShift; + } for (uint32_t idig = 0; idig < cd.header.nTriggers; idig++) { // restore ROFRecord if (cd.orbitInc[idig]) { // non-0 increment => new orbit diff --git a/Detectors/FIT/FT0/workflow/src/EntropyDecoderSpec.cxx b/Detectors/FIT/FT0/workflow/src/EntropyDecoderSpec.cxx index 066c5cc547c2e..97ea337705fee 100644 --- a/Detectors/FIT/FT0/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/FIT/FT0/workflow/src/EntropyDecoderSpec.cxx @@ -29,6 +29,7 @@ EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdict mTimer.Stop(); mTimer.Reset(); mCTFCoder.setVerbosity(verbosity); + mCTFCoder.setSupportBCShifts(true); mCTFCoder.setDictBinding("ctfdict_FT0"); } diff --git a/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h b/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h index 80dcd6060455b..082fbd93a705a 100644 --- a/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h +++ b/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h @@ -168,7 +168,9 @@ void CTFCoder::decompress(const CompressedDigits& cd, VDIG& digitVec, VCHAN& cha uint32_t firstEntry = 0, clCount = 0, chipCount = 0; o2::InteractionRecord ir(cd.header.firstBC, cd.header.firstOrbit); - + if (mBCShift && ir.toLong() >= mBCShift) { + ir -= mBCShift; + } for (uint32_t idig = 0; idig < cd.header.nTriggers; idig++) { // restore ROFRecord if (cd.orbitInc[idig]) { // non-0 increment => new orbit diff --git a/Detectors/FIT/FV0/workflow/src/EntropyDecoderSpec.cxx b/Detectors/FIT/FV0/workflow/src/EntropyDecoderSpec.cxx index 7babe9fdea6ed..6cf8043cf683f 100644 --- a/Detectors/FIT/FV0/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/FIT/FV0/workflow/src/EntropyDecoderSpec.cxx @@ -29,6 +29,7 @@ EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdict mTimer.Stop(); mTimer.Reset(); mCTFCoder.setVerbosity(verbosity); + mCTFCoder.setSupportBCShifts(true); mCTFCoder.setDictBinding("ctfdict_FV0"); } From 1010e83338a2f7ee389e99c5655e7f6151ee765c Mon Sep 17 00:00:00 2001 From: Ernst Hellbar Date: Thu, 9 Apr 2026 11:42:50 +0200 Subject: [PATCH 058/285] secondary-vertexing-workflow: request CTP sources only if TPC is included --- .../src/secondary-vertexing-workflow.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx index 9108e8577fd5a..5bc80f527d4d0 100644 --- a/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx @@ -96,9 +96,9 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) } if (src[GID::TPC]) { srcClus |= GID::getSourceMask(GID::TPC); - } - if (sclOpt.requestCTPLumi) { - src = src | GID::getSourcesMask("CTP"); + if (sclOpt.requestCTPLumi) { + src = src | GID::getSourcesMask("CTP"); + } } WorkflowSpec specs; if (sclOpt.needTPCScalersWorkflow() && !configcontext.options().get("disable-root-input")) { From ef2f17856086155b6a4885d3b7c685151d51edde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Tue, 14 Apr 2026 08:47:10 +0200 Subject: [PATCH 059/285] [ALICE3] IOTOF: Add geometry macros (#15257) --- .../Upgrades/ALICE3/IOTOF/CMakeLists.txt | 3 +- .../ALICE3/IOTOF/macros/CMakeLists.txt | 13 ++ .../ALICE3/IOTOF/macros/defineIOTOFGeo.C | 139 ++++++++++++++++++ 3 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 Detectors/Upgrades/ALICE3/IOTOF/macros/CMakeLists.txt create mode 100644 Detectors/Upgrades/ALICE3/IOTOF/macros/defineIOTOFGeo.C diff --git a/Detectors/Upgrades/ALICE3/IOTOF/CMakeLists.txt b/Detectors/Upgrades/ALICE3/IOTOF/CMakeLists.txt index 83838a01d13f1..808320bf66404 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/IOTOF/CMakeLists.txt @@ -10,4 +10,5 @@ # or submit itself to any jurisdiction. add_subdirectory(base) -add_subdirectory(simulation) \ No newline at end of file +add_subdirectory(simulation) +add_subdirectory(macros) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/macros/CMakeLists.txt b/Detectors/Upgrades/ALICE3/IOTOF/macros/CMakeLists.txt new file mode 100644 index 0000000000000..b2f1857186c0b --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/macros/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2_add_test_root_macro(defineIOTOFGeo.C + LABELS alice3) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/macros/defineIOTOFGeo.C b/Detectors/Upgrades/ALICE3/IOTOF/macros/defineIOTOFGeo.C new file mode 100644 index 0000000000000..f096fc85aec7a --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/macros/defineIOTOFGeo.C @@ -0,0 +1,139 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void defineIOTOFGeo(const double rAvg = 21, // cm, average radius of the layer (used for stave size calculations) + const int nStaves = 24, // Number of staves + const double staveWidth = 5.42, // cm, Stave width (arc length at avg radius at 0 degrees) + const double staveHeightX2X0 = 0.02, // Stave height (radial at 0 degrees) + const double staveTilt = 10 // Stave tilt angle in degrees +) +{ + const double Si_X0 = 9.5f; // cm, radiation length of silicon + const double staveHeight = staveHeightX2X0 * Si_X0; + + // 1. Define inner and outer radii for the disk. + // The radius corresponds to the distance of the center of the stave to the origin + const double rInner = rAvg - staveHeight / 2.0; + const double rOuter = rAvg + staveHeight / 2.0; + + const double alpha = staveTilt * TMath::DegToRad(); // Tilt angle in radians + const double H = staveHeight; + const double W = staveWidth; + + // 2. Analytical calculation of Inscribed and Outscribed Radii + // We project the global origin (0,0) into the local, unrotated coordinate + // system of a single stave centered at (0,0). + const double u0 = -rAvg * TMath::Cos(alpha); + const double v0 = rAvg * TMath::Sin(alpha); + + // Inscribed Radius: Distance to the closest point on the stave rectangle + const double uc = std::max(-H / 2.0, std::min(H / 2.0, u0)); + const double vc = std::max(-W / 2.0, std::min(W / 2.0, v0)); + const double rInscribed = TMath::Sqrt((uc - u0) * (uc - u0) + (vc - v0) * (vc - v0)); + + // Outscribed Radius: Maximum distance to one of the 4 corners + double rOutscribed = 0; + const double uCorners[4] = {-H / 2.0, H / 2.0, H / 2.0, -H / 2.0}; + const double vCorners[4] = {-W / 2.0, -W / 2.0, W / 2.0, W / 2.0}; + for (int i = 0; i < 4; ++i) { + const double dist = std::hypot(uCorners[i] - u0, vCorners[i] - v0); + if (dist > rOutscribed) { + rOutscribed = dist; + } + } + + // 3. Visualization + new TCanvas("DiskWithStaves", "Disk with Staves", 800, 800); + gPad->SetGrid(); + gPad->SetLeftMargin(0.15); + gPad->SetBottomMargin(0.15); + gPad->SetRightMargin(0.05); + gPad->SetTopMargin(0.05); + + const double maxR = std::max(rOuter, rOutscribed) * 1.5; + gPad->DrawFrame(-maxR, -maxR, maxR, maxR, ";X (cm);Y (cm)"); + + // Draw Inner and Outer Disk Radii (Reference) + TArc* arcInner = new TArc(0, 0, rInner); + arcInner->SetLineStyle(2); + arcInner->SetLineColor(kGray + 1); + arcInner->SetFillStyle(0); + arcInner->Draw("same"); + + TArc* arcOuter = new TArc(0, 0, rOuter); + arcOuter->SetLineStyle(2); + arcOuter->SetLineColor(kGray + 1); + arcOuter->SetFillStyle(0); + arcOuter->Draw("same"); + + // Draw Inscribed and Outscribed circles + TArc* arcInscribed = new TArc(0, 0, rInscribed); + arcInscribed->SetLineColor(kBlue); + arcInscribed->SetLineWidth(2); + arcInscribed->SetFillStyle(0); + arcInscribed->Draw("same"); + + TArc* arcOutscribed = new TArc(0, 0, rOutscribed); + arcOutscribed->SetLineColor(kRed); + arcOutscribed->SetLineWidth(2); + arcOutscribed->SetFillStyle(0); + arcOutscribed->Draw("same"); + + // Generate and Draw Staves + for (int i = 0; i < nStaves; ++i) { + double phi = i * TMath::TwoPi() / nStaves; + double xPts[5], yPts[5]; + for (int j = 0; j < 4; ++j) { + double u = uCorners[j]; + double v = vCorners[j]; + // Apply stave tilt (alpha) around its own center + double uRot = u * TMath::Cos(alpha) - v * TMath::Sin(alpha); + double vRot = u * TMath::Sin(alpha) + v * TMath::Cos(alpha); + // Move stave to rAvg and apply azimuthal rotation (phi) + double x_phi0 = rAvg + uRot; + double y_phi0 = vRot; + xPts[j] = x_phi0 * TMath::Cos(phi) - y_phi0 * TMath::Sin(phi); + yPts[j] = x_phi0 * TMath::Sin(phi) + y_phi0 * TMath::Cos(phi); + } + // Close the geometric polygon + xPts[4] = xPts[0]; + yPts[4] = yPts[0]; + TGraph* gStave = new TGraph(5, xPts, yPts); + gStave->SetFillColorAlpha(kGreen + 2, 0.4); + gStave->SetLineColor(kBlack); + gStave->SetLineWidth(1); + gStave->Draw("f same"); // Fill + gStave->Draw("l same"); // Outline + } + + // 7. Add Legend / Parameter Text + TLatex* tex = new TLatex(); + tex->SetNDC(); + tex->SetTextSize(0.028); + tex->SetTextFont(42); + tex->SetTextColor(kBlack); + tex->DrawLatex(0.12, 0.88, Form("R_{inner} = %.1f, R_{outer} = %.1f", rInner, rOuter)); + tex->DrawLatex(0.12, 0.84, Form("Staves: %d, Tilt: %.1f#circ", nStaves, staveTilt)); + tex->SetTextColor(kBlue); + tex->DrawLatex(0.12, 0.80, Form("Inscribed Radius = %.2f", rInscribed)); + tex->SetTextColor(kRed); + tex->DrawLatex(0.12, 0.76, Form("Outscribed Radius = %.2f", rOutscribed)); +} From 14045caca9a91c39625e97bde4e57131c14b3131 Mon Sep 17 00:00:00 2001 From: shahoian Date: Tue, 14 Apr 2026 14:29:09 +0200 Subject: [PATCH 060/285] Restrict ITS max timestamp uncertainty to shortest ROF --- Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx index f996c0d25e7d7..3cf462206bf94 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx @@ -757,6 +757,7 @@ void TrackerTraits::findRoads(const int iteration) }); }); + const float smallestROFHalf = mTimeFrame->getROFOverlapTableView().getClockLayer().mROFLength * 0.5f; for (auto& track : tracks) { int nShared = 0; bool isFirstShared{false}; @@ -799,6 +800,10 @@ void TrackerTraits::findRoads(const int iteration) } } track.getTimeStamp() = ts.makeSymmetrical(); + if (track.getTimeStamp().getTimeStampError() > smallestROFHalf) { + track.getTimeStamp().setTimeStampError(smallestROFHalf); + } + track.setUserField(0); track.getParamOut().setUserField(0); mTimeFrame->getTracks().emplace_back(track); From 74098a4054b9560c36836a22f405dd4881e2678c Mon Sep 17 00:00:00 2001 From: Felix Weiglhofer <9267733+fweig@users.noreply.github.com> Date: Wed, 15 Apr 2026 10:55:36 +0200 Subject: [PATCH 061/285] GPU/TPC: Simplify pad indexing in noisy-pad filter --- .../DataTypes/CalibdEdxContainer.cxx | 2 +- GPU/GPUTracking/DataTypes/TPCPadBitMap.cxx | 2 +- GPU/GPUTracking/DataTypes/TPCPadBitMap.h | 2 +- GPU/GPUTracking/DataTypes/TPCPadGainCalib.cxx | 2 +- GPU/GPUTracking/DataTypes/TPCPadGainCalib.h | 6 +-- .../DataTypes/TPCZSLinkMapping.cxx | 2 +- .../Definitions/clusterFinderDefs.h | 16 +++--- .../Global/GPUChainTrackingClusterizer.cxx | 4 +- GPU/GPUTracking/TPCClusterFinder/CfArray2D.h | 8 +-- .../TPCClusterFinder/CfChargePos.h | 6 +-- .../GPUTPCCFCheckPadBaseline.cxx | 53 ++++--------------- .../GPUTPCCFCheckPadBaseline.h | 11 +--- .../TPCClusterFinder/GPUTPCCFPeakFinder.cxx | 6 +-- .../TPCClusterFinder/GPUTPCClusterFinder.cxx | 2 +- .../GPUTPCClusterFinderDump.cxx | 4 +- 15 files changed, 44 insertions(+), 82 deletions(-) diff --git a/GPU/GPUTracking/DataTypes/CalibdEdxContainer.cxx b/GPU/GPUTracking/DataTypes/CalibdEdxContainer.cxx index 0b3ee65ef7578..ba4b230e1f6f2 100644 --- a/GPU/GPUTracking/DataTypes/CalibdEdxContainer.cxx +++ b/GPU/GPUTracking/DataTypes/CalibdEdxContainer.cxx @@ -265,7 +265,7 @@ void CalibdEdxContainer::setDefaultZeroSupresssionThreshold() mThresholdMap.setMinCorrectionFactor(defaultVal - 0.1f); mThresholdMap.setMaxCorrectionFactor(defaultVal + 0.1f); for (int32_t sector = 0; sector < o2::tpc::constants::MAXSECTOR; ++sector) { - for (uint16_t globPad = 0; globPad < TPC_PADS_IN_SECTOR; ++globPad) { + for (uint16_t globPad = 0; globPad < TPC_REAL_PADS_IN_SECTOR; ++globPad) { mThresholdMap.setGainCorrection(sector, globPad, defaultVal); } } diff --git a/GPU/GPUTracking/DataTypes/TPCPadBitMap.cxx b/GPU/GPUTracking/DataTypes/TPCPadBitMap.cxx index 0b8e67fbe495e..2d12f98b8cf16 100644 --- a/GPU/GPUTracking/DataTypes/TPCPadBitMap.cxx +++ b/GPU/GPUTracking/DataTypes/TPCPadBitMap.cxx @@ -40,7 +40,7 @@ TPCPadBitMap::TPCPadBitMap(const o2::tpc::CalDet& map) : TPCPadBitMap() void TPCPadBitMap::setFromMap(const o2::tpc::CalDet& map) { for (int32_t sector = 0; sector < o2::tpc::constants::MAXSECTOR; sector++) { - for (int32_t p = 0; p < TPC_PADS_IN_SECTOR; p++) { + for (int32_t p = 0; p < TPC_REAL_PADS_IN_SECTOR; p++) { const auto val = map.getValue(sector, p); mBitMap[sector].set(p, val); } diff --git a/GPU/GPUTracking/DataTypes/TPCPadBitMap.h b/GPU/GPUTracking/DataTypes/TPCPadBitMap.h index 6ddfac8c268ee..299b880fcbcc6 100644 --- a/GPU/GPUTracking/DataTypes/TPCPadBitMap.h +++ b/GPU/GPUTracking/DataTypes/TPCPadBitMap.h @@ -68,7 +68,7 @@ struct TPCPadBitMap { { public: using T = uint32_t; - static constexpr int32_t NWORDS = (TPC_PADS_IN_SECTOR + sizeof(T) * 8 - 1) / sizeof(T); + static constexpr int32_t NWORDS = (TPC_REAL_PADS_IN_SECTOR + sizeof(T) * 8 - 1) / sizeof(T); GPUdi() SectorBitMap() { reset(); diff --git a/GPU/GPUTracking/DataTypes/TPCPadGainCalib.cxx b/GPU/GPUTracking/DataTypes/TPCPadGainCalib.cxx index a20f3dc8aac1d..6cc70c7afa7e1 100644 --- a/GPU/GPUTracking/DataTypes/TPCPadGainCalib.cxx +++ b/GPU/GPUTracking/DataTypes/TPCPadGainCalib.cxx @@ -47,7 +47,7 @@ TPCPadGainCalib::TPCPadGainCalib(const o2::tpc::CalDet& gainMap, const fl void TPCPadGainCalib::setFromMap(const o2::tpc::CalDet& gainMap, const bool inv) { for (int32_t sector = 0; sector < o2::tpc::constants::MAXSECTOR; sector++) { - for (int32_t p = 0; p < TPC_PADS_IN_SECTOR; p++) { + for (int32_t p = 0; p < TPC_REAL_PADS_IN_SECTOR; p++) { const float gainVal = gainMap.getValue(sector, p); inv ? mGainCorrection[sector].set(p, (gainVal > 1.e-5f) ? 1.f / gainVal : 1.f) : mGainCorrection[sector].set(p, gainVal); } diff --git a/GPU/GPUTracking/DataTypes/TPCPadGainCalib.h b/GPU/GPUTracking/DataTypes/TPCPadGainCalib.h index 263956c8b5602..dbea56ee5ea6b 100644 --- a/GPU/GPUTracking/DataTypes/TPCPadGainCalib.h +++ b/GPU/GPUTracking/DataTypes/TPCPadGainCalib.h @@ -120,12 +120,14 @@ struct TPCPadGainCalib { GPUd() void reset() { - for (uint16_t p = 0; p < TPC_PADS_IN_SECTOR; p++) { + for (uint16_t p = 0; p < TPC_REAL_PADS_IN_SECTOR; p++) { set(p, 1.0f); } } private: + T mGainCorrection[TPC_REAL_PADS_IN_SECTOR]; + GPUd() T pack(float f) const { f = CAMath::Clamp(f, mMinCorrectionFactor, mMaxCorrectionFactor); @@ -140,8 +142,6 @@ struct TPCPadGainCalib { return mMinCorrectionFactor + (mMaxCorrectionFactor - mMinCorrectionFactor) * float(c) / float(NumOfSteps); } - T mGainCorrection[TPC_PADS_IN_SECTOR]; - GPUdi() T& at(uint16_t globalPad) { return mGainCorrection[globalPad]; diff --git a/GPU/GPUTracking/DataTypes/TPCZSLinkMapping.cxx b/GPU/GPUTracking/DataTypes/TPCZSLinkMapping.cxx index 60f960d1b25f0..f520282bfa35b 100644 --- a/GPU/GPUTracking/DataTypes/TPCZSLinkMapping.cxx +++ b/GPU/GPUTracking/DataTypes/TPCZSLinkMapping.cxx @@ -27,7 +27,7 @@ TPCZSLinkMapping::TPCZSLinkMapping(o2::tpc::Mapper& mapper) assert(fecToGlobalPad.size() == TPC_FEC_IDS_IN_SECTOR); const auto& globalPadToPadPos = mapper.getMapGlobalPadToPadPos(); - assert(globalPadToPadPos.size() == TPC_PADS_IN_SECTOR); + assert(globalPadToPadPos.size() == TPC_REAL_PADS_IN_SECTOR); for (size_t i = 0; i < TPC_FEC_IDS_IN_SECTOR; i++) { FECIDToPadPos[i] = globalPadToPadPos[fecToGlobalPad[i]]; diff --git a/GPU/GPUTracking/Definitions/clusterFinderDefs.h b/GPU/GPUTracking/Definitions/clusterFinderDefs.h index b36a94fc2bd54..8d9ec60e551b9 100644 --- a/GPU/GPUTracking/Definitions/clusterFinderDefs.h +++ b/GPU/GPUTracking/Definitions/clusterFinderDefs.h @@ -32,15 +32,19 @@ #endif // Padding of 2 and 3 respectively would be enough. But this ensures that -// rows are always aligned along cache lines. Likewise for TPC_PADS_PER_ROW. +// rows are always aligned along cache lines. Likewise for TPC_CLUSTERER_ROW_PAD_CAPACITY. #define GPUCF_PADDING_PAD 8 #define GPUCF_PADDING_TIME 4 -#define TPC_PADS_PER_ROW 144 +// Largest possible number of pads in a TPC row +#define TPC_CLUSTERER_ROW_PAD_CAPACITY 144 -#define TPC_ROWS_PER_CRU 18 -#define TPC_PADS_PER_ROW_PADDED (TPC_PADS_PER_ROW + GPUCF_PADDING_PAD) -#define TPC_NUM_OF_PADS (GPUCA_ROW_COUNT * TPC_PADS_PER_ROW_PADDED + GPUCF_PADDING_PAD) -#define TPC_PADS_IN_SECTOR 14560 +// Stride between rows as stored internally by the clusterizer +#define TPC_CLUSTERER_ROW_STRIDE (TPC_CLUSTERER_ROW_PAD_CAPACITY + GPUCF_PADDING_PAD) +// Number of pads in a sector as stored internally by the clusterizer. +// This includes fake pads for constant strides between rows +#define TPC_CLUSTERER_STRIDED_PAD_COUNT (GPUCA_ROW_COUNT * TPC_CLUSTERER_ROW_STRIDE + GPUCF_PADDING_PAD) +// Real of number of pads in a sector +#define TPC_REAL_PADS_IN_SECTOR 14560 #define TPC_FEC_IDS_IN_SECTOR 23296 #define TPC_MAX_FRAGMENT_LEN_GPU 4000 #define TPC_MAX_FRAGMENT_LEN_HOST 1000 diff --git a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx index bf6577cfd929e..12a12d4c47585 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx @@ -861,7 +861,7 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) runKernel({GetGridAutoStep(lane, RecoStep::TPCClusterFinding)}, clustererShadow.mPchargeMap, TPCMapMemoryLayout::items(GetProcessingSettings().overrideClusterizerFragmentLen) * sizeof(ChargeMapType)); runKernel({GetGridAutoStep(lane, RecoStep::TPCClusterFinding)}, clustererShadow.mPpeakMap, TPCMapMemoryLayout::items(GetProcessingSettings().overrideClusterizerFragmentLen) * sizeof(PeakMapType)); if (fragment.index == 0) { - runKernel({GetGridAutoStep(lane, RecoStep::TPCClusterFinding)}, clustererShadow.mPpadIsNoisy, TPC_PADS_IN_SECTOR * sizeof(*clustererShadow.mPpadIsNoisy)); + runKernel({GetGridAutoStep(lane, RecoStep::TPCClusterFinding)}, clustererShadow.mPpadIsNoisy, TPC_CLUSTERER_STRIDED_PAD_COUNT * sizeof(*clustererShadow.mPpadIsNoisy)); } DoDebugAndDump(RecoStep::TPCClusterFinding, GPUChainTrackingDebugFlags::TPCClustererZeroedCharges, clusterer, &GPUTPCClusterFinder::DumpChargeMap, *mDebugFile, "Zeroed Charges"); @@ -965,7 +965,7 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) const int32_t nBlocks = GPUTPCCFCheckPadBaseline::GetNBlocks(doGPU); runKernel({GetGridBlk(nBlocks, lane), {iSector}}); - getKernelTimer(RecoStep::TPCClusterFinding, iSector, TPC_PADS_IN_SECTOR * fragment.lengthWithoutOverlap() * sizeof(PackedCharge), false); + getKernelTimer(RecoStep::TPCClusterFinding, iSector, TPC_REAL_PADS_IN_SECTOR * fragment.lengthWithoutOverlap() * sizeof(PackedCharge), false); } runKernel({GetGrid(clusterer.mPmemory->counters.nPositions, lane), {iSector}}); diff --git a/GPU/GPUTracking/TPCClusterFinder/CfArray2D.h b/GPU/GPUTracking/TPCClusterFinder/CfArray2D.h index 3c8bcf94da4b3..e61ec532bf7e0 100644 --- a/GPU/GPUTracking/TPCClusterFinder/CfArray2D.h +++ b/GPU/GPUTracking/TPCClusterFinder/CfArray2D.h @@ -49,7 +49,7 @@ class TilingLayout enum { Height = Grid::Height, Width = Grid::Width, - WidthInTiles = (TPC_NUM_OF_PADS + Width - 1) / Width, + WidthInTiles = (TPC_CLUSTERER_STRIDED_PAD_COUNT + Width - 1) / Width, }; GPUdi() static tpccf::SizeT idx(const CfChargePos& p) @@ -65,7 +65,7 @@ class TilingLayout GPUd() static size_t items(size_t fragmentLen) { - return (TPC_NUM_OF_PADS + Width - 1) / Width * Width * (TPC_MAX_FRAGMENT_LEN_PADDED(fragmentLen) + Height - 1) / Height * Height; + return (TPC_CLUSTERER_STRIDED_PAD_COUNT + Width - 1) / Width * Width * (TPC_MAX_FRAGMENT_LEN_PADDED(fragmentLen) + Height - 1) / Height * Height; } }; @@ -74,12 +74,12 @@ class LinearLayout public: GPUdi() static tpccf::SizeT idx(const CfChargePos& p) { - return TPC_NUM_OF_PADS * p.timePadded + p.gpad; + return TPC_CLUSTERER_STRIDED_PAD_COUNT * p.timePadded + p.gpad; } GPUd() static size_t items(size_t fragmentLen) { - return TPC_NUM_OF_PADS * TPC_MAX_FRAGMENT_LEN_PADDED(fragmentLen); + return TPC_CLUSTERER_STRIDED_PAD_COUNT * TPC_MAX_FRAGMENT_LEN_PADDED(fragmentLen); } }; diff --git a/GPU/GPUTracking/TPCClusterFinder/CfChargePos.h b/GPU/GPUTracking/TPCClusterFinder/CfChargePos.h index bf6ce2fc804ba..3d853345b8f95 100644 --- a/GPU/GPUTracking/TPCClusterFinder/CfChargePos.h +++ b/GPU/GPUTracking/TPCClusterFinder/CfChargePos.h @@ -42,8 +42,8 @@ struct CfChargePos { GPUdi() bool valid() const { return timePadded >= 0; } - GPUdi() tpccf::Row row() const { return gpad / TPC_PADS_PER_ROW_PADDED; } - GPUdi() tpccf::Pad pad() const { return gpad % TPC_PADS_PER_ROW_PADDED - GPUCF_PADDING_PAD; } + GPUdi() tpccf::Row row() const { return gpad / TPC_CLUSTERER_ROW_STRIDE; } + GPUdi() tpccf::Pad pad() const { return gpad % TPC_CLUSTERER_ROW_STRIDE - GPUCF_PADDING_PAD; } GPUdi() tpccf::TPCFragmentTime time() const { return timePadded - GPUCF_PADDING_TIME; } GPUdi() tpccf::TPCFragmentTime globalTime() const { return timePadded; } @@ -52,7 +52,7 @@ struct CfChargePos { // index between 0 and TPC_NUM_OF_PADS. static constexpr GPUdi() tpccf::GlobalPad tpcGlobalPadIdx(tpccf::Row row, tpccf::Pad pad) { - return TPC_PADS_PER_ROW_PADDED * row + pad + GPUCF_PADDING_PAD; + return TPC_CLUSTERER_ROW_STRIDE * row + pad + GPUCF_PADDING_PAD; } }; diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.cxx index 33ed089890bc4..8cbcf320e2547 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.cxx @@ -50,8 +50,10 @@ GPUd() void GPUTPCCFCheckPadBaseline::CheckBaselineGPU(int32_t nBlocks, int32_t const CfFragment& fragment = clusterer.mPmemory->fragment; CfArray2D chargeMap(reinterpret_cast(clusterer.mPchargeMap)); + constexpr GPUTPCGeometry geo; + const auto iRow = iBlock; - const auto rowinfo = GetRowInfo(iRow); + const auto nPads = geo.NPads(iRow); const CfChargePos basePos{(Row)iRow, 0, 0}; int32_t totalCharges = 0; @@ -62,7 +64,7 @@ GPUd() void GPUTPCCFCheckPadBaseline::CheckBaselineGPU(int32_t nBlocks, int32_t const int16_t iPadOffset = iThread % MaxNPadsPerRow; const int16_t iTimeOffset = iThread / MaxNPadsPerRow; const int16_t iPadHandle = iThread; - const bool handlePad = iPadHandle < rowinfo.nPads; + const bool handlePad = iPadHandle < nPads; const auto firstTB = fragment.firstNonOverlapTimeBin(); const auto lastTB = fragment.lastNonOverlapTimeBin(); @@ -73,7 +75,7 @@ GPUd() void GPUTPCCFCheckPadBaseline::CheckBaselineGPU(int32_t nBlocks, int32_t const CfChargePos pos = basePos.delta({iPadOffset, iTime}); - smem.charges[iTimeOffset][iPadOffset] = iTime < lastTB && iPadOffset < rowinfo.nPads ? chargeMap[pos].unpack() : 0; + smem.charges[iTimeOffset][iPadOffset] = iTime < lastTB && iPadOffset < nPads ? chargeMap[pos].unpack() : 0; GPUbarrier(); @@ -91,7 +93,7 @@ GPUd() void GPUTPCCFCheckPadBaseline::CheckBaselineGPU(int32_t nBlocks, int32_t } if (handlePad) { - updatePadBaseline(rowinfo.globalPadOffset + iPadOffset, clusterer, totalCharges, maxConsecCharges, maxCharge); + updatePadBaseline(basePos.gpad + iPadHandle, clusterer, totalCharges, maxConsecCharges, maxCharge); } #endif } @@ -102,11 +104,10 @@ GPUd() void GPUTPCCFCheckPadBaseline::CheckBaselineCPU(int32_t nBlocks, int32_t const CfFragment& fragment = clusterer.mPmemory->fragment; CfArray2D chargeMap(reinterpret_cast(clusterer.mPchargeMap)); - int32_t basePad = iBlock * PadsPerCacheline; - int32_t padsPerRow; - CfChargePos basePos = padToCfChargePos(basePad, clusterer, padsPerRow); + CfChargePos basePos(iBlock * PadsPerCacheline, 0); - if (not basePos.valid()) { + constexpr GPUTPCGeometry geo; + if (basePos.pad() >= geo.NPads(basePos.row())) { return; } @@ -153,45 +154,11 @@ GPUd() void GPUTPCCFCheckPadBaseline::CheckBaselineCPU(int32_t nBlocks, int32_t } for (tpccf::Pad localpad = 0; localpad < PadsPerCacheline; localpad++) { - updatePadBaseline(basePad + localpad, clusterer, totalCharges[localpad], maxConsecCharges[localpad], maxCharge[localpad]); + updatePadBaseline(basePos.gpad + localpad, clusterer, totalCharges[localpad], maxConsecCharges[localpad], maxCharge[localpad]); } #endif } -template -GPUd() CfChargePos GPUTPCCFCheckPadBaseline::padToCfChargePos(int32_t& pad, const GPUTPCClusterFinder& clusterer, int32_t& padsPerRow) -{ - constexpr GPUTPCGeometry geo; - - int32_t padOffset = 0; - for (Row r = 0; r < GPUCA_ROW_COUNT; r++) { - int32_t npads = geo.NPads(r); - int32_t padInRow = pad - padOffset; - if (0 <= padInRow && padInRow < npads) { - int32_t cachelineOffset = padInRow % PadsPerBlock; - pad -= cachelineOffset; - padsPerRow = npads; - return CfChargePos{r, Pad(padInRow - cachelineOffset), 0}; - } - padOffset += npads; - } - - padsPerRow = 0; - return CfChargePos{0, 0, INVALID_TIME_BIN}; -} - -GPUd() GPUTPCCFCheckPadBaseline::RowInfo GPUTPCCFCheckPadBaseline::GetRowInfo(int16_t row) -{ - constexpr GPUTPCGeometry geo; - - int16_t padOffset = 0; - for (int16_t r = 0; r < row; r++) { - padOffset += geo.NPads(r); - } - - return RowInfo{padOffset, geo.NPads(row)}; -} - GPUd() void GPUTPCCFCheckPadBaseline::updatePadBaseline(int32_t pad, const GPUTPCClusterFinder& clusterer, int32_t totalCharges, int32_t consecCharges, Charge maxCharge) { const CfFragment& fragment = clusterer.mPmemory->fragment; diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.h index a71f1358a73a6..bb44e5e69a9e1 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.h @@ -63,7 +63,7 @@ class GPUTPCCFCheckPadBaseline : public GPUKernelTemplate static int32_t GetNBlocks(bool isGPU) { - const int32_t nBlocks = TPC_PADS_IN_SECTOR / PadsPerCacheline; + const int32_t nBlocks = TPC_CLUSTERER_STRIDED_PAD_COUNT / PadsPerCacheline; return isGPU ? GPUCA_ROW_COUNT : nBlocks; } @@ -74,15 +74,6 @@ class GPUTPCCFCheckPadBaseline : public GPUKernelTemplate GPUd() static void CheckBaselineGPU(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& clusterer); GPUd() static void CheckBaselineCPU(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUSharedMemory& smem, processorType& clusterer); - template - GPUd() static CfChargePos padToCfChargePos(int32_t& pad, const GPUTPCClusterFinder&, int32_t& padsPerRow); - - struct RowInfo { - int16_t globalPadOffset; - int16_t nPads; - }; - GPUd() static RowInfo GetRowInfo(int16_t row); - GPUd() static void updatePadBaseline(int32_t pad, const GPUTPCClusterFinder&, int32_t totalCharges, int32_t consecCharges, tpccf::Charge maxCharge); }; diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFPeakFinder.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFPeakFinder.cxx index 6749ab8e8485e..5d94e36febc0a 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFPeakFinder.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFPeakFinder.cxx @@ -105,11 +105,11 @@ GPUd() void GPUTPCCFPeakFinder::findPeaksImpl(int32_t nBlocks, int32_t nThreads, // For certain configurations dummy work items are added, so the total // number of work items is dividable by 64. // These dummy items also compute the last digit but discard the result. - CfChargePos pos = positions[CAMath::Min(idx, (SizeT)(digitnum - 1))]; + CfChargePos pos = positions[CAMath::Min(idx, digitnum - 1)]; Charge charge = pos.valid() ? chargeMap[pos].unpack() : Charge(0); - bool hasLostBaseline = padHasLostBaseline[gainCorrection.globalPad(pos.row(), pos.pad())]; - charge = (hasLostBaseline) ? 0.f : charge; + bool hasLostBaseline = padHasLostBaseline[pos.gpad]; + charge = hasLostBaseline ? 0.f : charge; uint8_t peak = isPeak(smem, charge, pos, SCRATCH_PAD_SEARCH_N, chargeMap, calib, smem.posBcast, smem.buf); diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinder.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinder.cxx index 44b005eb20233..979980f32a479 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinder.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinder.cxx @@ -79,7 +79,7 @@ void* GPUTPCClusterFinder::SetPointersOutput(void* mem) void* GPUTPCClusterFinder::SetPointersScratch(void* mem) { - computePointerWithAlignment(mem, mPpadIsNoisy, TPC_PADS_IN_SECTOR); + computePointerWithAlignment(mem, mPpadIsNoisy, TPC_CLUSTERER_STRIDED_PAD_COUNT); computePointerWithAlignment(mem, mPpositions, mNMaxDigitsFragment); computePointerWithAlignment(mem, mPpeakPositions, mNMaxPeaks); computePointerWithAlignment(mem, mPfilteredPeakPositions, mNMaxClusters); diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinderDump.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinderDump.cxx index d676cf9cd3887..242f6963a0b50 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinderDump.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinderDump.cxx @@ -48,7 +48,7 @@ void GPUTPCClusterFinder::DumpChargeMap(std::ostream& out, std::string_view titl for (TPCFragmentTime i = start; i < end; i++) { int32_t zeros = 0; - for (GlobalPad j = 0; j < TPC_NUM_OF_PADS; j++) { + for (GlobalPad j = 0; j < TPC_CLUSTERER_STRIDED_PAD_COUNT; j++) { uint16_t q = map[{j, i}]; zeros += (q == 0); if (q != 0) { @@ -84,7 +84,7 @@ void GPUTPCClusterFinder::DumpPeakMap(std::ostream& out, std::string_view title) int32_t zeros = 0; out << i << ":"; - for (GlobalPad j = 0; j < TPC_NUM_OF_PADS; j++) { + for (GlobalPad j = 0; j < TPC_CLUSTERER_STRIDED_PAD_COUNT; j++) { uint8_t q = map[{j, i}]; zeros += (q == 0); if (q != 0) { From b7bfb2c0c2235654afe8a162eca0cf48aed40a0b Mon Sep 17 00:00:00 2001 From: Stefano Cannito <143754257+scannito@users.noreply.github.com> Date: Thu, 16 Apr 2026 10:16:21 +0200 Subject: [PATCH 062/285] [ALICE3] Rough attempt to pave ML disks as done for OT (#15269) * Updated FT3Module * First attempt to pave ML disks --- .../ALICE3/FT3/simulation/src/Detector.cxx | 30 +++++++++---------- .../ALICE3/FT3/simulation/src/FT3Layer.cxx | 2 +- .../ALICE3/FT3/simulation/src/FT3Module.cxx | 16 ++++++---- 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx index 94d56fd9625a0..2a9a9633cdd11 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx @@ -631,7 +631,7 @@ void Detector::defineSensitiveVolumes() LOG(info) << "Adding FT3 Sensitive Volume for direction " << direction << " layer " << iLayer << "/" << getNumberOfLayers(); volumeName = o2::ft3::GeometryTGeo::getFT3SensorPattern() + std::to_string(iLayer); int iSens = 0; - if (mLayers[direction][iLayer].getIsInMiddleLayer()) { // ML disks + /*if (mLayers[direction][iLayer].getIsInMiddleLayer()) { // ML disks const std::string sensorName = Form("%s_%d_%d", GeometryTGeo::getFT3SensorPattern(), direction, iLayer); v = geoManager->GetVolume(sensorName.c_str()); if (!v) { @@ -640,22 +640,22 @@ void Detector::defineSensitiveVolumes() } AddSensitiveVolume(v); iSens++; - } else { // OT disks - for (int sensor_count = 0; sensor_count < MAX_SENSORS; ++sensor_count) { - std::string sensor_name_front = "FT3Sensor_front_" + std::to_string(iLayer) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); - std::string sensor_name_back = "FT3Sensor_back_" + std::to_string(iLayer) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); - v = geoManager->GetVolume(sensor_name_front.c_str()); - if (v) { - AddSensitiveVolume(v); - iSens++; - } - v = geoManager->GetVolume(sensor_name_back.c_str()); - if (v) { - AddSensitiveVolume(v); - iSens++; - } + } else { // OT disks*/ + for (int sensor_count = 0; sensor_count < MAX_SENSORS; ++sensor_count) { + std::string sensor_name_front = "FT3Sensor_front_" + std::to_string(iLayer) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + std::string sensor_name_back = "FT3Sensor_back_" + std::to_string(iLayer) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + v = geoManager->GetVolume(sensor_name_front.c_str()); + if (v) { + AddSensitiveVolume(v); + iSens++; + } + v = geoManager->GetVolume(sensor_name_back.c_str()); + if (v) { + AddSensitiveVolume(v); + iSens++; } } + //} LOG(info) << iSens << " sensitive volumes added"; } } diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx index 333599c85eab6..5be3c7abc30a3 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx @@ -237,7 +237,7 @@ void FT3Layer::createLayer(TGeoVolume* motherVolume) LOG(info) << "FT3: ft3Params.layoutFT3 = " << ft3Params.layoutFT3; // ### options for ML and OT disk layout - if (ft3Params.layoutFT3 == kTrapezoidal || (mIsMiddleLayer && ft3Params.layoutFT3 == kSegmented)) { + if (ft3Params.layoutFT3 == kTrapezoidal /*|| (mIsMiddleLayer && ft3Params.layoutFT3 == kSegmented)*/) { // trapezoidal ML+OT disks // (disks with TGeoTubes doesn'n work properly in ACTS, due to polar coordinates on TGeoTube sides) diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx index 99322aa91f53f..4ed330c35ae59 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx @@ -124,7 +124,6 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double } else if (sensor_height == 19.2 && sensor_width == 5) { x_offset = 0.7; y_offset = 9; - } else { x_offset = sensor_width / 2; y_offset = sensor_height / 2; @@ -146,14 +145,12 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double if (Rin == 7 && sensor_height == 9.6 && sensor_width == 5) { x_condition_min = -Rin - 2; x_condition_max = Rin; + dist_offset = 2; adjust_bottom_y_pos = true; adjust_bottom_y_neg = true; x_adjust_bottom_y_pos = 3.5; bottom_y_pos_value = 3.5; bottom_y_neg_value = -3.5; - - dist_offset = 2; - } else if (Rin == 5 && sensor_height == 9.6 && sensor_width == 5) { x_condition_min = -Rin - 6; x_condition_max = Rin; @@ -201,6 +198,15 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double x_adjust_bottom_y_pos = 5.5; bottom_y_pos_value = 3.5; bottom_y_neg_value = -3.5; + } else if (Rin == 10 && sensor_height == 9.6 && sensor_width == 5.0) { + x_condition_min = -Rin - 4; + x_condition_max = Rin; + dist_offset = 2; + adjust_bottom_y_pos = false; + adjust_bottom_y_neg = false; + x_adjust_bottom_y_pos = 3.5; + bottom_y_pos_value = 3.5; + bottom_y_neg_value = -3.5; } else if (Rin == 20 && sensor_height == 9.6 && sensor_width == 5.0) { x_condition_min = -Rin - 4; x_condition_max = Rin; @@ -244,7 +250,7 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double justSkipped1 = {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}; } } else { - if (Rin == 20) { // v3 paving, rough attempt + if (Rin == 10 || Rin == 20) { // v3 paving, rough attempt float overlap = 0.3; // NB: these are left edges float X_start = -2.0 - 13.5 * (sensor_width - overlap); From af3e63398be353dedec10d97545101eac2fee123 Mon Sep 17 00:00:00 2001 From: Maximiliano Puccio Date: Mon, 13 Apr 2026 15:25:03 +0200 Subject: [PATCH 063/285] FastMultEst does not need to be streamed --- Detectors/ITSMFT/ITS/tracking/CMakeLists.txt | 1 - Detectors/ITSMFT/ITS/tracking/include/ITStracking/FastMultEst.h | 2 -- Detectors/ITSMFT/ITS/tracking/src/TrackingLinkDef.h | 1 - 3 files changed, 4 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt index 8d8304d16764f..c9c5196da617b 100644 --- a/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt @@ -52,7 +52,6 @@ o2_target_root_dictionary(ITStracking include/ITStracking/Tracklet.h include/ITStracking/Cluster.h include/ITStracking/Definitions.h - include/ITStracking/FastMultEst.h include/ITStracking/FastMultEstConfig.h include/ITStracking/TrackingConfigParam.h LINKDEF src/TrackingLinkDef.h) diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/FastMultEst.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/FastMultEst.h index 3083a8fe9c2ec..f94c7c2034b46 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/FastMultEst.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/FastMultEst.h @@ -84,8 +84,6 @@ struct FastMultEst { return process(countClustersOnLayer(clusters)); } static bool sSeedSet; - - ClassDefNV(FastMultEst, 1); }; } // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackingLinkDef.h b/Detectors/ITSMFT/ITS/tracking/src/TrackingLinkDef.h index 9efd6dde0176d..0640ff98297b9 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackingLinkDef.h +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackingLinkDef.h @@ -42,7 +42,6 @@ #pragma link C++ class o2::its::ITSGpuTrackingParamConfig + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::ITSGpuTrackingParamConfig> + ; -#pragma link C++ class o2::its::FastMultEst + ; #pragma link C++ class o2::its::FastMultEstConfig + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::FastMultEstConfig> + ; From 97af52220cb1a28a426b7dec6191e941235e52bb Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Thu, 16 Apr 2026 16:57:53 +0200 Subject: [PATCH 064/285] ITS: speedup final step in vertexer (#15279) * ITS: speedup final step in vertexer Signed-off-by: Felix Schlepper * ITS: suppress low mult 2nd vertices Signed-off-by: Felix Schlepper --------- Signed-off-by: Felix Schlepper --- .../ITSMFT/ITS/macros/test/CMakeLists.txt | 6 + .../ITSMFT/ITS/macros/test/CheckSeeding.C | 706 +++++++++++ Detectors/ITSMFT/ITS/tracking/CMakeLists.txt | 1 + .../include/ITStracking/ClusterLines.h | 6 +- .../include/ITStracking/Configuration.h | 39 +- .../include/ITStracking/LineVertexerHelpers.h | 46 + .../tracking/include/ITStracking/MathUtils.h | 10 + .../include/ITStracking/TrackingConfigParam.h | 32 +- .../include/ITStracking/VertexerTraits.h | 3 + .../ITSMFT/ITS/tracking/src/ClusterLines.cxx | 32 + .../ITSMFT/ITS/tracking/src/Configuration.cxx | 22 +- .../ITS/tracking/src/LineVertexerHelpers.cxx | 1036 +++++++++++++++++ .../ITSMFT/ITS/tracking/src/TimeFrame.cxx | 13 +- .../ITS/tracking/src/TrackingInterface.cxx | 13 +- .../ITS/tracking/src/VertexerTraits.cxx | 309 +++-- prodtests/full-system-test/dpl-workflow.sh | 16 +- prodtests/full_system_test.sh | 2 +- prodtests/sim_challenge.sh | 2 +- 18 files changed, 2121 insertions(+), 173 deletions(-) create mode 100644 Detectors/ITSMFT/ITS/macros/test/CheckSeeding.C create mode 100644 Detectors/ITSMFT/ITS/tracking/include/ITStracking/LineVertexerHelpers.h create mode 100644 Detectors/ITSMFT/ITS/tracking/src/LineVertexerHelpers.cxx diff --git a/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt b/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt index a23682b085311..ffdbdf1990a32 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt @@ -128,3 +128,9 @@ o2_add_test_root_macro(CheckStaggering.C O2::DetectorsVertexing O2::ReconstructionDataFormats LABELS its COMPILE_ONLY) + +o2_add_test_root_macro(CheckSeeding.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsITS + O2::SimulationDataFormat + O2::Steer + LABELS its COMPILE_ONLY) diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckSeeding.C b/Detectors/ITSMFT/ITS/macros/test/CheckSeeding.C new file mode 100644 index 0000000000000..915f2dda75032 --- /dev/null +++ b/Detectors/ITSMFT/ITS/macros/test/CheckSeeding.C @@ -0,0 +1,706 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DataFormatsITS/Vertex.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTrack.h" +#include "SimulationDataFormat/O2DatabasePDG.h" +#include "Steer/MCKinematicsReader.h" +#endif + +constexpr const char* tracFile = "o2trac_its.root"; +constexpr const char* collContextFile = "collisioncontext.root"; + +namespace +{ +namespace fs = std::filesystem; + +constexpr float MinPt = 0.05f; +constexpr float MaxEta = 1.1f; +constexpr int NMultiplicityBins = 11; +constexpr std::array MultiplicityLabels{{"2", "3", "4-5", "6-8", "9-13", "14-21", "22-33", "34-52", "53-83", "84-128", "128+"}}; + +struct TruthInfo { + int multiplicity = 0; + float x = 0.f; + float y = 0.f; + float z = 0.f; +}; + +struct BestRecoInfo { + o2::its::Vertex vertex; + float purity = -1.f; +}; + +struct GaussianSummary { + bool valid = false; + double mean = 0.; + double sigma = 0.; +}; + +bool isTrueVertexLabel(const o2::MCCompLabel& label) +{ + return label.isValid() && !label.isFake() && label.getSourceID() == 0; +} + +bool isChargedPrimary(const o2::MCTrack& track) +{ + if (!track.isPrimary() || track.GetPt() < MinPt || std::abs(track.GetEta()) > MaxEta) { + return false; + } + auto* pdg = o2::O2DatabasePDG::Instance()->GetParticle(track.GetPdgCode()); + return pdg != nullptr && pdg->Charge() != 0.; +} + +bool isBetterReco(const o2::its::Vertex& candidate, float candidatePurity, const o2::its::Vertex& current, float currentPurity) +{ + if (candidatePurity != currentPurity) { + return candidatePurity > currentPurity; + } + if (candidate.getNContributors() != current.getNContributors()) { + return candidate.getNContributors() > current.getNContributors(); + } + return candidate.getChi2() < current.getChi2(); +} + +int getMultiplicityCategory(int multiplicity) +{ + if (multiplicity <= 2) { + return 1; + } + if (multiplicity <= 3) { + return 2; + } + if (multiplicity <= 5) { + return 3; + } + if (multiplicity <= 8) { + return 4; + } + if (multiplicity <= 13) { + return 5; + } + if (multiplicity <= 21) { + return 6; + } + if (multiplicity <= 33) { + return 7; + } + if (multiplicity <= 52) { + return 8; + } + if (multiplicity <= 83) { + return 9; + } + if (multiplicity <= 128) { + return 10; + } + return 11; +} + +void fillMultiplicityHistogram(TH1* hist, int multiplicity) +{ + hist->Fill(getMultiplicityCategory(multiplicity)); +} + +GaussianSummary fitGaussianCore(TH1* hist, const char* funcName) +{ + if (hist == nullptr || hist->GetEntries() < 20) { + return {}; + } + const auto rms = hist->GetRMS(); + if (!(rms > 0.)) { + return {}; + } + + TF1 fit(funcName, "gaus", hist->GetMean() - 2. * rms, hist->GetMean() + 2. * rms); + fit.SetParameters(hist->GetMaximum(), hist->GetMean(), rms); + hist->Fit(&fit, "Q0R"); + + const auto mean = fit.GetParameter(1); + const auto sigma = std::abs(fit.GetParameter(2)); + if (!(sigma > 0.)) { + return {}; + } + + fit.SetRange(mean - 2. * sigma, mean + 2. * sigma); + hist->Fit(&fit, "Q0R"); + return {true, fit.GetParameter(1), std::abs(fit.GetParameter(2))}; +} + +TH1D* makeNormalizedCopy(const TH1D* source, const char* name, const char* title) +{ + auto* copy = static_cast(source->Clone(name)); + copy->SetTitle(title); + const auto integral = copy->Integral("width"); + if (integral > 0.) { + copy->Scale(1. / integral); + } + return copy; +} + +void setMultiplicityBinLabels(TH1* hist) +{ + for (int i = 0; i < NMultiplicityBins; ++i) { + hist->GetXaxis()->SetBinLabel(i + 1, MultiplicityLabels[i]); + } +} + +void setHistogramStyle(TH1* hist, int color, int marker) +{ + hist->SetLineColor(color); + hist->SetMarkerColor(color); + hist->SetMarkerStyle(marker); + hist->SetLineWidth(2); +} + +void printGaussianByMultiplicity(const std::array& summaries, const char* title) +{ + std::printf("%s:\n", title); + for (int i = 0; i < NMultiplicityBins; ++i) { + if (summaries[i].valid) { + std::printf(" %-4s : mean=%.6g sigma=%.6g\n", MultiplicityLabels[i], summaries[i].mean, summaries[i].sigma); + } else { + std::printf(" %-4s : n/a\n", MultiplicityLabels[i]); + } + } +} + +fs::path resolveContextFile(const fs::path& dir) +{ + const std::array candidates{ + dir / collContextFile, + dir.parent_path() / collContextFile, + fs::current_path() / collContextFile}; + for (const auto& candidate : candidates) { + if (!candidate.empty() && fs::exists(candidate) && fs::is_regular_file(candidate)) { + return candidate; + } + } + return {}; +} + +void printBinnedFractions(const TH1* numerator, const TH1* denominator, const char* title) +{ + if (numerator == nullptr || denominator == nullptr) { + return; + } + std::printf("%s:\n", title); + for (int iBin = 1; iBin <= denominator->GetNbinsX(); ++iBin) { + const auto den = denominator->GetBinContent(iBin); + const auto num = numerator->GetBinContent(iBin); + const auto value = den > 0. ? num / den : 0.; + std::printf(" %-4s : %.4f (%g / %g)\n", denominator->GetXaxis()->GetBinLabel(iBin), value, num, den); + } +} + +std::vector findDirs(const std::string& roots) +{ + fs::path root = roots.empty() ? fs::current_path() : fs::path{roots}; + std::vector result; + const auto hasFiles = [](const fs::path& dir) { + const auto tracPath = dir / tracFile; + return fs::exists(tracPath) && fs::is_regular_file(tracPath); + }; + + if (fs::is_directory(root) && hasFiles(root)) { + result.push_back(root); + return result; + } + + for (const auto& entry : fs::recursive_directory_iterator(root)) { + if (entry.is_directory() && hasFiles(entry.path())) { + result.push_back(entry.path()); + } + } + std::sort(result.begin(), result.end()); + return result; +} + +} // namespace + +void CheckSeeding(const std::string& dir = "") +{ + using Vertex = o2::its::Vertex; + const auto cwd = fs::current_path(); + gStyle->SetOptStat(0); + TH1::AddDirectory(kFALSE); + + auto dirs = findDirs(dir); + std::printf("Will iterate over %zu input dirs\n", dirs.size()); + if (dirs.empty()) { + std::printf("No input directories containing %s were found.\n", tracFile); + return; + } + + auto* hTruthMultiplicityFindable = new TH1D("hTruthMultiplicityFindable", + "Findable truth vertices;truth multiplicity bin;vertices", + NMultiplicityBins, 0.5, NMultiplicityBins + 0.5); + auto* hTruthMultiplicityFound = new TH1D("hTruthMultiplicityFound", + "Found truth vertices;truth multiplicity bin;vertices", + NMultiplicityBins, 0.5, NMultiplicityBins + 0.5); + auto* hRecoMultiplicityTrue = new TH1D("hRecoMultiplicityTrue", + "True reconstructed vertices;reco multiplicity bin;vertices", + NMultiplicityBins, 0.5, NMultiplicityBins + 0.5); + auto* hRecoMultiplicityFake = new TH1D("hRecoMultiplicityFake", + "Fake reconstructed vertices;reco multiplicity bin;vertices", + NMultiplicityBins, 0.5, NMultiplicityBins + 0.5); + auto* hDx = new TH1D("hDx", "Matched vertex residuals;x_{reco}-x_{MC} (cm);vertices", 400, -0.02, 0.02); + auto* hDy = new TH1D("hDy", "Matched vertex residuals;y_{reco}-y_{MC} (cm);vertices", 400, -0.02, 0.02); + auto* hDz = new TH1D("hDz", "Matched vertex residuals;z_{reco}-z_{MC} (cm);vertices", 400, -0.02, 0.02); + auto* hPullX = new TH1D("hPullX", "Matched vertex pulls;x pull;vertices", 600, -30., 30.); + auto* hPullY = new TH1D("hPullY", "Matched vertex pulls;y pull;vertices", 600, -30., 30.); + auto* hPullZ = new TH1D("hPullZ", "Matched vertex pulls;z pull;vertices", 600, -30., 30.); + std::array hPullXByMult{}; + std::array hPullYByMult{}; + std::array hPullZByMult{}; + for (int i = 0; i < NMultiplicityBins; ++i) { + const auto nameX = std::string("hPullX_") + std::to_string(i + 1); + const auto nameY = std::string("hPullY_") + std::to_string(i + 1); + const auto nameZ = std::string("hPullZ_") + std::to_string(i + 1); + const auto titleX = std::string("x pull ") + MultiplicityLabels[i] + ";x pull;vertices"; + const auto titleY = std::string("y pull ") + MultiplicityLabels[i] + ";y pull;vertices"; + const auto titleZ = std::string("z pull ") + MultiplicityLabels[i] + ";z pull;vertices"; + hPullXByMult[i] = new TH1D(nameX.c_str(), titleX.c_str(), 600, -30., 30.); + hPullYByMult[i] = new TH1D(nameY.c_str(), titleY.c_str(), 600, -30., 30.); + hPullZByMult[i] = new TH1D(nameZ.c_str(), titleZ.c_str(), 600, -30., 30.); + } + + setMultiplicityBinLabels(hTruthMultiplicityFindable); + setMultiplicityBinLabels(hTruthMultiplicityFound); + setMultiplicityBinLabels(hRecoMultiplicityTrue); + setMultiplicityBinLabels(hRecoMultiplicityFake); + setHistogramStyle(hTruthMultiplicityFindable, kGray + 2, 20); + setHistogramStyle(hTruthMultiplicityFound, kAzure + 2, 20); + setHistogramStyle(hRecoMultiplicityTrue, kAzure + 2, 20); + setHistogramStyle(hRecoMultiplicityFake, kOrange + 7, 24); + setHistogramStyle(hDx, kAzure + 2, 20); + setHistogramStyle(hDy, kGreen + 2, 21); + setHistogramStyle(hDz, kRed + 1, 24); + setHistogramStyle(hPullX, kAzure + 2, 20); + setHistogramStyle(hPullY, kGreen + 2, 21); + setHistogramStyle(hPullZ, kRed + 1, 24); + + size_t findable = 0; + size_t totalFound = 0; + size_t trueFound = 0; + size_t fakeFound = 0; + size_t uniqueTrueReco = 0; + size_t uniqueFindableFound = 0; + size_t sigmaXCount = 0; + size_t sigmaYCount = 0; + size_t sigmaZCount = 0; + double sumSigmaX = 0.; + double sumSigmaY = 0.; + double sumSigmaZ = 0.; + + for (const auto& inputDir : dirs) { + fs::current_path(inputDir); + std::printf("Working on %s\n", inputDir.c_str()); + const auto contextPath = resolveContextFile(inputDir); + if (contextPath.empty()) { + std::printf("Skipping %s: could not locate %s\n", inputDir.c_str(), collContextFile); + continue; + } + + o2::steer::MCKinematicsReader mcReader(contextPath.string()); + if (!mcReader.isInitialized()) { + std::printf("Skipping %s: failed to initialize MCKinematicsReader from %s\n", inputDir.c_str(), contextPath.c_str()); + continue; + } + + std::unordered_map findableTruths; + std::unordered_set uniqueTrueLabelsReco; + std::unordered_set uniqueFindableTruthFound; + std::unordered_map bestRecoByTruth; + + const int iSrc = 0; + const auto nEvents = static_cast(mcReader.getNEvents(iSrc)); + for (int iEve = 0; iEve < nEvents; ++iEve) { + const auto& tracks = mcReader.getTracks(iSrc, iEve); + const auto contributors = static_cast(std::count_if(tracks.begin(), tracks.end(), isChargedPrimary)); + if (contributors >= 2) { + const auto& header = mcReader.getMCEventHeader(iSrc, iEve); + findableTruths.emplace(iEve, TruthInfo{contributors, (float)header.GetX(), (float)header.GetY(), (float)header.GetZ()}); + fillMultiplicityHistogram(hTruthMultiplicityFindable, contributors); + } + mcReader.releaseTracksForSourceAndEvent(iSrc, iEve); + } + + auto* tracFileHandle = TFile::Open((inputDir / tracFile).c_str()); + if (tracFileHandle == nullptr || tracFileHandle->IsZombie()) { + std::printf("Skipping %s: failed to open %s\n", inputDir.c_str(), tracFile); + delete tracFileHandle; + continue; + } + + auto* tracTree = tracFileHandle->Get("o2sim"); + if (tracTree == nullptr) { + std::printf("Skipping %s: missing o2sim tree in %s\n", inputDir.c_str(), tracFile); + tracFileHandle->Close(); + delete tracFileHandle; + continue; + } + + if (tracTree->GetBranch("Vertices") == nullptr || tracTree->GetBranch("ITSVertexMCTruth") == nullptr) { + std::printf("Skipping %s: missing vertex branches in %s\n", inputDir.c_str(), tracFile); + tracFileHandle->Close(); + delete tracFileHandle; + continue; + } + + std::vector* vertices = nullptr; + std::vector* labels = nullptr; + std::vector* purities = nullptr; + const bool hasPurityBranch = tracTree->GetBranch("ITSVertexMCPurity") != nullptr; + + tracTree->SetBranchAddress("Vertices", &vertices); + tracTree->SetBranchAddress("ITSVertexMCTruth", &labels); + if (hasPurityBranch) { + tracTree->SetBranchAddress("ITSVertexMCPurity", &purities); + } + + const auto nEntries = tracTree->GetEntriesFast(); + for (Long64_t iEntry = 0; iEntry < nEntries; ++iEntry) { + tracTree->GetEntry(iEntry); + if (vertices == nullptr || labels == nullptr) { + continue; + } + auto nVertices = std::min(vertices->size(), labels->size()); + if (hasPurityBranch && purities != nullptr) { + nVertices = std::min(nVertices, purities->size()); + } + + for (size_t iVtx = 0; iVtx < nVertices; ++iVtx) { + const auto& vertex = (*vertices)[iVtx]; + const auto& label = (*labels)[iVtx]; + const auto multiplicity = static_cast(vertex.getNContributors()); + ++totalFound; + + if (!isTrueVertexLabel(label)) { + ++fakeFound; + fillMultiplicityHistogram(hRecoMultiplicityFake, multiplicity); + continue; + } + + ++trueFound; + const auto eventID = label.getEventID(); + uniqueTrueLabelsReco.insert(eventID); + fillMultiplicityHistogram(hRecoMultiplicityTrue, multiplicity); + + const auto truthIt = findableTruths.find(eventID); + if (truthIt == findableTruths.end()) { + continue; + } + + uniqueFindableTruthFound.insert(eventID); + const auto purity = (hasPurityBranch && purities != nullptr) ? (*purities)[iVtx] : -1.f; + const auto bestIt = bestRecoByTruth.find(eventID); + if (bestIt == bestRecoByTruth.end() || isBetterReco(vertex, purity, bestIt->second.vertex, bestIt->second.purity)) { + bestRecoByTruth[eventID] = BestRecoInfo{vertex, purity}; + } + } + } + + tracFileHandle->Close(); + delete tracFileHandle; + + findable += findableTruths.size(); + uniqueTrueReco += uniqueTrueLabelsReco.size(); + uniqueFindableFound += uniqueFindableTruthFound.size(); + + for (const auto eventID : uniqueFindableTruthFound) { + const auto truthIt = findableTruths.find(eventID); + if (truthIt != findableTruths.end()) { + fillMultiplicityHistogram(hTruthMultiplicityFound, truthIt->second.multiplicity); + } + } + + for (const auto& [eventID, reco] : bestRecoByTruth) { + const auto truthIt = findableTruths.find(eventID); + if (truthIt == findableTruths.end()) { + continue; + } + const auto dx = reco.vertex.getX() - truthIt->second.x; + const auto dy = reco.vertex.getY() - truthIt->second.y; + const auto dz = reco.vertex.getZ() - truthIt->second.z; + hDx->Fill(dx); + hDy->Fill(dy); + hDz->Fill(dz); + if (reco.vertex.getSigmaX() > 0.f) { + const auto pullX = dx / reco.vertex.getSigmaX(); + hPullX->Fill(pullX); + hPullXByMult[getMultiplicityCategory(reco.vertex.getNContributors()) - 1]->Fill(pullX); + sumSigmaX += reco.vertex.getSigmaX(); + ++sigmaXCount; + } + if (reco.vertex.getSigmaY() > 0.f) { + const auto pullY = dy / reco.vertex.getSigmaY(); + hPullY->Fill(pullY); + hPullYByMult[getMultiplicityCategory(reco.vertex.getNContributors()) - 1]->Fill(pullY); + sumSigmaY += reco.vertex.getSigmaY(); + ++sigmaYCount; + } + if (reco.vertex.getSigmaZ() > 0.f) { + const auto pullZ = dz / reco.vertex.getSigmaZ(); + hPullZ->Fill(pullZ); + hPullZByMult[getMultiplicityCategory(reco.vertex.getNContributors()) - 1]->Fill(pullZ); + sumSigmaZ += reco.vertex.getSigmaZ(); + ++sigmaZCount; + } + } + fs::current_path(cwd); + } + + auto* hTruthMultiplicityEfficiency = static_cast(hTruthMultiplicityFound->Clone("hTruthMultiplicityEfficiency")); + hTruthMultiplicityEfficiency->SetTitle("Unique efficiency vs truth multiplicity;truth multiplicity bin;efficiency"); + hTruthMultiplicityEfficiency->Divide(hTruthMultiplicityFound, hTruthMultiplicityFindable, 1., 1., "B"); + setMultiplicityBinLabels(hTruthMultiplicityEfficiency); + hTruthMultiplicityEfficiency->SetMinimum(0.); + hTruthMultiplicityEfficiency->SetMaximum(1.05); + + auto* hRecoMultiplicityTotal = static_cast(hRecoMultiplicityTrue->Clone("hRecoMultiplicityTotal")); + hRecoMultiplicityTotal->SetTitle("All reconstructed vertices;reco multiplicity bin;vertices"); + hRecoMultiplicityTotal->Add(hRecoMultiplicityFake); + setMultiplicityBinLabels(hRecoMultiplicityTotal); + + auto* hRecoMultiplicityPurity = static_cast(hRecoMultiplicityTrue->Clone("hRecoMultiplicityPurity")); + hRecoMultiplicityPurity->SetTitle("Purity vs reconstructed multiplicity;reco multiplicity bin;purity"); + hRecoMultiplicityPurity->Divide(hRecoMultiplicityTrue, hRecoMultiplicityTotal, 1., 1., "B"); + setMultiplicityBinLabels(hRecoMultiplicityPurity); + hRecoMultiplicityPurity->SetMinimum(0.); + hRecoMultiplicityPurity->SetMaximum(1.05); + + const auto duplicates = trueFound >= uniqueTrueReco ? (trueFound - uniqueTrueReco) : 0UL; + + const double uniqueEfficiency = findable > 0 ? static_cast(uniqueFindableFound) / findable : 0.; + const double purity = totalFound > 0 ? static_cast(trueFound) / totalFound : 0.; + const double fakeRate = totalFound > 0 ? static_cast(fakeFound) / totalFound : 0.; + const double duplicateRate = trueFound > 0 ? static_cast(duplicates) / trueFound : 0.; + const double f1 = (uniqueEfficiency + purity) > 0. ? 2. * uniqueEfficiency * purity / (uniqueEfficiency + purity) : 0.; + + const auto dxFit = fitGaussianCore(hDx, "fitDx"); + const auto dyFit = fitGaussianCore(hDy, "fitDy"); + const auto dzFit = fitGaussianCore(hDz, "fitDz"); + const auto pullXFit = fitGaussianCore(hPullX, "fitPullX"); + const auto pullYFit = fitGaussianCore(hPullY, "fitPullY"); + const auto pullZFit = fitGaussianCore(hPullZ, "fitPullZ"); + std::array pullXByMultFit{}; + std::array pullYByMultFit{}; + std::array pullZByMultFit{}; + for (int i = 0; i < NMultiplicityBins; ++i) { + const auto fitX = std::string("fitPullX_") + std::to_string(i + 1); + const auto fitY = std::string("fitPullY_") + std::to_string(i + 1); + const auto fitZ = std::string("fitPullZ_") + std::to_string(i + 1); + pullXByMultFit[i] = fitGaussianCore(hPullXByMult[i], fitX.c_str()); + pullYByMultFit[i] = fitGaussianCore(hPullYByMult[i], fitY.c_str()); + pullZByMultFit[i] = fitGaussianCore(hPullZByMult[i], fitZ.c_str()); + } + + std::printf("\nVertex validation summary\n"); + std::printf(" findable truth vertices : %zu\n", findable); + std::printf(" total reconstructed vertices : %zu\n", totalFound); + std::printf(" true reconstructed vertices : %zu\n", trueFound); + std::printf(" fake reconstructed vertices : %zu\n", fakeFound); + std::printf(" unique true labels (all) : %zu\n", uniqueTrueReco); + std::printf(" unique findable truth found : %zu\n", uniqueFindableFound); + std::printf(" unique efficiency : %.5f\n", uniqueEfficiency); + std::printf(" purity : %.5f\n", purity); + std::printf(" fake rate : %.5f\n", fakeRate); + std::printf(" duplicate rate : %.5f\n", duplicateRate); + std::printf(" F1(purity,efficiency) : %.5f\n", f1); + std::printf(" mean reported sigma x/y/z : %.6g / %.6g / %.6g cm\n", + sigmaXCount > 0 ? sumSigmaX / sigmaXCount : 0., + sigmaYCount > 0 ? sumSigmaY / sigmaYCount : 0., + sigmaZCount > 0 ? sumSigmaZ / sigmaZCount : 0.); + + if (dxFit.valid) { + std::printf(" x residual Gaussian: mean=%.6g cm sigma=%.6g cm\n", dxFit.mean, dxFit.sigma); + } + if (dyFit.valid) { + std::printf(" y residual Gaussian: mean=%.6g cm sigma=%.6g cm\n", dyFit.mean, dyFit.sigma); + } + if (dzFit.valid) { + std::printf(" z residual Gaussian: mean=%.6g cm sigma=%.6g cm\n", dzFit.mean, dzFit.sigma); + } + if (pullXFit.valid) { + std::printf(" x pull Gaussian : mean=%.6g sigma=%.6g\n", pullXFit.mean, pullXFit.sigma); + } + if (pullYFit.valid) { + std::printf(" y pull Gaussian : mean=%.6g sigma=%.6g\n", pullYFit.mean, pullYFit.sigma); + } + if (pullZFit.valid) { + std::printf(" z pull Gaussian : mean=%.6g sigma=%.6g\n", pullZFit.mean, pullZFit.sigma); + } + printGaussianByMultiplicity(pullXByMultFit, "x pull Gaussian by reconstructed multiplicity"); + printGaussianByMultiplicity(pullYByMultFit, "y pull Gaussian by reconstructed multiplicity"); + printGaussianByMultiplicity(pullZByMultFit, "z pull Gaussian by reconstructed multiplicity"); + + printBinnedFractions(hTruthMultiplicityFound, hTruthMultiplicityFindable, "Efficiency vs truth multiplicity"); + printBinnedFractions(hRecoMultiplicityTrue, hRecoMultiplicityTotal, "Purity vs reconstructed multiplicity"); + + auto* cValidation = new TCanvas("cVertexValidation", "Vertex validation summary", 1800, 1000); + cValidation->Divide(3, 2); + + cValidation->cd(1); + gPad->SetMargin(0.05, 0.05, 0.05, 0.05); + auto* summary = new TPaveText(0.02, 0.02, 0.98, 0.98, "NDC"); + summary->SetBorderSize(0); + summary->SetFillColor(0); + summary->SetTextAlign(12); + summary->SetTextFont(42); + char line[256]; + std::snprintf(line, sizeof(line), "Findable truth vertices : %zu", findable); + summary->AddText(line); + std::snprintf(line, sizeof(line), "Total reconstructed : %zu", totalFound); + summary->AddText(line); + std::snprintf(line, sizeof(line), "True reconstructed : %zu", trueFound); + summary->AddText(line); + std::snprintf(line, sizeof(line), "Fake reconstructed : %zu", fakeFound); + summary->AddText(line); + summary->AddText(""); + std::snprintf(line, sizeof(line), "Unique truth found : %zu", uniqueFindableFound); + summary->AddText(line); + std::snprintf(line, sizeof(line), "Unique efficiency : %.5f", uniqueEfficiency); + summary->AddText(line); + std::snprintf(line, sizeof(line), "Purity : %.5f", purity); + summary->AddText(line); + std::snprintf(line, sizeof(line), "Fake rate : %.5f", fakeRate); + summary->AddText(line); + std::snprintf(line, sizeof(line), "Duplicate rate : %.5f", duplicateRate); + summary->AddText(line); + std::snprintf(line, sizeof(line), "F1 : %.5f", f1); + summary->AddText(line); + std::snprintf(line, sizeof(line), "mean sigma x/y/z cm : %.3g / %.3g / %.3g", + sigmaXCount > 0 ? sumSigmaX / sigmaXCount : 0., + sigmaYCount > 0 ? sumSigmaY / sigmaYCount : 0., + sigmaZCount > 0 ? sumSigmaZ / sigmaZCount : 0.); + summary->AddText(line); + summary->AddText(""); + if (dxFit.valid) { + std::snprintf(line, sizeof(line), "dx fit mean/sigma cm : %.3g / %.3g", dxFit.mean, dxFit.sigma); + summary->AddText(line); + } + if (dyFit.valid) { + std::snprintf(line, sizeof(line), "dy fit mean/sigma cm : %.3g / %.3g", dyFit.mean, dyFit.sigma); + summary->AddText(line); + } + if (dzFit.valid) { + std::snprintf(line, sizeof(line), "dz fit mean/sigma cm : %.3g / %.3g", dzFit.mean, dzFit.sigma); + summary->AddText(line); + } + if (pullXFit.valid) { + std::snprintf(line, sizeof(line), "pull x sigma : %.3g", pullXFit.sigma); + summary->AddText(line); + } + if (pullYFit.valid) { + std::snprintf(line, sizeof(line), "pull y sigma : %.3g", pullYFit.sigma); + summary->AddText(line); + } + if (pullZFit.valid) { + std::snprintf(line, sizeof(line), "pull z sigma : %.3g", pullZFit.sigma); + summary->AddText(line); + } + summary->Draw(); + + cValidation->cd(2); + gPad->SetGridy(); + hTruthMultiplicityEfficiency->Draw("hist e1"); + + cValidation->cd(3); + gPad->SetGridy(); + const auto maxReco = std::max(hRecoMultiplicityTrue->GetMaximum(), hRecoMultiplicityFake->GetMaximum()); + hRecoMultiplicityTrue->SetMaximum(1.2 * std::max(1., maxReco)); + hRecoMultiplicityTrue->Draw("hist e1"); + hRecoMultiplicityFake->Draw("hist e1 same"); + auto hRecoMultiplicitySum = (TH1D*)hRecoMultiplicityTrue->Clone("hRecoMultiplicitySum"); + hRecoMultiplicitySum->Add(hRecoMultiplicityFake); + setHistogramStyle(hRecoMultiplicitySum, kBlack, 23); + hRecoMultiplicitySum->Draw("hist e1 same"); + { + auto* legend = new TLegend(0.58, 0.75, 0.88, 0.88); + legend->SetBorderSize(0); + legend->AddEntry(hRecoMultiplicityTrue, "true", "lep"); + legend->AddEntry(hRecoMultiplicityFake, "fake", "lep"); + legend->AddEntry(hRecoMultiplicitySum, "sum", "lep"); + legend->Draw(); + } + + cValidation->cd(4); + gPad->SetGridy(); + hRecoMultiplicityPurity->Draw("hist e1"); + + cValidation->cd(5); + gPad->SetGridy(); + auto* hDxNorm = makeNormalizedCopy(hDx, "hDxNorm", "Matched vertex residuals;residual (cm);normalized entries"); + auto* hDyNorm = makeNormalizedCopy(hDy, "hDyNorm", "Matched vertex residuals;residual (cm);normalized entries"); + auto* hDzNorm = makeNormalizedCopy(hDz, "hDzNorm", "Matched vertex residuals;residual (cm);normalized entries"); + const auto maxResidual = std::max({hDxNorm->GetMaximum(), hDyNorm->GetMaximum(), hDzNorm->GetMaximum()}); + hDzNorm->SetMaximum(1.2 * std::max(1., maxResidual)); + hDzNorm->Draw("hist"); + hDxNorm->Draw("hist same"); + hDyNorm->Draw("hist same"); + { + auto* legend = new TLegend(0.62, 0.72, 0.88, 0.88); + legend->SetBorderSize(0); + legend->AddEntry(hDxNorm, "dx", "l"); + legend->AddEntry(hDyNorm, "dy", "l"); + legend->AddEntry(hDzNorm, "dz", "l"); + legend->Draw(); + } + + cValidation->cd(6); + gPad->SetGridy(); + auto* hPullXNorm = makeNormalizedCopy(hPullX, "hPullXNorm", "Matched vertex pulls;pull;normalized entries"); + auto* hPullYNorm = makeNormalizedCopy(hPullY, "hPullYNorm", "Matched vertex pulls;pull;normalized entries"); + auto* hPullZNorm = makeNormalizedCopy(hPullZ, "hPullZNorm", "Matched vertex pulls;pull;normalized entries"); + const auto maxPull = std::max({hPullXNorm->GetMaximum(), hPullYNorm->GetMaximum(), hPullZNorm->GetMaximum()}); + hPullZNorm->SetMaximum(1.2 * std::max(1., maxPull)); + hPullZNorm->Draw("hist"); + hPullXNorm->Draw("hist same"); + hPullYNorm->Draw("hist same"); + { + auto* legend = new TLegend(0.62, 0.72, 0.88, 0.88); + legend->SetBorderSize(0); + legend->AddEntry(hPullXNorm, "pull x", "l"); + legend->AddEntry(hPullYNorm, "pull y", "l"); + legend->AddEntry(hPullZNorm, "pull z", "l"); + legend->Draw(); + } + + cValidation->cd(); + cValidation->Update(); + cValidation->SaveAs("checkSeeding.pdf"); +} diff --git a/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt index c9c5196da617b..1dd64b6f1874b 100644 --- a/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt @@ -16,6 +16,7 @@ o2_add_library(ITStracking src/Configuration.cxx src/FastMultEstConfig.cxx src/FastMultEst.cxx + src/LineVertexerHelpers.cxx src/TimeFrame.cxx src/IOUtils.cxx src/Tracker.cxx diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h index 6fbc6d7da7721..bcb8a98a62cab 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h @@ -12,8 +12,9 @@ #ifndef O2_ITS_CLUSTERLINES_H #define O2_ITS_CLUSTERLINES_H -#include +#include #include +#include #include #include #include "ITStracking/Cluster.h" @@ -59,6 +60,7 @@ class ClusterLines final 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); @@ -67,7 +69,7 @@ class ClusterLines final const float* getRMS2() const { return mRMS2.Array(); } float getAvgDistance2() const { return mAvgDistance2; } auto getSize() const noexcept { return mLabels.size(); } - auto& getLabels() noexcept { return mLabels; } + 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]); } diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h index 02dbeb8cf3992..1f55a95ca0d65 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h @@ -18,11 +18,9 @@ #include #ifndef GPUCA_GPUCODE_DEVICE -#include #include #include #include -#include #endif #include "DetectorsBase/Propagator.h" @@ -89,21 +87,28 @@ struct VertexingParameters { int nIterations = 1; // Number of vertexing passes to perform 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 ZBins{1}; - int PhiBins{128}; - float zCut = 0.002f; - float phiCut = 0.005f; - float pairCut = 0.04f; - float clusterCut = 0.8f; - float histPairCut = 0.04f; - float tanLambdaCut = 0.002f; // tanLambda = deltaZ/deltaR - float lowMultBeamDistCut = 0.1f; // XY cut for low-multiplicity pile up - int vertNsigmaCut = 6; // N sigma cut for vertex XY - float vertRadiusSigma = 0.33f; // sigma of vertex XY - float trackletSigma = 0.01f; // tracklet to vertex sigma - float maxZPositionAllowed = 25.f; - int clusterContributorsCut = 16; - int maxTrackletsPerCluster = 2e3; + int ZBins = 1; + int PhiBins = 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 vertNsigmaCut = -1.f; + float vertRadiusSigma = -1.f; + float trackletSigma = -1.f; + 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; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/LineVertexerHelpers.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/LineVertexerHelpers.h new file mode 100644 index 0000000000000..0e3807aba8efb --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/LineVertexerHelpers.h @@ -0,0 +1,46 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_ITS_TRACKING_LINE_VERTEXER_HELPERS_H_ +#define O2_ITS_TRACKING_LINE_VERTEXER_HELPERS_H_ + +#include +#include + +#include "ITStracking/BoundedAllocator.h" +#include "ITStracking/ClusterLines.h" + +namespace o2::its::line_vertexer +{ + +struct Settings { + float beamX = 0.f; + float beamY = 0.f; + float pairCut = 0.f; + float pairCut2 = 0.f; + float clusterCut = 0.f; + float coarseZWindow = 0.f; + float seedDedupZCut = 0.f; + float refitDedupZCut = 0.f; + float duplicateZCut = 0.f; + float duplicateDistance2Cut = 0.f; + float finalSelectionZCut = 0.f; + float maxZ = 0.f; + int seedMemberRadiusTime = 1; + int seedMemberRadiusZ = 2; + std::shared_ptr memoryPool; +}; + +bounded_vector buildClusters(std::span lines, const Settings& settings); + +} // namespace o2::its::line_vertexer + +#endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/MathUtils.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/MathUtils.h index 95e0b4554e32c..ab3c7d5d29873 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/MathUtils.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/MathUtils.h @@ -94,6 +94,16 @@ 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); diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h index e77200a1432d1..cb291b46f5e44 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h @@ -24,24 +24,30 @@ struct VertexerParamConfig : public o2::conf::ConfigurableParamHelper50% + elem.setFakeFlag(); + } return std::make_pair(elem, static_cast(maxCount) / static_cast(elements.size())); } diff --git a/Detectors/ITSMFT/ITS/tracking/src/ClusterLines.cxx b/Detectors/ITSMFT/ITS/tracking/src/ClusterLines.cxx index f561fe0436c4a..3e3e1b8b46338 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/ClusterLines.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/ClusterLines.cxx @@ -148,6 +148,38 @@ ClusterLines::ClusterLines(const int firstLabel, const Line& firstLine, const in 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; diff --git a/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx b/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx index c447bb6bcc880..6c88b61f2df07 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx @@ -45,8 +45,8 @@ std::string TrackingParameters::asString() const } if (!AddTimeError.empty()) { str += " AddTimeError:"; - for (size_t i = 0; i < AddTimeError.size(); i++) { - str += std::format("{} ", AddTimeError[i]); + for (unsigned int i : AddTimeError) { + str += std::format("{} ", i); } } if (std::numeric_limits::max() != MaxMemory) { @@ -57,7 +57,8 @@ std::string TrackingParameters::asString() const std::string VertexingParameters::asString() const { - std::string str = std::format("NZb:{} NPhB:{} ClsCont:{} MaxTrkltCls:{} ZCut:{} PhCut:{}", ZBins, PhiBins, clusterContributorsCut, maxTrackletsPerCluster, zCut, phiCut); + std::string str = std::format("NZb:{} NPhB:{} MinVtxCont:{} SupLowMultDebris:{} MaxTrkltCls:{} ZCut:{} PhCut:{} PairCut:{} ClCut:{} SeedRad:{}x{}", + ZBins, PhiBins, clusterContributorsCut, suppressLowMultDebris, maxTrackletsPerCluster, zCut, phiCut, pairCut, clusterCut, seedMemberRadiusTime, seedMemberRadiusZ); if (std::numeric_limits::max() != MaxMemory) { str += std::format(" MemLimit {:.2f} GB", double(MaxMemory) / constants::GB); } @@ -173,8 +174,8 @@ std::vector TrackingMode::getTrackingParameters(TrackingMode LOGP(fatal, "Unsupported ITS tracking mode {} ", toString(mode)); } - float bFactor = std::abs(o2::base::Propagator::Instance()->getNominalBz()) / 5.0066791; - float bFactorTracklets = bFactor < 0.01 ? 1. : bFactor; // for tracklets only + float bFactor = std::abs(o2::base::Propagator::Instance()->getNominalBz()) / 5.0066791f; + float bFactorTracklets = bFactor < 0.01f ? 1.f : bFactor; // for tracklets only // global parameters set for every iteration for (auto& p : trackParams) { @@ -262,6 +263,9 @@ std::vector TrackingMode::getVertexingParameters(TrackingMo p.trackletSigma = vc.trackletSigma; p.maxZPositionAllowed = vc.maxZPositionAllowed; p.clusterContributorsCut = vc.clusterContributorsCut; + p.suppressLowMultDebris = vc.suppressLowMultDebris; + p.seedMemberRadiusTime = vc.seedMemberRadiusTime; + p.seedMemberRadiusZ = vc.seedMemberRadiusZ; p.phiSpan = vc.phiSpan; p.nThreads = vc.nThreads; p.ZBins = vc.ZBins; @@ -273,12 +277,16 @@ std::vector TrackingMode::getVertexingParameters(TrackingMo vertParams[0].vertNsigmaCut = vc.vertNsigmaCut; vertParams[0].vertRadiusSigma = vc.vertRadiusSigma; vertParams[0].maxTrackletsPerCluster = vc.maxTrackletsPerCluster; - vertParams[0].lowMultBeamDistCut = vc.lowMultBeamDistCut; vertParams[0].zCut = vc.zCut; vertParams[0].phiCut = vc.phiCut; vertParams[0].pairCut = vc.pairCut; vertParams[0].clusterCut = vc.clusterCut; - vertParams[0].histPairCut = vc.histPairCut; + vertParams[0].coarseZWindow = vc.coarseZWindow; + vertParams[0].seedDedupZCut = vc.seedDedupZCut; + vertParams[0].refitDedupZCut = vc.refitDedupZCut; + vertParams[0].duplicateZCut = vc.duplicateZCut; + vertParams[0].finalSelectionZCut = vc.finalSelectionZCut; + vertParams[0].duplicateDistance2Cut = vc.duplicateDistance2Cut; vertParams[0].tanLambdaCut = vc.tanLambdaCut; return vertParams; diff --git a/Detectors/ITSMFT/ITS/tracking/src/LineVertexerHelpers.cxx b/Detectors/ITSMFT/ITS/tracking/src/LineVertexerHelpers.cxx new file mode 100644 index 0000000000000..592c22dedf347 --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/src/LineVertexerHelpers.cxx @@ -0,0 +1,1036 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ITStracking/Constants.h" +#include "ITStracking/MathUtils.h" +#include "ITStracking/LineVertexerHelpers.h" + +namespace o2::its::line_vertexer +{ +namespace +{ +using SymMatrix3 = ROOT::Math::SMatrix>; +using SVector3 = ROOT::Math::SVector; + +constexpr float TukeyC = 4.685f; +constexpr float TukeyC2 = TukeyC * TukeyC; +constexpr float InitialScale2 = 5.f; +constexpr float MinScale2 = 1.f; +constexpr float MedianToSigma = 1.4826f; +constexpr float VertexShiftZTol = 0.01f; +constexpr float VertexShiftR2Tol = 1.e-4f; +constexpr int MaxFitIterations = 10; +constexpr int MaxSeedsPerCluster = 32; +constexpr float MinRelativePeakSupport = 0.1f; +constexpr int MaxHistogramBins = 0x7fff; +constexpr float TieTolerance = 1e-5f; + +struct LineRef { + LineRef(const Line& line, const int index, const float beamX, const float beamY, const float maxZ) : lineIndex(index) + { + const auto symTime = line.mTime.makeSymmetrical(); + tCenter = symTime.getTimeStamp(); + tHalfWidth = symTime.getTimeStampError(); + const auto dx = line.originPoint(0) - beamX; + const auto dy = line.originPoint(1) - beamY; + const auto ux = line.cosinesDirector(0); + const auto uy = line.cosinesDirector(1); + const auto uz = line.cosinesDirector(2); + const auto den = math_utils::SqSum(ux, uy); + if (den <= constants::Tolerance) { + lineIndex = constants::UnusedIndex; + return; + } + const auto s0 = -((dx * ux) + (dy * uy)) / den; + const auto xb = dx + (s0 * ux); + const auto yb = dy + (s0 * uy); + zBeam = line.originPoint(2) + s0 * uz; + if (!std::isfinite(zBeam) || o2::gpu::CAMath::Abs(zBeam) > maxZ) { + lineIndex = constants::UnusedIndex; + } + } + bool isDead() const noexcept { return lineIndex == constants::UnusedIndex; } + + int lineIndex = constants::UnusedIndex; + float zBeam = 0.f; + float tCenter = 0.f; + float tHalfWidth = 0.f; +}; + +struct VertexSeed { + explicit VertexSeed(const std::shared_ptr& mr) : contributors(mr.get()), assigned(mr.get()) {} + + std::array vertex = {}; + TimeEstBC time; + float scale2 = InitialScale2; + bounded_vector contributors; + bounded_vector assigned; + bool valid = false; + bool isUsableSeed() const noexcept + { + return valid && contributors.size() >= 2; + } +}; + +void compactSeeds(bounded_vector& seeds) +{ + seeds.erase(std::remove_if(seeds.begin(), seeds.end(), [](const VertexSeed& seed) { + return !seed.isUsableSeed(); + }), + seeds.end()); +} + +struct Histogram2D { + explicit Histogram2D(const std::shared_ptr& mr) : bins(mr.get()) {} + + int nTimeBins = 0; + int nZBins = 0; + float timeMin = 0.f; + float zMin = 0.f; + float timeBinSize = 1.f; + float zBinSize = 1.f; + bounded_vector bins; + + int getIndex(const int tBin, const int zBin) const noexcept + { + return (tBin * nZBins) + zBin; + } + + std::pair decodeIndex(const int index) const noexcept + { + return {index / nZBins, index % nZBins}; + } + + int getTimeBin(const float time) const noexcept + { + if (time < timeMin) { + return -1; + } + const auto bin = static_cast((time - timeMin) / timeBinSize); + return (bin >= 0 && bin < nTimeBins) ? bin : -1; + } + + int getZBin(const float z) const noexcept + { + if (z < zMin) { + return -1; + } + const auto bin = static_cast((z - zMin) / zBinSize); + return (bin >= 0 && bin < nZBins) ? bin : -1; + } + + void fill(const float time, const float z, const float weight) noexcept + { + const auto tBin = getTimeBin(time); + const auto zBin = getZBin(z); + if (tBin < 0 || zBin < 0) { + return; + } + bins[getIndex(tBin, zBin)] += weight; + } + + int findPeakBin() const noexcept + { + float bestWeight = 0.f; + int bestIndex = -1; + for (int index = 0; index < static_cast(bins.size()); ++index) { + if (bins[index] > bestWeight) { + bestWeight = bins[index]; + bestIndex = index; + } + } + return bestIndex; + } + + void suppressBin(const int index) noexcept + { + if (index >= 0 && index < static_cast(bins.size())) { + bins[index] = -1.f; + } + } + + void suppressNeighborhood(const int index, const int radiusTime, const int radiusZ) noexcept + { + if (index < 0) { + return; + } + const auto [tBin, zBin] = decodeIndex(index); + for (int dt = -radiusTime; dt <= radiusTime; ++dt) { + const auto tt = tBin + dt; + if (tt < 0 || tt >= nTimeBins) { + continue; + } + for (int dz = -radiusZ; dz <= radiusZ; ++dz) { + const auto zz = zBin + dz; + if (zz < 0 || zz >= nZBins) { + continue; + } + bins[getIndex(tt, zz)] = -1.f; + } + } + } + + float getNeighborhoodSum(const int index, const int radiusTime, const int radiusZ) const noexcept + { + if (index < 0) { + return 0.f; + } + const auto [tBin, zBin] = decodeIndex(index); + float sum = 0.f; + for (int dt = -radiusTime; dt <= radiusTime; ++dt) { + const auto tt = tBin + dt; + if (tt < 0 || tt >= nTimeBins) { + continue; + } + for (int dz = -radiusZ; dz <= radiusZ; ++dz) { + const auto zz = zBin + dz; + if (zz < 0 || zz >= nZBins) { + continue; + } + const auto value = bins[getIndex(tt, zz)]; + if (value > 0.f) { + sum += value; + } + } + } + return sum; + } + + float getTimeBinCenter(const int tBin) const noexcept + { + return timeMin + ((static_cast(tBin) + 0.5f) * timeBinSize); + } + + float getZBinCenter(const int zBin) const noexcept + { + return zMin + ((static_cast(zBin) + 0.5f) * zBinSize); + } + + TimeEstBC getTimeInterval(const int tBin) const noexcept + { + const auto lowFloat = timeMin + (static_cast(tBin) * timeBinSize); + const auto highFloat = lowFloat + timeBinSize; + const auto low = std::max(0., std::floor(lowFloat)); + const auto high = std::max(low + 1., (double)std::ceil(highFloat)); + constexpr auto maxTS = std::numeric_limits::max(); + const auto clampedLow = std::min(low, maxTS - 1.); + const auto width = std::min(high - clampedLow, std::numeric_limits::max()); + return {static_cast(clampedLow), static_cast(std::max(1., width))}; + } + + TimeEstBC getTimeNeighborhoodInterval(const int tBin, const int radius) const noexcept + { + const auto lowBin = std::max(0, tBin - radius); + const auto highBin = std::min(nTimeBins - 1, tBin + radius); + const auto lowFloat = timeMin + (static_cast(lowBin) * timeBinSize); + const auto highFloat = timeMin + (static_cast(highBin + 1) * timeBinSize); + const auto low = std::max(0., std::floor(lowFloat)); + const auto high = std::max(low + 1., (double)std::ceil(highFloat)); + constexpr auto maxTS = std::numeric_limits::max(); + const auto clampedLow = std::min(low, maxTS - 1.); + const auto width = std::min(high - clampedLow, std::numeric_limits::max()); + return {static_cast(clampedLow), static_cast(std::max(1., width))}; + } +}; + +class SeedHistogram +{ + public: + SeedHistogram(std::span members, + std::span lineRefs, + std::span lines, + const Settings& settings) + : mMembers(members), mLineRefs(lineRefs), mSeedMemberRadiusTime(settings.seedMemberRadiusTime), mSeedMemberRadiusZ(settings.seedMemberRadiusZ), mMemoryPool(settings.memoryPool), mHistogram(mMemoryPool) + { + const auto zBinSize = 0.25f * settings.clusterCut; + const auto timeBinSize = medianTimeError(lines); + + float minZ = std::numeric_limits::max(); + float maxZ = std::numeric_limits::lowest(); + float minTime = std::numeric_limits::max(); + float maxTime = std::numeric_limits::lowest(); + for (const auto lineRefIdx : mMembers) { + minZ = std::min(minZ, mLineRefs[lineRefIdx].zBeam); + maxZ = std::max(maxZ, mLineRefs[lineRefIdx].zBeam); + minTime = std::min(minTime, mLineRefs[lineRefIdx].tCenter); + maxTime = std::max(maxTime, mLineRefs[lineRefIdx].tCenter); + } + + const auto dz = std::max(0.f, maxZ - minZ); + const auto dt = std::max(0.f, maxTime - minTime); + mHistogram.nZBins = 1 + static_cast(dz / zBinSize); + mHistogram.nTimeBins = 1 + static_cast(dt / timeBinSize); + if (mHistogram.nTimeBins * mHistogram.nZBins > MaxHistogramBins) { + if (mHistogram.nTimeBins > mHistogram.nZBins) { + mHistogram.nTimeBins = std::max(1, (MaxHistogramBins - 1) / std::max(1, mHistogram.nZBins)); + } else { + mHistogram.nZBins = std::max(1, (MaxHistogramBins - 1) / std::max(1, mHistogram.nTimeBins)); + } + } + + mHistogram.timeBinSize = std::max(timeBinSize, dt / (float)std::max(1, mHistogram.nTimeBins)); + mHistogram.zBinSize = std::max(zBinSize, dz / (float)std::max(1, mHistogram.nZBins)); + const auto paddedTime = 0.5f * ((float)mHistogram.nTimeBins * mHistogram.timeBinSize - dt); + const auto paddedZ = 0.5f * ((float)mHistogram.nZBins * mHistogram.zBinSize - dz); + mHistogram.timeMin = minTime - paddedTime; + mHistogram.zMin = minZ - paddedZ; + mHistogram.bins.assign((size_t)mHistogram.nTimeBins * (size_t)mHistogram.nZBins, 0.f); + + for (const auto lineRefIdx : mMembers) { + mHistogram.fill(mLineRefs[lineRefIdx].tCenter, mLineRefs[lineRefIdx].zBeam, 1.f); + } + } + + int findPeakBin() const noexcept + { + return mHistogram.findPeakBin(); + } + + float getPeakSupport(const int peakIndex) const noexcept + { + return mHistogram.getNeighborhoodSum(peakIndex, mSeedMemberRadiusTime, mSeedMemberRadiusZ); + } + + bounded_vector collectLocalMembers(const int peakIndex, const int radiusTime, const int radiusZ) const + { + bounded_vector localMembers(mMemoryPool.get()); + localMembers.reserve(mMembers.size()); + const auto [timeBin, zBin] = mHistogram.decodeIndex(peakIndex); + for (const auto lineRefIdx : mMembers) { + const auto memberTimeBin = mHistogram.getTimeBin(mLineRefs[lineRefIdx].tCenter); + const auto memberZBin = mHistogram.getZBin(mLineRefs[lineRefIdx].zBeam); + if (memberTimeBin < 0 || memberZBin < 0) { + continue; + } + if (o2::gpu::GPUCommonMath::Abs(memberTimeBin - timeBin) > radiusTime) { + continue; + } + if (o2::gpu::GPUCommonMath::Abs(memberZBin - zBin) > radiusZ) { + continue; + } + localMembers.push_back(lineRefIdx); + } + return localMembers; + } + + TimeEstBC getPeakTimeInterval(const int peakIndex, const int radius = 0) const noexcept + { + return mHistogram.getTimeNeighborhoodInterval(mHistogram.decodeIndex(peakIndex).first, radius); + } + + float getPeakZCenter(const int peakIndex) const noexcept + { + return mHistogram.getZBinCenter(mHistogram.decodeIndex(peakIndex).second); + } + + void suppressPeak(const int peakIndex) noexcept + { + mHistogram.suppressBin(peakIndex); + } + + void suppressPeakNeighborhood(const int peakIndex) noexcept + { + mHistogram.suppressNeighborhood(peakIndex, mSeedMemberRadiusTime, mSeedMemberRadiusZ); + } + + private: + float medianTimeError(std::span lines) const + { + bounded_vector errors(mMemoryPool.get()); + errors.reserve(mMembers.size()); + for (const auto lineRefIdx : mMembers) { + errors.push_back(static_cast(lines[mLineRefs[lineRefIdx].lineIndex].mTime.getTimeStampError())); + } + std::sort(errors.begin(), errors.end()); + return errors.empty() ? 1.f : std::max(1.f, errors[errors.size() / 2]); + } + + std::span mMembers; + std::span mLineRefs; + int mSeedMemberRadiusTime = 1; + int mSeedMemberRadiusZ = 2; + std::shared_ptr mMemoryPool; + Histogram2D mHistogram; +}; + +float updateScale2(const std::span chi2s, const std::shared_ptr& mr) noexcept +{ + if (chi2s.empty()) { + return MinScale2; + } + + bounded_vector sorted(chi2s.begin(), chi2s.end(), mr.get()); + std::sort(sorted.begin(), sorted.end()); + const auto median = sorted[sorted.size() / 2]; + + for (auto& value : sorted) { + value = o2::gpu::GPUCommonMath::Abs(value - median); + } + std::sort(sorted.begin(), sorted.end()); + const auto mad = sorted[sorted.size() / 2]; + if (!std::isfinite(mad) || mad <= constants::Tolerance) { + return MinScale2; + } + return std::max(MinScale2, MedianToSigma * mad); +} + +class VertexFit +{ + public: + void add(const Line& line, const float weight) noexcept + { + const auto& direction = line.cosinesDirector; + const auto& origin = line.originPoint; + const auto det = ROOT::Math::Dot(direction, direction); + if (det <= constants::Tolerance) { + return; + } + + for (int i = 0; i < 3; ++i) { + for (int j = i; j < 3; ++j) { + mMatrix(i, j) += weight * (((i == j ? det : 0.f) - direction(i) * direction(j)) / det); + } + } + + const auto dDotO = ROOT::Math::Dot(direction, origin); + for (int i = 0; i < 3; ++i) { + mRhs(i) += weight * ((direction(i) * dDotO - det * origin(i)) / det); + } + } + + bool solve(std::array& vertexOut) const noexcept + { + SymMatrix3 inv{mMatrix}; + if (!inv.InvertFast()) { + return false; + } + const auto solution = inv * mRhs; + vertexOut[0] = static_cast(-solution(0)); + vertexOut[1] = static_cast(-solution(1)); + vertexOut[2] = static_cast(-solution(2)); + return std::isfinite(vertexOut[0]) && std::isfinite(vertexOut[1]) && std::isfinite(vertexOut[2]); + } + + private: + SymMatrix3 mMatrix; + SVector3 mRhs; +}; + +VertexSeed fitSeed(const VertexSeed& initialSeed, + std::span members, + std::span lineRefs, + std::span lines, + const std::shared_ptr& mr, + const float pairCut2) +{ + VertexSeed seed{mr}; + seed.vertex = initialSeed.vertex; + seed.time = initialSeed.time; + seed.scale2 = initialSeed.scale2; + seed.valid = false; + seed.contributors.clear(); + seed.assigned.clear(); + if (members.size() < 2) { + return seed; + } + + for (int iteration = 0; iteration < MaxFitIterations; ++iteration) { + VertexFit vertexFit; + TimeEstBC commonTime{}; + bool hasCommonTime = false; + bounded_vector contributors{mr.get()}; + const auto scale2 = std::max(seed.scale2, MinScale2); + const auto tukeyFactor = 1.f / (scale2 * TukeyC2); + + for (const auto lineRefIdx : members) { + const auto lineIdx = lineRefs[lineRefIdx].lineIndex; + const auto& line = lines[lineIdx]; + if (!line.mTime.isCompatible(seed.time)) { + continue; + } + if (hasCommonTime && !line.mTime.isCompatible(commonTime)) { + continue; + } + + const auto chi2 = Line::getDistance2FromPoint(line, seed.vertex) / pairCut2; + auto weight = 1.f - (chi2 * tukeyFactor); + if (weight <= 0.f) { + continue; + } + weight *= weight; + + if (!hasCommonTime) { + commonTime = line.mTime; + hasCommonTime = true; + } else { + commonTime += line.mTime; + } + + contributors.push_back(lineRefIdx); + vertexFit.add(line, weight); + } + + if (!hasCommonTime || contributors.size() < 2) { + return seed; + } + + std::sort(contributors.begin(), contributors.end()); + + std::array updatedVertex{}; + if (!vertexFit.solve(updatedVertex)) { + return seed; + } + + const auto sameContributors = contributors == seed.contributors; + const auto dz = o2::gpu::GPUCommonMath::Abs(updatedVertex[2] - seed.vertex[2]); + const auto oldR2 = (seed.vertex[0] * seed.vertex[0]) + (seed.vertex[1] * seed.vertex[1]); + const auto newR2 = (updatedVertex[0] * updatedVertex[0]) + (updatedVertex[1] * updatedVertex[1]); + const auto dr2 = o2::gpu::GPUCommonMath::Abs(newR2 - oldR2); + + seed.vertex = updatedVertex; + seed.time = commonTime; + bounded_vector updatedChi2s{mr.get()}; + updatedChi2s.reserve(contributors.size()); + for (const auto lineRefIx : contributors) { + updatedChi2s.push_back(Line::getDistance2FromPoint(lines[lineRefs[lineRefIx].lineIndex], seed.vertex) / pairCut2); + } + seed.scale2 = updateScale2(updatedChi2s, mr); + seed.contributors = std::move(contributors); + seed.valid = true; + + if (sameContributors && dz < VertexShiftZTol && dr2 < VertexShiftR2Tol) { + break; + } + } + + return seed; +} + +size_t countSharedContributors(std::span lhs, std::span rhs) noexcept +{ + size_t shared = 0; + auto lhsIt = lhs.begin(); + auto rhsIt = rhs.begin(); + while (lhsIt != lhs.end() && rhsIt != rhs.end()) { + if (*lhsIt == *rhsIt) { + ++shared; + ++lhsIt; + ++rhsIt; + } else if (*lhsIt < *rhsIt) { + ++lhsIt; + } else { + ++rhsIt; + } + } + return shared; +} + +bounded_vector collectCompatibleContributors(const VertexSeed& seed, + std::span members, + std::span lineRefs, + std::span lines, + const std::shared_ptr& mr, + const float pairCut2) +{ + bounded_vector contributors{mr.get()}; + contributors.reserve(members.size()); + for (const auto lineRefIdx : members) { + const auto lineIdx = lineRefs[lineRefIdx].lineIndex; + const auto& line = lines[lineIdx]; + if (!line.mTime.isCompatible(seed.time)) { + continue; + } + if (Line::getDistance2FromPoint(line, seed.vertex) >= pairCut2) { + continue; + } + contributors.push_back(lineRefIdx); + } + std::sort(contributors.begin(), contributors.end()); + return contributors; +} + +void deduplicateSeeds(bounded_vector& seeds, const Settings& settings) +{ + if (seeds.size() < 2) { + return; + } + + std::sort(seeds.begin(), seeds.end(), [](const VertexSeed& lhs, const VertexSeed& rhs) { + if (lhs.contributors.size() != rhs.contributors.size()) { + return lhs.contributors.size() > rhs.contributors.size(); + } + if (o2::gpu::GPUCommonMath::Abs(lhs.scale2 - rhs.scale2) > constants::Tolerance) { + return lhs.scale2 < rhs.scale2; + } + return lhs.vertex[2] < rhs.vertex[2]; + }); + + const auto dedupZCut = settings.seedDedupZCut > 0.f ? settings.seedDedupZCut : 0.25f * settings.clusterCut; + for (size_t i = 0; i < seeds.size(); ++i) { + auto& candidate = seeds[i]; + if (!candidate.isUsableSeed()) { + candidate.valid = false; + continue; + } + bool duplicate = false; + for (size_t j = 0; j < i; ++j) { + const auto& kept = seeds[j]; + if (!kept.isUsableSeed()) { + continue; + } + if (!candidate.time.isCompatible(kept.time)) { + continue; + } + const auto shared = countSharedContributors(candidate.contributors, kept.contributors); + const auto minSize = std::min(candidate.contributors.size(), kept.contributors.size()); + const auto zDelta = o2::gpu::GPUCommonMath::Abs(candidate.vertex[2] - kept.vertex[2]); + const bool clearlyWorse = kept.contributors.size() > candidate.contributors.size() || + kept.scale2 + constants::Tolerance < 0.9f * candidate.scale2; + const bool overlapDuplicate = shared > 0 && shared * 2 >= minSize; + const bool nearbyDuplicate = zDelta < dedupZCut && (shared > 0 || clearlyWorse); + if (overlapDuplicate || nearbyDuplicate) { + duplicate = true; + break; + } + } + if (duplicate) { + candidate.valid = false; + } + } + compactSeeds(seeds); +} + +void deduplicateRefittedSeeds(bounded_vector& seeds, const Settings& settings) +{ + if (seeds.size() < 2) { + return; + } + + std::sort(seeds.begin(), seeds.end(), [](const VertexSeed& lhs, const VertexSeed& rhs) { + if (lhs.contributors.size() != rhs.contributors.size()) { + return lhs.contributors.size() > rhs.contributors.size(); + } + if (o2::gpu::GPUCommonMath::Abs(lhs.scale2 - rhs.scale2) > constants::Tolerance) { + return lhs.scale2 < rhs.scale2; + } + return lhs.vertex[2] < rhs.vertex[2]; + }); + + const auto zCut = settings.refitDedupZCut > 0.f ? settings.refitDedupZCut : 0.25f * settings.clusterCut; + for (size_t i = 0; i < seeds.size(); ++i) { + auto& candidate = seeds[i]; + if (!candidate.isUsableSeed()) { + candidate.valid = false; + continue; + } + bool duplicate = false; + for (size_t j = 0; j < i; ++j) { + const auto& kept = seeds[j]; + if (!kept.isUsableSeed()) { + continue; + } + if (!candidate.time.isCompatible(kept.time)) { + continue; + } + const auto shared = countSharedContributors(candidate.contributors, kept.contributors); + const auto minSize = std::min(candidate.contributors.size(), kept.contributors.size()); + const auto zDelta = o2::gpu::GPUCommonMath::Abs(candidate.vertex[2] - kept.vertex[2]); + const bool overlapDuplicate = shared > 0 && shared * 2 >= minSize; + const bool lowSupportPair = std::min(candidate.contributors.size(), kept.contributors.size()) < 4; + const bool clearlyWorse = kept.contributors.size() > candidate.contributors.size() || + kept.scale2 + constants::Tolerance < 0.9f * candidate.scale2; + const bool geometricDuplicate = zDelta < zCut && (lowSupportPair || clearlyWorse); + if (overlapDuplicate || geometricDuplicate) { + duplicate = true; + break; + } + } + if (duplicate) { + candidate.valid = false; + } + } + compactSeeds(seeds); +} + +struct OrderedComponent { + explicit OrderedComponent(const std::shared_ptr& mr) : members(mr.get()) {} + float center = 0.f; + bounded_vector members; +}; + +bounded_vector> buildCoarseClusters(std::span lineRefs, + std::span lines, + const Settings& settings) +{ + bounded_vector> clusters(settings.memoryPool.get()); + if (lineRefs.size() < 2) { + return clusters; + } + + bounded_vector sortedByLower(lineRefs.size(), settings.memoryPool.get()); + std::iota(sortedByLower.begin(), sortedByLower.end(), 0); + std::sort(sortedByLower.begin(), sortedByLower.end(), [&](const int lhs, const int rhs) { + const auto lhsLower = lines[lineRefs[lhs].lineIndex].mTime.lower(); + const auto rhsLower = lines[lineRefs[rhs].lineIndex].mTime.lower(); + if (lhsLower != rhsLower) { + return lhsLower < rhsLower; + } + return lineRefs[lhs].lineIndex < lineRefs[rhs].lineIndex; + }); + + const auto coarseZWindow = settings.coarseZWindow > 0.f ? settings.coarseZWindow : settings.clusterCut; + bounded_vector parent(lineRefs.size(), settings.memoryPool.get()); + bounded_vector componentSize(lineRefs.size(), 1, settings.memoryPool.get()); + std::iota(parent.begin(), parent.end(), 0); + float minZ = std::numeric_limits::max(); + float maxZ = std::numeric_limits::lowest(); + for (const auto& lineRef : lineRefs) { + minZ = std::min(minZ, lineRef.zBeam); + maxZ = std::max(maxZ, lineRef.zBeam); + } + const auto nZBins = std::max(1, 1 + static_cast((maxZ - minZ) / coarseZWindow)); + auto getZBin = [&](const float z) { + return std::clamp(static_cast((z - minZ) / coarseZWindow), 0, nZBins - 1); + }; + + auto findRoot = [&](int idx) { + int root = idx; + while (parent[root] != root) { + root = parent[root]; + } + while (parent[idx] != idx) { + const auto next = parent[idx]; + parent[idx] = root; + idx = next; + } + return root; + }; + + auto unite = [&](const int lhs, const int rhs) { + auto lhsRoot = findRoot(lhs); + auto rhsRoot = findRoot(rhs); + if (lhsRoot == rhsRoot) { + return; + } + if (componentSize[lhsRoot] < componentSize[rhsRoot]) { + std::swap(lhsRoot, rhsRoot); + } + parent[rhsRoot] = lhsRoot; + componentSize[lhsRoot] += componentSize[rhsRoot]; + }; + + using ActiveEntry = std::pair; + bounded_vector activeEntries(settings.memoryPool.get()); + std::priority_queue, std::greater> activeByUpper(std::greater{}, std::move(activeEntries)); + bounded_vector activeMask(lineRefs.size(), 0, settings.memoryPool.get()); + bounded_vector> activeByZBin(settings.memoryPool.get()); + activeByZBin.reserve(nZBins); + for (int iBin = 0; iBin < nZBins; ++iBin) { + activeByZBin.emplace_back(); + } + for (const auto lineRefIdx : sortedByLower) { + const auto& lineRef = lineRefs[lineRefIdx]; + const auto& line = lines[lineRef.lineIndex]; + const auto currentLower = line.mTime.lower(); + + while (!activeByUpper.empty() && activeByUpper.top().first < currentLower) { + activeMask[activeByUpper.top().second] = 0; + activeByUpper.pop(); + } + + const auto zBin = getZBin(lineRef.zBeam); + for (int neighborBin = std::max(0, zBin - 1); neighborBin <= std::min(nZBins - 1, zBin + 1); ++neighborBin) { + auto& bucket = activeByZBin[neighborBin]; + size_t writePos = 0; + for (size_t readPos = 0; readPos < bucket.size(); ++readPos) { + const auto oLineRefIdx = bucket[readPos]; + if (!activeMask[oLineRefIdx]) { + continue; + } + bucket[writePos++] = oLineRefIdx; + const auto& oLineRef = lineRefs[oLineRefIdx]; + if (o2::gpu::GPUCommonMath::Abs(lineRef.zBeam - oLineRef.zBeam) >= coarseZWindow) { + continue; + } + const auto& otherLine = lines[oLineRef.lineIndex]; + if (line.mTime.isCompatible(otherLine.mTime)) { + unite(lineRefIdx, oLineRefIdx); + } + } + bucket.resize(writePos); + } + + activeMask[lineRefIdx] = 1; + activeByUpper.emplace(line.mTime.upper(), lineRefIdx); + activeByZBin[zBin].push_back(lineRefIdx); + } + + std::unordered_map> components; + components.reserve(lineRefs.size()); + for (int lineRefIdx = 0; lineRefIdx < static_cast(lineRefs.size()); ++lineRefIdx) { + const auto root = findRoot(lineRefIdx); + auto [it, inserted] = components.try_emplace(root, std::pmr::polymorphic_allocator{settings.memoryPool.get()}); + (void)inserted; + it->second.push_back(lineRefIdx); + } + + bounded_vector orderedComponents(settings.memoryPool.get()); + orderedComponents.reserve(components.size()); + for (auto& [root, members] : components) { + (void)root; + if (members.size() < 2) { + continue; + } + std::sort(members.begin(), members.end(), [&](const int lhs, const int rhs) { + const auto lhsLower = lines[lineRefs[lhs].lineIndex].mTime.lower(); + const auto rhsLower = lines[lineRefs[rhs].lineIndex].mTime.lower(); + if (lhsLower != rhsLower) { + return lhsLower < rhsLower; + } + return lineRefs[lhs].lineIndex < lineRefs[rhs].lineIndex; + }); + orderedComponents.emplace_back(settings.memoryPool); + orderedComponents.back().center = lineRefs[members.front()].tCenter; + orderedComponents.back().members = std::move(members); + } + + std::sort(orderedComponents.begin(), orderedComponents.end(), [](const auto& lhs, const auto& rhs) { + if (o2::gpu::GPUCommonMath::Abs(lhs.center - rhs.center) > TieTolerance) { + return lhs.center < rhs.center; + } + return lhs.members.front() < rhs.members.front(); + }); + clusters.reserve(orderedComponents.size()); + for (auto& component : orderedComponents) { + clusters.push_back(std::move(component.members)); + } + return clusters; +} + +bounded_vector buildSeeds(std::span members, + std::span lineRefs, + std::span lines, + const Settings& settings) +{ + SeedHistogram histogram(members, lineRefs, lines, settings); + bounded_vector seeds(settings.memoryPool.get()); + seeds.reserve(MaxSeedsPerCluster); + float leadingPeakSupport = 0.f; + + while (static_cast(seeds.size()) < MaxSeedsPerCluster) { + const auto peak = histogram.findPeakBin(); + if (peak < 0) { + break; + } + const auto peakSupport = histogram.getPeakSupport(peak); + if (peakSupport < 2.f) { + break; + } + if (leadingPeakSupport <= 0.f) { + leadingPeakSupport = peakSupport; + } else if (peakSupport < std::max(2.f, MinRelativePeakSupport * leadingPeakSupport)) { + break; + } + auto localMembers = histogram.collectLocalMembers(peak, 0, 0); + if (localMembers.size() < 2) { + localMembers = histogram.collectLocalMembers(peak, settings.seedMemberRadiusTime, settings.seedMemberRadiusZ); + } + if (localMembers.size() < 2) { + histogram.suppressPeak(peak); + continue; + } + + VertexSeed seed(settings.memoryPool); + seed.vertex = {settings.beamX, settings.beamY, histogram.getPeakZCenter(peak)}; + seed.time = histogram.getPeakTimeInterval(peak); + seed.scale2 = InitialScale2; + + auto fitted = fitSeed(seed, localMembers, lineRefs, lines, settings.memoryPool, settings.pairCut2); + if (fitted.valid && fitted.contributors.size() >= 2) { + seeds.push_back(std::move(fitted)); + histogram.suppressPeakNeighborhood(peak); + } else { + histogram.suppressPeak(peak); + } + } + + return seeds; +} + +void assignLinesToSeeds(bounded_vector& seeds, + std::span members, + std::span lineRefs, + std::span lines, + const float pairCut2) +{ + for (auto& seed : seeds) { + seed.assigned.clear(); + } + + for (const auto lineRefIdx : members) { + const auto lineIdx = lineRefs[lineRefIdx].lineIndex; + const auto& line = lines[lineIdx]; + + int bestSeed = -1; + float bestScore = std::numeric_limits::max(); + size_t bestMult = 0; + float bestZResidual = std::numeric_limits::max(); + + for (int seedIdx = 0; seedIdx < static_cast(seeds.size()); ++seedIdx) { + const auto& seed = seeds[seedIdx]; + if (!seed.valid || seed.contributors.size() < 2) { + continue; + } + if (!line.mTime.isCompatible(seed.time)) { + continue; + } + + const auto distance2 = Line::getDistance2FromPoint(line, seed.vertex); + if (distance2 >= pairCut2) { + continue; + } + + const auto score = distance2 / std::max(seed.scale2, MinScale2); + const auto zResidual = o2::gpu::GPUCommonMath::Abs(lineRefs[lineRefIdx].zBeam - seed.vertex[2]); + const auto multiplicity = seed.contributors.size(); + + const auto betterScore = score + TieTolerance < bestScore; + const auto betterMultiplicity = o2::gpu::GPUCommonMath::Abs(score - bestScore) <= TieTolerance && multiplicity > bestMult; + const auto betterZ = o2::gpu::GPUCommonMath::Abs(score - bestScore) <= TieTolerance && + multiplicity == bestMult && zResidual + constants::Tolerance < bestZResidual; + if (betterScore || betterMultiplicity || betterZ) { + bestSeed = seedIdx; + bestScore = score; + bestMult = multiplicity; + bestZResidual = zResidual; + } + } + + if (bestSeed >= 0) { + seeds[bestSeed].assigned.push_back(lineRefIdx); + } + } +} + +ClusterLines materializeCluster(const VertexSeed& seed, + std::span lineRefs, + std::span lines, + const std::shared_ptr& mr) +{ + bounded_vector lineIndices{mr.get()}; + lineIndices.reserve(seed.contributors.size()); + for (const auto lineRefIdx : seed.contributors) { + lineIndices.push_back(lineRefs[lineRefIdx].lineIndex); + } + std::sort(lineIndices.begin(), lineIndices.end()); + lineIndices.erase(std::unique(lineIndices.begin(), lineIndices.end()), lineIndices.end()); + + if (lineIndices.size() < 2) { + return {}; + } + + return {std::span{lineIndices.data(), lineIndices.size()}, lines}; +} + +} // namespace + +bounded_vector buildClusters(std::span lines, const Settings& settings) +{ + bounded_vector clusters(settings.memoryPool.get()); + if (lines.size() < 2) { + return clusters; + } + + bounded_vector refs(settings.memoryPool.get()); + refs.reserve(lines.size()); + for (int lineIdx = 0; lineIdx < static_cast(lines.size()); ++lineIdx) { + LineRef ref(lines[lineIdx], lineIdx, settings.beamX, settings.beamY, settings.maxZ); + if (!ref.isDead()) { + refs.push_back(ref); + } + } + + if (refs.size() < 2) { + return clusters; + } + + const auto coarseClusters = buildCoarseClusters(refs, lines, settings); + + for (const auto& members : coarseClusters) { + auto seeds = buildSeeds(members, refs, lines, settings); + if (seeds.empty()) { + continue; + } + + for (auto& seed : seeds) { + if (!seed.isUsableSeed()) { + seed.valid = false; + continue; + } + auto contributors = collectCompatibleContributors(seed, members, refs, lines, settings.memoryPool, settings.pairCut2); + if (contributors.size() < 2) { + seed.valid = false; + continue; + } + seed.contributors = std::move(contributors); + } + compactSeeds(seeds); + if (seeds.empty()) { + continue; + } + deduplicateSeeds(seeds, settings); + if (seeds.empty()) { + continue; + } + assignLinesToSeeds(seeds, members, refs, lines, settings.pairCut2); + for (auto& seed : seeds) { + if (seed.assigned.size() < 2) { + seed.valid = false; + continue; + } + seed = fitSeed(seed, seed.assigned, refs, lines, settings.memoryPool, settings.pairCut2); + if (!seed.isUsableSeed()) { + seed.valid = false; + continue; + } + } + compactSeeds(seeds); + deduplicateRefittedSeeds(seeds, settings); + for (auto& refit : seeds) { + auto cluster = materializeCluster(refit, refs, lines, settings.memoryPool); + if (cluster.getSize() < 2) { + continue; + } + if (!cluster.isValid()) { + continue; + } + clusters.push_back(std::move(cluster)); + } + } + + return clusters; +} + +} // namespace o2::its::line_vertexer diff --git a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx index 5a32b3d3b1a95..5b412ea4eea69 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx @@ -14,19 +14,16 @@ /// #include -#include #include "Framework/Logger.h" #include "ITStracking/TimeFrame.h" #include "ITStracking/MathUtils.h" -#include "DataFormatsITSMFT/Cluster.h" #include "DataFormatsITSMFT/CompCluster.h" #include "DataFormatsITSMFT/ROFRecord.h" #include "DataFormatsITSMFT/TopologyDictionary.h" #include "ITSBase/GeometryTGeo.h" #include "ITSMFTBase/SegmentationAlpide.h" #include "ITStracking/BoundedAllocator.h" -#include "ITStracking/TrackingConfigParam.h" namespace { @@ -271,7 +268,7 @@ void TimeFrame::initialise(const int iteration, const TrackingParameter 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.SystErrorZ2[iLayer] + trkParam.SystErrorY2[iLayer]) + trkParam.LayerResolution[iLayer] * trkParam.LayerResolution[iLayer]); + mPositionResolution[iLayer] = o2::gpu::CAMath::Sqrt((0.5f * (trkParam.SystErrorZ2[iLayer] + trkParam.SystErrorY2[iLayer])) + (trkParam.LayerResolution[iLayer] * trkParam.LayerResolution[iLayer])); } clearResizeBoundedVector(mLines, getNrof(1), mMemoryPool.get()); clearResizeBoundedVector(mTrackletClusters, getNrof(1), mMemoryPool.get()); @@ -312,17 +309,17 @@ void TimeFrame::initialise(const int iteration, const TrackingParameter float oneOverR{0.001f * 0.3f * std::abs(mBz) / trkParam.TrackletMinPt}; for (unsigned int iLayer{0}; iLayer < NLayers; ++iLayer) { mMSangles[iLayer] = math_utils::MSangle(0.14f, trkParam.TrackletMinPt, trkParam.LayerxX0[iLayer]); - mPositionResolution[iLayer] = o2::gpu::CAMath::Sqrt(0.5f * (trkParam.SystErrorZ2[iLayer] + trkParam.SystErrorY2[iLayer]) + trkParam.LayerResolution[iLayer] * trkParam.LayerResolution[iLayer]); + mPositionResolution[iLayer] = o2::gpu::CAMath::Sqrt((0.5f * (trkParam.SystErrorZ2[iLayer] + trkParam.SystErrorY2[iLayer])) + (trkParam.LayerResolution[iLayer] * trkParam.LayerResolution[iLayer])); if (iLayer < mClusters.size() - 1) { const float& r1 = trkParam.LayerRadii[iLayer]; const float& r2 = trkParam.LayerRadii[iLayer + 1]; - oneOverR = (0.5 * oneOverR >= 1.f / r2) ? 2.f / r2 - o2::constants::math::Almost0 : oneOverR; + 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[iLayer]); const float res2 = o2::gpu::CAMath::Hypot(trkParam.PVres, mPositionResolution[iLayer + 1]); 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))); + 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) mPhiCuts[iLayer] = std::min(o2::gpu::CAMath::ASin(0.5f * x * oneOverR) + 2.f * mMSangles[iLayer] + delta, o2::constants::math::PI * 0.5f); } diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx index a41560e2e9e9a..eb0841888b03e 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx @@ -10,6 +10,8 @@ // or submit itself to any jurisdiction. #include +#include +#include #include #include @@ -128,12 +130,12 @@ void ITSTrackingInterface::run(framework::ProcessingContext& pc) gsl::span physTriggers; std::vector fromTRD; if (mUseTriggers == 2) { // use TRD triggers - o2::InteractionRecord ir{0, tfInfo.firstTForbit}; + o2::InteractionRecord irFirstTF{0, tfInfo.firstTForbit}; auto trdTriggers = pc.inputs().get>("phystrig"); for (const auto& trig : trdTriggers) { - if (trig.getBCData() >= ir && trig.getNumberOfTracklets()) { - ir = trig.getBCData(); - fromTRD.emplace_back(o2::itsmft::PhysTrigger{.ir = ir, .data = 0}); + if (trig.getBCData() >= irFirstTF && trig.getNumberOfTracklets()) { + irFirstTF = trig.getBCData(); + fromTRD.emplace_back(o2::itsmft::PhysTrigger{.ir = irFirstTF, .data = 0}); } } physTriggers = gsl::span(fromTRD.data(), fromTRD.size()); @@ -215,7 +217,8 @@ void ITSTrackingInterface::run(framework::ProcessingContext& pc) auto vtxSpan = mTimeFrame->getPrimaryVertices(clockLayerId, iRof); if (o2::its::TrackerParamConfig::Instance().doUPCIteration) { if (!vtxSpan.empty()) { - if (vtxSpan[0].isFlagSet(Vertex::UPCMode) == 1) { // at least one vertex in this ROF and it is from second vertex iteration + bool hasUPC = std::any_of(vtxSpan.begin(), vtxSpan.end(), [](const auto& v) { return v.isFlagSet(Vertex::UPCMode); }); + if (hasUPC) { // at least one vertex in this ROF and it is from second vertex iteration LOGP(debug, "ROF {} rejected as vertices are from the UPC iteration", iRof); processUPCMask.selectROF({clockTiming.getROFStartInBC(iRof), clockTiming.getROFEndInBC(iRof)}); vtxROF.setFlag(o2::itsmft::ROFRecord::VtxUPCMode); diff --git a/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx index 5e27e20b3ddee..a22d2d6c60990 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// 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. // @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include @@ -22,6 +24,7 @@ #include "ITStracking/BoundedAllocator.h" #include "ITStracking/ClusterLines.h" #include "ITStracking/Definitions.h" +#include "ITStracking/LineVertexerHelpers.h" #include "ITStracking/Tracklet.h" #include "SimulationDataFormat/DigitizationContext.h" #include "SimulationDataFormat/O2DatabasePDG.h" @@ -31,13 +34,14 @@ namespace o2::its { - +namespace +{ template -static void trackleterKernelHost( +void trackleterKernelHost( const gsl::span& clustersNextLayer, // 0 2 const gsl::span& clustersCurrentLayer, // 1 1 const gsl::span& usedClustersNextLayer, // 0 2 - int* indexTableNext, + const int* indexTableNext, const float phiCut, bounded_vector& tracklets, gsl::span foundTracklets, @@ -94,7 +98,7 @@ static void trackleterKernelHost( } } -static void trackletSelectionKernelHost( +void trackletSelectionKernelHost( const Cluster* clusters0, // global layer 0 clusters const Cluster* clusters1, // global layer 1 clusters gsl::span usedClusters0, // global layer 0 used clusters @@ -145,6 +149,7 @@ static void trackletSelectionKernelHost( offset12 += foundTracklets12[iCurrentLayerClusterIndex]; } } +} // namespace template void VertexerTraits::updateVertexingParameters(const std::vector& vrtPar) @@ -255,7 +260,7 @@ void VertexerTraits::computeTracklets(const int iteration) }); }); - /// Create tracklets labels for L0-L1, information is as flat as in tracklets vector (no rofId) + /// Create flat L0-L1 tracklet labels (no rofId) if (mTimeFrame->hasMCinformation()) { for (const auto& trk : mTimeFrame->getTracklets()[0]) { o2::MCCompLabel label; @@ -309,15 +314,7 @@ void VertexerTraits::computeTrackletMatching(const int iteration) static_cast(mTimeFrame->getClustersOnLayer(pivotRofId, 1).size()), mVrtParams[iteration].tanLambdaCut, mVrtParams[iteration].phiCut); - auto& lines = mTimeFrame->getLines(pivotRofId); - totalLines.local() += lines.size(); - std::stable_sort(lines.begin(), lines.end(), [](const Line& a, const Line& b) { - // sort by lower edge and secondly prefer wider windows - if (a.mTime.lower() != b.mTime.lower()) { - return a.mTime.lower() < b.mTime.lower(); - } - return a.mTime.upper() > b.mTime.upper(); - }); + totalLines.local() += mTimeFrame->getLines(pivotRofId).size(); } }); mTimeFrame->setNLinesTotal(totalLines.combine(std::plus())); @@ -330,125 +327,214 @@ void VertexerTraits::computeTrackletMatching(const int iteration) template void VertexerTraits::computeVertices(const int iteration) { - const auto nsigmaCut{std::min(mVrtParams[iteration].vertNsigmaCut * mVrtParams[iteration].vertNsigmaCut * (mVrtParams[iteration].vertRadiusSigma * mVrtParams[iteration].vertRadiusSigma + mVrtParams[iteration].trackletSigma * mVrtParams[iteration].trackletSigma), 1.98f)}; - const auto pairCut2{mVrtParams[iteration].pairCut * mVrtParams[iteration].pairCut}; const int nRofs = mTimeFrame->getNrof(1); - const bool hasMC = mTimeFrame->hasMCinformation(); std::vector> rofVertices(nRofs); std::vector> rofLabels(nRofs); + const float nsigmaCut = std::min(mVrtParams[iteration].vertNsigmaCut * mVrtParams[iteration].vertNsigmaCut * (mVrtParams[iteration].vertRadiusSigma * mVrtParams[iteration].vertRadiusSigma + mVrtParams[iteration].trackletSigma * mVrtParams[iteration].trackletSigma), 1.98f); + const float pairCut2 = mVrtParams[iteration].pairCut * mVrtParams[iteration].pairCut; + const float duplicateZCut = mVrtParams[iteration].duplicateZCut > 0.f ? mVrtParams[iteration].duplicateZCut : std::max(4.f * mVrtParams[iteration].pairCut, 0.5f * mVrtParams[iteration].clusterCut); + const float duplicateDistance2Cut = mVrtParams[iteration].duplicateDistance2Cut > 0.f ? mVrtParams[iteration].duplicateDistance2Cut : std::max(16.f * pairCut2, 0.0625f * mVrtParams[iteration].clusterCut * mVrtParams[iteration].clusterCut); + line_vertexer::Settings settings; + settings.beamX = mTimeFrame->getBeamX(); + settings.beamY = mTimeFrame->getBeamY(); + settings.pairCut = mVrtParams[iteration].pairCut; + settings.pairCut2 = pairCut2; + settings.clusterCut = mVrtParams[iteration].clusterCut; + settings.coarseZWindow = mVrtParams[iteration].coarseZWindow; + settings.seedDedupZCut = mVrtParams[iteration].seedDedupZCut; + settings.refitDedupZCut = mVrtParams[iteration].refitDedupZCut; + settings.duplicateZCut = duplicateZCut; + settings.duplicateDistance2Cut = duplicateDistance2Cut; + settings.finalSelectionZCut = mVrtParams[iteration].finalSelectionZCut; + settings.maxZ = mVrtParams[iteration].maxZPositionAllowed; + settings.seedMemberRadiusTime = mVrtParams[iteration].seedMemberRadiusTime; + settings.seedMemberRadiusZ = mVrtParams[iteration].seedMemberRadiusZ; + settings.memoryPool = mMemoryPool; const auto processROF = [&](const int rofId) { auto& lines = mTimeFrame->getLines(rofId); - const int nLines{static_cast(lines.size())}; - bounded_vector usedTracklets(nLines, 0, mMemoryPool.get()); - auto& clusters = mTimeFrame->getTrackletClusters(rofId); - - for (int iLine1{0}; iLine1 < nLines; ++iLine1) { - if (usedTracklets[iLine1]) { - continue; + auto clusters = line_vertexer::buildClusters(std::span{lines.data(), lines.size()}, settings); + deepVectorClear(lines); // not needed after + auto clusterBeamDistance2 = [&](const ClusterLines& cluster) { + return (mTimeFrame->getBeamX() - cluster.getVertex()[0]) * (mTimeFrame->getBeamX() - cluster.getVertex()[0]) + + (mTimeFrame->getBeamY() - cluster.getVertex()[1]) * (mTimeFrame->getBeamY() - cluster.getVertex()[1]); + }; + auto clusterBetter = [&](const ClusterLines& lhs, const ClusterLines& rhs) { + if (lhs.getSize() != rhs.getSize()) { + return lhs.getSize() > rhs.getSize(); } - const auto& line1 = lines[iLine1]; - for (int iLine2{iLine1 + 1}; iLine2 < nLines; ++iLine2) { - if (usedTracklets[iLine2]) { - continue; - } - const auto& line2 = lines[iLine2]; - if (!line1.mTime.isCompatible(line2.mTime)) { + if (o2::gpu::GPUCommonMath::Abs(lhs.getAvgDistance2() - rhs.getAvgDistance2()) > constants::Tolerance) { + return lhs.getAvgDistance2() < rhs.getAvgDistance2(); + } + const auto lhsBeam = clusterBeamDistance2(lhs); + const auto rhsBeam = clusterBeamDistance2(rhs); + if (o2::gpu::GPUCommonMath::Abs(lhsBeam - rhsBeam) > constants::Tolerance) { + return lhsBeam < rhsBeam; + } + return lhs.getVertex()[2] < rhs.getVertex()[2]; + }; + + // Cluster deduplication by local non-maximum suppression in time/space + std::sort(clusters.begin(), clusters.end(), clusterBetter); + float minClusterZ = std::numeric_limits::max(); + for (const auto& cluster : clusters) { + minClusterZ = std::min(minClusterZ, cluster.getVertex()[2]); + } + bounded_vector deduplicated(mMemoryPool.get()); + deduplicated.reserve(clusters.size()); + std::unordered_map> keptByZBin; + for (auto& candidate : clusters) { + bool duplicate = false; + const auto candidateZ = candidate.getVertex()[2]; + const auto zBin = static_cast(std::floor((candidateZ - minClusterZ) / settings.duplicateZCut)); + for (int neighborBin = zBin - 1; neighborBin <= zBin + 1 && !duplicate; ++neighborBin) { + const auto found = keptByZBin.find(neighborBin); + if (found == keptByZBin.end()) { continue; } - auto dca2{Line::getDCA2(line1, line2)}; - if (dca2 < pairCut2) { - auto& cluster = clusters.emplace_back(iLine1, line1, iLine2, line2); - if (!cluster.isValid() || cluster.getR2() > 4.f) { - clusters.pop_back(); + for (const auto ownerId : found->second) { + const auto& owner = deduplicated[ownerId]; + if (!candidate.getTimeStamp().isCompatible(owner.getTimeStamp())) { continue; } - - usedTracklets[iLine1] = 1; - usedTracklets[iLine2] = 1; - for (int iLine3{0}; iLine3 < nLines; ++iLine3) { - if (usedTracklets[iLine3]) { - continue; - } - const auto& line3 = lines[iLine3]; - if (!line3.mTime.isCompatible(cluster.getTimeStamp())) { - continue; - } - const auto distance2 = Line::getDistance2FromPoint(line3, cluster.getVertex()); - if (distance2 < pairCut2) { - cluster.add(iLine3, line3); - usedTracklets[iLine3] = 1; - } + if (o2::gpu::GPUCommonMath::Abs(candidate.getVertex()[2] - owner.getVertex()[2]) >= settings.duplicateZCut) { + continue; + } + const auto dx = candidate.getVertex()[0] - owner.getVertex()[0]; + const auto dy = candidate.getVertex()[1] - owner.getVertex()[1]; + const auto dz = candidate.getVertex()[2] - owner.getVertex()[2]; + const auto distance2 = math_utils::SqSum(dx, dy, dz); + if (distance2 < settings.duplicateDistance2Cut) { + duplicate = true; + break; } - break; } } - } + if (duplicate) { + continue; + } - // Cluster merging - std::sort(clusters.begin(), clusters.end(), - [](ClusterLines& cluster1, ClusterLines& cluster2) { return cluster1.getSize() > cluster2.getSize(); }); + const auto ownerId = static_cast(deduplicated.size()); + keptByZBin[zBin].push_back(ownerId); + deduplicated.push_back(std::move(candidate)); + } + clusters = std::move(deduplicated); int nClusters = static_cast(clusters.size()); - for (int iCluster1{0}; iCluster1 < nClusters; ++iCluster1) { - std::array vertex1{clusters[iCluster1].getVertex()}; - std::array vertex2{}; - for (int iCluster2{iCluster1 + 1}; iCluster2 < nClusters; ++iCluster2) { - if (clusters[iCluster1].getTimeStamp().isCompatible(clusters[iCluster2].getTimeStamp())) { - vertex2 = clusters[iCluster2].getVertex(); - if (o2::gpu::GPUCommonMath::Abs(vertex1[2] - vertex2[2]) < mVrtParams[iteration].clusterCut) { - float distance{((vertex1[0] - vertex2[0]) * (vertex1[0] - vertex2[0])) + - ((vertex1[1] - vertex2[1]) * (vertex1[1] - vertex2[1])) + - ((vertex1[2] - vertex2[2]) * (vertex1[2] - vertex2[2]))}; - if (distance < mVrtParams[iteration].pairCut * mVrtParams[iteration].pairCut) { - for (auto label : clusters[iCluster2].getLabels()) { - clusters[iCluster1].add(label, lines[label]); - vertex1 = clusters[iCluster1].getVertex(); - } - clusters.erase(clusters.begin() + iCluster2); - --iCluster2; - --nClusters; - } - } - } + + // Vertex filtering with score-based local NMS + std::sort(clusters.begin(), clusters.end(), clusterBetter); + std::vector candidateIndices; + candidateIndices.reserve(nClusters); + for (int iCluster{0}; iCluster < nClusters; ++iCluster) { + const bool zCompatible = o2::gpu::GPUCommonMath::Abs(clusters[iCluster].getVertex()[2]) < mVrtParams[iteration].maxZPositionAllowed; + + if (zCompatible) { + candidateIndices.push_back(iCluster); } } - // Vertex filtering - std::sort(clusters.begin(), clusters.end(), - [](const ClusterLines& cluster1, const ClusterLines& cluster2) { return cluster1.getSize() > cluster2.getSize(); }); - bool atLeastOneFound{false}; - for (int iCluster{0}; iCluster < nClusters; ++iCluster) { - bool lowMultCandidate{false}; - double beamDistance2{(mTimeFrame->getBeamX() - clusters[iCluster].getVertex()[0]) * (mTimeFrame->getBeamX() - clusters[iCluster].getVertex()[0]) + - (mTimeFrame->getBeamY() - clusters[iCluster].getVertex()[1]) * (mTimeFrame->getBeamY() - clusters[iCluster].getVertex()[1])}; - if (atLeastOneFound && (lowMultCandidate = clusters[iCluster].getSize() < mVrtParams[iteration].clusterContributorsCut)) { - lowMultCandidate &= (beamDistance2 < mVrtParams[iteration].lowMultBeamDistCut * mVrtParams[iteration].lowMultBeamDistCut); - if (!lowMultCandidate) { - clusters.erase(clusters.begin() + iCluster); - nClusters--; - continue; + if (candidateIndices.empty()) { + return; + } + + auto countSharedLabels = [](const ClusterLines& lhs, const ClusterLines& rhs) { + size_t shared = 0; + auto lhsIt = lhs.getLabels().begin(); + auto rhsIt = rhs.getLabels().begin(); + while (lhsIt != lhs.getLabels().end() && rhsIt != rhs.getLabels().end()) { + if (*lhsIt == *rhsIt) { + ++shared; + ++lhsIt; + ++rhsIt; + } else if (*lhsIt < *rhsIt) { + ++lhsIt; + } else { + ++rhsIt; } } + return shared; + }; - if (beamDistance2 < nsigmaCut && o2::gpu::GPUCommonMath::Abs(clusters[iCluster].getVertex()[2]) < mVrtParams[iteration].maxZPositionAllowed) { - atLeastOneFound = true; - Vertex vertex{clusters[iCluster].getVertex().data(), - clusters[iCluster].getRMS2(), - (ushort)clusters[iCluster].getSize(), - clusters[iCluster].getAvgDistance2()}; - - if (iteration) { - vertex.setFlags(Vertex::UPCMode); + float minCandidateZ = std::numeric_limits::max(); + for (const auto clusterId : candidateIndices) { + minCandidateZ = std::min(minCandidateZ, clusters[clusterId].getVertex()[2]); + } + std::unordered_map> selectedByZBin; + std::vector selectedIndices; + selectedIndices.reserve(candidateIndices.size()); + for (const auto clusterId : candidateIndices) { + const auto& candidate = clusters[clusterId]; + const auto candidateZ = candidate.getVertex()[2]; + const auto zBin = static_cast((candidateZ - minCandidateZ) / settings.finalSelectionZCut); + bool suppressed = false; + for (int neighborBin = zBin - 1; neighborBin <= zBin + 1 && !suppressed; ++neighborBin) { + const auto found = selectedByZBin.find(neighborBin); + if (found == selectedByZBin.end()) { + continue; } - vertex.setTimeStamp(clusters[iCluster].getTimeStamp()); - rofVertices[rofId].push_back(vertex); - if (hasMC) { - bounded_vector labels(mMemoryPool.get()); - for (auto& index : clusters[iCluster].getLabels()) { - labels.push_back(mTimeFrame->getLinesLabel(rofId)[index]); + for (const auto selectedId : found->second) { + const auto& selected = clusters[selectedId]; + if (!candidate.getTimeStamp().isCompatible(selected.getTimeStamp())) { + continue; } - rofLabels[rofId].push_back(computeMain(labels)); + const auto zDelta = o2::gpu::GPUCommonMath::Abs(candidateZ - selected.getVertex()[2]); + const auto sharedLabels = countSharedLabels(candidate, selected); + const auto minSize = std::min(candidate.getSize(), selected.getSize()); + const bool overlapDuplicate = sharedLabels > 0 && sharedLabels * 4 >= minSize; + const bool strongZDuplicate = zDelta < settings.finalSelectionZCut; + const bool clearlyBetterMultiplicity = selected.getSize() >= candidate.getSize() + 3; + const bool clearlyBetterQuality = selected.getSize() > candidate.getSize() && + selected.getAvgDistance2() + constants::Tolerance < 0.8f * candidate.getAvgDistance2(); + const bool weakCandidate = clearlyBetterMultiplicity || clearlyBetterQuality; + if (overlapDuplicate || (strongZDuplicate && weakCandidate)) { + suppressed = true; + break; + } + } + } + if (suppressed) { + continue; + } + selectedByZBin[zBin].push_back(clusterId); + selectedIndices.push_back(clusterId); + } + + // sort vertices by their multiplicity to opt. suppress lower mult. debris + std::vector sortedIndices(selectedIndices.size()); + std::iota(sortedIndices.begin(), sortedIndices.end(), 0); + std::sort(sortedIndices.begin(), sortedIndices.end(), [&selectedIndices, &clusters](int i, int j) { + return clusters[selectedIndices[i]].getSize() > clusters[selectedIndices[j]].getSize(); + }); + for (const auto sortedId : sortedIndices) { + const auto& cluster = clusters[selectedIndices[sortedId]]; + const auto beamDistance2 = clusterBeamDistance2(cluster); + if (!(beamDistance2 < nsigmaCut)) { + continue; + } + if (cluster.getSize() < mVrtParams[iteration].clusterContributorsCut) { + continue; + } + if (!rofVertices[rofId].empty() && cluster.getSize() < mVrtParams[iteration].suppressLowMultDebris) { + continue; + } + + Vertex vertex{cluster.getVertex().data(), + cluster.getRMS2(), + (ushort)cluster.getSize(), + cluster.getAvgDistance2()}; + if (iteration) { + vertex.setFlags(Vertex::UPCMode); + } + vertex.setTimeStamp(cluster.getTimeStamp()); + rofVertices[rofId].push_back(vertex); + if (mTimeFrame->hasMCinformation()) { + auto& lineLabels = mTimeFrame->getLinesLabel(rofId); + bounded_vector labels(mMemoryPool.get()); + for (auto& index : cluster.getLabels()) { + labels.push_back(lineLabels[index]); } + const auto mainLabel = computeMain(labels); + rofLabels[rofId].push_back(mainLabel); } } }; @@ -469,7 +555,7 @@ void VertexerTraits::computeVertices(const int iteration) for (auto& vertex : rofVertices[rofId]) { mTimeFrame->addPrimaryVertex(vertex); } - if (hasMC) { + if (mTimeFrame->hasMCinformation()) { for (auto& label : rofLabels[rofId]) { mTimeFrame->addPrimaryVertexLabel(label); } @@ -504,7 +590,8 @@ void VertexerTraits::addTruthSeedingVertices() if (!trk.isPrimary() || trk.GetPt() < 0.05 || std::abs(trk.GetEta()) > 1.1) { return false; } - return o2::O2DatabasePDG::Instance()->GetParticle(trk.GetPdgCode())->Charge() != 0; + const auto* p = o2::O2DatabasePDG::Instance()->GetParticle(trk.GetPdgCode()); + return (!p) ? false : p->Charge() != 0; }))); vert.setXYZ((float)eve.GetX(), (float)eve.GetY(), (float)eve.GetZ()); vert.setChi2(1); // not used as constraint diff --git a/prodtests/full-system-test/dpl-workflow.sh b/prodtests/full-system-test/dpl-workflow.sh index 9f982513fdffd..d54c05ff0f20e 100755 --- a/prodtests/full-system-test/dpl-workflow.sh +++ b/prodtests/full-system-test/dpl-workflow.sh @@ -117,6 +117,11 @@ EVE_OPT=" --jsons-folder $EDJSONS_DIR" [[ "0$ITSSTAGGERED" == "01" ]] && ITS_STAGGERED=" --enable-its-staggering " || ITS_STAGGERED= [[ "0$MFTSTAGGERED" == "01" ]] && MFT_STAGGERED=" --enable-its-staggering " || MFT_STAGGERED= +# ITS vertexing settings +if [[ $BEAMTYPE == "pp" || $LIGHTNUCLEI == "1" ]]; then + ITS_CONFIG_KEY+=";ITSVertexerParam.pairCut=0.0317563;ITSVertexerParam.clusterCut=0.6640964;ITSVertexerParam.coarseZWindow=0.2049018;ITSVertexerParam.seedDedupZCut=0.0711793;ITSVertexerParam.refitDedupZCut=0.0680009;ITSVertexerParam.duplicateZCut=0.1582193;ITSVertexerParam.finalSelectionZCut=0.1081465;ITSVertexerParam.duplicateDistance2Cut=0.0117033;ITSVertexerParam.clusterContributorsCut=2;ITSVertexerParam.seedMemberRadiusZ=0;ITSVertexerParam.vertNsigmaCut=4.0;ITSVertexerParam.vertRadiusSigma=0.0452309;ITSVertexerParam.trackletSigma=0.0025941;ITSVertexerParam.suppressLowMultDebris=0;" +fi + if [[ $CTFINPUT != 1 ]]; then GPU_OUTPUT+=",tpc-triggers" fi @@ -126,10 +131,10 @@ if [[ $SYNCMODE == 1 ]]; then MFT_STF_DEC_CONFIG+="MFTClustererParam.maxBCDiffToMaskBias=-1;" [[ $BEAMTYPE == "PbPb" || $BEAMTYPE == "pp" || $LIGHTNUCLEI == "1" ]] && MFT_CONFIG_KEY+="MFTTracking.cutMultClusLow=0;MFTTracking.cutMultClusHigh=4000;" if [[ $BEAMTYPE == "PbPb" ]]; then - ITS_CONFIG_KEY+="fastMultConfig.cutMultClusLow=${CUT_MULT_MIN_ITS:-0};fastMultConfig.cutMultClusHigh=${CUT_MULT_MAX_ITS:-400};fastMultConfig.cutMultVtxHigh=${CUT_MULT_VTX_ITS:-20};" + ITS_CONFIG_KEY+=";fastMultConfig.cutMultClusLow=${CUT_MULT_MIN_ITS:-0};fastMultConfig.cutMultClusHigh=${CUT_MULT_MAX_ITS:-400};fastMultConfig.cutMultVtxHigh=${CUT_MULT_VTX_ITS:-20};" MCH_CONFIG_KEY="MCHTracking.maxCandidates=50000;MCHTracking.maxTrackingDuration=20;" elif [[ $BEAMTYPE == "pp" || $LIGHTNUCLEI == "1" ]]; then - ITS_CONFIG_KEY+="fastMultConfig.cutMultClusLow=${CUT_MULT_MIN_ITS:--1};fastMultConfig.cutMultClusHigh=${CUT_MULT_MAX_ITS:--1};fastMultConfig.cutMultVtxHigh=${CUT_MULT_VTX_ITS:--1};ITSVertexerParam.phiCut=0.5;ITSVertexerParam.clusterContributorsCut=3;ITSVertexerParam.tanLambdaCut=0.2;" + ITS_CONFIG_KEY+=";fastMultConfig.cutMultClusLow=${CUT_MULT_MIN_ITS:--1};fastMultConfig.cutMultClusHigh=${CUT_MULT_MAX_ITS:--1};fastMultConfig.cutMultVtxHigh=${CUT_MULT_VTX_ITS:--1};" MCH_CONFIG_KEY="MCHTracking.maxCandidates=20000;MCHTracking.maxTrackingDuration=10;" fi [[ -n ${CUT_RANDOM_FRACTION_ITS:-} ]] && ITS_CONFIG_KEY+="fastMultConfig.cutRandomFraction=$CUT_RANDOM_FRACTION_ITS;" @@ -157,11 +162,6 @@ if [[ $SYNCMODE == 1 ]]; then has_detector ITS && TRD_FILTER_CONFIG+=" --filter-trigrec" else has_detectors_gpu TPC ITS && ITS_CONFIG_KEY+="ITSCATrackerParam.trackingMode=1;" # sets ITS gpu reco to async - if [[ $BEAMTYPE == "pp" || $LIGHTNUCLEI == "1" ]]; then - ITS_CONFIG_KEY+="ITSVertexerParam.phiCut=0.5;ITSVertexerParam.clusterContributorsCut=3;ITSVertexerParam.tanLambdaCut=0.2;" - elif [[ $BEAMTYPE == "PbPb" ]]; then - ITS_CONFIG_KEY+="ITSVertexerParam.lowMultBeamDistCut=0;" - fi if [[ $IS_SIMULATED_DATA == 0 && $CTFINPUT == 1 ]]; then # Enable fixes to the MCH readout mapping for async processing of real data MCH_CONFIG_KEY+="MCHDigitModifier.updateST1=true;MCHDigitModifier.updateST2=true;" @@ -585,7 +585,7 @@ has_detector_gpu ITS && GPU_OUTPUT+=",its-tracks" # --------------------------------------------------------------------------------------------------------------------- # Common reconstruction workflows -(has_detector_reco ITS && ! has_detector_gpu ITS) && ! has_detector_from_global_reader ITS && add_W o2-its-reco-workflow "--trackerCA $ITS_CONFIG $ITS_STAGGERED $DISABLE_MC ${DISABLE_DIGIT_CLUSTER_INPUT:-} $DISABLE_ROOT_OUTPUT --pipeline $(get_N its-tracker ITS REST 1 ITSTRK),$(get_N its-clusterer ITS REST 1 ITSCL)" "$ITS_CONFIG_KEY;$ITSMFT_STROBES;$ITSEXTRAERR" +(has_detector_reco ITS && ! has_detector_gpu ITS) && ! has_detector_from_global_reader ITS && add_W o2-its-reco-workflow "$ITS_CONFIG $ITS_STAGGERED $DISABLE_MC ${DISABLE_DIGIT_CLUSTER_INPUT:-} $DISABLE_ROOT_OUTPUT --pipeline $(get_N its-tracker ITS REST 1 ITSTRK),$(get_N its-clusterer ITS REST 1 ITSCL)" "$ITS_CONFIG_KEY;$ITSMFT_STROBES;$ITSEXTRAERR" [[ ${DISABLE_DIGIT_CLUSTER_INPUT:-} =~ "--digits-from-upstream" ]] && has_detector_gpu ITS && ! has_detector_from_global_reader ITS && add_W o2-its-reco-workflow "--disable-tracking ${DISABLE_DIGIT_CLUSTER_INPUT:-} $ITS_STAGGERED $DISABLE_MC $DISABLE_ROOT_OUTPUT --pipeline $(get_N its-clusterer ITS REST 1 ITSCL)" "$ITS_CONFIG_KEY;$ITSMFT_STROBES;$ITSEXTRAERR" (has_detector_reco TPC || has_detector_ctf TPC) && ! has_detector_from_global_reader TPC && add_W o2-gpu-reco-workflow "--gpu-reconstruction \"$GPU_CONFIG_SELF\" --input-type=$GPU_INPUT $DISABLE_MC --output-type $GPU_OUTPUT $TPC_CORR_OPT $ITS_STAGGERED --pipeline gpu-reconstruction:${N_TPCTRK:-1},gpu-reconstruction-prepare:${N_TPCTRK:-1} $GPU_CONFIG" "GPU_global.deviceType=$GPUTYPE;GPU_proc.debugLevel=0;$GPU_CONFIG_KEY;$TRACKTUNETPCINNER;$TPC_CORR_KEY" (has_detector_reco TOF || has_detector_ctf TOF) && ! has_detector_from_global_reader TOF && add_W o2-tof-reco-workflow "$TOF_CONFIG --input-type $TOF_INPUT --output-type $TOF_OUTPUT $DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC --pipeline $(get_N tof-compressed-decoder TOF RAW 1),$(get_N TOFClusterer TOF REST 1)" diff --git a/prodtests/full_system_test.sh b/prodtests/full_system_test.sh index 46739e76f103b..e89d8ee09dee9 100755 --- a/prodtests/full_system_test.sh +++ b/prodtests/full_system_test.sh @@ -188,7 +188,7 @@ taskwrapper digi.log o2-sim-digitizer-workflow -n $NEvents ${DIGIQED} ${NOMCLABE touch digiTRD.log_done if [[ "0$GENERATE_ITSMFT_DICTIONARIES" == "01" ]]; then - taskwrapper itsmftdict1.log o2-its-reco-workflow --trackerCA --disable-mc --configKeyValues '"fastMultConfig.cutMultClusLow=30000;fastMultConfig.cutMultClusHigh=2000000;fastMultConfig.cutMultVtxHigh=500;"' + taskwrapper itsmftdict1.log o2-its-reco-workflow --disable-mc --configKeyValues '"fastMultConfig.cutMultClusLow=30000;fastMultConfig.cutMultClusHigh=2000000;fastMultConfig.cutMultVtxHigh=500;"' cp ~/alice/O2/Detectors/ITSMFT/ITS/macros/test/CreateDictionaries.C . taskwrapper itsmftdict2.log root -b -q CreateDictionaries.C++ rm -f CreateDictionaries_C* CreateDictionaries.C diff --git a/prodtests/sim_challenge.sh b/prodtests/sim_challenge.sh index 8c7cfb1a024b0..f5bbf8ab74ff8 100755 --- a/prodtests/sim_challenge.sh +++ b/prodtests/sim_challenge.sh @@ -153,7 +153,7 @@ if [ "$doreco" == "1" ]; then echo "Return status of tpcreco: $?" echo "Running ITS reco flow" - taskwrapper itsreco.log o2-its-reco-workflow --trackerCA --tracking-mode async $gloOpt $ITSRecOpt + taskwrapper itsreco.log o2-its-reco-workflow --tracking-mode async $gloOpt $ITSRecOpt echo "Return status of itsreco: $?" # existing checks From c91f52ea802aee9e43fa68e1b7c597e0bfe45df6 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Thu, 16 Apr 2026 17:51:40 +0200 Subject: [PATCH 065/285] ITS: move all pp settings to dpl-workflow.sh Signed-off-by: Felix Schlepper --- .../ITS/tracking/include/ITStracking/TrackingConfigParam.h | 6 +++--- prodtests/full-system-test/dpl-workflow.sh | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h index cb291b46f5e44..5ffd55f715a1a 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h @@ -25,9 +25,9 @@ struct VertexerParamConfig : public o2::conf::ConfigurableParamHelper Date: Mon, 13 Apr 2026 16:34:19 +0200 Subject: [PATCH 066/285] Remove unnecessary dictionaries in ALICE3 --- .../ALICE3/TRK/reconstruction/CMakeLists.txt | 12 --------- .../src/TRKReconstructionLinkDef.h | 25 ------------------- 2 files changed, 37 deletions(-) delete mode 100644 Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt index 59a7f47955938..b8cb6a88f7163 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt @@ -41,15 +41,3 @@ o2_add_library(TRKReconstruction if(Acts_FOUND) target_compile_definitions(${targetName} PUBLIC O2_WITH_ACTS) endif() - -set(dictHeaders include/TRKReconstruction/TimeFrame.h - include/TRKReconstruction/Clusterer.h) - -if(Acts_FOUND) - list(APPEND dictHeaders include/TRKReconstruction/ClustererACTS.h - include/TRKReconstruction/TrackerACTS.h) -endif() - -o2_target_root_dictionary(TRKReconstruction - HEADERS ${dictHeaders} - LINKDEF src/TRKReconstructionLinkDef.h) diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h deleted file mode 100644 index 1f4c2193b91b1..0000000000000 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h +++ /dev/null @@ -1,25 +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. - -#ifdef __CLING__ - -#pragma link off all globals; -#pragma link off all classes; -#pragma link off all functions; - -#pragma link C++ class o2::trk::TimeFrame < 11> + ; -#pragma link C++ class o2::trk::Clusterer + ; -#ifdef O2_WITH_ACTS -#pragma link C++ class o2::trk::ClustererACTS + ; - -#endif - -#endif From b083788580acd997aaced935f2f107daf0917182 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Fri, 17 Apr 2026 08:43:36 +0200 Subject: [PATCH 067/285] [ALICE3] TRK: Fix getNrof calls to use index in TrackerACTS (#15277) --- .../Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx index 67dcfe25e33bb..732a0acc14b66 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx @@ -261,10 +261,10 @@ void TrackerACTS::clustersToTracks() double totalTime = 0.; LOG(info) << "==== TRK ACTS Tracking ===="; - LOG(info) << "Processing " << mTimeFrame->getNrof() << " ROFs with B = " << mBz << " T"; + LOG(info) << "Processing " << mTimeFrame->getNrof(0) << " ROFs with B = " << mBz << " T"; // Process each ROF - for (int iROF = 0; iROF < mTimeFrame->getNrof(); ++iROF) { + for (int iROF = 0; iROF < mTimeFrame->getNrof(0); ++iROF) { LOG(info) << "Processing ROF " << iROF; // Build space points mCurState = SpacePointBuilding; From b50e6f2d05366106888c1f6918d9eb1be6496a4a Mon Sep 17 00:00:00 2001 From: Stefano Cannito <143754257+scannito@users.noreply.github.com> Date: Fri, 17 Apr 2026 08:53:21 +0200 Subject: [PATCH 068/285] [ALICE3] TRK: update geometry, fix in extrusions, cleanup (#15262) * Update TRK * For L6 the nominal radius corresponds to the outer one * Removed unused variable * Fix extrusions for OT layers * Minor * Negative staggering for L6 --- .../include/TRKSimulation/TRKLayer.h | 35 ++- .../ALICE3/TRK/simulation/src/Detector.cxx | 90 ++++++-- .../ALICE3/TRK/simulation/src/TRKLayer.cxx | 217 +++++++++++++++--- 3 files changed, 282 insertions(+), 60 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h index 6077d9e5f9839..ef4d5657a1b4f 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h @@ -12,11 +12,14 @@ #ifndef ALICEO2_TRK_LAYER_H #define ALICEO2_TRK_LAYER_H +#include "TRKBase/Specs.h" +#include "TRKBase/TRKBaseParam.h" #include + #include -#include "TRKBase/TRKBaseParam.h" -#include "TRKBase/Specs.h" +#include +#include namespace o2 { @@ -68,7 +71,7 @@ class TRKSegmentedLayer : public TRKCylindricalLayer { public: TRKSegmentedLayer() = default; - TRKSegmentedLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode); + TRKSegmentedLayer(int layerNumber, std::string layerName, float rInn, float tiltAngle, int numberOfStaves, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode); ~TRKSegmentedLayer() override = default; TGeoVolume* createSensor() override; @@ -80,7 +83,10 @@ class TRKSegmentedLayer : public TRKCylindricalLayer void createLayer(TGeoVolume* motherVolume) override = 0; protected: + float mTiltAngle; int mNumberOfModules; + int mNumberOfStaves; + bool mIsFlipped = false; // Fixed parameters for the layer, to be set based on the specifications of the chip and module static constexpr double sChipWidth = constants::moduleMLOT::chip::width; @@ -93,6 +99,12 @@ class TRKSegmentedLayer : public TRKCylindricalLayer // TGeo objects outside logical volumes can cause errors static constexpr float sLogicalVolumeThickness = 1.3; + // For the segmented layers, because of tilting and staggering the bounding radii can be different + // from the inner radius and inner radius + thickness. + // This function calculates the bounding radii based on the geometry of the stave and the tilt angle, + // to ensure that the layer volume is large enough to contain all the staves without overlaps. + virtual std::pair getBoundingRadii(double staveWidth) const; + ClassDefOverride(TRKSegmentedLayer, 0); }; @@ -100,14 +112,20 @@ class TRKMLLayer : public TRKSegmentedLayer { public: TRKMLLayer() = default; - TRKMLLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode); + TRKMLLayer(int layerNumber, std::string layerName, float rInn, float staggerOffset, float tiltAngle, int numberOfStaves, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode); ~TRKMLLayer() override = default; TGeoVolume* createStave() override; void createLayer(TGeoVolume* motherVolume) override; private: + float mStaggerOffset; + static constexpr double sStaveWidth = constants::ML::width; + static constexpr int sFlippedLayerNumber = 3; + + // Override to account for the staggering offset present in specific ML layers + std::pair getBoundingRadii(double staveWidth) const override; ClassDefOverride(TRKMLLayer, 0); }; @@ -116,7 +134,7 @@ class TRKOTLayer : public TRKSegmentedLayer { public: TRKOTLayer() = default; - TRKOTLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode); + TRKOTLayer(int layerNumber, std::string layerName, float rInn, float tiltAngle, int numberOfStaves, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode); ~TRKOTLayer() override = default; TGeoVolume* createStave() override; @@ -128,9 +146,12 @@ class TRKOTLayer : public TRKSegmentedLayer static constexpr double sInStaveOverlap = constants::moduleMLOT::gaps::outerEdgeLongSide + constants::moduleMLOT::chip::passiveEdgeReadOut + 0.1; // 1.5mm outer-edge + 1mm deadzone + 1mm (true) overlap static constexpr double sStaveWidth = constants::OT::width - sInStaveOverlap; - ClassDefOverride(TRKOTLayer, 0) + // Override to account for the staggering offset present in OT layers + std::pair getBoundingRadii(double staveWidth) const override; + + ClassDefOverride(TRKOTLayer, 0); }; } // namespace trk } // namespace o2 -#endif // ALICEO2_TRK_LAYER_H \ No newline at end of file +#endif // ALICEO2_TRK_LAYER_H diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx index 66c02a080e0b6..66ace4746d399 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx @@ -9,18 +9,19 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include - -#include -#include -#include +#include "TRKSimulation/Detector.h" #include "DetectorsBase/Stack.h" -#include "TRKSimulation/Hit.h" -#include "TRKSimulation/Detector.h" + #include "TRKBase/TRKBaseParam.h" +#include "TRKSimulation/Hit.h" #include "TRKSimulation/VDGeometryBuilder.h" #include "TRKSimulation/VDSensorRegistry.h" +#include +#include +#include + +#include #include #include @@ -105,14 +106,21 @@ void Detector::configMLOT() break; } case kSegmented: { + const std::vector tiltAngles{11.2f, 11.9f, 11.4f, 0.f, 0.f, 0.f, 0.f, 0.f}; + // const std::vector tiltAngles{10.f, 16.1f, 19.2f, 0.f, 0.f, 0.f, 0.f, 0.f}; + const std::vector nStaves{10, 14, 18, 26, 38, 32, 42, 56}; + // const std::vector nStaves{10, 16, 22, 26, 38, 32, 42, 56}; const std::vector nMods{10, 10, 10, 10, 10, 20, 20, 20}; + + const std::vector stagOffsets{0.f, 0.f, 0.f, 1.17f, 0.89f}; + LOGP(warning, "Loading segmented configuration for ALICE3 TRK"); for (int i{0}; i < 8; ++i) { std::string name = GeometryTGeo::getTRKLayerPattern() + std::to_string(i); - if (i < 4) { - mLayers.push_back(std::make_unique(i, name, rInn[i], nMods[i], thick, MatBudgetParamMode::Thickness)); + if (i < 5) { + mLayers.push_back(std::make_unique(i, name, rInn[i], stagOffsets[i], tiltAngles[i], nStaves[i], nMods[i], thick, MatBudgetParamMode::Thickness)); } else { - mLayers.push_back(std::make_unique(i, name, rInn[i], nMods[i], thick, MatBudgetParamMode::Thickness)); + mLayers.push_back(std::make_unique(i, name, rInn[i], tiltAngles[i], nStaves[i], nMods[i], thick, MatBudgetParamMode::Thickness)); } } break; @@ -153,16 +161,66 @@ void Detector::configFromFile(std::string fileName) } std::string name = GeometryTGeo::getTRKLayerPattern() + std::to_string(layerCount); + switch (trkPars.layoutMLOT) { - case kCylindrical: - mLayers.push_back(std::make_unique(layerCount, name, tmpBuff[0], tmpBuff[1], tmpBuff[2], MatBudgetParamMode::Thickness)); + case kCylindrical: { + // Cylindrical requires at least 3 parameters + if (tmpBuff.size() < 3) { + LOGP(fatal, "Invalid configuration for cylindrical layer {}: insufficient parameters.", layerCount); + } + + // Default mode is Thickness + MatBudgetParamMode mode = MatBudgetParamMode::Thickness; + if (tmpBuff.size() >= 4) { + mode = static_cast(static_cast(tmpBuff[3])); + } + + mLayers.push_back(std::make_unique(layerCount, name, tmpBuff[0], tmpBuff[1], tmpBuff[2], mode)); break; + } case kSegmented: { - int nMods = static_cast(tmpBuff[1]); - if (layerCount < 4) { - mLayers.push_back(std::make_unique(layerCount, name, tmpBuff[0], nMods, tmpBuff[2], MatBudgetParamMode::Thickness)); + // Expected column mapping in the text file (separated by \t): + // tmpBuff[0] = rInn + // tmpBuff[1] = thick + // tmpBuff[2] = tiltAngle + // tmpBuff[3] = nStaves + // tmpBuff[4] = nMods + // tmpBuff[5] = stagOffset (required ONLY for ML) + // tmpBuff[6] = matBudgetMode (optional, default = Thickness) + + // Base parameters for all segmented layers (at least 5 needed) + if (tmpBuff.size() < 5) { + LOGP(fatal, "Invalid configuration for segmented layer {}: missing base parameters.", layerCount); + } + + float rInn = tmpBuff[0]; + float thick = tmpBuff[1]; + float tiltAngle = tmpBuff[2]; + int nStaves = static_cast(tmpBuff[3]); + int nMods = static_cast(tmpBuff[4]); + + // Default mode is Thickness + MatBudgetParamMode mode = MatBudgetParamMode::Thickness; + + if (layerCount < 5) { + // ML layers (0 to 4) require stagOffset (index 5) + if (tmpBuff.size() < 6) { + LOGP(fatal, "Invalid configuration for ML layer {}: stagOffset is missing.", layerCount); + } + float stagOffset = tmpBuff[5]; + + if (tmpBuff.size() >= 7) { + mode = static_cast(static_cast(tmpBuff[6])); + } + + mLayers.push_back(std::make_unique(layerCount, name, rInn, stagOffset, tiltAngle, nStaves, nMods, thick, mode)); } else { - mLayers.push_back(std::make_unique(layerCount, name, tmpBuff[0], nMods, tmpBuff[2], MatBudgetParamMode::Thickness)); + // OT layers (5+) do NOT have stagOffset. The optional mode is at index 5. + if (tmpBuff.size() >= 6) { + mode = static_cast(static_cast(tmpBuff[5])); + } + + mLayers.push_back(std::make_unique(layerCount, name, rInn, tiltAngle, nStaves, nMods, thick, mode)); } break; } diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx index 39c7b3598d19b..7a4b7bef34e03 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx @@ -10,17 +10,21 @@ // or submit itself to any jurisdiction. #include "TRKSimulation/TRKLayer.h" -#include "TRKBase/GeometryTGeo.h" -#include "TRKBase/Specs.h" #include "Framework/Logger.h" -#include +#include "TRKBase/GeometryTGeo.h" +#include "TRKBase/Specs.h" #include +#include #include - #include +#include +#include +#include +#include + namespace o2 { namespace trk @@ -84,9 +88,10 @@ void TRKCylindricalLayer::createLayer(TGeoVolume* motherVolume) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -TRKSegmentedLayer::TRKSegmentedLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode) - : TRKCylindricalLayer(layerNumber, layerName, rInn, numberOfModules * sModuleLength, thickOrX2X0, mode), mNumberOfModules(numberOfModules) +TRKSegmentedLayer::TRKSegmentedLayer(int layerNumber, std::string layerName, float rInn, float tiltAngle, int numberOfStaves, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode) + : TRKCylindricalLayer(layerNumber, layerName, rInn, numberOfModules * sModuleLength, thickOrX2X0, mode), mTiltAngle(tiltAngle), mNumberOfStaves(numberOfStaves), mNumberOfModules(numberOfModules) { + assert(numberOfStaves % 2 == 0 && "Error: numberOfStaves must be even!"); } TGeoVolume* TRKSegmentedLayer::createSensor() @@ -132,22 +137,29 @@ TGeoVolume* TRKSegmentedLayer::createChip() TGeoVolume* sensVol = createSensor(); TGeoCombiTrans* transSens = new TGeoCombiTrans(); - // transSens->SetTranslation(-sDeadzoneWidth / 2, -(mChipThickness - sSensorThickness) / 2, 0); - transSens->SetTranslation(-sDeadzoneWidth / 2, (mChipThickness - sSensorThickness) / 2, 0); - LOGP(debug, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); - chipVol->AddNode(sensVol, 1, transSens); TGeoVolume* deadVol = createDeadzone(); TGeoCombiTrans* transDead = new TGeoCombiTrans(); - // transDead->SetTranslation((sChipWidth - sDeadzoneWidth) / 2, -(mChipThickness - sSensorThickness) / 2, 0); - transDead->SetTranslation((sChipWidth - sDeadzoneWidth) / 2, (mChipThickness - sSensorThickness) / 2, 0); - LOGP(debug, "Inserting {} in {} ", deadVol->GetName(), chipVol->GetName()); - chipVol->AddNode(deadVol, 1, transDead); TGeoVolume* metalVol = createMetalStack(); TGeoCombiTrans* transMetal = new TGeoCombiTrans(); - // transMetal->SetTranslation(0, sSensorThickness / 2, 0); - transMetal->SetTranslation(0, -sSensorThickness / 2, 0); + + if (!mIsFlipped) { + transSens->SetTranslation(-sDeadzoneWidth / 2, (mChipThickness - sSensorThickness) / 2, 0); + transDead->SetTranslation((sChipWidth - sDeadzoneWidth) / 2, (mChipThickness - sSensorThickness) / 2, 0); + transMetal->SetTranslation(0, -sSensorThickness / 2, 0); + } else { + transSens->SetTranslation(-sDeadzoneWidth / 2, -(mChipThickness - sSensorThickness) / 2, 0); + transDead->SetTranslation((sChipWidth - sDeadzoneWidth) / 2, -(mChipThickness - sSensorThickness) / 2, 0); + transMetal->SetTranslation(0, sSensorThickness / 2, 0); + } + + LOGP(debug, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); + chipVol->AddNode(sensVol, 1, transSens); + + LOGP(debug, "Inserting {} in {} ", deadVol->GetName(), chipVol->GetName()); + chipVol->AddNode(deadVol, 1, transDead); + LOGP(debug, "Inserting {} in {} ", metalVol->GetName(), chipVol->GetName()); chipVol->AddNode(metalVol, 1, transMetal); @@ -186,11 +198,59 @@ TGeoVolume* TRKSegmentedLayer::createModule() return moduleVol; } +std::pair TRKSegmentedLayer::getBoundingRadii(double staveWidth) const +{ + const float avgRadius = 0.5 * (mInnerRadius + mOuterRadius); + const float staveSizeX = staveWidth; + const float staveSizeY = mOuterRadius - mInnerRadius; + + /*const float deltaForTilt = 0.5 * (std::sin(TMath::DegToRad() * mTiltAngle) * staveSizeX + std::cos(TMath::DegToRad() * mTiltAngle) * staveSizeY); + + float radiusMin = std::sqrt(avgRadius * avgRadius + 0.25 * staveSizeX * staveSizeX + 0.25 * staveSizeY * staveSizeY - avgRadius * 2. * deltaForTilt); + float radiusMax = std::sqrt(avgRadius * avgRadius + 0.25 * staveSizeX * staveSizeX + 0.25 * staveSizeY * staveSizeY + avgRadius * 2. * deltaForTilt);*/ + + const double alpha = TMath::DegToRad() * std::abs(mTiltAngle); + + // The maximum distance from the center is always the outer top corner + double u_max = avgRadius * std::sin(alpha) + staveSizeX / 2.0; + double v_max = avgRadius * std::cos(alpha) + staveSizeY / 2.0; + double radiusMax = std::sqrt(u_max * u_max + v_max * v_max); + + // The perpendicular distance from the center to the line where the inner face lies + double perpDistance = avgRadius * std::cos(alpha) - staveSizeY / 2.0; + + // The projection of the center along the width of the stave + double projDistance = avgRadius * std::sin(alpha); + + double radiusMin; + if (projDistance <= staveSizeX / 2.0) { + // The center projects directly inside the flat face. + // The closest point is on the face itself, not on the corner + radiusMin = perpDistance; + } else { + // The center projects outside the face. The closest point is the inner corner + double u_min = projDistance - staveSizeX / 2.0; + radiusMin = std::sqrt(u_min * u_min + perpDistance * perpDistance); + } + + // Add a 0.5 mm safety margin to prevent false-positive overlaps in ROOT's geometry checker caused by floating-point inaccuracies + const float precisionMargin = 0.05f; + + return {radiusMin - precisionMargin, radiusMax + precisionMargin}; +} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -TRKMLLayer::TRKMLLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode) - : TRKSegmentedLayer(layerNumber, layerName, rInn, numberOfModules, thickOrX2X0, mode) +TRKMLLayer::TRKMLLayer(int layerNumber, std::string layerName, float rInn, float staggerOffset, float tiltAngle, int numberOfStaves, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode) + : TRKSegmentedLayer(layerNumber, layerName, rInn, tiltAngle, numberOfStaves, numberOfModules, thickOrX2X0, mode), mStaggerOffset(staggerOffset) { + if (mLayerNumber == sFlippedLayerNumber) { + mOuterRadius = rInn; + mInnerRadius = rInn - mChipThickness; + mIsFlipped = true; + mStaggerOffset = -staggerOffset; + LOGP(info, "Layer {} is flipped: sensor and metal stack positions are switched", mLayerNumber); + } } TGeoVolume* TRKMLLayer::createStave() @@ -215,32 +275,43 @@ TGeoVolume* TRKMLLayer::createStave() void TRKMLLayer::createLayer(TGeoVolume* motherVolume) { + // Retrieve exact bounding boundaries and create the logical container volume + auto [rMin, rMax] = getBoundingRadii(sStaveWidth); + TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); - TGeoTube* layer = new TGeoTube(mInnerRadius - 0.333 * sLogicalVolumeThickness, mInnerRadius + 0.667 * sLogicalVolumeThickness, mLength / 2); + // TGeoTube* layer = new TGeoTube(mInnerRadius - 0.333 * sLogicalVolumeThickness, mInnerRadius + 0.667 * sLogicalVolumeThickness, mLength / 2); + TGeoTube* layer = new TGeoTube(rMin, rMax, mLength / 2); TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); layerVol->SetLineColor(kYellow); // Compute the number of staves - int nStaves = (int)std::ceil(mInnerRadius * 2 * TMath::Pi() / sStaveWidth); - nStaves += nStaves % 2; // Require an even number of staves + // int nStaves = (int)std::ceil(mInnerRadius * 2 * TMath::Pi() / sStaveWidth); + // nStaves += nStaves % 2; // Require an even number of staves + + // Nominal average radii used as placement barycenters for the staves + const double avgRadiusInner = 0.5 * (mInnerRadius + mOuterRadius); + const double avgRadiusOuter = avgRadiusInner + mStaggerOffset; // Compute the size of the overlap region - double theta = 2 * TMath::Pi() / nStaves; + double theta = 2. * TMath::Pi() / mNumberOfStaves; double theta1 = std::atan(sStaveWidth / 2 / mInnerRadius); double st = std::sin(theta); double ct = std::cos(theta); double theta2 = std::atan((mInnerRadius * st - sStaveWidth / 2 * ct) / (mInnerRadius * ct + sStaveWidth / 2 * st)); double overlap = (theta1 - theta2) * mInnerRadius; - LOGP(info, "Creating a layer with {} staves and {} mm overlap", nStaves, overlap * 10); + LOGP(info, "Creating a layer with {} staves and {} mm overlap", mNumberOfStaves, overlap * 10); - for (int iStave = 0; iStave < nStaves; iStave++) { + for (int iStave = 0; iStave < mNumberOfStaves; iStave++) { TGeoVolume* staveVol = createStave(); TGeoCombiTrans* trans = new TGeoCombiTrans(); - double theta = 360. * iStave / nStaves; - // TGeoRotation* rot = new TGeoRotation("rot", theta - 90 + 4, 0, 0); - TGeoRotation* rot = new TGeoRotation("rot", theta + 90 + 4, 0, 0); + // If the number of staves is a multiple of 4, rotate by half a stave to avoid having the first one exactly on the x + double phi = (mNumberOfStaves % 4 == 0) ? theta * (iStave + 0.5) : theta * iStave; + double phiDeg = phi * TMath::RadToDeg(); + TGeoRotation* rot = new TGeoRotation("rot", phiDeg + 90 + mTiltAngle, 0, 0); trans->SetRotation(rot); - trans->SetTranslation(mInnerRadius * std::cos(2. * TMath::Pi() * iStave / nStaves), mInnerRadius * std::sin(2 * TMath::Pi() * iStave / nStaves), 0); + // float trueRadius = (mLayerNumber == 3 || mLayerNumber == 4) ? (iStave % 2 == 0 ? mInnerRadius : mInnerRadius + mStaggerOffset) : mInnerRadius; + float trueRadius = (mLayerNumber == 3 || mLayerNumber == 4) ? (iStave % 2 == 0 ? avgRadiusInner : avgRadiusOuter) : avgRadiusInner; + trans->SetTranslation(trueRadius * std::cos(phi), trueRadius * std::sin(phi), 0); LOGP(debug, "Inserting {} in {} ", staveVol->GetName(), layerVol->GetName()); layerVol->AddNode(staveVol, iStave, trans); } @@ -249,10 +320,67 @@ void TRKMLLayer::createLayer(TGeoVolume* motherVolume) motherVolume->AddNode(layerVol, 1, nullptr); } +std::pair TRKMLLayer::getBoundingRadii(double staveWidth) const +{ + // Get the baseline RMin from the base class + auto [defaultRadiusMin, defaultRadiusMax] = TRKSegmentedLayer::getBoundingRadii(staveWidth); + + // If we are not in the staggered layers, return the baseline values + if (mLayerNumber != 3 && mLayerNumber != 4) { + return {defaultRadiusMin, defaultRadiusMax}; + } + + /*// For staggered layers, we must recalculate RMax based on the outer shifted row + const float avgRadiusInner = 0.5 * (mInnerRadius + mOuterRadius); + const float avgRadiusOuter = avgRadiusInner + mStaggerOffset; + + const float staveSizeX = staveWidth; + const float staveSizeY = mOuterRadius - mInnerRadius; + + const float deltaForTiltOuter = 0.5 * (std::sin(TMath::DegToRad() * mTiltAngle) * staveSizeX + std::cos(TMath::DegToRad() * mTiltAngle) * staveSizeY); + + const float radiusMax = std::sqrt(avgRadiusOuter * avgRadiusOuter + 0.25 * staveSizeX * staveSizeX + 0.25 * staveSizeY * staveSizeY + avgRadiusOuter * 2. * deltaForTiltOuter);*/ + + const float avgRadiusInner = 0.5 * (mInnerRadius + mOuterRadius); + const float avgRadiusStaggered = avgRadiusInner + mStaggerOffset; + + const float staveSizeX = staveWidth; + const float staveSizeY = mOuterRadius - mInnerRadius; + const float alpha = TMath::DegToRad() * std::abs(mTiltAngle); + + const float precisionMargin = 0.05f; + + // If the layer is NOT flipped (e.g., Layer 4), the stagger goes outwards + // Therefore, we must recalculate only the maximum radius based on the outer shifted row + if (!mIsFlipped) { + float u_max = avgRadiusStaggered * std::sin(alpha) + staveSizeX / 2.0; + float v_max = avgRadiusStaggered * std::cos(alpha) + staveSizeY / 2.0; + float radiusMax = std::sqrt(u_max * u_max + v_max * v_max); + + return {defaultRadiusMin, radiusMax + precisionMargin}; + } + // If the layer IS flipped (e.g., Layer 3), the stagger goes inwards + // Therefore, we must recalculate only the minimum radius based on the inner shifted row + else { + double perpDistance = avgRadiusStaggered * std::cos(alpha) - staveSizeY / 2.0; + double projDistance = avgRadiusStaggered * std::sin(alpha); + double newRadiusMin; + + if (projDistance <= staveSizeX / 2.0) { + newRadiusMin = perpDistance; + } else { + double u_min = projDistance - staveSizeX / 2.0; + newRadiusMin = std::sqrt(u_min * u_min + perpDistance * perpDistance); + } + + return {newRadiusMin - precisionMargin, defaultRadiusMax}; + } +} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -TRKOTLayer::TRKOTLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode) - : TRKSegmentedLayer(layerNumber, layerName, rInn, numberOfModules, thickOrX2X0, mode) +TRKOTLayer::TRKOTLayer(int layerNumber, std::string layerName, float rInn, float tiltAngle, int numberOfStaves, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode) + : TRKSegmentedLayer(layerNumber, layerName, rInn, tiltAngle, numberOfStaves, numberOfModules, thickOrX2X0, mode) { } @@ -298,8 +426,12 @@ TGeoVolume* TRKOTLayer::createStave() void TRKOTLayer::createLayer(TGeoVolume* motherVolume) { + // Retrieve exact bounding boundaries automatically inherited from TRKSegmentedLayer + auto [rMin, rMax] = getBoundingRadii(sStaveWidth); + TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); - TGeoTube* layer = new TGeoTube(mInnerRadius - 0.333 * sLogicalVolumeThickness, mInnerRadius + 0.667 * sLogicalVolumeThickness, mLength / 2); + // TGeoTube* layer = new TGeoTube(mInnerRadius - 0.333 * sLogicalVolumeThickness, mInnerRadius + 0.667 * sLogicalVolumeThickness, mLength / 2); + TGeoTube* layer = new TGeoTube(rMin, rMax, mLength / 2); TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); layerVol->SetLineColor(kYellow); @@ -307,8 +439,11 @@ void TRKOTLayer::createLayer(TGeoVolume* motherVolume) int nStaves = (int)std::ceil(mInnerRadius * 2 * TMath::Pi() / sStaveWidth); nStaves += nStaves % 2; // Require an even number of staves + // Nominal average radius used as the placement barycenter for all staves + const double avgRadius = 0.5 * (mInnerRadius + mOuterRadius); + // Compute the size of the overlap region - double theta = 2 * TMath::Pi() / nStaves; + double theta = 2. * TMath::Pi() / nStaves; double theta1 = std::atan(sStaveWidth / 2 / mInnerRadius); double st = std::sin(theta); double ct = std::cos(theta); @@ -319,11 +454,12 @@ void TRKOTLayer::createLayer(TGeoVolume* motherVolume) for (int iStave = 0; iStave < nStaves; iStave++) { TGeoVolume* staveVol = createStave(); TGeoCombiTrans* trans = new TGeoCombiTrans(); - double theta = 360. * iStave / nStaves; - // TGeoRotation* rot = new TGeoRotation("rot", theta - 90, 0, 0); - TGeoRotation* rot = new TGeoRotation("rot", theta + 90, 0, 0); + double phi = theta * iStave; + double phiDeg = phi * TMath::RadToDeg(); + TGeoRotation* rot = new TGeoRotation("rot", phiDeg + 90 + mTiltAngle, 0, 0); trans->SetRotation(rot); - trans->SetTranslation(mInnerRadius * std::cos(2. * TMath::Pi() * iStave / nStaves), mInnerRadius * std::sin(2 * TMath::Pi() * iStave / nStaves), 0); + // trans->SetTranslation(mInnerRadius * std::cos(phi), mInnerRadius * std::sin(phi), 0); + trans->SetTranslation(avgRadius * std::cos(phi), avgRadius * std::sin(phi), 0); LOGP(debug, "Inserting {} in {} ", staveVol->GetName(), layerVol->GetName()); layerVol->AddNode(staveVol, iStave, trans); } @@ -331,7 +467,14 @@ void TRKOTLayer::createLayer(TGeoVolume* motherVolume) LOGP(debug, "Inserting {} in {} ", layerVol->GetName(), motherVolume->GetName()); motherVolume->AddNode(layerVol, 1, nullptr); } + +std::pair TRKOTLayer::getBoundingRadii(double staveWidth) const +{ + auto [radiusMin, radiusMax] = TRKSegmentedLayer::getBoundingRadii(staveWidth); + + return {radiusMin - 0.201f, radiusMax}; +} // ClassImp(TRKLayer); } // namespace trk -} // namespace o2 \ No newline at end of file +} // namespace o2 From b1bac33d1cd77bfedf9465b4b5e0665bf86c8dc3 Mon Sep 17 00:00:00 2001 From: altsybee Date: Fri, 17 Apr 2026 09:34:38 +0200 Subject: [PATCH 069/285] [ALICE3] TKR: add post CheckClusterSize.C macro vs Eta (#15274) --- .../ALICE3/TRK/macros/test/CMakeLists.txt | 8 + .../TRK/macros/test/postClusterSizeVsEta.C | 199 ++++++++++++++++++ 2 files changed, 207 insertions(+) create mode 100644 Detectors/Upgrades/ALICE3/TRK/macros/test/postClusterSizeVsEta.C diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt index 54e42c6857249..33d1b4a5afdc6 100644 --- a/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt @@ -49,3 +49,11 @@ o2_add_test_root_macro(CheckClusters.C O2::TRKBase O2::TRKSimulation LABELS trk COMPILE_ONLY) + +o2_add_test_root_macro(postClusterSizeVsEta.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsTRK + O2::SimulationDataFormat + O2::Framework + O2::TRKBase + O2::TRKSimulation + LABELS trk COMPILE_ONLY) diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/postClusterSizeVsEta.C b/Detectors/Upgrades/ALICE3/TRK/macros/test/postClusterSizeVsEta.C new file mode 100644 index 0000000000000..47beaf36f2957 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/postClusterSizeVsEta.C @@ -0,0 +1,199 @@ +// 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 postClusterSizeVsEta.C +/// \brief A post-processing macro to draw average cluster size vs eta + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +using namespace std; + +// ### required input file: CheckClusters.root, which is the output of CheckClusters.C macro +void postClusterSizeVsEta(const std::string& strFileInput = "CheckClusters.root") +{ + gStyle->SetOptStat(0); + + TFile* fileInput = new TFile(strFileInput.c_str()); + TTree* tree = (TTree*)fileInput->Get("ntc"); + std::cout << "Opened tree: " << tree->GetName() << ", entries = " << tree->GetEntries() << std::endl; + + // set branch addresses + Float_t event; + Float_t mcTrackID; + Float_t hitLocX, hitLocZ; + Float_t hitGlobX, hitGlobY, hitGlobZ; + Float_t clusGlobX, clusGlobY, clusGlobZ; + Float_t clusLocX, clusLocZ; + Float_t rofFrame; + Float_t clusSize; + Float_t chipID; + Float_t layer; + Float_t disk; + Float_t subdet; + Float_t row, col; + Float_t pt; + + // set branch addresses + tree->SetBranchAddress("event", &event); + tree->SetBranchAddress("mcTrackID", &mcTrackID); + tree->SetBranchAddress("hitLocX", &hitLocX); + tree->SetBranchAddress("hitLocZ", &hitLocZ); + tree->SetBranchAddress("hitGlobX", &hitGlobX); + tree->SetBranchAddress("hitGlobY", &hitGlobY); + tree->SetBranchAddress("hitGlobZ", &hitGlobZ); + tree->SetBranchAddress("clusGlobX", &clusGlobX); + tree->SetBranchAddress("clusGlobY", &clusGlobY); + tree->SetBranchAddress("clusGlobZ", &clusGlobZ); + tree->SetBranchAddress("clusLocX", &clusLocX); + tree->SetBranchAddress("clusLocZ", &clusLocZ); + tree->SetBranchAddress("rofFrame", &rofFrame); + tree->SetBranchAddress("clusSize", &clusSize); + tree->SetBranchAddress("chipID", &chipID); + tree->SetBranchAddress("layer", &layer); + tree->SetBranchAddress("disk", &disk); + tree->SetBranchAddress("subdet", &subdet); + tree->SetBranchAddress("row", &row); + tree->SetBranchAddress("col", &col); + tree->SetBranchAddress("pt", &pt); + + // Some QA histograms + TH1F* hPt = new TH1F("hPt", "p_{T};p_{T};Entries", 100, 0., 10.); + TH1F* hClusSize = new TH1F("hClusSize", "Cluster size;clusSize;Entries", 20, 0., 20.); + TH1F* hLayer = new TH1F("hLayer", "Layer;layer;Entries", 20, -0.5, 19.5); + TH1F* hDxGlob = new TH1F("hDxGlob", "clusGlobX - hitGlobX;#DeltaX [global];Entries", 200, -1., 1.); + TH1F* hDzGlob = new TH1F("hDzGlob", "clusGlobZ - hitGlobZ;#DeltaZ [global];Entries", 200, -1., 1.); + TH2F* hHitXY = new TH2F("hHitXY", "Hit global XY;hitGlobX;hitGlobY", 200, -20., 20., 200, -20., 20.); + TH2F* hClusVsHitX = new TH2F("hClusVsHitX", "clusGlobX vs hitGlobX;hitGlobX;clusGlobX", 200, -20., 20., 200, -20., 20.); + + // histograms for cluster size vs eta for each barrel layer: + const int nLayers = 11; + TH2F* hClustSizePerLayerVsEta[nLayers]; + for (int i = 0; i < nLayers; i++) { + hClustSizePerLayerVsEta[i] = new TH2F(Form("hClustSizePerLayerVsEta_Lay%d", i), Form("Cluster size vs eta for layer %d;#eta;Cluster size", i), 200, -5, 5, 101, -0.5, 100.5); + } + + // Loop over entries + const Long64_t nEntries = tree->GetEntries(); + for (Long64_t i = 0; i < nEntries; ++i) { + tree->GetEntry(i); + + // Fill QA histograms + float dXGlob = clusGlobX - hitGlobX; + float dZGlob = clusGlobZ - hitGlobZ; + hPt->Fill(pt); + hClusSize->Fill(clusSize); + hLayer->Fill(layer); + hDxGlob->Fill(dXGlob); + hDzGlob->Fill(dZGlob); + hHitXY->Fill(hitGlobX, hitGlobY); + hClusVsHitX->Fill(hitGlobX, clusGlobX); + + // cls size vs eta: + float clustR = sqrt(clusGlobX * clusGlobX + clusGlobY * clusGlobY); + float clustPhi = atan2(clusGlobY, clusGlobX); + float clustTheta = atan2(clustR, clusGlobZ); + float clustEta = -log(tan(clustTheta / 2)); + + // !!! important: to avoid VD layers (numeration for ML starts from 0, while VD layers are also numbered as 0,1,2) + if (clustR > 5) // cm + hClustSizePerLayerVsEta[(int)layer + 3]->Fill(clustEta, clusSize); + else if (layer < 3) // VD layers + hClustSizePerLayerVsEta[(int)layer]->Fill(clustEta, clusSize); + + // progress print + if ((i + 1) % 200000 == 0) { + std::cout << "Processed " << (i + 1) << " / " << nEntries << " entries" << std::endl; + } + } + + // Save histograms to file + TFile* fout = TFile::Open("clusterSizes_vs_eta.root", "RECREATE"); + hPt->Write(); + hClusSize->Write(); + hLayer->Write(); + hDxGlob->Write(); + hDzGlob->Write(); + hHitXY->Write(); + hClusVsHitX->Write(); + + // draw some QA histograms + TCanvas* c1 = new TCanvas("canv_clusters_QA", "Clusters QA", 1200, 800); + c1->Divide(2, 2); + c1->cd(1); + hPt->Draw(); + c1->cd(2); + hClusSize->Draw(); + c1->cd(3); + hDxGlob->Draw(); + c1->cd(4); + hHitXY->Draw("COLZ"); + + int colors[] = {kRed, kBlue + 1, kMagenta + 1, + kRed, kBlue + 1, kMagenta + 1, + kCyan + 1, kGray + 2, kRed, kBlue, kMagenta + 1, kCyan, kAzure + 1, kOrange - 9, kRed + 2, kBlue + 2, kMagenta + 2}; + + TCanvas* canv_clsSize_vs_eta[nLayers]; + TProfile* profPerLayerVsEta[nLayers]; + for (int i = 0; i < nLayers; i++) { + canv_clsSize_vs_eta[i] = new TCanvas(Form("canv_clsSize_vs_eta_Lay%d", i), Form("Cluster size vs eta for layer %d", i), 800, 600); + hClustSizePerLayerVsEta[i]->Draw("COLZ"); + gPad->SetLogz(); + profPerLayerVsEta[i] = hClustSizePerLayerVsEta[i]->ProfileX(); + profPerLayerVsEta[i]->SetLineColor(colors[i]); + profPerLayerVsEta[i]->SetMarkerColor(colors[i]); + profPerLayerVsEta[i]->SetMarkerStyle(i < 8 ? 20 : 24); + profPerLayerVsEta[i]->SetTitle(";#eta;#LTcluster size#GT"); + profPerLayerVsEta[i]->DrawCopy("same"); + + hClustSizePerLayerVsEta[i]->Write(); + profPerLayerVsEta[i]->Write(); + } + + // ### canvas with profiles for 3 VD layers + TCanvas* canv_av_clsSize_vs_eta_VD_layers = new TCanvas("canv_clsSize_vs_eta_VD_layers", "Cluster size vs eta for VD layers", 800, 600); + TLegend* legLayersVD = new TLegend(0.3, 0.72, 0.65, 0.89); + for (int i = 0; i < 3; i++) { + profPerLayerVsEta[i]->GetYaxis()->SetRangeUser(0., 60.); + profPerLayerVsEta[i]->DrawCopy(i == 0 ? "P" : "P same"); + legLayersVD->AddEntry(profPerLayerVsEta[i], Form("VD layer %d", i), "P"); + } + legLayersVD->Draw(); + gPad->SetGrid(); + canv_av_clsSize_vs_eta_VD_layers->SaveAs("clsSize_vs_eta_VD_layers.png"); + canv_av_clsSize_vs_eta_VD_layers->Write(); + + // ### canvas with profiles for MLOT layers + TCanvas* canv_av_clsSize_vs_eta_MLOT_layers = new TCanvas("canv_clsSize_vs_eta_MLOT_layers", "Cluster size vs eta for MLOT layers", 800, 600); + TLegend* legLayersMLOT = new TLegend(0.3, 0.52, 0.65, 0.89); + for (int i = 3; i < nLayers; i++) { + profPerLayerVsEta[i]->GetYaxis()->SetRangeUser(0., 12.5); + profPerLayerVsEta[i]->GetXaxis()->SetRangeUser(-3.5, 3.5); + profPerLayerVsEta[i]->DrawCopy(i == 3 ? "P" : "P same"); + legLayersMLOT->AddEntry(profPerLayerVsEta[i], Form("MLOT layer %d", i), "P"); + } + legLayersMLOT->Draw(); + gPad->SetGrid(); + canv_av_clsSize_vs_eta_MLOT_layers->SaveAs("clsSize_vs_eta_MLOT_layers.png"); + canv_av_clsSize_vs_eta_MLOT_layers->Write(); +} \ No newline at end of file From b61cf4a80bcadf909cae6135b412faa54b92f106 Mon Sep 17 00:00:00 2001 From: shahoian Date: Thu, 16 Apr 2026 17:28:07 +0200 Subject: [PATCH 070/285] Tentative improvement of 3-body decay cov. matrix creation --- DataFormats/Reconstruction/src/Decay3Body.cxx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/DataFormats/Reconstruction/src/Decay3Body.cxx b/DataFormats/Reconstruction/src/Decay3Body.cxx index aa071cea675cd..eb1b7ea1fd57d 100644 --- a/DataFormats/Reconstruction/src/Decay3Body.cxx +++ b/DataFormats/Reconstruction/src/Decay3Body.cxx @@ -16,15 +16,14 @@ using namespace o2::dataformats; Decay3Body::Decay3Body(const std::array& xyz, const std::array& pxyz, const std::array& covxyz, const Track& tr0, const Track& tr1, const Track& tr2, o2::track::PID pid) : mProngs{tr0, tr1, tr2} { - std::array cov{}, cov1{}, cov2{}; - tr0.getCovXYZPxPyPzGlo(cov); + std::array cov{}, cov0{}, cov1{}, cov2{}; + tr0.getCovXYZPxPyPzGlo(cov0); tr1.getCovXYZPxPyPzGlo(cov1); tr2.getCovXYZPxPyPzGlo(cov2); - for (int i = 0; i < 21; i++) { - cov[i] += cov1[i] + cov2[i]; - } + constexpr int MomInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component for (int i = 0; i < 6; i++) { cov[i] = covxyz[i]; + cov[MomInd[i]] = cov0[MomInd[i]] + cov1[MomInd[i]] + cov2[MomInd[i]]; } this->set(xyz, pxyz, cov, tr0.getCharge() + tr1.getCharge() + tr2.getCharge(), true, pid); } From 1e1c6e59cd667daec5e23981670bd3c767a710c0 Mon Sep 17 00:00:00 2001 From: shahoian Date: Fri, 17 Apr 2026 14:49:54 +0200 Subject: [PATCH 071/285] Fix typo in the TPCTimeSeriesSpec --- Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx b/Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx index ee3acc808ccb7..ac3ff15fd3a29 100644 --- a/Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx +++ b/Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx @@ -1377,7 +1377,7 @@ class TPCTimeSeries : public Task if (propTPCOk) { // store delta parameters deltaP0OuterITS = trackTmp.getParam(0) - trackTmpOut.getParam(0); - deltaP1OuterITS = trackTmp.getParam(1) - trackTmpOut.getParam(2); + deltaP1OuterITS = trackTmp.getParam(1) - trackTmpOut.getParam(1); deltaP2OuterITS = trackTmp.getParam(2) - trackTmpOut.getParam(2); deltaP3OuterITS = trackTmp.getParam(3) - trackTmpOut.getParam(3); deltaP4OuterITS = trackTmp.getParam(4) - trackTmpOut.getParam(4); From 8a51b175ab3fd3bf02ced8bd3c6eb837407f1ff1 Mon Sep 17 00:00:00 2001 From: Andreas Morsch Date: Fri, 17 Apr 2026 09:23:37 +0200 Subject: [PATCH 072/285] link to neutron point-like cross-sections needed starting from FLUKA_4.5 --- Detectors/gconfig/src/FlukaConfig.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/Detectors/gconfig/src/FlukaConfig.cxx b/Detectors/gconfig/src/FlukaConfig.cxx index 4723d98244ca9..fe5f73bc7026c 100644 --- a/Detectors/gconfig/src/FlukaConfig.cxx +++ b/Detectors/gconfig/src/FlukaConfig.cxx @@ -42,6 +42,7 @@ void linkFlukaFiles() gSystem->Exec("ln -s $FLUKADATA/random.dat ."); gSystem->Exec("ln -s $FLUKADATA/dnr.dat ."); gSystem->Exec("ln -s $FLUKADATA/nunstab.data ."); + gSystem->Exec("ln -s $FLUKADATA/neutron ."); // Give some meaningfull name to the output gSystem->Exec("ln -s fluka.out fort.11"); gSystem->Exec("ln -s fluka.err fort.15"); From 5500dfcc8141feaf3d86bfb0f43f4b990353edb0 Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Thu, 26 Mar 2026 11:34:49 +0100 Subject: [PATCH 073/285] Cleanup: Remove old version of jobutils and cpulimit tool Remove code no longer used --- Utilities/Tools/CMakeLists.txt | 2 - Utilities/Tools/cpulimit/.clang-format | 2 - Utilities/Tools/cpulimit/CMakeLists.txt | 16 - Utilities/Tools/cpulimit/README | 2 - Utilities/Tools/cpulimit/cpulimit.c | 529 ------------------ Utilities/Tools/cpulimit/list.c | 148 ----- Utilities/Tools/cpulimit/list.h | 138 ----- Utilities/Tools/cpulimit/process_group.c | 219 -------- Utilities/Tools/cpulimit/process_group.h | 55 -- Utilities/Tools/cpulimit/process_iterator.c | 49 -- Utilities/Tools/cpulimit/process_iterator.h | 97 ---- .../Tools/cpulimit/process_iterator_apple.c | 148 ----- .../Tools/cpulimit/process_iterator_freebsd.c | 119 ---- .../Tools/cpulimit/process_iterator_linux.c | 198 ------- prodtests/full_system_test.sh | 2 +- prodtests/full_system_test_ci_extra_tests.sh | 2 +- prodtests/full_system_test_pipeline.sh | 4 +- prodtests/sim_challenge.sh | 2 +- run/SimExamples/SimAsService_biasing1/run.sh | 4 +- 19 files changed, 7 insertions(+), 1729 deletions(-) delete mode 100644 Utilities/Tools/cpulimit/.clang-format delete mode 100644 Utilities/Tools/cpulimit/CMakeLists.txt delete mode 100644 Utilities/Tools/cpulimit/README delete mode 100644 Utilities/Tools/cpulimit/cpulimit.c delete mode 100644 Utilities/Tools/cpulimit/list.c delete mode 100644 Utilities/Tools/cpulimit/list.h delete mode 100644 Utilities/Tools/cpulimit/process_group.c delete mode 100644 Utilities/Tools/cpulimit/process_group.h delete mode 100644 Utilities/Tools/cpulimit/process_iterator.c delete mode 100644 Utilities/Tools/cpulimit/process_iterator.h delete mode 100644 Utilities/Tools/cpulimit/process_iterator_apple.c delete mode 100644 Utilities/Tools/cpulimit/process_iterator_freebsd.c delete mode 100644 Utilities/Tools/cpulimit/process_iterator_linux.c diff --git a/Utilities/Tools/CMakeLists.txt b/Utilities/Tools/CMakeLists.txt index 55048623739b9..04002ad85dae7 100644 --- a/Utilities/Tools/CMakeLists.txt +++ b/Utilities/Tools/CMakeLists.txt @@ -9,9 +9,7 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. -add_subdirectory(cpulimit) install(PROGRAMS monitor-mem.sh DESTINATION share/scripts/) -install(PROGRAMS jobutils.sh DESTINATION share/scripts/) install(PROGRAMS jobutils2.sh DESTINATION share/scripts/) install(PROGRAMS grid_submit.sh DESTINATION share/scripts/) diff --git a/Utilities/Tools/cpulimit/.clang-format b/Utilities/Tools/cpulimit/.clang-format deleted file mode 100644 index a43d914ec38dd..0000000000000 --- a/Utilities/Tools/cpulimit/.clang-format +++ /dev/null @@ -1,2 +0,0 @@ -DisableFormat: true -SortIncludes: false \ No newline at end of file diff --git a/Utilities/Tools/cpulimit/CMakeLists.txt b/Utilities/Tools/cpulimit/CMakeLists.txt deleted file mode 100644 index f1109c65fdb69..0000000000000 --- a/Utilities/Tools/cpulimit/CMakeLists.txt +++ /dev/null @@ -1,16 +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. - -add_executable(cpulimit - cpulimit.c list.c process_group.c process_iterator.c) -target_compile_definitions(cpulimit PUBLIC _GNU_SOURCE) - -install(TARGETS cpulimit DESTINATION share/scripts/) diff --git a/Utilities/Tools/cpulimit/README b/Utilities/Tools/cpulimit/README deleted file mode 100644 index 20f543f9491ee..0000000000000 --- a/Utilities/Tools/cpulimit/README +++ /dev/null @@ -1,2 +0,0 @@ -These sources have been copied from https://github.com/opsengine/cpulimit -commit f4d2682804931e. \ No newline at end of file diff --git a/Utilities/Tools/cpulimit/cpulimit.c b/Utilities/Tools/cpulimit/cpulimit.c deleted file mode 100644 index e35a1565253e7..0000000000000 --- a/Utilities/Tools/cpulimit/cpulimit.c +++ /dev/null @@ -1,529 +0,0 @@ -/** - * - * cpulimit - a CPU limiter for Linux - * - * Copyright (C) 2005-2012, by: Angelo Marletta - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - ************************************************************** - * - * This is a simple program to limit the cpu usage of a process - * If you modify this code, send me a copy please - * - * Get the latest version at: http://github.com/opsengine/cpulimit - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if defined(__APPLE__) -#include -#endif -#include -#include -#include - -#if defined(__APPLE__) || defined(__FREEBSD__) -#include -#endif - -#include "process_group.h" -#include "list.h" - -#ifdef HAVE_SYS_SYSINFO_H -#include -#endif - -//some useful macro -#ifndef MIN -#define MIN(a,b) (((a)<(b))?(a):(b)) -#endif -#ifndef MAX -#define MAX(a,b) (((a)>(b))?(a):(b)) -#endif - -//control time slot in microseconds -//each slot is splitted in a working slice and a sleeping slice -//TODO: make it adaptive, based on the actual system load -#define TIME_SLOT 100000 - -#define MAX_PRIORITY -10 - -/* GLOBAL VARIABLES */ - -//the "family" -struct process_group pgroup; -//pid of cpulimit -pid_t cpulimit_pid; -//name of this program (maybe cpulimit...) -char *program_name; - -//number of cpu -int NCPU; - -/* CONFIGURATION VARIABLES */ - -//verbose mode -int verbose = 0; -//lazy mode (exits if there is no process) -int lazy = 0; - -//SIGINT and SIGTERM signal handler -static void quit(int sig) -{ - //let all the processes continue if stopped - struct list_node *node = NULL; - if (pgroup.proclist != NULL) - { - for (node = pgroup.proclist->first; node != NULL; node = node->next) { - struct process *p = (struct process*)(node->data); - kill(p->pid, SIGCONT); - } - close_process_group(&pgroup); - } - //fix ^C little problem - printf("\r"); - fflush(stdout); - exit(0); -} - -//return t1-t2 in microseconds (no overflow checks, so better watch out!) -static inline unsigned long timediff(const struct timeval *t1,const struct timeval *t2) -{ - return (t1->tv_sec - t2->tv_sec) * 1000000 + (t1->tv_usec - t2->tv_usec); -} - -static void print_usage(FILE *stream, int exit_code) -{ - fprintf(stream, "Usage: %s [OPTIONS...] TARGET\n", program_name); - fprintf(stream, " OPTIONS\n"); - fprintf(stream, " -l, --limit=N percentage of cpu allowed from 0 to %d (required)\n", 100*NCPU); - fprintf(stream, " -v, --verbose show control statistics\n"); - fprintf(stream, " -z, --lazy exit if there is no target process, or if it dies\n"); - fprintf(stream, " -i, --include-children limit also the children processes\n"); - fprintf(stream, " -h, --help display this help and exit\n"); - fprintf(stream, " TARGET must be exactly one of these:\n"); - fprintf(stream, " -p, --pid=N pid of the process (implies -z)\n"); - fprintf(stream, " -e, --exe=FILE name of the executable program file or path name\n"); - fprintf(stream, " COMMAND [ARGS] run this command and limit it (implies -z)\n"); - fprintf(stream, "\nReport bugs to .\n"); - exit(exit_code); -} - -static void increase_priority() { - //find the best available nice value - int old_priority = getpriority(PRIO_PROCESS, 0); - int priority = old_priority; - while (setpriority(PRIO_PROCESS, 0, priority-1) == 0 && priority>MAX_PRIORITY) { - priority--; - } - if (priority != old_priority) { - if (verbose) { printf("Priority changed to %d\n", priority); } - } - else { - if (verbose) { printf("Warning: Cannot change priority. Run as root or renice for best results.\n"); } - } -} - -/* Get the number of CPUs */ -static int get_ncpu() { - int ncpu; -#ifdef _SC_NPROCESSORS_ONLN - ncpu = sysconf(_SC_NPROCESSORS_ONLN); -#elif defined __APPLE__ - int mib[2] = {CTL_HW, HW_NCPU}; - size_t len = sizeof(ncpu); - sysctl(mib, 2, &ncpu, &len, NULL, 0); -#elif defined _GNU_SOURCE - ncpu = get_nprocs(); -#else - ncpu = -1; -#endif - return ncpu; -} - -int get_pid_max() -{ -#ifdef __linux__ - //read /proc/sys/kernel/pid_max - static char buffer[1024]; - FILE *fd = fopen("/proc/sys/kernel/pid_max", "r"); - if (fd==NULL) { return -1; } - if (fgets(buffer, sizeof(buffer), fd)==NULL) { - fclose(fd); - return -1; - } - fclose(fd); - return atoi(buffer); -#elif defined __FreeBSD__ - return 99998; -#elif defined __APPLE__ - return 99998; -#endif -} - -void limit_process(pid_t pid, double limit, int include_children) -{ - //slice of the slot in which the process is allowed to run - struct timespec twork; - //slice of the slot in which the process is stopped - struct timespec tsleep; - //when the last twork has started - struct timeval startwork; - //when the last twork has finished - struct timeval endwork; - //initialization - memset(&twork, 0, sizeof(struct timespec)); - memset(&tsleep, 0, sizeof(struct timespec)); - memset(&startwork, 0, sizeof(struct timeval)); - memset(&endwork, 0, sizeof(struct timeval)); - //last working time in microseconds - unsigned long workingtime = 0; - //generic list item - struct list_node *node; - //counter - int c = 0; - - //get a better priority - increase_priority(); - - //build the family - init_process_group(&pgroup, pid, include_children); - - if (verbose) { printf("Members in the process group owned by %d: %d\n", pgroup.target_pid, pgroup.proclist->count); } - - //rate at which we are keeping active the processes (range 0-1) - //1 means that the process are using all the twork slice - double workingrate = -1; - while(1) { - update_process_group(&pgroup); - - if (pgroup.proclist->count==0) { - if (verbose) { printf("No more processes.\n"); } - break; - } - - //total cpu actual usage (range 0-1) - //1 means that the processes are using 100% cpu - double pcpu = -1; - - //estimate how much the controlled processes are using the cpu in the working interval - for (node = pgroup.proclist->first; node != NULL; node = node->next) { - struct process *proc = (struct process*)(node->data); - if (proc->cpu_usage < 0) { - continue; - } - if (pcpu < 0) { pcpu = 0; } - pcpu += proc->cpu_usage; - } - - //adjust work and sleep time slices - if (pcpu < 0) { - //it's the 1st cycle, initialize workingrate - pcpu = limit; - workingrate = limit; - twork.tv_nsec = TIME_SLOT * limit * 1000; - } - else { - //adjust workingrate - workingrate = MIN(workingrate / pcpu * limit, 1); - twork.tv_nsec = TIME_SLOT * 1000 * workingrate; - } - tsleep.tv_nsec = TIME_SLOT * 1000 - twork.tv_nsec; - - if (verbose) { - if (c%200==0) { - printf("\n%%CPU\twork quantum\tsleep quantum\tactive rate\n"); - } - if (c%10==0 && c>0) { - printf("%0.2lf%%\t%6ld us\t%6ld us\t%0.2lf%%\n", pcpu*100, twork.tv_nsec/1000, tsleep.tv_nsec/1000, workingrate*100); - } - } - - //resume processes - node = pgroup.proclist->first; - while (node != NULL) - { - struct list_node *next_node = node->next; - struct process *proc = (struct process*)(node->data); - if (kill(proc->pid,SIGCONT) != 0) { - //process is dead, remove it from family - if (verbose) { fprintf(stderr, "SIGCONT failed. Process %d dead!\n", proc->pid); } - //remove process from group - delete_node(pgroup.proclist, node); - remove_process(&pgroup, proc->pid); - } - node = next_node; - } - - //now processes are free to run (same working slice for all) - gettimeofday(&startwork, NULL); - nanosleep(&twork, NULL); - gettimeofday(&endwork, NULL); - workingtime = timediff(&endwork, &startwork); - - long delay = workingtime - twork.tv_nsec/1000; - if (c>0 && delay>10000) { - //delay is too much! signal to user? - //fprintf(stderr, "%d %ld us\n", c, delay); - } - - if (tsleep.tv_nsec>0) { - //stop processes only if tsleep>0 - node = pgroup.proclist->first; - while (node != NULL) - { - struct list_node *next_node = node->next; - struct process *proc = (struct process*)(node->data); - if (kill(proc->pid,SIGSTOP)!=0) { - //process is dead, remove it from family - if (verbose) { fprintf(stderr, "SIGSTOP failed. Process %d dead!\n", proc->pid); } - //remove process from group - delete_node(pgroup.proclist, node); - remove_process(&pgroup, proc->pid); - } - node = next_node; - } - //now the processes are sleeping - nanosleep(&tsleep,NULL); - } - c++; - } - close_process_group(&pgroup); -} - -int main(int argc, char **argv) { - //argument variables - const char *exe = NULL; - int perclimit = 0; - int exe_ok = 0; - int pid_ok = 0; - int limit_ok = 0; - pid_t pid = 0; - int include_children = 0; - - //get program name - char *p = (char*)strrchr(argv[0], '/'); - program_name = p==NULL ? argv[0] : (p+1); - //get current pid - cpulimit_pid = getpid(); - //get cpu count - NCPU = get_ncpu(); - - //parse arguments - int next_option; - int option_index = 0; - //A string listing valid short options letters - const char* short_options = "+p:e:l:vzih"; - //An array describing valid long options - const struct option long_options[] = { - { "pid", required_argument, NULL, 'p' }, - { "exe", required_argument, NULL, 'e' }, - { "limit", required_argument, NULL, 'l' }, - { "verbose", no_argument, NULL, 'v' }, - { "lazy", no_argument, NULL, 'z' }, - { "include-children", no_argument, NULL, 'i' }, - { "help", no_argument, NULL, 'h' }, - { 0, 0, 0, 0 } - }; - - do { - next_option = getopt_long(argc, argv, short_options,long_options, &option_index); - switch(next_option) { - case 'p': - pid = atoi(optarg); - pid_ok = 1; - break; - case 'e': - exe = optarg; - exe_ok = 1; - break; - case 'l': - perclimit = atoi(optarg); - limit_ok = 1; - break; - case 'v': - verbose = 1; - break; - case 'z': - lazy = 1; - break; - case 'i': - include_children = 1; - break; - case 'h': - print_usage(stdout, 1); - break; - case '?': - print_usage(stderr, 1); - break; - case -1: - break; - default: - abort(); - } - } while(next_option != -1); - - if (pid_ok && (pid <= 1 || pid >= get_pid_max())) { - fprintf(stderr,"Error: Invalid value for argument PID\n"); - print_usage(stderr, 1); - exit(1); - } - if (pid != 0) { - lazy = 1; - } - - if (!limit_ok) { - fprintf(stderr,"Error: You must specify a cpu limit percentage\n"); - print_usage(stderr, 1); - exit(1); - } - double limit = perclimit / 100.0; - if (limit<0 || limit >NCPU) { - fprintf(stderr,"Error: limit must be in the range 0-%d00\n", NCPU); - print_usage(stderr, 1); - exit(1); - } - - int command_mode = optind < argc; - if (exe_ok + pid_ok + command_mode == 0) { - fprintf(stderr,"Error: You must specify one target process, either by name, pid, or command line\n"); - print_usage(stderr, 1); - exit(1); - } - - if (exe_ok + pid_ok + command_mode > 1) { - fprintf(stderr,"Error: You must specify exactly one target process, either by name, pid, or command line\n"); - print_usage(stderr, 1); - exit(1); - } - - //all arguments are ok! - signal(SIGINT, quit); - signal(SIGTERM, quit); - - //print the number of available cpu - if (verbose) { printf("%d cpu detected\n", NCPU); } - - if (command_mode) { - int i; - //executable file - const char *cmd = argv[optind]; - //command line arguments - char **cmd_args = (char**)malloc((argc-optind + 1) * sizeof(char*)); - if (cmd_args==NULL) { exit(2); } - for (i=0; i 0) { - //parent - int status_process; - int status_limiter; - waitpid(child, &status_process, 0); - waitpid(limiter, &status_limiter, 0); - if (WIFEXITED(status_process)) { - if (verbose) { printf("Process %d terminated with exit status %d\n", child, (int)WEXITSTATUS(status_process)); } - exit(WEXITSTATUS(status_process)); - } - printf("Process %d terminated abnormally\n", child); - exit(status_process); - } - else { - //limiter code - if (verbose) { printf("Limiting process %d\n",child); } - limit_process(child, limit, include_children); - exit(0); - } - } - } - - while(1) { - //look for the target process..or wait for it - pid_t ret = 0; - if (pid_ok) { - //search by pid - ret = find_process_by_pid(pid); - if (ret == 0) { - printf("No process found\n"); - } - else if (ret < 0) { - printf("Process found but you aren't allowed to control it\n"); - } - } - else { - //search by file or path name - ret = find_process_by_name(exe); - if (ret == 0) { - printf("No process found\n"); - } - else if (ret < 0) { - printf("Process found but you aren't allowed to control it\n"); - } - else { - pid = ret; - } - } - if (ret > 0) { - if (ret == cpulimit_pid) { - printf("Target process %d is cpulimit itself! Aborting because it makes no sense\n", ret); - exit(1); - } - printf("Process %d found\n", pid); - //control - limit_process(pid, limit, include_children); - } - if (lazy) { break; } - sleep(2); - }; - - exit(0); -} diff --git a/Utilities/Tools/cpulimit/list.c b/Utilities/Tools/cpulimit/list.c deleted file mode 100644 index 2ac36708d4a08..0000000000000 --- a/Utilities/Tools/cpulimit/list.c +++ /dev/null @@ -1,148 +0,0 @@ -/** - * - * cpulimit - a CPU limiter for Linux - * - * Copyright (C) 2005-2012, by: Angelo Marletta - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include -#include - -#include "list.h" - -#define EMPTYLIST NULL - -void init_list(struct list *l,int keysize) { - l->first=l->last=NULL; - l->keysize=keysize; - l->count=0; -} - -struct list_node *add_elem(struct list *l,void *elem) { - struct list_node *newnode=(struct list_node*)malloc(sizeof(struct list_node)); - newnode->data=elem; - newnode->previous=l->last; - newnode->next=NULL; - if (l->count==0) { - l->first=l->last=newnode; - } - else { - l->last->next=newnode; - l->last=newnode; - } - l->count++; - return newnode; -} - -void delete_node(struct list *l,struct list_node *node) { - if (l->count==1) { - l->first=l->last=NULL; - } - else if (node==l->first) { - node->next->previous=NULL; - l->first=node->next; - } - else if (node==l->last) { - node->previous->next=NULL; - l->last=node->previous; - } - else { - node->previous->next=node->next; - node->next->previous=node->previous; - } - l->count--; - free(node); -} - -void destroy_node(struct list *l,struct list_node *node) { - free(node->data); - node->data=NULL; - delete_node(l,node); -} - -int is_empty_list(struct list *l) { - return (l->count==0?TRUE:FALSE); -} - -int get_list_count(struct list *l) { - return l->count; -} - -void *first_elem(struct list *l) { - return l->first->data; -} - -struct list_node *first_node(struct list *l) { - return l->first; -} - -void *last_elem(struct list *l) { - return l->last->data; -} - -struct list_node *last_node(struct list *l) { - return l->last; -} - -struct list_node *xlocate_node(struct list *l,void *elem,int offset,int length) { - struct list_node *tmp; - tmp=l->first; - while(tmp!=NULL) { - if(!memcmp((char*)tmp->data+offset,elem,length==0?l->keysize:length)) { return (tmp); } - tmp=tmp->next; - } - return EMPTYLIST; -} - -struct list_node *locate_node(struct list *l,void *elem) { - return(xlocate_node(l,elem,0,0)); -} - -void *xlocate_elem(struct list *l,void *elem,int offset,int length) { - struct list_node *node=xlocate_node(l,elem,offset,length); - return(node==NULL?NULL:node->data); -} - -void *locate_elem(struct list *l,void *elem) { - return(xlocate_elem(l,elem,0,0)); -} - -void clear_list(struct list *l) { - while(l->first!=EMPTYLIST) { - struct list_node *tmp; - tmp=l->first; - l->first=l->first->next; - free(tmp); - tmp=NULL; - } - l->last=EMPTYLIST; - l->count=0; -} - -void destroy_list(struct list *l) { - while(l->first!=EMPTYLIST) { - struct list_node *tmp; - tmp=l->first; - l->first=l->first->next; - free(tmp->data); - tmp->data=NULL; - free(tmp); - tmp=NULL; - } - l->last=EMPTYLIST; - l->count=0; -} diff --git a/Utilities/Tools/cpulimit/list.h b/Utilities/Tools/cpulimit/list.h deleted file mode 100644 index 0b43a2b39c0f3..0000000000000 --- a/Utilities/Tools/cpulimit/list.h +++ /dev/null @@ -1,138 +0,0 @@ -/** - * - * cpulimit - a CPU limiter for Linux - * - * Copyright (C) 2005-2012, by: Angelo Marletta - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef __LIST__ - -#define __LIST__ - -#ifndef TRUE - #define TRUE 1 - #define FALSE 0 -#endif - -struct list_node { - //pointer to the content of the node - void *data; - //pointer to previous node - struct list_node *previous; - //pointer to next node - struct list_node *next; -}; - -struct list { - //first node - struct list_node *first; - //last node - struct list_node *last; - //size of the search key in bytes - int keysize; - //element count - int count; -}; - -/* - * Initialize a list, with a specified key size - */ -void init_list(struct list *l,int keysize); - -/* - * Add a new element at the end of the list - * return the pointer to the new node - */ -struct list_node *add_elem(struct list *l,void *elem); - -/* - * Delete a node - */ -void delete_node(struct list *l,struct list_node *node); - -/* - * Delete a node from the list, even the content pointed by it - * Use only when the content is a dynamically allocated pointer - */ -void destroy_node(struct list *l,struct list_node *node); - -/* - * Check whether a list is empty or not - */ -int is_empty_list(struct list *l); - -/* - * Return the element count of the list - */ -int get_list_count(struct list *l); - -/* - * Return the first element (content of the node) from the list - */ -void *first_elem(struct list *l); - -/* - * Return the first node from the list - */ -struct list_node *first_node(struct list *l); - -/* - * Return the last element (content of the node) from the list - */ -void *last_elem(struct list *l); - -/* - * Return the last node from the list - */ -struct list_node *last_node(struct list *l); - -/* - * Search an element of the list by content - * the comparison is done from the specified offset and for a specified length - * if offset=0, the comparison starts from the address pointed by data - * if length=0, default keysize is used for length - * if the element is found, return the node address - * else return NULL - */ -struct list_node *xlocate_node(struct list *l,void *elem,int offset,int length); - -/* - * The same of xlocate_node(), but return the content of the node - */ -void *xlocate_elem(struct list *l,void *elem,int offset,int length); - -/* - * The same of calling xlocate_node() with offset=0 and length=0 - */ -struct list_node *locate_node(struct list *l,void *elem); - -/* - * The same of locate_node, but return the content of the node - */ -void *locate_elem(struct list *l,void *elem); - -/* - * Delete all the elements in the list - */ -void clear_list(struct list *l); - -/* - * Delete every element in the list, and free the memory pointed by all the node data - */ -void destroy_list(struct list *l); - -#endif diff --git a/Utilities/Tools/cpulimit/process_group.c b/Utilities/Tools/cpulimit/process_group.c deleted file mode 100644 index c5343e32bd9a1..0000000000000 --- a/Utilities/Tools/cpulimit/process_group.c +++ /dev/null @@ -1,219 +0,0 @@ -/** - * - * cpulimit - a CPU limiter for Linux - * - * Copyright (C) 2005-2012, by: Angelo Marletta - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#if defined(__APPLE__) || defined(__FREEBSD__) -#include -#endif - -#include -#include -#include -#include -#include - -#include - -#include "process_iterator.h" -#include "process_group.h" -#include "list.h" - -// look for a process by pid -// search_pid : pid of the wanted process -// return: pid of the found process, if successful -// negative pid, if the process does not exist or if the signal fails -int find_process_by_pid(pid_t pid) -{ - return (kill(pid,0)==0) ? pid : -pid; -} - -// look for a process with a given name -// process: the name of the wanted process. it can be an absolute path name to the executable file -// or just the file name -// return: pid of the found process, if it is found -// 0, if it's not found -// negative pid, if it is found but it's not possible to control it -int find_process_by_name(const char *process_name) -{ - //pid of the target process - pid_t pid = -1; - - //process iterator - struct process_iterator it; - struct process proc; - struct process_filter filter; - filter.pid = 0; - filter.include_children = 0; - init_process_iterator(&it, &filter); - while (get_next_process(&it, &proc) != -1) - { - //process found - if (strncmp(basename(proc.command), process_name, strlen(process_name))==0 && kill(pid,SIGCONT)==0) { - //process is ok! - pid = proc.pid; - break; - } - } - if (close_process_iterator(&it) != 0) { - exit(1); - } - if (pid >= 0) { - //ok, the process was found - return pid; - } - else { - //process not found - return 0; - } -} - -int init_process_group(struct process_group *pgroup, int target_pid, int include_children) -{ - //hashtable initialization - memset(&pgroup->proctable, 0, sizeof(pgroup->proctable)); - pgroup->target_pid = target_pid; - pgroup->include_children = include_children; - pgroup->proclist = (struct list*)malloc(sizeof(struct list)); - init_list(pgroup->proclist, 4); - memset(&pgroup->last_update, 0, sizeof(pgroup->last_update)); - update_process_group(pgroup); - return 0; -} - -int close_process_group(struct process_group *pgroup) -{ - int i; - int size = sizeof(pgroup->proctable) / sizeof(struct process*); - for (i=0; iproctable[i] != NULL) { - //free() history for each process - destroy_list(pgroup->proctable[i]); - free(pgroup->proctable[i]); - pgroup->proctable[i] = NULL; - } - } - clear_list(pgroup->proclist); - free(pgroup->proclist); - pgroup->proclist = NULL; - return 0; -} - -void remove_terminated_processes(struct process_group *pgroup) -{ - //TODO -} - -//return t1-t2 in microseconds (no overflow checks, so better watch out!) -static inline unsigned long timediff(const struct timeval *t1,const struct timeval *t2) -{ - return (t1->tv_sec - t2->tv_sec) * 1000000 + (t1->tv_usec - t2->tv_usec); -} - -//parameter in range 0-1 -#define ALFA 0.08 -#define MIN_DT 20 - -void update_process_group(struct process_group *pgroup) -{ - struct process_iterator it; - struct process tmp_process; - struct process_filter filter; - struct timeval now; - gettimeofday(&now, NULL); - //time elapsed from previous sample (in ms) - long dt = timediff(&now, &pgroup->last_update) / 1000; - filter.pid = pgroup->target_pid; - filter.include_children = pgroup->include_children; - init_process_iterator(&it, &filter); - clear_list(pgroup->proclist); - init_list(pgroup->proclist, 4); - - while (get_next_process(&it, &tmp_process) != -1) - { -// struct timeval t; -// gettimeofday(&t, NULL); -// printf("T=%ld.%ld PID=%d PPID=%d START=%d CPUTIME=%d\n", t.tv_sec, t.tv_usec, tmp_process.pid, tmp_process.ppid, tmp_process.starttime, tmp_process.cputime); - int hashkey = pid_hashfn(tmp_process.pid); - if (pgroup->proctable[hashkey] == NULL) - { - //empty bucket - pgroup->proctable[hashkey] = malloc(sizeof(struct list)); - struct process *new_process = malloc(sizeof(struct process)); - tmp_process.cpu_usage = -1; - memcpy(new_process, &tmp_process, sizeof(struct process)); - init_list(pgroup->proctable[hashkey], 4); - add_elem(pgroup->proctable[hashkey], new_process); - add_elem(pgroup->proclist, new_process); - } - else - { - //existing bucket - struct process *p = (struct process*)locate_elem(pgroup->proctable[hashkey], &tmp_process); - if (p == NULL) - { - //process is new. add it - struct process *new_process = malloc(sizeof(struct process)); - tmp_process.cpu_usage = -1; - memcpy(new_process, &tmp_process, sizeof(struct process)); - add_elem(pgroup->proctable[hashkey], new_process); - add_elem(pgroup->proclist, new_process); - } - else - { - assert(tmp_process.pid == p->pid); - assert(tmp_process.starttime == p->starttime); - add_elem(pgroup->proclist, p); - if (dt < MIN_DT) { - continue; - } - //process exists. update CPU usage - double sample = 1.0 * (tmp_process.cputime - p->cputime) / dt; - if (p->cpu_usage == -1) { - //initialization - p->cpu_usage = sample; - } - else { - //usage adjustment - p->cpu_usage = (1.0-ALFA) * p->cpu_usage + ALFA * sample; - } - p->cputime = tmp_process.cputime; - } - } - } - close_process_iterator(&it); - if (dt < MIN_DT) { - return; - } - pgroup->last_update = now; -} - -int remove_process(struct process_group *pgroup, int pid) -{ - int hashkey = pid_hashfn(pid); - if (pgroup->proctable[hashkey] == NULL) { - return 1; //nothing to delete - } - struct list_node *node = (struct list_node*)locate_node(pgroup->proctable[hashkey], &pid); - if (node == NULL) { - return 2; - } - delete_node(pgroup->proctable[hashkey], node); - return 0; -} diff --git a/Utilities/Tools/cpulimit/process_group.h b/Utilities/Tools/cpulimit/process_group.h deleted file mode 100644 index 5a5b581554a80..0000000000000 --- a/Utilities/Tools/cpulimit/process_group.h +++ /dev/null @@ -1,55 +0,0 @@ -/** - * - * cpulimit - a CPU limiter for Linux - * - * Copyright (C) 2005-2012, by: Angelo Marletta - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef __PROCESS_GROUP_H - -#define __PROCESS_GROUP_H - -#include "process_iterator.h" - -#include "list.h" - -#define PIDHASH_SZ 1024 -#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1)) - -struct process_group -{ - //hashtable with all the processes (array of struct list of struct process) - struct list *proctable[PIDHASH_SZ]; - struct list *proclist; - pid_t target_pid; - int include_children; - struct timeval last_update; -}; - -int init_process_group(struct process_group *pgroup, int target_pid, int include_children); - -void update_process_group(struct process_group *pgroup); - -int close_process_group(struct process_group *pgroup); - -int find_process_by_pid(pid_t pid); - -int find_process_by_name(const char *process_name); - -int remove_process(struct process_group *pgroup, int pid); - -#endif diff --git a/Utilities/Tools/cpulimit/process_iterator.c b/Utilities/Tools/cpulimit/process_iterator.c deleted file mode 100644 index 8b4019d237f2b..0000000000000 --- a/Utilities/Tools/cpulimit/process_iterator.c +++ /dev/null @@ -1,49 +0,0 @@ -/** - * - * cpulimit - a CPU limiter for Linux - * - * Copyright (C) 2005-2012, by: Angelo Marletta - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include -#include -#include -#ifndef __APPLE__ -#include -#endif -#include -#include "process_iterator.h" - -//See this link to port to other systems: http://www.steve.org.uk/Reference/Unix/faq_8.html#SEC85 - -#ifdef __linux__ - -#include "process_iterator_linux.c" - -#elif defined __FreeBSD__ - -#include "process_iterator_freebsd.c" - -#elif defined __APPLE__ - -#include "process_iterator_apple.c" - -#else - -#error Platform not supported - -#endif diff --git a/Utilities/Tools/cpulimit/process_iterator.h b/Utilities/Tools/cpulimit/process_iterator.h deleted file mode 100644 index 70520b68a6e88..0000000000000 --- a/Utilities/Tools/cpulimit/process_iterator.h +++ /dev/null @@ -1,97 +0,0 @@ -/** - * - * cpulimit - a CPU limiter for Linux - * - * Copyright (C) 2005-2012, by: Angelo Marletta - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef __PROCESS_ITERATOR_H - -#define __PROCESS_ITERATOR_H - -#include -#include -#include - -//USER_HZ detection, from openssl code -#ifndef HZ -# if defined(_SC_CLK_TCK) \ - && (!defined(OPENSSL_SYS_VMS) || __CTRL_VER >= 70000000) -# define HZ ((double)sysconf(_SC_CLK_TCK)) -# else -# ifndef CLK_TCK -# ifndef _BSD_CLK_TCK_ /* FreeBSD hack */ -# define HZ 100.0 -# else /* _BSD_CLK_TCK_ */ -# define HZ ((double)_BSD_CLK_TCK_) -# endif -# else /* CLK_TCK */ -# define HZ ((double)CLK_TCK) -# endif -# endif -#endif - -#ifdef __FreeBSD__ -#include -#endif - -// process descriptor -struct process { - //pid of the process - pid_t pid; - //ppid of the process - pid_t ppid; - //start time (unix timestamp) - int starttime; - //cputime used by the process (in milliseconds) - int cputime; - //actual cpu usage estimation (value in range 0-1) - double cpu_usage; - //absolute path of the executable file - char command[PATH_MAX+1]; -}; - -struct process_filter { - int pid; - int include_children; - char program_name[PATH_MAX+1]; -}; - -struct process_iterator { -#ifdef __linux__ - DIR *dip; - int boot_time; -#elif defined __FreeBSD__ - kvm_t *kd; - struct kinfo_proc *procs; - int count; - int i; -#elif defined __APPLE__ - int i; - int count; - int *pidlist; -#endif - struct process_filter *filter; -}; - -int init_process_iterator(struct process_iterator *i, struct process_filter *filter); - -int get_next_process(struct process_iterator *i, struct process *p); - -int close_process_iterator(struct process_iterator *i); - -#endif diff --git a/Utilities/Tools/cpulimit/process_iterator_apple.c b/Utilities/Tools/cpulimit/process_iterator_apple.c deleted file mode 100644 index b878ed8c9a946..0000000000000 --- a/Utilities/Tools/cpulimit/process_iterator_apple.c +++ /dev/null @@ -1,148 +0,0 @@ -/** - * - * cpulimit - a CPU limiter for Linux - * - * Copyright (C) 2005-2012, by: Angelo Marletta - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Author: Simon Sigurdhsson - * - */ - -#include -#include -#include - -int unique_nonzero_ints(int* arr_in, int len_in, int* arr_out) { - int* source = arr_in; - if (arr_out == NULL) return -1; - if (arr_in == arr_out) { - source = malloc(sizeof(int)*len_in); - memcpy(source, arr_in, sizeof(int)*len_in); - memset(arr_out, -1, sizeof(int)*len_in); - } - int len_out = 0; - int i, j; - for (i=0; ii = 0; - /* Find out how much to allocate for it->pidlist */ - if ((it->count = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0)) <= 0) { - fprintf(stderr, "proc_listpids: %s\n", strerror(errno)); - return -1; - } - /* Allocate and populate it->pidlist */ - if ((it->pidlist = (int *)malloc((it->count)*sizeof(int))) == NULL) { - fprintf(stderr, "malloc: %s\n", strerror(errno)); - } - if ((it->count = proc_listpids(PROC_ALL_PIDS, 0, it->pidlist, it->count)) <= 0) { - fprintf(stderr, "proc_listpids: %s\n", strerror(errno)); - return -1; - } - it->count = unique_nonzero_ints(it->pidlist, it->count, it->pidlist); - it->filter = filter; - return 0; -} - -static int pti2proc(struct proc_taskallinfo *ti, struct process *process) { - int bytes; - process->pid = ti->pbsd.pbi_pid; - process->ppid = ti->pbsd.pbi_ppid; - process->starttime = ti->pbsd.pbi_start_tvsec; - process->cputime = (ti->ptinfo.pti_total_user + ti->ptinfo.pti_total_system) / 1000000; - bytes = strlen(ti->pbsd.pbi_comm); - memcpy(process->command, ti->pbsd.pbi_comm, (bytes < PATH_MAX ? bytes : PATH_MAX) + 1); - return 0; -} - -static int get_process_pti(pid_t pid, struct proc_taskallinfo *ti) { - int bytes; - bytes = proc_pidinfo(pid, PROC_PIDTASKALLINFO, 0, ti, sizeof(*ti)); - if (bytes <= 0) { - if (!(errno & (EPERM | ESRCH))) { - fprintf(stderr, "proc_pidinfo: %s\n", strerror(errno)); - } - return -1; - } else if (bytes < sizeof(ti)) { - fprintf(stderr, "proc_pidinfo: too few bytes; expected %ld, got %d\n", sizeof(ti), bytes); - return -1; - } - return 0; -} - -int get_next_process(struct process_iterator *it, struct process *p) { - if (it->i == it->count) return -1; - if (it->filter->pid != 0 && !it->filter->include_children) { - struct proc_taskallinfo ti; - if (get_process_pti(it->filter->pid, &ti) != 0) { - it->i = it->count = 0; - return -1; - } - it->i = it->count = 1; - return pti2proc(&ti, p); - } - while (it->i < it->count) { - struct proc_taskallinfo ti; - if (get_process_pti(it->pidlist[it->i], &ti) != 0) { - it->i++; - continue; - } - if (ti.pbsd.pbi_flags & PROC_FLAG_SYSTEM) { - it->i++; - continue; - } - if (it->filter->pid != 0 && it->filter->include_children) { - pti2proc(&ti, p); - it->i++; - if (p->pid != it->pidlist[it->i - 1]) // I don't know why this can happen - continue; - if (p->pid != it->filter->pid && p->ppid != it->filter->pid) - continue; - return 0; - } - else if (it->filter->pid == 0) - { - pti2proc(&ti, p); - it->i++; - return 0; - } - } - return -1; -} - -int close_process_iterator(struct process_iterator *it) { - free(it->pidlist); - it->pidlist = NULL; - it->filter = NULL; - it->count = 0; - it->i = 0; - return 0; -} diff --git a/Utilities/Tools/cpulimit/process_iterator_freebsd.c b/Utilities/Tools/cpulimit/process_iterator_freebsd.c deleted file mode 100644 index a6381123e1251..0000000000000 --- a/Utilities/Tools/cpulimit/process_iterator_freebsd.c +++ /dev/null @@ -1,119 +0,0 @@ -/** - * - * cpulimit - a CPU limiter for Linux - * - * Copyright (C) 2005-2012, by: Angelo Marletta - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include -#include -#include -#include - -int init_process_iterator(struct process_iterator *it, struct process_filter *filter) { - char errbuf[_POSIX2_LINE_MAX]; - it->i = 0; - /* Open the kvm interface, get a descriptor */ - if ((it->kd = kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, errbuf)) == NULL) { - fprintf(stderr, "kvm_open: %s\n", errbuf); - return -1; - } - /* Get the list of processes. */ - if ((it->procs = kvm_getprocs(it->kd, KERN_PROC_PROC, 0, &it->count)) == NULL) { - kvm_close(it->kd); -// fprintf(stderr, "kvm_getprocs: %s\n", kvm_geterr(it->kd)); - return -1; - } - it->filter = filter; - return 0; -} - -static int kproc2proc(kvm_t *kd, struct kinfo_proc *kproc, struct process *proc) -{ - proc->pid = kproc->ki_pid; - proc->ppid = kproc->ki_ppid; - proc->cputime = kproc->ki_runtime / 1000; - proc->starttime = kproc->ki_start.tv_sec; - char **args = kvm_getargv(kd, kproc, sizeof(proc->command)); - if (args == NULL) return -1; - memcpy(proc->command, args[0], strlen(args[0]) + 1); - return 0; -} - -static int get_single_process(kvm_t *kd, pid_t pid, struct process *process) -{ - int count; - struct kinfo_proc *kproc = kvm_getprocs(kd, KERN_PROC_PID, pid, &count); - if (count == 0 || kproc == NULL) - { -// fprintf(stderr, "kvm_getprocs: %s\n", kvm_geterr(kd)); - return -1; - } - kproc2proc(kd, kproc, process); - return 0; -} - -int get_next_process(struct process_iterator *it, struct process *p) { - if (it->i == it->count) - { - return -1; - } - if (it->filter->pid != 0 && !it->filter->include_children) - { - if (get_single_process(it->kd, it->filter->pid, p) != 0) - { - it->i = it->count = 0; - return -1; - } - it->i = it->count = 1; - return 0; - } - while (it->i < it->count) - { - struct kinfo_proc *kproc = &(it->procs[it->i]); - if (kproc->ki_flag & P_SYSTEM) - { - // skip system processes - it->i++; - continue; - } - if (it->filter->pid != 0 && it->filter->include_children) - { - kproc2proc(it->kd, kproc, p); - it->i++; - if (p->pid != it->filter->pid && p->ppid != it->filter->pid) - continue; - return 0; - } - else if (it->filter->pid == 0) - { - kproc2proc(it->kd, kproc, p); - it->i++; - return 0; - } - } - return -1; -} - -int close_process_iterator(struct process_iterator *it) { - if (kvm_close(it->kd) == -1) { - fprintf(stderr, "kvm_getprocs: %s\n", kvm_geterr(it->kd)); - return -1; - } - return 0; -} - diff --git a/Utilities/Tools/cpulimit/process_iterator_linux.c b/Utilities/Tools/cpulimit/process_iterator_linux.c deleted file mode 100644 index d8d2cab3571da..0000000000000 --- a/Utilities/Tools/cpulimit/process_iterator_linux.c +++ /dev/null @@ -1,198 +0,0 @@ -/** - * - * cpulimit - a CPU limiter for Linux - * - * Copyright (C) 2005-2012, by: Angelo Marletta - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include - -static int get_boot_time() -{ - int uptime = 0; - FILE *fp = fopen ("/proc/uptime", "r"); - if (fp != NULL) - { - char buf[BUFSIZ]; - char *b = fgets(buf, BUFSIZ, fp); - if (b == buf) - { - char *end_ptr; - double upsecs = strtod(buf, &end_ptr); - uptime = (int)upsecs; - } - fclose (fp); - } - time_t now = time(NULL); - return now - uptime; -} - -static int check_proc() -{ - struct statfs mnt; - if (statfs("/proc", &mnt) < 0) { - return 0; - } - if (mnt.f_type!=0x9fa0) { - return 0; - } - return 1; -} - -int init_process_iterator(struct process_iterator *it, struct process_filter *filter) -{ - if (!check_proc()) { - fprintf(stderr, "procfs is not mounted!\nAborting\n"); - exit(-2); - } - //open a directory stream to /proc directory - if ((it->dip = opendir("/proc")) == NULL) - { - perror("opendir"); - return -1; - } - it->filter = filter; - it->boot_time = get_boot_time(); - return 0; -} - -static int read_process_info(pid_t pid, struct process *p) -{ - static char buffer[1024]; - static char statfile[32]; - static char exefile[1024]; - p->pid = pid; - //read stat file - sprintf(statfile, "/proc/%d/stat", p->pid); - FILE *fd = fopen(statfile, "r"); - if (fd==NULL) { - return -1; - } - if (fgets(buffer, sizeof(buffer), fd)==NULL) { - fclose(fd); - return -1; - } - fclose(fd); - char *token = strtok(buffer, " "); - int i; - for (i=0; i<3; i++) { - token = strtok(NULL, " "); - } - p->ppid = atoi(token); - for (i=0; i<10; i++) { - token = strtok(NULL, " "); - } - p->cputime = atoi(token) * 1000 / HZ; - token = strtok(NULL, " "); - p->cputime += atoi(token) * 1000 / HZ; - for (i=0; i<7; i++) { - token = strtok(NULL, " "); - } - p->starttime = atoi(token) / sysconf(_SC_CLK_TCK); - //read command line - sprintf(exefile,"/proc/%d/cmdline", p->pid); - fd = fopen(exefile, "r"); - if (fgets(buffer, sizeof(buffer), fd)==NULL) { - fclose(fd); - return -1; - } - fclose(fd); - strcpy(p->command, buffer); - return 0; -} - -static pid_t getppid_of(pid_t pid) -{ - char statfile[20]; - char buffer[1024]; - sprintf(statfile, "/proc/%d/stat", pid); - FILE *fd = fopen(statfile, "r"); - if (fd==NULL) { - return -1; - } - if (fgets(buffer, sizeof(buffer), fd)==NULL) { - fclose(fd); - return -1; - } - fclose(fd); - char *token = strtok(buffer, " "); - int i; - for (i=0; i<3; i++) { - token = strtok(NULL, " "); - } - return atoi(token); -} - -static int is_child_of(pid_t child_pid, pid_t parent_pid) -{ - int ppid = child_pid; - while(ppid > 1 && ppid != parent_pid) { - ppid = getppid_of(ppid); - } - return ppid == parent_pid; -} - -int get_next_process(struct process_iterator *it, struct process *p) -{ - if (it->dip == NULL) - { - //end of processes - return -1; - } - if (it->filter->pid != 0 && !it->filter->include_children) - { - int ret = read_process_info(it->filter->pid, p); - //p->starttime += it->boot_time; - closedir(it->dip); - it->dip = NULL; - if (ret != 0) { - return -1; - } - return 0; - } - struct dirent *dit = NULL; - //read in from /proc and seek for process dirs - while ((dit = readdir(it->dip)) != NULL) { - if(strtok(dit->d_name, "0123456789") != NULL) { - continue; - } - p->pid = atoi(dit->d_name); - if (it->filter->pid != 0 && it->filter->pid != p->pid && !is_child_of(p->pid, it->filter->pid)) { - continue; - } - read_process_info(p->pid, p); - //p->starttime += it->boot_time; - break; - } - if (dit == NULL) - { - //end of processes - closedir(it->dip); - it->dip = NULL; - return -1; - } - return 0; -} - -int close_process_iterator(struct process_iterator *it) { - if (it->dip != NULL && closedir(it->dip) == -1) { - perror("closedir"); - return 1; - } - it->dip = NULL; - return 0; -} diff --git a/prodtests/full_system_test.sh b/prodtests/full_system_test.sh index e89d8ee09dee9..8496a31d577bc 100755 --- a/prodtests/full_system_test.sh +++ b/prodtests/full_system_test.sh @@ -29,7 +29,7 @@ fi # include jobutils, which notably brings # --> the taskwrapper as a simple control and monitoring tool -# (look inside the jobutils.sh file for documentation) +# (look inside the jobutils2.sh file for documentation) # --> utilities to query CPU count . ${O2_ROOT}/share/scripts/jobutils2.sh diff --git a/prodtests/full_system_test_ci_extra_tests.sh b/prodtests/full_system_test_ci_extra_tests.sh index 2d8a165f647fd..d0c4f23ef93c6 100755 --- a/prodtests/full_system_test_ci_extra_tests.sh +++ b/prodtests/full_system_test_ci_extra_tests.sh @@ -3,7 +3,7 @@ # Set of extra tests which may run after the full_system_test.sh # Particularly, they use the files generated by the full_system_test.sh # -. ${O2_ROOT}/share/scripts/jobutils.sh +. ${O2_ROOT}/share/scripts/jobutils2.sh if [ "0$O2_ROOT" == "0" ]; then eval "`alienv shell-helper`" diff --git a/prodtests/full_system_test_pipeline.sh b/prodtests/full_system_test_pipeline.sh index 235a590953d8e..bb29ea263dae0 100755 --- a/prodtests/full_system_test_pipeline.sh +++ b/prodtests/full_system_test_pipeline.sh @@ -14,9 +14,9 @@ # include jobutils, which notably brings # --> the taskwrapper as a simple control and monitoring tool -# (look inside the jobutils.sh file for documentation) +# (look inside the jobutils2.sh file for documentation) # --> utilities to query CPU count -. ${O2_ROOT}/share/scripts/jobutils.sh +. ${O2_ROOT}/share/scripts/jobutils2.sh export NEvents=${NEvents:-10} #550 for full TF (the number of PbPb events) export NEventsQED=${NEventsQED:-1000} #35000 for full TF diff --git a/prodtests/sim_challenge.sh b/prodtests/sim_challenge.sh index f5bbf8ab74ff8..a7c7e7f7993d7 100755 --- a/prodtests/sim_challenge.sh +++ b/prodtests/sim_challenge.sh @@ -8,7 +8,7 @@ # and it is advised to use that one. Some documentation can be found here: https://aliceo2group.github.io/simulation/docs/o2dpgworkflow/ # ------------ LOAD UTILITY FUNCTIONS ---------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh +. ${O2_ROOT}/share/scripts/jobutils2.sh # ----------- START WITH ACTUAL SCRIPT --------------------------- diff --git a/run/SimExamples/SimAsService_biasing1/run.sh b/run/SimExamples/SimAsService_biasing1/run.sh index 3bf8f51890fec..e038541ff6035 100755 --- a/run/SimExamples/SimAsService_biasing1/run.sh +++ b/run/SimExamples/SimAsService_biasing1/run.sh @@ -83,11 +83,11 @@ o2-sim-client.py --pid ${SERVICE2_PID} --command "--stop 1" sleep 1 # just some tmp safety-net to make sure all processes are really gone -. ${O2_ROOT}/share/scripts/jobutils.sh +. ${O2_ROOT}/share/scripts/jobutils2.sh for p in $(childprocs ${SERVICE1_PID}); do kill -9 ${p} done -. ${O2_ROOT}/share/scripts/jobutils.sh +. ${O2_ROOT}/share/scripts/jobutils2.sh for p in $(childprocs ${SERVICE2_PID}); do kill -9 ${p} done From a105f1ea0a7e8b336921abf0adaa46243690ea9e Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Mon, 20 Apr 2026 12:21:10 +0200 Subject: [PATCH 074/285] DPL Analysis: return span directly if the CCDB column is declared as a span (#15275) --- Framework/Core/include/Framework/ASoA.h | 78 +++++++++++++------------ 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/Framework/Core/include/Framework/ASoA.h b/Framework/Core/include/Framework/ASoA.h index 7a3307ae1a58c..96a4fe08cdd61 100644 --- a/Framework/Core/include/Framework/ASoA.h +++ b/Framework/Core/include/Framework/ASoA.h @@ -2452,43 +2452,47 @@ consteval static std::string_view namespace_prefix() }; \ [[maybe_unused]] static constexpr o2::framework::expressions::BindingNode _Getter_ { _Label_, _Name_::hash, o2::framework::expressions::selectArrowType<_Type_>() } -#define DECLARE_SOA_CCDB_COLUMN_FULL(_Name_, _Label_, _Getter_, _ConcreteType_, _CCDBQuery_) \ - struct _Name_ : o2::soa::Column, _Name_> { \ - static constexpr const char* mLabel = _Label_; \ - static constexpr const char* query = _CCDBQuery_; \ - static constexpr const uint32_t hash = crc32(namespace_prefix<_Name_>(), std::string_view{#_Getter_}); \ - using base = o2::soa::Column, _Name_>; \ - using type = std::span; \ - using column_t = _Name_; \ - _Name_(arrow::ChunkedArray const* column) \ - : o2::soa::Column, _Name_>(o2::soa::ColumnIterator>(column)) \ - { \ - } \ - \ - _Name_() = default; \ - _Name_(_Name_ const& other) = default; \ - _Name_& operator=(_Name_ const& other) = default; \ - \ - decltype(auto) _Getter_() const \ - { \ - static std::byte* payload = nullptr; \ - static _ConcreteType_* deserialised = nullptr; \ - static TClass* c = TClass::GetClass(#_ConcreteType_); \ - auto span = *mColumnIterator; \ - if (payload != (std::byte*)span.data()) { \ - payload = (std::byte*)span.data(); \ - delete deserialised; \ - TBufferFile f(TBufferFile::EMode::kRead, span.size(), (char*)span.data(), kFALSE); \ - deserialised = (_ConcreteType_*)soa::extractCCDBPayload((char*)payload, span.size(), c, "ccdb_object"); \ - } \ - return *deserialised; \ - } \ - \ - decltype(auto) \ - get() const \ - { \ - return _Getter_(); \ - } \ +#define DECLARE_SOA_CCDB_COLUMN_FULL(_Name_, _Label_, _Getter_, _ConcreteType_, _CCDBQuery_) \ + struct _Name_ : o2::soa::Column, _Name_> { \ + static constexpr const char* mLabel = _Label_; \ + static constexpr const char* query = _CCDBQuery_; \ + static constexpr const uint32_t hash = crc32(namespace_prefix<_Name_>(), std::string_view{#_Getter_}); \ + using base = o2::soa::Column, _Name_>; \ + using type = std::span; \ + using column_t = _Name_; \ + _Name_(arrow::ChunkedArray const* column) \ + : o2::soa::Column, _Name_>(o2::soa::ColumnIterator>(column)) \ + { \ + } \ + \ + _Name_() = default; \ + _Name_(_Name_ const& other) = default; \ + _Name_& operator=(_Name_ const& other) = default; \ + \ + decltype(auto) _Getter_() const \ + { \ + if constexpr (std::same_as<_ConcreteType_, std::span>) { \ + return *mColumnIterator; \ + } else { \ + static std::byte* payload = nullptr; \ + static _ConcreteType_* deserialised = nullptr; \ + static TClass* c = TClass::GetClass(#_ConcreteType_); \ + auto span = *mColumnIterator; \ + if (payload != (std::byte*)span.data()) { \ + payload = (std::byte*)span.data(); \ + delete deserialised; \ + TBufferFile f(TBufferFile::EMode::kRead, span.size(), (char*)span.data(), kFALSE); \ + deserialised = (_ConcreteType_*)soa::extractCCDBPayload((char*)payload, span.size(), c, "ccdb_object"); \ + } \ + return *deserialised; \ + } \ + } \ + \ + decltype(auto) \ + get() const \ + { \ + return _Getter_(); \ + } \ }; #define DECLARE_SOA_CCDB_COLUMN(_Name_, _Getter_, _ConcreteType_, _CCDBQuery_) \ From ded827ee065090f35bdeb91e2e0195b9b8d880ef Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Mon, 20 Apr 2026 14:30:43 +0200 Subject: [PATCH 075/285] ITS: restore previous UPC iteration (#15289) * ITS: fix upc iteration Signed-off-by: Felix Schlepper * ITS: simplify configurables to single one for Vertexer Signed-off-by: Felix Schlepper * ITS: make MaxIter a constant Signed-off-by: Felix Schlepper * ITS: remove GPU params Signed-off-by: Felix Schlepper --------- Signed-off-by: Felix Schlepper --- .../GPU/ITStrackingGPU/TrackingKernels.h | 24 +----- .../ITS/tracking/GPU/cuda/TimeFrameGPU.cu | 3 +- .../tracking/GPU/cuda/TrackerTraitsGPU.cxx | 32 +------- .../ITS/tracking/GPU/cuda/TrackingKernels.cu | 74 +++++-------------- .../include/ITStracking/Configuration.h | 6 +- .../tracking/include/ITStracking/Constants.h | 9 ++- .../include/ITStracking/TrackingConfigParam.h | 67 ++++------------- .../tracking/include/ITStracking/Vertexer.h | 2 +- .../include/ITStracking/VertexerTraits.h | 2 + .../ITSMFT/ITS/tracking/src/Configuration.cxx | 67 +++++++++-------- Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx | 7 -- .../ITS/tracking/src/TrackingConfigParam.cxx | 32 +------- .../ITS/tracking/src/TrackingInterface.cxx | 4 - .../ITSMFT/ITS/tracking/src/TrackingLinkDef.h | 3 - .../ITSMFT/ITS/tracking/src/Vertexer.cxx | 16 ++-- .../ITS/tracking/src/VertexerTraits.cxx | 38 ++++++---- GPU/Workflow/src/GPUWorkflowITS.cxx | 1 - prodtests/full-system-test/dpl-workflow.sh | 2 +- 18 files changed, 118 insertions(+), 271 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h index a83d9d0d52e8f..6a977f8fef21a 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h @@ -60,8 +60,6 @@ void countTrackletsInROFsHandler(const IndexTableUtils* utils, std::vector& radii, bounded_vector& mulScatAng, o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads, gpu::Streams& streams); template @@ -93,8 +91,6 @@ void computeTrackletsInROFsHandler(const IndexTableUtils* utils, std::vector& radii, bounded_vector& mulScatAng, o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads, gpu::Streams& streams); template @@ -113,8 +109,6 @@ void countCellsHandler(const Cluster** sortedClusters, const float cellDeltaTanLambdaSigma, const float nSigmaCut, o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads, gpu::Streams& streams); template @@ -132,8 +126,6 @@ void computeCellsHandler(const Cluster** sortedClusters, const float maxChi2ClusterAttachment, const float cellDeltaTanLambdaSigma, const float nSigmaCut, - const int nBlocks, - const int nThreads, gpu::Streams& streams); template @@ -150,8 +142,6 @@ void countCellNeighboursHandler(CellSeed** cellsLayersDevice, const unsigned int nCellsNext, const int maxCellNeighbours, o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads, gpu::Stream& stream); template @@ -167,8 +157,6 @@ void computeCellNeighboursHandler(CellSeed** cellsLayersDevice, const unsigned int nCells, const unsigned int nCellsNext, const int maxCellNeighbours, - const int nBlocks, - const int nThreads, gpu::Stream& stream); int filterCellNeighboursHandler(gpuPair*, @@ -193,9 +181,7 @@ void processNeighboursHandler(const int startLayer, const float maxChi2NDF, const o2::base::Propagator* propagator, const o2::base::PropagatorF::MatCorrType matCorrType, - o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads); + o2::its::ExternalAllocator* alloc); template void countTrackSeedHandler(CellSeed* trackSeeds, @@ -214,9 +200,7 @@ void countTrackSeedHandler(CellSeed* trackSeeds, const bool shiftRefToCluster, const o2::base::Propagator* propagator, const o2::base::PropagatorF::MatCorrType matCorrType, - o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads); + o2::its::ExternalAllocator* alloc); template void computeTrackSeedHandler(CellSeed* trackSeeds, @@ -237,9 +221,7 @@ void computeTrackSeedHandler(CellSeed* trackSeeds, const bool shiftRefToCluster, const o2::base::Propagator* propagator, const o2::base::PropagatorF::MatCorrType matCorrType, - o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads); + o2::its::ExternalAllocator* alloc); } // namespace o2::its #endif // ITSTRACKINGGPU_TRACKINGKERNELS_H_ diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu index a9b51580f9be7..bd5e7a8bc59f8 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu @@ -627,8 +627,7 @@ constexpr auto makeIterTags(std::index_sequence) { return std::array{makeIterTag()...}; } -// FIXME: we have to be careful that the MaxIter does not diverge from the 4 here! -constexpr auto kIterTags = makeIterTags(std::make_index_sequence<4>{}); +constexpr auto kIterTags = makeIterTags(std::make_index_sequence{}); } // namespace detail template diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx index 3de2871dd458e..f7a416808fec7 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx @@ -18,7 +18,6 @@ #include "ITStrackingGPU/TrackerTraitsGPU.h" #include "ITStrackingGPU/TrackingKernels.h" -#include "ITStracking/TrackingConfigParam.h" #include "ITStracking/Constants.h" namespace o2::its @@ -63,8 +62,6 @@ void TrackerTraitsGPU::adoptTimeFrame(TimeFrame* tf) template void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int iVertex) { - const auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); - // start by queuing loading needed of two last layers for (int iLayer{NLayers}; iLayer-- > NLayers - 2;) { mTimeFrameGPU->createUsedClustersDevice(iteration, iLayer); @@ -109,8 +106,6 @@ void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int i this->mTrkParams[iteration].LayerRadii, mTimeFrameGPU->getMSangles(), mTimeFrameGPU->getFrameworkAllocator(), - conf.nBlocksLayerTracklets[iteration], - conf.nThreadsLayerTracklets[iteration], mTimeFrameGPU->getStreams()); mTimeFrameGPU->createTrackletsBuffers(iLayer); if (mTimeFrameGPU->getNTracklets()[iLayer] == 0) { @@ -144,8 +139,6 @@ void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int i this->mTrkParams[iteration].LayerRadii, mTimeFrameGPU->getMSangles(), mTimeFrameGPU->getFrameworkAllocator(), - conf.nBlocksLayerTracklets[iteration], - conf.nThreadsLayerTracklets[iteration], mTimeFrameGPU->getStreams()); } } @@ -153,8 +146,6 @@ void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int i template void TrackerTraitsGPU::computeLayerCells(const int iteration) { - auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); - // start by queuing loading needed of three last layers for (int iLayer{NLayers}; iLayer-- > NLayers - 3;) { mTimeFrameGPU->loadUnsortedClustersDevice(iteration, iLayer); @@ -194,8 +185,6 @@ void TrackerTraitsGPU::computeLayerCells(const int iteration) this->mTrkParams[iteration].CellDeltaTanLambdaSigma, this->mTrkParams[iteration].NSigmaCut, mTimeFrameGPU->getFrameworkAllocator(), - conf.nBlocksLayerCells[iteration], - conf.nThreadsLayerCells[iteration], mTimeFrameGPU->getStreams()); mTimeFrameGPU->createCellsBuffers(iLayer); if (mTimeFrameGPU->getNCells()[iLayer] == 0) { @@ -215,8 +204,6 @@ void TrackerTraitsGPU::computeLayerCells(const int iteration) this->mTrkParams[iteration].MaxChi2ClusterAttachment, this->mTrkParams[iteration].CellDeltaTanLambdaSigma, this->mTrkParams[iteration].NSigmaCut, - conf.nBlocksLayerCells[iteration], - conf.nThreadsLayerCells[iteration], mTimeFrameGPU->getStreams()); } } @@ -224,8 +211,6 @@ void TrackerTraitsGPU::computeLayerCells(const int iteration) template void TrackerTraitsGPU::findCellsNeighbours(const int iteration) { - const auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); - for (int iLayer{0}; iLayer < this->mTrkParams[iteration].NeighboursPerRoad(); ++iLayer) { const int currentLayerCellsNum{static_cast(mTimeFrameGPU->getNCells()[iLayer])}; const int nextLayerCellsNum{static_cast(mTimeFrameGPU->getNCells()[iLayer + 1])}; @@ -248,8 +233,6 @@ void TrackerTraitsGPU::findCellsNeighbours(const int iteration) nextLayerCellsNum, 1e2, mTimeFrameGPU->getFrameworkAllocator(), - conf.nBlocksFindNeighbours[iteration], - conf.nThreadsFindNeighbours[iteration], mTimeFrameGPU->getStream(iLayer)); mTimeFrameGPU->createNeighboursDevice(iLayer); if (mTimeFrameGPU->getNNeighbours()[iLayer] == 0) { @@ -267,8 +250,6 @@ void TrackerTraitsGPU::findCellsNeighbours(const int iteration) currentLayerCellsNum, nextLayerCellsNum, 1e2, - conf.nBlocksFindNeighbours[iteration], - conf.nThreadsFindNeighbours[iteration], mTimeFrameGPU->getStream(iLayer)); mTimeFrameGPU->getArrayNNeighbours()[iLayer] = filterCellNeighboursHandler(mTimeFrameGPU->getDeviceNeighbourPairs(iLayer), mTimeFrameGPU->getDeviceNeighbours(iLayer), @@ -282,7 +263,6 @@ void TrackerTraitsGPU::findCellsNeighbours(const int iteration) template void TrackerTraitsGPU::findRoads(const int iteration) { - auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); for (int startLevel{this->mTrkParams[iteration].CellsPerRoad()}; startLevel >= this->mTrkParams[iteration].CellMinimumLevel(); --startLevel) { const int minimumLayer{startLevel - 1}; bounded_vector> trackSeeds(this->getMemoryPool().get()); @@ -305,9 +285,7 @@ void TrackerTraitsGPU::findRoads(const int iteration) this->mTrkParams[0].MaxChi2NDF, mTimeFrameGPU->getDevicePropagator(), this->mTrkParams[0].CorrType, - mTimeFrameGPU->getFrameworkAllocator(), - conf.nBlocksProcessNeighbours[iteration], - conf.nThreadsProcessNeighbours[iteration]); + mTimeFrameGPU->getFrameworkAllocator()); } // fixme: I don't want to move tracks back and forth, but I need a way to use a thrust::allocator that is aware of our managed memory. if (trackSeeds.empty()) { @@ -334,9 +312,7 @@ void TrackerTraitsGPU::findRoads(const int iteration) this->mTrkParams[0].ShiftRefToCluster, mTimeFrameGPU->getDevicePropagator(), this->mTrkParams[0].CorrType, - mTimeFrameGPU->getFrameworkAllocator(), - conf.nBlocksTracksSeeds[iteration], - conf.nThreadsTracksSeeds[iteration]); + mTimeFrameGPU->getFrameworkAllocator()); mTimeFrameGPU->createTrackITSExtDevice(trackSeeds.size()); computeTrackSeedHandler(mTimeFrameGPU->getDeviceTrackSeeds(), mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), @@ -356,9 +332,7 @@ void TrackerTraitsGPU::findRoads(const int iteration) this->mTrkParams[0].ShiftRefToCluster, mTimeFrameGPU->getDevicePropagator(), this->mTrkParams[0].CorrType, - mTimeFrameGPU->getFrameworkAllocator(), - conf.nBlocksTracksSeeds[iteration], - conf.nThreadsTracksSeeds[iteration]); + mTimeFrameGPU->getFrameworkAllocator()); mTimeFrameGPU->downloadTrackITSExtDevice(); auto& tracks = mTimeFrameGPU->getTrackITSExt(); diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu index 795b568f6174d..54f92411a3df1 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu @@ -813,11 +813,9 @@ void countTrackletsInROFsHandler(const IndexTableUtils* utils, std::vector& radii, bounded_vector& mulScatAng, o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads, gpu::Streams& streams) { - gpu::computeLayerTrackletsMultiROFKernel<<>>( + gpu::computeLayerTrackletsMultiROFKernel<<<60, 256, 0, streams[layer].get()>>>( utils, rofMask, layer, @@ -874,11 +872,9 @@ void computeTrackletsInROFsHandler(const IndexTableUtils* utils, std::vector& radii, bounded_vector& mulScatAng, o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads, gpu::Streams& streams) { - gpu::computeLayerTrackletsMultiROFKernel<<>>( + gpu::computeLayerTrackletsMultiROFKernel<<<60, 256, 0, streams[layer].get()>>>( utils, rofMask, layer, @@ -909,7 +905,7 @@ void computeTrackletsInROFsHandler(const IndexTableUtils* utils, nTracklets[layer] = unique_end - tracklets_ptr; if (layer) { GPUChkErrS(cudaMemsetAsync(trackletsLUTsHost[layer], 0, (nClusters[layer] + 1) * sizeof(int), streams[layer].get())); - gpu::compileTrackletsLookupTableKernel<<>>( + gpu::compileTrackletsLookupTableKernel<<<60, 256, 0, streams[layer].get()>>>( spanTracklets[layer], trackletsLUTsHost[layer], nTracklets[layer]); @@ -934,11 +930,9 @@ void countCellsHandler( const float cellDeltaTanLambdaSigma, const float nSigmaCut, o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads, gpu::Streams& streams) { - gpu::computeLayerCellsKernel<<>>( + gpu::computeLayerCellsKernel<<<60, 256, 0, streams[layer].get()>>>( sortedClusters, // const Cluster** unsortedClusters, // const Cluster** tfInfo, // const TrackingFrameInfo** @@ -972,11 +966,9 @@ void computeCellsHandler( const float maxChi2ClusterAttachment, const float cellDeltaTanLambdaSigma, const float nSigmaCut, - const int nBlocks, - const int nThreads, gpu::Streams& streams) { - gpu::computeLayerCellsKernel<<>>( + gpu::computeLayerCellsKernel<<<60, 256, 0, streams[layer].get()>>>( sortedClusters, // const Cluster** unsortedClusters, // const Cluster** tfInfo, // const TrackingFrameInfo** @@ -1006,11 +998,9 @@ void countCellNeighboursHandler(CellSeed** cellsLayersDevice, const unsigned int nCellsNext, const int maxCellNeighbours, o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads, gpu::Stream& stream) { - gpu::computeLayerCellNeighboursKernel<<>>( + gpu::computeLayerCellNeighboursKernel<<<60, 256, 0, stream.get()>>>( cellsLayersDevice, neighboursLUT, neighboursIndexTable, @@ -1040,11 +1030,9 @@ void computeCellNeighboursHandler(CellSeed** cellsLayersDevice, const unsigned int nCells, const unsigned int nCellsNext, const int maxCellNeighbours, - const int nBlocks, - const int nThreads, gpu::Stream& stream) { - gpu::computeLayerCellNeighboursKernel<<>>( + gpu::computeLayerCellNeighboursKernel<<<60, 256, 0, stream.get()>>>( cellsLayersDevice, neighboursLUT, neighboursIndexTable, @@ -1090,9 +1078,7 @@ void processNeighboursHandler(const int startLayer, const float maxChi2NDF, const o2::base::Propagator* propagator, const o2::base::PropagatorF::MatCorrType matCorrType, - o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads) + o2::its::ExternalAllocator* alloc) { constexpr uint64_t Tag = qStr2Tag("ITS_PNH1"); alloc->pushTagOnStack(Tag); @@ -1101,7 +1087,7 @@ void processNeighboursHandler(const int startLayer, thrust::device_vector> foundSeedsTable(nCells[startLayer] + 1, 0, allocInt); auto nosync_policy = THRUST_NAMESPACE::par_nosync(gpu::TypedAllocator(alloc)).on(gpu::Stream::DefaultStream); - gpu::processNeighboursKernel<<>>( + gpu::processNeighboursKernel<<<60, 256>>>( startLayer, startLevel, allCellSeeds, @@ -1123,7 +1109,7 @@ void processNeighboursHandler(const int startLayer, thrust::device_vector> updatedCellId(foundSeedsTable.back(), 0, allocInt); thrust::device_vector, gpu::TypedAllocator>> updatedCellSeed(foundSeedsTable.back(), allocCellSeed); - gpu::processNeighboursKernel<<>>( + gpu::processNeighboursKernel<<<60, 256>>>( startLayer, startLevel, allCellSeeds, @@ -1155,7 +1141,7 @@ void processNeighboursHandler(const int startLayer, foundSeedsTable.resize(lastCellSeedSize + 1); thrust::fill(nosync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), 0); - gpu::processNeighboursKernel<<>>( + gpu::processNeighboursKernel<<<60, 256>>>( iLayer, --level, allCellSeeds, @@ -1181,7 +1167,7 @@ void processNeighboursHandler(const int startLayer, updatedCellSeed.resize(foundSeeds); thrust::fill(nosync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), CellSeed()); - gpu::processNeighboursKernel<<>>( + gpu::processNeighboursKernel<<<60, 256>>>( iLayer, level, allCellSeeds, @@ -1226,16 +1212,14 @@ void countTrackSeedHandler(CellSeed* trackSeeds, const bool shiftRefToCluster, const o2::base::Propagator* propagator, const o2::base::PropagatorF::MatCorrType matCorrType, - o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads) + o2::its::ExternalAllocator* alloc) { // TODO: the minPts&layerRadii is transfered twice // we should allocate this in constant memory and stop these // small transferes! thrust::device_vector minPts(minPtsHost); thrust::device_vector layerRadii(layerRadiiHost); - gpu::fitTrackSeedsKernel<<>>( + gpu::fitTrackSeedsKernel<<<60, 256>>>( trackSeeds, // CellSeed* foundTrackingFrameInfo, // TrackingFrameInfo** unsortedClusters, // Cluster** @@ -1276,13 +1260,11 @@ void computeTrackSeedHandler(CellSeed* trackSeeds, const bool shiftRefToCluster, const o2::base::Propagator* propagator, const o2::base::PropagatorF::MatCorrType matCorrType, - o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads) + o2::its::ExternalAllocator* alloc) { thrust::device_vector minPts(minPtsHost); thrust::device_vector layerRadii(layerRadiiHost); - gpu::fitTrackSeedsKernel<<>>( + gpu::fitTrackSeedsKernel<<<60, 256>>>( trackSeeds, // CellSeed* foundTrackingFrameInfo, // TrackingFrameInfo** unsortedClusters, // Cluster** @@ -1331,8 +1313,6 @@ template void countTrackletsInROFsHandler<7>(const IndexTableUtils<7>* utils, std::vector& radii, bounded_vector& mulScatAng, o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads, gpu::Streams& streams); template void computeTrackletsInROFsHandler<7>(const IndexTableUtils<7>* utils, @@ -1363,8 +1343,6 @@ template void computeTrackletsInROFsHandler<7>(const IndexTableUtils<7>* utils, std::vector& radii, bounded_vector& mulScatAng, o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads, gpu::Streams& streams); template void countCellsHandler<7>(const Cluster** sortedClusters, @@ -1382,8 +1360,6 @@ template void countCellsHandler<7>(const Cluster** sortedClusters, const float cellDeltaTanLambdaSigma, const float nSigmaCut, o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads, gpu::Streams& streams); template void computeCellsHandler<7>(const Cluster** sortedClusters, @@ -1400,8 +1376,6 @@ template void computeCellsHandler<7>(const Cluster** sortedClusters, const float maxChi2ClusterAttachment, const float cellDeltaTanLambdaSigma, const float nSigmaCut, - const int nBlocks, - const int nThreads, gpu::Streams& streams); template void countCellNeighboursHandler<7>(CellSeed<7>** cellsLayersDevice, @@ -1417,8 +1391,6 @@ template void countCellNeighboursHandler<7>(CellSeed<7>** cellsLayersDevice, const unsigned int nCellsNext, const int maxCellNeighbours, o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads, gpu::Stream& stream); template void computeCellNeighboursHandler(CellSeed<7>** cellsLayersDevice, @@ -1433,8 +1405,6 @@ template void computeCellNeighboursHandler(CellSeed<7>** cellsLayersDevice, const unsigned int nCells, const unsigned int nCellsNext, const int maxCellNeighbours, - const int nBlocks, - const int nThreads, gpu::Stream& stream); template void processNeighboursHandler<7>(const int startLayer, @@ -1452,9 +1422,7 @@ template void processNeighboursHandler<7>(const int startLayer, const float maxChi2NDF, const o2::base::Propagator* propagator, const o2::base::PropagatorF::MatCorrType matCorrType, - o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads); + o2::its::ExternalAllocator* alloc); template void countTrackSeedHandler(CellSeed<7>* trackSeeds, const TrackingFrameInfo** foundTrackingFrameInfo, @@ -1472,9 +1440,7 @@ template void countTrackSeedHandler(CellSeed<7>* trackSeeds, const bool shiftRefToCluster, const o2::base::Propagator* propagator, const o2::base::PropagatorF::MatCorrType matCorrType, - o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads); + o2::its::ExternalAllocator* alloc); template void computeTrackSeedHandler(CellSeed<7>* trackSeeds, const TrackingFrameInfo** foundTrackingFrameInfo, @@ -1494,8 +1460,6 @@ template void computeTrackSeedHandler(CellSeed<7>* trackSeeds, const bool shiftRefToCluster, const o2::base::Propagator* propagator, const o2::base::PropagatorF::MatCorrType matCorrType, - o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads); + o2::its::ExternalAllocator* alloc); } // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h index 1f55a95ca0d65..dbce5e0dc08a7 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h @@ -84,9 +84,9 @@ struct TrackingParameters { struct VertexingParameters { std::string asString() const; - int nIterations = 1; // Number of vertexing passes to perform 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 ZBins = 1; int PhiBins = 128; float zCut = -1.f; @@ -100,9 +100,7 @@ struct VertexingParameters { float finalSelectionZCut = -1.f; float duplicateDistance2Cut = -1.f; float tanLambdaCut = -1.f; - float vertNsigmaCut = -1.f; - float vertRadiusSigma = -1.f; - float trackletSigma = -1.f; + float NSigmaCut = -1; float maxZPositionAllowed = -1.f; int clusterContributorsCut = -1; int suppressLowMultDebris = -1; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h index 4b2528b62f057..f8009e3ce8008 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h @@ -31,12 +31,13 @@ constexpr float GB = MB * KB; constexpr bool DoTimeBenchmarks = true; constexpr bool SaveTimeBenchmarks = false; -GPUconstexpr() float Tolerance{1e-12}; // numerical tolerance -GPUconstexpr() int ClustersPerCell{3}; -GPUconstexpr() int UnusedIndex{-1}; -GPUconstexpr() float Resolution{0.0005f}; +GPUconstexpr() float Tolerance = 1e-12; // numerical tolerance +GPUconstexpr() int ClustersPerCell = 3; +GPUconstexpr() int UnusedIndex = -1; +GPUconstexpr() float Resolution = 0.0005f; GPUconstexpr() float Radl = 9.36f; // Radiation length of Si [cm] GPUconstexpr() float Rho = 2.33f; // Density of Si [g/cm^3] +GPUconstexpr() int MaxIter = 4; // Max. supported iterations namespace helpers { diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h index 5ffd55f715a1a..acb55eb1cf993 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h @@ -15,6 +15,7 @@ #include #include "CommonUtils/ConfigurableParam.h" #include "CommonUtils/ConfigurableParamHelper.h" +#include "ITStracking/Constants.h" namespace o2::its { @@ -36,11 +37,9 @@ struct VertexerParamConfig : public o2::conf::ConfigurableParamHelper { - // Use TGeo for mat. budget - static const int MaxIter = 4; static const int MinTrackLength = 4; static const int MaxTrackLength = 7; - 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[7] = {0}; // configure the width of the window in BC to be considered for the tracking. - int minTrackLgtIter[MaxIter] = {}; // minimum track length at each iteration, used only if >0, otherwise use code defaults - uint8_t startLayerMask[MaxIter] = {}; // mask of start layer for this iteration (if >0) - float minPtIterLgt[MaxIter * (MaxTrackLength - MinTrackLength + 1)] = {}; // min.pT for given track length at this iteration, used only if >0, otherwise use code defaults - float sysErrY2[7] = {0}; // systematic error^2 in Y per layer - float sysErrZ2[7] = {0}; // systematic error^2 in Z per layer + + 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[7] = {0}; // configure the width of the window in BC to be considered for the tracking. + int minTrackLgtIter[constants::MaxIter] = {}; // minimum track length at each iteration, used only if >0, otherwise use code defaults + uint8_t startLayerMask[constants::MaxIter] = {}; // mask of start layer for this iteration (if >0) + float minPtIterLgt[constants::MaxIter * (MaxTrackLength - MinTrackLength + 1)] = {}; // min.pT for given track length at this iteration, used only if >0, otherwise use code defaults + float sysErrY2[7] = {0}; // systematic error^2 in Y per layer + float sysErrZ2[7] = {0}; // systematic error^2 in Z per layer float maxChi2ClusterAttachment = -1.f; float maxChi2NDF = -1.f; float nSigmaCut = -1.f; @@ -91,7 +89,7 @@ struct TrackerParamConfig : public o2::conf::ConfigurableParamHelper { - static constexpr int MaxIter = TrackerParamConfig::MaxIter; - - /// Set nBlocks/nThreads to summarily override all kernel launch parameters in each iteration. - /// Parameters must start with nBlocks/nThreads. - static constexpr int OverrideValue{-1}; - static constexpr char const* BlocksName = "nBlocks"; - static constexpr char const* ThreadsName = "nThreads"; - int nBlocks = OverrideValue; - int nThreads = OverrideValue; - void maybeOverride() const; - - /// Individual kernel launch parameter for each iteration - int nBlocksLayerTracklets[MaxIter] = {60, 60, 60, 60}; - int nThreadsLayerTracklets[MaxIter] = {256, 256, 256, 256}; - - int nBlocksLayerCells[MaxIter] = {60, 60, 60, 60}; - int nThreadsLayerCells[MaxIter] = {256, 256, 256, 256}; - - int nBlocksFindNeighbours[MaxIter] = {60, 60, 60, 60}; - int nThreadsFindNeighbours[MaxIter] = {256, 256, 256, 256}; - - int nBlocksProcessNeighbours[MaxIter] = {60, 60, 60, 60}; - int nThreadsProcessNeighbours[MaxIter] = {256, 256, 256, 256}; - - int nBlocksTracksSeeds[MaxIter] = {60, 60, 60, 60}; - int nThreadsTracksSeeds[MaxIter] = {256, 256, 256, 256}; - - int nBlocksVtxComputeTracklets[2] = {60, 60}; - int nThreadsVtxComputeTracklets[2] = {256, 256}; - - int nBlocksVtxComputeMatching[2] = {60, 60}; - int nThreadsVtxComputeMatching[2] = {256, 256}; - - O2ParamDef(ITSGpuTrackingParamConfig, "ITSGpuTrackingParam"); -}; - } // namespace o2::its #endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h index 77218754dbda3..a045ba1639b13 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h @@ -91,7 +91,7 @@ class Vertexer void printEpilog(LogFunc& logger, const unsigned int trackletN01, const unsigned int trackletN12, - const unsigned selectedN, const unsigned int vertexN, const float initT, + const unsigned selectedN, const unsigned int vertexN, const unsigned int totalVertexN, const float trackletT, const float selecT, const float vertexT); void setNThreads(int n, std::shared_ptr& arena) { mTraits->setNThreads(n, arena); } diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h index 5b609c2fa6c85..1adb09551e326 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h @@ -115,6 +115,8 @@ class VertexerTraits // Frame related quantities TimeFrameN* mTimeFrame = nullptr; // observer ptr private: + bool skipROF(int iteration, int rof) const; + std::shared_ptr mMemoryPool; std::shared_ptr mTaskArena; }; diff --git a/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx b/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx index 6c88b61f2df07..49bf9b5b1887d 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx @@ -136,12 +136,12 @@ std::vector TrackingMode::getTrackingParameters(TrackingMode trackParams[3].TrackletMinPt = 0.1f; trackParams[3].CellDeltaTanLambdaSigma *= 4.; } - for (size_t ip = 0; ip < trackParams.size(); ip++) { + for (int ip = 0; ip < (int)trackParams.size(); ip++) { auto& param = trackParams[ip]; param.ZBins = 64; param.PhiBins = 32; // check if something was overridden via configurable params - if (ip < tc.MaxIter) { + if (ip < constants::MaxIter) { if (tc.startLayerMask[ip] > 0) { trackParams[2].StartLayerMask = tc.startLayerMask[ip]; } @@ -149,7 +149,7 @@ std::vector TrackingMode::getTrackingParameters(TrackingMode param.MinTrackLength = tc.minTrackLgtIter[ip]; } for (int ilg = tc.MaxTrackLength; ilg >= tc.MinTrackLength; ilg--) { - int lslot0 = (tc.MaxTrackLength - ilg), lslot = lslot0 + ip * (tc.MaxTrackLength - tc.MinTrackLength + 1); + int lslot0 = (tc.MaxTrackLength - ilg), lslot = lslot0 + (ip * (tc.MaxTrackLength - tc.MinTrackLength + 1)); if (tc.minPtIterLgt[lslot] > 0.) { param.MinPt[lslot0] = tc.minPtIterLgt[lslot]; } @@ -240,27 +240,15 @@ std::vector TrackingMode::getTrackingParameters(TrackingMode std::vector TrackingMode::getVertexingParameters(TrackingMode::Type mode) { const auto& vc = o2::its::VertexerParamConfig::Instance(); - std::vector vertParams; - if (mode == TrackingMode::Async) { - vertParams.resize(2); // The number of actual iterations will be set as a configKeyVal to allow for pp/PbPb choice - vertParams[1].phiCut = 0.015f; - vertParams[1].tanLambdaCut = 0.015f; - } else if (mode == TrackingMode::Sync) { - vertParams.resize(1); - } else if (mode == TrackingMode::Cosmics) { - vertParams.resize(1); - } else { - LOGP(fatal, "Unsupported ITS vertexing mode {} ", toString(mode)); - } - + std::vector vertParams(2); // The number of actual iterations will be set as a configKeyVal to allow for pp/PbPb choice // global parameters set for every iteration for (auto& p : vertParams) { + p.vertPerRofThreshold = vc.vertPerRofThreshold; p.SaveTimeBenchmarks = vc.saveTimeBenchmarks; p.PrintMemory = vc.printMemory; p.MaxMemory = vc.maxMemory; p.DropTFUponFailure = vc.dropTFUponFailure; - p.nIterations = vc.nIterations; - p.trackletSigma = vc.trackletSigma; + p.NSigmaCut = vc.nSigmaCut; p.maxZPositionAllowed = vc.maxZPositionAllowed; p.clusterContributorsCut = vc.clusterContributorsCut; p.suppressLowMultDebris = vc.suppressLowMultDebris; @@ -270,24 +258,35 @@ std::vector TrackingMode::getVertexingParameters(TrackingMo p.nThreads = vc.nThreads; p.ZBins = vc.ZBins; p.PhiBins = vc.PhiBins; - p.useTruthSeeding = vc.useTruthSeeding; + p.maxTrackletsPerCluster = vc.maxTrackletsPerCluster; + p.zCut = vc.zCut; + p.phiCut = vc.phiCut; + p.pairCut = vc.pairCut; + p.clusterCut = vc.clusterCut; + p.coarseZWindow = vc.coarseZWindow; + p.seedDedupZCut = vc.seedDedupZCut; + p.refitDedupZCut = vc.refitDedupZCut; + p.duplicateZCut = vc.duplicateZCut; + p.finalSelectionZCut = vc.finalSelectionZCut; + p.duplicateDistance2Cut = vc.duplicateDistance2Cut; + p.tanLambdaCut = vc.tanLambdaCut; + } + + if (mode == TrackingMode::Async) { + // relax for UPC iteration + vertParams[1].phiCut = 0.015f; + vertParams[1].tanLambdaCut = 0.015f; + vertParams[1].maxTrackletsPerCluster = 2000; + } else if (mode == TrackingMode::Sync || TrackingMode::Cosmics) { + vertParams.resize(1); + } else { + LOGP(fatal, "Unsupported ITS vertexing mode {} ", toString(mode)); + } + + if (vertParams.size() > vc.nIterations) { + vertParams.resize(vc.nIterations); } - // set for now outside to not disturb status quo - vertParams[0].vertNsigmaCut = vc.vertNsigmaCut; - vertParams[0].vertRadiusSigma = vc.vertRadiusSigma; - vertParams[0].maxTrackletsPerCluster = vc.maxTrackletsPerCluster; - vertParams[0].zCut = vc.zCut; - vertParams[0].phiCut = vc.phiCut; - vertParams[0].pairCut = vc.pairCut; - vertParams[0].clusterCut = vc.clusterCut; - vertParams[0].coarseZWindow = vc.coarseZWindow; - vertParams[0].seedDedupZCut = vc.seedDedupZCut; - vertParams[0].refitDedupZCut = vc.refitDedupZCut; - vertParams[0].duplicateZCut = vc.duplicateZCut; - vertParams[0].finalSelectionZCut = vc.finalSelectionZCut; - vertParams[0].duplicateDistance2Cut = vc.duplicateDistance2Cut; - vertParams[0].tanLambdaCut = vc.tanLambdaCut; return vertParams; } diff --git a/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx b/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx index dc032a46213a9..fa881789af296 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx @@ -31,12 +31,6 @@ using o2::its::constants::GB; template Tracker::Tracker(TrackerTraits* traits) : mTraits(traits) { - /// Initialise standard configuration with 1 iteration - mTrkParams.resize(1); - if (traits->isGPU()) { - ITSGpuTrackingParamConfig::Instance().maybeOverride(); - ITSGpuTrackingParamConfig::Instance().printKeyValues(true, true); - } } template @@ -46,7 +40,6 @@ void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& er double total{0}; mTraits->updateTrackingParameters(mTrkParams); - mTimeFrame->updateROFVertexLookupTable(); int maxNvertices{-1}; if (mTrkParams[0].PerPrimaryVertexProcessing) { diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackingConfigParam.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackingConfigParam.cxx index 3101c34d4ab8f..47b5f8ffffdb1 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackingConfigParam.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackingConfigParam.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// 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. // @@ -9,36 +9,6 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include - -#include "Framework/Logger.h" #include "ITStracking/TrackingConfigParam.h" - O2ParamImpl(o2::its::VertexerParamConfig); O2ParamImpl(o2::its::TrackerParamConfig); -O2ParamImpl(o2::its::ITSGpuTrackingParamConfig); - -namespace o2::its -{ - -void ITSGpuTrackingParamConfig::maybeOverride() const -{ - if (nBlocks == OverrideValue && nThreads == OverrideValue) { - return; - } - const auto name = getName(); - auto members = getDataMembers(); - for (auto member : *members) { - if (!member.name.ends_with(BlocksName) && !member.name.ends_with(ThreadsName)) { - if (nBlocks != OverrideValue && member.name.starts_with(BlocksName) && (member.value != nBlocks)) { - o2::conf::ConfigurableParam::setValue(name, member.name, nBlocks); - } - if (nThreads != OverrideValue && member.name.starts_with(ThreadsName) && (member.value != nThreads)) { - o2::conf::ConfigurableParam::setValue(name, member.name, nThreads); - } - } - } - LOGP(info, "Overwriting gpu threading parameters"); -} // namespace o2::its - -} // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx index eb0841888b03e..fcd9024a74709 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx @@ -209,10 +209,7 @@ void ITSTrackingInterface::run(framework::ProcessingContext& pc) auto clockROFspan = rofsinput[clockLayerId]; auto clockTiming = mTimeFrame->getROFOverlapTableView().getClockLayer(); for (auto iRof{0}; iRof < clockROFspan.size(); ++iRof) { - bounded_vector vtxVecLoc; auto& vtxROF = vertROFvec.emplace_back(clockROFspan[iRof]); - vtxROF.setFirstEntry((int)vertices.size()); - if (mRunVertexer) { auto vtxSpan = mTimeFrame->getPrimaryVertices(clockLayerId, iRof); if (o2::its::TrackerParamConfig::Instance().doUPCIteration) { @@ -231,7 +228,6 @@ void ITSTrackingInterface::run(framework::ProcessingContext& pc) } else { vtxROF.setFlag(o2::itsmft::ROFRecord::VtxStdMode); } - vtxROF.setNEntries((int)vtxSpan.size()); } } diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackingLinkDef.h b/Detectors/ITSMFT/ITS/tracking/src/TrackingLinkDef.h index 0640ff98297b9..46af692fe0c15 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackingLinkDef.h +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackingLinkDef.h @@ -39,9 +39,6 @@ #pragma link C++ class o2::its::TrackerParamConfig + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::TrackerParamConfig> + ; -#pragma link C++ class o2::its::ITSGpuTrackingParamConfig + ; -#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::ITSGpuTrackingParamConfig> + ; - #pragma link C++ class o2::its::FastMultEstConfig + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::FastMultEstConfig> + ; diff --git a/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx b/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx index 222b4801a5767..cbff174634ec8 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx @@ -60,7 +60,7 @@ float Vertexer::clustersToVertices(LogFunc logger) float timeTracklet{0.f}, timeSelection{0.f}, timeVertexing{0.f}, timeInit{0.f}; try { - for (int iteration = 0; iteration < std::min(mVertParams[0].nIterations, (int)mVertParams.size()); ++iteration) { + for (int iteration = 0; iteration < (int)mVertParams.size(); ++iteration) { mMemoryPool->setMaxMemory(mVertParams[iteration].MaxMemory); unsigned int nTracklets01{0}, nTracklets12{0}; logger(fmt::format("=== ITS {} Seeding vertexer iteration {} summary:", mTraits->getName(), iteration)); @@ -71,12 +71,18 @@ float Vertexer::clustersToVertices(LogFunc logger) nTracklets01 = mTimeFrame->getTotalTrackletsTF(0); nTracklets12 = mTimeFrame->getTotalTrackletsTF(1); auto timeSelectionIteration = evaluateTask(&Vertexer::validateTracklets, StateNames[mCurState = Validating], iteration, evalLog, iteration); + const auto nVerticesBefore = mTimeFrame->getPrimaryVertices().size(); auto timeVertexingIteration = evaluateTask(&Vertexer::findVertices, StateNames[mCurState = Finding], iteration, evalLog, iteration); - printEpilog(logger, nTracklets01, nTracklets12, mTimeFrame->getNLinesTotal(), mTimeFrame->getPrimaryVertices().size(), timeInitIteration, timeTrackletIteration, timeSelectionIteration, timeVertexingIteration); + const auto nVerticesAfter = mTimeFrame->getPrimaryVertices().size(); + printEpilog(logger, nTracklets01, nTracklets12, mTimeFrame->getNLinesTotal(), nVerticesAfter - nVerticesBefore, nVerticesAfter, timeTrackletIteration, timeSelectionIteration, timeVertexingIteration); timeInit += timeInitIteration; timeTracklet += timeTrackletIteration; timeSelection += timeSelectionIteration; timeVertexing += timeVertexingIteration; + + // update LUT with all currently found vertices so in second iteration we can check vertPerROFThreshold + sortVertices(); + mTimeFrame->updateROFVertexLookupTable(); } } catch (const BoundedMemoryResource::MemoryLimitExceeded& err) { handleException(err); @@ -86,8 +92,6 @@ float Vertexer::clustersToVertices(LogFunc logger) LOGP(fatal, "Uncaught exception!"); } - sortVertices(); - return timeInit + timeTracklet + timeSelection + timeVertexing; } @@ -134,12 +138,12 @@ void Vertexer::adoptTimeFrame(TimeFrameN& tf) template void Vertexer::printEpilog(LogFunc& logger, const unsigned int trackletN01, const unsigned int trackletN12, - const unsigned selectedN, const unsigned int vertexN, const float initT, + const unsigned selectedN, const unsigned int vertexN, const unsigned int totalVertexN, const float trackletT, const float selecT, const float vertexT) { logger(fmt::format(" - {} Vertexer: found {} | {} tracklets in: {} ms", mTraits->getName(), trackletN01, trackletN12, trackletT)); logger(fmt::format(" - {} Vertexer: selected {} tracklets in: {} ms", mTraits->getName(), selectedN, selecT)); - logger(fmt::format(" - {} Vertexer: found {} vertices in: {} ms", mTraits->getName(), vertexN, vertexT)); + logger(fmt::format(" - {} Vertexer: found {} vertices in: {} ms (total: {})", mTraits->getName(), vertexN, vertexT, totalVertexN)); if (mVertParams[0].PrintMemory) { mTimeFrame->printArtefactsMemory(); mMemoryPool->print(); diff --git a/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx index a22d2d6c60990..d0baa65c49147 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx @@ -168,15 +168,15 @@ void VertexerTraits::computeTracklets(const int iteration) { mTaskArena->execute([&] { tbb::parallel_for(0, mTimeFrame->getNrof(1), [&](const short pivotRofId) { - bool skipROF = !mTimeFrame->getROFMaskView().isROFEnabled(1, pivotRofId); + bool skip = skipROF(iteration, pivotRofId); const auto& rofRange01 = mTimeFrame->getROFOverlapTableView().getOverlap(1, 0, pivotRofId); for (auto targetRofId = rofRange01.getFirstEntry(); targetRofId < rofRange01.getEntriesBound(); ++targetRofId) { const auto timeErr = mTimeFrame->getROFOverlapTableView().getTimeStamp(0, targetRofId, 1, pivotRofId); trackleterKernelHost( - !skipROF ? mTimeFrame->getClustersOnLayer(targetRofId, 0) : gsl::span(), // Clusters to be matched with the next layer in target rof - !skipROF ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), // Clusters to be matched with the current layer in pivot rof - mTimeFrame->getUsedClustersROF(targetRofId, 0), // Span of the used clusters in the target rof - mTimeFrame->getIndexTable(targetRofId, 0).data(), // Index table to access the data on the next layer in target rof + !skip ? mTimeFrame->getClustersOnLayer(targetRofId, 0) : gsl::span(), // Clusters to be matched with the next layer in target rof + !skip ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), // Clusters to be matched with the current layer in pivot rof + mTimeFrame->getUsedClustersROF(targetRofId, 0), // Span of the used clusters in the target rof + mTimeFrame->getIndexTable(targetRofId, 0).data(), // Index table to access the data on the next layer in target rof mVrtParams[iteration].phiCut, mTimeFrame->getTracklets()[0], // Flat tracklet buffer mTimeFrame->getNTrackletsCluster(pivotRofId, 0), // Span of the number of tracklets per each cluster in pivot rof @@ -191,8 +191,8 @@ void VertexerTraits::computeTracklets(const int iteration) for (auto targetRofId = rofRange12.getFirstEntry(); targetRofId < rofRange12.getEntriesBound(); ++targetRofId) { const auto timeErr = mTimeFrame->getROFOverlapTableView().getTimeStamp(2, targetRofId, 1, pivotRofId); trackleterKernelHost( - !skipROF ? mTimeFrame->getClustersOnLayer(targetRofId, 2) : gsl::span(), - !skipROF ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), + !skip ? mTimeFrame->getClustersOnLayer(targetRofId, 2) : gsl::span(), + !skip ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), mTimeFrame->getUsedClustersROF(targetRofId, 2), mTimeFrame->getIndexTable(targetRofId, 2).data(), mVrtParams[iteration].phiCut, @@ -219,14 +219,14 @@ void VertexerTraits::computeTracklets(const int iteration) } tbb::parallel_for(0, mTimeFrame->getNrof(1), [&](const short pivotRofId) { - bool skipROF = !mTimeFrame->getROFMaskView().isROFEnabled(1, pivotRofId); + bool skip = skipROF(iteration, pivotRofId); const int globalOffsetPivot = mTimeFrame->getSortedStartIndex(pivotRofId, 1); const auto& rofRange01 = mTimeFrame->getROFOverlapTableView().getOverlap(1, 0, pivotRofId); for (auto targetRofId = rofRange01.getFirstEntry(); targetRofId < rofRange01.getEntriesBound(); ++targetRofId) { const auto timeErr = mTimeFrame->getROFOverlapTableView().getTimeStamp(0, targetRofId, 1, pivotRofId); trackleterKernelHost( - !skipROF ? mTimeFrame->getClustersOnLayer(targetRofId, 0) : gsl::span(), - !skipROF ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), + !skip ? mTimeFrame->getClustersOnLayer(targetRofId, 0) : gsl::span(), + !skip ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), mTimeFrame->getUsedClustersROF(targetRofId, 0), mTimeFrame->getIndexTable(targetRofId, 0).data(), mVrtParams[iteration].phiCut, @@ -243,8 +243,8 @@ void VertexerTraits::computeTracklets(const int iteration) for (auto targetRofId = rofRange12.getFirstEntry(); targetRofId < rofRange12.getEntriesBound(); ++targetRofId) { const auto timeErr = mTimeFrame->getROFOverlapTableView().getTimeStamp(2, targetRofId, 1, pivotRofId); trackleterKernelHost( - !skipROF ? mTimeFrame->getClustersOnLayer(targetRofId, 2) : gsl::span(), - !skipROF ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), + !skip ? mTimeFrame->getClustersOnLayer(targetRofId, 2) : gsl::span(), + !skip ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), mTimeFrame->getUsedClustersROF(targetRofId, 2), mTimeFrame->getIndexTable(targetRofId, 2).data(), mVrtParams[iteration].phiCut, @@ -293,7 +293,7 @@ void VertexerTraits::computeTrackletMatching(const int iteration) tbb::blocked_range(0, (short)mTimeFrame->getNrof(1)), [&](const tbb::blocked_range& Rofs) { for (short pivotRofId = Rofs.begin(); pivotRofId < Rofs.end(); ++pivotRofId) { - if (mTimeFrame->getFoundTracklets(pivotRofId, 0).empty()) { + if (mTimeFrame->getFoundTracklets(pivotRofId, 0).empty() || skipROF(iteration, pivotRofId)) { continue; } mTimeFrame->getLines(pivotRofId).reserve(mTimeFrame->getNTrackletsCluster(pivotRofId, 0).size()); @@ -330,7 +330,6 @@ void VertexerTraits::computeVertices(const int iteration) const int nRofs = mTimeFrame->getNrof(1); std::vector> rofVertices(nRofs); std::vector> rofLabels(nRofs); - const float nsigmaCut = std::min(mVrtParams[iteration].vertNsigmaCut * mVrtParams[iteration].vertNsigmaCut * (mVrtParams[iteration].vertRadiusSigma * mVrtParams[iteration].vertRadiusSigma + mVrtParams[iteration].trackletSigma * mVrtParams[iteration].trackletSigma), 1.98f); const float pairCut2 = mVrtParams[iteration].pairCut * mVrtParams[iteration].pairCut; const float duplicateZCut = mVrtParams[iteration].duplicateZCut > 0.f ? mVrtParams[iteration].duplicateZCut : std::max(4.f * mVrtParams[iteration].pairCut, 0.5f * mVrtParams[iteration].clusterCut); const float duplicateDistance2Cut = mVrtParams[iteration].duplicateDistance2Cut > 0.f ? mVrtParams[iteration].duplicateDistance2Cut : std::max(16.f * pairCut2, 0.0625f * mVrtParams[iteration].clusterCut * mVrtParams[iteration].clusterCut); @@ -352,6 +351,9 @@ void VertexerTraits::computeVertices(const int iteration) settings.memoryPool = mMemoryPool; const auto processROF = [&](const int rofId) { + if (skipROF(iteration, rofId)) { + return; + } auto& lines = mTimeFrame->getLines(rofId); auto clusters = line_vertexer::buildClusters(std::span{lines.data(), lines.size()}, settings); deepVectorClear(lines); // not needed after @@ -508,7 +510,7 @@ void VertexerTraits::computeVertices(const int iteration) for (const auto sortedId : sortedIndices) { const auto& cluster = clusters[selectedIndices[sortedId]]; const auto beamDistance2 = clusterBeamDistance2(cluster); - if (!(beamDistance2 < nsigmaCut)) { + if (!(beamDistance2 < mVrtParams[iteration].NSigmaCut)) { continue; } if (cluster.getSize() < mVrtParams[iteration].clusterContributorsCut) { @@ -620,5 +622,11 @@ void VertexerTraits::setNThreads(int n, std::shared_ptr +bool VertexerTraits::skipROF(int iteration, int rof) const +{ + return iteration && (int)mTimeFrame->getROFVertexLookupTableView().getVertices(1, rof).getEntries() > mVrtParams[iteration].vertPerRofThreshold; +} + template class VertexerTraits<7>; } // namespace o2::its diff --git a/GPU/Workflow/src/GPUWorkflowITS.cxx b/GPU/Workflow/src/GPUWorkflowITS.cxx index fb27df2ec08b9..ac9834d3eacd1 100644 --- a/GPU/Workflow/src/GPUWorkflowITS.cxx +++ b/GPU/Workflow/src/GPUWorkflowITS.cxx @@ -40,7 +40,6 @@ int32_t GPURecoWorkflowSpec::runITSTracking(o2::framework::ProcessingContext& pc if (mNTFs == 1 && pc.services().get().inputTimesliceId == 0) { o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, o2::its::VertexerParamConfig::Instance().getName()), o2::its::VertexerParamConfig::Instance().getName()); o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, o2::its::TrackerParamConfig::Instance().getName()), o2::its::TrackerParamConfig::Instance().getName()); - o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, o2::its::ITSGpuTrackingParamConfig::Instance().getName()), o2::its::ITSGpuTrackingParamConfig::Instance().getName()); } return 0; } diff --git a/prodtests/full-system-test/dpl-workflow.sh b/prodtests/full-system-test/dpl-workflow.sh index e954f6875eb30..52407cc3a4073 100755 --- a/prodtests/full-system-test/dpl-workflow.sh +++ b/prodtests/full-system-test/dpl-workflow.sh @@ -119,7 +119,7 @@ EVE_OPT=" --jsons-folder $EDJSONS_DIR" # ITS vertexing settings if [[ $BEAMTYPE == "pp" || $LIGHTNUCLEI == "1" ]]; then - ITS_CONFIG_KEY+="ITSVertexerParam.phiCut=0.4;ITSVertexerParam.tanLambdaCut=0.17;ITSVertexerParam.pairCut=0.0317563;ITSVertexerParam.clusterCut=0.6640964;ITSVertexerParam.coarseZWindow=0.2049018;ITSVertexerParam.seedDedupZCut=0.0711793;ITSVertexerParam.refitDedupZCut=0.0680009;ITSVertexerParam.duplicateZCut=0.1582193;ITSVertexerParam.finalSelectionZCut=0.1081465;ITSVertexerParam.duplicateDistance2Cut=0.0117033;ITSVertexerParam.clusterContributorsCut=2;ITSVertexerParam.seedMemberRadiusZ=0;ITSVertexerParam.vertNsigmaCut=4.0;ITSVertexerParam.vertRadiusSigma=0.0452309;ITSVertexerParam.trackletSigma=0.0025941;ITSVertexerParam.suppressLowMultDebris=0;" + ITS_CONFIG_KEY+="ITSVertexerParam.phiCut=0.4;ITSVertexerParam.tanLambdaCut=0.17;ITSVertexerParam.pairCut=0.0317563;ITSVertexerParam.clusterCut=0.6640964;ITSVertexerParam.coarseZWindow=0.2049018;ITSVertexerParam.seedDedupZCut=0.0711793;ITSVertexerParam.refitDedupZCut=0.0680009;ITSVertexerParam.duplicateZCut=0.1582193;ITSVertexerParam.finalSelectionZCut=0.1081465;ITSVertexerParam.duplicateDistance2Cut=0.0117033;ITSVertexerParam.clusterContributorsCut=2;ITSVertexerParam.seedMemberRadiusZ=0;ITSVertexerParam.nSigmaCut=0.032841;ITSVertexerParam.suppressLowMultDebris=0;" fi if [[ $CTFINPUT != 1 ]]; then From 541bbcc89994e8595ec5090d1d9131277ad9a260 Mon Sep 17 00:00:00 2001 From: shahoian Date: Sun, 19 Apr 2026 19:13:34 +0200 Subject: [PATCH 076/285] Modernize to std::greater<> to please code checker --- Detectors/ITSMFT/ITS/tracking/src/LineVertexerHelpers.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Detectors/ITSMFT/ITS/tracking/src/LineVertexerHelpers.cxx b/Detectors/ITSMFT/ITS/tracking/src/LineVertexerHelpers.cxx index 592c22dedf347..cbb8d52571ec9 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/LineVertexerHelpers.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/LineVertexerHelpers.cxx @@ -743,7 +743,7 @@ bounded_vector> buildCoarseClusters(std::span using ActiveEntry = std::pair; bounded_vector activeEntries(settings.memoryPool.get()); - std::priority_queue, std::greater> activeByUpper(std::greater{}, std::move(activeEntries)); + std::priority_queue, std::greater<>> activeByUpper(std::greater<>{}, std::move(activeEntries)); bounded_vector activeMask(lineRefs.size(), 0, settings.memoryPool.get()); bounded_vector> activeByZBin(settings.memoryPool.get()); activeByZBin.reserve(nZBins); From 08549f4ee915deb8b18e205b2dd631a66c7c4114 Mon Sep 17 00:00:00 2001 From: Maximiliano Puccio Date: Tue, 21 Apr 2026 10:40:03 +0200 Subject: [PATCH 077/285] ALICE3: Add VD bandwidth monitoring macro (#15295) --- .../ALICE3/TRK/macros/test/CheckBandwidth.C | 561 +++++++++++++----- 1 file changed, 418 insertions(+), 143 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckBandwidth.C b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckBandwidth.C index 2087f88a87d6b..06d24361c7721 100644 --- a/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckBandwidth.C +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckBandwidth.C @@ -9,17 +9,19 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file CheckDigits.C -/// \brief Simple macro to check TRK digits +/// \file CheckBandwidth.C +/// \brief Simple macro to check TRK bandwidth #if !defined(__CLING__) || defined(__ROOTCLING__) #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -38,7 +40,7 @@ namespace { -constexpr double DigitBits = 16.; +constexpr double DigitBits = 24.; constexpr double BunchCrossingNS = 25.; constexpr int ReadoutCycleBC = 18; constexpr int ReadoutCycleSimBC = 18; @@ -50,13 +52,15 @@ void CheckBandwidth(std::string digifile = "trkdigits.root", std::string inputGe gStyle->SetPalette(55); gStyle->SetOptStat(0); + // --- Drawing helpers --- + auto drawSummary = [](double averageValue, double peakValue, const char* unit) { TLatex latex; latex.SetNDC(); latex.SetTextSize(0.03); latex.SetTextAlign(13); - latex.DrawLatex(0.04, 0.05, Form("avg: %.3f %s", averageValue, unit)); - latex.DrawLatex(0.34, 0.05, Form("peak: %.3f %s", peakValue, unit)); + latex.DrawLatex(0.04, 0.06, Form("avg: %.3f %s", averageValue, unit)); + latex.DrawLatex(0.04, 0.03, Form("peak: %.3f %s", peakValue, unit)); }; auto drawCollisionSummary = [](double averageValue, double nonEmptyAverageValue, double peakValue) { @@ -69,17 +73,103 @@ void CheckBandwidth(std::string digifile = "trkdigits.root", std::string inputGe latex.DrawLatex(0.04, 0.06, Form("avg non-empty: %.3f collisions/ROF", nonEmptyAverageValue)); }; + auto drawCollisionInfoBox = [](double averageValue) { + const double effectiveIRRateHz = ReadoutCycleSeconds > 0. ? averageValue / ReadoutCycleSeconds : 0.; + TPaveText infoBox(0.55, 0.79, 0.88, 0.9, "NDC"); + infoBox.SetFillColor(0); + infoBox.SetBorderSize(1); + infoBox.SetTextAlign(12); + infoBox.SetTextSize(0.028); + infoBox.AddText(Form("effective IR: %.3f MHz", effectiveIRRateHz * 1.e-6)); + infoBox.AddText(Form("ROF length: %d BC", ReadoutCycleBC)); + infoBox.DrawClone(); + }; + + const TString outputPdf = "trk_bandwidth_report.pdf"; + bool pdfOpened = false; + TCanvas* lastPdfCanvas = nullptr; + auto appendCanvasToPdf = [&](TCanvas* canvas) { + if (!pdfOpened) { + canvas->Print(Form("%s[", outputPdf.Data())); + pdfOpened = true; + } + canvas->Print(outputPdf.Data()); + lastPdfCanvas = canvas; + }; + using namespace o2::base; using namespace o2::trk; TFile* f = TFile::Open("CheckBandwidth.root", "recreate"); - // Geometry + // --- Geometry --- + o2::base::GeometryManager::loadGeometry(inputGeom); auto* gman = o2::trk::GeometryTGeo::Instance(); gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); - // Collision Context + const int nVDPetals = gman->extractNumberOfPetalsVD(); + const int nVDLayers = gman->extractNumberOfLayersVD(); + const int nMLOTLayers = gman->getNumberOfLayersMLOT(); + const int nTotalLayers = nVDLayers + nMLOTLayers; + const int nChips = gman->getNumberOfChips(); + + // Precompute per-chip geometry — centralises all gman queries. + // globalLayer maps VD layers to [0, nVDLayers) and MLOT layers to [nVDLayers, nTotalLayers). + // disk == -1 identifies barrel chips (the only ones that produce digits in this detector). + struct ChipGeom { + int subDetID = -1, localLayer = -1, globalLayer = -1, disk = -1; + int stave = -1, halfStave = -1, petal = -1; + }; + std::vector chipGeom(nChips); + for (int chipID = 0; chipID < nChips; ++chipID) { + auto& g = chipGeom[chipID]; + g.subDetID = gman->getSubDetID(chipID); + g.localLayer = gman->getLayer(chipID); + g.disk = gman->getDisk(chipID); + g.globalLayer = g.localLayer + g.subDetID * nVDLayers; + g.stave = gman->getStave(chipID); + g.halfStave = std::max(0, gman->getHalfStave(chipID)); + g.petal = (g.subDetID == 0) ? gman->getPetalCase(chipID) : -1; + } + + // Number of barrel chips per global layer (used for per-layer bandwidth normalisation). + std::vector chipsPerLayer(nTotalLayers, 0u); + for (int chipID = 0; chipID < nChips; ++chipID) { + const auto& g = chipGeom[chipID]; + if (g.disk != -1 || g.globalLayer < 0 || g.globalLayer >= nTotalLayers) { + continue; + } + ++chipsPerLayer[g.globalLayer]; + } + + // MLOT sensor index within its half-stave, ordered by Z position. + // Precomputed here so the plotting loop only reads results. + std::vector chipSensorIndex(nChips, -1); + std::vector maxSensorsPerHalfStaveMLOT(nMLOTLayers, 0); + for (int layer = 0; layer < nMLOTLayers; ++layer) { + std::map, std::vector>> chipsPerHalfStave; + for (int chipID = 0; chipID < nChips; ++chipID) { + const auto& g = chipGeom[chipID]; + if (g.subDetID != 1 || g.localLayer != layer || g.disk != -1) { + continue; + } + const auto center = gman->getMatrixL2G(chipID)(o2::math_utils::Point3D(0.f, 0.f, 0.f)); + chipsPerHalfStave[{g.stave, g.halfStave}].push_back({center.Z(), chipID}); + } + for (auto& [key, chips] : chipsPerHalfStave) { + std::sort(chips.begin(), chips.end(), [](const auto& a, const auto& b) { + return std::abs(a.first - b.first) > 1.e-4 ? a.first < b.first : a.second < b.second; + }); + for (size_t i = 0; i < chips.size(); ++i) { + chipSensorIndex[chips[i].second] = (int)i; + } + maxSensorsPerHalfStaveMLOT[layer] = std::max(maxSensorsPerHalfStaveMLOT[layer], (int)chips.size()); + } + } + + // --- Collision context --- + TFile* ccFile = TFile::Open(collContextFile.data()); auto* digiContext = (o2::steer::DigitizationContext*)ccFile->Get("DigitizationContext"); const o2::InteractionRecord firstSampledIR{0, digiContext->getFirstOrbitForSampling()}; @@ -93,7 +183,6 @@ void CheckBandwidth(std::string digifile = "trkdigits.root", std::string inputGe if (nbc < 0) { continue; } - const size_t rofID = nbc / ReadoutCycleSimBC; if (rofID >= collisionsPerROF.size()) { collisionsPerROF.resize(rofID + 1, 0u); @@ -101,197 +190,383 @@ void CheckBandwidth(std::string digifile = "trkdigits.root", std::string inputGe ++collisionsPerROF[rofID]; } - // Digits + // --- Digits --- + TFile* digFile = TFile::Open(digifile.data()); TTree* digTree = (TTree*)digFile->Get("o2sim"); const int nDigitTreeEntries = digTree->GetEntries(); std::vector* digArr = nullptr; + std::vector* rofRecords = nullptr; digTree->SetBranchAddress("TRKDigit", &digArr); - - // Get Read Out Frame arrays - std::vector* ROFRecordArrray = nullptr; - digTree->SetBranchAddress("TRKDigitROF", &ROFRecordArrray); - std::vector& ROFRecordArrrayRef = *ROFRecordArrray; + digTree->SetBranchAddress("TRKDigitROF", &rofRecords); digTree->GetEntry(0); - if (nDigitTreeEntries > 1) { LOG(warning) << "Digit tree has " << nDigitTreeEntries << " entries, but this macro processes entry 0 only."; } - std::vector digitsPerChip(gman->getNumberOfChips(), 0ull); - std::vector maxDigitsPerROFPerChip(gman->getNumberOfChips(), 0u); - std::vector digitsInCurrentROFPerChip(gman->getNumberOfChips(), 0u); + const int nROFRec = (int)rofRecords->size(); + if (nROFRec != (int)collisionsPerROF.size()) { + LOG(fatal) << "Mismatch between number of ROF records in digit tree (" << nROFRec + << ") and number of ROFs computed from collisioncontext.root (" << collisionsPerROF.size() + << "). Check input files."; + } + + // --- Accumulate per-chip digit counts across all ROFs --- - const int nROFRec = (int)ROFRecordArrrayRef.size(); - const int nCollisionROFBins = std::max(nROFRec, static_cast(collisionsPerROF.size())); + const double rofNorm = nROFRec > 0 ? 1. / nROFRec : 0.; + const double bitsToGbps = ReadoutCycleSeconds > 0. ? DigitBits / ReadoutCycleSeconds / 1.e9 : 0.; - if (nCollisionROFBins > 0) { - auto* hCollisionsPerROF = new TH1D("h_collisions_per_rof", "Collisions per ROF;ROF id;N collisions", nCollisionROFBins, -0.5, nCollisionROFBins - 0.5); - double totalCollisionsPerROF = 0.; - double peakCollisionsPerROF = 0.; - int nNonEmptyROFs = 0; + std::vector digitsPerChip(nChips, 0ull); + std::vector maxDigitsPerROFPerChip(nChips, 0u); + std::vector digitsInCurrentROFPerChip(nChips, 0u); - for (int rofID = 0; rofID < nCollisionROFBins; ++rofID) { - const double nCollisions = rofID < static_cast(collisionsPerROF.size()) ? collisionsPerROF[rofID] : 0.; - hCollisionsPerROF->SetBinContent(rofID + 1, nCollisions); - totalCollisionsPerROF += nCollisions; - peakCollisionsPerROF = std::max(peakCollisionsPerROF, nCollisions); - if (nCollisions > 0.) { - ++nNonEmptyROFs; + for (unsigned int iROF = 0; iROF < rofRecords->size(); ++iROF) { + std::vector touchedChips; + const unsigned int rofStart = (*rofRecords)[iROF].getFirstEntry(); + const unsigned int rofEnd = rofStart + (*rofRecords)[iROF].getNEntries(); + + for (unsigned int iDigit = rofStart; iDigit < rofEnd; ++iDigit) { + if (iDigit % 1000 == 0) { + std::cout << "Reading digit " << iDigit << " / " << digArr->size() << "\r" << std::flush; } + const int chipID = (*digArr)[iDigit].getChipIndex(); + if (chipGeom[chipID].disk != -1) { + continue; + } + if (digitsInCurrentROFPerChip[chipID] == 0) { + touchedChips.push_back(chipID); + } + ++digitsPerChip[chipID]; + ++digitsInCurrentROFPerChip[chipID]; } - auto* canvCollisionsPerROF = new TCanvas("canvCollisionsPerROF", "Collisions per ROF", 1050, 1050); - canvCollisionsPerROF->SetTopMargin(0.08); - hCollisionsPerROF->Draw("hist"); - drawCollisionSummary(totalCollisionsPerROF / nCollisionROFBins, - nNonEmptyROFs > 0 ? totalCollisionsPerROF / nNonEmptyROFs : 0., - peakCollisionsPerROF); - canvCollisionsPerROF->SaveAs("trk_collisions_per_rof.png"); + for (const int chipID : touchedChips) { + maxDigitsPerROFPerChip[chipID] = std::max(maxDigitsPerROFPerChip[chipID], digitsInCurrentROFPerChip[chipID]); + digitsInCurrentROFPerChip[chipID] = 0; + } } - unsigned int rofIndex = 0; - unsigned int rofNEntries = 0; - - // LOOP on : ROFRecord array - for (unsigned int iROF = 0; iROF < ROFRecordArrrayRef.size(); iROF++) { - std::vector touchedChips; - - rofIndex = ROFRecordArrrayRef[iROF].getFirstEntry(); - rofNEntries = ROFRecordArrrayRef[iROF].getNEntries(); - - // LOOP on : digits array - for (unsigned int iDigit = rofIndex; iDigit < rofIndex + rofNEntries; iDigit++) { - if (iDigit % 1000 == 0) - std::cout << "Reading digit " << iDigit << " / " << digArr->size() << std::endl; + // --- Per-layer bandwidth distribution histograms (second scan over digits) --- - Int_t iDetID = (*digArr)[iDigit].getChipIndex(); - Int_t disk = gman->getDisk(iDetID); - Int_t subDetID = gman->getSubDetID(iDetID); + // Per-layer peak digit count (from per-chip maxima) — drives histogram binning. + std::vector maxDigitsPerLayer(nTotalLayers, 0u); + for (int chipID = 0; chipID < nChips; ++chipID) { + const auto& g = chipGeom[chipID]; + if (g.disk != -1 || g.globalLayer < 0 || g.globalLayer >= nTotalLayers) { + continue; + } + maxDigitsPerLayer[g.globalLayer] = std::max(maxDigitsPerLayer[g.globalLayer], maxDigitsPerROFPerChip[chipID]); + } - if (subDetID == 1 && disk == -1) { - if (digitsInCurrentROFPerChip[iDetID] == 0) { - touchedChips.push_back(iDetID); + std::vector hDigitsDistPerLayer(nTotalLayers, nullptr); + for (int l = 0; l < nTotalLayers; ++l) { + if (chipsPerLayer[l] == 0 || maxDigitsPerLayer[l] == 0) { + continue; + } + const int nBins = std::min((int)maxDigitsPerLayer[l] + 1, 200); + hDigitsDistPerLayer[l] = new TH1D(Form("h_digits_dist_layer%d", l), + Form("Layer %d;Fired pixels / ROF / chip;Probability", l), + nBins, -0.5, (double)maxDigitsPerLayer[l] + 0.5); + } + // digitsInCurrentROFPerChip is all zeros after the first scan — reuse it here. + { + std::vector touchedChips; + for (unsigned int iROF = 0; iROF < rofRecords->size(); ++iROF) { + touchedChips.clear(); + const unsigned int rofStart = (*rofRecords)[iROF].getFirstEntry(); + const unsigned int rofEnd = rofStart + (*rofRecords)[iROF].getNEntries(); + for (unsigned int iDigit = rofStart; iDigit < rofEnd; ++iDigit) { + const int chipID = (*digArr)[iDigit].getChipIndex(); + if (chipGeom[chipID].disk != -1) { + continue; } - digitsPerChip[iDetID]++; - ++digitsInCurrentROFPerChip[iDetID]; + if (digitsInCurrentROFPerChip[chipID] == 0) { + touchedChips.push_back(chipID); + } + ++digitsInCurrentROFPerChip[chipID]; + } + for (const int chipID : touchedChips) { + const int l = chipGeom[chipID].globalLayer; + if (hDigitsDistPerLayer[l]) { + hDigitsDistPerLayer[l]->Fill(digitsInCurrentROFPerChip[chipID]); + } + digitsInCurrentROFPerChip[chipID] = 0; } + } + } - } // end loop on digits array + // --- Per-layer bandwidth statistics, normalised by chips per layer --- + // + // avgDigitsPerROF : mean over chips of (total chip digits / nROFs) + // peakAvgDigitsPerROF : max over chips of (total chip digits / nROFs) + // avgMaxDigitsPerROF : mean over chips of (peak single-ROF digit count) + // peakMaxDigitsPerROF : max over chips of (peak single-ROF digit count) + // avg/peakBandwidthGbps derived from the avg/peak digit quantities above. - for (const auto chipID : touchedChips) { - maxDigitsPerROFPerChip[chipID] = std::max(maxDigitsPerROFPerChip[chipID], digitsInCurrentROFPerChip[chipID]); - digitsInCurrentROFPerChip[chipID] = 0; - } + struct LayerStats { + double avgDigitsPerROF = 0.; + double peakAvgDigitsPerROF = 0.; + double avgMaxDigitsPerROF = 0.; + double peakMaxDigitsPerROF = 0.; + double avgBandwidthGbps = 0.; + double peakBandwidthGbps = 0.; + }; + std::vector layerStats(nTotalLayers); - } // end loop on ROFRecords array + for (int chipID = 0; chipID < nChips; ++chipID) { + const auto& g = chipGeom[chipID]; + if (g.disk != -1 || g.globalLayer < 0 || g.globalLayer >= nTotalLayers) { + continue; + } + const int l = g.globalLayer; + const double avgDigits = digitsPerChip[chipID] * rofNorm; + const double maxDigits = (double)maxDigitsPerROFPerChip[chipID]; + layerStats[l].avgDigitsPerROF += avgDigits; + layerStats[l].avgMaxDigitsPerROF += maxDigits; + layerStats[l].peakAvgDigitsPerROF = std::max(layerStats[l].peakAvgDigitsPerROF, avgDigits); + layerStats[l].peakMaxDigitsPerROF = std::max(layerStats[l].peakMaxDigitsPerROF, maxDigits); + } + for (int l = 0; l < nTotalLayers; ++l) { + if (chipsPerLayer[l] > 0) { + const double norm = 1. / chipsPerLayer[l]; + layerStats[l].avgDigitsPerROF *= norm; + layerStats[l].avgMaxDigitsPerROF *= norm; + } + layerStats[l].avgBandwidthGbps = layerStats[l].avgDigitsPerROF * bitsToGbps; + layerStats[l].peakBandwidthGbps = layerStats[l].peakAvgDigitsPerROF * bitsToGbps; + } - const double rofNorm = nROFRec > 0 ? 1. / nROFRec : 0.; - const double bitsToMbps = ReadoutCycleSeconds > 0. ? DigitBits / ReadoutCycleSeconds / 1.e6 : 0.; - const int nMLOTLayers = gman->getNumberOfLayersMLOT(); + // --- Collision plots --- - for (int layer = 0; layer < nMLOTLayers; ++layer) { - int nStaves = gman->extractNumberOfStavesMLOT(layer); - std::map>> chipsPerStave; - std::vector sensorIdPerChip(gman->getNumberOfChips(), -1); - int maxSensorsPerStave = 0; + if (nROFRec > 0) { + auto* hCollisionsPerROF = new TH1D("h_collisions_per_rof", "Collisions per ROF;ROF id;N collisions", + nROFRec, -0.5, nROFRec - 0.5); + double totalCollisionsPerROF = 0.; + double peakCollisionsPerROF = 0.; + int nNonEmptyROFs = 0; - for (int chipID = 0; chipID < gman->getNumberOfChips(); ++chipID) { - if (gman->getSubDetID(chipID) != 1 || gman->getLayer(chipID) != layer) { - continue; + for (int rofID = 0; rofID < nROFRec; ++rofID) { + const double nColl = collisionsPerROF[rofID]; + hCollisionsPerROF->SetBinContent(rofID + 1, nColl); + totalCollisionsPerROF += nColl; + peakCollisionsPerROF = std::max(peakCollisionsPerROF, nColl); + if (nColl > 0.) { + ++nNonEmptyROFs; } - const int staveID = gman->getStave(chipID); - const auto sensorCenter = gman->getMatrixL2G(chipID)(o2::math_utils::Point3D(0.f, 0.f, 0.f)); - chipsPerStave[staveID].push_back({sensorCenter.Z(), chipID}); } - for (auto& [staveID, chips] : chipsPerStave) { - std::sort(chips.begin(), chips.end(), [](const auto& left, const auto& right) { - if (std::abs(left.first - right.first) > 1.e-4) { - return left.first < right.first; - } - return left.second < right.second; - }); - - for (size_t sensorIndex = 0; sensorIndex < chips.size(); ++sensorIndex) { - sensorIdPerChip[chips[sensorIndex].second] = sensorIndex; - } + const double avgCollisionsPerROF = totalCollisionsPerROF / nROFRec; + auto* canvCollisionsPerROF = new TCanvas("canvCollisionsPerROF", "Collisions per ROF", 1050, 1050); + canvCollisionsPerROF->SetTopMargin(0.08); + hCollisionsPerROF->Draw("hist"); + drawCollisionSummary(avgCollisionsPerROF, + nNonEmptyROFs > 0 ? totalCollisionsPerROF / nNonEmptyROFs : 0., + peakCollisionsPerROF); + drawCollisionInfoBox(avgCollisionsPerROF); + appendCanvasToPdf(canvCollisionsPerROF); + } - maxSensorsPerStave = std::max(maxSensorsPerStave, static_cast(chips.size())); + // --- VD plots --- + + auto* hVDDigitsPerROF = new TH2F("h_digits_per_rof_vd", + "VD average digits per ROF;petal id;layer id;digits / ROF", + nVDPetals, -0.5, nVDPetals - 0.5, nVDLayers, -0.5, nVDLayers - 0.5); + auto* hVDMaxDigitsPerROF = new TH2F("h_max_digits_per_rof_vd", + "VD max digits in one ROF;petal id;layer id;max digits / ROF", + nVDPetals, -0.5, nVDPetals - 0.5, nVDLayers, -0.5, nVDLayers - 0.5); + auto* hVDBandwidth = new TH2F("h_bandwidth_vd", + "VD bandwidth map;petal id;layer id;bandwidth (Gbit/s)", + nVDPetals, -0.5, nVDPetals - 0.5, nVDLayers, -0.5, nVDLayers - 0.5); + + for (auto* hist : {hVDDigitsPerROF, hVDMaxDigitsPerROF, hVDBandwidth}) { + for (int petalID = 0; petalID < nVDPetals; ++petalID) { + hist->GetXaxis()->SetBinLabel(petalID + 1, Form("%d", petalID)); } + for (int layerID = 0; layerID < nVDLayers; ++layerID) { + hist->GetYaxis()->SetBinLabel(layerID + 1, Form("%d", layerID)); + } + hist->GetXaxis()->SetNdivisions(0, kFALSE); + hist->GetYaxis()->SetNdivisions(0, kFALSE); + hist->LabelsOption("h", "X"); + hist->LabelsOption("h", "Y"); + } - if (maxSensorsPerStave == 0) { + double totalVDAvgDigits = 0., peakVDAvgDigits = 0.; + double totalVDMaxDigits = 0., peakVDMaxDigits = 0.; + double totalVDBandwidth = 0., peakVDBandwidth = 0.; + + for (int chipID = 0; chipID < nChips; ++chipID) { + const auto& g = chipGeom[chipID]; + if (g.subDetID != 0 || g.disk != -1 || g.localLayer < 0 || g.localLayer >= nVDLayers) { continue; } + if (g.petal < 0 || g.petal >= nVDPetals) { + continue; + } + const double avgDigits = digitsPerChip[chipID] * rofNorm; + const double maxDigits = (double)maxDigitsPerROFPerChip[chipID]; + const double bandwidth = avgDigits * bitsToGbps; + + hVDDigitsPerROF->SetBinContent(g.petal + 1, g.localLayer + 1, avgDigits); + hVDMaxDigitsPerROF->SetBinContent(g.petal + 1, g.localLayer + 1, maxDigits); + hVDBandwidth->SetBinContent(g.petal + 1, g.localLayer + 1, bandwidth); + + totalVDAvgDigits += avgDigits; + totalVDMaxDigits += maxDigits; + totalVDBandwidth += bandwidth; + peakVDAvgDigits = std::max(peakVDAvgDigits, avgDigits); + peakVDMaxDigits = std::max(peakVDMaxDigits, maxDigits); + peakVDBandwidth = std::max(peakVDBandwidth, bandwidth); + } - auto* hDigitsPerROF = new TH2F(Form("h_digits_per_rof_layer%d", layer), - Form("Layer %d average digits per ROF;stave id;sensor id in stave;digits / ROF", layer), - nStaves, -0.5, nStaves - 0.5, maxSensorsPerStave, -0.5, maxSensorsPerStave - 0.5); - auto* hMaxDigitsPerROF = new TH2F(Form("h_max_digits_per_rof_layer%d", layer), - Form("Layer %d max digits in one ROF;stave id;sensor id in stave;max digits / ROF", layer), - nStaves, -0.5, nStaves - 0.5, maxSensorsPerStave, -0.5, maxSensorsPerStave - 0.5); - auto* hBandwidth = new TH2F(Form("h_bandwidth_layer%d", layer), - Form("Layer %d bandwidth map;stave id;sensor id in stave;bandwidth (Mbit/s)", layer), - nStaves, -0.5, nStaves - 0.5, maxSensorsPerStave, -0.5, maxSensorsPerStave - 0.5); - double totalAvgDigitsPerROF = 0.; - double totalMaxDigitsPerROF = 0.; - double totalBandwidthMbps = 0.; - double peakAvgDigitsPerROF = 0.; - double peakMaxDigitsPerROF = 0.; - double peakBandwidthMbps = 0.; - int nFilledSensors = 0; + const int nVDBarrelChips = std::accumulate(chipsPerLayer.begin(), chipsPerLayer.begin() + nVDLayers, 0); + const double normVD = nVDBarrelChips > 0 ? 1. / nVDBarrelChips : 0.; + const double avgVDAvgDigits = totalVDAvgDigits * normVD; + const double avgVDMaxDigits = totalVDMaxDigits * normVD; + const double avgVDBandwidth = totalVDBandwidth * normVD; + + auto* canvVDBandwidth = new TCanvas("canvBandwidthVD", "VD bandwidth", 1050, 1050); + canvVDBandwidth->SetTopMargin(0.08); + canvVDBandwidth->SetRightMargin(0.18); + hVDBandwidth->GetZaxis()->SetRangeUser(0., avgVDBandwidth > 0. ? 3. * avgVDBandwidth : 1.); + hVDBandwidth->SetMarkerSize(1.8); + hVDBandwidth->Draw("colz text"); + drawSummary(avgVDBandwidth, peakVDBandwidth, "Gbit/s"); + appendCanvasToPdf(canvVDBandwidth); + + auto* canvVDDigits = new TCanvas("canvDigitsVD", "VD digits per ROF", 1050, 1050); + canvVDDigits->SetTopMargin(0.08); + canvVDDigits->SetRightMargin(0.18); + hVDDigitsPerROF->SetMarkerSize(1.8); + hVDDigitsPerROF->Draw("colz text"); + drawSummary(avgVDAvgDigits, peakVDAvgDigits, "digits/ROF"); + appendCanvasToPdf(canvVDDigits); + + auto* canvVDMaxDigits = new TCanvas("canvMaxDigitsVD", "VD max digits per ROF", 1050, 1050); + canvVDMaxDigits->SetTopMargin(0.08); + canvVDMaxDigits->SetRightMargin(0.18); + hVDMaxDigitsPerROF->SetMarkerSize(1.8); + hVDMaxDigitsPerROF->Draw("colz text"); + drawSummary(avgVDMaxDigits, peakVDMaxDigits, "digits/ROF"); + appendCanvasToPdf(canvVDMaxDigits); + + // --- MLOT per-layer plots --- - for (int chipID = 0; chipID < gman->getNumberOfChips(); ++chipID) { - if (gman->getSubDetID(chipID) != 1 || gman->getLayer(chipID) != layer) { + for (int layer = 0; layer < nMLOTLayers; ++layer) { + if (maxSensorsPerHalfStaveMLOT[layer] == 0) { + continue; + } + const int outputLayer = nVDLayers + layer; + const int nStaves = gman->extractNumberOfStavesMLOT(layer); + const int nHalfStaves = std::max(1, gman->getNumberOfHalfStaves(layer)); + const int maxSensors = maxSensorsPerHalfStaveMLOT[layer]; + + auto* hDigitsPerROF = new TH2F(Form("h_digits_per_rof_layer%d", outputLayer), + Form("Layer %d average digits per ROF;stave id / half-stave;sensor id in half-stave;digits / ROF", outputLayer), + nStaves * nHalfStaves, -0.5, nStaves - 0.5, maxSensors, -0.5, maxSensors - 0.5); + auto* hMaxDigitsPerROF = new TH2F(Form("h_max_digits_per_rof_layer%d", outputLayer), + Form("Layer %d max digits in one ROF;stave id / half-stave;sensor id in half-stave;max digits / ROF", outputLayer), + nStaves * nHalfStaves, -0.5, nStaves - 0.5, maxSensors, -0.5, maxSensors - 0.5); + auto* hBandwidth = new TH2F(Form("h_bandwidth_layer%d", outputLayer), + Form("Layer %d bandwidth map;stave id / half-stave;sensor id in half-stave;bandwidth (Gbit/s)", outputLayer), + nStaves * nHalfStaves, -0.5, nStaves - 0.5, maxSensors, -0.5, maxSensors - 0.5); + + for (int chipID = 0; chipID < nChips; ++chipID) { + const auto& g = chipGeom[chipID]; + if (g.subDetID != 1 || g.localLayer != layer || g.disk != -1) { continue; } - - const int staveID = gman->getStave(chipID); - const int sensorID = sensorIdPerChip[chipID]; - const double avgDigitsPerROF = digitsPerChip[chipID] * rofNorm; - const double maxDigitsPerROF = maxDigitsPerROFPerChip[chipID]; - const double bandwidthMbps = avgDigitsPerROF * bitsToMbps; - - if (sensorID >= 0) { - hDigitsPerROF->Fill(staveID, sensorID, avgDigitsPerROF); - hMaxDigitsPerROF->Fill(staveID, sensorID, maxDigitsPerROF); - hBandwidth->Fill(staveID, sensorID, bandwidthMbps); - totalAvgDigitsPerROF += avgDigitsPerROF; - totalMaxDigitsPerROF += maxDigitsPerROF; - totalBandwidthMbps += bandwidthMbps; - peakAvgDigitsPerROF = std::max(peakAvgDigitsPerROF, avgDigitsPerROF); - peakMaxDigitsPerROF = std::max(peakMaxDigitsPerROF, maxDigitsPerROF); - peakBandwidthMbps = std::max(peakBandwidthMbps, bandwidthMbps); - ++nFilledSensors; + const int sensorID = chipSensorIndex[chipID]; + if (sensorID < 0) { + continue; } + const double staveBinX = g.stave + (g.halfStave + 0.5) / nHalfStaves - 0.5; + const double avgDigits = digitsPerChip[chipID] * rofNorm; + const double maxDigits = (double)maxDigitsPerROFPerChip[chipID]; + + hDigitsPerROF->Fill(staveBinX, sensorID, avgDigits); + hMaxDigitsPerROF->Fill(staveBinX, sensorID, maxDigits); + hBandwidth->Fill(staveBinX, sensorID, avgDigits * bitsToGbps); } - auto* canvLayer = new TCanvas(Form("canvBandwidthLayer%d", layer), Form("Layer %d bandwidth", layer), 1050, 1050); + const auto& ls = layerStats[outputLayer]; + + auto* canvLayer = new TCanvas(Form("canvBandwidthLayer%d", outputLayer), Form("Layer %d bandwidth", outputLayer), 1050, 1050); canvLayer->SetTopMargin(0.08); canvLayer->SetRightMargin(0.18); - const double avgDigitsPerROFLayer = nFilledSensors > 0 ? totalAvgDigitsPerROF / nFilledSensors : 0.; - const double avgMaxDigitsPerROFLayer = nFilledSensors > 0 ? totalMaxDigitsPerROF / nFilledSensors : 0.; - const double avgBandwidthMbps = nFilledSensors > 0 ? totalBandwidthMbps / nFilledSensors : 0.; - hBandwidth->GetZaxis()->SetRangeUser(0., avgBandwidthMbps > 0. ? 3. * avgBandwidthMbps : 1.); + hBandwidth->GetZaxis()->SetRangeUser(0., ls.avgBandwidthGbps > 0. ? 3. * ls.avgBandwidthGbps : 1.); hBandwidth->Draw("colz"); - drawSummary(avgBandwidthMbps, peakBandwidthMbps, "Mbit/s"); - canvLayer->SaveAs(Form("trk_layer%d_bandwidth_map.png", layer)); + drawSummary(ls.avgBandwidthGbps, ls.peakBandwidthGbps, "Gbit/s"); + appendCanvasToPdf(canvLayer); - auto* canvLayerDigits = new TCanvas(Form("canvDigitsLayer%d", layer), Form("Layer %d digits per ROF", layer), 1050, 1050); + auto* canvLayerDigits = new TCanvas(Form("canvDigitsLayer%d", outputLayer), Form("Layer %d digits per ROF", outputLayer), 1050, 1050); canvLayerDigits->SetTopMargin(0.08); canvLayerDigits->SetRightMargin(0.18); hDigitsPerROF->Draw("colz"); - drawSummary(avgDigitsPerROFLayer, peakAvgDigitsPerROF, "digits/ROF"); - canvLayerDigits->SaveAs(Form("trk_layer%d_digits_per_rof_map.png", layer)); + drawSummary(ls.avgDigitsPerROF, ls.peakAvgDigitsPerROF, "digits/ROF"); + appendCanvasToPdf(canvLayerDigits); - auto* canvLayerMaxDigits = new TCanvas(Form("canvMaxDigitsLayer%d", layer), Form("Layer %d max digits per ROF", layer), 1050, 1050); + auto* canvLayerMaxDigits = new TCanvas(Form("canvMaxDigitsLayer%d", outputLayer), Form("Layer %d max digits per ROF", outputLayer), 1050, 1050); canvLayerMaxDigits->SetTopMargin(0.08); canvLayerMaxDigits->SetRightMargin(0.18); hMaxDigitsPerROF->Draw("colz"); - drawSummary(avgMaxDigitsPerROFLayer, peakMaxDigitsPerROF, "digits/ROF"); - canvLayerMaxDigits->SaveAs(Form("trk_layer%d_max_digits_per_rof_map.png", layer)); + drawSummary(ls.avgMaxDigitsPerROF, ls.peakMaxDigitsPerROF, "digits/ROF"); + appendCanvasToPdf(canvLayerMaxDigits); + } + + // --- Digits distribution per layer --- + // Each histogram shows the distribution of total-layer bandwidth across ROFs. + + { + const int nCols = std::max(1, (int)std::ceil(std::sqrt((double)nTotalLayers))); + const int nRows = (nTotalLayers + nCols - 1) / nCols; + auto* canvBwDist = new TCanvas("canvDigitsDistPerLayer", "Digits distribution per layer", 350 * nCols, 300 * nRows); + canvBwDist->Divide(nCols, nRows); + for (int layer = 0; layer < nTotalLayers; ++layer) { + if (!hDigitsDistPerLayer[layer]) { + continue; + } + canvBwDist->cd(layer + 1); + gPad->SetLogy(); + gPad->SetTopMargin(0.10); + gPad->SetBottomMargin(0.14); + gPad->SetLeftMargin(0.14); + hDigitsDistPerLayer[layer]->Scale(1. / hDigitsDistPerLayer[layer]->GetEntries()); + hDigitsDistPerLayer[layer]->Draw("hist"); + } + appendCanvasToPdf(canvBwDist); + } + + // --- Summary: bandwidth vs layer --- + + auto* hAvgBandwidthVsLayer = new TH1D("h_avg_bandwidth_vs_layer", + "Average bandwidth by layer;layer id;average bandwidth (Gbit/s)", + nTotalLayers, -0.5, nTotalLayers - 0.5); + auto* hPeakBandwidthVsLayer = new TH1D("h_peak_bandwidth_vs_layer", + "Peak bandwidth by layer;layer id;peak bandwidth (Gbit/s)", + nTotalLayers, -0.5, nTotalLayers - 0.5); + for (int layer = 0; layer < nTotalLayers; ++layer) { + hAvgBandwidthVsLayer->SetBinContent(layer + 1, layerStats[layer].avgBandwidthGbps); + hPeakBandwidthVsLayer->SetBinContent(layer + 1, layerStats[layer].peakBandwidthGbps); + } + + auto* canvBandwidthSummary = new TCanvas("canvBandwidthSummary", "Bandwidth summary by layer", 1050, 1050); + gStyle->SetOptTitle(0); + canvBandwidthSummary->cd(); + canvBandwidthSummary->SetTopMargin(0.08); + canvBandwidthSummary->SetBottomMargin(0.14); + canvBandwidthSummary->SetLogy(); + hAvgBandwidthVsLayer->SetTitle("Average bandwidth by layer;layer id;Bandwidth (Gbit/s)"); + hAvgBandwidthVsLayer->Draw("hist"); + hPeakBandwidthVsLayer->SetLineColor(kRed); + hPeakBandwidthVsLayer->Draw("hist same"); + canvBandwidthSummary->BuildLegend(0.6, 0.75, 0.9, 0.9); + appendCanvasToPdf(canvBandwidthSummary); + + if (lastPdfCanvas != nullptr) { + lastPdfCanvas->Print(Form("%s]", outputPdf.Data())); } f->Write(); From afa69e4b715bf1057de28ac8b85b04b8e3a69e53 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Fri, 15 Mar 2024 15:59:14 +0000 Subject: [PATCH 078/285] TPC Splines: fix initialization of the track residuals in the test macro --- .../macro/TPCFastTransformInit.C | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C index 6b3756aca3b73..b13d031d6d10d 100644 --- a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C +++ b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C @@ -21,19 +21,24 @@ /// root -l TPCFastTransformInit.C'("debugVoxRes.root")' /// +#include "Algorithm/RangeTokenizer.h" + #if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include #include "TFile.h" #include "TSystem.h" #include "TTree.h" #include "TNtuple.h" #include "Riostream.h" +#include "Algorithm/RangeTokenizer.h" +#include "Framework/Logger.h" #include "GPU/TPCFastTransform.h" #include "SpacePoints/TrackResiduals.h" #include "TPCReconstruction/TPCFastTransformHelperO2.h" #include "TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h" - #endif using namespace o2::tpc; @@ -54,6 +59,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", corr->Draw("cx:y:z","iRoc==0&&iRow==10","") grid->Draw("cx:y:z","iRoc==0&&iRow==10","same") vox->Draw("vx:y:z","iRoc==0&&iRow==10","same") + points->Draw("px:y:z","iRoc==0&&iRow==10","same") */ if (gSystem->AccessPathName(fileName)) { @@ -75,15 +81,42 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", return; } + auto userInfo = voxResTree->GetUserInfo(); + + if (!userInfo->FindObject("y2xBinning") || !userInfo->FindObject("z2xBinning")) { + std::cout << "'y2xBinning' or 'z2xBinning' not found in UserInfo, but required to get the correct binning" << std::endl; + return; + } + + userInfo->Print(); + + // required for the binning that was used o2::tpc::TrackResiduals trackResiduals; - trackResiduals.init(); // also initializes the default binning which was used + auto y2xBins = o2::RangeTokenizer::tokenize(userInfo->FindObject("y2xBinning")->GetTitle()); + auto z2xBins = o2::RangeTokenizer::tokenize(userInfo->FindObject("z2xBinning")->GetTitle()); + trackResiduals.setY2XBinning(y2xBins); + trackResiduals.setZ2XBinning(z2xBins); + trackResiduals.init(); + + std::cout << "y2xBins: " << y2xBins.size() << " z2xBins: " << z2xBins.size() << std::endl; + for (auto y2x : y2xBins) { + std::cout << "y2x: " << y2x << std::endl; + } + + std::cout << std::endl; + + for (auto z2x : z2xBins) { + std::cout << "z2x: " << z2x << std::endl; + } std::cout << "create fast transformation ... " << std::endl; auto* helper = o2::tpc::TPCFastTransformHelperO2::instance(); o2::tpc::TPCFastSpaceChargeCorrectionHelper* corrHelper = o2::tpc::TPCFastSpaceChargeCorrectionHelper::instance(); + corrHelper->setNthreadsToMaximum(); + auto corrPtr = corrHelper->createFromTrackResiduals(trackResiduals, voxResTree, useSmoothed, invertSigns); std::unique_ptr fastTransform( @@ -162,6 +195,9 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", branch->SetAddress(&v); branch->SetAutoDelete(kTRUE); + int iRocLast = -1; + int iRowLast = -1; + for (int32_t iVox = 0; iVox < voxResTree->GetEntriesFast(); iVox++) { voxResTree->GetEntry(iVox); @@ -180,6 +216,9 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", int32_t iRoc = (int32_t)v->bsec; int32_t iRow = (int32_t)xBin; + iRocLast = iRoc; + iRowLast = iRow; + double x = trackResiduals.getX(xBin); // radius of the pad row double y2x = trackResiduals.getY2X( From f878d89be6c7b930f8f7c5b65979064d2418d8fb Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Fri, 15 Mar 2024 16:02:00 +0000 Subject: [PATCH 079/285] TPC Splines: fix propagation of the track residual data to the TPC row edges --- .../TPCFastSpaceChargeCorrectionHelper.cxx | 389 +++++++++++++----- 1 file changed, 279 insertions(+), 110 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index e2960c73e4d50..9910e2206ca11 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -118,6 +118,8 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas // calculate correction map: dx,du,dv = ( origTransform() -> x,u,v) - fastTransformNominal:x,u,v // for the future: switch TOF correction off for a while + TStopwatch watch; + if (!mIsInitialized) { initGeometry(); } @@ -176,6 +178,10 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas } // slice + watch.Stop(); + + LOGP(info, "Space charge correction tooks: {}s", watch.RealTime()); + initInverse(correction, 0); } @@ -380,20 +386,14 @@ std::unique_ptr TPCFastSpaceChargeCorrect { // create o2::gpu::TPCFastSpaceChargeCorrection from o2::tpc::TrackResiduals::VoxRes voxel tree - LOG(info) << "fast space charge correction helper: create correction using " << mNthreads << " threads"; + LOG(info) << "fast space charge correction helper: create correction from track residuals using " << mNthreads << " threads"; + + TStopwatch watch1, watch2; std::unique_ptr correctionPtr(new o2::gpu::TPCFastSpaceChargeCorrection); o2::gpu::TPCFastSpaceChargeCorrection& correction = *correctionPtr; - // o2::tpc::TrackResiduals::VoxRes* v = nullptr; - // voxResTree->SetBranchAddress("voxRes", &v); - - o2::tpc::TrackResiduals::VoxRes* v = nullptr; - TBranch* branch = voxResTree->GetBranch("voxRes"); - branch->SetAddress(&v); - branch->SetAutoDelete(kTRUE); - auto* helper = o2::tpc::TPCFastSpaceChargeCorrectionHelper::instance(); const o2::gpu::TPCFastTransformGeo& geo = helper->getGeometry(); @@ -417,9 +417,11 @@ std::unique_ptr TPCFastSpaceChargeCorrect // std::cout << "n knots Y: " << nKnotsY << std::endl; // std::cout << "n knots Z: " << nKnotsZ << std::endl; + const int nRows = geo.getNumberOfRows(); + const int nROCs = geo.getNumberOfSlices(); + { // create the correction object - const int nRows = geo.getNumberOfRows(); const int nCorrectionScenarios = 1; correction.startConstruction(geo, nCorrectionScenarios); @@ -451,131 +453,298 @@ std::unique_ptr TPCFastSpaceChargeCorrect LOG(info) << "fast space charge correction helper: fill data points from track residuals"; - for (int iVox = 0; iVox < voxResTree->GetEntriesFast(); iVox++) { - - voxResTree->GetEntry(iVox); - auto xBin = - v->bvox[o2::tpc::TrackResiduals::VoxX]; // bin number in x (= pad row) - auto y2xBin = - v->bvox[o2::tpc::TrackResiduals::VoxF]; // bin number in y/x 0..14 - auto z2xBin = - v->bvox[o2::tpc::TrackResiduals::VoxZ]; // bin number in z/x 0..4 - - int iRoc = (int)v->bsec; - int iRow = (int)xBin; + // o2::tpc::TrackResiduals::VoxRes* v = nullptr; + // voxResTree->SetBranchAddress("voxRes", &v); - // x,y,z of the voxel in local TPC coordinates + o2::tpc::TrackResiduals::VoxRes* v = nullptr; + TBranch* branch = voxResTree->GetBranch("voxRes"); + branch->SetAddress(&v); + branch->SetAutoDelete(kTRUE); - double x = trackResiduals.getX(xBin); // radius of the pad row - double y2x = trackResiduals.getY2X( - xBin, y2xBin); // y/x coordinate of the bin ~-0.15 ... 0.15 - double z2x = - trackResiduals.getZ2X(z2xBin); // z/x coordinate of the bin 0.1 .. 0.9 - double y = x * y2x; - double z = x * z2x; + // find the first and the last voxel for each ROC + // we assume the data is sorted by ROC, othwerwise it will be read nROCs times - if (iRoc >= geo.getNumberOfSlicesA()) { - z = -z; - // y = -y; - } + std::vector vROCdataFirst(nROCs, -1); + std::vector vROCdataLast(nROCs, -2); - { - float sx, sy, sz; - trackResiduals.getVoxelCoordinates(iRoc, xBin, y2xBin, z2xBin, sx, sy, sz); - sy *= x; - sz *= x; - if (fabs(sx - x) + fabs(sy - y) + fabs(sz - z) > 1.e-4) { - std::cout << "wrong coordinates: " << x << " " << y << " " << z << " / " << sx << " " << sy << " " << sz << std::endl; + { + int iRocLast = -1; + bool isSorted = true; + for (int iVox = 0; iVox < voxResTree->GetEntriesFast(); iVox++) { + voxResTree->GetEntry(iVox); + int iRoc = (int)v->bsec; + // ensure the data is in the expacted order + if (iRoc < iRocLast) { + isSorted = false; + } + iRocLast = iRoc; + if (iRoc < 0 || iRoc >= nROCs) { + LOG(fatal) << "ROC number " << iRoc << " is out of range"; } + if (vROCdataFirst[iRoc] < 0) { + vROCdataFirst[iRoc] = iVox; + } + vROCdataLast[iRoc] = iVox; } - - // skip empty voxels - float voxEntries = v->stat[o2::tpc::TrackResiduals::VoxV]; - if (voxEntries < 1.) { // no statistics - continue; + if (!isSorted) { + LOG(warning) << "Data is not sorted by ROC as expected"; } + } - // double statX = v->stat[o2::tpc::TrackResiduals::VoxX]; // weight - // double statY = v->stat[o2::tpc::TrackResiduals::VoxF]; // weight - // double statZ = v->stat[o2::tpc::TrackResiduals::VoxZ]; // weight - - // double dx = 1. / trackResiduals.getDXI(xBin); - double dy = x / trackResiduals.getDY2XI(xBin, y2xBin); - double dz = x * trackResiduals.getDZ2X(z2xBin); - - double correctionX = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResX] : v->D[o2::tpc::TrackResiduals::ResX]; - double correctionY = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResY] : v->D[o2::tpc::TrackResiduals::ResY]; - double correctionZ = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResZ] : v->D[o2::tpc::TrackResiduals::ResZ]; - if (invertSigns) { - correctionX *= -1.; - correctionY *= -1.; - correctionZ *= -1.; - } - // add one point per voxel + // read the data ROC by ROC - // map.addCorrectionPoint(iRoc, iRow, y, z, correctionX, correctionY, - // correctionZ); + // data in the tree is not sorted by row + // first find which data belong to which row - // add several points per voxel, - // extend values of the edge voxels to the edges of the TPC row - // + struct VoxelData { + int mNentries{0}; // number of entries + float mCx, mCy, mCz; // corrections to the local coordinates + }; - double yFirst = y - dy / 2.; - double yLast = y + dy / 2.; + std::vector vRocData[nRows]; + for (int ir = 0; ir < nRows; ir++) { + vRocData[ir].resize(nY2Xbins * nZ2Xbins); + } - if (y2xBin == 0) { // extend value of the first Y bin to the row edge - float u, v; - if (iRoc < geo.getNumberOfSlicesA()) { - geo.convScaledUVtoUV(iRoc, iRow, 0., 0., u, v); - } else { - geo.convScaledUVtoUV(iRoc, iRow, 1., 0., u, v); + struct Voxel { + float mY, mZ; // not-distorted local coordinates + float mDy, mDz; // bin size + int mSmoothingStep{100}; // is the voxel data original or smoothed at this step + }; + + std::vector vRowVoxels(nY2Xbins * nZ2Xbins); + + for (int iRoc = 0; iRoc < nROCs; iRoc++) { + + for (int ir = 0; ir < nRows; ir++) { + for (int iv = 0; iv < nY2Xbins * nZ2Xbins; iv++) { + vRocData[ir][iv].mNentries = 0; } - float py, pz; - geo.convUVtoLocal(iRoc, u, v, py, pz); - yFirst = py; } - if (y2xBin == trackResiduals.getNY2XBins() - 1) { // extend value of the last Y bin to the row edge - float u, v; - if (iRoc < geo.getNumberOfSlicesA()) { - geo.convScaledUVtoUV(iRoc, iRow, 1., 0., u, v); - } else { - geo.convScaledUVtoUV(iRoc, iRow, 0., 0., u, v); + for (int iVox = vROCdataFirst[iRoc]; iVox <= vROCdataLast[iRoc]; iVox++) { + voxResTree->GetEntry(iVox); + if ((int)v->bsec != iRoc) { + LOG(fatal) << "ROC number " << v->bsec << " is not equal to " << iRoc; + continue; + } + int iRow = (int)v->bvox[o2::tpc::TrackResiduals::VoxX]; // bin number in x (= pad row) + if (iRow < 0 || iRow >= nRows) { + LOG(fatal) << "Row number " << iRow << " is out of range"; } - float py, pz; - geo.convUVtoLocal(iRoc, u, v, py, pz); - yLast = py; + int iy = v->bvox[o2::tpc::TrackResiduals::VoxF]; // bin number in y/x 0..14 + int iz = v->bvox[o2::tpc::TrackResiduals::VoxZ]; // bin number in z/x 0..4 + auto& vox = vRocData[iRow][iy * nZ2Xbins + iz]; + vox.mNentries = (int)v->stat[o2::tpc::TrackResiduals::VoxV]; + vox.mCx = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResX] : v->D[o2::tpc::TrackResiduals::ResX]; + vox.mCy = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResY] : v->D[o2::tpc::TrackResiduals::ResY]; + vox.mCz = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResZ] : v->D[o2::tpc::TrackResiduals::ResZ]; } - double z0 = 0.; - if (iRoc < geo.getNumberOfSlicesA()) { - z0 = geo.getTPCzLengthA(); - } else { - z0 = -geo.getTPCzLengthC(); - } + // now process the data row-by-row + + for (int iRow = 0; iRow < nRows; iRow++) { + + // LOG(info) << "Processing ROC " << iRoc << " row " << iRow; + + // complete the voxel data + { + int xBin = iRow; + double x = trackResiduals.getX(xBin); // radius of the pad row + bool isDataFound = false; + for (int iy = 0; iy < nY2Xbins; iy++) { + for (int iz = 0; iz < nZ2Xbins; iz++) { + auto& data = vRocData[iRow][iy * nZ2Xbins + iz]; + auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; + // y/x coordinate of the bin ~-0.15 ... 0.15 + double y2x = trackResiduals.getY2X(xBin, iy); + // z/x coordinate of the bin 0.1 .. 0.9 + double z2x = trackResiduals.getZ2X(iz); + vox.mY = x * y2x; + vox.mZ = x * z2x; + vox.mDy = x / trackResiduals.getDY2XI(xBin, iy); + vox.mDz = x * trackResiduals.getDZ2X(iz); + if (iRoc >= geo.getNumberOfSlicesA()) { + vox.mZ = -vox.mZ; + } + if (data.mNentries < 1) { // no data + data.mCx = 0.; + data.mCy = 0.; + data.mCz = 0.; + vox.mSmoothingStep = 100; + } else { // voxel contains data + if (invertSigns) { + data.mCx *= -1.; + data.mCy *= -1.; + data.mCz *= -1.; + } + vox.mSmoothingStep = 0; // original data + isDataFound = true; + } + } + } - double yStep = (yLast - yFirst) / 2; + if (!isDataFound) { // fill everything with 0 + for (int iy = 0; iy < nY2Xbins; iy++) { + for (int iz = 0; iz < nZ2Xbins; iz++) { + vRowVoxels[iy * nZ2Xbins + iz].mSmoothingStep = 0; + } + } + } + } // complete the voxel data + + // repare the voxel data: fill empty voxels + + int nRepairs = 0; + + for (int ismooth = 1; ismooth <= 2; ismooth++) { + for (int iy = 0; iy < nY2Xbins; iy++) { + for (int iz = 0; iz < nZ2Xbins; iz++) { + auto& data = vRocData[iRow][iy * nZ2Xbins + iz]; + auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; + if (vox.mSmoothingStep <= ismooth) { // already filled + continue; + } + nRepairs++; + data.mCx = 0.; + data.mCy = 0.; + data.mCz = 0.; + double w = 0.; + bool filled = false; + auto update = [&](int iy1, int iz1) { + auto& data1 = vRocData[iRow][iy1 * nZ2Xbins + iz1]; + auto& vox1 = vRowVoxels[iy1 * nZ2Xbins + iz1]; + if (vox1.mSmoothingStep >= ismooth) { + return false; + } + double w1 = 1. / (abs(iy - iy1) + abs(iz - iz1) + 1); + data.mCx += w1 * data1.mCx; + data.mCy += w1 * data1.mCy; + data.mCz += w1 * data1.mCz; + w += w1; + filled = true; + return true; + }; + + for (int iy1 = iy - 1; iy1 >= 0 && !update(iy1, iz); iy1--) { + } + for (int iy1 = iy + 1; iy1 < nY2Xbins && !update(iy1, iz); iy1++) { + } + for (int iz1 = iz - 1; iz1 >= 0 && !update(iy, iz1); iz1--) { + } + for (int iz1 = iz + 1; iz1 < nZ2Xbins && !update(iy, iz1); iz1++) { + } - for (double py = yFirst; py <= yLast + yStep / 2.; py += yStep) { + if (filled) { + data.mCx /= w; + data.mCy /= w; + data.mCz /= w; + vox.mSmoothingStep = ismooth; + } + } // iz + } // iy + } // ismooth - for (double pz = z - dz / 2.; pz <= z + dz / 2. + 1.e-4; pz += dz / 2.) { - map.addCorrectionPoint(iRoc, iRow, py, pz, correctionX, correctionY, - correctionZ); + if (nRepairs > 0) { + LOG(info) << "ROC " << iRoc << " row " << iRow << ": " << nRepairs << " voxel repairs for " << nY2Xbins * nZ2Xbins << " voxels"; } - if (z2xBin == trackResiduals.getNZ2XBins() - 1) { - // extend value of the first Z bin to the readout, linear decrease of all values to 0. - int nZsteps = 3; - for (int is = 0; is < nZsteps; is++) { - double pz = z + (z0 - z) * (is + 1.) / nZsteps; - double s = (nZsteps - 1. - is) / nZsteps; - map.addCorrectionPoint(iRoc, iRow, py, pz, s * correctionX, - s * correctionY, s * correctionZ); + // feed the row data to the helper + + double yMin = 0., yMax = 0.; + + { + float u, v; + if (iRoc < geo.getNumberOfSlicesA()) { + geo.convScaledUVtoUV(iRoc, iRow, 0., 0., u, v); + } else { + geo.convScaledUVtoUV(iRoc, iRow, 1., 0., u, v); } + float py, pz; + geo.convUVtoLocal(iRoc, u, v, py, pz); + yMin = py; } - } - } + { + float u, v; + if (iRoc < geo.getNumberOfSlicesA()) { + geo.convScaledUVtoUV(iRoc, iRow, 1., 0., u, v); + } else { + geo.convScaledUVtoUV(iRoc, iRow, 0., 0., u, v); + } + float py, pz; + geo.convUVtoLocal(iRoc, u, v, py, pz); + yMax = py; + } + + double zEdge = 0.; + if (iRoc < geo.getNumberOfSlicesA()) { + zEdge = geo.getTPCzLengthA(); + } else { + zEdge = -geo.getTPCzLengthC(); + } + + for (int iy = 0; iy < nY2Xbins; iy++) { + for (int iz = 0; iz < nZ2Xbins; iz++) { + auto& data = vRocData[iRow][iy * nZ2Xbins + iz]; + auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; + if (vox.mSmoothingStep > 2) { + LOG(fatal) << "empty voxel is not repared"; + } + + double y = vox.mY; + double z = vox.mZ; + double dy = vox.mDy; + double dz = vox.mDz; + double correctionX = data.mCx; + double correctionY = data.mCy; + double correctionZ = data.mCz; + + double yFirst = y - dy / 2.; + double yLast = y + dy / 2.; + + if (iy == 0) { // extend value of the first Y bin to the row edge + yFirst = yMin; + } + + if (iy == nY2Xbins - 1) { // extend value of the last Y bin to the row edge + yLast = yMax; + } + + double yStep = (yLast - yFirst) / 2; + + for (double py = yFirst; py <= yLast + yStep / 2.; py += yStep) { + + for (double pz = z - dz / 2.; pz <= z + dz / 2. + 1.e-4; pz += dz / 2.) { + map.addCorrectionPoint(iRoc, iRow, py, pz, correctionX, correctionY, + correctionZ); + } + + if (iz == nZ2Xbins - 1) { + // extend value of the first Z bin to the readout, linear decrease of all values to 0. + int nZsteps = 3; + for (int is = 0; is < nZsteps; is++) { + double pz = z + (zEdge - z) * (is + 1.) / nZsteps; + double s = (nZsteps - 1. - is) / nZsteps; + map.addCorrectionPoint(iRoc, iRow, py, pz, s * correctionX, + s * correctionY, s * correctionZ); + } + } + } + } // iz + } // iy + + } // iRow + + } // iRoc + + LOGP(info, "Reading & reparing of the track residuals tooks: {}s", watch1.RealTime()); + + LOG(info) << "fast space charge correction helper: create space charge from the map of data points.."; + helper->fillSpaceChargeCorrectionFromMap(correction); + + LOGP(info, "Creation from track residuals tooks in total: {}s", watch2.RealTime()); + return std::move(correctionPtr); } @@ -814,7 +983,7 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector Date: Mon, 15 Apr 2024 14:41:19 +0000 Subject: [PATCH 080/285] TPC Splines: non-uniform grid that corresponds to the track residual voxels --- .../include/SpacePoints/TrackResiduals.h | 19 +- .../TPCFastSpaceChargeCorrectionHelper.cxx | 530 ++++++++++-------- .../TPCFastSpaceChargeCorrection.cxx | 181 +++--- .../TPCFastSpaceChargeCorrection.h | 64 +-- .../TPCFastTransformGeo.h | 9 + .../macro/TPCFastTransformInit.C | 203 +++++-- 6 files changed, 619 insertions(+), 387 deletions(-) diff --git a/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackResiduals.h b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackResiduals.h index 2ade12d951c58..c9226589ec703 100644 --- a/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackResiduals.h +++ b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackResiduals.h @@ -318,9 +318,14 @@ class TrackResiduals void getVoxelCoordinates(int isec, int ix, int ip, int iz, float& x, float& p, float& z) const; /// Calculates the x-coordinate for given x bin. - /// \param i Bin index + /// \param ix Bin index in x /// \return Coordinate in X - float getX(int i) const; + float getX(int ix) const; + + /// Calculates the max y/x-coordinate for given x bin taking the dead zone into account. + /// \param ix Bin index in x + /// \return Max coordinate in Y/X + float getMaxY2X(int ix) const; /// Calculates the y/x-coordinate. /// \param ix Bin index in X @@ -560,9 +565,15 @@ inline float TrackResiduals::getDXI(int ix) const } //_____________________________________________________ -inline float TrackResiduals::getX(int i) const +inline float TrackResiduals::getX(int ix) const +{ + return mUniformBins[VoxX] ? param::MinX + (ix + 0.5) * mDX : param::RowX[ix]; +} + +//_____________________________________________________ +inline float TrackResiduals::getMaxY2X(int ix) const { - return mUniformBins[VoxX] ? param::MinX + (i + 0.5) * mDX : param::RowX[i]; + return mMaxY2X[ix]; } //_____________________________________________________ diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index 9910e2206ca11..861cacbe00012 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -29,6 +29,8 @@ #include #include #include "TStopwatch.h" +#include "TTreeReader.h" +#include "TTreeReaderValue.h" using namespace o2::gpu; @@ -154,7 +156,7 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas pointCorr[3 * i + 1] = du; pointCorr[3 * i + 2] = dv; } - helper.approximateDataPoints(spline, splineParameters, 0., spline.getGridX1().getNumberOfKnots() - 1, 0., spline.getGridX2().getNumberOfKnots() - 1, &pointSU[0], + helper.approximateDataPoints(spline, splineParameters, 0., spline.getGridX1().getUmax(), 0., spline.getGridX2().getUmax(), &pointSU[0], &pointSV[0], &pointCorr[0], nDataPoints); } else { for (int i = 0; i < spline.getNumberOfParameters(); i++) { @@ -388,7 +390,7 @@ std::unique_ptr TPCFastSpaceChargeCorrect LOG(info) << "fast space charge correction helper: create correction from track residuals using " << mNthreads << " threads"; - TStopwatch watch1, watch2; + TStopwatch watch, watch1; std::unique_ptr correctionPtr(new o2::gpu::TPCFastSpaceChargeCorrection); @@ -403,17 +405,90 @@ std::unique_ptr TPCFastSpaceChargeCorrect int nY2Xbins = trackResiduals.getNY2XBins(); int nZ2Xbins = trackResiduals.getNZ2XBins(); - int nKnotsY = nY2Xbins / 2; - int nKnotsZ = nZ2Xbins / 2; + std::vector yBinsInt; + { + std::vector yBins; + yBins.reserve(nY2Xbins); + for (int i = 0, j = nY2Xbins - 1; i <= j; i += 2, j -= 2) { + if (i == j) { + yBins.push_back(trackResiduals.getY2X(0, i)); + } else if (i + 1 == j) { + yBins.push_back(trackResiduals.getY2X(0, i)); + } else { + yBins.push_back(trackResiduals.getY2X(0, i)); + yBins.push_back(trackResiduals.getY2X(0, j)); + } + } + std::sort(yBins.begin(), yBins.end()); + double dy = yBins[1] - yBins[0]; + for (int i = 1; i < yBins.size(); i++) { + if (yBins[i] - yBins[i - 1] < dy) { + dy = yBins[i] - yBins[i - 1]; + } + } + yBinsInt.reserve(yBins.size()); + // spline knots must be positioned on the grid with integer internal coordinate + // take the knot position accuracy of 0.1*dy + dy = dy / 10.; + double y0 = yBins[0]; + double y1 = yBins[yBins.size() - 1]; + for (auto& y : yBins) { + y -= y0; + int iy = int(y / dy + 0.5); + yBinsInt.push_back(iy); + double yold = y / (y1 - y0) * 2 - 1.; + y = iy * dy; + y = y / (y1 - y0) * 2 - 1.; + LOG(info) << "convert y bin: " << yold << " -> " << y << " -> " << iy; + } + } + + std::vector zBinsInt; + { + std::vector zBins; + zBins.reserve(nZ2Xbins); + for (int i = 0; i < nZ2Xbins; i += 2) { + zBins.push_back(-trackResiduals.getZ2X(i)); + } + std::sort(zBins.begin(), zBins.end()); + double dz = zBins[1] - zBins[0]; + for (int i = 1; i < zBins.size(); i++) { + if (zBins[i] - zBins[i - 1] < dz) { + dz = zBins[i] - zBins[i - 1]; + } + } + zBinsInt.reserve(zBins.size()); + // spline knots must be positioned on the grid with an integer internal coordinate + // lets copy the knot positions with the accuracy of 0.1*dz + dz = dz / 10.; + double z0 = zBins[0]; + double z1 = zBins[zBins.size() - 1]; + for (auto& z : zBins) { + z -= z0; + int iz = int(z / dz + 0.5); + zBinsInt.push_back(iz); + double zold = z / (z1 - z0); + z = iz * dz; + z = z / (z1 - z0); + LOG(info) << "convert z bin: " << zold << " -> " << z << " -> " << iz; + } + } - if (nKnotsY < 2) { - nKnotsY = 2; + if (yBinsInt.size() < 2) { + yBinsInt.clear(); + yBinsInt.push_back(0); + yBinsInt.push_back(1); } - if (nKnotsZ < 2) { - nKnotsZ = 2; + if (zBinsInt.size() < 2) { + zBinsInt.clear(); + zBinsInt.push_back(0); + zBinsInt.push_back(1); } + int nKnotsY = yBinsInt.size(); + int nKnotsZ = zBinsInt.size(); + // std::cout << "n knots Y: " << nKnotsY << std::endl; // std::cout << "n knots Z: " << nKnotsZ << std::endl; @@ -432,64 +507,42 @@ std::unique_ptr TPCFastSpaceChargeCorrect } { // init spline scenario TPCFastSpaceChargeCorrection::SplineType spline; - spline.recreate(nKnotsY, nKnotsZ); + spline.recreate(nKnotsY, &yBinsInt[0], nKnotsZ, &zBinsInt[0]); correction.setSplineScenario(0, spline); } correction.finishConstruction(); } // .. create the correction object - // set the grid borders in Z to Z/X==1 + // set the grid borders for (int iRoc = 0; iRoc < geo.getNumberOfSlices(); iRoc++) { for (int iRow = 0; iRow < geo.getNumberOfRows(); iRow++) { - auto rowInfo = geo.getRowInfo(iRow); - o2::gpu::TPCFastSpaceChargeCorrection::SliceRowInfo& info = correction.getSliceRowInfo(iRoc, iRow); - double len = geo.getTPCzLength(iRoc); - info.gridV0 = len - rowInfo.x; - if (info.gridV0 < 0.) { - info.gridV0 = 0.; - } + const auto& rowInfo = geo.getRowInfo(iRow); + auto& info = correction.getSliceRowInfo(iRoc, iRow); + const auto& spline = correction.getSpline(iRoc, iRow); + double yMin = rowInfo.x * trackResiduals.getY2X(iRow, 0); + double yMax = rowInfo.x * trackResiduals.getY2X(iRow, trackResiduals.getNY2XBins() - 1); + double zMin = rowInfo.x * trackResiduals.getZ2X(0); + double zMax = rowInfo.x * trackResiduals.getZ2X(trackResiduals.getNZ2XBins() - 1); + double uMin = yMin; + double uMax = yMax; + double vMin = geo.getTPCzLength(iRoc) - zMax; + double vMax = geo.getTPCzLength(iRoc) - zMin; + // std::cout << " uMin: " << uMin << " uMax: " << yuMax << " zMin: " << vMin << " zMax: " << vMax << std::endl; + info.gridU0 = uMin; + info.scaleUtoGrid = spline.getGridX1().getUmax() / (uMax - uMin); + info.gridV0 = vMin; + info.scaleVtoGrid = spline.getGridX2().getUmax() / (vMax - vMin); } } - LOG(info) << "fast space charge correction helper: fill data points from track residuals"; - - // o2::tpc::TrackResiduals::VoxRes* v = nullptr; - // voxResTree->SetBranchAddress("voxRes", &v); + LOG(info) << "fast space charge correction helper: preparation took " << watch1.RealTime() << "s"; - o2::tpc::TrackResiduals::VoxRes* v = nullptr; - TBranch* branch = voxResTree->GetBranch("voxRes"); - branch->SetAddress(&v); - branch->SetAutoDelete(kTRUE); + LOG(info) << "fast space charge correction helper: fill data points from track residuals.. "; - // find the first and the last voxel for each ROC - // we assume the data is sorted by ROC, othwerwise it will be read nROCs times + TStopwatch watch3; - std::vector vROCdataFirst(nROCs, -1); - std::vector vROCdataLast(nROCs, -2); - - { - int iRocLast = -1; - bool isSorted = true; - for (int iVox = 0; iVox < voxResTree->GetEntriesFast(); iVox++) { - voxResTree->GetEntry(iVox); - int iRoc = (int)v->bsec; - // ensure the data is in the expacted order - if (iRoc < iRocLast) { - isSorted = false; - } - iRocLast = iRoc; - if (iRoc < 0 || iRoc >= nROCs) { - LOG(fatal) << "ROC number " << iRoc << " is out of range"; - } - if (vROCdataFirst[iRoc] < 0) { - vROCdataFirst[iRoc] = iVox; - } - vROCdataLast[iRoc] = iVox; - } - if (!isSorted) { - LOG(warning) << "Data is not sorted by ROC as expected"; - } - } + // TTreeProcessorMT treeProcessor(*voxResTree); // multi-threaded tree processor + // treeProcessor.Init(voxResTree); // read the data ROC by ROC @@ -506,14 +559,6 @@ std::unique_ptr TPCFastSpaceChargeCorrect vRocData[ir].resize(nY2Xbins * nZ2Xbins); } - struct Voxel { - float mY, mZ; // not-distorted local coordinates - float mDy, mDz; // bin size - int mSmoothingStep{100}; // is the voxel data original or smoothed at this step - }; - - std::vector vRowVoxels(nY2Xbins * nZ2Xbins); - for (int iRoc = 0; iRoc < nROCs; iRoc++) { for (int ir = 0; ir < nRows; ir++) { @@ -522,10 +567,17 @@ std::unique_ptr TPCFastSpaceChargeCorrect } } - for (int iVox = vROCdataFirst[iRoc]; iVox <= vROCdataLast[iRoc]; iVox++) { - voxResTree->GetEntry(iVox); + const int rocDataStart = iRoc * trackResiduals.getNVoxelsPerSector(); + const int rocDataEnd = rocDataStart + trackResiduals.getNVoxelsPerSector(); + + TTreeReader reader(voxResTree); + reader.SetEntriesRange(rocDataStart, rocDataEnd); + TTreeReaderValue v(reader, "voxRes"); + for (int iVox = rocDataStart; iVox < rocDataEnd; iVox++) { + reader.Next(); + // voxResTree->GetEntry(iVox); if ((int)v->bsec != iRoc) { - LOG(fatal) << "ROC number " << v->bsec << " is not equal to " << iRoc; + LOG(fatal) << "Error reading voxels: voxel ROC number " << v->bsec << " is not equal to the expected " << iRoc; continue; } int iRow = (int)v->bvox[o2::tpc::TrackResiduals::VoxX]; // bin number in x (= pad row) @@ -543,207 +595,237 @@ std::unique_ptr TPCFastSpaceChargeCorrect // now process the data row-by-row - for (int iRow = 0; iRow < nRows; iRow++) { + auto myThread = [&](int iThread, int nTreads) { + struct Voxel { + float mY, mZ; // not-distorted local coordinates + float mDy, mDz; // bin size + int mSmoothingStep{100}; // is the voxel data original or smoothed at this step + }; - // LOG(info) << "Processing ROC " << iRoc << " row " << iRow; + std::vector vRowVoxels(nY2Xbins * nZ2Xbins); - // complete the voxel data - { - int xBin = iRow; - double x = trackResiduals.getX(xBin); // radius of the pad row - bool isDataFound = false; - for (int iy = 0; iy < nY2Xbins; iy++) { - for (int iz = 0; iz < nZ2Xbins; iz++) { - auto& data = vRocData[iRow][iy * nZ2Xbins + iz]; - auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; - // y/x coordinate of the bin ~-0.15 ... 0.15 - double y2x = trackResiduals.getY2X(xBin, iy); - // z/x coordinate of the bin 0.1 .. 0.9 - double z2x = trackResiduals.getZ2X(iz); - vox.mY = x * y2x; - vox.mZ = x * z2x; - vox.mDy = x / trackResiduals.getDY2XI(xBin, iy); - vox.mDz = x * trackResiduals.getDZ2X(iz); - if (iRoc >= geo.getNumberOfSlicesA()) { - vox.mZ = -vox.mZ; - } - if (data.mNentries < 1) { // no data - data.mCx = 0.; - data.mCy = 0.; - data.mCz = 0.; - vox.mSmoothingStep = 100; - } else { // voxel contains data - if (invertSigns) { - data.mCx *= -1.; - data.mCy *= -1.; - data.mCz *= -1.; + for (int iRow = iThread; iRow < nRows; iRow += nTreads) { + // LOG(info) << "Processing ROC " << iRoc << " row " << iRow; + + // complete the voxel data + + { + int xBin = iRow; + double x = trackResiduals.getX(xBin); // radius of the pad row + bool isDataFound = false; + for (int iy = 0; iy < nY2Xbins; iy++) { + for (int iz = 0; iz < nZ2Xbins; iz++) { + auto& data = vRocData[iRow][iy * nZ2Xbins + iz]; + auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; + // y/x coordinate of the bin ~-0.15 ... 0.15 + double y2x = trackResiduals.getY2X(xBin, iy); + // z/x coordinate of the bin 0.1 .. 0.9 + double z2x = trackResiduals.getZ2X(iz); + vox.mY = x * y2x; + vox.mZ = x * z2x; + vox.mDy = x / trackResiduals.getDY2XI(xBin, iy); + vox.mDz = x * trackResiduals.getDZ2X(iz); + if (iRoc >= geo.getNumberOfSlicesA()) { + vox.mZ = -vox.mZ; + } + if (data.mNentries < 1) { // no data + data.mCx = 0.; + data.mCy = 0.; + data.mCz = 0.; + vox.mSmoothingStep = 100; + } else { // voxel contains data + if (invertSigns) { + data.mCx *= -1.; + data.mCy *= -1.; + data.mCz *= -1.; + } + vox.mSmoothingStep = 0; // original data + isDataFound = true; } - vox.mSmoothingStep = 0; // original data - isDataFound = true; } } - } - if (!isDataFound) { // fill everything with 0 - for (int iy = 0; iy < nY2Xbins; iy++) { - for (int iz = 0; iz < nZ2Xbins; iz++) { - vRowVoxels[iy * nZ2Xbins + iz].mSmoothingStep = 0; + if (!isDataFound) { // fill everything with 0 + for (int iy = 0; iy < nY2Xbins; iy++) { + for (int iz = 0; iz < nZ2Xbins; iz++) { + vRowVoxels[iy * nZ2Xbins + iz].mSmoothingStep = 0; + } } } - } - } // complete the voxel data + } // complete the voxel data - // repare the voxel data: fill empty voxels + // repare the voxel data: fill empty voxels - int nRepairs = 0; + int nRepairs = 0; - for (int ismooth = 1; ismooth <= 2; ismooth++) { - for (int iy = 0; iy < nY2Xbins; iy++) { - for (int iz = 0; iz < nZ2Xbins; iz++) { - auto& data = vRocData[iRow][iy * nZ2Xbins + iz]; - auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; - if (vox.mSmoothingStep <= ismooth) { // already filled - continue; - } - nRepairs++; - data.mCx = 0.; - data.mCy = 0.; - data.mCz = 0.; - double w = 0.; - bool filled = false; - auto update = [&](int iy1, int iz1) { - auto& data1 = vRocData[iRow][iy1 * nZ2Xbins + iz1]; - auto& vox1 = vRowVoxels[iy1 * nZ2Xbins + iz1]; - if (vox1.mSmoothingStep >= ismooth) { - return false; + for (int ismooth = 1; ismooth <= 2; ismooth++) { + for (int iy = 0; iy < nY2Xbins; iy++) { + for (int iz = 0; iz < nZ2Xbins; iz++) { + auto& data = vRocData[iRow][iy * nZ2Xbins + iz]; + auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; + if (vox.mSmoothingStep <= ismooth) { // already filled + continue; + } + nRepairs++; + data.mCx = 0.; + data.mCy = 0.; + data.mCz = 0.; + double w = 0.; + bool filled = false; + auto update = [&](int iy1, int iz1) { + auto& data1 = vRocData[iRow][iy1 * nZ2Xbins + iz1]; + auto& vox1 = vRowVoxels[iy1 * nZ2Xbins + iz1]; + if (vox1.mSmoothingStep >= ismooth) { + return false; + } + double w1 = 1. / (abs(iy - iy1) + abs(iz - iz1) + 1); + data.mCx += w1 * data1.mCx; + data.mCy += w1 * data1.mCy; + data.mCz += w1 * data1.mCz; + w += w1; + filled = true; + return true; + }; + + for (int iy1 = iy - 1; iy1 >= 0 && !update(iy1, iz); iy1--) { + } + for (int iy1 = iy + 1; iy1 < nY2Xbins && !update(iy1, iz); iy1++) { + } + for (int iz1 = iz - 1; iz1 >= 0 && !update(iy, iz1); iz1--) { + } + for (int iz1 = iz + 1; iz1 < nZ2Xbins && !update(iy, iz1); iz1++) { } - double w1 = 1. / (abs(iy - iy1) + abs(iz - iz1) + 1); - data.mCx += w1 * data1.mCx; - data.mCy += w1 * data1.mCy; - data.mCz += w1 * data1.mCz; - w += w1; - filled = true; - return true; - }; - - for (int iy1 = iy - 1; iy1 >= 0 && !update(iy1, iz); iy1--) { - } - for (int iy1 = iy + 1; iy1 < nY2Xbins && !update(iy1, iz); iy1++) { - } - for (int iz1 = iz - 1; iz1 >= 0 && !update(iy, iz1); iz1--) { - } - for (int iz1 = iz + 1; iz1 < nZ2Xbins && !update(iy, iz1); iz1++) { - } - if (filled) { - data.mCx /= w; - data.mCy /= w; - data.mCz /= w; - vox.mSmoothingStep = ismooth; - } - } // iz - } // iy - } // ismooth + if (filled) { + data.mCx /= w; + data.mCy /= w; + data.mCz /= w; + vox.mSmoothingStep = ismooth; + } + } // iz + } // iy + } // ismooth - if (nRepairs > 0) { - LOG(info) << "ROC " << iRoc << " row " << iRow << ": " << nRepairs << " voxel repairs for " << nY2Xbins * nZ2Xbins << " voxels"; - } + if (nRepairs > 0) { + LOG(debug) << "ROC " << iRoc << " row " << iRow << ": " << nRepairs << " voxel repairs for " << nY2Xbins * nZ2Xbins << " voxels"; + } - // feed the row data to the helper + // feed the row data to the helper - double yMin = 0., yMax = 0.; + double yMin = 0., yMax = 0.; - { - float u, v; - if (iRoc < geo.getNumberOfSlicesA()) { - geo.convScaledUVtoUV(iRoc, iRow, 0., 0., u, v); - } else { - geo.convScaledUVtoUV(iRoc, iRow, 1., 0., u, v); + { + float u, v; + if (iRoc < geo.getNumberOfSlicesA()) { + geo.convScaledUVtoUV(iRoc, iRow, 0., 0., u, v); + } else { + geo.convScaledUVtoUV(iRoc, iRow, 1., 0., u, v); + } + float py, pz; + geo.convUVtoLocal(iRoc, u, v, py, pz); + yMin = py; } - float py, pz; - geo.convUVtoLocal(iRoc, u, v, py, pz); - yMin = py; - } - { - float u, v; + { + float u, v; + if (iRoc < geo.getNumberOfSlicesA()) { + geo.convScaledUVtoUV(iRoc, iRow, 1., 0., u, v); + } else { + geo.convScaledUVtoUV(iRoc, iRow, 0., 0., u, v); + } + float py, pz; + geo.convUVtoLocal(iRoc, u, v, py, pz); + yMax = py; + } + + double zEdge = 0.; if (iRoc < geo.getNumberOfSlicesA()) { - geo.convScaledUVtoUV(iRoc, iRow, 1., 0., u, v); + zEdge = geo.getTPCzLengthA(); } else { - geo.convScaledUVtoUV(iRoc, iRow, 0., 0., u, v); + zEdge = -geo.getTPCzLengthC(); } - float py, pz; - geo.convUVtoLocal(iRoc, u, v, py, pz); - yMax = py; - } - - double zEdge = 0.; - if (iRoc < geo.getNumberOfSlicesA()) { - zEdge = geo.getTPCzLengthA(); - } else { - zEdge = -geo.getTPCzLengthC(); - } - for (int iy = 0; iy < nY2Xbins; iy++) { - for (int iz = 0; iz < nZ2Xbins; iz++) { - auto& data = vRocData[iRow][iy * nZ2Xbins + iz]; - auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; - if (vox.mSmoothingStep > 2) { - LOG(fatal) << "empty voxel is not repared"; - } + for (int iy = 0; iy < nY2Xbins; iy++) { + for (int iz = 0; iz < nZ2Xbins; iz++) { + auto& data = vRocData[iRow][iy * nZ2Xbins + iz]; + auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; + if (vox.mSmoothingStep > 2) { + LOG(fatal) << "empty voxel is not repared"; + } - double y = vox.mY; - double z = vox.mZ; - double dy = vox.mDy; - double dz = vox.mDz; - double correctionX = data.mCx; - double correctionY = data.mCy; - double correctionZ = data.mCz; + double y = vox.mY; + double z = vox.mZ; + double dy = vox.mDy; + double dz = vox.mDz; + double correctionX = data.mCx; + double correctionY = data.mCy; + double correctionZ = data.mCz; - double yFirst = y - dy / 2.; - double yLast = y + dy / 2.; + double yFirst = y - dy / 2.; + double yLast = y + dy / 2.; - if (iy == 0) { // extend value of the first Y bin to the row edge - yFirst = yMin; - } + if (iy == 0) { // extend value of the first Y bin to the row edge + yFirst = yMin; + } - if (iy == nY2Xbins - 1) { // extend value of the last Y bin to the row edge - yLast = yMax; - } + if (iy == nY2Xbins - 1) { // extend value of the last Y bin to the row edge + yLast = yMax; + } - double yStep = (yLast - yFirst) / 2; + double yStep = (yLast - yFirst) / 2; - for (double py = yFirst; py <= yLast + yStep / 2.; py += yStep) { + for (double py = yFirst; py <= yLast + yStep / 2.; py += yStep) { - for (double pz = z - dz / 2.; pz <= z + dz / 2. + 1.e-4; pz += dz / 2.) { - map.addCorrectionPoint(iRoc, iRow, py, pz, correctionX, correctionY, - correctionZ); - } + for (double pz = z - dz / 2.; pz <= z + dz / 2. + 1.e-4; pz += dz / 2.) { + map.addCorrectionPoint(iRoc, iRow, py, pz, correctionX, correctionY, + correctionZ); + } - if (iz == nZ2Xbins - 1) { - // extend value of the first Z bin to the readout, linear decrease of all values to 0. - int nZsteps = 3; - for (int is = 0; is < nZsteps; is++) { - double pz = z + (zEdge - z) * (is + 1.) / nZsteps; - double s = (nZsteps - 1. - is) / nZsteps; - map.addCorrectionPoint(iRoc, iRow, py, pz, s * correctionX, - s * correctionY, s * correctionZ); + if (iz == nZ2Xbins - 1) { + // extend value of the first Z bin to the readout, linear decrease of all values to 0. + int nZsteps = 3; + for (int is = 0; is < nZsteps; is++) { + double pz = z + (zEdge - z) * (is + 1.) / nZsteps; + double s = (nZsteps - 1. - is) / nZsteps; + map.addCorrectionPoint(iRoc, iRow, py, pz, s * correctionX, + s * correctionY, s * correctionZ); + } } } - } - } // iz - } // iy + } // iz + } // iy + + } // iRow + }; // myThread + + // run n threads + + int nThreads = mNthreads; + // nThreads = 1; + + std::vector threads(nThreads); + + for (int i = 0; i < nThreads; i++) { + threads[i] = std::thread(myThread, i, nThreads); + } - } // iRow + // wait for the threads to finish + for (auto& th : threads) { + th.join(); + } } // iRoc - LOGP(info, "Reading & reparing of the track residuals tooks: {}s", watch1.RealTime()); + LOGP(info, "Reading & reparing of the track residuals tooks: {}s", watch3.RealTime()); LOG(info) << "fast space charge correction helper: create space charge from the map of data points.."; + TStopwatch watch4; + helper->fillSpaceChargeCorrectionFromMap(correction); - LOGP(info, "Creation from track residuals tooks in total: {}s", watch2.RealTime()); + LOG(info) << "fast space charge correction helper: creation from the data map took " << watch4.RealTime() << "s"; + + LOGP(info, "Creation from track residuals tooks in total: {}s", watch.RealTime()); return std::move(correctionPtr); } diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx index 5a7dffd2a753b..35c6e43daa43b 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx @@ -29,12 +29,9 @@ ClassImp(TPCFastSpaceChargeCorrection); TPCFastSpaceChargeCorrection::TPCFastSpaceChargeCorrection() : FlatObject(), - mConstructionRowInfos(nullptr), mConstructionScenarios(nullptr), mNumberOfScenarios(0), mScenarioPtr(nullptr), - mRowInfoPtr(nullptr), - mSliceRowInfoPtr(nullptr), mTimeStamp(-1), mSplineData{nullptr, nullptr, nullptr}, mSliceDataSizeBytes{0, 0, 0} @@ -52,21 +49,16 @@ void TPCFastSpaceChargeCorrection::releaseConstructionMemory() { // release temporary arrays #if !defined(GPUCA_GPUCODE) - delete[] mConstructionRowInfos; delete[] mConstructionScenarios; #endif - mConstructionRowInfos = nullptr; mConstructionScenarios = nullptr; } void TPCFastSpaceChargeCorrection::destroy() { releaseConstructionMemory(); - mConstructionRowInfos = nullptr; mConstructionScenarios = nullptr; mNumberOfScenarios = 0; - mRowInfoPtr = nullptr; - mSliceRowInfoPtr = nullptr; mScenarioPtr = nullptr; mTimeStamp = -1; for (int32_t is = 0; is < 3; is++) { @@ -78,8 +70,6 @@ void TPCFastSpaceChargeCorrection::destroy() void TPCFastSpaceChargeCorrection::relocateBufferPointers(const char* oldBuffer, char* newBuffer) { - mRowInfoPtr = FlatObject::relocatePointer(oldBuffer, newBuffer, mRowInfoPtr); - mSliceRowInfoPtr = FlatObject::relocatePointer(oldBuffer, newBuffer, mSliceRowInfoPtr); mScenarioPtr = FlatObject::relocatePointer(oldBuffer, newBuffer, mScenarioPtr); for (int32_t i = 0; i < mNumberOfScenarios; i++) { @@ -119,13 +109,21 @@ void TPCFastSpaceChargeCorrection::cloneFromObject(const TPCFastSpaceChargeCorre mSliceDataSizeBytes[2] = obj.mSliceDataSizeBytes[2]; // variable-size data - mRowInfoPtr = obj.mRowInfoPtr; - mSliceRowInfoPtr = obj.mSliceRowInfoPtr; mScenarioPtr = obj.mScenarioPtr; mSplineData[0] = obj.mSplineData[0]; mSplineData[1] = obj.mSplineData[1]; mSplineData[2] = obj.mSplineData[2]; + mClassVersion = obj.mClassVersion; + + for (int i = 0; i < TPCFastTransformGeo::getMaxNumberOfRows(); i++) { + mRowInfos[i] = obj.mRowInfos[i]; + } + + for (int i = 0; i < TPCFastTransformGeo::getNumberOfSlices() * TPCFastTransformGeo::getMaxNumberOfRows(); i++) { + mSliceRowInfos[i] = obj.mSliceRowInfos[i]; + } + relocateBufferPointers(oldFlatBufferPtr, mFlatBufferPtr); } @@ -140,19 +138,43 @@ void TPCFastSpaceChargeCorrection::moveBufferTo(char* newFlatBufferPtr) void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBufferPtr) { - /// Sets the actual location of the external flat buffer after it has been moved (i.e. to another maschine) + /// Sets the actual location of the external flat buffer after it has been moved (e.g. to another maschine) + + struct RowInfoVersion3 { + int splineScenarioID{0}; ///< scenario index (which of Spline2D splines to use) + size_t dataOffsetBytes[3]{0}; ///< offset for the spline data withing a TPC slice + }; + + struct RowActiveAreaVersion3 { + float maxDriftLengthCheb[5]{0.f}; + float vMax{0.f}; + float cuMin{0.f}; + float cuMax{0.f}; + float cvMax{0.f}; + }; + + struct SliceRowInfoVersion3 { + float gridV0{0.f}; ///< V coordinate of the V-grid start + float gridCorrU0{0.f}; ///< U coordinate of the U-grid start for corrected U + float gridCorrV0{0.f}; ///< V coordinate of the V-grid start for corrected V + float scaleCorrUtoGrid{0.f}; ///< scale corrected U to U-grid coordinate + float scaleCorrVtoGrid{0.f}; ///< scale corrected V to V-grid coordinate + RowActiveAreaVersion3 activeArea; + }; FlatObject::setActualBufferAddress(actualFlatBufferPtr); size_t rowsOffset = 0; - size_t rowsSize = sizeof(RowInfo) * mGeo.getNumberOfRows(); - - mRowInfoPtr = reinterpret_cast(mFlatBufferPtr + rowsOffset); + size_t rowsSize = 0; + if (mClassVersion == 3) { + rowsSize = sizeof(RowInfoVersion3) * mGeo.getNumberOfRows(); + } size_t sliceRowsOffset = rowsOffset + rowsSize; - size_t sliceRowsSize = sizeof(SliceRowInfo) * mGeo.getNumberOfRows() * mGeo.getNumberOfSlices(); - - mSliceRowInfoPtr = reinterpret_cast(mFlatBufferPtr + sliceRowsOffset); + size_t sliceRowsSize = 0; + if (mClassVersion == 3) { // copy old-format slicerow data from the buffer to the arrays + sliceRowsSize = sizeof(SliceRowInfoVersion3) * mGeo.getNumberOfRows() * mGeo.getNumberOfSlices(); + } size_t scOffset = alignSize(sliceRowsOffset + sliceRowsSize, SplineType::getClassAlignmentBytes()); size_t scSize = sizeof(SplineType) * mNumberOfScenarios; @@ -173,6 +195,53 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer mSplineData[is] = reinterpret_cast(mFlatBufferPtr + sliceDataOffset); bufferSize = sliceDataOffset + mSliceDataSizeBytes[is] * mGeo.getNumberOfSlices(); } + + if (mClassVersion == 3) { // copy old-format slicerow data from the buffer to the arrays + + auto* rowInfosOld = reinterpret_cast(mFlatBufferPtr + rowsOffset); + for (int i = 0; i < mGeo.getNumberOfRows(); i++) { + RowInfoVersion3& infoOld = rowInfosOld[i]; + RowInfo& info = mRowInfos[i]; + info.splineScenarioID = infoOld.splineScenarioID; + for (int is = 0; is < 3; is++) { + info.dataOffsetBytes[is] = infoOld.dataOffsetBytes[is]; + } + } + + for (int is = 0; is < mNumberOfScenarios; is++) { + auto& spline = mScenarioPtr[is]; + spline.setXrange(0., spline.getGridX1().getUmax(), 0., spline.getGridX2().getUmax()); + } + + auto* sliceRowInfosOld = reinterpret_cast(mFlatBufferPtr + sliceRowsOffset); + + for (int slice = 0; slice < mGeo.getNumberOfSlices(); slice++) { + for (int row = 0; row < mGeo.getNumberOfRows(); row++) { + SliceRowInfoVersion3& infoOld = sliceRowInfosOld[mGeo.getNumberOfRows() * slice + row]; + SliceRowInfo& info = getSliceRowInfo(slice, row); + const auto& spline = getSpline(slice, row); + info.gridU0 = mGeo.getRowInfo(row).u0; + info.scaleUtoGrid = spline.getGridX1().getUmax() / mGeo.getRowInfo(row).getUwidth(); + + info.gridV0 = infoOld.gridV0; + info.scaleVtoGrid = spline.getGridX2().getUmax() / (mGeo.getTPCzLength(slice) + 3. - info.gridV0); + + info.gridCorrU0 = infoOld.gridCorrU0; + info.scaleCorrUtoGrid = infoOld.scaleCorrUtoGrid; + + info.gridCorrV0 = infoOld.gridCorrV0; + info.scaleCorrVtoGrid = infoOld.scaleCorrVtoGrid; + + info.activeArea.vMax = infoOld.activeArea.vMax; + info.activeArea.cuMin = infoOld.activeArea.cuMin; + info.activeArea.cuMax = infoOld.activeArea.cuMax; + info.activeArea.cvMax = infoOld.activeArea.cvMax; + for (int i = 0; i < 5; i++) { + info.activeArea.maxDriftLengthCheb[i] = infoOld.activeArea.maxDriftLengthCheb[i]; + } + } + } + } } void TPCFastSpaceChargeCorrection::setFutureBufferAddress(char* futureFlatBufferPtr) @@ -187,10 +256,7 @@ void TPCFastSpaceChargeCorrection::setFutureBufferAddress(char* futureFlatBuffer char* oldBuffer = mFlatBufferPtr; char* newBuffer = futureFlatBufferPtr; - mRowInfoPtr = relocatePointer(oldBuffer, newBuffer, mRowInfoPtr); - mSliceRowInfoPtr = relocatePointer(oldBuffer, newBuffer, mSliceRowInfoPtr); - - for (int32_t i = 0; i < mNumberOfScenarios; i++) { + for (int i = 0; i < mNumberOfScenarios; i++) { SplineType& sp = mScenarioPtr[i]; char* newSplineBuf = relocatePointer(oldBuffer, newBuffer, sp.getFlatBufferPtr()); sp.setFutureBufferAddress(newSplineBuf); @@ -210,10 +276,10 @@ void TPCFastSpaceChargeCorrection::print() const LOG(info) << " mNumberOfScenarios = " << mNumberOfScenarios; LOG(info) << " mTimeStamp = " << mTimeStamp; LOG(info) << " mSliceDataSizeBytes = " << mSliceDataSizeBytes[0] << " " << mSliceDataSizeBytes[1] << " " << mSliceDataSizeBytes[2]; - if (mRowInfoPtr) { + { LOG(info) << " TPC rows: "; - for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { - RowInfo& r = mRowInfoPtr[i]; + for (int i = 0; i < mGeo.getNumberOfRows(); i++) { + const RowInfo& r = mRowInfos[i]; LOG(info) << " tpc row " << i << ": splineScenarioID = " << r.splineScenarioID << " dataOffsetBytes = " << r.dataOffsetBytes; } } @@ -223,7 +289,7 @@ void TPCFastSpaceChargeCorrection::print() const mScenarioPtr[i].print(); } } - if (mRowInfoPtr && mScenarioPtr && mSliceRowInfoPtr) { + if (mScenarioPtr) { LOG(info) << " Spline Data: "; for (int32_t is = 0; is < mGeo.getNumberOfSlices(); is++) { for (int32_t ir = 0; ir < mGeo.getNumberOfRows(); ir++) { @@ -260,15 +326,13 @@ void TPCFastSpaceChargeCorrection::startConstruction(const TPCFastTransformGeo& releaseConstructionMemory(); #if !defined(GPUCA_GPUCODE) - mConstructionRowInfos = new RowInfo[mGeo.getNumberOfRows()]; mConstructionScenarios = new SplineType[mNumberOfScenarios]; #endif - assert(mConstructionRowInfos != nullptr); assert(mConstructionScenarios != nullptr); - for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { - mConstructionRowInfos[i].splineScenarioID = -1; + for (int i = 0; i < mGeo.getNumberOfRows(); i++) { + mRowInfos[i].splineScenarioID = -1; } for (int32_t i = 0; i < mNumberOfScenarios; i++) { @@ -277,13 +341,12 @@ void TPCFastSpaceChargeCorrection::startConstruction(const TPCFastTransformGeo& mTimeStamp = -1; - mRowInfoPtr = nullptr; - mSliceRowInfoPtr = nullptr; mScenarioPtr = nullptr; for (int32_t s = 0; s < 3; s++) { mSplineData[s] = nullptr; mSliceDataSizeBytes[s] = 0; } + mClassVersion = 4; } void TPCFastSpaceChargeCorrection::setRowScenarioID(int32_t iRow, int32_t iScenario) @@ -292,7 +355,7 @@ void TPCFastSpaceChargeCorrection::setRowScenarioID(int32_t iRow, int32_t iScena assert(mConstructionMask & ConstructionState::InProgress); assert(iRow >= 0 && iRow < mGeo.getNumberOfRows() && iScenario >= 0 && iScenario < mNumberOfScenarios); - RowInfo& row = mConstructionRowInfos[iRow]; + RowInfo& row = mRowInfos[iRow]; row.splineScenarioID = iScenario; for (int32_t s = 0; s < 3; s++) { row.dataOffsetBytes[s] = 0; @@ -315,8 +378,8 @@ void TPCFastSpaceChargeCorrection::finishConstruction() assert(mConstructionMask & ConstructionState::InProgress); - for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { - assert(mConstructionRowInfos[i].splineScenarioID >= 0); + for (int i = 0; i < mGeo.getNumberOfRows(); i++) { + assert(mRowInfos[i].splineScenarioID >= 0); } for (int32_t i = 0; i < mNumberOfScenarios; i++) { assert(mConstructionScenarios[i].isConstructed()); @@ -324,13 +387,7 @@ void TPCFastSpaceChargeCorrection::finishConstruction() // organize memory for the flat buffer and caculate its size - size_t rowsOffset = 0; - size_t rowsSize = sizeof(RowInfo) * mGeo.getNumberOfRows(); - - size_t sliceRowsOffset = rowsSize; - size_t sliceRowsSize = sizeof(SliceRowInfo) * mGeo.getNumberOfRows() * mGeo.getNumberOfSlices(); - - size_t scOffset = alignSize(sliceRowsOffset + sliceRowsSize, SplineType::getClassAlignmentBytes()); + size_t scOffset = 0; size_t scSize = sizeof(SplineType) * mNumberOfScenarios; size_t scBufferOffsets[mNumberOfScenarios]; @@ -347,8 +404,8 @@ void TPCFastSpaceChargeCorrection::finishConstruction() for (int32_t is = 0; is < 3; is++) { sliceDataOffset[is] = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); mSliceDataSizeBytes[is] = 0; - for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { - RowInfo& row = mConstructionRowInfos[i]; + for (int i = 0; i < mGeo.getNumberOfRows(); i++) { + RowInfo& row = mRowInfos[i]; SplineType& spline = mConstructionScenarios[row.splineScenarioID]; row.dataOffsetBytes[is] = alignSize(mSliceDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); mSliceDataSizeBytes[is] = row.dataOffsetBytes[is] + spline.getSizeOfParameters(); @@ -359,20 +416,6 @@ void TPCFastSpaceChargeCorrection::finishConstruction() FlatObject::finishConstruction(bufferSize); - mRowInfoPtr = reinterpret_cast(mFlatBufferPtr + rowsOffset); - for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { - mRowInfoPtr[i] = mConstructionRowInfos[i]; - } - - mSliceRowInfoPtr = reinterpret_cast(mFlatBufferPtr + sliceRowsOffset); - for (int32_t s = 0; s < mGeo.getNumberOfSlices(); s++) { - for (int32_t r = 0; r < mGeo.getNumberOfRows(); r++) { - mSliceRowInfoPtr[s * mGeo.getNumberOfRows() + r].gridCorrU0 = 0.; - mSliceRowInfoPtr[s * mGeo.getNumberOfRows() + r].scaleCorrUtoGrid = 0.; - mSliceRowInfoPtr[s * mGeo.getNumberOfRows() + r].scaleCorrVtoGrid = 0.; - } - } - mScenarioPtr = reinterpret_cast(mFlatBufferPtr + scOffset); for (int32_t i = 0; i < mNumberOfScenarios; i++) { @@ -417,20 +460,28 @@ GPUd() void TPCFastSpaceChargeCorrection::setNoCorrection() } SliceRowInfo& info = getSliceRowInfo(slice, row); + + info.gridU0 = mGeo.getRowInfo(row).u0; + info.scaleUtoGrid = spline.getGridX1().getUmax() / mGeo.getRowInfo(row).getUwidth(); + + info.gridV0 = 0.f; + info.scaleVtoGrid = spline.getGridX2().getUmax() / vLength; + + info.gridCorrU0 = info.gridU0; + info.gridCorrV0 = info.gridV0; + info.scaleCorrUtoGrid = info.scaleUtoGrid; + info.scaleCorrVtoGrid = info.scaleVtoGrid; + RowActiveArea& area = info.activeArea; for (int32_t i = 1; i < 5; i++) { area.maxDriftLengthCheb[i] = 0; } area.maxDriftLengthCheb[0] = vLength; - area.cuMin = mGeo.convPadToU(row, 0.f); + area.cuMin = info.gridCorrU0; area.cuMax = -area.cuMin; area.vMax = vLength; area.cvMax = vLength; - info.gridV0 = 0.f; - info.gridCorrU0 = area.cuMin; - info.gridCorrV0 = info.gridV0; - info.scaleCorrUtoGrid = spline.getGridX1().getUmax() / (area.cuMax - area.cuMin); - info.scaleCorrVtoGrid = spline.getGridX2().getUmax() / area.cvMax; + } // row } // slice } diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index 9589ecbfc1fc4..b29d65b98458a 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -58,13 +58,16 @@ class TPCFastSpaceChargeCorrection : public FlatObject }; struct SliceRowInfo { + float gridU0{0.f}; //< U coordinate of the U-grid start + float scaleUtoGrid{0.f}; //< scale U to U-grid coordinate float gridV0{0.f}; ///< V coordinate of the V-grid start + float scaleVtoGrid{0.f}; //< scale V to V-grid coordinate float gridCorrU0{0.f}; ///< U coordinate of the U-grid start for corrected U - float gridCorrV0{0.f}; ///< V coordinate of the V-grid start for corrected V float scaleCorrUtoGrid{0.f}; ///< scale corrected U to U-grid coordinate + float gridCorrV0{0.f}; ///< V coordinate of the V-grid start for corrected V float scaleCorrVtoGrid{0.f}; ///< scale corrected V to V-grid coordinate RowActiveArea activeArea; - ClassDefNV(SliceRowInfo, 1); + ClassDefNV(SliceRowInfo, 2); }; struct SliceInfo { @@ -199,7 +202,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject GPUd() float getInterpolationSafetyMargin() const { return fInterpolationSafetyMargin; } /// Gives TPC row info - GPUd() const RowInfo& getRowInfo(int32_t row) const { return mRowInfoPtr[row]; } + GPUd() const RowInfo& getRowInfo(int row) const { return mRowInfos[row]; } /// Gives TPC slice info GPUd() const SliceInfo& getSliceInfo(int32_t slice) const @@ -216,13 +219,13 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// Gives TPC slice & row info GPUd() const SliceRowInfo& getSliceRowInfo(int32_t slice, int32_t row) const { - return mSliceRowInfoPtr[mGeo.getNumberOfRows() * slice + row]; + return mSliceRowInfos[mGeo.getMaxNumberOfRows() * slice + row]; } /// Gives TPC slice & row info GPUd() SliceRowInfo& getSliceRowInfo(int32_t slice, int32_t row) { - return mSliceRowInfoPtr[mGeo.getNumberOfRows() * slice + row]; + return mSliceRowInfos[mGeo.getMaxNumberOfRows() * slice + row]; } #if !defined(GPUCA_GPUCODE) @@ -244,7 +247,6 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// _______________ Construction control _______________________________________________ - RowInfo* mConstructionRowInfos = nullptr; //! (transient!!) Temporary container of the row infos during construction SplineType* mConstructionScenarios = nullptr; //! (transient!!) Temporary container for spline scenarios /// _______________ Geometry _______________________________________________ @@ -255,9 +257,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject SliceInfo mSliceInfo[TPCFastTransformGeo::getNumberOfSlices()]; ///< SliceInfo array - SplineType* mScenarioPtr; //! (transient!!) pointer to spline scenarios - RowInfo* mRowInfoPtr; //! (transient!!) pointer to RowInfo array inside the mFlatBufferPtr buffer - SliceRowInfo* mSliceRowInfoPtr; //! (transient!!) pointer to SliceRowInfo array inside the mFlatBufferPtr + SplineType* mScenarioPtr; //! (transient!!) pointer to spline scenarios /// _______________ Calibration data _______________________________________________ @@ -269,7 +269,16 @@ class TPCFastSpaceChargeCorrection : public FlatObject float fInterpolationSafetyMargin{0.1f}; // 10% area around the TPC row. Outside of this area the interpolation returns the boundary values. - ClassDefNV(TPCFastSpaceChargeCorrection, 3); + /// Class version. It is used to read older versions from disc. + /// The default version 3 is the one before this field was introduced. + /// The actual version must be set in startConstruction(). + int mClassVersion{3}; + + RowInfo mRowInfos[TPCFastTransformGeo::getMaxNumberOfRows()]; ///< RowInfo array + + SliceRowInfo mSliceRowInfos[TPCFastTransformGeo::getNumberOfSlices() * TPCFastTransformGeo::getMaxNumberOfRows()]; ///< SliceRowInfo array + + ClassDefNV(TPCFastSpaceChargeCorrection, 4); }; /// ==================================================== @@ -279,28 +288,28 @@ class TPCFastSpaceChargeCorrection : public FlatObject GPUdi() const TPCFastSpaceChargeCorrection::SplineType& TPCFastSpaceChargeCorrection::getSpline(int32_t slice, int32_t row) const { /// Gives const pointer to spline - const RowInfo& rowInfo = mRowInfoPtr[row]; + const RowInfo& rowInfo = mRowInfos[row]; return mScenarioPtr[rowInfo.splineScenarioID]; } GPUdi() TPCFastSpaceChargeCorrection::SplineType& TPCFastSpaceChargeCorrection::getSpline(int32_t slice, int32_t row) { /// Gives pointer to spline - const RowInfo& rowInfo = mRowInfoPtr[row]; + const RowInfo& rowInfo = mRowInfos[row]; return mScenarioPtr[rowInfo.splineScenarioID]; } GPUdi() float* TPCFastSpaceChargeCorrection::getSplineData(int32_t slice, int32_t row, int32_t iSpline) { /// Gives pointer to spline data - const RowInfo& rowInfo = mRowInfoPtr[row]; + const RowInfo& rowInfo = mRowInfos[row]; return reinterpret_cast(mSplineData[iSpline] + mSliceDataSizeBytes[iSpline] * slice + rowInfo.dataOffsetBytes[iSpline]); } GPUdi() const float* TPCFastSpaceChargeCorrection::getSplineData(int32_t slice, int32_t row, int32_t iSpline) const { /// Gives pointer to spline data - const RowInfo& rowInfo = mRowInfoPtr[row]; + const RowInfo& rowInfo = mRowInfos[row]; return reinterpret_cast(mSplineData[iSpline] + mSliceDataSizeBytes[iSpline] * slice + rowInfo.dataOffsetBytes[iSpline]); } @@ -356,35 +365,18 @@ GPUdi() void TPCFastSpaceChargeCorrection::schrinkCorrectedUV(int32_t slice, int GPUdi() void TPCFastSpaceChargeCorrection::convUVtoGrid(int32_t slice, int32_t row, float u, float v, float& gu, float& gv) const { - // TODO optimise !!! - gu = 0.f; - gv = 0.f; - schrinkUV(slice, row, u, v); - const SliceRowInfo& info = getSliceRowInfo(slice, row); - const SplineType& spline = getSpline(slice, row); - - float su0 = 0.f, sv0 = 0.f; - mGeo.convUVtoScaledUV(slice, row, u, info.gridV0, su0, sv0); - mGeo.convUVtoScaledUV(slice, row, u, v, gu, gv); - - gv = (gv - sv0) / (1.f - sv0); - gu *= spline.getGridX1().getUmax(); - gv *= spline.getGridX2().getUmax(); + gu = (u - info.gridU0) * info.scaleUtoGrid; + gv = (v - info.gridV0) * info.scaleVtoGrid; } GPUdi() void TPCFastSpaceChargeCorrection::convGridToUV(int32_t slice, int32_t row, float gridU, float gridV, float& u, float& v) const { - // TODO optimise - /// convert u,v to internal grid coordinates - float su0 = 0.f, sv0 = 0.f; + /// convert internal grid coordinates to u,v const SliceRowInfo& info = getSliceRowInfo(slice, row); - const SplineType& spline = getSpline(slice, row); - mGeo.convUVtoScaledUV(slice, row, 0.f, info.gridV0, su0, sv0); - float su = gridU / spline.getGridX1().getUmax(); - float sv = sv0 + gridV / spline.getGridX2().getUmax() * (1.f - sv0); - mGeo.convScaledUVtoUV(slice, row, su, sv, u, v); + u = info.gridU0 + gridU / info.scaleUtoGrid; + v = info.gridV0 + gridV / info.scaleVtoGrid; } GPUdi() void TPCFastSpaceChargeCorrection::convCorrectedUVtoGrid(int32_t slice, int32_t row, float corrU, float corrV, float& gridU, float& gridV) const diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.h b/GPU/TPCFastTransformation/TPCFastTransformGeo.h index 5eddada1e9acc..d20331ba6ab0f 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.h +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.h @@ -50,6 +50,12 @@ class TPCFastTransformGeo float scaleUtoSU; ///< scale for su (scaled u ) coordinate float scaleSUtoU; ///< scale for u coordinate + /// get U min + GPUd() float getUmin() const { return u0; } + + /// get U max + GPUd() float getUmax() const { return -u0; } + /// get width in U GPUd() float getUwidth() const { return -2.f * u0; } ClassDefNV(RowInfo, 1); @@ -110,6 +116,9 @@ class TPCFastTransformGeo /// Gives number of TPC rows GPUd() int32_t getNumberOfRows() const { return mNumberOfRows; } + /// Gives number of TPC rows + GPUd() static constexpr int getMaxNumberOfRows() { return MaxNumberOfRows; } + /// Gives slice info GPUd() const SliceInfo& getSliceInfo(int32_t slice) const; diff --git a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C index b13d031d6d10d..bf3e14d552715 100644 --- a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C +++ b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C @@ -59,6 +59,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", corr->Draw("cx:y:z","iRoc==0&&iRow==10","") grid->Draw("cx:y:z","iRoc==0&&iRow==10","same") vox->Draw("vx:y:z","iRoc==0&&iRow==10","same") + corrvox->Draw("cx:y:z","iRoc==0&&iRow==10","same") points->Draw("px:y:z","iRoc==0&&iRow==10","same") */ @@ -98,17 +99,36 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", trackResiduals.setZ2XBinning(z2xBins); trackResiduals.init(); - std::cout << "y2xBins: " << y2xBins.size() << " z2xBins: " << z2xBins.size() << std::endl; + { + std::cout << "input track residuals: " << std::endl; + std::cout << "voxel tree y2xBins: " << y2xBins.size() << std::endl; - for (auto y2x : y2xBins) { - std::cout << "y2x: " << y2x << std::endl; - } + for (auto y2x : y2xBins) { + std::cout << " y2x: " << y2x << std::endl; + } + std::cout << std::endl; + + int nY2Xbins = trackResiduals.getNY2XBins(); + + std::cout << " TrackResiduals y2x bins: " << nY2Xbins << std::endl; + for (int i = 0; i < nY2Xbins; i++) { + std::cout << "scaled getY2X(bin) : " << trackResiduals.getY2X(0, i) / trackResiduals.getMaxY2X(0) << std::endl; + } + + std::cout << "voxel tree z2xBins: " << z2xBins.size() << std::endl; - std::cout << std::endl; + for (auto z2x : z2xBins) { + std::cout << "z2x: " << z2x << std::endl; + } + std::cout << std::endl; - for (auto z2x : z2xBins) { - std::cout << "z2x: " << z2x << std::endl; + int nZ2Xbins = trackResiduals.getNZ2XBins(); + std::cout << " TrackResiduals z2x bins: " << nZ2Xbins << std::endl; + for (int i = 0; i < nZ2Xbins; i++) { + std::cout << "getZ2X(bin) : " << trackResiduals.getZ2X(i) << std::endl; + } } + std::cout << "create fast transformation ... " << std::endl; auto* helper = o2::tpc::TPCFastTransformHelperO2::instance(); @@ -122,16 +142,47 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", std::unique_ptr fastTransform( helper->create(0, *corrPtr)); - o2::gpu::TPCFastSpaceChargeCorrection& corr = fastTransform->getCorrection(); - std::cout << "... create fast transformation completed " << std::endl; if (*outFileName) { fastTransform->writeToFile(outFileName, "ccdb_object"); } + if (1) { // read transformation from the file + + // const char* fileName = "master/out.root"; + + const char* fileName = outFileName; + + std::cout << "load corrections from file " << fileName << std::endl; + + fastTransform->cloneFromObject(*TPCFastTransform::loadFromFile(fileName, "ccdb_object"), nullptr); + + o2::gpu::TPCFastSpaceChargeCorrection& corr = fastTransform->getCorrection(); + + if (0) { + std::cout << "check the loaded correction ..." << std::endl; + + const o2::gpu::TPCFastTransformGeo& geo = helper->getGeometry(); + + // for (int iRoc = 0; iRoc < geo.getNumberOfSlices(); iRoc++) { + for (int iRoc = 0; iRoc < 1; iRoc++) { + for (int iRow = 0; iRow < geo.getNumberOfRows(); iRow++) { + auto& info = corr.getSliceRowInfo(iRoc, iRow); + std::cout << "roc " << iRoc << " row " << iRow + << " gridV0 " << info.gridV0 << " gridCorrU0 " << info.gridCorrU0 << " gridCorrV0 " << info.gridCorrV0 + << " scaleCorrUtoGrid " << info.scaleCorrUtoGrid << " scaleCorrVtoGrid " << info.scaleCorrVtoGrid + << " gridU0 " << info.gridU0 << " scaleUtoGrid " << info.scaleUtoGrid << " scaleVtoGrid " << info.scaleVtoGrid + << std::endl; + } + } + } + } + std::cout << "verify the results ..." << std::endl; + o2::gpu::TPCFastSpaceChargeCorrection& corr = fastTransform->getCorrection(); + // the difference double maxDiff[3] = {0., 0., 0.}; @@ -158,12 +209,21 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", // ntuple with the input data: voxel corrections debugFile->cd(); TNtuple* debugVox = - new TNtuple("vox", "vox", "iRoc:iRow:x:y:z:vx:vy:vz:cx:cy:cz"); + new TNtuple("vox", "vox", "iRoc:iRow:n:x:y:z:vx:vy:vz:cx:cy:cz"); debugVox->SetMarkerStyle(8); debugVox->SetMarkerSize(0.8); debugVox->SetMarkerColor(kBlue); + // duplicate of debugVox + debugFile->cd(); + TNtuple* debugCorrVox = + new TNtuple("corrvox", "corrvox", "iRoc:iRow:n:x:y:z:vx:vy:vz:cx:cy:cz"); + + debugCorrVox->SetMarkerStyle(8); + debugCorrVox->SetMarkerSize(0.8); + debugCorrVox->SetMarkerColor(kMagenta); + // ntuple with spline grid points debugFile->cd(); TNtuple* debugGrid = new TNtuple("grid", "grid", "iRoc:iRow:x:y:z:cx:cy:cz"); @@ -244,13 +304,6 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", correctionZ *= -1.; } - // TODO: skip empty voxels? - if (voxEntries < 1.) { // no statistics - // std::cout << "Empty Voxel!!! corrections: " << correctionX << " " - // << correctionY << " " << correctionZ << std::endl; - // continue; - } - float u, v, cx, cu, cv, cy, cz; geo.convLocalToUV(iRoc, y, z, u, v); corr.getCorrection(iRoc, iRow, u, v, cx, cu, cv); @@ -258,19 +311,24 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", cy -= y; cz -= z; double d[3] = {cx - correctionX, cy - correctionY, cz - correctionZ}; - for (int32_t i = 0; i < 3; i++) { - if (fabs(maxDiff[i]) < fabs(d[i])) { - maxDiff[i] = d[i]; - maxDiffRoc[i] = iRoc; - maxDiffRow[i] = iRow; - std::cout << " roc " << iRoc << " row " << iRow << " xyz " << i - << " diff " << d[i] << std::endl; + if (voxEntries >= 1.) { + for (int i = 0; i < 3; i++) { + if (fabs(maxDiff[i]) < fabs(d[i])) { + maxDiff[i] = d[i]; + maxDiffRoc[i] = iRoc; + maxDiffRow[i] = iRow; + std::cout << " roc " << iRoc << " row " << iRow << " xyz " << i + << " diff " << d[i] << " entries " << voxEntries << " y " << y2xBin << " z " << z2xBin << std::endl; + } + sumDiff[i] += d[i] * d[i]; } - sumDiff[i] += d[i] * d[i]; + nDiff++; } - nDiff++; - debugVox->Fill(iRoc, iRow, x, y, z, correctionX, correctionY, correctionZ, + + debugVox->Fill(iRoc, iRow, voxEntries, x, y, z, correctionX, correctionY, correctionZ, cx, cy, cz); + debugCorrVox->Fill(iRoc, iRow, voxEntries, x, y, z, correctionX, correctionY, correctionZ, + cx, cy, cz); } std::cout << "create debug ntuples ..." << std::endl; @@ -282,43 +340,71 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", double x = geo.getRowInfo(iRow).x; + // the spline grid + + const auto& gridU = corr.getSpline(iRoc, iRow).getGridX1(); + const auto& gridV = corr.getSpline(iRoc, iRow).getGridX2(); + if (iRoc == 0 && iRow == 0) { + std::cout << "spline scenario " << corr.getRowInfo(iRow).splineScenarioID << std::endl; + std::cout << "spline grid U: u = " << 0 << ".." << gridU.getUmax() << ", x = " << gridU.getXmin() << ".." << gridU.getXmax() << std::endl; + std::cout << "spline grid V: u = " << 0 << ".." << gridV.getUmax() << ", x = " << gridV.getXmin() << ".." << gridV.getXmax() << std::endl; + } + // the correction + { + std::vector p[2], g[2]; - for (double su = 0.; su <= 1.0001; su += 0.01) { - for (double sv = 0.; sv <= 1.0001; sv += 0.1) { + p[0].push_back(geo.getRowInfo(iRow).getUmin()); + for (int iu = 0; iu < gridU.getNumberOfKnots(); iu++) { float u, v; - geo.convScaledUVtoUV(iRoc, iRow, su, sv, u, v); - float y, z; - geo.convUVtoLocal(iRoc, u, v, y, z); - float cx, cu, cv; - corr.getCorrection(iRoc, iRow, u, v, cx, cu, cv); - float cy, cz; - geo.convUVtoLocal(iRoc, u + cu, v + cv, cy, cz); - cy -= y; - cz -= z; - debugCorr->Fill(iRoc, iRow, x, y, z, cx, cy, cz); + corr.convGridToUV(iRoc, iRow, gridU.getKnot(iu).getU(), 0., u, v); + g[0].push_back(u); + p[0].push_back(u); } - } - - // the spline grid + p[0].push_back(geo.getRowInfo(iRow).getUmax()); - const auto& gridU = corr.getSpline(iRoc, iRow).getGridX1(); - const auto& gridV = corr.getSpline(iRoc, iRow).getGridX2(); - for (int32_t iu = 0; iu < gridU.getNumberOfKnots(); iu++) { - double su = gridU.convUtoX(gridU.getKnot(iu).getU()); - for (int32_t iv = 0; iv < gridV.getNumberOfKnots(); iv++) { - double sv = gridV.convUtoX(gridV.getKnot(iv).getU()); + p[1].push_back(0.); + for (int iv = 0; iv < gridV.getNumberOfKnots(); iv++) { float u, v; - corr.convGridToUV(iRoc, iRow, iu, iv, u, v); - float y, z; - geo.convUVtoLocal(iRoc, u, v, y, z); - float cx, cu, cv; - corr.getCorrection(iRoc, iRow, u, v, cx, cu, cv); - float cy, cz; - geo.convUVtoLocal(iRoc, u + cu, v + cv, cy, cz); - cy -= y; - cz -= z; - debugGrid->Fill(iRoc, iRow, x, y, z, cx, cy, cz); + corr.convGridToUV(iRoc, iRow, 0., gridV.getKnot(iv).getU(), u, v); + g[1].push_back(v); + p[1].push_back(v); + } + p[1].push_back(geo.getTPCzLength(iRoc)); + + for (int iuv = 0; iuv < 2; iuv++) { + int n = p[iuv].size(); + for (unsigned int i = 0; i < n - 1; i++) { + double d = (p[iuv][i + 1] - p[iuv][i]) / 10.; + for (int ii = 1; ii < 10; ii++) { + p[iuv].push_back(p[iuv][i] + d * ii); + } + } + std::sort(p[iuv].begin(), p[iuv].end()); + } + + for (int iter = 0; iter < 2; iter++) { + std::vector& pu = ((iter == 0) ? g[0] : p[0]); + std::vector& pv = ((iter == 0) ? g[1] : p[1]); + for (unsigned int iu = 0; iu < pu.size(); iu++) { + for (unsigned int iv = 0; iv < pv.size(); iv++) { + float u = pu[iu]; + float v = pv[iv]; + float x, y, z; + geo.convUVtoLocal(iRoc, u, v, y, z); + float cx, cu, cv; + corr.getCorrection(iRoc, iRow, u, v, cx, cu, cv); + float cy, cz; + geo.convUVtoLocal(iRoc, u + cu, v + cv, cy, cz); + cy -= y; + cz -= z; + if (iter == 0) { + debugGrid->Fill(iRoc, iRow, x, y, z, cx, cy, cz); + } else { + debugCorr->Fill(iRoc, iRow, x, y, z, cx, cy, cz); + } + } + } } } @@ -372,6 +458,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", debugFile->cd(); debugCorr->Write(); debugVox->Write(); + debugCorrVox->Write(); debugGrid->Write(); debugPoints->Write(); debugFile->Close(); From 658149075aa6fb4d8749d0a19dea12e1b792a3f8 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Thu, 18 Apr 2024 00:30:38 +0000 Subject: [PATCH 081/285] TPC Splines: multithreaded reading of the residual tree --- .../TPCFastSpaceChargeCorrectionHelper.cxx | 73 +++++++++---------- 1 file changed, 33 insertions(+), 40 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index 861cacbe00012..ce0954120281f 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -31,6 +31,7 @@ #include "TStopwatch.h" #include "TTreeReader.h" #include "TTreeReaderValue.h" +#include "ROOT/TTreeProcessorMT.hxx" using namespace o2::gpu; @@ -541,9 +542,6 @@ std::unique_ptr TPCFastSpaceChargeCorrect TStopwatch watch3; - // TTreeProcessorMT treeProcessor(*voxResTree); // multi-threaded tree processor - // treeProcessor.Init(voxResTree); - // read the data ROC by ROC // data in the tree is not sorted by row @@ -554,44 +552,40 @@ std::unique_ptr TPCFastSpaceChargeCorrect float mCx, mCy, mCz; // corrections to the local coordinates }; - std::vector vRocData[nRows]; - for (int ir = 0; ir < nRows; ir++) { + std::vector vRocData[nRows * nROCs]; + for (int ir = 0; ir < nRows * nROCs; ir++) { vRocData[ir].resize(nY2Xbins * nZ2Xbins); } - for (int iRoc = 0; iRoc < nROCs; iRoc++) { + { // read data from the tree to vRocData - for (int ir = 0; ir < nRows; ir++) { - for (int iv = 0; iv < nY2Xbins * nZ2Xbins; iv++) { - vRocData[ir][iv].mNentries = 0; - } - } + ROOT::TTreeProcessorMT processor(*voxResTree, mNthreads); - const int rocDataStart = iRoc * trackResiduals.getNVoxelsPerSector(); - const int rocDataEnd = rocDataStart + trackResiduals.getNVoxelsPerSector(); - - TTreeReader reader(voxResTree); - reader.SetEntriesRange(rocDataStart, rocDataEnd); - TTreeReaderValue v(reader, "voxRes"); - for (int iVox = rocDataStart; iVox < rocDataEnd; iVox++) { - reader.Next(); - // voxResTree->GetEntry(iVox); - if ((int)v->bsec != iRoc) { - LOG(fatal) << "Error reading voxels: voxel ROC number " << v->bsec << " is not equal to the expected " << iRoc; - continue; - } - int iRow = (int)v->bvox[o2::tpc::TrackResiduals::VoxX]; // bin number in x (= pad row) - if (iRow < 0 || iRow >= nRows) { - LOG(fatal) << "Row number " << iRow << " is out of range"; + auto myThread = [&](TTreeReader& readerSubRange) { + TTreeReaderValue v(readerSubRange, "voxRes"); + while (readerSubRange.Next()) { + int iRoc = (int)v->bsec; + if (iRoc < 0 || iRoc >= nROCs) { + LOG(fatal) << "Error reading voxels: voxel ROC number " << iRoc << " is out of range"; + continue; + } + int iRow = (int)v->bvox[o2::tpc::TrackResiduals::VoxX]; // bin number in x (= pad row) + if (iRow < 0 || iRow >= nRows) { + LOG(fatal) << "Row number " << iRow << " is out of range"; + } + int iy = v->bvox[o2::tpc::TrackResiduals::VoxF]; // bin number in y/x 0..14 + int iz = v->bvox[o2::tpc::TrackResiduals::VoxZ]; // bin number in z/x 0..4 + auto& vox = vRocData[iRoc * nRows + iRow][iy * nZ2Xbins + iz]; + vox.mNentries = (int)v->stat[o2::tpc::TrackResiduals::VoxV]; + vox.mCx = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResX] : v->D[o2::tpc::TrackResiduals::ResX]; + vox.mCy = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResY] : v->D[o2::tpc::TrackResiduals::ResY]; + vox.mCz = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResZ] : v->D[o2::tpc::TrackResiduals::ResZ]; } - int iy = v->bvox[o2::tpc::TrackResiduals::VoxF]; // bin number in y/x 0..14 - int iz = v->bvox[o2::tpc::TrackResiduals::VoxZ]; // bin number in z/x 0..4 - auto& vox = vRocData[iRow][iy * nZ2Xbins + iz]; - vox.mNentries = (int)v->stat[o2::tpc::TrackResiduals::VoxV]; - vox.mCx = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResX] : v->D[o2::tpc::TrackResiduals::ResX]; - vox.mCy = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResY] : v->D[o2::tpc::TrackResiduals::ResY]; - vox.mCz = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResZ] : v->D[o2::tpc::TrackResiduals::ResZ]; - } + }; + processor.Process(myThread); + } + + for (int iRoc = 0; iRoc < nROCs; iRoc++) { // now process the data row-by-row @@ -615,7 +609,7 @@ std::unique_ptr TPCFastSpaceChargeCorrect bool isDataFound = false; for (int iy = 0; iy < nY2Xbins; iy++) { for (int iz = 0; iz < nZ2Xbins; iz++) { - auto& data = vRocData[iRow][iy * nZ2Xbins + iz]; + auto& data = vRocData[iRoc * nRows + iRow][iy * nZ2Xbins + iz]; auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; // y/x coordinate of the bin ~-0.15 ... 0.15 double y2x = trackResiduals.getY2X(xBin, iy); @@ -661,7 +655,7 @@ std::unique_ptr TPCFastSpaceChargeCorrect for (int ismooth = 1; ismooth <= 2; ismooth++) { for (int iy = 0; iy < nY2Xbins; iy++) { for (int iz = 0; iz < nZ2Xbins; iz++) { - auto& data = vRocData[iRow][iy * nZ2Xbins + iz]; + auto& data = vRocData[iRoc * nRows + iRow][iy * nZ2Xbins + iz]; auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; if (vox.mSmoothingStep <= ismooth) { // already filled continue; @@ -673,7 +667,7 @@ std::unique_ptr TPCFastSpaceChargeCorrect double w = 0.; bool filled = false; auto update = [&](int iy1, int iz1) { - auto& data1 = vRocData[iRow][iy1 * nZ2Xbins + iz1]; + auto& data1 = vRocData[iRoc * nRows + iRow][iy1 * nZ2Xbins + iz1]; auto& vox1 = vRowVoxels[iy1 * nZ2Xbins + iz1]; if (vox1.mSmoothingStep >= ismooth) { return false; @@ -746,7 +740,7 @@ std::unique_ptr TPCFastSpaceChargeCorrect for (int iy = 0; iy < nY2Xbins; iy++) { for (int iz = 0; iz < nZ2Xbins; iz++) { - auto& data = vRocData[iRow][iy * nZ2Xbins + iz]; + auto& data = vRocData[iRoc * nRows + iRow][iy * nZ2Xbins + iz]; auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; if (vox.mSmoothingStep > 2) { LOG(fatal) << "empty voxel is not repared"; @@ -812,7 +806,6 @@ std::unique_ptr TPCFastSpaceChargeCorrect for (auto& th : threads) { th.join(); } - } // iRoc LOGP(info, "Reading & reparing of the track residuals tooks: {}s", watch3.RealTime()); From 2ffa48f319f2f1670ee52bccc6a24968cd3de4f0 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Thu, 20 Jun 2024 15:15:41 +0000 Subject: [PATCH 082/285] TPC Splines: add limits for SP correction values per TPC row --- .../TPCFastSpaceChargeCorrectionHelper.cxx | 18 +++- .../TPCFastSpaceChargeCorrection.h | 96 ++++++++++++++----- .../macro/TPCFastTransformInit.C | 15 +-- 3 files changed, 96 insertions(+), 33 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index ce0954120281f..e71340a555227 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -144,6 +144,9 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas float* splineParameters = correction.getSplineData(slice, row); const std::vector& data = mCorrectionMap.getPoints(slice, row); int nDataPoints = data.size(); + auto& info = correction.getSliceRowInfo(slice, row); + info.resetMaxValues(); + info.resetMaxValuesInv(); if (nDataPoints >= 4) { std::vector pointSU(nDataPoints); std::vector pointSV(nDataPoints); @@ -156,6 +159,8 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas pointCorr[3 * i + 0] = dx; pointCorr[3 * i + 1] = du; pointCorr[3 * i + 2] = dv; + info.updateMaxValues(2. * dx, 2. * du, 2. * dv); + info.updateMaxValuesInv(-2. * dx, -2. * du, -2. * dv); } helper.approximateDataPoints(spline, splineParameters, 0., spline.getGridX1().getUmax(), 0., spline.getGridX2().getUmax(), &pointSU[0], &pointSV[0], &pointCorr[0], nDataPoints); @@ -767,9 +772,20 @@ std::unique_ptr TPCFastSpaceChargeCorrect double yStep = (yLast - yFirst) / 2; + double zFirst = z - dz / 2.; + double zLast = z + dz / 2.; + double zStep = (zLast - zFirst) / 2.; + + if (0) { // no smoothing + yFirst = y; + yLast = y; + zFirst = z; + zLast = z; + } + for (double py = yFirst; py <= yLast + yStep / 2.; py += yStep) { - for (double pz = z - dz / 2.; pz <= z + dz / 2. + 1.e-4; pz += dz / 2.) { + for (double pz = zFirst; pz <= zLast + zStep / 2.; pz += zStep) { map.addCorrectionPoint(iRoc, iRow, py, pz, correctionX, correctionY, correctionZ); } diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index b29d65b98458a..3fdc9b32e640c 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -58,15 +58,64 @@ class TPCFastSpaceChargeCorrection : public FlatObject }; struct SliceRowInfo { - float gridU0{0.f}; //< U coordinate of the U-grid start - float scaleUtoGrid{0.f}; //< scale U to U-grid coordinate - float gridV0{0.f}; ///< V coordinate of the V-grid start - float scaleVtoGrid{0.f}; //< scale V to V-grid coordinate - float gridCorrU0{0.f}; ///< U coordinate of the U-grid start for corrected U - float scaleCorrUtoGrid{0.f}; ///< scale corrected U to U-grid coordinate - float gridCorrV0{0.f}; ///< V coordinate of the V-grid start for corrected V - float scaleCorrVtoGrid{0.f}; ///< scale corrected V to V-grid coordinate + float gridU0{0.f}; //< U coordinate of the U-grid start + float scaleUtoGrid{0.f}; //< scale U to U-grid coordinate + float gridV0{0.f}; ///< V coordinate of the V-grid start + float scaleVtoGrid{0.f}; //< scale V to V-grid coordinate + float gridCorrU0{0.f}; ///< U coordinate of the U-grid start for corrected U + float scaleCorrUtoGrid{0.f}; ///< scale corrected U to U-grid coordinate + float gridCorrV0{0.f}; ///< V coordinate of the V-grid start for corrected V + float scaleCorrVtoGrid{0.f}; ///< scale corrected V to V-grid coordinate + float maxCorr[3]{10.f, 10.f, 10.f}; ///< max correction for dX, dU, dV + float minCorr[3]{-10.f, -10.f, -10.f}; ///< min correction for dX, dU, dV + float maxInvCorr[3]{10.f, 10.f, 10.f}; ///< max inverse correction for dX, dU, dV + float minInvCorr[3]{-10.f, -10.f, -10.f}; ///< min inverse correction for dX, dU, dV RowActiveArea activeArea; + + void resetMaxValues() + { + maxCorr[0] = 1.f; + minCorr[0] = -1.f; + maxCorr[1] = 1.f; + minCorr[1] = -1.f; + maxCorr[2] = 1.f; + minCorr[2] = -1.f; + } + + void updateMaxValues(float dx, float du, float dv) + { + maxCorr[0] = GPUCommonMath::Max(maxCorr[0], dx); + minCorr[0] = GPUCommonMath::Min(minCorr[0], dx); + + maxCorr[1] = GPUCommonMath::Max(maxCorr[1], du); + minCorr[1] = GPUCommonMath::Min(minCorr[1], du); + + maxCorr[2] = GPUCommonMath::Max(maxCorr[2], dv); + minCorr[2] = GPUCommonMath::Min(minCorr[2], dv); + } + + void resetMaxValuesInv() + { + maxInvCorr[0] = 1.f; + minInvCorr[0] = -1.f; + maxInvCorr[1] = 1.f; + minInvCorr[1] = -1.f; + maxInvCorr[2] = 1.f; + minInvCorr[2] = -1.f; + } + + void updateMaxValuesInv(float dx, float du, float dv) + { + maxInvCorr[0] = GPUCommonMath::Max(maxInvCorr[0], dx); + minInvCorr[0] = GPUCommonMath::Min(minInvCorr[0], dx); + + maxInvCorr[1] = GPUCommonMath::Max(maxInvCorr[1], du); + minInvCorr[1] = GPUCommonMath::Min(minInvCorr[1], du); + + maxInvCorr[2] = GPUCommonMath::Max(maxInvCorr[2], dv); + minInvCorr[2] = GPUCommonMath::Min(minInvCorr[2], dv); + } + ClassDefNV(SliceRowInfo, 2); }; @@ -397,12 +446,10 @@ GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrection(int32_t slice, int32 convUVtoGrid(slice, row, u, v, gridU, gridV); float dxuv[3]; spline.interpolateU(splineData, gridU, gridV, dxuv); - if (CAMath::Abs(dxuv[0]) > 100 || CAMath::Abs(dxuv[1]) > 100 || CAMath::Abs(dxuv[2]) > 100) { - dxuv[0] = dxuv[1] = dxuv[2] = 0; - } - dx = dxuv[0]; - du = dxuv[1]; - dv = dxuv[2]; + const auto& info = getSliceRowInfo(slice, row); + dx = GPUCommonMath::Max(info.minCorr[0], GPUCommonMath::Min(info.maxCorr[0], dxuv[0])); + du = GPUCommonMath::Max(info.minCorr[1], GPUCommonMath::Min(info.maxCorr[1], dxuv[1])); + dv = GPUCommonMath::Max(info.minCorr[2], GPUCommonMath::Min(info.maxCorr[2], dxuv[2])); return 0; } @@ -414,12 +461,10 @@ GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrectionOld(int32_t slice, in convUVtoGrid(slice, row, u, v, gridU, gridV); float dxuv[3]; spline.interpolateUold(splineData, gridU, gridV, dxuv); - if (CAMath::Abs(dxuv[0]) > 100 || CAMath::Abs(dxuv[1]) > 100 || CAMath::Abs(dxuv[2]) > 100) { - dxuv[0] = dxuv[1] = dxuv[2] = 0; - } - dx = dxuv[0]; - du = dxuv[1]; - dv = dxuv[2]; + const auto& info = getSliceRowInfo(slice, row); + dx = GPUCommonMath::Max(info.minCorr[0], GPUCommonMath::Min(info.maxCorr[0], dxuv[0])); + du = GPUCommonMath::Max(info.minCorr[1], GPUCommonMath::Min(info.maxCorr[1], dxuv[1])); + dv = GPUCommonMath::Max(info.minCorr[2], GPUCommonMath::Min(info.maxCorr[2], dxuv[2])); return 0; } @@ -433,9 +478,8 @@ GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvCorrectedX( const float* splineData = getSplineData(slice, row, 1); float dx = 0; spline.interpolateU(splineData, gridU, gridV, &dx); - if (CAMath::Abs(dx) > 100) { - dx = 0; - } + const auto& info = getSliceRowInfo(slice, row); + dx = GPUCommonMath::Max(info.minInvCorr[0], GPUCommonMath::Min(info.maxInvCorr[0], dx)); x = mGeo.getRowInfo(row).x + dx; } @@ -450,9 +494,9 @@ GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvUV( float duv[2]; spline.interpolateU(splineData, gridU, gridV, duv); - if (CAMath::Abs(duv[0]) > 100 || CAMath::Abs(duv[1]) > 100) { - duv[0] = duv[1] = 0; - } + const auto& info = getSliceRowInfo(slice, row); + duv[0] = GPUCommonMath::Max(info.minInvCorr[1], GPUCommonMath::Min(info.maxInvCorr[1], duv[0])); + duv[1] = GPUCommonMath::Max(info.minInvCorr[2], GPUCommonMath::Min(info.maxInvCorr[2], duv[1])); nomU = corrU - duv[0]; nomV = corrV - duv[1]; } diff --git a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C index bf3e14d552715..6134f33bcc423 100644 --- a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C +++ b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C @@ -21,8 +21,6 @@ /// root -l TPCFastTransformInit.C'("debugVoxRes.root")' /// -#include "Algorithm/RangeTokenizer.h" - #if !defined(__CLING__) || defined(__ROOTCLING__) #include @@ -41,6 +39,8 @@ #include "TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h" #endif +#include "Algorithm/RangeTokenizer.h" + using namespace o2::tpc; using namespace o2::gpu; @@ -99,8 +99,9 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", trackResiduals.setZ2XBinning(z2xBins); trackResiduals.init(); - { - std::cout << "input track residuals: " << std::endl; + { // debug output + + std::cout << " ===== input track residuals ==== " << std::endl; std::cout << "voxel tree y2xBins: " << y2xBins.size() << std::endl; for (auto y2x : y2xBins) { @@ -127,6 +128,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", for (int i = 0; i < nZ2Xbins; i++) { std::cout << "getZ2X(bin) : " << trackResiduals.getZ2X(i) << std::endl; } + std::cout << " ==================================== " << std::endl; } std::cout << "create fast transformation ... " << std::endl; @@ -310,6 +312,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", geo.convUVtoLocal(iRoc, u + cu, v + cv, cy, cz); cy -= y; cz -= z; + double d[3] = {cx - correctionX, cy - correctionY, cz - correctionZ}; if (voxEntries >= 1.) { for (int i = 0; i < 3; i++) { @@ -317,8 +320,8 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", maxDiff[i] = d[i]; maxDiffRoc[i] = iRoc; maxDiffRow[i] = iRow; - std::cout << " roc " << iRoc << " row " << iRow << " xyz " << i - << " diff " << d[i] << " entries " << voxEntries << " y " << y2xBin << " z " << z2xBin << std::endl; + // std::cout << " roc " << iRoc << " row " << iRow << " xyz " << i + // << " diff " << d[i] << " entries " << voxEntries << " y " << y2xBin << " z " << z2xBin << std::endl; } sumDiff[i] += d[i] * d[i]; } From 552c46fb3fc56278ae4396724567828e6cf740f6 Mon Sep 17 00:00:00 2001 From: sgorbunov Date: Fri, 5 Jul 2024 00:09:05 +0200 Subject: [PATCH 083/285] TPC Splines: disable smoothing --- .../TPCFastSpaceChargeCorrectionHelper.cxx | 106 ++++++++++-------- 1 file changed, 61 insertions(+), 45 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index e71340a555227..acaf9c474e275 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -411,10 +411,14 @@ std::unique_ptr TPCFastSpaceChargeCorrect int nY2Xbins = trackResiduals.getNY2XBins(); int nZ2Xbins = trackResiduals.getNZ2XBins(); + double marginY2X = trackResiduals.getY2X(0, 2) - trackResiduals.getY2X(0, 0); + double marginZ2X = trackResiduals.getZ2X(1) - trackResiduals.getZ2X(0); + std::vector yBinsInt; { std::vector yBins; - yBins.reserve(nY2Xbins); + yBins.reserve(nY2Xbins + 2); + yBins.push_back(trackResiduals.getY2X(0, 0) - marginY2X); for (int i = 0, j = nY2Xbins - 1; i <= j; i += 2, j -= 2) { if (i == j) { yBins.push_back(trackResiduals.getY2X(0, i)); @@ -425,6 +429,8 @@ std::unique_ptr TPCFastSpaceChargeCorrect yBins.push_back(trackResiduals.getY2X(0, j)); } } + yBins.push_back(trackResiduals.getY2X(0, nY2Xbins - 1) + marginY2X); + std::sort(yBins.begin(), yBins.end()); double dy = yBins[1] - yBins[0]; for (int i = 1; i < yBins.size(); i++) { @@ -452,10 +458,13 @@ std::unique_ptr TPCFastSpaceChargeCorrect std::vector zBinsInt; { std::vector zBins; - zBins.reserve(nZ2Xbins); + zBins.reserve(nZ2Xbins + 2); + zBins.push_back(-(trackResiduals.getZ2X(0) - marginZ2X)); for (int i = 0; i < nZ2Xbins; i += 2) { zBins.push_back(-trackResiduals.getZ2X(i)); } + zBins.push_back(-(trackResiduals.getZ2X(nZ2Xbins - 1) + 2. * marginZ2X)); + std::sort(zBins.begin(), zBins.end()); double dz = zBins[1] - zBins[0]; for (int i = 1; i < zBins.size(); i++) { @@ -465,7 +474,7 @@ std::unique_ptr TPCFastSpaceChargeCorrect } zBinsInt.reserve(zBins.size()); // spline knots must be positioned on the grid with an integer internal coordinate - // lets copy the knot positions with the accuracy of 0.1*dz + // lets copy the knot positions with the accuracy of 0.01*dz dz = dz / 10.; double z0 = zBins[0]; double z1 = zBins[zBins.size() - 1]; @@ -525,10 +534,10 @@ std::unique_ptr TPCFastSpaceChargeCorrect const auto& rowInfo = geo.getRowInfo(iRow); auto& info = correction.getSliceRowInfo(iRoc, iRow); const auto& spline = correction.getSpline(iRoc, iRow); - double yMin = rowInfo.x * trackResiduals.getY2X(iRow, 0); - double yMax = rowInfo.x * trackResiduals.getY2X(iRow, trackResiduals.getNY2XBins() - 1); - double zMin = rowInfo.x * trackResiduals.getZ2X(0); - double zMax = rowInfo.x * trackResiduals.getZ2X(trackResiduals.getNZ2XBins() - 1); + double yMin = rowInfo.x * (trackResiduals.getY2X(iRow, 0) - marginY2X); + double yMax = rowInfo.x * (trackResiduals.getY2X(iRow, trackResiduals.getNY2XBins() - 1) + marginY2X); + double zMin = rowInfo.x * (trackResiduals.getZ2X(0) - marginZ2X); + double zMax = rowInfo.x * (trackResiduals.getZ2X(trackResiduals.getNZ2XBins() - 1) + 2. * marginZ2X); double uMin = yMin; double uMax = yMax; double vMin = geo.getTPCzLength(iRoc) - zMax; @@ -585,6 +594,12 @@ std::unique_ptr TPCFastSpaceChargeCorrect vox.mCx = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResX] : v->D[o2::tpc::TrackResiduals::ResX]; vox.mCy = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResY] : v->D[o2::tpc::TrackResiduals::ResY]; vox.mCz = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResZ] : v->D[o2::tpc::TrackResiduals::ResZ]; + if (0 && vox.mNentries < 1) { + vox.mCx = 0.; + vox.mCy = 0.; + vox.mCz = 0.; + vox.mNentries = 1; + } } }; processor.Process(myThread); @@ -711,29 +726,27 @@ std::unique_ptr TPCFastSpaceChargeCorrect // feed the row data to the helper - double yMin = 0., yMax = 0.; + double yMin = 0., yMax = 0., zMin = 0.; + + auto& info = correction.getSliceRowInfo(iRoc, iRow); + const auto& spline = correction.getSpline(iRoc, iRow); { - float u, v; - if (iRoc < geo.getNumberOfSlicesA()) { - geo.convScaledUVtoUV(iRoc, iRow, 0., 0., u, v); - } else { - geo.convScaledUVtoUV(iRoc, iRow, 1., 0., u, v); - } - float py, pz; - geo.convUVtoLocal(iRoc, u, v, py, pz); - yMin = py; - } - { - float u, v; + float u0, u1, v0, v1; + correction.convGridToUV(iRoc, iRow, 0., 0., u0, v0); + correction.convGridToUV(iRoc, iRow, + spline.getGridX1().getUmax(), spline.getGridX2().getUmax(), u1, v1); + float y0, y1, z0, z1; + geo.convUVtoLocal(iRoc, u0, v0, y0, z0); + geo.convUVtoLocal(iRoc, u1, v1, y1, z1); if (iRoc < geo.getNumberOfSlicesA()) { - geo.convScaledUVtoUV(iRoc, iRow, 1., 0., u, v); + yMin = y0; + yMax = y1; } else { - geo.convScaledUVtoUV(iRoc, iRow, 0., 0., u, v); + yMin = y1; + yMax = y0; } - float py, pz; - geo.convUVtoLocal(iRoc, u, v, py, pz); - yMax = py; + zMin = z1; } double zEdge = 0.; @@ -759,28 +772,22 @@ std::unique_ptr TPCFastSpaceChargeCorrect double correctionY = data.mCy; double correctionZ = data.mCz; - double yFirst = y - dy / 2.; - double yLast = y + dy / 2.; + double yStep = dy / 2.; + double zStep = dz / 2.; + + double yFirst = y; + double yLast = y; + double zFirst = z; + double zLast = z; if (iy == 0) { // extend value of the first Y bin to the row edge yFirst = yMin; + yStep = (yLast - yFirst) / 2.; } if (iy == nY2Xbins - 1) { // extend value of the last Y bin to the row edge yLast = yMax; - } - - double yStep = (yLast - yFirst) / 2; - - double zFirst = z - dz / 2.; - double zLast = z + dz / 2.; - double zStep = (zLast - zFirst) / 2.; - - if (0) { // no smoothing - yFirst = y; - yLast = y; - zFirst = z; - zLast = z; + yStep = (yLast - yFirst) / 2.; } for (double py = yFirst; py <= yLast + yStep / 2.; py += yStep) { @@ -790,9 +797,19 @@ std::unique_ptr TPCFastSpaceChargeCorrect correctionZ); } + if (iz == 0) { // extend value of the first Z bin to Z=0. + int nZsteps = 2; + for (int is = 0; is < nZsteps; is++) { + double pz = z + (zMin - z) * (is + 1.) / nZsteps; + double s = 1.; //(nZsteps - 1. - is) / nZsteps; + map.addCorrectionPoint(iRoc, iRow, py, pz, s * correctionX, + s * correctionY, s * correctionZ); + } + } + if (iz == nZ2Xbins - 1) { - // extend value of the first Z bin to the readout, linear decrease of all values to 0. - int nZsteps = 3; + // extend value of the last Z bin to the readout, linear decrease of all values to 0. + int nZsteps = 2; for (int is = 0; is < nZsteps; is++) { double pz = z + (zEdge - z) * (is + 1.) / nZsteps; double s = (nZsteps - 1. - is) / nZsteps; @@ -803,9 +820,8 @@ std::unique_ptr TPCFastSpaceChargeCorrect } } // iz } // iy - - } // iRow - }; // myThread + } // iRow + }; // myThread // run n threads From bc7e81e7766c4390137eb847ba5ab3e2d5c92f75 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Thu, 18 Jul 2024 20:27:35 +0200 Subject: [PATCH 084/285] TPC Splines: smooth to linear edges, crop at grid borders, use mean position of residuals --- .../TPCFastSpaceChargeCorrectionHelper.cxx | 302 ++++++++---------- GPU/TPCFastTransformation/Spline1DSpec.h | 20 +- .../TPCFastSpaceChargeCorrection.cxx | 28 +- .../TPCFastSpaceChargeCorrection.h | 31 +- .../macro/TPCFastTransformInit.C | 44 +-- 5 files changed, 207 insertions(+), 218 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index acaf9c474e275..82a23dfa5242a 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -159,8 +159,8 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas pointCorr[3 * i + 0] = dx; pointCorr[3 * i + 1] = du; pointCorr[3 * i + 2] = dv; - info.updateMaxValues(2. * dx, 2. * du, 2. * dv); - info.updateMaxValuesInv(-2. * dx, -2. * du, -2. * dv); + info.updateMaxValues(20. * dx, 20. * du, 20. * dv); + info.updateMaxValuesInv(-20. * dx, -20. * du, -20. * dv); } helper.approximateDataPoints(spline, splineParameters, 0., spline.getGridX1().getUmax(), 0., spline.getGridX2().getUmax(), &pointSU[0], &pointSV[0], &pointCorr[0], nDataPoints); @@ -411,95 +411,69 @@ std::unique_ptr TPCFastSpaceChargeCorrect int nY2Xbins = trackResiduals.getNY2XBins(); int nZ2Xbins = trackResiduals.getNZ2XBins(); - double marginY2X = trackResiduals.getY2X(0, 2) - trackResiduals.getY2X(0, 0); - double marginZ2X = trackResiduals.getZ2X(1) - trackResiduals.getZ2X(0); + std::vector uvBinsDouble[2]; - std::vector yBinsInt; - { - std::vector yBins; - yBins.reserve(nY2Xbins + 2); - yBins.push_back(trackResiduals.getY2X(0, 0) - marginY2X); - for (int i = 0, j = nY2Xbins - 1; i <= j; i += 2, j -= 2) { - if (i == j) { - yBins.push_back(trackResiduals.getY2X(0, i)); - } else if (i + 1 == j) { - yBins.push_back(trackResiduals.getY2X(0, i)); - } else { - yBins.push_back(trackResiduals.getY2X(0, i)); - yBins.push_back(trackResiduals.getY2X(0, j)); - } + uvBinsDouble[0].reserve(nY2Xbins); + uvBinsDouble[1].reserve(nZ2Xbins); + + for (int i = 0, j = nY2Xbins - 1; i <= j; i += 2, j -= 2) { + uvBinsDouble[0].push_back(trackResiduals.getY2X(0, i)); + if (j >= i + 1) { + uvBinsDouble[0].push_back(trackResiduals.getY2X(0, j)); } - yBins.push_back(trackResiduals.getY2X(0, nY2Xbins - 1) + marginY2X); + } - std::sort(yBins.begin(), yBins.end()); - double dy = yBins[1] - yBins[0]; - for (int i = 1; i < yBins.size(); i++) { - if (yBins[i] - yBins[i - 1] < dy) { - dy = yBins[i] - yBins[i - 1]; + for (int i = 0, j = nZ2Xbins - 1; i <= j; i += 2, j -= 2) { + uvBinsDouble[1].push_back(-trackResiduals.getZ2X(i)); + if (j >= i + 1) { + uvBinsDouble[1].push_back(-trackResiduals.getZ2X(j)); + } + } + + std::vector uvBinsInt[2]; + + for (int iuv = 0; iuv < 2; iuv++) { + auto& bins = uvBinsDouble[iuv]; + std::sort(bins.begin(), bins.end()); + + auto& binsInt = uvBinsInt[iuv]; + binsInt.reserve(bins.size()); + + double dy = bins[1] - bins[0]; + for (int i = 2; i < bins.size(); i++) { + double dd = bins[i] - bins[i - 1]; + if (dd < dy) { + dy = dd; } } - yBinsInt.reserve(yBins.size()); // spline knots must be positioned on the grid with integer internal coordinate // take the knot position accuracy of 0.1*dy dy = dy / 10.; - double y0 = yBins[0]; - double y1 = yBins[yBins.size() - 1]; - for (auto& y : yBins) { + double y0 = bins[0]; + double y1 = bins[bins.size() - 1]; + for (auto& y : bins) { y -= y0; int iy = int(y / dy + 0.5); - yBinsInt.push_back(iy); + binsInt.push_back(iy); double yold = y / (y1 - y0) * 2 - 1.; y = iy * dy; y = y / (y1 - y0) * 2 - 1.; - LOG(info) << "convert y bin: " << yold << " -> " << y << " -> " << iy; - } - } - - std::vector zBinsInt; - { - std::vector zBins; - zBins.reserve(nZ2Xbins + 2); - zBins.push_back(-(trackResiduals.getZ2X(0) - marginZ2X)); - for (int i = 0; i < nZ2Xbins; i += 2) { - zBins.push_back(-trackResiduals.getZ2X(i)); - } - zBins.push_back(-(trackResiduals.getZ2X(nZ2Xbins - 1) + 2. * marginZ2X)); - - std::sort(zBins.begin(), zBins.end()); - double dz = zBins[1] - zBins[0]; - for (int i = 1; i < zBins.size(); i++) { - if (zBins[i] - zBins[i - 1] < dz) { - dz = zBins[i] - zBins[i - 1]; + if (iuv == 0) { + LOG(info) << "convert y bin: " << yold << " -> " << y << " -> " << iy; + } else { + LOG(info) << "convert z bin: " << yold << " -> " << y << " -> " << iy; } } - zBinsInt.reserve(zBins.size()); - // spline knots must be positioned on the grid with an integer internal coordinate - // lets copy the knot positions with the accuracy of 0.01*dz - dz = dz / 10.; - double z0 = zBins[0]; - double z1 = zBins[zBins.size() - 1]; - for (auto& z : zBins) { - z -= z0; - int iz = int(z / dz + 0.5); - zBinsInt.push_back(iz); - double zold = z / (z1 - z0); - z = iz * dz; - z = z / (z1 - z0); - LOG(info) << "convert z bin: " << zold << " -> " << z << " -> " << iz; - } - } - if (yBinsInt.size() < 2) { - yBinsInt.clear(); - yBinsInt.push_back(0); - yBinsInt.push_back(1); + if (binsInt.size() < 2) { + binsInt.clear(); + binsInt.push_back(0); + binsInt.push_back(1); + } } - if (zBinsInt.size() < 2) { - zBinsInt.clear(); - zBinsInt.push_back(0); - zBinsInt.push_back(1); - } + auto& yBinsInt = uvBinsInt[0]; + auto& zBinsInt = uvBinsInt[1]; int nKnotsY = yBinsInt.size(); int nKnotsZ = zBinsInt.size(); @@ -534,10 +508,10 @@ std::unique_ptr TPCFastSpaceChargeCorrect const auto& rowInfo = geo.getRowInfo(iRow); auto& info = correction.getSliceRowInfo(iRoc, iRow); const auto& spline = correction.getSpline(iRoc, iRow); - double yMin = rowInfo.x * (trackResiduals.getY2X(iRow, 0) - marginY2X); - double yMax = rowInfo.x * (trackResiduals.getY2X(iRow, trackResiduals.getNY2XBins() - 1) + marginY2X); - double zMin = rowInfo.x * (trackResiduals.getZ2X(0) - marginZ2X); - double zMax = rowInfo.x * (trackResiduals.getZ2X(trackResiduals.getNZ2XBins() - 1) + 2. * marginZ2X); + double yMin = rowInfo.x * trackResiduals.getY2X(iRow, 0); + double yMax = rowInfo.x * trackResiduals.getY2X(iRow, trackResiduals.getNY2XBins() - 1); + double zMin = rowInfo.x * trackResiduals.getZ2X(0); + double zMax = rowInfo.x * trackResiduals.getZ2X(trackResiduals.getNZ2XBins() - 1); double uMin = yMin; double uMax = yMax; double vMin = geo.getTPCzLength(iRoc) - zMax; @@ -563,6 +537,7 @@ std::unique_ptr TPCFastSpaceChargeCorrect struct VoxelData { int mNentries{0}; // number of entries + float mX, mY, mZ; // mean position in the local coordinates float mCx, mCy, mCz; // corrections to the local coordinates }; @@ -589,16 +564,19 @@ std::unique_ptr TPCFastSpaceChargeCorrect } int iy = v->bvox[o2::tpc::TrackResiduals::VoxF]; // bin number in y/x 0..14 int iz = v->bvox[o2::tpc::TrackResiduals::VoxZ]; // bin number in z/x 0..4 - auto& vox = vRocData[iRoc * nRows + iRow][iy * nZ2Xbins + iz]; - vox.mNentries = (int)v->stat[o2::tpc::TrackResiduals::VoxV]; - vox.mCx = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResX] : v->D[o2::tpc::TrackResiduals::ResX]; - vox.mCy = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResY] : v->D[o2::tpc::TrackResiduals::ResY]; - vox.mCz = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResZ] : v->D[o2::tpc::TrackResiduals::ResZ]; - if (0 && vox.mNentries < 1) { - vox.mCx = 0.; - vox.mCy = 0.; - vox.mCz = 0.; - vox.mNentries = 1; + auto& data = vRocData[iRoc * nRows + iRow][iy * nZ2Xbins + iz]; + data.mNentries = (int)v->stat[o2::tpc::TrackResiduals::VoxV]; + data.mX = v->stat[o2::tpc::TrackResiduals::VoxX]; + data.mY = v->stat[o2::tpc::TrackResiduals::VoxF]; + data.mZ = v->stat[o2::tpc::TrackResiduals::VoxZ]; + data.mCx = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResX] : v->D[o2::tpc::TrackResiduals::ResX]; + data.mCy = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResY] : v->D[o2::tpc::TrackResiduals::ResY]; + data.mCz = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResZ] : v->D[o2::tpc::TrackResiduals::ResZ]; + if (0 && data.mNentries < 1) { + data.mCx = 0.; + data.mCy = 0.; + data.mCz = 0.; + data.mNentries = 1; } } }; @@ -642,10 +620,27 @@ std::unique_ptr TPCFastSpaceChargeCorrect if (iRoc >= geo.getNumberOfSlicesA()) { vox.mZ = -vox.mZ; } + data.mY *= x; + data.mZ *= x; + /* + if ( fabs(x - data.mX) > 0.01 || fabs(vox.mY - data.mY) > 5. || fabs(vox.mZ - data.mZ) > 5.) { + std::cout + << " roc " << iRoc << " row " << iRow + << " voxel x " << x << " y " << vox.mY << " z " << vox.mZ + << " data x " << data.mX << " y " << data.mY << " z " << data.mZ + << std::endl; + } + */ + if (1) { // always use voxel center instead of the mean position + data.mY = vox.mY; + data.mZ = vox.mZ; + } if (data.mNentries < 1) { // no data data.mCx = 0.; data.mCy = 0.; data.mCz = 0.; + data.mY = vox.mY; + data.mZ = vox.mZ; vox.mSmoothingStep = 100; } else { // voxel contains data if (invertSigns) { @@ -726,102 +721,59 @@ std::unique_ptr TPCFastSpaceChargeCorrect // feed the row data to the helper - double yMin = 0., yMax = 0., zMin = 0.; - auto& info = correction.getSliceRowInfo(iRoc, iRow); const auto& spline = correction.getSpline(iRoc, iRow); - { - float u0, u1, v0, v1; - correction.convGridToUV(iRoc, iRow, 0., 0., u0, v0); - correction.convGridToUV(iRoc, iRow, - spline.getGridX1().getUmax(), spline.getGridX2().getUmax(), u1, v1); - float y0, y1, z0, z1; - geo.convUVtoLocal(iRoc, u0, v0, y0, z0); - geo.convUVtoLocal(iRoc, u1, v1, y1, z1); - if (iRoc < geo.getNumberOfSlicesA()) { - yMin = y0; - yMax = y1; - } else { - yMin = y1; - yMax = y0; + auto addEdge = [&](int iy1, int iz1, int iy2, int iz2, int nSteps) { + auto& data1 = vRocData[iRoc * nRows + iRow][iy1 * nZ2Xbins + iz1]; + auto& vox1 = vRowVoxels[iy1 * nZ2Xbins + iz1]; + auto& data2 = vRocData[iRoc * nRows + iRow][iy2 * nZ2Xbins + iz2]; + auto& vox2 = vRowVoxels[iy2 * nZ2Xbins + iz2]; + if (vox1.mSmoothingStep > 2) { + LOG(fatal) << "empty voxel is not repared: y " << iy1 << " z " << iz1; } - zMin = z1; - } - - double zEdge = 0.; - if (iRoc < geo.getNumberOfSlicesA()) { - zEdge = geo.getTPCzLengthA(); - } else { - zEdge = -geo.getTPCzLengthC(); - } + if (vox2.mSmoothingStep > 2) { + LOG(fatal) << "empty voxel is not repared: y " << iy2 << " z " << iz2; + } + double y1 = vox1.mY; + double z1 = vox1.mZ; + double cx1 = data1.mCx; + double cy1 = data1.mCy; + double cz1 = data1.mCz; + double y2 = vox2.mY; + double z2 = vox2.mZ; + double cx2 = data2.mCx; + double cy2 = data2.mCy; + double cz2 = data2.mCz; + + for (int is = 0; is < nSteps; is++) { + double s2 = is / (double)nSteps; + double s1 = 1. - s2; + double y = s1 * y1 + s2 * y2; + double z = s1 * z1 + s2 * z2; + double cx = s1 * cx1 + s2 * cx2; + double cy = s1 * cy1 + s2 * cy2; + double cz = s1 * cz1 + s2 * cz2; + map.addCorrectionPoint(iRoc, iRow, y, z, cx, cy, cz); + } + }; for (int iy = 0; iy < nY2Xbins; iy++) { - for (int iz = 0; iz < nZ2Xbins; iz++) { - auto& data = vRocData[iRoc * nRows + iRow][iy * nZ2Xbins + iz]; - auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; - if (vox.mSmoothingStep > 2) { - LOG(fatal) << "empty voxel is not repared"; - } - - double y = vox.mY; - double z = vox.mZ; - double dy = vox.mDy; - double dz = vox.mDz; - double correctionX = data.mCx; - double correctionY = data.mCy; - double correctionZ = data.mCz; - - double yStep = dy / 2.; - double zStep = dz / 2.; - - double yFirst = y; - double yLast = y; - double zFirst = z; - double zLast = z; - - if (iy == 0) { // extend value of the first Y bin to the row edge - yFirst = yMin; - yStep = (yLast - yFirst) / 2.; - } - - if (iy == nY2Xbins - 1) { // extend value of the last Y bin to the row edge - yLast = yMax; - yStep = (yLast - yFirst) / 2.; - } - - for (double py = yFirst; py <= yLast + yStep / 2.; py += yStep) { - - for (double pz = zFirst; pz <= zLast + zStep / 2.; pz += zStep) { - map.addCorrectionPoint(iRoc, iRow, py, pz, correctionX, correctionY, - correctionZ); - } + for (int iz = 0; iz < nZ2Xbins - 1; iz++) { + addEdge(iy, iz, iy, iz + 1, 3); + } + addEdge(iy, nZ2Xbins - 1, iy, nZ2Xbins - 1, 1); + } - if (iz == 0) { // extend value of the first Z bin to Z=0. - int nZsteps = 2; - for (int is = 0; is < nZsteps; is++) { - double pz = z + (zMin - z) * (is + 1.) / nZsteps; - double s = 1.; //(nZsteps - 1. - is) / nZsteps; - map.addCorrectionPoint(iRoc, iRow, py, pz, s * correctionX, - s * correctionY, s * correctionZ); - } - } + for (int iz = 0; iz < nZ2Xbins; iz++) { + for (int iy = 0; iy < nY2Xbins - 1; iy++) { + addEdge(iy, iz, iy + 1, iz, 3); + } + addEdge(nY2Xbins - 1, iz, nY2Xbins - 1, iz, 1); + } // iy - if (iz == nZ2Xbins - 1) { - // extend value of the last Z bin to the readout, linear decrease of all values to 0. - int nZsteps = 2; - for (int is = 0; is < nZsteps; is++) { - double pz = z + (zEdge - z) * (is + 1.) / nZsteps; - double s = (nZsteps - 1. - is) / nZsteps; - map.addCorrectionPoint(iRoc, iRow, py, pz, s * correctionX, - s * correctionY, s * correctionZ); - } - } - } - } // iz - } // iy - } // iRow - }; // myThread + } // iRow + }; // myThread // run n threads diff --git a/GPU/TPCFastTransformation/Spline1DSpec.h b/GPU/TPCFastTransformation/Spline1DSpec.h index 6462f291d1136..dc59e77e308a1 100644 --- a/GPU/TPCFastTransformation/Spline1DSpec.h +++ b/GPU/TPCFastTransformation/Spline1DSpec.h @@ -313,6 +313,14 @@ class Spline1DSpec : public Spline1DContainer { const auto nYdimTmp = SplineUtil::getNdim(inpYdim); const auto nYdim = nYdimTmp.get(); + + if (u < (DataT)0) { + u = (DataT)0; + } + if (u > (DataT)TBase::getUmax()) { + u = (DataT)TBase::getUmax(); + } + T uu = T(u - knotL.u); T li = T(knotL.Li); T v = uu * li; // scaled u @@ -337,11 +345,19 @@ class Spline1DSpec : public Spline1DContainer } template - GPUd() static void getUderivatives(const Knot& knotL, DataT u, - T& dSl, T& dDl, T& dSr, T& dDr) + GPUd() void getUderivatives(const Knot& knotL, DataT u, + T& dSl, T& dDl, T& dSr, T& dDr) const { /// Get derivatives of the interpolated value {S(u): 1D -> nYdim} at the segment [knotL, next knotR] /// over the spline values Sl, Sr and the slopes Dl, Dr + + if (u < (DataT)0) { + u = (DataT)0; + } + if (u > (DataT)TBase::getUmax()) { + u = (DataT)TBase::getUmax(); + } + u = u - knotL.u; T v = u * T(knotL.Li); // scaled u T vm1 = v - 1.; diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx index 35c6e43daa43b..eb69983cf87ce 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx @@ -116,11 +116,11 @@ void TPCFastSpaceChargeCorrection::cloneFromObject(const TPCFastSpaceChargeCorre mClassVersion = obj.mClassVersion; - for (int i = 0; i < TPCFastTransformGeo::getMaxNumberOfRows(); i++) { + for (int32_t i = 0; i < TPCFastTransformGeo::getMaxNumberOfRows(); i++) { mRowInfos[i] = obj.mRowInfos[i]; } - for (int i = 0; i < TPCFastTransformGeo::getNumberOfSlices() * TPCFastTransformGeo::getMaxNumberOfRows(); i++) { + for (int32_t i = 0; i < TPCFastTransformGeo::getNumberOfSlices() * TPCFastTransformGeo::getMaxNumberOfRows(); i++) { mSliceRowInfos[i] = obj.mSliceRowInfos[i]; } @@ -141,7 +141,7 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer /// Sets the actual location of the external flat buffer after it has been moved (e.g. to another maschine) struct RowInfoVersion3 { - int splineScenarioID{0}; ///< scenario index (which of Spline2D splines to use) + int32_t splineScenarioID{0}; ///< scenario index (which of Spline2D splines to use) size_t dataOffsetBytes[3]{0}; ///< offset for the spline data withing a TPC slice }; @@ -199,24 +199,24 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer if (mClassVersion == 3) { // copy old-format slicerow data from the buffer to the arrays auto* rowInfosOld = reinterpret_cast(mFlatBufferPtr + rowsOffset); - for (int i = 0; i < mGeo.getNumberOfRows(); i++) { + for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { RowInfoVersion3& infoOld = rowInfosOld[i]; RowInfo& info = mRowInfos[i]; info.splineScenarioID = infoOld.splineScenarioID; - for (int is = 0; is < 3; is++) { + for (int32_t is = 0; is < 3; is++) { info.dataOffsetBytes[is] = infoOld.dataOffsetBytes[is]; } } - for (int is = 0; is < mNumberOfScenarios; is++) { + for (int32_t is = 0; is < mNumberOfScenarios; is++) { auto& spline = mScenarioPtr[is]; spline.setXrange(0., spline.getGridX1().getUmax(), 0., spline.getGridX2().getUmax()); } auto* sliceRowInfosOld = reinterpret_cast(mFlatBufferPtr + sliceRowsOffset); - for (int slice = 0; slice < mGeo.getNumberOfSlices(); slice++) { - for (int row = 0; row < mGeo.getNumberOfRows(); row++) { + for (int32_t slice = 0; slice < mGeo.getNumberOfSlices(); slice++) { + for (int32_t row = 0; row < mGeo.getNumberOfRows(); row++) { SliceRowInfoVersion3& infoOld = sliceRowInfosOld[mGeo.getNumberOfRows() * slice + row]; SliceRowInfo& info = getSliceRowInfo(slice, row); const auto& spline = getSpline(slice, row); @@ -236,7 +236,7 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer info.activeArea.cuMin = infoOld.activeArea.cuMin; info.activeArea.cuMax = infoOld.activeArea.cuMax; info.activeArea.cvMax = infoOld.activeArea.cvMax; - for (int i = 0; i < 5; i++) { + for (int32_t i = 0; i < 5; i++) { info.activeArea.maxDriftLengthCheb[i] = infoOld.activeArea.maxDriftLengthCheb[i]; } } @@ -256,7 +256,7 @@ void TPCFastSpaceChargeCorrection::setFutureBufferAddress(char* futureFlatBuffer char* oldBuffer = mFlatBufferPtr; char* newBuffer = futureFlatBufferPtr; - for (int i = 0; i < mNumberOfScenarios; i++) { + for (int32_t i = 0; i < mNumberOfScenarios; i++) { SplineType& sp = mScenarioPtr[i]; char* newSplineBuf = relocatePointer(oldBuffer, newBuffer, sp.getFlatBufferPtr()); sp.setFutureBufferAddress(newSplineBuf); @@ -278,7 +278,7 @@ void TPCFastSpaceChargeCorrection::print() const LOG(info) << " mSliceDataSizeBytes = " << mSliceDataSizeBytes[0] << " " << mSliceDataSizeBytes[1] << " " << mSliceDataSizeBytes[2]; { LOG(info) << " TPC rows: "; - for (int i = 0; i < mGeo.getNumberOfRows(); i++) { + for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { const RowInfo& r = mRowInfos[i]; LOG(info) << " tpc row " << i << ": splineScenarioID = " << r.splineScenarioID << " dataOffsetBytes = " << r.dataOffsetBytes; } @@ -331,7 +331,7 @@ void TPCFastSpaceChargeCorrection::startConstruction(const TPCFastTransformGeo& assert(mConstructionScenarios != nullptr); - for (int i = 0; i < mGeo.getNumberOfRows(); i++) { + for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { mRowInfos[i].splineScenarioID = -1; } @@ -378,7 +378,7 @@ void TPCFastSpaceChargeCorrection::finishConstruction() assert(mConstructionMask & ConstructionState::InProgress); - for (int i = 0; i < mGeo.getNumberOfRows(); i++) { + for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { assert(mRowInfos[i].splineScenarioID >= 0); } for (int32_t i = 0; i < mNumberOfScenarios; i++) { @@ -404,7 +404,7 @@ void TPCFastSpaceChargeCorrection::finishConstruction() for (int32_t is = 0; is < 3; is++) { sliceDataOffset[is] = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); mSliceDataSizeBytes[is] = 0; - for (int i = 0; i < mGeo.getNumberOfRows(); i++) { + for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { RowInfo& row = mRowInfos[i]; SplineType& spline = mConstructionScenarios[row.splineScenarioID]; row.dataOffsetBytes[is] = alignSize(mSliceDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index 3fdc9b32e640c..e69983fab9175 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -251,7 +251,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject GPUd() float getInterpolationSafetyMargin() const { return fInterpolationSafetyMargin; } /// Gives TPC row info - GPUd() const RowInfo& getRowInfo(int row) const { return mRowInfos[row]; } + GPUd() const RowInfo& getRowInfo(int32_t row) const { return mRowInfos[row]; } /// Gives TPC slice info GPUd() const SliceInfo& getSliceInfo(int32_t slice) const @@ -321,7 +321,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// Class version. It is used to read older versions from disc. /// The default version 3 is the one before this field was introduced. /// The actual version must be set in startConstruction(). - int mClassVersion{3}; + int32_t mClassVersion{3}; RowInfo mRowInfos[TPCFastTransformGeo::getMaxNumberOfRows()]; ///< RowInfo array @@ -447,9 +447,17 @@ GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrection(int32_t slice, int32 float dxuv[3]; spline.interpolateU(splineData, gridU, gridV, dxuv); const auto& info = getSliceRowInfo(slice, row); - dx = GPUCommonMath::Max(info.minCorr[0], GPUCommonMath::Min(info.maxCorr[0], dxuv[0])); - du = GPUCommonMath::Max(info.minCorr[1], GPUCommonMath::Min(info.maxCorr[1], dxuv[1])); - dv = GPUCommonMath::Max(info.minCorr[2], GPUCommonMath::Min(info.maxCorr[2], dxuv[2])); + float s = v / info.gridV0; + if (s < 0.) { + s = 0.; + } + if (s > 1.) { + s = 1.; + } + + dx = GPUCommonMath::Max(info.minCorr[0], GPUCommonMath::Min(info.maxCorr[0], s * dxuv[0])); + du = GPUCommonMath::Max(info.minCorr[1], GPUCommonMath::Min(info.maxCorr[1], s * dxuv[1])); + dv = GPUCommonMath::Max(info.minCorr[2], GPUCommonMath::Min(info.maxCorr[2], s * dxuv[2])); return 0; } @@ -462,9 +470,16 @@ GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrectionOld(int32_t slice, in float dxuv[3]; spline.interpolateUold(splineData, gridU, gridV, dxuv); const auto& info = getSliceRowInfo(slice, row); - dx = GPUCommonMath::Max(info.minCorr[0], GPUCommonMath::Min(info.maxCorr[0], dxuv[0])); - du = GPUCommonMath::Max(info.minCorr[1], GPUCommonMath::Min(info.maxCorr[1], dxuv[1])); - dv = GPUCommonMath::Max(info.minCorr[2], GPUCommonMath::Min(info.maxCorr[2], dxuv[2])); + float s = v / info.gridV0; + if (s < 0.) { + s = 0.; + } + if (s > 1.) { + s = 1.; + } + dx = GPUCommonMath::Max(info.minCorr[0], GPUCommonMath::Min(info.maxCorr[0], s * dxuv[0])); + du = GPUCommonMath::Max(info.minCorr[1], GPUCommonMath::Min(info.maxCorr[1], s * dxuv[1])); + dv = GPUCommonMath::Max(info.minCorr[2], GPUCommonMath::Min(info.maxCorr[2], s * dxuv[2])); return 0; } diff --git a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C index 6134f33bcc423..c4b0680f2edd4 100644 --- a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C +++ b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C @@ -109,10 +109,10 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", } std::cout << std::endl; - int nY2Xbins = trackResiduals.getNY2XBins(); + int32_t nY2Xbins = trackResiduals.getNY2XBins(); std::cout << " TrackResiduals y2x bins: " << nY2Xbins << std::endl; - for (int i = 0; i < nY2Xbins; i++) { + for (int32_t i = 0; i < nY2Xbins; i++) { std::cout << "scaled getY2X(bin) : " << trackResiduals.getY2X(0, i) / trackResiduals.getMaxY2X(0) << std::endl; } @@ -123,9 +123,9 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", } std::cout << std::endl; - int nZ2Xbins = trackResiduals.getNZ2XBins(); + int32_t nZ2Xbins = trackResiduals.getNZ2XBins(); std::cout << " TrackResiduals z2x bins: " << nZ2Xbins << std::endl; - for (int i = 0; i < nZ2Xbins; i++) { + for (int32_t i = 0; i < nZ2Xbins; i++) { std::cout << "getZ2X(bin) : " << trackResiduals.getZ2X(i) << std::endl; } std::cout << " ==================================== " << std::endl; @@ -138,6 +138,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", o2::tpc::TPCFastSpaceChargeCorrectionHelper* corrHelper = o2::tpc::TPCFastSpaceChargeCorrectionHelper::instance(); corrHelper->setNthreadsToMaximum(); + // corrHelper->setNthreads(1); auto corrPtr = corrHelper->createFromTrackResiduals(trackResiduals, voxResTree, useSmoothed, invertSigns); @@ -167,9 +168,9 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const o2::gpu::TPCFastTransformGeo& geo = helper->getGeometry(); - // for (int iRoc = 0; iRoc < geo.getNumberOfSlices(); iRoc++) { - for (int iRoc = 0; iRoc < 1; iRoc++) { - for (int iRow = 0; iRow < geo.getNumberOfRows(); iRow++) { + // for (int32_t iRoc = 0; iRoc < geo.getNumberOfSlices(); iRoc++) { + for (int32_t iRoc = 0; iRoc < 1; iRoc++) { + for (int32_t iRow = 0; iRow < geo.getNumberOfRows(); iRow++) { auto& info = corr.getSliceRowInfo(iRoc, iRow); std::cout << "roc " << iRoc << " row " << iRow << " gridV0 " << info.gridV0 << " gridCorrU0 " << info.gridCorrU0 << " gridCorrV0 " << info.gridCorrV0 @@ -257,8 +258,8 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", branch->SetAddress(&v); branch->SetAutoDelete(kTRUE); - int iRocLast = -1; - int iRowLast = -1; + int32_t iRocLast = -1; + int32_t iRowLast = -1; for (int32_t iVox = 0; iVox < voxResTree->GetEntriesFast(); iVox++) { @@ -306,6 +307,11 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", correctionZ *= -1.; } + if (voxEntries > 0.) { // use mean statistical positions instead of the bin centers: + y = x * v->stat[o2::tpc::TrackResiduals::VoxF]; + z = x * v->stat[o2::tpc::TrackResiduals::VoxZ]; + } + float u, v, cx, cu, cv, cy, cz; geo.convLocalToUV(iRoc, y, z, u, v); corr.getCorrection(iRoc, iRow, u, v, cx, cu, cv); @@ -315,7 +321,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", double d[3] = {cx - correctionX, cy - correctionY, cz - correctionZ}; if (voxEntries >= 1.) { - for (int i = 0; i < 3; i++) { + for (int32_t i = 0; i < 3; i++) { if (fabs(maxDiff[i]) < fabs(d[i])) { maxDiff[i] = d[i]; maxDiffRoc[i] = iRoc; @@ -358,7 +364,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", std::vector p[2], g[2]; p[0].push_back(geo.getRowInfo(iRow).getUmin()); - for (int iu = 0; iu < gridU.getNumberOfKnots(); iu++) { + for (int32_t iu = 0; iu < gridU.getNumberOfKnots(); iu++) { float u, v; corr.convGridToUV(iRoc, iRow, gridU.getKnot(iu).getU(), 0., u, v); g[0].push_back(u); @@ -367,7 +373,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", p[0].push_back(geo.getRowInfo(iRow).getUmax()); p[1].push_back(0.); - for (int iv = 0; iv < gridV.getNumberOfKnots(); iv++) { + for (int32_t iv = 0; iv < gridV.getNumberOfKnots(); iv++) { float u, v; corr.convGridToUV(iRoc, iRow, 0., gridV.getKnot(iv).getU(), u, v); g[1].push_back(v); @@ -375,22 +381,22 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", } p[1].push_back(geo.getTPCzLength(iRoc)); - for (int iuv = 0; iuv < 2; iuv++) { - int n = p[iuv].size(); - for (unsigned int i = 0; i < n - 1; i++) { + for (int32_t iuv = 0; iuv < 2; iuv++) { + int32_t n = p[iuv].size(); + for (int32_t i = 0; i < n - 1; i++) { double d = (p[iuv][i + 1] - p[iuv][i]) / 10.; - for (int ii = 1; ii < 10; ii++) { + for (int32_t ii = 1; ii < 10; ii++) { p[iuv].push_back(p[iuv][i] + d * ii); } } std::sort(p[iuv].begin(), p[iuv].end()); } - for (int iter = 0; iter < 2; iter++) { + for (int32_t iter = 0; iter < 2; iter++) { std::vector& pu = ((iter == 0) ? g[0] : p[0]); std::vector& pv = ((iter == 0) ? g[1] : p[1]); - for (unsigned int iu = 0; iu < pu.size(); iu++) { - for (unsigned int iv = 0; iv < pv.size(); iv++) { + for (uint32_t iu = 0; iu < pu.size(); iu++) { + for (uint32_t iv = 0; iv < pv.size(); iv++) { float u = pu[iu]; float v = pv[iv]; float x, y, z; From d15629fbfa3a511c9553cf08d03963a7c6b16a0f Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Thu, 16 Jan 2025 21:06:18 +0000 Subject: [PATCH 085/285] TPC Splines: fix the inverse correction --- .../TPCFastSpaceChargeCorrectionHelper.cxx | 100 ++++++++++-------- .../TPCFastSpaceChargeCorrection.cxx | 70 +++++++----- .../TPCFastSpaceChargeCorrection.h | 45 ++++---- .../macro/TPCFastTransformInit.C | 88 +++++++++------ 4 files changed, 179 insertions(+), 124 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index 82a23dfa5242a..3696df5343ad3 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -146,7 +146,6 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas int nDataPoints = data.size(); auto& info = correction.getSliceRowInfo(slice, row); info.resetMaxValues(); - info.resetMaxValuesInv(); if (nDataPoints >= 4) { std::vector pointSU(nDataPoints); std::vector pointSV(nDataPoints); @@ -160,7 +159,6 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas pointCorr[3 * i + 1] = du; pointCorr[3 * i + 2] = dv; info.updateMaxValues(20. * dx, 20. * du, 20. * dv); - info.updateMaxValuesInv(-20. * dx, -20. * du, -20. * dv); } helper.approximateDataPoints(spline, splineParameters, 0., spline.getGridX1().getUmax(), 0., spline.getGridX2().getUmax(), &pointSU[0], &pointSV[0], &pointCorr[0], nDataPoints); @@ -908,46 +906,60 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector helper; std::vector splineParameters; - ChebyshevFit1D chebFitterX, chebFitterU, chebFitterV; for (int row = iThread; row < mGeo.getNumberOfRows(); row += mNthreads) { TPCFastSpaceChargeCorrection::SplineType spline = correction.getSpline(slice, row); helper.setSpline(spline, 10, 10); - std::vector dataPointCU, dataPointCV, dataPointF; - - float u0, u1, v0, v1; - mGeo.convScaledUVtoUV(slice, row, 0., 0., u0, v0); - mGeo.convScaledUVtoUV(slice, row, 1., 1., u1, v1); double x = mGeo.getRowInfo(row).x; - int nPointsU = (spline.getGridX1().getNumberOfKnots() - 1) * 10; - int nPointsV = (spline.getGridX2().getNumberOfKnots() - 1) * 10; - - double stepU = (u1 - u0) / (nPointsU - 1); - double stepV = (v1 - v0) / (nPointsV - 1); + auto& sliceRowInfo = correction.getSliceRowInfo(slice, row); - if (prn) { - LOG(info) << "u0 " << u0 << " u1 " << u1 << " v0 " << v0 << " v1 " << v1; + std::vector gridU; + { + const auto& grid = spline.getGridX1(); + for (int i = 0; i < grid.getNumberOfKnots(); i++) { + if (i == grid.getNumberOfKnots() - 1) { + gridU.push_back(grid.getKnot(i).u); + break; + } + for (double s = 1.; s > 0.; s -= 0.1) { + gridU.push_back(s * grid.getKnot(i).u + (1. - s) * grid.getKnot(i + 1).u); + } + } + } + std::vector gridV; + { + const auto& grid = spline.getGridX2(); + for (int i = 0; i < grid.getNumberOfKnots(); i++) { + if (i == grid.getNumberOfKnots() - 1) { + gridV.push_back(grid.getKnot(i).u); + break; + } + for (double s = 1.; s > 0.; s -= 0.1) { + gridV.push_back(s * grid.getKnot(i).u + (1. - s) * grid.getKnot(i + 1).u); + } + } } - TPCFastSpaceChargeCorrection::RowActiveArea& area = correction.getSliceRowInfo(slice, row).activeArea; + + std::vector dataPointCU, dataPointCV, dataPointF; + dataPointCU.reserve(gridU.size() * gridV.size()); + dataPointCV.reserve(gridU.size() * gridV.size()); + dataPointF.reserve(gridU.size() * gridV.size()); + + TPCFastSpaceChargeCorrection::RowActiveArea& area = sliceRowInfo.activeArea; area.cuMin = 1.e10; area.cuMax = -1.e10; + double cvMin = 1.e10; - /* - v1 = area.vMax; - stepV = (v1 - v0) / (nPointsU - 1); - if (stepV < 1.f) { - stepV = 1.f; - } - */ + for (int iu = 0; iu < gridU.size(); iu++) { + for (int iv = 0; iv < gridV.size(); iv++) { + float u, v; + correction.convGridToUV(slice, row, gridU[iu], gridV[iv], u, v); - for (double u = u0; u < u1 + stepU; u += stepU) { - for (double v = v0; v < v1 + stepV; v += stepV) { float dx, du, dv; correction.getCorrection(slice, row, u, v, dx, du, dv); dx *= scaling[0]; @@ -976,39 +988,41 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector +#include #include #include "Spline2DHelper.h" #endif @@ -514,15 +515,41 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) tpcR2max = tpcR2max / cos(2 * M_PI / mGeo.getNumberOfSlicesA() / 2) + 1.; tpcR2max = tpcR2max * tpcR2max; - double maxDtpc[3] = {0, 0, 0}; - double maxD = 0; + struct MaxValue { + double V{0.}; + int Roc{-1}; + int Row{-1}; + + void update(double v, int roc, int row) + { + if (fabs(v) > fabs(V)) { + V = v; + Roc = roc; + Row = row; + } + } + void update(const MaxValue& other) + { + update(other.V, other.Roc, other.Row); + } + + std::string toString() + { + std::stringstream ss; + ss << V << "(" << Roc << "," << Row << ")"; + return ss.str(); + } + }; + + MaxValue maxDtpc[3]; + MaxValue maxD; for (int32_t slice = 0; slice < mGeo.getNumberOfSlices(); slice++) { if (prn) { LOG(info) << "check inverse transform for slice " << slice; } - double vLength = (slice < mGeo.getNumberOfSlicesA()) ? mGeo.getTPCzLengthA() : mGeo.getTPCzLengthC(); - double maxDslice[3] = {0, 0, 0}; + double vLength = mGeo.getTPCzLength(slice); + MaxValue maxDslice[3]; for (int32_t row = 0; row < mGeo.getNumberOfRows(); row++) { float u0, u1, v0, v1; mGeo.convScaledUVtoUV(slice, row, 0., 0., u0, v0); @@ -530,9 +557,12 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) double x = mGeo.getRowInfo(row).x; double stepU = (u1 - u0) / 100.; double stepV = (v1 - v0) / 100.; - double maxDrow[3] = {0, 0, 0}; + MaxValue maxDrow[3]; for (double u = u0; u < u1; u += stepU) { for (double v = v0; v < v1; v += stepV) { + if (v < getSliceRowInfo(slice, row).gridV0) { + continue; + } float dx, du, dv; getCorrection(slice, row, u, v, dx, du, dv); double cx = x + dx; @@ -545,11 +575,9 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) float nx, nu, nv; getCorrectionInvCorrectedX(slice, row, cu, cv, nx); getCorrectionInvUV(slice, row, cu, cv, nu, nv); - double d[3] = {nx - cx, nu - u, nv - v}; + double d[3] = {(cx - nx) - dx, (cu - nu) - du, (cv - nv) - dv}; for (int32_t i = 0; i < 3; i++) { - if (fabs(d[i]) > fabs(maxDrow[i])) { - maxDrow[i] = d[i]; - } + maxDrow[i].update(d[i], slice, row); } if (0 && prn && fabs(d[0]) + fabs(d[1]) + fabs(d[2]) > 0.1) { @@ -560,32 +588,26 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) } } } - if (0 && prn) { + if (1 && prn) { LOG(info) << "slice " << slice << " row " << row - << " dx " << maxDrow[0] << " du " << maxDrow[1] << " dv " << maxDrow[2]; + << " dx " << maxDrow[0].V << " du " << maxDrow[1].V << " dv " << maxDrow[2].V; } for (int32_t i = 0; i < 3; i++) { - if (fabs(maxDslice[i]) < fabs(maxDrow[i])) { - maxDslice[i] = maxDrow[i]; - } - if (fabs(maxDtpc[i]) < fabs(maxDrow[i])) { - maxDtpc[i] = maxDrow[i]; - } - if (fabs(maxD) < fabs(maxDrow[i])) { - maxD = maxDrow[i]; - } + maxDslice[i].update(maxDrow[i]); + maxDtpc[i].update(maxDrow[i]); + maxD.update(maxDrow[i]); } } if (prn) { - LOG(info) << "inverse correction: slice " << slice - << " dx " << maxDslice[0] << " du " << maxDslice[1] << " dv " << maxDslice[2]; + LOG(info) << "inverse correction: slice " << slice << ". Max deviations: " + << " dx " << maxDslice[0].toString() << " du " << maxDslice[1].toString() << " dv " << maxDslice[2].toString(); } } // slice LOG(info) << "Test inverse TPC correction. max deviations: " - << " dx " << maxDtpc[0] << " du " << maxDtpc[1] << " dv " << maxDtpc[2] << " cm"; + << " dx " << maxDtpc[0].toString() << " du " << maxDtpc[1].toString() << " dv " << maxDtpc[2].toString() << " cm"; - return maxD; + return maxD.V; } #endif // GPUCA_GPUCODE diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index e69983fab9175..2d2940054023e 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -68,8 +68,6 @@ class TPCFastSpaceChargeCorrection : public FlatObject float scaleCorrVtoGrid{0.f}; ///< scale corrected V to V-grid coordinate float maxCorr[3]{10.f, 10.f, 10.f}; ///< max correction for dX, dU, dV float minCorr[3]{-10.f, -10.f, -10.f}; ///< min correction for dX, dU, dV - float maxInvCorr[3]{10.f, 10.f, 10.f}; ///< max inverse correction for dX, dU, dV - float minInvCorr[3]{-10.f, -10.f, -10.f}; ///< min inverse correction for dX, dU, dV RowActiveArea activeArea; void resetMaxValues() @@ -94,28 +92,6 @@ class TPCFastSpaceChargeCorrection : public FlatObject minCorr[2] = GPUCommonMath::Min(minCorr[2], dv); } - void resetMaxValuesInv() - { - maxInvCorr[0] = 1.f; - minInvCorr[0] = -1.f; - maxInvCorr[1] = 1.f; - minInvCorr[1] = -1.f; - maxInvCorr[2] = 1.f; - minInvCorr[2] = -1.f; - } - - void updateMaxValuesInv(float dx, float du, float dv) - { - maxInvCorr[0] = GPUCommonMath::Max(maxInvCorr[0], dx); - minInvCorr[0] = GPUCommonMath::Min(minInvCorr[0], dx); - - maxInvCorr[1] = GPUCommonMath::Max(maxInvCorr[1], du); - minInvCorr[1] = GPUCommonMath::Min(minInvCorr[1], du); - - maxInvCorr[2] = GPUCommonMath::Max(maxInvCorr[2], dv); - minInvCorr[2] = GPUCommonMath::Min(minInvCorr[2], dv); - } - ClassDefNV(SliceRowInfo, 2); }; @@ -494,7 +470,15 @@ GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvCorrectedX( float dx = 0; spline.interpolateU(splineData, gridU, gridV, &dx); const auto& info = getSliceRowInfo(slice, row); - dx = GPUCommonMath::Max(info.minInvCorr[0], GPUCommonMath::Min(info.maxInvCorr[0], dx)); + + float s = corrV / info.gridCorrV0; + if (s < 0.) { + s = 0.; + } + if (s > 1.) { + s = 1.; + } + dx = GPUCommonMath::Clamp(s * dx, info.minCorr[0], info.maxCorr[0]); x = mGeo.getRowInfo(row).x + dx; } @@ -510,8 +494,15 @@ GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvUV( float duv[2]; spline.interpolateU(splineData, gridU, gridV, duv); const auto& info = getSliceRowInfo(slice, row); - duv[0] = GPUCommonMath::Max(info.minInvCorr[1], GPUCommonMath::Min(info.maxInvCorr[1], duv[0])); - duv[1] = GPUCommonMath::Max(info.minInvCorr[2], GPUCommonMath::Min(info.maxInvCorr[2], duv[1])); + float s = corrV / info.gridCorrV0; + if (s < 0.) { + s = 0.; + } + if (s > 1.) { + s = 1.; + } + duv[0] = GPUCommonMath::Clamp(s * duv[0], info.minCorr[1], info.maxCorr[1]); + duv[1] = GPUCommonMath::Clamp(s * duv[1], info.minCorr[2], info.maxCorr[2]); nomU = corrU - duv[0]; nomV = corrV - duv[1]; } diff --git a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C index c4b0680f2edd4..7e889d5a9e7db 100644 --- a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C +++ b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C @@ -202,41 +202,44 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", TFile* debugFile = new TFile("transformDebug.root", "RECREATE"); debugFile->cd(); - // ntuple with created TPC corrections - TNtuple* debugCorr = new TNtuple("corr", "corr", "iRoc:iRow:x:y:z:cx:cy:cz"); + // debug ntuple with created TPC corrections + // + // measured x,y,z; corrections cx,cy,cz from the measured to the real x,y,z; + // inverse corrections ix,iy,iz at the real position (x+cx,y+cy,z+cz) + // ideally, ix = cx, iy = cy, iz = cz + TNtuple* debugCorr = new TNtuple("corr", "corr", "iRoc:iRow:x:y:z:cx:cy:cz:ix:iy:iz"); debugCorr->SetMarkerStyle(8); debugCorr->SetMarkerSize(0.1); debugCorr->SetMarkerColor(kBlack); - // ntuple with the input data: voxel corrections + // ntuple with the input data: voxels and corrections debugFile->cd(); TNtuple* debugVox = - new TNtuple("vox", "vox", "iRoc:iRow:n:x:y:z:vx:vy:vz:cx:cy:cz"); + new TNtuple("vox", "vox", "iRoc:iRow:n:x:y:z:vx:vy:vz"); debugVox->SetMarkerStyle(8); debugVox->SetMarkerSize(0.8); debugVox->SetMarkerColor(kBlue); - // duplicate of debugVox + // duplicate of debugVox + the spline data at voxels in a different color debugFile->cd(); TNtuple* debugCorrVox = - new TNtuple("corrvox", "corrvox", "iRoc:iRow:n:x:y:z:vx:vy:vz:cx:cy:cz"); + new TNtuple("corrvox", "corrvox", "iRoc:iRow:n:x:y:z:vx:vy:vz:cx:cy:cz:ix:iy:iz"); debugCorrVox->SetMarkerStyle(8); debugCorrVox->SetMarkerSize(0.8); debugCorrVox->SetMarkerColor(kMagenta); - // ntuple with spline grid points + // corrections at the spline grid points debugFile->cd(); - TNtuple* debugGrid = new TNtuple("grid", "grid", "iRoc:iRow:x:y:z:cx:cy:cz"); + TNtuple* debugGrid = new TNtuple("grid", "grid", "iRoc:iRow:x:y:z:cx:cy:cz:ix:iy:iz"); debugGrid->SetMarkerStyle(8); debugGrid->SetMarkerSize(1.2); debugGrid->SetMarkerColor(kBlack); - // ntuple with data points created from voxels (with data smearing and - // extension to the edges) + // ntuple with data points created from voxels (with the data smearing, extension to the edges etc.) debugFile->cd(); TNtuple* debugPoints = new TNtuple("points", "points", "iRoc:iRow:x:y:z:px:py:pz:cx:cy:cz"); @@ -253,6 +256,34 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const o2::gpu::TPCFastTransformGeo& geo = helper->getGeometry(); + auto getAllCorrections = [&](int iRoc, int iRow, float u, float v, float& x, float& y, float& z, float& cx, float& cy, float& cz, float& ix, float& iy, float& iz) { + // define x,y,z + + x = geo.getRowInfo(iRow).x; + geo.convUVtoLocal(iRoc, u, v, y, z); + + // get the corrections cx,cy,cz at x,y,z + float cu, cv; + corr.getCorrection(iRoc, iRow, u, v, cx, cu, cv); + geo.convUVtoLocal(iRoc, cu, cv, cy, cz); + + float corrected_u = u + cu; + float corrected_v = v + cv; + float corrected_x = x + cx; + float corrected_y, corrected_z; + geo.convUVtoLocal(iRoc, corrected_u, corrected_v, corrected_y, corrected_z); + + // get the inverse corrections ix,iy,iz at the corrected x,y,z + float inverted_x, inverted_u, inverted_v, inverted_y, inverted_z; + corr.getCorrectionInvCorrectedX(iRoc, iRow, corrected_u, corrected_v, inverted_x); + corr.getCorrectionInvUV(iRoc, iRow, corrected_u, corrected_v, inverted_u, inverted_v); + geo.convUVtoLocal(iRoc, inverted_u, inverted_v, inverted_y, inverted_z); + + ix = corrected_x - inverted_x; + iy = corrected_y - inverted_y; + iz = corrected_z - inverted_z; + }; + o2::tpc::TrackResiduals::VoxRes* v = nullptr; TBranch* branch = voxResTree->GetBranch("voxRes"); branch->SetAddress(&v); @@ -261,6 +292,8 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", int32_t iRocLast = -1; int32_t iRowLast = -1; + std::cout << "fill debug ntuples at voxels ..." << std::endl; + for (int32_t iVox = 0; iVox < voxResTree->GetEntriesFast(); iVox++) { voxResTree->GetEntry(iVox); @@ -312,12 +345,10 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", z = x * v->stat[o2::tpc::TrackResiduals::VoxZ]; } - float u, v, cx, cu, cv, cy, cz; + float u, v; geo.convLocalToUV(iRoc, y, z, u, v); - corr.getCorrection(iRoc, iRow, u, v, cx, cu, cv); - geo.convUVtoLocal(iRoc, u + cu, v + cv, cy, cz); - cy -= y; - cz -= z; + float x1, y1, z1, cx, cy, cz, ix, iy, iz; + getAllCorrections(iRoc, iRow, u, v, x1, y1, z1, cx, cy, cz, ix, iy, iz); double d[3] = {cx - correctionX, cy - correctionY, cz - correctionZ}; if (voxEntries >= 1.) { @@ -334,13 +365,14 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", nDiff++; } - debugVox->Fill(iRoc, iRow, voxEntries, x, y, z, correctionX, correctionY, correctionZ, - cx, cy, cz); + debugVox->Fill(iRoc, iRow, voxEntries, x, y, z, correctionX, correctionY, correctionZ); + debugCorrVox->Fill(iRoc, iRow, voxEntries, x, y, z, correctionX, correctionY, correctionZ, - cx, cy, cz); + cx, cy, cz, ix, iy, iz); } - std::cout << "create debug ntuples ..." << std::endl; + std::cout + << "fill debug ntuples everywhere .." << std::endl; for (int32_t iRoc = 0; iRoc < geo.getNumberOfSlices(); iRoc++) { // for (int32_t iRoc = 0; iRoc < 1; iRoc++) { @@ -399,18 +431,14 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", for (uint32_t iv = 0; iv < pv.size(); iv++) { float u = pu[iu]; float v = pv[iv]; - float x, y, z; - geo.convUVtoLocal(iRoc, u, v, y, z); - float cx, cu, cv; - corr.getCorrection(iRoc, iRow, u, v, cx, cu, cv); - float cy, cz; - geo.convUVtoLocal(iRoc, u + cu, v + cv, cy, cz); - cy -= y; - cz -= z; + + float x, y, z, cx, cy, cz, ix, iy, iz; + getAllCorrections(iRoc, iRow, u, v, x, y, z, cx, cy, cz, ix, iy, iz); + if (iter == 0) { - debugGrid->Fill(iRoc, iRow, x, y, z, cx, cy, cz); + debugGrid->Fill(iRoc, iRow, x, y, z, cx, cy, cz, ix, iy, iz); } else { - debugCorr->Fill(iRoc, iRow, x, y, z, cx, cy, cz); + debugCorr->Fill(iRoc, iRow, x, y, z, cx, cy, cz, ix, iy, iz); } } } @@ -462,7 +490,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", std::cout << "Mean difference in x,y,z : " << sumDiff[0] << " " << sumDiff[1] << " " << sumDiff[2] << std::endl; - corr.testInverse(0); + corr.testInverse(true); debugFile->cd(); debugCorr->Write(); From 2caa885e6231d3aa71291cd5d451c9f9c8ca78f9 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Mon, 27 Jan 2025 17:35:50 +0000 Subject: [PATCH 086/285] TPC Splines: fix reading track residuals --- .../src/TPCFastSpaceChargeCorrectionHelper.cxx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index 3696df5343ad3..c0bba6f4908a8 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -457,9 +457,9 @@ std::unique_ptr TPCFastSpaceChargeCorrect y = iy * dy; y = y / (y1 - y0) * 2 - 1.; if (iuv == 0) { - LOG(info) << "convert y bin: " << yold << " -> " << y << " -> " << iy; + LOG(info) << "TPC SC splines: convert y bin: " << yold << " -> " << y << " -> " << iy; } else { - LOG(info) << "convert z bin: " << yold << " -> " << y << " -> " << iy; + LOG(info) << "TPC SC splines: convert z bin: " << yold << " -> " << y << " -> " << iy; } } @@ -514,11 +514,12 @@ std::unique_ptr TPCFastSpaceChargeCorrect double uMax = yMax; double vMin = geo.getTPCzLength(iRoc) - zMax; double vMax = geo.getTPCzLength(iRoc) - zMin; - // std::cout << " uMin: " << uMin << " uMax: " << yuMax << " zMin: " << vMin << " zMax: " << vMax << std::endl; info.gridU0 = uMin; info.scaleUtoGrid = spline.getGridX1().getUmax() / (uMax - uMin); info.gridV0 = vMin; info.scaleVtoGrid = spline.getGridX2().getUmax() / (vMax - vMin); + // std::cout << " iRoc " << iRoc << " iRow " << iRow << " uMin: " << uMin << " uMax: " << uMax << " vMin: " << vMin << " vMax: " << vMax + //<< " grid scale u "<< info.scaleUtoGrid << " grid scale v "<< info.scaleVtoGrid<< std::endl; } } @@ -629,7 +630,7 @@ std::unique_ptr TPCFastSpaceChargeCorrect << std::endl; } */ - if (1) { // always use voxel center instead of the mean position + if (0) { // debug: always use voxel center instead of the mean position data.mY = vox.mY; data.mZ = vox.mZ; } From d5fc994e621f559e7d0d063c05c0a0048143b378 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Mon, 27 Jan 2025 17:37:35 +0000 Subject: [PATCH 087/285] TPC Splines: fix scaling splines outside of the measured area --- .../TPCFastSpaceChargeCorrection.h | 152 +++++++----------- 1 file changed, 54 insertions(+), 98 deletions(-) diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index 2d2940054023e..7957d36b494c3 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -58,16 +58,16 @@ class TPCFastSpaceChargeCorrection : public FlatObject }; struct SliceRowInfo { - float gridU0{0.f}; //< U coordinate of the U-grid start - float scaleUtoGrid{0.f}; //< scale U to U-grid coordinate - float gridV0{0.f}; ///< V coordinate of the V-grid start - float scaleVtoGrid{0.f}; //< scale V to V-grid coordinate - float gridCorrU0{0.f}; ///< U coordinate of the U-grid start for corrected U - float scaleCorrUtoGrid{0.f}; ///< scale corrected U to U-grid coordinate - float gridCorrV0{0.f}; ///< V coordinate of the V-grid start for corrected V - float scaleCorrVtoGrid{0.f}; ///< scale corrected V to V-grid coordinate - float maxCorr[3]{10.f, 10.f, 10.f}; ///< max correction for dX, dU, dV - float minCorr[3]{-10.f, -10.f, -10.f}; ///< min correction for dX, dU, dV + float gridU0{0.f}; //< U coordinate of the U-grid start + float scaleUtoGrid{0.f}; //< scale U to U-grid coordinate + float gridV0{0.f}; ///< V coordinate of the V-grid start + float scaleVtoGrid{0.f}; //< scale V to V-grid coordinate + float gridCorrU0{0.f}; ///< U coordinate of the U-grid start for corrected U + float scaleCorrUtoGrid{0.f}; ///< scale corrected U to U-grid coordinate + float gridCorrV0{0.f}; ///< V coordinate of the V-grid start for corrected V + float scaleCorrVtoGrid{0.f}; ///< scale corrected V to V-grid coordinate + float maxCorr[3]{10.f, 10.f, 10.f}; ///< max correction for dX, dU, dV + float minCorr[3]{-10.f, -10.f, -10.f}; ///< min correction for dX, dU, dV RowActiveArea activeArea; void resetMaxValues() @@ -199,12 +199,6 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// _______________ Utilities _______________________________________________ - /// shrink u,v coordinats to the TPC row area +/- fkInterpolationSafetyMargin - GPUd() void schrinkUV(int32_t slice, int32_t row, float& u, float& v) const; - - /// shrink corrected u,v coordinats to the TPC row area +/- fkInterpolationSafetyMargin - GPUd() void schrinkCorrectedUV(int32_t slice, int32_t row, float& corrU, float& corrV) const; - /// convert u,v to internal grid coordinates GPUd() void convUVtoGrid(int32_t slice, int32_t row, float u, float v, float& gridU, float& gridV) const; @@ -338,60 +332,9 @@ GPUdi() const float* TPCFastSpaceChargeCorrection::getSplineData(int32_t slice, return reinterpret_cast(mSplineData[iSpline] + mSliceDataSizeBytes[iSpline] * slice + rowInfo.dataOffsetBytes[iSpline]); } -GPUdi() void TPCFastSpaceChargeCorrection::schrinkUV(int32_t slice, int32_t row, float& u, float& v) const -{ - /// shrink u,v coordinats to the TPC row area +/- fInterpolationSafetyMargin - - const TPCFastTransformGeo::RowInfo& rowInfo = mGeo.getRowInfo(row); - - float uWidth05 = rowInfo.getUwidth() * (0.5f + fInterpolationSafetyMargin); - float vWidth = mGeo.getTPCzLength(slice); - - if (u < -uWidth05) { - u = -uWidth05; - } - if (u > uWidth05) { - u = uWidth05; - } - if (v < -0.1f * vWidth) { - v = -0.1f * vWidth; - } - if (v > 1.1f * vWidth) { - v = 1.1f * vWidth; - } -} - -GPUdi() void TPCFastSpaceChargeCorrection::schrinkCorrectedUV(int32_t slice, int32_t row, float& corrU, float& corrV) const -{ - /// shrink corrected u,v coordinats to the TPC row area +/- fInterpolationSafetyMargin - - const TPCFastTransformGeo::RowInfo& rowInfo = mGeo.getRowInfo(row); - const SliceRowInfo& sliceRowInfo = getSliceRowInfo(slice, row); - - float uMargin = fInterpolationSafetyMargin * rowInfo.getUwidth(); - float vMargin = fInterpolationSafetyMargin * mGeo.getTPCzLength(slice); - - if (corrU < sliceRowInfo.activeArea.cuMin - uMargin) { - corrU = sliceRowInfo.activeArea.cuMin - uMargin; - } - - if (corrU > sliceRowInfo.activeArea.cuMax + uMargin) { - corrU = sliceRowInfo.activeArea.cuMax + uMargin; - } - - if (corrV < 0.f - vMargin) { - corrV = 0.f - vMargin; - } - - if (corrV > sliceRowInfo.activeArea.cvMax + vMargin) { - corrV = sliceRowInfo.activeArea.cvMax + vMargin; - } -} - GPUdi() void TPCFastSpaceChargeCorrection::convUVtoGrid(int32_t slice, int32_t row, float u, float v, float& gu, float& gv) const { - schrinkUV(slice, row, u, v); - const SliceRowInfo& info = getSliceRowInfo(slice, row); + const auto& info = getSliceRowInfo(slice, row); gu = (u - info.gridU0) * info.scaleUtoGrid; gv = (v - info.gridV0) * info.scaleVtoGrid; } @@ -406,34 +349,36 @@ GPUdi() void TPCFastSpaceChargeCorrection::convGridToUV(int32_t slice, int32_t r GPUdi() void TPCFastSpaceChargeCorrection::convCorrectedUVtoGrid(int32_t slice, int32_t row, float corrU, float corrV, float& gridU, float& gridV) const { - schrinkCorrectedUV(slice, row, corrU, corrV); - - const SliceRowInfo& sliceRowInfo = getSliceRowInfo(slice, row); - - gridU = (corrU - sliceRowInfo.gridCorrU0) * sliceRowInfo.scaleCorrUtoGrid; - gridV = (corrV - sliceRowInfo.gridCorrV0) * sliceRowInfo.scaleCorrVtoGrid; + const SliceRowInfo& info = getSliceRowInfo(slice, row); + gridU = (corrU - info.gridCorrU0) * info.scaleCorrUtoGrid; + gridV = (corrV - info.gridCorrV0) * info.scaleCorrVtoGrid; } GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrection(int32_t slice, int32_t row, float u, float v, float& dx, float& du, float& dv) const { + const auto& info = getSliceRowInfo(slice, row); const SplineType& spline = getSpline(slice, row); const float* splineData = getSplineData(slice, row); float gridU = 0, gridV = 0; convUVtoGrid(slice, row, u, v, gridU, gridV); + // shrink to the grid area + gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); + gridV = GPUCommonMath::Clamp(gridV, 0.f, (float)spline.getGridX2().getUmax()); + float dxuv[3]; spline.interpolateU(splineData, gridU, gridV, dxuv); - const auto& info = getSliceRowInfo(slice, row); + float s = v / info.gridV0; - if (s < 0.) { - s = 0.; - } - if (s > 1.) { - s = 1.; + + if (v >= info.gridV0) { + s = 1.f; + } else if (v <= 0.f) { + s = 0.f; } - dx = GPUCommonMath::Max(info.minCorr[0], GPUCommonMath::Min(info.maxCorr[0], s * dxuv[0])); - du = GPUCommonMath::Max(info.minCorr[1], GPUCommonMath::Min(info.maxCorr[1], s * dxuv[1])); - dv = GPUCommonMath::Max(info.minCorr[2], GPUCommonMath::Min(info.maxCorr[2], s * dxuv[2])); + dx = GPUCommonMath::Clamp(s * dxuv[0], info.minCorr[0], info.maxCorr[0]); + du = GPUCommonMath::Clamp(s * dxuv[1], info.minCorr[1], info.maxCorr[1]); + dv = GPUCommonMath::Clamp(s * dxuv[2], info.minCorr[2], info.maxCorr[2]); return 0; } @@ -462,22 +407,28 @@ GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrectionOld(int32_t slice, in GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvCorrectedX( int32_t slice, int32_t row, float corrU, float corrV, float& x) const { + const auto& info = getSliceRowInfo(slice, row); + const Spline2D& spline = reinterpret_cast&>(getSpline(slice, row)); + const float* splineData = getSplineData(slice, row, 1); + float gridU, gridV; convCorrectedUVtoGrid(slice, row, corrU, corrV, gridU, gridV); - const Spline2D& spline = reinterpret_cast&>(getSpline(slice, row)); - const float* splineData = getSplineData(slice, row, 1); + // shrink to the grid area + gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); + gridV = GPUCommonMath::Clamp(gridV, 0.f, (float)spline.getGridX2().getUmax()); + float dx = 0; spline.interpolateU(splineData, gridU, gridV, &dx); - const auto& info = getSliceRowInfo(slice, row); float s = corrV / info.gridCorrV0; - if (s < 0.) { - s = 0.; - } - if (s > 1.) { - s = 1.; + + if (corrV >= info.gridCorrV0) { + s = 1.f; + } else if (corrV <= 0.f) { + s = 0.f; } + dx = GPUCommonMath::Clamp(s * dx, info.minCorr[0], info.maxCorr[0]); x = mGeo.getRowInfo(row).x + dx; } @@ -485,22 +436,27 @@ GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvCorrectedX( GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvUV( int32_t slice, int32_t row, float corrU, float corrV, float& nomU, float& nomV) const { + const Spline2D& spline = reinterpret_cast&>(getSpline(slice, row)); + const float* splineData = getSplineData(slice, row, 2); + float gridU, gridV; convCorrectedUVtoGrid(slice, row, corrU, corrV, gridU, gridV); - const Spline2D& spline = reinterpret_cast&>(getSpline(slice, row)); - const float* splineData = getSplineData(slice, row, 2); + // shrink to the grid area + gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); + gridV = GPUCommonMath::Clamp(gridV, 0.f, (float)spline.getGridX2().getUmax()); float duv[2]; spline.interpolateU(splineData, gridU, gridV, duv); const auto& info = getSliceRowInfo(slice, row); float s = corrV / info.gridCorrV0; - if (s < 0.) { - s = 0.; - } - if (s > 1.) { - s = 1.; + + if (corrV >= info.gridCorrV0) { + s = 1.f; + } else if (corrV <= 0.f) { + s = 0.f; } + duv[0] = GPUCommonMath::Clamp(s * duv[0], info.minCorr[1], info.maxCorr[1]); duv[1] = GPUCommonMath::Clamp(s * duv[1], info.minCorr[2], info.maxCorr[2]); nomU = corrU - duv[0]; From 020b243ecb859232223db86d63b1a97ab1d783c5 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Mon, 27 Jan 2025 19:41:36 +0000 Subject: [PATCH 088/285] TPC Splines: rename Slice -> Roc in geometry --- .../TPCFastSpaceChargeCorrectionHelper.cxx | 110 +++---- .../src/TPCFastTransformHelperO2.cxx | 4 +- .../test/testTPCFastTransform.cxx | 30 +- .../TPCFastSpaceChargeCorrection.cxx | 130 ++++---- .../TPCFastSpaceChargeCorrection.h | 146 ++++----- GPU/TPCFastTransformation/TPCFastTransform.h | 278 +++++++++--------- .../TPCFastTransformGeo.cxx | 24 +- .../TPCFastTransformGeo.h | 107 +++---- .../TPCFastTransformManager.cxx | 26 +- .../TPCFastTransformationLinkDef_O2.h | 6 +- .../macro/generateTPCCorrectionNTuple.C | 24 +- 11 files changed, 443 insertions(+), 442 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index c0bba6f4908a8..bac332a837c55 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -134,17 +134,17 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas LOG(info) << "fast space charge correction helper: init from data points"; - for (int slice = 0; slice < correction.getGeometry().getNumberOfSlices(); slice++) { + for (int roc = 0; roc < correction.getGeometry().getNumberOfRocs(); roc++) { auto myThread = [&](int iThread) { for (int row = iThread; row < correction.getGeometry().getNumberOfRows(); row += mNthreads) { - TPCFastSpaceChargeCorrection::SplineType& spline = correction.getSpline(slice, row); + TPCFastSpaceChargeCorrection::SplineType& spline = correction.getSpline(roc, row); Spline2DHelper helper; - float* splineParameters = correction.getSplineData(slice, row); - const std::vector& data = mCorrectionMap.getPoints(slice, row); + float* splineParameters = correction.getSplineData(roc, row); + const std::vector& data = mCorrectionMap.getPoints(roc, row); int nDataPoints = data.size(); - auto& info = correction.getSliceRowInfo(slice, row); + auto& info = correction.getRocRowInfo(roc, row); info.resetMaxValues(); if (nDataPoints >= 4) { std::vector pointSU(nDataPoints); @@ -152,7 +152,7 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas std::vector pointCorr(3 * nDataPoints); // 3 dimensions for (int i = 0; i < nDataPoints; ++i) { double su, sv, dx, du, dv; - getSpaceChargeCorrection(correction, slice, row, data[i], su, sv, dx, du, dv); + getSpaceChargeCorrection(correction, roc, row, data[i], su, sv, dx, du, dv); pointSU[i] = su; pointSV[i] = sv; pointCorr[3 * i + 0] = dx; @@ -182,7 +182,7 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas th.join(); } - } // slice + } // roc watch.Stop(); @@ -191,7 +191,7 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas initInverse(correction, 0); } -void TPCFastSpaceChargeCorrectionHelper::getSpaceChargeCorrection(const TPCFastSpaceChargeCorrection& correction, int slice, int row, o2::gpu::TPCFastSpaceChargeCorrectionMap::CorrectionPoint p, +void TPCFastSpaceChargeCorrectionHelper::getSpaceChargeCorrection(const TPCFastSpaceChargeCorrection& correction, int roc, int row, o2::gpu::TPCFastSpaceChargeCorrectionMap::CorrectionPoint p, double& su, double& sv, double& dx, double& du, double& dv) { // get space charge correction in internal TPCFastTransform coordinates su,sv->dx,du,dv @@ -202,14 +202,14 @@ void TPCFastSpaceChargeCorrectionHelper::getSpaceChargeCorrection(const TPCFastS // not corrected coordinates in u,v float u = 0.f, v = 0.f, fsu = 0.f, fsv = 0.f; - mGeo.convLocalToUV(slice, p.mY, p.mZ, u, v); - correction.convUVtoGrid(slice, row, u, v, fsu, fsv); - // mGeo.convUVtoScaledUV(slice, row, u, v, fsu, fsv); + mGeo.convLocalToUV(roc, p.mY, p.mZ, u, v); + correction.convUVtoGrid(roc, row, u, v, fsu, fsv); + // mGeo.convUVtoScaledUV(roc, row, u, v, fsu, fsv); su = fsu; sv = fsv; // corrected coordinates in u,v float u1 = 0.f, v1 = 0.f; - mGeo.convLocalToUV(slice, p.mY + p.mDy, p.mZ + p.mDz, u1, v1); + mGeo.convLocalToUV(roc, p.mY + p.mDy, p.mZ + p.mDz, u1, v1); dx = p.mDx; du = u1 - u; @@ -286,7 +286,7 @@ std::unique_ptr TPCFastSpaceChargeCorrectionHelper /// set space charge correction in the local coordinates /// as a continious function - int nRocs = mGeo.getNumberOfSlices(); + int nRocs = mGeo.getNumberOfRocs(); int nRows = mGeo.getNumberOfRows(); mCorrectionMap.init(nRocs, nRows); @@ -337,8 +337,8 @@ void TPCFastSpaceChargeCorrectionHelper::testGeometry(const TPCFastTransformGeo& { const Mapper& mapper = Mapper::instance(); - if (geo.getNumberOfSlices() != Sector::MAXSECTOR) { - LOG(fatal) << "Wrong number of sectors :" << geo.getNumberOfSlices() << " instead of " << Sector::MAXSECTOR << std::endl; + if (geo.getNumberOfRocs() != Sector::MAXSECTOR) { + LOG(fatal) << "Wrong number of sectors :" << geo.getNumberOfRocs() << " instead of " << Sector::MAXSECTOR << std::endl; } if (geo.getNumberOfRows() != mapper.getNumberOfRows()) { @@ -404,7 +404,7 @@ std::unique_ptr TPCFastSpaceChargeCorrect const o2::gpu::TPCFastTransformGeo& geo = helper->getGeometry(); o2::gpu::TPCFastSpaceChargeCorrectionMap& map = helper->getCorrectionMap(); - map.init(geo.getNumberOfSlices(), geo.getNumberOfRows()); + map.init(geo.getNumberOfRocs(), geo.getNumberOfRows()); int nY2Xbins = trackResiduals.getNY2XBins(); int nZ2Xbins = trackResiduals.getNZ2XBins(); @@ -480,7 +480,7 @@ std::unique_ptr TPCFastSpaceChargeCorrect // std::cout << "n knots Z: " << nKnotsZ << std::endl; const int nRows = geo.getNumberOfRows(); - const int nROCs = geo.getNumberOfSlices(); + const int nROCs = geo.getNumberOfRocs(); { // create the correction object @@ -501,10 +501,10 @@ std::unique_ptr TPCFastSpaceChargeCorrect } // .. create the correction object // set the grid borders - for (int iRoc = 0; iRoc < geo.getNumberOfSlices(); iRoc++) { + for (int iRoc = 0; iRoc < geo.getNumberOfRocs(); iRoc++) { for (int iRow = 0; iRow < geo.getNumberOfRows(); iRow++) { const auto& rowInfo = geo.getRowInfo(iRow); - auto& info = correction.getSliceRowInfo(iRoc, iRow); + auto& info = correction.getRocRowInfo(iRoc, iRow); const auto& spline = correction.getSpline(iRoc, iRow); double yMin = rowInfo.x * trackResiduals.getY2X(iRow, 0); double yMax = rowInfo.x * trackResiduals.getY2X(iRow, trackResiduals.getNY2XBins() - 1); @@ -616,7 +616,7 @@ std::unique_ptr TPCFastSpaceChargeCorrect vox.mZ = x * z2x; vox.mDy = x / trackResiduals.getDY2XI(xBin, iy); vox.mDz = x * trackResiduals.getDZ2X(iz); - if (iRoc >= geo.getNumberOfSlicesA()) { + if (iRoc >= geo.getNumberOfRocsA()) { vox.mZ = -vox.mZ; } data.mY *= x; @@ -720,7 +720,7 @@ std::unique_ptr TPCFastSpaceChargeCorrect // feed the row data to the helper - auto& info = correction.getSliceRowInfo(iRoc, iRow); + auto& info = correction.getRocRowInfo(iRoc, iRow); const auto& spline = correction.getSpline(iRoc, iRow); auto addEdge = [&](int iy1, int iz1, int iy2, int iz2, int nSteps) { @@ -813,21 +813,21 @@ void TPCFastSpaceChargeCorrectionHelper::initMaxDriftLength(o2::gpu::TPCFastSpac double tpcR2min = mGeo.getRowInfo(0).x - 1.; tpcR2min = tpcR2min * tpcR2min; double tpcR2max = mGeo.getRowInfo(mGeo.getNumberOfRows() - 1).x; - tpcR2max = tpcR2max / cos(2 * M_PI / mGeo.getNumberOfSlicesA() / 2) + 1.; + tpcR2max = tpcR2max / cos(2 * M_PI / mGeo.getNumberOfRocsA() / 2) + 1.; tpcR2max = tpcR2max * tpcR2max; ChebyshevFit1D chebFitter; - for (int slice = 0; slice < mGeo.getNumberOfSlices(); slice++) { + for (int roc = 0; roc < mGeo.getNumberOfRocs(); roc++) { if (prn) { - LOG(info) << "init MaxDriftLength for slice " << slice; + LOG(info) << "init MaxDriftLength for roc " << roc; } - double vLength = (slice < mGeo.getNumberOfSlicesA()) ? mGeo.getTPCzLengthA() : mGeo.getTPCzLengthC(); - TPCFastSpaceChargeCorrection::SliceInfo& sliceInfo = correction.getSliceInfo(slice); - sliceInfo.vMax = 0.f; + double vLength = (roc < mGeo.getNumberOfRocsA()) ? mGeo.getTPCzLengthA() : mGeo.getTPCzLengthC(); + TPCFastSpaceChargeCorrection::RocInfo& rocInfo = correction.getRocInfo(roc); + rocInfo.vMax = 0.f; for (int row = 0; row < mGeo.getNumberOfRows(); row++) { - TPCFastSpaceChargeCorrection::RowActiveArea& area = correction.getSliceRowInfo(slice, row).activeArea; + TPCFastSpaceChargeCorrection::RowActiveArea& area = correction.getRocRowInfo(roc, row).activeArea; area.cvMax = 0; area.vMax = 0; area.cuMin = mGeo.convPadToU(row, 0.f); @@ -843,7 +843,7 @@ void TPCFastSpaceChargeCorrectionHelper::initMaxDriftLength(o2::gpu::TPCFastSpac while (v1 - v0 > 0.1) { float v = 0.5 * (v0 + v1); float dx, du, dv; - correction.getCorrection(slice, row, u, v, dx, du, dv); + correction.getCorrection(roc, row, u, v, dx, du, dv); double cx = x + dx; double cu = u + du; double cv = v + dv; @@ -872,11 +872,11 @@ void TPCFastSpaceChargeCorrectionHelper::initMaxDriftLength(o2::gpu::TPCFastSpac for (int i = 0; i < 5; i++) { area.maxDriftLengthCheb[i] = chebFitter.getCoefficients()[i]; } - if (sliceInfo.vMax < area.vMax) { - sliceInfo.vMax = area.vMax; + if (rocInfo.vMax < area.vMax) { + rocInfo.vMax = area.vMax; } } // row - } // slice + } // roc } void TPCFastSpaceChargeCorrectionHelper::initInverse(o2::gpu::TPCFastSpaceChargeCorrection& correction, bool prn) @@ -902,22 +902,22 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector helper; std::vector splineParameters; for (int row = iThread; row < mGeo.getNumberOfRows(); row += mNthreads) { - TPCFastSpaceChargeCorrection::SplineType spline = correction.getSpline(slice, row); + TPCFastSpaceChargeCorrection::SplineType spline = correction.getSpline(roc, row); helper.setSpline(spline, 10, 10); double x = mGeo.getRowInfo(row).x; - auto& sliceRowInfo = correction.getSliceRowInfo(slice, row); + auto& rocRowInfo = correction.getRocRowInfo(roc, row); std::vector gridU; { @@ -951,7 +951,7 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vectorgetCorrection(slice, row, u, v, dxTmp, duTmp, dvTmp); + corrections[i]->getCorrection(roc, row, u, v, dxTmp, duTmp, dvTmp); dx += dxTmp * scaling[i]; du += duTmp * scaling[i]; dv += dvTmp * scaling[i]; @@ -1002,28 +1002,28 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vectorgetCorrectionMap(); scData.init(nRocs, nRows); @@ -143,7 +143,7 @@ BOOST_AUTO_TEST_CASE(FastTransform_test_setSpaceChargeCorrection) } } } // row - } // slice + } // roc std::unique_ptr fastTransform(TPCFastTransformHelperO2::instance()->create(0)); @@ -158,12 +158,12 @@ BOOST_AUTO_TEST_CASE(FastTransform_test_setSpaceChargeCorrection) double statDiff = 0., statN = 0.; double statDiffFile = 0., statNFile = 0.; - for (int slice = 0; slice < geo.getNumberOfSlices(); slice += 1) { - //std::cout << "slice " << slice << " ... " << std::endl; + for (int roc = 0; roc < geo.getNumberOfRocs(); roc += 1) { + // std::cout << "roc " << roc << " ... " << std::endl; - const TPCFastTransformGeo::SliceInfo& sliceInfo = geo.getSliceInfo(slice); + const TPCFastTransformGeo::RocInfo& rocInfo = geo.getRocInfo(roc); - float lastTimeBin = fastTransform->getMaxDriftTime(slice, 0.f); + float lastTimeBin = fastTransform->getMaxDriftTime(roc, 0.f); for (int row = 0; row < geo.getNumberOfRows(); row++) { @@ -172,31 +172,31 @@ BOOST_AUTO_TEST_CASE(FastTransform_test_setSpaceChargeCorrection) for (int pad = 0; pad < nPads; pad += 10) { for (float time = 0; time < lastTimeBin; time += 30) { - //std::cout<<"slice "<setApplyCorrectionOff(); float x0, y0, z0; - fastTransform->Transform(slice, row, pad, time, x0, y0, z0); + fastTransform->Transform(roc, row, pad, time, x0, y0, z0); - BOOST_CHECK_EQUAL(geo.test(slice, row, y0, z0), 0); + BOOST_CHECK_EQUAL(geo.test(roc, row, y0, z0), 0); fastTransform->setApplyCorrectionOn(); float x1, y1, z1; - fastTransform->Transform(slice, row, pad, time, x1, y1, z1); + fastTransform->Transform(roc, row, pad, time, x1, y1, z1); // local to UV float u0, v0, u1, v1; - geo.convLocalToUV(slice, y0, z0, u0, v0); - geo.convLocalToUV(slice, y1, z1, u1, v1); + geo.convLocalToUV(roc, y0, z0, u0, v0); + geo.convLocalToUV(roc, y1, z1, u1, v1); double dx, du, dv; - correctionUV(slice, row, u0, v0, dx, du, dv); + correctionUV(roc, row, u0, v0, dx, du, dv); statDiff += fabs((x1 - x0) - dx) + fabs((u1 - u0) - du) + fabs((v1 - v0) - dv); statN += 3; //std::cout << (x1 - x0) - dx << " " << (u1 - u0) - du << " " << (v1 - v0) - dv << std::endl; //": v0 " << v0 <<" z0 "<Transform(slice, row, pad, time, x1f, y1f, z1f); + fromFile->Transform(roc, row, pad, time, x1f, y1f, z1f); statDiffFile += fabs(x1f - x1) + fabs(y1f - y1) + fabs(z1f - z1); statNFile += 3; } diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx index e519716b6eec0..5d3c186a06d42 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx @@ -35,7 +35,7 @@ TPCFastSpaceChargeCorrection::TPCFastSpaceChargeCorrection() mScenarioPtr(nullptr), mTimeStamp(-1), mSplineData{nullptr, nullptr, nullptr}, - mSliceDataSizeBytes{0, 0, 0} + mRocDataSizeBytes{0, 0, 0} { // Default Constructor: creates an empty uninitialized object } @@ -64,7 +64,7 @@ void TPCFastSpaceChargeCorrection::destroy() mTimeStamp = -1; for (int32_t is = 0; is < 3; is++) { mSplineData[is] = nullptr; - mSliceDataSizeBytes[is] = 0; + mRocDataSizeBytes[is] = 0; } FlatObject::destroy(); } @@ -101,13 +101,13 @@ void TPCFastSpaceChargeCorrection::cloneFromObject(const TPCFastSpaceChargeCorre mTimeStamp = obj.mTimeStamp; - for (int32_t i = 0; i < TPCFastTransformGeo::getNumberOfSlices(); ++i) { - mSliceInfo[i] = obj.mSliceInfo[i]; + for (int32_t i = 0; i < TPCFastTransformGeo::getNumberOfRocs(); ++i) { + mRocInfo[i] = obj.mRocInfo[i]; } - mSliceDataSizeBytes[0] = obj.mSliceDataSizeBytes[0]; - mSliceDataSizeBytes[1] = obj.mSliceDataSizeBytes[1]; - mSliceDataSizeBytes[2] = obj.mSliceDataSizeBytes[2]; + mRocDataSizeBytes[0] = obj.mRocDataSizeBytes[0]; + mRocDataSizeBytes[1] = obj.mRocDataSizeBytes[1]; + mRocDataSizeBytes[2] = obj.mRocDataSizeBytes[2]; // variable-size data mScenarioPtr = obj.mScenarioPtr; @@ -121,8 +121,8 @@ void TPCFastSpaceChargeCorrection::cloneFromObject(const TPCFastSpaceChargeCorre mRowInfos[i] = obj.mRowInfos[i]; } - for (int32_t i = 0; i < TPCFastTransformGeo::getNumberOfSlices() * TPCFastTransformGeo::getMaxNumberOfRows(); i++) { - mSliceRowInfos[i] = obj.mSliceRowInfos[i]; + for (int32_t i = 0; i < TPCFastTransformGeo::getNumberOfRocs() * TPCFastTransformGeo::getMaxNumberOfRows(); i++) { + mRocRowInfos[i] = obj.mRocRowInfos[i]; } relocateBufferPointers(oldFlatBufferPtr, mFlatBufferPtr); @@ -143,7 +143,7 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer struct RowInfoVersion3 { int32_t splineScenarioID{0}; ///< scenario index (which of Spline2D splines to use) - size_t dataOffsetBytes[3]{0}; ///< offset for the spline data withing a TPC slice + size_t dataOffsetBytes[3]{0}; ///< offset for the spline data withing a TPC roc }; struct RowActiveAreaVersion3 { @@ -154,7 +154,7 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer float cvMax{0.f}; }; - struct SliceRowInfoVersion3 { + struct RocRowInfoVersion3 { float gridV0{0.f}; ///< V coordinate of the V-grid start float gridCorrU0{0.f}; ///< U coordinate of the U-grid start for corrected U float gridCorrV0{0.f}; ///< V coordinate of the V-grid start for corrected V @@ -171,13 +171,13 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer rowsSize = sizeof(RowInfoVersion3) * mGeo.getNumberOfRows(); } - size_t sliceRowsOffset = rowsOffset + rowsSize; - size_t sliceRowsSize = 0; - if (mClassVersion == 3) { // copy old-format slicerow data from the buffer to the arrays - sliceRowsSize = sizeof(SliceRowInfoVersion3) * mGeo.getNumberOfRows() * mGeo.getNumberOfSlices(); + size_t rocRowsOffset = rowsOffset + rowsSize; + size_t rocRowsSize = 0; + if (mClassVersion == 3) { // copy old-format rocrow data from the buffer to the arrays + rocRowsSize = sizeof(RocRowInfoVersion3) * mGeo.getNumberOfRows() * mGeo.getNumberOfRocs(); } - size_t scOffset = alignSize(sliceRowsOffset + sliceRowsSize, SplineType::getClassAlignmentBytes()); + size_t scOffset = alignSize(rocRowsOffset + rocRowsSize, SplineType::getClassAlignmentBytes()); size_t scSize = sizeof(SplineType) * mNumberOfScenarios; mScenarioPtr = reinterpret_cast(mFlatBufferPtr + scOffset); @@ -192,12 +192,12 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer } size_t bufferSize = scBufferOffset + scBufferSize; for (int32_t is = 0; is < 3; is++) { - size_t sliceDataOffset = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); - mSplineData[is] = reinterpret_cast(mFlatBufferPtr + sliceDataOffset); - bufferSize = sliceDataOffset + mSliceDataSizeBytes[is] * mGeo.getNumberOfSlices(); + size_t rocDataOffset = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); + mSplineData[is] = reinterpret_cast(mFlatBufferPtr + rocDataOffset); + bufferSize = rocDataOffset + mRocDataSizeBytes[is] * mGeo.getNumberOfRocs(); } - if (mClassVersion == 3) { // copy old-format slicerow data from the buffer to the arrays + if (mClassVersion == 3) { // copy old-format rocrow data from the buffer to the arrays auto* rowInfosOld = reinterpret_cast(mFlatBufferPtr + rowsOffset); for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { @@ -214,18 +214,18 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer spline.setXrange(0., spline.getGridX1().getUmax(), 0., spline.getGridX2().getUmax()); } - auto* sliceRowInfosOld = reinterpret_cast(mFlatBufferPtr + sliceRowsOffset); + auto* rocRowInfosOld = reinterpret_cast(mFlatBufferPtr + rocRowsOffset); - for (int32_t slice = 0; slice < mGeo.getNumberOfSlices(); slice++) { + for (int32_t roc = 0; roc < mGeo.getNumberOfRocs(); roc++) { for (int32_t row = 0; row < mGeo.getNumberOfRows(); row++) { - SliceRowInfoVersion3& infoOld = sliceRowInfosOld[mGeo.getNumberOfRows() * slice + row]; - SliceRowInfo& info = getSliceRowInfo(slice, row); - const auto& spline = getSpline(slice, row); + RocRowInfoVersion3& infoOld = rocRowInfosOld[mGeo.getNumberOfRows() * roc + row]; + RocRowInfo& info = getRocRowInfo(roc, row); + const auto& spline = getSpline(roc, row); info.gridU0 = mGeo.getRowInfo(row).u0; info.scaleUtoGrid = spline.getGridX1().getUmax() / mGeo.getRowInfo(row).getUwidth(); info.gridV0 = infoOld.gridV0; - info.scaleVtoGrid = spline.getGridX2().getUmax() / (mGeo.getTPCzLength(slice) + 3. - info.gridV0); + info.scaleVtoGrid = spline.getGridX2().getUmax() / (mGeo.getTPCzLength(roc) + 3. - info.gridV0); info.gridCorrU0 = infoOld.gridCorrU0; info.scaleCorrUtoGrid = infoOld.scaleCorrUtoGrid; @@ -276,7 +276,7 @@ void TPCFastSpaceChargeCorrection::print() const mGeo.print(); LOG(info) << " mNumberOfScenarios = " << mNumberOfScenarios; LOG(info) << " mTimeStamp = " << mTimeStamp; - LOG(info) << " mSliceDataSizeBytes = " << mSliceDataSizeBytes[0] << " " << mSliceDataSizeBytes[1] << " " << mSliceDataSizeBytes[2]; + LOG(info) << " mRocDataSizeBytes = " << mRocDataSizeBytes[0] << " " << mRocDataSizeBytes[1] << " " << mRocDataSizeBytes[2]; { LOG(info) << " TPC rows: "; for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { @@ -292,9 +292,9 @@ void TPCFastSpaceChargeCorrection::print() const } if (mScenarioPtr) { LOG(info) << " Spline Data: "; - for (int32_t is = 0; is < mGeo.getNumberOfSlices(); is++) { + for (int32_t is = 0; is < mGeo.getNumberOfRocs(); is++) { for (int32_t ir = 0; ir < mGeo.getNumberOfRows(); ir++) { - LOG(info) << "slice " << is << " row " << ir << ": "; + LOG(info) << "roc " << is << " row " << ir << ": "; const SplineType& spline = getSpline(is, ir); const float* d = getSplineData(is, ir); int32_t k = 0; @@ -305,8 +305,8 @@ void TPCFastSpaceChargeCorrection::print() const LOG(info) << ""; } } - // LOG(info) << "inverse correction: slice " << slice - // << " dx " << maxDslice[0] << " du " << maxDslice[1] << " dv " << maxDslice[2] ; + // LOG(info) << "inverse correction: roc " << roc + // << " dx " << maxDroc[0] << " du " << maxDroc[1] << " dv " << maxDroc[2] ; } } } @@ -345,7 +345,7 @@ void TPCFastSpaceChargeCorrection::startConstruction(const TPCFastTransformGeo& mScenarioPtr = nullptr; for (int32_t s = 0; s < 3; s++) { mSplineData[s] = nullptr; - mSliceDataSizeBytes[s] = 0; + mRocDataSizeBytes[s] = 0; } mClassVersion = 4; } @@ -401,18 +401,18 @@ void TPCFastSpaceChargeCorrection::finishConstruction() scBufferSize = alignSize(scBufferSize + sp.getFlatBufferSize(), sp.getBufferAlignmentBytes()); } size_t bufferSize = scBufferOffsets[0] + scBufferSize; - size_t sliceDataOffset[3]; + size_t rocDataOffset[3]; for (int32_t is = 0; is < 3; is++) { - sliceDataOffset[is] = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); - mSliceDataSizeBytes[is] = 0; + rocDataOffset[is] = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); + mRocDataSizeBytes[is] = 0; for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { RowInfo& row = mRowInfos[i]; SplineType& spline = mConstructionScenarios[row.splineScenarioID]; - row.dataOffsetBytes[is] = alignSize(mSliceDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); - mSliceDataSizeBytes[is] = row.dataOffsetBytes[is] + spline.getSizeOfParameters(); + row.dataOffsetBytes[is] = alignSize(mRocDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); + mRocDataSizeBytes[is] = row.dataOffsetBytes[is] + spline.getSizeOfParameters(); } - mSliceDataSizeBytes[is] = alignSize(mSliceDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); - bufferSize = sliceDataOffset[is] + mSliceDataSizeBytes[is] * mGeo.getNumberOfSlices(); + mRocDataSizeBytes[is] = alignSize(mRocDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); + bufferSize = rocDataOffset[is] + mRocDataSizeBytes[is] * mGeo.getNumberOfRocs(); } FlatObject::finishConstruction(bufferSize); @@ -427,7 +427,7 @@ void TPCFastSpaceChargeCorrection::finishConstruction() } for (int32_t is = 0; is < 3; is++) { - mSplineData[is] = reinterpret_cast(mFlatBufferPtr + sliceDataOffset[is]); + mSplineData[is] = reinterpret_cast(mFlatBufferPtr + rocDataOffset[is]); } releaseConstructionMemory(); @@ -439,15 +439,15 @@ void TPCFastSpaceChargeCorrection::finishConstruction() GPUd() void TPCFastSpaceChargeCorrection::setNoCorrection() { // initialise all corrections to 0. - for (int32_t slice = 0; slice < mGeo.getNumberOfSlices(); slice++) { - double vLength = (slice < mGeo.getNumberOfSlicesA()) ? mGeo.getTPCzLengthA() : mGeo.getTPCzLengthC(); - SliceInfo& sliceInfo = getSliceInfo(slice); - sliceInfo.vMax = vLength; + for (int32_t roc = 0; roc < mGeo.getNumberOfRocs(); roc++) { + double vLength = (roc < mGeo.getNumberOfRocsA()) ? mGeo.getTPCzLengthA() : mGeo.getTPCzLengthC(); + RocInfo& rocInfo = getRocInfo(roc); + rocInfo.vMax = vLength; for (int32_t row = 0; row < mGeo.getNumberOfRows(); row++) { - const SplineType& spline = getSpline(slice, row); + const SplineType& spline = getSpline(roc, row); for (int32_t is = 0; is < 3; is++) { - float* data = getSplineData(slice, row, is); + float* data = getSplineData(roc, row, is); int32_t nPar = spline.getNumberOfParameters(); if (is == 1) { nPar = nPar / 3; @@ -460,7 +460,7 @@ GPUd() void TPCFastSpaceChargeCorrection::setNoCorrection() } } - SliceRowInfo& info = getSliceRowInfo(slice, row); + RocRowInfo& info = getRocRowInfo(roc, row); info.gridU0 = mGeo.getRowInfo(row).u0; info.scaleUtoGrid = spline.getGridX1().getUmax() / mGeo.getRowInfo(row).getUwidth(); @@ -484,7 +484,7 @@ GPUd() void TPCFastSpaceChargeCorrection::setNoCorrection() area.cvMax = vLength; } // row - } // slice + } // roc } void TPCFastSpaceChargeCorrection::constructWithNoCorrection(const TPCFastTransformGeo& geo) @@ -512,7 +512,7 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) double tpcR2min = mGeo.getRowInfo(0).x - 1.; tpcR2min = tpcR2min * tpcR2min; double tpcR2max = mGeo.getRowInfo(mGeo.getNumberOfRows() - 1).x; - tpcR2max = tpcR2max / cos(2 * M_PI / mGeo.getNumberOfSlicesA() / 2) + 1.; + tpcR2max = tpcR2max / cos(2 * M_PI / mGeo.getNumberOfRocsA() / 2) + 1.; tpcR2max = tpcR2max * tpcR2max; struct MaxValue { @@ -544,27 +544,27 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) MaxValue maxDtpc[3]; MaxValue maxD; - for (int32_t slice = 0; slice < mGeo.getNumberOfSlices(); slice++) { + for (int32_t roc = 0; roc < mGeo.getNumberOfRocs(); roc++) { if (prn) { - LOG(info) << "check inverse transform for slice " << slice; + LOG(info) << "check inverse transform for roc " << roc; } - double vLength = mGeo.getTPCzLength(slice); - MaxValue maxDslice[3]; + double vLength = mGeo.getTPCzLength(roc); + MaxValue maxDroc[3]; for (int32_t row = 0; row < mGeo.getNumberOfRows(); row++) { float u0, u1, v0, v1; - mGeo.convScaledUVtoUV(slice, row, 0., 0., u0, v0); - mGeo.convScaledUVtoUV(slice, row, 1., 1., u1, v1); + mGeo.convScaledUVtoUV(roc, row, 0., 0., u0, v0); + mGeo.convScaledUVtoUV(roc, row, 1., 1., u1, v1); double x = mGeo.getRowInfo(row).x; double stepU = (u1 - u0) / 100.; double stepV = (v1 - v0) / 100.; MaxValue maxDrow[3]; for (double u = u0; u < u1; u += stepU) { for (double v = v0; v < v1; v += stepV) { - if (v < getSliceRowInfo(slice, row).gridV0) { + if (v < getRocRowInfo(roc, row).gridV0) { continue; } float dx, du, dv; - getCorrection(slice, row, u, v, dx, du, dv); + getCorrection(roc, row, u, v, dx, du, dv); double cx = x + dx; double cu = u + du; double cv = v + dv; @@ -573,11 +573,11 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) continue; } float nx, nu, nv; - getCorrectionInvCorrectedX(slice, row, cu, cv, nx); - getCorrectionInvUV(slice, row, cu, cv, nu, nv); + getCorrectionInvCorrectedX(roc, row, cu, cv, nx); + getCorrectionInvUV(roc, row, cu, cv, nu, nv); double d[3] = {(cx - nx) - dx, (cu - nu) - du, (cv - nv) - dv}; for (int32_t i = 0; i < 3; i++) { - maxDrow[i].update(d[i], slice, row); + maxDrow[i].update(d[i], roc, row); } if (0 && prn && fabs(d[0]) + fabs(d[1]) + fabs(d[2]) > 0.1) { @@ -589,20 +589,20 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) } } if (1 && prn) { - LOG(info) << "slice " << slice << " row " << row + LOG(info) << "roc " << roc << " row " << row << " dx " << maxDrow[0].V << " du " << maxDrow[1].V << " dv " << maxDrow[2].V; } for (int32_t i = 0; i < 3; i++) { - maxDslice[i].update(maxDrow[i]); + maxDroc[i].update(maxDrow[i]); maxDtpc[i].update(maxDrow[i]); maxD.update(maxDrow[i]); } } if (prn) { - LOG(info) << "inverse correction: slice " << slice << ". Max deviations: " - << " dx " << maxDslice[0].toString() << " du " << maxDslice[1].toString() << " dv " << maxDslice[2].toString(); + LOG(info) << "inverse correction: roc " << roc << ". Max deviations: " + << " dx " << maxDroc[0].toString() << " du " << maxDroc[1].toString() << " dv " << maxDroc[2].toString(); } - } // slice + } // roc LOG(info) << "Test inverse TPC correction. max deviations: " << " dx " << maxDtpc[0].toString() << " du " << maxDtpc[1].toString() << " dv " << maxDtpc[2].toString() << " cm"; diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index 7957d36b494c3..b6244bfee1e0f 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -44,7 +44,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// struct RowInfo { int32_t splineScenarioID{0}; ///< scenario index (which of Spline2D splines to use) - size_t dataOffsetBytes[3]{0}; ///< offset for the spline data withing a TPC slice + size_t dataOffsetBytes[3]{0}; ///< offset for the spline data withing the TPC roc ClassDefNV(RowInfo, 1); }; @@ -57,7 +57,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject ClassDefNV(RowActiveArea, 1); }; - struct SliceRowInfo { + struct RocRowInfo { float gridU0{0.f}; //< U coordinate of the U-grid start float scaleUtoGrid{0.f}; //< scale U to U-grid coordinate float gridV0{0.f}; ///< V coordinate of the V-grid start @@ -92,12 +92,12 @@ class TPCFastSpaceChargeCorrection : public FlatObject minCorr[2] = GPUCommonMath::Min(minCorr[2], dv); } - ClassDefNV(SliceRowInfo, 2); + ClassDefNV(RocRowInfo, 2); }; - struct SliceInfo { + struct RocInfo { float vMax{0.f}; ///< Max value of V coordinate - ClassDefNV(SliceInfo, 1); + ClassDefNV(RocInfo, 1); }; typedef Spline2D SplineType; @@ -167,46 +167,46 @@ class TPCFastSpaceChargeCorrection : public FlatObject GPUd() void setInterpolationSafetyMargin(float val) { fInterpolationSafetyMargin = val; } /// Gives const pointer to a spline - GPUd() const SplineType& getSpline(int32_t slice, int32_t row) const; + GPUd() const SplineType& getSpline(int32_t roc, int32_t row) const; /// Gives pointer to a spline - GPUd() SplineType& getSpline(int32_t slice, int32_t row); + GPUd() SplineType& getSpline(int32_t roc, int32_t row); /// Gives pointer to spline data - GPUd() float* getSplineData(int32_t slice, int32_t row, int32_t iSpline = 0); + GPUd() float* getSplineData(int32_t roc, int32_t row, int32_t iSpline = 0); /// Gives pointer to spline data - GPUd() const float* getSplineData(int32_t slice, int32_t row, int32_t iSpline = 0) const; + GPUd() const float* getSplineData(int32_t roc, int32_t row, int32_t iSpline = 0) const; /// _______________ The main method: cluster correction _______________________ /// - GPUd() int32_t getCorrection(int32_t slice, int32_t row, float u, float v, float& dx, float& du, float& dv) const; + GPUd() int32_t getCorrection(int32_t roc, int32_t row, float u, float v, float& dx, float& du, float& dv) const; /// inverse correction: Corrected U and V -> coorrected X - GPUd() void getCorrectionInvCorrectedX(int32_t slice, int32_t row, float corrU, float corrV, float& corrX) const; + GPUd() void getCorrectionInvCorrectedX(int32_t roc, int32_t row, float corrU, float corrV, float& corrX) const; /// inverse correction: Corrected U and V -> uncorrected U and V - GPUd() void getCorrectionInvUV(int32_t slice, int32_t row, float corrU, float corrV, float& nomU, float& nomV) const; + GPUd() void getCorrectionInvUV(int32_t roc, int32_t row, float corrU, float corrV, float& nomU, float& nomV) const; /// maximal possible drift length of the active area - GPUd() float getMaxDriftLength(int32_t slice, int32_t row, float pad) const; + GPUd() float getMaxDriftLength(int32_t roc, int32_t row, float pad) const; /// maximal possible drift length of the active area - GPUd() float getMaxDriftLength(int32_t slice, int32_t row) const; + GPUd() float getMaxDriftLength(int32_t roc, int32_t row) const; /// maximal possible drift length of the active area - GPUd() float getMaxDriftLength(int32_t slice) const; + GPUd() float getMaxDriftLength(int32_t roc) const; /// _______________ Utilities _______________________________________________ /// convert u,v to internal grid coordinates - GPUd() void convUVtoGrid(int32_t slice, int32_t row, float u, float v, float& gridU, float& gridV) const; + GPUd() void convUVtoGrid(int32_t roc, int32_t row, float u, float v, float& gridU, float& gridV) const; /// convert u,v to internal grid coordinates - GPUd() void convGridToUV(int32_t slice, int32_t row, float gridU, float gridV, float& u, float& v) const; + GPUd() void convGridToUV(int32_t roc, int32_t row, float gridU, float gridV, float& u, float& v) const; /// convert corrected u,v to internal grid coordinates - GPUd() void convCorrectedUVtoGrid(int32_t slice, int32_t row, float cu, float cv, float& gridU, float& gridV) const; + GPUd() void convCorrectedUVtoGrid(int32_t roc, int32_t row, float cu, float cv, float& gridU, float& gridV) const; /// TPC geometry information GPUd() const TPCFastTransformGeo& getGeometry() const @@ -223,28 +223,28 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// Gives TPC row info GPUd() const RowInfo& getRowInfo(int32_t row) const { return mRowInfos[row]; } - /// Gives TPC slice info - GPUd() const SliceInfo& getSliceInfo(int32_t slice) const + /// Gives TPC roc info + GPUd() const RocInfo& getRocInfo(int32_t roc) const { - return mSliceInfo[slice]; + return mRocInfo[roc]; } - /// Gives TPC slice info - GPUd() SliceInfo& getSliceInfo(int32_t slice) + /// Gives TPC roc info + GPUd() RocInfo& getRocInfo(int32_t roc) { - return mSliceInfo[slice]; + return mRocInfo[roc]; } - /// Gives TPC slice & row info - GPUd() const SliceRowInfo& getSliceRowInfo(int32_t slice, int32_t row) const + /// Gives TPC roc & row info + GPUd() const RocRowInfo& getRocRowInfo(int32_t roc, int32_t row) const { - return mSliceRowInfos[mGeo.getMaxNumberOfRows() * slice + row]; + return mRocRowInfos[mGeo.getMaxNumberOfRows() * roc + row]; } - /// Gives TPC slice & row info - GPUd() SliceRowInfo& getSliceRowInfo(int32_t slice, int32_t row) + /// Gives TPC roc & row info + GPUd() RocRowInfo& getRocRowInfo(int32_t roc, int32_t row) { - return mSliceRowInfos[mGeo.getMaxNumberOfRows() * slice + row]; + return mRocRowInfos[mGeo.getMaxNumberOfRows() * roc + row]; } #if !defined(GPUCA_GPUCODE) @@ -260,7 +260,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject void releaseConstructionMemory(); /// temporary method with the an way of calculating 2D spline - GPUd() int32_t getCorrectionOld(int32_t slice, int32_t row, float u, float v, float& dx, float& du, float& dv) const; + GPUd() int32_t getCorrectionOld(int32_t roc, int32_t row, float u, float v, float& dx, float& du, float& dv) const; /// _______________ Data members _______________________________________________ @@ -274,7 +274,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject int32_t mNumberOfScenarios; ///< Number of approximation spline scenarios - SliceInfo mSliceInfo[TPCFastTransformGeo::getNumberOfSlices()]; ///< SliceInfo array + RocInfo mRocInfo[TPCFastTransformGeo::getNumberOfRocs()]; ///< RocInfo array SplineType* mScenarioPtr; //! (transient!!) pointer to spline scenarios @@ -284,7 +284,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject char* mSplineData[3]; //! (transient!!) pointer to the spline data in the flat buffer - size_t mSliceDataSizeBytes[3]; ///< size of the data for one slice in the flat buffer + size_t mRocDataSizeBytes[3]; ///< size of the data for one roc in the flat buffer float fInterpolationSafetyMargin{0.1f}; // 10% area around the TPC row. Outside of this area the interpolation returns the boundary values. @@ -295,72 +295,72 @@ class TPCFastSpaceChargeCorrection : public FlatObject RowInfo mRowInfos[TPCFastTransformGeo::getMaxNumberOfRows()]; ///< RowInfo array - SliceRowInfo mSliceRowInfos[TPCFastTransformGeo::getNumberOfSlices() * TPCFastTransformGeo::getMaxNumberOfRows()]; ///< SliceRowInfo array + RocRowInfo mRocRowInfos[TPCFastTransformGeo::getNumberOfRocs() * TPCFastTransformGeo::getMaxNumberOfRows()]; ///< RocRowInfo array - ClassDefNV(TPCFastSpaceChargeCorrection, 4); + ClassDefNV(TPCFastSpaceChargeCorrection, 5); }; /// ==================================================== /// Inline implementations of some methods /// ==================================================== -GPUdi() const TPCFastSpaceChargeCorrection::SplineType& TPCFastSpaceChargeCorrection::getSpline(int32_t slice, int32_t row) const +GPUdi() const TPCFastSpaceChargeCorrection::SplineType& TPCFastSpaceChargeCorrection::getSpline(int32_t roc, int32_t row) const { /// Gives const pointer to spline const RowInfo& rowInfo = mRowInfos[row]; return mScenarioPtr[rowInfo.splineScenarioID]; } -GPUdi() TPCFastSpaceChargeCorrection::SplineType& TPCFastSpaceChargeCorrection::getSpline(int32_t slice, int32_t row) +GPUdi() TPCFastSpaceChargeCorrection::SplineType& TPCFastSpaceChargeCorrection::getSpline(int32_t roc, int32_t row) { /// Gives pointer to spline const RowInfo& rowInfo = mRowInfos[row]; return mScenarioPtr[rowInfo.splineScenarioID]; } -GPUdi() float* TPCFastSpaceChargeCorrection::getSplineData(int32_t slice, int32_t row, int32_t iSpline) +GPUdi() float* TPCFastSpaceChargeCorrection::getSplineData(int32_t roc, int32_t row, int32_t iSpline) { /// Gives pointer to spline data const RowInfo& rowInfo = mRowInfos[row]; - return reinterpret_cast(mSplineData[iSpline] + mSliceDataSizeBytes[iSpline] * slice + rowInfo.dataOffsetBytes[iSpline]); + return reinterpret_cast(mSplineData[iSpline] + mRocDataSizeBytes[iSpline] * roc + rowInfo.dataOffsetBytes[iSpline]); } -GPUdi() const float* TPCFastSpaceChargeCorrection::getSplineData(int32_t slice, int32_t row, int32_t iSpline) const +GPUdi() const float* TPCFastSpaceChargeCorrection::getSplineData(int32_t roc, int32_t row, int32_t iSpline) const { /// Gives pointer to spline data const RowInfo& rowInfo = mRowInfos[row]; - return reinterpret_cast(mSplineData[iSpline] + mSliceDataSizeBytes[iSpline] * slice + rowInfo.dataOffsetBytes[iSpline]); + return reinterpret_cast(mSplineData[iSpline] + mRocDataSizeBytes[iSpline] * roc + rowInfo.dataOffsetBytes[iSpline]); } -GPUdi() void TPCFastSpaceChargeCorrection::convUVtoGrid(int32_t slice, int32_t row, float u, float v, float& gu, float& gv) const +GPUdi() void TPCFastSpaceChargeCorrection::convUVtoGrid(int32_t roc, int32_t row, float u, float v, float& gu, float& gv) const { - const auto& info = getSliceRowInfo(slice, row); + const auto& info = getRocRowInfo(roc, row); gu = (u - info.gridU0) * info.scaleUtoGrid; gv = (v - info.gridV0) * info.scaleVtoGrid; } -GPUdi() void TPCFastSpaceChargeCorrection::convGridToUV(int32_t slice, int32_t row, float gridU, float gridV, float& u, float& v) const +GPUdi() void TPCFastSpaceChargeCorrection::convGridToUV(int32_t roc, int32_t row, float gridU, float gridV, float& u, float& v) const { /// convert internal grid coordinates to u,v - const SliceRowInfo& info = getSliceRowInfo(slice, row); + const RocRowInfo& info = getRocRowInfo(roc, row); u = info.gridU0 + gridU / info.scaleUtoGrid; v = info.gridV0 + gridV / info.scaleVtoGrid; } -GPUdi() void TPCFastSpaceChargeCorrection::convCorrectedUVtoGrid(int32_t slice, int32_t row, float corrU, float corrV, float& gridU, float& gridV) const +GPUdi() void TPCFastSpaceChargeCorrection::convCorrectedUVtoGrid(int32_t roc, int32_t row, float corrU, float corrV, float& gridU, float& gridV) const { - const SliceRowInfo& info = getSliceRowInfo(slice, row); + const RocRowInfo& info = getRocRowInfo(roc, row); gridU = (corrU - info.gridCorrU0) * info.scaleCorrUtoGrid; gridV = (corrV - info.gridCorrV0) * info.scaleCorrVtoGrid; } -GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrection(int32_t slice, int32_t row, float u, float v, float& dx, float& du, float& dv) const +GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrection(int32_t roc, int32_t row, float u, float v, float& dx, float& du, float& dv) const { - const auto& info = getSliceRowInfo(slice, row); - const SplineType& spline = getSpline(slice, row); - const float* splineData = getSplineData(slice, row); + const auto& info = getRocRowInfo(roc, row); + const SplineType& spline = getSpline(roc, row); + const float* splineData = getSplineData(roc, row); float gridU = 0, gridV = 0; - convUVtoGrid(slice, row, u, v, gridU, gridV); + convUVtoGrid(roc, row, u, v, gridU, gridV); // shrink to the grid area gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); gridV = GPUCommonMath::Clamp(gridV, 0.f, (float)spline.getGridX2().getUmax()); @@ -382,15 +382,15 @@ GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrection(int32_t slice, int32 return 0; } -GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrectionOld(int32_t slice, int32_t row, float u, float v, float& dx, float& du, float& dv) const +GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrectionOld(int32_t roc, int32_t row, float u, float v, float& dx, float& du, float& dv) const { - const SplineType& spline = getSpline(slice, row); - const float* splineData = getSplineData(slice, row); + const SplineType& spline = getSpline(roc, row); + const float* splineData = getSplineData(roc, row); float gridU = 0, gridV = 0; - convUVtoGrid(slice, row, u, v, gridU, gridV); + convUVtoGrid(roc, row, u, v, gridU, gridV); float dxuv[3]; spline.interpolateUold(splineData, gridU, gridV, dxuv); - const auto& info = getSliceRowInfo(slice, row); + const auto& info = getRocRowInfo(roc, row); float s = v / info.gridV0; if (s < 0.) { s = 0.; @@ -405,14 +405,14 @@ GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrectionOld(int32_t slice, in } GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvCorrectedX( - int32_t slice, int32_t row, float corrU, float corrV, float& x) const + int32_t roc, int32_t row, float corrU, float corrV, float& x) const { - const auto& info = getSliceRowInfo(slice, row); - const Spline2D& spline = reinterpret_cast&>(getSpline(slice, row)); - const float* splineData = getSplineData(slice, row, 1); + const auto& info = getRocRowInfo(roc, row); + const Spline2D& spline = reinterpret_cast&>(getSpline(roc, row)); + const float* splineData = getSplineData(roc, row, 1); float gridU, gridV; - convCorrectedUVtoGrid(slice, row, corrU, corrV, gridU, gridV); + convCorrectedUVtoGrid(roc, row, corrU, corrV, gridU, gridV); // shrink to the grid area gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); @@ -434,13 +434,13 @@ GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvCorrectedX( } GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvUV( - int32_t slice, int32_t row, float corrU, float corrV, float& nomU, float& nomV) const + int32_t roc, int32_t row, float corrU, float corrV, float& nomU, float& nomV) const { - const Spline2D& spline = reinterpret_cast&>(getSpline(slice, row)); - const float* splineData = getSplineData(slice, row, 2); + const Spline2D& spline = reinterpret_cast&>(getSpline(roc, row)); + const float* splineData = getSplineData(roc, row, 2); float gridU, gridV; - convCorrectedUVtoGrid(slice, row, corrU, corrV, gridU, gridV); + convCorrectedUVtoGrid(roc, row, corrU, corrV, gridU, gridV); // shrink to the grid area gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); @@ -448,7 +448,7 @@ GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvUV( float duv[2]; spline.interpolateU(splineData, gridU, gridV, duv); - const auto& info = getSliceRowInfo(slice, row); + const auto& info = getRocRowInfo(roc, row); float s = corrV / info.gridCorrV0; if (corrV >= info.gridCorrV0) { @@ -463,9 +463,9 @@ GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvUV( nomV = corrV - duv[1]; } -GPUdi() float TPCFastSpaceChargeCorrection::getMaxDriftLength(int32_t slice, int32_t row, float pad) const +GPUdi() float TPCFastSpaceChargeCorrection::getMaxDriftLength(int32_t roc, int32_t row, float pad) const { - const RowActiveArea& area = getSliceRowInfo(slice, row).activeArea; + const RowActiveArea& area = getRocRowInfo(roc, row).activeArea; const float* c = area.maxDriftLengthCheb; float x = -1.f + 2.f * pad / mGeo.getRowInfo(row).maxPad; float y = c[0] + c[1] * x; @@ -481,14 +481,14 @@ GPUdi() float TPCFastSpaceChargeCorrection::getMaxDriftLength(int32_t slice, int return y; } -GPUdi() float TPCFastSpaceChargeCorrection::getMaxDriftLength(int32_t slice, int32_t row) const +GPUdi() float TPCFastSpaceChargeCorrection::getMaxDriftLength(int32_t roc, int32_t row) const { - return getSliceRowInfo(slice, row).activeArea.vMax; + return getRocRowInfo(roc, row).activeArea.vMax; } -GPUdi() float TPCFastSpaceChargeCorrection::getMaxDriftLength(int32_t slice) const +GPUdi() float TPCFastSpaceChargeCorrection::getMaxDriftLength(int32_t roc) const { - return getSliceInfo(slice).vMax; + return getRocInfo(roc).vMax; } } // namespace gpu diff --git a/GPU/TPCFastTransformation/TPCFastTransform.h b/GPU/TPCFastTransformation/TPCFastTransform.h index d9e35ba8bf405..8aef1748ebf62 100644 --- a/GPU/TPCFastTransformation/TPCFastTransform.h +++ b/GPU/TPCFastTransformation/TPCFastTransform.h @@ -46,14 +46,14 @@ struct TPCSlowSpaceChargeCorrection { ~TPCSlowSpaceChargeCorrection(); /// getting the corrections for global coordinates - void getCorrections(const float gx, const float gy, const float gz, const int32_t slice, float& gdxC, float& gdyC, float& gdzC) const; + void getCorrections(const float gx, const float gy, const float gz, const int32_t roc, float& gdxC, float& gdyC, float& gdzC) const; o2::tpc::SpaceCharge* mCorr{nullptr}; ///< reference space charge corrections #else ~TPCSlowSpaceChargeCorrection() = default; /// setting dummy corrections for GPU - GPUd() void getCorrections(const float gx, const float gy, const float gz, const int32_t slice, float& gdxC, float& gdyC, float& gdzC) const + GPUd() void getCorrections(const float gx, const float gy, const float gz, const int32_t roc, float& gdxC, float& gdyC, float& gdzC) const { gdxC = 0; gdyC = 0; @@ -182,50 +182,50 @@ class TPCFastTransform : public FlatObject /// _______________ The main method: cluster transformation _______________________ /// - /// Transforms raw TPC coordinates to local XYZ withing a slice + /// Transforms raw TPC coordinates to local XYZ withing a roc /// taking calibration + alignment into account. /// - GPUd() void Transform(int32_t slice, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime = 0, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; - GPUd() void TransformXYZ(int32_t slice, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; + GPUd() void Transform(int32_t roc, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime = 0, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; + GPUd() void TransformXYZ(int32_t roc, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; /// Transformation in the time frame - GPUd() void TransformInTimeFrame(int32_t slice, int32_t row, float pad, float time, float& x, float& y, float& z, float maxTimeBin) const; - GPUd() void TransformInTimeFrame(int32_t slice, float time, float& z, float maxTimeBin) const; + GPUd() void TransformInTimeFrame(int32_t roc, int32_t row, float pad, float time, float& x, float& y, float& z, float maxTimeBin) const; + GPUd() void TransformInTimeFrame(int32_t roc, float time, float& z, float maxTimeBin) const; /// Inverse transformation - GPUd() void InverseTransformInTimeFrame(int32_t slice, int32_t row, float /*x*/, float y, float z, float& pad, float& time, float maxTimeBin) const; - GPUd() float InverseTransformInTimeFrame(int32_t slice, float z, float maxTimeBin) const; + GPUd() void InverseTransformInTimeFrame(int32_t roc, int32_t row, float /*x*/, float y, float z, float& pad, float& time, float maxTimeBin) const; + GPUd() float InverseTransformInTimeFrame(int32_t roc, float z, float maxTimeBin) const; /// Inverse transformation: Transformed Y and Z -> transformed X - GPUd() void InverseTransformYZtoX(int32_t slice, int32_t row, float y, float z, float& x, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; + GPUd() void InverseTransformYZtoX(int32_t roc, int32_t row, float y, float z, float& x, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; /// Inverse transformation: Transformed Y and Z -> Y and Z, transformed w/o space charge correction - GPUd() void InverseTransformYZtoNominalYZ(int32_t slice, int32_t row, float y, float z, float& ny, float& nz, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; + GPUd() void InverseTransformYZtoNominalYZ(int32_t roc, int32_t row, float y, float z, float& ny, float& nz, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; /// Inverse transformation: Transformed X, Y and Z -> X, Y and Z, transformed w/o space charge correction - GPUd() void InverseTransformXYZtoNominalXYZ(int32_t slice, int32_t row, float x, float y, float z, float& nx, float& ny, float& nz, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; + GPUd() void InverseTransformXYZtoNominalXYZ(int32_t roc, int32_t row, float x, float y, float z, float& nx, float& ny, float& nz, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; /// Ideal transformation with Vdrift only - without calibration - GPUd() void TransformIdeal(int32_t slice, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime) const; - GPUd() void TransformIdealZ(int32_t slice, float time, float& z, float vertexTime) const; + GPUd() void TransformIdeal(int32_t roc, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime) const; + GPUd() void TransformIdealZ(int32_t roc, float time, float& z, float vertexTime) const; - GPUd() void convPadTimeToUV(int32_t slice, int32_t row, float pad, float time, float& u, float& v, float vertexTime) const; - GPUd() void convPadTimeToUVinTimeFrame(int32_t slice, int32_t row, float pad, float time, float& u, float& v, float maxTimeBin) const; - GPUd() void convTimeToVinTimeFrame(int32_t slice, float time, float& v, float maxTimeBin) const; + GPUd() void convPadTimeToUV(int32_t roc, int32_t row, float pad, float time, float& u, float& v, float vertexTime) const; + GPUd() void convPadTimeToUVinTimeFrame(int32_t roc, int32_t row, float pad, float time, float& u, float& v, float maxTimeBin) const; + GPUd() void convTimeToVinTimeFrame(int32_t roc, float time, float& v, float maxTimeBin) const; - GPUd() void convUVtoPadTime(int32_t slice, int32_t row, float u, float v, float& pad, float& time, float vertexTime) const; - GPUd() void convUVtoPadTimeInTimeFrame(int32_t slice, int32_t row, float u, float v, float& pad, float& time, float maxTimeBin) const; + GPUd() void convUVtoPadTime(int32_t roc, int32_t row, float u, float v, float& pad, float& time, float vertexTime) const; + GPUd() void convUVtoPadTimeInTimeFrame(int32_t roc, int32_t row, float u, float v, float& pad, float& time, float maxTimeBin) const; GPUd() void convVtoTime(float v, float& time, float vertexTime) const; - GPUd() float convTimeToZinTimeFrame(int32_t slice, float time, float maxTimeBin) const; - GPUd() float convZtoTimeInTimeFrame(int32_t slice, float z, float maxTimeBin) const; - GPUd() float convDeltaTimeToDeltaZinTimeFrame(int32_t slice, float deltaTime) const; - GPUd() float convDeltaZtoDeltaTimeInTimeFrame(int32_t slice, float deltaZ) const; + GPUd() float convTimeToZinTimeFrame(int32_t roc, float time, float maxTimeBin) const; + GPUd() float convZtoTimeInTimeFrame(int32_t roc, float z, float maxTimeBin) const; + GPUd() float convDeltaTimeToDeltaZinTimeFrame(int32_t roc, float deltaTime) const; + GPUd() float convDeltaZtoDeltaTimeInTimeFrame(int32_t roc, float deltaZ) const; GPUd() float convDeltaZtoDeltaTimeInTimeFrameAbs(float deltaZ) const; - GPUd() float convZOffsetToVertexTime(int32_t slice, float zOffset, float maxTimeBin) const; - GPUd() float convVertexTimeToZOffset(int32_t slice, float vertexTime, float maxTimeBin) const; + GPUd() float convZOffsetToVertexTime(int32_t roc, float zOffset, float maxTimeBin) const; + GPUd() float convVertexTimeToZOffset(int32_t roc, float vertexTime, float maxTimeBin) const; - GPUd() void getTOFcorrection(int32_t slice, int32_t row, float x, float y, float z, float& dz) const; + GPUd() void getTOFcorrection(int32_t roc, int32_t row, float x, float y, float z, float& dz) const; void setApplyCorrectionOn() { mApplyCorrection = 1; } void setApplyCorrectionOff() { mApplyCorrection = 0; } @@ -276,13 +276,13 @@ class TPCFastTransform : public FlatObject GPUd() float getLumiScaleFactor() const { return mLumiScaleFactor; } /// maximal possible drift time of the active area - GPUd() float getMaxDriftTime(int32_t slice, int32_t row, float pad) const; + GPUd() float getMaxDriftTime(int32_t roc, int32_t row, float pad) const; /// maximal possible drift time of the active area - GPUd() float getMaxDriftTime(int32_t slice, int32_t row) const; + GPUd() float getMaxDriftTime(int32_t roc, int32_t row) const; /// maximal possible drift time of the active area - GPUd() float getMaxDriftTime(int32_t slice) const; + GPUd() float getMaxDriftTime(int32_t roc) const; #if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) @@ -359,7 +359,7 @@ class TPCFastTransform : public FlatObject /// Correction of (x,u,v) with tricubic interpolator on a regular grid TPCSlowSpaceChargeCorrection* mCorrectionSlow{nullptr}; ///< reference space charge corrections - GPUd() void TransformInternal(int32_t slice, int32_t row, float& u, float& v, float& x, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const; + GPUd() void TransformInternal(int32_t roc, int32_t row, float& u, float& v, float& x, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const; ClassDefNV(TPCFastTransform, 4); }; @@ -368,69 +368,69 @@ class TPCFastTransform : public FlatObject // Inline implementations of some methods // ======================================================================= -GPUdi() void TPCFastTransform::convPadTimeToUV(int32_t slice, int32_t row, float pad, float time, float& u, float& v, float vertexTime) const +GPUdi() void TPCFastTransform::convPadTimeToUV(int32_t roc, int32_t row, float pad, float time, float& u, float& v, float vertexTime) const { - bool sideC = (slice >= getGeometry().getNumberOfSlicesA()); + bool sideC = (roc >= getGeometry().getNumberOfRocsA()); const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); - const TPCFastTransformGeo::SliceInfo& sliceInfo = getGeometry().getSliceInfo(slice); + const TPCFastTransformGeo::RocInfo& rocInfo = getGeometry().getRocInfo(roc); float x = rowInfo.x; u = (pad - 0.5f * rowInfo.maxPad) * rowInfo.padWidth; float y = sideC ? -u : u; // pads are mirrorred on C-side - float yLab = y * sliceInfo.cosAlpha + x * sliceInfo.sinAlpha; + float yLab = y * rocInfo.cosAlpha + x * rocInfo.sinAlpha; v = (time - mT0 - vertexTime) * (mVdrift + mVdriftCorrY * yLab) + mLdriftCorr; // drift length cm } -GPUdi() void TPCFastTransform::convTimeToVinTimeFrame(int32_t slice, float time, float& v, float maxTimeBin) const +GPUdi() void TPCFastTransform::convTimeToVinTimeFrame(int32_t roc, float time, float& v, float maxTimeBin) const { v = (time - mT0 - maxTimeBin) * mVdrift + mLdriftCorr; // drift length cm - if (slice < getGeometry().getNumberOfSlicesA()) { + if (roc < getGeometry().getNumberOfRocsA()) { v += getGeometry().getTPCzLengthA(); } else { v += getGeometry().getTPCzLengthC(); } } -GPUdi() void TPCFastTransform::convPadTimeToUVinTimeFrame(int32_t slice, int32_t row, float pad, float time, float& u, float& v, float maxTimeBin) const +GPUdi() void TPCFastTransform::convPadTimeToUVinTimeFrame(int32_t roc, int32_t row, float pad, float time, float& u, float& v, float maxTimeBin) const { const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); u = (pad - 0.5f * rowInfo.maxPad) * rowInfo.padWidth; - convTimeToVinTimeFrame(slice, time, v, maxTimeBin); + convTimeToVinTimeFrame(roc, time, v, maxTimeBin); } -GPUdi() float TPCFastTransform::convZOffsetToVertexTime(int32_t slice, float zOffset, float maxTimeBin) const +GPUdi() float TPCFastTransform::convZOffsetToVertexTime(int32_t roc, float zOffset, float maxTimeBin) const { - if (slice < getGeometry().getNumberOfSlicesA()) { + if (roc < getGeometry().getNumberOfRocsA()) { return maxTimeBin - (getGeometry().getTPCzLengthA() + zOffset) / mVdrift; } else { return maxTimeBin - (getGeometry().getTPCzLengthC() - zOffset) / mVdrift; } } -GPUdi() float TPCFastTransform::convVertexTimeToZOffset(int32_t slice, float vertexTime, float maxTimeBin) const +GPUdi() float TPCFastTransform::convVertexTimeToZOffset(int32_t roc, float vertexTime, float maxTimeBin) const { - if (slice < getGeometry().getNumberOfSlicesA()) { + if (roc < getGeometry().getNumberOfRocsA()) { return (maxTimeBin - vertexTime) * mVdrift - getGeometry().getTPCzLengthA(); } else { return -((maxTimeBin - vertexTime) * mVdrift - getGeometry().getTPCzLengthC()); } } -GPUdi() void TPCFastTransform::convUVtoPadTime(int32_t slice, int32_t row, float u, float v, float& pad, float& time, float vertexTime) const +GPUdi() void TPCFastTransform::convUVtoPadTime(int32_t roc, int32_t row, float u, float v, float& pad, float& time, float vertexTime) const { - bool sideC = (slice >= getGeometry().getNumberOfSlicesA()); + bool sideC = (roc >= getGeometry().getNumberOfRocsA()); const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); - const TPCFastTransformGeo::SliceInfo& sliceInfo = getGeometry().getSliceInfo(slice); + const TPCFastTransformGeo::RocInfo& rocInfo = getGeometry().getRocInfo(roc); pad = u / rowInfo.padWidth + 0.5f * rowInfo.maxPad; float x = rowInfo.x; float y = sideC ? -u : u; // pads are mirrorred on C-side - float yLab = y * sliceInfo.cosAlpha + x * sliceInfo.sinAlpha; + float yLab = y * rocInfo.cosAlpha + x * rocInfo.sinAlpha; time = mT0 + vertexTime + (v - mLdriftCorr) / (mVdrift + mVdriftCorrY * yLab); } @@ -440,9 +440,9 @@ GPUdi() void TPCFastTransform::convVtoTime(float v, float& time, float vertexTim time = mT0 + vertexTime + (v - mLdriftCorr) / (mVdrift + mVdriftCorrY * yLab); } -GPUdi() void TPCFastTransform::convUVtoPadTimeInTimeFrame(int32_t slice, int32_t row, float u, float v, float& pad, float& time, float maxTimeBin) const +GPUdi() void TPCFastTransform::convUVtoPadTimeInTimeFrame(int32_t roc, int32_t row, float u, float v, float& pad, float& time, float maxTimeBin) const { - if (slice < getGeometry().getNumberOfSlicesA()) { + if (roc < getGeometry().getNumberOfRocsA()) { v -= getGeometry().getTPCzLengthA(); } else { v -= getGeometry().getTPCzLengthC(); @@ -452,17 +452,17 @@ GPUdi() void TPCFastTransform::convUVtoPadTimeInTimeFrame(int32_t slice, int32_t time = mT0 + maxTimeBin + (v - mLdriftCorr) / mVdrift; } -GPUdi() void TPCFastTransform::getTOFcorrection(int32_t slice, int32_t /*row*/, float x, float y, float z, float& dz) const +GPUdi() void TPCFastTransform::getTOFcorrection(int32_t roc, int32_t /*row*/, float x, float y, float z, float& dz) const { // calculate time of flight correction for z coordinate - bool sideC = (slice >= getGeometry().getNumberOfSlicesA()); + bool sideC = (roc >= getGeometry().getNumberOfRocsA()); float distZ = z - mPrimVtxZ; float dv = -GPUCommonMath::Sqrt(x * x + y * y + distZ * distZ) * mTOFcorr; dz = sideC ? dv : -dv; } -GPUdi() void TPCFastTransform::TransformInternal(int32_t slice, int32_t row, float& u, float& v, float& x, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const +GPUdi() void TPCFastTransform::TransformInternal(int32_t roc, int32_t row, float& u, float& v, float& x, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const { GPUCA_RTC_SPECIAL_CODE(ref2 = nullptr; scale2 = 0.f;); if (mApplyCorrection) { @@ -471,15 +471,15 @@ GPUdi() void TPCFastTransform::TransformInternal(int32_t slice, int32_t row, flo #ifndef GPUCA_GPUCODE if (mCorrectionSlow) { float ly, lz; - getGeometry().convUVtoLocal(slice, u, v, ly, lz); + getGeometry().convUVtoLocal(roc, u, v, ly, lz); float gx, gy, gz; - getGeometry().convLocalToGlobal(slice, x, ly, lz, gx, gy, gz); + getGeometry().convLocalToGlobal(roc, x, ly, lz, gx, gy, gz); float gdxC, gdyC, gdzC; - mCorrectionSlow->getCorrections(gx, gy, gz, slice, gdxC, gdyC, gdzC); - getGeometry().convGlobalToLocal(slice, gdxC, gdyC, gdzC, dx, du, dv); + mCorrectionSlow->getCorrections(gx, gy, gz, roc, gdxC, gdyC, gdzC); + getGeometry().convGlobalToLocal(roc, gdxC, gdyC, gdzC, dx, du, dv); - if (slice >= 18) { + if (roc >= 18) { du = -du; // mirror for c-Side } else { dv = -dv; // mirror z for A-Side @@ -487,17 +487,17 @@ GPUdi() void TPCFastTransform::TransformInternal(int32_t slice, int32_t row, flo } else #endif // GPUCA_GPUCODE { - mCorrection.getCorrection(slice, row, u, v, dx, du, dv); + mCorrection.getCorrection(roc, row, u, v, dx, du, dv); if (ref) { if ((scale > 0.f) && (scaleMode == 0)) { // scaling was requested float dxRef, duRef, dvRef; - ref->mCorrection.getCorrection(slice, row, u, v, dxRef, duRef, dvRef); + ref->mCorrection.getCorrection(roc, row, u, v, dxRef, duRef, dvRef); dx = (dx - dxRef) * scale + dxRef; du = (du - duRef) * scale + duRef; dv = (dv - dvRef) * scale + dvRef; } else if ((scale != 0.f) && ((scaleMode == 1) || (scaleMode == 2))) { float dxRef, duRef, dvRef; - ref->mCorrection.getCorrection(slice, row, u, v, dxRef, duRef, dvRef); + ref->mCorrection.getCorrection(roc, row, u, v, dxRef, duRef, dvRef); dx = dxRef * scale + dx; du = duRef * scale + du; dv = dvRef * scale + dv; @@ -505,7 +505,7 @@ GPUdi() void TPCFastTransform::TransformInternal(int32_t slice, int32_t row, flo } if (ref2 && (scale2 != 0)) { float dxRef, duRef, dvRef; - ref2->mCorrection.getCorrection(slice, row, u, v, dxRef, duRef, dvRef); + ref2->mCorrection.getCorrection(roc, row, u, v, dxRef, duRef, dvRef); dx = dxRef * scale2 + dx; du = duRef * scale2 + du; dv = dvRef * scale2 + dv; @@ -514,43 +514,43 @@ GPUdi() void TPCFastTransform::TransformInternal(int32_t slice, int32_t row, flo } GPUCA_DEBUG_STREAMER_CHECK(if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamFastTransform)) { float ly, lz; - getGeometry().convUVtoLocal(slice, u, v, ly, lz); + getGeometry().convUVtoLocal(roc, u, v, ly, lz); float gx, gy, gz; - getGeometry().convLocalToGlobal(slice, x, ly, lz, gx, gy, gz); + getGeometry().convLocalToGlobal(roc, x, ly, lz, gx, gy, gz); float lyT, lzT; float uCorr = u + du; float vCorr = v + dv; float lxT = x + dx; - getGeometry().convUVtoLocal(slice, uCorr, vCorr, lyT, lzT); + getGeometry().convUVtoLocal(roc, uCorr, vCorr, lyT, lzT); float invYZtoXScaled; - InverseTransformYZtoX(slice, row, lyT, lzT, invYZtoXScaled, ref, ref2, scale, scale2, scaleMode); + InverseTransformYZtoX(roc, row, lyT, lzT, invYZtoXScaled, ref, ref2, scale, scale2, scaleMode); float invYZtoX; - InverseTransformYZtoX(slice, row, lyT, lzT, invYZtoX); + InverseTransformYZtoX(roc, row, lyT, lzT, invYZtoX); float YZtoNominalY; float YZtoNominalZ; - InverseTransformYZtoNominalYZ(slice, row, lyT, lzT, YZtoNominalY, YZtoNominalZ); + InverseTransformYZtoNominalYZ(roc, row, lyT, lzT, YZtoNominalY, YZtoNominalZ); float YZtoNominalYScaled; float YZtoNominalZScaled; - InverseTransformYZtoNominalYZ(slice, row, lyT, lzT, YZtoNominalYScaled, YZtoNominalZScaled, ref, ref2, scale, scale2, scaleMode); + InverseTransformYZtoNominalYZ(roc, row, lyT, lzT, YZtoNominalYScaled, YZtoNominalZScaled, ref, ref2, scale, scale2, scaleMode); float dxRef, duRef, dvRef; if (ref) { - ref->mCorrection.getCorrection(slice, row, u, v, dxRef, duRef, dvRef); + ref->mCorrection.getCorrection(roc, row, u, v, dxRef, duRef, dvRef); } float dxRef2, duRef2, dvRef2; if (ref2) { - ref2->mCorrection.getCorrection(slice, row, u, v, dxRef2, duRef2, dvRef2); + ref2->mCorrection.getCorrection(roc, row, u, v, dxRef2, duRef2, dvRef2); } float dxOrig, duOrig, dvOrig; - mCorrection.getCorrection(slice, row, u, v, dxOrig, duOrig, dvOrig); + mCorrection.getCorrection(roc, row, u, v, dxOrig, duOrig, dvOrig); o2::utils::DebugStreamer::instance()->getStreamer("debug_fasttransform", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_Transform").data() // corrections in x, u, v @@ -569,7 +569,7 @@ GPUdi() void TPCFastTransform::TransformInternal(int32_t slice, int32_t row, flo << "v=" << v << "u=" << u << "row=" << row - << "slice=" << slice + << "roc=" << roc << "scale=" << scale << "scale2=" << scale2 // original local coordinates @@ -601,51 +601,51 @@ GPUdi() void TPCFastTransform::TransformInternal(int32_t slice, int32_t row, flo } } -GPUdi() void TPCFastTransform::TransformXYZ(int32_t slice, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const +GPUdi() void TPCFastTransform::TransformXYZ(int32_t roc, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const { float u, v; - getGeometry().convLocalToUV(slice, y, z, u, v); - TransformInternal(slice, row, u, v, x, ref, ref2, scale, scale2, scaleMode); - getGeometry().convUVtoLocal(slice, u, v, y, z); + getGeometry().convLocalToUV(roc, y, z, u, v); + TransformInternal(roc, row, u, v, x, ref, ref2, scale, scale2, scaleMode); + getGeometry().convUVtoLocal(roc, u, v, y, z); float dzTOF = 0; - getTOFcorrection(slice, row, x, y, z, dzTOF); + getTOFcorrection(roc, row, x, y, z, dzTOF); z += dzTOF; } -GPUdi() void TPCFastTransform::Transform(int32_t slice, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const +GPUdi() void TPCFastTransform::Transform(int32_t roc, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const { /// _______________ The main method: cluster transformation _______________________ /// - /// Transforms raw TPC coordinates to local XYZ withing a slice + /// Transforms raw TPC coordinates to local XYZ withing a roc /// taking calibration + alignment into account. /// const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); - // const SliceInfo &sliceInfo = getSliceInfo( slice ); - // bool sideC = ( slice >= NumberOfSlices / 2 ); + // const RocInfo &rocInfo = getRocInfo( roc ); + // bool sideC = ( roc >= NumberOfRocs / 2 ); x = rowInfo.x; float u = 0, v = 0; - convPadTimeToUV(slice, row, pad, time, u, v, vertexTime); + convPadTimeToUV(roc, row, pad, time, u, v, vertexTime); - TransformInternal(slice, row, u, v, x, ref, ref2, scale, scale2, scaleMode); + TransformInternal(roc, row, u, v, x, ref, ref2, scale, scale2, scaleMode); - getGeometry().convUVtoLocal(slice, u, v, y, z); + getGeometry().convUVtoLocal(roc, u, v, y, z); float dzTOF = 0; - getTOFcorrection(slice, row, x, y, z, dzTOF); + getTOFcorrection(roc, row, x, y, z, dzTOF); z += dzTOF; } -GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t slice, float time, float& z, float maxTimeBin) const +GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t roc, float time, float& z, float maxTimeBin) const { float v = 0; - convTimeToVinTimeFrame(slice, time, v, maxTimeBin); - getGeometry().convVtoLocal(slice, v, z); + convTimeToVinTimeFrame(roc, time, v, maxTimeBin); + getGeometry().convVtoLocal(roc, v, z); } -GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t slice, int32_t row, float pad, float time, float& x, float& y, float& z, float maxTimeBin) const +GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t roc, int32_t row, float pad, float time, float& x, float& y, float& z, float maxTimeBin) const { /// _______________ Special cluster transformation for a time frame _______________________ /// @@ -656,43 +656,43 @@ GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t slice, int32_t row, const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); x = rowInfo.x; float u = 0, v = 0; - convPadTimeToUVinTimeFrame(slice, row, pad, time, u, v, maxTimeBin); - getGeometry().convUVtoLocal(slice, u, v, y, z); + convPadTimeToUVinTimeFrame(roc, row, pad, time, u, v, maxTimeBin); + getGeometry().convUVtoLocal(roc, u, v, y, z); } -GPUdi() void TPCFastTransform::InverseTransformInTimeFrame(int32_t slice, int32_t row, float /*x*/, float y, float z, float& pad, float& time, float maxTimeBin) const +GPUdi() void TPCFastTransform::InverseTransformInTimeFrame(int32_t roc, int32_t row, float /*x*/, float y, float z, float& pad, float& time, float maxTimeBin) const { /// Inverse transformation to TransformInTimeFrame float u = 0, v = 0; - getGeometry().convLocalToUV(slice, y, z, u, v); - convUVtoPadTimeInTimeFrame(slice, row, u, v, pad, time, maxTimeBin); + getGeometry().convLocalToUV(roc, y, z, u, v); + convUVtoPadTimeInTimeFrame(roc, row, u, v, pad, time, maxTimeBin); } -GPUdi() float TPCFastTransform::InverseTransformInTimeFrame(int32_t slice, float z, float maxTimeBin) const +GPUdi() float TPCFastTransform::InverseTransformInTimeFrame(int32_t roc, float z, float maxTimeBin) const { float pad, time; - InverseTransformInTimeFrame(slice, 0, 0, 0, z, pad, time, maxTimeBin); + InverseTransformInTimeFrame(roc, 0, 0, 0, z, pad, time, maxTimeBin); return time; } -GPUdi() void TPCFastTransform::TransformIdealZ(int32_t slice, float time, float& z, float vertexTime) const +GPUdi() void TPCFastTransform::TransformIdealZ(int32_t roc, float time, float& z, float vertexTime) const { /// _______________ The main method: cluster transformation _______________________ /// - /// Transforms time TPC coordinates to local Z withing a slice + /// Transforms time TPC coordinates to local Z withing a roc /// Ideal transformation: only Vdrift from DCS. /// No space charge corrections, no time of flight correction /// float v = (time - mT0 - vertexTime) * mVdrift; // drift length cm - getGeometry().convVtoLocal(slice, v, z); + getGeometry().convVtoLocal(roc, v, z); } -GPUdi() void TPCFastTransform::TransformIdeal(int32_t slice, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime) const +GPUdi() void TPCFastTransform::TransformIdeal(int32_t roc, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime) const { /// _______________ The main method: cluster transformation _______________________ /// - /// Transforms raw TPC coordinates to local XYZ withing a slice + /// Transforms raw TPC coordinates to local XYZ withing a roc /// Ideal transformation: only Vdrift from DCS. /// No space charge corrections, no time of flight correction /// @@ -703,10 +703,10 @@ GPUdi() void TPCFastTransform::TransformIdeal(int32_t slice, int32_t row, float float u = (pad - 0.5f * rowInfo.maxPad) * rowInfo.padWidth; float v = (time - mT0 - vertexTime) * mVdrift; // drift length cm - getGeometry().convUVtoLocal(slice, u, v, y, z); + getGeometry().convUVtoLocal(roc, u, v, y, z); } -GPUdi() float TPCFastTransform::convTimeToZinTimeFrame(int32_t slice, float time, float maxTimeBin) const +GPUdi() float TPCFastTransform::convTimeToZinTimeFrame(int32_t roc, float time, float maxTimeBin) const { /// _______________ Special cluster transformation for a time frame _______________________ /// @@ -717,7 +717,7 @@ GPUdi() float TPCFastTransform::convTimeToZinTimeFrame(int32_t slice, float time float v = (time - mT0 - maxTimeBin) * mVdrift + mLdriftCorr; // drift length cm float z = getGeometry().getTPCalignmentZ(); // global TPC alignment - if (slice < getGeometry().getNumberOfSlicesA()) { + if (roc < getGeometry().getNumberOfRocsA()) { z -= v; } else { z += v; @@ -725,11 +725,11 @@ GPUdi() float TPCFastTransform::convTimeToZinTimeFrame(int32_t slice, float time return z; } -GPUdi() float TPCFastTransform::convZtoTimeInTimeFrame(int32_t slice, float z, float maxTimeBin) const +GPUdi() float TPCFastTransform::convZtoTimeInTimeFrame(int32_t roc, float z, float maxTimeBin) const { /// Inverse transformation of convTimeToZinTimeFrame() float v; - if (slice < getGeometry().getNumberOfSlicesA()) { + if (roc < getGeometry().getNumberOfRocsA()) { v = getGeometry().getTPCalignmentZ() - z; } else { v = z - getGeometry().getTPCalignmentZ(); @@ -737,10 +737,10 @@ GPUdi() float TPCFastTransform::convZtoTimeInTimeFrame(int32_t slice, float z, f return mT0 + maxTimeBin + (v - mLdriftCorr) / mVdrift; } -GPUdi() float TPCFastTransform::convDeltaTimeToDeltaZinTimeFrame(int32_t slice, float deltaTime) const +GPUdi() float TPCFastTransform::convDeltaTimeToDeltaZinTimeFrame(int32_t roc, float deltaTime) const { float deltaZ = deltaTime * mVdrift; - return slice < getGeometry().getNumberOfSlicesA() ? -deltaZ : deltaZ; + return roc < getGeometry().getNumberOfRocsA() ? -deltaZ : deltaZ; } GPUdi() float TPCFastTransform::convDeltaZtoDeltaTimeInTimeFrameAbs(float deltaZ) const @@ -748,80 +748,80 @@ GPUdi() float TPCFastTransform::convDeltaZtoDeltaTimeInTimeFrameAbs(float deltaZ return deltaZ / mVdrift; } -GPUdi() float TPCFastTransform::convDeltaZtoDeltaTimeInTimeFrame(int32_t slice, float deltaZ) const +GPUdi() float TPCFastTransform::convDeltaZtoDeltaTimeInTimeFrame(int32_t roc, float deltaZ) const { float deltaT = deltaZ / mVdrift; - return slice < getGeometry().getNumberOfSlicesA() ? -deltaT : deltaT; + return roc < getGeometry().getNumberOfRocsA() ? -deltaT : deltaT; } /* -GPUdi() float TPCFastTransform::getLastCalibratedTimeBin(int32_t slice) const +GPUdi() float TPCFastTransform::getLastCalibratedTimeBin(int32_t roc) const { /// Return a value of the last timebin where correction map is valid float u, v, pad, time; - getGeometry().convScaledUVtoUV(slice, 0, 0.f, 1.f, u, v); - convUVtoPadTime(slice, 0, u, v, pad, time, 0); + getGeometry().convScaledUVtoUV(roc, 0, 0.f, 1.f, u, v); + convUVtoPadTime(roc, 0, u, v, pad, time, 0); return time; } */ -GPUdi() float TPCFastTransform::getMaxDriftTime(int32_t slice, int32_t row, float pad) const +GPUdi() float TPCFastTransform::getMaxDriftTime(int32_t roc, int32_t row, float pad) const { /// maximal possible drift time of the active area - float maxL = mCorrection.getMaxDriftLength(slice, row, pad); + float maxL = mCorrection.getMaxDriftLength(roc, row, pad); - bool sideC = (slice >= getGeometry().getNumberOfSlicesA()); + bool sideC = (roc >= getGeometry().getNumberOfRocsA()); const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); - const TPCFastTransformGeo::SliceInfo& sliceInfo = getGeometry().getSliceInfo(slice); + const TPCFastTransformGeo::RocInfo& rocInfo = getGeometry().getRocInfo(roc); float x = rowInfo.x; float u = (pad - 0.5f * rowInfo.maxPad) * rowInfo.padWidth; float y = sideC ? -u : u; // pads are mirrorred on C-side - float yLab = y * sliceInfo.cosAlpha + x * sliceInfo.sinAlpha; + float yLab = y * rocInfo.cosAlpha + x * rocInfo.sinAlpha; return mT0 + (maxL - mLdriftCorr) / (mVdrift + mVdriftCorrY * yLab); } -GPUdi() float TPCFastTransform::getMaxDriftTime(int32_t slice, int32_t row) const +GPUdi() float TPCFastTransform::getMaxDriftTime(int32_t roc, int32_t row) const { /// maximal possible drift time of the active area - float maxL = mCorrection.getMaxDriftLength(slice, row); + float maxL = mCorrection.getMaxDriftLength(roc, row); float maxTime = 0.f; convVtoTime(maxL, maxTime, 0.f); return maxTime; } -GPUdi() float TPCFastTransform::getMaxDriftTime(int32_t slice) const +GPUdi() float TPCFastTransform::getMaxDriftTime(int32_t roc) const { /// maximal possible drift time of the active area - float maxL = mCorrection.getMaxDriftLength(slice); + float maxL = mCorrection.getMaxDriftLength(roc); float maxTime = 0.f; convVtoTime(maxL, maxTime, 0.f); return maxTime; } -GPUdi() void TPCFastTransform::InverseTransformYZtoX(int32_t slice, int32_t row, float y, float z, float& x, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const +GPUdi() void TPCFastTransform::InverseTransformYZtoX(int32_t roc, int32_t row, float y, float z, float& x, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const { GPUCA_RTC_SPECIAL_CODE(ref2 = nullptr; scale2 = 0.f;); /// Transformation y,z -> x float u = 0, v = 0; - getGeometry().convLocalToUV(slice, y, z, u, v); + getGeometry().convLocalToUV(roc, y, z, u, v); if ((scale >= 0.f) || (scaleMode == 1) || (scaleMode == 2)) { - mCorrection.getCorrectionInvCorrectedX(slice, row, u, v, x); + mCorrection.getCorrectionInvCorrectedX(roc, row, u, v, x); if (ref) { // scaling was requested if (scaleMode == 0 && scale > 0.f) { float xr; - ref->mCorrection.getCorrectionInvCorrectedX(slice, row, u, v, xr); + ref->mCorrection.getCorrectionInvCorrectedX(roc, row, u, v, xr); x = (x - xr) * scale + xr; } else if ((scale != 0) && ((scaleMode == 1) || (scaleMode == 2))) { float xr; - ref->mCorrection.getCorrectionInvCorrectedX(slice, row, u, v, xr); + ref->mCorrection.getCorrectionInvCorrectedX(roc, row, u, v, xr); x = (xr - getGeometry().getRowInfo(row).x) * scale + x; // xr=mGeo.getRowInfo(row).x + dx; } } if (ref2 && (scale2 != 0)) { float xr; - ref2->mCorrection.getCorrectionInvCorrectedX(slice, row, u, v, xr); + ref2->mCorrection.getCorrectionInvCorrectedX(roc, row, u, v, xr); x = (xr - getGeometry().getRowInfo(row).x) * scale2 + x; // xr=mGeo.getRowInfo(row).x + dx; } } else { @@ -829,7 +829,7 @@ GPUdi() void TPCFastTransform::InverseTransformYZtoX(int32_t slice, int32_t row, } GPUCA_DEBUG_STREAMER_CHECK(if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamFastTransform)) { o2::utils::DebugStreamer::instance()->getStreamer("debug_fasttransform", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_InverseTransformYZtoX").data() - << "slice=" << slice + << "roc=" << roc << "row=" << row << "scale=" << scale << "y=" << y @@ -841,29 +841,29 @@ GPUdi() void TPCFastTransform::InverseTransformYZtoX(int32_t slice, int32_t row, }) } -GPUdi() void TPCFastTransform::InverseTransformYZtoNominalYZ(int32_t slice, int32_t row, float y, float z, float& ny, float& nz, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const +GPUdi() void TPCFastTransform::InverseTransformYZtoNominalYZ(int32_t roc, int32_t row, float y, float z, float& ny, float& nz, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const { GPUCA_RTC_SPECIAL_CODE(ref2 = nullptr; scale2 = 0.f;); /// Transformation y,z -> x float u = 0, v = 0, un = 0, vn = 0; - getGeometry().convLocalToUV(slice, y, z, u, v); + getGeometry().convLocalToUV(roc, y, z, u, v); if ((scale >= 0.f) || (scaleMode == 1) || (scaleMode == 2)) { - mCorrection.getCorrectionInvUV(slice, row, u, v, un, vn); + mCorrection.getCorrectionInvUV(roc, row, u, v, un, vn); if (ref) { // scaling was requested if (scaleMode == 0 && scale > 0.f) { float unr = 0, vnr = 0; - ref->mCorrection.getCorrectionInvUV(slice, row, u, v, unr, vnr); + ref->mCorrection.getCorrectionInvUV(roc, row, u, v, unr, vnr); un = (un - unr) * scale + unr; vn = (vn - vnr) * scale + vnr; } else if ((scale != 0) && ((scaleMode == 1) || (scaleMode == 2))) { float unr = 0, vnr = 0; - ref->mCorrection.getCorrectionInvUV(slice, row, u, v, unr, vnr); + ref->mCorrection.getCorrectionInvUV(roc, row, u, v, unr, vnr); un = (unr - u) * scale + un; // unr = u - duv[0]; vn = (vnr - v) * scale + vn; } if (ref2 && (scale2 != 0)) { float unr = 0, vnr = 0; - ref2->mCorrection.getCorrectionInvUV(slice, row, u, v, unr, vnr); + ref2->mCorrection.getCorrectionInvUV(roc, row, u, v, unr, vnr); un = (unr - u) * scale2 + un; // unr = u - duv[0]; vn = (vnr - v) * scale2 + vn; } @@ -872,11 +872,11 @@ GPUdi() void TPCFastTransform::InverseTransformYZtoNominalYZ(int32_t slice, int3 un = u; vn = v; } - getGeometry().convUVtoLocal(slice, un, vn, ny, nz); + getGeometry().convUVtoLocal(roc, un, vn, ny, nz); GPUCA_DEBUG_STREAMER_CHECK(if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamFastTransform)) { o2::utils::DebugStreamer::instance()->getStreamer("debug_fasttransform", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_InverseTransformYZtoNominalYZ").data() - << "slice=" << slice + << "roc=" << roc << "row=" << row << "scale=" << scale << "y=" << y @@ -891,7 +891,7 @@ GPUdi() void TPCFastTransform::InverseTransformYZtoNominalYZ(int32_t slice, int3 }) } -GPUdi() void TPCFastTransform::InverseTransformXYZtoNominalXYZ(int32_t slice, int32_t row, float x, float y, float z, float& nx, float& ny, float& nz, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const +GPUdi() void TPCFastTransform::InverseTransformXYZtoNominalXYZ(int32_t roc, int32_t row, float x, float y, float z, float& nx, float& ny, float& nz, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const { /// Inverse transformation: Transformed X, Y and Z -> X, Y and Z, transformed w/o space charge correction int32_t row2 = row + 1; @@ -902,8 +902,8 @@ GPUdi() void TPCFastTransform::InverseTransformXYZtoNominalXYZ(int32_t slice, in float nx2, ny2, nz2; // nominal coordinates for row2 nx1 = getGeometry().getRowInfo(row).x; nx2 = getGeometry().getRowInfo(row2).x; - InverseTransformYZtoNominalYZ(slice, row, y, z, ny1, nz1, ref, ref2, scale, scale2, scaleMode); - InverseTransformYZtoNominalYZ(slice, row2, y, z, ny2, nz2, ref, ref2, scale, scale2, scaleMode); + InverseTransformYZtoNominalYZ(roc, row, y, z, ny1, nz1, ref, ref2, scale, scale2, scaleMode); + InverseTransformYZtoNominalYZ(roc, row2, y, z, ny2, nz2, ref, ref2, scale, scale2, scaleMode); float c1 = (nx2 - nx) / (nx2 - nx1); float c2 = (nx - nx1) / (nx2 - nx1); nx = x; diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx b/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx index 3c624b3222d77..b472868fa1071 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx @@ -28,14 +28,14 @@ using namespace o2::gpu; TPCFastTransformGeo::TPCFastTransformGeo() { // Default Constructor: creates an empty uninitialized object - double dAlpha = 2. * M_PI / (NumberOfSlicesA); - for (int32_t i = 0; i < NumberOfSlices; i++) { - SliceInfo& s = mSliceInfos[i]; + double dAlpha = 2. * M_PI / (NumberOfRocsA); + for (int32_t i = 0; i < NumberOfRocs; i++) { + RocInfo& s = mRocInfos[i]; double alpha = dAlpha * (i + 0.5); s.sinAlpha = sin(alpha); s.cosAlpha = cos(alpha); } - mSliceInfos[NumberOfSlices] = SliceInfo{0.f, 0.f}; + mRocInfos[NumberOfRocs] = RocInfo{0.f, 0.f}; for (int32_t i = 0; i < MaxNumberOfRows + 1; i++) { mRowInfos[i] = RowInfo{0.f, -1, 0.f, 0.f, 0.f, 0.f}; @@ -104,7 +104,7 @@ void TPCFastTransformGeo::setTPCrow(int32_t iRow, float x, int32_t nPads, float // Make scaled U = area between the geometrical sector borders - const double sectorAngle = 2. * M_PI / NumberOfSlicesA; + const double sectorAngle = 2. * M_PI / NumberOfRocsA; const double scaleXtoRowWidth = 2. * tan(0.5 * sectorAngle); double uWidth = x * scaleXtoRowWidth; // distance to the sector border @@ -148,7 +148,7 @@ void TPCFastTransformGeo::print() const #endif } -int32_t TPCFastTransformGeo::test(int32_t slice, int32_t row, float ly, float lz) const +int32_t TPCFastTransformGeo::test(int32_t roc, int32_t row, float ly, float lz) const { /// Check consistency of the class @@ -164,16 +164,16 @@ int32_t TPCFastTransformGeo::test(int32_t slice, int32_t row, float ly, float lz float lx1 = 0.f, ly1 = 0.f, lz1 = 0.f; float gx = 0.f, gy = 0.f, gz = 0.f; - convLocalToGlobal(slice, lx, ly, lz, gx, gy, gz); - convGlobalToLocal(slice, gx, gy, gz, lx1, ly1, lz1); + convLocalToGlobal(roc, lx, ly, lz, gx, gy, gz); + convGlobalToLocal(roc, gx, gy, gz, lx1, ly1, lz1); if (fabs(lx1 - lx) > 1.e-4 || fabs(ly1 - ly) > 1.e-4 || fabs(lz1 - lz) > 1.e-7) { LOG(info) << "Error local <-> global: x " << lx << " dx " << lx1 - lx << " y " << ly << " dy " << ly1 - ly << " z " << lz << " dz " << lz1 - lz; error = -3; } float u = 0.f, v = 0.f; - convLocalToUV(slice, ly, lz, u, v); - convUVtoLocal(slice, u, v, ly1, lz1); + convLocalToUV(roc, ly, lz, u, v); + convUVtoLocal(roc, u, v, ly1, lz1); if (fabs(ly1 - ly) + fabs(lz1 - lz) > 1.e-6) { LOG(info) << "Error local <-> UV: y " << ly << " dy " << ly1 - ly << " z " << lz << " dz " << lz1 - lz; @@ -182,7 +182,7 @@ int32_t TPCFastTransformGeo::test(int32_t slice, int32_t row, float ly, float lz float su = 0.f, sv = 0.f; - convUVtoScaledUV(slice, row, u, v, su, sv); + convUVtoScaledUV(roc, row, u, v, su, sv); if (su < 0.f || su > 1.f) { LOG(info) << "Error scaled U range: u " << u << " su " << su; @@ -190,7 +190,7 @@ int32_t TPCFastTransformGeo::test(int32_t slice, int32_t row, float ly, float lz } float u1 = 0.f, v1 = 0.f; - convScaledUVtoUV(slice, row, su, sv, u1, v1); + convScaledUVtoUV(roc, row, su, sv, u1, v1); if (fabs(u1 - u) > 1.e-4 || fabs(v1 - v) > 1.e-4) { LOG(info) << "Error UV<->scaled UV: u " << u << " du " << u1 - u << " v " << v << " dv " << v1 - v; diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.h b/GPU/TPCFastTransformation/TPCFastTransformGeo.h index d20331ba6ab0f..3382d1d926ce2 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.h +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.h @@ -34,11 +34,11 @@ namespace gpu class TPCFastTransformGeo { public: - /// The struct contains necessary info for TPC slice - struct SliceInfo { + /// The struct contains necessary info for TPC ROC + struct RocInfo { float sinAlpha; float cosAlpha; - ClassDefNV(SliceInfo, 1); + ClassDefNV(RocInfo, 1); }; /// The struct contains necessary info about TPC padrow @@ -58,6 +58,7 @@ class TPCFastTransformGeo /// get width in U GPUd() float getUwidth() const { return -2.f * u0; } + ClassDefNV(RowInfo, 1); }; @@ -107,11 +108,11 @@ class TPCFastTransformGeo /// _______________ Getters _________________________________ - /// Gives number of TPC slices - GPUd() static constexpr int32_t getNumberOfSlices() { return NumberOfSlices; } + /// Gives number of TPC ROCs + GPUd() static constexpr int32_t getNumberOfRocs() { return NumberOfRocs; } - /// Gives number of TPC slices in A side - GPUd() static constexpr int32_t getNumberOfSlicesA() { return NumberOfSlicesA; } + /// Gives number of TPC ROCs on the A side + GPUd() static constexpr int32_t getNumberOfRocsA() { return NumberOfRocsA; } /// Gives number of TPC rows GPUd() int32_t getNumberOfRows() const { return mNumberOfRows; } @@ -119,8 +120,8 @@ class TPCFastTransformGeo /// Gives number of TPC rows GPUd() static constexpr int getMaxNumberOfRows() { return MaxNumberOfRows; } - /// Gives slice info - GPUd() const SliceInfo& getSliceInfo(int32_t slice) const; + /// Gives roc info + GPUd() const RocInfo& getRocInfo(int32_t roc) const; /// Gives TPC row info GPUd() const RowInfo& getRowInfo(int32_t row) const; @@ -131,11 +132,11 @@ class TPCFastTransformGeo /// Gives Z length of the TPC, side C GPUd() float getTPCzLengthC() const { return mTPCzLengthC; } - /// Gives Z length of the TPC, depending on the slice - GPUd() float getTPCzLength(int32_t slice) const + /// Gives Z length of the TPC, depending on the roc + GPUd() float getTPCzLength(int32_t roc) const { - return (slice < NumberOfSlicesA) ? mTPCzLengthA - : mTPCzLengthC; + return (roc < NumberOfRocsA) ? mTPCzLengthA + : mTPCzLengthC; } /// Gives TPC alignment in Z @@ -144,26 +145,26 @@ class TPCFastTransformGeo /// _______________ Conversion of coordinate systems __________ /// convert Local -> Global c.s. - GPUd() void convLocalToGlobal(int32_t slice, float lx, float ly, float lz, float& gx, float& gy, float& gz) const; + GPUd() void convLocalToGlobal(int32_t roc, float lx, float ly, float lz, float& gx, float& gy, float& gz) const; /// convert Global->Local c.s. - GPUd() void convGlobalToLocal(int32_t slice, float gx, float gy, float gz, float& lx, float& ly, float& lz) const; + GPUd() void convGlobalToLocal(int32_t roc, float gx, float gy, float gz, float& lx, float& ly, float& lz) const; /// convert UV -> Local c.s. - GPUd() void convUVtoLocal(int32_t slice, float u, float v, float& y, float& z) const; - GPUd() void convVtoLocal(int32_t slice, float v, float& z) const; + GPUd() void convUVtoLocal(int32_t roc, float u, float v, float& y, float& z) const; + GPUd() void convVtoLocal(int32_t roc, float v, float& z) const; /// convert Local-> UV c.s. - GPUd() void convLocalToUV(int32_t slice, float y, float z, float& u, float& v) const; + GPUd() void convLocalToUV(int32_t roc, float y, float z, float& u, float& v) const; /// convert UV -> Scaled UV - GPUd() void convUVtoScaledUV(int32_t slice, int32_t row, float u, float v, float& su, float& sv) const; + GPUd() void convUVtoScaledUV(int32_t roc, int32_t row, float u, float v, float& su, float& sv) const; /// convert Scaled UV -> UV - GPUd() void convScaledUVtoUV(int32_t slice, int32_t row, float su, float sv, float& u, float& v) const; + GPUd() void convScaledUVtoUV(int32_t roc, int32_t row, float su, float sv, float& u, float& v) const; /// convert Scaled UV -> Local c.s. - GPUd() void convScaledUVtoLocal(int32_t slice, int32_t row, float su, float sv, float& ly, float& lz) const; + GPUd() void convScaledUVtoLocal(int32_t roc, int32_t row, float su, float sv, float& ly, float& lz) const; /// convert Pad coordinate -> U GPUd() float convPadToU(int32_t row, float pad) const; @@ -175,7 +176,7 @@ class TPCFastTransformGeo void print() const; /// Method for testing consistency - int32_t test(int32_t slice, int32_t row, float ly, float lz) const; + int32_t test(int32_t roc, int32_t row, float ly, float lz) const; /// Method for testing consistency int32_t test() const; @@ -183,9 +184,9 @@ class TPCFastTransformGeo private: /// _______________ Data members _______________________________________________ - static constexpr int32_t NumberOfSlices = 36; ///< Number of TPC slices ( slice = inner + outer sector ) - static constexpr int32_t NumberOfSlicesA = NumberOfSlices / 2; ///< Number of TPC slices side A - static constexpr int32_t MaxNumberOfRows = 160; ///< Max Number of TPC rows in a slice + static constexpr int32_t NumberOfRocs = 36; ///< Number of TPC rocs ( roc = inner + outer sector ) + static constexpr int32_t NumberOfRocsA = NumberOfRocs / 2; ///< Number of TPC rocs side A + static constexpr int32_t MaxNumberOfRows = 160; ///< Max Number of TPC rows in a roc /// _______________ Construction control _______________________________________________ @@ -211,23 +212,23 @@ class TPCFastTransformGeo float mScaleSVtoVsideA = 0.f; ///< scale for sv->v for TPC side A float mScaleSVtoVsideC = 0.f; ///< scale for sv->v for TPC side C - SliceInfo mSliceInfos[NumberOfSlices + 1]; ///< array of slice information [fixed size] + RocInfo mRocInfos[NumberOfRocs + 1]; ///< array of roc information [fixed size] RowInfo mRowInfos[MaxNumberOfRows + 1]; ///< array of row information [fixed size] - ClassDefNV(TPCFastTransformGeo, 1); + ClassDefNV(TPCFastTransformGeo, 2); }; // ======================================================================= // Inline implementations of some methods // ======================================================================= -GPUdi() const TPCFastTransformGeo::SliceInfo& TPCFastTransformGeo::getSliceInfo(int32_t slice) const +GPUdi() const TPCFastTransformGeo::RocInfo& TPCFastTransformGeo::getRocInfo(int32_t roc) const { - /// Gives slice info - if (slice < 0 || slice >= NumberOfSlices) { // return zero object - slice = NumberOfSlices; + /// Gives roc info + if (roc < 0 || roc >= NumberOfRocs) { // return zero object + roc = NumberOfRocs; } - return mSliceInfos[slice]; + return mRocInfos[roc]; } GPUdi() const TPCFastTransformGeo::RowInfo& TPCFastTransformGeo::getRowInfo(int32_t row) const @@ -239,28 +240,28 @@ GPUdi() const TPCFastTransformGeo::RowInfo& TPCFastTransformGeo::getRowInfo(int3 return mRowInfos[row]; } -GPUdi() void TPCFastTransformGeo::convLocalToGlobal(int32_t slice, float lx, float ly, float lz, float& gx, float& gy, float& gz) const +GPUdi() void TPCFastTransformGeo::convLocalToGlobal(int32_t roc, float lx, float ly, float lz, float& gx, float& gy, float& gz) const { /// convert Local -> Global c.s. - const SliceInfo& sliceInfo = getSliceInfo(slice); - gx = lx * sliceInfo.cosAlpha - ly * sliceInfo.sinAlpha; - gy = lx * sliceInfo.sinAlpha + ly * sliceInfo.cosAlpha; + const RocInfo& rocInfo = getRocInfo(roc); + gx = lx * rocInfo.cosAlpha - ly * rocInfo.sinAlpha; + gy = lx * rocInfo.sinAlpha + ly * rocInfo.cosAlpha; gz = lz; } -GPUdi() void TPCFastTransformGeo::convGlobalToLocal(int32_t slice, float gx, float gy, float gz, float& lx, float& ly, float& lz) const +GPUdi() void TPCFastTransformGeo::convGlobalToLocal(int32_t roc, float gx, float gy, float gz, float& lx, float& ly, float& lz) const { /// convert Global -> Local c.s. - const SliceInfo& sliceInfo = getSliceInfo(slice); - lx = gx * sliceInfo.cosAlpha + gy * sliceInfo.sinAlpha; - ly = -gx * sliceInfo.sinAlpha + gy * sliceInfo.cosAlpha; + const RocInfo& rocInfo = getRocInfo(roc); + lx = gx * rocInfo.cosAlpha + gy * rocInfo.sinAlpha; + ly = -gx * rocInfo.sinAlpha + gy * rocInfo.cosAlpha; lz = gz; } -GPUdi() void TPCFastTransformGeo::convVtoLocal(int32_t slice, float v, float& lz) const +GPUdi() void TPCFastTransformGeo::convVtoLocal(int32_t roc, float v, float& lz) const { /// convert UV -> Local c.s. - if (slice < NumberOfSlicesA) { // TPC side A + if (roc < NumberOfRocsA) { // TPC side A lz = mTPCzLengthA - v; } else { // TPC side C lz = v - mTPCzLengthC; // drift direction is mirrored on C-side @@ -268,10 +269,10 @@ GPUdi() void TPCFastTransformGeo::convVtoLocal(int32_t slice, float v, float& lz lz += mTPCalignmentZ; // global TPC alignment } -GPUdi() void TPCFastTransformGeo::convUVtoLocal(int32_t slice, float u, float v, float& ly, float& lz) const +GPUdi() void TPCFastTransformGeo::convUVtoLocal(int32_t roc, float u, float v, float& ly, float& lz) const { /// convert UV -> Local c.s. - if (slice < NumberOfSlicesA) { // TPC side A + if (roc < NumberOfRocsA) { // TPC side A ly = u; lz = mTPCzLengthA - v; } else { // TPC side C @@ -281,11 +282,11 @@ GPUdi() void TPCFastTransformGeo::convUVtoLocal(int32_t slice, float u, float v, lz += mTPCalignmentZ; // global TPC alignment } -GPUdi() void TPCFastTransformGeo::convLocalToUV(int32_t slice, float ly, float lz, float& u, float& v) const +GPUdi() void TPCFastTransformGeo::convLocalToUV(int32_t roc, float ly, float lz, float& u, float& v) const { /// convert Local-> UV c.s. lz = lz - mTPCalignmentZ; // global TPC alignment - if (slice < NumberOfSlicesA) { // TPC side A + if (roc < NumberOfRocsA) { // TPC side A u = ly; v = mTPCzLengthA - lz; } else { // TPC side C @@ -294,36 +295,36 @@ GPUdi() void TPCFastTransformGeo::convLocalToUV(int32_t slice, float ly, float l } } -GPUdi() void TPCFastTransformGeo::convUVtoScaledUV(int32_t slice, int32_t row, float u, float v, float& su, float& sv) const +GPUdi() void TPCFastTransformGeo::convUVtoScaledUV(int32_t roc, int32_t row, float u, float v, float& su, float& sv) const { /// convert UV -> Scaled UV const RowInfo& rowInfo = getRowInfo(row); su = (u - rowInfo.u0) * rowInfo.scaleUtoSU; - if (slice < NumberOfSlicesA) { + if (roc < NumberOfRocsA) { sv = v * mScaleVtoSVsideA; } else { sv = v * mScaleVtoSVsideC; } } -GPUdi() void TPCFastTransformGeo::convScaledUVtoUV(int32_t slice, int32_t row, float su, float sv, float& u, float& v) const +GPUdi() void TPCFastTransformGeo::convScaledUVtoUV(int32_t roc, int32_t row, float su, float sv, float& u, float& v) const { /// convert Scaled UV -> UV const RowInfo& rowInfo = getRowInfo(row); u = rowInfo.u0 + su * rowInfo.scaleSUtoU; - if (slice < NumberOfSlicesA) { + if (roc < NumberOfRocsA) { v = sv * mScaleSVtoVsideA; } else { v = sv * mScaleSVtoVsideC; } } -GPUdi() void TPCFastTransformGeo::convScaledUVtoLocal(int32_t slice, int32_t row, float su, float sv, float& ly, float& lz) const +GPUdi() void TPCFastTransformGeo::convScaledUVtoLocal(int32_t roc, int32_t row, float su, float sv, float& ly, float& lz) const { /// convert Scaled UV -> Local c.s. float u, v; - convScaledUVtoUV(slice, row, su, sv, u, v); - convUVtoLocal(slice, u, v, ly, lz); + convScaledUVtoUV(roc, row, su, sv, u, v); + convUVtoLocal(roc, u, v, ly, lz); } GPUdi() float TPCFastTransformGeo::convPadToU(int32_t row, float pad) const diff --git a/GPU/TPCFastTransformation/TPCFastTransformManager.cxx b/GPU/TPCFastTransformation/TPCFastTransformManager.cxx index 7d0aa29545578..aa28b6a414876 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformManager.cxx +++ b/GPU/TPCFastTransformation/TPCFastTransformManager.cxx @@ -87,14 +87,14 @@ int32_t TPCFastTransformManager::create(TPCFastTransform& fastTransform, float tpcZlengthSideA = tpcParam->GetZLength(0); float tpcZlengthSideC = - tpcParam->GetZLength(TPCFastTransformGeo::getNumberOfSlices() / 2); + tpcParam->GetZLength(TPCFastTransformGeo::getNumberOfRocs() / 2); geo.setTPCzLength(tpcZlengthSideA, tpcZlengthSideC); geo.setTPCalignmentZ(-mOrigTransform->GetDeltaZCorrTime()); for (int32_t row = 0; row < geo.getNumberOfRows(); row++) { - int32_t slice = 0, sector = 0, secrow = 0; - AliHLTTPCGeometry::Slice2Sector(slice, row, sector, secrow); + int32_t roc = 0, sector = 0, secrow = 0; + AliHLTTPCGeometry::Slice2Sector(roc, row, sector, secrow); Int_t nPads = tpcParam->GetNPads(sector, secrow); float xRow = tpcParam->GetPadRowRadii(sector, secrow); float padWidth = tpcParam->GetInnerPadPitchWidth(); @@ -272,40 +272,40 @@ int32_t TPCFastTransformManager::updateCalibration(TPCFastTransform& fastTransfo recoParam->SetUseTOFCorrection(kFALSE); - for (int32_t slice = 0; slice < geo.getNumberOfSlices(); slice++) { + for (int32_t roc = 0; roc < geo.getNumberOfRocs(); roc++) { for (int32_t row = 0; row < geo.getNumberOfRows(); row++) { const TPCFastTransformGeo::RowInfo& rowInfo = geo.getRowInfo(row); - const TPCFastSpaceChargeCorrection::SplineType& spline = correction.getSpline(slice, row); - float* data = correction.getSplineData(slice, row); + const TPCFastSpaceChargeCorrection::SplineType& spline = correction.getSpline(roc, row); + float* data = correction.getSplineData(roc, row); Spline2DHelper helper; helper.setSpline(spline, 4, 4); auto F = [&](double su, double sv, double dxuv[3]) { float x = rowInfo.x; - // x, u, v cordinates of the knot (local cartesian coord. of slice + // x, u, v cordinates of the knot (local cartesian coord. of roc // towards central electrode ) float u = 0, v = 0; - geo.convScaledUVtoUV(slice, row, su, sv, u, v); + geo.convScaledUVtoUV(roc, row, su, sv, u, v); // row, pad, time coordinates of the knot float vertexTime = 0.f; float pad = 0.f, time = 0.f; - fastTransform.convUVtoPadTime(slice, row, u, v, pad, time, vertexTime); + fastTransform.convUVtoPadTime(roc, row, u, v, pad, time, vertexTime); // nominal x,y,z coordinates of the knot (without corrections and // time-of-flight correction) float y = 0, z = 0; - geo.convUVtoLocal(slice, u, v, y, z); + geo.convUVtoLocal(roc, u, v, y, z); // original TPC transformation (row,pad,time) -> (x,y,z) without // time-of-flight correction float ox = 0, oy = 0, oz = 0; { int32_t sector = 0, secrow = 0; - AliHLTTPCGeometry::Slice2Sector(slice, row, sector, secrow); + AliHLTTPCGeometry::Slice2Sector(roc, row, sector, secrow); int32_t is[] = {sector}; double xx[] = {static_cast(secrow), pad, time}; mOrigTransform->Transform(xx, is, 0, 1); @@ -315,7 +315,7 @@ int32_t TPCFastTransformManager::updateCalibration(TPCFastTransform& fastTransfo } // convert to u,v float ou = 0, ov = 0; - geo.convLocalToUV(slice, oy, oz, ou, ov); + geo.convLocalToUV(roc, oy, oz, ou, ov); // corrections in x,u,v: dxuv[0] = ox - x; @@ -325,7 +325,7 @@ int32_t TPCFastTransformManager::updateCalibration(TPCFastTransform& fastTransfo helper.approximateFunction(data, 0., 1., 0., 1., F); } // row - } // slice + } // roc // set back the time-of-flight correction; diff --git a/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h b/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h index 4421d44aab0c8..7c1ae8fd56800 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h +++ b/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h @@ -59,7 +59,7 @@ #pragma link C++ class o2::gpu::IrregularSpline2D3DCalibrator + ; #pragma link C++ class o2::gpu::TPCFastTransformGeo + ; -#pragma link C++ class o2::gpu::TPCFastTransformGeo::SliceInfo + ; +#pragma link C++ class o2::gpu::TPCFastTransformGeo::RocInfo + ; #pragma link C++ class o2::gpu::TPCFastTransformGeo::RowInfo + ; #pragma link C++ class o2::gpu::TPCFastTransform + ; @@ -68,9 +68,9 @@ #pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::RowInfo + ; #pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection + ; -#pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::SliceInfo + ; +#pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::RocInfo + ; #pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::RowActiveArea + ; -#pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::SliceRowInfo + ; +#pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::RocRowInfo + ; #pragma link C++ class o2::gpu::CorrectionMapsHelper + ; #pragma link C++ struct o2::gpu::MultivariatePolynomialContainer + ; diff --git a/GPU/TPCFastTransformation/macro/generateTPCCorrectionNTuple.C b/GPU/TPCFastTransformation/macro/generateTPCCorrectionNTuple.C index 67a0f09522f60..69b7909cda683 100644 --- a/GPU/TPCFastTransformation/macro/generateTPCCorrectionNTuple.C +++ b/GPU/TPCFastTransformation/macro/generateTPCCorrectionNTuple.C @@ -82,12 +82,12 @@ void generateTPCCorrectionNTuple(const char* path = "InputSCDensityHistograms.ro const o2::gpu::TPCFastTransformGeo& geo = fastTransform->getGeometry(); TFile* f = new TFile("tpcCorrection.root", "RECREATE"); - TNtuple* nt = new TNtuple("dist", "dist", "slice:row:su:sv:dx:du:dv"); + TNtuple* nt = new TNtuple("dist", "dist", "sector:row:su:sv:dx:du:dv"); - int32_t nSlices = 1; // fastTransform->getNumberOfSlices(); - // for( int32_t slice=0; slicegetNumberOfSectors(); + // for( int32_t sector=0; sectorFill(slice, row, su, sv, dx, du, dv); + std::cout << sector << " " << row << " " << su << " " << sv << " " << dx << " " << du << " " << dv << std::endl; + nt->Fill(sector, row, su, sv, dx, du, dv); } } } From 45105a91fc28e811998b96ce64a6ebbe581b83f8 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Wed, 5 Feb 2025 16:19:23 +0000 Subject: [PATCH 089/285] TPC Splines: minimise the amount of transformations --- .../TPCFastSpaceChargeCorrectionHelper.cxx | 20 +- .../src/TPCFastTransformHelperO2.cxx | 25 +- .../test/testTPCFastTransform.cxx | 6 +- .../TPCFastSpaceChargeCorrection.cxx | 16 +- .../TPCFastSpaceChargeCorrection.h | 52 ++- .../TPCFastTransform.cxx | 16 +- GPU/TPCFastTransformation/TPCFastTransform.h | 413 +++++++----------- .../TPCFastTransformGeo.cxx | 71 +-- .../TPCFastTransformGeo.h | 116 +---- .../TPCFastTransformManager.cxx | 1 + 10 files changed, 262 insertions(+), 474 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index bac332a837c55..710a4356dd457 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -63,12 +63,8 @@ void TPCFastSpaceChargeCorrectionHelper::initGeometry() mGeo.startConstruction(nRows); auto& detParam = ParameterDetector::Instance(); - float tpcZlengthSideA = detParam.TPClength; - float tpcZlengthSideC = detParam.TPClength; - mGeo.setTPCzLength(tpcZlengthSideA, tpcZlengthSideC); - - mGeo.setTPCalignmentZ(0.); + mGeo.setTPCzLength(detParam.TPClength); for (int iRow = 0; iRow < mGeo.getNumberOfRows(); iRow++) { Sector sector = 0; @@ -295,7 +291,7 @@ std::unique_ptr TPCFastSpaceChargeCorrectionHelper auto myThread = [&](int iThread) { for (int iRow = iThread; iRow < nRows; iRow += mNthreads) { const auto& info = mGeo.getRowInfo(iRow); - double vMax = mGeo.getTPCzLength(iRoc); + double vMax = mGeo.getTPCzLength(); double dv = vMax / (6. * (nKnotsZ - 1)); double dpad = info.maxPad / (6. * (nKnotsY - 1)); @@ -512,8 +508,8 @@ std::unique_ptr TPCFastSpaceChargeCorrect double zMax = rowInfo.x * trackResiduals.getZ2X(trackResiduals.getNZ2XBins() - 1); double uMin = yMin; double uMax = yMax; - double vMin = geo.getTPCzLength(iRoc) - zMax; - double vMax = geo.getTPCzLength(iRoc) - zMin; + double vMin = geo.getTPCzLength() - zMax; + double vMax = geo.getTPCzLength() - zMin; info.gridU0 = uMin; info.scaleUtoGrid = spline.getGridX1().getUmax() / (uMax - uMin); info.gridV0 = vMin; @@ -822,7 +818,7 @@ void TPCFastSpaceChargeCorrectionHelper::initMaxDriftLength(o2::gpu::TPCFastSpac if (prn) { LOG(info) << "init MaxDriftLength for roc " << roc; } - double vLength = (roc < mGeo.getNumberOfRocsA()) ? mGeo.getTPCzLengthA() : mGeo.getTPCzLengthC(); + double vLength = mGeo.getTPCzLength(); TPCFastSpaceChargeCorrection::RocInfo& rocInfo = correction.getRocInfo(roc); rocInfo.vMax = 0.f; @@ -843,7 +839,7 @@ void TPCFastSpaceChargeCorrectionHelper::initMaxDriftLength(o2::gpu::TPCFastSpac while (v1 - v0 > 0.1) { float v = 0.5 * (v0 + v1); float dx, du, dv; - correction.getCorrection(roc, row, u, v, dx, du, dv); + correction.getCorrectionInternal(roc, row, u, v, dx, du, dv); double cx = x + dx; double cu = u + du; double cv = v + dv; @@ -962,14 +958,14 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vectorgetCorrection(roc, row, u, v, dxTmp, duTmp, dvTmp); + corrections[i]->getCorrectionInternal(roc, row, u, v, dxTmp, duTmp, dvTmp); dx += dxTmp * scaling[i]; du += duTmp * scaling[i]; dv += dvTmp * scaling[i]; diff --git a/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx b/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx index cfa54a12f9f42..c83ee6d0cfa19 100644 --- a/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx +++ b/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx @@ -56,12 +56,7 @@ void TPCFastTransformHelperO2::init() mGeo.startConstruction(nRows); auto& detParam = ParameterDetector::Instance(); - float tpcZlengthSideA = detParam.TPClength; - float tpcZlengthSideC = detParam.TPClength; - - mGeo.setTPCzLength(tpcZlengthSideA, tpcZlengthSideC); - - mGeo.setTPCalignmentZ(0.); + mGeo.setTPCzLength(detParam.TPClength); for (int iRow = 0; iRow < mGeo.getNumberOfRows(); iRow++) { Sector sector = 0; @@ -114,12 +109,8 @@ std::unique_ptr TPCFastTransformHelperO2::create(Long_t TimeSt // set some initial calibration values, will be reinitialised later int updateCalibration() const float t0 = 0.; const float vDrift = 0.f; - const float vdCorrY = 0.; - const float ldCorr = 0.; - const float tofCorr = 0.; - const float primVtxZ = 0.; const long int initTimeStamp = -1; - fastTransform.setCalibration(initTimeStamp, t0, vDrift, vdCorrY, ldCorr, tofCorr, primVtxZ); + fastTransform.setCalibration1(initTimeStamp, t0, vDrift); fastTransform.finishConstruction(); } @@ -171,19 +162,13 @@ int TPCFastTransformHelperO2::updateCalibration(TPCFastTransform& fastTransform, const double vDrift = elParam.ZbinWidth * vDriftRef * vDriftFactor; // cm/timebin // fast transform formula: - // L = (t-t0)*(mVdrift + mVdriftCorrY*yLab ) + mLdriftCorr - // Z = Z(L) + tpcAlignmentZ + // L = (t-t0)*mVdrift + // Z = Z(L) // spline corrections for xyz - // Time-of-flight correction: ldrift += dist-to-vtx*tofCorr const double t0 = (driftTimeOffset + elParam.getAverageShapingTime()) / elParam.ZbinWidth; - const double vdCorrY = 0.; - const double ldCorr = 0.; - const double tofCorr = 0.; - const double primVtxZ = 0.; - - fastTransform.setCalibration(TimeStamp, t0, vDrift, vdCorrY, ldCorr, tofCorr, primVtxZ); + fastTransform.setCalibration1(TimeStamp, t0, vDrift); return 0; } diff --git a/Detectors/TPC/reconstruction/test/testTPCFastTransform.cxx b/Detectors/TPC/reconstruction/test/testTPCFastTransform.cxx index 0141b80819b64..53cfe08f3a7f4 100644 --- a/Detectors/TPC/reconstruction/test/testTPCFastTransform.cxx +++ b/Detectors/TPC/reconstruction/test/testTPCFastTransform.cxx @@ -72,7 +72,7 @@ BOOST_AUTO_TEST_CASE(FastTransform_test1) const GlobalPadNumber p = mapper.globalPadNumber(PadPos(row, pad)); const PadCentre& c = mapper.padCentre(p); float u = 0, v = 0; - fastTransform.convPadTimeToUV(0, row, pad, 0, u, v, 0.); + fastTransform.convPadTimeToUV(row, pad, 0, u, v, 0.); double dx = x - c.X(); double dy = u - (-c.Y()); // diferent sign convention for Y coordinate in the map @@ -192,8 +192,8 @@ BOOST_AUTO_TEST_CASE(FastTransform_test_setSpaceChargeCorrection) correctionUV(roc, row, u0, v0, dx, du, dv); statDiff += fabs((x1 - x0) - dx) + fabs((u1 - u0) - du) + fabs((v1 - v0) - dv); statN += 3; - //std::cout << (x1 - x0) - dx << " " << (u1 - u0) - du << " " << (v1 - v0) - dv << std::endl; //": v0 " << v0 <<" z0 "<Transform(roc, row, pad, time, x1f, y1f, z1f); diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx index 5d3c186a06d42..111e70072c58e 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx @@ -225,7 +225,7 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer info.scaleUtoGrid = spline.getGridX1().getUmax() / mGeo.getRowInfo(row).getUwidth(); info.gridV0 = infoOld.gridV0; - info.scaleVtoGrid = spline.getGridX2().getUmax() / (mGeo.getTPCzLength(roc) + 3. - info.gridV0); + info.scaleVtoGrid = spline.getGridX2().getUmax() / (mGeo.getTPCzLength() + 3. - info.gridV0); info.gridCorrU0 = infoOld.gridCorrU0; info.scaleCorrUtoGrid = infoOld.scaleCorrUtoGrid; @@ -440,7 +440,7 @@ GPUd() void TPCFastSpaceChargeCorrection::setNoCorrection() { // initialise all corrections to 0. for (int32_t roc = 0; roc < mGeo.getNumberOfRocs(); roc++) { - double vLength = (roc < mGeo.getNumberOfRocsA()) ? mGeo.getTPCzLengthA() : mGeo.getTPCzLengthC(); + double vLength = mGeo.getTPCzLength(); RocInfo& rocInfo = getRocInfo(roc); rocInfo.vMax = vLength; for (int32_t row = 0; row < mGeo.getNumberOfRows(); row++) { @@ -548,12 +548,14 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) if (prn) { LOG(info) << "check inverse transform for roc " << roc; } - double vLength = mGeo.getTPCzLength(roc); + double vLength = mGeo.getTPCzLength(); MaxValue maxDroc[3]; for (int32_t row = 0; row < mGeo.getNumberOfRows(); row++) { - float u0, u1, v0, v1; - mGeo.convScaledUVtoUV(roc, row, 0., 0., u0, v0); - mGeo.convScaledUVtoUV(roc, row, 1., 1., u1, v1); + float u0 = mGeo.getRowInfo(row).getUmin(); + float u1 = mGeo.getRowInfo(row).getUmax(); + float v0 = 0.; + float v1 = vLength; + double x = mGeo.getRowInfo(row).x; double stepU = (u1 - u0) / 100.; double stepV = (v1 - v0) / 100.; @@ -564,7 +566,7 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) continue; } float dx, du, dv; - getCorrection(roc, row, u, v, dx, du, dv); + getCorrectionInternal(roc, row, u, v, dx, du, dv); double cx = x + dx; double cu = u + du; double cv = v + dv; diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index b6244bfee1e0f..fa5cf7a1736bd 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -180,7 +180,9 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// _______________ The main method: cluster correction _______________________ /// - GPUd() int32_t getCorrection(int32_t roc, int32_t row, float u, float v, float& dx, float& du, float& dv) const; + GPUd() int32_t getCorrectionInternal(int32_t roc, int32_t row, float u, float v, float& dx, float& du, float& dv) const; + + GPUdi() std::tuple getCorrectionLocal(int32_t roc, int32_t row, float y, float z) const; /// inverse correction: Corrected U and V -> coorrected X GPUd() void getCorrectionInvCorrectedX(int32_t roc, int32_t row, float corrU, float corrV, float& corrX) const; @@ -199,6 +201,10 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// _______________ Utilities _______________________________________________ + /// convert local y, z to internal grid coordinates u,v + /// return values: u, v, scaling factor + GPUd() std::tuple convLocalToGrid(int32_t roc, int32_t row, float y, float z) const; + /// convert u,v to internal grid coordinates GPUd() void convUVtoGrid(int32_t roc, int32_t row, float u, float v, float& gridU, float& gridV) const; @@ -259,9 +265,6 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// release temporary memory used during construction void releaseConstructionMemory(); - /// temporary method with the an way of calculating 2D spline - GPUd() int32_t getCorrectionOld(int32_t roc, int32_t row, float u, float v, float& dx, float& du, float& dv) const; - /// _______________ Data members _______________________________________________ /// _______________ Construction control _______________________________________________ @@ -354,7 +357,7 @@ GPUdi() void TPCFastSpaceChargeCorrection::convCorrectedUVtoGrid(int32_t roc, in gridV = (corrV - info.gridCorrV0) * info.scaleCorrVtoGrid; } -GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrection(int32_t roc, int32_t row, float u, float v, float& dx, float& du, float& dv) const +GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrectionInternal(int32_t roc, int32_t row, float u, float v, float& dx, float& du, float& dv) const { const auto& info = getRocRowInfo(roc, row); const SplineType& spline = getSpline(roc, row); @@ -382,26 +385,41 @@ GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrection(int32_t roc, int32_t return 0; } -GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrectionOld(int32_t roc, int32_t row, float u, float v, float& dx, float& du, float& dv) const +GPUdi() std::tuple TPCFastSpaceChargeCorrection::getCorrectionLocal(int32_t roc, int32_t row, float y, float z) const { + const auto& info = getRocRowInfo(roc, row); const SplineType& spline = getSpline(roc, row); const float* splineData = getSplineData(roc, row); + + float u, v; + + mGeo.convLocalToUV(roc, y, z, u, v); + float gridU = 0, gridV = 0; convUVtoGrid(roc, row, u, v, gridU, gridV); + // shrink to the grid area + gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); + gridV = GPUCommonMath::Clamp(gridV, 0.f, (float)spline.getGridX2().getUmax()); + float dxuv[3]; - spline.interpolateUold(splineData, gridU, gridV, dxuv); - const auto& info = getRocRowInfo(roc, row); + spline.interpolateU(splineData, gridU, gridV, dxuv); + float s = v / info.gridV0; - if (s < 0.) { - s = 0.; - } - if (s > 1.) { - s = 1.; + + if (v >= info.gridV0) { + s = 1.f; + } else if (v <= 0.f) { + s = 0.f; } - dx = GPUCommonMath::Max(info.minCorr[0], GPUCommonMath::Min(info.maxCorr[0], s * dxuv[0])); - du = GPUCommonMath::Max(info.minCorr[1], GPUCommonMath::Min(info.maxCorr[1], s * dxuv[1])); - dv = GPUCommonMath::Max(info.minCorr[2], GPUCommonMath::Min(info.maxCorr[2], s * dxuv[2])); - return 0; + + float dx = GPUCommonMath::Clamp(s * dxuv[0], info.minCorr[0], info.maxCorr[0]); + float du = GPUCommonMath::Clamp(s * dxuv[1], info.minCorr[1], info.maxCorr[1]); + float dv = GPUCommonMath::Clamp(s * dxuv[2], info.minCorr[2], info.maxCorr[2]); + + float dy, dz; + mGeo.convUVtoLocal(roc, du, dv, dy, dz); + + return {dx, dy, dz}; } GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvCorrectedX( diff --git a/GPU/TPCFastTransformation/TPCFastTransform.cxx b/GPU/TPCFastTransformation/TPCFastTransform.cxx index bd29a760615ad..625f70c1710a1 100644 --- a/GPU/TPCFastTransformation/TPCFastTransform.cxx +++ b/GPU/TPCFastTransformation/TPCFastTransform.cxx @@ -37,7 +37,7 @@ using namespace o2::gpu; TPCFastTransform::TPCFastTransform() - : FlatObject(), mTimeStamp(0), mCorrection(), mApplyCorrection(1), mT0(0.f), mVdrift(0.f), mVdriftCorrY(0.f), mLdriftCorr(0.f), mTOFcorr(0.f), mPrimVtxZ(0.f), mLumi(TPCFastTransform::DEFLUMI), mLumiError(0.f), mLumiScaleFactor(1.0f), mIDC(TPCFastTransform::DEFIDC), mIDCError(0.f), mCTP2IDCFallBackThreshold(30.f) + : FlatObject(), mTimeStamp(0), mCorrection(), mApplyCorrection(1), mT0(0.f), mVdrift(0.f), mLumi(TPCFastTransform::DEFLUMI), mLumiError(0.f), mLumiScaleFactor(1.0f), mIDC(TPCFastTransform::DEFIDC), mIDCError(0.f), mCTP2IDCFallBackThreshold(30.f) { // Default Constructor: creates an empty uninitialized object } @@ -54,10 +54,6 @@ void TPCFastTransform::cloneFromObject(const TPCFastTransform& obj, char* newFla mApplyCorrection = obj.mApplyCorrection; mT0 = obj.mT0; mVdrift = obj.mVdrift; - mVdriftCorrY = obj.mVdriftCorrY; - mLdriftCorr = obj.mLdriftCorr; - mTOFcorr = obj.mTOFcorr; - mPrimVtxZ = obj.mPrimVtxZ; mLumi = obj.mLumi; mLumiError = obj.mLumiError; mIDC = obj.mIDC; @@ -123,7 +119,7 @@ void TPCFastTransform::startConstruction(const TPCFastSpaceChargeCorrection& cor mCorrection.cloneFromObject(correction, nullptr); } -void TPCFastTransform::setCalibration(int64_t timeStamp, float t0, float vDrift, float vDriftCorrY, float lDriftCorr, float tofCorr, float primVtxZ) +void TPCFastTransform::setCalibration1(int64_t timeStamp, float t0, float vDrift) { /// Sets all drift calibration parameters and the time stamp /// @@ -133,10 +129,6 @@ void TPCFastTransform::setCalibration(int64_t timeStamp, float t0, float vDrift, mTimeStamp = timeStamp; mT0 = t0; mVdrift = vDrift; - mVdriftCorrY = vDriftCorrY; - mLdriftCorr = lDriftCorr; - mTOFcorr = tofCorr; - mPrimVtxZ = primVtxZ; mConstructionMask |= ConstructionExtraState::CalibrationIsSet; } @@ -160,10 +152,6 @@ void TPCFastTransform::print() const LOG(info) << "mApplyCorrection = " << mApplyCorrection; LOG(info) << "mT0 = " << mT0; LOG(info) << "mVdrift = " << mVdrift; - LOG(info) << "mVdriftCorrY = " << mVdriftCorrY; - LOG(info) << "mLdriftCorr = " << mLdriftCorr; - LOG(info) << "mTOFcorr = " << mTOFcorr; - LOG(info) << "mPrimVtxZ = " << mPrimVtxZ; LOG(info) << "mLumi = " << mLumi; LOG(info) << "mLumiError = " << mLumiError; LOG(info) << "mIDC = " << mIDC; diff --git a/GPU/TPCFastTransformation/TPCFastTransform.h b/GPU/TPCFastTransformation/TPCFastTransform.h index 8aef1748ebf62..1ecd577eb7dac 100644 --- a/GPU/TPCFastTransformation/TPCFastTransform.h +++ b/GPU/TPCFastTransformation/TPCFastTransform.h @@ -159,7 +159,7 @@ class TPCFastTransform : public FlatObject /// /// It must be called once during construction, /// but also may be called afterwards to reset these parameters. - void setCalibration(int64_t timeStamp, float t0, float vDrift, float vDriftCorrY, float lDriftCorr, float tofCorr, float primVtxZ); + void setCalibration1(int64_t timeStamp, float t0, float vDrift); /// Set Lumi info void setLumi(float l) { mLumi = l; } @@ -183,7 +183,7 @@ class TPCFastTransform : public FlatObject /// _______________ The main method: cluster transformation _______________________ /// /// Transforms raw TPC coordinates to local XYZ withing a roc - /// taking calibration + alignment into account. + /// taking calibration into account. /// GPUd() void Transform(int32_t roc, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime = 0, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; GPUd() void TransformXYZ(int32_t roc, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; @@ -209,12 +209,12 @@ class TPCFastTransform : public FlatObject GPUd() void TransformIdeal(int32_t roc, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime) const; GPUd() void TransformIdealZ(int32_t roc, float time, float& z, float vertexTime) const; - GPUd() void convPadTimeToUV(int32_t roc, int32_t row, float pad, float time, float& u, float& v, float vertexTime) const; - GPUd() void convPadTimeToUVinTimeFrame(int32_t roc, int32_t row, float pad, float time, float& u, float& v, float maxTimeBin) const; - GPUd() void convTimeToVinTimeFrame(int32_t roc, float time, float& v, float maxTimeBin) const; + GPUd() void convPadTimeToUV(int32_t row, float pad, float time, float& u, float& v, float vertexTime) const; + GPUd() void convPadTimeToUVinTimeFrame(int32_t row, float pad, float time, float& u, float& v, float maxTimeBin) const; + GPUd() void convTimeToVinTimeFrame(float time, float& v, float maxTimeBin) const; - GPUd() void convUVtoPadTime(int32_t roc, int32_t row, float u, float v, float& pad, float& time, float vertexTime) const; - GPUd() void convUVtoPadTimeInTimeFrame(int32_t roc, int32_t row, float u, float v, float& pad, float& time, float maxTimeBin) const; + GPUd() void convUVtoPadTime(int32_t row, float u, float v, float& pad, float& time, float vertexTime) const; + GPUd() void convUVtoPadTimeInTimeFrame(int32_t row, float u, float v, float& pad, float& time, float maxTimeBin) const; GPUd() void convVtoTime(float v, float& time, float vertexTime) const; GPUd() float convTimeToZinTimeFrame(int32_t roc, float time, float maxTimeBin) const; @@ -222,10 +222,8 @@ class TPCFastTransform : public FlatObject GPUd() float convDeltaTimeToDeltaZinTimeFrame(int32_t roc, float deltaTime) const; GPUd() float convDeltaZtoDeltaTimeInTimeFrame(int32_t roc, float deltaZ) const; GPUd() float convDeltaZtoDeltaTimeInTimeFrameAbs(float deltaZ) const; - GPUd() float convZOffsetToVertexTime(int32_t roc, float zOffset, float maxTimeBin) const; - GPUd() float convVertexTimeToZOffset(int32_t roc, float vertexTime, float maxTimeBin) const; - - GPUd() void getTOFcorrection(int32_t roc, int32_t row, float x, float y, float z, float& dz) const; + GPUd() float convZOffsetToVertexTime(int32_t sector, float zOffset, float maxTimeBin) const; + GPUd() float convVertexTimeToZOffset(int32_t sector, float vertexTime, float maxTimeBin) const; void setApplyCorrectionOn() { mApplyCorrection = 1; } void setApplyCorrectionOff() { mApplyCorrection = 0; } @@ -245,15 +243,6 @@ class TPCFastTransform : public FlatObject /// Return T0 in time bin units GPUd() float getT0() const { return mT0; } - /// Return VdriftCorrY in time_bin / cn - GPUd() float getVdriftCorrY() const { return mVdriftCorrY; } - - /// Return LdriftCorr offset in cm - GPUd() float getLdriftCorr() const { return mLdriftCorr; } - - /// Return TOF correction (vdrift / C) - GPUd() float getTOFCorr() const { return mLdriftCorr; } - /// Return map lumi GPUd() float getLumi() const { return mLumi; } @@ -330,23 +319,10 @@ class TPCFastTransform : public FlatObject /// /// t = (float) time bin, y = global y /// - /// L(t,y) = (t-mT0)*(mVdrift + mVdriftCorrY*y ) + mLdriftCorr ____ + /// L(t,y) = (t-mT0)*mVdrift ____ /// - float mT0; ///< T0 in [time bin] - float mVdrift; ///< VDrift in [cm/time bin] - float mVdriftCorrY; ///< VDrift correction for global Y[cm] in [1/time bin] - float mLdriftCorr; ///< drift length correction in [cm] - - /// A coefficient for Time-Of-Flight correction: drift length -= EstimatedDistanceToVtx[cm]*mTOFcorr - /// - /// Since this correction requires a knowledge of the spatial position, it is appied after mCorrection, - /// not on the drift length but directly on V coordinate. - /// - /// mTOFcorr == mVdrift/(speed of light) - /// - float mTOFcorr; - - float mPrimVtxZ; ///< Z of the primary vertex, needed for the Time-Of-Flight correction + float mT0; ///< T0 in [time bin] + float mVdrift; ///< VDrift in [cm/time bin] float mLumi; ///< luminosity estimator float mLumiError; ///< error on luminosity @@ -359,7 +335,7 @@ class TPCFastTransform : public FlatObject /// Correction of (x,u,v) with tricubic interpolator on a regular grid TPCSlowSpaceChargeCorrection* mCorrectionSlow{nullptr}; ///< reference space charge corrections - GPUd() void TransformInternal(int32_t roc, int32_t row, float& u, float& v, float& x, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const; + GPUd() void TransformLocal(int32_t roc, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const; ClassDefNV(TPCFastTransform, 4); }; @@ -368,248 +344,195 @@ class TPCFastTransform : public FlatObject // Inline implementations of some methods // ======================================================================= -GPUdi() void TPCFastTransform::convPadTimeToUV(int32_t roc, int32_t row, float pad, float time, float& u, float& v, float vertexTime) const +GPUdi() void TPCFastTransform::convPadTimeToUV(int32_t row, float pad, float time, float& u, float& v, float vertexTime) const { - bool sideC = (roc >= getGeometry().getNumberOfRocsA()); - const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); - const TPCFastTransformGeo::RocInfo& rocInfo = getGeometry().getRocInfo(roc); - float x = rowInfo.x; u = (pad - 0.5f * rowInfo.maxPad) * rowInfo.padWidth; - - float y = sideC ? -u : u; // pads are mirrorred on C-side - float yLab = y * rocInfo.cosAlpha + x * rocInfo.sinAlpha; - - v = (time - mT0 - vertexTime) * (mVdrift + mVdriftCorrY * yLab) + mLdriftCorr; // drift length cm + v = (time - mT0 - vertexTime) * (mVdrift); // drift length cm } -GPUdi() void TPCFastTransform::convTimeToVinTimeFrame(int32_t roc, float time, float& v, float maxTimeBin) const +GPUdi() void TPCFastTransform::convTimeToVinTimeFrame(float time, float& v, float maxTimeBin) const { - v = (time - mT0 - maxTimeBin) * mVdrift + mLdriftCorr; // drift length cm - if (roc < getGeometry().getNumberOfRocsA()) { - v += getGeometry().getTPCzLengthA(); - } else { - v += getGeometry().getTPCzLengthC(); - } + v = (time - mT0 - maxTimeBin) * mVdrift; // drift length cm + v += getGeometry().getTPCzLength(); } -GPUdi() void TPCFastTransform::convPadTimeToUVinTimeFrame(int32_t roc, int32_t row, float pad, float time, float& u, float& v, float maxTimeBin) const +GPUdi() void TPCFastTransform::convPadTimeToUVinTimeFrame(int32_t row, float pad, float time, float& u, float& v, float maxTimeBin) const { const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); u = (pad - 0.5f * rowInfo.maxPad) * rowInfo.padWidth; - convTimeToVinTimeFrame(roc, time, v, maxTimeBin); + convTimeToVinTimeFrame(time, v, maxTimeBin); } -GPUdi() float TPCFastTransform::convZOffsetToVertexTime(int32_t roc, float zOffset, float maxTimeBin) const +GPUdi() float TPCFastTransform::convZOffsetToVertexTime(int32_t sector, float zOffset, float maxTimeBin) const { - if (roc < getGeometry().getNumberOfRocsA()) { - return maxTimeBin - (getGeometry().getTPCzLengthA() + zOffset) / mVdrift; + if (sector < getGeometry().getNumberOfSectorsA()) { + return maxTimeBin - (getGeometry().getTPCzLength() + zOffset) / mVdrift; } else { - return maxTimeBin - (getGeometry().getTPCzLengthC() - zOffset) / mVdrift; + return maxTimeBin - (getGeometry().getTPCzLength() - zOffset) / mVdrift; } } -GPUdi() float TPCFastTransform::convVertexTimeToZOffset(int32_t roc, float vertexTime, float maxTimeBin) const +GPUdi() float TPCFastTransform::convVertexTimeToZOffset(int32_t sector, float vertexTime, float maxTimeBin) const { - if (roc < getGeometry().getNumberOfRocsA()) { - return (maxTimeBin - vertexTime) * mVdrift - getGeometry().getTPCzLengthA(); + if (sector < getGeometry().getNumberOfSectorsA()) { + return (maxTimeBin - vertexTime) * mVdrift - getGeometry().getTPCzLength(); } else { - return -((maxTimeBin - vertexTime) * mVdrift - getGeometry().getTPCzLengthC()); + return -((maxTimeBin - vertexTime) * mVdrift - getGeometry().getTPCzLength()); } } -GPUdi() void TPCFastTransform::convUVtoPadTime(int32_t roc, int32_t row, float u, float v, float& pad, float& time, float vertexTime) const +GPUdi() void TPCFastTransform::convUVtoPadTime(int32_t row, float u, float v, float& pad, float& time, float vertexTime) const { - bool sideC = (roc >= getGeometry().getNumberOfRocsA()); - const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); - const TPCFastTransformGeo::RocInfo& rocInfo = getGeometry().getRocInfo(roc); - pad = u / rowInfo.padWidth + 0.5f * rowInfo.maxPad; - - float x = rowInfo.x; - float y = sideC ? -u : u; // pads are mirrorred on C-side - float yLab = y * rocInfo.cosAlpha + x * rocInfo.sinAlpha; - time = mT0 + vertexTime + (v - mLdriftCorr) / (mVdrift + mVdriftCorrY * yLab); + time = mT0 + vertexTime + v / mVdrift; } GPUdi() void TPCFastTransform::convVtoTime(float v, float& time, float vertexTime) const { - float yLab = 0.f; - time = mT0 + vertexTime + (v - mLdriftCorr) / (mVdrift + mVdriftCorrY * yLab); + time = mT0 + vertexTime + v / mVdrift; } -GPUdi() void TPCFastTransform::convUVtoPadTimeInTimeFrame(int32_t roc, int32_t row, float u, float v, float& pad, float& time, float maxTimeBin) const +GPUdi() void TPCFastTransform::convUVtoPadTimeInTimeFrame(int32_t row, float u, float v, float& pad, float& time, float maxTimeBin) const { - if (roc < getGeometry().getNumberOfRocsA()) { - v -= getGeometry().getTPCzLengthA(); - } else { - v -= getGeometry().getTPCzLengthC(); - } + v -= getGeometry().getTPCzLength(); const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); pad = u / rowInfo.padWidth + 0.5f * rowInfo.maxPad; - time = mT0 + maxTimeBin + (v - mLdriftCorr) / mVdrift; + time = mT0 + maxTimeBin + v / mVdrift; } -GPUdi() void TPCFastTransform::getTOFcorrection(int32_t roc, int32_t /*row*/, float x, float y, float z, float& dz) const +GPUdi() void TPCFastTransform::TransformLocal(int32_t roc, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const { - // calculate time of flight correction for z coordinate + GPUCA_RTC_SPECIAL_CODE(ref2 = nullptr; scale2 = 0.f;); - bool sideC = (roc >= getGeometry().getNumberOfRocsA()); - float distZ = z - mPrimVtxZ; - float dv = -GPUCommonMath::Sqrt(x * x + y * y + distZ * distZ) * mTOFcorr; - dz = sideC ? dv : -dv; -} + if (!mApplyCorrection) { + return; + } -GPUdi() void TPCFastTransform::TransformInternal(int32_t roc, int32_t row, float& u, float& v, float& x, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const -{ - GPUCA_RTC_SPECIAL_CODE(ref2 = nullptr; scale2 = 0.f;); - if (mApplyCorrection) { - float dx = 0.f, du = 0.f, dv = 0.f; - if ((scale >= 0.f) || (scaleMode == 1) || (scaleMode == 2)) { + float dx = 0.f, dy = 0.f, dz = 0.f; + + if ((scale >= 0.f) || (scaleMode == 1) || (scaleMode == 2)) { #ifndef GPUCA_GPUCODE - if (mCorrectionSlow) { - float ly, lz; - getGeometry().convUVtoLocal(roc, u, v, ly, lz); - float gx, gy, gz; - getGeometry().convLocalToGlobal(roc, x, ly, lz, gx, gy, gz); - - float gdxC, gdyC, gdzC; - mCorrectionSlow->getCorrections(gx, gy, gz, roc, gdxC, gdyC, gdzC); - getGeometry().convGlobalToLocal(roc, gdxC, gdyC, gdzC, dx, du, dv); - - if (roc >= 18) { - du = -du; // mirror for c-Side - } else { - dv = -dv; // mirror z for A-Side - } - } else + if (mCorrectionSlow) { + float gx, gy, gz; + getGeometry().convLocalToGlobal(roc, x, y, z, gx, gy, gz); + float gdxC, gdyC, gdzC; + mCorrectionSlow->getCorrections(gx, gy, gz, roc, gdxC, gdyC, gdzC); + getGeometry().convGlobalToLocal(roc, gdxC, gdyC, gdzC, dx, dy, dz); + } else #endif // GPUCA_GPUCODE - { - mCorrection.getCorrection(roc, row, u, v, dx, du, dv); - if (ref) { - if ((scale > 0.f) && (scaleMode == 0)) { // scaling was requested - float dxRef, duRef, dvRef; - ref->mCorrection.getCorrection(roc, row, u, v, dxRef, duRef, dvRef); - dx = (dx - dxRef) * scale + dxRef; - du = (du - duRef) * scale + duRef; - dv = (dv - dvRef) * scale + dvRef; - } else if ((scale != 0.f) && ((scaleMode == 1) || (scaleMode == 2))) { - float dxRef, duRef, dvRef; - ref->mCorrection.getCorrection(roc, row, u, v, dxRef, duRef, dvRef); - dx = dxRef * scale + dx; - du = duRef * scale + du; - dv = dvRef * scale + dv; - } - } - if (ref2 && (scale2 != 0)) { - float dxRef, duRef, dvRef; - ref2->mCorrection.getCorrection(roc, row, u, v, dxRef, duRef, dvRef); - dx = dxRef * scale2 + dx; - du = duRef * scale2 + du; - dv = dvRef * scale2 + dv; + { + std::tie(dx, dy, dz) = mCorrection.getCorrectionLocal(roc, row, y, z); + if (ref) { + if ((scale > 0.f) && (scaleMode == 0)) { // scaling was requested + auto [dxRef, dyRef, dzRef] = ref->mCorrection.getCorrectionLocal(roc, row, y, z); + dx = (dx - dxRef) * scale + dxRef; + dy = (dy - dyRef) * scale + dyRef; + dz = (dz - dzRef) * scale + dzRef; + } else if ((scale != 0.f) && ((scaleMode == 1) || (scaleMode == 2))) { + auto [dxRef, dyRef, dzRef] = ref->mCorrection.getCorrectionLocal(roc, row, y, z); + dx = dxRef * scale + dx; + dy = dyRef * scale + dy; + dz = dzRef * scale + dz; } } + if (ref2 && (scale2 != 0)) { + auto [dxRef, dyRef, dzRef] = ref2->mCorrection.getCorrectionLocal(roc, row, y, z); + dx = dxRef * scale2 + dx; + dy = dyRef * scale2 + dy; + dz = dzRef * scale2 + dz; + } } - GPUCA_DEBUG_STREAMER_CHECK(if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamFastTransform)) { - float ly, lz; - getGeometry().convUVtoLocal(roc, u, v, ly, lz); + } - float gx, gy, gz; - getGeometry().convLocalToGlobal(roc, x, ly, lz, gx, gy, gz); + GPUCA_DEBUG_STREAMER_CHECK(if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamFastTransform)) { + float lx = x, ly = y, lz = z; - float lyT, lzT; - float uCorr = u + du; - float vCorr = v + dv; - float lxT = x + dx; - getGeometry().convUVtoLocal(roc, uCorr, vCorr, lyT, lzT); + float gx, gy, gz; + getGeometry().convLocalToGlobal(roc, lx, ly, lz, gx, gy, gz); - float invYZtoXScaled; - InverseTransformYZtoX(roc, row, lyT, lzT, invYZtoXScaled, ref, ref2, scale, scale2, scaleMode); + float lxT = lx + dx; + float lyT = ly + dy; + float lzT = lz + dz; - float invYZtoX; - InverseTransformYZtoX(roc, row, lyT, lzT, invYZtoX); + float invYZtoXScaled; + InverseTransformYZtoX(roc, row, lyT, lzT, invYZtoXScaled, ref, ref2, scale, scale2, scaleMode); - float YZtoNominalY; - float YZtoNominalZ; - InverseTransformYZtoNominalYZ(roc, row, lyT, lzT, YZtoNominalY, YZtoNominalZ); + float invYZtoX; + InverseTransformYZtoX(roc, row, lyT, lzT, invYZtoX); - float YZtoNominalYScaled; - float YZtoNominalZScaled; - InverseTransformYZtoNominalYZ(roc, row, lyT, lzT, YZtoNominalYScaled, YZtoNominalZScaled, ref, ref2, scale, scale2, scaleMode); + float YZtoNominalY; + float YZtoNominalZ; + InverseTransformYZtoNominalYZ(roc, row, lyT, lzT, YZtoNominalY, YZtoNominalZ); - float dxRef, duRef, dvRef; - if (ref) { - ref->mCorrection.getCorrection(roc, row, u, v, dxRef, duRef, dvRef); - } + float YZtoNominalYScaled; + float YZtoNominalZScaled; + InverseTransformYZtoNominalYZ(roc, row, lyT, lzT, YZtoNominalYScaled, YZtoNominalZScaled, ref, ref2, scale, scale2, scaleMode); - float dxRef2, duRef2, dvRef2; - if (ref2) { - ref2->mCorrection.getCorrection(roc, row, u, v, dxRef2, duRef2, dvRef2); - } + float dxRef = 0.f, dyRef = 0.f, dzRef = 0.f; + if (ref) { + std::tie(dxRef, dyRef, dzRef) = ref->mCorrection.getCorrectionLocal(roc, row, y, z); + } - float dxOrig, duOrig, dvOrig; - mCorrection.getCorrection(roc, row, u, v, dxOrig, duOrig, dvOrig); - - o2::utils::DebugStreamer::instance()->getStreamer("debug_fasttransform", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_Transform").data() - // corrections in x, u, v - << "dxOrig=" << dxOrig - << "duOrig=" << duOrig - << "dvOrig=" << dvOrig - << "dxRef=" << dxRef - << "duRef=" << duRef - << "dvRef=" << dvRef - << "dxRef2=" << dxRef2 - << "duRef2=" << duRef2 - << "dvRef2=" << dvRef2 - << "dx=" << dx - << "du=" << du - << "dv=" << dv - << "v=" << v - << "u=" << u - << "row=" << row - << "roc=" << roc - << "scale=" << scale - << "scale2=" << scale2 - // original local coordinates - << "ly=" << ly - << "lz=" << lz - << "lx=" << x - // corrected local coordinated - << "lxT=" << lxT - << "lyT=" << lyT - << "lzT=" << lzT - // global uncorrected coordinates - << "gx=" << gx - << "gy=" << gy - << "gz=" << gz - // some transformations which are applied - << "invYZtoX=" << invYZtoX - << "invYZtoXScaled=" << invYZtoXScaled - << "YZtoNominalY=" << YZtoNominalY - << "YZtoNominalYScaled=" << YZtoNominalYScaled - << "YZtoNominalZ=" << YZtoNominalZ - << "YZtoNominalZScaled=" << YZtoNominalZScaled - << "scaleMode=" << scaleMode - << "\n"; - }) - - x += dx; - u += du; - v += dv; - } + float dxRef2 = 0.f, duRef2 = 0.f, dvRef2 = 0.f; + if (ref2) { + std::tie(dxRef2, duRef2, dvRef2) = ref2->mCorrection.getCorrectionLocal(roc, row, y, z); + } + + auto [dxOrig, dyOrig, dzOrig] = mCorrection.getCorrectionLocal(roc, row, y, z); + + o2::utils::DebugStreamer::instance()->getStreamer("debug_fasttransform", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_Transform").data() + // corrections in x, u, v + << "dxOrig=" << dxOrig + << "dyOrig=" << dyOrig + << "dzOrig=" << dzOrig + << "dxRef=" << dxRef + << "dyRef=" << dyRef + << "dzRef=" << dzRef + << "dxRef2=" << dxRef2 + << "dyRef2=" << dyRef2 + << "dzRef2=" << dzRef2 + << "dx=" << dx + << "dy=" << dy + << "dz=" << dz + << "row=" << row + << "roc=" << roc + << "scale=" << scale + << "scale2=" << scale2 + // original local coordinates + << "ly=" << ly + << "lz=" << lz + << "lx=" << lx + // corrected local coordinated + << "lxT=" << lxT + << "lyT=" << lyT + << "lzT=" << lzT + // global uncorrected coordinates + << "gx=" << gx + << "gy=" << gy + << "gz=" << gz + // some transformations which are applied + << "invYZtoX=" << invYZtoX + << "invYZtoXScaled=" << invYZtoXScaled + << "YZtoNominalY=" << YZtoNominalY + << "YZtoNominalYScaled=" << YZtoNominalYScaled + << "YZtoNominalZ=" << YZtoNominalZ + << "YZtoNominalZScaled=" << YZtoNominalZScaled + << "scaleMode=" << scaleMode + << "\n"; + }) + + x += dx; + y += dy; + z += dz; } GPUdi() void TPCFastTransform::TransformXYZ(int32_t roc, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const { - float u, v; - getGeometry().convLocalToUV(roc, y, z, u, v); - TransformInternal(roc, row, u, v, x, ref, ref2, scale, scale2, scaleMode); - getGeometry().convUVtoLocal(roc, u, v, y, z); - float dzTOF = 0; - getTOFcorrection(roc, row, x, y, z, dzTOF); - z += dzTOF; + + TransformLocal(roc, row, x, y, z, ref, ref2, scale, scale2, scaleMode); } GPUdi() void TPCFastTransform::Transform(int32_t roc, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const @@ -617,31 +540,23 @@ GPUdi() void TPCFastTransform::Transform(int32_t roc, int32_t row, float pad, fl /// _______________ The main method: cluster transformation _______________________ /// /// Transforms raw TPC coordinates to local XYZ withing a roc - /// taking calibration + alignment into account. + /// taking calibration into account. /// const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); - // const RocInfo &rocInfo = getRocInfo( roc ); - // bool sideC = ( roc >= NumberOfRocs / 2 ); - x = rowInfo.x; float u = 0, v = 0; - convPadTimeToUV(roc, row, pad, time, u, v, vertexTime); - - TransformInternal(roc, row, u, v, x, ref, ref2, scale, scale2, scaleMode); - + convPadTimeToUV(row, pad, time, u, v, vertexTime); getGeometry().convUVtoLocal(roc, u, v, y, z); - float dzTOF = 0; - getTOFcorrection(roc, row, x, y, z, dzTOF); - z += dzTOF; + TransformLocal(roc, row, x, y, z, ref, ref2, scale, scale2, scaleMode); } GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t roc, float time, float& z, float maxTimeBin) const { float v = 0; - convTimeToVinTimeFrame(roc, time, v, maxTimeBin); + convTimeToVinTimeFrame(time, v, maxTimeBin); getGeometry().convVtoLocal(roc, v, z); } @@ -656,7 +571,7 @@ GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t roc, int32_t row, fl const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); x = rowInfo.x; float u = 0, v = 0; - convPadTimeToUVinTimeFrame(roc, row, pad, time, u, v, maxTimeBin); + convPadTimeToUVinTimeFrame(row, pad, time, u, v, maxTimeBin); getGeometry().convUVtoLocal(roc, u, v, y, z); } @@ -665,7 +580,7 @@ GPUdi() void TPCFastTransform::InverseTransformInTimeFrame(int32_t roc, int32_t /// Inverse transformation to TransformInTimeFrame float u = 0, v = 0; getGeometry().convLocalToUV(roc, y, z, u, v); - convUVtoPadTimeInTimeFrame(roc, row, u, v, pad, time, maxTimeBin); + convUVtoPadTimeInTimeFrame(row, u, v, pad, time, maxTimeBin); } GPUdi() float TPCFastTransform::InverseTransformInTimeFrame(int32_t roc, float z, float maxTimeBin) const @@ -715,26 +630,16 @@ GPUdi() float TPCFastTransform::convTimeToZinTimeFrame(int32_t roc, float time, /// Only Z coordinate. /// - float v = (time - mT0 - maxTimeBin) * mVdrift + mLdriftCorr; // drift length cm - float z = getGeometry().getTPCalignmentZ(); // global TPC alignment - if (roc < getGeometry().getNumberOfRocsA()) { - z -= v; - } else { - z += v; - } + float v = (time - mT0 - maxTimeBin) * mVdrift; // drift length cm + float z = (roc < getGeometry().getNumberOfRocsA()) ? -v : v; return z; } GPUdi() float TPCFastTransform::convZtoTimeInTimeFrame(int32_t roc, float z, float maxTimeBin) const { /// Inverse transformation of convTimeToZinTimeFrame() - float v; - if (roc < getGeometry().getNumberOfRocsA()) { - v = getGeometry().getTPCalignmentZ() - z; - } else { - v = z - getGeometry().getTPCalignmentZ(); - } - return mT0 + maxTimeBin + (v - mLdriftCorr) / mVdrift; + float v = (roc < getGeometry().getNumberOfRocsA()) ? -z : z; + return mT0 + maxTimeBin + v / mVdrift; } GPUdi() float TPCFastTransform::convDeltaTimeToDeltaZinTimeFrame(int32_t roc, float deltaTime) const @@ -769,17 +674,7 @@ GPUdi() float TPCFastTransform::getMaxDriftTime(int32_t roc, int32_t row, float { /// maximal possible drift time of the active area float maxL = mCorrection.getMaxDriftLength(roc, row, pad); - - bool sideC = (roc >= getGeometry().getNumberOfRocsA()); - const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); - const TPCFastTransformGeo::RocInfo& rocInfo = getGeometry().getRocInfo(roc); - - float x = rowInfo.x; - float u = (pad - 0.5f * rowInfo.maxPad) * rowInfo.padWidth; - - float y = sideC ? -u : u; // pads are mirrorred on C-side - float yLab = y * rocInfo.cosAlpha + x * rocInfo.sinAlpha; - return mT0 + (maxL - mLdriftCorr) / (mVdrift + mVdriftCorrY * yLab); + return mT0 + maxL / mVdrift; } GPUdi() float TPCFastTransform::getMaxDriftTime(int32_t roc, int32_t row) const diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx b/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx index b472868fa1071..c8982f05d4730 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx @@ -35,10 +35,10 @@ TPCFastTransformGeo::TPCFastTransformGeo() s.sinAlpha = sin(alpha); s.cosAlpha = cos(alpha); } - mRocInfos[NumberOfRocs] = RocInfo{0.f, 0.f}; + mRocInfos[NumberOfRocs] = RocInfo{}; for (int32_t i = 0; i < MaxNumberOfRows + 1; i++) { - mRowInfos[i] = RowInfo{0.f, -1, 0.f, 0.f, 0.f, 0.f}; + mRowInfos[i] = RowInfo{}; } } @@ -51,45 +51,25 @@ void TPCFastTransformGeo::startConstruction(int32_t numberOfRows) mConstructionMask = ConstructionState::InProgress; mNumberOfRows = numberOfRows; - mTPCzLengthA = 0.f; - mTPCzLengthC = 0.f; - mTPCalignmentZ = 0.f; - mScaleVtoSVsideA = 0.f; - mScaleVtoSVsideC = 0.f; - mScaleSVtoVsideA = 0.f; - mScaleSVtoVsideC = 0.f; + mTPCzLength = 0.f; for (int32_t i = 0; i < MaxNumberOfRows; i++) { - mRowInfos[i] = RowInfo{0.f, -1, 0.f, 0.f, 0.f, 0.f}; + mRowInfos[i] = RowInfo{}; } } -void TPCFastTransformGeo::setTPCzLength(float tpcZlengthSideA, float tpcZlengthSideC) +void TPCFastTransformGeo::setTPCzLength(float tpcZlength) { /// Sets TPC z length for both sides assert(mConstructionMask & ConstructionState::InProgress); - assert((tpcZlengthSideA > 0.f) && (tpcZlengthSideC > 0.f)); + assert(tpcZlength > 0.f); - mTPCzLengthA = tpcZlengthSideA; - mTPCzLengthC = tpcZlengthSideC; - mScaleSVtoVsideA = tpcZlengthSideA + 3.; // add some extra possible drift length due to the space charge distortions - mScaleSVtoVsideC = tpcZlengthSideC + 3.; - mScaleVtoSVsideA = 1. / mScaleSVtoVsideA; - mScaleVtoSVsideC = 1. / mScaleSVtoVsideC; + mTPCzLength = tpcZlength; mConstructionMask |= ConstructionState::GeometryIsSet; } -void TPCFastTransformGeo::setTPCalignmentZ(float tpcAlignmentZ) -{ - /// Sets the TPC alignment - assert(mConstructionMask & ConstructionState::InProgress); - - mTPCalignmentZ = tpcAlignmentZ; - mConstructionMask |= ConstructionState::AlignmentIsSet; -} - void TPCFastTransformGeo::setTPCrow(int32_t iRow, float x, int32_t nPads, float padWidth) { /// Initializes a TPC row @@ -113,8 +93,6 @@ void TPCFastTransformGeo::setTPCrow(int32_t iRow, float x, int32_t nPads, float row.maxPad = nPads - 1; row.padWidth = padWidth; row.u0 = -uWidth / 2.; - row.scaleUtoSU = 1. / uWidth; - row.scaleSUtoU = uWidth; } void TPCFastTransformGeo::finishConstruction() @@ -123,7 +101,6 @@ void TPCFastTransformGeo::finishConstruction() assert(mConstructionMask & ConstructionState::InProgress); // construction in process assert(mConstructionMask & ConstructionState::GeometryIsSet); // geometry is set - assert(mConstructionMask & ConstructionState::AlignmentIsSet); // alignment is set for (int32_t i = 0; i < mNumberOfRows; i++) { // all TPC rows are initialized assert(getRowInfo(i).maxPad > 0); @@ -138,9 +115,7 @@ void TPCFastTransformGeo::print() const #if !defined(GPUCA_GPUCODE) LOG(info) << "TPC Fast Transformation Geometry: "; LOG(info) << "mNumberOfRows = " << mNumberOfRows; - LOG(info) << "mTPCzLengthA = " << mTPCzLengthA; - LOG(info) << "mTPCzLengthC = " << mTPCzLengthC; - LOG(info) << "mTPCalignmentZ = " << mTPCalignmentZ; + LOG(info) << "mTPCzLength = " << mTPCzLength; LOG(info) << "TPC Rows : "; for (int32_t i = 0; i < mNumberOfRows; i++) { LOG(info) << " tpc row " << i << ": x = " << mRowInfos[i].x << " maxPad = " << mRowInfos[i].maxPad << " padWidth = " << mRowInfos[i].padWidth; @@ -179,26 +154,26 @@ int32_t TPCFastTransformGeo::test(int32_t roc, int32_t row, float ly, float lz) LOG(info) << "Error local <-> UV: y " << ly << " dy " << ly1 - ly << " z " << lz << " dz " << lz1 - lz; error = -4; } + /* + float su = 0.f, sv = 0.f; - float su = 0.f, sv = 0.f; + convUVtoScaledUV(roc, row, u, v, su, sv); - convUVtoScaledUV(roc, row, u, v, su, sv); + if (su < 0.f || su > 1.f) { + LOG(info) << "Error scaled U range: u " << u << " su " << su; + error = -5; + } - if (su < 0.f || su > 1.f) { - LOG(info) << "Error scaled U range: u " << u << " su " << su; - error = -5; - } - - float u1 = 0.f, v1 = 0.f; - convScaledUVtoUV(roc, row, su, sv, u1, v1); - - if (fabs(u1 - u) > 1.e-4 || fabs(v1 - v) > 1.e-4) { - LOG(info) << "Error UV<->scaled UV: u " << u << " du " << u1 - u << " v " << v << " dv " << v1 - v; - error = -6; - } + float u1 = 0.f, v1 = 0.f; + convScaledUVtoUV(roc, row, su, sv, u1, v1); + if (fabs(u1 - u) > 1.e-4 || fabs(v1 - v) > 1.e-4) { + LOG(info) << "Error UV<->scaled UV: u " << u << " du " << u1 - u << " v " << v << " dv " << v1 - v; + error = -6; + } + */ float pad = convUtoPad(row, u); - u1 = convPadToU(row, pad); + float u1 = convPadToU(row, pad); if (fabs(u1 - u) > 1.e-5) { LOG(info) << "Error U<->Pad: u " << u << " pad " << pad << " du " << u1 - u; diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.h b/GPU/TPCFastTransformation/TPCFastTransformGeo.h index 3382d1d926ce2..a5d642158cd8f 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.h +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.h @@ -36,19 +36,17 @@ class TPCFastTransformGeo public: /// The struct contains necessary info for TPC ROC struct RocInfo { - float sinAlpha; - float cosAlpha; + float sinAlpha{0.f}; ///< sin of the angle between the local x and the global x + float cosAlpha{0.f}; ///< cos of the angle between the local x and the global x ClassDefNV(RocInfo, 1); }; /// The struct contains necessary info about TPC padrow struct RowInfo { - float x; ///< nominal X coordinate of the row [cm] - int32_t maxPad; ///< maximal pad number = n pads - 1 - float padWidth; ///< width of pads [cm] - float u0; ///< min. u coordinate - float scaleUtoSU; ///< scale for su (scaled u ) coordinate - float scaleSUtoU; ///< scale for u coordinate + float x{0.f}; ///< nominal X coordinate of the padrow [cm] + int32_t maxPad{0}; ///< maximal pad number = n pads - 1 + float padWidth{0.f}; ///< width of pads [cm] + float u0{0.f}; ///< min. u coordinate /// get U min GPUd() float getUmin() const { return u0; } @@ -92,13 +90,7 @@ class TPCFastTransformGeo /// Sets TPC geometry /// /// It must be called once during initialization - void setTPCzLength(float tpcZlengthSideA, float tpcZlengthSideC); - - /// Sets all drift calibration parameters and the time stamp - /// - /// It must be called once during construction, - /// but also may be called afterwards to reset these parameters. - void setTPCalignmentZ(float tpcAlignmentZ); + void setTPCzLength(float tpcZlength); /// Finishes initialization: puts everything to the flat buffer, releases temporary memory void finishConstruction(); @@ -126,21 +118,8 @@ class TPCFastTransformGeo /// Gives TPC row info GPUd() const RowInfo& getRowInfo(int32_t row) const; - /// Gives Z length of the TPC, side A - GPUd() float getTPCzLengthA() const { return mTPCzLengthA; } - - /// Gives Z length of the TPC, side C - GPUd() float getTPCzLengthC() const { return mTPCzLengthC; } - - /// Gives Z length of the TPC, depending on the roc - GPUd() float getTPCzLength(int32_t roc) const - { - return (roc < NumberOfRocsA) ? mTPCzLengthA - : mTPCzLengthC; - } - - /// Gives TPC alignment in Z - GPUd() float getTPCalignmentZ() const { return mTPCalignmentZ; } + /// Gives Z length of the TPC, one Z side + GPUd() float getTPCzLength() const { return mTPCzLength; } /// _______________ Conversion of coordinate systems __________ @@ -157,15 +136,6 @@ class TPCFastTransformGeo /// convert Local-> UV c.s. GPUd() void convLocalToUV(int32_t roc, float y, float z, float& u, float& v) const; - /// convert UV -> Scaled UV - GPUd() void convUVtoScaledUV(int32_t roc, int32_t row, float u, float v, float& su, float& sv) const; - - /// convert Scaled UV -> UV - GPUd() void convScaledUVtoUV(int32_t roc, int32_t row, float su, float sv, float& u, float& v) const; - - /// convert Scaled UV -> Local c.s. - GPUd() void convScaledUVtoLocal(int32_t roc, int32_t row, float su, float sv, float& ly, float& lz) const; - /// convert Pad coordinate -> U GPUd() float convPadToU(int32_t row, float pad) const; @@ -196,7 +166,6 @@ class TPCFastTransformGeo Constructed = 0x1, ///< the object is constructed, temporary memory is released InProgress = 0x2, ///< construction started: temporary memory is reserved GeometryIsSet = 0x4, ///< the TPC geometry is set - AlignmentIsSet = 0x8 ///< the TPC alignment is set }; uint32_t mConstructionMask = ConstructionState::NotConstructed; ///< mask for constructed object members, first two bytes are used by this class @@ -204,18 +173,12 @@ class TPCFastTransformGeo /// _______________ Geometry _______________________________________________ int32_t mNumberOfRows = 0; ///< Number of TPC rows. It is different for the Run2 and the Run3 setups - float mTPCzLengthA = 0.f; ///< Z length of the TPC, side A - float mTPCzLengthC = 0.f; ///< Z length of the TPC, side C - float mTPCalignmentZ = 0.f; ///< Global Z shift of the TPC detector. It is applied at the end of the transformation. - float mScaleVtoSVsideA = 0.f; ///< scale for v->sv for TPC side A - float mScaleVtoSVsideC = 0.f; ///< scale for v->sv for TPC side C - float mScaleSVtoVsideA = 0.f; ///< scale for sv->v for TPC side A - float mScaleSVtoVsideC = 0.f; ///< scale for sv->v for TPC side C - - RocInfo mRocInfos[NumberOfRocs + 1]; ///< array of roc information [fixed size] - RowInfo mRowInfos[MaxNumberOfRows + 1]; ///< array of row information [fixed size] - - ClassDefNV(TPCFastTransformGeo, 2); + float mTPCzLength = 0.f; ///< Z length of one TPC side (A or C) + + RocInfo mRocInfos[NumberOfRocs + 1]; ///< array of roc information [fixed size] + RowInfo mRowInfos[MaxNumberOfRows + 1]; ///< array of row information [fixed size] + + ClassDefNV(TPCFastTransformGeo, 3); }; // ======================================================================= @@ -262,11 +225,10 @@ GPUdi() void TPCFastTransformGeo::convVtoLocal(int32_t roc, float v, float& lz) { /// convert UV -> Local c.s. if (roc < NumberOfRocsA) { // TPC side A - lz = mTPCzLengthA - v; + lz = mTPCzLength - v; } else { // TPC side C - lz = v - mTPCzLengthC; // drift direction is mirrored on C-side + lz = v - mTPCzLength; // drift direction is mirrored on C-side } - lz += mTPCalignmentZ; // global TPC alignment } GPUdi() void TPCFastTransformGeo::convUVtoLocal(int32_t roc, float u, float v, float& ly, float& lz) const @@ -274,59 +236,25 @@ GPUdi() void TPCFastTransformGeo::convUVtoLocal(int32_t roc, float u, float v, f /// convert UV -> Local c.s. if (roc < NumberOfRocsA) { // TPC side A ly = u; - lz = mTPCzLengthA - v; + lz = mTPCzLength - v; } else { // TPC side C ly = -u; // pads are mirrorred on C-side - lz = v - mTPCzLengthC; // drift direction is mirrored on C-side + lz = v - mTPCzLength; // drift direction is mirrored on C-side } - lz += mTPCalignmentZ; // global TPC alignment } GPUdi() void TPCFastTransformGeo::convLocalToUV(int32_t roc, float ly, float lz, float& u, float& v) const { /// convert Local-> UV c.s. - lz = lz - mTPCalignmentZ; // global TPC alignment - if (roc < NumberOfRocsA) { // TPC side A + if (roc < NumberOfRocsA) { // TPC side A u = ly; - v = mTPCzLengthA - lz; + v = mTPCzLength - lz; } else { // TPC side C u = -ly; // pads are mirrorred on C-side - v = lz + mTPCzLengthC; // drift direction is mirrored on C-side + v = lz + mTPCzLength; // drift direction is mirrored on C-side } } -GPUdi() void TPCFastTransformGeo::convUVtoScaledUV(int32_t roc, int32_t row, float u, float v, float& su, float& sv) const -{ - /// convert UV -> Scaled UV - const RowInfo& rowInfo = getRowInfo(row); - su = (u - rowInfo.u0) * rowInfo.scaleUtoSU; - if (roc < NumberOfRocsA) { - sv = v * mScaleVtoSVsideA; - } else { - sv = v * mScaleVtoSVsideC; - } -} - -GPUdi() void TPCFastTransformGeo::convScaledUVtoUV(int32_t roc, int32_t row, float su, float sv, float& u, float& v) const -{ - /// convert Scaled UV -> UV - const RowInfo& rowInfo = getRowInfo(row); - u = rowInfo.u0 + su * rowInfo.scaleSUtoU; - if (roc < NumberOfRocsA) { - v = sv * mScaleSVtoVsideA; - } else { - v = sv * mScaleSVtoVsideC; - } -} - -GPUdi() void TPCFastTransformGeo::convScaledUVtoLocal(int32_t roc, int32_t row, float su, float sv, float& ly, float& lz) const -{ - /// convert Scaled UV -> Local c.s. - float u, v; - convScaledUVtoUV(roc, row, su, sv, u, v); - convUVtoLocal(roc, u, v, ly, lz); -} - GPUdi() float TPCFastTransformGeo::convPadToU(int32_t row, float pad) const { /// convert Pad coordinate -> U diff --git a/GPU/TPCFastTransformation/TPCFastTransformManager.cxx b/GPU/TPCFastTransformation/TPCFastTransformManager.cxx index aa28b6a414876..c553d9cc6dac1 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformManager.cxx +++ b/GPU/TPCFastTransformation/TPCFastTransformManager.cxx @@ -22,6 +22,7 @@ #include "AliTPCcalibDB.h" #include "TPCFastTransform.h" #include "Spline2DHelper.h" +blabla using namespace o2::gpu; From 6e876d178f27aa1c8683b14006562317137a5060 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Thu, 6 Mar 2025 23:58:56 +0000 Subject: [PATCH 090/285] TPC Splines: init inverse from the inverse voxel map; rebase --- .../TPCFastSpaceChargeCorrectionHelper.h | 13 +- .../TPCFastSpaceChargeCorrectionHelper.cxx | 921 +++++++++--------- .../src/TPCFastTransformHelperO2.cxx | 4 +- .../test/testTPCFastTransform.cxx | 61 +- GPU/TPCFastTransformation/Spline1DSpec.h | 36 +- .../TPCFastSpaceChargeCorrection.cxx | 201 ++-- .../TPCFastSpaceChargeCorrection.h | 334 +++---- .../TPCFastSpaceChargeCorrectionMap.h | 24 +- GPU/TPCFastTransformation/TPCFastTransform.h | 347 +++---- .../TPCFastTransformGeo.cxx | 55 +- .../TPCFastTransformGeo.h | 160 ++- .../TPCFastTransformManager.cxx | 336 ------- .../TPCFastTransformManager.h | 86 -- .../TPCFastTransformationLinkDef_O2.h | 7 +- .../macro/TPCFastTransformInit.C | 211 ++-- 15 files changed, 1159 insertions(+), 1637 deletions(-) delete mode 100644 GPU/TPCFastTransformation/TPCFastTransformManager.cxx delete mode 100644 GPU/TPCFastTransformation/TPCFastTransformManager.h diff --git a/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h b/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h index eff4972679ed8..abbc5b7116b2d 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h @@ -86,15 +86,14 @@ class TPCFastSpaceChargeCorrectionHelper /// Create SpaceCharge correction out of the voxel tree std::unique_ptr createFromTrackResiduals( - const o2::tpc::TrackResiduals& trackResiduals, TTree* voxResTree, bool useSmoothed = false, bool invertSigns = false); + const o2::tpc::TrackResiduals& trackResiduals, TTree* voxResTree, TTree* voxResTreeInverse, bool useSmoothed, bool invertSigns); + /// _______________ Utilities ________________________ const TPCFastTransformGeo& getGeometry() { return mGeo; } TPCFastSpaceChargeCorrectionMap& getCorrectionMap() { return mCorrectionMap; } - void fillSpaceChargeCorrectionFromMap(TPCFastSpaceChargeCorrection& correction); - void testGeometry(const TPCFastTransformGeo& geo) const; /// initialise inverse transformation @@ -103,15 +102,13 @@ class TPCFastSpaceChargeCorrectionHelper /// initialise inverse transformation from linear combination of several input corrections void initInverse(std::vector& corrections, const std::vector& scaling, bool prn); + void MergeCorrections(std::vector& corrections, const std::vector& scaling, bool prn); + private: /// geometry initialization void initGeometry(); - /// get space charge correction in internal TPCFastTransform coordinates u,v->dx,du,dv - void getSpaceChargeCorrection(const TPCFastSpaceChargeCorrection& correction, int slice, int row, o2::gpu::TPCFastSpaceChargeCorrectionMap::CorrectionPoint p, double& su, double& sv, double& dx, double& du, double& dv); - - /// initialise max drift length - void initMaxDriftLength(o2::gpu::TPCFastSpaceChargeCorrection& correction, bool prn); + void fillSpaceChargeCorrectionFromMap(TPCFastSpaceChargeCorrection& correction, bool processingInverseCorrection); static TPCFastSpaceChargeCorrectionHelper* sInstance; ///< singleton instance bool mIsInitialized = 0; ///< initialization flag diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index 710a4356dd457..92817063831f6 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -32,6 +32,7 @@ #include "TTreeReader.h" #include "TTreeReaderValue.h" #include "ROOT/TTreeProcessorMT.hxx" +#include using namespace o2::gpu; @@ -112,7 +113,7 @@ void TPCFastSpaceChargeCorrectionHelper::setNthreadsToMaximum() } } -void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFastSpaceChargeCorrection& correction) +void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFastSpaceChargeCorrection& correction, bool processingInverseCorrection) { // calculate correction map: dx,du,dv = ( origTransform() -> x,u,v) - fastTransformNominal:x,u,v // for the future: switch TOF correction off for a while @@ -130,39 +131,64 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas LOG(info) << "fast space charge correction helper: init from data points"; - for (int roc = 0; roc < correction.getGeometry().getNumberOfRocs(); roc++) { + for (int sector = 0; sector < correction.getGeometry().getNumberOfSectors(); sector++) { auto myThread = [&](int iThread) { for (int row = iThread; row < correction.getGeometry().getNumberOfRows(); row += mNthreads) { - TPCFastSpaceChargeCorrection::SplineType& spline = correction.getSpline(roc, row); + TPCFastSpaceChargeCorrection::SplineType& spline = correction.getSpline(sector, row); Spline2DHelper helper; - float* splineParameters = correction.getSplineData(roc, row); - const std::vector& data = mCorrectionMap.getPoints(roc, row); + std::vector splineParameters; + splineParameters.resize(spline.getNumberOfParameters()); + + const std::vector& data = mCorrectionMap.getPoints(sector, row); int nDataPoints = data.size(); - auto& info = correction.getRocRowInfo(roc, row); - info.resetMaxValues(); + auto& info = correction.getSectorRowInfo(sector, row); + if (!processingInverseCorrection) { + info.resetMaxValues(); + } if (nDataPoints >= 4) { - std::vector pointSU(nDataPoints); - std::vector pointSV(nDataPoints); + std::vector pointGU(nDataPoints); + std::vector pointGV(nDataPoints); std::vector pointCorr(3 * nDataPoints); // 3 dimensions for (int i = 0; i < nDataPoints; ++i) { - double su, sv, dx, du, dv; - getSpaceChargeCorrection(correction, roc, row, data[i], su, sv, dx, du, dv); - pointSU[i] = su; - pointSV[i] = sv; - pointCorr[3 * i + 0] = dx; - pointCorr[3 * i + 1] = du; - pointCorr[3 * i + 2] = dv; - info.updateMaxValues(20. * dx, 20. * du, 20. * dv); + o2::gpu::TPCFastSpaceChargeCorrectionMap::CorrectionPoint p = data[i]; + // not corrected grid coordinates + auto [gu, gv, scale] = correction.convLocalToGrid(sector, row, p.mY, p.mZ); + if (scale - 1.f > 1.e-6) { // point is outside the grid + continue; + } + pointGU[i] = gu; + pointGV[i] = gv; + pointCorr[3 * i + 0] = p.mDx; + pointCorr[3 * i + 1] = p.mDy; + pointCorr[3 * i + 2] = p.mDz; + if (!processingInverseCorrection) { + info.updateMaxValues(20. * p.mDx, 20. * p.mDy, 20. * p.mDz); + } } - helper.approximateDataPoints(spline, splineParameters, 0., spline.getGridX1().getUmax(), 0., spline.getGridX2().getUmax(), &pointSU[0], - &pointSV[0], &pointCorr[0], nDataPoints); + helper.approximateDataPoints(spline, splineParameters.data(), 0., spline.getGridX1().getUmax(), 0., spline.getGridX2().getUmax(), &pointGU[0], + &pointGV[0], &pointCorr[0], nDataPoints); } else { for (int i = 0; i < spline.getNumberOfParameters(); i++) { splineParameters[i] = 0.f; } } + + if (processingInverseCorrection) { + float* splineX = correction.getSplineData(sector, row, 1); + float* splineYZ = correction.getSplineData(sector, row, 2); + for (int i = 0; i < spline.getNumberOfParameters() / 3; i++) { + splineX[i] = splineParameters[3 * i + 0]; + splineYZ[2 * i + 0] = splineParameters[3 * i + 1]; + splineYZ[2 * i + 1] = splineParameters[3 * i + 2]; + } + } else { + float* splineXYZ = correction.getSplineData(sector, row); + for (int i = 0; i < spline.getNumberOfParameters(); i++) { + splineXYZ[i] = splineParameters[i]; + } + } } // row }; // thread @@ -178,57 +204,30 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas th.join(); } - } // roc + } // sector watch.Stop(); LOGP(info, "Space charge correction tooks: {}s", watch.RealTime()); - - initInverse(correction, 0); -} - -void TPCFastSpaceChargeCorrectionHelper::getSpaceChargeCorrection(const TPCFastSpaceChargeCorrection& correction, int roc, int row, o2::gpu::TPCFastSpaceChargeCorrectionMap::CorrectionPoint p, - double& su, double& sv, double& dx, double& du, double& dv) -{ - // get space charge correction in internal TPCFastTransform coordinates su,sv->dx,du,dv - - if (!mIsInitialized) { - initGeometry(); - } - - // not corrected coordinates in u,v - float u = 0.f, v = 0.f, fsu = 0.f, fsv = 0.f; - mGeo.convLocalToUV(roc, p.mY, p.mZ, u, v); - correction.convUVtoGrid(roc, row, u, v, fsu, fsv); - // mGeo.convUVtoScaledUV(roc, row, u, v, fsu, fsv); - su = fsu; - sv = fsv; - // corrected coordinates in u,v - float u1 = 0.f, v1 = 0.f; - mGeo.convLocalToUV(roc, p.mY + p.mDy, p.mZ + p.mDz, u1, v1); - - dx = p.mDx; - du = u1 - u; - dv = v1 - v; -} +} // fillSpaceChargeCorrectionFromMap std::unique_ptr TPCFastSpaceChargeCorrectionHelper::createFromGlobalCorrection( - std::function correctionGlobal, const int nKnotsY, const int nKnotsZ) { /// creates TPCFastSpaceChargeCorrection object from a continious space charge correction in global coordinates - auto correctionLocal = [&](int roc, int irow, double ly, double lz, + auto correctionLocal = [&](int sector, int irow, double ly, double lz, double& dlx, double& dly, double& dlz) { double lx = mGeo.getRowInfo(irow).x; float gx, gy, gz; - mGeo.convLocalToGlobal(roc, lx, ly, lz, gx, gy, gz); + mGeo.convLocalToGlobal(sector, lx, ly, lz, gx, gy, gz); double dgx, dgy, dgz; - correctionGlobal(roc, gx, gy, gz, dgx, dgy, dgz); + correctionGlobal(sector, gx, gy, gz, dgx, dgy, dgz); float lx1, ly1, lz1; - mGeo.convGlobalToLocal(roc, gx + dgx, gy + dgy, gz + dgz, lx1, ly1, lz1); + mGeo.convGlobalToLocal(sector, gx + dgx, gy + dgy, gz + dgz, lx1, ly1, lz1); dlx = lx1 - lx; dly = ly1 - ly; dlz = lz1 - lz; @@ -237,7 +236,7 @@ std::unique_ptr TPCFastSpaceChargeCorrectionHelper } std::unique_ptr TPCFastSpaceChargeCorrectionHelper::createFromLocalCorrection( - std::function correctionLocal, + std::function correctionLocal, const int nKnotsY, const int nKnotsZ) { /// creates TPCFastSpaceChargeCorrection object from a continious space charge correction in local coordinates @@ -282,28 +281,24 @@ std::unique_ptr TPCFastSpaceChargeCorrectionHelper /// set space charge correction in the local coordinates /// as a continious function - int nRocs = mGeo.getNumberOfRocs(); + int nSectors = mGeo.getNumberOfSectors(); int nRows = mGeo.getNumberOfRows(); - mCorrectionMap.init(nRocs, nRows); + mCorrectionMap.init(nSectors, nRows); - for (int iRoc = 0; iRoc < nRocs; iRoc++) { + for (int iSector = 0; iSector < nSectors; iSector++) { auto myThread = [&](int iThread) { for (int iRow = iThread; iRow < nRows; iRow += mNthreads) { const auto& info = mGeo.getRowInfo(iRow); - double vMax = mGeo.getTPCzLength(); - double dv = vMax / (6. * (nKnotsZ - 1)); - + double dl = mGeo.getTPCzLength() / (6. * (nKnotsZ - 1)); double dpad = info.maxPad / (6. * (nKnotsY - 1)); for (double pad = 0; pad < info.maxPad + .5 * dpad; pad += dpad) { - float u = mGeo.convPadToU(iRow, pad); - for (double v = 0.; v < vMax + .5 * dv; v += dv) { - float ly, lz; - mGeo.convUVtoLocal(iRoc, u, v, ly, lz); + for (double l = 0.; l < mGeo.getTPCzLength() + .5 * dl; l += dl) { + auto [y, z] = mGeo.convPadDriftLengthToLocal(iSector, iRow, pad, l); double dx, dy, dz; - correctionLocal(iRoc, iRow, ly, lz, dx, dy, dz); - mCorrectionMap.addCorrectionPoint(iRoc, iRow, - ly, lz, dx, dy, dz); + correctionLocal(iSector, iRow, y, z, dx, dy, dz); + mCorrectionMap.addCorrectionPoint(iSector, iRow, + y, z, dx, dy, dz); } } } // row @@ -321,20 +316,21 @@ std::unique_ptr TPCFastSpaceChargeCorrectionHelper th.join(); } - } // roc + } // sector - fillSpaceChargeCorrectionFromMap(correction); + fillSpaceChargeCorrectionFromMap(correction, false); + initInverse(correction, false); } return std::move(correctionPtr); -} +} // createFromLocalCorrection void TPCFastSpaceChargeCorrectionHelper::testGeometry(const TPCFastTransformGeo& geo) const { const Mapper& mapper = Mapper::instance(); - if (geo.getNumberOfRocs() != Sector::MAXSECTOR) { - LOG(fatal) << "Wrong number of sectors :" << geo.getNumberOfRocs() << " instead of " << Sector::MAXSECTOR << std::endl; + if (geo.getNumberOfSectors() != Sector::MAXSECTOR) { + LOG(fatal) << "Wrong number of sectors :" << geo.getNumberOfSectors() << " instead of " << Sector::MAXSECTOR << std::endl; } if (geo.getNumberOfRows() != mapper.getNumberOfRows()) { @@ -384,7 +380,7 @@ void TPCFastSpaceChargeCorrectionHelper::testGeometry(const TPCFastTransformGeo& } std::unique_ptr TPCFastSpaceChargeCorrectionHelper::createFromTrackResiduals( - const o2::tpc::TrackResiduals& trackResiduals, TTree* voxResTree, bool useSmoothed, bool invertSigns) + const o2::tpc::TrackResiduals& trackResiduals, TTree* voxResTree, TTree* voxResTreeInverse, bool useSmoothed, bool invertSigns) { // create o2::gpu::TPCFastSpaceChargeCorrection from o2::tpc::TrackResiduals::VoxRes voxel tree @@ -399,9 +395,6 @@ std::unique_ptr TPCFastSpaceChargeCorrect auto* helper = o2::tpc::TPCFastSpaceChargeCorrectionHelper::instance(); const o2::gpu::TPCFastTransformGeo& geo = helper->getGeometry(); - o2::gpu::TPCFastSpaceChargeCorrectionMap& map = helper->getCorrectionMap(); - map.init(geo.getNumberOfRocs(), geo.getNumberOfRows()); - int nY2Xbins = trackResiduals.getNY2XBins(); int nZ2Xbins = trackResiduals.getNZ2XBins(); @@ -476,7 +469,7 @@ std::unique_ptr TPCFastSpaceChargeCorrect // std::cout << "n knots Z: " << nKnotsZ << std::endl; const int nRows = geo.getNumberOfRows(); - const int nROCs = geo.getNumberOfRocs(); + const int nSectors = geo.getNumberOfSectors(); { // create the correction object @@ -497,11 +490,11 @@ std::unique_ptr TPCFastSpaceChargeCorrect } // .. create the correction object // set the grid borders - for (int iRoc = 0; iRoc < geo.getNumberOfRocs(); iRoc++) { + for (int iSector = 0; iSector < geo.getNumberOfSectors(); iSector++) { for (int iRow = 0; iRow < geo.getNumberOfRows(); iRow++) { const auto& rowInfo = geo.getRowInfo(iRow); - auto& info = correction.getRocRowInfo(iRoc, iRow); - const auto& spline = correction.getSpline(iRoc, iRow); + auto& info = correction.getSectorRowInfo(iSector, iRow); + const auto& spline = correction.getSpline(iSector, iRow); double yMin = rowInfo.x * trackResiduals.getY2X(iRow, 0); double yMax = rowInfo.x * trackResiduals.getY2X(iRow, trackResiduals.getNY2XBins() - 1); double zMin = rowInfo.x * trackResiduals.getZ2X(0); @@ -514,366 +507,319 @@ std::unique_ptr TPCFastSpaceChargeCorrect info.scaleUtoGrid = spline.getGridX1().getUmax() / (uMax - uMin); info.gridV0 = vMin; info.scaleVtoGrid = spline.getGridX2().getUmax() / (vMax - vMin); - // std::cout << " iRoc " << iRoc << " iRow " << iRow << " uMin: " << uMin << " uMax: " << uMax << " vMin: " << vMin << " vMax: " << vMax + info.gridCorrU0 = info.gridU0; + info.gridCorrV0 = info.gridV0; + info.scaleCorrUtoGrid = info.scaleUtoGrid; + info.scaleCorrVtoGrid = info.scaleVtoGrid; + + // std::cout << " iSector " << iSector << " iRow " << iRow << " uMin: " << uMin << " uMax: " << uMax << " vMin: " << vMin << " vMax: " << vMax //<< " grid scale u "<< info.scaleUtoGrid << " grid scale v "<< info.scaleVtoGrid<< std::endl; } } LOG(info) << "fast space charge correction helper: preparation took " << watch1.RealTime() << "s"; - LOG(info) << "fast space charge correction helper: fill data points from track residuals.. "; - - TStopwatch watch3; + for (int processingInverseCorrection = 0; processingInverseCorrection < 2; processingInverseCorrection++) { - // read the data ROC by ROC + TTree* currentTree = (processingInverseCorrection) ? voxResTreeInverse : voxResTree; - // data in the tree is not sorted by row - // first find which data belong to which row + if (!currentTree) { + continue; + } - struct VoxelData { - int mNentries{0}; // number of entries - float mX, mY, mZ; // mean position in the local coordinates - float mCx, mCy, mCz; // corrections to the local coordinates - }; + LOG(info) << "fast space charge correction helper: " << ((processingInverseCorrection) ? "inverse" : "direct") + << " : fill data points from track residuals.. "; - std::vector vRocData[nRows * nROCs]; - for (int ir = 0; ir < nRows * nROCs; ir++) { - vRocData[ir].resize(nY2Xbins * nZ2Xbins); - } + TStopwatch watch3; + o2::gpu::TPCFastSpaceChargeCorrectionMap& map = helper->getCorrectionMap(); + map.init(geo.getNumberOfSectors(), geo.getNumberOfRows()); - { // read data from the tree to vRocData + // read the data Sector by Sector - ROOT::TTreeProcessorMT processor(*voxResTree, mNthreads); + // data in the tree is not sorted by row + // first find which data belong to which row - auto myThread = [&](TTreeReader& readerSubRange) { - TTreeReaderValue v(readerSubRange, "voxRes"); - while (readerSubRange.Next()) { - int iRoc = (int)v->bsec; - if (iRoc < 0 || iRoc >= nROCs) { - LOG(fatal) << "Error reading voxels: voxel ROC number " << iRoc << " is out of range"; - continue; - } - int iRow = (int)v->bvox[o2::tpc::TrackResiduals::VoxX]; // bin number in x (= pad row) - if (iRow < 0 || iRow >= nRows) { - LOG(fatal) << "Row number " << iRow << " is out of range"; - } - int iy = v->bvox[o2::tpc::TrackResiduals::VoxF]; // bin number in y/x 0..14 - int iz = v->bvox[o2::tpc::TrackResiduals::VoxZ]; // bin number in z/x 0..4 - auto& data = vRocData[iRoc * nRows + iRow][iy * nZ2Xbins + iz]; - data.mNentries = (int)v->stat[o2::tpc::TrackResiduals::VoxV]; - data.mX = v->stat[o2::tpc::TrackResiduals::VoxX]; - data.mY = v->stat[o2::tpc::TrackResiduals::VoxF]; - data.mZ = v->stat[o2::tpc::TrackResiduals::VoxZ]; - data.mCx = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResX] : v->D[o2::tpc::TrackResiduals::ResX]; - data.mCy = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResY] : v->D[o2::tpc::TrackResiduals::ResY]; - data.mCz = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResZ] : v->D[o2::tpc::TrackResiduals::ResZ]; - if (0 && data.mNentries < 1) { - data.mCx = 0.; - data.mCy = 0.; - data.mCz = 0.; - data.mNentries = 1; - } - } + struct VoxelData { + int mNentries{0}; // number of entries + float mX, mY, mZ; // mean position in the local coordinates + float mCx, mCy, mCz; // corrections to the local coordinates }; - processor.Process(myThread); - } - for (int iRoc = 0; iRoc < nROCs; iRoc++) { + std::vector vSectorData[nRows * nSectors]; + for (int ir = 0; ir < nRows * nSectors; ir++) { + vSectorData[ir].resize(nY2Xbins * nZ2Xbins); + } - // now process the data row-by-row + { // read data from the tree to vSectorData - auto myThread = [&](int iThread, int nTreads) { - struct Voxel { - float mY, mZ; // not-distorted local coordinates - float mDy, mDz; // bin size - int mSmoothingStep{100}; // is the voxel data original or smoothed at this step + ROOT::TTreeProcessorMT processor(*currentTree, mNthreads); + + auto myThread = [&](TTreeReader& readerSubRange) { + TTreeReaderValue v(readerSubRange, "voxRes"); + while (readerSubRange.Next()) { + int iSector = (int)v->bsec; + if (iSector < 0 || iSector >= nSectors) { + LOG(fatal) << "Error reading voxels: voxel Sector number " << iSector << " is out of range"; + continue; + } + int iRow = (int)v->bvox[o2::tpc::TrackResiduals::VoxX]; // bin number in x (= pad row) + if (iRow < 0 || iRow >= nRows) { + LOG(fatal) << "Row number " << iRow << " is out of range"; + } + int iy = v->bvox[o2::tpc::TrackResiduals::VoxF]; // bin number in y/x 0..14 + int iz = v->bvox[o2::tpc::TrackResiduals::VoxZ]; // bin number in z/x 0..4 + auto& data = vSectorData[iSector * nRows + iRow][iy * nZ2Xbins + iz]; + data.mNentries = (int)v->stat[o2::tpc::TrackResiduals::VoxV]; + data.mX = v->stat[o2::tpc::TrackResiduals::VoxX]; + data.mY = v->stat[o2::tpc::TrackResiduals::VoxF]; + data.mZ = v->stat[o2::tpc::TrackResiduals::VoxZ]; + data.mCx = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResX] : v->D[o2::tpc::TrackResiduals::ResX]; + data.mCy = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResY] : v->D[o2::tpc::TrackResiduals::ResY]; + data.mCz = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResZ] : v->D[o2::tpc::TrackResiduals::ResZ]; + if (0 && data.mNentries < 1) { + data.mCx = 0.; + data.mCy = 0.; + data.mCz = 0.; + data.mNentries = 1; + } + } }; + processor.Process(myThread); + } - std::vector vRowVoxels(nY2Xbins * nZ2Xbins); + for (int iSector = 0; iSector < nSectors; iSector++) { - for (int iRow = iThread; iRow < nRows; iRow += nTreads) { - // LOG(info) << "Processing ROC " << iRoc << " row " << iRow; + // now process the data row-by-row - // complete the voxel data + auto myThread = [&](int iThread, int nTreads) { + struct Voxel { + float mY, mZ; // not-distorted local coordinates + float mDy, mDz; // bin size + int mSmoothingStep{100}; // is the voxel data original or smoothed at this step + }; - { - int xBin = iRow; - double x = trackResiduals.getX(xBin); // radius of the pad row - bool isDataFound = false; - for (int iy = 0; iy < nY2Xbins; iy++) { - for (int iz = 0; iz < nZ2Xbins; iz++) { - auto& data = vRocData[iRoc * nRows + iRow][iy * nZ2Xbins + iz]; - auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; - // y/x coordinate of the bin ~-0.15 ... 0.15 - double y2x = trackResiduals.getY2X(xBin, iy); - // z/x coordinate of the bin 0.1 .. 0.9 - double z2x = trackResiduals.getZ2X(iz); - vox.mY = x * y2x; - vox.mZ = x * z2x; - vox.mDy = x / trackResiduals.getDY2XI(xBin, iy); - vox.mDz = x * trackResiduals.getDZ2X(iz); - if (iRoc >= geo.getNumberOfRocsA()) { - vox.mZ = -vox.mZ; - } - data.mY *= x; - data.mZ *= x; - /* - if ( fabs(x - data.mX) > 0.01 || fabs(vox.mY - data.mY) > 5. || fabs(vox.mZ - data.mZ) > 5.) { - std::cout - << " roc " << iRoc << " row " << iRow - << " voxel x " << x << " y " << vox.mY << " z " << vox.mZ - << " data x " << data.mX << " y " << data.mY << " z " << data.mZ - << std::endl; - } - */ - if (0) { // debug: always use voxel center instead of the mean position - data.mY = vox.mY; - data.mZ = vox.mZ; - } - if (data.mNentries < 1) { // no data - data.mCx = 0.; - data.mCy = 0.; - data.mCz = 0.; - data.mY = vox.mY; - data.mZ = vox.mZ; - vox.mSmoothingStep = 100; - } else { // voxel contains data - if (invertSigns) { - data.mCx *= -1.; - data.mCy *= -1.; - data.mCz *= -1.; - } - vox.mSmoothingStep = 0; // original data - isDataFound = true; - } - } - } + std::vector vRowVoxels(nY2Xbins * nZ2Xbins); + + for (int iRow = iThread; iRow < nRows; iRow += nTreads) { + // LOG(info) << "Processing Sector " << iSector << " row " << iRow; - if (!isDataFound) { // fill everything with 0 + // complete the voxel data + + { + int xBin = iRow; + double x = trackResiduals.getX(xBin); // radius of the pad row + bool isDataFound = false; for (int iy = 0; iy < nY2Xbins; iy++) { for (int iz = 0; iz < nZ2Xbins; iz++) { - vRowVoxels[iy * nZ2Xbins + iz].mSmoothingStep = 0; + auto& data = vSectorData[iSector * nRows + iRow][iy * nZ2Xbins + iz]; + auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; + // y/x coordinate of the bin ~-0.15 ... 0.15 + double y2x = trackResiduals.getY2X(xBin, iy); + // z/x coordinate of the bin 0.1 .. 0.9 + double z2x = trackResiduals.getZ2X(iz); + vox.mY = x * y2x; + vox.mZ = x * z2x; + vox.mDy = x / trackResiduals.getDY2XI(xBin, iy); + vox.mDz = x * trackResiduals.getDZ2X(iz); + if (iSector >= geo.getNumberOfSectorsA()) { + vox.mZ = -vox.mZ; + } + data.mY *= x; + data.mZ *= x; + /* + if ( fabs(x - data.mX) > 0.01 || fabs(vox.mY - data.mY) > 5. || fabs(vox.mZ - data.mZ) > 5.) { + std::cout + << " sector " << iSector << " row " << iRow + << " voxel x " << x << " y " << vox.mY << " z " << vox.mZ + << " data x " << data.mX << " y " << data.mY << " z " << data.mZ + << std::endl; + } + */ + if (0) { // debug: always use voxel center instead of the mean position + data.mY = vox.mY; + data.mZ = vox.mZ; + } + if (data.mNentries < 1) { // no data + data.mCx = 0.; + data.mCy = 0.; + data.mCz = 0.; + data.mY = vox.mY; + data.mZ = vox.mZ; + vox.mSmoothingStep = 100; + } else { // voxel contains data + if (invertSigns) { + data.mCx *= -1.; + data.mCy *= -1.; + data.mCz *= -1.; + } + vox.mSmoothingStep = 0; // original data + isDataFound = true; + } } } - } - } // complete the voxel data - // repare the voxel data: fill empty voxels - - int nRepairs = 0; - - for (int ismooth = 1; ismooth <= 2; ismooth++) { - for (int iy = 0; iy < nY2Xbins; iy++) { - for (int iz = 0; iz < nZ2Xbins; iz++) { - auto& data = vRocData[iRoc * nRows + iRow][iy * nZ2Xbins + iz]; - auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; - if (vox.mSmoothingStep <= ismooth) { // already filled - continue; - } - nRepairs++; - data.mCx = 0.; - data.mCy = 0.; - data.mCz = 0.; - double w = 0.; - bool filled = false; - auto update = [&](int iy1, int iz1) { - auto& data1 = vRocData[iRoc * nRows + iRow][iy1 * nZ2Xbins + iz1]; - auto& vox1 = vRowVoxels[iy1 * nZ2Xbins + iz1]; - if (vox1.mSmoothingStep >= ismooth) { - return false; + if (!isDataFound) { // fill everything with 0 + for (int iy = 0; iy < nY2Xbins; iy++) { + for (int iz = 0; iz < nZ2Xbins; iz++) { + vRowVoxels[iy * nZ2Xbins + iz].mSmoothingStep = 0; } - double w1 = 1. / (abs(iy - iy1) + abs(iz - iz1) + 1); - data.mCx += w1 * data1.mCx; - data.mCy += w1 * data1.mCy; - data.mCz += w1 * data1.mCz; - w += w1; - filled = true; - return true; - }; - - for (int iy1 = iy - 1; iy1 >= 0 && !update(iy1, iz); iy1--) { - } - for (int iy1 = iy + 1; iy1 < nY2Xbins && !update(iy1, iz); iy1++) { - } - for (int iz1 = iz - 1; iz1 >= 0 && !update(iy, iz1); iz1--) { - } - for (int iz1 = iz + 1; iz1 < nZ2Xbins && !update(iy, iz1); iz1++) { } + } + } // complete the voxel data - if (filled) { - data.mCx /= w; - data.mCy /= w; - data.mCz /= w; - vox.mSmoothingStep = ismooth; - } - } // iz - } // iy - } // ismooth + // repare the voxel data: fill empty voxels - if (nRepairs > 0) { - LOG(debug) << "ROC " << iRoc << " row " << iRow << ": " << nRepairs << " voxel repairs for " << nY2Xbins * nZ2Xbins << " voxels"; - } + int nRepairs = 0; - // feed the row data to the helper + for (int ismooth = 1; ismooth <= 2; ismooth++) { + for (int iy = 0; iy < nY2Xbins; iy++) { + for (int iz = 0; iz < nZ2Xbins; iz++) { + auto& data = vSectorData[iSector * nRows + iRow][iy * nZ2Xbins + iz]; + auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; + if (vox.mSmoothingStep <= ismooth) { // already filled + continue; + } + nRepairs++; + data.mCx = 0.; + data.mCy = 0.; + data.mCz = 0.; + double w = 0.; + bool filled = false; + auto update = [&](int iy1, int iz1) { + auto& data1 = vSectorData[iSector * nRows + iRow][iy1 * nZ2Xbins + iz1]; + auto& vox1 = vRowVoxels[iy1 * nZ2Xbins + iz1]; + if (vox1.mSmoothingStep >= ismooth) { + return false; + } + double w1 = 1. / (abs(iy - iy1) + abs(iz - iz1) + 1); + data.mCx += w1 * data1.mCx; + data.mCy += w1 * data1.mCy; + data.mCz += w1 * data1.mCz; + w += w1; + filled = true; + return true; + }; + + for (int iy1 = iy - 1; iy1 >= 0 && !update(iy1, iz); iy1--) { + } + for (int iy1 = iy + 1; iy1 < nY2Xbins && !update(iy1, iz); iy1++) { + } + for (int iz1 = iz - 1; iz1 >= 0 && !update(iy, iz1); iz1--) { + } + for (int iz1 = iz + 1; iz1 < nZ2Xbins && !update(iy, iz1); iz1++) { + } - auto& info = correction.getRocRowInfo(iRoc, iRow); - const auto& spline = correction.getSpline(iRoc, iRow); + if (filled) { + data.mCx /= w; + data.mCy /= w; + data.mCz /= w; + vox.mSmoothingStep = ismooth; + } + } // iz + } // iy + } // ismooth - auto addEdge = [&](int iy1, int iz1, int iy2, int iz2, int nSteps) { - auto& data1 = vRocData[iRoc * nRows + iRow][iy1 * nZ2Xbins + iz1]; - auto& vox1 = vRowVoxels[iy1 * nZ2Xbins + iz1]; - auto& data2 = vRocData[iRoc * nRows + iRow][iy2 * nZ2Xbins + iz2]; - auto& vox2 = vRowVoxels[iy2 * nZ2Xbins + iz2]; - if (vox1.mSmoothingStep > 2) { - LOG(fatal) << "empty voxel is not repared: y " << iy1 << " z " << iz1; - } - if (vox2.mSmoothingStep > 2) { - LOG(fatal) << "empty voxel is not repared: y " << iy2 << " z " << iz2; - } - double y1 = vox1.mY; - double z1 = vox1.mZ; - double cx1 = data1.mCx; - double cy1 = data1.mCy; - double cz1 = data1.mCz; - double y2 = vox2.mY; - double z2 = vox2.mZ; - double cx2 = data2.mCx; - double cy2 = data2.mCy; - double cz2 = data2.mCz; - - for (int is = 0; is < nSteps; is++) { - double s2 = is / (double)nSteps; - double s1 = 1. - s2; - double y = s1 * y1 + s2 * y2; - double z = s1 * z1 + s2 * z2; - double cx = s1 * cx1 + s2 * cx2; - double cy = s1 * cy1 + s2 * cy2; - double cz = s1 * cz1 + s2 * cz2; - map.addCorrectionPoint(iRoc, iRow, y, z, cx, cy, cz); + if (nRepairs > 0) { + LOG(debug) << "Sector " << iSector << " row " << iRow << ": " << nRepairs << " voxel repairs for " << nY2Xbins * nZ2Xbins << " voxels"; } - }; - for (int iy = 0; iy < nY2Xbins; iy++) { - for (int iz = 0; iz < nZ2Xbins - 1; iz++) { - addEdge(iy, iz, iy, iz + 1, 3); - } - addEdge(iy, nZ2Xbins - 1, iy, nZ2Xbins - 1, 1); - } + // feed the row data to the helper - for (int iz = 0; iz < nZ2Xbins; iz++) { - for (int iy = 0; iy < nY2Xbins - 1; iy++) { - addEdge(iy, iz, iy + 1, iz, 3); - } - addEdge(nY2Xbins - 1, iz, nY2Xbins - 1, iz, 1); - } // iy + auto& info = correction.getSectorRowInfo(iSector, iRow); + const auto& spline = correction.getSpline(iSector, iRow); - } // iRow - }; // myThread + auto addEdge = [&](int iy1, int iz1, int iy2, int iz2, int nSteps) { + auto& data1 = vSectorData[iSector * nRows + iRow][iy1 * nZ2Xbins + iz1]; + auto& vox1 = vRowVoxels[iy1 * nZ2Xbins + iz1]; + auto& data2 = vSectorData[iSector * nRows + iRow][iy2 * nZ2Xbins + iz2]; + auto& vox2 = vRowVoxels[iy2 * nZ2Xbins + iz2]; + if (vox1.mSmoothingStep > 2) { + LOG(fatal) << "empty voxel is not repared: y " << iy1 << " z " << iz1; + } + if (vox2.mSmoothingStep > 2) { + LOG(fatal) << "empty voxel is not repared: y " << iy2 << " z " << iz2; + } + double y1 = vox1.mY; + double z1 = vox1.mZ; + double cx1 = data1.mCx; + double cy1 = data1.mCy; + double cz1 = data1.mCz; + double y2 = vox2.mY; + double z2 = vox2.mZ; + double cx2 = data2.mCx; + double cy2 = data2.mCy; + double cz2 = data2.mCz; + + for (int is = 0; is < nSteps; is++) { + double s2 = is / (double)nSteps; + double s1 = 1. - s2; + double y = s1 * y1 + s2 * y2; + double z = s1 * z1 + s2 * z2; + double cx = s1 * cx1 + s2 * cx2; + double cy = s1 * cy1 + s2 * cy2; + double cz = s1 * cz1 + s2 * cz2; + map.addCorrectionPoint(iSector, iRow, y, z, cx, cy, cz); + } + }; - // run n threads + for (int iy = 0; iy < nY2Xbins; iy++) { + for (int iz = 0; iz < nZ2Xbins - 1; iz++) { + addEdge(iy, iz, iy, iz + 1, 3); + } + addEdge(iy, nZ2Xbins - 1, iy, nZ2Xbins - 1, 1); + } - int nThreads = mNthreads; - // nThreads = 1; + for (int iz = 0; iz < nZ2Xbins; iz++) { + for (int iy = 0; iy < nY2Xbins - 1; iy++) { + addEdge(iy, iz, iy + 1, iz, 3); + } + addEdge(nY2Xbins - 1, iz, nY2Xbins - 1, iz, 1); + } // iy - std::vector threads(nThreads); + } // iRow + }; // myThread - for (int i = 0; i < nThreads; i++) { - threads[i] = std::thread(myThread, i, nThreads); - } + // run n threads - // wait for the threads to finish - for (auto& th : threads) { - th.join(); - } - } // iRoc + int nThreads = mNthreads; + // nThreads = 1; - LOGP(info, "Reading & reparing of the track residuals tooks: {}s", watch3.RealTime()); + std::vector threads(nThreads); - LOG(info) << "fast space charge correction helper: create space charge from the map of data points.."; + for (int i = 0; i < nThreads; i++) { + threads[i] = std::thread(myThread, i, nThreads); + } - TStopwatch watch4; + // wait for the threads to finish + for (auto& th : threads) { + th.join(); + } + } // iSector - helper->fillSpaceChargeCorrectionFromMap(correction); + LOGP(info, "Reading & reparing of the track residuals tooks: {}s", watch3.RealTime()); - LOG(info) << "fast space charge correction helper: creation from the data map took " << watch4.RealTime() << "s"; + LOG(info) << "fast space charge correction helper: create space charge from the map of data points.."; - LOGP(info, "Creation from track residuals tooks in total: {}s", watch.RealTime()); + TStopwatch watch4; - return std::move(correctionPtr); -} + helper->fillSpaceChargeCorrectionFromMap(correction, processingInverseCorrection); -void TPCFastSpaceChargeCorrectionHelper::initMaxDriftLength(o2::gpu::TPCFastSpaceChargeCorrection& correction, bool prn) -{ - /// initialise max drift length + LOG(info) << "fast space charge correction helper: creation from the data map took " << watch4.RealTime() << "s"; - double tpcR2min = mGeo.getRowInfo(0).x - 1.; - tpcR2min = tpcR2min * tpcR2min; - double tpcR2max = mGeo.getRowInfo(mGeo.getNumberOfRows() - 1).x; - tpcR2max = tpcR2max / cos(2 * M_PI / mGeo.getNumberOfRocsA() / 2) + 1.; - tpcR2max = tpcR2max * tpcR2max; + } // processingInverseCorrection - ChebyshevFit1D chebFitter; + if (voxResTree && !voxResTreeInverse) { + LOG(info) << "fast space charge correction helper: init inverse correction from direct correction.."; + TStopwatch watch4; + helper->initInverse(correction, false); + LOG(info) << "fast space charge correction helper: init inverse correction took " << watch4.RealTime() << "s"; + } - for (int roc = 0; roc < mGeo.getNumberOfRocs(); roc++) { - if (prn) { - LOG(info) << "init MaxDriftLength for roc " << roc; - } - double vLength = mGeo.getTPCzLength(); - TPCFastSpaceChargeCorrection::RocInfo& rocInfo = correction.getRocInfo(roc); - rocInfo.vMax = 0.f; + LOGP(info, "Creation from track residuals tooks in total: {}s", watch.RealTime()); - for (int row = 0; row < mGeo.getNumberOfRows(); row++) { - TPCFastSpaceChargeCorrection::RowActiveArea& area = correction.getRocRowInfo(roc, row).activeArea; - area.cvMax = 0; - area.vMax = 0; - area.cuMin = mGeo.convPadToU(row, 0.f); - area.cuMax = -area.cuMin; - chebFitter.reset(4, 0., mGeo.getRowInfo(row).maxPad); - double x = mGeo.getRowInfo(row).x; - for (int pad = 0; pad < mGeo.getRowInfo(row).maxPad; pad++) { - float u = mGeo.convPadToU(row, (float)pad); - float v0 = 0; - float v1 = 1.1 * vLength; - float vLastValid = -1; - float cvLastValid = -1; - while (v1 - v0 > 0.1) { - float v = 0.5 * (v0 + v1); - float dx, du, dv; - correction.getCorrectionInternal(roc, row, u, v, dx, du, dv); - double cx = x + dx; - double cu = u + du; - double cv = v + dv; - double r2 = cx * cx + cu * cu; - if (cv < 0) { - v0 = v; - } else if (cv <= vLength && r2 >= tpcR2min && r2 <= tpcR2max) { - v0 = v; - vLastValid = v; - cvLastValid = cv; - } else { - v1 = v; - } - } - if (vLastValid > 0.) { - chebFitter.addMeasurement(pad, vLastValid); - } - if (area.vMax < vLastValid) { - area.vMax = vLastValid; - } - if (area.cvMax < cvLastValid) { - area.cvMax = cvLastValid; - } - } - chebFitter.fit(); - for (int i = 0; i < 5; i++) { - area.maxDriftLengthCheb[i] = chebFitter.getCoefficients()[i]; - } - if (rocInfo.vMax < area.vMax) { - rocInfo.vMax = area.vMax; - } - } // row - } // roc -} + return std::move(correctionPtr); + +} // createFromTrackResiduals void TPCFastSpaceChargeCorrectionHelper::initInverse(o2::gpu::TPCFastSpaceChargeCorrection& correction, bool prn) { @@ -893,28 +839,24 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector helper; std::vector splineParameters; for (int row = iThread; row < mGeo.getNumberOfRows(); row += mNthreads) { - TPCFastSpaceChargeCorrection::SplineType spline = correction.getSpline(roc, row); + TPCFastSpaceChargeCorrection::SplineType spline = correction.getSpline(sector, row); helper.setSpline(spline, 10, 10); - double x = mGeo.getRowInfo(row).x; - auto& rocRowInfo = correction.getRocRowInfo(roc, row); - std::vector gridU; { const auto& grid = spline.getGridX1(); @@ -942,95 +884,64 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector dataPointCU, dataPointCV, dataPointF; - dataPointCU.reserve(gridU.size() * gridV.size()); - dataPointCV.reserve(gridU.size() * gridV.size()); - dataPointF.reserve(gridU.size() * gridV.size()); - - TPCFastSpaceChargeCorrection::RowActiveArea& area = rocRowInfo.activeArea; - area.cuMin = 1.e10; - area.cuMax = -1.e10; - double cvMin = 1.e10; + std::vector dataPointGridU, dataPointGridV, dataPointF; + dataPointGridU.reserve(gridU.size() * gridV.size()); + dataPointGridV.reserve(gridU.size() * gridV.size()); + dataPointF.reserve(3 * gridU.size() * gridV.size()); for (int iu = 0; iu < gridU.size(); iu++) { for (int iv = 0; iv < gridV.size(); iv++) { - float u, v; - correction.convGridToUV(roc, row, gridU[iu], gridV[iv], u, v); - - float dx, du, dv; - correction.getCorrectionInternal(roc, row, u, v, dx, du, dv); - dx *= scaling[0]; - du *= scaling[0]; - dv *= scaling[0]; - // add remaining corrections - for (int i = 1; i < corrections.size(); ++i) { - float dxTmp, duTmp, dvTmp; - corrections[i]->getCorrectionInternal(roc, row, u, v, dxTmp, duTmp, dvTmp); + + auto [y, z] = correction.convGridToLocal(sector, row, gridU[iu], gridV[iv]); + double dx = 0, dy = 0, dz = 0; + + // add corrections + for (int i = 0; i < corrections.size(); ++i) { + auto [dxTmp, dyTmp, dzTmp] = corrections[i]->getCorrectionLocal(sector, row, y, z); dx += dxTmp * scaling[i]; - du += duTmp * scaling[i]; - dv += dvTmp * scaling[i]; - } - double cx = x + dx; - double cu = u + du; - double cv = v + dv; - if (cu < area.cuMin) { - area.cuMin = cu; - } - if (cu > area.cuMax) { - area.cuMax = cu; + dy += dyTmp * scaling[i]; + dz += dzTmp * scaling[i]; } - dataPointCU.push_back(cu); - dataPointCV.push_back(cv); + double realY = y + dy; + double realZ = z + dz; + float realU, realV; + mGeo.convLocalToUV1(sector, realY, realZ, realU, realV); + + dataPointGridU.push_back(realU); + dataPointGridV.push_back(realV); dataPointF.push_back(dx); - dataPointF.push_back(du); - dataPointF.push_back(dv); + dataPointF.push_back(dy); + dataPointF.push_back(dz); } } - if (area.cuMax - area.cuMin < 0.2) { - area.cuMax = .1; - area.cuMin = -.1; - } - if (area.cvMax - cvMin < 0.2) { - area.cvMax = .1; - cvMin = -.1; - } - - if (prn) { - LOG(info) << "roc " << roc << " row " << row << " max drift L = " << correction.getMaxDriftLength(roc, row) - << " active area: cuMin " << area.cuMin << " cuMax " << area.cuMax << " vMax " << area.vMax << " cvMax " << area.cvMax; - } - // define the grid for the inverse correction - rocRowInfo.gridCorrU0 = area.cuMin; - rocRowInfo.gridCorrV0 = cvMin; - rocRowInfo.scaleCorrUtoGrid = spline.getGridX1().getUmax() / (area.cuMax - area.cuMin); - rocRowInfo.scaleCorrVtoGrid = spline.getGridX2().getUmax() / area.cvMax; + auto& sectorRowInfo = correction.getSectorRowInfo(sector, row); + + sectorRowInfo.gridCorrU0 = sectorRowInfo.gridU0; + sectorRowInfo.gridCorrV0 = sectorRowInfo.gridV0; + sectorRowInfo.scaleCorrUtoGrid = sectorRowInfo.scaleUtoGrid; + sectorRowInfo.scaleCorrVtoGrid = sectorRowInfo.scaleVtoGrid; - /* - rocRowInfo.gridCorrU0 = rocRowInfo.gridU0; - rocRowInfo.gridCorrV0 = rocRowInfo.gridV0; - rocRowInfo.scaleCorrUtoGrid = rocRowInfo.scaleUtoGrid; - rocRowInfo.scaleCorrVtoGrid = rocRowInfo.scaleVtoGrid; - */ + int nDataPoints = dataPointGridU.size(); - int nDataPoints = dataPointCU.size(); + // convert real Y,Z to grid U,V for (int i = 0; i < nDataPoints; i++) { - dataPointCU[i] = (dataPointCU[i] - rocRowInfo.gridCorrU0) * rocRowInfo.scaleCorrUtoGrid; - dataPointCV[i] = (dataPointCV[i] - rocRowInfo.gridCorrV0) * rocRowInfo.scaleCorrVtoGrid; + dataPointGridU[i] = (dataPointGridU[i] - sectorRowInfo.gridCorrU0) * sectorRowInfo.scaleCorrUtoGrid; + dataPointGridV[i] = (dataPointGridV[i] - sectorRowInfo.gridCorrV0) * sectorRowInfo.scaleCorrVtoGrid; } splineParameters.resize(spline.getNumberOfParameters()); helper.approximateDataPoints(spline, splineParameters.data(), 0., spline.getGridX1().getUmax(), 0., spline.getGridX2().getUmax(), - dataPointCU.data(), dataPointCV.data(), - dataPointF.data(), dataPointCU.size()); + dataPointGridU.data(), dataPointGridV.data(), + dataPointF.data(), nDataPoints); - float* splineX = correction.getSplineData(roc, row, 1); - float* splineUV = correction.getSplineData(roc, row, 2); + float* splineX = correction.getSplineData(sector, row, 1); + float* splineUV = correction.getSplineData(sector, row, 2); for (int i = 0; i < spline.getNumberOfParameters() / 3; i++) { splineX[i] = splineParameters[3 * i + 0]; splineUV[2 * i + 0] = splineParameters[3 * i + 1]; @@ -1051,10 +962,100 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector& corrections, const std::vector& scaling, bool prn) +{ + /// merge several corrections + /* + TStopwatch watch; + LOG(info) << "fast space charge correction helper: Merge corrections"; + + if (corrections.size() != scaling.size()) { + LOGP(error, "Input corrections and scaling values have different size"); + return; + } + + auto& correction = *(corrections.front()); + + for (int sector = 0; sector < mGeo.getNumberOfSectors(); sector++) { + + auto myThread = [&](int iThread) { + for (int row = iThread; row < mGeo.getNumberOfRows(); row += mNthreads) { + TPCFastSpaceChargeCorrection::SplineType spline = correction.getSpline(sector, row); + + std::vector splineParameters(spline.getNumberOfParameters()); + std::vector splineParametersInvX(spline.getNumberOfParameters()); + std::vector splineParametersInvYZ(spline.getNumberOfParameters()); + + const auto& gridU = spline.getGridX1(); + const auto& gridV = spline.getGridX2(); + + for (int iu = 0; iu < gridU.getNumberOfKnots(); iu++) { + double u = gridU.getKnot(iu).u; + for (int iv = 0; iv < gridV.getNumberOfKnots(); iv++) { + int knotIndex = spline.getKnotIndex(iu, iv); + + double v = gridV.getKnot(iu).u; + auto [y, z] = correction.convGridToLocal(sector, row, u, v); + constexpr int nKnotPar1d = 4; + constexpr int nKnotPar2d = nKnotPar1d * 2; + constexpr int nKnotPar3d = nKnotPar1d * 3; + + for (int i = 0; i < corrections.size(); ++i) { + double s = scaling[i]; + auto p = corrections[i]->getCorrectionParameters(sector, row, y, z); + for (int j = 0; j < nKnotPar3d; ++j) { + splineParameters[knotIndex * nKnotPar3d + j] += s * p[j]; + } + auto pInvX = corrections[i]->getCorrectionParametersInvX(sector, row, y, z); + for (int j = 0; j < nKnotPar1d; ++j) { + splineParametersInvX[knotIndex * nKnotPar1d + j] += s * pInvX[j]; + } + auto pInvYZ = corrections[i]->getCorrectionParametersInvYZ(sector, row, y, z); + for (int j = 0; j < nKnotPar2d; ++j) { + splineParametersInvYZ[knotIndex * nKnotPar2d + j] += s * pInvYZ[j]; + } + } + } // iv + } // iu + + float* splineXYZ = correction.getSplineData(sector, row, 0); + float* splineInvX = correction.getSplineData(sector, row, 1); + float* splineInvYZ = correction.getSplineData(sector, row, 2); + + for (int i = 0; i < spline.getNumberOfParameters(); i++) { + splineXYZ[i] = splineParameters[i]; + } + for (int i = 0; i < spline.getNumberOfParameters() / 3; i++) { + splineX[i] = splineParametersInvX[i]; + splineYZ[2 * i + 0] = splineParametersInvYZ[2 * i + 0]; + splineYZ[2 * i + 1] = splineParametersInvYZ[2 * i + 1]; + } + + } // row + }; // thread + + std::vector threads(mNthreads); + + // run n threads + for (int i = 0; i < mNthreads; i++) { + threads[i] = std::thread(myThread, i); + } + + // wait for the threads to finish + for (auto& th : threads) { + th.join(); + } + + } // sector + float duration = watch.RealTime(); + LOGP(info, "Merge of corrections tooks: {}s", duration); + */ +} + } // namespace tpc } // namespace o2 diff --git a/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx b/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx index c83ee6d0cfa19..a6a2c9722caeb 100644 --- a/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx +++ b/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx @@ -177,8 +177,8 @@ void TPCFastTransformHelperO2::testGeometry(const TPCFastTransformGeo& geo) cons { const Mapper& mapper = Mapper::instance(); - if (geo.getNumberOfRocs() != Sector::MAXSECTOR) { - LOG(fatal) << "Wrong number of sectors :" << geo.getNumberOfRocs() << " instead of " << Sector::MAXSECTOR << std::endl; + if (geo.getNumberOfSectors() != Sector::MAXSECTOR) { + LOG(fatal) << "Wrong number of sectors :" << geo.getNumberOfSectors() << " instead of " << Sector::MAXSECTOR << std::endl; } if (geo.getNumberOfRows() != mapper.getNumberOfRows()) { diff --git a/Detectors/TPC/reconstruction/test/testTPCFastTransform.cxx b/Detectors/TPC/reconstruction/test/testTPCFastTransform.cxx index 53cfe08f3a7f4..fee63e9e38bc2 100644 --- a/Detectors/TPC/reconstruction/test/testTPCFastTransform.cxx +++ b/Detectors/TPC/reconstruction/test/testTPCFastTransform.cxx @@ -53,7 +53,7 @@ BOOST_AUTO_TEST_CASE(FastTransform_test1) BOOST_CHECK_EQUAL(geo.test(), 0); - BOOST_CHECK_EQUAL(geo.getNumberOfRocs(), Sector::MAXSECTOR); + BOOST_CHECK_EQUAL(geo.getNumberOfSectors(), Sector::MAXSECTOR); BOOST_CHECK_EQUAL(geo.getNumberOfRows(), mapper.getNumberOfRows()); double maxDx = 0, maxDy = 0; @@ -71,15 +71,16 @@ BOOST_AUTO_TEST_CASE(FastTransform_test1) for (int pad = 0; pad < nPads; pad++) { const GlobalPadNumber p = mapper.globalPadNumber(PadPos(row, pad)); const PadCentre& c = mapper.padCentre(p); - float u = 0, v = 0; - fastTransform.convPadTimeToUV(row, pad, 0, u, v, 0.); - + float y = 0, z = 0; + int sector = 0; + float time = 0.; + fastTransform.convPadTimeToLocal(sector, row, pad, time, y, z, 0.); double dx = x - c.X(); - double dy = u - (-c.Y()); // diferent sign convention for Y coordinate in the map + double dy = y - (-c.Y()); // diferent sign convention for Y coordinate in the map BOOST_CHECK(fabs(dx) < 1.e-6); BOOST_CHECK(fabs(dy) < 1.e-5); if (fabs(dy) >= 1.e-5) { - std::cout << "row " << row << " pad " << pad << " y calc " << u << " y in map " << -c.Y() << " dy " << dy << std::endl; + std::cout << "row " << row << " pad " << pad << " y calc " << y << " y in map " << -c.Y() << " dy " << dy << std::endl; } if (fabs(maxDx) < fabs(dx)) { maxDx = dx; @@ -104,46 +105,46 @@ BOOST_AUTO_TEST_CASE(FastTransform_test_setSpaceChargeCorrection) std::unique_ptr fastTransform0(TPCFastTransformHelperO2::instance()->create(0)); const TPCFastTransformGeo& geo = fastTransform0->getGeometry(); - auto correctionUV = [&](int roc, int /*row*/, const double u, const double v, double& dX, double& dU, double& dV) { + auto correctionUV = [&](int sector, int /*row*/, const double u, const double v, double& dX, double& dU, double& dV) { // float lx = geo.getRowInfo(row).x; dX = 1. + 1 * u + 0.1 * u * u; dU = 2. + 0.2 * u + 0.002 * u * u; // + 0.001 * u * u * u; dV = 3. + 0.1 * v + 0.01 * v * v; //+ 0.0001 * v * v * v; }; - auto correctionLocal = [&](int roc, int row, double ly, double lz, + auto correctionLocal = [&](int sector, int row, double ly, double lz, double& dx, double& dly, double& dlz) { float u, v; - geo.convLocalToUV(roc, ly, lz, u, v); + geo.convLocalToUV(sector, ly, lz, u, v); double du, dv; - correctionUV(roc, row, u, v, dx, du, dv); + correctionUV(sector, row, u, v, dx, du, dv); float ly1, lz1; - geo.convUVtoLocal(roc, u + du, v + dv, ly1, lz1); + geo.convUVtoLocal(sector, u + du, v + dv, ly1, lz1); dly = ly1 - ly; dlz = lz1 - lz; }; - int nRocs = geo.getNumberOfRocs(); + int nSectors = geo.getNumberOfSectors(); int nRows = geo.getNumberOfRows(); TPCFastSpaceChargeCorrectionMap& scData = TPCFastTransformHelperO2::instance()->getCorrectionMap(); - scData.init(nRocs, nRows); + scData.init(nSectors, nRows); - for (int iRoc = 0; iRoc < nRocs; iRoc++) { + for (int iSector = 0; iSector < nSectors; iSector++) { for (int iRow = 0; iRow < nRows; iRow++) { double dsu = 1. / (3 * 8 - 3); double dsv = 1. / (3 * 20 - 3); for (double su = 0.f; su < 1.f + .5 * dsu; su += dsv) { for (double sv = 0.f; sv < 1.f + .5 * dsv; sv += dsv) { float ly = 0.f, lz = 0.f; - geo.convScaledUVtoLocal(iRoc, iRow, su, sv, ly, lz); + geo.convScaledUVtoLocal(iSector, iRow, su, sv, ly, lz); double dx, dy, dz; - correctionLocal(iRoc, iRow, ly, lz, dx, dy, dz); - scData.addCorrectionPoint(iRoc, iRow, + correctionLocal(iSector, iRow, ly, lz, dx, dy, dz); + scData.addCorrectionPoint(iSector, iRow, ly, lz, dx, dy, dz); } } } // row - } // roc + } // sector std::unique_ptr fastTransform(TPCFastTransformHelperO2::instance()->create(0)); @@ -158,12 +159,12 @@ BOOST_AUTO_TEST_CASE(FastTransform_test_setSpaceChargeCorrection) double statDiff = 0., statN = 0.; double statDiffFile = 0., statNFile = 0.; - for (int roc = 0; roc < geo.getNumberOfRocs(); roc += 1) { - // std::cout << "roc " << roc << " ... " << std::endl; + for (int sector = 0; sector < geo.getNumberOfSectors(); sector += 1) { + // std::cout << "sector " << sector << " ... " << std::endl; - const TPCFastTransformGeo::RocInfo& rocInfo = geo.getRocInfo(roc); + const TPCFastTransformGeo::SectorInfo& sectorInfo = geo.getSectorInfo(sector); - float lastTimeBin = fastTransform->getMaxDriftTime(roc, 0.f); + float lastTimeBin = fastTransform->getMaxDriftTime(sector, 0.f); for (int row = 0; row < geo.getNumberOfRows(); row++) { @@ -172,31 +173,31 @@ BOOST_AUTO_TEST_CASE(FastTransform_test_setSpaceChargeCorrection) for (int pad = 0; pad < nPads; pad += 10) { for (float time = 0; time < lastTimeBin; time += 30) { - // std::cout<<"roc "<setApplyCorrectionOff(); float x0, y0, z0; - fastTransform->Transform(roc, row, pad, time, x0, y0, z0); + fastTransform->Transform(sector, row, pad, time, x0, y0, z0); - BOOST_CHECK_EQUAL(geo.test(roc, row, y0, z0), 0); + BOOST_CHECK_EQUAL(geo.test(sector, row, y0, z0), 0); fastTransform->setApplyCorrectionOn(); float x1, y1, z1; - fastTransform->Transform(roc, row, pad, time, x1, y1, z1); + fastTransform->Transform(sector, row, pad, time, x1, y1, z1); // local to UV float u0, v0, u1, v1; - geo.convLocalToUV(roc, y0, z0, u0, v0); - geo.convLocalToUV(roc, y1, z1, u1, v1); + geo.convLocalToUV(sector, y0, z0, u0, v0); + geo.convLocalToUV(sector, y1, z1, u1, v1); double dx, du, dv; - correctionUV(roc, row, u0, v0, dx, du, dv); + correctionUV(sector, row, u0, v0, dx, du, dv); statDiff += fabs((x1 - x0) - dx) + fabs((u1 - u0) - du) + fabs((v1 - v0) - dv); statN += 3; // std::cout << (x1 - x0) - dx << " " << (u1 - u0) - du << " " << (v1 - v0) - dv << std::endl; //": v0 " << v0 <<" z0 "<Transform(roc, row, pad, time, x1f, y1f, z1f); + fromFile->Transform(sector, row, pad, time, x1f, y1f, z1f); statDiffFile += fabs(x1f - x1) + fabs(y1f - y1) + fabs(z1f - z1); statNFile += 3; } diff --git a/GPU/TPCFastTransformation/Spline1DSpec.h b/GPU/TPCFastTransformation/Spline1DSpec.h index dc59e77e308a1..2cc95ebdcab9f 100644 --- a/GPU/TPCFastTransformation/Spline1DSpec.h +++ b/GPU/TPCFastTransformation/Spline1DSpec.h @@ -369,7 +369,41 @@ class Spline1DSpec : public Spline1DContainer dDr = v * a; // F(u) = dSl * Sl + dSr * Sr + dDl * Dl + dDr * Dr; } - + /* + template + GPUd() void getUsecondDerivatives(const Knot& knotL, DataT u, + T& dSl, T& dDl, T& dSr, T& dDr, + T& dSl2, T& dDl2, T& dSr2, T& dDr2) const + { + /// Get derivatives of the interpolated value {S(u): 1D -> nYdim} at the segment [knotL, next knotR] + /// over the spline values Sl, Sr and the slopes Dl, Dr + + if (u < (DataT)0) { + u = (DataT)0; + } + if (u > (DataT)TBase::getUmax()) { + u = (DataT)TBase::getUmax(); + } + + u = u - knotL.u; + T v = u * T(knotL.Li); // scaled u + T vm1 = v - 1.; + T a = u * vm1; + T v2 = v * v; + dSr = v2 * (3. - 2 * v); + dSl = 1. - dSr; + dDl = vm1 * a; + dDr = v * a; + T dv = T(knotL.Li); + dSr2 = 6. * v * (1. - v) * dv; + dSl2 = -dSr2; + dDl2 = (v - 1) * (3 * v - 1); + dDr = u * (v * v - v); + dDr2 = 3.f * v * v - 2.f * v; + // F(u) = dSl * Sl + dSr * Sr + dDl * Dl + dDr * Dr; + // dF(u)/du = dSl2 * Sl + dSr2 * Sr + dDl2 * Dl + dDr2 * Dr; + } + */ using TBase::convXtoU; using TBase::getKnot; using TBase::getKnots; diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx index 111e70072c58e..2921a74b025ce 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx @@ -35,7 +35,7 @@ TPCFastSpaceChargeCorrection::TPCFastSpaceChargeCorrection() mScenarioPtr(nullptr), mTimeStamp(-1), mSplineData{nullptr, nullptr, nullptr}, - mRocDataSizeBytes{0, 0, 0} + mSectorDataSizeBytes{0, 0, 0} { // Default Constructor: creates an empty uninitialized object } @@ -64,7 +64,7 @@ void TPCFastSpaceChargeCorrection::destroy() mTimeStamp = -1; for (int32_t is = 0; is < 3; is++) { mSplineData[is] = nullptr; - mRocDataSizeBytes[is] = 0; + mSectorDataSizeBytes[is] = 0; } FlatObject::destroy(); } @@ -101,13 +101,13 @@ void TPCFastSpaceChargeCorrection::cloneFromObject(const TPCFastSpaceChargeCorre mTimeStamp = obj.mTimeStamp; - for (int32_t i = 0; i < TPCFastTransformGeo::getNumberOfRocs(); ++i) { - mRocInfo[i] = obj.mRocInfo[i]; + for (int32_t i = 0; i < TPCFastTransformGeo::getNumberOfSectors(); ++i) { + mSectorInfo[i] = obj.mSectorInfo[i]; } - mRocDataSizeBytes[0] = obj.mRocDataSizeBytes[0]; - mRocDataSizeBytes[1] = obj.mRocDataSizeBytes[1]; - mRocDataSizeBytes[2] = obj.mRocDataSizeBytes[2]; + mSectorDataSizeBytes[0] = obj.mSectorDataSizeBytes[0]; + mSectorDataSizeBytes[1] = obj.mSectorDataSizeBytes[1]; + mSectorDataSizeBytes[2] = obj.mSectorDataSizeBytes[2]; // variable-size data mScenarioPtr = obj.mScenarioPtr; @@ -121,8 +121,8 @@ void TPCFastSpaceChargeCorrection::cloneFromObject(const TPCFastSpaceChargeCorre mRowInfos[i] = obj.mRowInfos[i]; } - for (int32_t i = 0; i < TPCFastTransformGeo::getNumberOfRocs() * TPCFastTransformGeo::getMaxNumberOfRows(); i++) { - mRocRowInfos[i] = obj.mRocRowInfos[i]; + for (int32_t i = 0; i < TPCFastTransformGeo::getNumberOfSectors() * TPCFastTransformGeo::getMaxNumberOfRows(); i++) { + mSectorRowInfos[i] = obj.mSectorRowInfos[i]; } relocateBufferPointers(oldFlatBufferPtr, mFlatBufferPtr); @@ -143,7 +143,7 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer struct RowInfoVersion3 { int32_t splineScenarioID{0}; ///< scenario index (which of Spline2D splines to use) - size_t dataOffsetBytes[3]{0}; ///< offset for the spline data withing a TPC roc + size_t dataOffsetBytes[3]{0}; ///< offset for the spline data withing a TPC sector }; struct RowActiveAreaVersion3 { @@ -154,7 +154,7 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer float cvMax{0.f}; }; - struct RocRowInfoVersion3 { + struct SectorRowInfoVersion3 { float gridV0{0.f}; ///< V coordinate of the V-grid start float gridCorrU0{0.f}; ///< U coordinate of the U-grid start for corrected U float gridCorrV0{0.f}; ///< V coordinate of the V-grid start for corrected V @@ -171,13 +171,13 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer rowsSize = sizeof(RowInfoVersion3) * mGeo.getNumberOfRows(); } - size_t rocRowsOffset = rowsOffset + rowsSize; - size_t rocRowsSize = 0; - if (mClassVersion == 3) { // copy old-format rocrow data from the buffer to the arrays - rocRowsSize = sizeof(RocRowInfoVersion3) * mGeo.getNumberOfRows() * mGeo.getNumberOfRocs(); + size_t sectorRowsOffset = rowsOffset + rowsSize; + size_t sectorRowsSize = 0; + if (mClassVersion == 3) { // copy old-format sectorrow data from the buffer to the arrays + sectorRowsSize = sizeof(SectorRowInfoVersion3) * mGeo.getNumberOfRows() * mGeo.getNumberOfSectors(); } - size_t scOffset = alignSize(rocRowsOffset + rocRowsSize, SplineType::getClassAlignmentBytes()); + size_t scOffset = alignSize(sectorRowsOffset + sectorRowsSize, SplineType::getClassAlignmentBytes()); size_t scSize = sizeof(SplineType) * mNumberOfScenarios; mScenarioPtr = reinterpret_cast(mFlatBufferPtr + scOffset); @@ -192,12 +192,12 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer } size_t bufferSize = scBufferOffset + scBufferSize; for (int32_t is = 0; is < 3; is++) { - size_t rocDataOffset = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); - mSplineData[is] = reinterpret_cast(mFlatBufferPtr + rocDataOffset); - bufferSize = rocDataOffset + mRocDataSizeBytes[is] * mGeo.getNumberOfRocs(); + size_t sectorDataOffset = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); + mSplineData[is] = reinterpret_cast(mFlatBufferPtr + sectorDataOffset); + bufferSize = sectorDataOffset + mSectorDataSizeBytes[is] * mGeo.getNumberOfSectors(); } - if (mClassVersion == 3) { // copy old-format rocrow data from the buffer to the arrays + if (mClassVersion == 3) { // copy old-format sectorrow data from the buffer to the arrays auto* rowInfosOld = reinterpret_cast(mFlatBufferPtr + rowsOffset); for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { @@ -214,13 +214,13 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer spline.setXrange(0., spline.getGridX1().getUmax(), 0., spline.getGridX2().getUmax()); } - auto* rocRowInfosOld = reinterpret_cast(mFlatBufferPtr + rocRowsOffset); + auto* sectorRowInfosOld = reinterpret_cast(mFlatBufferPtr + sectorRowsOffset); - for (int32_t roc = 0; roc < mGeo.getNumberOfRocs(); roc++) { + for (int32_t sector = 0; sector < mGeo.getNumberOfSectors(); sector++) { for (int32_t row = 0; row < mGeo.getNumberOfRows(); row++) { - RocRowInfoVersion3& infoOld = rocRowInfosOld[mGeo.getNumberOfRows() * roc + row]; - RocRowInfo& info = getRocRowInfo(roc, row); - const auto& spline = getSpline(roc, row); + SectorRowInfoVersion3& infoOld = sectorRowInfosOld[mGeo.getNumberOfRows() * sector + row]; + SectorRowInfo& info = getSectorRowInfo(sector, row); + const auto& spline = getSpline(sector, row); info.gridU0 = mGeo.getRowInfo(row).u0; info.scaleUtoGrid = spline.getGridX1().getUmax() / mGeo.getRowInfo(row).getUwidth(); @@ -232,14 +232,6 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer info.gridCorrV0 = infoOld.gridCorrV0; info.scaleCorrVtoGrid = infoOld.scaleCorrVtoGrid; - - info.activeArea.vMax = infoOld.activeArea.vMax; - info.activeArea.cuMin = infoOld.activeArea.cuMin; - info.activeArea.cuMax = infoOld.activeArea.cuMax; - info.activeArea.cvMax = infoOld.activeArea.cvMax; - for (int32_t i = 0; i < 5; i++) { - info.activeArea.maxDriftLengthCheb[i] = infoOld.activeArea.maxDriftLengthCheb[i]; - } } } } @@ -276,7 +268,7 @@ void TPCFastSpaceChargeCorrection::print() const mGeo.print(); LOG(info) << " mNumberOfScenarios = " << mNumberOfScenarios; LOG(info) << " mTimeStamp = " << mTimeStamp; - LOG(info) << " mRocDataSizeBytes = " << mRocDataSizeBytes[0] << " " << mRocDataSizeBytes[1] << " " << mRocDataSizeBytes[2]; + LOG(info) << " mSectorDataSizeBytes = " << mSectorDataSizeBytes[0] << " " << mSectorDataSizeBytes[1] << " " << mSectorDataSizeBytes[2]; { LOG(info) << " TPC rows: "; for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { @@ -292,9 +284,9 @@ void TPCFastSpaceChargeCorrection::print() const } if (mScenarioPtr) { LOG(info) << " Spline Data: "; - for (int32_t is = 0; is < mGeo.getNumberOfRocs(); is++) { + for (int32_t is = 0; is < mGeo.getNumberOfSectors(); is++) { for (int32_t ir = 0; ir < mGeo.getNumberOfRows(); ir++) { - LOG(info) << "roc " << is << " row " << ir << ": "; + LOG(info) << "sector " << is << " row " << ir << ": "; const SplineType& spline = getSpline(is, ir); const float* d = getSplineData(is, ir); int32_t k = 0; @@ -305,8 +297,8 @@ void TPCFastSpaceChargeCorrection::print() const LOG(info) << ""; } } - // LOG(info) << "inverse correction: roc " << roc - // << " dx " << maxDroc[0] << " du " << maxDroc[1] << " dv " << maxDroc[2] ; + // LOG(info) << "inverse correction: sector " << sector + // << " dx " << maxDsector[0] << " du " << maxDsector[1] << " dv " << maxDsector[2] ; } } } @@ -345,7 +337,7 @@ void TPCFastSpaceChargeCorrection::startConstruction(const TPCFastTransformGeo& mScenarioPtr = nullptr; for (int32_t s = 0; s < 3; s++) { mSplineData[s] = nullptr; - mRocDataSizeBytes[s] = 0; + mSectorDataSizeBytes[s] = 0; } mClassVersion = 4; } @@ -401,18 +393,18 @@ void TPCFastSpaceChargeCorrection::finishConstruction() scBufferSize = alignSize(scBufferSize + sp.getFlatBufferSize(), sp.getBufferAlignmentBytes()); } size_t bufferSize = scBufferOffsets[0] + scBufferSize; - size_t rocDataOffset[3]; + size_t sectorDataOffset[3]; for (int32_t is = 0; is < 3; is++) { - rocDataOffset[is] = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); - mRocDataSizeBytes[is] = 0; + sectorDataOffset[is] = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); + mSectorDataSizeBytes[is] = 0; for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { RowInfo& row = mRowInfos[i]; SplineType& spline = mConstructionScenarios[row.splineScenarioID]; - row.dataOffsetBytes[is] = alignSize(mRocDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); - mRocDataSizeBytes[is] = row.dataOffsetBytes[is] + spline.getSizeOfParameters(); + row.dataOffsetBytes[is] = alignSize(mSectorDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); + mSectorDataSizeBytes[is] = row.dataOffsetBytes[is] + spline.getSizeOfParameters(); } - mRocDataSizeBytes[is] = alignSize(mRocDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); - bufferSize = rocDataOffset[is] + mRocDataSizeBytes[is] * mGeo.getNumberOfRocs(); + mSectorDataSizeBytes[is] = alignSize(mSectorDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); + bufferSize = sectorDataOffset[is] + mSectorDataSizeBytes[is] * mGeo.getNumberOfSectors(); } FlatObject::finishConstruction(bufferSize); @@ -427,7 +419,7 @@ void TPCFastSpaceChargeCorrection::finishConstruction() } for (int32_t is = 0; is < 3; is++) { - mSplineData[is] = reinterpret_cast(mFlatBufferPtr + rocDataOffset[is]); + mSplineData[is] = reinterpret_cast(mFlatBufferPtr + sectorDataOffset[is]); } releaseConstructionMemory(); @@ -439,15 +431,15 @@ void TPCFastSpaceChargeCorrection::finishConstruction() GPUd() void TPCFastSpaceChargeCorrection::setNoCorrection() { // initialise all corrections to 0. - for (int32_t roc = 0; roc < mGeo.getNumberOfRocs(); roc++) { + for (int32_t sector = 0; sector < mGeo.getNumberOfSectors(); sector++) { double vLength = mGeo.getTPCzLength(); - RocInfo& rocInfo = getRocInfo(roc); - rocInfo.vMax = vLength; + SectorInfo& sectorInfo = getSectorInfo(sector); + sectorInfo.vMax = vLength; for (int32_t row = 0; row < mGeo.getNumberOfRows(); row++) { - const SplineType& spline = getSpline(roc, row); + const SplineType& spline = getSpline(sector, row); for (int32_t is = 0; is < 3; is++) { - float* data = getSplineData(roc, row, is); + float* data = getSplineData(sector, row, is); int32_t nPar = spline.getNumberOfParameters(); if (is == 1) { nPar = nPar / 3; @@ -460,7 +452,7 @@ GPUd() void TPCFastSpaceChargeCorrection::setNoCorrection() } } - RocRowInfo& info = getRocRowInfo(roc, row); + SectorRowInfo& info = getSectorRowInfo(sector, row); info.gridU0 = mGeo.getRowInfo(row).u0; info.scaleUtoGrid = spline.getGridX1().getUmax() / mGeo.getRowInfo(row).getUwidth(); @@ -473,18 +465,8 @@ GPUd() void TPCFastSpaceChargeCorrection::setNoCorrection() info.scaleCorrUtoGrid = info.scaleUtoGrid; info.scaleCorrVtoGrid = info.scaleVtoGrid; - RowActiveArea& area = info.activeArea; - for (int32_t i = 1; i < 5; i++) { - area.maxDriftLengthCheb[i] = 0; - } - area.maxDriftLengthCheb[0] = vLength; - area.cuMin = info.gridCorrU0; - area.cuMax = -area.cuMin; - area.vMax = vLength; - area.cvMax = vLength; - } // row - } // roc + } // sector } void TPCFastSpaceChargeCorrection::constructWithNoCorrection(const TPCFastTransformGeo& geo) @@ -512,31 +494,31 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) double tpcR2min = mGeo.getRowInfo(0).x - 1.; tpcR2min = tpcR2min * tpcR2min; double tpcR2max = mGeo.getRowInfo(mGeo.getNumberOfRows() - 1).x; - tpcR2max = tpcR2max / cos(2 * M_PI / mGeo.getNumberOfRocsA() / 2) + 1.; + tpcR2max = tpcR2max / cos(2 * M_PI / mGeo.getNumberOfSectorsA() / 2) + 1.; tpcR2max = tpcR2max * tpcR2max; struct MaxValue { double V{0.}; - int Roc{-1}; + int Sector{-1}; int Row{-1}; - void update(double v, int roc, int row) + void update(double v, int sector, int row) { if (fabs(v) > fabs(V)) { V = v; - Roc = roc; + Sector = sector; Row = row; } } void update(const MaxValue& other) { - update(other.V, other.Roc, other.Row); + update(other.V, other.Sector, other.Row); } std::string toString() { std::stringstream ss; - ss << V << "(" << Roc << "," << Row << ")"; + ss << V << "(" << Sector << "," << Row << ")"; return ss.str(); } }; @@ -544,70 +526,75 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) MaxValue maxDtpc[3]; MaxValue maxD; - for (int32_t roc = 0; roc < mGeo.getNumberOfRocs(); roc++) { + for (int32_t sector = 0; sector < mGeo.getNumberOfSectors(); sector++) { if (prn) { - LOG(info) << "check inverse transform for roc " << roc; + LOG(info) << "check inverse transform for sector " << sector; } double vLength = mGeo.getTPCzLength(); - MaxValue maxDroc[3]; + MaxValue maxDsector[3]; for (int32_t row = 0; row < mGeo.getNumberOfRows(); row++) { - float u0 = mGeo.getRowInfo(row).getUmin(); - float u1 = mGeo.getRowInfo(row).getUmax(); - float v0 = 0.; - float v1 = vLength; - double x = mGeo.getRowInfo(row).x; - double stepU = (u1 - u0) / 100.; - double stepV = (v1 - v0) / 100.; + auto [y0, y1] = mGeo.getRowInfo(row).getYrange(); + auto [z0, z1] = mGeo.getZrange(sector); + + // grid borders + if (sector < mGeo.getNumberOfSectorsA()) { + z1 = vLength - getSectorRowInfo(sector, row).gridV0; + } else { + z0 = getSectorRowInfo(sector, row).gridV0 - vLength; + } + + double stepY = (y1 - y0) / 100.; + double stepZ = (z1 - z0) / 100.; MaxValue maxDrow[3]; - for (double u = u0; u < u1; u += stepU) { - for (double v = v0; v < v1; v += stepV) { - if (v < getRocRowInfo(roc, row).gridV0) { + for (double y = y0; y < y1; y += stepY) { + for (double z = z0; z < z1; z += stepZ) { + auto [dx, dy, dz] = getCorrectionLocal(sector, row, y, z); + double realX = x + dx; + double realY = y + dy; + double realZ = z + dz; + if (!isLocalInsideGrid(sector, row, y, z) || !isLocalInsideGrid(sector, row, realY, realZ)) { continue; } - float dx, du, dv; - getCorrectionInternal(roc, row, u, v, dx, du, dv); - double cx = x + dx; - double cu = u + du; - double cv = v + dv; - double r2 = cx * cx + cu * cu; - if (cv < 0 || cv > vLength || r2 < tpcR2min || r2 > tpcR2max) { + double r2 = realX * realX + realY * realY; + if (realY < y0 || realY > y1 || + realZ < z0 || realZ > z1 || + r2 < tpcR2min || r2 > tpcR2max) { continue; } - float nx, nu, nv; - getCorrectionInvCorrectedX(roc, row, cu, cv, nx); - getCorrectionInvUV(roc, row, cu, cv, nu, nv); - double d[3] = {(cx - nx) - dx, (cu - nu) - du, (cv - nv) - dv}; + float dxr = getCorrectionXatRealYZ(sector, row, realY, realZ); + auto [dyr, dzr] = getCorrectionYZatRealYZ(sector, row, realY, realZ); + double d[3] = {dxr - dx, dyr - dy, dzr - dz}; for (int32_t i = 0; i < 3; i++) { - maxDrow[i].update(d[i], roc, row); + maxDrow[i].update(d[i], sector, row); } if (0 && prn && fabs(d[0]) + fabs(d[1]) + fabs(d[2]) > 0.1) { - LOG(info) << nx - cx << " " << nu - u << " " << nv - v - << " x,u,v " << x << ", " << u << ", " << v - << " dx,du,dv " << cx - x << ", " << cu - u << ", " << cv - v - << " nx,nu,nv " << nx - x << ", " << cu - nu << ", " << cv - nv; + LOG(info) << dxr - dx << " " << dyr - dy << " " << dzr - dz + << " measured xyz " << x << ", " << y << ", " << z + << " dx,dy,dz from measured point " << dx << ", " << dy << ", " << dz + << " dx,dy,dz from real point " << dxr << ", " << dyr << ", " << dzr; } } } - if (1 && prn) { - LOG(info) << "roc " << roc << " row " << row - << " dx " << maxDrow[0].V << " du " << maxDrow[1].V << " dv " << maxDrow[2].V; + if (0 && prn) { + LOG(info) << "sector " << sector << " row " << row + << " dx " << maxDrow[0].V << " dy " << maxDrow[1].V << " dz " << maxDrow[2].V; } for (int32_t i = 0; i < 3; i++) { - maxDroc[i].update(maxDrow[i]); + maxDsector[i].update(maxDrow[i]); maxDtpc[i].update(maxDrow[i]); maxD.update(maxDrow[i]); } } if (prn) { - LOG(info) << "inverse correction: roc " << roc << ". Max deviations: " - << " dx " << maxDroc[0].toString() << " du " << maxDroc[1].toString() << " dv " << maxDroc[2].toString(); + LOG(info) << "inverse correction: sector " << sector << ". Max deviations: " + << " dx " << maxDsector[0].toString() << " dy " << maxDsector[1].toString() << " dz " << maxDsector[2].toString(); } - } // roc + } // sector LOG(info) << "Test inverse TPC correction. max deviations: " - << " dx " << maxDtpc[0].toString() << " du " << maxDtpc[1].toString() << " dv " << maxDtpc[2].toString() << " cm"; + << " dx " << maxDtpc[0].toString() << " dy " << maxDtpc[1].toString() << " dz " << maxDtpc[2].toString() << " cm"; return maxD.V; } diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index fa5cf7a1736bd..f84fde4fffd8c 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -44,20 +44,11 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// struct RowInfo { int32_t splineScenarioID{0}; ///< scenario index (which of Spline2D splines to use) - size_t dataOffsetBytes[3]{0}; ///< offset for the spline data withing the TPC roc + size_t dataOffsetBytes[3]{0}; ///< offset for the spline data withing the TPC sector ClassDefNV(RowInfo, 1); }; - struct RowActiveArea { - float maxDriftLengthCheb[5]{0.f}; - float vMax{0.f}; - float cuMin{0.f}; - float cuMax{0.f}; - float cvMax{0.f}; - ClassDefNV(RowActiveArea, 1); - }; - - struct RocRowInfo { + struct SectorRowInfo { float gridU0{0.f}; //< U coordinate of the U-grid start float scaleUtoGrid{0.f}; //< scale U to U-grid coordinate float gridV0{0.f}; ///< V coordinate of the V-grid start @@ -66,38 +57,37 @@ class TPCFastSpaceChargeCorrection : public FlatObject float scaleCorrUtoGrid{0.f}; ///< scale corrected U to U-grid coordinate float gridCorrV0{0.f}; ///< V coordinate of the V-grid start for corrected V float scaleCorrVtoGrid{0.f}; ///< scale corrected V to V-grid coordinate - float maxCorr[3]{10.f, 10.f, 10.f}; ///< max correction for dX, dU, dV float minCorr[3]{-10.f, -10.f, -10.f}; ///< min correction for dX, dU, dV - RowActiveArea activeArea; + float maxCorr[3]{10.f, 10.f, 10.f}; ///< max correction for dX, dU, dV void resetMaxValues() { - maxCorr[0] = 1.f; minCorr[0] = -1.f; - maxCorr[1] = 1.f; + maxCorr[0] = 1.f; minCorr[1] = -1.f; - maxCorr[2] = 1.f; + maxCorr[1] = 1.f; minCorr[2] = -1.f; + maxCorr[2] = 1.f; } void updateMaxValues(float dx, float du, float dv) { - maxCorr[0] = GPUCommonMath::Max(maxCorr[0], dx); minCorr[0] = GPUCommonMath::Min(minCorr[0], dx); + maxCorr[0] = GPUCommonMath::Max(maxCorr[0], dx); - maxCorr[1] = GPUCommonMath::Max(maxCorr[1], du); minCorr[1] = GPUCommonMath::Min(minCorr[1], du); + maxCorr[1] = GPUCommonMath::Max(maxCorr[1], du); - maxCorr[2] = GPUCommonMath::Max(maxCorr[2], dv); minCorr[2] = GPUCommonMath::Min(minCorr[2], dv); + maxCorr[2] = GPUCommonMath::Max(maxCorr[2], dv); } - ClassDefNV(RocRowInfo, 2); + ClassDefNV(SectorRowInfo, 2); }; - struct RocInfo { + struct SectorInfo { float vMax{0.f}; ///< Max value of V coordinate - ClassDefNV(RocInfo, 1); + ClassDefNV(SectorInfo, 1); }; typedef Spline2D SplineType; @@ -167,52 +157,43 @@ class TPCFastSpaceChargeCorrection : public FlatObject GPUd() void setInterpolationSafetyMargin(float val) { fInterpolationSafetyMargin = val; } /// Gives const pointer to a spline - GPUd() const SplineType& getSpline(int32_t roc, int32_t row) const; + GPUd() const SplineType& getSpline(int32_t sector, int32_t row) const; /// Gives pointer to a spline - GPUd() SplineType& getSpline(int32_t roc, int32_t row); + GPUd() SplineType& getSpline(int32_t sector, int32_t row); /// Gives pointer to spline data - GPUd() float* getSplineData(int32_t roc, int32_t row, int32_t iSpline = 0); + GPUd() float* getSplineData(int32_t sector, int32_t row, int32_t iSpline = 0); /// Gives pointer to spline data - GPUd() const float* getSplineData(int32_t roc, int32_t row, int32_t iSpline = 0) const; + GPUd() const float* getSplineData(int32_t sector, int32_t row, int32_t iSpline = 0) const; /// _______________ The main method: cluster correction _______________________ /// - GPUd() int32_t getCorrectionInternal(int32_t roc, int32_t row, float u, float v, float& dx, float& du, float& dv) const; + // GPUd() int32_t getCorrectionInternal(int32_t sector, int32_t row, float u, float v, float& dx, float& du, float& dv) const; - GPUdi() std::tuple getCorrectionLocal(int32_t roc, int32_t row, float y, float z) const; + GPUdi() std::tuple getCorrectionLocal(int32_t sector, int32_t row, float y, float z) const; /// inverse correction: Corrected U and V -> coorrected X - GPUd() void getCorrectionInvCorrectedX(int32_t roc, int32_t row, float corrU, float corrV, float& corrX) const; + GPUd() float getCorrectionXatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const; /// inverse correction: Corrected U and V -> uncorrected U and V - GPUd() void getCorrectionInvUV(int32_t roc, int32_t row, float corrU, float corrV, float& nomU, float& nomV) const; - - /// maximal possible drift length of the active area - GPUd() float getMaxDriftLength(int32_t roc, int32_t row, float pad) const; - - /// maximal possible drift length of the active area - GPUd() float getMaxDriftLength(int32_t roc, int32_t row) const; - - /// maximal possible drift length of the active area - GPUd() float getMaxDriftLength(int32_t roc) const; + GPUd() std::tuple getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const; /// _______________ Utilities _______________________________________________ /// convert local y, z to internal grid coordinates u,v /// return values: u, v, scaling factor - GPUd() std::tuple convLocalToGrid(int32_t roc, int32_t row, float y, float z) const; - - /// convert u,v to internal grid coordinates - GPUd() void convUVtoGrid(int32_t roc, int32_t row, float u, float v, float& gridU, float& gridV) const; + GPUd() std::tuple convLocalToGrid(int32_t sector, int32_t row, float y, float z) const; - /// convert u,v to internal grid coordinates - GPUd() void convGridToUV(int32_t roc, int32_t row, float gridU, float gridV, float& u, float& v) const; + /// convert internal grid coordinates u,v to local y, z + /// return values: y, z, scaling factor + GPUd() std::tuple convGridToLocal(int32_t sector, int32_t row, float u, float v) const; /// convert corrected u,v to internal grid coordinates - GPUd() void convCorrectedUVtoGrid(int32_t roc, int32_t row, float cu, float cv, float& gridU, float& gridV) const; + GPUd() std::tuple convCorrectedLocalToGrid(int32_t sector, int32_t row, float y, float z) const; + + GPUd() bool isLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const; /// TPC geometry information GPUd() const TPCFastTransformGeo& getGeometry() const @@ -229,28 +210,28 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// Gives TPC row info GPUd() const RowInfo& getRowInfo(int32_t row) const { return mRowInfos[row]; } - /// Gives TPC roc info - GPUd() const RocInfo& getRocInfo(int32_t roc) const + /// Gives TPC sector info + GPUd() const SectorInfo& getSectorInfo(int32_t sector) const { - return mRocInfo[roc]; + return mSectorInfo[sector]; } - /// Gives TPC roc info - GPUd() RocInfo& getRocInfo(int32_t roc) + /// Gives TPC sector info + GPUd() SectorInfo& getSectorInfo(int32_t sector) { - return mRocInfo[roc]; + return mSectorInfo[sector]; } - /// Gives TPC roc & row info - GPUd() const RocRowInfo& getRocRowInfo(int32_t roc, int32_t row) const + /// Gives TPC sector & row info + GPUd() const SectorRowInfo& getSectorRowInfo(int32_t sector, int32_t row) const { - return mRocRowInfos[mGeo.getMaxNumberOfRows() * roc + row]; + return mSectorRowInfos[mGeo.getMaxNumberOfRows() * sector + row]; } - /// Gives TPC roc & row info - GPUd() RocRowInfo& getRocRowInfo(int32_t roc, int32_t row) + /// Gives TPC sector & row info + GPUd() SectorRowInfo& getSectorRowInfo(int32_t sector, int32_t row) { - return mRocRowInfos[mGeo.getMaxNumberOfRows() * roc + row]; + return mSectorRowInfos[mGeo.getMaxNumberOfRows() * sector + row]; } #if !defined(GPUCA_GPUCODE) @@ -277,7 +258,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject int32_t mNumberOfScenarios; ///< Number of approximation spline scenarios - RocInfo mRocInfo[TPCFastTransformGeo::getNumberOfRocs()]; ///< RocInfo array + SectorInfo mSectorInfo[TPCFastTransformGeo::getNumberOfSectors()]; ///< SectorInfo array SplineType* mScenarioPtr; //! (transient!!) pointer to spline scenarios @@ -287,7 +268,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject char* mSplineData[3]; //! (transient!!) pointer to the spline data in the flat buffer - size_t mRocDataSizeBytes[3]; ///< size of the data for one roc in the flat buffer + size_t mSectorDataSizeBytes[3]; ///< size of the data for one sector in the flat buffer float fInterpolationSafetyMargin{0.1f}; // 10% area around the TPC row. Outside of this area the interpolation returns the boundary values. @@ -298,7 +279,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject RowInfo mRowInfos[TPCFastTransformGeo::getMaxNumberOfRows()]; ///< RowInfo array - RocRowInfo mRocRowInfos[TPCFastTransformGeo::getNumberOfRocs() * TPCFastTransformGeo::getMaxNumberOfRows()]; ///< RocRowInfo array + SectorRowInfo mSectorRowInfos[TPCFastTransformGeo::getNumberOfSectors() * TPCFastTransformGeo::getMaxNumberOfRows()]; ///< SectorRowInfo array ClassDefNV(TPCFastSpaceChargeCorrection, 5); }; @@ -307,206 +288,167 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// Inline implementations of some methods /// ==================================================== -GPUdi() const TPCFastSpaceChargeCorrection::SplineType& TPCFastSpaceChargeCorrection::getSpline(int32_t roc, int32_t row) const +GPUdi() const TPCFastSpaceChargeCorrection::SplineType& TPCFastSpaceChargeCorrection::getSpline(int32_t sector, int32_t row) const { /// Gives const pointer to spline const RowInfo& rowInfo = mRowInfos[row]; return mScenarioPtr[rowInfo.splineScenarioID]; } -GPUdi() TPCFastSpaceChargeCorrection::SplineType& TPCFastSpaceChargeCorrection::getSpline(int32_t roc, int32_t row) +GPUdi() TPCFastSpaceChargeCorrection::SplineType& TPCFastSpaceChargeCorrection::getSpline(int32_t sector, int32_t row) { /// Gives pointer to spline const RowInfo& rowInfo = mRowInfos[row]; return mScenarioPtr[rowInfo.splineScenarioID]; } -GPUdi() float* TPCFastSpaceChargeCorrection::getSplineData(int32_t roc, int32_t row, int32_t iSpline) +GPUdi() float* TPCFastSpaceChargeCorrection::getSplineData(int32_t sector, int32_t row, int32_t iSpline) { /// Gives pointer to spline data const RowInfo& rowInfo = mRowInfos[row]; - return reinterpret_cast(mSplineData[iSpline] + mRocDataSizeBytes[iSpline] * roc + rowInfo.dataOffsetBytes[iSpline]); + return reinterpret_cast(mSplineData[iSpline] + mSectorDataSizeBytes[iSpline] * sector + rowInfo.dataOffsetBytes[iSpline]); } -GPUdi() const float* TPCFastSpaceChargeCorrection::getSplineData(int32_t roc, int32_t row, int32_t iSpline) const +GPUdi() const float* TPCFastSpaceChargeCorrection::getSplineData(int32_t sector, int32_t row, int32_t iSpline) const { /// Gives pointer to spline data const RowInfo& rowInfo = mRowInfos[row]; - return reinterpret_cast(mSplineData[iSpline] + mRocDataSizeBytes[iSpline] * roc + rowInfo.dataOffsetBytes[iSpline]); + return reinterpret_cast(mSplineData[iSpline] + mSectorDataSizeBytes[iSpline] * sector + rowInfo.dataOffsetBytes[iSpline]); } -GPUdi() void TPCFastSpaceChargeCorrection::convUVtoGrid(int32_t roc, int32_t row, float u, float v, float& gu, float& gv) const +GPUdi() std::tuple TPCFastSpaceChargeCorrection::convLocalToGrid(int32_t sector, int32_t row, float y, float z) const { - const auto& info = getRocRowInfo(roc, row); - gu = (u - info.gridU0) * info.scaleUtoGrid; - gv = (v - info.gridV0) * info.scaleVtoGrid; -} + /// convert local y, z to internal grid coordinates u,v + /// return values: u, v, scaling factor + const auto& info = getSectorRowInfo(sector, row); + const SplineType& spline = getSpline(sector, row); -GPUdi() void TPCFastSpaceChargeCorrection::convGridToUV(int32_t roc, int32_t row, float gridU, float gridV, float& u, float& v) const -{ - /// convert internal grid coordinates to u,v - const RocRowInfo& info = getRocRowInfo(roc, row); - u = info.gridU0 + gridU / info.scaleUtoGrid; - v = info.gridV0 + gridV / info.scaleVtoGrid; -} + float u, v; + mGeo.convLocalToUV1(sector, y, z, u, v); -GPUdi() void TPCFastSpaceChargeCorrection::convCorrectedUVtoGrid(int32_t roc, int32_t row, float corrU, float corrV, float& gridU, float& gridV) const -{ - const RocRowInfo& info = getRocRowInfo(roc, row); - gridU = (corrU - info.gridCorrU0) * info.scaleCorrUtoGrid; - gridV = (corrV - info.gridCorrV0) * info.scaleCorrVtoGrid; -} + float scale = 1.f; + if (v < 0.f) { + scale = 0.f; + } else if (v < info.gridV0) { + scale = v / info.gridV0; + } + + float gridU = (u - info.gridU0) * info.scaleUtoGrid; + float gridV = (v - info.gridV0) * info.scaleVtoGrid; -GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrectionInternal(int32_t roc, int32_t row, float u, float v, float& dx, float& du, float& dv) const -{ - const auto& info = getRocRowInfo(roc, row); - const SplineType& spline = getSpline(roc, row); - const float* splineData = getSplineData(roc, row); - float gridU = 0, gridV = 0; - convUVtoGrid(roc, row, u, v, gridU, gridV); // shrink to the grid area gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); gridV = GPUCommonMath::Clamp(gridV, 0.f, (float)spline.getGridX2().getUmax()); - float dxuv[3]; - spline.interpolateU(splineData, gridU, gridV, dxuv); + return {gridU, gridV, scale}; +} - float s = v / info.gridV0; +GPUdi() bool TPCFastSpaceChargeCorrection::isLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const +{ + /// ccheck if local y, z are inside the grid - if (v >= info.gridV0) { - s = 1.f; - } else if (v <= 0.f) { - s = 0.f; - } + const auto& info = getSectorRowInfo(sector, row); + const SplineType& spline = getSpline(sector, row); - dx = GPUCommonMath::Clamp(s * dxuv[0], info.minCorr[0], info.maxCorr[0]); - du = GPUCommonMath::Clamp(s * dxuv[1], info.minCorr[1], info.maxCorr[1]); - dv = GPUCommonMath::Clamp(s * dxuv[2], info.minCorr[2], info.maxCorr[2]); - return 0; + float u, v; + mGeo.convLocalToUV1(sector, y, z, u, v); + + float gridU = (u - info.gridU0) * info.scaleUtoGrid; + float gridV = (v - info.gridV0) * info.scaleVtoGrid; + + // shrink to the grid area + if (gridU < 0.f || gridU > (float)spline.getGridX1().getUmax()) + return false; + if (gridV < 0.f || gridV > (float)spline.getGridX2().getUmax()) + return false; + return true; } -GPUdi() std::tuple TPCFastSpaceChargeCorrection::getCorrectionLocal(int32_t roc, int32_t row, float y, float z) const +GPUdi() std::tuple TPCFastSpaceChargeCorrection::convGridToLocal(int32_t sector, int32_t row, float gridU, float gridV) const { - const auto& info = getRocRowInfo(roc, row); - const SplineType& spline = getSpline(roc, row); - const float* splineData = getSplineData(roc, row); + /// convert internal grid coordinates u,v to local y, z + const SectorRowInfo& info = getSectorRowInfo(sector, row); + float u = info.gridU0 + gridU / info.scaleUtoGrid; + float v = info.gridV0 + gridV / info.scaleVtoGrid; + float y, z; + mGeo.convUVtoLocal1(sector, u, v, y, z); + return {y, z}; +} + +GPUdi() std::tuple TPCFastSpaceChargeCorrection::convCorrectedLocalToGrid(int32_t sector, int32_t row, float y, float z) const +{ + /// convert corrected y, z to the internal grid coordinates + const auto& info = getSectorRowInfo(sector, row); + const Spline2D& spline = reinterpret_cast&>(getSpline(sector, row)); float u, v; + mGeo.convLocalToUV1(sector, y, z, u, v); + + float scale = 1.f; + if (v < 0.f) { + scale = 0.f; + } else if (v < info.gridCorrV0) { + scale = v / info.gridCorrV0; + } - mGeo.convLocalToUV(roc, y, z, u, v); + float gridU = (u - info.gridCorrU0) * info.scaleCorrUtoGrid; + float gridV = (v - info.gridCorrV0) * info.scaleCorrVtoGrid; - float gridU = 0, gridV = 0; - convUVtoGrid(roc, row, u, v, gridU, gridV); // shrink to the grid area gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); gridV = GPUCommonMath::Clamp(gridV, 0.f, (float)spline.getGridX2().getUmax()); - float dxuv[3]; - spline.interpolateU(splineData, gridU, gridV, dxuv); - - float s = v / info.gridV0; + return {gridU, gridV, scale}; +} - if (v >= info.gridV0) { - s = 1.f; - } else if (v <= 0.f) { - s = 0.f; - } +GPUdi() std::tuple TPCFastSpaceChargeCorrection::getCorrectionLocal(int32_t sector, int32_t row, float y, float z) const +{ + const auto& info = getSectorRowInfo(sector, row); + const SplineType& spline = getSpline(sector, row); + const float* splineData = getSplineData(sector, row); - float dx = GPUCommonMath::Clamp(s * dxuv[0], info.minCorr[0], info.maxCorr[0]); - float du = GPUCommonMath::Clamp(s * dxuv[1], info.minCorr[1], info.maxCorr[1]); - float dv = GPUCommonMath::Clamp(s * dxuv[2], info.minCorr[2], info.maxCorr[2]); + auto [gridU, gridV, scale] = convLocalToGrid(sector, row, y, z); - float dy, dz; - mGeo.convUVtoLocal(roc, du, dv, dy, dz); + float dxyz[3]; + spline.interpolateU(splineData, gridU, gridV, dxyz); + float dx = scale * GPUCommonMath::Clamp(dxyz[0], info.minCorr[0], info.maxCorr[0]); + float dy = scale * GPUCommonMath::Clamp(dxyz[1], info.minCorr[1], info.maxCorr[1]); + float dz = scale * GPUCommonMath::Clamp(dxyz[2], info.minCorr[2], info.maxCorr[2]); return {dx, dy, dz}; } -GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvCorrectedX( - int32_t roc, int32_t row, float corrU, float corrV, float& x) const +GPUdi() float TPCFastSpaceChargeCorrection::getCorrectionXatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const { - const auto& info = getRocRowInfo(roc, row); - const Spline2D& spline = reinterpret_cast&>(getSpline(roc, row)); - const float* splineData = getSplineData(roc, row, 1); + const auto& info = getSectorRowInfo(sector, row); + const Spline2D& spline = reinterpret_cast&>(getSpline(sector, row)); + const float* splineData = getSplineData(sector, row, 1); - float gridU, gridV; - convCorrectedUVtoGrid(roc, row, corrU, corrV, gridU, gridV); - - // shrink to the grid area - gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); - gridV = GPUCommonMath::Clamp(gridV, 0.f, (float)spline.getGridX2().getUmax()); + auto [gridU, gridV, scale] = convCorrectedLocalToGrid(sector, row, realY, realZ); float dx = 0; spline.interpolateU(splineData, gridU, gridV, &dx); - float s = corrV / info.gridCorrV0; - - if (corrV >= info.gridCorrV0) { - s = 1.f; - } else if (corrV <= 0.f) { - s = 0.f; - } - - dx = GPUCommonMath::Clamp(s * dx, info.minCorr[0], info.maxCorr[0]); - x = mGeo.getRowInfo(row).x + dx; + dx = scale * GPUCommonMath::Clamp(dx, info.minCorr[0], info.maxCorr[0]); + return dx; } -GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvUV( - int32_t roc, int32_t row, float corrU, float corrV, float& nomU, float& nomV) const +GPUdi() std::tuple TPCFastSpaceChargeCorrection::getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const { - const Spline2D& spline = reinterpret_cast&>(getSpline(roc, row)); - const float* splineData = getSplineData(roc, row, 2); - float gridU, gridV; - convCorrectedUVtoGrid(roc, row, corrU, corrV, gridU, gridV); + auto [gridU, gridV, scale] = convCorrectedLocalToGrid(sector, row, realY, realZ); - // shrink to the grid area - gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); - gridV = GPUCommonMath::Clamp(gridV, 0.f, (float)spline.getGridX2().getUmax()); - - float duv[2]; - spline.interpolateU(splineData, gridU, gridV, duv); - const auto& info = getRocRowInfo(roc, row); - float s = corrV / info.gridCorrV0; + const auto& info = getSectorRowInfo(sector, row); + const Spline2D& spline = reinterpret_cast&>(getSpline(sector, row)); + const float* splineData = getSplineData(sector, row, 2); - if (corrV >= info.gridCorrV0) { - s = 1.f; - } else if (corrV <= 0.f) { - s = 0.f; - } + float dyz[2]; + spline.interpolateU(splineData, gridU, gridV, dyz); - duv[0] = GPUCommonMath::Clamp(s * duv[0], info.minCorr[1], info.maxCorr[1]); - duv[1] = GPUCommonMath::Clamp(s * duv[1], info.minCorr[2], info.maxCorr[2]); - nomU = corrU - duv[0]; - nomV = corrV - duv[1]; -} + dyz[0] = scale * GPUCommonMath::Clamp(dyz[0], info.minCorr[1], info.maxCorr[1]); + dyz[1] = scale * GPUCommonMath::Clamp(dyz[1], info.minCorr[2], info.maxCorr[2]); -GPUdi() float TPCFastSpaceChargeCorrection::getMaxDriftLength(int32_t roc, int32_t row, float pad) const -{ - const RowActiveArea& area = getRocRowInfo(roc, row).activeArea; - const float* c = area.maxDriftLengthCheb; - float x = -1.f + 2.f * pad / mGeo.getRowInfo(row).maxPad; - float y = c[0] + c[1] * x; - float f0 = 1.f; - float f1 = x; - x *= 2.f; - for (int32_t i = 2; i < 5; i++) { - double f = x * f1 - f0; - y += c[i] * f; - f0 = f1; - f1 = f; - } - return y; -} - -GPUdi() float TPCFastSpaceChargeCorrection::getMaxDriftLength(int32_t roc, int32_t row) const -{ - return getRocRowInfo(roc, row).activeArea.vMax; -} - -GPUdi() float TPCFastSpaceChargeCorrection::getMaxDriftLength(int32_t roc) const -{ - return getRocInfo(roc).vMax; + return {dyz[0], dyz[1]}; } } // namespace gpu diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrectionMap.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrectionMap.h index 97b824aa6da32..fcee61ff09425 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrectionMap.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrectionMap.h @@ -49,20 +49,20 @@ class TPCFastSpaceChargeCorrectionMap /// _____________ Constructors / destructors __________________________ /// Default constructor: creates an empty uninitialized object - TPCFastSpaceChargeCorrectionMap(int32_t nRocs, int32_t nRows) + TPCFastSpaceChargeCorrectionMap(int32_t nSectors, int32_t nRows) { - init(nRocs, nRows); + init(nSectors, nRows); } /// Destructor ~TPCFastSpaceChargeCorrectionMap() = default; /// (re-)init the map - void init(int32_t nRocs, int32_t nRows) + void init(int32_t nSectors, int32_t nRows) { - mNrocs = nRocs; + mNsectors = nSectors; mNrows = nRows; - int32_t n = mNrocs * mNrows; + int32_t n = mNsectors * mNrows; fDataPoints.resize(n); for (uint32_t i = 0; i < fDataPoints.size(); ++i) { fDataPoints[i].clear(); @@ -70,30 +70,30 @@ class TPCFastSpaceChargeCorrectionMap } /// Starts the construction procedure, reserves temporary memory - void addCorrectionPoint(int32_t iRoc, int32_t iRow, + void addCorrectionPoint(int32_t iSector, int32_t iRow, double y, double z, double dx, double dy, double dz) { - int32_t ind = mNrows * iRoc + iRow; + int32_t ind = mNrows * iSector + iRow; fDataPoints.at(ind).push_back(CorrectionPoint{y, z, dx, dy, dz}); } - const std::vector& getPoints(int32_t iRoc, int32_t iRow) const + const std::vector& getPoints(int32_t iSector, int32_t iRow) const { - int32_t ind = mNrows * iRoc + iRow; + int32_t ind = mNrows * iSector + iRow; return fDataPoints.at(ind); } - int32_t getNrocs() const { return mNrocs; } + int32_t getNsectors() const { return mNsectors; } int32_t getNrows() const { return mNrows; } - bool isInitialized() const { return mNrocs > 0 && mNrows > 0; } + bool isInitialized() const { return mNsectors > 0 && mNrows > 0; } private: /// _______________ Data members _______________________________________________ - int32_t mNrocs{0}; + int32_t mNsectors{0}; int32_t mNrows{0}; std::vector> fDataPoints; //! (transient!!) points with space charge correction diff --git a/GPU/TPCFastTransformation/TPCFastTransform.h b/GPU/TPCFastTransformation/TPCFastTransform.h index 1ecd577eb7dac..03d9eaf43ce9b 100644 --- a/GPU/TPCFastTransformation/TPCFastTransform.h +++ b/GPU/TPCFastTransformation/TPCFastTransform.h @@ -46,14 +46,14 @@ struct TPCSlowSpaceChargeCorrection { ~TPCSlowSpaceChargeCorrection(); /// getting the corrections for global coordinates - void getCorrections(const float gx, const float gy, const float gz, const int32_t roc, float& gdxC, float& gdyC, float& gdzC) const; + void getCorrections(const float gx, const float gy, const float gz, const int32_t sector, float& gdxC, float& gdyC, float& gdzC) const; o2::tpc::SpaceCharge* mCorr{nullptr}; ///< reference space charge corrections #else ~TPCSlowSpaceChargeCorrection() = default; /// setting dummy corrections for GPU - GPUd() void getCorrections(const float gx, const float gy, const float gz, const int32_t roc, float& gdxC, float& gdyC, float& gdzC) const + GPUd() void getCorrections(const float gx, const float gy, const float gz, const int32_t sector, float& gdxC, float& gdyC, float& gdzC) const { gdxC = 0; gdyC = 0; @@ -182,45 +182,43 @@ class TPCFastTransform : public FlatObject /// _______________ The main method: cluster transformation _______________________ /// - /// Transforms raw TPC coordinates to local XYZ withing a roc + /// Transforms raw TPC coordinates to local XYZ withing a sector /// taking calibration into account. /// - GPUd() void Transform(int32_t roc, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime = 0, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; - GPUd() void TransformXYZ(int32_t roc, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; + GPUd() void Transform(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime = 0, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; + GPUd() void TransformXYZ(int32_t sector, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; /// Transformation in the time frame - GPUd() void TransformInTimeFrame(int32_t roc, int32_t row, float pad, float time, float& x, float& y, float& z, float maxTimeBin) const; - GPUd() void TransformInTimeFrame(int32_t roc, float time, float& z, float maxTimeBin) const; + GPUd() void TransformInTimeFrame(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float maxTimeBin) const; + GPUd() void TransformInTimeFrame(int32_t sector, float time, float& z, float maxTimeBin) const; /// Inverse transformation - GPUd() void InverseTransformInTimeFrame(int32_t roc, int32_t row, float /*x*/, float y, float z, float& pad, float& time, float maxTimeBin) const; - GPUd() float InverseTransformInTimeFrame(int32_t roc, float z, float maxTimeBin) const; + GPUd() void InverseTransformInTimeFrame(int32_t sector, int32_t row, float /*x*/, float y, float z, float& pad, float& time, float maxTimeBin) const; + GPUd() float InverseTransformInTimeFrame(int32_t sector, float z, float maxTimeBin) const; /// Inverse transformation: Transformed Y and Z -> transformed X - GPUd() void InverseTransformYZtoX(int32_t roc, int32_t row, float y, float z, float& x, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; + GPUd() void InverseTransformYZtoX(int32_t sector, int32_t row, float y, float z, float& x, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; /// Inverse transformation: Transformed Y and Z -> Y and Z, transformed w/o space charge correction - GPUd() void InverseTransformYZtoNominalYZ(int32_t roc, int32_t row, float y, float z, float& ny, float& nz, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; + GPUd() void InverseTransformYZtoNominalYZ(int32_t sector, int32_t row, float y, float z, float& ny, float& nz, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; /// Inverse transformation: Transformed X, Y and Z -> X, Y and Z, transformed w/o space charge correction - GPUd() void InverseTransformXYZtoNominalXYZ(int32_t roc, int32_t row, float x, float y, float z, float& nx, float& ny, float& nz, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; + GPUd() void InverseTransformXYZtoNominalXYZ(int32_t sector, int32_t row, float x, float y, float z, float& nx, float& ny, float& nz, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; /// Ideal transformation with Vdrift only - without calibration - GPUd() void TransformIdeal(int32_t roc, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime) const; - GPUd() void TransformIdealZ(int32_t roc, float time, float& z, float vertexTime) const; + GPUd() void TransformIdeal(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime) const; + GPUd() void TransformIdealZ(int32_t sector, float time, float& z, float vertexTime) const; - GPUd() void convPadTimeToUV(int32_t row, float pad, float time, float& u, float& v, float vertexTime) const; - GPUd() void convPadTimeToUVinTimeFrame(int32_t row, float pad, float time, float& u, float& v, float maxTimeBin) const; - GPUd() void convTimeToVinTimeFrame(float time, float& v, float maxTimeBin) const; + GPUd() void convPadTimeToLocal(int32_t sector, int32_t row, float pad, float time, float& y, float& z, float vertexTime) const; + GPUd() void convPadTimeToLocalInTimeFrame(int32_t sector, int32_t row, float pad, float time, float& y, float& z, float maxTimeBin) const; - GPUd() void convUVtoPadTime(int32_t row, float u, float v, float& pad, float& time, float vertexTime) const; - GPUd() void convUVtoPadTimeInTimeFrame(int32_t row, float u, float v, float& pad, float& time, float maxTimeBin) const; - GPUd() void convVtoTime(float v, float& time, float vertexTime) const; + GPUd() void convLocalToPadTime(int32_t sector, int32_t row, float y, float z, float& pad, float& time, float vertexTime) const; + GPUd() void convLocalToPadTimeInTimeFrame(int32_t sector, int32_t row, float y, float z, float& pad, float& time, float maxTimeBin) const; - GPUd() float convTimeToZinTimeFrame(int32_t roc, float time, float maxTimeBin) const; - GPUd() float convZtoTimeInTimeFrame(int32_t roc, float z, float maxTimeBin) const; - GPUd() float convDeltaTimeToDeltaZinTimeFrame(int32_t roc, float deltaTime) const; - GPUd() float convDeltaZtoDeltaTimeInTimeFrame(int32_t roc, float deltaZ) const; + GPUd() float convTimeToZinTimeFrame(int32_t sector, float time, float maxTimeBin) const; + GPUd() float convZtoTimeInTimeFrame(int32_t sector, float z, float maxTimeBin) const; + GPUd() float convDeltaTimeToDeltaZinTimeFrame(int32_t sector, float deltaTime) const; + GPUd() float convDeltaZtoDeltaTimeInTimeFrame(int32_t sector, float deltaZ) const; GPUd() float convDeltaZtoDeltaTimeInTimeFrameAbs(float deltaZ) const; GPUd() float convZOffsetToVertexTime(int32_t sector, float zOffset, float maxTimeBin) const; GPUd() float convVertexTimeToZOffset(int32_t sector, float vertexTime, float maxTimeBin) const; @@ -265,13 +263,13 @@ class TPCFastTransform : public FlatObject GPUd() float getLumiScaleFactor() const { return mLumiScaleFactor; } /// maximal possible drift time of the active area - GPUd() float getMaxDriftTime(int32_t roc, int32_t row, float pad) const; + GPUd() float getMaxDriftTime(int32_t sector, int32_t row, float pad) const; /// maximal possible drift time of the active area - GPUd() float getMaxDriftTime(int32_t roc, int32_t row) const; + GPUd() float getMaxDriftTime(int32_t sector, int32_t row) const; /// maximal possible drift time of the active area - GPUd() float getMaxDriftTime(int32_t roc) const; + GPUd() float getMaxDriftTime(int32_t sector) const; #if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) @@ -292,6 +290,8 @@ class TPCFastTransform : public FlatObject /// Print method void print() const; + GPUd() float convDriftLengthToTime(float driftLength, float vertexTime) const; + private: /// Enumeration of possible initialization states enum ConstructionExtraState : uint32_t { @@ -335,7 +335,7 @@ class TPCFastTransform : public FlatObject /// Correction of (x,u,v) with tricubic interpolator on a regular grid TPCSlowSpaceChargeCorrection* mCorrectionSlow{nullptr}; ///< reference space charge corrections - GPUd() void TransformLocal(int32_t roc, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const; + GPUd() void TransformLocal(int32_t sector, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const; ClassDefNV(TPCFastTransform, 4); }; @@ -344,27 +344,22 @@ class TPCFastTransform : public FlatObject // Inline implementations of some methods // ======================================================================= -GPUdi() void TPCFastTransform::convPadTimeToUV(int32_t row, float pad, float time, float& u, float& v, float vertexTime) const -{ - const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); - float x = rowInfo.x; - u = (pad - 0.5f * rowInfo.maxPad) * rowInfo.padWidth; - v = (time - mT0 - vertexTime) * (mVdrift); // drift length cm -} +// ---------------------------------------------------------------------- -GPUdi() void TPCFastTransform::convTimeToVinTimeFrame(float time, float& v, float maxTimeBin) const +GPUdi() void TPCFastTransform::convPadTimeToLocal(int32_t sector, int32_t row, float pad, float time, float& y, float& z, float vertexTime) const { - v = (time - mT0 - maxTimeBin) * mVdrift; // drift length cm - v += getGeometry().getTPCzLength(); + float l = (time - mT0 - vertexTime) * mVdrift; // drift length [cm] + std::tie(y, z) = getGeometry().convPadDriftLengthToLocal(sector, row, pad, l); } -GPUdi() void TPCFastTransform::convPadTimeToUVinTimeFrame(int32_t row, float pad, float time, float& u, float& v, float maxTimeBin) const +GPUdi() void TPCFastTransform::convPadTimeToLocalInTimeFrame(int32_t sector, int32_t row, float pad, float time, float& y, float& z, float maxTimeBin) const { - const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); - u = (pad - 0.5f * rowInfo.maxPad) * rowInfo.padWidth; - convTimeToVinTimeFrame(time, v, maxTimeBin); + float l = getGeometry().getTPCzLength() + (time - mT0 - maxTimeBin) * mVdrift; // drift length [cm] + std::tie(y, z) = getGeometry().convPadDriftLengthToLocal(sector, row, pad, l); } +// ---------------------------------------------------------------------- + GPUdi() float TPCFastTransform::convZOffsetToVertexTime(int32_t sector, float zOffset, float maxTimeBin) const { if (sector < getGeometry().getNumberOfSectorsA()) { @@ -383,27 +378,30 @@ GPUdi() float TPCFastTransform::convVertexTimeToZOffset(int32_t sector, float ve } } -GPUdi() void TPCFastTransform::convUVtoPadTime(int32_t row, float u, float v, float& pad, float& time, float vertexTime) const +GPUdi() float TPCFastTransform::convDriftLengthToTime(float driftLength, float vertexTime) const { - const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); - pad = u / rowInfo.padWidth + 0.5f * rowInfo.maxPad; - time = mT0 + vertexTime + v / mVdrift; + return (mT0 + vertexTime + driftLength / mVdrift); } -GPUdi() void TPCFastTransform::convVtoTime(float v, float& time, float vertexTime) const +// ---------------------------------------------------------------------- + +GPUdi() void TPCFastTransform::convLocalToPadTime(int32_t sector, int32_t row, float y, float z, float& pad, float& time, float vertexTime) const { - time = mT0 + vertexTime + v / mVdrift; + float l; + std::tie(pad, l) = getGeometry().convLocalToPadDriftLength(sector, row, y, z); + time = convDriftLengthToTime(l, vertexTime); } -GPUdi() void TPCFastTransform::convUVtoPadTimeInTimeFrame(int32_t row, float u, float v, float& pad, float& time, float maxTimeBin) const +GPUdi() void TPCFastTransform::convLocalToPadTimeInTimeFrame(int32_t sector, int32_t row, float y, float z, float& pad, float& time, float maxTimeBin) const { - v -= getGeometry().getTPCzLength(); - const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); - pad = u / rowInfo.padWidth + 0.5f * rowInfo.maxPad; - time = mT0 + maxTimeBin + v / mVdrift; + float l; + std::tie(pad, l) = getGeometry().convLocalToPadDriftLength(sector, row, y, z); + time = convDriftLengthToTime(l, maxTimeBin); } -GPUdi() void TPCFastTransform::TransformLocal(int32_t roc, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const +// ---------------------------------------------------------------------- + +GPUdi() void TPCFastTransform::TransformLocal(int32_t sector, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const { GPUCA_RTC_SPECIAL_CODE(ref2 = nullptr; scale2 = 0.f;); @@ -417,29 +415,29 @@ GPUdi() void TPCFastTransform::TransformLocal(int32_t roc, int32_t row, float& x #ifndef GPUCA_GPUCODE if (mCorrectionSlow) { float gx, gy, gz; - getGeometry().convLocalToGlobal(roc, x, y, z, gx, gy, gz); + getGeometry().convLocalToGlobal(sector, x, y, z, gx, gy, gz); float gdxC, gdyC, gdzC; - mCorrectionSlow->getCorrections(gx, gy, gz, roc, gdxC, gdyC, gdzC); - getGeometry().convGlobalToLocal(roc, gdxC, gdyC, gdzC, dx, dy, dz); + mCorrectionSlow->getCorrections(gx, gy, gz, sector, gdxC, gdyC, gdzC); + getGeometry().convGlobalToLocal(sector, gdxC, gdyC, gdzC, dx, dy, dz); } else #endif // GPUCA_GPUCODE { - std::tie(dx, dy, dz) = mCorrection.getCorrectionLocal(roc, row, y, z); + std::tie(dx, dy, dz) = mCorrection.getCorrectionLocal(sector, row, y, z); if (ref) { if ((scale > 0.f) && (scaleMode == 0)) { // scaling was requested - auto [dxRef, dyRef, dzRef] = ref->mCorrection.getCorrectionLocal(roc, row, y, z); + auto [dxRef, dyRef, dzRef] = ref->mCorrection.getCorrectionLocal(sector, row, y, z); dx = (dx - dxRef) * scale + dxRef; dy = (dy - dyRef) * scale + dyRef; dz = (dz - dzRef) * scale + dzRef; } else if ((scale != 0.f) && ((scaleMode == 1) || (scaleMode == 2))) { - auto [dxRef, dyRef, dzRef] = ref->mCorrection.getCorrectionLocal(roc, row, y, z); + auto [dxRef, dyRef, dzRef] = ref->mCorrection.getCorrectionLocal(sector, row, y, z); dx = dxRef * scale + dx; dy = dyRef * scale + dy; dz = dzRef * scale + dz; } } if (ref2 && (scale2 != 0)) { - auto [dxRef, dyRef, dzRef] = ref2->mCorrection.getCorrectionLocal(roc, row, y, z); + auto [dxRef, dyRef, dzRef] = ref2->mCorrection.getCorrectionLocal(sector, row, y, z); dx = dxRef * scale2 + dx; dy = dyRef * scale2 + dy; dz = dzRef * scale2 + dz; @@ -451,37 +449,37 @@ GPUdi() void TPCFastTransform::TransformLocal(int32_t roc, int32_t row, float& x float lx = x, ly = y, lz = z; float gx, gy, gz; - getGeometry().convLocalToGlobal(roc, lx, ly, lz, gx, gy, gz); + getGeometry().convLocalToGlobal(sector, lx, ly, lz, gx, gy, gz); float lxT = lx + dx; float lyT = ly + dy; float lzT = lz + dz; float invYZtoXScaled; - InverseTransformYZtoX(roc, row, lyT, lzT, invYZtoXScaled, ref, ref2, scale, scale2, scaleMode); + InverseTransformYZtoX(sector, row, lyT, lzT, invYZtoXScaled, ref, ref2, scale, scale2, scaleMode); float invYZtoX; - InverseTransformYZtoX(roc, row, lyT, lzT, invYZtoX); + InverseTransformYZtoX(sector, row, lyT, lzT, invYZtoX); float YZtoNominalY; float YZtoNominalZ; - InverseTransformYZtoNominalYZ(roc, row, lyT, lzT, YZtoNominalY, YZtoNominalZ); + InverseTransformYZtoNominalYZ(sector, row, lyT, lzT, YZtoNominalY, YZtoNominalZ); float YZtoNominalYScaled; float YZtoNominalZScaled; - InverseTransformYZtoNominalYZ(roc, row, lyT, lzT, YZtoNominalYScaled, YZtoNominalZScaled, ref, ref2, scale, scale2, scaleMode); + InverseTransformYZtoNominalYZ(sector, row, lyT, lzT, YZtoNominalYScaled, YZtoNominalZScaled, ref, ref2, scale, scale2, scaleMode); float dxRef = 0.f, dyRef = 0.f, dzRef = 0.f; if (ref) { - std::tie(dxRef, dyRef, dzRef) = ref->mCorrection.getCorrectionLocal(roc, row, y, z); + std::tie(dxRef, dyRef, dzRef) = ref->mCorrection.getCorrectionLocal(sector, row, y, z); } - float dxRef2 = 0.f, duRef2 = 0.f, dvRef2 = 0.f; + float dxRef2 = 0.f, dyRef2 = 0.f, dzRef2 = 0.f; if (ref2) { - std::tie(dxRef2, duRef2, dvRef2) = ref2->mCorrection.getCorrectionLocal(roc, row, y, z); + std::tie(dxRef2, dyRef2, dzRef2) = ref2->mCorrection.getCorrectionLocal(sector, row, y, z); } - auto [dxOrig, dyOrig, dzOrig] = mCorrection.getCorrectionLocal(roc, row, y, z); + auto [dxOrig, dyOrig, dzOrig] = mCorrection.getCorrectionLocal(sector, row, y, z); o2::utils::DebugStreamer::instance()->getStreamer("debug_fasttransform", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_Transform").data() // corrections in x, u, v @@ -498,7 +496,7 @@ GPUdi() void TPCFastTransform::TransformLocal(int32_t roc, int32_t row, float& x << "dy=" << dy << "dz=" << dz << "row=" << row - << "roc=" << roc + << "sector=" << sector << "scale=" << scale << "scale2=" << scale2 // original local coordinates @@ -529,38 +527,34 @@ GPUdi() void TPCFastTransform::TransformLocal(int32_t roc, int32_t row, float& x z += dz; } -GPUdi() void TPCFastTransform::TransformXYZ(int32_t roc, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const +GPUdi() void TPCFastTransform::TransformXYZ(int32_t sector, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const { - TransformLocal(roc, row, x, y, z, ref, ref2, scale, scale2, scaleMode); + TransformLocal(sector, row, x, y, z, ref, ref2, scale, scale2, scaleMode); } -GPUdi() void TPCFastTransform::Transform(int32_t roc, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const +GPUdi() void TPCFastTransform::Transform(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const { /// _______________ The main method: cluster transformation _______________________ /// - /// Transforms raw TPC coordinates to local XYZ withing a roc + /// Transforms raw TPC coordinates to local XYZ withing a sector /// taking calibration into account. /// const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); x = rowInfo.x; - float u = 0, v = 0; - convPadTimeToUV(row, pad, time, u, v, vertexTime); - getGeometry().convUVtoLocal(roc, u, v, y, z); - - TransformLocal(roc, row, x, y, z, ref, ref2, scale, scale2, scaleMode); + convPadTimeToLocal(sector, row, pad, time, y, z, vertexTime); + TransformLocal(sector, row, x, y, z, ref, ref2, scale, scale2, scaleMode); } -GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t roc, float time, float& z, float maxTimeBin) const +GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t sector, float time, float& z, float maxTimeBin) const { - float v = 0; - convTimeToVinTimeFrame(time, v, maxTimeBin); - getGeometry().convVtoLocal(roc, v, z); + float l = (time - mT0 - maxTimeBin) * mVdrift; // drift length cm + z = getGeometry().convDriftLengthToLocal(sector, l); } -GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t roc, int32_t row, float pad, float time, float& x, float& y, float& z, float maxTimeBin) const +GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float maxTimeBin) const { /// _______________ Special cluster transformation for a time frame _______________________ /// @@ -570,58 +564,50 @@ GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t roc, int32_t row, fl const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); x = rowInfo.x; - float u = 0, v = 0; - convPadTimeToUVinTimeFrame(row, pad, time, u, v, maxTimeBin); - getGeometry().convUVtoLocal(roc, u, v, y, z); + convPadTimeToLocalInTimeFrame(sector, row, pad, time, y, z, maxTimeBin); } -GPUdi() void TPCFastTransform::InverseTransformInTimeFrame(int32_t roc, int32_t row, float /*x*/, float y, float z, float& pad, float& time, float maxTimeBin) const +GPUdi() void TPCFastTransform::InverseTransformInTimeFrame(int32_t sector, int32_t row, float /*x*/, float y, float z, float& pad, float& time, float maxTimeBin) const { /// Inverse transformation to TransformInTimeFrame - float u = 0, v = 0; - getGeometry().convLocalToUV(roc, y, z, u, v); - convUVtoPadTimeInTimeFrame(row, u, v, pad, time, maxTimeBin); + convLocalToPadTimeInTimeFrame(sector, row, y, z, pad, time, maxTimeBin); } -GPUdi() float TPCFastTransform::InverseTransformInTimeFrame(int32_t roc, float z, float maxTimeBin) const +GPUdi() float TPCFastTransform::InverseTransformInTimeFrame(int32_t sector, float z, float maxTimeBin) const { float pad, time; - InverseTransformInTimeFrame(roc, 0, 0, 0, z, pad, time, maxTimeBin); + InverseTransformInTimeFrame(sector, 0, 0, 0, z, pad, time, maxTimeBin); return time; } -GPUdi() void TPCFastTransform::TransformIdealZ(int32_t roc, float time, float& z, float vertexTime) const +GPUdi() void TPCFastTransform::TransformIdealZ(int32_t sector, float time, float& z, float vertexTime) const { /// _______________ The main method: cluster transformation _______________________ /// - /// Transforms time TPC coordinates to local Z withing a roc + /// Transforms time TPC coordinates to local Z withing a sector /// Ideal transformation: only Vdrift from DCS. /// No space charge corrections, no time of flight correction /// - float v = (time - mT0 - vertexTime) * mVdrift; // drift length cm - getGeometry().convVtoLocal(roc, v, z); + float l = (time - mT0 - vertexTime) * mVdrift; // drift length cm + z = getGeometry().convDriftLengthToLocal(sector, l); } -GPUdi() void TPCFastTransform::TransformIdeal(int32_t roc, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime) const +GPUdi() void TPCFastTransform::TransformIdeal(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime) const { /// _______________ The main method: cluster transformation _______________________ /// - /// Transforms raw TPC coordinates to local XYZ withing a roc + /// Transforms raw TPC coordinates to local XYZ withing a sector /// Ideal transformation: only Vdrift from DCS. /// No space charge corrections, no time of flight correction /// - const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); - - x = rowInfo.x; - float u = (pad - 0.5f * rowInfo.maxPad) * rowInfo.padWidth; - float v = (time - mT0 - vertexTime) * mVdrift; // drift length cm - - getGeometry().convUVtoLocal(roc, u, v, y, z); + x = getGeometry().getRowInfo(row).x; + float driftLength = (time - mT0 - vertexTime) * mVdrift; // drift length cm + std::tie(y, z) = getGeometry().convPadDriftLengthToLocal(sector, row, pad, driftLength); } -GPUdi() float TPCFastTransform::convTimeToZinTimeFrame(int32_t roc, float time, float maxTimeBin) const +GPUdi() float TPCFastTransform::convTimeToZinTimeFrame(int32_t sector, float time, float maxTimeBin) const { /// _______________ Special cluster transformation for a time frame _______________________ /// @@ -631,21 +617,21 @@ GPUdi() float TPCFastTransform::convTimeToZinTimeFrame(int32_t roc, float time, /// float v = (time - mT0 - maxTimeBin) * mVdrift; // drift length cm - float z = (roc < getGeometry().getNumberOfRocsA()) ? -v : v; + float z = (sector < getGeometry().getNumberOfSectorsA()) ? -v : v; return z; } -GPUdi() float TPCFastTransform::convZtoTimeInTimeFrame(int32_t roc, float z, float maxTimeBin) const +GPUdi() float TPCFastTransform::convZtoTimeInTimeFrame(int32_t sector, float z, float maxTimeBin) const { /// Inverse transformation of convTimeToZinTimeFrame() - float v = (roc < getGeometry().getNumberOfRocsA()) ? -z : z; + float v = (sector < getGeometry().getNumberOfSectorsA()) ? -z : z; return mT0 + maxTimeBin + v / mVdrift; } -GPUdi() float TPCFastTransform::convDeltaTimeToDeltaZinTimeFrame(int32_t roc, float deltaTime) const +GPUdi() float TPCFastTransform::convDeltaTimeToDeltaZinTimeFrame(int32_t sector, float deltaTime) const { float deltaZ = deltaTime * mVdrift; - return roc < getGeometry().getNumberOfRocsA() ? -deltaZ : deltaZ; + return sector < getGeometry().getNumberOfSectorsA() ? -deltaZ : deltaZ; } GPUdi() float TPCFastTransform::convDeltaZtoDeltaTimeInTimeFrameAbs(float deltaZ) const @@ -653,140 +639,115 @@ GPUdi() float TPCFastTransform::convDeltaZtoDeltaTimeInTimeFrameAbs(float deltaZ return deltaZ / mVdrift; } -GPUdi() float TPCFastTransform::convDeltaZtoDeltaTimeInTimeFrame(int32_t roc, float deltaZ) const +GPUdi() float TPCFastTransform::convDeltaZtoDeltaTimeInTimeFrame(int32_t sector, float deltaZ) const { float deltaT = deltaZ / mVdrift; - return roc < getGeometry().getNumberOfRocsA() ? -deltaT : deltaT; + return sector < getGeometry().getNumberOfSectorsA() ? -deltaT : deltaT; } -/* -GPUdi() float TPCFastTransform::getLastCalibratedTimeBin(int32_t roc) const -{ - /// Return a value of the last timebin where correction map is valid - float u, v, pad, time; - getGeometry().convScaledUVtoUV(roc, 0, 0.f, 1.f, u, v); - convUVtoPadTime(roc, 0, u, v, pad, time, 0); - return time; -} -*/ - -GPUdi() float TPCFastTransform::getMaxDriftTime(int32_t roc, int32_t row, float pad) const +GPUdi() float TPCFastTransform::getMaxDriftTime(int32_t sector, int32_t row, float pad) const { /// maximal possible drift time of the active area - float maxL = mCorrection.getMaxDriftLength(roc, row, pad); - return mT0 + maxL / mVdrift; + return convDriftLengthToTime(getGeometry().getTPCzLength(), 0.f); } -GPUdi() float TPCFastTransform::getMaxDriftTime(int32_t roc, int32_t row) const +GPUdi() float TPCFastTransform::getMaxDriftTime(int32_t sector, int32_t row) const { /// maximal possible drift time of the active area - float maxL = mCorrection.getMaxDriftLength(roc, row); - float maxTime = 0.f; - convVtoTime(maxL, maxTime, 0.f); - return maxTime; + return convDriftLengthToTime(getGeometry().getTPCzLength(), 0.f); } -GPUdi() float TPCFastTransform::getMaxDriftTime(int32_t roc) const +GPUdi() float TPCFastTransform::getMaxDriftTime(int32_t sector) const { /// maximal possible drift time of the active area - float maxL = mCorrection.getMaxDriftLength(roc); - float maxTime = 0.f; - convVtoTime(maxL, maxTime, 0.f); - return maxTime; + return convDriftLengthToTime(getGeometry().getTPCzLength(), 0.f); } -GPUdi() void TPCFastTransform::InverseTransformYZtoX(int32_t roc, int32_t row, float y, float z, float& x, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const +GPUdi() void TPCFastTransform::InverseTransformYZtoX(int32_t sector, int32_t row, float realY, float realZ, float& realX, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const { GPUCA_RTC_SPECIAL_CODE(ref2 = nullptr; scale2 = 0.f;); /// Transformation y,z -> x - float u = 0, v = 0; - getGeometry().convLocalToUV(roc, y, z, u, v); + + float dx = 0.f; + if ((scale >= 0.f) || (scaleMode == 1) || (scaleMode == 2)) { - mCorrection.getCorrectionInvCorrectedX(roc, row, u, v, x); + dx = mCorrection.getCorrectionXatRealYZ(sector, row, realY, realZ); if (ref) { // scaling was requested if (scaleMode == 0 && scale > 0.f) { - float xr; - ref->mCorrection.getCorrectionInvCorrectedX(roc, row, u, v, xr); - x = (x - xr) * scale + xr; + float dxref = ref->mCorrection.getCorrectionXatRealYZ(sector, row, realY, realZ); + dx = (dx - dxref) * scale + dxref; } else if ((scale != 0) && ((scaleMode == 1) || (scaleMode == 2))) { - float xr; - ref->mCorrection.getCorrectionInvCorrectedX(roc, row, u, v, xr); - x = (xr - getGeometry().getRowInfo(row).x) * scale + x; // xr=mGeo.getRowInfo(row).x + dx; + float dxref = ref->mCorrection.getCorrectionXatRealYZ(sector, row, realY, realZ); + dx = dxref * scale + dx; } } if (ref2 && (scale2 != 0)) { - float xr; - ref2->mCorrection.getCorrectionInvCorrectedX(roc, row, u, v, xr); - x = (xr - getGeometry().getRowInfo(row).x) * scale2 + x; // xr=mGeo.getRowInfo(row).x + dx; + float dxref = ref2->mCorrection.getCorrectionXatRealYZ(sector, row, realY, realZ); + dx = dxref * scale2 + dx; } - } else { - x = mCorrection.getGeometry().getRowInfo(row).x; // corrections are disabled } + + realX = mCorrection.getGeometry().getRowInfo(row).x + dx; + GPUCA_DEBUG_STREAMER_CHECK(if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamFastTransform)) { o2::utils::DebugStreamer::instance()->getStreamer("debug_fasttransform", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_InverseTransformYZtoX").data() - << "roc=" << roc + << "sector=" << sector << "row=" << row << "scale=" << scale - << "y=" << y - << "z=" << z - << "x=" << x - << "v=" << v - << "u=" << u + << "y=" << realY + << "z=" << realZ + << "x=" << realX << "\n"; }) } -GPUdi() void TPCFastTransform::InverseTransformYZtoNominalYZ(int32_t roc, int32_t row, float y, float z, float& ny, float& nz, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const +GPUdi() void TPCFastTransform::InverseTransformYZtoNominalYZ(int32_t sector, int32_t row, float realY, float realZ, float& measuredY, float& measuredZ, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const { + /// Transformation real y,z -> measured y,z + GPUCA_RTC_SPECIAL_CODE(ref2 = nullptr; scale2 = 0.f;); - /// Transformation y,z -> x - float u = 0, v = 0, un = 0, vn = 0; - getGeometry().convLocalToUV(roc, y, z, u, v); + + float dy = 0; + float dz = 0; + if ((scale >= 0.f) || (scaleMode == 1) || (scaleMode == 2)) { - mCorrection.getCorrectionInvUV(roc, row, u, v, un, vn); + std::tie(dy, dz) = mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); + if (ref) { // scaling was requested if (scaleMode == 0 && scale > 0.f) { - float unr = 0, vnr = 0; - ref->mCorrection.getCorrectionInvUV(roc, row, u, v, unr, vnr); - un = (un - unr) * scale + unr; - vn = (vn - vnr) * scale + vnr; + auto [dyRef, dzRef] = ref->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); + dy = (dy - dyRef) * scale + dyRef; + dz = (dz - dzRef) * scale + dzRef; } else if ((scale != 0) && ((scaleMode == 1) || (scaleMode == 2))) { - float unr = 0, vnr = 0; - ref->mCorrection.getCorrectionInvUV(roc, row, u, v, unr, vnr); - un = (unr - u) * scale + un; // unr = u - duv[0]; - vn = (vnr - v) * scale + vn; + auto [dyRef, dzRef] = ref->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); + dy = dyRef * scale + dy; + dz = dzRef * scale + dz; } if (ref2 && (scale2 != 0)) { - float unr = 0, vnr = 0; - ref2->mCorrection.getCorrectionInvUV(roc, row, u, v, unr, vnr); - un = (unr - u) * scale2 + un; // unr = u - duv[0]; - vn = (vnr - v) * scale2 + vn; + auto [dyRef, dzRef] = ref2->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); + dy = dyRef * scale2 + dy; + dz = dzRef * scale2 + dz; } } - } else { - un = u; - vn = v; } - getGeometry().convUVtoLocal(roc, un, vn, ny, nz); + + measuredY = realY - dy; + measuredZ = realZ - dz; GPUCA_DEBUG_STREAMER_CHECK(if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamFastTransform)) { o2::utils::DebugStreamer::instance()->getStreamer("debug_fasttransform", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_InverseTransformYZtoNominalYZ").data() - << "roc=" << roc + << "sector=" << sector << "row=" << row << "scale=" << scale - << "y=" << y - << "z=" << z - << "ny=" << ny - << "nz=" << nz - << "u=" << u - << "v=" << v - << "un=" << un - << "vn=" << vn + << "real y=" << realY + << "real z=" << realZ + << "measured y=" << measuredY + << "measured z=" << measuredZ << "\n"; }) } -GPUdi() void TPCFastTransform::InverseTransformXYZtoNominalXYZ(int32_t roc, int32_t row, float x, float y, float z, float& nx, float& ny, float& nz, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const +GPUdi() void TPCFastTransform::InverseTransformXYZtoNominalXYZ(int32_t sector, int32_t row, float x, float y, float z, float& nx, float& ny, float& nz, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const { /// Inverse transformation: Transformed X, Y and Z -> X, Y and Z, transformed w/o space charge correction int32_t row2 = row + 1; @@ -797,8 +758,8 @@ GPUdi() void TPCFastTransform::InverseTransformXYZtoNominalXYZ(int32_t roc, int3 float nx2, ny2, nz2; // nominal coordinates for row2 nx1 = getGeometry().getRowInfo(row).x; nx2 = getGeometry().getRowInfo(row2).x; - InverseTransformYZtoNominalYZ(roc, row, y, z, ny1, nz1, ref, ref2, scale, scale2, scaleMode); - InverseTransformYZtoNominalYZ(roc, row2, y, z, ny2, nz2, ref, ref2, scale, scale2, scaleMode); + InverseTransformYZtoNominalYZ(sector, row, y, z, ny1, nz1, ref, ref2, scale, scale2, scaleMode); + InverseTransformYZtoNominalYZ(sector, row2, y, z, ny2, nz2, ref, ref2, scale, scale2, scaleMode); float c1 = (nx2 - nx) / (nx2 - nx1); float c2 = (nx - nx1) / (nx2 - nx1); nx = x; diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx b/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx index c8982f05d4730..e7e026f464818 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx @@ -28,14 +28,14 @@ using namespace o2::gpu; TPCFastTransformGeo::TPCFastTransformGeo() { // Default Constructor: creates an empty uninitialized object - double dAlpha = 2. * M_PI / (NumberOfRocsA); - for (int32_t i = 0; i < NumberOfRocs; i++) { - RocInfo& s = mRocInfos[i]; + double dAlpha = 2. * M_PI / (NumberOfSectorsA); + for (int32_t i = 0; i < NumberOfSectors; i++) { + SectorInfo& s = mSectorInfos[i]; double alpha = dAlpha * (i + 0.5); s.sinAlpha = sin(alpha); s.cosAlpha = cos(alpha); } - mRocInfos[NumberOfRocs] = RocInfo{}; + mSectorInfos[NumberOfSectors] = SectorInfo{}; for (int32_t i = 0; i < MaxNumberOfRows + 1; i++) { mRowInfos[i] = RowInfo{}; @@ -84,7 +84,7 @@ void TPCFastTransformGeo::setTPCrow(int32_t iRow, float x, int32_t nPads, float // Make scaled U = area between the geometrical sector borders - const double sectorAngle = 2. * M_PI / NumberOfRocsA; + const double sectorAngle = 2. * M_PI / NumberOfSectorsA; const double scaleXtoRowWidth = 2. * tan(0.5 * sectorAngle); double uWidth = x * scaleXtoRowWidth; // distance to the sector border @@ -99,8 +99,8 @@ void TPCFastTransformGeo::finishConstruction() { /// Finishes initialization: puts everything to the flat buffer, releases temporary memory - assert(mConstructionMask & ConstructionState::InProgress); // construction in process - assert(mConstructionMask & ConstructionState::GeometryIsSet); // geometry is set + assert(mConstructionMask & ConstructionState::InProgress); // construction in process + assert(mConstructionMask & ConstructionState::GeometryIsSet); // geometry is set for (int32_t i = 0; i < mNumberOfRows; i++) { // all TPC rows are initialized assert(getRowInfo(i).maxPad > 0); @@ -123,7 +123,7 @@ void TPCFastTransformGeo::print() const #endif } -int32_t TPCFastTransformGeo::test(int32_t roc, int32_t row, float ly, float lz) const +int32_t TPCFastTransformGeo::test(int32_t sector, int32_t row, float ly, float lz) const { /// Check consistency of the class @@ -139,46 +139,21 @@ int32_t TPCFastTransformGeo::test(int32_t roc, int32_t row, float ly, float lz) float lx1 = 0.f, ly1 = 0.f, lz1 = 0.f; float gx = 0.f, gy = 0.f, gz = 0.f; - convLocalToGlobal(roc, lx, ly, lz, gx, gy, gz); - convGlobalToLocal(roc, gx, gy, gz, lx1, ly1, lz1); + convLocalToGlobal(sector, lx, ly, lz, gx, gy, gz); + convGlobalToLocal(sector, gx, gy, gz, lx1, ly1, lz1); if (fabs(lx1 - lx) > 1.e-4 || fabs(ly1 - ly) > 1.e-4 || fabs(lz1 - lz) > 1.e-7) { LOG(info) << "Error local <-> global: x " << lx << " dx " << lx1 - lx << " y " << ly << " dy " << ly1 - ly << " z " << lz << " dz " << lz1 - lz; error = -3; } - float u = 0.f, v = 0.f; - convLocalToUV(roc, ly, lz, u, v); - convUVtoLocal(roc, u, v, ly1, lz1); - if (fabs(ly1 - ly) + fabs(lz1 - lz) > 1.e-6) { - LOG(info) << "Error local <-> UV: y " << ly << " dy " << ly1 - ly << " z " << lz << " dz " << lz1 - lz; + auto [pad, length] = convLocalToPadDriftLength(sector, 10, ly, lz); + auto [ly2, lz2] = convPadDriftLengthToLocal(sector, 10, pad, length); + + if (fabs(ly2 - ly) + fabs(lz2 - lz) > 1.e-6) { + LOG(info) << "Error local <-> UV: y " << ly << " dy " << ly2 - ly << " z " << lz << " dz " << lz2 - lz; error = -4; } - /* - float su = 0.f, sv = 0.f; - - convUVtoScaledUV(roc, row, u, v, su, sv); - - if (su < 0.f || su > 1.f) { - LOG(info) << "Error scaled U range: u " << u << " su " << su; - error = -5; - } - - float u1 = 0.f, v1 = 0.f; - convScaledUVtoUV(roc, row, su, sv, u1, v1); - - if (fabs(u1 - u) > 1.e-4 || fabs(v1 - v) > 1.e-4) { - LOG(info) << "Error UV<->scaled UV: u " << u << " du " << u1 - u << " v " << v << " dv " << v1 - v; - error = -6; - } - */ - float pad = convUtoPad(row, u); - float u1 = convPadToU(row, pad); - - if (fabs(u1 - u) > 1.e-5) { - LOG(info) << "Error U<->Pad: u " << u << " pad " << pad << " du " << u1 - u; - error = -7; - } #if !defined(GPUCA_GPUCODE) if (error != 0) { diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.h b/GPU/TPCFastTransformation/TPCFastTransformGeo.h index a5d642158cd8f..4072435e948a5 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.h +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.h @@ -34,11 +34,11 @@ namespace gpu class TPCFastTransformGeo { public: - /// The struct contains necessary info for TPC ROC - struct RocInfo { + /// The struct contains necessary info for TPC sector + struct SectorInfo { float sinAlpha{0.f}; ///< sin of the angle between the local x and the global x float cosAlpha{0.f}; ///< cos of the angle between the local x and the global x - ClassDefNV(RocInfo, 1); + ClassDefNV(SectorInfo, 1); }; /// The struct contains necessary info about TPC padrow @@ -54,6 +54,15 @@ class TPCFastTransformGeo /// get U max GPUd() float getUmax() const { return -u0; } + /// get Y min + GPUd() float getYmin() const { return u0; } + + /// get Y max + GPUd() float getYmax() const { return -u0; } + + /// get Y range + GPUd() std::tuple getYrange() const { return {getYmin(), getYmax()}; } + /// get width in U GPUd() float getUwidth() const { return -2.f * u0; } @@ -81,7 +90,7 @@ class TPCFastTransformGeo /// _______________ Construction interface ________________________ - /// Starts the initialization procedure, reserves temporary memory + /// Starts the initialization psectoredure, reserves temporary memory void startConstruction(int32_t numberOfRows); /// Initializes a TPC row @@ -100,11 +109,11 @@ class TPCFastTransformGeo /// _______________ Getters _________________________________ - /// Gives number of TPC ROCs - GPUd() static constexpr int32_t getNumberOfRocs() { return NumberOfRocs; } + /// Gives number of TPC sectors + GPUd() static constexpr int32_t getNumberOfSectors() { return NumberOfSectors; } - /// Gives number of TPC ROCs on the A side - GPUd() static constexpr int32_t getNumberOfRocsA() { return NumberOfRocsA; } + /// Gives number of TPC sectors on the A side + GPUd() static constexpr int32_t getNumberOfSectorsA() { return NumberOfSectorsA; } /// Gives number of TPC rows GPUd() int32_t getNumberOfRows() const { return mNumberOfRows; } @@ -112,8 +121,8 @@ class TPCFastTransformGeo /// Gives number of TPC rows GPUd() static constexpr int getMaxNumberOfRows() { return MaxNumberOfRows; } - /// Gives roc info - GPUd() const RocInfo& getRocInfo(int32_t roc) const; + /// Gives sector info + GPUd() const SectorInfo& getSectorInfo(int32_t sector) const; /// Gives TPC row info GPUd() const RowInfo& getRowInfo(int32_t row) const; @@ -121,20 +130,31 @@ class TPCFastTransformGeo /// Gives Z length of the TPC, one Z side GPUd() float getTPCzLength() const { return mTPCzLength; } + /// Gives Z range for the corresponding TPC side + GPUd() std::tuple getZrange(int32_t sector) const; + /// _______________ Conversion of coordinate systems __________ /// convert Local -> Global c.s. - GPUd() void convLocalToGlobal(int32_t roc, float lx, float ly, float lz, float& gx, float& gy, float& gz) const; + GPUd() void convLocalToGlobal(int32_t sector, float lx, float ly, float lz, float& gx, float& gy, float& gz) const; /// convert Global->Local c.s. - GPUd() void convGlobalToLocal(int32_t roc, float gx, float gy, float gz, float& lx, float& ly, float& lz) const; + GPUd() void convGlobalToLocal(int32_t sector, float gx, float gy, float gz, float& lx, float& ly, float& lz) const; + + /// convert Pad, DriftLength -> Local c.s. + GPUd() std::tuple convPadDriftLengthToLocal(int32_t sector, int32_t row, float pad, float driftLength) const; + + /// convert DriftLength -> Local c.s. + GPUd() float convDriftLengthToLocal(int32_t sector, float driftLength) const; + + /// convert Local c.s. -> Pad, DriftLength + GPUd() std::tuple convLocalToPadDriftLength(int32_t sector, int32_t row, float y, float z) const; /// convert UV -> Local c.s. - GPUd() void convUVtoLocal(int32_t roc, float u, float v, float& y, float& z) const; - GPUd() void convVtoLocal(int32_t roc, float v, float& z) const; + GPUd() void convUVtoLocal1(int32_t sector, float u, float v, float& y, float& z) const; /// convert Local-> UV c.s. - GPUd() void convLocalToUV(int32_t roc, float y, float z, float& u, float& v) const; + GPUd() void convLocalToUV1(int32_t sector, float y, float z, float& u, float& v) const; /// convert Pad coordinate -> U GPUd() float convPadToU(int32_t row, float pad) const; @@ -146,7 +166,7 @@ class TPCFastTransformGeo void print() const; /// Method for testing consistency - int32_t test(int32_t roc, int32_t row, float ly, float lz) const; + int32_t test(int32_t sector, int32_t row, float ly, float lz) const; /// Method for testing consistency int32_t test() const; @@ -154,9 +174,9 @@ class TPCFastTransformGeo private: /// _______________ Data members _______________________________________________ - static constexpr int32_t NumberOfRocs = 36; ///< Number of TPC rocs ( roc = inner + outer sector ) - static constexpr int32_t NumberOfRocsA = NumberOfRocs / 2; ///< Number of TPC rocs side A - static constexpr int32_t MaxNumberOfRows = 160; ///< Max Number of TPC rows in a roc + static constexpr int32_t NumberOfSectors = 36; ///< Number of TPC sectors ( sector = inner + outer sector ) + static constexpr int32_t NumberOfSectorsA = NumberOfSectors / 2; ///< Number of TPC sectors side A + static constexpr int32_t MaxNumberOfRows = 160; ///< Max Number of TPC rows in a sector /// _______________ Construction control _______________________________________________ @@ -172,10 +192,10 @@ class TPCFastTransformGeo /// _______________ Geometry _______________________________________________ - int32_t mNumberOfRows = 0; ///< Number of TPC rows. It is different for the Run2 and the Run3 setups - float mTPCzLength = 0.f; ///< Z length of one TPC side (A or C) + int32_t mNumberOfRows = 0; ///< Number of TPC rows. It is different for the Run2 and the Run3 setups + float mTPCzLength = 0.f; ///< Z length of one TPC side (A or C) - RocInfo mRocInfos[NumberOfRocs + 1]; ///< array of roc information [fixed size] + SectorInfo mSectorInfos[NumberOfSectors + 1]; ///< array of sector information [fixed size] RowInfo mRowInfos[MaxNumberOfRows + 1]; ///< array of row information [fixed size] ClassDefNV(TPCFastTransformGeo, 3); @@ -185,13 +205,13 @@ class TPCFastTransformGeo // Inline implementations of some methods // ======================================================================= -GPUdi() const TPCFastTransformGeo::RocInfo& TPCFastTransformGeo::getRocInfo(int32_t roc) const +GPUdi() const TPCFastTransformGeo::SectorInfo& TPCFastTransformGeo::getSectorInfo(int32_t sector) const { - /// Gives roc info - if (roc < 0 || roc >= NumberOfRocs) { // return zero object - roc = NumberOfRocs; + /// Gives sector info + if (sector < 0 || sector >= NumberOfSectors) { // return zero object + sector = NumberOfSectors; } - return mRocInfos[roc]; + return mSectorInfos[sector]; } GPUdi() const TPCFastTransformGeo::RowInfo& TPCFastTransformGeo::getRowInfo(int32_t row) const @@ -203,55 +223,93 @@ GPUdi() const TPCFastTransformGeo::RowInfo& TPCFastTransformGeo::getRowInfo(int3 return mRowInfos[row]; } -GPUdi() void TPCFastTransformGeo::convLocalToGlobal(int32_t roc, float lx, float ly, float lz, float& gx, float& gy, float& gz) const +GPUdi() void TPCFastTransformGeo::convLocalToGlobal(int32_t sector, float lx, float ly, float lz, float& gx, float& gy, float& gz) const { /// convert Local -> Global c.s. - const RocInfo& rocInfo = getRocInfo(roc); - gx = lx * rocInfo.cosAlpha - ly * rocInfo.sinAlpha; - gy = lx * rocInfo.sinAlpha + ly * rocInfo.cosAlpha; + const SectorInfo& sectorInfo = getSectorInfo(sector); + gx = lx * sectorInfo.cosAlpha - ly * sectorInfo.sinAlpha; + gy = lx * sectorInfo.sinAlpha + ly * sectorInfo.cosAlpha; gz = lz; } -GPUdi() void TPCFastTransformGeo::convGlobalToLocal(int32_t roc, float gx, float gy, float gz, float& lx, float& ly, float& lz) const +GPUdi() void TPCFastTransformGeo::convGlobalToLocal(int32_t sector, float gx, float gy, float gz, float& lx, float& ly, float& lz) const { /// convert Global -> Local c.s. - const RocInfo& rocInfo = getRocInfo(roc); - lx = gx * rocInfo.cosAlpha + gy * rocInfo.sinAlpha; - ly = -gx * rocInfo.sinAlpha + gy * rocInfo.cosAlpha; + const SectorInfo& sectorInfo = getSectorInfo(sector); + lx = gx * sectorInfo.cosAlpha + gy * sectorInfo.sinAlpha; + ly = -gx * sectorInfo.sinAlpha + gy * sectorInfo.cosAlpha; lz = gz; } -GPUdi() void TPCFastTransformGeo::convVtoLocal(int32_t roc, float v, float& lz) const +GPUdi() std::tuple TPCFastTransformGeo::convPadDriftLengthToLocal(int32_t sector, int32_t row, float pad, float driftLength) const { - /// convert UV -> Local c.s. - if (roc < NumberOfRocsA) { // TPC side A - lz = mTPCzLength - v; - } else { // TPC side C - lz = v - mTPCzLength; // drift direction is mirrored on C-side + /// convert Pad, DriftLength -> Local c.s. + const RowInfo& rowInfo = getRowInfo(row); + float u = (pad - 0.5f * rowInfo.maxPad) * rowInfo.padWidth; + float y, z; + if (sector < NumberOfSectorsA) { // TPC side A + y = u; + z = mTPCzLength - driftLength; + } else { // TPC side C + y = -u; // pads are mirrorred on C-side + z = driftLength - mTPCzLength; // drift direction is mirrored on C-side } + return {y, z}; } -GPUdi() void TPCFastTransformGeo::convUVtoLocal(int32_t roc, float u, float v, float& ly, float& lz) const +GPUdi() float TPCFastTransformGeo::convDriftLengthToLocal(int32_t sector, float driftLength) const +{ + /// convert DriftLength -> Local c.s. + return (sector < NumberOfSectorsA) ? (mTPCzLength - driftLength) : (driftLength - mTPCzLength); +} + +GPUdi() void TPCFastTransformGeo::convUVtoLocal1(int32_t sector, float u, float v, float& ly, float& lz) const { /// convert UV -> Local c.s. - if (roc < NumberOfRocsA) { // TPC side A + if (sector < NumberOfSectorsA) { // TPC side A ly = u; lz = mTPCzLength - v; - } else { // TPC side C - ly = -u; // pads are mirrorred on C-side - lz = v - mTPCzLength; // drift direction is mirrored on C-side + } else { // TPC side C + ly = -u; // pads are mirrorred on C-side + lz = v - mTPCzLength; // drift direction is mirrored on C-side + } +} + +GPUdi() std::tuple TPCFastTransformGeo::getZrange(int32_t sector) const +{ + /// z range for the sector + if (sector < NumberOfSectorsA) { // TPC side A + return {0.f, mTPCzLength}; + } else { // TPC side C + return {-mTPCzLength, 0.f}; + } +} + +GPUdi() std::tuple TPCFastTransformGeo::convLocalToPadDriftLength(int32_t sector, int32_t row, float y, float z) const +{ + /// convert Local c.s. -> Pad, DriftLength + float u, l; + if (sector < NumberOfSectorsA) { // TPC side A + u = y; + l = mTPCzLength - z; + } else { // TPC side C + u = -y; // pads are mirrorred on C-side + l = z + mTPCzLength; // drift direction is mirrored on C-side } + const TPCFastTransformGeo::RowInfo& rowInfo = getRowInfo(row); + float pad = u / rowInfo.padWidth + 0.5f * rowInfo.maxPad; + return {pad, l}; } -GPUdi() void TPCFastTransformGeo::convLocalToUV(int32_t roc, float ly, float lz, float& u, float& v) const +GPUdi() void TPCFastTransformGeo::convLocalToUV1(int32_t sector, float ly, float lz, float& u, float& v) const { /// convert Local-> UV c.s. - if (roc < NumberOfRocsA) { // TPC side A + if (sector < NumberOfSectorsA) { // TPC side A u = ly; v = mTPCzLength - lz; - } else { // TPC side C - u = -ly; // pads are mirrorred on C-side - v = lz + mTPCzLength; // drift direction is mirrored on C-side + } else { // TPC side C + u = -ly; // pads are mirrorred on C-side + v = lz + mTPCzLength; // drift direction is mirrored on C-side } } diff --git a/GPU/TPCFastTransformation/TPCFastTransformManager.cxx b/GPU/TPCFastTransformation/TPCFastTransformManager.cxx deleted file mode 100644 index c553d9cc6dac1..0000000000000 --- a/GPU/TPCFastTransformation/TPCFastTransformManager.cxx +++ /dev/null @@ -1,336 +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 TPCFastTransformManager.cxx -/// \brief Implementation of TPCFastTransformManager class -/// -/// \author Sergey Gorbunov - -#include "TPCFastTransformManager.h" -#include "AliHLTTPCGeometry.h" -#include "AliTPCParam.h" -#include "AliTPCRecoParam.h" -#include "AliTPCTransform.h" -#include "AliTPCcalibDB.h" -#include "TPCFastTransform.h" -#include "Spline2DHelper.h" -blabla - -using namespace o2::gpu; - -TPCFastTransformManager::TPCFastTransformManager() - : mError(), mOrigTransform(nullptr), fLastTimeBin(0) {} - -int32_t TPCFastTransformManager::create(TPCFastTransform& fastTransform, - AliTPCTransform* transform, - long TimeStamp) -{ - /// Initializes TPCFastTransform object - - AliTPCcalibDB* pCalib = AliTPCcalibDB::Instance(); - if (!pCalib) { - return storeError( - -1, "TPCFastTransformManager::Init: No TPC calibration instance found"); - } - - AliTPCParam* tpcParam = pCalib->GetParameters(); - if (!tpcParam) { - return storeError( - -2, "TPCFastTransformManager::Init: No TPCParam object found"); - } - - if (!transform) { - transform = pCalib->GetTransform(); - } - if (!transform) { - return storeError( - -3, "TPCFastTransformManager::Init: No TPC transformation found"); - } - - mOrigTransform = transform; - - tpcParam->Update(); - tpcParam->ReadGeoMatrices(); - - const AliTPCRecoParam* rec = transform->GetCurrentRecoParam(); - if (!rec) { - return storeError(-5, - "TPCFastTransformManager::Init: No TPC Reco Param " - "set in transformation"); - } - - bool useCorrectionMap = rec->GetUseCorrectionMap(); - - if (useCorrectionMap) { - transform->SetCorrectionMapMode(kTRUE); // If the simulation set this to - // false to simulate corrections, we - // need to reverse it for the - // transformation - } - // find last calibrated time bin - - fLastTimeBin = rec->GetLastBin(); - - const int32_t nRows = tpcParam->GetNRowLow() + tpcParam->GetNRowUp(); - - TPCFastTransformGeo geo; - - { // construct the geometry - geo.startConstruction(nRows); - - float tpcZlengthSideA = tpcParam->GetZLength(0); - float tpcZlengthSideC = - tpcParam->GetZLength(TPCFastTransformGeo::getNumberOfRocs() / 2); - - geo.setTPCzLength(tpcZlengthSideA, tpcZlengthSideC); - geo.setTPCalignmentZ(-mOrigTransform->GetDeltaZCorrTime()); - - for (int32_t row = 0; row < geo.getNumberOfRows(); row++) { - int32_t roc = 0, sector = 0, secrow = 0; - AliHLTTPCGeometry::Slice2Sector(roc, row, sector, secrow); - Int_t nPads = tpcParam->GetNPads(sector, secrow); - float xRow = tpcParam->GetPadRowRadii(sector, secrow); - float padWidth = tpcParam->GetInnerPadPitchWidth(); - if (row >= tpcParam->GetNRowLow()) { - padWidth = tpcParam->GetOuterPadPitchWidth(); - } - geo.setTPCrow(row, xRow, nPads, padWidth); - } - geo.finishConstruction(); - } - - TPCFastSpaceChargeCorrection correction; - - { // create the correction map - - const int32_t nDistortionScenarios = 1; - - correction.startConstruction(geo, nDistortionScenarios); - - TPCFastSpaceChargeCorrection::SplineType spline; - spline.recreate(8, 20); - - int32_t scenario = 0; - correction.setSplineScenario(scenario, spline); - - for (int32_t row = 0; row < geo.getNumberOfRows(); row++) { - correction.setRowScenarioID(row, scenario); - } - - correction.finishConstruction(); - } // .. create the correction map - - { // create the fast transform object - - fastTransform.startConstruction(correction); - - // tell the transformation to apply the space charge corrections - fastTransform.setApplyCorrectionOn(); - - // set some initial calibration values, will be reinitialised later int32_t - // updateCalibration() - const float t0 = 0.; - const float vDrift = 0.f; - const float vdCorrY = 0.; - const float ldCorr = 0.; - const float tofCorr = 0.; - const float primVtxZ = 0.; - const int64_t initTimeStamp = -1; - fastTransform.setCalibration(initTimeStamp, t0, vDrift, vdCorrY, ldCorr, - tofCorr, primVtxZ); - - fastTransform.finishConstruction(); - } - - return updateCalibration(fastTransform, TimeStamp); -} - -int32_t TPCFastTransformManager::updateCalibration(TPCFastTransform& fastTransform, - long TimeStamp) -{ - // Update the calibration with the new time stamp - - long lastTS = fastTransform.getTimeStamp(); - - // deinitialize - - fastTransform.setTimeStamp(-1); - - if (TimeStamp < 0) { - return 0; - } - - // search for the calibration database - - if (!mOrigTransform) { - return storeError(-1, - "TPCFastTransformManager::SetCurrentTimeStamp: TPC " - "transformation has not been set properly"); - } - - AliTPCcalibDB* pCalib = AliTPCcalibDB::Instance(); - if (!pCalib) { - return storeError(-2, - "TPCFastTransformManager::SetCurrentTimeStamp: No " - "TPC calibration found"); - } - - AliTPCParam* tpcParam = pCalib->GetParameters(); - if (!tpcParam) { - return storeError(-3, - "TPCFastTransformManager::SetCurrentTimeStamp: No " - "TPCParam object found"); - } - - AliTPCRecoParam* recoParam = mOrigTransform->GetCurrentRecoParamNonConst(); - if (!recoParam) { - return storeError(-5, - "TPCFastTransformManager::Init: No TPC Reco Param " - "set in transformation"); - } - - // calibration found, set the initialized status back - - fastTransform.setTimeStamp(lastTS); - - // less than 60 seconds from the previois time stamp, don't do anything - - if (lastTS >= 0 && TMath::Abs(lastTS - TimeStamp) < 60) { - return 0; - } - - // start the initialization - - bool useCorrectionMap = recoParam->GetUseCorrectionMap(); - - if (useCorrectionMap) { - // If the simulation set this to false to simulate corrections, we need to - // reverse it for the transformation This is a design feature. Historically - // HLT code runs as a part of simulation, not reconstruction. - mOrigTransform->SetCorrectionMapMode(kTRUE); - } - - // set the current time stamp - - mOrigTransform->SetCurrentTimeStamp(static_cast(TimeStamp)); - fastTransform.setTimeStamp(TimeStamp); - - // find last calibrated time bin - - fLastTimeBin = recoParam->GetLastBin(); - - double t0 = mOrigTransform->GetTBinOffset(); - double driftCorrPT = mOrigTransform->GetDriftCorrPT(); - double vdCorrectionTime = mOrigTransform->GetVDCorrectionTime(); - double vdCorrectionTimeGY = mOrigTransform->GetVDCorrectionTimeGY(); - double time0CorrTime = mOrigTransform->GetTime0CorrTime(); - - // original formula: - // L = (t-t0)*ZWidth*driftCorrPT*vdCorrectionTime*( 1 + - // yLab*vdCorrectionTimeGY ) - time0CorrTime + 3.*tpcParam->GetZSigma(); Z = - // Z(L) - fDeltaZCorrTime chebyshev corrections for xyz Time-of-flight - // correction: ldrift += dist-to-vtx*tofCorr - - // fast transform formula: - // L = (t-t0)*(mVdrift + mVdriftCorrY*yLab ) + mLdriftCorr - // Z = Z(L) + tpcAlignmentZ - // spline corrections for xyz - // Time-of-flight correction: ldrift += dist-to-vtx*tofCorr - - double vDrift = tpcParam->GetZWidth() * driftCorrPT * vdCorrectionTime; - double vdCorrY = vDrift * vdCorrectionTimeGY; - double ldCorr = -time0CorrTime + 3 * tpcParam->GetZSigma(); - - double tofCorr = (0.01 * tpcParam->GetDriftV()) / TMath::C(); - double primVtxZ = mOrigTransform->GetPrimVertex()[2]; - - bool useTOFcorrection = recoParam->GetUseTOFCorrection(); - - if (!useTOFcorrection) { - tofCorr = 0; - } - - fastTransform.setCalibration(TimeStamp, t0, vDrift, vdCorrY, ldCorr, tofCorr, - primVtxZ); - - // now calculate the correction map: dx,du,dv = ( origTransform() -> x,u,v) - - // fastTransformNominal:x,u,v - - const TPCFastTransformGeo& geo = fastTransform.getGeometry(); - - TPCFastSpaceChargeCorrection& correction = - fastTransform.getCorrection(); - - // switch TOF correction off for a while - - recoParam->SetUseTOFCorrection(kFALSE); - - for (int32_t roc = 0; roc < geo.getNumberOfRocs(); roc++) { - - for (int32_t row = 0; row < geo.getNumberOfRows(); row++) { - - const TPCFastTransformGeo::RowInfo& rowInfo = geo.getRowInfo(row); - - const TPCFastSpaceChargeCorrection::SplineType& spline = correction.getSpline(roc, row); - float* data = correction.getSplineData(roc, row); - - Spline2DHelper helper; - helper.setSpline(spline, 4, 4); - auto F = [&](double su, double sv, double dxuv[3]) { - float x = rowInfo.x; - // x, u, v cordinates of the knot (local cartesian coord. of roc - // towards central electrode ) - float u = 0, v = 0; - geo.convScaledUVtoUV(roc, row, su, sv, u, v); - - // row, pad, time coordinates of the knot - float vertexTime = 0.f; - float pad = 0.f, time = 0.f; - fastTransform.convUVtoPadTime(roc, row, u, v, pad, time, vertexTime); - - // nominal x,y,z coordinates of the knot (without corrections and - // time-of-flight correction) - float y = 0, z = 0; - geo.convUVtoLocal(roc, u, v, y, z); - - // original TPC transformation (row,pad,time) -> (x,y,z) without - // time-of-flight correction - float ox = 0, oy = 0, oz = 0; - { - int32_t sector = 0, secrow = 0; - AliHLTTPCGeometry::Slice2Sector(roc, row, sector, secrow); - int32_t is[] = {sector}; - double xx[] = {static_cast(secrow), pad, time}; - mOrigTransform->Transform(xx, is, 0, 1); - ox = xx[0]; - oy = xx[1]; - oz = xx[2]; - } - // convert to u,v - float ou = 0, ov = 0; - geo.convLocalToUV(roc, oy, oz, ou, ov); - - // corrections in x,u,v: - dxuv[0] = ox - x; - dxuv[1] = ou - u; - dxuv[2] = ov - v; - }; - - helper.approximateFunction(data, 0., 1., 0., 1., F); - } // row - } // roc - - // set back the time-of-flight correction; - - recoParam->SetUseTOFCorrection(useTOFcorrection); - - return 0; -} diff --git a/GPU/TPCFastTransformation/TPCFastTransformManager.h b/GPU/TPCFastTransformation/TPCFastTransformManager.h deleted file mode 100644 index f981b05bec241..0000000000000 --- a/GPU/TPCFastTransformation/TPCFastTransformManager.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 TPCFastTransformManager.h -/// \brief Definition of TPCFastTransformManager class -/// -/// \author Sergey Gorbunov - -#ifndef ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_TPCFASTTRANSFORMMANAGER_H -#define ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_TPCFASTTRANSFORMMANAGER_H - -#include - -#include "GPUCommonDef.h" -#include "Rtypes.h" -#include "TString.h" -#include "AliTPCTransform.h" - -namespace o2 -{ -namespace gpu -{ -class TPCFastTransform; - -/// -/// The TPCFastTransformManager class is to initialize TPCFastTransformation object -/// - -class TPCFastTransformManager -{ - public: - /// _____________ Constructors / destructors __________________________ - - /// Default constructor - TPCFastTransformManager(); - - /// Copy constructor: disabled - TPCFastTransformManager(const TPCFastTransformManager&) = delete; - - /// Assignment operator: disabled - TPCFastTransformManager& operator=(const TPCFastTransformManager&) = delete; - - /// Destructor - ~TPCFastTransformManager() = default; - - /// _______________ Main functionality ________________________ - - /// Initializes TPCFastTransform object - int32_t create(TPCFastTransform& spline, AliTPCTransform* transform, long TimeStamp); - - /// Updates the transformation with the new time stamp - Int_t updateCalibration(TPCFastTransform& spline, long TimeStamp); - - /// _______________ Utilities ________________________ - - AliTPCTransform* getOriginalTransform() { return mOrigTransform; } - - /// Gives error string - const char* getLastError() const { return mError.Data(); } - - private: - /// Stores an error message - int32_t storeError(Int_t code, const char* msg); - - TString mError; ///< error string - AliTPCTransform* mOrigTransform; ///< transient - int32_t fLastTimeBin; ///< last calibrated time bin -}; - -inline int32_t TPCFastTransformManager::storeError(int32_t code, const char* msg) -{ - mError = msg; - return code; -} -} // namespace gpu -} // namespace o2 - -#endif diff --git a/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h b/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h index 7c1ae8fd56800..fc15506d5397c 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h +++ b/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h @@ -59,7 +59,7 @@ #pragma link C++ class o2::gpu::IrregularSpline2D3DCalibrator + ; #pragma link C++ class o2::gpu::TPCFastTransformGeo + ; -#pragma link C++ class o2::gpu::TPCFastTransformGeo::RocInfo + ; +#pragma link C++ class o2::gpu::TPCFastTransformGeo::SectorInfo + ; #pragma link C++ class o2::gpu::TPCFastTransformGeo::RowInfo + ; #pragma link C++ class o2::gpu::TPCFastTransform + ; @@ -68,9 +68,8 @@ #pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::RowInfo + ; #pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection + ; -#pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::RocInfo + ; -#pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::RowActiveArea + ; -#pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::RocRowInfo + ; +#pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::SectorInfo + ; +#pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::SectorRowInfo + ; #pragma link C++ class o2::gpu::CorrectionMapsHelper + ; #pragma link C++ struct o2::gpu::MultivariatePolynomialContainer + ; diff --git a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C index 7e889d5a9e7db..bee1f9107ddd2 100644 --- a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C +++ b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C @@ -44,7 +44,7 @@ using namespace o2::tpc; using namespace o2::gpu; -void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", +void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* fileNameInv = "debugVoxResInv.root", const char* outFileName = "TPCFastTransform_VoxRes.root", bool useSmoothed = false, bool invertSigns = false) { @@ -56,11 +56,11 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", To visiualise the results: root -l transformDebug.root - corr->Draw("cx:y:z","iRoc==0&&iRow==10","") - grid->Draw("cx:y:z","iRoc==0&&iRow==10","same") - vox->Draw("vx:y:z","iRoc==0&&iRow==10","same") - corrvox->Draw("cx:y:z","iRoc==0&&iRow==10","same") - points->Draw("px:y:z","iRoc==0&&iRow==10","same") + corr->Draw("cx:y:z","iSector==0&&iRow==10","") + grid->Draw("cx:y:z","iSector==0&&iRow==10","same") + vox->Draw("vx:y:z","iSector==0&&iRow==10","same") + corrvox->Draw("cx:y:z","iSector==0&&iRow==10","same") + points->Draw("px:y:z","iSector==0&&iRow==10","same") */ if (gSystem->AccessPathName(fileName)) { @@ -82,6 +82,18 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", return; } + TTree* voxResTreeInverse = nullptr; + std::unique_ptr fileInv; + if (fileNameInv && !std::string(fileNameInv).empty()) { + fileInv = std::unique_ptr(TFile::Open(fileNameInv, "READ")); + if (!fileInv || !fileInv->IsOpen()) { + std::cout << " input file " << fileNameInv << " does not exist!" << std::endl; + return; + } + fileInv->cd(); + gDirectory->GetObject("voxResTree", voxResTreeInverse); + } + auto userInfo = voxResTree->GetUserInfo(); if (!userInfo->FindObject("y2xBinning") || !userInfo->FindObject("z2xBinning")) { @@ -140,7 +152,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", corrHelper->setNthreadsToMaximum(); // corrHelper->setNthreads(1); - auto corrPtr = corrHelper->createFromTrackResiduals(trackResiduals, voxResTree, useSmoothed, invertSigns); + auto corrPtr = corrHelper->createFromTrackResiduals(trackResiduals, voxResTree, voxResTreeInverse, useSmoothed, invertSigns); std::unique_ptr fastTransform( helper->create(0, *corrPtr)); @@ -168,11 +180,11 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const o2::gpu::TPCFastTransformGeo& geo = helper->getGeometry(); - // for (int32_t iRoc = 0; iRoc < geo.getNumberOfSlices(); iRoc++) { - for (int32_t iRoc = 0; iRoc < 1; iRoc++) { + // for (int32_t iSector = 0; iSector < geo.getNumberOfSectors(); iSector++) { + for (int32_t iSector = 0; iSector < 1; iSector++) { for (int32_t iRow = 0; iRow < geo.getNumberOfRows(); iRow++) { - auto& info = corr.getSliceRowInfo(iRoc, iRow); - std::cout << "roc " << iRoc << " row " << iRow + auto& info = corr.getSectorRowInfo(iSector, iRow); + std::cout << "sector " << iSector << " row " << iRow << " gridV0 " << info.gridV0 << " gridCorrU0 " << info.gridCorrU0 << " gridCorrV0 " << info.gridCorrV0 << " scaleCorrUtoGrid " << info.scaleCorrUtoGrid << " scaleCorrVtoGrid " << info.scaleCorrVtoGrid << " gridU0 " << info.gridU0 << " scaleUtoGrid " << info.scaleUtoGrid << " scaleVtoGrid " << info.scaleVtoGrid @@ -189,7 +201,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", // the difference double maxDiff[3] = {0., 0., 0.}; - int32_t maxDiffRoc[3] = {0, 0, 0}; + int32_t maxDiffSector[3] = {0, 0, 0}; int32_t maxDiffRow[3] = {0, 0, 0}; double sumDiff[3] = {0., 0., 0.}; @@ -207,7 +219,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", // measured x,y,z; corrections cx,cy,cz from the measured to the real x,y,z; // inverse corrections ix,iy,iz at the real position (x+cx,y+cy,z+cz) // ideally, ix = cx, iy = cy, iz = cz - TNtuple* debugCorr = new TNtuple("corr", "corr", "iRoc:iRow:x:y:z:cx:cy:cz:ix:iy:iz"); + TNtuple* debugCorr = new TNtuple("corr", "corr", "iSector:iRow:x:y:z:cx:cy:cz:ix:iy:iz"); debugCorr->SetMarkerStyle(8); debugCorr->SetMarkerSize(0.1); @@ -216,7 +228,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", // ntuple with the input data: voxels and corrections debugFile->cd(); TNtuple* debugVox = - new TNtuple("vox", "vox", "iRoc:iRow:n:x:y:z:vx:vy:vz"); + new TNtuple("vox", "vox", "iSector:iRow:n:x:y:z:vx:vy:vz"); debugVox->SetMarkerStyle(8); debugVox->SetMarkerSize(0.8); @@ -225,7 +237,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", // duplicate of debugVox + the spline data at voxels in a different color debugFile->cd(); TNtuple* debugCorrVox = - new TNtuple("corrvox", "corrvox", "iRoc:iRow:n:x:y:z:vx:vy:vz:cx:cy:cz:ix:iy:iz"); + new TNtuple("corrvox", "corrvox", "iSector:iRow:n:x:y:z:vx:vy:vz:cx:cy:cz:ix:iy:iz"); debugCorrVox->SetMarkerStyle(8); debugCorrVox->SetMarkerSize(0.8); @@ -233,7 +245,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", // corrections at the spline grid points debugFile->cd(); - TNtuple* debugGrid = new TNtuple("grid", "grid", "iRoc:iRow:x:y:z:cx:cy:cz:ix:iy:iz"); + TNtuple* debugGrid = new TNtuple("grid", "grid", "iSector:iRow:x:y:z:cx:cy:cz:ix:iy:iz"); debugGrid->SetMarkerStyle(8); debugGrid->SetMarkerSize(1.2); @@ -242,7 +254,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", // ntuple with data points created from voxels (with the data smearing, extension to the edges etc.) debugFile->cd(); TNtuple* debugPoints = - new TNtuple("points", "points", "iRoc:iRow:x:y:z:px:py:pz:cx:cy:cz"); + new TNtuple("points", "points", "iSector:iRow:x:y:z:px:py:pz:cx:cy:cz"); debugPoints->SetMarkerStyle(8); debugPoints->SetMarkerSize(0.4); @@ -256,32 +268,13 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const o2::gpu::TPCFastTransformGeo& geo = helper->getGeometry(); - auto getAllCorrections = [&](int iRoc, int iRow, float u, float v, float& x, float& y, float& z, float& cx, float& cy, float& cz, float& ix, float& iy, float& iz) { - // define x,y,z - - x = geo.getRowInfo(iRow).x; - geo.convUVtoLocal(iRoc, u, v, y, z); - + auto getAllCorrections = [&](int iSector, int iRow, float y, float z, float& cx, float& cy, float& cz, float& ix, float& iy, float& iz) { // get the corrections cx,cy,cz at x,y,z - float cu, cv; - corr.getCorrection(iRoc, iRow, u, v, cx, cu, cv); - geo.convUVtoLocal(iRoc, cu, cv, cy, cz); - - float corrected_u = u + cu; - float corrected_v = v + cv; - float corrected_x = x + cx; - float corrected_y, corrected_z; - geo.convUVtoLocal(iRoc, corrected_u, corrected_v, corrected_y, corrected_z); - - // get the inverse corrections ix,iy,iz at the corrected x,y,z - float inverted_x, inverted_u, inverted_v, inverted_y, inverted_z; - corr.getCorrectionInvCorrectedX(iRoc, iRow, corrected_u, corrected_v, inverted_x); - corr.getCorrectionInvUV(iRoc, iRow, corrected_u, corrected_v, inverted_u, inverted_v); - geo.convUVtoLocal(iRoc, inverted_u, inverted_v, inverted_y, inverted_z); - - ix = corrected_x - inverted_x; - iy = corrected_y - inverted_y; - iz = corrected_z - inverted_z; + std::tie(cx, cy, cz) = corr.getCorrectionLocal(iSector, iRow, y, z); + float realY = y + cy; + float realZ = z + cz; + ix = corr.getCorrectionXatRealYZ(iSector, iRow, realY, realZ); + std::tie(iy, iz) = corr.getCorrectionYZatRealYZ(iSector, iRow, realY, realZ); }; o2::tpc::TrackResiduals::VoxRes* v = nullptr; @@ -289,7 +282,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", branch->SetAddress(&v); branch->SetAutoDelete(kTRUE); - int32_t iRocLast = -1; + int32_t iSectorLast = -1; int32_t iRowLast = -1; std::cout << "fill debug ntuples at voxels ..." << std::endl; @@ -309,10 +302,10 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", int32_t z2xBin = v->bvox[o2::tpc::TrackResiduals::VoxZ]; // bin number in z/x 0..4 - int32_t iRoc = (int32_t)v->bsec; + int32_t iSector = (int32_t)v->bsec; int32_t iRow = (int32_t)xBin; - iRocLast = iRoc; + iSectorLast = iSector; iRowLast = iRow; double x = trackResiduals.getX(xBin); // radius of the pad row @@ -326,7 +319,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", double y = x * y2x; double z = x * z2x; - if (iRoc >= geo.getNumberOfSlicesA()) { + if (iSector >= geo.getNumberOfSectorsA()) { z = -z; } @@ -345,19 +338,18 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", z = x * v->stat[o2::tpc::TrackResiduals::VoxZ]; } - float u, v; - geo.convLocalToUV(iRoc, y, z, u, v); - float x1, y1, z1, cx, cy, cz, ix, iy, iz; - getAllCorrections(iRoc, iRow, u, v, x1, y1, z1, cx, cy, cz, ix, iy, iz); + float cx, cy, cz, ix, iy, iz; + getAllCorrections(iSector, iRow, y, z, cx, cy, cz, ix, iy, iz); - double d[3] = {cx - correctionX, cy - correctionY, cz - correctionZ}; if (voxEntries >= 1.) { + double d[3] = {cx - correctionX, cy - correctionY, cz - correctionZ}; + for (int32_t i = 0; i < 3; i++) { if (fabs(maxDiff[i]) < fabs(d[i])) { maxDiff[i] = d[i]; - maxDiffRoc[i] = iRoc; + maxDiffSector[i] = iSector; maxDiffRow[i] = iRow; - // std::cout << " roc " << iRoc << " row " << iRow << " xyz " << i + // std::cout << " sector " << iSector << " row " << iRow << " xyz " << i // << " diff " << d[i] << " entries " << voxEntries << " y " << y2xBin << " z " << z2xBin << std::endl; } sumDiff[i] += d[i] * d[i]; @@ -365,80 +357,81 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", nDiff++; } - debugVox->Fill(iRoc, iRow, voxEntries, x, y, z, correctionX, correctionY, correctionZ); + debugVox->Fill(iSector, iRow, voxEntries, x, y, z, correctionX, correctionY, correctionZ); - debugCorrVox->Fill(iRoc, iRow, voxEntries, x, y, z, correctionX, correctionY, correctionZ, + debugCorrVox->Fill(iSector, iRow, voxEntries, x, y, z, correctionX, correctionY, correctionZ, cx, cy, cz, ix, iy, iz); } std::cout << "fill debug ntuples everywhere .." << std::endl; - for (int32_t iRoc = 0; iRoc < geo.getNumberOfSlices(); iRoc++) { - // for (int32_t iRoc = 0; iRoc < 1; iRoc++) { - std::cout << "debug ntules for roc " << iRoc << std::endl; + for (int32_t iSector = 0; iSector < geo.getNumberOfSectors(); iSector++) { + // for (int32_t iSector = 0; iSector < 1; iSector++) { + std::cout << "debug ntules for sector " << iSector << std::endl; for (int32_t iRow = 0; iRow < geo.getNumberOfRows(); iRow++) { double x = geo.getRowInfo(iRow).x; // the spline grid - const auto& gridU = corr.getSpline(iRoc, iRow).getGridX1(); - const auto& gridV = corr.getSpline(iRoc, iRow).getGridX2(); - if (iRoc == 0 && iRow == 0) { + const auto& gridY = corr.getSpline(iSector, iRow).getGridX1(); + const auto& gridZ = corr.getSpline(iSector, iRow).getGridX2(); + if (iSector == 0 && iRow == 0) { std::cout << "spline scenario " << corr.getRowInfo(iRow).splineScenarioID << std::endl; - std::cout << "spline grid U: u = " << 0 << ".." << gridU.getUmax() << ", x = " << gridU.getXmin() << ".." << gridU.getXmax() << std::endl; - std::cout << "spline grid V: u = " << 0 << ".." << gridV.getUmax() << ", x = " << gridV.getXmin() << ".." << gridV.getXmax() << std::endl; + std::cout << "spline grid Y: u = " << 0 << ".." << gridY.getUmax() << ", x = " << gridY.getXmin() << ".." << gridY.getXmax() << std::endl; + std::cout << "spline grid Z: u = " << 0 << ".." << gridZ.getUmax() << ", x = " << gridZ.getXmin() << ".." << gridZ.getXmax() << std::endl; } // the correction { - std::vector p[2], g[2]; - - p[0].push_back(geo.getRowInfo(iRow).getUmin()); - for (int32_t iu = 0; iu < gridU.getNumberOfKnots(); iu++) { - float u, v; - corr.convGridToUV(iRoc, iRow, gridU.getKnot(iu).getU(), 0., u, v); - g[0].push_back(u); - p[0].push_back(u); + std::vector points[2], knots[2]; + + auto [yMin, yMax] = geo.getRowInfo(iRow).getYrange(); + auto [zMin, zMax] = geo.getZrange(iSector); + + points[0].push_back(yMin); + points[0].push_back(yMax); + points[1].push_back(zMin); + points[1].push_back(zMax); + + for (int32_t iu = 0; iu < gridY.getNumberOfKnots(); iu++) { + auto [y, z] = corr.convGridToLocal(iSector, iRow, gridY.getKnot(iu).getU(), 0.); + knots[0].push_back(y); + points[0].push_back(y); } - p[0].push_back(geo.getRowInfo(iRow).getUmax()); - - p[1].push_back(0.); - for (int32_t iv = 0; iv < gridV.getNumberOfKnots(); iv++) { - float u, v; - corr.convGridToUV(iRoc, iRow, 0., gridV.getKnot(iv).getU(), u, v); - g[1].push_back(v); - p[1].push_back(v); + for (int32_t iv = 0; iv < gridZ.getNumberOfKnots(); iv++) { + auto [y, z] = corr.convGridToLocal(iSector, iRow, 0., gridZ.getKnot(iv).getU()); + knots[1].push_back(z); + points[1].push_back(z); } - p[1].push_back(geo.getTPCzLength(iRoc)); - for (int32_t iuv = 0; iuv < 2; iuv++) { - int32_t n = p[iuv].size(); + for (int32_t iyz = 0; iyz <= 1; iyz++) { + std::sort(knots[iyz].begin(), knots[iyz].end()); + std::sort(points[iyz].begin(), points[iyz].end()); + int32_t n = points[iyz].size(); for (int32_t i = 0; i < n - 1; i++) { - double d = (p[iuv][i + 1] - p[iuv][i]) / 10.; + double d = (points[iyz][i + 1] - points[iyz][i]) / 10.; for (int32_t ii = 1; ii < 10; ii++) { - p[iuv].push_back(p[iuv][i] + d * ii); + points[iyz].push_back(points[iyz][i] + d * ii); } } - std::sort(p[iuv].begin(), p[iuv].end()); + std::sort(points[iyz].begin(), points[iyz].end()); } for (int32_t iter = 0; iter < 2; iter++) { - std::vector& pu = ((iter == 0) ? g[0] : p[0]); - std::vector& pv = ((iter == 0) ? g[1] : p[1]); - for (uint32_t iu = 0; iu < pu.size(); iu++) { - for (uint32_t iv = 0; iv < pv.size(); iv++) { - float u = pu[iu]; - float v = pv[iv]; - - float x, y, z, cx, cy, cz, ix, iy, iz; - getAllCorrections(iRoc, iRow, u, v, x, y, z, cx, cy, cz, ix, iy, iz); - + std::vector& py = ((iter == 0) ? knots[0] : points[0]); + std::vector& pz = ((iter == 0) ? knots[1] : points[1]); + for (uint32_t iu = 0; iu < py.size(); iu++) { + for (uint32_t iv = 0; iv < pz.size(); iv++) { + float y = py[iu]; + float z = pz[iv]; + float cx, cy, cz, ix, iy, iz; + getAllCorrections(iSector, iRow, y, z, cx, cy, cz, ix, iy, iz); if (iter == 0) { - debugGrid->Fill(iRoc, iRow, x, y, z, cx, cy, cz, ix, iy, iz); + debugGrid->Fill(iSector, iRow, x, y, z, cx, cy, cz, ix, iy, iz); } else { - debugCorr->Fill(iRoc, iRow, x, y, z, cx, cy, cz, ix, iy, iz); + debugCorr->Fill(iSector, iRow, x, y, z, cx, cy, cz, ix, iy, iz); } } } @@ -451,7 +444,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", o2::gpu::TPCFastSpaceChargeCorrectionMap& map = corrHelper->getCorrectionMap(); - auto& points = map.getPoints(iRoc, iRow); + auto& points = map.getPoints(iSector, iRow); for (uint32_t ip = 0; ip < points.size(); ip++) { auto point = points[ip]; @@ -461,14 +454,10 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", float correctionY = point.mDy; float correctionZ = point.mDz; - float u, v, cx, cu, cv, cy, cz; - geo.convLocalToUV(iRoc, y, z, u, v); - corr.getCorrection(iRoc, iRow, u, v, cx, cu, cv); - geo.convUVtoLocal(iRoc, u + cu, v + cv, cy, cz); - cy -= y; - cz -= z; + auto [cx, cy, cz] = + corr.getCorrectionLocal(iSector, iRow, y, z); - debugPoints->Fill(iRoc, iRow, x, y, z, correctionX, correctionY, + debugPoints->Fill(iSector, iRow, x, y, z, correctionX, correctionY, correctionZ, cx, cy, cz); } } @@ -478,14 +467,14 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", sumDiff[i] = sqrt(sumDiff[i]) / nDiff; } - std::cout << "Max difference in x : " << maxDiff[0] << " at ROC " - << maxDiffRoc[0] << " row " << maxDiffRow[0] << std::endl; + std::cout << "Max difference in x : " << maxDiff[0] << " at Sector " + << maxDiffSector[0] << " row " << maxDiffRow[0] << std::endl; - std::cout << "Max difference in y : " << maxDiff[1] << " at ROC " - << maxDiffRoc[1] << " row " << maxDiffRow[1] << std::endl; + std::cout << "Max difference in y : " << maxDiff[1] << " at Sector " + << maxDiffSector[1] << " row " << maxDiffRow[1] << std::endl; - std::cout << "Max difference in z : " << maxDiff[2] << " at ROC " - << maxDiffRoc[2] << " row " << maxDiffRow[2] << std::endl; + std::cout << "Max difference in z : " << maxDiff[2] << " at Sector " + << maxDiffSector[2] << " row " << maxDiffRow[2] << std::endl; std::cout << "Mean difference in x,y,z : " << sumDiff[0] << " " << sumDiff[1] << " " << sumDiff[2] << std::endl; From 458532fa7e75ee72a887b2fbb892e22b1f6d320b Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Sun, 6 Apr 2025 18:43:55 +0000 Subject: [PATCH 091/285] TPC Splines: cleanup --- GPU/TPCFastTransformation/Spline1D.h | 2 +- GPU/TPCFastTransformation/Spline1DSpec.h | 58 ++++++++++--------- GPU/TPCFastTransformation/Spline2DHelper.cxx | 2 +- GPU/TPCFastTransformation/Spline2DSpec.h | 36 ++++++------ GPU/TPCFastTransformation/SplineHelper.cxx | 4 +- GPU/TPCFastTransformation/SplineSpec.h | 20 +++---- .../TPCFastSpaceChargeCorrection.h | 6 +- 7 files changed, 66 insertions(+), 62 deletions(-) diff --git a/GPU/TPCFastTransformation/Spline1D.h b/GPU/TPCFastTransformation/Spline1D.h index ccadaeed23b79..c7a4d927dec1d 100644 --- a/GPU/TPCFastTransformation/Spline1D.h +++ b/GPU/TPCFastTransformation/Spline1D.h @@ -77,7 +77,7 @@ namespace gpu /// One can store all F-dependent spline parameters outside of the spline object /// and provide them at each interpolation call. /// To do so, create a spline with nYdimensions=0; create spline parameters for F via Spline1DHelper class; -/// then use special interpolateU(..) methods for the interpolation. +/// then use special interpolateAtU(..) methods for the interpolation. /// /// This feature allows one to use the same spline object for the approximation of different functions /// on the same grid of knots. diff --git a/GPU/TPCFastTransformation/Spline1DSpec.h b/GPU/TPCFastTransformation/Spline1DSpec.h index 2cc95ebdcab9f..fcabbbad12098 100644 --- a/GPU/TPCFastTransformation/Spline1DSpec.h +++ b/GPU/TPCFastTransformation/Spline1DSpec.h @@ -287,33 +287,38 @@ class Spline1DSpec : public Spline1DContainer /// Get interpolated value S(x) GPUd() void interpolate(DataT x, GPUgeneric() DataT S[/*mYdim*/]) const { - interpolateU(mYdim, mParameters, convXtoU(x), S); + interpolateAtU(mYdim, mParameters, convXtoU(x), S); } /// Get interpolated value for an nYdim-dimensional S(u) using spline parameters Parameters. template - GPUd() void interpolateU(int32_t inpYdim, GPUgeneric() const DataT Parameters[], - DataT u, GPUgeneric() DataT S[/*nYdim*/]) const + GPUd() void interpolateAtU(int32_t inpYdim, GPUgeneric() const DataT Parameters[], + DataT u, GPUgeneric() DataT S[/*nYdim*/]) const { const auto nYdimTmp = SplineUtil::getNdim(inpYdim); const auto nYdim = nYdimTmp.get(); int32_t iknot = TBase::template getLeftKnotIndexForU(u); const DataT* d = Parameters + (2 * nYdim) * iknot; - interpolateU(nYdim, getKnots()[iknot], &(d[0]), &(d[nYdim]), &(d[2 * nYdim]), &(d[3 * nYdim]), u, S); + interpolateAtU(nYdim, getKnots()[iknot], &(d[0]), &(d[nYdim]), &(d[2 * nYdim]), &(d[3 * nYdim]), u, S); } /// The main mathematical utility. /// Get interpolated value {S(u): 1D -> nYdim} at the segment [knotL, next knotR] /// using the spline values Sl, Sr and the slopes Dl, Dr template - GPUd() void interpolateU(int32_t inpYdim, const Knot& knotL, - GPUgeneric() const T Sl[/*mYdim*/], GPUgeneric() const T Dl[/*mYdim*/], - GPUgeneric() const T Sr[/*mYdim*/], GPUgeneric() const T Dr[/*mYdim*/], - DataT u, GPUgeneric() T S[/*mYdim*/]) const + GPUd() void interpolateAtU(int32_t inpYdim, const Knot& knotL, + GPUgeneric() const T Sl[/*mYdim*/], GPUgeneric() const T Dl[/*mYdim*/], + GPUgeneric() const T Sr[/*mYdim*/], GPUgeneric() const T Dr[/*mYdim*/], + DataT u, GPUgeneric() T S[/*mYdim*/]) const { const auto nYdimTmp = SplineUtil::getNdim(inpYdim); const auto nYdim = nYdimTmp.get(); + auto [dSdSl, dSdDl, dSdSr, dSdDr] = getSderivativesOverParsAtU(knotL, u); + for (int32_t dim = 0; dim < nYdim; ++dim) { + S[dim] = dSdSr * Sr[dim] + dSdSl * Sl[dim] + dSdDl * Dl[dim] + dSdDr * Dr[dim]; + } + /* if (u < (DataT)0) { u = (DataT)0; } @@ -330,6 +335,7 @@ class Spline1DSpec : public Spline1DContainer T b = df - Dl[dim] - a; S[dim] = ((a * v + b) * v + Dl[dim]) * uu + Sl[dim]; } + */ /* another way to calculate f(u): T uu = T(u - knotL.u); @@ -345,11 +351,10 @@ class Spline1DSpec : public Spline1DContainer } template - GPUd() void getUderivatives(const Knot& knotL, DataT u, - T& dSl, T& dDl, T& dSr, T& dDr) const + GPUd() std::tuple getSderivativesOverParsAtU(const Knot& knotL, DataT u) const { /// Get derivatives of the interpolated value {S(u): 1D -> nYdim} at the segment [knotL, next knotR] - /// over the spline values Sl, Sr and the slopes Dl, Dr + /// over the spline parameters Sl(eft), Sr(ight) and the slopes Dl, Dr if (u < (DataT)0) { u = (DataT)0; @@ -363,11 +368,12 @@ class Spline1DSpec : public Spline1DContainer T vm1 = v - 1.; T a = u * vm1; T v2 = v * v; - dSr = v2 * (3. - 2 * v); - dSl = 1. - dSr; - dDl = vm1 * a; - dDr = v * a; - // F(u) = dSl * Sl + dSr * Sr + dDl * Dl + dDr * Dr; + T dSdSr = v2 * (3. - 2 * v); + T dSdSl = 1. - dSdSr; + T dSdDl = vm1 * a; + T dSdDr = v * a; + // S(u) = dSdSl * Sl + dSdSr * Sr + dSdDl * Dl + dSdDr * Dr; + return std::make_tuple(dSdSl, dSdDl, dSdSr, dSdDr); } /* template @@ -480,21 +486,21 @@ class Spline1DSpec /// Get interpolated value for an YdimT-dimensional S(u) using spline parameters Parameters. template - GPUd() void interpolateU(GPUgeneric() const DataT Parameters[], - DataT u, GPUgeneric() DataT S[/*nYdim*/]) const + GPUd() void interpolateAtU(GPUgeneric() const DataT Parameters[], + DataT u, GPUgeneric() DataT S[/*nYdim*/]) const { - TBase::template interpolateU(YdimT, Parameters, u, S); + TBase::template interpolateAtU(YdimT, Parameters, u, S); } /// Get interpolated value for an YdimT-dimensional S(u) at the segment [knotL, next knotR] /// using the spline values Sl, Sr and the slopes Dl, Dr template - GPUd() void interpolateU(const typename TBase::Knot& knotL, - GPUgeneric() const T Sl[/*mYdim*/], GPUgeneric() const T Dl[/*mYdim*/], - GPUgeneric() const T Sr[/*mYdim*/], GPUgeneric() const T Dr[/*mYdim*/], - DataT u, GPUgeneric() T S[/*mYdim*/]) const + GPUd() void interpolateAtU(const typename TBase::Knot& knotL, + GPUgeneric() const T Sl[/*mYdim*/], GPUgeneric() const T Dl[/*mYdim*/], + GPUgeneric() const T Sr[/*mYdim*/], GPUgeneric() const T Dr[/*mYdim*/], + DataT u, GPUgeneric() T S[/*mYdim*/]) const { - TBase::interpolateU(YdimT, knotL, Sl, Dl, Sr, Dr, u, S); + TBase::interpolateAtU(YdimT, knotL, Sl, Dl, Sr, Dr, u, S); } using TBase::getNumberOfKnots; @@ -504,7 +510,7 @@ class Spline1DSpec #if !defined(GPUCA_GPUCODE) using TBase::recreate; #endif - using TBase::interpolateU; + using TBase::interpolateAtU; }; /// ================================================================================================== @@ -552,7 +558,7 @@ class Spline1DSpec /// _______ Expert tools: interpolation with given nYdim and external Parameters _______ - using TBase::interpolateU; + using TBase::interpolateAtU; ClassDefNV(Spline1DSpec, 0); }; diff --git a/GPU/TPCFastTransformation/Spline2DHelper.cxx b/GPU/TPCFastTransformation/Spline2DHelper.cxx index 03ecf4a3f1707..bec6fc58ff8d7 100644 --- a/GPU/TPCFastTransformation/Spline2DHelper.cxx +++ b/GPU/TPCFastTransformation/Spline2DHelper.cxx @@ -194,7 +194,7 @@ void Spline2DHelper::approximateFunction( for (int32_t ipu = 0; ipu < nDataPointsU; ipu++) { double splineF[Ndim]; double u = mHelperU1.getDataPoint(ipu).u; - mHelperU1.getSpline().interpolateU(Ndim, parUdbl.get(), u, splineF); + mHelperU1.getSpline().interpolateAtU(Ndim, parUdbl.get(), u, splineF); for (int32_t dim = 0; dim < Ndim; dim++) { rotDataPointF[(ipu * nDataPointsV + ipv) * Ndim + dim] = splineF[dim]; } diff --git a/GPU/TPCFastTransformation/Spline2DSpec.h b/GPU/TPCFastTransformation/Spline2DSpec.h index b4d351e8d0407..d235b7ddfde07 100644 --- a/GPU/TPCFastTransformation/Spline2DSpec.h +++ b/GPU/TPCFastTransformation/Spline2DSpec.h @@ -239,13 +239,13 @@ class Spline2DSpec /// Get interpolated value S(x) GPUd() void interpolate(DataT x1, DataT x2, GPUgeneric() DataT S[/*mYdim*/]) const { - interpolateU(mYdim, mParameters, mGridX1.convXtoU(x1), mGridX2.convXtoU(x2), S); + interpolateAtU(mYdim, mParameters, mGridX1.convXtoU(x1), mGridX2.convXtoU(x2), S); } /// Get interpolated value for an inpYdim-dimensional S(u1,u2) using spline parameters Parameters. template - GPUd() void interpolateUold(int32_t inpYdim, GPUgeneric() const DataT Parameters[], - DataT u1, DataT u2, GPUgeneric() DataT S[/*inpYdim*/]) const + GPUd() void interpolateAtUold(int32_t inpYdim, GPUgeneric() const DataT Parameters[], + DataT u1, DataT u2, GPUgeneric() DataT S[/*inpYdim*/]) const { const auto nYdimTmp = SplineUtil::getNdim(inpYdim); @@ -295,7 +295,7 @@ class Spline2DSpec typedef Spline1DSpec TGridX1; const TGridX1& gridX1 = reinterpret_cast(mGridX1); - gridX1.interpolateU(nYdim4, knotU, Su0, Du0, Su1, Du1, u, parU); + gridX1.interpolateAtU(nYdim4, knotU, Su0, Du0, Su1, Du1, u, parU); const DataT* Sv0 = parU + 0; const DataT* Dv0 = parU + nYdim; @@ -304,13 +304,13 @@ class Spline2DSpec typedef Spline1DSpec TGridX2; const TGridX2& gridX2 = reinterpret_cast(mGridX2); - gridX2.interpolateU(nYdim, knotV, Sv0, Dv0, Sv1, Dv1, v, S); + gridX2.interpolateAtU(nYdim, knotV, Sv0, Dv0, Sv1, Dv1, v, S); } /// Get interpolated value for an inpYdim-dimensional S(u1,u2) using spline parameters Parameters. template - GPUd() void interpolateU(int32_t inpYdim, GPUgeneric() const DataT Parameters[], - DataT u1, DataT u2, GPUgeneric() DataT S[/*inpYdim*/]) const + GPUd() void interpolateAtU(int32_t inpYdim, GPUgeneric() const DataT Parameters[], + DataT u1, DataT u2, GPUgeneric() DataT S[/*inpYdim*/]) const { const auto nYdimTmp = SplineUtil::getNdim(inpYdim); @@ -334,10 +334,8 @@ class Spline2DSpec const DataT* A = Parameters + (nu * iv + iu) * nYdim4; // values { {Y1,Y2,Y3}, {Y1,Y2,Y3}'v, {Y1,Y2,Y3}'u, {Y1,Y2,Y3}''vu } at {u0, v0} const DataT* B = A + nYdim4 * nu; // values { ... } at {u0, v1} - DataT dSl, dDl, dSr, dDr; - mGridX1.getUderivatives(knotU, u, dSl, dDl, dSr, dDr); - DataT dSd, dDd, dSu, dDu; - mGridX2.getUderivatives(knotV, v, dSd, dDd, dSu, dDu); + auto [dSl, dDl, dSr, dDr] = mGridX1.template getSderivativesOverParsAtU(knotU, u); + auto [dSd, dDd, dSu, dDu] = mGridX2.template getSderivativesOverParsAtU(knotV, v); // when nYdim == 1: // S = dSl * (dSd * A[0] + dDd * A[1]) + dDl * (dSd * A[2] + dDd * A[3]) + @@ -430,18 +428,18 @@ class Spline2DSpec /// Get interpolated value for an YdimT-dimensional S(u1,u2) using spline parameters Parameters. template - GPUd() void interpolateU(GPUgeneric() const DataT Parameters[], - DataT u1, DataT u2, GPUgeneric() DataT S[/*nYdim*/]) const + GPUd() void interpolateAtU(GPUgeneric() const DataT Parameters[], + DataT u1, DataT u2, GPUgeneric() DataT S[/*nYdim*/]) const { - TBase::template interpolateU(YdimT, Parameters, u1, u2, S); + TBase::template interpolateAtU(YdimT, Parameters, u1, u2, S); } /// Get interpolated value for an YdimT-dimensional S(u1,u2) using spline parameters Parameters. template - GPUd() void interpolateUold(GPUgeneric() const DataT Parameters[], - DataT u1, DataT u2, GPUgeneric() DataT S[/*nYdim*/]) const + GPUd() void interpolateAtUold(GPUgeneric() const DataT Parameters[], + DataT u1, DataT u2, GPUgeneric() DataT S[/*nYdim*/]) const { - TBase::template interpolateUold(YdimT, Parameters, u1, u2, S); + TBase::template interpolateAtUold(YdimT, Parameters, u1, u2, S); } using TBase::getNumberOfKnots; @@ -451,7 +449,7 @@ class Spline2DSpec #if !defined(GPUCA_GPUCODE) using TBase::recreate; #endif - using TBase::interpolateU; + using TBase::interpolateAtU; }; /// ================================================================================================== @@ -507,7 +505,7 @@ class Spline2DSpec /// _______ Expert tools: interpolation with given nYdim and external Parameters _______ - using TBase::interpolateU; + using TBase::interpolateAtU; }; /// ================================================================================================== diff --git a/GPU/TPCFastTransformation/SplineHelper.cxx b/GPU/TPCFastTransformation/SplineHelper.cxx index 6e1b53510e0d0..af3efb1c4817d 100644 --- a/GPU/TPCFastTransformation/SplineHelper.cxx +++ b/GPU/TPCFastTransformation/SplineHelper.cxx @@ -410,8 +410,8 @@ void SplineHelper::approximateFunction( } double splineF[mFdimensions]; double u = mHelpers[dimension].getDataPoint(i).u; - mHelpers[dimension].getSpline().interpolateU(mFdimensions, parD[dimension].get(), u, splineF); // recalculate at all datapoints of dimension - for (int32_t dim = 0; dim < mFdimensions; dim++) { // writing it in allParameters + mHelpers[dimension].getSpline().interpolateAtU(mFdimensions, parD[dimension].get(), u, splineF); // recalculate at all datapoints of dimension + for (int32_t dim = 0; dim < mFdimensions; dim++) { // writing it in allParameters // LOG(info)< : public SplineContainer for (int32_t i = 0; i < nXdim; i++) { u[i] = mGrid[i].convXtoU(x[i]); } - interpolateU(mXdim, mYdim, mParameters, u, S); + interpolateAtU(mXdim, mYdim, mParameters, u, S); } /// Get interpolated value for S(u):inpXdim->inpYdim using spline parameters Parameters template - GPUd() void interpolateU(int32_t inpXdim, int32_t inpYdim, GPUgeneric() const DataT Parameters[], - const DataT u[/*inpXdim*/], GPUgeneric() DataT S[/*inpYdim*/]) const + GPUd() void interpolateAtU(int32_t inpXdim, int32_t inpYdim, GPUgeneric() const DataT Parameters[], + const DataT u[/*inpXdim*/], GPUgeneric() DataT S[/*inpYdim*/]) const { const auto nXdimTmp = SplineUtil::getNdim(mXdim); const auto nXdim = nXdimTmp.get(); @@ -345,7 +345,7 @@ class SplineSpec : public SplineContainer DataT coordinate = u[d]; typedef Spline1DSpec TGridX; const TGridX& gridX = *((const TGridX*)&(mGrid[d])); - gridX.interpolateU(nInterpolations, knotL, S0, D0, S1, D1, coordinate, iParameters); + gridX.interpolateAtU(nInterpolations, knotL, S0, D0, S1, D1, coordinate, iParameters); nInterpolations /= 4; nKnots /= 2; } // end d (every dimension) @@ -354,7 +354,7 @@ class SplineSpec : public SplineContainer S[i] = iParameters[i]; // write into result-array // LOG(info)< /// Get interpolated value for an YdimT-dimensional S(u1,u2) using spline parameters Parameters. template - GPUd() void interpolateU(GPUgeneric() const DataT Parameters[], - const DataT u[/*XdimT*/], GPUgeneric() DataT S[/*YdimT*/]) const + GPUd() void interpolateAtU(GPUgeneric() const DataT Parameters[], + const DataT u[/*XdimT*/], GPUgeneric() DataT S[/*YdimT*/]) const { - TBase::template interpolateU(XdimT, YdimT, Parameters, u, S); + TBase::template interpolateAtU(XdimT, YdimT, Parameters, u, S); } /// _______________ Suppress some parent class methods ________________________ @@ -432,7 +432,7 @@ class SplineSpec #if !defined(GPUCA_GPUCODE) using TBase::recreate; #endif - using TBase::interpolateU; + using TBase::interpolateAtU; }; /// ================================================================================================== @@ -490,7 +490,7 @@ class SplineSpec /// _______ Expert tools: interpolation with given nYdim and external Parameters _______ - using TBase::interpolateU; + using TBase::interpolateAtU; /// Check dimensions void checkDimensions(int32_t& nXdim, int32_t& nYdim) diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index f84fde4fffd8c..76368efcd8a4f 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -410,7 +410,7 @@ GPUdi() std::tuple TPCFastSpaceChargeCorrection::getCorrect auto [gridU, gridV, scale] = convLocalToGrid(sector, row, y, z); float dxyz[3]; - spline.interpolateU(splineData, gridU, gridV, dxyz); + spline.interpolateAtU(splineData, gridU, gridV, dxyz); float dx = scale * GPUCommonMath::Clamp(dxyz[0], info.minCorr[0], info.maxCorr[0]); float dy = scale * GPUCommonMath::Clamp(dxyz[1], info.minCorr[1], info.maxCorr[1]); @@ -427,7 +427,7 @@ GPUdi() float TPCFastSpaceChargeCorrection::getCorrectionXatRealYZ(int32_t secto auto [gridU, gridV, scale] = convCorrectedLocalToGrid(sector, row, realY, realZ); float dx = 0; - spline.interpolateU(splineData, gridU, gridV, &dx); + spline.interpolateAtU(splineData, gridU, gridV, &dx); dx = scale * GPUCommonMath::Clamp(dx, info.minCorr[0], info.maxCorr[0]); return dx; @@ -443,7 +443,7 @@ GPUdi() std::tuple TPCFastSpaceChargeCorrection::getCorrectionYZat const float* splineData = getSplineData(sector, row, 2); float dyz[2]; - spline.interpolateU(splineData, gridU, gridV, dyz); + spline.interpolateAtU(splineData, gridU, gridV, dyz); dyz[0] = scale * GPUCommonMath::Clamp(dyz[0], info.minCorr[1], info.maxCorr[1]); dyz[1] = scale * GPUCommonMath::Clamp(dyz[1], info.minCorr[2], info.maxCorr[2]); From 47283cd6ac5df3046a2324387b9e776ec54ae367 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Fri, 11 Apr 2025 15:06:35 +0000 Subject: [PATCH 092/285] TPC Splines: fast merge of SC corrections --- .../TPCFastSpaceChargeCorrectionHelper.h | 10 +- .../TPCFastSpaceChargeCorrectionHelper.cxx | 163 ++++++++++++------ GPU/TPCFastTransformation/Spline1DSpec.h | 88 +++++----- GPU/TPCFastTransformation/Spline2DSpec.h | 135 ++++++++++++++- .../TPCFastSpaceChargeCorrection.h | 127 ++++++++++++-- 5 files changed, 401 insertions(+), 122 deletions(-) diff --git a/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h b/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h index abbc5b7116b2d..747ed74c9bcad 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h @@ -102,7 +102,15 @@ class TPCFastSpaceChargeCorrectionHelper /// initialise inverse transformation from linear combination of several input corrections void initInverse(std::vector& corrections, const std::vector& scaling, bool prn); - void MergeCorrections(std::vector& corrections, const std::vector& scaling, bool prn); + /// merge several corrections + /// \param mainCorrection main correction + /// \param scale scaling factor for the main correction + /// \param additionalCorrections vector of pairs of additional corrections and their scaling factors + /// \param prn printout flag + /// \return main correction merged with additional corrections + void MergeCorrections( + o2::gpu::TPCFastSpaceChargeCorrection& mainCorrection, float scale, + const std::vector>& additionalCorrections, bool prn); private: /// geometry initialization diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index 92817063831f6..6ba3d6e12dd9e 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -176,8 +176,8 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas } if (processingInverseCorrection) { - float* splineX = correction.getSplineData(sector, row, 1); - float* splineYZ = correction.getSplineData(sector, row, 2); + float* splineX = correction.getSplineDataInvX(sector, row); + float* splineYZ = correction.getSplineDataInvYZ(sector, row); for (int i = 0; i < spline.getNumberOfParameters() / 3; i++) { splineX[i] = splineParameters[3 * i + 0]; splineYZ[2 * i + 0] = splineParameters[3 * i + 1]; @@ -940,8 +940,8 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector& corrections, const std::vector& scaling, bool prn) +void TPCFastSpaceChargeCorrectionHelper::MergeCorrections( + o2::gpu::TPCFastSpaceChargeCorrection& mainCorrection, float mainScale, + const std::vector>& additionalCorrections, bool /*prn*/) { /// merge several corrections - /* + TStopwatch watch; LOG(info) << "fast space charge correction helper: Merge corrections"; - if (corrections.size() != scaling.size()) { - LOGP(error, "Input corrections and scaling values have different size"); - return; - } - - auto& correction = *(corrections.front()); + const auto& geo = mainCorrection.getGeometry(); - for (int sector = 0; sector < mGeo.getNumberOfSectors(); sector++) { + for (int sector = 0; sector < geo.getNumberOfSectors(); sector++) { auto myThread = [&](int iThread) { - for (int row = iThread; row < mGeo.getNumberOfRows(); row += mNthreads) { - TPCFastSpaceChargeCorrection::SplineType spline = correction.getSpline(sector, row); + for (int row = iThread; row < geo.getNumberOfRows(); row += mNthreads) { + const auto& spline = mainCorrection.getSpline(sector, row); - std::vector splineParameters(spline.getNumberOfParameters()); - std::vector splineParametersInvX(spline.getNumberOfParameters()); - std::vector splineParametersInvYZ(spline.getNumberOfParameters()); + float* splineParameters = mainCorrection.getSplineData(sector, row); + float* splineParametersInvX = mainCorrection.getSplineDataInvX(sector, row); + float* splineParametersInvYZ = mainCorrection.getSplineDataInvYZ(sector, row); - const auto& gridU = spline.getGridX1(); - const auto& gridV = spline.getGridX2(); - - for (int iu = 0; iu < gridU.getNumberOfKnots(); iu++) { - double u = gridU.getKnot(iu).u; - for (int iv = 0; iv < gridV.getNumberOfKnots(); iv++) { - int knotIndex = spline.getKnotIndex(iu, iv); + auto& secRowInfo = mainCorrection.getSectorRowInfo(sector, row); - double v = gridV.getKnot(iu).u; - auto [y, z] = correction.convGridToLocal(sector, row, u, v); - constexpr int nKnotPar1d = 4; - constexpr int nKnotPar2d = nKnotPar1d * 2; - constexpr int nKnotPar3d = nKnotPar1d * 3; + constexpr int nKnotPar1d = 4; + constexpr int nKnotPar2d = nKnotPar1d * 2; + constexpr int nKnotPar3d = nKnotPar1d * 3; - for (int i = 0; i < corrections.size(); ++i) { - double s = scaling[i]; - auto p = corrections[i]->getCorrectionParameters(sector, row, y, z); - for (int j = 0; j < nKnotPar3d; ++j) { - splineParameters[knotIndex * nKnotPar3d + j] += s * p[j]; + { // scale the main correction + for (int i = 0; i < 3; i++) { + secRowInfo.maxCorr[i] *= mainScale; + secRowInfo.minCorr[i] *= mainScale; + } + double parscale[4] = {mainScale, mainScale, mainScale, mainScale * mainScale}; + for (int iknot = 0, ind = 0; iknot < spline.getNumberOfKnots(); iknot++) { + for (int ipar = 0; ipar < nKnotPar1d; ++ipar) { + for (int idim = 0; idim < 3; idim++, ind++) { + splineParameters[ind] *= parscale[ipar]; } - auto pInvX = corrections[i]->getCorrectionParametersInvX(sector, row, y, z); - for (int j = 0; j < nKnotPar1d; ++j) { - splineParametersInvX[knotIndex * nKnotPar1d + j] += s * pInvX[j]; + } + } + for (int iknot = 0, ind = 0; iknot < spline.getNumberOfKnots(); iknot++) { + for (int ipar = 0; ipar < nKnotPar1d; ++ipar) { + for (int idim = 0; idim < 1; idim++, ind++) { + splineParametersInvX[ind] *= parscale[ipar]; } - auto pInvYZ = corrections[i]->getCorrectionParametersInvYZ(sector, row, y, z); - for (int j = 0; j < nKnotPar2d; ++j) { - splineParametersInvYZ[knotIndex * nKnotPar2d + j] += s * pInvYZ[j]; + } + } + for (int iknot = 0, ind = 0; iknot < spline.getNumberOfKnots(); iknot++) { + for (int ipar = 0; ipar < nKnotPar1d; ++ipar) { + for (int idim = 0; idim < 2; idim++, ind++) { + splineParametersInvYZ[ind] *= parscale[ipar]; } } - } // iv - } // iu + } + } - float* splineXYZ = correction.getSplineData(sector, row, 0); - float* splineInvX = correction.getSplineData(sector, row, 1); - float* splineInvYZ = correction.getSplineData(sector, row, 2); + // add the other corrections - for (int i = 0; i < spline.getNumberOfParameters(); i++) { - splineXYZ[i] = splineParameters[i]; - } - for (int i = 0; i < spline.getNumberOfParameters() / 3; i++) { - splineX[i] = splineParametersInvX[i]; - splineYZ[2 * i + 0] = splineParametersInvYZ[2 * i + 0]; - splineYZ[2 * i + 1] = splineParametersInvYZ[2 * i + 1]; - } + const auto& gridU = spline.getGridX1(); + const auto& gridV = spline.getGridX2(); + + for (int icorr = 0; icorr < additionalCorrections.size(); ++icorr) { + const auto& corr = *(additionalCorrections[icorr].first); + double scale = additionalCorrections[icorr].second; + auto& linfo = corr.getSectorRowInfo(sector, row); + secRowInfo.updateMaxValues(linfo.getMaxValues(), scale); + secRowInfo.updateMaxValues(linfo.getMinValues(), scale); + + double scaleU = secRowInfo.scaleUtoGrid / linfo.scaleUtoGrid; + double scaleV = secRowInfo.scaleVtoGrid / linfo.scaleVtoGrid; + + for (int iu = 0; iu < gridU.getNumberOfKnots(); iu++) { + double u = gridU.getKnot(iu).u; + for (int iv = 0; iv < gridV.getNumberOfKnots(); iv++) { + double v = gridV.getKnot(iu).u; + int knotIndex = spline.getKnotIndex(iu, iv); + float P[nKnotPar3d]; + + { // direct correction + auto [y, z] = mainCorrection.convGridToLocal(sector, row, u, v); + // return values: u, v, scaling factor + auto [lu, lv, ls] = corr.convLocalToGrid(sector, row, y, z); + ls *= scale; + double parscale[4] = {ls, ls * scaleU, ls * scaleV, ls * ls * scaleU * scaleV}; + const auto& spl = corr.getSpline(sector, row); + spl.interpolateParametersAtU(corr.getSplineData(sector, row), lu, lv, P); + for (int ipar = 0, ind = 0; ipar < nKnotPar1d; ++ipar) { + for (int idim = 0; idim < 3; idim++, ind++) { + splineParameters[knotIndex * nKnotPar3d + ind] += parscale[ipar] * P[ind]; + } + } + } + + auto [y, z] = mainCorrection.convGridToCorrectedLocal(sector, row, u, v); + // return values: u, v, scaling factor + auto [lu, lv, ls] = corr.convCorrectedLocalToGrid(sector, row, y, z); + ls *= scale; + double parscale[4] = {ls, ls * scaleU, ls * scaleV, ls * ls * scaleU * scaleV}; + + { // inverse X correction + corr.getSplineInvX(sector, row).interpolateParametersAtU(corr.getSplineDataInvX(sector, row), lu, lv, P); + for (int ipar = 0, ind = 0; ipar < nKnotPar1d; ++ipar) { + for (int idim = 0; idim < 1; idim++, ind++) { + splineParametersInvX[knotIndex * nKnotPar1d + ind] += parscale[ipar] * P[ind]; + } + } + } + + { // inverse YZ correction + corr.getSplineInvYZ(sector, row).interpolateParametersAtU(corr.getSplineDataInvYZ(sector, row), lu, lv, P); + for (int ipar = 0, ind = 0; ipar < nKnotPar1d; ++ipar) { + for (int idim = 0; idim < 2; idim++, ind++) { + splineParametersInvYZ[knotIndex * nKnotPar2d + ind] += parscale[ipar] * P[ind]; + } + } + } + + } // iv + } // iu + } // corrections } // row - }; // thread + }; // thread std::vector threads(mNthreads); @@ -1054,7 +1106,6 @@ void TPCFastSpaceChargeCorrectionHelper::MergeCorrections(std::vector : public Spline1DContainer for (int32_t dim = 0; dim < nYdim; ++dim) { S[dim] = dSdSr * Sr[dim] + dSdSl * Sl[dim] + dSdDl * Dl[dim] + dSdDr * Dr[dim]; } + /* + another way to calculate f(u): + if (u < (DataT)0) { u = (DataT)0; } @@ -336,18 +339,6 @@ class Spline1DSpec : public Spline1DContainer S[dim] = ((a * v + b) * v + Dl[dim]) * uu + Sl[dim]; } */ - /* - another way to calculate f(u): - T uu = T(u - knotL.u); - T v = uu * T(knotL.Li); // scaled u - T vm1 = v-1; - T v2 = v * v; - float cSr = v2*(3-2*v); - float cSl = 1-cSr; - float cDl = v*vm1*vm1*knotL.L; - float cDr = v2*vm1*knotL.L; - return cSl*Sl + cSr*Sr + cDl*Dl + cDr*Dr; - */ } template @@ -365,51 +356,50 @@ class Spline1DSpec : public Spline1DContainer u = u - knotL.u; T v = u * T(knotL.Li); // scaled u - T vm1 = v - 1.; + T vm1 = v - T(1.); T a = u * vm1; T v2 = v * v; - T dSdSr = v2 * (3. - 2 * v); - T dSdSl = 1. - dSdSr; + T dSdSr = v2 * (T(3.) - v - v); + T dSdSl = T(1.) - dSdSr; T dSdDl = vm1 * a; T dSdDr = v * a; // S(u) = dSdSl * Sl + dSdSr * Sr + dSdDl * Dl + dSdDr * Dr; return std::make_tuple(dSdSl, dSdDl, dSdSr, dSdDr); } - /* - template - GPUd() void getUsecondDerivatives(const Knot& knotL, DataT u, - T& dSl, T& dDl, T& dSr, T& dDr, - T& dSl2, T& dDl2, T& dSr2, T& dDr2) const - { - /// Get derivatives of the interpolated value {S(u): 1D -> nYdim} at the segment [knotL, next knotR] - /// over the spline values Sl, Sr and the slopes Dl, Dr - - if (u < (DataT)0) { - u = (DataT)0; - } - if (u > (DataT)TBase::getUmax()) { - u = (DataT)TBase::getUmax(); - } - - u = u - knotL.u; - T v = u * T(knotL.Li); // scaled u - T vm1 = v - 1.; - T a = u * vm1; - T v2 = v * v; - dSr = v2 * (3. - 2 * v); - dSl = 1. - dSr; - dDl = vm1 * a; - dDr = v * a; - T dv = T(knotL.Li); - dSr2 = 6. * v * (1. - v) * dv; - dSl2 = -dSr2; - dDl2 = (v - 1) * (3 * v - 1); - dDr = u * (v * v - v); - dDr2 = 3.f * v * v - 2.f * v; - // F(u) = dSl * Sl + dSr * Sr + dDl * Dl + dDr * Dr; - // dF(u)/du = dSl2 * Sl + dSr2 * Sr + dDl2 * Dl + dDr2 * Dr; + + template + GPUd() std::tuple getSDderivativesOverParsAtU(const Knot& knotL, DataT u) const + { + /// Get derivatives of the interpolated value {S(u): 1D -> nYdim} at the segment [knotL, next knotR] + /// over the spline values Sl, Sr and the slopes Dl, Dr + + if (u < (DataT)0) { + u = (DataT)0; } - */ + if (u > (DataT)TBase::getUmax()) { + u = (DataT)TBase::getUmax(); + } + + u = u - knotL.u; + T v = u * T(knotL.Li); // scaled u + T vm1 = v - T(1.); + T a = u * vm1; + T v2 = v * v; + T dSdSr = v2 * (T(3.) - v - v); + T dSdSl = T(1.) - dSdSr; + T dSdDl = vm1 * a; + T dSdDr = v * a; + + T dv = T(knotL.Li); + T dDdSr = 6. * v * (T(1.) - v) * dv; + T dDdSl = -dDdSr; + T dDdDl = vm1 * (v + v + vm1); + T dDdDr = v * (v + vm1 + vm1); + // S(u) = dSdSl * Sl + dSdSr * Sr + dSdDl * Dl + dSdDr * Dr; + // D(u) = dS(u)/du = dDdSl * Sl + dDdSr * Sr + dDdDl * Dl + dDdDr * Dr; + return std::make_tuple(dSdSl, dSdDl, dSdSr, dSdDr, dDdSl, dDdDl, dDdSr, dDdDr); + } + using TBase::convXtoU; using TBase::getKnot; using TBase::getKnots; diff --git a/GPU/TPCFastTransformation/Spline2DSpec.h b/GPU/TPCFastTransformation/Spline2DSpec.h index d235b7ddfde07..987ce1ad5d256 100644 --- a/GPU/TPCFastTransformation/Spline2DSpec.h +++ b/GPU/TPCFastTransformation/Spline2DSpec.h @@ -358,6 +358,132 @@ class Spline2DSpec } } + /// Get interpolated parameters (like parameters stored at knots) for an inpYdim-dimensional S(u1,u2) using spline parameters Parameters. + template + GPUd() void interpolateParametersAtU(int32_t inpYdim, GPUgeneric() const DataT Parameters[], + DataT u1, DataT u2, GPUgeneric() DataT P[/* 4*inpYdim */]) const + { + + const auto nYdimTmp = SplineUtil::getNdim(inpYdim); + const int32_t nYdim = nYdimTmp.get(); + + // const auto maxYdim = SplineUtil::getMaxNdim(inpYdim); + // const int32_t maxYdim4 = 4 * maxYdim.get(); + + // const auto nYdim2 = nYdim * 2; + const auto nYdim4 = nYdim * 4; + + DataT *S = P, + *R = P + nYdim, + *Q = P + nYdim * 2, + *W = P + nYdim * 3; + + const DataT& u = u1; + const DataT& v = u2; + int32_t nu = mGridX1.getNumberOfKnots(); + int32_t iu = mGridX1.template getLeftKnotIndexForU(u); + int32_t iv = mGridX2.template getLeftKnotIndexForU(v); + + const typename TBase::Knot& knotU = mGridX1.template getKnot(iu); + const typename TBase::Knot& knotV = mGridX2.template getKnot(iv); + + const DataT* A = Parameters + (nu * iv + iu) * nYdim4; // values { {Y1,Y2,Y3}, {Y1,Y2,Y3}'v, {Y1,Y2,Y3}'u, {Y1,Y2,Y3}''vu } at {u0, v0} + const DataT* B = A + nYdim4 * nu; // values { ... } at {u0, v1} + + auto [dSdSl, dSdDl, dSdSr, dSdDr, dRdSl, dRdDl, dRdSr, dRdDr] = mGridX1.template getSDderivativesOverParsAtU(knotU, u); + auto [dSdSd, dSdDd, dSdSu, dSdDu, dQdSd, dQdDd, dQdSu, dQdDu] = mGridX2.template getSDderivativesOverParsAtU(knotV, v); + + // when nYdim == 1: + + // Function value S + // S = dSdSl * (dSdSd * A[0] + dSdDd * A[1]) + dSdDl * (dSdSd * A[2] + dSdDd * A[3]) + + // dSdSr * (dSdSd * A[4] + dSdDd * A[5]) + dSdDr * (dSdSd * A[6] + dSdDd * A[7]) + + // dSdSl * (dSdSu * B[0] + dSdDu * B[1]) + dSdDl * (dSdSu * B[2] + dSdDu * B[3]) + + // dSdSr * (dSdSu * B[4] + dSdDu * B[5]) + dSdDr * (dSdSu * B[6] + dSdDu * B[7]); + + { + DataT a[8] = {dSdSl * dSdSd, dSdSl * dSdDd, dSdDl * dSdSd, dSdDl * dSdDd, + dSdSr * dSdSd, dSdSr * dSdDd, dSdDr * dSdSd, dSdDr * dSdDd}; + DataT b[8] = {dSdSl * dSdSu, dSdSl * dSdDu, dSdDl * dSdSu, dSdDl * dSdDu, + dSdSr * dSdSu, dSdSr * dSdDu, dSdDr * dSdSu, dSdDr * dSdDu}; + + // S = sum a[i]*A[i] + b[i]*B[i] + + for (int32_t dim = 0; dim < nYdim; dim++) { + S[dim] = 0; + for (int32_t i = 0; i < 8; i++) { + S[dim] += a[i] * A[nYdim * i + dim] + b[i] * B[nYdim * i + dim]; + } + } + } + + // Derivative R = dS / du + // R = dRdSl * (dSdSd * A[0] + dSdDd * A[1]) + dRdDl * (dSdSd * A[2] + dSdDd * A[3]) + + // dRdSr * (dSdSd * A[4] + dSdDd * A[5]) + dRdDr * (dSdSd * A[6] + dSdDd * A[7]) + + // dRdSl * (dSdSu * B[0] + dSdDu * B[1]) + dRdDl * (dSdSu * B[2] + dSdDu * B[3]) + + // dRdSr * (dSdSu * B[4] + dSdDu * B[5]) + dRdDr * (dSdSu * B[6] + dSdDu * B[7]); + + { + DataT a[8] = {dRdSl * dSdSd, dRdSl * dSdDd, dRdDl * dSdSd, dRdDl * dSdDd, + dRdSr * dSdSd, dRdSr * dSdDd, dRdDr * dSdSd, dRdDr * dSdDd}; + DataT b[8] = {dRdSl * dSdSu, dRdSl * dSdDu, dRdDl * dSdSu, dRdDl * dSdDu, + dRdSr * dSdSu, dRdSr * dSdDu, dRdDr * dSdSu, dRdDr * dSdDu}; + + // R = sum a[i]*A[i] + b[i]*B[i] + + for (int32_t dim = 0; dim < nYdim; dim++) { + R[dim] = 0; + for (int32_t i = 0; i < 8; i++) { + R[dim] += a[i] * A[nYdim * i + dim] + b[i] * B[nYdim * i + dim]; + } + } + } + + // Derivative Q = dS / dv + // Q = dSdSl * (dQdSd * A[0] + dQdDd * A[1]) + dSdDl * (dQdSd * A[2] + dQdDd * A[3]) + + // dSdSr * (dQdSd * A[4] + dQdDd * A[5]) + dSdDr * (dQdSd * A[6] + dQdDd * A[7]) + + // dSdSl * (dQdSu * B[0] + dQdDu * B[1]) + dSdDl * (dQdSu * B[2] + dQdDu * B[3]) + + // dSdSr * (dQdSu * B[4] + dQdDu * B[5]) + dSdDr * (dQdSu * B[6] + dQdDu * B[7]); + + { + DataT a[8] = {dSdSl * dQdSd, dSdSl * dQdDd, dSdDl * dQdSd, dSdDl * dQdDd, + dSdSr * dQdSd, dSdSr * dQdDd, dSdDr * dQdSd, dSdDr * dQdDd}; + DataT b[8] = {dSdSl * dQdSu, dSdSl * dQdDu, dSdDl * dQdSu, dSdDl * dQdDu, + dSdSr * dQdSu, dSdSr * dQdDu, dSdDr * dQdSu, dSdDr * dQdDu}; + + // Q = sum a[i]*A[i] + b[i]*B[i] + + for (int32_t dim = 0; dim < nYdim; dim++) { + Q[dim] = 0; + for (int32_t i = 0; i < 8; i++) { + Q[dim] += a[i] * A[nYdim * i + dim] + b[i] * B[nYdim * i + dim]; + } + } + } + + // cross-derivative W = (dS)^2 / du / dv + // W = dRdSl * (dQdSd * A[0] + dQdDd * A[1]) + dRdDl * (dQdSd * A[2] + dQdDd * A[3]) + + // dRdSr * (dQdSd * A[4] + dQdDd * A[5]) + dRdDr * (dQdSd * A[6] + dQdDd * A[7]) + + // dRdSl * (dQdSu * B[0] + dQdDu * B[1]) + dRdDl * (dQdSu * B[2] + dQdDu * B[3]) + + // dRdSr * (dQdSu * B[4] + dQdDu * B[5]) + dRdDr * (dQdSu * B[6] + dQdDu * B[7]); + + { + DataT a[8] = {dRdSl * dQdSd, dRdSl * dQdDd, dRdDl * dQdSd, dRdDl * dQdDd, + dRdSr * dQdSd, dRdSr * dQdDd, dRdDr * dQdSd, dRdDr * dQdDd}; + DataT b[8] = {dRdSl * dQdSu, dRdSl * dQdDu, dRdDl * dQdSu, dRdDl * dQdDu, + dRdSr * dQdSu, dRdSr * dQdDu, dRdDr * dQdSu, dRdDr * dQdDu}; + + // W = sum a[i]*A[i] + b[i]*B[i] + + for (int32_t dim = 0; dim < nYdim; dim++) { + W[dim] = 0; + for (int32_t i = 0; i < 8; i++) { + W[dim] += a[i] * A[nYdim * i + dim] + b[i] * B[nYdim * i + dim]; + } + } + } + } + protected: using TBase::mGridX1; using TBase::mGridX2; @@ -429,11 +555,18 @@ class Spline2DSpec /// Get interpolated value for an YdimT-dimensional S(u1,u2) using spline parameters Parameters. template GPUd() void interpolateAtU(GPUgeneric() const DataT Parameters[], - DataT u1, DataT u2, GPUgeneric() DataT S[/*nYdim*/]) const + DataT u1, DataT u2, GPUgeneric() DataT S[/*YdimT*/]) const { TBase::template interpolateAtU(YdimT, Parameters, u1, u2, S); } + template + GPUd() void interpolateParametersAtU(GPUgeneric() const DataT Parameters[], + DataT u1, DataT u2, GPUgeneric() DataT P[/* 4*YdimT */]) const + { + TBase::template interpolateParametersAtU(YdimT, Parameters, u1, u2, P); + } + /// Get interpolated value for an YdimT-dimensional S(u1,u2) using spline parameters Parameters. template GPUd() void interpolateAtUold(GPUgeneric() const DataT Parameters[], diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index 76368efcd8a4f..da37409221d6c 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -82,6 +82,24 @@ class TPCFastSpaceChargeCorrection : public FlatObject maxCorr[2] = GPUCommonMath::Max(maxCorr[2], dv); } + void updateMaxValues(std::tuple dxdudv, float scale) + { + float dx = std::get<0>(dxdudv) * scale; + float du = std::get<1>(dxdudv) * scale; + float dv = std::get<2>(dxdudv) * scale; + updateMaxValues(dx, du, dv); + } + + std::tuple getMaxValues() const + { + return std::make_tuple(maxCorr[0], maxCorr[1], maxCorr[2]); + } + + std::tuple getMinValues() const + { + return std::make_tuple(minCorr[0], minCorr[1], minCorr[2]); + } + ClassDefNV(SectorRowInfo, 2); }; @@ -90,7 +108,11 @@ class TPCFastSpaceChargeCorrection : public FlatObject ClassDefNV(SectorInfo, 1); }; - typedef Spline2D SplineType; + typedef Spline2D SplineTypeXYZ; + typedef Spline2D SplineTypeInvX; + typedef Spline2D SplineTypeInvYZ; + + typedef SplineTypeXYZ SplineType; /// _____________ Constructors / destructors __________________________ @@ -168,6 +190,30 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// Gives pointer to spline data GPUd() const float* getSplineData(int32_t sector, int32_t row, int32_t iSpline = 0) const; + /// Gives const pointer to a spline for the inverse X correction + GPUd() const SplineTypeInvX& getSplineInvX(int32_t sector, int32_t row) const; + + /// Gives pointer to a spline for the inverse X correction + GPUd() SplineTypeInvX& getSplineInvX(int32_t sector, int32_t row); + + /// Gives pointer to spline data for the inverse X correction + GPUd() float* getSplineDataInvX(int32_t sector, int32_t row); + + /// Gives pointer to spline data for the inverse X correction + GPUd() const float* getSplineDataInvX(int32_t sector, int32_t row) const; + + /// Gives const pointer to a spline for the inverse YZ correction + GPUd() const SplineTypeInvYZ& getSplineInvYZ(int32_t sector, int32_t row) const; + + /// Gives pointer to a spline for the inverse YZ correction + GPUd() SplineTypeInvYZ& getSplineInvYZ(int32_t sector, int32_t row); + + /// Gives pointer to spline data for the inverse YZ correction + GPUd() float* getSplineDataInvYZ(int32_t sector, int32_t row); + + /// Gives pointer to spline data for the inverse YZ correction + GPUd() const float* getSplineDataInvYZ(int32_t sector, int32_t row) const; + /// _______________ The main method: cluster correction _______________________ /// // GPUd() int32_t getCorrectionInternal(int32_t sector, int32_t row, float u, float v, float& dx, float& du, float& dv) const; @@ -193,6 +239,10 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// convert corrected u,v to internal grid coordinates GPUd() std::tuple convCorrectedLocalToGrid(int32_t sector, int32_t row, float y, float z) const; + /// convert internal grid coordinates to corrected u,v + /// return values: u, v, scaling factor + GPUd() std::tuple convGridToCorrectedLocal(int32_t sector, int32_t row, float u, float v) const; + GPUd() bool isLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const; /// TPC geometry information @@ -316,6 +366,54 @@ GPUdi() const float* TPCFastSpaceChargeCorrection::getSplineData(int32_t sector, return reinterpret_cast(mSplineData[iSpline] + mSectorDataSizeBytes[iSpline] * sector + rowInfo.dataOffsetBytes[iSpline]); } +GPUdi() TPCFastSpaceChargeCorrection::SplineTypeInvX& TPCFastSpaceChargeCorrection::getSplineInvX(int32_t sector, int32_t row) +{ + /// Gives pointer to spline for the inverse X correction + return reinterpret_cast(getSpline(sector, row)); +} + +GPUdi() const TPCFastSpaceChargeCorrection::SplineTypeInvX& TPCFastSpaceChargeCorrection::getSplineInvX(int32_t sector, int32_t row) const +{ + /// Gives const pointer to spline for the inverse X correction + return reinterpret_cast(getSpline(sector, row)); +} + +GPUdi() float* TPCFastSpaceChargeCorrection::getSplineDataInvX(int32_t sector, int32_t row) +{ + /// Gives pointer to spline data for the inverse X correction + return getSplineData(sector, row, 1); +} + +GPUdi() const float* TPCFastSpaceChargeCorrection::getSplineDataInvX(int32_t sector, int32_t row) const +{ + /// Gives pointer to spline data for the inverse X correction + return getSplineData(sector, row, 1); +} + +GPUdi() TPCFastSpaceChargeCorrection::SplineTypeInvYZ& TPCFastSpaceChargeCorrection::getSplineInvYZ(int32_t sector, int32_t row) +{ + /// Gives pointer to spline for the inverse YZ correction + return reinterpret_cast(getSpline(sector, row)); +} + +GPUdi() const TPCFastSpaceChargeCorrection::SplineTypeInvYZ& TPCFastSpaceChargeCorrection::getSplineInvYZ(int32_t sector, int32_t row) const +{ + /// Gives const pointer to spline for the inverse YZ correction + return reinterpret_cast(getSpline(sector, row)); +} + +GPUdi() float* TPCFastSpaceChargeCorrection::getSplineDataInvYZ(int32_t sector, int32_t row) +{ + /// Gives pointer to spline data for the inverse YZ correction + return getSplineData(sector, row, 2); +} + +GPUdi() const float* TPCFastSpaceChargeCorrection::getSplineDataInvYZ(int32_t sector, int32_t row) const +{ + /// Gives pointer to spline data for the inverse YZ correction + return getSplineData(sector, row, 2); +} + GPUdi() std::tuple TPCFastSpaceChargeCorrection::convLocalToGrid(int32_t sector, int32_t row, float y, float z) const { /// convert local y, z to internal grid coordinates u,v @@ -401,6 +499,17 @@ GPUdi() std::tuple TPCFastSpaceChargeCorrection::convCorrec return {gridU, gridV, scale}; } +GPUdi() std::tuple TPCFastSpaceChargeCorrection::convGridToCorrectedLocal(int32_t sector, int32_t row, float gridU, float gridV) const +{ + /// convert internal grid coordinates u,v to corrected y, z + const SectorRowInfo& info = getSectorRowInfo(sector, row); + float u = info.gridCorrU0 + gridU / info.scaleCorrUtoGrid; + float v = info.gridCorrV0 + gridV / info.scaleCorrVtoGrid; + float y, z; + mGeo.convUVtoLocal1(sector, u, v, y, z); + return {y, z}; +} + GPUdi() std::tuple TPCFastSpaceChargeCorrection::getCorrectionLocal(int32_t sector, int32_t row, float y, float z) const { const auto& info = getSectorRowInfo(sector, row); @@ -421,33 +530,21 @@ GPUdi() std::tuple TPCFastSpaceChargeCorrection::getCorrect GPUdi() float TPCFastSpaceChargeCorrection::getCorrectionXatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const { const auto& info = getSectorRowInfo(sector, row); - const Spline2D& spline = reinterpret_cast&>(getSpline(sector, row)); - const float* splineData = getSplineData(sector, row, 1); - auto [gridU, gridV, scale] = convCorrectedLocalToGrid(sector, row, realY, realZ); - float dx = 0; - spline.interpolateAtU(splineData, gridU, gridV, &dx); - + getSplineInvX(sector, row).interpolateAtU(getSplineDataInvX(sector, row), gridU, gridV, &dx); dx = scale * GPUCommonMath::Clamp(dx, info.minCorr[0], info.maxCorr[0]); return dx; } GPUdi() std::tuple TPCFastSpaceChargeCorrection::getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const { - auto [gridU, gridV, scale] = convCorrectedLocalToGrid(sector, row, realY, realZ); - const auto& info = getSectorRowInfo(sector, row); - const Spline2D& spline = reinterpret_cast&>(getSpline(sector, row)); - const float* splineData = getSplineData(sector, row, 2); - float dyz[2]; - spline.interpolateAtU(splineData, gridU, gridV, dyz); - + getSplineInvYZ(sector, row).interpolateAtU(getSplineDataInvYZ(sector, row), gridU, gridV, dyz); dyz[0] = scale * GPUCommonMath::Clamp(dyz[0], info.minCorr[1], info.maxCorr[1]); dyz[1] = scale * GPUCommonMath::Clamp(dyz[1], info.minCorr[2], info.maxCorr[2]); - return {dyz[0], dyz[1]}; } From 98e7ac28070839a7985ee342af2d39824729a627 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Sat, 12 Apr 2025 16:10:13 +0000 Subject: [PATCH 093/285] TPC Splines: get rid of internal UV coordinates --- .../TPCFastSpaceChargeCorrectionHelper.cxx | 59 ++++----- .../src/TPCFastTransformHelperO2.cxx | 7 +- .../TPCFastSpaceChargeCorrection.cxx | 100 ++------------ .../TPCFastSpaceChargeCorrection.h | 125 +++++++++--------- GPU/TPCFastTransformation/TPCFastTransform.h | 4 +- .../TPCFastTransformGeo.cxx | 2 +- .../TPCFastTransformGeo.h | 73 ++-------- .../macro/TPCFastTransformInit.C | 7 +- 8 files changed, 124 insertions(+), 253 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index 6ba3d6e12dd9e..021074c49f21a 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -354,15 +354,14 @@ void TPCFastSpaceChargeCorrectionHelper::testGeometry(const TPCFastTransformGeo& for (int pad = 0; pad < nPads; pad++) { const GlobalPadNumber p = mapper.globalPadNumber(PadPos(row, pad)); const PadCentre& c = mapper.padCentre(p); - double u = geo.convPadToU(row, pad); - + auto [y, z] = geo.convPadDriftLengthToLocal(0, row, pad, 0.); const double dx = x - c.X(); - const double dy = u - (-c.Y()); // diferent sign convention for Y coordinate in the map + const double dy = y - (-c.Y()); // diferent sign convention for Y coordinate in the map if (fabs(dx) >= 1.e-6 || fabs(dy) >= 1.e-5) { LOG(warning) << "wrong calculated pad position:" << " row " << row << " pad " << pad << " x calc " << x << " x in map " << c.X() << " dx " << (x - c.X()) - << " y calc " << u << " y in map " << -c.Y() << " dy " << dy << std::endl; + << " y calc " << y << " y in map " << -c.Y() << " dy " << dy << std::endl; } if (fabs(maxDx) < fabs(dx)) { maxDx = dx; @@ -499,18 +498,14 @@ std::unique_ptr TPCFastSpaceChargeCorrect double yMax = rowInfo.x * trackResiduals.getY2X(iRow, trackResiduals.getNY2XBins() - 1); double zMin = rowInfo.x * trackResiduals.getZ2X(0); double zMax = rowInfo.x * trackResiduals.getZ2X(trackResiduals.getNZ2XBins() - 1); - double uMin = yMin; - double uMax = yMax; - double vMin = geo.getTPCzLength() - zMax; - double vMax = geo.getTPCzLength() - zMin; - info.gridU0 = uMin; - info.scaleUtoGrid = spline.getGridX1().getUmax() / (uMax - uMin); - info.gridV0 = vMin; - info.scaleVtoGrid = spline.getGridX2().getUmax() / (vMax - vMin); - info.gridCorrU0 = info.gridU0; - info.gridCorrV0 = info.gridV0; - info.scaleCorrUtoGrid = info.scaleUtoGrid; - info.scaleCorrVtoGrid = info.scaleVtoGrid; + double lMin = geo.getTPCzLength() - zMax; + double lMax = geo.getTPCzLength() - zMin; + info.gridMeasured.y0 = yMin; + info.gridMeasured.yScale = spline.getGridX1().getUmax() / (yMax - yMin); + info.gridMeasured.l0 = lMin; + info.gridMeasured.lScale = spline.getGridX2().getUmax() / (lMax - lMin); + + info.gridReal = info.gridMeasured; // std::cout << " iSector " << iSector << " iRow " << iRow << " uMin: " << uMin << " uMax: " << uMax << " vMin: " << vMin << " vMax: " << vMax //<< " grid scale u "<< info.scaleUtoGrid << " grid scale v "<< info.scaleVtoGrid<< std::endl; @@ -593,8 +588,8 @@ std::unique_ptr TPCFastSpaceChargeCorrect auto myThread = [&](int iThread, int nTreads) { struct Voxel { - float mY, mZ; // not-distorted local coordinates - float mDy, mDz; // bin size + float mY, mZ; // non-distorted local coordinates + float mDy, mDz; // voxel size int mSmoothingStep{100}; // is the voxel data original or smoothed at this step }; @@ -905,11 +900,10 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector= 1.e-6 || fabs(dy) >= 1.e-5) { LOG(warning) << "wrong calculated pad position:" << " row " << row << " pad " << pad << " x calc " << x << " x in map " << c.X() << " dx " << (x - c.X()) - << " y calc " << u << " y in map " << -c.Y() << " dy " << dy << std::endl; + << " y calc " << y << " y in map " << -c.Y() << " dy " << dy << std::endl; } if (fabs(maxDx) < fabs(dx)) { maxDx = dx; diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx index 2921a74b025ce..1e6d84b7f8dd9 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx @@ -141,48 +141,18 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer { /// Sets the actual location of the external flat buffer after it has been moved (e.g. to another maschine) - struct RowInfoVersion3 { - int32_t splineScenarioID{0}; ///< scenario index (which of Spline2D splines to use) - size_t dataOffsetBytes[3]{0}; ///< offset for the spline data withing a TPC sector - }; - - struct RowActiveAreaVersion3 { - float maxDriftLengthCheb[5]{0.f}; - float vMax{0.f}; - float cuMin{0.f}; - float cuMax{0.f}; - float cvMax{0.f}; - }; - - struct SectorRowInfoVersion3 { - float gridV0{0.f}; ///< V coordinate of the V-grid start - float gridCorrU0{0.f}; ///< U coordinate of the U-grid start for corrected U - float gridCorrV0{0.f}; ///< V coordinate of the V-grid start for corrected V - float scaleCorrUtoGrid{0.f}; ///< scale corrected U to U-grid coordinate - float scaleCorrVtoGrid{0.f}; ///< scale corrected V to V-grid coordinate - RowActiveAreaVersion3 activeArea; - }; - - FlatObject::setActualBufferAddress(actualFlatBufferPtr); - - size_t rowsOffset = 0; - size_t rowsSize = 0; - if (mClassVersion == 3) { - rowsSize = sizeof(RowInfoVersion3) * mGeo.getNumberOfRows(); + if (mClassVersion != 4) { + LOG(error) << "TPCFastSpaceChargeCorrection::setActualBufferAddress() called with class version " << mClassVersion << ". This is not supported."; + return; } - size_t sectorRowsOffset = rowsOffset + rowsSize; - size_t sectorRowsSize = 0; - if (mClassVersion == 3) { // copy old-format sectorrow data from the buffer to the arrays - sectorRowsSize = sizeof(SectorRowInfoVersion3) * mGeo.getNumberOfRows() * mGeo.getNumberOfSectors(); - } + FlatObject::setActualBufferAddress(actualFlatBufferPtr); - size_t scOffset = alignSize(sectorRowsOffset + sectorRowsSize, SplineType::getClassAlignmentBytes()); size_t scSize = sizeof(SplineType) * mNumberOfScenarios; - mScenarioPtr = reinterpret_cast(mFlatBufferPtr + scOffset); + mScenarioPtr = reinterpret_cast(mFlatBufferPtr); - size_t scBufferOffset = alignSize(scOffset + scSize, SplineType::getBufferAlignmentBytes()); + size_t scBufferOffset = alignSize(scSize, SplineType::getBufferAlignmentBytes()); size_t scBufferSize = 0; for (int32_t i = 0; i < mNumberOfScenarios; i++) { @@ -196,45 +166,6 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer mSplineData[is] = reinterpret_cast(mFlatBufferPtr + sectorDataOffset); bufferSize = sectorDataOffset + mSectorDataSizeBytes[is] * mGeo.getNumberOfSectors(); } - - if (mClassVersion == 3) { // copy old-format sectorrow data from the buffer to the arrays - - auto* rowInfosOld = reinterpret_cast(mFlatBufferPtr + rowsOffset); - for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { - RowInfoVersion3& infoOld = rowInfosOld[i]; - RowInfo& info = mRowInfos[i]; - info.splineScenarioID = infoOld.splineScenarioID; - for (int32_t is = 0; is < 3; is++) { - info.dataOffsetBytes[is] = infoOld.dataOffsetBytes[is]; - } - } - - for (int32_t is = 0; is < mNumberOfScenarios; is++) { - auto& spline = mScenarioPtr[is]; - spline.setXrange(0., spline.getGridX1().getUmax(), 0., spline.getGridX2().getUmax()); - } - - auto* sectorRowInfosOld = reinterpret_cast(mFlatBufferPtr + sectorRowsOffset); - - for (int32_t sector = 0; sector < mGeo.getNumberOfSectors(); sector++) { - for (int32_t row = 0; row < mGeo.getNumberOfRows(); row++) { - SectorRowInfoVersion3& infoOld = sectorRowInfosOld[mGeo.getNumberOfRows() * sector + row]; - SectorRowInfo& info = getSectorRowInfo(sector, row); - const auto& spline = getSpline(sector, row); - info.gridU0 = mGeo.getRowInfo(row).u0; - info.scaleUtoGrid = spline.getGridX1().getUmax() / mGeo.getRowInfo(row).getUwidth(); - - info.gridV0 = infoOld.gridV0; - info.scaleVtoGrid = spline.getGridX2().getUmax() / (mGeo.getTPCzLength() + 3. - info.gridV0); - - info.gridCorrU0 = infoOld.gridCorrU0; - info.scaleCorrUtoGrid = infoOld.scaleCorrUtoGrid; - - info.gridCorrV0 = infoOld.gridCorrV0; - info.scaleCorrVtoGrid = infoOld.scaleCorrVtoGrid; - } - } - } } void TPCFastSpaceChargeCorrection::setFutureBufferAddress(char* futureFlatBufferPtr) @@ -454,17 +385,12 @@ GPUd() void TPCFastSpaceChargeCorrection::setNoCorrection() SectorRowInfo& info = getSectorRowInfo(sector, row); - info.gridU0 = mGeo.getRowInfo(row).u0; - info.scaleUtoGrid = spline.getGridX1().getUmax() / mGeo.getRowInfo(row).getUwidth(); - - info.gridV0 = 0.f; - info.scaleVtoGrid = spline.getGridX2().getUmax() / vLength; - - info.gridCorrU0 = info.gridU0; - info.gridCorrV0 = info.gridV0; - info.scaleCorrUtoGrid = info.scaleUtoGrid; - info.scaleCorrVtoGrid = info.scaleVtoGrid; + info.gridMeasured.y0 = mGeo.getRowInfo(row).getYmin(); + info.gridMeasured.yScale = spline.getGridX1().getUmax() / mGeo.getRowInfo(row).getYwidth(); + info.gridMeasured.l0 = 0.f; + info.gridMeasured.lScale = spline.getGridX2().getUmax() / vLength; + info.gridReal = info.gridMeasured; } // row } // sector } @@ -539,9 +465,9 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) // grid borders if (sector < mGeo.getNumberOfSectorsA()) { - z1 = vLength - getSectorRowInfo(sector, row).gridV0; + z1 = vLength - getSectorRowInfo(sector, row).gridMeasured.l0; } else { - z0 = getSectorRowInfo(sector, row).gridV0 - vLength; + z0 = getSectorRowInfo(sector, row).gridMeasured.l0 - vLength; } double stepY = (y1 - y0) / 100.; diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index da37409221d6c..4564d584c8dce 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -48,17 +48,31 @@ class TPCFastSpaceChargeCorrection : public FlatObject ClassDefNV(RowInfo, 1); }; + struct GridInfo { + float y0{0.f}; ///< Y coordinate of the U-grid start + float yScale{0.f}; //< scale Y to U-grid coordinate + float l0{0.f}; ///< Drift Length coordinate of the V-grid start + float lScale{0.f}; //< scale Drift Length to V-grid coordinate + + float getScale(float l) const + { + if (l < 0.f) { // outside of the TPC + return 0.f; + } + if (l < l0) { // between the grid and the readout + return l / l0; + } + return 1.f; // inside the grid + } + ClassDefNV(GridInfo, 1); + }; + struct SectorRowInfo { - float gridU0{0.f}; //< U coordinate of the U-grid start - float scaleUtoGrid{0.f}; //< scale U to U-grid coordinate - float gridV0{0.f}; ///< V coordinate of the V-grid start - float scaleVtoGrid{0.f}; //< scale V to V-grid coordinate - float gridCorrU0{0.f}; ///< U coordinate of the U-grid start for corrected U - float scaleCorrUtoGrid{0.f}; ///< scale corrected U to U-grid coordinate - float gridCorrV0{0.f}; ///< V coordinate of the V-grid start for corrected V - float scaleCorrVtoGrid{0.f}; ///< scale corrected V to V-grid coordinate - float minCorr[3]{-10.f, -10.f, -10.f}; ///< min correction for dX, dU, dV - float maxCorr[3]{10.f, 10.f, 10.f}; ///< max correction for dX, dU, dV + GridInfo gridMeasured; ///< grid info for measured coordinates + GridInfo gridReal; ///< grid info for real coordinates + + float minCorr[3]{-10.f, -10.f, -10.f}; ///< min correction for dX, dY, dZ + float maxCorr[3]{10.f, 10.f, 10.f}; ///< max correction for dX, dY, dZ void resetMaxValues() { @@ -220,10 +234,10 @@ class TPCFastSpaceChargeCorrection : public FlatObject GPUdi() std::tuple getCorrectionLocal(int32_t sector, int32_t row, float y, float z) const; - /// inverse correction: Corrected U and V -> coorrected X + /// inverse correction: Real Y and Z -> Real X GPUd() float getCorrectionXatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const; - /// inverse correction: Corrected U and V -> uncorrected U and V + /// inverse correction: Real Y and Z -> measred Y and Z GPUd() std::tuple getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const; /// _______________ Utilities _______________________________________________ @@ -236,12 +250,13 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// return values: y, z, scaling factor GPUd() std::tuple convGridToLocal(int32_t sector, int32_t row, float u, float v) const; - /// convert corrected u,v to internal grid coordinates - GPUd() std::tuple convCorrectedLocalToGrid(int32_t sector, int32_t row, float y, float z) const; - - /// convert internal grid coordinates to corrected u,v + /// convert real Y, Z to the internal grid coordinates /// return values: u, v, scaling factor - GPUd() std::tuple convGridToCorrectedLocal(int32_t sector, int32_t row, float u, float v) const; + GPUd() std::tuple convRealLocalToGrid(int32_t sector, int32_t row, float y, float z) const; + + /// convert internal grid coordinates to the real Y, Z + /// return values: y, z + GPUd() std::tuple convGridToRealLocal(int32_t sector, int32_t row, float u, float v) const; GPUd() bool isLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const; @@ -421,18 +436,10 @@ GPUdi() std::tuple TPCFastSpaceChargeCorrection::convLocalT const auto& info = getSectorRowInfo(sector, row); const SplineType& spline = getSpline(sector, row); - float u, v; - mGeo.convLocalToUV1(sector, y, z, u, v); - - float scale = 1.f; - if (v < 0.f) { - scale = 0.f; - } else if (v < info.gridV0) { - scale = v / info.gridV0; - } - - float gridU = (u - info.gridU0) * info.scaleUtoGrid; - float gridV = (v - info.gridV0) * info.scaleVtoGrid; + float l = mGeo.convZtoDriftLength(sector, z); + float scale = info.gridMeasured.getScale(l); + float gridU = (y - info.gridMeasured.y0) * info.gridMeasured.yScale; + float gridV = (l - info.gridMeasured.l0) * info.gridMeasured.lScale; // shrink to the grid area gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); @@ -446,19 +453,19 @@ GPUdi() bool TPCFastSpaceChargeCorrection::isLocalInsideGrid(int32_t sector, int /// ccheck if local y, z are inside the grid const auto& info = getSectorRowInfo(sector, row); - const SplineType& spline = getSpline(sector, row); + const auto& spline = getSpline(sector, row); + float l = mGeo.convZtoDriftLength(sector, z); - float u, v; - mGeo.convLocalToUV1(sector, y, z, u, v); - - float gridU = (u - info.gridU0) * info.scaleUtoGrid; - float gridV = (v - info.gridV0) * info.scaleVtoGrid; + float gridU = (y - info.gridMeasured.y0) * info.gridMeasured.yScale; + float gridV = (l - info.gridMeasured.l0) * info.gridMeasured.lScale; // shrink to the grid area - if (gridU < 0.f || gridU > (float)spline.getGridX1().getUmax()) + if (gridU < 0.f || gridU > (float)spline.getGridX1().getUmax()) { return false; - if (gridV < 0.f || gridV > (float)spline.getGridX2().getUmax()) + } + if (gridV < 0.f || gridV > (float)spline.getGridX2().getUmax()) { return false; + } return true; } @@ -466,31 +473,22 @@ GPUdi() std::tuple TPCFastSpaceChargeCorrection::convGridToLocal(i { /// convert internal grid coordinates u,v to local y, z const SectorRowInfo& info = getSectorRowInfo(sector, row); - float u = info.gridU0 + gridU / info.scaleUtoGrid; - float v = info.gridV0 + gridV / info.scaleVtoGrid; - float y, z; - mGeo.convUVtoLocal1(sector, u, v, y, z); + float y = info.gridMeasured.y0 + gridU / info.gridMeasured.yScale; + float l = info.gridMeasured.l0 + gridV / info.gridMeasured.lScale; + float z = mGeo.convDriftLengthToZ(sector, l); return {y, z}; } -GPUdi() std::tuple TPCFastSpaceChargeCorrection::convCorrectedLocalToGrid(int32_t sector, int32_t row, float y, float z) const +GPUdi() std::tuple TPCFastSpaceChargeCorrection::convRealLocalToGrid(int32_t sector, int32_t row, float y, float z) const { - /// convert corrected y, z to the internal grid coordinates + /// convert real y, z to the internal grid coordinates + scale const auto& info = getSectorRowInfo(sector, row); - const Spline2D& spline = reinterpret_cast&>(getSpline(sector, row)); - - float u, v; - mGeo.convLocalToUV1(sector, y, z, u, v); - - float scale = 1.f; - if (v < 0.f) { - scale = 0.f; - } else if (v < info.gridCorrV0) { - scale = v / info.gridCorrV0; - } + const auto& spline = getSpline(sector, row); - float gridU = (u - info.gridCorrU0) * info.scaleCorrUtoGrid; - float gridV = (v - info.gridCorrV0) * info.scaleCorrVtoGrid; + float l = mGeo.convZtoDriftLength(sector, z); + float scale = info.gridReal.getScale(l); + float gridU = (y - info.gridReal.y0) * info.gridReal.yScale; + float gridV = (l - info.gridReal.l0) * info.gridReal.lScale; // shrink to the grid area gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); @@ -499,14 +497,13 @@ GPUdi() std::tuple TPCFastSpaceChargeCorrection::convCorrec return {gridU, gridV, scale}; } -GPUdi() std::tuple TPCFastSpaceChargeCorrection::convGridToCorrectedLocal(int32_t sector, int32_t row, float gridU, float gridV) const +GPUdi() std::tuple TPCFastSpaceChargeCorrection::convGridToRealLocal(int32_t sector, int32_t row, float gridU, float gridV) const { - /// convert internal grid coordinates u,v to corrected y, z + /// convert internal grid coordinates u,v to the real y, z const SectorRowInfo& info = getSectorRowInfo(sector, row); - float u = info.gridCorrU0 + gridU / info.scaleCorrUtoGrid; - float v = info.gridCorrV0 + gridV / info.scaleCorrVtoGrid; - float y, z; - mGeo.convUVtoLocal1(sector, u, v, y, z); + float y = info.gridReal.y0 + gridU / info.gridReal.yScale; + float l = info.gridReal.l0 + gridV / info.gridReal.lScale; + float z = mGeo.convDriftLengthToZ(sector, l); return {y, z}; } @@ -530,7 +527,7 @@ GPUdi() std::tuple TPCFastSpaceChargeCorrection::getCorrect GPUdi() float TPCFastSpaceChargeCorrection::getCorrectionXatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const { const auto& info = getSectorRowInfo(sector, row); - auto [gridU, gridV, scale] = convCorrectedLocalToGrid(sector, row, realY, realZ); + auto [gridU, gridV, scale] = convRealLocalToGrid(sector, row, realY, realZ); float dx = 0; getSplineInvX(sector, row).interpolateAtU(getSplineDataInvX(sector, row), gridU, gridV, &dx); dx = scale * GPUCommonMath::Clamp(dx, info.minCorr[0], info.maxCorr[0]); @@ -539,7 +536,7 @@ GPUdi() float TPCFastSpaceChargeCorrection::getCorrectionXatRealYZ(int32_t secto GPUdi() std::tuple TPCFastSpaceChargeCorrection::getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const { - auto [gridU, gridV, scale] = convCorrectedLocalToGrid(sector, row, realY, realZ); + auto [gridU, gridV, scale] = convRealLocalToGrid(sector, row, realY, realZ); const auto& info = getSectorRowInfo(sector, row); float dyz[2]; getSplineInvYZ(sector, row).interpolateAtU(getSplineDataInvYZ(sector, row), gridU, gridV, dyz); diff --git a/GPU/TPCFastTransformation/TPCFastTransform.h b/GPU/TPCFastTransformation/TPCFastTransform.h index 03d9eaf43ce9b..8807c0e3206f4 100644 --- a/GPU/TPCFastTransformation/TPCFastTransform.h +++ b/GPU/TPCFastTransformation/TPCFastTransform.h @@ -551,7 +551,7 @@ GPUdi() void TPCFastTransform::Transform(int32_t sector, int32_t row, float pad, GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t sector, float time, float& z, float maxTimeBin) const { float l = (time - mT0 - maxTimeBin) * mVdrift; // drift length cm - z = getGeometry().convDriftLengthToLocal(sector, l); + z = getGeometry().convDriftLengthToZ(sector, l); } GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float maxTimeBin) const @@ -590,7 +590,7 @@ GPUdi() void TPCFastTransform::TransformIdealZ(int32_t sector, float time, float /// float l = (time - mT0 - vertexTime) * mVdrift; // drift length cm - z = getGeometry().convDriftLengthToLocal(sector, l); + z = getGeometry().convDriftLengthToZ(sector, l); } GPUdi() void TPCFastTransform::TransformIdeal(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime) const diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx b/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx index e7e026f464818..5b2dcc8da82d5 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx @@ -92,7 +92,7 @@ void TPCFastTransformGeo::setTPCrow(int32_t iRow, float x, int32_t nPads, float row.x = x; row.maxPad = nPads - 1; row.padWidth = padWidth; - row.u0 = -uWidth / 2.; + row.yMin = -uWidth / 2.; } void TPCFastTransformGeo::finishConstruction() diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.h b/GPU/TPCFastTransformation/TPCFastTransformGeo.h index 4072435e948a5..bbb94dcb8bedd 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.h +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.h @@ -46,25 +46,19 @@ class TPCFastTransformGeo float x{0.f}; ///< nominal X coordinate of the padrow [cm] int32_t maxPad{0}; ///< maximal pad number = n pads - 1 float padWidth{0.f}; ///< width of pads [cm] - float u0{0.f}; ///< min. u coordinate - - /// get U min - GPUd() float getUmin() const { return u0; } - - /// get U max - GPUd() float getUmax() const { return -u0; } + float yMin{0.f}; ///< min. y coordinate /// get Y min - GPUd() float getYmin() const { return u0; } + GPUd() float getYmin() const { return yMin; } /// get Y max - GPUd() float getYmax() const { return -u0; } + GPUd() float getYmax() const { return -yMin; } /// get Y range GPUd() std::tuple getYrange() const { return {getYmin(), getYmax()}; } - /// get width in U - GPUd() float getUwidth() const { return -2.f * u0; } + /// get width in Y + GPUd() float getYwidth() const { return -2.f * yMin; } ClassDefNV(RowInfo, 1); }; @@ -145,23 +139,14 @@ class TPCFastTransformGeo GPUd() std::tuple convPadDriftLengthToLocal(int32_t sector, int32_t row, float pad, float driftLength) const; /// convert DriftLength -> Local c.s. - GPUd() float convDriftLengthToLocal(int32_t sector, float driftLength) const; + GPUd() float convDriftLengthToZ(int32_t sector, float driftLength) const; + + /// convert Z to DriftLength + GPUd() float convZtoDriftLength(int32_t sector, float z) const; /// convert Local c.s. -> Pad, DriftLength GPUd() std::tuple convLocalToPadDriftLength(int32_t sector, int32_t row, float y, float z) const; - /// convert UV -> Local c.s. - GPUd() void convUVtoLocal1(int32_t sector, float u, float v, float& y, float& z) const; - - /// convert Local-> UV c.s. - GPUd() void convLocalToUV1(int32_t sector, float y, float z, float& u, float& v) const; - - /// convert Pad coordinate -> U - GPUd() float convPadToU(int32_t row, float pad) const; - - /// convert U -> Pad coordinate - GPUd() float convUtoPad(int32_t row, float u) const; - /// Print method void print() const; @@ -257,22 +242,16 @@ GPUdi() std::tuple TPCFastTransformGeo::convPadDriftLengthToLocal( return {y, z}; } -GPUdi() float TPCFastTransformGeo::convDriftLengthToLocal(int32_t sector, float driftLength) const +GPUdi() float TPCFastTransformGeo::convDriftLengthToZ(int32_t sector, float driftLength) const { /// convert DriftLength -> Local c.s. return (sector < NumberOfSectorsA) ? (mTPCzLength - driftLength) : (driftLength - mTPCzLength); } -GPUdi() void TPCFastTransformGeo::convUVtoLocal1(int32_t sector, float u, float v, float& ly, float& lz) const +GPUdi() float TPCFastTransformGeo::convZtoDriftLength(int32_t sector, float z) const { - /// convert UV -> Local c.s. - if (sector < NumberOfSectorsA) { // TPC side A - ly = u; - lz = mTPCzLength - v; - } else { // TPC side C - ly = -u; // pads are mirrorred on C-side - lz = v - mTPCzLength; // drift direction is mirrored on C-side - } + /// convert Z to DriftLength + return (sector < NumberOfSectorsA) ? (mTPCzLength - z) : (z + mTPCzLength); } GPUdi() std::tuple TPCFastTransformGeo::getZrange(int32_t sector) const @@ -301,32 +280,6 @@ GPUdi() std::tuple TPCFastTransformGeo::convLocalToPadDriftLength( return {pad, l}; } -GPUdi() void TPCFastTransformGeo::convLocalToUV1(int32_t sector, float ly, float lz, float& u, float& v) const -{ - /// convert Local-> UV c.s. - if (sector < NumberOfSectorsA) { // TPC side A - u = ly; - v = mTPCzLength - lz; - } else { // TPC side C - u = -ly; // pads are mirrorred on C-side - v = lz + mTPCzLength; // drift direction is mirrored on C-side - } -} - -GPUdi() float TPCFastTransformGeo::convPadToU(int32_t row, float pad) const -{ - /// convert Pad coordinate -> U - const RowInfo& rowInfo = getRowInfo(row); - return (pad - 0.5f * rowInfo.maxPad) * rowInfo.padWidth; -} - -GPUdi() float TPCFastTransformGeo::convUtoPad(int32_t row, float u) const -{ - /// convert U -> Pad coordinate - const RowInfo& rowInfo = getRowInfo(row); - return u / rowInfo.padWidth + 0.5f * rowInfo.maxPad; -} - } // namespace gpu } // namespace o2 diff --git a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C index bee1f9107ddd2..f0c03d9f5f081 100644 --- a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C +++ b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C @@ -185,9 +185,10 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* for (int32_t iRow = 0; iRow < geo.getNumberOfRows(); iRow++) { auto& info = corr.getSectorRowInfo(iSector, iRow); std::cout << "sector " << iSector << " row " << iRow - << " gridV0 " << info.gridV0 << " gridCorrU0 " << info.gridCorrU0 << " gridCorrV0 " << info.gridCorrV0 - << " scaleCorrUtoGrid " << info.scaleCorrUtoGrid << " scaleCorrVtoGrid " << info.scaleCorrVtoGrid - << " gridU0 " << info.gridU0 << " scaleUtoGrid " << info.scaleUtoGrid << " scaleVtoGrid " << info.scaleVtoGrid + << " gridY0 " << info.gridMeasured.y0 << " gridL0 " << info.gridMeasured.l0 + << " scaleYtoGrid " << info.gridMeasured.yScale << " scaleLtoGrid " << info.gridMeasured.lScale + << " gridRealY0 " << info.gridReal.y0 << " gridRealL0 " << info.gridReal.l0 + << " scaleRealYtoGrid " << info.gridReal.yScale << " scaleRealLtoGrid " << info.gridReal.lScale << std::endl; } } From 0e98f2942391c7977494fa58f384fa3708489ee9 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Mon, 14 Apr 2025 19:01:36 +0000 Subject: [PATCH 094/285] TPC Splines: completely switch to local TPC coordinates in the grid --- .../TPCFastSpaceChargeCorrectionHelper.h | 10 +- .../TPCFastSpaceChargeCorrectionHelper.cxx | 270 +++++++++--------- .../TPCFastSpaceChargeCorrection.cxx | 119 ++++---- .../TPCFastSpaceChargeCorrection.h | 137 ++++----- .../TPCFastTransform.cxx | 4 - GPU/TPCFastTransformation/TPCFastTransform.h | 4 +- .../TPCFastTransformGeo.h | 43 ++- .../TPCFastTransformationLinkDef_O2.h | 1 + .../macro/TPCFastTransformInit.C | 12 +- 9 files changed, 328 insertions(+), 272 deletions(-) diff --git a/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h b/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h index 747ed74c9bcad..e8afd9be97d5f 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h @@ -85,8 +85,16 @@ class TPCFastSpaceChargeCorrectionHelper const int nKnotsY = 10, const int nKnotsZ = 20); /// Create SpaceCharge correction out of the voxel tree + /// \param trackResiduals TrackResiduals object + /// \param voxResTree TTree with voxel residuals + /// \param voxResTreeInverse TTree with inverse voxel residuals + /// \param useSmoothed if true, use smoothed residuals + /// \param invertSigns if true, invert the signs of the residuals + /// \return pointer to the created TPCFastSpaceChargeCorrection object + /// \note voxel trees wont be changed. They are read as non-const because of the ROOT::TTreeProcessorMT interface std::unique_ptr createFromTrackResiduals( - const o2::tpc::TrackResiduals& trackResiduals, TTree* voxResTree, TTree* voxResTreeInverse, bool useSmoothed, bool invertSigns); + const o2::tpc::TrackResiduals& trackResiduals, TTree* voxResTree, TTree* voxResTreeInverse, // + bool useSmoothed, bool invertSigns); /// _______________ Utilities ________________________ diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index 021074c49f21a..d2f6cf57b0de7 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -258,12 +258,14 @@ std::unique_ptr TPCFastSpaceChargeCorrectionHelper correction.startConstruction(mGeo, nCorrectionScenarios); // assign spline type for TPC rows - for (int row = 0; row < mGeo.getNumberOfRows(); row++) { - int scenario = row / 10; - if (scenario >= nCorrectionScenarios) { - scenario = nCorrectionScenarios - 1; + for (int sector = 0; sector < mGeo.getNumberOfSectors(); sector++) { + for (int row = 0; row < mGeo.getNumberOfRows(); row++) { + int scenario = row / 10; + if (scenario >= nCorrectionScenarios) { + scenario = nCorrectionScenarios - 1; + } + correction.setRowScenarioID(sector, row, scenario); } - correction.setRowScenarioID(row, scenario); } for (int scenario = 0; scenario < nCorrectionScenarios; scenario++) { @@ -397,93 +399,100 @@ std::unique_ptr TPCFastSpaceChargeCorrect int nY2Xbins = trackResiduals.getNY2XBins(); int nZ2Xbins = trackResiduals.getNZ2XBins(); - std::vector uvBinsDouble[2]; + std::vector knotsDouble[3]; + + knotsDouble[0].reserve(nY2Xbins); + knotsDouble[1].reserve(nZ2Xbins); + knotsDouble[2].reserve(nZ2Xbins); - uvBinsDouble[0].reserve(nY2Xbins); - uvBinsDouble[1].reserve(nZ2Xbins); + // to get enouth measurements, make a spline knot at every second bin. Boundary bins are always included. for (int i = 0, j = nY2Xbins - 1; i <= j; i += 2, j -= 2) { - uvBinsDouble[0].push_back(trackResiduals.getY2X(0, i)); + knotsDouble[0].push_back(trackResiduals.getY2X(0, i)); if (j >= i + 1) { - uvBinsDouble[0].push_back(trackResiduals.getY2X(0, j)); + knotsDouble[0].push_back(trackResiduals.getY2X(0, j)); } } for (int i = 0, j = nZ2Xbins - 1; i <= j; i += 2, j -= 2) { - uvBinsDouble[1].push_back(-trackResiduals.getZ2X(i)); + knotsDouble[1].push_back(trackResiduals.getZ2X(i)); + knotsDouble[2].push_back(-trackResiduals.getZ2X(i)); if (j >= i + 1) { - uvBinsDouble[1].push_back(-trackResiduals.getZ2X(j)); + knotsDouble[1].push_back(trackResiduals.getZ2X(j)); + knotsDouble[2].push_back(-trackResiduals.getZ2X(j)); } } - std::vector uvBinsInt[2]; - - for (int iuv = 0; iuv < 2; iuv++) { - auto& bins = uvBinsDouble[iuv]; - std::sort(bins.begin(), bins.end()); + std::vector knotsInt[3]; - auto& binsInt = uvBinsInt[iuv]; - binsInt.reserve(bins.size()); + for (int dim = 0; dim < 3; dim++) { + auto& knotsD = knotsDouble[dim]; + std::sort(knotsD.begin(), knotsD.end()); - double dy = bins[1] - bins[0]; - for (int i = 2; i < bins.size(); i++) { - double dd = bins[i] - bins[i - 1]; - if (dd < dy) { - dy = dd; + double pitch = knotsD[1] - knotsD[0]; // min distance between the knots + for (int i = 2; i < knotsD.size(); i++) { + double d = knotsD[i] - knotsD[i - 1]; + if (d < pitch) { + pitch = d; } } - // spline knots must be positioned on the grid with integer internal coordinate - // take the knot position accuracy of 0.1*dy - dy = dy / 10.; - double y0 = bins[0]; - double y1 = bins[bins.size() - 1]; - for (auto& y : bins) { - y -= y0; - int iy = int(y / dy + 0.5); - binsInt.push_back(iy); - double yold = y / (y1 - y0) * 2 - 1.; - y = iy * dy; - y = y / (y1 - y0) * 2 - 1.; - if (iuv == 0) { - LOG(info) << "TPC SC splines: convert y bin: " << yold << " -> " << y << " -> " << iy; - } else { - LOG(info) << "TPC SC splines: convert z bin: " << yold << " -> " << y << " -> " << iy; - } + // spline knots must be positioned on the grid with an integer internal coordinate + // we set the knot positioning accuracy to 0.1*pitch + pitch = 0.1 * pitch; + auto& knotsI = knotsInt[dim]; + knotsI.reserve(knotsD.size()); + double u0 = knotsD[0]; + double u1 = knotsD[knotsD.size() - 1]; + for (auto& u : knotsD) { + u -= u0; + int iu = int(u / pitch + 0.5); + knotsI.push_back(iu); + // debug printout: corrected vs original knot positions, scaled to [-1,1] interval + double uorig = u / (u1 - u0) * 2 - 1.; + u = (iu * pitch) / (u1 - u0) * 2 - 1.; + LOG(info) << "TPC SC splines: convert " << (dim == 0 ? "y" : (dim == 1 ? "z" : "-z")) << " bin to the knot: " << uorig << " -> " << u << " -> " << iu; } - if (binsInt.size() < 2) { - binsInt.clear(); - binsInt.push_back(0); - binsInt.push_back(1); + if (knotsI.size() < 2) { // minimum 2 knots + knotsI.clear(); + knotsI.push_back(0); + knotsI.push_back(1); } } - auto& yBinsInt = uvBinsInt[0]; - auto& zBinsInt = uvBinsInt[1]; + auto& yKnotsInt = knotsInt[0]; + auto& zKnotsIntA = knotsInt[1]; + auto& zKnotsIntC = knotsInt[2]; - int nKnotsY = yBinsInt.size(); - int nKnotsZ = zBinsInt.size(); + int nKnotsY = yKnotsInt.size(); + int nKnotsZA = zKnotsIntA.size(); + int nKnotsZC = zKnotsIntC.size(); // std::cout << "n knots Y: " << nKnotsY << std::endl; - // std::cout << "n knots Z: " << nKnotsZ << std::endl; + // std::cout << "n knots Z: " << nKnotsZA << ", " << nKnotsZC << std::endl; const int nRows = geo.getNumberOfRows(); const int nSectors = geo.getNumberOfSectors(); { // create the correction object - const int nCorrectionScenarios = 1; + const int nCorrectionScenarios = 2; // different grids for TPC A and TPC C sides correction.startConstruction(geo, nCorrectionScenarios); // init rows - for (int row = 0; row < geo.getNumberOfRows(); row++) { - correction.setRowScenarioID(row, 0); + for (int iSector = 0; iSector < nSectors; iSector++) { + int id = iSector < geo.getNumberOfSectorsA() ? 0 : 1; + for (int row = 0; row < geo.getNumberOfRows(); row++) { + correction.setRowScenarioID(iSector, row, id); + } } { // init spline scenario TPCFastSpaceChargeCorrection::SplineType spline; - spline.recreate(nKnotsY, &yBinsInt[0], nKnotsZ, &zBinsInt[0]); + spline.recreate(nKnotsY, &yKnotsInt[0], nKnotsZA, &zKnotsIntA[0]); correction.setSplineScenario(0, spline); + spline.recreate(nKnotsY, &yKnotsInt[0], nKnotsZC, &zKnotsIntC[0]); + correction.setSplineScenario(1, spline); } correction.finishConstruction(); } // .. create the correction object @@ -491,19 +500,23 @@ std::unique_ptr TPCFastSpaceChargeCorrect // set the grid borders for (int iSector = 0; iSector < geo.getNumberOfSectors(); iSector++) { for (int iRow = 0; iRow < geo.getNumberOfRows(); iRow++) { - const auto& rowInfo = geo.getRowInfo(iRow); auto& info = correction.getSectorRowInfo(iSector, iRow); const auto& spline = correction.getSpline(iSector, iRow); - double yMin = rowInfo.x * trackResiduals.getY2X(iRow, 0); - double yMax = rowInfo.x * trackResiduals.getY2X(iRow, trackResiduals.getNY2XBins() - 1); - double zMin = rowInfo.x * trackResiduals.getZ2X(0); - double zMax = rowInfo.x * trackResiduals.getZ2X(trackResiduals.getNZ2XBins() - 1); - double lMin = geo.getTPCzLength() - zMax; - double lMax = geo.getTPCzLength() - zMin; - info.gridMeasured.y0 = yMin; - info.gridMeasured.yScale = spline.getGridX1().getUmax() / (yMax - yMin); - info.gridMeasured.l0 = lMin; - info.gridMeasured.lScale = spline.getGridX2().getUmax() / (lMax - lMin); + double rowX = geo.getRowInfo(iRow).x; + double yMin = rowX * trackResiduals.getY2X(iRow, 0); + double yMax = rowX * trackResiduals.getY2X(iRow, trackResiduals.getNY2XBins() - 1); + double zMin = rowX * trackResiduals.getZ2X(0); + double zMax = rowX * trackResiduals.getZ2X(trackResiduals.getNZ2XBins() - 1); + double zOut = zMax; + if (iSector >= geo.getNumberOfSectorsA()) { + // TPC C side + zOut = -zOut; + zMax = -zMin; + zMin = zOut; + } + info.gridMeasured.set(yMin, spline.getGridX1().getUmax() / (yMax - yMin), // y + zMin, spline.getGridX2().getUmax() / (zMax - zMin), // z + zOut, geo.getZreadout(iSector)); // correction scaling region info.gridReal = info.gridMeasured; @@ -514,16 +527,16 @@ std::unique_ptr TPCFastSpaceChargeCorrect LOG(info) << "fast space charge correction helper: preparation took " << watch1.RealTime() << "s"; - for (int processingInverseCorrection = 0; processingInverseCorrection < 2; processingInverseCorrection++) { + for (int processingInverseCorrection = 0; processingInverseCorrection <= 1; processingInverseCorrection++) { TTree* currentTree = (processingInverseCorrection) ? voxResTreeInverse : voxResTree; if (!currentTree) { continue; } - - LOG(info) << "fast space charge correction helper: " << ((processingInverseCorrection) ? "inverse" : "direct") - << " : fill data points from track residuals.. "; + const char* directionName = (processingInverseCorrection) ? "inverse" : "direct"; + LOG(info) << "\n fast space charge correction helper: Process " << directionName + << " correction: fill data points from track residuals.. "; TStopwatch watch3; o2::gpu::TPCFastSpaceChargeCorrectionMap& map = helper->getCorrectionMap(); @@ -548,34 +561,42 @@ std::unique_ptr TPCFastSpaceChargeCorrect { // read data from the tree to vSectorData ROOT::TTreeProcessorMT processor(*currentTree, mNthreads); - + std::string errMsg = std::string("Error reading ") + directionName + " track residuals: "; auto myThread = [&](TTreeReader& readerSubRange) { TTreeReaderValue v(readerSubRange, "voxRes"); while (readerSubRange.Next()) { int iSector = (int)v->bsec; if (iSector < 0 || iSector >= nSectors) { - LOG(fatal) << "Error reading voxels: voxel Sector number " << iSector << " is out of range"; + LOG(fatal) << errMsg << "Sector number " << iSector << " is out of range"; continue; } int iRow = (int)v->bvox[o2::tpc::TrackResiduals::VoxX]; // bin number in x (= pad row) if (iRow < 0 || iRow >= nRows) { - LOG(fatal) << "Row number " << iRow << " is out of range"; + LOG(fatal) << errMsg << "Row number " << iRow << " is out of range"; } + double rowX = trackResiduals.getX(iRow); // X of the pad row int iy = v->bvox[o2::tpc::TrackResiduals::VoxF]; // bin number in y/x 0..14 int iz = v->bvox[o2::tpc::TrackResiduals::VoxZ]; // bin number in z/x 0..4 auto& data = vSectorData[iSector * nRows + iRow][iy * nZ2Xbins + iz]; data.mNentries = (int)v->stat[o2::tpc::TrackResiduals::VoxV]; data.mX = v->stat[o2::tpc::TrackResiduals::VoxX]; - data.mY = v->stat[o2::tpc::TrackResiduals::VoxF]; - data.mZ = v->stat[o2::tpc::TrackResiduals::VoxZ]; + data.mY = v->stat[o2::tpc::TrackResiduals::VoxF] * rowX; + data.mZ = v->stat[o2::tpc::TrackResiduals::VoxZ] * rowX; data.mCx = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResX] : v->D[o2::tpc::TrackResiduals::ResX]; data.mCy = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResY] : v->D[o2::tpc::TrackResiduals::ResY]; data.mCz = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResZ] : v->D[o2::tpc::TrackResiduals::ResZ]; - if (0 && data.mNentries < 1) { - data.mCx = 0.; - data.mCy = 0.; - data.mCz = 0.; - data.mNentries = 1; + if (invertSigns) { + data.mCx *= -1.; + data.mCy *= -1.; + data.mCz *= -1.; + } + if (data.mNentries > 0) { + if (iSector < geo.getNumberOfSectorsA() && data.mZ < 0) { + LOG(error) << errMsg << "fitted Z coordinate " << data.mZ << " is negative for sector " << iSector; + } + if (iSector >= geo.getNumberOfSectorsA() && data.mZ > 0) { + LOG(error) << errMsg << "fitted Z coordinate " << data.mZ << " is positive for sector " << iSector; + } } } }; @@ -599,7 +620,6 @@ std::unique_ptr TPCFastSpaceChargeCorrect // LOG(info) << "Processing Sector " << iSector << " row " << iRow; // complete the voxel data - { int xBin = iRow; double x = trackResiduals.getX(xBin); // radius of the pad row @@ -619,36 +639,28 @@ std::unique_ptr TPCFastSpaceChargeCorrect if (iSector >= geo.getNumberOfSectorsA()) { vox.mZ = -vox.mZ; } - data.mY *= x; - data.mZ *= x; - /* - if ( fabs(x - data.mX) > 0.01 || fabs(vox.mY - data.mY) > 5. || fabs(vox.mZ - data.mZ) > 5.) { - std::cout - << " sector " << iSector << " row " << iRow - << " voxel x " << x << " y " << vox.mY << " z " << vox.mZ - << " data x " << data.mX << " y " << data.mY << " z " << data.mZ - << std::endl; - } - */ - if (0) { // debug: always use voxel center instead of the mean position - data.mY = vox.mY; - data.mZ = vox.mZ; - } - if (data.mNentries < 1) { // no data + if (data.mNentries > 0) { // voxel contains data + vox.mSmoothingStep = 0; // take original data + isDataFound = true; + if (fabs(x - data.mX) > 1. || fabs(vox.mY - data.mY) > 5. || fabs(vox.mZ - data.mZ) > 5.) { + std::cout << directionName << ": fitted voxel is too far from the nominal position: " + << " sector " << iSector << " row " << iRow + << " center x " << x << " y " << vox.mY << " z " << vox.mZ + << " fitted x " << data.mX << " y " << data.mY << " z " << data.mZ + << std::endl; + } + } else { // no data, take voxel center position data.mCx = 0.; data.mCy = 0.; data.mCz = 0.; + data.mX = x; + data.mY = vox.mY; + data.mZ = vox.mZ; + vox.mSmoothingStep = 100; // fill this data point with smoothed values from the neighbours + } + if (0) { // debug: always use voxel center instead of the mean position data.mY = vox.mY; data.mZ = vox.mZ; - vox.mSmoothingStep = 100; - } else { // voxel contains data - if (invertSigns) { - data.mCx *= -1.; - data.mCy *= -1.; - data.mCz *= -1.; - } - vox.mSmoothingStep = 0; // original data - isDataFound = true; } } } @@ -734,13 +746,13 @@ std::unique_ptr TPCFastSpaceChargeCorrect if (vox2.mSmoothingStep > 2) { LOG(fatal) << "empty voxel is not repared: y " << iy2 << " z " << iz2; } - double y1 = vox1.mY; - double z1 = vox1.mZ; + double y1 = data1.mY; + double z1 = data1.mZ; double cx1 = data1.mCx; double cy1 = data1.mCy; double cz1 = data1.mCz; - double y2 = vox2.mY; - double z2 = vox2.mZ; + double y2 = data2.mY; + double z2 = data2.mZ; double cx2 = data2.mCx; double cy2 = data2.mCy; double cz2 = data2.mCz; @@ -849,6 +861,9 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector splineParameters; for (int row = iThread; row < mGeo.getNumberOfRows(); row += mNthreads) { + auto& sectorRowInfo = correction.getSectorRowInfo(sector, row); + sectorRowInfo.gridReal = sectorRowInfo.gridMeasured; + TPCFastSpaceChargeCorrection::SplineType spline = correction.getSpline(sector, row); helper.setSpline(spline, 10, 10); @@ -897,33 +912,16 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector(mFlatBufferPtr + sectorDataOffset); - bufferSize = sectorDataOffset + mSectorDataSizeBytes[is] * mGeo.getNumberOfSectors(); + size_t splineDataOffset = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); + mSplineData[is] = reinterpret_cast(mFlatBufferPtr + splineDataOffset); + bufferSize = splineDataOffset + mDataSizeBytes[is]; } } @@ -199,14 +199,8 @@ void TPCFastSpaceChargeCorrection::print() const mGeo.print(); LOG(info) << " mNumberOfScenarios = " << mNumberOfScenarios; LOG(info) << " mTimeStamp = " << mTimeStamp; - LOG(info) << " mSectorDataSizeBytes = " << mSectorDataSizeBytes[0] << " " << mSectorDataSizeBytes[1] << " " << mSectorDataSizeBytes[2]; - { - LOG(info) << " TPC rows: "; - for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { - const RowInfo& r = mRowInfos[i]; - LOG(info) << " tpc row " << i << ": splineScenarioID = " << r.splineScenarioID << " dataOffsetBytes = " << r.dataOffsetBytes; - } - } + LOG(info) << " mDataSizeBytes = " << mDataSizeBytes[0] << " " << mDataSizeBytes[1] << " " << mDataSizeBytes[2]; + if (mScenarioPtr) { for (int32_t i = 0; i < mNumberOfScenarios; i++) { LOG(info) << " SplineScenario " << i << ": "; @@ -255,8 +249,23 @@ void TPCFastSpaceChargeCorrection::startConstruction(const TPCFastTransformGeo& assert(mConstructionScenarios != nullptr); - for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { - mRowInfos[i].splineScenarioID = -1; + for (int32_t i = 0; i < mGeo.getNumberOfSectors(); i++) { + mSectorInfo[i].vMax1 = 0.; + for (int32_t j = 0; j < mGeo.getNumberOfRows(); j++) { + auto& row = mSectorRowInfos[mGeo.getMaxNumberOfRows() * i + j]; + row.splineScenarioID = -1; + row.gridReal = {}; + row.gridMeasured = {}; + row.dataOffsetBytes[0] = 0; + row.dataOffsetBytes[1] = 0; + row.dataOffsetBytes[2] = 0; + row.minCorr[0] = 0; + row.minCorr[1] = 0; + row.minCorr[2] = 0; + row.maxCorr[0] = 0; + row.maxCorr[1] = 0; + row.maxCorr[2] = 0; + } } for (int32_t i = 0; i < mNumberOfScenarios; i++) { @@ -268,18 +277,18 @@ void TPCFastSpaceChargeCorrection::startConstruction(const TPCFastTransformGeo& mScenarioPtr = nullptr; for (int32_t s = 0; s < 3; s++) { mSplineData[s] = nullptr; - mSectorDataSizeBytes[s] = 0; + mDataSizeBytes[s] = 0; } mClassVersion = 4; } -void TPCFastSpaceChargeCorrection::setRowScenarioID(int32_t iRow, int32_t iScenario) +void TPCFastSpaceChargeCorrection::setRowScenarioID(int32_t iSector, int32_t iRow, int32_t iScenario) { /// Initializes a TPC row assert(mConstructionMask & ConstructionState::InProgress); + assert(iSector >= 0 && iSector < mGeo.getNumberOfSectors()); assert(iRow >= 0 && iRow < mGeo.getNumberOfRows() && iScenario >= 0 && iScenario < mNumberOfScenarios); - - RowInfo& row = mRowInfos[iRow]; + auto& row = getSectorRowInfo(iSector, iRow); row.splineScenarioID = iScenario; for (int32_t s = 0; s < 3; s++) { row.dataOffsetBytes[s] = 0; @@ -302,9 +311,14 @@ void TPCFastSpaceChargeCorrection::finishConstruction() assert(mConstructionMask & ConstructionState::InProgress); - for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { - assert(mRowInfos[i].splineScenarioID >= 0); + for (int32_t i = 0; i < mGeo.getNumberOfSectors(); i++) { + for (int32_t j = 0; j < mGeo.getNumberOfRows(); j++) { + SectorRowInfo& row = getSectorRowInfo(i, j); + assert(row.splineScenarioID >= 0); + assert(row.splineScenarioID < mNumberOfScenarios); + } } + for (int32_t i = 0; i < mNumberOfScenarios; i++) { assert(mConstructionScenarios[i].isConstructed()); } @@ -324,18 +338,20 @@ void TPCFastSpaceChargeCorrection::finishConstruction() scBufferSize = alignSize(scBufferSize + sp.getFlatBufferSize(), sp.getBufferAlignmentBytes()); } size_t bufferSize = scBufferOffsets[0] + scBufferSize; - size_t sectorDataOffset[3]; + size_t splineDataOffset[3]; for (int32_t is = 0; is < 3; is++) { - sectorDataOffset[is] = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); - mSectorDataSizeBytes[is] = 0; - for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { - RowInfo& row = mRowInfos[i]; - SplineType& spline = mConstructionScenarios[row.splineScenarioID]; - row.dataOffsetBytes[is] = alignSize(mSectorDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); - mSectorDataSizeBytes[is] = row.dataOffsetBytes[is] + spline.getSizeOfParameters(); + splineDataOffset[is] = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); + mDataSizeBytes[is] = 0; + for (int32_t i = 0; i < mGeo.getNumberOfSectors(); i++) { + for (int32_t j = 0; j < mGeo.getNumberOfRows(); j++) { + SectorRowInfo& row = getSectorRowInfo(i, j); + SplineType& spline = mConstructionScenarios[row.splineScenarioID]; + row.dataOffsetBytes[is] = alignSize(mDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); + mDataSizeBytes[is] = row.dataOffsetBytes[is] + spline.getSizeOfParameters(); + } } - mSectorDataSizeBytes[is] = alignSize(mSectorDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); - bufferSize = sectorDataOffset[is] + mSectorDataSizeBytes[is] * mGeo.getNumberOfSectors(); + mDataSizeBytes[is] = alignSize(mDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); + bufferSize = splineDataOffset[is] + mDataSizeBytes[is]; } FlatObject::finishConstruction(bufferSize); @@ -350,7 +366,7 @@ void TPCFastSpaceChargeCorrection::finishConstruction() } for (int32_t is = 0; is < 3; is++) { - mSplineData[is] = reinterpret_cast(mFlatBufferPtr + sectorDataOffset[is]); + mSplineData[is] = reinterpret_cast(mFlatBufferPtr + splineDataOffset[is]); } releaseConstructionMemory(); @@ -363,9 +379,9 @@ GPUd() void TPCFastSpaceChargeCorrection::setNoCorrection() { // initialise all corrections to 0. for (int32_t sector = 0; sector < mGeo.getNumberOfSectors(); sector++) { - double vLength = mGeo.getTPCzLength(); - SectorInfo& sectorInfo = getSectorInfo(sector); - sectorInfo.vMax = vLength; + + getSectorInfo(sector).vMax1 = mGeo.getTPCzLength(); + for (int32_t row = 0; row < mGeo.getNumberOfRows(); row++) { const SplineType& spline = getSpline(sector, row); @@ -385,10 +401,12 @@ GPUd() void TPCFastSpaceChargeCorrection::setNoCorrection() SectorRowInfo& info = getSectorRowInfo(sector, row); - info.gridMeasured.y0 = mGeo.getRowInfo(row).getYmin(); - info.gridMeasured.yScale = spline.getGridX1().getUmax() / mGeo.getRowInfo(row).getYwidth(); - info.gridMeasured.l0 = 0.f; - info.gridMeasured.lScale = spline.getGridX2().getUmax() / vLength; + float y0 = mGeo.getRowInfo(row).getYmin(); + float yScale = spline.getGridX1().getUmax() / mGeo.getRowInfo(row).getYwidth(); + float z0 = mGeo.getZmin(sector); + float zScale = spline.getGridX2().getUmax() / mGeo.getTPCzLength(); + float zReadout = mGeo.getZreadout(sector); + info.gridMeasured.set(y0, yScale, z0, zScale, zReadout, zReadout); info.gridReal = info.gridMeasured; } // row @@ -399,8 +417,10 @@ void TPCFastSpaceChargeCorrection::constructWithNoCorrection(const TPCFastTransf { const int32_t nCorrectionScenarios = 1; startConstruction(geo, nCorrectionScenarios); - for (int32_t row = 0; row < geo.getNumberOfRows(); row++) { - setRowScenarioID(row, 0); + for (int32_t sector = 0; sector < geo.getNumberOfSectors(); sector++) { + for (int32_t row = 0; row < geo.getNumberOfRows(); row++) { + setRowScenarioID(sector, row, 0); + } } { TPCFastSpaceChargeCorrection::SplineType spline; @@ -456,20 +476,13 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) if (prn) { LOG(info) << "check inverse transform for sector " << sector; } - double vLength = mGeo.getTPCzLength(); + MaxValue maxDsector[3]; for (int32_t row = 0; row < mGeo.getNumberOfRows(); row++) { double x = mGeo.getRowInfo(row).x; auto [y0, y1] = mGeo.getRowInfo(row).getYrange(); auto [z0, z1] = mGeo.getZrange(sector); - // grid borders - if (sector < mGeo.getNumberOfSectorsA()) { - z1 = vLength - getSectorRowInfo(sector, row).gridMeasured.l0; - } else { - z0 = getSectorRowInfo(sector, row).gridMeasured.l0 - vLength; - } - double stepY = (y1 - y0) / 100.; double stepZ = (z1 - z0) / 100.; MaxValue maxDrow[3]; @@ -479,7 +492,7 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) double realX = x + dx; double realY = y + dy; double realZ = z + dz; - if (!isLocalInsideGrid(sector, row, y, z) || !isLocalInsideGrid(sector, row, realY, realZ)) { + if (!isLocalInsideGrid(sector, row, y, z) || !isRealLocalInsideGrid(sector, row, realY, realZ)) { continue; } double r2 = realX * realX + realY * realY; diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index 4564d584c8dce..7112a04b364c6 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -43,31 +43,58 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// \brief The struct contains necessary info for TPC padrow /// struct RowInfo { - int32_t splineScenarioID{0}; ///< scenario index (which of Spline2D splines to use) - size_t dataOffsetBytes[3]{0}; ///< offset for the spline data withing the TPC sector ClassDefNV(RowInfo, 1); }; struct GridInfo { - float y0{0.f}; ///< Y coordinate of the U-grid start - float yScale{0.f}; //< scale Y to U-grid coordinate - float l0{0.f}; ///< Drift Length coordinate of the V-grid start - float lScale{0.f}; //< scale Drift Length to V-grid coordinate + private: + float y0{0.f}; ///< Y coordinate of the U-grid start + float yScale{0.f}; //< scale Y to U-grid coordinate + float z0{0.f}; ///< Z coordinate of the V-grid start + float zScale{0.f}; //< scale Z to V-grid coordinate + float zOut{0.f}; // outer z of the grid; + float splineScalingWithZ{0.f}; ///< spline scaling factor in the Z region between the zOut and the readout plane + + public: + void set(float y0, float yScale, float z0, float zScale, float zOut, float zReadout) + { + this->y0 = y0; + this->yScale = yScale; + this->z0 = z0; + this->zScale = zScale; + this->zOut = zOut; + // no scaling when the distance to the readout is too small + this->splineScalingWithZ = fabs(zReadout - zOut) > 1. ? 1. / (zReadout - zOut) : 0.; + } + + float getY0() const { return y0; } + float getYscale() const { return yScale; } + float getZ0() const { return z0; } + float getZscale() const { return zScale; } + + float getSpineScaleForZ(float z) const + { + return 1.f - GPUCommonMath::Clamp((z - zOut) * splineScalingWithZ, 0.f, 1.f); + } + + /// convert local y, z to internal grid coordinates u,v, and spline scale + std::tuple convLocalToGridUntruncated(float y, float z) const + { + return {(y - y0) * yScale, (z - z0) * zScale, getSpineScaleForZ(z)}; + } - float getScale(float l) const + /// convert internal grid coordinates u,v to local y, z + std::tuple convGridToLocal(float gridU, float gridV) const { - if (l < 0.f) { // outside of the TPC - return 0.f; - } - if (l < l0) { // between the grid and the readout - return l / l0; - } - return 1.f; // inside the grid + return {y0 + gridU / yScale, z0 + gridV / zScale}; } ClassDefNV(GridInfo, 1); }; struct SectorRowInfo { + int32_t splineScenarioID{0}; ///< scenario index (which of Spline2D splines to use) + size_t dataOffsetBytes[3]{0}; ///< offset for the spline data withing a TPC sector + GridInfo gridMeasured; ///< grid info for measured coordinates GridInfo gridReal; ///< grid info for real coordinates @@ -118,7 +145,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject }; struct SectorInfo { - float vMax{0.f}; ///< Max value of V coordinate + float vMax1{0.f}; ///< Max value of V coordinate ClassDefNV(SectorInfo, 1); }; @@ -170,7 +197,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject void startConstruction(const TPCFastTransformGeo& geo, int32_t numberOfSplineScenarios); /// Initializes a TPC row - void setRowScenarioID(int32_t iRow, int32_t iScenario); + void setRowScenarioID(int32_t iSector, int32_t iRow, int32_t iScenario); /// Sets approximation scenario void setSplineScenario(int32_t scenarioIndex, const SplineType& spline); @@ -259,6 +286,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject GPUd() std::tuple convGridToRealLocal(int32_t sector, int32_t row, float u, float v) const; GPUd() bool isLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const; + GPUd() bool isRealLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const; /// TPC geometry information GPUd() const TPCFastTransformGeo& getGeometry() const @@ -333,7 +361,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject char* mSplineData[3]; //! (transient!!) pointer to the spline data in the flat buffer - size_t mSectorDataSizeBytes[3]; ///< size of the data for one sector in the flat buffer + size_t mDataSizeBytes[3]; ///< size of the data for one sector in the flat buffer float fInterpolationSafetyMargin{0.1f}; // 10% area around the TPC row. Outside of this area the interpolation returns the boundary values. @@ -356,29 +384,25 @@ class TPCFastSpaceChargeCorrection : public FlatObject GPUdi() const TPCFastSpaceChargeCorrection::SplineType& TPCFastSpaceChargeCorrection::getSpline(int32_t sector, int32_t row) const { /// Gives const pointer to spline - const RowInfo& rowInfo = mRowInfos[row]; - return mScenarioPtr[rowInfo.splineScenarioID]; + return mScenarioPtr[getSectorRowInfo(sector, row).splineScenarioID]; } GPUdi() TPCFastSpaceChargeCorrection::SplineType& TPCFastSpaceChargeCorrection::getSpline(int32_t sector, int32_t row) { /// Gives pointer to spline - const RowInfo& rowInfo = mRowInfos[row]; - return mScenarioPtr[rowInfo.splineScenarioID]; + return mScenarioPtr[getSectorRowInfo(sector, row).splineScenarioID]; } GPUdi() float* TPCFastSpaceChargeCorrection::getSplineData(int32_t sector, int32_t row, int32_t iSpline) { /// Gives pointer to spline data - const RowInfo& rowInfo = mRowInfos[row]; - return reinterpret_cast(mSplineData[iSpline] + mSectorDataSizeBytes[iSpline] * sector + rowInfo.dataOffsetBytes[iSpline]); + return reinterpret_cast(mSplineData[iSpline] + getSectorRowInfo(sector, row).dataOffsetBytes[iSpline]); } GPUdi() const float* TPCFastSpaceChargeCorrection::getSplineData(int32_t sector, int32_t row, int32_t iSpline) const { /// Gives pointer to spline data - const RowInfo& rowInfo = mRowInfos[row]; - return reinterpret_cast(mSplineData[iSpline] + mSectorDataSizeBytes[iSpline] * sector + rowInfo.dataOffsetBytes[iSpline]); + return reinterpret_cast(mSplineData[iSpline] + getSectorRowInfo(sector, row).dataOffsetBytes[iSpline]); } GPUdi() TPCFastSpaceChargeCorrection::SplineTypeInvX& TPCFastSpaceChargeCorrection::getSplineInvX(int32_t sector, int32_t row) @@ -433,37 +457,35 @@ GPUdi() std::tuple TPCFastSpaceChargeCorrection::convLocalT { /// convert local y, z to internal grid coordinates u,v /// return values: u, v, scaling factor - const auto& info = getSectorRowInfo(sector, row); const SplineType& spline = getSpline(sector, row); - - float l = mGeo.convZtoDriftLength(sector, z); - float scale = info.gridMeasured.getScale(l); - float gridU = (y - info.gridMeasured.y0) * info.gridMeasured.yScale; - float gridV = (l - info.gridMeasured.l0) * info.gridMeasured.lScale; - - // shrink to the grid area + auto [gridU, gridV, scale] = getSectorRowInfo(sector, row).gridMeasured.convLocalToGridUntruncated(y, z); + // shrink to the grid gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); gridV = GPUCommonMath::Clamp(gridV, 0.f, (float)spline.getGridX2().getUmax()); - return {gridU, gridV, scale}; } GPUdi() bool TPCFastSpaceChargeCorrection::isLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const { - /// ccheck if local y, z are inside the grid - - const auto& info = getSectorRowInfo(sector, row); + /// check if local y, z are inside the grid + auto [gridU, gridV, scale] = getSectorRowInfo(sector, row).gridMeasured.convLocalToGridUntruncated(y, z); const auto& spline = getSpline(sector, row); - float l = mGeo.convZtoDriftLength(sector, z); - - float gridU = (y - info.gridMeasured.y0) * info.gridMeasured.yScale; - float gridV = (l - info.gridMeasured.l0) * info.gridMeasured.lScale; - - // shrink to the grid area - if (gridU < 0.f || gridU > (float)spline.getGridX1().getUmax()) { + // shrink to the grid + if (gridU < 0.f || gridU > (float)spline.getGridX1().getUmax() || // + gridV < 0.f || gridV > (float)spline.getGridX2().getUmax()) { return false; } - if (gridV < 0.f || gridV > (float)spline.getGridX2().getUmax()) { + return true; +} + +GPUdi() bool TPCFastSpaceChargeCorrection::isRealLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const +{ + /// check if local y, z are inside the grid + auto [gridU, gridV, scale] = getSectorRowInfo(sector, row).gridReal.convLocalToGridUntruncated(y, z); + const auto& spline = getSpline(sector, row); + // shrink to the grid + if (gridU < 0.f || gridU > (float)spline.getGridX1().getUmax() || // + gridV < 0.f || gridV > (float)spline.getGridX2().getUmax()) { return false; } return true; @@ -472,39 +494,24 @@ GPUdi() bool TPCFastSpaceChargeCorrection::isLocalInsideGrid(int32_t sector, int GPUdi() std::tuple TPCFastSpaceChargeCorrection::convGridToLocal(int32_t sector, int32_t row, float gridU, float gridV) const { /// convert internal grid coordinates u,v to local y, z - const SectorRowInfo& info = getSectorRowInfo(sector, row); - float y = info.gridMeasured.y0 + gridU / info.gridMeasured.yScale; - float l = info.gridMeasured.l0 + gridV / info.gridMeasured.lScale; - float z = mGeo.convDriftLengthToZ(sector, l); - return {y, z}; + return getSectorRowInfo(sector, row).gridMeasured.convGridToLocal(gridU, gridV); } GPUdi() std::tuple TPCFastSpaceChargeCorrection::convRealLocalToGrid(int32_t sector, int32_t row, float y, float z) const { /// convert real y, z to the internal grid coordinates + scale - const auto& info = getSectorRowInfo(sector, row); - const auto& spline = getSpline(sector, row); - - float l = mGeo.convZtoDriftLength(sector, z); - float scale = info.gridReal.getScale(l); - float gridU = (y - info.gridReal.y0) * info.gridReal.yScale; - float gridV = (l - info.gridReal.l0) * info.gridReal.lScale; - - // shrink to the grid area + const SplineType& spline = getSpline(sector, row); + auto [gridU, gridV, scale] = getSectorRowInfo(sector, row).gridReal.convLocalToGridUntruncated(y, z); + // shrink to the grid gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); gridV = GPUCommonMath::Clamp(gridV, 0.f, (float)spline.getGridX2().getUmax()); - return {gridU, gridV, scale}; } GPUdi() std::tuple TPCFastSpaceChargeCorrection::convGridToRealLocal(int32_t sector, int32_t row, float gridU, float gridV) const { /// convert internal grid coordinates u,v to the real y, z - const SectorRowInfo& info = getSectorRowInfo(sector, row); - float y = info.gridReal.y0 + gridU / info.gridReal.yScale; - float l = info.gridReal.l0 + gridV / info.gridReal.lScale; - float z = mGeo.convDriftLengthToZ(sector, l); - return {y, z}; + return getSectorRowInfo(sector, row).gridReal.convGridToLocal(gridU, gridV); } GPUdi() std::tuple TPCFastSpaceChargeCorrection::getCorrectionLocal(int32_t sector, int32_t row, float y, float z) const diff --git a/GPU/TPCFastTransformation/TPCFastTransform.cxx b/GPU/TPCFastTransformation/TPCFastTransform.cxx index 625f70c1710a1..42c4c57ffa086 100644 --- a/GPU/TPCFastTransformation/TPCFastTransform.cxx +++ b/GPU/TPCFastTransformation/TPCFastTransform.cxx @@ -103,10 +103,6 @@ void TPCFastTransform::startConstruction(const TPCFastSpaceChargeCorrection& cor mApplyCorrection = 1; mT0 = 0.f; mVdrift = 0.f; - mVdriftCorrY = 0.f; - mLdriftCorr = 0.f; - mTOFcorr = 0.f; - mPrimVtxZ = 0.f; mLumi = DEFLUMI; mLumiError = 0.f; mIDC = DEFIDC; diff --git a/GPU/TPCFastTransformation/TPCFastTransform.h b/GPU/TPCFastTransformation/TPCFastTransform.h index 8807c0e3206f4..3b08296525fc7 100644 --- a/GPU/TPCFastTransformation/TPCFastTransform.h +++ b/GPU/TPCFastTransformation/TPCFastTransform.h @@ -551,7 +551,7 @@ GPUdi() void TPCFastTransform::Transform(int32_t sector, int32_t row, float pad, GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t sector, float time, float& z, float maxTimeBin) const { float l = (time - mT0 - maxTimeBin) * mVdrift; // drift length cm - z = getGeometry().convDriftLengthToZ(sector, l); + z = getGeometry().convDriftLengthToZ1(sector, l); } GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float maxTimeBin) const @@ -590,7 +590,7 @@ GPUdi() void TPCFastTransform::TransformIdealZ(int32_t sector, float time, float /// float l = (time - mT0 - vertexTime) * mVdrift; // drift length cm - z = getGeometry().convDriftLengthToZ(sector, l); + z = getGeometry().convDriftLengthToZ1(sector, l); } GPUdi() void TPCFastTransform::TransformIdeal(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime) const diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.h b/GPU/TPCFastTransformation/TPCFastTransformGeo.h index bbb94dcb8bedd..89b099ec63127 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.h +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.h @@ -126,6 +126,9 @@ class TPCFastTransformGeo /// Gives Z range for the corresponding TPC side GPUd() std::tuple getZrange(int32_t sector) const; + GPUd() float getZmin(int32_t sector) const; + GPUd() float getZmax(int32_t sector) const; + GPUd() float getZreadout(int32_t sector) const; /// _______________ Conversion of coordinate systems __________ @@ -139,10 +142,10 @@ class TPCFastTransformGeo GPUd() std::tuple convPadDriftLengthToLocal(int32_t sector, int32_t row, float pad, float driftLength) const; /// convert DriftLength -> Local c.s. - GPUd() float convDriftLengthToZ(int32_t sector, float driftLength) const; + GPUd() float convDriftLengthToZ1(int32_t sector, float driftLength) const; /// convert Z to DriftLength - GPUd() float convZtoDriftLength(int32_t sector, float z) const; + GPUd() float convZtoDriftLength1(int32_t sector, float z) const; /// convert Local c.s. -> Pad, DriftLength GPUd() std::tuple convLocalToPadDriftLength(int32_t sector, int32_t row, float y, float z) const; @@ -181,7 +184,7 @@ class TPCFastTransformGeo float mTPCzLength = 0.f; ///< Z length of one TPC side (A or C) SectorInfo mSectorInfos[NumberOfSectors + 1]; ///< array of sector information [fixed size] - RowInfo mRowInfos[MaxNumberOfRows + 1]; ///< array of row information [fixed size] + RowInfo mRowInfos[MaxNumberOfRows + 1]; ///< array of row information [fixed size] ClassDefNV(TPCFastTransformGeo, 3); }; @@ -242,13 +245,13 @@ GPUdi() std::tuple TPCFastTransformGeo::convPadDriftLengthToLocal( return {y, z}; } -GPUdi() float TPCFastTransformGeo::convDriftLengthToZ(int32_t sector, float driftLength) const +GPUdi() float TPCFastTransformGeo::convDriftLengthToZ1(int32_t sector, float driftLength) const { /// convert DriftLength -> Local c.s. return (sector < NumberOfSectorsA) ? (mTPCzLength - driftLength) : (driftLength - mTPCzLength); } -GPUdi() float TPCFastTransformGeo::convZtoDriftLength(int32_t sector, float z) const +GPUdi() float TPCFastTransformGeo::convZtoDriftLength1(int32_t sector, float z) const { /// convert Z to DriftLength return (sector < NumberOfSectorsA) ? (mTPCzLength - z) : (z + mTPCzLength); @@ -264,6 +267,36 @@ GPUdi() std::tuple TPCFastTransformGeo::getZrange(int32_t sector) } } +GPUdi() float TPCFastTransformGeo::getZmin(int32_t sector) const +{ + /// z min for the sector + if (sector < NumberOfSectorsA) { // TPC side A + return 0.f; + } else { // TPC side C + return -mTPCzLength; + } +} + +GPUdi() float TPCFastTransformGeo::getZmax(int32_t sector) const +{ + /// z max for the sector + if (sector < NumberOfSectorsA) { // TPC side A + return mTPCzLength; + } else { // TPC side C + return 0.f; + } +} + +GPUdi() float TPCFastTransformGeo::getZreadout(int32_t sector) const +{ + /// z readout for the sector + if (sector < NumberOfSectorsA) { // TPC side A + return mTPCzLength; + } else { // TPC side C + return -mTPCzLength; + } +} + GPUdi() std::tuple TPCFastTransformGeo::convLocalToPadDriftLength(int32_t sector, int32_t row, float y, float z) const { /// convert Local c.s. -> Pad, DriftLength diff --git a/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h b/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h index fc15506d5397c..916695a3be1c7 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h +++ b/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h @@ -70,6 +70,7 @@ #pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection + ; #pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::SectorInfo + ; #pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::SectorRowInfo + ; +#pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::GridInfo + ; #pragma link C++ class o2::gpu::CorrectionMapsHelper + ; #pragma link C++ struct o2::gpu::MultivariatePolynomialContainer + ; diff --git a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C index f0c03d9f5f081..974582792266b 100644 --- a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C +++ b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C @@ -150,7 +150,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* o2::tpc::TPCFastSpaceChargeCorrectionHelper* corrHelper = o2::tpc::TPCFastSpaceChargeCorrectionHelper::instance(); corrHelper->setNthreadsToMaximum(); - // corrHelper->setNthreads(1); + corrHelper->setNthreads(1); auto corrPtr = corrHelper->createFromTrackResiduals(trackResiduals, voxResTree, voxResTreeInverse, useSmoothed, invertSigns); @@ -185,10 +185,10 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* for (int32_t iRow = 0; iRow < geo.getNumberOfRows(); iRow++) { auto& info = corr.getSectorRowInfo(iSector, iRow); std::cout << "sector " << iSector << " row " << iRow - << " gridY0 " << info.gridMeasured.y0 << " gridL0 " << info.gridMeasured.l0 - << " scaleYtoGrid " << info.gridMeasured.yScale << " scaleLtoGrid " << info.gridMeasured.lScale - << " gridRealY0 " << info.gridReal.y0 << " gridRealL0 " << info.gridReal.l0 - << " scaleRealYtoGrid " << info.gridReal.yScale << " scaleRealLtoGrid " << info.gridReal.lScale + << " gridY0 " << info.gridMeasured.getY0() << " gridZ0 " << info.gridMeasured.getZ0() + << " scaleYtoGrid " << info.gridMeasured.getYscale() << " scaleLtoGrid " << info.gridMeasured.getZscale() + << " gridRealY0 " << info.gridReal.getY0() << " gridRealZ0 " << info.gridReal.getZ0() + << " scaleRealYtoGrid " << info.gridReal.getYscale() << " scaleRealLtoGrid " << info.gridReal.getZscale() << std::endl; } } @@ -379,7 +379,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* const auto& gridY = corr.getSpline(iSector, iRow).getGridX1(); const auto& gridZ = corr.getSpline(iSector, iRow).getGridX2(); if (iSector == 0 && iRow == 0) { - std::cout << "spline scenario " << corr.getRowInfo(iRow).splineScenarioID << std::endl; + std::cout << "spline scenario " << corr.getSectorRowInfo(iSector, iRow).splineScenarioID << std::endl; std::cout << "spline grid Y: u = " << 0 << ".." << gridY.getUmax() << ", x = " << gridY.getXmin() << ".." << gridY.getXmax() << std::endl; std::cout << "spline grid Z: u = " << 0 << ".." << gridZ.getUmax() << ", x = " << gridZ.getXmin() << ".." << gridZ.getXmax() << std::endl; } From 19120c81ab88c5be50b5e32acfde98500a602ab7 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Wed, 20 Aug 2025 07:50:56 +0000 Subject: [PATCH 095/285] TPC Splines: correct biased voxels; features for debugging --- .../TPCFastSpaceChargeCorrectionHelper.h | 32 +- .../TPCFastSpaceChargeCorrectionHelper.cxx | 75 ++- .../macro/TPCFastTransformInit.C | 521 +++++++++++------- 3 files changed, 419 insertions(+), 209 deletions(-) diff --git a/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h b/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h index e8afd9be97d5f..40c5634b4f1e8 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h @@ -90,11 +90,16 @@ class TPCFastSpaceChargeCorrectionHelper /// \param voxResTreeInverse TTree with inverse voxel residuals /// \param useSmoothed if true, use smoothed residuals /// \param invertSigns if true, invert the signs of the residuals + /// \param fitPointsDirect debug: pointer to the data used for the direct correction + /// \param fitPointsInverse debug: pointer to the data used for the inverse correction /// \return pointer to the created TPCFastSpaceChargeCorrection object /// \note voxel trees wont be changed. They are read as non-const because of the ROOT::TTreeProcessorMT interface + /// std::unique_ptr createFromTrackResiduals( const o2::tpc::TrackResiduals& trackResiduals, TTree* voxResTree, TTree* voxResTreeInverse, // - bool useSmoothed, bool invertSigns); + bool useSmoothed, bool invertSigns, // + TPCFastSpaceChargeCorrectionMap* fitPointsDirect = nullptr, + TPCFastSpaceChargeCorrectionMap* fitPointsInverse = nullptr); /// _______________ Utilities ________________________ @@ -116,10 +121,28 @@ class TPCFastSpaceChargeCorrectionHelper /// \param additionalCorrections vector of pairs of additional corrections and their scaling factors /// \param prn printout flag /// \return main correction merged with additional corrections - void MergeCorrections( + void mergeCorrections( o2::gpu::TPCFastSpaceChargeCorrection& mainCorrection, float scale, const std::vector>& additionalCorrections, bool prn); + /// how far the voxel mean is allowed to be outside of the voxel (1.1 means 10%) + void setVoxelMeanValidityRange(double range) + { + mVoxelMeanValidityRange = range; + } + + double getVoxelMeanValidityRange() const { return mVoxelMeanValidityRange; } + + /// debug: if true, use voxel centers instead of the fitted positions for correction + void setDebugUseVoxelCenters(); + + bool isDebugUseVoxelCenters() const { return mDebugUseVoxelCenters; } + + /// debug: if true, mirror the data from the A side to the C side of the TPC + void setDebugMirrorAdata2C(); + + bool isDebugMirrorAdata2C() const { return mDebugMirrorAdata2C; } + private: /// geometry initialization void initGeometry(); @@ -133,6 +156,11 @@ class TPCFastSpaceChargeCorrectionHelper TPCFastSpaceChargeCorrectionMap mCorrectionMap{0, 0}; + double mVoxelMeanValidityRange{1.1}; ///< debug: how far the voxel mean is allowed to be outside of the voxel (1.1 means 10%) + + bool mDebugUseVoxelCenters{false}; ///< debug: if true, use voxel centers instead of the fitted positions for correction + bool mDebugMirrorAdata2C{false}; ///< debug: if true, mirror the data from the A side to the C side of the TPC + ClassDefNV(TPCFastSpaceChargeCorrectionHelper, 0); }; diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index d2f6cf57b0de7..6122c5717fcbb 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -33,6 +33,7 @@ #include "TTreeReaderValue.h" #include "ROOT/TTreeProcessorMT.hxx" #include +#include using namespace o2::gpu; @@ -381,7 +382,9 @@ void TPCFastSpaceChargeCorrectionHelper::testGeometry(const TPCFastTransformGeo& } std::unique_ptr TPCFastSpaceChargeCorrectionHelper::createFromTrackResiduals( - const o2::tpc::TrackResiduals& trackResiduals, TTree* voxResTree, TTree* voxResTreeInverse, bool useSmoothed, bool invertSigns) + const o2::tpc::TrackResiduals& trackResiduals, TTree* voxResTree, TTree* voxResTreeInverse, bool useSmoothed, bool invertSigns, + TPCFastSpaceChargeCorrectionMap* fitPointsDirect, + TPCFastSpaceChargeCorrectionMap* fitPointsInverse) { // create o2::gpu::TPCFastSpaceChargeCorrection from o2::tpc::TrackResiduals::VoxRes voxel tree @@ -603,6 +606,24 @@ std::unique_ptr TPCFastSpaceChargeCorrect processor.Process(myThread); } + // debug: mirror the data for TPC C side + + if (mDebugMirrorAdata2C) { + for (int iSector = 0; iSector < geo.getNumberOfSectorsA(); iSector++) { + for (int iRow = 0; iRow < nRows; iRow++) { + for (int iy = 0; iy < nY2Xbins; iy++) { + for (int iz = 0; iz < nZ2Xbins; iz++) { + auto& dataA = vSectorData[iSector * nRows + iRow][iy * nZ2Xbins + iz]; + auto& dataC = vSectorData[(iSector + geo.getNumberOfSectorsA()) * nRows + iRow][iy * nZ2Xbins + iz]; + dataC = dataA; // copy the data + dataC.mZ = -dataC.mZ; // mirror the Z coordinate + dataC.mCz = -dataC.mCz; // mirror the Z correction + } + } + } + } + } + for (int iSector = 0; iSector < nSectors; iSector++) { // now process the data row-by-row @@ -623,6 +644,7 @@ std::unique_ptr TPCFastSpaceChargeCorrect { int xBin = iRow; double x = trackResiduals.getX(xBin); // radius of the pad row + double dx = 1. / trackResiduals.getDXI(xBin); bool isDataFound = false; for (int iy = 0; iy < nY2Xbins; iy++) { for (int iz = 0; iz < nZ2Xbins; iz++) { @@ -642,13 +664,29 @@ std::unique_ptr TPCFastSpaceChargeCorrect if (data.mNentries > 0) { // voxel contains data vox.mSmoothingStep = 0; // take original data isDataFound = true; - if (fabs(x - data.mX) > 1. || fabs(vox.mY - data.mY) > 5. || fabs(vox.mZ - data.mZ) > 5.) { - std::cout << directionName << ": fitted voxel is too far from the nominal position: " - << " sector " << iSector << " row " << iRow - << " center x " << x << " y " << vox.mY << " z " << vox.mZ - << " fitted x " << data.mX << " y " << data.mY << " z " << data.mZ - << std::endl; + + // correct the mean position if it is outside the voxel + std::stringstream msg; + if (fabs(x - data.mX) > mVoxelMeanValidityRange * dx / 2.) { + msg << "\n x: center " << x << " dx " << data.mX - x << " half bin size: " << dx / 2; + } + + if (fabs(vox.mY - data.mY) > mVoxelMeanValidityRange * vox.mDy / 2.) { + msg << "\n y: center " << vox.mY << " dy " << data.mY - vox.mY << " half bin size: " << vox.mDy / 2; + data.mY = vox.mY; + } + + if (fabs(vox.mZ - data.mZ) > mVoxelMeanValidityRange * vox.mDz / 2.) { + msg << "\n z: center " << vox.mZ << " dz " << data.mZ - vox.mZ << " half bin size: " << vox.mDz / 2; + data.mZ = vox.mZ; } + + if (!msg.str().empty()) { + LOG(warning) << directionName << " correction: fitted voxel position is outside the voxel: " + << " sector " << iSector << " row " << iRow << " bin: " << iy << " " << iz + << msg.str(); + } + } else { // no data, take voxel center position data.mCx = 0.; data.mCy = 0.; @@ -658,7 +696,7 @@ std::unique_ptr TPCFastSpaceChargeCorrect data.mZ = vox.mZ; vox.mSmoothingStep = 100; // fill this data point with smoothed values from the neighbours } - if (0) { // debug: always use voxel center instead of the mean position + if (mDebugUseVoxelCenters) { // debug: always use voxel center instead of the mean position data.mY = vox.mY; data.mZ = vox.mZ; } @@ -809,6 +847,13 @@ std::unique_ptr TPCFastSpaceChargeCorrect TStopwatch watch4; + if (!processingInverseCorrection && fitPointsDirect) { + *fitPointsDirect = helper->getCorrectionMap(); + } + if (processingInverseCorrection && fitPointsInverse) { + *fitPointsInverse = helper->getCorrectionMap(); + } + helper->fillSpaceChargeCorrectionFromMap(correction, processingInverseCorrection); LOG(info) << "fast space charge correction helper: creation from the data map took " << watch4.RealTime() << "s"; @@ -956,7 +1001,7 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector>& additionalCorrections, bool /*prn*/) { @@ -1099,5 +1144,17 @@ void TPCFastSpaceChargeCorrectionHelper::MergeCorrections( LOGP(info, "Merge of corrections tooks: {}s", duration); } +void TPCFastSpaceChargeCorrectionHelper::setDebugUseVoxelCenters() +{ + LOG(info) << "fast space charge correction helper: use voxel centers for correction"; + mDebugUseVoxelCenters = true; +} + +void TPCFastSpaceChargeCorrectionHelper::setDebugMirrorAdata2C() +{ + LOG(info) << "fast space charge correction helper: mirror A data to C data"; + mDebugMirrorAdata2C = true; +} + } // namespace tpc } // namespace o2 diff --git a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C index 974582792266b..50b667bb3e023 100644 --- a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C +++ b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C @@ -56,13 +56,14 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* To visiualise the results: root -l transformDebug.root - corr->Draw("cx:y:z","iSector==0&&iRow==10","") - grid->Draw("cx:y:z","iSector==0&&iRow==10","same") - vox->Draw("vx:y:z","iSector==0&&iRow==10","same") - corrvox->Draw("cx:y:z","iSector==0&&iRow==10","same") - points->Draw("px:y:z","iSector==0&&iRow==10","same") + all->Draw("cx:y:z","sec==0&&iRow==10","") + grid->Draw("cx:y:z","sec==0&&iRow==10","same") + vox->Draw("vx:y:z","sec==0&&iRow==10","same") + points->Draw("px:y:z","sec==0&&row==10","same") */ + const bool debugMirrorAdata2C = 0; + if (gSystem->AccessPathName(fileName)) { std::cout << " input file " << fileName << " does not exist!" << std::endl; return; @@ -152,7 +153,15 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* corrHelper->setNthreadsToMaximum(); corrHelper->setNthreads(1); - auto corrPtr = corrHelper->createFromTrackResiduals(trackResiduals, voxResTree, voxResTreeInverse, useSmoothed, invertSigns); + if (debugMirrorAdata2C) { + corrHelper->setDebugMirrorAdata2C(); + } + // corrHelper->setDebugUseVoxelCenters(); + + o2::gpu::TPCFastSpaceChargeCorrectionMap mapDirect(0, 0), mapInverse(0, 0); + + auto corrPtr = corrHelper->createFromTrackResiduals(trackResiduals, voxResTree, voxResTreeInverse, useSmoothed, invertSigns, + &mapDirect, &mapInverse); std::unique_ptr fastTransform( helper->create(0, *corrPtr)); @@ -199,15 +208,6 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* o2::gpu::TPCFastSpaceChargeCorrection& corr = fastTransform->getCorrection(); - // the difference - - double maxDiff[3] = {0., 0., 0.}; - int32_t maxDiffSector[3] = {0, 0, 0}; - int32_t maxDiffRow[3] = {0, 0, 0}; - - double sumDiff[3] = {0., 0., 0.}; - int64_t nDiff = 0; - // a debug file with some NTuples TDirectory* currDir = gDirectory; @@ -220,46 +220,77 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* // measured x,y,z; corrections cx,cy,cz from the measured to the real x,y,z; // inverse corrections ix,iy,iz at the real position (x+cx,y+cy,z+cz) // ideally, ix = cx, iy = cy, iz = cz - TNtuple* debugCorr = new TNtuple("corr", "corr", "iSector:iRow:x:y:z:cx:cy:cz:ix:iy:iz"); + TNtuple* ntAll = new TNtuple("all", "all", + debugMirrorAdata2C ? "sec:row:x:y:z:cx:cy:cz:ix:iy:iz:cxC:cyC:czC:ixC:iyC:izC" + : "sec:row:x:y:z:cx:cy:cz:ix:iy:iz"); - debugCorr->SetMarkerStyle(8); - debugCorr->SetMarkerSize(0.1); - debugCorr->SetMarkerColor(kBlack); + ntAll->SetMarkerStyle(8); + ntAll->SetMarkerSize(0.1); + ntAll->SetMarkerColor(kBlack); - // ntuple with the input data: voxels and corrections debugFile->cd(); - TNtuple* debugVox = - new TNtuple("vox", "vox", "iSector:iRow:n:x:y:z:vx:vy:vz"); + TNtuple* ntInvAll = new TNtuple("invall", "invall", + debugMirrorAdata2C ? "sec:row:x:y:z:cx:cy:cz:cxC:cyC:czC" + : "sec:row:x:y:z:cx:cy:cz"); - debugVox->SetMarkerStyle(8); - debugVox->SetMarkerSize(0.8); - debugVox->SetMarkerColor(kBlue); + ntInvAll->SetMarkerStyle(8); + ntInvAll->SetMarkerSize(0.1); + ntInvAll->SetMarkerColor(kBlack); // duplicate of debugVox + the spline data at voxels in a different color debugFile->cd(); - TNtuple* debugCorrVox = - new TNtuple("corrvox", "corrvox", "iSector:iRow:n:x:y:z:vx:vy:vz:cx:cy:cz:ix:iy:iz"); + TNtuple* ntVox = + new TNtuple("vox", "vox", + debugMirrorAdata2C ? "sec:row:n:x:y:z:vx:vy:vz:cx:cy:cz:ix:iy:iz:cxC:cyC:czC:ixC:iyC:izC" + : "sec:row:n:x:y:z:vx:vy:vz:cx:cy:cz:ix:iy:iz"); - debugCorrVox->SetMarkerStyle(8); - debugCorrVox->SetMarkerSize(0.8); - debugCorrVox->SetMarkerColor(kMagenta); + ntVox->SetMarkerStyle(8); + ntVox->SetMarkerSize(0.8); + ntVox->SetMarkerColor(kMagenta); + + // duplicate of debugVox + the spline data at voxels in a different color + debugFile->cd(); + TNtuple* ntInvVox = + new TNtuple("invvox", "invvox", + debugMirrorAdata2C ? "sec:row:n:x:y:z:vx:vy:vz:cx:cy:cz:cxC:cyC:czC" + : "sec:row:n:x:y:z:vx:vy:vz:cx:cy:cz"); + + ntInvVox->SetMarkerStyle(8); + ntInvVox->SetMarkerSize(0.8); + ntInvVox->SetMarkerColor(kMagenta); + + // corrections at the spline grid points + debugFile->cd(); + TNtuple* ntGrid = new TNtuple("grid", "grid", "sec:row:x:y:z:cx:cy:cz:ix:iy:iz"); + + ntGrid->SetMarkerStyle(8); + ntGrid->SetMarkerSize(1.2); + ntGrid->SetMarkerColor(kBlack); // corrections at the spline grid points debugFile->cd(); - TNtuple* debugGrid = new TNtuple("grid", "grid", "iSector:iRow:x:y:z:cx:cy:cz:ix:iy:iz"); + TNtuple* ntInvGrid = new TNtuple("invgrid", "invgrid", "sec:row:x:y:z:cx:cy:cz"); - debugGrid->SetMarkerStyle(8); - debugGrid->SetMarkerSize(1.2); - debugGrid->SetMarkerColor(kBlack); + ntInvGrid->SetMarkerStyle(8); + ntInvGrid->SetMarkerSize(1.2); + ntGrid->SetMarkerColor(kBlack); // ntuple with data points created from voxels (with the data smearing, extension to the edges etc.) debugFile->cd(); - TNtuple* debugPoints = - new TNtuple("points", "points", "iSector:iRow:x:y:z:px:py:pz:cx:cy:cz"); + TNtuple* ntFitPoints = + new TNtuple("fitpoints", "fit points", "sec:row:x:y:z:px:py:pz:cx:cy:cz"); - debugPoints->SetMarkerStyle(8); - debugPoints->SetMarkerSize(0.4); - debugPoints->SetMarkerColor(kRed); + ntFitPoints->SetMarkerStyle(8); + ntFitPoints->SetMarkerSize(0.4); + ntFitPoints->SetMarkerColor(kRed); + + debugFile->cd(); + TNtuple* ntInvFitPoints = + new TNtuple("invfitpoints", "fit points", "sec:row:x:y:z:px:py:pz:cx:cy:cz"); + + ntInvFitPoints->SetMarkerStyle(8); + ntInvFitPoints->SetMarkerSize(0.4); + ntInvFitPoints->SetMarkerColor(kRed); currDir->cd(); @@ -269,224 +300,318 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* const o2::gpu::TPCFastTransformGeo& geo = helper->getGeometry(); + auto getInvCorrections = [&](int iSector, int iRow, float realY, float realZ, float& ix, float& iy, float& iz) { + // get the inverse corrections ix, iy, iz at x,y,z + ix = corr.getCorrectionXatRealYZ(iSector, iRow, realY, realZ); + std::tie(iy, iz) = corr.getCorrectionYZatRealYZ(iSector, iRow, realY, realZ); + }; + auto getAllCorrections = [&](int iSector, int iRow, float y, float z, float& cx, float& cy, float& cz, float& ix, float& iy, float& iz) { // get the corrections cx,cy,cz at x,y,z std::tie(cx, cy, cz) = corr.getCorrectionLocal(iSector, iRow, y, z); - float realY = y + cy; - float realZ = z + cz; - ix = corr.getCorrectionXatRealYZ(iSector, iRow, realY, realZ); - std::tie(iy, iz) = corr.getCorrectionYZatRealYZ(iSector, iRow, realY, realZ); + getInvCorrections(iSector, iRow, y + cy, z + cz, ix, iy, iz); }; - o2::tpc::TrackResiduals::VoxRes* v = nullptr; - TBranch* branch = voxResTree->GetBranch("voxRes"); - branch->SetAddress(&v); - branch->SetAutoDelete(kTRUE); + for (int direction = 0; direction < 2; direction++) { // 0 - normal, 1 - inverse + + TTree* currentTree = (direction == 0) ? voxResTree : voxResTreeInverse; + if (!currentTree) { + std::cout << "tree voxResTree does not exist!" << std::endl; + return; + } - int32_t iSectorLast = -1; - int32_t iRowLast = -1; + o2::tpc::TrackResiduals::VoxRes* v = nullptr; + TBranch* branch = currentTree->GetBranch("voxRes"); + branch->SetAddress(&v); + branch->SetAutoDelete(kTRUE); - std::cout << "fill debug ntuples at voxels ..." << std::endl; + int32_t iSectorLast = -1; + int32_t iRowLast = -1; - for (int32_t iVox = 0; iVox < voxResTree->GetEntriesFast(); iVox++) { + // the difference - voxResTree->GetEntry(iVox); + double maxDiff[3] = {0., 0., 0.}; + int32_t maxDiffSector[3] = {0, 0, 0}; + int32_t maxDiffRow[3] = {0, 0, 0}; - float voxEntries = v->stat[o2::tpc::TrackResiduals::VoxV]; + double sumDiff[3] = {0., 0., 0.}; + int64_t nDiff = 0; - int32_t xBin = - v->bvox[o2::tpc::TrackResiduals::VoxX]; // bin number in x (= pad row) + std::cout << "fill debug ntuples at voxels ..." << std::endl; - int32_t y2xBin = - v->bvox[o2::tpc::TrackResiduals::VoxF]; // bin number in y/x 0..14 + for (int32_t iVox = 0; iVox < currentTree->GetEntriesFast(); iVox++) { - int32_t z2xBin = - v->bvox[o2::tpc::TrackResiduals::VoxZ]; // bin number in z/x 0..4 + currentTree->GetEntry(iVox); - int32_t iSector = (int32_t)v->bsec; - int32_t iRow = (int32_t)xBin; + float voxEntries = v->stat[o2::tpc::TrackResiduals::VoxV]; - iSectorLast = iSector; - iRowLast = iRow; + int32_t xBin = + v->bvox[o2::tpc::TrackResiduals::VoxX]; // bin number in x (= pad row) - double x = trackResiduals.getX(xBin); // radius of the pad row + int32_t y2xBin = + v->bvox[o2::tpc::TrackResiduals::VoxF]; // bin number in y/x 0..14 - double y2x = trackResiduals.getY2X( - xBin, y2xBin); // y/x coordinate of the bin ~-0.15 ... 0.15 + int32_t z2xBin = + v->bvox[o2::tpc::TrackResiduals::VoxZ]; // bin number in z/x 0..4 - double z2x = - trackResiduals.getZ2X(z2xBin); // z/x coordinate of the bin 0.1 .. 0.9 + int32_t iSector = (int32_t)v->bsec; + int32_t iRow = (int32_t)xBin; - double y = x * y2x; - double z = x * z2x; + iSectorLast = iSector; + iRowLast = iRow; - if (iSector >= geo.getNumberOfSectorsA()) { - z = -z; - } + double x = trackResiduals.getX(xBin); // radius of the pad row - double correctionX = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResX] : v->D[o2::tpc::TrackResiduals::ResX]; - double correctionY = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResY] : v->D[o2::tpc::TrackResiduals::ResY]; - double correctionZ = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResZ] : v->D[o2::tpc::TrackResiduals::ResZ]; + double y2x = trackResiduals.getY2X( + xBin, y2xBin); // y/x coordinate of the bin ~-0.15 ... 0.15 - if (invertSigns) { - correctionX *= -1.; - correctionY *= -1.; - correctionZ *= -1.; - } + double z2x = + trackResiduals.getZ2X(z2xBin); // z/x coordinate of the bin 0.1 .. 0.9 - if (voxEntries > 0.) { // use mean statistical positions instead of the bin centers: - y = x * v->stat[o2::tpc::TrackResiduals::VoxF]; - z = x * v->stat[o2::tpc::TrackResiduals::VoxZ]; - } + double y = x * y2x; + double z = x * z2x; + + double correctionX = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResX] : v->D[o2::tpc::TrackResiduals::ResX]; + double correctionY = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResY] : v->D[o2::tpc::TrackResiduals::ResY]; + double correctionZ = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResZ] : v->D[o2::tpc::TrackResiduals::ResZ]; + + double voxelSizeY = x / trackResiduals.getDY2XI(xBin, y2xBin); + double voxelSizeZ = x * trackResiduals.getDZ2X(z2xBin); + + if (invertSigns) { + correctionX *= -1.; + correctionY *= -1.; + correctionZ *= -1.; + } - float cx, cy, cz, ix, iy, iz; - getAllCorrections(iSector, iRow, y, z, cx, cy, cz, ix, iy, iz); + if (!corrHelper->isDebugUseVoxelCenters()) { + if (voxEntries > 0.) { + // use mean statistical positions instead of the bin centers, unless they are wrong + double yFit = x * v->stat[o2::tpc::TrackResiduals::VoxF]; + if (fabs(yFit - y) <= corrHelper->getVoxelMeanValidityRange() * voxelSizeY / 2.) { + y = yFit; + } + double zFit = x * v->stat[o2::tpc::TrackResiduals::VoxZ]; + if (fabs(zFit - z) <= corrHelper->getVoxelMeanValidityRange() * voxelSizeZ / 2.) { + z = zFit; + } + } + } - if (voxEntries >= 1.) { - double d[3] = {cx - correctionX, cy - correctionY, cz - correctionZ}; + int mirrorSector = iSector + geo.getNumberOfSectorsA(); - for (int32_t i = 0; i < 3; i++) { - if (fabs(maxDiff[i]) < fabs(d[i])) { - maxDiff[i] = d[i]; - maxDiffSector[i] = iSector; - maxDiffRow[i] = iRow; - // std::cout << " sector " << iSector << " row " << iRow << " xyz " << i - // << " diff " << d[i] << " entries " << voxEntries << " y " << y2xBin << " z " << z2xBin << std::endl; + if (iSector >= geo.getNumberOfSectorsA()) { + z = -z; + mirrorSector = iSector - geo.getNumberOfSectorsA(); + } + + float cx{0.f}, cy{0.f}, cz{0.f}, ix{0.f}, iy{0.f}, iz{0.f}; + float cxC{0.f}, cyC{0.f}, czC{0.f}, ixC{0.f}, iyC{0.f}, izC{0.f}; + if (direction == 0) { + getAllCorrections(iSector, iRow, y, z, cx, cy, cz, ix, iy, iz); + if (debugMirrorAdata2C) { + getAllCorrections(mirrorSector, iRow, y, -z, cxC, cyC, czC, ixC, iyC, izC); } - sumDiff[i] += d[i] * d[i]; + float ntEntry[] = {(float)iSector, (float)iRow, voxEntries, + (float)x, (float)y, (float)z, + (float)correctionX, (float)correctionY, (float)correctionZ, + (float)cx, (float)cy, (float)cz, + (float)ix, (float)iy, (float)iz, + (float)cxC, (float)cyC, (float)czC, (float)ixC, (float)iyC, (float)izC}; + + // fill the ntuple with the correction at the voxel + ntVox->Fill(ntEntry); + } else { + getInvCorrections(iSector, iRow, y, z, cx, cy, cz); + if (debugMirrorAdata2C) { + getInvCorrections(mirrorSector, iRow, y, -z, cxC, cyC, czC); + } + float ntEntry[] = {(float)iSector, (float)iRow, voxEntries, + (float)x, (float)y, (float)z, + (float)correctionX, (float)correctionY, (float)correctionZ, + (float)cx, (float)cy, (float)cz, + (float)cxC, (float)cyC, (float)czC}; + // fill the ntuple with the correction at the voxel + ntInvVox->Fill(ntEntry); + } + + if (voxEntries >= 1.) { + double d[3] = {cx - correctionX, cy - correctionY, cz - correctionZ}; + + for (int32_t i = 0; i < 3; i++) { + if (fabs(maxDiff[i]) < fabs(d[i])) { + maxDiff[i] = d[i]; + maxDiffSector[i] = iSector; + maxDiffRow[i] = iRow; + // std::cout << " sector " << iSector << " row " << iRow << " xyz " << i + // << " diff " << d[i] << " entries " << voxEntries << " y " << y2xBin << " z " << z2xBin << std::endl; + } + sumDiff[i] += d[i] * d[i]; + } + nDiff++; } - nDiff++; } - debugVox->Fill(iSector, iRow, voxEntries, x, y, z, correctionX, correctionY, correctionZ); + std::cout + << "fill debug ntuples everywhere .." << std::endl; - debugCorrVox->Fill(iSector, iRow, voxEntries, x, y, z, correctionX, correctionY, correctionZ, - cx, cy, cz, ix, iy, iz); - } + for (int32_t iSector = 0; iSector < geo.getNumberOfSectors(); iSector++) { + // for (int32_t iSector = 0; iSector < 1; iSector++) { + std::cout << "debug ntules for sector " << iSector << std::endl; - std::cout - << "fill debug ntuples everywhere .." << std::endl; + int mirrorSector = (iSector >= geo.getNumberOfSectorsA()) ? iSector - geo.getNumberOfSectorsA() : iSector + geo.getNumberOfSectorsA(); - for (int32_t iSector = 0; iSector < geo.getNumberOfSectors(); iSector++) { - // for (int32_t iSector = 0; iSector < 1; iSector++) { - std::cout << "debug ntules for sector " << iSector << std::endl; - for (int32_t iRow = 0; iRow < geo.getNumberOfRows(); iRow++) { + for (int32_t iRow = 0; iRow < geo.getNumberOfRows(); iRow++) { - double x = geo.getRowInfo(iRow).x; + double x = geo.getRowInfo(iRow).x; - // the spline grid + // the spline grid - const auto& gridY = corr.getSpline(iSector, iRow).getGridX1(); - const auto& gridZ = corr.getSpline(iSector, iRow).getGridX2(); - if (iSector == 0 && iRow == 0) { - std::cout << "spline scenario " << corr.getSectorRowInfo(iSector, iRow).splineScenarioID << std::endl; - std::cout << "spline grid Y: u = " << 0 << ".." << gridY.getUmax() << ", x = " << gridY.getXmin() << ".." << gridY.getXmax() << std::endl; - std::cout << "spline grid Z: u = " << 0 << ".." << gridZ.getUmax() << ", x = " << gridZ.getXmin() << ".." << gridZ.getXmax() << std::endl; - } + const auto& gridY = corr.getSpline(iSector, iRow).getGridX1(); + const auto& gridZ = corr.getSpline(iSector, iRow).getGridX2(); + if (iSector == 0 && iRow == 0) { + std::cout << "spline scenario " << corr.getSectorRowInfo(iSector, iRow).splineScenarioID << std::endl; + std::cout << "spline grid Y: u = " << 0 << ".." << gridY.getUmax() << ", x = " << gridY.getXmin() << ".." << gridY.getXmax() << std::endl; + std::cout << "spline grid Z: u = " << 0 << ".." << gridZ.getUmax() << ", x = " << gridZ.getXmin() << ".." << gridZ.getXmax() << std::endl; + } - // the correction - { - std::vector points[2], knots[2]; + // the correction + { + std::vector points[2], knots[2]; - auto [yMin, yMax] = geo.getRowInfo(iRow).getYrange(); - auto [zMin, zMax] = geo.getZrange(iSector); + auto [yMin, yMax] = geo.getRowInfo(iRow).getYrange(); + auto [zMin, zMax] = geo.getZrange(iSector); - points[0].push_back(yMin); - points[0].push_back(yMax); - points[1].push_back(zMin); - points[1].push_back(zMax); + points[0].push_back(yMin); + points[0].push_back(yMax); + points[1].push_back(zMin); + points[1].push_back(zMax); - for (int32_t iu = 0; iu < gridY.getNumberOfKnots(); iu++) { - auto [y, z] = corr.convGridToLocal(iSector, iRow, gridY.getKnot(iu).getU(), 0.); - knots[0].push_back(y); - points[0].push_back(y); - } - for (int32_t iv = 0; iv < gridZ.getNumberOfKnots(); iv++) { - auto [y, z] = corr.convGridToLocal(iSector, iRow, 0., gridZ.getKnot(iv).getU()); - knots[1].push_back(z); - points[1].push_back(z); - } + for (int32_t iu = 0; iu < gridY.getNumberOfKnots(); iu++) { + auto [y, z] = corr.convGridToLocal(iSector, iRow, gridY.getKnot(iu).getU(), 0.); + knots[0].push_back(y); + points[0].push_back(y); + } + for (int32_t iv = 0; iv < gridZ.getNumberOfKnots(); iv++) { + auto [y, z] = corr.convGridToLocal(iSector, iRow, 0., gridZ.getKnot(iv).getU()); + knots[1].push_back(z); + points[1].push_back(z); + } - for (int32_t iyz = 0; iyz <= 1; iyz++) { - std::sort(knots[iyz].begin(), knots[iyz].end()); - std::sort(points[iyz].begin(), points[iyz].end()); - int32_t n = points[iyz].size(); - for (int32_t i = 0; i < n - 1; i++) { - double d = (points[iyz][i + 1] - points[iyz][i]) / 10.; - for (int32_t ii = 1; ii < 10; ii++) { - points[iyz].push_back(points[iyz][i] + d * ii); + for (int32_t iyz = 0; iyz <= 1; iyz++) { + std::sort(knots[iyz].begin(), knots[iyz].end()); + std::sort(points[iyz].begin(), points[iyz].end()); + int32_t n = points[iyz].size(); + for (int32_t i = 0; i < n - 1; i++) { + double d = (points[iyz][i + 1] - points[iyz][i]) / 10.; + for (int32_t ii = 1; ii < 10; ii++) { + points[iyz].push_back(points[iyz][i] + d * ii); + } } + std::sort(points[iyz].begin(), points[iyz].end()); } - std::sort(points[iyz].begin(), points[iyz].end()); - } - for (int32_t iter = 0; iter < 2; iter++) { - std::vector& py = ((iter == 0) ? knots[0] : points[0]); - std::vector& pz = ((iter == 0) ? knots[1] : points[1]); - for (uint32_t iu = 0; iu < py.size(); iu++) { - for (uint32_t iv = 0; iv < pz.size(); iv++) { - float y = py[iu]; - float z = pz[iv]; - float cx, cy, cz, ix, iy, iz; - getAllCorrections(iSector, iRow, y, z, cx, cy, cz, ix, iy, iz); - if (iter == 0) { - debugGrid->Fill(iSector, iRow, x, y, z, cx, cy, cz, ix, iy, iz); - } else { - debugCorr->Fill(iSector, iRow, x, y, z, cx, cy, cz, ix, iy, iz); + for (int32_t iter = 0; iter < 2; iter++) { + std::vector& py = ((iter == 0) ? knots[0] : points[0]); + std::vector& pz = ((iter == 0) ? knots[1] : points[1]); + for (uint32_t iu = 0; iu < py.size(); iu++) { + for (uint32_t iv = 0; iv < pz.size(); iv++) { + float y = py[iu]; + float z = pz[iv]; + float cx{0}, cy{0}, cz{0}, ix{0}, iy{0}, iz{0}; + float cxC{0}, cyC{0}, czC{0}, ixC{0}, iyC{0}, izC{0}; + if (direction == 0) { + getAllCorrections(iSector, iRow, y, z, cx, cy, cz, ix, iy, iz); + if (debugMirrorAdata2C) { + getAllCorrections(mirrorSector, iRow, y, -z, cxC, cyC, czC, ixC, iyC, izC); + } + if (iter == 0) { + ntGrid->Fill(iSector, iRow, x, y, z, cx, cy, cz, ix, iy, iz); + } else { + float ntEntry[] = {(float)iSector, (float)iRow, (float)x, y, z, + cx, cy, cz, ix, iy, iz, + cxC, cyC, czC, ixC, iyC, izC}; + ntAll->Fill(ntEntry); + } + } else { + getInvCorrections(iSector, iRow, y, z, cx, cy, cz); + if (debugMirrorAdata2C) { + getInvCorrections(mirrorSector, iRow, y, -z, cxC, cyC, czC); + } + if (iter == 0) { + ntInvGrid->Fill(iSector, iRow, x, y, z, cx, cy, cz); + } else { + float ntEntry[] = {(float)iSector, (float)iRow, (float)x, y, z, + cx, cy, cz, + cxC, cyC, czC}; + ntInvAll->Fill(ntEntry); + } + } } } } } - } - - // the data points used in spline fit - // (they are kept in - // TPCFastTransformHelperO2::instance()->getCorrectionMap() ) - - o2::gpu::TPCFastSpaceChargeCorrectionMap& map = - corrHelper->getCorrectionMap(); - auto& points = map.getPoints(iSector, iRow); - for (uint32_t ip = 0; ip < points.size(); ip++) { - auto point = points[ip]; - float y = point.mY; - float z = point.mZ; - float correctionX = point.mDx; - float correctionY = point.mDy; - float correctionZ = point.mDz; - - auto [cx, cy, cz] = - corr.getCorrectionLocal(iSector, iRow, y, z); - - debugPoints->Fill(iSector, iRow, x, y, z, correctionX, correctionY, - correctionZ, cx, cy, cz); + // the data points used in spline fit + // (they are kept in + // TPCFastTransformHelperO2::instance()->getCorrectionMap() ) + + o2::gpu::TPCFastSpaceChargeCorrectionMap& map = (direction == 0 ? mapDirect : mapInverse); + + auto& points = map.getPoints(iSector, iRow); + + for (uint32_t ip = 0; ip < points.size(); ip++) { + auto point = points[ip]; + float y = point.mY; + float z = point.mZ; + float correctionX = point.mDx; + float correctionY = point.mDy; + float correctionZ = point.mDz; + if (direction == 0) { + auto [cx, cy, cz] = + corr.getCorrectionLocal(iSector, iRow, y, z); + ntFitPoints->Fill(iSector, iRow, x, y, z, correctionX, correctionY, + correctionZ, cx, cy, cz); + } else { + float cx = + corr.getCorrectionXatRealYZ(iSector, iRow, y, z); + auto [cy, cz] = + corr.getCorrectionYZatRealYZ(iSector, iRow, y, z); + ntInvFitPoints->Fill(iSector, iRow, x, y, z, correctionX, correctionY, + correctionZ, cx, cy, cz); + } + } } } - } - for (int32_t i = 0; i < 3; i++) { - sumDiff[i] = sqrt(sumDiff[i]) / nDiff; - } + for (int32_t i = 0; i < 3; i++) { + sumDiff[i] = sqrt(sumDiff[i]) / nDiff; + } - std::cout << "Max difference in x : " << maxDiff[0] << " at Sector " - << maxDiffSector[0] << " row " << maxDiffRow[0] << std::endl; + std::cout << "Max difference in x : " << maxDiff[0] << " at Sector " + << maxDiffSector[0] << " row " << maxDiffRow[0] << std::endl; - std::cout << "Max difference in y : " << maxDiff[1] << " at Sector " - << maxDiffSector[1] << " row " << maxDiffRow[1] << std::endl; + std::cout << "Max difference in y : " << maxDiff[1] << " at Sector " + << maxDiffSector[1] << " row " << maxDiffRow[1] << std::endl; - std::cout << "Max difference in z : " << maxDiff[2] << " at Sector " - << maxDiffSector[2] << " row " << maxDiffRow[2] << std::endl; + std::cout << "Max difference in z : " << maxDiff[2] << " at Sector " + << maxDiffSector[2] << " row " << maxDiffRow[2] << std::endl; - std::cout << "Mean difference in x,y,z : " << sumDiff[0] << " " << sumDiff[1] - << " " << sumDiff[2] << std::endl; + std::cout << "Mean difference in x,y,z : " << sumDiff[0] << " " << sumDiff[1] + << " " << sumDiff[2] << std::endl; + } // direction corr.testInverse(true); debugFile->cd(); - debugCorr->Write(); - debugVox->Write(); - debugCorrVox->Write(); - debugGrid->Write(); - debugPoints->Write(); + ntAll->Write(); + ntVox->Write(); + ntGrid->Write(); + ntFitPoints->Write(); + ntInvAll->Write(); + ntInvVox->Write(); + ntInvGrid->Write(); + ntInvFitPoints->Write(); + debugFile->Close(); } From 954b28c2823ac6017a38e5e7489f792585e04d14 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Wed, 27 Aug 2025 17:04:52 +0000 Subject: [PATCH 096/285] TPC Splines: replace std::tuple by std::array --- .../TPCFastSpaceChargeCorrectionHelper.cxx | 8 +-- GPU/TPCFastTransformation/Spline1DSpec.h | 8 +-- GPU/TPCFastTransformation/Spline2DSpec.cxx | 2 - .../TPCFastSpaceChargeCorrection.h | 44 ++++++++--------- GPU/TPCFastTransformation/TPCFastTransform.h | 49 +++++++++++++------ .../TPCFastTransformGeo.h | 15 +++--- .../macro/TPCFastTransformInit.C | 9 +++- 7 files changed, 78 insertions(+), 57 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index 6122c5717fcbb..9232598e6a35e 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -668,22 +668,22 @@ std::unique_ptr TPCFastSpaceChargeCorrect // correct the mean position if it is outside the voxel std::stringstream msg; if (fabs(x - data.mX) > mVoxelMeanValidityRange * dx / 2.) { - msg << "\n x: center " << x << " dx " << data.mX - x << " half bin size: " << dx / 2; + msg << "\n x: center " << x << " dx " << data.mX - x << " half bin size " << dx / 2; } if (fabs(vox.mY - data.mY) > mVoxelMeanValidityRange * vox.mDy / 2.) { - msg << "\n y: center " << vox.mY << " dy " << data.mY - vox.mY << " half bin size: " << vox.mDy / 2; + msg << "\n y: center " << vox.mY << " dy " << data.mY - vox.mY << " half bin size " << vox.mDy / 2; data.mY = vox.mY; } if (fabs(vox.mZ - data.mZ) > mVoxelMeanValidityRange * vox.mDz / 2.) { - msg << "\n z: center " << vox.mZ << " dz " << data.mZ - vox.mZ << " half bin size: " << vox.mDz / 2; + msg << "\n z: center " << vox.mZ << " dz " << data.mZ - vox.mZ << " half bin size " << vox.mDz / 2; data.mZ = vox.mZ; } if (!msg.str().empty()) { LOG(warning) << directionName << " correction: fitted voxel position is outside the voxel: " - << " sector " << iSector << " row " << iRow << " bin: " << iy << " " << iz + << " sector " << iSector << " row " << iRow << " bin y " << iy << " bin z " << iz << msg.str(); } diff --git a/GPU/TPCFastTransformation/Spline1DSpec.h b/GPU/TPCFastTransformation/Spline1DSpec.h index 1c591a957847d..28be5dd20e235 100644 --- a/GPU/TPCFastTransformation/Spline1DSpec.h +++ b/GPU/TPCFastTransformation/Spline1DSpec.h @@ -342,7 +342,7 @@ class Spline1DSpec : public Spline1DContainer } template - GPUd() std::tuple getSderivativesOverParsAtU(const Knot& knotL, DataT u) const + GPUd() std::array getSderivativesOverParsAtU(const Knot& knotL, DataT u) const { /// Get derivatives of the interpolated value {S(u): 1D -> nYdim} at the segment [knotL, next knotR] /// over the spline parameters Sl(eft), Sr(ight) and the slopes Dl, Dr @@ -364,11 +364,11 @@ class Spline1DSpec : public Spline1DContainer T dSdDl = vm1 * a; T dSdDr = v * a; // S(u) = dSdSl * Sl + dSdSr * Sr + dSdDl * Dl + dSdDr * Dr; - return std::make_tuple(dSdSl, dSdDl, dSdSr, dSdDr); + return {dSdSl, dSdDl, dSdSr, dSdDr}; } template - GPUd() std::tuple getSDderivativesOverParsAtU(const Knot& knotL, DataT u) const + GPUd() std::array getSDderivativesOverParsAtU(const Knot& knotL, DataT u) const { /// Get derivatives of the interpolated value {S(u): 1D -> nYdim} at the segment [knotL, next knotR] /// over the spline values Sl, Sr and the slopes Dl, Dr @@ -397,7 +397,7 @@ class Spline1DSpec : public Spline1DContainer T dDdDr = v * (v + vm1 + vm1); // S(u) = dSdSl * Sl + dSdSr * Sr + dSdDl * Dl + dSdDr * Dr; // D(u) = dS(u)/du = dDdSl * Sl + dDdSr * Sr + dDdDl * Dl + dDdDr * Dr; - return std::make_tuple(dSdSl, dSdDl, dSdSr, dSdDr, dDdSl, dDdDl, dDdSr, dDdDr); + return {dSdSl, dSdDl, dSdSr, dSdDr, dDdSl, dDdDl, dDdSr, dDdDr}; } using TBase::convXtoU; diff --git a/GPU/TPCFastTransformation/Spline2DSpec.cxx b/GPU/TPCFastTransformation/Spline2DSpec.cxx index 055530b9314c2..4fbd4dc3d0110 100644 --- a/GPU/TPCFastTransformation/Spline2DSpec.cxx +++ b/GPU/TPCFastTransformation/Spline2DSpec.cxx @@ -29,8 +29,6 @@ #include "Riostream.h" #include "TMath.h" #include "Spline2DHelper.h" -#include "TCanvas.h" -#include "TNtuple.h" #include "TFile.h" #include "GPUCommonMath.h" diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index 7112a04b364c6..4ca5b74025743 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -78,13 +78,13 @@ class TPCFastSpaceChargeCorrection : public FlatObject } /// convert local y, z to internal grid coordinates u,v, and spline scale - std::tuple convLocalToGridUntruncated(float y, float z) const + std::array convLocalToGridUntruncated(float y, float z) const { return {(y - y0) * yScale, (z - z0) * zScale, getSpineScaleForZ(z)}; } /// convert internal grid coordinates u,v to local y, z - std::tuple convGridToLocal(float gridU, float gridV) const + std::array convGridToLocal(float gridU, float gridV) const { return {y0 + gridU / yScale, z0 + gridV / zScale}; } @@ -123,22 +123,22 @@ class TPCFastSpaceChargeCorrection : public FlatObject maxCorr[2] = GPUCommonMath::Max(maxCorr[2], dv); } - void updateMaxValues(std::tuple dxdudv, float scale) + void updateMaxValues(std::array dxdudv, float scale) { - float dx = std::get<0>(dxdudv) * scale; - float du = std::get<1>(dxdudv) * scale; - float dv = std::get<2>(dxdudv) * scale; + float dx = dxdudv[0] * scale; + float du = dxdudv[1] * scale; + float dv = dxdudv[2] * scale; updateMaxValues(dx, du, dv); } - std::tuple getMaxValues() const + std::array getMaxValues() const { - return std::make_tuple(maxCorr[0], maxCorr[1], maxCorr[2]); + return {maxCorr[0], maxCorr[1], maxCorr[2]}; } - std::tuple getMinValues() const + std::array getMinValues() const { - return std::make_tuple(minCorr[0], minCorr[1], minCorr[2]); + return {minCorr[0], minCorr[1], minCorr[2]}; } ClassDefNV(SectorRowInfo, 2); @@ -259,31 +259,31 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// // GPUd() int32_t getCorrectionInternal(int32_t sector, int32_t row, float u, float v, float& dx, float& du, float& dv) const; - GPUdi() std::tuple getCorrectionLocal(int32_t sector, int32_t row, float y, float z) const; + GPUdi() std::array getCorrectionLocal(int32_t sector, int32_t row, float y, float z) const; /// inverse correction: Real Y and Z -> Real X GPUd() float getCorrectionXatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const; /// inverse correction: Real Y and Z -> measred Y and Z - GPUd() std::tuple getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const; + GPUd() std::array getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const; /// _______________ Utilities _______________________________________________ /// convert local y, z to internal grid coordinates u,v /// return values: u, v, scaling factor - GPUd() std::tuple convLocalToGrid(int32_t sector, int32_t row, float y, float z) const; + GPUd() std::array convLocalToGrid(int32_t sector, int32_t row, float y, float z) const; /// convert internal grid coordinates u,v to local y, z /// return values: y, z, scaling factor - GPUd() std::tuple convGridToLocal(int32_t sector, int32_t row, float u, float v) const; + GPUd() std::array convGridToLocal(int32_t sector, int32_t row, float u, float v) const; /// convert real Y, Z to the internal grid coordinates /// return values: u, v, scaling factor - GPUd() std::tuple convRealLocalToGrid(int32_t sector, int32_t row, float y, float z) const; + GPUd() std::array convRealLocalToGrid(int32_t sector, int32_t row, float y, float z) const; /// convert internal grid coordinates to the real Y, Z /// return values: y, z - GPUd() std::tuple convGridToRealLocal(int32_t sector, int32_t row, float u, float v) const; + GPUd() std::array convGridToRealLocal(int32_t sector, int32_t row, float u, float v) const; GPUd() bool isLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const; GPUd() bool isRealLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const; @@ -453,7 +453,7 @@ GPUdi() const float* TPCFastSpaceChargeCorrection::getSplineDataInvYZ(int32_t se return getSplineData(sector, row, 2); } -GPUdi() std::tuple TPCFastSpaceChargeCorrection::convLocalToGrid(int32_t sector, int32_t row, float y, float z) const +GPUdi() std::array TPCFastSpaceChargeCorrection::convLocalToGrid(int32_t sector, int32_t row, float y, float z) const { /// convert local y, z to internal grid coordinates u,v /// return values: u, v, scaling factor @@ -491,13 +491,13 @@ GPUdi() bool TPCFastSpaceChargeCorrection::isRealLocalInsideGrid(int32_t sector, return true; } -GPUdi() std::tuple TPCFastSpaceChargeCorrection::convGridToLocal(int32_t sector, int32_t row, float gridU, float gridV) const +GPUdi() std::array TPCFastSpaceChargeCorrection::convGridToLocal(int32_t sector, int32_t row, float gridU, float gridV) const { /// convert internal grid coordinates u,v to local y, z return getSectorRowInfo(sector, row).gridMeasured.convGridToLocal(gridU, gridV); } -GPUdi() std::tuple TPCFastSpaceChargeCorrection::convRealLocalToGrid(int32_t sector, int32_t row, float y, float z) const +GPUdi() std::array TPCFastSpaceChargeCorrection::convRealLocalToGrid(int32_t sector, int32_t row, float y, float z) const { /// convert real y, z to the internal grid coordinates + scale const SplineType& spline = getSpline(sector, row); @@ -508,13 +508,13 @@ GPUdi() std::tuple TPCFastSpaceChargeCorrection::convRealLo return {gridU, gridV, scale}; } -GPUdi() std::tuple TPCFastSpaceChargeCorrection::convGridToRealLocal(int32_t sector, int32_t row, float gridU, float gridV) const +GPUdi() std::array TPCFastSpaceChargeCorrection::convGridToRealLocal(int32_t sector, int32_t row, float gridU, float gridV) const { /// convert internal grid coordinates u,v to the real y, z return getSectorRowInfo(sector, row).gridReal.convGridToLocal(gridU, gridV); } -GPUdi() std::tuple TPCFastSpaceChargeCorrection::getCorrectionLocal(int32_t sector, int32_t row, float y, float z) const +GPUdi() std::array TPCFastSpaceChargeCorrection::getCorrectionLocal(int32_t sector, int32_t row, float y, float z) const { const auto& info = getSectorRowInfo(sector, row); const SplineType& spline = getSpline(sector, row); @@ -541,7 +541,7 @@ GPUdi() float TPCFastSpaceChargeCorrection::getCorrectionXatRealYZ(int32_t secto return dx; } -GPUdi() std::tuple TPCFastSpaceChargeCorrection::getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const +GPUdi() std::array TPCFastSpaceChargeCorrection::getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const { auto [gridU, gridV, scale] = convRealLocalToGrid(sector, row, realY, realZ); const auto& info = getSectorRowInfo(sector, row); diff --git a/GPU/TPCFastTransformation/TPCFastTransform.h b/GPU/TPCFastTransformation/TPCFastTransform.h index 3b08296525fc7..35b94446ce088 100644 --- a/GPU/TPCFastTransformation/TPCFastTransform.h +++ b/GPU/TPCFastTransformation/TPCFastTransform.h @@ -349,13 +349,17 @@ class TPCFastTransform : public FlatObject GPUdi() void TPCFastTransform::convPadTimeToLocal(int32_t sector, int32_t row, float pad, float time, float& y, float& z, float vertexTime) const { float l = (time - mT0 - vertexTime) * mVdrift; // drift length [cm] - std::tie(y, z) = getGeometry().convPadDriftLengthToLocal(sector, row, pad, l); + const auto local = getGeometry().convPadDriftLengthToLocal(sector, row, pad, l); + y = local[0]; + z = local[1]; } GPUdi() void TPCFastTransform::convPadTimeToLocalInTimeFrame(int32_t sector, int32_t row, float pad, float time, float& y, float& z, float maxTimeBin) const { float l = getGeometry().getTPCzLength() + (time - mT0 - maxTimeBin) * mVdrift; // drift length [cm] - std::tie(y, z) = getGeometry().convPadDriftLengthToLocal(sector, row, pad, l); + const auto local = getGeometry().convPadDriftLengthToLocal(sector, row, pad, l); + y = local[0]; + z = local[1]; } // ---------------------------------------------------------------------- @@ -387,16 +391,16 @@ GPUdi() float TPCFastTransform::convDriftLengthToTime(float driftLength, float v GPUdi() void TPCFastTransform::convLocalToPadTime(int32_t sector, int32_t row, float y, float z, float& pad, float& time, float vertexTime) const { - float l; - std::tie(pad, l) = getGeometry().convLocalToPadDriftLength(sector, row, y, z); - time = convDriftLengthToTime(l, vertexTime); + const auto padLength = getGeometry().convLocalToPadDriftLength(sector, row, y, z); + pad = padLength[0]; + time = convDriftLengthToTime(padLength[1], vertexTime); } GPUdi() void TPCFastTransform::convLocalToPadTimeInTimeFrame(int32_t sector, int32_t row, float y, float z, float& pad, float& time, float maxTimeBin) const { - float l; - std::tie(pad, l) = getGeometry().convLocalToPadDriftLength(sector, row, y, z); - time = convDriftLengthToTime(l, maxTimeBin); + const auto padLength = getGeometry().convLocalToPadDriftLength(sector, row, y, z); + pad = padLength[0]; + time = convDriftLengthToTime(padLength[1], maxTimeBin); } // ---------------------------------------------------------------------- @@ -422,7 +426,10 @@ GPUdi() void TPCFastTransform::TransformLocal(int32_t sector, int32_t row, float } else #endif // GPUCA_GPUCODE { - std::tie(dx, dy, dz) = mCorrection.getCorrectionLocal(sector, row, y, z); + const auto corrLocal = mCorrection.getCorrectionLocal(sector, row, y, z); + dx = corrLocal[0]; + dy = corrLocal[1]; + dz = corrLocal[2]; if (ref) { if ((scale > 0.f) && (scaleMode == 0)) { // scaling was requested auto [dxRef, dyRef, dzRef] = ref->mCorrection.getCorrectionLocal(sector, row, y, z); @@ -471,12 +478,18 @@ GPUdi() void TPCFastTransform::TransformLocal(int32_t sector, int32_t row, float float dxRef = 0.f, dyRef = 0.f, dzRef = 0.f; if (ref) { - std::tie(dxRef, dyRef, dzRef) = ref->mCorrection.getCorrectionLocal(sector, row, y, z); + const auto corr = ref->mCorrection.getCorrectionLocal(sector, row, y, z); + dxRef = corr[0]; + dyRef = corr[1]; + dzRef = corr[2]; } float dxRef2 = 0.f, dyRef2 = 0.f, dzRef2 = 0.f; if (ref2) { - std::tie(dxRef2, dyRef2, dzRef2) = ref2->mCorrection.getCorrectionLocal(sector, row, y, z); + const auto corr = ref2->mCorrection.getCorrectionLocal(sector, row, y, z); + dxRef2 = corr[0]; + dyRef2 = corr[1]; + dzRef2 = corr[2]; } auto [dxOrig, dyOrig, dzOrig] = mCorrection.getCorrectionLocal(sector, row, y, z); @@ -604,7 +617,9 @@ GPUdi() void TPCFastTransform::TransformIdeal(int32_t sector, int32_t row, float x = getGeometry().getRowInfo(row).x; float driftLength = (time - mT0 - vertexTime) * mVdrift; // drift length cm - std::tie(y, z) = getGeometry().convPadDriftLengthToLocal(sector, row, pad, driftLength); + const auto local = getGeometry().convPadDriftLengthToLocal(sector, row, pad, driftLength); + y = local[0]; + z = local[1]; } GPUdi() float TPCFastTransform::convTimeToZinTimeFrame(int32_t sector, float time, float maxTimeBin) const @@ -711,20 +726,22 @@ GPUdi() void TPCFastTransform::InverseTransformYZtoNominalYZ(int32_t sector, int float dz = 0; if ((scale >= 0.f) || (scaleMode == 1) || (scaleMode == 2)) { - std::tie(dy, dz) = mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); + const auto corrYZ = mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); + dy = corrYZ[0]; + dz = corrYZ[1]; if (ref) { // scaling was requested if (scaleMode == 0 && scale > 0.f) { - auto [dyRef, dzRef] = ref->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); + const auto [dyRef, dzRef] = ref->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); dy = (dy - dyRef) * scale + dyRef; dz = (dz - dzRef) * scale + dzRef; } else if ((scale != 0) && ((scaleMode == 1) || (scaleMode == 2))) { - auto [dyRef, dzRef] = ref->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); + const auto [dyRef, dzRef] = ref->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); dy = dyRef * scale + dy; dz = dzRef * scale + dz; } if (ref2 && (scale2 != 0)) { - auto [dyRef, dzRef] = ref2->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); + const auto [dyRef, dzRef] = ref2->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); dy = dyRef * scale2 + dy; dz = dzRef * scale2 + dz; } diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.h b/GPU/TPCFastTransformation/TPCFastTransformGeo.h index 89b099ec63127..fc28bbef33602 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.h +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.h @@ -18,6 +18,7 @@ #define ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_TPCFASTTRANSFORMGEO_H #include "GPUCommonDef.h" +#include "GPUCommonArray.h" #ifndef GPUCA_GPUCODE_DEVICE #include #include "GPUCommonRtypes.h" @@ -55,7 +56,7 @@ class TPCFastTransformGeo GPUd() float getYmax() const { return -yMin; } /// get Y range - GPUd() std::tuple getYrange() const { return {getYmin(), getYmax()}; } + GPUd() std::array getYrange() const { return {getYmin(), getYmax()}; } /// get width in Y GPUd() float getYwidth() const { return -2.f * yMin; } @@ -125,7 +126,7 @@ class TPCFastTransformGeo GPUd() float getTPCzLength() const { return mTPCzLength; } /// Gives Z range for the corresponding TPC side - GPUd() std::tuple getZrange(int32_t sector) const; + GPUd() std::array getZrange(int32_t sector) const; GPUd() float getZmin(int32_t sector) const; GPUd() float getZmax(int32_t sector) const; GPUd() float getZreadout(int32_t sector) const; @@ -139,7 +140,7 @@ class TPCFastTransformGeo GPUd() void convGlobalToLocal(int32_t sector, float gx, float gy, float gz, float& lx, float& ly, float& lz) const; /// convert Pad, DriftLength -> Local c.s. - GPUd() std::tuple convPadDriftLengthToLocal(int32_t sector, int32_t row, float pad, float driftLength) const; + GPUd() std::array convPadDriftLengthToLocal(int32_t sector, int32_t row, float pad, float driftLength) const; /// convert DriftLength -> Local c.s. GPUd() float convDriftLengthToZ1(int32_t sector, float driftLength) const; @@ -148,7 +149,7 @@ class TPCFastTransformGeo GPUd() float convZtoDriftLength1(int32_t sector, float z) const; /// convert Local c.s. -> Pad, DriftLength - GPUd() std::tuple convLocalToPadDriftLength(int32_t sector, int32_t row, float y, float z) const; + GPUd() std::array convLocalToPadDriftLength(int32_t sector, int32_t row, float y, float z) const; /// Print method void print() const; @@ -229,7 +230,7 @@ GPUdi() void TPCFastTransformGeo::convGlobalToLocal(int32_t sector, float gx, fl lz = gz; } -GPUdi() std::tuple TPCFastTransformGeo::convPadDriftLengthToLocal(int32_t sector, int32_t row, float pad, float driftLength) const +GPUdi() std::array TPCFastTransformGeo::convPadDriftLengthToLocal(int32_t sector, int32_t row, float pad, float driftLength) const { /// convert Pad, DriftLength -> Local c.s. const RowInfo& rowInfo = getRowInfo(row); @@ -257,7 +258,7 @@ GPUdi() float TPCFastTransformGeo::convZtoDriftLength1(int32_t sector, float z) return (sector < NumberOfSectorsA) ? (mTPCzLength - z) : (z + mTPCzLength); } -GPUdi() std::tuple TPCFastTransformGeo::getZrange(int32_t sector) const +GPUdi() std::array TPCFastTransformGeo::getZrange(int32_t sector) const { /// z range for the sector if (sector < NumberOfSectorsA) { // TPC side A @@ -297,7 +298,7 @@ GPUdi() float TPCFastTransformGeo::getZreadout(int32_t sector) const } } -GPUdi() std::tuple TPCFastTransformGeo::convLocalToPadDriftLength(int32_t sector, int32_t row, float y, float z) const +GPUdi() std::array TPCFastTransformGeo::convLocalToPadDriftLength(int32_t sector, int32_t row, float y, float z) const { /// convert Local c.s. -> Pad, DriftLength float u, l; diff --git a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C index 50b667bb3e023..3cf3b697d2da9 100644 --- a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C +++ b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C @@ -303,12 +303,17 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* auto getInvCorrections = [&](int iSector, int iRow, float realY, float realZ, float& ix, float& iy, float& iz) { // get the inverse corrections ix, iy, iz at x,y,z ix = corr.getCorrectionXatRealYZ(iSector, iRow, realY, realZ); - std::tie(iy, iz) = corr.getCorrectionYZatRealYZ(iSector, iRow, realY, realZ); + const auto c = corr.getCorrectionYZatRealYZ(iSector, iRow, realY, realZ); + iy = c[0]; + iz = c[1]; }; auto getAllCorrections = [&](int iSector, int iRow, float y, float z, float& cx, float& cy, float& cz, float& ix, float& iy, float& iz) { // get the corrections cx,cy,cz at x,y,z - std::tie(cx, cy, cz) = corr.getCorrectionLocal(iSector, iRow, y, z); + const auto c = corr.getCorrectionLocal(iSector, iRow, y, z); + cx = c[0]; + cy = c[1]; + cz = c[2]; getInvCorrections(iSector, iRow, y + cy, z + cz, ix, iy, iz); }; From 4f9c0338ea02e35a3ca915c901765c3b8e5517c2 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Wed, 27 Aug 2025 18:28:46 +0000 Subject: [PATCH 097/285] TPC Splines: better smoothing between the voxels --- .../TPCFastSpaceChargeCorrectionHelper.cxx | 104 +++++++++++------- GPU/TPCFastTransformation/Spline2DHelper.cxx | 14 ++- GPU/TPCFastTransformation/Spline2DHelper.h | 2 +- .../TPCFastSpaceChargeCorrectionMap.h | 9 +- .../macro/TPCFastTransformInit.C | 25 +++-- 5 files changed, 94 insertions(+), 60 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index 9232598e6a35e..02cdefa85e119 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -148,9 +148,13 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas if (!processingInverseCorrection) { info.resetMaxValues(); } + info.updateMaxValues(1., 1., 1.); + info.updateMaxValues(-1., -1., -1.); + if (nDataPoints >= 4) { std::vector pointGU(nDataPoints); std::vector pointGV(nDataPoints); + std::vector pointWeight(nDataPoints); std::vector pointCorr(3 * nDataPoints); // 3 dimensions for (int i = 0; i < nDataPoints; ++i) { o2::gpu::TPCFastSpaceChargeCorrectionMap::CorrectionPoint p = data[i]; @@ -161,15 +165,14 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas } pointGU[i] = gu; pointGV[i] = gv; + pointWeight[i] = p.mWeight; pointCorr[3 * i + 0] = p.mDx; pointCorr[3 * i + 1] = p.mDy; pointCorr[3 * i + 2] = p.mDz; - if (!processingInverseCorrection) { - info.updateMaxValues(20. * p.mDx, 20. * p.mDy, 20. * p.mDz); - } + info.updateMaxValues(5. * p.mDx, 5. * p.mDy, 5. * p.mDz); } - helper.approximateDataPoints(spline, splineParameters.data(), 0., spline.getGridX1().getUmax(), 0., spline.getGridX2().getUmax(), &pointGU[0], - &pointGV[0], &pointCorr[0], nDataPoints); + helper.approximateDataPoints(spline, splineParameters.data(), 0., spline.getGridX1().getUmax(), 0., spline.getGridX2().getUmax(), pointGU.data(), + pointGV.data(), pointCorr.data(), pointWeight.data(), nDataPoints); } else { for (int i = 0; i < spline.getNumberOfParameters(); i++) { splineParameters[i] = 0.f; @@ -301,7 +304,7 @@ std::unique_ptr TPCFastSpaceChargeCorrectionHelper double dx, dy, dz; correctionLocal(iSector, iRow, y, z, dx, dy, dz); mCorrectionMap.addCorrectionPoint(iSector, iRow, - y, z, dx, dy, dz); + y, z, dx, dy, dz, 1.); } } } // row @@ -593,14 +596,6 @@ std::unique_ptr TPCFastSpaceChargeCorrect data.mCy *= -1.; data.mCz *= -1.; } - if (data.mNentries > 0) { - if (iSector < geo.getNumberOfSectorsA() && data.mZ < 0) { - LOG(error) << errMsg << "fitted Z coordinate " << data.mZ << " is negative for sector " << iSector; - } - if (iSector >= geo.getNumberOfSectorsA() && data.mZ > 0) { - LOG(error) << errMsg << "fitted Z coordinate " << data.mZ << " is positive for sector " << iSector; - } - } } }; processor.Process(myThread); @@ -624,6 +619,9 @@ std::unique_ptr TPCFastSpaceChargeCorrect } } + double maxError[3] = {0., 0., 0.}; + int nErrors = 0; + for (int iSector = 0; iSector < nSectors; iSector++) { // now process the data row-by-row @@ -682,9 +680,21 @@ std::unique_ptr TPCFastSpaceChargeCorrect } if (!msg.str().empty()) { - LOG(warning) << directionName << " correction: fitted voxel position is outside the voxel: " - << " sector " << iSector << " row " << iRow << " bin y " << iy << " bin z " << iz - << msg.str(); + bool isMaxErrorExceeded = (fabs(data.mX - x) / dx > maxError[0]) || + (fabs(data.mY - vox.mY) / vox.mDy > maxError[1]) || + (fabs(data.mZ - vox.mZ) / vox.mDz > maxError[2]); + static std::mutex mutex; + mutex.lock(); + nErrors++; + if (nErrors < 20 || isMaxErrorExceeded) { + LOG(warning) << directionName << " correction: error N " << nErrors << "fitted voxel position is outside the voxel: " + << " sector " << iSector << " row " << iRow << " bin y " << iy << " bin z " << iz + << msg.str(); + maxError[0] = GPUCommonMath::Max(maxError[0], fabs(data.mX - x) / dx); + maxError[1] = GPUCommonMath::Max(maxError[1], fabs(data.mY - vox.mY) / vox.mDy); + maxError[2] = GPUCommonMath::Max(maxError[2], fabs(data.mZ - vox.mZ) / vox.mDz); + } + mutex.unlock(); } } else { // no data, take voxel center position @@ -773,17 +783,27 @@ std::unique_ptr TPCFastSpaceChargeCorrect auto& info = correction.getSectorRowInfo(iSector, iRow); const auto& spline = correction.getSpline(iSector, iRow); - auto addEdge = [&](int iy1, int iz1, int iy2, int iz2, int nSteps) { + auto addVoxel = [&](int iy, int iz, double weight) { + auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; + if (vox.mSmoothingStep > 2) { + LOG(fatal) << "empty voxel is not repared: y " << iy << " z " << iz; + } + auto& data = vSectorData[iSector * nRows + iRow][iy * nZ2Xbins + iz]; + map.addCorrectionPoint(iSector, iRow, data.mY, data.mZ, data.mCx, data.mCy, data.mCz, weight); + }; + + auto addEdge = [&](int iy1, int iz1, int iy2, int iz2, int nPoints) { + // add n points on the edge between two voxels excluding the voxel points + if (nPoints < 1) + return; + if (iy1 < 0 || iy1 >= nY2Xbins || iz1 < 0 || iz1 >= nZ2Xbins) + return; + if (iy2 < 0 || iy2 >= nY2Xbins || iz2 < 0 || iz2 >= nZ2Xbins) + return; auto& data1 = vSectorData[iSector * nRows + iRow][iy1 * nZ2Xbins + iz1]; auto& vox1 = vRowVoxels[iy1 * nZ2Xbins + iz1]; auto& data2 = vSectorData[iSector * nRows + iRow][iy2 * nZ2Xbins + iz2]; auto& vox2 = vRowVoxels[iy2 * nZ2Xbins + iz2]; - if (vox1.mSmoothingStep > 2) { - LOG(fatal) << "empty voxel is not repared: y " << iy1 << " z " << iz1; - } - if (vox2.mSmoothingStep > 2) { - LOG(fatal) << "empty voxel is not repared: y " << iy2 << " z " << iz2; - } double y1 = data1.mY; double z1 = data1.mZ; double cx1 = data1.mCx; @@ -795,32 +815,36 @@ std::unique_ptr TPCFastSpaceChargeCorrect double cy2 = data2.mCy; double cz2 = data2.mCz; - for (int is = 0; is < nSteps; is++) { - double s2 = is / (double)nSteps; + for (int is = 1; is <= nPoints; is++) { + double s2 = is / (double)(nPoints + 1); double s1 = 1. - s2; double y = s1 * y1 + s2 * y2; double z = s1 * z1 + s2 * z2; double cx = s1 * cx1 + s2 * cx2; double cy = s1 * cy1 + s2 * cy2; double cz = s1 * cz1 + s2 * cz2; - map.addCorrectionPoint(iSector, iRow, y, z, cx, cy, cz); + map.addCorrectionPoint(iSector, iRow, y, z, cx, cy, cz, 1.); } }; + // original measurements weighted by 8 at each voxel and 8 additional artificial measurements around each voxel + // + // (y+1, z) 8 1 1 8 (y+1, z+1) + // 1 1 1 1 1 + // 1 1 1 1 1 + // (y,z) 8 1 1 8 1 + // 1 1 1 1 1 + for (int iy = 0; iy < nY2Xbins; iy++) { - for (int iz = 0; iz < nZ2Xbins - 1; iz++) { - addEdge(iy, iz, iy, iz + 1, 3); + for (int iz = 0; iz < nZ2Xbins; iz++) { + addVoxel(iy, iz, 8); + addEdge(iy, iz, iy, iz + 1, 2); + addEdge(iy, iz, iy + 1, iz, 2); + addEdge(iy, iz, iy + 1, iz + 1, 2); + addEdge(iy + 1, iz, iy, iz + 1, 2); } - addEdge(iy, nZ2Xbins - 1, iy, nZ2Xbins - 1, 1); } - for (int iz = 0; iz < nZ2Xbins; iz++) { - for (int iy = 0; iy < nY2Xbins - 1; iy++) { - addEdge(iy, iz, iy + 1, iz, 3); - } - addEdge(nY2Xbins - 1, iz, nY2Xbins - 1, iz, 1); - } // iy - } // iRow }; // myThread @@ -939,10 +963,11 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector dataPointGridU, dataPointGridV, dataPointF; + std::vector dataPointGridU, dataPointGridV, dataPointF, dataPointWeight; dataPointGridU.reserve(gridU.size() * gridV.size()); dataPointGridV.reserve(gridU.size() * gridV.size()); dataPointF.reserve(3 * gridU.size() * gridV.size()); + dataPointWeight.reserve(gridU.size() * gridV.size()); for (int iu = 0; iu < gridU.size(); iu++) { for (int iv = 0; iv < gridV.size(); iv++) { @@ -963,6 +988,7 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector::approximateFunctionViaDataPoints( mFdimensions = spline.getYdimensions(); std::vector dataPointX1(getNumberOfDataPoints()); std::vector dataPointX2(getNumberOfDataPoints()); + std::vector dataPointWeight(getNumberOfDataPoints(), 1.); std::vector dataPointF(getNumberOfDataPoints() * mFdimensions); double scaleX1 = (x1Max - x1Min) / ((double)mHelperU1.getSpline().getUmax()); @@ -256,7 +257,8 @@ void Spline2DHelper::approximateFunctionViaDataPoints( F(x1, x2, &dataPointF[ind * mFdimensions]); } } - approximateDataPoints(spline, spline.getParameters(), x1Min, x1Max, x2Min, x2Max, &dataPointX1[0], &dataPointX2[0], &dataPointF[0], getNumberOfDataPoints()); + approximateDataPoints(spline, spline.getParameters(), x1Min, x1Max, x2Min, x2Max, dataPointX1.data(), dataPointX2.data(), dataPointF.data(), + dataPointWeight.data(), getNumberOfDataPoints()); } template @@ -326,7 +328,7 @@ template void Spline2DHelper::approximateDataPoints( Spline2DContainer& spline, DataT* splineParameters, double x1Min, double x1Max, double x2Min, double x2Max, const double dataPointX1[], const double dataPointX2[], const double dataPointF[/*getNumberOfDataPoints() x nFdim*/], - int32_t nDataPoints) + const double dataPointWeight[], int32_t nDataPoints) { /// Create best-fit spline parameters for a given input function F @@ -343,6 +345,10 @@ void Spline2DHelper::approximateDataPoints( for (int32_t iPoint = 0; iPoint < nDataPoints; ++iPoint) { double u = fGridU.convXtoU(dataPointX1[iPoint]); double v = fGridV.convXtoU(dataPointX2[iPoint]); + double weight = dataPointWeight[iPoint]; + if (!(weight > 0.)) { + continue; + } int32_t iu = fGridU.getLeftKnotIndexForU(u); int32_t iv = fGridV.getLeftKnotIndexForU(v); double c[16]; @@ -353,14 +359,14 @@ void Spline2DHelper::approximateDataPoints( for (int32_t i = 0; i < 16; i++) { for (int32_t j = i; j < 16; j++) { - solver.A(ind[i], ind[j]) += c[i] * c[j]; + solver.A(ind[i], ind[j]) += weight * c[i] * c[j]; } } for (int32_t iDim = 0; iDim < nFdim; iDim++) { double f = (double)dataPointF[iPoint * nFdim + iDim]; for (int32_t i = 0; i < 16; i++) { - solver.B(ind[i], iDim) += f * c[i]; + solver.B(ind[i], iDim) += weight * f * c[i]; } } } // data points diff --git a/GPU/TPCFastTransformation/Spline2DHelper.h b/GPU/TPCFastTransformation/Spline2DHelper.h index 7195bab925f85..aa52c306a1a53 100644 --- a/GPU/TPCFastTransformation/Spline2DHelper.h +++ b/GPU/TPCFastTransformation/Spline2DHelper.h @@ -74,7 +74,7 @@ class Spline2DHelper void approximateDataPoints( Spline2DContainer& spline, DataT* splineParameters, double x1Min, double x1Max, double x2Min, double x2Max, const double dataPointX1[/*nDataPoints*/], const double dataPointX2[/*nDataPoints*/], - const double dataPointF[/*nDataPoints x spline.getYdimensions*/], int32_t nDataPoints); + const double dataPointF[/*nDataPoints x spline.getYdimensions*/], const double dataPointWeight[/*nDataPoints*/], int32_t nDataPoints); /// _______________ Interface for a step-wise construction of the best-fit spline ________________________ diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrectionMap.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrectionMap.h index fcee61ff09425..e54cf878ee2ff 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrectionMap.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrectionMap.h @@ -42,8 +42,9 @@ class TPCFastSpaceChargeCorrectionMap /// \brief The struct contains necessary info for TPC padrow /// struct CorrectionPoint { - double mY, mZ; // not-distorted local coordinates - double mDx, mDy, mDz; // corrections to the local coordinates + double mY{0.}, mZ{0.}; // not-distorted local coordinates + double mDx{0.}, mDy{0.}, mDz{0.}; // corrections to the local coordinates + double mWeight{0.}; // weight of the point }; /// _____________ Constructors / destructors __________________________ @@ -72,11 +73,11 @@ class TPCFastSpaceChargeCorrectionMap /// Starts the construction procedure, reserves temporary memory void addCorrectionPoint(int32_t iSector, int32_t iRow, double y, double z, - double dx, double dy, double dz) + double dx, double dy, double dz, double weight) { int32_t ind = mNrows * iSector + iRow; fDataPoints.at(ind).push_back(CorrectionPoint{y, z, - dx, dy, dz}); + dx, dy, dz, weight}); } const std::vector& getPoints(int32_t iSector, int32_t iRow) const diff --git a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C index 3cf3b697d2da9..3cb4812abafc1 100644 --- a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C +++ b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C @@ -319,6 +319,8 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* for (int direction = 0; direction < 2; direction++) { // 0 - normal, 1 - inverse + std::string directionName = (direction == 0) ? "direct" : "inverse"; + TTree* currentTree = (direction == 0) ? voxResTree : voxResTreeInverse; if (!currentTree) { std::cout << "tree voxResTree does not exist!" << std::endl; @@ -342,7 +344,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* double sumDiff[3] = {0., 0., 0.}; int64_t nDiff = 0; - std::cout << "fill debug ntuples at voxels ..." << std::endl; + LOG(info) << directionName << " correction: fill debug ntuples at voxels ..."; for (int32_t iVox = 0; iVox < currentTree->GetEntriesFast(); iVox++) { @@ -457,12 +459,11 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* } } - std::cout - << "fill debug ntuples everywhere .." << std::endl; + LOG(info) << directionName << " correction: fill debug ntuples everywhere .."; for (int32_t iSector = 0; iSector < geo.getNumberOfSectors(); iSector++) { // for (int32_t iSector = 0; iSector < 1; iSector++) { - std::cout << "debug ntules for sector " << iSector << std::endl; + LOG(info) << directionName << " correction: fill debug ntuples everywhere in sector " << iSector; int mirrorSector = (iSector >= geo.getNumberOfSectorsA()) ? iSector - geo.getNumberOfSectorsA() : iSector + geo.getNumberOfSectorsA(); @@ -592,17 +593,17 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* for (int32_t i = 0; i < 3; i++) { sumDiff[i] = sqrt(sumDiff[i]) / nDiff; } + LOG(info) << directionName << " correction: max and mean differences between spline and voxel corrections:"; + LOG(info) << "Max difference in x : " << maxDiff[0] << " at Sector " + << maxDiffSector[0] << " row " << maxDiffRow[0]; - std::cout << "Max difference in x : " << maxDiff[0] << " at Sector " - << maxDiffSector[0] << " row " << maxDiffRow[0] << std::endl; - - std::cout << "Max difference in y : " << maxDiff[1] << " at Sector " - << maxDiffSector[1] << " row " << maxDiffRow[1] << std::endl; + LOG(info) << "Max difference in y : " << maxDiff[1] << " at Sector " + << maxDiffSector[1] << " row " << maxDiffRow[1]; - std::cout << "Max difference in z : " << maxDiff[2] << " at Sector " - << maxDiffSector[2] << " row " << maxDiffRow[2] << std::endl; + LOG(info) << "Max difference in z : " << maxDiff[2] << " at Sector " + << maxDiffSector[2] << " row " << maxDiffRow[2]; - std::cout << "Mean difference in x,y,z : " << sumDiff[0] << " " << sumDiff[1] + LOG(info) << "Mean difference in x,y,z : " << sumDiff[0] << " " << sumDiff[1] << " " << sumDiff[2] << std::endl; } // direction From 29a87bf2e97952bfb30a0fdb0fa4f9c1b9e15d6a Mon Sep 17 00:00:00 2001 From: David Rohr Date: Mon, 1 Sep 2025 15:52:24 +0200 Subject: [PATCH 098/285] TPCFastTransform: fix compilation on GPU with the new splines --- GPU/TPCFastTransformation/Spline1DSpec.h | 6 +- GPU/TPCFastTransformation/Spline2DSpec.h | 12 +++- .../TPCFastSpaceChargeCorrection.h | 70 +++++++++---------- GPU/TPCFastTransformation/TPCFastTransform.h | 60 ++++++++-------- 4 files changed, 80 insertions(+), 68 deletions(-) diff --git a/GPU/TPCFastTransformation/Spline1DSpec.h b/GPU/TPCFastTransformation/Spline1DSpec.h index 28be5dd20e235..d72de5a446718 100644 --- a/GPU/TPCFastTransformation/Spline1DSpec.h +++ b/GPU/TPCFastTransformation/Spline1DSpec.h @@ -314,7 +314,11 @@ class Spline1DSpec : public Spline1DContainer const auto nYdimTmp = SplineUtil::getNdim(inpYdim); const auto nYdim = nYdimTmp.get(); - auto [dSdSl, dSdDl, dSdSr, dSdDr] = getSderivativesOverParsAtU(knotL, u); + auto val = getSderivativesOverParsAtU(knotL, u); + const auto& dSdSl = val[0]; + const auto& dSdDl = val[1]; + const auto& dSdSr = val[2]; + const auto& dSdDr = val[3]; for (int32_t dim = 0; dim < nYdim; ++dim) { S[dim] = dSdSr * Sr[dim] + dSdSl * Sl[dim] + dSdDl * Dl[dim] + dSdDr * Dr[dim]; } diff --git a/GPU/TPCFastTransformation/Spline2DSpec.h b/GPU/TPCFastTransformation/Spline2DSpec.h index 987ce1ad5d256..7c34b0890ce50 100644 --- a/GPU/TPCFastTransformation/Spline2DSpec.h +++ b/GPU/TPCFastTransformation/Spline2DSpec.h @@ -334,8 +334,16 @@ class Spline2DSpec const DataT* A = Parameters + (nu * iv + iu) * nYdim4; // values { {Y1,Y2,Y3}, {Y1,Y2,Y3}'v, {Y1,Y2,Y3}'u, {Y1,Y2,Y3}''vu } at {u0, v0} const DataT* B = A + nYdim4 * nu; // values { ... } at {u0, v1} - auto [dSl, dDl, dSr, dDr] = mGridX1.template getSderivativesOverParsAtU(knotU, u); - auto [dSd, dDd, dSu, dDu] = mGridX2.template getSderivativesOverParsAtU(knotV, v); + auto val1 = mGridX1.template getSderivativesOverParsAtU(knotU, u); + auto val2 = mGridX2.template getSderivativesOverParsAtU(knotV, v); + const auto& dSl = val1[0]; + const auto& dDl = val1[1]; + const auto& dSr = val1[2]; + const auto& dDr = val1[3]; + const auto& dSd = val2[0]; + const auto& dDd = val2[1]; + const auto& dSu = val2[2]; + const auto& dDu = val2[3]; // when nYdim == 1: // S = dSl * (dSd * A[0] + dDd * A[1]) + dDl * (dSd * A[2] + dDd * A[3]) + diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index 4ca5b74025743..ffbc8691ea268 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -56,15 +56,15 @@ class TPCFastSpaceChargeCorrection : public FlatObject float splineScalingWithZ{0.f}; ///< spline scaling factor in the Z region between the zOut and the readout plane public: - void set(float y0, float yScale, float z0, float zScale, float zOut, float zReadout) + void set(float y0_, float yScale_, float z0_, float zScale_, float zOut_, float zReadout_) { - this->y0 = y0; - this->yScale = yScale; - this->z0 = z0; - this->zScale = zScale; - this->zOut = zOut; + this->y0 = y0_; + this->yScale = yScale_; + this->z0 = z0_; + this->zScale = zScale_; + this->zOut = zOut_; // no scaling when the distance to the readout is too small - this->splineScalingWithZ = fabs(zReadout - zOut) > 1. ? 1. / (zReadout - zOut) : 0.; + this->splineScalingWithZ = fabs(zReadout_ - zOut_) > 1. ? 1. / (zReadout_ - zOut_) : 0.; } float getY0() const { return y0; } @@ -72,13 +72,13 @@ class TPCFastSpaceChargeCorrection : public FlatObject float getZ0() const { return z0; } float getZscale() const { return zScale; } - float getSpineScaleForZ(float z) const + GPUd() float getSpineScaleForZ(float z) const { return 1.f - GPUCommonMath::Clamp((z - zOut) * splineScalingWithZ, 0.f, 1.f); } /// convert local y, z to internal grid coordinates u,v, and spline scale - std::array convLocalToGridUntruncated(float y, float z) const + GPUd() std::array convLocalToGridUntruncated(float y, float z) const { return {(y - y0) * yScale, (z - z0) * zScale, getSpineScaleForZ(z)}; } @@ -458,21 +458,21 @@ GPUdi() std::array TPCFastSpaceChargeCorrection::convLocalToGrid(int32 /// convert local y, z to internal grid coordinates u,v /// return values: u, v, scaling factor const SplineType& spline = getSpline(sector, row); - auto [gridU, gridV, scale] = getSectorRowInfo(sector, row).gridMeasured.convLocalToGridUntruncated(y, z); + auto val = getSectorRowInfo(sector, row).gridMeasured.convLocalToGridUntruncated(y, z); // shrink to the grid - gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); - gridV = GPUCommonMath::Clamp(gridV, 0.f, (float)spline.getGridX2().getUmax()); - return {gridU, gridV, scale}; + val[0] = GPUCommonMath::Clamp(val[0], 0.f, (float)spline.getGridX1().getUmax()); + val[1] = GPUCommonMath::Clamp(val[1], 0.f, (float)spline.getGridX2().getUmax()); + return val; } GPUdi() bool TPCFastSpaceChargeCorrection::isLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const { /// check if local y, z are inside the grid - auto [gridU, gridV, scale] = getSectorRowInfo(sector, row).gridMeasured.convLocalToGridUntruncated(y, z); + auto val = getSectorRowInfo(sector, row).gridMeasured.convLocalToGridUntruncated(y, z); const auto& spline = getSpline(sector, row); // shrink to the grid - if (gridU < 0.f || gridU > (float)spline.getGridX1().getUmax() || // - gridV < 0.f || gridV > (float)spline.getGridX2().getUmax()) { + if (val[0] < 0.f || val[0] > (float)spline.getGridX1().getUmax() || // + val[1] < 0.f || val[1] > (float)spline.getGridX2().getUmax()) { return false; } return true; @@ -481,11 +481,11 @@ GPUdi() bool TPCFastSpaceChargeCorrection::isLocalInsideGrid(int32_t sector, int GPUdi() bool TPCFastSpaceChargeCorrection::isRealLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const { /// check if local y, z are inside the grid - auto [gridU, gridV, scale] = getSectorRowInfo(sector, row).gridReal.convLocalToGridUntruncated(y, z); + auto val = getSectorRowInfo(sector, row).gridReal.convLocalToGridUntruncated(y, z); const auto& spline = getSpline(sector, row); // shrink to the grid - if (gridU < 0.f || gridU > (float)spline.getGridX1().getUmax() || // - gridV < 0.f || gridV > (float)spline.getGridX2().getUmax()) { + if (val[0] < 0.f || val[0] > (float)spline.getGridX1().getUmax() || // + val[1] < 0.f || val[1] > (float)spline.getGridX2().getUmax()) { return false; } return true; @@ -501,11 +501,11 @@ GPUdi() std::array TPCFastSpaceChargeCorrection::convRealLocalToGrid(i { /// convert real y, z to the internal grid coordinates + scale const SplineType& spline = getSpline(sector, row); - auto [gridU, gridV, scale] = getSectorRowInfo(sector, row).gridReal.convLocalToGridUntruncated(y, z); + auto val = getSectorRowInfo(sector, row).gridReal.convLocalToGridUntruncated(y, z); // shrink to the grid - gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); - gridV = GPUCommonMath::Clamp(gridV, 0.f, (float)spline.getGridX2().getUmax()); - return {gridU, gridV, scale}; + val[0] = GPUCommonMath::Clamp(val[0], 0.f, (float)spline.getGridX1().getUmax()); + val[1] = GPUCommonMath::Clamp(val[1], 0.f, (float)spline.getGridX2().getUmax()); + return val; } GPUdi() std::array TPCFastSpaceChargeCorrection::convGridToRealLocal(int32_t sector, int32_t row, float gridU, float gridV) const @@ -520,35 +520,35 @@ GPUdi() std::array TPCFastSpaceChargeCorrection::getCorrectionLocal(in const SplineType& spline = getSpline(sector, row); const float* splineData = getSplineData(sector, row); - auto [gridU, gridV, scale] = convLocalToGrid(sector, row, y, z); + auto val = convLocalToGrid(sector, row, y, z); float dxyz[3]; - spline.interpolateAtU(splineData, gridU, gridV, dxyz); + spline.interpolateAtU(splineData, val[0], val[1], dxyz); - float dx = scale * GPUCommonMath::Clamp(dxyz[0], info.minCorr[0], info.maxCorr[0]); - float dy = scale * GPUCommonMath::Clamp(dxyz[1], info.minCorr[1], info.maxCorr[1]); - float dz = scale * GPUCommonMath::Clamp(dxyz[2], info.minCorr[2], info.maxCorr[2]); + float dx = val[2] * GPUCommonMath::Clamp(dxyz[0], info.minCorr[0], info.maxCorr[0]); + float dy = val[2] * GPUCommonMath::Clamp(dxyz[1], info.minCorr[1], info.maxCorr[1]); + float dz = val[2] * GPUCommonMath::Clamp(dxyz[2], info.minCorr[2], info.maxCorr[2]); return {dx, dy, dz}; } GPUdi() float TPCFastSpaceChargeCorrection::getCorrectionXatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const { const auto& info = getSectorRowInfo(sector, row); - auto [gridU, gridV, scale] = convRealLocalToGrid(sector, row, realY, realZ); + auto val = convRealLocalToGrid(sector, row, realY, realZ); float dx = 0; - getSplineInvX(sector, row).interpolateAtU(getSplineDataInvX(sector, row), gridU, gridV, &dx); - dx = scale * GPUCommonMath::Clamp(dx, info.minCorr[0], info.maxCorr[0]); + getSplineInvX(sector, row).interpolateAtU(getSplineDataInvX(sector, row), val[0], val[1], &dx); + dx = val[2] * GPUCommonMath::Clamp(dx, info.minCorr[0], info.maxCorr[0]); return dx; } GPUdi() std::array TPCFastSpaceChargeCorrection::getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const { - auto [gridU, gridV, scale] = convRealLocalToGrid(sector, row, realY, realZ); + auto val = convRealLocalToGrid(sector, row, realY, realZ); const auto& info = getSectorRowInfo(sector, row); float dyz[2]; - getSplineInvYZ(sector, row).interpolateAtU(getSplineDataInvYZ(sector, row), gridU, gridV, dyz); - dyz[0] = scale * GPUCommonMath::Clamp(dyz[0], info.minCorr[1], info.maxCorr[1]); - dyz[1] = scale * GPUCommonMath::Clamp(dyz[1], info.minCorr[2], info.maxCorr[2]); + getSplineInvYZ(sector, row).interpolateAtU(getSplineDataInvYZ(sector, row), val[0], val[1], dyz); + dyz[0] = val[2] * GPUCommonMath::Clamp(dyz[0], info.minCorr[1], info.maxCorr[1]); + dyz[1] = val[2] * GPUCommonMath::Clamp(dyz[1], info.minCorr[2], info.maxCorr[2]); return {dyz[0], dyz[1]}; } diff --git a/GPU/TPCFastTransformation/TPCFastTransform.h b/GPU/TPCFastTransformation/TPCFastTransform.h index 35b94446ce088..60f5952e6a1fc 100644 --- a/GPU/TPCFastTransformation/TPCFastTransform.h +++ b/GPU/TPCFastTransformation/TPCFastTransform.h @@ -349,17 +349,17 @@ class TPCFastTransform : public FlatObject GPUdi() void TPCFastTransform::convPadTimeToLocal(int32_t sector, int32_t row, float pad, float time, float& y, float& z, float vertexTime) const { float l = (time - mT0 - vertexTime) * mVdrift; // drift length [cm] - const auto local = getGeometry().convPadDriftLengthToLocal(sector, row, pad, l); - y = local[0]; - z = local[1]; + const auto localval = getGeometry().convPadDriftLengthToLocal(sector, row, pad, l); + y = localval[0]; + z = localval[1]; } GPUdi() void TPCFastTransform::convPadTimeToLocalInTimeFrame(int32_t sector, int32_t row, float pad, float time, float& y, float& z, float maxTimeBin) const { float l = getGeometry().getTPCzLength() + (time - mT0 - maxTimeBin) * mVdrift; // drift length [cm] - const auto local = getGeometry().convPadDriftLengthToLocal(sector, row, pad, l); - y = local[0]; - z = local[1]; + const auto localval = getGeometry().convPadDriftLengthToLocal(sector, row, pad, l); + y = localval[0]; + z = localval[1]; } // ---------------------------------------------------------------------- @@ -432,22 +432,22 @@ GPUdi() void TPCFastTransform::TransformLocal(int32_t sector, int32_t row, float dz = corrLocal[2]; if (ref) { if ((scale > 0.f) && (scaleMode == 0)) { // scaling was requested - auto [dxRef, dyRef, dzRef] = ref->mCorrection.getCorrectionLocal(sector, row, y, z); - dx = (dx - dxRef) * scale + dxRef; - dy = (dy - dyRef) * scale + dyRef; - dz = (dz - dzRef) * scale + dzRef; + auto val = ref->mCorrection.getCorrectionLocal(sector, row, y, z); + dx = (dx - val[0]) * scale + val[0]; + dy = (dy - val[1]) * scale + val[1]; + dz = (dz - val[2]) * scale + val[2]; } else if ((scale != 0.f) && ((scaleMode == 1) || (scaleMode == 2))) { - auto [dxRef, dyRef, dzRef] = ref->mCorrection.getCorrectionLocal(sector, row, y, z); - dx = dxRef * scale + dx; - dy = dyRef * scale + dy; - dz = dzRef * scale + dz; + auto val = ref->mCorrection.getCorrectionLocal(sector, row, y, z); + dx = val[0] * scale + dx; + dy = val[1] * scale + dy; + dz = val[2] * scale + dz; } } if (ref2 && (scale2 != 0)) { - auto [dxRef, dyRef, dzRef] = ref2->mCorrection.getCorrectionLocal(sector, row, y, z); - dx = dxRef * scale2 + dx; - dy = dyRef * scale2 + dy; - dz = dzRef * scale2 + dz; + auto val = ref2->mCorrection.getCorrectionLocal(sector, row, y, z); + dx = val[0] * scale2 + dx; + dy = val[1] * scale2 + dy; + dz = val[2] * scale2 + dz; } } } @@ -617,9 +617,9 @@ GPUdi() void TPCFastTransform::TransformIdeal(int32_t sector, int32_t row, float x = getGeometry().getRowInfo(row).x; float driftLength = (time - mT0 - vertexTime) * mVdrift; // drift length cm - const auto local = getGeometry().convPadDriftLengthToLocal(sector, row, pad, driftLength); - y = local[0]; - z = local[1]; + const auto localval = getGeometry().convPadDriftLengthToLocal(sector, row, pad, driftLength); + y = localval[0]; + z = localval[1]; } GPUdi() float TPCFastTransform::convTimeToZinTimeFrame(int32_t sector, float time, float maxTimeBin) const @@ -732,18 +732,18 @@ GPUdi() void TPCFastTransform::InverseTransformYZtoNominalYZ(int32_t sector, int if (ref) { // scaling was requested if (scaleMode == 0 && scale > 0.f) { - const auto [dyRef, dzRef] = ref->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); - dy = (dy - dyRef) * scale + dyRef; - dz = (dz - dzRef) * scale + dzRef; + const auto val = ref->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); + dy = (dy - val[0]) * scale + val[0]; + dz = (dz - val[1]) * scale + val[1]; } else if ((scale != 0) && ((scaleMode == 1) || (scaleMode == 2))) { - const auto [dyRef, dzRef] = ref->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); - dy = dyRef * scale + dy; - dz = dzRef * scale + dz; + const auto val = ref->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); + dy = val[0] * scale + dy; + dz = val[1] * scale + dz; } if (ref2 && (scale2 != 0)) { - const auto [dyRef, dzRef] = ref2->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); - dy = dyRef * scale2 + dy; - dz = dzRef * scale2 + dz; + const auto val = ref2->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); + dy = val[0] * scale2 + dy; + dz = val[1] * scale2 + dz; } } } From 4879ec57b1f38f4d3bea73be08e3a457a191ee92 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Wed, 17 Sep 2025 17:49:29 +0000 Subject: [PATCH 099/285] TPC Splines: bugfixes in spline merging --- .../TPCFastSpaceChargeCorrectionHelper.cxx | 2 +- GPU/TPCFastTransformation/Spline2DSpec.h | 48 +++++++++---------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index 02cdefa85e119..eb7620c358774 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -1103,7 +1103,7 @@ void TPCFastSpaceChargeCorrectionHelper::mergeCorrections( for (int iu = 0; iu < gridU.getNumberOfKnots(); iu++) { double u = gridU.getKnot(iu).u; for (int iv = 0; iv < gridV.getNumberOfKnots(); iv++) { - double v = gridV.getKnot(iu).u; + double v = gridV.getKnot(iv).u; int knotIndex = spline.getKnotIndex(iu, iv); float P[nKnotPar3d]; diff --git a/GPU/TPCFastTransformation/Spline2DSpec.h b/GPU/TPCFastTransformation/Spline2DSpec.h index 7c34b0890ce50..5681de2dc5fe9 100644 --- a/GPU/TPCFastTransformation/Spline2DSpec.h +++ b/GPU/TPCFastTransformation/Spline2DSpec.h @@ -382,8 +382,8 @@ class Spline2DSpec const auto nYdim4 = nYdim * 4; DataT *S = P, - *R = P + nYdim, - *Q = P + nYdim * 2, + *Q = P + nYdim, + *R = P + nYdim * 2, *W = P + nYdim * 3; const DataT& u = u1; @@ -425,28 +425,6 @@ class Spline2DSpec } } - // Derivative R = dS / du - // R = dRdSl * (dSdSd * A[0] + dSdDd * A[1]) + dRdDl * (dSdSd * A[2] + dSdDd * A[3]) + - // dRdSr * (dSdSd * A[4] + dSdDd * A[5]) + dRdDr * (dSdSd * A[6] + dSdDd * A[7]) + - // dRdSl * (dSdSu * B[0] + dSdDu * B[1]) + dRdDl * (dSdSu * B[2] + dSdDu * B[3]) + - // dRdSr * (dSdSu * B[4] + dSdDu * B[5]) + dRdDr * (dSdSu * B[6] + dSdDu * B[7]); - - { - DataT a[8] = {dRdSl * dSdSd, dRdSl * dSdDd, dRdDl * dSdSd, dRdDl * dSdDd, - dRdSr * dSdSd, dRdSr * dSdDd, dRdDr * dSdSd, dRdDr * dSdDd}; - DataT b[8] = {dRdSl * dSdSu, dRdSl * dSdDu, dRdDl * dSdSu, dRdDl * dSdDu, - dRdSr * dSdSu, dRdSr * dSdDu, dRdDr * dSdSu, dRdDr * dSdDu}; - - // R = sum a[i]*A[i] + b[i]*B[i] - - for (int32_t dim = 0; dim < nYdim; dim++) { - R[dim] = 0; - for (int32_t i = 0; i < 8; i++) { - R[dim] += a[i] * A[nYdim * i + dim] + b[i] * B[nYdim * i + dim]; - } - } - } - // Derivative Q = dS / dv // Q = dSdSl * (dQdSd * A[0] + dQdDd * A[1]) + dSdDl * (dQdSd * A[2] + dQdDd * A[3]) + // dSdSr * (dQdSd * A[4] + dQdDd * A[5]) + dSdDr * (dQdSd * A[6] + dQdDd * A[7]) + @@ -469,6 +447,28 @@ class Spline2DSpec } } + // Derivative R = dS / du + // R = dRdSl * (dSdSd * A[0] + dSdDd * A[1]) + dRdDl * (dSdSd * A[2] + dSdDd * A[3]) + + // dRdSr * (dSdSd * A[4] + dSdDd * A[5]) + dRdDr * (dSdSd * A[6] + dSdDd * A[7]) + + // dRdSl * (dSdSu * B[0] + dSdDu * B[1]) + dRdDl * (dSdSu * B[2] + dSdDu * B[3]) + + // dRdSr * (dSdSu * B[4] + dSdDu * B[5]) + dRdDr * (dSdSu * B[6] + dSdDu * B[7]); + + { + DataT a[8] = {dRdSl * dSdSd, dRdSl * dSdDd, dRdDl * dSdSd, dRdDl * dSdDd, + dRdSr * dSdSd, dRdSr * dSdDd, dRdDr * dSdSd, dRdDr * dSdDd}; + DataT b[8] = {dRdSl * dSdSu, dRdSl * dSdDu, dRdDl * dSdSu, dRdDl * dSdDu, + dRdSr * dSdSu, dRdSr * dSdDu, dRdDr * dSdSu, dRdDr * dSdDu}; + + // R = sum a[i]*A[i] + b[i]*B[i] + + for (int32_t dim = 0; dim < nYdim; dim++) { + R[dim] = 0; + for (int32_t i = 0; i < 8; i++) { + R[dim] += a[i] * A[nYdim * i + dim] + b[i] * B[nYdim * i + dim]; + } + } + } + // cross-derivative W = (dS)^2 / du / dv // W = dRdSl * (dQdSd * A[0] + dQdDd * A[1]) + dRdDl * (dQdSd * A[2] + dQdDd * A[3]) + // dRdSr * (dQdSd * A[4] + dQdDd * A[5]) + dRdDr * (dQdSd * A[6] + dQdDd * A[7]) + From a4c73037924841e964da679408d1e66fcca2340e Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Mon, 6 Oct 2025 21:19:19 +0000 Subject: [PATCH 100/285] TPC Splines: add backward compatibility --- .../TPCFastSpaceChargeCorrectionHelper.cxx | 22 +- .../TPCFastSpaceChargeCorrection.cxx | 331 +++++++++++++++--- .../TPCFastSpaceChargeCorrection.h | 90 ++--- GPU/TPCFastTransformation/TPCFastTransform.h | 2 +- .../TPCFastTransformGeo.cxx | 8 +- .../TPCFastTransformGeo.h | 10 +- .../TPCFastTransformationLinkDef_O2.h | 23 +- .../macro/TPCFastTransformInit.C | 92 ++--- 8 files changed, 408 insertions(+), 170 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index eb7620c358774..faba4f2ce065e 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -180,15 +180,15 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas } if (processingInverseCorrection) { - float* splineX = correction.getSplineDataInvX(sector, row); - float* splineYZ = correction.getSplineDataInvYZ(sector, row); + float* splineX = correction.getCorrectionDataInvX(sector, row); + float* splineYZ = correction.getCorrectionDataInvYZ(sector, row); for (int i = 0; i < spline.getNumberOfParameters() / 3; i++) { splineX[i] = splineParameters[3 * i + 0]; splineYZ[2 * i + 0] = splineParameters[3 * i + 1]; splineYZ[2 * i + 1] = splineParameters[3 * i + 2]; } } else { - float* splineXYZ = correction.getSplineData(sector, row); + float* splineXYZ = correction.getCorrectionData(sector, row); for (int i = 0; i < spline.getNumberOfParameters(); i++) { splineXYZ[i] = splineParameters[i]; } @@ -1000,8 +1000,8 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector(mFlatBufferPtr + splineDataOffset); - bufferSize = splineDataOffset + mDataSizeBytes[is]; + size_t correctionDataOffset = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); + mCorrectionData[is] = reinterpret_cast(mFlatBufferPtr + correctionDataOffset); + bufferSize = correctionDataOffset + mCorrectionDataSize[is]; } } +void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBufferPtr) +{ + /// Sets the actual location of the external flat buffer after it has been moved (e.g. to another maschine) + + if (mClassVersion == 4) { + FlatObject::setActualBufferAddress(actualFlatBufferPtr); + + size_t scSize = sizeof(SplineType) * mNumberOfScenarios; + + mScenarioPtr = reinterpret_cast(mFlatBufferPtr); + + size_t scBufferOffset = alignSize(scSize, SplineType::getBufferAlignmentBytes()); + size_t scBufferSize = 0; + + for (int32_t i = 0; i < mNumberOfScenarios; i++) { + SplineType& sp = mScenarioPtr[i]; + sp.setActualBufferAddress(mFlatBufferPtr + scBufferOffset + scBufferSize); + scBufferSize = alignSize(scBufferSize + sp.getFlatBufferSize(), sp.getBufferAlignmentBytes()); + } + size_t bufferSize = scBufferOffset + scBufferSize; + for (int32_t is = 0; is < 3; is++) { + size_t correctionDataOffset = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); + mCorrectionData[is] = reinterpret_cast(mFlatBufferPtr + correctionDataOffset); + bufferSize = correctionDataOffset + mCorrectionDataSize[is]; + } + return; + } + + if (mClassVersion != 3) { + LOG(fatal) << "TPCFastSpaceChargeCorrection::setActualBufferAddress() called with class version " << mClassVersion << ". This is not supported."; + return; + } + + // Class version 3 + + struct RowInfoVersion3 { + int32_t splineScenarioID{0}; ///< scenario index (which of Spline2D splines to use) + size_t dataOffsetBytes[3]{0}; ///< offset for the spline data withing a TPC sector + }; + + struct RowActiveAreaVersion3 { + float maxDriftLengthCheb[5]{0.f}; + float vMax{0.f}; + float cuMin{0.f}; + float cuMax{0.f}; + float cvMax{0.f}; + }; + + struct SectorRowInfoVersion3 { + float gridV0{0.f}; ///< V coordinate of the V-grid start + float gridCorrU0{0.f}; ///< U coordinate of the U-grid start for corrected U + float gridCorrV0{0.f}; ///< V coordinate of the V-grid start for corrected V + float scaleCorrUtoGrid{0.f}; ///< scale corrected U to U-grid coordinate + float scaleCorrVtoGrid{0.f}; ///< scale corrected V to V-grid coordinate + RowActiveAreaVersion3 activeArea; + }; + + FlatObject::setActualBufferAddress(actualFlatBufferPtr); + + size_t oldRowsOffset = 0; + size_t oldRowsSize = sizeof(RowInfoVersion3) * mGeo.getNumberOfRows(); + + size_t oldSectorRowsOffset = oldRowsOffset + oldRowsSize; + size_t oldSectorRowsSize = sizeof(SectorRowInfoVersion3) * mGeo.getNumberOfRows() * mGeo.getNumberOfSectors(); + + size_t oldScenariosOffset = alignSize(oldSectorRowsOffset + oldSectorRowsSize, SplineType::getClassAlignmentBytes()); + size_t scenariosSize = sizeof(SplineType) * mNumberOfScenarios; + + SplineType* oldScenarioPtr = reinterpret_cast(mFlatBufferPtr + oldScenariosOffset); + + { // copy old-format sector and row parameters from the buffer to the arrays + + auto* oldRowInfos = reinterpret_cast(mFlatBufferPtr + oldRowsOffset); + auto* oldSectorRowInfos = reinterpret_cast(mFlatBufferPtr + oldSectorRowsOffset); + + size_t sectorDataSize[3]; + for (int32_t is = 0; is < 3; is++) { + sectorDataSize[is] = mCorrectionDataSize[is] / mGeo.getNumberOfSectors(); + } + + for (int32_t iSector = 0; iSector < mGeo.getNumberOfSectors(); iSector++) { + + for (int32_t iRow = 0; iRow < mGeo.getNumberOfRows(); iRow++) { + RowInfoVersion3& oldRowInfo = oldRowInfos[iRow]; + SectorRowInfoVersion3& oldSectorRowInfo = oldSectorRowInfos[mGeo.getNumberOfRows() * iSector + iRow]; + + // the spline buffer is not yet initialised, don't try to access knot positions etc + const auto& spline = oldScenarioPtr[oldRowInfo.splineScenarioID]; + + SectorRowInfo& newSectorRow = getSectorRowInfo(iSector, iRow); + + newSectorRow.splineScenarioID = oldRowInfo.splineScenarioID; + for (int32_t is = 0; is < 3; is++) { + newSectorRow.dataOffsetBytes[is] = sectorDataSize[is] * iSector + oldRowInfo.dataOffsetBytes[is]; + } + + { // grid for the measured coordinates + float y0 = mGeo.getRowInfo(iRow).yMin; + float yScale = spline.getGridX1().getUmax() / mGeo.getRowInfo(iRow).getYwidth(); + float zReadout = mGeo.getZreadout(iSector); + float zOut = mGeo.getTPCzLength() - oldSectorRowInfo.gridV0; + float z0 = -3.; + float zScale = spline.getGridX2().getUmax() / (zOut - z0); + if (iSector >= mGeo.getNumberOfSectorsA()) { + zOut = -zOut; + z0 = zOut; + } + newSectorRow.gridMeasured.set(y0, yScale, z0, zScale, zOut, zReadout); + } + + { // grid for the real coordinates + float y0 = oldSectorRowInfo.gridCorrU0; + float yScale = oldSectorRowInfo.scaleCorrUtoGrid; + float zReadout = mGeo.getZreadout(iSector); + float zOut = mGeo.getTPCzLength() - oldSectorRowInfo.gridCorrV0; + float zScale = oldSectorRowInfo.scaleCorrVtoGrid; + float z0 = zOut - spline.getGridX2().getUmax() / zScale; + if (iSector >= mGeo.getNumberOfSectorsA()) { + zOut = -zOut; + z0 = zOut; + } + newSectorRow.gridReal.set(y0, yScale, z0, zScale, zOut, zReadout); + } + + newSectorRow.resetMaxValues(); + newSectorRow.updateMaxValues(-50.f, -50.f, -50.f); + newSectorRow.updateMaxValues(50.f, 50.f, 50.f); + } + } + } + + // move spline scenarios to the new place in the buffer + + mScenarioPtr = reinterpret_cast(mFlatBufferPtr); + memmove(mScenarioPtr, oldScenarioPtr, scenariosSize); + + size_t oldScenariosBufferOffset = alignSize(oldScenariosOffset + scenariosSize, SplineType::getBufferAlignmentBytes()); + size_t scenariosBufferOffset = alignSize(scenariosSize, SplineType::getBufferAlignmentBytes()); + + size_t oldScenariosBufferSize = 0; + size_t scenariosBufferSize = 0; + for (int32_t i = 0; i < mNumberOfScenarios; i++) { + SplineType& sp = mScenarioPtr[i]; + char* oldAddress = mFlatBufferPtr + oldScenariosBufferOffset + oldScenariosBufferSize; + char* newAddress = mFlatBufferPtr + scenariosBufferOffset + scenariosBufferSize; + memmove(newAddress, oldAddress, sp.getFlatBufferSize()); + sp.setActualBufferAddress(newAddress); + oldScenariosBufferSize = alignSize(oldScenariosBufferSize + sp.getFlatBufferSize(), sp.getBufferAlignmentBytes()); + scenariosBufferSize = alignSize(scenariosBufferSize + sp.getFlatBufferSize(), sp.getBufferAlignmentBytes()); + } + + size_t oldBufferSize = oldScenariosBufferOffset + oldScenariosBufferSize; + size_t bufferSize = scenariosBufferOffset + scenariosBufferSize; + + // move spline data to the new place in the buffer + + for (int32_t is = 0; is < 3; is++) { + size_t oldCorrectionDataOffset = alignSize(oldBufferSize, SplineType::getParameterAlignmentBytes()); + size_t correctionDataOffset = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); + mCorrectionData[is] = reinterpret_cast(mFlatBufferPtr + correctionDataOffset); + memmove(mCorrectionData[is], mFlatBufferPtr + oldCorrectionDataOffset, mCorrectionDataSize[is]); + oldBufferSize = oldCorrectionDataOffset + mCorrectionDataSize[is]; + bufferSize = correctionDataOffset + mCorrectionDataSize[is]; + } + + mFlatBufferSize = bufferSize; + + // now convert the spline data to the new format + for (int32_t iSector = 0; iSector < mGeo.getNumberOfSectors(); iSector++) { + bool isAside = (iSector < mGeo.getNumberOfSectorsA()); + for (int32_t iRow = 0; iRow < mGeo.getNumberOfRows(); iRow++) { + + SectorRowInfo& sectorRow = getSectorRowInfo(iSector, iRow); + const auto& spline = mScenarioPtr[sectorRow.splineScenarioID]; + + int nSplineDimensions[3] = {3, 1, 2}; + + for (int iSpline = 0; iSpline < 3; iSpline++) { + int nDim = nSplineDimensions[iSpline]; + int nKnotParameters = 4 * nDim; + auto* data = getCorrectionData(iSector, iRow, iSpline); + + // lambda to swap parameters at two knots + auto swapKnots = [&](int i1, int j1, int i2, int j2) { + auto k1 = spline.getKnotIndex(i1, j1); + auto k2 = spline.getKnotIndex(i2, j2); + for (int ipar = 0; ipar < nKnotParameters; ipar++) { + std::swap(data[nKnotParameters * k1 + ipar], data[nKnotParameters * k2 + ipar]); + } + }; + + // reorder knots for the A side Y == old U, Z == - old V + if (isAside) { + for (int32_t i = 0; i < spline.getGridX1().getNumberOfKnots(); i++) { + for (int32_t j = 0; j < spline.getGridX2().getNumberOfKnots() / 2; j++) { + swapKnots(i, j, i, spline.getGridX2().getNumberOfKnots() - 1 - j); + } + } + } else { // reorder knots for the C side Y == - old U, Z == old V + for (int32_t i = 0; i < spline.getGridX1().getNumberOfKnots() / 2; i++) { + for (int32_t j = 0; j < spline.getGridX2().getNumberOfKnots(); j++) { + swapKnots(i, j, spline.getGridX1().getNumberOfKnots() - 1 - i, j); + } + } + } + + // correct sign of the parameters due to the coordinate swaps + + for (int32_t iKnot = 0; iKnot < spline.getNumberOfKnots(); iKnot++) { + // new grid directions for all corrections + for (int iDim = 0; iDim < nDim; iDim++) { + if (isAside) { + data[nKnotParameters * iKnot + nDim * 1 + iDim] *= -1; // invert Z derivatives on A side + } else { + data[nKnotParameters * iKnot + nDim * 2 + iDim] *= -1; // invert Y derivatives on C side + } + data[nKnotParameters * iKnot + nDim * 3 + iDim] *= -1; // invert cross derivatives on both sides + } + // new correction directions + if (iSpline == 0) { // dX,dU,dV -> dX,dY,dZ + if (isAside) { + data[nKnotParameters * iKnot + nDim * 0 + 2] *= -1; // invert correction in Z + data[nKnotParameters * iKnot + nDim * 1 + 2] *= -1; // invert correction in Z Z-derivative + data[nKnotParameters * iKnot + nDim * 2 + 2] *= -1; // invert correction in Z Y-derivative + data[nKnotParameters * iKnot + nDim * 3 + 2] *= -1; // invert correction in Z cross derivative + } else { + data[nKnotParameters * iKnot + nDim * 0 + 1] *= -1; // invert correction in Y + data[nKnotParameters * iKnot + nDim * 1 + 1] *= -1; // invert correction in Y Z-derivative + data[nKnotParameters * iKnot + nDim * 2 + 1] *= -1; // invert correction in Y Y-derivative + data[nKnotParameters * iKnot + nDim * 3 + 1] *= -1; // invert correction in Y cross derivative + } + } else if (iSpline == 2) { // dU,dV at real U,V -> dY,dZ at real Y,Z + if (isAside) { + data[nKnotParameters * iKnot + nDim * 0 + 1] *= -1; // invert correction in Z + data[nKnotParameters * iKnot + nDim * 1 + 1] *= -1; // invert correction in Z Z-derivative + data[nKnotParameters * iKnot + nDim * 2 + 1] *= -1; // invert correction in Z Y-derivative + data[nKnotParameters * iKnot + nDim * 3 + 1] *= -1; // invert correction in Z cross derivative + } else { + data[nKnotParameters * iKnot + nDim * 0 + 0] *= -1; // invert correction in Y + data[nKnotParameters * iKnot + nDim * 1 + 0] *= -1; // invert correction in Y Z-derivative + data[nKnotParameters * iKnot + nDim * 2 + 0] *= -1; // invert correction in Y Y-derivative + data[nKnotParameters * iKnot + nDim * 3 + 0] *= -1; // invert correction in Y cross derivative + } + } + } + + } // iSpline + } // iRow + } // iSector + + // set the class version to the current one + mClassVersion = 4; +} + void TPCFastSpaceChargeCorrection::setFutureBufferAddress(char* futureFlatBufferPtr) { /// Sets a future location of the external flat buffer before moving it to this location (i.e. when copying to GPU). @@ -186,9 +432,9 @@ void TPCFastSpaceChargeCorrection::setFutureBufferAddress(char* futureFlatBuffer sp.setFutureBufferAddress(newSplineBuf); } mScenarioPtr = relocatePointer(oldBuffer, newBuffer, mScenarioPtr); - mSplineData[0] = relocatePointer(oldBuffer, newBuffer, mSplineData[0]); - mSplineData[1] = relocatePointer(oldBuffer, newBuffer, mSplineData[1]); - mSplineData[2] = relocatePointer(oldBuffer, newBuffer, mSplineData[2]); + mCorrectionData[0] = relocatePointer(oldBuffer, newBuffer, mCorrectionData[0]); + mCorrectionData[1] = relocatePointer(oldBuffer, newBuffer, mCorrectionData[1]); + mCorrectionData[2] = relocatePointer(oldBuffer, newBuffer, mCorrectionData[2]); FlatObject::setFutureBufferAddress(futureFlatBufferPtr); } @@ -199,7 +445,7 @@ void TPCFastSpaceChargeCorrection::print() const mGeo.print(); LOG(info) << " mNumberOfScenarios = " << mNumberOfScenarios; LOG(info) << " mTimeStamp = " << mTimeStamp; - LOG(info) << " mDataSizeBytes = " << mDataSizeBytes[0] << " " << mDataSizeBytes[1] << " " << mDataSizeBytes[2]; + LOG(info) << " mCorrectionDataSize = " << mCorrectionDataSize[0] << " " << mCorrectionDataSize[1] << " " << mCorrectionDataSize[2]; if (mScenarioPtr) { for (int32_t i = 0; i < mNumberOfScenarios; i++) { @@ -213,7 +459,7 @@ void TPCFastSpaceChargeCorrection::print() const for (int32_t ir = 0; ir < mGeo.getNumberOfRows(); ir++) { LOG(info) << "sector " << is << " row " << ir << ": "; const SplineType& spline = getSpline(is, ir); - const float* d = getSplineData(is, ir); + const float* d = getCorrectionData(is, ir); int32_t k = 0; for (int32_t i = 0; i < spline.getGridX1().getNumberOfKnots(); i++) { for (int32_t j = 0; j < spline.getGridX2().getNumberOfKnots(); j++, k++) { @@ -250,7 +496,6 @@ void TPCFastSpaceChargeCorrection::startConstruction(const TPCFastTransformGeo& assert(mConstructionScenarios != nullptr); for (int32_t i = 0; i < mGeo.getNumberOfSectors(); i++) { - mSectorInfo[i].vMax1 = 0.; for (int32_t j = 0; j < mGeo.getNumberOfRows(); j++) { auto& row = mSectorRowInfos[mGeo.getMaxNumberOfRows() * i + j]; row.splineScenarioID = -1; @@ -276,8 +521,8 @@ void TPCFastSpaceChargeCorrection::startConstruction(const TPCFastTransformGeo& mScenarioPtr = nullptr; for (int32_t s = 0; s < 3; s++) { - mSplineData[s] = nullptr; - mDataSizeBytes[s] = 0; + mCorrectionData[s] = nullptr; + mCorrectionDataSize[s] = 0; } mClassVersion = 4; } @@ -338,20 +583,20 @@ void TPCFastSpaceChargeCorrection::finishConstruction() scBufferSize = alignSize(scBufferSize + sp.getFlatBufferSize(), sp.getBufferAlignmentBytes()); } size_t bufferSize = scBufferOffsets[0] + scBufferSize; - size_t splineDataOffset[3]; + size_t correctionDataOffset[3]; for (int32_t is = 0; is < 3; is++) { - splineDataOffset[is] = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); - mDataSizeBytes[is] = 0; + correctionDataOffset[is] = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); + mCorrectionDataSize[is] = 0; for (int32_t i = 0; i < mGeo.getNumberOfSectors(); i++) { for (int32_t j = 0; j < mGeo.getNumberOfRows(); j++) { SectorRowInfo& row = getSectorRowInfo(i, j); SplineType& spline = mConstructionScenarios[row.splineScenarioID]; - row.dataOffsetBytes[is] = alignSize(mDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); - mDataSizeBytes[is] = row.dataOffsetBytes[is] + spline.getSizeOfParameters(); + row.dataOffsetBytes[is] = alignSize(mCorrectionDataSize[is], SplineType::getParameterAlignmentBytes()); + mCorrectionDataSize[is] = row.dataOffsetBytes[is] + spline.getSizeOfParameters(); } } - mDataSizeBytes[is] = alignSize(mDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); - bufferSize = splineDataOffset[is] + mDataSizeBytes[is]; + mCorrectionDataSize[is] = alignSize(mCorrectionDataSize[is], SplineType::getParameterAlignmentBytes()); + bufferSize = correctionDataOffset[is] + mCorrectionDataSize[is]; } FlatObject::finishConstruction(bufferSize); @@ -366,7 +611,7 @@ void TPCFastSpaceChargeCorrection::finishConstruction() } for (int32_t is = 0; is < 3; is++) { - mSplineData[is] = reinterpret_cast(mFlatBufferPtr + splineDataOffset[is]); + mCorrectionData[is] = reinterpret_cast(mFlatBufferPtr + correctionDataOffset[is]); } releaseConstructionMemory(); @@ -380,13 +625,11 @@ GPUd() void TPCFastSpaceChargeCorrection::setNoCorrection() // initialise all corrections to 0. for (int32_t sector = 0; sector < mGeo.getNumberOfSectors(); sector++) { - getSectorInfo(sector).vMax1 = mGeo.getTPCzLength(); - for (int32_t row = 0; row < mGeo.getNumberOfRows(); row++) { const SplineType& spline = getSpline(sector, row); for (int32_t is = 0; is < 3; is++) { - float* data = getSplineData(sector, row, is); + float* data = getCorrectionData(sector, row, is); int32_t nPar = spline.getNumberOfParameters(); if (is == 1) { nPar = nPar / 3; diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index ffbc8691ea268..b4fab68b91542 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -39,11 +39,9 @@ namespace gpu class TPCFastSpaceChargeCorrection : public FlatObject { public: - /// - /// \brief The struct contains necessary info for TPC padrow - /// - struct RowInfo { - ClassDefNV(RowInfo, 1); + // obsolete structure, declared here only for backward compatibility + struct SliceInfo { + ClassDefNV(SliceInfo, 2); }; struct GridInfo { @@ -144,11 +142,6 @@ class TPCFastSpaceChargeCorrection : public FlatObject ClassDefNV(SectorRowInfo, 2); }; - struct SectorInfo { - float vMax1{0.f}; ///< Max value of V coordinate - ClassDefNV(SectorInfo, 1); - }; - typedef Spline2D SplineTypeXYZ; typedef Spline2D SplineTypeInvX; typedef Spline2D SplineTypeInvYZ; @@ -188,6 +181,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// Moving the class with its external buffer to another location + void setActualBufferAddressOld(char* actualFlatBufferPtr); void setActualBufferAddress(char* actualFlatBufferPtr); void setFutureBufferAddress(char* futureFlatBufferPtr); @@ -215,10 +209,6 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// Sets the time stamp of the current calibaration GPUd() void setTimeStamp(int64_t v) { mTimeStamp = v; } - /// Set safety marging for the interpolation around the TPC row. - /// Outside of this area the interpolation returns the boundary values. - GPUd() void setInterpolationSafetyMargin(float val) { fInterpolationSafetyMargin = val; } - /// Gives const pointer to a spline GPUd() const SplineType& getSpline(int32_t sector, int32_t row) const; @@ -226,10 +216,10 @@ class TPCFastSpaceChargeCorrection : public FlatObject GPUd() SplineType& getSpline(int32_t sector, int32_t row); /// Gives pointer to spline data - GPUd() float* getSplineData(int32_t sector, int32_t row, int32_t iSpline = 0); + GPUd() float* getCorrectionData(int32_t sector, int32_t row, int32_t iSpline = 0); /// Gives pointer to spline data - GPUd() const float* getSplineData(int32_t sector, int32_t row, int32_t iSpline = 0) const; + GPUd() const float* getCorrectionData(int32_t sector, int32_t row, int32_t iSpline = 0) const; /// Gives const pointer to a spline for the inverse X correction GPUd() const SplineTypeInvX& getSplineInvX(int32_t sector, int32_t row) const; @@ -238,10 +228,10 @@ class TPCFastSpaceChargeCorrection : public FlatObject GPUd() SplineTypeInvX& getSplineInvX(int32_t sector, int32_t row); /// Gives pointer to spline data for the inverse X correction - GPUd() float* getSplineDataInvX(int32_t sector, int32_t row); + GPUd() float* getCorrectionDataInvX(int32_t sector, int32_t row); /// Gives pointer to spline data for the inverse X correction - GPUd() const float* getSplineDataInvX(int32_t sector, int32_t row) const; + GPUd() const float* getCorrectionDataInvX(int32_t sector, int32_t row) const; /// Gives const pointer to a spline for the inverse YZ correction GPUd() const SplineTypeInvYZ& getSplineInvYZ(int32_t sector, int32_t row) const; @@ -250,10 +240,10 @@ class TPCFastSpaceChargeCorrection : public FlatObject GPUd() SplineTypeInvYZ& getSplineInvYZ(int32_t sector, int32_t row); /// Gives pointer to spline data for the inverse YZ correction - GPUd() float* getSplineDataInvYZ(int32_t sector, int32_t row); + GPUd() float* getCorrectionDataInvYZ(int32_t sector, int32_t row); /// Gives pointer to spline data for the inverse YZ correction - GPUd() const float* getSplineDataInvYZ(int32_t sector, int32_t row) const; + GPUd() const float* getCorrectionDataInvYZ(int32_t sector, int32_t row) const; /// _______________ The main method: cluster correction _______________________ /// @@ -297,24 +287,6 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// Gives the time stamp of the current calibaration parameters int64_t getTimeStamp() const { return mTimeStamp; } - /// Gives the interpolation safety marging around the TPC row. - GPUd() float getInterpolationSafetyMargin() const { return fInterpolationSafetyMargin; } - - /// Gives TPC row info - GPUd() const RowInfo& getRowInfo(int32_t row) const { return mRowInfos[row]; } - - /// Gives TPC sector info - GPUd() const SectorInfo& getSectorInfo(int32_t sector) const - { - return mSectorInfo[sector]; - } - - /// Gives TPC sector info - GPUd() SectorInfo& getSectorInfo(int32_t sector) - { - return mSectorInfo[sector]; - } - /// Gives TPC sector & row info GPUd() const SectorRowInfo& getSectorRowInfo(int32_t sector, int32_t row) const { @@ -351,30 +323,24 @@ class TPCFastSpaceChargeCorrection : public FlatObject int32_t mNumberOfScenarios; ///< Number of approximation spline scenarios - SectorInfo mSectorInfo[TPCFastTransformGeo::getNumberOfSectors()]; ///< SectorInfo array - SplineType* mScenarioPtr; //! (transient!!) pointer to spline scenarios /// _______________ Calibration data _______________________________________________ int64_t mTimeStamp; ///< time stamp of the current calibration - char* mSplineData[3]; //! (transient!!) pointer to the spline data in the flat buffer + char* mCorrectionData[3]; //! (transient!!) pointer to the spline data in the flat buffer - size_t mDataSizeBytes[3]; ///< size of the data for one sector in the flat buffer - - float fInterpolationSafetyMargin{0.1f}; // 10% area around the TPC row. Outside of this area the interpolation returns the boundary values. + size_t mCorrectionDataSize[3]; ///< size of the data per transformation (direct, inverseX, inverse YZ) in the flat buffer /// Class version. It is used to read older versions from disc. /// The default version 3 is the one before this field was introduced. /// The actual version must be set in startConstruction(). int32_t mClassVersion{3}; - RowInfo mRowInfos[TPCFastTransformGeo::getMaxNumberOfRows()]; ///< RowInfo array - SectorRowInfo mSectorRowInfos[TPCFastTransformGeo::getNumberOfSectors() * TPCFastTransformGeo::getMaxNumberOfRows()]; ///< SectorRowInfo array - ClassDefNV(TPCFastSpaceChargeCorrection, 5); + ClassDefNV(TPCFastSpaceChargeCorrection, 4); }; /// ==================================================== @@ -393,16 +359,16 @@ GPUdi() TPCFastSpaceChargeCorrection::SplineType& TPCFastSpaceChargeCorrection:: return mScenarioPtr[getSectorRowInfo(sector, row).splineScenarioID]; } -GPUdi() float* TPCFastSpaceChargeCorrection::getSplineData(int32_t sector, int32_t row, int32_t iSpline) +GPUdi() float* TPCFastSpaceChargeCorrection::getCorrectionData(int32_t sector, int32_t row, int32_t iSpline) { /// Gives pointer to spline data - return reinterpret_cast(mSplineData[iSpline] + getSectorRowInfo(sector, row).dataOffsetBytes[iSpline]); + return reinterpret_cast(mCorrectionData[iSpline] + getSectorRowInfo(sector, row).dataOffsetBytes[iSpline]); } -GPUdi() const float* TPCFastSpaceChargeCorrection::getSplineData(int32_t sector, int32_t row, int32_t iSpline) const +GPUdi() const float* TPCFastSpaceChargeCorrection::getCorrectionData(int32_t sector, int32_t row, int32_t iSpline) const { /// Gives pointer to spline data - return reinterpret_cast(mSplineData[iSpline] + getSectorRowInfo(sector, row).dataOffsetBytes[iSpline]); + return reinterpret_cast(mCorrectionData[iSpline] + getSectorRowInfo(sector, row).dataOffsetBytes[iSpline]); } GPUdi() TPCFastSpaceChargeCorrection::SplineTypeInvX& TPCFastSpaceChargeCorrection::getSplineInvX(int32_t sector, int32_t row) @@ -417,16 +383,16 @@ GPUdi() const TPCFastSpaceChargeCorrection::SplineTypeInvX& TPCFastSpaceChargeCo return reinterpret_cast(getSpline(sector, row)); } -GPUdi() float* TPCFastSpaceChargeCorrection::getSplineDataInvX(int32_t sector, int32_t row) +GPUdi() float* TPCFastSpaceChargeCorrection::getCorrectionDataInvX(int32_t sector, int32_t row) { /// Gives pointer to spline data for the inverse X correction - return getSplineData(sector, row, 1); + return getCorrectionData(sector, row, 1); } -GPUdi() const float* TPCFastSpaceChargeCorrection::getSplineDataInvX(int32_t sector, int32_t row) const +GPUdi() const float* TPCFastSpaceChargeCorrection::getCorrectionDataInvX(int32_t sector, int32_t row) const { /// Gives pointer to spline data for the inverse X correction - return getSplineData(sector, row, 1); + return getCorrectionData(sector, row, 1); } GPUdi() TPCFastSpaceChargeCorrection::SplineTypeInvYZ& TPCFastSpaceChargeCorrection::getSplineInvYZ(int32_t sector, int32_t row) @@ -441,16 +407,16 @@ GPUdi() const TPCFastSpaceChargeCorrection::SplineTypeInvYZ& TPCFastSpaceChargeC return reinterpret_cast(getSpline(sector, row)); } -GPUdi() float* TPCFastSpaceChargeCorrection::getSplineDataInvYZ(int32_t sector, int32_t row) +GPUdi() float* TPCFastSpaceChargeCorrection::getCorrectionDataInvYZ(int32_t sector, int32_t row) { /// Gives pointer to spline data for the inverse YZ correction - return getSplineData(sector, row, 2); + return getCorrectionData(sector, row, 2); } -GPUdi() const float* TPCFastSpaceChargeCorrection::getSplineDataInvYZ(int32_t sector, int32_t row) const +GPUdi() const float* TPCFastSpaceChargeCorrection::getCorrectionDataInvYZ(int32_t sector, int32_t row) const { /// Gives pointer to spline data for the inverse YZ correction - return getSplineData(sector, row, 2); + return getCorrectionData(sector, row, 2); } GPUdi() std::array TPCFastSpaceChargeCorrection::convLocalToGrid(int32_t sector, int32_t row, float y, float z) const @@ -518,7 +484,7 @@ GPUdi() std::array TPCFastSpaceChargeCorrection::getCorrectionLocal(in { const auto& info = getSectorRowInfo(sector, row); const SplineType& spline = getSpline(sector, row); - const float* splineData = getSplineData(sector, row); + const float* splineData = getCorrectionData(sector, row); auto val = convLocalToGrid(sector, row, y, z); @@ -536,7 +502,7 @@ GPUdi() float TPCFastSpaceChargeCorrection::getCorrectionXatRealYZ(int32_t secto const auto& info = getSectorRowInfo(sector, row); auto val = convRealLocalToGrid(sector, row, realY, realZ); float dx = 0; - getSplineInvX(sector, row).interpolateAtU(getSplineDataInvX(sector, row), val[0], val[1], &dx); + getSplineInvX(sector, row).interpolateAtU(getCorrectionDataInvX(sector, row), val[0], val[1], &dx); dx = val[2] * GPUCommonMath::Clamp(dx, info.minCorr[0], info.maxCorr[0]); return dx; } @@ -546,7 +512,7 @@ GPUdi() std::array TPCFastSpaceChargeCorrection::getCorrectionYZatReal auto val = convRealLocalToGrid(sector, row, realY, realZ); const auto& info = getSectorRowInfo(sector, row); float dyz[2]; - getSplineInvYZ(sector, row).interpolateAtU(getSplineDataInvYZ(sector, row), val[0], val[1], dyz); + getSplineInvYZ(sector, row).interpolateAtU(getCorrectionDataInvYZ(sector, row), val[0], val[1], dyz); dyz[0] = val[2] * GPUCommonMath::Clamp(dyz[0], info.minCorr[1], info.maxCorr[1]); dyz[1] = val[2] * GPUCommonMath::Clamp(dyz[1], info.minCorr[2], info.maxCorr[2]); return {dyz[0], dyz[1]}; diff --git a/GPU/TPCFastTransformation/TPCFastTransform.h b/GPU/TPCFastTransformation/TPCFastTransform.h index 60f5952e6a1fc..64fdba9d94bd3 100644 --- a/GPU/TPCFastTransformation/TPCFastTransform.h +++ b/GPU/TPCFastTransformation/TPCFastTransform.h @@ -337,7 +337,7 @@ class TPCFastTransform : public FlatObject GPUd() void TransformLocal(int32_t sector, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const; - ClassDefNV(TPCFastTransform, 4); + ClassDefNV(TPCFastTransform, 5); }; // ======================================================================= diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx b/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx index 5b2dcc8da82d5..2fe773a76e4d3 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx @@ -28,12 +28,10 @@ using namespace o2::gpu; TPCFastTransformGeo::TPCFastTransformGeo() { // Default Constructor: creates an empty uninitialized object - double dAlpha = 2. * M_PI / (NumberOfSectorsA); for (int32_t i = 0; i < NumberOfSectors; i++) { - SectorInfo& s = mSectorInfos[i]; - double alpha = dAlpha * (i + 0.5); - s.sinAlpha = sin(alpha); - s.cosAlpha = cos(alpha); + double angle = (i + 0.5) * 2. * M_PI / NumberOfSectorsA; + mSectorInfos[i].sinAlpha = sin(angle); + mSectorInfos[i].cosAlpha = cos(angle); } mSectorInfos[NumberOfSectors] = SectorInfo{}; diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.h b/GPU/TPCFastTransformation/TPCFastTransformGeo.h index fc28bbef33602..09793b6677d83 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.h +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.h @@ -19,6 +19,8 @@ #include "GPUCommonDef.h" #include "GPUCommonArray.h" +#include "GPUCommonMath.h" + #ifndef GPUCA_GPUCODE_DEVICE #include #include "GPUCommonRtypes.h" @@ -34,6 +36,7 @@ namespace gpu /// class TPCFastTransformGeo { + public: /// The struct contains necessary info for TPC sector struct SectorInfo { @@ -61,7 +64,7 @@ class TPCFastTransformGeo /// get width in Y GPUd() float getYwidth() const { return -2.f * yMin; } - ClassDefNV(RowInfo, 1); + ClassDefNV(RowInfo, 2); }; /// _____________ Constructors / destructors __________________________ @@ -187,6 +190,11 @@ class TPCFastTransformGeo SectorInfo mSectorInfos[NumberOfSectors + 1]; ///< array of sector information [fixed size] RowInfo mRowInfos[MaxNumberOfRows + 1]; ///< array of row information [fixed size] + public: + struct SliceInfo { // legacy, needed only for schema evolution + ClassDefNV(SliceInfo, 2); + }; + ClassDefNV(TPCFastTransformGeo, 3); }; diff --git a/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h b/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h index 916695a3be1c7..284d5f229d5e0 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h +++ b/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h @@ -58,19 +58,36 @@ #pragma link C++ class o2::gpu::SemiregularSpline2D3D + ; #pragma link C++ class o2::gpu::IrregularSpline2D3DCalibrator + ; -#pragma link C++ class o2::gpu::TPCFastTransformGeo + ; +#pragma link C++ class o2::gpu::TPCFastTransformGeo::SliceInfo + ; #pragma link C++ class o2::gpu::TPCFastTransformGeo::SectorInfo + ; + +#pragma link C++ class o2::gpu::TPCFastTransformGeo + ; +#pragma read \ + sourceClass = "o2::gpu::TPCFastTransformGeo" targetClass = "o2::gpu::TPCFastTransformGeo" source = "float mTPCzLengthA; float mTPCzLengthC; float mTPCalignmentZ; float mScaleVtoSVsideA; float mScaleVtoSVsideC; float mScaleSVtoVsideA; float mScaleSVtoVsideC;" version = "[-1]" target = "mTPCzLength" code = "{ mTPCzLength = onfile.mTPCzLengthA; }"; + +#pragma read \ + sourceClass = "o2::gpu::TPCFastTransformGeo" targetClass = "o2::gpu::TPCFastTransformGeo" source = "o2::gpu::TPCFastTransformGeo::SliceInfo mSliceInfos[37]" version = "[1-]" target = "" code = "{}"; + #pragma link C++ class o2::gpu::TPCFastTransformGeo::RowInfo + ; +#pragma read \ + sourceClass = "o2::gpu::TPCFastTransformGeo::RowInfo" targetClass = "o2::gpu::TPCFastTransformGeo::RowInfo" source = "float u0; float scaleUtoSU; float scaleSUtoU" version = "[-2]" target = "yMin" code = "{ yMin = onfile.u0; }" #pragma link C++ class o2::gpu::TPCFastTransform + ; #pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrectionMap + ; -#pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::RowInfo + ; #pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection + ; -#pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::SectorInfo + ; +#pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::SliceInfo + ; #pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::SectorRowInfo + ; #pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::GridInfo + ; +#pragma read \ + sourceClass = "o2::gpu::TPCFastSpaceChargeCorrection" targetClass = "o2::gpu::TPCFastSpaceChargeCorrection" source = "o2::gpu::TPCFastSpaceChargeCorrection::SliceInfo mSliceInfo[36]" version = "[-3]" target = "" code = "{}"; + +#pragma read \ + sourceClass = "o2::gpu::TPCFastSpaceChargeCorrection" targetClass = "o2::gpu::TPCFastSpaceChargeCorrection" source = "size_t mSliceDataSizeBytes[3]" version = "[-3]" target = "mCorrectionDataSize" code = "{ for (int i=0; i<3; i++) mCorrectionDataSize[i] = onfile.mSliceDataSizeBytes[i] * 36; }"; + +#pragma read \ + sourceClass = "o2::gpu::TPCFastSpaceChargeCorrection" targetClass = "o2::gpu::TPCFastSpaceChargeCorrection" source = "float fInterpolationSafetyMargin" version = "[-3]" target = "" code = "{}"; #pragma link C++ class o2::gpu::CorrectionMapsHelper + ; #pragma link C++ struct o2::gpu::MultivariatePolynomialContainer + ; diff --git a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C index 3cb4812abafc1..baaeca90202d5 100644 --- a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C +++ b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C @@ -18,7 +18,7 @@ /// how to run the macro: /// -/// root -l TPCFastTransformInit.C'("debugVoxRes.root")' +/// root -l TPCFastTransformInit.C'("VoxRes.root", "VoxResInv.root")' /// #if !defined(__CLING__) || defined(__ROOTCLING__) @@ -45,7 +45,7 @@ using namespace o2::tpc; using namespace o2::gpu; void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* fileNameInv = "debugVoxResInv.root", - const char* outFileName = "TPCFastTransform_VoxRes.root", bool useSmoothed = false, bool invertSigns = false) + const char* outFileName = "TPCFastTransform_VoxRes.root", bool useSmoothed = false, bool invertSigns = false, bool doDebug = true) { // Initialise TPCFastTransform object from "voxRes" tree of @@ -56,9 +56,9 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* To visiualise the results: root -l transformDebug.root - all->Draw("cx:y:z","sec==0&&iRow==10","") - grid->Draw("cx:y:z","sec==0&&iRow==10","same") - vox->Draw("vx:y:z","sec==0&&iRow==10","same") + all->Draw("cx:y:z","sec==0&&row==10","") + grid->Draw("cx:y:z","sec==0&&row==10","same") + vox->Draw("vx:y:z","sec==0&&row==10","same") points->Draw("px:y:z","sec==0&&row==10","same") */ @@ -112,6 +112,38 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* trackResiduals.setZ2XBinning(z2xBins); trackResiduals.init(); + std::cout << "create fast transformation ... " << std::endl; + + auto* helper = o2::tpc::TPCFastTransformHelperO2::instance(); + + o2::tpc::TPCFastSpaceChargeCorrectionHelper* corrHelper = o2::tpc::TPCFastSpaceChargeCorrectionHelper::instance(); + + corrHelper->setNthreadsToMaximum(); + // corrHelper->setNthreads(1); + + if (debugMirrorAdata2C) { + corrHelper->setDebugMirrorAdata2C(); + } + // corrHelper->setDebugUseVoxelCenters(); + + o2::gpu::TPCFastSpaceChargeCorrectionMap mapDirect(0, 0), mapInverse(0, 0); + + auto corrPtr = corrHelper->createFromTrackResiduals(trackResiduals, voxResTree, voxResTreeInverse, useSmoothed, invertSigns, + &mapDirect, &mapInverse); + + std::unique_ptr fastTransform( + helper->create(0, *corrPtr)); + + std::cout << "... create fast transformation completed " << std::endl; + + if (*outFileName) { + fastTransform->writeToFile(outFileName, "ccdb_object"); + } + + if (!doDebug) { + return; + } + { // debug output std::cout << " ===== input track residuals ==== " << std::endl; @@ -144,40 +176,12 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* std::cout << " ==================================== " << std::endl; } - std::cout << "create fast transformation ... " << std::endl; - - auto* helper = o2::tpc::TPCFastTransformHelperO2::instance(); - - o2::tpc::TPCFastSpaceChargeCorrectionHelper* corrHelper = o2::tpc::TPCFastSpaceChargeCorrectionHelper::instance(); - - corrHelper->setNthreadsToMaximum(); - corrHelper->setNthreads(1); - - if (debugMirrorAdata2C) { - corrHelper->setDebugMirrorAdata2C(); - } - // corrHelper->setDebugUseVoxelCenters(); - - o2::gpu::TPCFastSpaceChargeCorrectionMap mapDirect(0, 0), mapInverse(0, 0); - - auto corrPtr = corrHelper->createFromTrackResiduals(trackResiduals, voxResTree, voxResTreeInverse, useSmoothed, invertSigns, - &mapDirect, &mapInverse); - - std::unique_ptr fastTransform( - helper->create(0, *corrPtr)); - - std::cout << "... create fast transformation completed " << std::endl; - - if (*outFileName) { - fastTransform->writeToFile(outFileName, "ccdb_object"); - } - - if (1) { // read transformation from the file - - // const char* fileName = "master/out.root"; + if (1) { // read transformation from the output file to verify the io const char* fileName = outFileName; + // fileName = "~/test/master/TPCFastTransform_VoxRes.root"; + std::cout << "load corrections from file " << fileName << std::endl; fastTransform->cloneFromObject(*TPCFastTransform::loadFromFile(fileName, "ccdb_object"), nullptr); @@ -488,11 +492,6 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* auto [yMin, yMax] = geo.getRowInfo(iRow).getYrange(); auto [zMin, zMax] = geo.getZrange(iSector); - points[0].push_back(yMin); - points[0].push_back(yMax); - points[1].push_back(zMin); - points[1].push_back(zMax); - for (int32_t iu = 0; iu < gridY.getNumberOfKnots(); iu++) { auto [y, z] = corr.convGridToLocal(iSector, iRow, gridY.getKnot(iu).getU(), 0.); knots[0].push_back(y); @@ -508,12 +507,19 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* std::sort(knots[iyz].begin(), knots[iyz].end()); std::sort(points[iyz].begin(), points[iyz].end()); int32_t n = points[iyz].size(); + int nsteps = (iyz == 0) ? 10 : 5; for (int32_t i = 0; i < n - 1; i++) { - double d = (points[iyz][i + 1] - points[iyz][i]) / 10.; - for (int32_t ii = 1; ii < 10; ii++) { + double d = (points[iyz][i + 1] - points[iyz][i]) / nsteps; + for (int32_t ii = 1; ii < nsteps; ii++) { points[iyz].push_back(points[iyz][i] + d * ii); } } + } + points[0].push_back(yMin); + points[0].push_back(yMax); + points[1].push_back(zMin); + points[1].push_back(zMax); + for (int32_t iyz = 0; iyz <= 1; iyz++) { std::sort(points[iyz].begin(), points[iyz].end()); } From ec3f6ac5a5c209d0167dd68651e57dc6d07f5f03 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Wed, 15 Oct 2025 16:36:51 +0200 Subject: [PATCH 101/285] Fix compiler-warnings, codechecker violations and compilation Fix compiler warnings on MacOS Fix compiler warning, memmove must only operate on trivial types Fix coding rule violations TPC Splines: compilation fix --- .../TPCFastSpaceChargeCorrectionHelper.cxx | 15 ++++---- .../macro/createTPCSpaceChargeCorrection.C | 34 +++++++++---------- .../TPCFastSpaceChargeCorrection.cxx | 2 +- .../TPCFastTransformationLinkDef_O2.h | 4 +-- .../macro/generateTPCCorrectionNTuple.C | 21 +++++------- 5 files changed, 36 insertions(+), 40 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index faba4f2ce065e..7622c40001e1d 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -690,9 +690,9 @@ std::unique_ptr TPCFastSpaceChargeCorrect LOG(warning) << directionName << " correction: error N " << nErrors << "fitted voxel position is outside the voxel: " << " sector " << iSector << " row " << iRow << " bin y " << iy << " bin z " << iz << msg.str(); - maxError[0] = GPUCommonMath::Max(maxError[0], fabs(data.mX - x) / dx); - maxError[1] = GPUCommonMath::Max(maxError[1], fabs(data.mY - vox.mY) / vox.mDy); - maxError[2] = GPUCommonMath::Max(maxError[2], fabs(data.mZ - vox.mZ) / vox.mDz); + maxError[0] = GPUCommonMath::Max(maxError[0], fabs(data.mX - x) / dx); + maxError[1] = GPUCommonMath::Max(maxError[1], fabs(data.mY - vox.mY) / vox.mDy); + maxError[2] = GPUCommonMath::Max(maxError[2], fabs(data.mZ - vox.mZ) / vox.mDz); } mutex.unlock(); } @@ -794,12 +794,15 @@ std::unique_ptr TPCFastSpaceChargeCorrect auto addEdge = [&](int iy1, int iz1, int iy2, int iz2, int nPoints) { // add n points on the edge between two voxels excluding the voxel points - if (nPoints < 1) + if (nPoints < 1) { return; - if (iy1 < 0 || iy1 >= nY2Xbins || iz1 < 0 || iz1 >= nZ2Xbins) + } + if (iy1 < 0 || iy1 >= nY2Xbins || iz1 < 0 || iz1 >= nZ2Xbins) { return; - if (iy2 < 0 || iy2 >= nY2Xbins || iz2 < 0 || iz2 >= nZ2Xbins) + } + if (iy2 < 0 || iy2 >= nY2Xbins || iz2 < 0 || iz2 >= nZ2Xbins) { return; + } auto& data1 = vSectorData[iSector * nRows + iRow][iy1 * nZ2Xbins + iz1]; auto& vox1 = vRowVoxels[iy1 * nZ2Xbins + iz1]; auto& data2 = vSectorData[iSector * nRows + iRow][iy2 * nZ2Xbins + iz2]; diff --git a/Detectors/TPC/reconstruction/macro/createTPCSpaceChargeCorrection.C b/Detectors/TPC/reconstruction/macro/createTPCSpaceChargeCorrection.C index 723cf2ee30491..af066598d1317 100644 --- a/Detectors/TPC/reconstruction/macro/createTPCSpaceChargeCorrection.C +++ b/Detectors/TPC/reconstruction/macro/createTPCSpaceChargeCorrection.C @@ -397,10 +397,9 @@ void debugInterpolation(utils::TreeStreamRedirector& pcstream, const o2::gpu::TPCFastTransformGeo& geo, TPCFastTransform* fastTransform) { - for (int slice = 0; slice < geo.getNumberOfSlices(); slice += 1) { - // for (int slice = 21; slice < 22; slice += 1) { - std::cout << "debug slice " << slice << " ... " << std::endl; - const o2::gpu::TPCFastTransformGeo::SliceInfo& sliceInfo = geo.getSliceInfo(slice); + for (int sector = 0; sector < geo.getNumberOfSectors(); sector += 1) { + // for (int sector = 21; sector < 22; sector += 1) { + std::cout << "debug sector " << sector << " ... " << std::endl; for (int row = 0; row < geo.getNumberOfRows(); row++) { int nPads = geo.getRowInfo(row).maxPad + 1; @@ -411,28 +410,28 @@ void debugInterpolation(utils::TreeStreamRedirector& pcstream, // non-corrected point fastTransform->setApplyCorrectionOff(); float lx, ly, lz; - fastTransform->Transform(slice, row, pad, time, lx, ly, lz); + fastTransform->Transform(sector, row, pad, time, lx, ly, lz); float gx, gy, gz, r, phi; - geo.convLocalToGlobal(slice, lx, ly, lz, gx, gy, gz); + geo.convLocalToGlobal(sector, lx, ly, lz, gx, gy, gz); r = std::sqrt(lx * lx + ly * ly); phi = std::atan2(gy, gx); fastTransform->setApplyCorrectionOn(); // fast transformation float lxT, lyT, lzT; - fastTransform->Transform(slice, row, pad, time, lxT, lyT, lzT); + fastTransform->Transform(sector, row, pad, time, lxT, lyT, lzT); float gxT, gyT, gzT, rT; - geo.convLocalToGlobal(slice, lxT, lyT, lzT, gxT, gyT, gzT); + geo.convLocalToGlobal(sector, lxT, lyT, lzT, gxT, gyT, gzT); rT = std::sqrt(lxT * lxT + lyT * lyT); // the original correction double gdC[3] = {0, 0, 0}; - Side side = slice < geo.getNumberOfSlicesA() ? Side::A : Side::C; + Side side = sector < geo.getNumberOfSectorsA() ? Side::A : Side::C; if (spaceCharge) { spaceCharge->getCorrections(gx, gy, gz, side, gdC[0], gdC[1], gdC[2]); } float ldxC, ldyC, ldzC; - geo.convGlobalToLocal(slice, gdC[0], gdC[1], gdC[2], ldxC, ldyC, ldzC); + geo.convGlobalToLocal(sector, gdC[0], gdC[1], gdC[2], ldxC, ldyC, ldzC); double rC = std::sqrt((gx + gdC[0]) * (gx + gdC[0]) + (gy + gdC[1]) * (gy + gdC[1])); @@ -466,7 +465,7 @@ void debugInterpolation(utils::TreeStreamRedirector& pcstream, if (spaceChargeExB) { double gdC_ExB[3] = {0, 0, 0}; spaceChargeExB->getCorrections(gx, gy, gz, side, gdC_ExB[0], gdC_ExB[1], gdC_ExB[2]); - geo.convGlobalToLocal(slice, gdC_ExB[0], gdC_ExB[1], gdC_ExB[2], ldxC_ExB, ldyC_ExB, ldzC_ExB); + geo.convGlobalToLocal(sector, gdC_ExB[0], gdC_ExB[1], gdC_ExB[2], ldxC_ExB, ldyC_ExB, ldzC_ExB); } // static distortions @@ -474,18 +473,18 @@ void debugInterpolation(utils::TreeStreamRedirector& pcstream, if (spaceChargeStack) { double gdC_static[3] = {0, 0, 0}; spaceChargeStack->getCorrections(gx, gy, gz, side, gdC_static[0], gdC_static[1], gdC_static[2]); - geo.convGlobalToLocal(slice, gdC_static[0], gdC_static[1], gdC_static[2], ldxC_static, ldyC_static, ldzC_static); + geo.convGlobalToLocal(sector, gdC_static[0], gdC_static[1], gdC_static[2], ldxC_static, ldyC_static, ldzC_static); } // get combined corrections double dx_comb = 0, dy_comb = 0, dz_comb = 0; - getGlobalSpaceChargeCorrectionLinearCombination(slice, gx, gy, gz, dx_comb, dy_comb, dz_comb); + getGlobalSpaceChargeCorrectionLinearCombination(sector, gx, gy, gz, dx_comb, dy_comb, dz_comb); float ldxC_comb, ldyC_comb, ldzC_comb; - geo.convGlobalToLocal(slice, dx_comb, dy_comb, dz_comb, ldxC_comb, ldyC_comb, ldzC_comb); + geo.convGlobalToLocal(sector, dx_comb, dy_comb, dz_comb, ldxC_comb, ldyC_comb, ldzC_comb); pcstream << "fastTransform" // internal coordinates - << "slice=" << slice + << "sector=" << sector << "row=" << row << "pad=" << pad << "time=" << time @@ -613,10 +612,9 @@ void debugGridpoints(utils::TreeStreamRedirector& pcstream, const o2::gpu::TPCFa break; } } - float u = 0.f, v = 0.f; - geo.convLocalToUV(sector, y0, z0, u, v); + float pad = 0.f, time = 0.f; - fastTransform->convUVtoPadTime(sector, row, u, v, pad, time, 0.f); + fastTransform->convLocalToPadTime(sector, row, y0, z0, pad, time, 0.f); if (pad < 0) { continue; } diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx index 5a2cd21deeb2b..5f39e749f73d9 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx @@ -294,7 +294,7 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer // move spline scenarios to the new place in the buffer mScenarioPtr = reinterpret_cast(mFlatBufferPtr); - memmove(mScenarioPtr, oldScenarioPtr, scenariosSize); + memmove((void*)mScenarioPtr, (const void*)oldScenarioPtr, scenariosSize); size_t oldScenariosBufferOffset = alignSize(oldScenariosOffset + scenariosSize, SplineType::getBufferAlignmentBytes()); size_t scenariosBufferOffset = alignSize(scenariosSize, SplineType::getBufferAlignmentBytes()); diff --git a/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h b/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h index 284d5f229d5e0..f1872549a46aa 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h +++ b/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h @@ -66,11 +66,11 @@ sourceClass = "o2::gpu::TPCFastTransformGeo" targetClass = "o2::gpu::TPCFastTransformGeo" source = "float mTPCzLengthA; float mTPCzLengthC; float mTPCalignmentZ; float mScaleVtoSVsideA; float mScaleVtoSVsideC; float mScaleSVtoVsideA; float mScaleSVtoVsideC;" version = "[-1]" target = "mTPCzLength" code = "{ mTPCzLength = onfile.mTPCzLengthA; }"; #pragma read \ - sourceClass = "o2::gpu::TPCFastTransformGeo" targetClass = "o2::gpu::TPCFastTransformGeo" source = "o2::gpu::TPCFastTransformGeo::SliceInfo mSliceInfos[37]" version = "[1-]" target = "" code = "{}"; + sourceClass = "o2::gpu::TPCFastTransformGeo" targetClass = "o2::gpu::TPCFastTransformGeo" source = "o2::gpu::TPCFastTransformGeo::SliceInfo mSliceInfos[37]" version = "[-2]" target = "" code = "{}"; #pragma link C++ class o2::gpu::TPCFastTransformGeo::RowInfo + ; #pragma read \ - sourceClass = "o2::gpu::TPCFastTransformGeo::RowInfo" targetClass = "o2::gpu::TPCFastTransformGeo::RowInfo" source = "float u0; float scaleUtoSU; float scaleSUtoU" version = "[-2]" target = "yMin" code = "{ yMin = onfile.u0; }" + sourceClass = "o2::gpu::TPCFastTransformGeo::RowInfo" targetClass = "o2::gpu::TPCFastTransformGeo::RowInfo" source = "float u0; float scaleUtoSU; float scaleSUtoU" version = "[-1]" target = "yMin" code = "{ yMin = onfile.u0; }" #pragma link C++ class o2::gpu::TPCFastTransform + ; diff --git a/GPU/TPCFastTransformation/macro/generateTPCCorrectionNTuple.C b/GPU/TPCFastTransformation/macro/generateTPCCorrectionNTuple.C index 69b7909cda683..2c8f22e4a3f3b 100644 --- a/GPU/TPCFastTransformation/macro/generateTPCCorrectionNTuple.C +++ b/GPU/TPCFastTransformation/macro/generateTPCCorrectionNTuple.C @@ -82,7 +82,7 @@ void generateTPCCorrectionNTuple(const char* path = "InputSCDensityHistograms.ro const o2::gpu::TPCFastTransformGeo& geo = fastTransform->getGeometry(); TFile* f = new TFile("tpcCorrection.root", "RECREATE"); - TNtuple* nt = new TNtuple("dist", "dist", "sector:row:su:sv:dx:du:dv"); + TNtuple* nt = new TNtuple("dist", "dist", "sector:row:x:y:z:dx:dy:dz"); int32_t nSectors = 1; // fastTransform->getNumberOfSectors(); // for( int32_t sector=0; sectorFill(sector, row, su, sv, dx, du, dv); + float dy = y1 - y; + float dz = z1 - z; + nt->Fill(sector, row, x, y, z, dx, dy, dz); } } } From 1ee89e2ef471b8fa18177ae0873e2185bcff633d Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Thu, 5 Feb 2026 09:38:08 +0000 Subject: [PATCH 102/285] TPC Splines: keep old cropping scheme --- .../TPCFastSpaceChargeCorrection.cxx | 4 ++-- .../TPCFastSpaceChargeCorrection.h | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx index 5f39e749f73d9..241a1fcfc795b 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx @@ -285,8 +285,8 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer } newSectorRow.resetMaxValues(); - newSectorRow.updateMaxValues(-50.f, -50.f, -50.f); - newSectorRow.updateMaxValues(50.f, 50.f, 50.f); + newSectorRow.updateMaxValues(-100.f, -100.f, -100.f); + newSectorRow.updateMaxValues(100.f, 100.f, 100.f); } } } diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index b4fab68b91542..b1a3d0c35da7c 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -491,9 +491,14 @@ GPUdi() std::array TPCFastSpaceChargeCorrection::getCorrectionLocal(in float dxyz[3]; spline.interpolateAtU(splineData, val[0], val[1], dxyz); + if (CAMath::Abs(dxyz[0]) > 100.f || CAMath::Abs(dxyz[1]) > 100.f || CAMath::Abs(dxyz[2]) > 100.f) { + val[2] = 0.f; // TODO: DR: Protect from FPEs, fix upstream and remove once guaranteed that it is fixed + } + float dx = val[2] * GPUCommonMath::Clamp(dxyz[0], info.minCorr[0], info.maxCorr[0]); float dy = val[2] * GPUCommonMath::Clamp(dxyz[1], info.minCorr[1], info.maxCorr[1]); float dz = val[2] * GPUCommonMath::Clamp(dxyz[2], info.minCorr[2], info.maxCorr[2]); + return {dx, dy, dz}; } @@ -503,6 +508,9 @@ GPUdi() float TPCFastSpaceChargeCorrection::getCorrectionXatRealYZ(int32_t secto auto val = convRealLocalToGrid(sector, row, realY, realZ); float dx = 0; getSplineInvX(sector, row).interpolateAtU(getCorrectionDataInvX(sector, row), val[0], val[1], &dx); + if (CAMath::Abs(dx) > 100.f) { + val[2] = 0.f; // TODO: DR: Protect from FPEs, fix upstream and remove once guaranteed that it is fixed + } dx = val[2] * GPUCommonMath::Clamp(dx, info.minCorr[0], info.maxCorr[0]); return dx; } @@ -513,6 +521,9 @@ GPUdi() std::array TPCFastSpaceChargeCorrection::getCorrectionYZatReal const auto& info = getSectorRowInfo(sector, row); float dyz[2]; getSplineInvYZ(sector, row).interpolateAtU(getCorrectionDataInvYZ(sector, row), val[0], val[1], dyz); + if (CAMath::Abs(dyz[0]) > 100.f || CAMath::Abs(dyz[1]) > 100.f) { + val[2] = 0.f; // TODO: DR: Protect from FPEs, fix upstream and remove once guaranteed that it is fixed + } dyz[0] = val[2] * GPUCommonMath::Clamp(dyz[0], info.minCorr[1], info.maxCorr[1]); dyz[1] = val[2] * GPUCommonMath::Clamp(dyz[1], info.minCorr[2], info.maxCorr[2]); return {dyz[0], dyz[1]}; From 5627eef4497518181f5bb66188602fd717a06740 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sun, 29 Mar 2026 19:10:08 +0200 Subject: [PATCH 103/285] GPU TPC FastTransformation: Do not use std::array --- .../TPCFastSpaceChargeCorrectionHelper.cxx | 31 +++-- .../src/TPCFastTransformHelperO2.cxx | 3 +- GPU/TPCFastTransformation/Spline1DSpec.h | 38 +++--- GPU/TPCFastTransformation/Spline2DSpec.h | 19 ++- .../TPCFastSpaceChargeCorrection.cxx | 6 +- .../TPCFastSpaceChargeCorrection.h | 108 ++++++++++-------- GPU/TPCFastTransformation/TPCFastTransform.h | 94 +++++++-------- .../TPCFastTransformGeo.cxx | 6 +- .../TPCFastTransformGeo.h | 41 ++++--- .../macro/TPCFastTransformInit.C | 6 +- 10 files changed, 178 insertions(+), 174 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index 7622c40001e1d..783c1837590b9 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -159,7 +159,8 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas for (int i = 0; i < nDataPoints; ++i) { o2::gpu::TPCFastSpaceChargeCorrectionMap::CorrectionPoint p = data[i]; // not corrected grid coordinates - auto [gu, gv, scale] = correction.convLocalToGrid(sector, row, p.mY, p.mZ); + float gu, gv, scale; + correction.convLocalToGrid(sector, row, p.mY, p.mZ, gu, gv, scale); if (scale - 1.f > 1.e-6) { // point is outside the grid continue; } @@ -300,7 +301,8 @@ std::unique_ptr TPCFastSpaceChargeCorrectionHelper double dpad = info.maxPad / (6. * (nKnotsY - 1)); for (double pad = 0; pad < info.maxPad + .5 * dpad; pad += dpad) { for (double l = 0.; l < mGeo.getTPCzLength() + .5 * dl; l += dl) { - auto [y, z] = mGeo.convPadDriftLengthToLocal(iSector, iRow, pad, l); + float y, z; + mGeo.convPadDriftLengthToLocal(iSector, iRow, pad, l, y, z); double dx, dy, dz; correctionLocal(iSector, iRow, y, z, dx, dy, dz); mCorrectionMap.addCorrectionPoint(iSector, iRow, @@ -360,7 +362,8 @@ void TPCFastSpaceChargeCorrectionHelper::testGeometry(const TPCFastTransformGeo& for (int pad = 0; pad < nPads; pad++) { const GlobalPadNumber p = mapper.globalPadNumber(PadPos(row, pad)); const PadCentre& c = mapper.padCentre(p); - auto [y, z] = geo.convPadDriftLengthToLocal(0, row, pad, 0.); + float y, z; + geo.convPadDriftLengthToLocal(0, row, pad, 0., y, z); const double dx = x - c.X(); const double dy = y - (-c.Y()); // diferent sign convention for Y coordinate in the map @@ -974,18 +977,20 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vectorgetCorrectionLocal(sector, row, y, z); + float dxTmp, dyTmp, dzTmp; + corrections[i]->getCorrectionLocal(sector, row, y, z, dxTmp, dyTmp, dzTmp); dx += dxTmp * scaling[i]; dy += dyTmp * scaling[i]; dz += dzTmp * scaling[i]; } - auto [gridU, gridV, scale] = correction.convRealLocalToGrid(sector, row, y + dy, z + dz); + float gridU, gridV, scale; + correction.convRealLocalToGrid(sector, row, y + dy, z + dz, gridU, gridV, scale); dataPointGridU.push_back(gridU); dataPointGridV.push_back(gridV); dataPointF.push_back(scale * dx); @@ -1111,9 +1116,11 @@ void TPCFastSpaceChargeCorrectionHelper::mergeCorrections( float P[nKnotPar3d]; { // direct correction - auto [y, z] = mainCorrection.convGridToLocal(sector, row, u, v); + float y, z; + mainCorrection.convGridToLocal(sector, row, u, v, y, z); // return values: u, v, scaling factor - auto [lu, lv, ls] = corr.convLocalToGrid(sector, row, y, z); + float lu, lv, ls; + corr.convLocalToGrid(sector, row, y, z, lu, lv, ls); ls *= scale; double parscale[4] = {ls, ls * scaleU, ls * scaleV, ls * ls * scaleU * scaleV}; const auto& spl = corr.getSpline(sector, row); @@ -1125,9 +1132,11 @@ void TPCFastSpaceChargeCorrectionHelper::mergeCorrections( } } - auto [y, z] = mainCorrection.convGridToRealLocal(sector, row, u, v); + float y, z; + mainCorrection.convGridToRealLocal(sector, row, u, v, y, z); // return values: u, v, scaling factor - auto [lu, lv, ls] = corr.convRealLocalToGrid(sector, row, y, z); + float lu, lv, ls; + corr.convRealLocalToGrid(sector, row, y, z, lu, lv, ls); ls *= scale; double parscale[4] = {ls, ls * scaleRealU, ls * scaleRealV, ls * ls * scaleRealU * scaleRealV}; diff --git a/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx b/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx index 687d4ce707f11..419ced9fa978e 100644 --- a/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx +++ b/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx @@ -203,7 +203,8 @@ void TPCFastTransformHelperO2::testGeometry(const TPCFastTransformGeo& geo) cons const GlobalPadNumber p = mapper.globalPadNumber(PadPos(row, pad)); const PadCentre& c = mapper.padCentre(p); - auto [y, z] = geo.convPadDriftLengthToLocal(0, row, pad, 0.); + float y, z; + geo.convPadDriftLengthToLocal(0, row, pad, 0., y, z); const double dx = x - c.X(); const double dy = y - (-c.Y()); // diferent sign convention for Y coordinate in the map diff --git a/GPU/TPCFastTransformation/Spline1DSpec.h b/GPU/TPCFastTransformation/Spline1DSpec.h index d72de5a446718..56349ba6f454a 100644 --- a/GPU/TPCFastTransformation/Spline1DSpec.h +++ b/GPU/TPCFastTransformation/Spline1DSpec.h @@ -314,11 +314,9 @@ class Spline1DSpec : public Spline1DContainer const auto nYdimTmp = SplineUtil::getNdim(inpYdim); const auto nYdim = nYdimTmp.get(); - auto val = getSderivativesOverParsAtU(knotL, u); - const auto& dSdSl = val[0]; - const auto& dSdDl = val[1]; - const auto& dSdSr = val[2]; - const auto& dSdDr = val[3]; + T dSdSl, dSdDl, dSdSr, dSdDr; + getSderivativesOverParsAtU(knotL, u, dSdSl, dSdDl, dSdSr, dSdDr); + for (int32_t dim = 0; dim < nYdim; ++dim) { S[dim] = dSdSr * Sr[dim] + dSdSl * Sl[dim] + dSdDl * Dl[dim] + dSdDr * Dr[dim]; } @@ -346,7 +344,7 @@ class Spline1DSpec : public Spline1DContainer } template - GPUd() std::array getSderivativesOverParsAtU(const Knot& knotL, DataT u) const + GPUd() void getSderivativesOverParsAtU(const Knot& knotL, DataT u, T& dSdSl, T& dSdDl, T& dSdSr, T& dSdDr) const { /// Get derivatives of the interpolated value {S(u): 1D -> nYdim} at the segment [knotL, next knotR] /// over the spline parameters Sl(eft), Sr(ight) and the slopes Dl, Dr @@ -363,16 +361,15 @@ class Spline1DSpec : public Spline1DContainer T vm1 = v - T(1.); T a = u * vm1; T v2 = v * v; - T dSdSr = v2 * (T(3.) - v - v); - T dSdSl = T(1.) - dSdSr; - T dSdDl = vm1 * a; - T dSdDr = v * a; + dSdSr = v2 * (T(3.) - v - v); + dSdSl = T(1.) - dSdSr; + dSdDl = vm1 * a; + dSdDr = v * a; // S(u) = dSdSl * Sl + dSdSr * Sr + dSdDl * Dl + dSdDr * Dr; - return {dSdSl, dSdDl, dSdSr, dSdDr}; } template - GPUd() std::array getSDderivativesOverParsAtU(const Knot& knotL, DataT u) const + GPUd() void getSDderivativesOverParsAtU(const Knot& knotL, DataT u, T& dSdSl, T& dSdDl, T& dSdSr, T& dSdDr, T& dDdSl, T& dDdDl, T& dDdSr, T& dDdDr) const { /// Get derivatives of the interpolated value {S(u): 1D -> nYdim} at the segment [knotL, next knotR] /// over the spline values Sl, Sr and the slopes Dl, Dr @@ -389,19 +386,18 @@ class Spline1DSpec : public Spline1DContainer T vm1 = v - T(1.); T a = u * vm1; T v2 = v * v; - T dSdSr = v2 * (T(3.) - v - v); - T dSdSl = T(1.) - dSdSr; - T dSdDl = vm1 * a; - T dSdDr = v * a; + dSdSr = v2 * (T(3.) - v - v); + dSdSl = T(1.) - dSdSr; + dSdDl = vm1 * a; + dSdDr = v * a; T dv = T(knotL.Li); - T dDdSr = 6. * v * (T(1.) - v) * dv; - T dDdSl = -dDdSr; - T dDdDl = vm1 * (v + v + vm1); - T dDdDr = v * (v + vm1 + vm1); + dDdSr = 6. * v * (T(1.) - v) * dv; + dDdSl = -dDdSr; + dDdDl = vm1 * (v + v + vm1); + dDdDr = v * (v + vm1 + vm1); // S(u) = dSdSl * Sl + dSdSr * Sr + dSdDl * Dl + dSdDr * Dr; // D(u) = dS(u)/du = dDdSl * Sl + dDdSr * Sr + dDdDl * Dl + dDdDr * Dr; - return {dSdSl, dSdDl, dSdSr, dSdDr, dDdSl, dDdDl, dDdSr, dDdDr}; } using TBase::convXtoU; diff --git a/GPU/TPCFastTransformation/Spline2DSpec.h b/GPU/TPCFastTransformation/Spline2DSpec.h index 5681de2dc5fe9..fc53767ed6d07 100644 --- a/GPU/TPCFastTransformation/Spline2DSpec.h +++ b/GPU/TPCFastTransformation/Spline2DSpec.h @@ -334,16 +334,9 @@ class Spline2DSpec const DataT* A = Parameters + (nu * iv + iu) * nYdim4; // values { {Y1,Y2,Y3}, {Y1,Y2,Y3}'v, {Y1,Y2,Y3}'u, {Y1,Y2,Y3}''vu } at {u0, v0} const DataT* B = A + nYdim4 * nu; // values { ... } at {u0, v1} - auto val1 = mGridX1.template getSderivativesOverParsAtU(knotU, u); - auto val2 = mGridX2.template getSderivativesOverParsAtU(knotV, v); - const auto& dSl = val1[0]; - const auto& dDl = val1[1]; - const auto& dSr = val1[2]; - const auto& dDr = val1[3]; - const auto& dSd = val2[0]; - const auto& dDd = val2[1]; - const auto& dSu = val2[2]; - const auto& dDu = val2[3]; + DataT dSl, dDl, dSr, dDr, dSd, dDd, dSu, dDu; + mGridX1.template getSderivativesOverParsAtU(knotU, u, dSl, dDl, dSr, dDr); + mGridX2.template getSderivativesOverParsAtU(knotV, v, dSd, dDd, dSu, dDu); // when nYdim == 1: // S = dSl * (dSd * A[0] + dDd * A[1]) + dDl * (dSd * A[2] + dDd * A[3]) + @@ -398,8 +391,10 @@ class Spline2DSpec const DataT* A = Parameters + (nu * iv + iu) * nYdim4; // values { {Y1,Y2,Y3}, {Y1,Y2,Y3}'v, {Y1,Y2,Y3}'u, {Y1,Y2,Y3}''vu } at {u0, v0} const DataT* B = A + nYdim4 * nu; // values { ... } at {u0, v1} - auto [dSdSl, dSdDl, dSdSr, dSdDr, dRdSl, dRdDl, dRdSr, dRdDr] = mGridX1.template getSDderivativesOverParsAtU(knotU, u); - auto [dSdSd, dSdDd, dSdSu, dSdDu, dQdSd, dQdDd, dQdSu, dQdDu] = mGridX2.template getSDderivativesOverParsAtU(knotV, v); + DataT dSdSl, dSdDl, dSdSr, dSdDr, dRdSl, dRdDl, dRdSr, dRdDr; + mGridX1.template getSDderivativesOverParsAtU(knotU, u, dSdSl, dSdDl, dSdSr, dSdDr, dRdSl, dRdDl, dRdSr, dRdDr); + DataT dSdSd, dSdDd, dSdSu, dSdDu, dQdSd, dQdDd, dQdSu, dQdDu; + mGridX2.template getSDderivativesOverParsAtU(knotV, v, dSdSd, dSdDd, dSdSu, dSdDu, dQdSd, dQdDd, dQdSu, dQdDu); // when nYdim == 1: diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx index 241a1fcfc795b..5f5943a00372e 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx @@ -731,7 +731,8 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) MaxValue maxDrow[3]; for (double y = y0; y < y1; y += stepY) { for (double z = z0; z < z1; z += stepZ) { - auto [dx, dy, dz] = getCorrectionLocal(sector, row, y, z); + float dx, dy, dz; + getCorrectionLocal(sector, row, y, z, dx, dy, dz); double realX = x + dx; double realY = y + dy; double realZ = z + dz; @@ -745,7 +746,8 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) continue; } float dxr = getCorrectionXatRealYZ(sector, row, realY, realZ); - auto [dyr, dzr] = getCorrectionYZatRealYZ(sector, row, realY, realZ); + float dyr, dzr; + getCorrectionYZatRealYZ(sector, row, realY, realZ, dyr, dzr); double d[3] = {dxr - dx, dyr - dy, dzr - dz}; for (int32_t i = 0; i < 3; i++) { maxDrow[i].update(d[i], sector, row); diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index b1a3d0c35da7c..2a94154591533 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -22,6 +22,9 @@ #include "FlatObject.h" #include "GPUCommonDef.h" #include "GPUCommonMath.h" +#ifndef GPUCA_GPUCODE_DEVICE +#include "GPUCommonArray.h" // Would work on GPU, but yields performance regressions +#endif namespace o2 { @@ -76,15 +79,18 @@ class TPCFastSpaceChargeCorrection : public FlatObject } /// convert local y, z to internal grid coordinates u,v, and spline scale - GPUd() std::array convLocalToGridUntruncated(float y, float z) const + GPUd() void convLocalToGridUntruncated(float y, float z, float& u, float& v, float& s) const { - return {(y - y0) * yScale, (z - z0) * zScale, getSpineScaleForZ(z)}; + u = (y - y0) * yScale; + v = (z - z0) * zScale; + s = getSpineScaleForZ(z); } /// convert internal grid coordinates u,v to local y, z - std::array convGridToLocal(float gridU, float gridV) const + GPUd() void convGridToLocal(float gridU, float gridV, float& y, float& z) const { - return {y0 + gridU / yScale, z0 + gridV / zScale}; + y = y0 + gridU / yScale; + z = z0 + gridV / zScale; } ClassDefNV(GridInfo, 1); }; @@ -121,6 +127,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject maxCorr[2] = GPUCommonMath::Max(maxCorr[2], dv); } +#ifndef GPUCA_GPUCODE_DEVICE void updateMaxValues(std::array dxdudv, float scale) { float dx = dxdudv[0] * scale; @@ -138,6 +145,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject { return {minCorr[0], minCorr[1], minCorr[2]}; } +#endif ClassDefNV(SectorRowInfo, 2); }; @@ -249,31 +257,31 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// // GPUd() int32_t getCorrectionInternal(int32_t sector, int32_t row, float u, float v, float& dx, float& du, float& dv) const; - GPUdi() std::array getCorrectionLocal(int32_t sector, int32_t row, float y, float z) const; + GPUdi() void getCorrectionLocal(int32_t sector, int32_t row, float y, float z, float& dx, float& dy, float& dz) const; /// inverse correction: Real Y and Z -> Real X GPUd() float getCorrectionXatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const; /// inverse correction: Real Y and Z -> measred Y and Z - GPUd() std::array getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const; + GPUd() void getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ, float& y, float& z) const; /// _______________ Utilities _______________________________________________ /// convert local y, z to internal grid coordinates u,v /// return values: u, v, scaling factor - GPUd() std::array convLocalToGrid(int32_t sector, int32_t row, float y, float z) const; + GPUd() void convLocalToGrid(int32_t sector, int32_t row, float y, float z, float& u, float& v, float& s) const; /// convert internal grid coordinates u,v to local y, z /// return values: y, z, scaling factor - GPUd() std::array convGridToLocal(int32_t sector, int32_t row, float u, float v) const; + GPUd() void convGridToLocal(int32_t sector, int32_t row, float u, float v, float& y, float& z) const; /// convert real Y, Z to the internal grid coordinates /// return values: u, v, scaling factor - GPUd() std::array convRealLocalToGrid(int32_t sector, int32_t row, float y, float z) const; + GPUd() void convRealLocalToGrid(int32_t sector, int32_t row, float y, float z, float& u, float& v, float& s) const; /// convert internal grid coordinates to the real Y, Z /// return values: y, z - GPUd() std::array convGridToRealLocal(int32_t sector, int32_t row, float u, float v) const; + GPUd() void convGridToRealLocal(int32_t sector, int32_t row, float u, float v, float& y, float& z) const; GPUd() bool isLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const; GPUd() bool isRealLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const; @@ -419,26 +427,26 @@ GPUdi() const float* TPCFastSpaceChargeCorrection::getCorrectionDataInvYZ(int32_ return getCorrectionData(sector, row, 2); } -GPUdi() std::array TPCFastSpaceChargeCorrection::convLocalToGrid(int32_t sector, int32_t row, float y, float z) const +GPUdi() void TPCFastSpaceChargeCorrection::convLocalToGrid(int32_t sector, int32_t row, float y, float z, float& u, float& v, float& s) const { /// convert local y, z to internal grid coordinates u,v /// return values: u, v, scaling factor const SplineType& spline = getSpline(sector, row); - auto val = getSectorRowInfo(sector, row).gridMeasured.convLocalToGridUntruncated(y, z); + getSectorRowInfo(sector, row).gridMeasured.convLocalToGridUntruncated(y, z, u, v, s); // shrink to the grid - val[0] = GPUCommonMath::Clamp(val[0], 0.f, (float)spline.getGridX1().getUmax()); - val[1] = GPUCommonMath::Clamp(val[1], 0.f, (float)spline.getGridX2().getUmax()); - return val; + u = GPUCommonMath::Clamp(u, 0.f, (float)spline.getGridX1().getUmax()); + v = GPUCommonMath::Clamp(v, 0.f, (float)spline.getGridX2().getUmax()); } GPUdi() bool TPCFastSpaceChargeCorrection::isLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const { /// check if local y, z are inside the grid - auto val = getSectorRowInfo(sector, row).gridMeasured.convLocalToGridUntruncated(y, z); + float u, v, s; + getSectorRowInfo(sector, row).gridMeasured.convLocalToGridUntruncated(y, z, u, v, s); const auto& spline = getSpline(sector, row); // shrink to the grid - if (val[0] < 0.f || val[0] > (float)spline.getGridX1().getUmax() || // - val[1] < 0.f || val[1] > (float)spline.getGridX2().getUmax()) { + if (u < 0.f || u > (float)spline.getGridX1().getUmax() || // + v < 0.f || v > (float)spline.getGridX2().getUmax()) { return false; } return true; @@ -447,86 +455,86 @@ GPUdi() bool TPCFastSpaceChargeCorrection::isLocalInsideGrid(int32_t sector, int GPUdi() bool TPCFastSpaceChargeCorrection::isRealLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const { /// check if local y, z are inside the grid - auto val = getSectorRowInfo(sector, row).gridReal.convLocalToGridUntruncated(y, z); + float u, v, s; + getSectorRowInfo(sector, row).gridReal.convLocalToGridUntruncated(y, z, u, v, s); const auto& spline = getSpline(sector, row); // shrink to the grid - if (val[0] < 0.f || val[0] > (float)spline.getGridX1().getUmax() || // - val[1] < 0.f || val[1] > (float)spline.getGridX2().getUmax()) { + if (u < 0.f || u > (float)spline.getGridX1().getUmax() || // + v < 0.f || v > (float)spline.getGridX2().getUmax()) { return false; } return true; } -GPUdi() std::array TPCFastSpaceChargeCorrection::convGridToLocal(int32_t sector, int32_t row, float gridU, float gridV) const +GPUdi() void TPCFastSpaceChargeCorrection::convGridToLocal(int32_t sector, int32_t row, float gridU, float gridV, float& y, float& z) const { /// convert internal grid coordinates u,v to local y, z - return getSectorRowInfo(sector, row).gridMeasured.convGridToLocal(gridU, gridV); + getSectorRowInfo(sector, row).gridMeasured.convGridToLocal(gridU, gridV, y, z); } -GPUdi() std::array TPCFastSpaceChargeCorrection::convRealLocalToGrid(int32_t sector, int32_t row, float y, float z) const +GPUdi() void TPCFastSpaceChargeCorrection::convRealLocalToGrid(int32_t sector, int32_t row, float y, float z, float& u, float& v, float& s) const { /// convert real y, z to the internal grid coordinates + scale const SplineType& spline = getSpline(sector, row); - auto val = getSectorRowInfo(sector, row).gridReal.convLocalToGridUntruncated(y, z); + getSectorRowInfo(sector, row).gridReal.convLocalToGridUntruncated(y, z, u, v, s); // shrink to the grid - val[0] = GPUCommonMath::Clamp(val[0], 0.f, (float)spline.getGridX1().getUmax()); - val[1] = GPUCommonMath::Clamp(val[1], 0.f, (float)spline.getGridX2().getUmax()); - return val; + u = GPUCommonMath::Clamp(u, 0.f, (float)spline.getGridX1().getUmax()); + v = GPUCommonMath::Clamp(v, 0.f, (float)spline.getGridX2().getUmax()); } -GPUdi() std::array TPCFastSpaceChargeCorrection::convGridToRealLocal(int32_t sector, int32_t row, float gridU, float gridV) const +GPUdi() void TPCFastSpaceChargeCorrection::convGridToRealLocal(int32_t sector, int32_t row, float gridU, float gridV, float& y, float& z) const { /// convert internal grid coordinates u,v to the real y, z - return getSectorRowInfo(sector, row).gridReal.convGridToLocal(gridU, gridV); + getSectorRowInfo(sector, row).gridReal.convGridToLocal(gridU, gridV, y, z); } -GPUdi() std::array TPCFastSpaceChargeCorrection::getCorrectionLocal(int32_t sector, int32_t row, float y, float z) const +GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionLocal(int32_t sector, int32_t row, float y, float z, float& dx, float& dy, float& dz) const { const auto& info = getSectorRowInfo(sector, row); const SplineType& spline = getSpline(sector, row); const float* splineData = getCorrectionData(sector, row); - auto val = convLocalToGrid(sector, row, y, z); + float u, v, s; + convLocalToGrid(sector, row, y, z, u, v, s); float dxyz[3]; - spline.interpolateAtU(splineData, val[0], val[1], dxyz); + spline.interpolateAtU(splineData, u, v, dxyz); if (CAMath::Abs(dxyz[0]) > 100.f || CAMath::Abs(dxyz[1]) > 100.f || CAMath::Abs(dxyz[2]) > 100.f) { - val[2] = 0.f; // TODO: DR: Protect from FPEs, fix upstream and remove once guaranteed that it is fixed + s = 0.f; // TODO: DR: Protect from FPEs, fix upstream and remove once guaranteed that it is fixed } - float dx = val[2] * GPUCommonMath::Clamp(dxyz[0], info.minCorr[0], info.maxCorr[0]); - float dy = val[2] * GPUCommonMath::Clamp(dxyz[1], info.minCorr[1], info.maxCorr[1]); - float dz = val[2] * GPUCommonMath::Clamp(dxyz[2], info.minCorr[2], info.maxCorr[2]); - - return {dx, dy, dz}; + dx = s * GPUCommonMath::Clamp(dxyz[0], info.minCorr[0], info.maxCorr[0]); + dy = s * GPUCommonMath::Clamp(dxyz[1], info.minCorr[1], info.maxCorr[1]); + dz = s * GPUCommonMath::Clamp(dxyz[2], info.minCorr[2], info.maxCorr[2]); } GPUdi() float TPCFastSpaceChargeCorrection::getCorrectionXatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const { const auto& info = getSectorRowInfo(sector, row); - auto val = convRealLocalToGrid(sector, row, realY, realZ); + float u, v, s; + convRealLocalToGrid(sector, row, realY, realZ, u, v, s); float dx = 0; - getSplineInvX(sector, row).interpolateAtU(getCorrectionDataInvX(sector, row), val[0], val[1], &dx); + getSplineInvX(sector, row).interpolateAtU(getCorrectionDataInvX(sector, row), u, v, &dx); if (CAMath::Abs(dx) > 100.f) { - val[2] = 0.f; // TODO: DR: Protect from FPEs, fix upstream and remove once guaranteed that it is fixed + s = 0.f; // TODO: DR: Protect from FPEs, fix upstream and remove once guaranteed that it is fixed } - dx = val[2] * GPUCommonMath::Clamp(dx, info.minCorr[0], info.maxCorr[0]); + dx = s * GPUCommonMath::Clamp(dx, info.minCorr[0], info.maxCorr[0]); return dx; } -GPUdi() std::array TPCFastSpaceChargeCorrection::getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const +GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ, float& y, float& z) const { - auto val = convRealLocalToGrid(sector, row, realY, realZ); + float u, v, s; + convRealLocalToGrid(sector, row, realY, realZ, u, v, s); const auto& info = getSectorRowInfo(sector, row); float dyz[2]; - getSplineInvYZ(sector, row).interpolateAtU(getCorrectionDataInvYZ(sector, row), val[0], val[1], dyz); + getSplineInvYZ(sector, row).interpolateAtU(getCorrectionDataInvYZ(sector, row), u, v, dyz); if (CAMath::Abs(dyz[0]) > 100.f || CAMath::Abs(dyz[1]) > 100.f) { - val[2] = 0.f; // TODO: DR: Protect from FPEs, fix upstream and remove once guaranteed that it is fixed + s = 0.f; // TODO: DR: Protect from FPEs, fix upstream and remove once guaranteed that it is fixed } - dyz[0] = val[2] * GPUCommonMath::Clamp(dyz[0], info.minCorr[1], info.maxCorr[1]); - dyz[1] = val[2] * GPUCommonMath::Clamp(dyz[1], info.minCorr[2], info.maxCorr[2]); - return {dyz[0], dyz[1]}; + y = s * GPUCommonMath::Clamp(dyz[0], info.minCorr[1], info.maxCorr[1]); + z = s * GPUCommonMath::Clamp(dyz[1], info.minCorr[2], info.maxCorr[2]); } } // namespace gpu diff --git a/GPU/TPCFastTransformation/TPCFastTransform.h b/GPU/TPCFastTransformation/TPCFastTransform.h index 64fdba9d94bd3..532ec855c77b0 100644 --- a/GPU/TPCFastTransformation/TPCFastTransform.h +++ b/GPU/TPCFastTransformation/TPCFastTransform.h @@ -349,17 +349,13 @@ class TPCFastTransform : public FlatObject GPUdi() void TPCFastTransform::convPadTimeToLocal(int32_t sector, int32_t row, float pad, float time, float& y, float& z, float vertexTime) const { float l = (time - mT0 - vertexTime) * mVdrift; // drift length [cm] - const auto localval = getGeometry().convPadDriftLengthToLocal(sector, row, pad, l); - y = localval[0]; - z = localval[1]; + getGeometry().convPadDriftLengthToLocal(sector, row, pad, l, y, z); } GPUdi() void TPCFastTransform::convPadTimeToLocalInTimeFrame(int32_t sector, int32_t row, float pad, float time, float& y, float& z, float maxTimeBin) const { float l = getGeometry().getTPCzLength() + (time - mT0 - maxTimeBin) * mVdrift; // drift length [cm] - const auto localval = getGeometry().convPadDriftLengthToLocal(sector, row, pad, l); - y = localval[0]; - z = localval[1]; + getGeometry().convPadDriftLengthToLocal(sector, row, pad, l, y, z); } // ---------------------------------------------------------------------- @@ -391,16 +387,16 @@ GPUdi() float TPCFastTransform::convDriftLengthToTime(float driftLength, float v GPUdi() void TPCFastTransform::convLocalToPadTime(int32_t sector, int32_t row, float y, float z, float& pad, float& time, float vertexTime) const { - const auto padLength = getGeometry().convLocalToPadDriftLength(sector, row, y, z); - pad = padLength[0]; - time = convDriftLengthToTime(padLength[1], vertexTime); + float l; + getGeometry().convLocalToPadDriftLength(sector, row, y, z, pad, l); + time = convDriftLengthToTime(l, vertexTime); } GPUdi() void TPCFastTransform::convLocalToPadTimeInTimeFrame(int32_t sector, int32_t row, float y, float z, float& pad, float& time, float maxTimeBin) const { - const auto padLength = getGeometry().convLocalToPadDriftLength(sector, row, y, z); - pad = padLength[0]; - time = convDriftLengthToTime(padLength[1], maxTimeBin); + float l; + getGeometry().convLocalToPadDriftLength(sector, row, y, z, pad, l); + time = convDriftLengthToTime(l, maxTimeBin); } // ---------------------------------------------------------------------- @@ -426,28 +422,28 @@ GPUdi() void TPCFastTransform::TransformLocal(int32_t sector, int32_t row, float } else #endif // GPUCA_GPUCODE { - const auto corrLocal = mCorrection.getCorrectionLocal(sector, row, y, z); - dx = corrLocal[0]; - dy = corrLocal[1]; - dz = corrLocal[2]; + mCorrection.getCorrectionLocal(sector, row, y, z, dx, dy, dz); if (ref) { if ((scale > 0.f) && (scaleMode == 0)) { // scaling was requested - auto val = ref->mCorrection.getCorrectionLocal(sector, row, y, z); - dx = (dx - val[0]) * scale + val[0]; - dy = (dy - val[1]) * scale + val[1]; - dz = (dz - val[2]) * scale + val[2]; + float dx1, dy1, dz1; + ref->mCorrection.getCorrectionLocal(sector, row, y, z, dx1, dy1, dz1); + dx = (dx - dx1) * scale + dx1; + dy = (dy - dy1) * scale + dy1; + dz = (dz - dz1) * scale + dz1; } else if ((scale != 0.f) && ((scaleMode == 1) || (scaleMode == 2))) { - auto val = ref->mCorrection.getCorrectionLocal(sector, row, y, z); - dx = val[0] * scale + dx; - dy = val[1] * scale + dy; - dz = val[2] * scale + dz; + float dx1, dy1, dz1; + ref->mCorrection.getCorrectionLocal(sector, row, y, z, dx1, dy1, dz1); + dx = dx1 * scale + dx; + dy = dy1 * scale + dy; + dz = dz1 * scale + dz; } } if (ref2 && (scale2 != 0)) { - auto val = ref2->mCorrection.getCorrectionLocal(sector, row, y, z); - dx = val[0] * scale2 + dx; - dy = val[1] * scale2 + dy; - dz = val[2] * scale2 + dz; + float dx1, dy1, dz1; + ref2->mCorrection.getCorrectionLocal(sector, row, y, z, dx1, dy1, dz1); + dx = dx1 * scale2 + dx; + dy = dy1 * scale2 + dy; + dz = dz1 * scale2 + dz; } } } @@ -478,21 +474,16 @@ GPUdi() void TPCFastTransform::TransformLocal(int32_t sector, int32_t row, float float dxRef = 0.f, dyRef = 0.f, dzRef = 0.f; if (ref) { - const auto corr = ref->mCorrection.getCorrectionLocal(sector, row, y, z); - dxRef = corr[0]; - dyRef = corr[1]; - dzRef = corr[2]; + ref->mCorrection.getCorrectionLocal(sector, row, y, z, dxRef, dyRef, dzRef); } float dxRef2 = 0.f, dyRef2 = 0.f, dzRef2 = 0.f; if (ref2) { - const auto corr = ref2->mCorrection.getCorrectionLocal(sector, row, y, z); - dxRef2 = corr[0]; - dyRef2 = corr[1]; - dzRef2 = corr[2]; + ref2->mCorrection.getCorrectionLocal(sector, row, y, z, dxRef2, dyRef2, dzRef2); } - auto [dxOrig, dyOrig, dzOrig] = mCorrection.getCorrectionLocal(sector, row, y, z); + float dxOrig, dyOrig, dzOrig; + mCorrection.getCorrectionLocal(sector, row, y, z, dxOrig, dyOrig, dzOrig); o2::utils::DebugStreamer::instance()->getStreamer("debug_fasttransform", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_Transform").data() // corrections in x, u, v @@ -617,9 +608,7 @@ GPUdi() void TPCFastTransform::TransformIdeal(int32_t sector, int32_t row, float x = getGeometry().getRowInfo(row).x; float driftLength = (time - mT0 - vertexTime) * mVdrift; // drift length cm - const auto localval = getGeometry().convPadDriftLengthToLocal(sector, row, pad, driftLength); - y = localval[0]; - z = localval[1]; + getGeometry().convPadDriftLengthToLocal(sector, row, pad, driftLength, y, z); } GPUdi() float TPCFastTransform::convTimeToZinTimeFrame(int32_t sector, float time, float maxTimeBin) const @@ -726,24 +715,25 @@ GPUdi() void TPCFastTransform::InverseTransformYZtoNominalYZ(int32_t sector, int float dz = 0; if ((scale >= 0.f) || (scaleMode == 1) || (scaleMode == 2)) { - const auto corrYZ = mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); - dy = corrYZ[0]; - dz = corrYZ[1]; + mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ, dy, dz); if (ref) { // scaling was requested if (scaleMode == 0 && scale > 0.f) { - const auto val = ref->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); - dy = (dy - val[0]) * scale + val[0]; - dz = (dz - val[1]) * scale + val[1]; + float dy1, dz1; + ref->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ, dy1, dz1); + dy = (dy - dy1) * scale + dy1; + dz = (dz - dz1) * scale + dz1; } else if ((scale != 0) && ((scaleMode == 1) || (scaleMode == 2))) { - const auto val = ref->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); - dy = val[0] * scale + dy; - dz = val[1] * scale + dz; + float dy1, dz1; + ref->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ, dy1, dz1); + dy = dy1 * scale + dy; + dz = dz1 * scale + dz; } if (ref2 && (scale2 != 0)) { - const auto val = ref2->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); - dy = val[0] * scale2 + dy; - dz = val[1] * scale2 + dz; + float dy1, dz1; + ref2->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ, dy1, dz1); + dy = dy1 * scale2 + dy; + dz = dz1 * scale2 + dz; } } } diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx b/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx index 2fe773a76e4d3..c7ed4243d7396 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx @@ -145,8 +145,10 @@ int32_t TPCFastTransformGeo::test(int32_t sector, int32_t row, float ly, float l error = -3; } - auto [pad, length] = convLocalToPadDriftLength(sector, 10, ly, lz); - auto [ly2, lz2] = convPadDriftLengthToLocal(sector, 10, pad, length); + float pad, length; + convLocalToPadDriftLength(sector, 10, ly, lz, pad, length); + float ly2, lz2; + convPadDriftLengthToLocal(sector, 10, pad, length, ly2, lz2); if (fabs(ly2 - ly) + fabs(lz2 - lz) > 1.e-6) { LOG(info) << "Error local <-> UV: y " << ly << " dy " << ly2 - ly << " z " << lz << " dz " << lz2 - lz; diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.h b/GPU/TPCFastTransformation/TPCFastTransformGeo.h index 09793b6677d83..55e36cf6efef7 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.h +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.h @@ -18,12 +18,12 @@ #define ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_TPCFASTTRANSFORMGEO_H #include "GPUCommonDef.h" -#include "GPUCommonArray.h" #include "GPUCommonMath.h" #ifndef GPUCA_GPUCODE_DEVICE #include #include "GPUCommonRtypes.h" +#include "GPUCommonArray.h" // Would work on GPU, but yields performance regressions #endif namespace o2 @@ -59,7 +59,9 @@ class TPCFastTransformGeo GPUd() float getYmax() const { return -yMin; } /// get Y range +#ifndef GPUCA_GPUCODE_DEVICE GPUd() std::array getYrange() const { return {getYmin(), getYmax()}; } +#endif /// get width in Y GPUd() float getYwidth() const { return -2.f * yMin; } @@ -129,7 +131,17 @@ class TPCFastTransformGeo GPUd() float getTPCzLength() const { return mTPCzLength; } /// Gives Z range for the corresponding TPC side - GPUd() std::array getZrange(int32_t sector) const; +#ifndef GPUCA_GPUCODE_DEVICE + GPUdi() std::array getZrange(int32_t sector) const + { + /// z range for the sector + if (sector < NumberOfSectorsA) { // TPC side A + return {0.f, mTPCzLength}; + } else { // TPC side C + return {-mTPCzLength, 0.f}; + } + } +#endif GPUd() float getZmin(int32_t sector) const; GPUd() float getZmax(int32_t sector) const; GPUd() float getZreadout(int32_t sector) const; @@ -143,7 +155,7 @@ class TPCFastTransformGeo GPUd() void convGlobalToLocal(int32_t sector, float gx, float gy, float gz, float& lx, float& ly, float& lz) const; /// convert Pad, DriftLength -> Local c.s. - GPUd() std::array convPadDriftLengthToLocal(int32_t sector, int32_t row, float pad, float driftLength) const; + GPUd() void convPadDriftLengthToLocal(int32_t sector, int32_t row, float pad, float driftLength, float& y, float& z) const; /// convert DriftLength -> Local c.s. GPUd() float convDriftLengthToZ1(int32_t sector, float driftLength) const; @@ -152,7 +164,7 @@ class TPCFastTransformGeo GPUd() float convZtoDriftLength1(int32_t sector, float z) const; /// convert Local c.s. -> Pad, DriftLength - GPUd() std::array convLocalToPadDriftLength(int32_t sector, int32_t row, float y, float z) const; + GPUd() void convLocalToPadDriftLength(int32_t sector, int32_t row, float y, float z, float& pad, float& l) const; /// Print method void print() const; @@ -238,12 +250,11 @@ GPUdi() void TPCFastTransformGeo::convGlobalToLocal(int32_t sector, float gx, fl lz = gz; } -GPUdi() std::array TPCFastTransformGeo::convPadDriftLengthToLocal(int32_t sector, int32_t row, float pad, float driftLength) const +GPUdi() void TPCFastTransformGeo::convPadDriftLengthToLocal(int32_t sector, int32_t row, float pad, float driftLength, float& y, float& z) const { /// convert Pad, DriftLength -> Local c.s. const RowInfo& rowInfo = getRowInfo(row); float u = (pad - 0.5f * rowInfo.maxPad) * rowInfo.padWidth; - float y, z; if (sector < NumberOfSectorsA) { // TPC side A y = u; z = mTPCzLength - driftLength; @@ -251,7 +262,6 @@ GPUdi() std::array TPCFastTransformGeo::convPadDriftLengthToLocal(int3 y = -u; // pads are mirrorred on C-side z = driftLength - mTPCzLength; // drift direction is mirrored on C-side } - return {y, z}; } GPUdi() float TPCFastTransformGeo::convDriftLengthToZ1(int32_t sector, float driftLength) const @@ -266,16 +276,6 @@ GPUdi() float TPCFastTransformGeo::convZtoDriftLength1(int32_t sector, float z) return (sector < NumberOfSectorsA) ? (mTPCzLength - z) : (z + mTPCzLength); } -GPUdi() std::array TPCFastTransformGeo::getZrange(int32_t sector) const -{ - /// z range for the sector - if (sector < NumberOfSectorsA) { // TPC side A - return {0.f, mTPCzLength}; - } else { // TPC side C - return {-mTPCzLength, 0.f}; - } -} - GPUdi() float TPCFastTransformGeo::getZmin(int32_t sector) const { /// z min for the sector @@ -306,10 +306,10 @@ GPUdi() float TPCFastTransformGeo::getZreadout(int32_t sector) const } } -GPUdi() std::array TPCFastTransformGeo::convLocalToPadDriftLength(int32_t sector, int32_t row, float y, float z) const +GPUdi() void TPCFastTransformGeo::convLocalToPadDriftLength(int32_t sector, int32_t row, float y, float z, float& pad, float& l) const { /// convert Local c.s. -> Pad, DriftLength - float u, l; + float u; if (sector < NumberOfSectorsA) { // TPC side A u = y; l = mTPCzLength - z; @@ -318,8 +318,7 @@ GPUdi() std::array TPCFastTransformGeo::convLocalToPadDriftLength(int3 l = z + mTPCzLength; // drift direction is mirrored on C-side } const TPCFastTransformGeo::RowInfo& rowInfo = getRowInfo(row); - float pad = u / rowInfo.padWidth + 0.5f * rowInfo.maxPad; - return {pad, l}; + pad = u / rowInfo.padWidth + 0.5f * rowInfo.maxPad; } } // namespace gpu diff --git a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C index baaeca90202d5..bc6fafbaa8bd0 100644 --- a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C +++ b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C @@ -493,12 +493,14 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* auto [zMin, zMax] = geo.getZrange(iSector); for (int32_t iu = 0; iu < gridY.getNumberOfKnots(); iu++) { - auto [y, z] = corr.convGridToLocal(iSector, iRow, gridY.getKnot(iu).getU(), 0.); + float y, z; + corr.convGridToLocal(iSector, iRow, gridY.getKnot(iu).getU(), 0., y, z); knots[0].push_back(y); points[0].push_back(y); } for (int32_t iv = 0; iv < gridZ.getNumberOfKnots(); iv++) { - auto [y, z] = corr.convGridToLocal(iSector, iRow, 0., gridZ.getKnot(iv).getU()); + float y, z; + corr.convGridToLocal(iSector, iRow, 0., gridZ.getKnot(iv).getU(), y, z); knots[1].push_back(z); points[1].push_back(z); } From 124fd41b563e455f82b45daee47c064d87a5d5f8 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Tue, 31 Mar 2026 11:56:27 +0200 Subject: [PATCH 104/285] GPU TPCFastTransformation: Do not use double --- GPU/TPCFastTransformation/Spline1DSpec.h | 4 ++-- GPU/TPCFastTransformation/Spline2DSpec.h | 2 +- GPU/TPCFastTransformation/SplineSpec.h | 2 +- GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/GPU/TPCFastTransformation/Spline1DSpec.h b/GPU/TPCFastTransformation/Spline1DSpec.h index 56349ba6f454a..3a2b5d0c4ee32 100644 --- a/GPU/TPCFastTransformation/Spline1DSpec.h +++ b/GPU/TPCFastTransformation/Spline1DSpec.h @@ -392,7 +392,7 @@ class Spline1DSpec : public Spline1DContainer dSdDr = v * a; T dv = T(knotL.Li); - dDdSr = 6. * v * (T(1.) - v) * dv; + dDdSr = T(6.) * v * (T(1.) - v) * dv; dDdSl = -dDdSr; dDdDl = vm1 * (v + v + vm1); dDdDr = v * (v + vm1 + vm1); @@ -567,7 +567,7 @@ class Spline1DSpec /// Simplified interface for 1D: return the interpolated value GPUd() DataT interpolate(DataT x) const { - DataT S = 0.; + DataT S = 0; TBase::interpolate(x, &S); return S; } diff --git a/GPU/TPCFastTransformation/Spline2DSpec.h b/GPU/TPCFastTransformation/Spline2DSpec.h index fc53767ed6d07..d0648f2afa22b 100644 --- a/GPU/TPCFastTransformation/Spline2DSpec.h +++ b/GPU/TPCFastTransformation/Spline2DSpec.h @@ -659,7 +659,7 @@ class Spline2DSpec /// Simplified interface for 1D: return the interpolated value GPUd() DataT interpolate(DataT x1, DataT x2) const { - DataT S = 0.; + DataT S = 0; TBase::interpolate(x1, x2, &S); return S; } diff --git a/GPU/TPCFastTransformation/SplineSpec.h b/GPU/TPCFastTransformation/SplineSpec.h index 2102b73e72900..31b6bef22103c 100644 --- a/GPU/TPCFastTransformation/SplineSpec.h +++ b/GPU/TPCFastTransformation/SplineSpec.h @@ -537,7 +537,7 @@ class SplineSpec /// Simplified interface for 1D: return the interpolated value GPUd() DataT interpolate(const DataT x[]) const { - DataT S = 0.; + DataT S = 0; TBase::interpolate(x, &S); return S; } diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index 2a94154591533..12dc5c2fdee54 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -65,7 +65,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject this->zScale = zScale_; this->zOut = zOut_; // no scaling when the distance to the readout is too small - this->splineScalingWithZ = fabs(zReadout_ - zOut_) > 1. ? 1. / (zReadout_ - zOut_) : 0.; + this->splineScalingWithZ = fabs(zReadout_ - zOut_) > 1.f ? 1.f / (zReadout_ - zOut_) : 0.f; } float getY0() const { return y0; } From 8b79a844836b0bc779424a6fa08f36e93432523e Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 1 Apr 2026 12:02:34 +0200 Subject: [PATCH 105/285] GPU TPCFastTransfomration: Add missing inline keywords --- .../TPCFastSpaceChargeCorrection.h | 14 +++++----- GPU/TPCFastTransformation/TPCFastTransform.h | 26 +++++++++---------- .../TPCFastTransformGeo.h | 16 ++++++------ 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index 12dc5c2fdee54..6dcf30b2991ba 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -73,13 +73,13 @@ class TPCFastSpaceChargeCorrection : public FlatObject float getZ0() const { return z0; } float getZscale() const { return zScale; } - GPUd() float getSpineScaleForZ(float z) const + GPUdi() float getSpineScaleForZ(float z) const { return 1.f - GPUCommonMath::Clamp((z - zOut) * splineScalingWithZ, 0.f, 1.f); } /// convert local y, z to internal grid coordinates u,v, and spline scale - GPUd() void convLocalToGridUntruncated(float y, float z, float& u, float& v, float& s) const + GPUdi() void convLocalToGridUntruncated(float y, float z, float& u, float& v, float& s) const { u = (y - y0) * yScale; v = (z - z0) * zScale; @@ -87,7 +87,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject } /// convert internal grid coordinates u,v to local y, z - GPUd() void convGridToLocal(float gridU, float gridV, float& y, float& z) const + GPUdi() void convGridToLocal(float gridU, float gridV, float& y, float& z) const { y = y0 + gridU / yScale; z = z0 + gridV / zScale; @@ -215,7 +215,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject GPUd() void setNoCorrection(); /// Sets the time stamp of the current calibaration - GPUd() void setTimeStamp(int64_t v) { mTimeStamp = v; } + GPUdi() void setTimeStamp(int64_t v) { mTimeStamp = v; } /// Gives const pointer to a spline GPUd() const SplineType& getSpline(int32_t sector, int32_t row) const; @@ -287,7 +287,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject GPUd() bool isRealLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const; /// TPC geometry information - GPUd() const TPCFastTransformGeo& getGeometry() const + GPUdi() const TPCFastTransformGeo& getGeometry() const { return mGeo; } @@ -296,13 +296,13 @@ class TPCFastSpaceChargeCorrection : public FlatObject int64_t getTimeStamp() const { return mTimeStamp; } /// Gives TPC sector & row info - GPUd() const SectorRowInfo& getSectorRowInfo(int32_t sector, int32_t row) const + GPUdi() const SectorRowInfo& getSectorRowInfo(int32_t sector, int32_t row) const { return mSectorRowInfos[mGeo.getMaxNumberOfRows() * sector + row]; } /// Gives TPC sector & row info - GPUd() SectorRowInfo& getSectorRowInfo(int32_t sector, int32_t row) + GPUdi() SectorRowInfo& getSectorRowInfo(int32_t sector, int32_t row) { return mSectorRowInfos[mGeo.getMaxNumberOfRows() * sector + row]; } diff --git a/GPU/TPCFastTransformation/TPCFastTransform.h b/GPU/TPCFastTransformation/TPCFastTransform.h index 532ec855c77b0..068c85b13836a 100644 --- a/GPU/TPCFastTransformation/TPCFastTransform.h +++ b/GPU/TPCFastTransformation/TPCFastTransform.h @@ -53,7 +53,7 @@ struct TPCSlowSpaceChargeCorrection { ~TPCSlowSpaceChargeCorrection() = default; /// setting dummy corrections for GPU - GPUd() void getCorrections(const float gx, const float gy, const float gz, const int32_t sector, float& gdxC, float& gdyC, float& gdzC) const + GPUdi() void getCorrections(const float gx, const float gy, const float gz, const int32_t sector, float& gdxC, float& gdyC, float& gdzC) const { gdxC = 0; gdyC = 0; @@ -172,7 +172,7 @@ class TPCFastTransform : public FlatObject void setTimeStamp(int64_t v) { mTimeStamp = v; } /// Gives a reference for external initialization of TPC corrections - GPUd() const TPCFastSpaceChargeCorrection& getCorrection() const { return mCorrection; } + GPUdi() const TPCFastSpaceChargeCorrection& getCorrection() const { return mCorrection; } /// Gives a reference for external initialization of TPC corrections TPCFastSpaceChargeCorrection& getCorrection() { return mCorrection; } @@ -230,37 +230,37 @@ class TPCFastTransform : public FlatObject /// _______________ Utilities _______________________________________________ /// TPC geometry information - GPUd() const TPCFastTransformGeo& getGeometry() const { return mCorrection.getGeometry(); } + GPUdi() const TPCFastTransformGeo& getGeometry() const { return mCorrection.getGeometry(); } /// Gives the time stamp of the current calibaration parameters - GPUd() int64_t getTimeStamp() const { return mTimeStamp; } + GPUdi() int64_t getTimeStamp() const { return mTimeStamp; } /// Return mVDrift in cm / time bin - GPUd() float getVDrift() const { return mVdrift; } + GPUdi() float getVDrift() const { return mVdrift; } /// Return T0 in time bin units - GPUd() float getT0() const { return mT0; } + GPUdi() float getT0() const { return mT0; } /// Return map lumi - GPUd() float getLumi() const { return mLumi; } + GPUdi() float getLumi() const { return mLumi; } - GPUd() float isLumiSet() const { return mLumi != DEFLUMI; } + GPUdi() float isLumiSet() const { return mLumi != DEFLUMI; } /// Return map lumi error - GPUd() float getLumiError() const { return mLumiError; } + GPUdi() float getLumiError() const { return mLumiError; } /// Return map lumi GPUd() float getIDC() const; - GPUd() bool isIDCSet() const { return mIDC != DEFIDC; } + GPUdi() bool isIDCSet() const { return mIDC != DEFIDC; } /// Return map lumi error - GPUd() float getIDCError() const { return mIDCError; } + GPUdi() float getIDCError() const { return mIDCError; } - GPUd() float getCTP2IDCFallBackThreshold() const { return mCTP2IDCFallBackThreshold; } + GPUdi() float getCTP2IDCFallBackThreshold() const { return mCTP2IDCFallBackThreshold; } /// Return map user defined lumi scale factor - GPUd() float getLumiScaleFactor() const { return mLumiScaleFactor; } + GPUdi() float getLumiScaleFactor() const { return mLumiScaleFactor; } /// maximal possible drift time of the active area GPUd() float getMaxDriftTime(int32_t sector, int32_t row, float pad) const; diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.h b/GPU/TPCFastTransformation/TPCFastTransformGeo.h index 55e36cf6efef7..31b81e02c2d4c 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.h +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.h @@ -53,10 +53,10 @@ class TPCFastTransformGeo float yMin{0.f}; ///< min. y coordinate /// get Y min - GPUd() float getYmin() const { return yMin; } + GPUdi() float getYmin() const { return yMin; } /// get Y max - GPUd() float getYmax() const { return -yMin; } + GPUdi() float getYmax() const { return -yMin; } /// get Y range #ifndef GPUCA_GPUCODE_DEVICE @@ -64,7 +64,7 @@ class TPCFastTransformGeo #endif /// get width in Y - GPUd() float getYwidth() const { return -2.f * yMin; } + GPUdi() float getYwidth() const { return -2.f * yMin; } ClassDefNV(RowInfo, 2); }; @@ -110,16 +110,16 @@ class TPCFastTransformGeo /// _______________ Getters _________________________________ /// Gives number of TPC sectors - GPUd() static constexpr int32_t getNumberOfSectors() { return NumberOfSectors; } + GPUdi() static constexpr int32_t getNumberOfSectors() { return NumberOfSectors; } /// Gives number of TPC sectors on the A side - GPUd() static constexpr int32_t getNumberOfSectorsA() { return NumberOfSectorsA; } + GPUdi() static constexpr int32_t getNumberOfSectorsA() { return NumberOfSectorsA; } /// Gives number of TPC rows - GPUd() int32_t getNumberOfRows() const { return mNumberOfRows; } + GPUdi() int32_t getNumberOfRows() const { return mNumberOfRows; } /// Gives number of TPC rows - GPUd() static constexpr int getMaxNumberOfRows() { return MaxNumberOfRows; } + GPUdi() static constexpr int getMaxNumberOfRows() { return MaxNumberOfRows; } /// Gives sector info GPUd() const SectorInfo& getSectorInfo(int32_t sector) const; @@ -128,7 +128,7 @@ class TPCFastTransformGeo GPUd() const RowInfo& getRowInfo(int32_t row) const; /// Gives Z length of the TPC, one Z side - GPUd() float getTPCzLength() const { return mTPCzLength; } + GPUdi() float getTPCzLength() const { return mTPCzLength; } /// Gives Z range for the corresponding TPC side #ifndef GPUCA_GPUCODE_DEVICE From bf2d88e5138b05643788eae70492559220995bea Mon Sep 17 00:00:00 2001 From: shahoian Date: Sun, 22 Feb 2026 12:43:33 +0100 Subject: [PATCH 106/285] Add POD version of TPCFastTransform The TPCFastTransformPOD is a pointerless version of the TPCFastTransform. It can be created from the original TPCFastTransform as e.g. auto lold = o2::gpu::TPCFastTransform::loadFromFile("o2-gpu-TPCFastTransform.root","ccdb_object"); // load original transform std::vector v; // one has to provide a vector (could be a std or pmr), which later can be messaged via DPL auto* pod = o2::gpu::TPCFastTransformPOD::create(v, *lold); // pointer pod is just v.data() cast to TPCFastTransformPOD* // run test: pod->test(*lold); [INFO] (ns per call) original this Nmissmatch [INFO] getCorrection 1.330e+02 1.400e+02 0 [INFO] getCorrectionInvCorrectedX 8.856e+01 8.434e+01 0 [INFO] getCorrectionInvUV 6.266e+01 6.142e+01 0 It can be also created directly from the TPCFastSpaceChargeCorrection as TPCFastSpaceChargeCorrection& oldCorr = lold->getCorrection(); auto* pod = o2::gpu::TPCFastTransformPOD::create(v, oldCorr); but in this case one should afterwards set the vdrift and t0 using provided getters. TPCFastTransformPOD replicates all the methods of the TPCFastTransform (and of the TPCFastSpaceChargeCorrection), including those which allow to query rescaled corrections (by providing refernce maps and scaling coefficients). Since the idea of this class is to create a final correction map as a weighted sum of different contribution and to distribute it to consumer processes via shared memory, also the query methods w/o rescaling are added, they have the suffix _new added. Eventually, the scalable legacy methods can be suppressed and the suffix new can be dropped. --- GPU/TPCFastTransformation/CMakeLists.txt | 1 + .../TPCFastSpaceChargeCorrection.h | 2 + .../TPCFastTransformPOD.cxx | 245 +++++ .../TPCFastTransformPOD.h | 916 ++++++++++++++++++ .../TPCFastTransformationLinkDef_O2.h | 1 + 5 files changed, 1165 insertions(+) create mode 100644 GPU/TPCFastTransformation/TPCFastTransformPOD.cxx create mode 100644 GPU/TPCFastTransformation/TPCFastTransformPOD.h diff --git a/GPU/TPCFastTransformation/CMakeLists.txt b/GPU/TPCFastTransformation/CMakeLists.txt index 182a66fb28296..769e9981102ef 100644 --- a/GPU/TPCFastTransformation/CMakeLists.txt +++ b/GPU/TPCFastTransformation/CMakeLists.txt @@ -26,6 +26,7 @@ set(SRCS TPCFastSpaceChargeCorrectionMap.cxx TPCFastTransform.cxx CorrectionMapsHelper.cxx + TPCFastTransformPOD.cxx ) if(NOT ALIGPU_BUILD_TYPE STREQUAL "Standalone") diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index 6dcf30b2991ba..aedb2531470fc 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -41,6 +41,8 @@ namespace gpu /// class TPCFastSpaceChargeCorrection : public FlatObject { + friend class TPCFastTransformPOD; + public: // obsolete structure, declared here only for backward compatibility struct SliceInfo { diff --git a/GPU/TPCFastTransformation/TPCFastTransformPOD.cxx b/GPU/TPCFastTransformation/TPCFastTransformPOD.cxx new file mode 100644 index 0000000000000..016eed2e6beb4 --- /dev/null +++ b/GPU/TPCFastTransformation/TPCFastTransformPOD.cxx @@ -0,0 +1,245 @@ +// 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 TPCFastTransformPOD.cxx +/// \brief Implementation of POD correction map +/// +/// \author ruben.shahoayn@cern.ch + +/// \brief Implementation of POD correction map +/// +/// \author ruben.shahoayn@cern.ch + +#if !defined(GPUCA_NO_ROOT) && !defined(GPUCA_NO_FMT) && !defined(GPUCA_STANDALONE) +#include +#endif +#include "TPCFastTransformPOD.h" +#include "GPUDebugStreamer.h" + +namespace o2 +{ +namespace gpu +{ + +#if !defined(GPUCA_NO_ROOT) && !defined(GPUCA_NO_FMT) && !defined(GPUCA_STANDALONE) + +size_t TPCFastTransformPOD::estimateSize(const TPCFastSpaceChargeCorrection& origCorr) +{ + // estimate size of own buffer + const size_t selfSizeFix = sizeof(TPCFastTransformPOD); + size_t nextDynOffs = alignOffset(selfSizeFix); + nextDynOffs = alignOffset(nextDynOffs + origCorr.mNumberOfScenarios * sizeof(size_t)); // spline scenarios start here + // space for splines + for (int isc = 0; isc < origCorr.mNumberOfScenarios; isc++) { + const auto& spline = origCorr.mScenarioPtr[isc]; + nextDynOffs = alignOffset(nextDynOffs + sizeof(spline)); + } + // space for splines data + for (int is = 0; is < 3; is++) { + for (int sector = 0; sector < origCorr.mGeo.getNumberOfSectors(); sector++) { + for (int row = 0; row < NROWS; row++) { + const auto& spline = origCorr.getSpline(sector, row); + int nPar = spline.getNumberOfParameters(); + if (is == 1) { + nPar = nPar / 3; + } + if (is == 2) { + nPar = nPar * 2 / 3; + } + nextDynOffs += nPar * sizeof(float); + } + } + } + nextDynOffs = alignOffset(nextDynOffs); + return nextDynOffs; +} + +TPCFastTransformPOD* TPCFastTransformPOD::create(char* buff, size_t buffSize, const TPCFastSpaceChargeCorrection& origCorr) +{ + // instantiate object to already created buffer of the right size + assert(buffSize > sizeof(TPCFastTransformPOD)); + auto& podMap = getNonConst(buff); + podMap.mApplyCorrection = true; // by default always apply corrections + + // copy fixed size data --- start + podMap.mNumberOfScenarios = origCorr.mNumberOfScenarios; + std::memcpy(&podMap.mGeo, &origCorr.mGeo, sizeof(TPCFastTransformGeo)); // copy geometry (fixed size) + for (int sector = 0; sector < TPCFastTransformGeo::getNumberOfSectors(); sector++) { + for (int row = 0; row < NROWS; row++) { + podMap.mSectorRowInfos[NROWS * sector + row] = origCorr.getSectorRowInfo(sector, row); + } + } + podMap.mTimeStamp = origCorr.mTimeStamp; + // + // init data members coming from the TPCFastTrasform + podMap.mVdrift = 0.; + podMap.mT0 = 0.; + // copy fixed size data --- end + + size_t nextDynOffs = alignOffset(sizeof(TPCFastTransformPOD)); + + // copy sector scenarios + podMap.mOffsScenariosOffsets = nextDynOffs; // spline scenarios offsets start here + LOGP(debug, "Set mOffsScenariosOffsets = {}", podMap.mOffsScenariosOffsets); + nextDynOffs = alignOffset(nextDynOffs + podMap.mNumberOfScenarios * sizeof(size_t)); // spline scenarios start here + + // copy spline objects + size_t* scenOffs = reinterpret_cast(buff + podMap.mOffsScenariosOffsets); + for (int isc = 0; isc < origCorr.mNumberOfScenarios; isc++) { + scenOffs[isc] = nextDynOffs; + const auto& spline = origCorr.mScenarioPtr[isc]; + if (buffSize < nextDynOffs + sizeof(spline)) { + throw std::runtime_error(fmt::format("attempt to copy {} bytes for spline for scenario {} to {}, overflowing the buffer of size {}", sizeof(spline), isc, nextDynOffs + sizeof(spline), buffSize)); + } + std::memcpy(buff + scenOffs[isc], &spline, sizeof(spline)); + nextDynOffs = alignOffset(nextDynOffs + sizeof(spline)); + LOGP(debug, "Copy {} bytes for spline scenario {} (ptr:{}) to offsset {}", sizeof(spline), isc, (void*)&spline, scenOffs[isc]); + } + + // copy splines data + for (int is = 0; is < 3; is++) { + float* data = reinterpret_cast(buff + nextDynOffs); + LOGP(debug, "splinID={} start offset {} -> {}", is, nextDynOffs, (void*)data); + for (int sector = 0; sector < origCorr.mGeo.getNumberOfSectors(); sector++) { + podMap.mSplineDataOffsets[sector][is] = nextDynOffs; + size_t rowDataOffs = 0; + for (int row = 0; row < NROWS; row++) { + const auto& spline = origCorr.getSpline(sector, row); + const float* dataOr = origCorr.getCorrectionData(sector, row, is); + int nPar = spline.getNumberOfParameters(); + if (is == 1) { + nPar = nPar / 3; + } + if (is == 2) { + nPar = nPar * 2 / 3; + } + LOGP(debug, "Copying {} floats for spline{} of sector:{} row:{} to offset {}", nPar, is, sector, row, nextDynOffs); + size_t nbcopy = nPar * sizeof(float); + if (buffSize < nextDynOffs + nbcopy) { + throw std::runtime_error(fmt::format("attempt to copy {} bytes of data for spline{} of sector{}/row{} to {}, overflowing the buffer of size {}", nbcopy, is, sector, row, nextDynOffs, buffSize)); + } + std::memcpy(data, dataOr, nbcopy); + podMap.getSectorRowInfo(sector, row).dataOffsetBytes[is] = rowDataOffs; + rowDataOffs += nbcopy; + data += nPar; + nextDynOffs += nbcopy; + } + } + } + podMap.mTotalSize = alignOffset(nextDynOffs); + if (buffSize != podMap.mTotalSize) { + throw std::runtime_error(fmt::format("Estimated buffer size {} differs from filled one {}", buffSize, podMap.mTotalSize)); + } + return &getNonConst(buff); +} + +TPCFastTransformPOD* TPCFastTransformPOD::create(char* buff, size_t buffSize, const TPCFastTransform& src) +{ + // instantiate objec to already created buffer of the right size + auto podMap = create(buff, buffSize, src.getCorrection()); + // set data members of TPCFastTransform + podMap->mVdrift = src.getVDrift(); + podMap->mT0 = src.getT0(); + // copy fixed size data --- end + return podMap; +} + +bool TPCFastTransformPOD::test(const TPCFastSpaceChargeCorrection& origCorr, int npoints) const +{ + if (npoints < 1) { + return false; + } + std::vector sector, row; + std::vector y, z; + std::vector> corr0, corr1; + std::vector> corrInv0, corrInv1; + std::vector corrInvX0, corrInvX1; + + sector.reserve(npoints); + row.reserve(npoints); + y.reserve(npoints); + z.reserve(npoints); + corr0.reserve(npoints); + corr1.reserve(npoints); + corrInv0.reserve(npoints); + corrInv1.reserve(npoints); + corrInvX0.reserve(npoints); + corrInvX1.reserve(npoints); + + for (int i = 0; i < npoints; i++) { + sector.push_back(gRandom->Integer(NSECTORS)); + row.push_back(gRandom->Integer(NROWS)); + y.push_back(2 * (gRandom->Rndm() - 0.5) * mGeo.getRowInfo(row.back()).getYmax()); + z.push_back((sector.back() < NSECTORS / 2 ? 1.f : -1.f) * gRandom->Rndm() * 240); + } + long origStart[3], origEnd[3], thisStart[3], thisEnd[3]; + origStart[0] = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + for (int i = 0; i < npoints; i++) { + std::array val; + origCorr.getCorrectionLocal(sector[i], row[i], y[i], z[i], val[0], val[1], val[2]); + corr0.push_back(val); + } + + origEnd[0] = origStart[1] = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + for (int i = 0; i < npoints; i++) { + std::array val; + origCorr.getCorrectionYZatRealYZ(sector[i], row[i], y[i], z[i], val[0], val[1]); + corrInv0.push_back(val); + } + + origEnd[1] = origStart[2] = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + for (int i = 0; i < npoints; i++) { + corrInvX0.push_back(origCorr.getCorrectionXatRealYZ(sector[i], row[i], y[i], z[i])); + } + // + origEnd[2] = thisStart[0] = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + for (int i = 0; i < npoints; i++) { + std::array val; + this->getCorrectionLocal(sector[i], row[i], y[i], z[i], val[0], val[1], val[2]); + corr1.push_back(val); + } + thisEnd[0] = thisStart[1] = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + for (int i = 0; i < npoints; i++) { + std::array val; + this->getCorrectionYZatRealYZ(sector[i], row[i], y[i], z[i], val[0], val[1]); + corrInv1.push_back(val); + } + + thisEnd[1] = thisStart[2] = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + for (int i = 0; i < npoints; i++) { + corrInvX1.push_back(this->getCorrectionXatRealYZ(sector[i], row[i], y[i], z[i])); + } + thisEnd[2] = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + // + size_t ndiff[3] = {}; + for (int i = 0; i < npoints; i++) { + if (corr0[i][0] != corr1[i][0] || corr0[i][1] != corr1[i][1] || corr0[i][2] != corr1[i][2]) { + ndiff[0]++; + } + if (corrInv0[i][0] != corrInv1[i][0] || corrInv0[i][1] != corrInv1[i][1]) { + ndiff[1]++; + } + if (corrInvX0[i] != corrInvX1[i]) { + ndiff[2]++; + } + } + // + LOGP(info, " (ns per call) original this Nmissmatch"); + LOGP(info, "getCorrection {:.3e} {:.3e} {}", double(origEnd[0] - origStart[0]) / npoints * 1000., double(thisEnd[0] - thisStart[0]) / npoints * 1000., ndiff[0]); + LOGP(info, "getCorrectionInvCorrectedX {:.3e} {:.3e} {}", double(origEnd[1] - origStart[1]) / npoints * 1000., double(thisEnd[1] - thisStart[1]) / npoints * 1000., ndiff[1]); + LOGP(info, "getCorrectionInvUV {:.3e} {:.3e} {}", double(origEnd[2] - origStart[2]) / npoints * 1000., double(thisEnd[2] - thisStart[2]) / npoints * 1000., ndiff[2]); + return ndiff[0] == 0 && ndiff[1] == 0 && ndiff[2] == 0; +} + +#endif + +} // namespace gpu +} // namespace o2 diff --git a/GPU/TPCFastTransformation/TPCFastTransformPOD.h b/GPU/TPCFastTransformation/TPCFastTransformPOD.h new file mode 100644 index 0000000000000..ca54a74115068 --- /dev/null +++ b/GPU/TPCFastTransformation/TPCFastTransformPOD.h @@ -0,0 +1,916 @@ +// 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 TPCFastTransformPOD.h +/// \brief POD correction map +/// +/// \author ruben.shahoayn@cern.ch + +#ifndef ALICEO2_GPU_TPCFastTransformPOD_H +#define ALICEO2_GPU_TPCFastTransformPOD_H + +#include "GPUCommonRtypes.h" +#include "TPCFastTransform.h" + +/* +Binary buffer should be cast to TPCFastTransformPOD class using static TPCFastTransformPOD& t = get(buffer); method, +so that its head becomes `this` pointer of the object. + +First we have all the fixed size data members mentioned explicitly. Part of them is duplicating fixed size +data members of TPCFastSpaceChargeCorrection but those starting with mOffs... provide the offset in bytes +(wrt this) for dynamic data which cannot be declared as data member explicitly (since we cannot have any +pointer except `this`) but obtained via getters using stored offsets wrt `this`. +This is followed dynamic part itself. + +dynamic part layout: +1) size_t[ mNumberOfScenarios ] array starting at offset mOffsScenariosOffsets, each element is the offset +of distict spline object (scenario in TPCFastSpaceChargeCorrection) +2) size_t[ mNSplineIDs ] array starting at offset mOffsSplineDataOffsets, each element is the offset of the +beginning of splines data for give splineID + +*/ + +namespace o2 +{ +namespace gpu +{ +class TPCFastTransformPOD +{ + public: + using SliceInfo = TPCFastSpaceChargeCorrection::SliceInfo; // obsolete + using GridInfo = TPCFastSpaceChargeCorrection::GridInfo; + using SectorRowInfo = TPCFastSpaceChargeCorrection::SectorRowInfo; + + using SplineTypeXYZ = TPCFastSpaceChargeCorrection::SplineTypeXYZ; + using SplineTypeInvX = TPCFastSpaceChargeCorrection::SplineTypeInvX; + using SplineTypeInvYZ = TPCFastSpaceChargeCorrection::SplineTypeInvYZ; + using SplineType = TPCFastSpaceChargeCorrection::SplineType; + + /// convert prefilled buffer to TPCFastTransformPOD + GPUd() static const TPCFastTransformPOD& get(const char* head) { return *reinterpret_cast(head); } + + /// _______________ high level methods a la TPCFastTransform _______________________ + /// + // Methods taking extra reference transform are legacy compound transforms used to scale corrections. + GPUd() void Transform(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime = 0, const TPCFastTransformPOD* ref = nullptr, const TPCFastTransformPOD* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; + GPUd() void TransformXYZ(int32_t sector, int32_t row, float& x, float& y, float& z, const TPCFastTransformPOD* ref = nullptr, const TPCFastTransformPOD* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; + + GPUd() void Transform_new(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime = 0) const; + GPUd() void TransformXYZ_new(int32_t sector, int32_t row, float& x, float& y, float& z) const; + + /// Transformation in the time frame + GPUd() void TransformInTimeFrame(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float maxTimeBin) const; + GPUd() void TransformInTimeFrame(int32_t sector, float time, float& z, float maxTimeBin) const; + + /// Inverse transformation + GPUd() void InverseTransformInTimeFrame(int32_t sector, int32_t row, float /*x*/, float y, float z, float& pad, float& time, float maxTimeBin) const; + GPUd() float InverseTransformInTimeFrame(int32_t sector, float z, float maxTimeBin) const; + + /// Inverse transformation: Transformed Y and Z -> transformed X + GPUd() void InverseTransformYZtoX(int32_t sector, int32_t row, float y, float z, float& x, const TPCFastTransformPOD* ref = nullptr, const TPCFastTransformPOD* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; + GPUd() void InverseTransformYZtoX_new(int32_t sector, int32_t row, float y, float z, float& x) const; + + /// Inverse transformation: Transformed Y and Z -> Y and Z, transformed w/o space charge correction + GPUd() void InverseTransformYZtoNominalYZ(int32_t sector, int32_t row, float y, float z, float& ny, float& nz, const TPCFastTransformPOD* ref = nullptr, const TPCFastTransformPOD* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; + GPUd() void InverseTransformYZtoNominalYZ_new(int32_t sector, int32_t row, float y, float z, float& ny, float& nz) const; + + /// Inverse transformation: Transformed X, Y and Z -> X, Y and Z, transformed w/o space charge correction + GPUd() void InverseTransformXYZtoNominalXYZ(int32_t sector, int32_t row, float x, float y, float z, float& nx, float& ny, float& nz, const TPCFastTransformPOD* ref = nullptr, const TPCFastTransformPOD* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; + GPUd() void InverseTransformXYZtoNominalXYZ_new(int32_t sector, int32_t row, float x, float y, float z, float& nx, float& ny, float& nz) const; + + /// Ideal transformation with Vdrift only - without calibration + GPUd() void TransformIdeal(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime) const; + GPUd() void TransformIdealZ(int32_t sector, float time, float& z, float vertexTime) const; + + GPUd() void convPadTimeToLocal(int32_t sector, int32_t row, float pad, float time, float& y, float& z, float vertexTime) const; + GPUd() void convPadTimeToLocalInTimeFrame(int32_t sector, int32_t row, float pad, float time, float& y, float& z, float maxTimeBin) const; + + GPUd() void convLocalToPadTime(int32_t sector, int32_t row, float y, float z, float& pad, float& time, float vertexTime) const; + GPUd() void convLocalToPadTimeInTimeFrame(int32_t sector, int32_t row, float y, float z, float& pad, float& time, float maxTimeBin) const; + + GPUd() float convTimeToZinTimeFrame(int32_t sector, float time, float maxTimeBin) const; + GPUd() float convZtoTimeInTimeFrame(int32_t sector, float z, float maxTimeBin) const; + GPUd() float convDeltaTimeToDeltaZinTimeFrame(int32_t sector, float deltaTime) const; + GPUd() float convDeltaZtoDeltaTimeInTimeFrame(int32_t sector, float deltaZ) const; + GPUd() float convDeltaZtoDeltaTimeInTimeFrameAbs(float deltaZ) const; + GPUd() float convZOffsetToVertexTime(int32_t sector, float zOffset, float maxTimeBin) const; + GPUd() float convVertexTimeToZOffset(int32_t sector, float vertexTime, float maxTimeBin) const; + + /// _______________ methods a la TPCFastSpaceChargeCorrection: cluster correction _______________________ + void setApplyCorrectionOn() { mApplyCorrection = 1; } + void setApplyCorrectionOff() { mApplyCorrection = 0; } + bool isCorrectionApplied() { return mApplyCorrection; } + + /// TPC geometry information + GPUd() const TPCFastTransformGeo& getGeometry() const { return mGeo; } + + /// Gives TPC sector & row info + GPUd() const SectorRowInfo& getSectorRowInfo(int32_t sector, int32_t row) const { return mSectorRowInfos[NROWS * sector + row]; } + + /// Gives TPC sector & row info + GPUd() SectorRowInfo& getSectorRowInfo(int32_t sector, int32_t row) { return mSectorRowInfos[NROWS * sector + row]; } + + /// Gives its own size including dynamic part + GPUd() size_t size() const { return mTotalSize; } + + /// Gives the time stamp of the current calibaration parameters + GPUd() long int getTimeStamp() const { return mTimeStamp; } + + /// Return mVDrift in cm / time bin + GPUd() float getVDrift() const { return mVdrift; } + + /// Return T0 in time bin units + GPUd() float getT0() const { return mT0; } + + /// Return IDC estimator + GPUd() float getIDC() const { return mIDC; } + + /// Return Lumi estimator + GPUd() float getLumi() const { return mLumi; } + + /// maximal possible drift time of the active area + GPUd() float getMaxDriftTime(int32_t sector, int32_t row, float pad) const; + + /// maximal possible drift time of the active area + GPUd() float getMaxDriftTime(int32_t sector, int32_t row) const; + + /// maximal possible drift time of the active area + GPUd() float getMaxDriftTime(int32_t sector) const; + + /// Sets the time stamp of the current calibaration + GPUd() void setTimeStamp(long int v) { mTimeStamp = v; } + + /// Sets current vdrift + GPUd() void setVDrift(float v) { mVdrift = v; } + + /// Sets current T0 + GPUd() void setT0(float v) { mT0 = v; } + + /// Sets IDC estimator + GPUd() void setIDC(float v) { mIDC = v; } + + /// Sets CTP Lumi estimator + GPUd() void setLumi(float v) { mLumi = v; } + + /// Gives a reference to a spline + GPUd() const SplineType& getSpline(int32_t sector, int32_t row) const { return *reinterpret_cast(getThis() + getScenarioOffset(getSectorRowInfo(sector, row).splineScenarioID)); } + + /// Gives pointer to spline data + GPUd() const float* getCorrectionData(int32_t sector, int32_t row, int32_t iSpline = 0) const { return reinterpret_cast(getThis() + mSplineDataOffsets[sector][iSpline] + getSectorRowInfo(sector, row).dataOffsetBytes[iSpline]); } + + /// Gives const pointer to a spline for the inverse X correction + GPUd() const SplineTypeInvX& getSplineInvX(int32_t sector, int32_t row) const { return reinterpret_cast(getSpline(sector, row)); } + + /// Gives pointer to spline data for the inverse X correction + GPUd() const float* getCorrectionDataInvX(int32_t sector, int32_t row) const { return getCorrectionData(sector, row, 1); } + + /// Gives const pointer to a spline for the inverse YZ correction + GPUd() const SplineTypeInvYZ& getSplineInvYZ(int32_t sector, int32_t row) const { return reinterpret_cast(getSpline(sector, row)); } + + /// Gives pointer to spline data for the inverse YZ correction + GPUd() const float* getCorrectionDataInvYZ(int32_t sector, int32_t row) const { return getCorrectionData(sector, row, 2); } + + /// _______________ The main method: cluster correction _______________________ + GPUdi() void getCorrectionLocal(int32_t sector, int32_t row, float y, float z, float& dx, float& dy, float& dz) const; + + /// inverse correction: Real Y and Z -> Real X + GPUd() float getCorrectionXatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const; + + /// inverse correction: Real Y and Z -> measred Y and Z + GPUd() void getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ, float& measuredY, float& measuredZ) const; + + /// transformation in the sector local frame + GPUd() void TransformLocal(int32_t sector, int32_t row, float& x, float& y, float& z, const TPCFastTransformPOD* ref, const TPCFastTransformPOD* ref2, float scale, float scale2, int32_t scaleMode) const; + GPUd() void TransformLocal(int32_t sector, int32_t row, float& x, float& y, float& z) const; + + /// _______________ Utilities _______________________________________________ + + /// convert local y, z to internal grid coordinates u,v + /// return values: u, v, scaling factor + GPUd() void convLocalToGrid(int32_t sector, int32_t row, float y, float z, float& u, float& v, float& s) const; + + /// convert internal grid coordinates u,v to local y, z + /// return values: y, z, scaling factor + GPUd() void convGridToLocal(int32_t sector, int32_t row, float u, float v, float& y, float& z) const; + + /// convert real Y, Z to the internal grid coordinates + /// return values: u, v, scaling factor + GPUd() void convRealLocalToGrid(int32_t sector, int32_t row, float y, float z, float& u, float& v, float& s) const; + + /// convert internal grid coordinates to the real Y, Z + /// return values: y, z + GPUd() void convGridToRealLocal(int32_t sector, int32_t row, float u, float v, float& y, float& z) const; + + GPUd() bool isLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const; + GPUd() bool isRealLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const; + +#if !defined(GPUCA_GPUCODE) + /// Create POD transform from old flat-buffer one. Provided vector will serve as a buffer + template + static TPCFastTransformPOD* create(V& destVector, const TPCFastTransform& src); + + /// create filling only part corresponding to TPCFastSpaceChargeCorrection. Data members coming from TPCFastTransform (e.g. VDrift, T0..) are not set + template + static TPCFastTransformPOD* create(V& destVector, const TPCFastSpaceChargeCorrection& src); + + bool test(const TPCFastTransform& src, int32_t npoints = 100000) const { return test(src.getCorrection(), npoints); } + bool test(const TPCFastSpaceChargeCorrection& origCorr, int32_t npoints = 100000) const; +#endif + + /// Print method + void print() const; + + GPUd() float convDriftLengthToTime(float driftLength, float vertexTime) const; + + static constexpr int NROWS = 152; + static constexpr int NSECTORS = TPCFastTransformGeo::getNumberOfSectors(); + static constexpr int NSplineIDs = 3; ///< number of spline data sets for each sector/row + + private: +#if !defined(GPUCA_GPUCODE) + static constexpr size_t AlignmentBytes = 8; + static size_t alignOffset(size_t offs) + { + auto res = offs % AlignmentBytes; + return res ? offs + (AlignmentBytes - res) : offs; + } + static size_t estimateSize(const TPCFastTransform& src) { return estimateSize(src.getCorrection()); } + static size_t estimateSize(const TPCFastSpaceChargeCorrection& origCorr); + static TPCFastTransformPOD* create(char* buff, size_t buffSize, const TPCFastTransform& src); + static TPCFastTransformPOD* create(char* buff, size_t buffSize, const TPCFastSpaceChargeCorrection& src); + ///< get address to which the offset in bytes must be added to arrive to particular dynamic part + GPUd() const char* getThis() const { return reinterpret_cast(this); } + GPUd() static TPCFastTransformPOD& getNonConst(char* head) { return *reinterpret_cast(head); } +#endif + + ///< return offset of the spline object start (equivalent of mScenarioPtr in the TPCFastSpaceChargeCorrection) + GPUd() size_t getScenarioOffset(int s) const { return (reinterpret_cast(getThis() + mOffsScenariosOffsets))[s]; } + + bool mApplyCorrection{}; ///< flag to apply corrections + int mNumberOfScenarios{}; ///< Number of approximation spline scenarios + size_t mTotalSize{}; ///< total size of the buffer + size_t mOffsScenariosOffsets{}; ///< start of the array of mNumberOfScenarios offsets for each type of spline + size_t mSplineDataOffsets[TPCFastTransformGeo::getNumberOfSectors()][NSplineIDs]; ///< start of data for each sector and iSpline data + long int mTimeStamp{}; ///< time stamp of the current calibration + float mT0; ///< T0 in [time bin] + float mVdrift; ///< VDrift in [cm/time bin] + float mLumi; ///< luminosity estimator (for info only) + float mIDC; ///< IDC estimator (for info only) + + TPCFastTransformGeo mGeo; ///< TPC geometry information + SectorRowInfo mSectorRowInfos[NROWS * TPCFastTransformGeo::getNumberOfSectors()]; + + ClassDefNV(TPCFastTransformPOD, 0); +}; + +GPUdi() void TPCFastTransformPOD::getCorrectionLocal(int32_t sector, int32_t row, float y, float z, float& dx, float& dy, float& dz) const +{ + const auto& info = getSectorRowInfo(sector, row); + const SplineType& spline = getSpline(sector, row); + const float* splineData = getCorrectionData(sector, row); + + float u, v, s; + convLocalToGrid(sector, row, y, z, u, v, s); + + float dxyz[3]; + spline.interpolateAtU(splineData, u, v, dxyz); + + if (CAMath::Abs(dxyz[0]) > 100.f || CAMath::Abs(dxyz[1]) > 100.f || CAMath::Abs(dxyz[2]) > 100.f) { + s = 0.f; // TODO: DR: Protect from FPEs, fix upstream and remove once guaranteed that it is fixed + } + + dx = s * GPUCommonMath::Clamp(dxyz[0], info.minCorr[0], info.maxCorr[0]); + dy = s * GPUCommonMath::Clamp(dxyz[1], info.minCorr[1], info.maxCorr[1]); + dz = s * GPUCommonMath::Clamp(dxyz[2], info.minCorr[2], info.maxCorr[2]); +} + +GPUdi() float TPCFastTransformPOD::getCorrectionXatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const +{ + const auto& info = getSectorRowInfo(sector, row); + float u, v, s; + convRealLocalToGrid(sector, row, realY, realZ, u, v, s); + float dx = 0; + getSplineInvX(sector, row).interpolateAtU(getCorrectionDataInvX(sector, row), u, v, &dx); + if (CAMath::Abs(dx) > 100.f) { + s = 0.f; // TODO: DR: Protect from FPEs, fix upstream and remove once guaranteed that it is fixed + } + dx = s * GPUCommonMath::Clamp(dx, info.minCorr[0], info.maxCorr[0]); + return dx; +} + +GPUdi() void TPCFastTransformPOD::getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ, float& y, float& z) const +{ + float u, v, s; + convRealLocalToGrid(sector, row, realY, realZ, u, v, s); + const auto& info = getSectorRowInfo(sector, row); + float dyz[2]; + getSplineInvYZ(sector, row).interpolateAtU(getCorrectionDataInvYZ(sector, row), u, v, dyz); + if (CAMath::Abs(dyz[0]) > 100.f || CAMath::Abs(dyz[1]) > 100.f) { + s = 0.f; // TODO: DR: Protect from FPEs, fix upstream and remove once guaranteed that it is fixed + } + y = s * GPUCommonMath::Clamp(dyz[0], info.minCorr[1], info.maxCorr[1]); + z = s * GPUCommonMath::Clamp(dyz[1], info.minCorr[2], info.maxCorr[2]); +} + +GPUdi() void TPCFastTransformPOD::convLocalToGrid(int32_t sector, int32_t row, float y, float z, float& u, float& v, float& s) const +{ + /// convert local y, z to internal grid coordinates u,v + /// return values: u, v, scaling factor + const SplineType& spline = getSpline(sector, row); + getSectorRowInfo(sector, row).gridMeasured.convLocalToGridUntruncated(y, z, u, v, s); + // shrink to the grid + u = GPUCommonMath::Clamp(u, 0.f, (float)spline.getGridX1().getUmax()); + v = GPUCommonMath::Clamp(v, 0.f, (float)spline.getGridX2().getUmax()); +} + +GPUdi() void TPCFastTransformPOD::convGridToLocal(int32_t sector, int32_t row, float gridU, float gridV, float& y, float& z) const +{ + /// convert internal grid coordinates u,v to local y, z + getSectorRowInfo(sector, row).gridMeasured.convGridToLocal(gridU, gridV, y, z); +} + +GPUdi() void TPCFastTransformPOD::convRealLocalToGrid(int32_t sector, int32_t row, float y, float z, float& u, float& v, float& s) const +{ + /// convert real y, z to the internal grid coordinates + scale + const SplineType& spline = getSpline(sector, row); + getSectorRowInfo(sector, row).gridReal.convLocalToGridUntruncated(y, z, u, v, s); + // shrink to the grid + u = GPUCommonMath::Clamp(u, 0.f, (float)spline.getGridX1().getUmax()); + v = GPUCommonMath::Clamp(v, 0.f, (float)spline.getGridX2().getUmax()); +} + +GPUdi() void TPCFastTransformPOD::convGridToRealLocal(int32_t sector, int32_t row, float gridU, float gridV, float& y, float& z) const +{ + /// convert internal grid coordinates u,v to the real y, z + getSectorRowInfo(sector, row).gridReal.convGridToLocal(gridU, gridV, y, z); +} + +GPUdi() bool TPCFastTransformPOD::isLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const +{ + /// check if local y, z are inside the grid + float u, v, s; + getSectorRowInfo(sector, row).gridMeasured.convLocalToGridUntruncated(y, z, u, v, s); + const auto& spline = getSpline(sector, row); + // shrink to the grid + if (u < 0.f || u > (float)spline.getGridX1().getUmax() || // + v < 0.f || v > (float)spline.getGridX2().getUmax()) { + return false; + } + return true; +} + +GPUdi() bool TPCFastTransformPOD::isRealLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const +{ + /// check if local y, z are inside the grid + float u, v, s; + getSectorRowInfo(sector, row).gridReal.convLocalToGridUntruncated(y, z, u, v, s); + const auto& spline = getSpline(sector, row); + // shrink to the grid + if (u < 0.f || u > (float)spline.getGridX1().getUmax() || // + v < 0.f || v > (float)spline.getGridX2().getUmax()) { + return false; + } + return true; +} + +#if !defined(GPUCA_GPUCODE) +/// Create POD transform from old flat-buffer one. Provided vector will serve as a buffer +template +TPCFastTransformPOD* TPCFastTransformPOD::create(V& destVector, const TPCFastTransform& src) +{ + const auto& origCorr = src.getCorrection(); + size_t estSize = estimateSize(src); + destVector.resize(estSize); // allocate exact size + LOGP(debug, "OrigCorrSize:{} SelfSize: {} Estimated POS size: {}", src.getCorrection().getFlatBufferSize(), sizeof(TPCFastTransformPOD), estSize); + char* base = destVector.data(); + auto res = create(destVector.data(), destVector.size(), src); + res->setTimeStamp(src.getTimeStamp()); + res->setVDrift(src.getVDrift()); + res->setT0(src.getT0()); + res->setLumi(src.getLumi()); + if (src.isIDCSet()) { + res->setIDC(src.getIDC()); + } + return res; +} + +template +TPCFastTransformPOD* TPCFastTransformPOD::create(V& destVector, const TPCFastSpaceChargeCorrection& origCorr) +{ + // create filling only part corresponding to TPCFastSpaceChargeCorrection. Data members coming from TPCFastTransform (e.g. VDrift, T0..) are not set + size_t estSize = estimateSize(origCorr); + destVector.resize(estSize); // allocate exact size + LOGP(debug, "OrigCorrSize:{} SelfSize: {} Estimated POS size: {}", origCorr.getFlatBufferSize(), sizeof(TPCFastTransformPOD), estSize); + char* base = destVector.data(); + return create(destVector.data(), destVector.size(), origCorr); +} +#endif + +GPUdi() void TPCFastTransformPOD::TransformLocal(int32_t sector, int32_t row, float& x, float& y, float& z, const TPCFastTransformPOD* ref, const TPCFastTransformPOD* ref2, float scale, float scale2, int32_t scaleMode) const +{ + GPUCA_RTC_SPECIAL_CODE(ref2 = nullptr; scale2 = 0.f;); + + if (!mApplyCorrection) { + return; + } + + float dx = 0.f, dy = 0.f, dz = 0.f; + + if ((scale >= 0.f) || (scaleMode == 1) || (scaleMode == 2)) { + getCorrectionLocal(sector, row, y, z, dx, dy, dz); + if (ref) { + if ((scale > 0.f) && (scaleMode == 0)) { // scaling was requested + float val[3]; + ref->getCorrectionLocal(sector, row, y, z, val[0], val[1], val[2]); + dx = (dx - val[0]) * scale + val[0]; + dy = (dy - val[1]) * scale + val[1]; + dz = (dz - val[2]) * scale + val[2]; + } else if ((scale != 0.f) && ((scaleMode == 1) || (scaleMode == 2))) { + float val[3]; + ref->getCorrectionLocal(sector, row, y, z, val[0], val[1], val[2]); + dx = val[0] * scale + dx; + dy = val[1] * scale + dy; + dz = val[2] * scale + dz; + } + } + if (ref2 && (scale2 != 0)) { + float val[3]; + ref2->getCorrectionLocal(sector, row, y, z, val[0], val[1], val[2]); + dx = val[0] * scale2 + dx; + dy = val[1] * scale2 + dy; + dz = val[2] * scale2 + dz; + } + } + + GPUCA_DEBUG_STREAMER_CHECK(if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamFastTransform)) { + float lx = x, ly = y, lz = z; + + float gx, gy, gz; + getGeometry().convLocalToGlobal(sector, lx, ly, lz, gx, gy, gz); + + float lxT = lx + dx; + float lyT = ly + dy; + float lzT = lz + dz; + + float invYZtoXScaled; + InverseTransformYZtoX(sector, row, lyT, lzT, invYZtoXScaled, ref, ref2, scale, scale2, scaleMode); + + float invYZtoX; + InverseTransformYZtoX(sector, row, lyT, lzT, invYZtoX); + + float YZtoNominalY; + float YZtoNominalZ; + InverseTransformYZtoNominalYZ(sector, row, lyT, lzT, YZtoNominalY, YZtoNominalZ); + + float YZtoNominalYScaled; + float YZtoNominalZScaled; + InverseTransformYZtoNominalYZ(sector, row, lyT, lzT, YZtoNominalYScaled, YZtoNominalZScaled, ref, ref2, scale, scale2, scaleMode); + + float dxRef = 0.f, dyRef = 0.f, dzRef = 0.f; + if (ref) { + ref->getCorrectionLocal(sector, row, y, z, dxRef, dyRef, dzRef); + } + + float dxRef2 = 0.f, dyRef2 = 0.f, dzRef2 = 0.f; + if (ref2) { + ref2->getCorrectionLocal(sector, row, y, z, dxRef2, dyRef2, dzRef2); + } + + float dxOrig, dyOrig, dzOrig; + getCorrectionLocal(sector, row, y, z, dyOrig, dyOrig, dzOrig); + + o2::utils::DebugStreamer::instance()->getStreamer("debug_fasttransform", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_Transform").data() + // corrections in x, u, v + << "dxOrig=" << dxOrig + << "dyOrig=" << dyOrig + << "dzOrig=" << dzOrig + << "dxRef=" << dxRef + << "dyRef=" << dyRef + << "dzRef=" << dzRef + << "dxRef2=" << dxRef2 + << "dyRef2=" << dyRef2 + << "dzRef2=" << dzRef2 + << "dx=" << dx + << "dy=" << dy + << "dz=" << dz + << "row=" << row + << "sector=" << sector + << "scale=" << scale + << "scale2=" << scale2 + // original local coordinates + << "ly=" << ly + << "lz=" << lz + << "lx=" << lx + // corrected local coordinated + << "lxT=" << lxT + << "lyT=" << lyT + << "lzT=" << lzT + // global uncorrected coordinates + << "gx=" << gx + << "gy=" << gy + << "gz=" << gz + // some transformations which are applied + << "invYZtoX=" << invYZtoX + << "invYZtoXScaled=" << invYZtoXScaled + << "YZtoNominalY=" << YZtoNominalY + << "YZtoNominalYScaled=" << YZtoNominalYScaled + << "YZtoNominalZ=" << YZtoNominalZ + << "YZtoNominalZScaled=" << YZtoNominalZScaled + << "scaleMode=" << scaleMode + << "\n"; + }) + + x += dx; + y += dy; + z += dz; +} + +GPUdi() void TPCFastTransformPOD::TransformLocal(int32_t sector, int32_t row, float& x, float& y, float& z) const +{ + if (!mApplyCorrection) { + return; + } + float dx, dy, dz; + getCorrectionLocal(sector, row, y, z, dx, dy, dz); + + GPUCA_DEBUG_STREAMER_CHECK(if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamFastTransform)) { + float lx = x, ly = y, lz = z; + float gx, gy, gz; + getGeometry().convLocalToGlobal(sector, lx, ly, lz, gx, gy, gz); + float lxT = lx + dx; + float lyT = ly + dy; + float lzT = lz + dz; + float invYZtoX; + InverseTransformYZtoX_new(sector, row, lyT, lzT, invYZtoX); + + float YZtoNominalY; + float YZtoNominalZ; + InverseTransformYZtoNominalYZ_new(sector, row, lyT, lzT, YZtoNominalY, YZtoNominalZ); + + o2::utils::DebugStreamer::instance()->getStreamer("debug_fasttransform", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_Transform").data() + // corrections in x, u, v + << "dx=" << dx + << "dy=" << dy + << "dz=" << dz + << "row=" << row + << "sector=" << sector + // original local coordinates + << "ly=" << ly + << "lz=" << lz + << "lx=" << lx + // corrected local coordinated + << "lxT=" << lxT + << "lyT=" << lyT + << "lzT=" << lzT + // global uncorrected coordinates + << "gx=" << gx + << "gy=" << gy + << "gz=" << gz + // some transformations which are applied + << "invYZtoX=" << invYZtoX + << "YZtoNominalY=" << YZtoNominalY + << "YZtoNominalZ=" << YZtoNominalZ + << "\n"; + }) + + x += dx; + y += dy; + z += dz; +} + +GPUdi() void TPCFastTransformPOD::Transform(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime, const TPCFastTransformPOD* ref, const TPCFastTransformPOD* ref2, float scale, float scale2, int32_t scaleMode) const +{ + /// _______________ The main method: cluster transformation _______________________ + /// + /// Transforms raw TPC coordinates to local XYZ withing a sector + /// taking calibration into account. + /// + + const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); + + x = rowInfo.x; + convPadTimeToLocal(sector, row, pad, time, y, z, vertexTime); + TransformLocal(sector, row, x, y, z, ref, ref2, scale, scale2, scaleMode); +} + +GPUdi() void TPCFastTransformPOD::Transform_new(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime) const +{ + /// _______________ The main method: cluster transformation _______________________ + /// + /// Transforms raw TPC coordinates to local XYZ withing a sector + /// taking calibration into account. + /// + + const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); + + x = rowInfo.x; + convPadTimeToLocal(sector, row, pad, time, y, z, vertexTime); + TransformLocal(sector, row, x, y, z); +} + +GPUdi() void TPCFastTransformPOD::TransformXYZ(int32_t sector, int32_t row, float& x, float& y, float& z, const TPCFastTransformPOD* ref, const TPCFastTransformPOD* ref2, float scale, float scale2, int32_t scaleMode) const +{ + + TransformLocal(sector, row, x, y, z, ref, ref2, scale, scale2, scaleMode); +} + +GPUdi() void TPCFastTransformPOD::TransformXYZ_new(int32_t sector, int32_t row, float& x, float& y, float& z) const +{ + + TransformLocal(sector, row, x, y, z); +} + +GPUdi() void TPCFastTransformPOD::TransformInTimeFrame(int32_t sector, float time, float& z, float maxTimeBin) const +{ + float l = (time - mT0 - maxTimeBin) * mVdrift; // drift length cm + z = getGeometry().convDriftLengthToZ1(sector, l); +} + +GPUdi() void TPCFastTransformPOD::TransformInTimeFrame(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float maxTimeBin) const +{ + /// _______________ Special cluster transformation for a time frame _______________________ + /// + /// Same as Transform(), but clusters are shifted in z such, that Z(maxTimeBin)==0 + /// Corrections and Time-Of-Flight correction are not alpplied. + /// + + const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); + x = rowInfo.x; + convPadTimeToLocalInTimeFrame(sector, row, pad, time, y, z, maxTimeBin); +} + +GPUdi() void TPCFastTransformPOD::InverseTransformInTimeFrame(int32_t sector, int32_t row, float /*x*/, float y, float z, float& pad, float& time, float maxTimeBin) const +{ + /// Inverse transformation to TransformInTimeFrame + convLocalToPadTimeInTimeFrame(sector, row, y, z, pad, time, maxTimeBin); +} + +GPUdi() float TPCFastTransformPOD::InverseTransformInTimeFrame(int32_t sector, float z, float maxTimeBin) const +{ + float pad, time; + InverseTransformInTimeFrame(sector, 0, 0, 0, z, pad, time, maxTimeBin); + return time; +} + +GPUdi() void TPCFastTransformPOD::TransformIdealZ(int32_t sector, float time, float& z, float vertexTime) const +{ + /// _______________ The main method: cluster transformation _______________________ + /// + /// Transforms time TPC coordinates to local Z withing a sector + /// Ideal transformation: only Vdrift from DCS. + /// No space charge corrections, no time of flight correction + /// + + float l = (time - mT0 - vertexTime) * mVdrift; // drift length cm + z = getGeometry().convDriftLengthToZ1(sector, l); +} + +GPUdi() void TPCFastTransformPOD::TransformIdeal(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime) const +{ + /// _______________ The main method: cluster transformation _______________________ + /// + /// Transforms raw TPC coordinates to local XYZ withing a sector + /// Ideal transformation: only Vdrift from DCS. + /// No space charge corrections, no time of flight correction + /// + + x = getGeometry().getRowInfo(row).x; + float driftLength = (time - mT0 - vertexTime) * mVdrift; // drift length cm + getGeometry().convPadDriftLengthToLocal(sector, row, pad, driftLength, y, z); +} + +GPUdi() float TPCFastTransformPOD::convTimeToZinTimeFrame(int32_t sector, float time, float maxTimeBin) const +{ + /// _______________ Special cluster transformation for a time frame _______________________ + /// + /// Same as Transform(), but clusters are shifted in z such, that Z(maxTimeBin)==0 + /// Corrections and Time-Of-Flight correction are not alpplied. + /// Only Z coordinate. + /// + + float v = (time - mT0 - maxTimeBin) * mVdrift; // drift length cm + float z = (sector < getGeometry().getNumberOfSectorsA()) ? -v : v; + return z; +} + +GPUdi() float TPCFastTransformPOD::convZtoTimeInTimeFrame(int32_t sector, float z, float maxTimeBin) const +{ + /// Inverse transformation of convTimeToZinTimeFrame() + float v = (sector < getGeometry().getNumberOfSectorsA()) ? -z : z; + return mT0 + maxTimeBin + v / mVdrift; +} + +GPUdi() float TPCFastTransformPOD::convDeltaTimeToDeltaZinTimeFrame(int32_t sector, float deltaTime) const +{ + float deltaZ = deltaTime * mVdrift; + return sector < getGeometry().getNumberOfSectorsA() ? -deltaZ : deltaZ; +} + +GPUdi() float TPCFastTransformPOD::convDeltaZtoDeltaTimeInTimeFrameAbs(float deltaZ) const +{ + return deltaZ / mVdrift; +} + +GPUdi() float TPCFastTransformPOD::convDeltaZtoDeltaTimeInTimeFrame(int32_t sector, float deltaZ) const +{ + float deltaT = deltaZ / mVdrift; + return sector < getGeometry().getNumberOfSectorsA() ? -deltaT : deltaT; +} + +GPUdi() float TPCFastTransformPOD::getMaxDriftTime(int32_t sector, int32_t row, float pad) const +{ + /// maximal possible drift time of the active area + return convDriftLengthToTime(getGeometry().getTPCzLength(), 0.f); +} + +GPUdi() float TPCFastTransformPOD::getMaxDriftTime(int32_t sector, int32_t row) const +{ + /// maximal possible drift time of the active area + return convDriftLengthToTime(getGeometry().getTPCzLength(), 0.f); +} + +GPUdi() float TPCFastTransformPOD::getMaxDriftTime(int32_t sector) const +{ + /// maximal possible drift time of the active area + return convDriftLengthToTime(getGeometry().getTPCzLength(), 0.f); +} + +GPUdi() void TPCFastTransformPOD::InverseTransformYZtoX(int32_t sector, int32_t row, float realY, float realZ, float& realX, const TPCFastTransformPOD* ref, const TPCFastTransformPOD* ref2, float scale, float scale2, int32_t scaleMode) const +{ + GPUCA_RTC_SPECIAL_CODE(ref2 = nullptr; scale2 = 0.f;); + /// Transformation y,z -> x + + float dx = 0.f; + + if ((scale >= 0.f) || (scaleMode == 1) || (scaleMode == 2)) { + dx = getCorrectionXatRealYZ(sector, row, realY, realZ); + if (ref) { // scaling was requested + if (scaleMode == 0 && scale > 0.f) { + float dxref = ref->getCorrectionXatRealYZ(sector, row, realY, realZ); + dx = (dx - dxref) * scale + dxref; + } else if ((scale != 0) && ((scaleMode == 1) || (scaleMode == 2))) { + float dxref = ref->getCorrectionXatRealYZ(sector, row, realY, realZ); + dx = dxref * scale + dx; + } + } + if (ref2 && (scale2 != 0)) { + float dxref = ref2->getCorrectionXatRealYZ(sector, row, realY, realZ); + dx = dxref * scale2 + dx; + } + } + + realX = getGeometry().getRowInfo(row).x + dx; + + GPUCA_DEBUG_STREAMER_CHECK(if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamFastTransform)) { + o2::utils::DebugStreamer::instance()->getStreamer("debug_fasttransform", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_InverseTransformYZtoX").data() + << "sector=" << sector + << "row=" << row + << "scale=" << scale + << "y=" << realY + << "z=" << realZ + << "x=" << realX + << "\n"; + }) +} + +GPUdi() void TPCFastTransformPOD::InverseTransformYZtoX_new(int32_t sector, int32_t row, float realY, float realZ, float& realX) const +{ + /// Transformation y,z -> x + + float dx = 0.f; + + dx = getCorrectionXatRealYZ(sector, row, realY, realZ); + realX = getGeometry().getRowInfo(row).x + dx; + + GPUCA_DEBUG_STREAMER_CHECK(if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamFastTransform)) { + o2::utils::DebugStreamer::instance()->getStreamer("debug_fasttransform", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_InverseTransformYZtoX").data() + << "sector=" << sector + << "row=" << row + << "y=" << realY + << "z=" << realZ + << "x=" << realX + << "\n"; + }) +} + +GPUdi() void TPCFastTransformPOD::InverseTransformYZtoNominalYZ(int32_t sector, int32_t row, float realY, float realZ, float& measuredY, float& measuredZ, const TPCFastTransformPOD* ref, const TPCFastTransformPOD* ref2, float scale, float scale2, int32_t scaleMode) const +{ + /// Transformation real y,z -> measured y,z + + GPUCA_RTC_SPECIAL_CODE(ref2 = nullptr; scale2 = 0.f;); + + float dy = 0; + float dz = 0; + + if ((scale >= 0.f) || (scaleMode == 1) || (scaleMode == 2)) { + getCorrectionYZatRealYZ(sector, row, realY, realZ, dy, dz); + + if (ref) { // scaling was requested + if (scaleMode == 0 && scale > 0.f) { + float val[2]; + ref->getCorrectionYZatRealYZ(sector, row, realY, realZ, val[0], val[1]); + dy = (dy - val[0]) * scale + val[0]; + dz = (dz - val[1]) * scale + val[1]; + } else if ((scale != 0) && ((scaleMode == 1) || (scaleMode == 2))) { + float val[2]; + ref->getCorrectionYZatRealYZ(sector, row, realY, realZ, val[0], val[1]); + dy = val[0] * scale + dy; + dz = val[1] * scale + dz; + } + if (ref2 && (scale2 != 0)) { + float val[2]; + ref2->getCorrectionYZatRealYZ(sector, row, realY, realZ, val[0], val[1]); + dy = val[0] * scale2 + dy; + dz = val[1] * scale2 + dz; + } + } + } + + measuredY = realY - dy; + measuredZ = realZ - dz; + + GPUCA_DEBUG_STREAMER_CHECK(if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamFastTransform)) { + o2::utils::DebugStreamer::instance()->getStreamer("debug_fasttransform", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_InverseTransformYZtoNominalYZ").data() + << "sector=" << sector + << "row=" << row + << "scale=" << scale + << "real y=" << realY + << "real z=" << realZ + << "measured y=" << measuredY + << "measured z=" << measuredZ + << "\n"; + }) +} + +GPUdi() void TPCFastTransformPOD::InverseTransformYZtoNominalYZ_new(int32_t sector, int32_t row, float realY, float realZ, float& measuredY, float& measuredZ) const +{ + /// Transformation real y,z -> measured y,z + float corrY, corrZ; + getCorrectionYZatRealYZ(sector, row, realY, realZ, corrY, corrZ); + measuredY = realY - corrY; + measuredZ = realZ - corrZ; + + GPUCA_DEBUG_STREAMER_CHECK(if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamFastTransform)) { + o2::utils::DebugStreamer::instance()->getStreamer("debug_fasttransform", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_InverseTransformYZtoNominalYZ").data() + << "sector=" << sector + << "row=" << row + << "real y=" << realY + << "real z=" << realZ + << "measured y=" << measuredY + << "measured z=" << measuredZ + << "\n"; + }) +} + +GPUdi() void TPCFastTransformPOD::InverseTransformXYZtoNominalXYZ(int32_t sector, int32_t row, float x, float y, float z, float& nx, float& ny, float& nz, const TPCFastTransformPOD* ref, const TPCFastTransformPOD* ref2, float scale, float scale2, int32_t scaleMode) const +{ + /// Inverse transformation: Transformed X, Y and Z -> X, Y and Z, transformed w/o space charge correction + int32_t row2 = row + 1; + if (row2 >= getGeometry().getNumberOfRows()) { + row2 = row - 1; + } + float nx1, ny1, nz1; // nominal coordinates for row + float nx2, ny2, nz2; // nominal coordinates for row2 + nx1 = getGeometry().getRowInfo(row).x; + nx2 = getGeometry().getRowInfo(row2).x; + InverseTransformYZtoNominalYZ(sector, row, y, z, ny1, nz1, ref, ref2, scale, scale2, scaleMode); + InverseTransformYZtoNominalYZ(sector, row2, y, z, ny2, nz2, ref, ref2, scale, scale2, scaleMode); + float c1 = (nx2 - nx) / (nx2 - nx1); + float c2 = (nx - nx1) / (nx2 - nx1); + nx = x; + ny = (ny1 * c1 + ny2 * c2); + nz = (nz1 * c1 + nz2 * c2); +} + +GPUdi() void TPCFastTransformPOD::InverseTransformXYZtoNominalXYZ_new(int32_t sector, int32_t row, float x, float y, float z, float& nx, float& ny, float& nz) const +{ + /// Inverse transformation: Transformed X, Y and Z -> X, Y and Z, transformed w/o space charge correction + int32_t row2 = row + 1; + if (row2 >= getGeometry().getNumberOfRows()) { + row2 = row - 1; + } + float nx1, ny1, nz1; // nominal coordinates for row + float nx2, ny2, nz2; // nominal coordinates for row2 + nx1 = getGeometry().getRowInfo(row).x; + nx2 = getGeometry().getRowInfo(row2).x; + InverseTransformYZtoNominalYZ_new(sector, row, y, z, ny1, nz1); + InverseTransformYZtoNominalYZ_new(sector, row2, y, z, ny2, nz2); + float c1 = (nx2 - nx) / (nx2 - nx1); + float c2 = (nx - nx1) / (nx2 - nx1); + nx = x; + ny = (ny1 * c1 + ny2 * c2); + nz = (nz1 * c1 + nz2 * c2); +} + +} // namespace gpu +} // namespace o2 + +#endif diff --git a/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h b/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h index f1872549a46aa..0247bbbfbb65b 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h +++ b/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h @@ -93,5 +93,6 @@ #pragma link C++ struct o2::gpu::MultivariatePolynomialContainer + ; #pragma link C++ struct o2::gpu::NDPiecewisePolynomialContainer + ; #pragma link C++ struct o2::gpu::TPCSlowSpaceChargeCorrection + ; +#pragma link C++ class o2::gpu::TPCFastTransformPOD + ; #endif From 19d6617a61ce56f268cf373b608f6ff71eef43e2 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Fri, 3 Apr 2026 10:29:35 +0200 Subject: [PATCH 107/285] GPU FlatObject: Make some more functions accessible on GPU --- GPU/TPCFastTransformation/TPCFastTransformGeo.h | 8 ++++---- GPU/Utils/FlatObject.h | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.h b/GPU/TPCFastTransformation/TPCFastTransformGeo.h index 31b81e02c2d4c..6dd0e716c833b 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.h +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.h @@ -86,7 +86,7 @@ class TPCFastTransformGeo /// _____________ FlatObject functionality, see FlatObject class for description ____________ /// Gives minimal alignment in bytes required for an object of the class - static constexpr size_t getClassAlignmentBytes() { return 8; } + inline static constexpr size_t getClassAlignmentBytes() { return 8; } /// _______________ Construction interface ________________________ @@ -110,16 +110,16 @@ class TPCFastTransformGeo /// _______________ Getters _________________________________ /// Gives number of TPC sectors - GPUdi() static constexpr int32_t getNumberOfSectors() { return NumberOfSectors; } + inline static constexpr int32_t getNumberOfSectors() { return NumberOfSectors; } /// Gives number of TPC sectors on the A side - GPUdi() static constexpr int32_t getNumberOfSectorsA() { return NumberOfSectorsA; } + inline static constexpr int32_t getNumberOfSectorsA() { return NumberOfSectorsA; } /// Gives number of TPC rows GPUdi() int32_t getNumberOfRows() const { return mNumberOfRows; } /// Gives number of TPC rows - GPUdi() static constexpr int getMaxNumberOfRows() { return MaxNumberOfRows; } + inline static constexpr int getMaxNumberOfRows() { return MaxNumberOfRows; } /// Gives sector info GPUd() const SectorInfo& getSectorInfo(int32_t sector) const; diff --git a/GPU/Utils/FlatObject.h b/GPU/Utils/FlatObject.h index 8e13a8dedb868..46fdec7703823 100644 --- a/GPU/Utils/FlatObject.h +++ b/GPU/Utils/FlatObject.h @@ -253,10 +253,10 @@ class FlatObject void destroy(); /// Gives size of the flat buffer - size_t getFlatBufferSize() const { return mFlatBufferSize; } + GPUdi() size_t getFlatBufferSize() const { return mFlatBufferSize; } /// Gives pointer to the flat buffer - const char* getFlatBufferPtr() const { return mFlatBufferPtr; } + GPUdi() const char* getFlatBufferPtr() const { return mFlatBufferPtr; } /// Tells if the object is constructed bool isConstructed() const { return (mConstructionMask & (uint32_t)ConstructionState::Constructed); } @@ -274,7 +274,7 @@ class FlatObject public: /// Increases given size to achieve required alignment - static size_t alignSize(size_t sizeBytes, size_t alignmentBytes) + static constexpr size_t alignSize(size_t sizeBytes, size_t alignmentBytes) { auto res = sizeBytes % alignmentBytes; return res ? sizeBytes + (alignmentBytes - res) : sizeBytes; From a5b24a599e45470b60b3c94b085e752c3060459d Mon Sep 17 00:00:00 2001 From: Matthias Kleiner Date: Mon, 27 Oct 2025 11:11:59 +0100 Subject: [PATCH 108/285] TPC: centralize correction map building in TPCScalerSpec `TPCScalerSpec` now owns the full correction map pipeline: loads maps from CCDB, applies luminosity scaling and M-shape corrections, applies VDrift into the final map, and publishes a single merged `TPCFastTransformPOD` (`TPC/TPCCORRMAP`) plus instantaneous CTP lumi (`CTP/LUMICTP`) each TF. Downstream consumers (ITS-TPC matcher, TRD, TOF, secondary vertexing, GPU reco, etc.) are simplified: lumi scale options removed from constructors, `corrMapsLoader.updateVDrift()` / `corrMapsLoader.accountCCDBInputs()` / `corrMapsLoader.init()` calls dropped, and `requestCCDBInputs()` replaced by `requestInputs()` subscribing only to the two new outputs. `TPCScalerSpec` is now always added unconditionally. `CorrectionMapsHelper` is reduced to a thin wrapper around `TPCFastTransformPOD*`; full lumi-scaling state moves to the new `CorrectionMapsHelperFull` / `CorrectionMapsLoaderFull` used only inside `TPCScalerSpec`. `TPCFastTransformPOD` gains a flat buffer offset table and `interpolateAtUZeroCopy()` for correct spline evaluation on zero-copy shared memory paths. --- .../AlignmentWorkflow/BarrelAlignmentSpec.h | 2 +- .../Workflow/src/BarrelAlignmentSpec.cxx | 26 +- .../src/barrel-alignment-workflow.cxx | 7 +- Detectors/GlobalTracking/src/MatchTOF.cxx | 1 + Detectors/GlobalTracking/src/MatchTPCITS.cxx | 1 + .../CosmicsMatchingSpec.h | 2 +- .../SecondaryVertexingSpec.h | 2 +- .../GlobalTrackingWorkflow/TOFMatcherSpec.h | 2 +- .../TPCITSMatchingSpec.h | 2 +- .../src/CosmicsMatchingSpec.cxx | 23 +- .../src/SecondaryVertexingSpec.cxx | 26 +- .../src/TOFMatcherSpec.cxx | 25 +- .../src/TPCITSMatchingSpec.cxx | 25 +- .../src/cosmics-match-workflow.cxx | 6 +- .../src/secondary-vertexing-workflow.cxx | 6 +- .../src/tof-matcher-workflow.cxx | 6 +- .../src/tpcits-match-workflow.cxx | 6 +- .../GlobalTrackingStudy/TPCTrackStudy.h | 2 +- .../GlobalTrackingStudy/TrackMCStudy.h | 2 +- .../GlobalTrackingStudy/TrackingStudy.h | 2 +- .../study/src/TPCTrackStudy.cxx | 30 +- .../study/src/TrackMCStudy.cxx | 30 +- .../study/src/TrackingStudy.cxx | 30 +- .../study/src/tpc-track-study-workflow.cxx | 6 +- .../study/src/trackMCStudy-workflow.cxx | 6 +- .../study/src/tracking-study-workflow.cxx | 6 +- Detectors/TPC/calibration/CMakeLists.txt | 2 + .../TPCCalibration/CorrectionMapsLoader.h | 32 +- .../TPCCalibration/CorrectionMapsLoaderFull.h | 64 +++ .../TPC/calibration/src/CalculatedEdx.cxx | 5 +- .../calibration/src/CorrectionMapsLoader.cxx | 369 ++---------------- .../src/CorrectionMapsLoaderFull.cxx | 253 ++++++++++++ Detectors/TPC/calibration/src/TrackDump.cxx | 9 +- .../TPCFastTransformHelperO2.h | 16 +- .../src/TPCFastTransformHelperO2.cxx | 10 +- .../reconstruction/test/testGPUCATracking.cxx | 11 +- .../TPCWorkflow/TPCCalibPadGainTracksSpec.h | 37 +- .../include/TPCWorkflow/TPCRefitter.h | 2 +- .../include/TPCWorkflow/TPCScalerSpec.h | 3 +- Detectors/TPC/workflow/src/RecoWorkflow.cxx | 13 +- Detectors/TPC/workflow/src/TPCRefitter.cxx | 32 +- Detectors/TPC/workflow/src/TPCScalerSpec.cxx | 102 ++++- .../workflow/src/tpc-calib-gainmap-tracks.cxx | 6 +- .../TPC/workflow/src/tpc-reco-workflow.cxx | 3 +- .../workflow/src/tpc-refitter-workflow.cxx | 10 +- Detectors/TPC/workflow/src/tpc-scaler.cxx | 6 +- .../TRDWorkflow/TRDGlobalTrackingSpec.h | 11 +- .../workflow/src/TRDGlobalTrackingSpec.cxx | 14 +- .../workflow/src/trd-tracking-workflow.cxx | 6 +- .../Base/GPUReconstructionConvert.cxx | 4 +- .../Base/GPUReconstructionConvert.h | 4 +- .../Base/GPUReconstructionTimeframe.cxx | 2 +- GPU/GPUTracking/DataTypes/GPUDataTypesIO.h | 6 +- GPU/GPUTracking/Global/GPUChainTracking.cxx | 42 +- GPU/GPUTracking/Global/GPUChainTracking.h | 10 +- GPU/GPUTracking/Global/GPUChainTrackingIO.cxx | 26 +- GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx | 2 +- GPU/GPUTracking/Merger/GPUTPCGMO2Output.cxx | 2 +- GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx | 2 +- .../TPCConvert/GPUTPCConvertImpl.h | 4 +- GPU/GPUTracking/qa/GPUQA.cxx | 2 +- .../CorrectionMapsHelper.cxx | 120 ++---- .../CorrectionMapsHelper.h | 192 +++------ .../CorrectionMapsHelperFull.cxx | 60 +++ .../CorrectionMapsHelperFull.h | 161 ++++++++ GPU/TPCFastTransformation/Spline1DSpec.h | 37 ++ GPU/TPCFastTransformation/Spline2DSpec.h | 96 +++++ .../TPCFastTransformPOD.cxx | 53 ++- .../TPCFastTransformPOD.h | 111 +++++- .../include/GPUWorkflow/GPUWorkflowSpec.h | 12 +- .../include/GPUWorkflow/O2GPUDPLDisplay.h | 3 - GPU/Workflow/src/GPUWorkflowSpec.cxx | 8 +- GPU/Workflow/src/GPUWorkflowTPC.cxx | 77 ++-- GPU/Workflow/src/O2GPUDPLDisplay.cxx | 13 +- GPU/Workflow/src/gpu-reco-workflow.cxx | 8 - prodtests/full-system-test/dpl-workflow.sh | 30 +- 76 files changed, 1260 insertions(+), 1125 deletions(-) create mode 100644 Detectors/TPC/calibration/include/TPCCalibration/CorrectionMapsLoaderFull.h create mode 100644 Detectors/TPC/calibration/src/CorrectionMapsLoaderFull.cxx create mode 100644 GPU/TPCFastTransformation/CorrectionMapsHelperFull.cxx create mode 100644 GPU/TPCFastTransformation/CorrectionMapsHelperFull.h diff --git a/Detectors/Align/Workflow/include/AlignmentWorkflow/BarrelAlignmentSpec.h b/Detectors/Align/Workflow/include/AlignmentWorkflow/BarrelAlignmentSpec.h index 197ace2bd9d20..fd5697a20bc2b 100644 --- a/Detectors/Align/Workflow/include/AlignmentWorkflow/BarrelAlignmentSpec.h +++ b/Detectors/Align/Workflow/include/AlignmentWorkflow/BarrelAlignmentSpec.h @@ -31,7 +31,7 @@ namespace align /// create a processor spec framework::DataProcessorSpec getBarrelAlignmentSpec(o2::dataformats::GlobalTrackID::mask_t srcMP, o2::dataformats::GlobalTrackID::mask_t src, - o2::detectors::DetID::mask_t dets, o2::detectors::DetID::mask_t skipDetClusters, bool enableCosmic, int postproc, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts); + o2::detectors::DetID::mask_t dets, o2::detectors::DetID::mask_t skipDetClusters, bool enableCosmic, int postproc, bool useMC); } // namespace align } // namespace o2 diff --git a/Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx b/Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx index 2e63a1a65483c..dc952b26e52f0 100644 --- a/Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx +++ b/Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx @@ -86,14 +86,9 @@ class BarrelAlignmentSpec : public Task CheckConstaints = 0x1 << 1, GenPedeFiles = 0x1 << 2, LabelPedeResults = 0x1 << 3 }; - BarrelAlignmentSpec(GTrackID::mask_t srcMP, std::shared_ptr dr, std::shared_ptr ggrec, const o2::tpc::CorrectionMapsLoaderGloOpts& tpcOpt, + BarrelAlignmentSpec(GTrackID::mask_t srcMP, std::shared_ptr dr, std::shared_ptr ggrec, DetID::mask_t detmask, bool cosmic, int postprocess, bool useMC, bool loadTPCCalib) - : mDataRequest(dr), mGRPGeomRequest(ggrec), mMPsrc{srcMP}, mDetMask{detmask}, mCosmic(cosmic), mPostProcessing(postprocess), mUseMC(useMC), mLoadTPCCalib(loadTPCCalib) - { - mTPCCorrMapsLoader.setLumiScaleType(tpcOpt.lumiType); - mTPCCorrMapsLoader.setLumiScaleMode(tpcOpt.lumiMode); - mTPCCorrMapsLoader.setCheckCTPIDCConsistency(tpcOpt.checkCTPIDCconsistency); - } + : mDataRequest(dr), mGRPGeomRequest(ggrec), mMPsrc{srcMP}, mDetMask{detmask}, mCosmic(cosmic), mPostProcessing(postprocess), mUseMC(useMC), mLoadTPCCalib(loadTPCCalib) {} ~BarrelAlignmentSpec() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -184,9 +179,6 @@ void BarrelAlignmentSpec::init(InitContext& ic) } mIgnoreCCDBAlignment = ic.options().get("ignore-ccdb-alignment"); if (!mPostProcessing) { - if (mLoadTPCCalib) { - mTPCCorrMapsLoader.init(ic); - } if (GTrackID::includesDet(DetID::TRD, mMPsrc)) { mTRDTransformer.reset(new o2::trd::TrackletTransformer); if (ic.options().get("apply-xor")) { @@ -278,7 +270,7 @@ void BarrelAlignmentSpec::updateTimeDependentParams(ProcessingContext& pc) mTPCCorrMapsLoader.acknowledgeUpdate(); updateMaps = true; } - mController->setTPCCorrMaps(&mTPCCorrMapsLoader); + // mController->setTPCCorrMaps(&mTPCCorrMapsLoader); if (mTPCVDriftHelper.isUpdated()) { LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, @@ -288,9 +280,6 @@ void BarrelAlignmentSpec::updateTimeDependentParams(ProcessingContext& pc) mTPCVDriftHelper.acknowledgeUpdate(); updateMaps = true; } - if (updateMaps) { - mTPCCorrMapsLoader.updateVDrift(mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().getTimeOffset()); - } } } @@ -314,9 +303,6 @@ void BarrelAlignmentSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& match return; } - if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { - return; - } } void BarrelAlignmentSpec::run(ProcessingContext& pc) @@ -374,7 +360,7 @@ void BarrelAlignmentSpec::endOfStream(EndOfStreamContext& ec) mDBGOut.reset(); } -DataProcessorSpec getBarrelAlignmentSpec(GTrackID::mask_t srcMP, GTrackID::mask_t src, DetID::mask_t dets, DetID::mask_t skipDetClusters, bool enableCosmic, int postprocess, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts) +DataProcessorSpec getBarrelAlignmentSpec(GTrackID::mask_t srcMP, GTrackID::mask_t src, DetID::mask_t dets, DetID::mask_t skipDetClusters, bool enableCosmic, int postprocess, bool useMC) { std::vector outputs; auto dataRequest = std::make_shared(); @@ -399,7 +385,7 @@ DataProcessorSpec getBarrelAlignmentSpec(GTrackID::mask_t srcMP, GTrackID::mask_ } if (src[DetID::TPC] && !skipDetClusters[DetID::TPC]) { o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); + o2::tpc::CorrectionMapsLoader::requestInputs(dataRequest->inputs, opts); loadTPCCalib = true; } } @@ -417,7 +403,7 @@ DataProcessorSpec getBarrelAlignmentSpec(GTrackID::mask_t srcMP, GTrackID::mask_ "barrel-alignment", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(srcMP, dataRequest, ccdbRequest, sclOpts, dets, enableCosmic, postprocess, useMC, loadTPCCalib)}, + AlgorithmSpec{adaptFromTask(srcMP, dataRequest, ccdbRequest, dets, enableCosmic, postprocess, useMC, loadTPCCalib)}, opts}; } diff --git a/Detectors/Align/Workflow/src/barrel-alignment-workflow.cxx b/Detectors/Align/Workflow/src/barrel-alignment-workflow.cxx index cdd0620affec9..03fc414113114 100644 --- a/Detectors/Align/Workflow/src/barrel-alignment-workflow.cxx +++ b/Detectors/Align/Workflow/src/barrel-alignment-workflow.cxx @@ -20,7 +20,6 @@ #include "TPCReaderWorkflow/ClusterReaderSpec.h" #include "TPCWorkflow/ClusterSharingMapSpec.h" #include "TPCWorkflow/TPCScalerSpec.h" -#include "TPCCalibration/CorrectionMapsLoader.h" #include "TOFWorkflowIO/ClusterReaderSpec.h" #include "TOFWorkflowIO/TOFMatchedReaderSpec.h" #include "TOFWorkflowIO/ClusterReaderSpec.h" @@ -150,11 +149,11 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) o2::conf::ConfigurableParam::writeINI("o2_barrel_alignment_configuration.ini"); } - if (sclOpt.needTPCScalersWorkflow() && !configcontext.options().get("disable-root-input")) { - specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == 2, sclOpt.enableMShapeCorrection)); + if (!configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpt.enableMShapeCorrection, sclOpt)); } - specs.emplace_back(o2::align::getBarrelAlignmentSpec(srcMP, src, dets, skipDetClusters, enableCosmic, postprocess, useMC, sclOpt)); + specs.emplace_back(o2::align::getBarrelAlignmentSpec(srcMP, src, dets, skipDetClusters, enableCosmic, postprocess, useMC)); // RS FIXME: check which clusters are really needed if (!postprocess) { GID::mask_t dummy; diff --git a/Detectors/GlobalTracking/src/MatchTOF.cxx b/Detectors/GlobalTracking/src/MatchTOF.cxx index 6a3486dd12044..ad050309fe030 100644 --- a/Detectors/GlobalTracking/src/MatchTOF.cxx +++ b/Detectors/GlobalTracking/src/MatchTOF.cxx @@ -2087,6 +2087,7 @@ void MatchTOF::updateTimeDependentParams() const auto& trackTune = TrackTuneParams::Instance(); float scale = mTPCCorrMapsHelper->getInstLumiCTP(); if (scale < 0.f) { + LOGP(warning, "Negative scale factor for TPC covariance correction, setting it to zero"); scale = 0.f; } mCovDiagInner = trackTune.getCovInnerTotal(scale); diff --git a/Detectors/GlobalTracking/src/MatchTPCITS.cxx b/Detectors/GlobalTracking/src/MatchTPCITS.cxx index 73216c8ce1eac..9b69397e1f9f6 100644 --- a/Detectors/GlobalTracking/src/MatchTPCITS.cxx +++ b/Detectors/GlobalTracking/src/MatchTPCITS.cxx @@ -288,6 +288,7 @@ void MatchTPCITS::updateTimeDependentParams() const auto& trackTune = TrackTuneParams::Instance(); float scale = mTPCCorrMapsHelper->getInstLumiCTP(); if (scale < 0.f) { + LOGP(warning, "Negative scale factor for TPC covariance correction, setting it to zero"); scale = 0.f; } mCovDiagInner = trackTune.getCovInnerTotal(scale); diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/CosmicsMatchingSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/CosmicsMatchingSpec.h index e0e74c3058086..0633bb6a64a22 100644 --- a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/CosmicsMatchingSpec.h +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/CosmicsMatchingSpec.h @@ -29,7 +29,7 @@ namespace globaltracking { /// create a processor spec -framework::DataProcessorSpec getCosmicsMatchingSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts); +framework::DataProcessorSpec getCosmicsMatchingSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useMC); } // namespace globaltracking } // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/SecondaryVertexingSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/SecondaryVertexingSpec.h index b8071ae83d347..9de5f158a0608 100644 --- a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/SecondaryVertexingSpec.h +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/SecondaryVertexingSpec.h @@ -29,7 +29,7 @@ namespace vertexing { /// create a processor spec -o2::framework::DataProcessorSpec getSecondaryVertexingSpec(o2::dataformats::GlobalTrackID::mask_t src, bool enableCasc, bool enable3body, bool enableStrangenesTracking, bool enableCCDBParams, bool useMC, bool useGeom, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts); +o2::framework::DataProcessorSpec getSecondaryVertexingSpec(o2::dataformats::GlobalTrackID::mask_t src, bool enableCasc, bool enable3body, bool enableStrangenesTracking, bool enableCCDBParams, bool useMC, bool useGeom); } // namespace vertexing } // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TOFMatcherSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TOFMatcherSpec.h index 79a4ee0ce0360..afc70f688a0b5 100644 --- a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TOFMatcherSpec.h +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TOFMatcherSpec.h @@ -29,7 +29,7 @@ namespace globaltracking { /// create a processor spec -framework::DataProcessorSpec getTOFMatcherSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useMC, bool useFIT, bool tpcRefit, bool strict, float extratolerancetrd, bool pushMatchable, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, int nlanes = 1); +framework::DataProcessorSpec getTOFMatcherSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useMC, bool useFIT, bool tpcRefit, bool strict, float extratolerancetrd, bool pushMatchable, int nlanes = 1); } // namespace globaltracking } // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TPCITSMatchingSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TPCITSMatchingSpec.h index 4aaed7d64eec5..4f4f13cde72b1 100644 --- a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TPCITSMatchingSpec.h +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TPCITSMatchingSpec.h @@ -27,7 +27,7 @@ struct CorrectionMapsLoaderGloOpts; namespace globaltracking { /// create a processor spec -framework::DataProcessorSpec getTPCITSMatchingSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useFT0, bool calib, bool skipTPCOnly, bool useGeom, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts); +framework::DataProcessorSpec getTPCITSMatchingSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useFT0, bool calib, bool skipTPCOnly, bool useGeom, bool useMC); } // namespace globaltracking } // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/src/CosmicsMatchingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/CosmicsMatchingSpec.cxx index 5bcdded0e1223..cf148d47b3d10 100644 --- a/Detectors/GlobalTrackingWorkflow/src/CosmicsMatchingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/CosmicsMatchingSpec.cxx @@ -62,12 +62,7 @@ namespace globaltracking class CosmicsMatchingSpec : public Task { public: - CosmicsMatchingSpec(std::shared_ptr dr, std::shared_ptr gr, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, bool useMC) : mDataRequest(dr), mGGCCDBRequest(gr), mUseMC(useMC) - { - mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); - mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); - mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); - } + CosmicsMatchingSpec(std::shared_ptr dr, std::shared_ptr gr, bool useMC) : mDataRequest(dr), mGGCCDBRequest(gr), mUseMC(useMC) {} ~CosmicsMatchingSpec() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -92,7 +87,6 @@ void CosmicsMatchingSpec::init(InitContext& ic) o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); mMatching.setDebugFlag(ic.options().get("debug-tree-flags")); mMatching.setUseMC(mUseMC); - mTPCCorrMapsLoader.init(ic); // } @@ -132,10 +126,8 @@ void CosmicsMatchingSpec::updateTimeDependentParams(ProcessingContext& pc) } mMatching.init(); } - bool updateMaps = false; if (mTPCCorrMapsLoader.isUpdated()) { mTPCCorrMapsLoader.acknowledgeUpdate(); - updateMaps = true; } mMatching.setTPCCorrMaps(&mTPCCorrMapsLoader); if (mTPCVDriftHelper.isUpdated()) { @@ -145,10 +137,6 @@ void CosmicsMatchingSpec::updateTimeDependentParams(ProcessingContext& pc) mTPCVDriftHelper.getSourceName()); mMatching.setTPCVDrift(mTPCVDriftHelper.getVDriftObject()); mTPCVDriftHelper.acknowledgeUpdate(); - updateMaps = true; - } - if (updateMaps) { - mTPCCorrMapsLoader.updateVDrift(mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().getTimeOffset()); } } @@ -160,9 +148,6 @@ void CosmicsMatchingSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { return; } - if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { - return; - } if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { LOG(info) << "cluster dictionary updated"; mMatching.setITSDict((const o2::itsmft::TopologyDictionary*)obj); @@ -177,7 +162,7 @@ void CosmicsMatchingSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getCosmicsMatchingSpec(GTrackID::mask_t src, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts) +DataProcessorSpec getCosmicsMatchingSpec(GTrackID::mask_t src, bool useMC) { std::vector outputs; Options opts{ @@ -203,13 +188,13 @@ DataProcessorSpec getCosmicsMatchingSpec(GTrackID::mask_t src, bool useMC, const dataRequest->inputs, true); o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); + o2::tpc::CorrectionMapsLoader::requestInputs(dataRequest->inputs, opts); return DataProcessorSpec{ "cosmics-matcher", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, sclOpts, useMC)}, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, useMC)}, opts}; } diff --git a/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx index 6dfd1cb770d7f..fb9fc328175ea 100644 --- a/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx @@ -58,14 +58,7 @@ namespace o2d = o2::dataformats; class SecondaryVertexingSpec : public Task { public: - SecondaryVertexingSpec(std::shared_ptr dr, std::shared_ptr gr, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, GTrackID::mask_t src, bool enabCasc, bool enable3body, bool enableStrangenessTracking, bool enableCCDBParams, bool useMC) : mDataRequest(dr), mGGCCDBRequest(gr), mSrc(src), mEnableCascades(enabCasc), mEnable3BodyVertices(enable3body), mEnableStrangenessTracking(enableStrangenessTracking), mEnableCCDBParams(enableCCDBParams), mUseMC(useMC) - { - if (mSrc[GTrackID::TPC]) { - mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); - mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); - mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); - } - } + SecondaryVertexingSpec(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool enabCasc, bool enable3body, bool enableStrangenessTracking, bool enableCCDBParams, bool useMC) : mDataRequest(dr), mGGCCDBRequest(gr), mSrc(src), mEnableCascades(enabCasc), mEnable3BodyVertices(enable3body), mEnableStrangenessTracking(enableStrangenessTracking), mEnableCCDBParams(enableCCDBParams), mUseMC(useMC) {} ~SecondaryVertexingSpec() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -107,9 +100,6 @@ void SecondaryVertexingSpec::init(InitContext& ic) mStrTracker.setMCTruthOn(mUseMC); mVertexer.setStrangenessTracker(&mStrTracker); } - if (mSrc[GTrackID::TPC]) { - mTPCCorrMapsLoader.init(ic); - } } void SecondaryVertexingSpec::run(ProcessingContext& pc) @@ -157,9 +147,6 @@ void SecondaryVertexingSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* ob if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { return; } - if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { - return; - } if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { LOG(info) << "cluster dictionary updated"; mStrTracker.setClusterDictionaryITS((const o2::itsmft::TopologyDictionary*)obj); @@ -229,6 +216,7 @@ void SecondaryVertexingSpec::updateTimeDependentParams(ProcessingContext& pc) updateMaps = true; } mVertexer.setTPCCorrMaps(&mTPCCorrMapsLoader); + if (mTPCVDriftHelper.isUpdated()) { LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, @@ -236,10 +224,6 @@ void SecondaryVertexingSpec::updateTimeDependentParams(ProcessingContext& pc) mTPCVDriftHelper.getSourceName()); mVertexer.setTPCVDrift(mTPCVDriftHelper.getVDriftObject()); mTPCVDriftHelper.acknowledgeUpdate(); - updateMaps = true; - } - if (updateMaps) { - mTPCCorrMapsLoader.updateVDrift(mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().getTimeOffset()); } } if (mEnableStrangenessTracking) { @@ -253,7 +237,7 @@ void SecondaryVertexingSpec::updateTimeDependentParams(ProcessingContext& pc) } DataProcessorSpec getSecondaryVertexingSpec(GTrackID::mask_t src, bool enableCasc, bool enable3body, bool enableStrangenesTracking, bool enableCCDBParams, - bool useMC, bool useGeom, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts) + bool useMC, bool useGeom) { std::vector outputs; Options opts{ @@ -297,7 +281,7 @@ DataProcessorSpec getSecondaryVertexingSpec(GTrackID::mask_t src, bool enableCas } if (src[GTrackID::TPC]) { o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); + o2::tpc::CorrectionMapsLoader::requestInputs(dataRequest->inputs, opts); } outputs.emplace_back("GLO", "V0S_IDX", 0, Lifetime::Timeframe); // found V0s indices outputs.emplace_back("GLO", "V0S", 0, Lifetime::Timeframe); // found V0s @@ -324,7 +308,7 @@ DataProcessorSpec getSecondaryVertexingSpec(GTrackID::mask_t src, bool enableCas "secondary-vertexing", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, sclOpts, src, enableCasc, enable3body, enableStrangenesTracking, enableCCDBParams, useMC)}, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, src, enableCasc, enable3body, enableStrangenesTracking, enableCCDBParams, useMC)}, opts}; } diff --git a/Detectors/GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx index 8081c48e390d3..ede3026647b1e 100644 --- a/Detectors/GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx @@ -59,12 +59,7 @@ namespace globaltracking class TOFMatcherSpec : public Task { public: - TOFMatcherSpec(std::shared_ptr dr, std::shared_ptr gr, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, bool useMC, bool useFIT, bool tpcRefit, bool strict, bool pushMatchable, int lanes = 1) : mDataRequest(dr), mGGCCDBRequest(gr), mUseMC(useMC), mUseFIT(useFIT), mDoTPCRefit(tpcRefit), mStrict(strict), mPushMatchable(pushMatchable), mNlanes(lanes) - { - mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); - mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); - mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); - } + TOFMatcherSpec(std::shared_ptr dr, std::shared_ptr gr, bool useMC, bool useFIT, bool tpcRefit, bool strict, bool pushMatchable, int lanes = 1) : mDataRequest(dr), mGGCCDBRequest(gr), mUseMC(useMC), mUseFIT(useFIT), mDoTPCRefit(tpcRefit), mStrict(strict), mPushMatchable(pushMatchable), mNlanes(lanes) {} ~TOFMatcherSpec() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -96,7 +91,6 @@ void TOFMatcherSpec::init(InitContext& ic) if (mStrict) { mMatcher.setHighPurity(); } - mTPCCorrMapsLoader.init(ic); mMatcher.storeMatchable(mPushMatchable); mMatcher.setExtraTimeToleranceTRD(mExtraTolTRD); mMatcher.setNlanes(mNlanes); @@ -117,23 +111,17 @@ void TOFMatcherSpec::updateTimeDependentParams(ProcessingContext& pc) // put here init-once stuff } // we may have other params which need to be queried regularly - bool updateMaps = false; if (mTPCCorrMapsLoader.isUpdated()) { mTPCCorrMapsLoader.acknowledgeUpdate(); - updateMaps = true; } mMatcher.setTPCCorrMaps(&mTPCCorrMapsLoader); - if (mTPCVDriftHelper.isUpdated()) { + if (mTPCVDriftHelper.isUpdated()) { LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().timeOffsetCorr, mTPCVDriftHelper.getVDriftObject().refTimeOffset, mTPCVDriftHelper.getSourceName()); mMatcher.setTPCVDrift(mTPCVDriftHelper.getVDriftObject()); mTPCVDriftHelper.acknowledgeUpdate(); - updateMaps = true; - } - if (updateMaps) { - mTPCCorrMapsLoader.updateVDrift(mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().getTimeOffset()); } } @@ -145,9 +133,6 @@ void TOFMatcherSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { return; } - if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { - return; - } } void TOFMatcherSpec::run(ProcessingContext& pc) @@ -247,7 +232,7 @@ void TOFMatcherSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getTOFMatcherSpec(GID::mask_t src, bool useMC, bool useFIT, bool tpcRefit, bool strict, float extratolerancetrd, bool pushMatchable, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, int nlanes) +DataProcessorSpec getTOFMatcherSpec(GID::mask_t src, bool useMC, bool useFIT, bool tpcRefit, bool strict, float extratolerancetrd, bool pushMatchable, int nlanes) { uint32_t ss = o2::globaltracking::getSubSpec(strict ? o2::globaltracking::MatchingType::Strict : o2::globaltracking::MatchingType::Standard); Options opts; @@ -273,7 +258,7 @@ DataProcessorSpec getTOFMatcherSpec(GID::mask_t src, bool useMC, bool useFIT, bo dataRequest->inputs, true); o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); + o2::tpc::CorrectionMapsLoader::requestInputs(dataRequest->inputs, opts); std::vector outputs; if (GID::includesSource(GID::TPC, src)) { outputs.emplace_back(o2::header::gDataOriginTOF, "MTC_TPC", ss, Lifetime::Timeframe); @@ -327,7 +312,7 @@ DataProcessorSpec getTOFMatcherSpec(GID::mask_t src, bool useMC, bool useFIT, bo "tof-matcher", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, sclOpts, useMC, useFIT, tpcRefit, strict, pushMatchable, nlanes)}, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, useMC, useFIT, tpcRefit, strict, pushMatchable, nlanes)}, opts}; } diff --git a/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx index cb3384b0631c2..38b6d6c1efb6e 100644 --- a/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx @@ -69,14 +69,9 @@ namespace globaltracking class TPCITSMatchingDPL : public Task { public: - TPCITSMatchingDPL(std::shared_ptr dr, std::shared_ptr gr, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, + TPCITSMatchingDPL(std::shared_ptr dr, std::shared_ptr gr, bool useFT0, bool calib, bool skipTPCOnly, bool useMC) - : mDataRequest(dr), mGGCCDBRequest(gr), mUseFT0(useFT0), mCalibMode(calib), mSkipTPCOnly(skipTPCOnly), mUseMC(useMC) - { - mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); - mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); - mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); - } + : mDataRequest(dr), mGGCCDBRequest(gr), mUseFT0(useFT0), mCalibMode(calib), mSkipTPCOnly(skipTPCOnly), mUseMC(useMC) {} ~TPCITSMatchingDPL() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -108,7 +103,6 @@ void TPCITSMatchingDPL::init(InitContext& ic) mMatching.setNThreads(std::max(1, ic.options().get("nthreads"))); mMatching.setUseBCFilling(!ic.options().get("ignore-bc-check")); mMatching.setDebugFlag(ic.options().get("debug-tree-flags")); - mTPCCorrMapsLoader.init(ic); } void TPCITSMatchingDPL::run(ProcessingContext& pc) @@ -157,9 +151,6 @@ void TPCITSMatchingDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { return; } - if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { - return; - } if (matcher == ConcreteDataMatcher("GLO", "ITSTPCPARAM", 0)) { LOG(info) << "ITS-TPC Matching params updated from ccdb"; return; @@ -236,20 +227,16 @@ void TPCITSMatchingDPL::updateTimeDependentParams(ProcessingContext& pc) mMatching.setTPCCorrMaps(&mTPCCorrMapsLoader); if (mTPCVDriftHelper.isUpdated()) { - LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", + LOGP(info, "Updating TPC VDrift: corrFact {} wrt refVDrift {} and DriftTimeOffset correction {} wrt {} from source {}", mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().timeOffsetCorr, mTPCVDriftHelper.getVDriftObject().refTimeOffset, mTPCVDriftHelper.getSourceName()); mMatching.setTPCVDrift(mTPCVDriftHelper.getVDriftObject()); mTPCVDriftHelper.acknowledgeUpdate(); - updateMaps = true; - } - if (updateMaps) { - mTPCCorrMapsLoader.updateVDrift(mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().getTimeOffset()); } } -DataProcessorSpec getTPCITSMatchingSpec(GTrackID::mask_t src, bool useFT0, bool calib, bool skipTPCOnly, bool useGeom, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts) +DataProcessorSpec getTPCITSMatchingSpec(GTrackID::mask_t src, bool useFT0, bool calib, bool skipTPCOnly, bool useGeom, bool useMC) { std::vector outputs; auto dataRequest = std::make_shared(); @@ -305,13 +292,13 @@ DataProcessorSpec getTPCITSMatchingSpec(GTrackID::mask_t src, bool useFT0, bool {"debug-tree-flags", VariantType::Int, 0, {"DebugFlagTypes bit-pattern for debug tree"}}}; o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); + o2::tpc::CorrectionMapsLoader::requestInputs(dataRequest->inputs, opts); return DataProcessorSpec{ "itstpc-track-matcher", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, sclOpts, useFT0, calib, skipTPCOnly, useMC)}, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, useFT0, calib, skipTPCOnly, useMC)}, opts}; } diff --git a/Detectors/GlobalTrackingWorkflow/src/cosmics-match-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/cosmics-match-workflow.cxx index 3f7ecfbbea809..db0e4253a7a51 100644 --- a/Detectors/GlobalTrackingWorkflow/src/cosmics-match-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/cosmics-match-workflow.cxx @@ -104,10 +104,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) } GID::mask_t srcCl = src; GID::mask_t dummy; - if (sclOpt.needTPCScalersWorkflow() && !configcontext.options().get("disable-root-input")) { - specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == 2, sclOpt.enableMShapeCorrection)); + if (!configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpt.enableMShapeCorrection, sclOpt)); } - specs.emplace_back(o2::globaltracking::getCosmicsMatchingSpec(src, useMC, sclOpt)); + specs.emplace_back(o2::globaltracking::getCosmicsMatchingSpec(src, useMC)); o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, src, src, src, useMC, dummy); // clusters MC is not needed diff --git a/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx index 5bc80f527d4d0..6d10fa786b0c9 100644 --- a/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx @@ -101,10 +101,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) } } WorkflowSpec specs; - if (sclOpt.needTPCScalersWorkflow() && !configcontext.options().get("disable-root-input")) { - specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == 2, sclOpt.enableMShapeCorrection)); + if (!configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpt.enableMShapeCorrection, sclOpt)); } - specs.emplace_back(o2::vertexing::getSecondaryVertexingSpec(src, enableCasc, enable3body, enableStrTr, enableCCDBParams, useMC, useGeom, sclOpt)); + specs.emplace_back(o2::vertexing::getSecondaryVertexingSpec(src, enableCasc, enable3body, enableStrTr, enableCCDBParams, useMC, useGeom)); // only TOF clusters are needed if TOF is involved, no clusters MC needed WorkflowSpec inputspecs; diff --git a/Detectors/GlobalTrackingWorkflow/src/tof-matcher-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/tof-matcher-workflow.cxx index 9a95c83617210..27adab7d50439 100644 --- a/Detectors/GlobalTrackingWorkflow/src/tof-matcher-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/tof-matcher-workflow.cxx @@ -168,10 +168,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) specs.push_back(s); } } - if (sclOpt.needTPCScalersWorkflow() && !configcontext.options().get("disable-root-input")) { - specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == 2, sclOpt.enableMShapeCorrection)); + if (!configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpt.enableMShapeCorrection, sclOpt)); } - specs.emplace_back(o2::globaltracking::getTOFMatcherSpec(src, useMC, useFIT, refitTPCTOF, strict, extratolerancetrd, writeMatchable, sclOpt, nLanes)); // doTPCrefit not yet supported (need to load TPC clusters?) + specs.emplace_back(o2::globaltracking::getTOFMatcherSpec(src, useMC, useFIT, refitTPCTOF, strict, extratolerancetrd, writeMatchable, nLanes)); // doTPCrefit not yet supported (need to load TPC clusters?) if (!disableRootOut) { std::vector writers; diff --git a/Detectors/GlobalTrackingWorkflow/src/tpcits-match-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/tpcits-match-workflow.cxx index 17ab2191f0e1e..0168c3076261e 100644 --- a/Detectors/GlobalTrackingWorkflow/src/tpcits-match-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/tpcits-match-workflow.cxx @@ -93,10 +93,10 @@ WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& configcont } o2::framework::WorkflowSpec specs; - if (sclOpt.needTPCScalersWorkflow() && !configcontext.options().get("disable-root-input")) { - specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == 2, sclOpt.enableMShapeCorrection)); + if (!configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpt.enableMShapeCorrection, sclOpt)); } - specs.emplace_back(o2::globaltracking::getTPCITSMatchingSpec(srcL, useFT0, calib, !GID::includesSource(GID::TPC, src), useGeom, useMC, sclOpt)); + specs.emplace_back(o2::globaltracking::getTPCITSMatchingSpec(srcL, useFT0, calib, !GID::includesSource(GID::TPC, src), useGeom, useMC)); if (!configcontext.options().get("disable-root-output")) { specs.emplace_back(o2::globaltracking::getTrackWriterTPCITSSpec(useMC)); diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TPCTrackStudy.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TPCTrackStudy.h index 47385f400ec01..8f95203b52ffd 100644 --- a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TPCTrackStudy.h +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TPCTrackStudy.h @@ -27,7 +27,7 @@ struct CorrectionMapsLoaderGloOpts; namespace o2::trackstudy { /// create a processor spec -o2::framework::DataProcessorSpec getTPCTrackStudySpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts); +o2::framework::DataProcessorSpec getTPCTrackStudySpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool useMC); } // namespace o2::trackstudy diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudy.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudy.h index d1326a47ac909..2fc21c6d7cd1c 100644 --- a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudy.h +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudy.h @@ -21,7 +21,7 @@ namespace o2::trackstudy { /// create a processor spec -o2::framework::DataProcessorSpec getTrackMCStudySpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, bool checkSV); +o2::framework::DataProcessorSpec getTrackMCStudySpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool checkSV); } // namespace o2::trackstudy diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackingStudy.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackingStudy.h index 7a15c191cbeed..caa50dc1d481a 100644 --- a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackingStudy.h +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackingStudy.h @@ -23,7 +23,7 @@ namespace o2::trackstudy { /// create a processor spec -o2::framework::DataProcessorSpec getTrackingStudySpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts); +o2::framework::DataProcessorSpec getTrackingStudySpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool useMC); } // namespace o2::trackstudy diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx index ee475acbbcf70..072993edfec4a 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx @@ -50,13 +50,8 @@ using timeEst = o2::dataformats::TimeStampWithError; class TPCTrackStudySpec final : public Task { public: - TPCTrackStudySpec(std::shared_ptr dr, std::shared_ptr gr, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, GTrackID::mask_t src, bool useMC) - : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mUseMC(useMC) - { - mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); - mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); - mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); - } + TPCTrackStudySpec(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool useMC) + : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mUseMC(useMC) {} ~TPCTrackStudySpec() final = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -108,7 +103,6 @@ void TPCTrackStudySpec::init(InitContext& ic) if (mXRef < 0.) { mXRef = 0.; } - mTPCCorrMapsLoader.init(ic); mDBGOut = std::make_unique("tpc-trackStudy.root", "recreate"); if (ic.options().get("dump-clusters")) { mDBGOutCl = std::make_unique("tpc-trackStudy-cl.root", "recreate"); @@ -151,17 +145,6 @@ void TPCTrackStudySpec::updateTimeDependentParams(ProcessingContext& pc) mTPCCorrMapsLoader.acknowledgeUpdate(); updateMaps = true; } - if (mTPCVDriftHelper.isUpdated()) { - LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", - mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, - mTPCVDriftHelper.getVDriftObject().timeOffsetCorr, mTPCVDriftHelper.getVDriftObject().refTimeOffset, - mTPCVDriftHelper.getSourceName()); - mTPCVDriftHelper.acknowledgeUpdate(); - updateMaps = true; - } - if (updateMaps) { - mTPCCorrMapsLoader.updateVDrift(mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().getTimeOffset()); - } } void TPCTrackStudySpec::process(o2::globaltracking::RecoContainer& recoData) @@ -415,12 +398,9 @@ void TPCTrackStudySpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { return; } - if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { - return; - } } -DataProcessorSpec getTPCTrackStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts) +DataProcessorSpec getTPCTrackStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC) { std::vector outputs; Options opts{ @@ -445,13 +425,13 @@ DataProcessorSpec getTPCTrackStudySpec(GTrackID::mask_t srcTracks, GTrackID::mas dataRequest->inputs, true); o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); + o2::tpc::CorrectionMapsLoader::requestInputs(dataRequest->inputs, opts); return DataProcessorSpec{ "tpc-track-study", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, sclOpts, srcTracks, useMC)}, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, useMC)}, opts}; } diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx index 1db303d20e5d9..01d127c6511bd 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx @@ -86,13 +86,8 @@ using timeEst = o2::dataformats::TimeStampWithError; class TrackMCStudy final : public Task { public: - TrackMCStudy(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, bool checkSV) - : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mCheckSV(checkSV) - { - mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); - mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); - mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); - } + TrackMCStudy(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool checkSV) + : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mCheckSV(checkSV) {} ~TrackMCStudy() final = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -179,7 +174,6 @@ void TrackMCStudy::init(InitContext& ic) mNCheckDecays++; } mDecaysMaps.resize(mNCheckDecays); - mTPCCorrMapsLoader.init(ic); } void TrackMCStudy::run(ProcessingContext& pc) @@ -208,17 +202,6 @@ void TrackMCStudy::updateTimeDependentParams(ProcessingContext& pc) mTPCCorrMapsLoader.acknowledgeUpdate(); updateMaps = true; } - if (mTPCVDriftHelper.isUpdated()) { - LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", - mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, - mTPCVDriftHelper.getVDriftObject().timeOffsetCorr, mTPCVDriftHelper.getVDriftObject().refTimeOffset, - mTPCVDriftHelper.getSourceName()); - mTPCVDriftHelper.acknowledgeUpdate(); - updateMaps = true; - } - if (updateMaps) { - mTPCCorrMapsLoader.updateVDrift(mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().getTimeOffset()); - } static bool initOnceDone = false; if (!initOnceDone) { // this params need to be queried only once initOnceDone = true; @@ -1023,9 +1006,6 @@ void TrackMCStudy::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { return; } - if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { - return; - } if (matcher == ConcreteDataMatcher("ITS", "ALPIDEPARAM", 0)) { LOG(info) << "ITS Alpide param updated"; const auto& par = o2::itsmft::DPLAlpideParam::Instance(); @@ -1371,7 +1351,7 @@ void TrackMCStudy::processITSTracks(const o2::globaltracking::RecoContainer& rec } } -DataProcessorSpec getTrackMCStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, bool checkSV) +DataProcessorSpec getTrackMCStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool checkSV) { std::vector outputs; Options opts{ @@ -1390,7 +1370,7 @@ DataProcessorSpec getTrackMCStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask dataRequest->requestSecondaryVertices(useMC); } o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); + o2::tpc::CorrectionMapsLoader::requestInputs(dataRequest->inputs, opts); auto ggRequest = std::make_shared(false, // orbitResetTime true, // GRPECS=true true, // GRPLHCIF @@ -1404,7 +1384,7 @@ DataProcessorSpec getTrackMCStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask "track-mc-study", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, sclOpts, checkSV)}, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, checkSV)}, opts}; } diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx index a184058a1bfd6..042e884824274 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx @@ -70,13 +70,8 @@ using timeEst = o2::dataformats::TimeStampWithError; class TrackingStudySpec final : public Task { public: - TrackingStudySpec(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts) - : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mUseMC(useMC) - { - mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); - mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); - mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); - } + TrackingStudySpec(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool useMC) + : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mUseMC(useMC) {} ~TrackingStudySpec() final = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -125,7 +120,6 @@ class TrackingStudySpec final : public Task void TrackingStudySpec::init(InitContext& ic) { o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); - mTPCCorrMapsLoader.init(ic); int lane = ic.services().get().inputTimesliceId; int maxLanes = ic.services().get().maxInputTimeslices; std::string dbgnm = maxLanes == 1 ? "trackStudy.root" : fmt::format("trackStudy_{}.root", lane); @@ -232,17 +226,6 @@ void TrackingStudySpec::updateTimeDependentParams(ProcessingContext& pc) mTPCCorrMapsLoader.acknowledgeUpdate(); updateMaps = true; } - if (mTPCVDriftHelper.isUpdated()) { - LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", - mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, - mTPCVDriftHelper.getVDriftObject().timeOffsetCorr, mTPCVDriftHelper.getVDriftObject().refTimeOffset, - mTPCVDriftHelper.getSourceName()); - mTPCVDriftHelper.acknowledgeUpdate(); - updateMaps = true; - } - if (updateMaps) { - mTPCCorrMapsLoader.updateVDrift(mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().getTimeOffset()); - } } void TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) @@ -724,9 +707,6 @@ void TrackingStudySpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { return; } - if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { - return; - } if (matcher == ConcreteDataMatcher("GLO", "MEANVERTEX", 0)) { LOG(info) << "Imposing new MeanVertex: " << ((const o2::dataformats::MeanVertexObject*)obj)->asString(); mMeanVtx = *(const o2::dataformats::MeanVertexObject*)obj; @@ -746,7 +726,7 @@ float TrackingStudySpec::getDCAZCut(float pt) const return fun.Eval(pt); } -DataProcessorSpec getTrackingStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts) +DataProcessorSpec getTrackingStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC) { std::vector outputs; auto dataRequest = std::make_shared(); @@ -781,13 +761,13 @@ DataProcessorSpec getTrackingStudySpec(GTrackID::mask_t srcTracks, GTrackID::mas {"min-x-prop", VariantType::Float, 100.f, {"track should be propagated to this X at least"}}, }; o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); + o2::tpc::CorrectionMapsLoader::requestInputs(dataRequest->inputs, opts); return DataProcessorSpec{ "track-study", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, useMC, sclOpts)}, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, useMC)}, opts}; } diff --git a/Detectors/GlobalTrackingWorkflow/study/src/tpc-track-study-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/tpc-track-study-workflow.cxx index 3e92178c81b7d..457ff034fa991 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/tpc-track-study-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/tpc-track-study-workflow.cxx @@ -71,10 +71,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) } o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC); o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); // P-vertex is always needed - if (sclOpt.needTPCScalersWorkflow() && !configcontext.options().get("disable-root-input")) { - specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == 2, sclOpt.enableMShapeCorrection)); + if (!configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpt.enableMShapeCorrection, sclOpt)); } - specs.emplace_back(o2::trackstudy::getTPCTrackStudySpec(srcTrc, srcCls, useMC, sclOpt)); + specs.emplace_back(o2::trackstudy::getTPCTrackStudySpec(srcTrc, srcCls, useMC)); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); diff --git a/Detectors/GlobalTrackingWorkflow/study/src/trackMCStudy-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/trackMCStudy-workflow.cxx index 9e0055a389bfe..74add7dfebb51 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/trackMCStudy-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/trackMCStudy-workflow.cxx @@ -82,11 +82,11 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) if (checkSV) { o2::globaltracking::InputHelper::addInputSpecsSVertex(configcontext, specs); } - if (sclOpt.needTPCScalersWorkflow() && !configcontext.options().get("disable-root-input")) { - specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == 2, sclOpt.enableMShapeCorrection)); + if (!configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpt.enableMShapeCorrection, sclOpt)); } - specs.emplace_back(o2::trackstudy::getTrackMCStudySpec(srcTrc, srcCls, sclOpt, checkSV)); + specs.emplace_back(o2::trackstudy::getTrackMCStudySpec(srcTrc, srcCls, checkSV)); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); diff --git a/Detectors/GlobalTrackingWorkflow/study/src/tracking-study-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/tracking-study-workflow.cxx index ae2e3b5301a14..932a21b6a902e 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/tracking-study-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/tracking-study-workflow.cxx @@ -71,13 +71,13 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) if (sclOpt.requestCTPLumi) { srcCls = srcCls | GID::getSourcesMask("CTP"); } - if (sclOpt.needTPCScalersWorkflow() && !configcontext.options().get("disable-root-input")) { - specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == 2, sclOpt.enableMShapeCorrection)); + if (!configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpt.enableMShapeCorrection, sclOpt)); } o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC); o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); // P-vertex is always needed - specs.emplace_back(o2::trackstudy::getTrackingStudySpec(srcTrc, srcCls, useMC, sclOpt)); + specs.emplace_back(o2::trackstudy::getTrackingStudySpec(srcTrc, srcCls, useMC)); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); diff --git a/Detectors/TPC/calibration/CMakeLists.txt b/Detectors/TPC/calibration/CMakeLists.txt index a1068b928780d..905aa22e90e3b 100644 --- a/Detectors/TPC/calibration/CMakeLists.txt +++ b/Detectors/TPC/calibration/CMakeLists.txt @@ -59,6 +59,7 @@ o2_add_library(TPCCalibration src/CorrectdEdxDistortions.cxx src/PressureTemperatureHelper.cxx src/CMVContainer.cxx + src/CorrectionMapsLoaderFull.cxx PUBLIC_LINK_LIBRARIES O2::DataFormatsTPC O2::TPCBaseRecSim O2::TPCReconstruction ROOT::Minuit Microsoft.GSL::GSL @@ -118,6 +119,7 @@ o2_target_root_dictionary(TPCCalibration include/TPCCalibration/CorrectdEdxDistortions.h include/TPCCalibration/PressureTemperatureHelper.h include/TPCCalibration/CMVContainer.h) + include/TPCCalibration/CorrectionMapsLoaderFull.h) o2_add_test_root_macro(macro/comparePedestalsAndNoise.C PUBLIC_LINK_LIBRARIES O2::TPCBaseRecSim diff --git a/Detectors/TPC/calibration/include/TPCCalibration/CorrectionMapsLoader.h b/Detectors/TPC/calibration/include/TPCCalibration/CorrectionMapsLoader.h index 5a11ce3ea24e5..5524c1ed1f59c 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/CorrectionMapsLoader.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/CorrectionMapsLoader.h @@ -27,29 +27,13 @@ namespace o2 namespace framework { class ProcessingContext; -class ConcreteDataMatcher; class InputSpec; class ConfigParamSpec; -class ConfigParamRegistry; -class InitContext; } // namespace framework namespace tpc { -struct CorrectionMapsLoaderGloOpts { - int lumiType = 0; ///< what estimator to used for corrections scaling: 0: no scaling, 1: CTP, 2: IDC - int lumiMode = 0; ///< what corrections method to use: 0: classical scaling, 1: Using of the derivative map, 2: Using of the derivative map for MC - bool enableMShapeCorrection = false; - bool requestCTPLumi = true; //< request CTP Lumi regardless of what is used for corrections scaling - bool checkCTPIDCconsistency = true; //< check the selected CTP or IDC scaling source being consistent with mean scaler of the map - - bool needTPCScalersWorkflow() const - { - return lumiType == 2 || enableMShapeCorrection; - } -}; - class CorrectionMapsLoader : public o2::gpu::CorrectionMapsHelper { public: @@ -58,27 +42,15 @@ class CorrectionMapsLoader : public o2::gpu::CorrectionMapsHelper CorrectionMapsLoader(const CorrectionMapsLoader&) = delete; #ifndef GPUCA_GPUCODE_DEVICE - bool accountCCDBInputs(const o2::framework::ConcreteDataMatcher& matcher, void* obj); void extractCCDBInputs(o2::framework::ProcessingContext& pc); - void updateVDrift(float vdriftCorr, float vdrifRef, float driftTimeOffset = 0); - void init(o2::framework::InitContext& ic); - void copySettings(const CorrectionMapsLoader& src); - void updateInverse(); /// recalculate inverse correction - void checkMeanScaleConsistency(float meanLumi, float threshold) const; - float getMapMeanRate(const o2::gpu::TPCFastTransform* mp, bool lumiOverridden) const; - static void requestCCDBInputs(std::vector& inputs, std::vector& options, const CorrectionMapsLoaderGloOpts& gloOpts); + static void requestInputs(std::vector& inputs, std::vector& options); + // static CorrectionMapsLoaderGloOpts parseGlobalOptions(const o2::framework::ConfigParamRegistry& opts); static void addGlobalOptions(std::vector& options); - static void addOptions(std::vector& options); - static CorrectionMapsLoaderGloOpts parseGlobalOptions(const o2::framework::ConfigParamRegistry& opts); protected: static void addOption(std::vector& options, o2::framework::ConfigParamSpec&& osp); static void addInput(std::vector& inputs, o2::framework::InputSpec&& isp); - - float mInstLumiCTPFactor = 1.0; // multiplicative factor for inst. lumi - int mLumiCTPSource = 0; // 0: main, 1: alternative CTP lumi source - std::unique_ptr mCorrMapMShape{nullptr}; bool mIDC2CTPFallbackActive = false; // flag indicating that fallback from IDC to CTP scaling is active #endif }; diff --git a/Detectors/TPC/calibration/include/TPCCalibration/CorrectionMapsLoaderFull.h b/Detectors/TPC/calibration/include/TPCCalibration/CorrectionMapsLoaderFull.h new file mode 100644 index 0000000000000..e60fa874c6d9f --- /dev/null +++ b/Detectors/TPC/calibration/include/TPCCalibration/CorrectionMapsLoaderFull.h @@ -0,0 +1,64 @@ +// 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 CorrectionMapsLoaderFull.h +/// \brief Helper class to access load maps from CCDB +/// \author matthias.kleiner@cern.ch + +#ifndef TPC_CORRECTION_MAPS_LOADERFULL_H_ +#define TPC_CORRECTION_MAPS_LOADERFULL_H_ + +#include +#include "CorrectionMapsHelperFull.h" +#include "CorrectionMapsHelper.h" + +namespace o2 +{ +namespace framework +{ +class ProcessingContext; +class ConcreteDataMatcher; +class InputSpec; +class ConfigParamSpec; +class InitContext; +} // namespace framework + +namespace tpc +{ + +class CorrectionMapsLoaderFull : public o2::gpu::CorrectionMapsHelperFull +{ + public: + CorrectionMapsLoaderFull() = default; + ~CorrectionMapsLoaderFull() = default; + CorrectionMapsLoaderFull(const CorrectionMapsLoaderFull&) = delete; + + bool accountCCDBInputs(const o2::framework::ConcreteDataMatcher& matcher, void* obj); + void extractCCDBInputs(o2::framework::ProcessingContext& pc, float tpcScaler = -1.f); + void init(o2::framework::InitContext& ic, bool idcsAvailable); + void checkMeanScaleConsistency(float meanLumi, float threshold) const; + + static void requestCCDBInputs(std::vector& inputs, const o2::tpc::CorrectionMapsLoaderGloOpts& gloOpts); + + protected: + static void addOption(std::vector& options, o2::framework::ConfigParamSpec&& osp); + static void addInput(std::vector& inputs, o2::framework::InputSpec&& isp); + + float mInstLumiCTPFactor = 1.0; // multiplicative factor for inst. lumi + int mLumiCTPSource = 0; // 0: main, 1: alternative CTP lumi source + bool mIDC2CTPFallbackActive = false; // flag indicating that fallback from IDC to CTP scaling is active +}; + +} // namespace tpc + +} // namespace o2 + +#endif diff --git a/Detectors/TPC/calibration/src/CalculatedEdx.cxx b/Detectors/TPC/calibration/src/CalculatedEdx.cxx index 478acda1189c2..c0f836e6b8452 100644 --- a/Detectors/TPC/calibration/src/CalculatedEdx.cxx +++ b/Detectors/TPC/calibration/src/CalculatedEdx.cxx @@ -32,8 +32,9 @@ using namespace o2::tpc; CalculatedEdx::CalculatedEdx() { - mTPCCorrMapsHelper.setOwner(true); - mTPCCorrMapsHelper.setCorrMap(TPCFastTransformHelperO2::instance()->create(0)); + std::vector buffer; + gpu::TPCFastTransformPOD::create(buffer, *TPCFastTransformHelperO2::instance()->create(0)); + mTPCCorrMapsHelper.setCorrMap(std::move(buffer)); } void CalculatedEdx::setMembers(std::vector* tpcTrackClIdxVecInput, const o2::tpc::ClusterNativeAccess& clIndex, std::vector* vTPCTracksArrayInp) diff --git a/Detectors/TPC/calibration/src/CorrectionMapsLoader.cxx b/Detectors/TPC/calibration/src/CorrectionMapsLoader.cxx index 038fe3c34e140..f20967f29b9f8 100644 --- a/Detectors/TPC/calibration/src/CorrectionMapsLoader.cxx +++ b/Detectors/TPC/calibration/src/CorrectionMapsLoader.cxx @@ -10,155 +10,74 @@ // or submit itself to any jurisdiction. #include "TPCCalibration/CorrectionMapsLoader.h" -#include "TPCCalibration/CorrMapParam.h" -#include "TPCReconstruction/TPCFastTransformHelperO2.h" -#include "TPCBaseRecSim/CDBInterface.h" #include "Framework/Logger.h" #include "Framework/ProcessingContext.h" -#include "Framework/CCDBParamSpec.h" #include "Framework/InputRecord.h" #include "Framework/ConfigParamSpec.h" -#include "Framework/ConcreteDataMatcher.h" -#include "Framework/InitContext.h" -#include "Framework/DeviceSpec.h" -#include "Framework/ConfigParamRegistry.h" -#include "DataFormatsCTP/LumiInfo.h" -#include "TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h" - +#include "TPCFastTransformPOD.h" using namespace o2::tpc; using namespace o2::framework; #ifndef GPUCA_GPUCODE_DEVICE -//________________________________________________________ -void CorrectionMapsLoader::updateVDrift(float vdriftCorr, float vdrifRef, float driftTimeOffset) -{ - o2::tpc::TPCFastTransformHelperO2::instance()->updateCalibration(*mCorrMap, 0, vdriftCorr, vdrifRef, driftTimeOffset); - if (mCorrMapRef) { - o2::tpc::TPCFastTransformHelperO2::instance()->updateCalibration(*mCorrMapRef, 0, vdriftCorr, vdrifRef, driftTimeOffset); - } - if (mCorrMapMShape) { - o2::tpc::TPCFastTransformHelperO2::instance()->updateCalibration(*mCorrMapMShape, 0, vdriftCorr, vdrifRef, driftTimeOffset); - } -} - //________________________________________________________ void CorrectionMapsLoader::extractCCDBInputs(ProcessingContext& pc) { - pc.inputs().get("tpcCorrPar"); - pc.inputs().get("tpcCorrMap"); - pc.inputs().get("tpcCorrMapRef"); - const int maxDumRep = 5; - int dumRep = 0; - o2::ctp::LumiInfo lumiObj; - static o2::ctp::LumiInfo lumiPrev; - - if (getLumiScaleType() == 2 || mIDC2CTPFallbackActive) { - float tpcScaler = pc.inputs().get("tpcscaler"); - // check if tpcScaler is valid and CTP fallback is allowed - if (tpcScaler == -1.f) { - const bool canUseCTPScaling = mCorrMap && mCorrMapRef && mCorrMap->isIDCSet() && mCorrMapRef->isIDCSet() && mCorrMap->isLumiSet() && mCorrMapRef->isLumiSet(); - if (canUseCTPScaling) { - LOGP(info, "Invalid TPC scaler value {} received for IDC-based scaling! Using CTP fallback", tpcScaler); - mIDC2CTPFallbackActive = true; - setMeanLumi(mCorrMap->getLumi(), false); - setMeanLumiRef(mCorrMapRef->getLumi()); - setLumiScaleType(1); - } else if (mCorrMap) { - // CTP scaling is not possible, dont do any scaling to avoid applying wrong corrections - const float storedIDC = mCorrMap->getIDC(); - LOGP(warning, "Invalid TPC scaler value {} received for IDC-based scaling! CTP fallback not possible, using stored IDC of {} from the map to avoid applying wrong corrections", tpcScaler, storedIDC); - setInstLumi(storedIDC); - } - } else { - if (mIDC2CTPFallbackActive) { - // reset back to normal operation - LOGP(info, "Valid TPC scaler value {} received, switching back to IDC-based scaling", tpcScaler); - mIDC2CTPFallbackActive = false; - setMeanLumi(mCorrMap->getIDC(), false); - setMeanLumiRef(mCorrMapRef->getIDC()); - setLumiScaleType(2); - } - // correct IDC received - setInstLumi(tpcScaler); - } - } - - if (getLumiCTPAvailable() && mInstCTPLumiOverride <= 0.) { - if (pc.inputs().get>("CTPLumi").size() == sizeof(o2::ctp::LumiInfo)) { - lumiPrev = lumiObj = pc.inputs().get("CTPLumi"); - } else { - if (dumRep < maxDumRep && lumiPrev.nHBFCounted == 0 && lumiPrev.nHBFCountedFV0 == 0) { - LOGP(alarm, "Previous TF lumi used to substitute dummy input is empty, warning {} of {}", ++dumRep, maxDumRep); - } - lumiObj = lumiPrev; - } - setInstLumiCTP(mInstLumiCTPFactor * (mLumiCTPSource == 0 ? lumiObj.getLumi() : lumiObj.getLumiAlt())); - if (getLumiScaleType() == 1) { - setInstLumi(getInstLumiCTP()); - } + const bool lumiValid = pc.inputs().isValid("lumiCTP"); + if(lumiValid) { + mInstLumiCTP = pc.inputs().get("lumiCTP"); } - if (getUseMShapeCorrection()) { - LOGP(info, "Setting M-Shape map"); - const auto mapMShape = pc.inputs().get("mshape"); - const_cast(mapMShape.get())->rectifyAfterReadingFromFile(); - mCorrMapMShape = std::unique_ptr(new TPCFastTransform); - mCorrMapMShape->cloneFromObject(*(mapMShape.get()), nullptr); - setCorrMapMShape(mCorrMapMShape.get()); - setUpdatedMapMShape(); + const bool mapValid = pc.inputs().isValid("corrMap"); + if(!mapValid) { + LOGP(info, "No correction map found in the input record!"); + return; } - // update inverse in case it is requested - if (!mScaleInverse) { - updateInverse(); - } - reportScaling(); + // get the raw buffer and reinterpret as TPCFastTransformPOD + auto const& raw = pc.inputs().get("corrMap"); + setCorrMap(&gpu::TPCFastTransformPOD::get(raw)); + setUpdatedMap(); } //________________________________________________________ -void CorrectionMapsLoader::requestCCDBInputs(std::vector& inputs, std::vector& options, const CorrectionMapsLoaderGloOpts& gloOpts) +void CorrectionMapsLoader::requestInputs(std::vector& inputs, std::vector& options) { - if (gloOpts.lumiMode == 0) { - addInput(inputs, {"tpcCorrMap", "TPC", "CorrMap", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalCorrMap), {}, 1)}); // time-dependent - addInput(inputs, {"tpcCorrMapRef", "TPC", "CorrMapRef", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalCorrMapRef), {}, 0)}); // load once - } else if (gloOpts.lumiMode == 1) { - addInput(inputs, {"tpcCorrMap", "TPC", "CorrMap", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalCorrMap), {}, 1)}); // time-dependent - addInput(inputs, {"tpcCorrMapRef", "TPC", "CorrMapRef", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalCorrDerivMap), {}, 1)}); // time-dependent - } else if (gloOpts.lumiMode == 2) { - // for MC corrections - addInput(inputs, {"tpcCorrMap", "TPC", "CorrMap", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalCorrMapMC), {}, 1)}); // time-dependent - addInput(inputs, {"tpcCorrMapRef", "TPC", "CorrMapRef", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalCorrDerivMapMC), {}, 1)}); // time-dependent - } else { - LOG(fatal) << "Correction mode unknown! Choose either 0 (default) or 1 (derivative map) for flag corrmap-lumi-mode."; - } - - if (gloOpts.requestCTPLumi) { - addInput(inputs, {"CTPLumi", "CTP", "LUMI", 0, Lifetime::Timeframe}); - } - - if (gloOpts.lumiType == 2) { - addInput(inputs, {"tpcscaler", o2::header::gDataOriginTPC, "TPCSCALER", 0, Lifetime::Timeframe}); - } - - addInput(inputs, {"tpcCorrPar", "TPC", "CorrMapParam", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CorrMapParam), {}, 0)}); // load once - - if (gloOpts.enableMShapeCorrection) { - addInput(inputs, {"mshape", o2::header::gDataOriginTPC, "TPCMSHAPE", 0, Lifetime::Timeframe}); - } - addOptions(options); + addInput(inputs, {"corrMap", o2::header::gDataOriginTPC, "TPCCORRMAP", 0, Lifetime::Timeframe}); + addInput(inputs, {"lumiCTP", o2::header::gDataOriginCTP, "LUMICTP", 0, Lifetime::Timeframe}); } -//________________________________________________________ -void CorrectionMapsLoader::addOptions(std::vector& options) +void CorrectionMapsLoader::addInput(std::vector& inputs, InputSpec&& isp) { - // these are options which should be added at the level of device using TPC corrections - // At the moment - nothing, all options are moved to configurable param CorrMapParam - addOption(options, ConfigParamSpec{"recalculate-inverse-correction", o2::framework::VariantType::Bool, false, {"recalculate the inverse correction in case lumi mode 1 or 2 is used"}}); - addOption(options, ConfigParamSpec{"nthreads-inverse-correction", o2::framework::VariantType::Int, 4, {"Number of threads used for calculating the inverse correction (-1=all threads)"}}); + if (std::find(inputs.begin(), inputs.end(), isp) == inputs.end()) { + inputs.emplace_back(isp); + } } -//________________________________________________________ +// CorrectionMapsLoaderGloOpts CorrectionMapsLoader::parseGlobalOptions(const o2::framework::ConfigParamRegistry& opts) +// { +// CorrectionMapsLoaderGloOpts tpcopt; +// auto lumiTypeVal = opts.get("lumi-type"); +// if (lumiTypeVal < -1 || lumiTypeVal > 2) { +// LOGP(fatal, "Invalid lumi-type value: {}", lumiTypeVal); +// } +// tpcopt.lumiType = static_cast(lumiTypeVal); + +// auto lumiModeVal = opts.get("corrmap-lumi-mode"); +// if (lumiModeVal < -1 || lumiModeVal > 2) { +// LOGP(fatal, "Invalid corrmap-lumi-mode value: {}", lumiModeVal); +// } +// tpcopt.lumiMode = static_cast(lumiModeVal); + +// tpcopt.enableMShapeCorrection = opts.get("enable-M-shape-correction"); +// tpcopt.requestCTPLumi = !opts.get("disable-ctp-lumi-request"); +// tpcopt.checkCTPIDCconsistency = !opts.get("disable-lumi-type-consistency-check"); +// if (!tpcopt.requestCTPLumi && tpcopt.lumiType == LumiScaleType::CTPLumi) { +// LOGP(fatal, "Scaling with CTP Lumi is requested but this input is disabled"); +// } +// return tpcopt; +// } + void CorrectionMapsLoader::addGlobalOptions(std::vector& options) { // these are options which should be added at the workflow level, since they modify the inputs of the devices @@ -169,30 +88,6 @@ void CorrectionMapsLoader::addGlobalOptions(std::vector& option addOption(options, ConfigParamSpec{"disable-lumi-type-consistency-check", o2::framework::VariantType::Bool, false, {"disable check of selected CTP or IDC scaling source being consistent with the map"}}); } -//________________________________________________________ -CorrectionMapsLoaderGloOpts CorrectionMapsLoader::parseGlobalOptions(const o2::framework::ConfigParamRegistry& opts) -{ - CorrectionMapsLoaderGloOpts tpcopt; - tpcopt.lumiType = opts.get("lumi-type"); - tpcopt.lumiMode = opts.get("corrmap-lumi-mode"); - tpcopt.enableMShapeCorrection = opts.get("enable-M-shape-correction"); - tpcopt.requestCTPLumi = !opts.get("disable-ctp-lumi-request"); - tpcopt.checkCTPIDCconsistency = !opts.get("disable-lumi-type-consistency-check"); - if (!tpcopt.requestCTPLumi && tpcopt.lumiType == 1) { - LOGP(fatal, "Scaling with CTP Lumi is requested but this input is disabled"); - } - return tpcopt; -} - -//________________________________________________________ -void CorrectionMapsLoader::addInput(std::vector& inputs, InputSpec&& isp) -{ - if (std::find(inputs.begin(), inputs.end(), isp) == inputs.end()) { - inputs.emplace_back(isp); - } -} - -//________________________________________________________ void CorrectionMapsLoader::addOption(std::vector& options, ConfigParamSpec&& osp) { if (std::find(options.begin(), options.end(), osp) == options.end()) { @@ -200,182 +95,4 @@ void CorrectionMapsLoader::addOption(std::vector& options, Conf } } -//________________________________________________________ -bool CorrectionMapsLoader::accountCCDBInputs(const ConcreteDataMatcher& matcher, void* obj) -{ - if (matcher == ConcreteDataMatcher("TPC", "CorrMap", 0)) { - setCorrMap((o2::gpu::TPCFastTransform*)obj); - mCorrMap->rectifyAfterReadingFromFile(); - mCorrMap->setCTP2IDCFallBackThreshold(o2::tpc::CorrMapParam::Instance().CTP2IDCFallBackThreshold); - if (getMeanLumiOverride() != 0) { - if (getLumiScaleType() == 1) { - mCorrMap->setLumi(getMeanLumiOverride()); - LOGP(info, "CorrMap mean lumi rate is overridden to {}", mCorrMap->getLumi()); - } else if (getLumiScaleType() == 2) { - mCorrMap->setIDC(getMeanLumiOverride()); - LOGP(info, "CorrMap mean IDC rate is overridden to {}", mCorrMap->getIDC()); - } - } - float mapMeanRate = 0; - if (getLumiScaleType() == 1) { - mapMeanRate = mCorrMap->getLumi(); - } else if (getLumiScaleType() == 2) { - mapMeanRate = mCorrMap->getIDC(); - } - if (mCheckCTPIDCConsistency) { - checkMeanScaleConsistency(mapMeanRate, mCorrMap->getCTP2IDCFallBackThreshold()); - } - if (getMeanLumiOverride() == 0 && mapMeanRate > 0.) { - setMeanLumi(mapMeanRate, false); - } - LOGP(debug, "MeanLumiOverride={} MeanLumiMap={} -> meanLumi = {}", getMeanLumiOverride(), mapMeanRate, getMeanLumi()); - setUpdatedMap(); - return true; - } - if (matcher == ConcreteDataMatcher("TPC", "CorrMapRef", 0)) { - setCorrMapRef((o2::gpu::TPCFastTransform*)obj); - mCorrMapRef->rectifyAfterReadingFromFile(); - mCorrMapRef->setCTP2IDCFallBackThreshold(o2::tpc::CorrMapParam::Instance().CTP2IDCFallBackThreshold); - if (getMeanLumiRefOverride() != 0) { - if (getLumiScaleType() == 1) { - mCorrMapRef->setLumi(getMeanLumiRefOverride()); - LOGP(info, "CorrMapRef mean lumi rate is overridden to {}", mCorrMapRef->getLumi()); - } else if (getLumiScaleType() == 2) { - mCorrMapRef->setIDC(getMeanLumiRefOverride()); - LOGP(info, "CorrMapRef mean IDC rate is overridden to {}", mCorrMapRef->getIDC()); - } - } - float mapRefMeanRate = 0; - if (getLumiScaleType() == 1) { - mapRefMeanRate = mCorrMapRef->getLumi(); - } else if (getLumiScaleType() == 2) { - mapRefMeanRate = mCorrMapRef->getIDC(); - } - if (mCheckCTPIDCConsistency) { - checkMeanScaleConsistency(mapRefMeanRate, mCorrMapRef->getCTP2IDCFallBackThreshold()); - } - if (getMeanLumiRefOverride() == 0) { - setMeanLumiRef(mapRefMeanRate); - } - LOGP(debug, "MeanLumiRefOverride={} MeanLumiMap={} -> meanLumi = {}", getMeanLumiRefOverride(), mapRefMeanRate, getMeanLumiRef()); - setUpdatedMapRef(); - return true; - } - if (matcher == ConcreteDataMatcher("TPC", "CorrMapParam", 0)) { - const auto& par = o2::tpc::CorrMapParam::Instance(); - mMeanLumiOverride = par.lumiMean; // negative value switches off corrections !!! - mMeanLumiRefOverride = par.lumiMeanRef; - mInstCTPLumiOverride = par.lumiInst; - mInstLumiCTPFactor = par.lumiInstFactor; - mLumiCTPSource = par.ctpLumiSource; - - if (mMeanLumiOverride != 0.) { - setMeanLumi(mMeanLumiOverride, false); - } - if (mMeanLumiRefOverride != 0.) { - setMeanLumiRef(mMeanLumiRefOverride); - } - if (mInstCTPLumiOverride != 0.) { - setInstLumiCTP(mInstCTPLumiOverride * mInstLumiCTPFactor); - if (getLumiScaleType() == 1) { - setInstLumi(getInstLumiCTP(), false); - } - } - setUpdatedLumi(); - int scaleType = getLumiScaleType(); - const std::array lumiS{"OFF", "CTP", "TPC scaler"}; - if (scaleType >= lumiS.size()) { - LOGP(fatal, "Wrong corrmap-lumi-mode provided!"); - } - - LOGP(info, "TPC correction map params updated: SP corrections: {} (corr.map scaling type={}, override values: lumiMean={} lumiRefMean={} lumiScaleMode={}), CTP Lumi: source={} lumiInstOverride={} , LumiInst scale={} ", - canUseCorrections() ? "ON" : "OFF", - lumiS[scaleType], mMeanLumiOverride, mMeanLumiRefOverride, mLumiScaleMode, mLumiCTPSource, mInstCTPLumiOverride, mInstLumiCTPFactor); - } - return false; -} - -//________________________________________________________ -void CorrectionMapsLoader::init(o2::framework::InitContext& ic) -{ - if (getLumiScaleMode() < 0) { - LOGP(fatal, "TPC correction lumi scaling mode is not set"); - } - const auto& inputRouts = ic.services().get().inputs; - bool foundCTP = false, foundTPCScl = false, foundMShape = false; - for (const auto& route : inputRouts) { - if (route.matcher == InputSpec{"CTPLumi", "CTP", "LUMI", 0, Lifetime::Timeframe}) { - foundCTP = true; - } else if (route.matcher == InputSpec{"tpcscaler", o2::header::gDataOriginTPC, "TPCSCALER", 0, Lifetime::Timeframe}) { - foundTPCScl = true; - } else if (route.matcher == InputSpec{"mshape", o2::header::gDataOriginTPC, "TPCMSHAPE", 0, Lifetime::Timeframe}) { - foundMShape = true; - } - } - setLumiCTPAvailable(foundCTP); - enableMShapeCorrection(foundMShape); - if ((getLumiScaleType() == 1 && !foundCTP) || (getLumiScaleType() == 2 && !foundTPCScl)) { - LOGP(fatal, "Lumi scaling source {}({}) is not available for TPC correction", getLumiScaleType(), getLumiScaleType() == 1 ? "CTP" : "TPCScaler"); - } - - if ((getLumiScaleMode() == 1) || (getLumiScaleMode() == 2)) { - mScaleInverse = !(ic.options().get("recalculate-inverse-correction")); - } else { - mScaleInverse = true; - } - const int nthreadsInv = (ic.options().get("nthreads-inverse-correction")); - (nthreadsInv < 0) ? TPCFastSpaceChargeCorrectionHelper::instance()->setNthreadsToMaximum() : TPCFastSpaceChargeCorrectionHelper::instance()->setNthreads(nthreadsInv); -} - -//________________________________________________________ -void CorrectionMapsLoader::copySettings(const CorrectionMapsLoader& src) -{ - setInstLumi(src.getInstLumi(), false); - setInstLumiCTP(src.getInstLumiCTP()); - setMeanLumi(src.getMeanLumi(), false); - setLumiCTPAvailable(src.getLumiCTPAvailable()); - setMeanLumiRef(src.getMeanLumiRef()); - setLumiScaleType(src.getLumiScaleType()); - setMeanLumiOverride(src.getMeanLumiOverride()); - setMeanLumiRefOverride(src.getMeanLumiRefOverride()); - setInstCTPLumiOverride(src.getInstCTPLumiOverride()); - setLumiScaleMode(src.getLumiScaleMode()); - enableMShapeCorrection(src.getUseMShapeCorrection()); - mInstLumiCTPFactor = src.mInstLumiCTPFactor; - mLumiCTPSource = src.mLumiCTPSource; - mLumiScaleMode = src.mLumiScaleMode; - mScaleInverse = src.getScaleInverse(); - mIDC2CTPFallbackActive = src.mIDC2CTPFallbackActive; -} - -void CorrectionMapsLoader::updateInverse() -{ - if (mLumiScaleMode == 1 || mLumiScaleMode == 2) { - LOGP(info, "Recalculating the inverse correction"); - setUpdatedMap(); - std::vector scaling{1, mLumiScale}; - std::vector corr{&(mCorrMap->getCorrection()), &(mCorrMapRef->getCorrection())}; - if (mCorrMapMShape) { - scaling.emplace_back(1); - corr.emplace_back(&(mCorrMapMShape->getCorrection())); - } - TPCFastSpaceChargeCorrectionHelper::instance()->initInverse(corr, scaling, false); - } else { - LOGP(info, "Reinitializing inverse correction with lumi scale mode {} not supported for now", mLumiScaleMode); - } -} - -void CorrectionMapsLoader::checkMeanScaleConsistency(float meanLumi, float threshold) const -{ - if (getLumiScaleType() == 1) { - if (meanLumi < threshold) { - LOGP(fatal, "CTP Lumi scaling source is requested, but the map mean scale {} is below the threshold {}", meanLumi, threshold); - } - } else if (getLumiScaleType() == 2) { - if (meanLumi > threshold) { - LOGP(fatal, "IDC scaling source is requested, but the map mean scale {} is above the threshold {}", meanLumi, threshold); - } - } -} - #endif // #ifndef GPUCA_GPUCODE_DEVICE diff --git a/Detectors/TPC/calibration/src/CorrectionMapsLoaderFull.cxx b/Detectors/TPC/calibration/src/CorrectionMapsLoaderFull.cxx new file mode 100644 index 0000000000000..717dcaf62779a --- /dev/null +++ b/Detectors/TPC/calibration/src/CorrectionMapsLoaderFull.cxx @@ -0,0 +1,253 @@ +// 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 "TPCCalibration/CorrectionMapsLoaderFull.h" +#include "TPCCalibration/CorrMapParam.h" +#include "TPCBaseRecSim/CDBTypes.h" +#include "Framework/Logger.h" +#include "Framework/ProcessingContext.h" +#include "Framework/CCDBParamSpec.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/ConcreteDataMatcher.h" +#include "Framework/InitContext.h" +#include "Framework/DeviceSpec.h" +#include "DataFormatsCTP/LumiInfo.h" + +using namespace o2::tpc; +using namespace o2::framework; +// using namespace o2::gpu; + +//________________________________________________________ +void CorrectionMapsLoaderFull::extractCCDBInputs(ProcessingContext& pc, float tpcScaler) +{ + pc.inputs().get("tpcCorrPar"); + pc.inputs().get("tpcCorrMap"); + pc.inputs().get("tpcCorrMapRef"); + const int maxDumRep = 5; + int dumRep = 0; + o2::ctp::LumiInfo lumiObj; + static o2::ctp::LumiInfo lumiPrev; + + if (getLumiScaleType() == LumiScaleType::TPCScaler || mIDC2CTPFallbackActive) { + // check if tpcScaler is valid and CTP fallback is allowed + if (tpcScaler == -1.f) { + const bool canUseCTPScaling = mCorrMap && mCorrMapRef && mCorrMap->isIDCSet() && mCorrMapRef->isIDCSet() && mCorrMap->isLumiSet() && mCorrMapRef->isLumiSet(); + if (canUseCTPScaling) { + LOGP(info, "Invalid TPC scaler value {} received for IDC-based scaling! Using CTP fallback", tpcScaler); + mIDC2CTPFallbackActive = true; + setMeanLumi(mCorrMap->getLumi(), false); + setMeanLumiRef(mCorrMapRef->getLumi()); + setLumiScaleType(LumiScaleType::CTPLumi); + } else if (mCorrMap) { + // CTP scaling is not possible, dont do any scaling to avoid applying wrong corrections + const float storedIDC = mCorrMap->getIDC(); + LOGP(warning, "Invalid TPC scaler value {} received for IDC-based scaling! CTP fallback not possible, using stored IDC of {} from the map to avoid applying wrong corrections", tpcScaler, storedIDC); + setInstLumi(storedIDC); + } + } else { + if (mIDC2CTPFallbackActive) { + // reset back to normal operation + LOGP(info, "Valid TPC scaler value {} received, switching back to IDC-based scaling", tpcScaler); + mIDC2CTPFallbackActive = false; + setMeanLumi(mCorrMap->getIDC(), false); + setMeanLumiRef(mCorrMapRef->getIDC()); + setLumiScaleType(LumiScaleType::TPCScaler); + } + // correct IDC received + setInstLumi(tpcScaler); + } + } + + if (getLumiCTPAvailable() && mInstCTPLumiOverride <= 0.) { + if (pc.inputs().get>("CTPLumi").size() == sizeof(o2::ctp::LumiInfo)) { + lumiPrev = lumiObj = pc.inputs().get("CTPLumi"); + } else { + if (dumRep < maxDumRep && lumiPrev.nHBFCounted == 0 && lumiPrev.nHBFCountedFV0 == 0) { + LOGP(alarm, "Previous TF lumi used to substitute dummy input is empty, warning {} of {}", ++dumRep, maxDumRep); + } + lumiObj = lumiPrev; + } + setInstLumiCTP(mInstLumiCTPFactor * (mLumiCTPSource == 0 ? lumiObj.getLumi() : lumiObj.getLumiAlt())); + if (getLumiScaleType() == LumiScaleType::CTPLumi) { + setInstLumi(getInstLumiCTP()); + } + } + + reportScaling(); +} + +//________________________________________________________ +void CorrectionMapsLoaderFull::requestCCDBInputs(std::vector& inputs, const CorrectionMapsLoaderGloOpts& gloOpts) +{ + LOGP(info, "Requesting CCDB inputs for TPC correction maps with lumiType={} and lumiMode={}", static_cast(gloOpts.lumiType), static_cast(gloOpts.lumiMode)); + if (gloOpts.lumiMode == LumiScaleMode::Linear) { + addInput(inputs, {"tpcCorrMap", "TPC", "CorrMap", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalCorrMap), {}, 1)}); // time-dependent + addInput(inputs, {"tpcCorrMapRef", "TPC", "CorrMapRef", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalCorrMapRef), {}, 0)}); // load once + } else if (gloOpts.lumiMode == LumiScaleMode::DerivativeMap) { + addInput(inputs, {"tpcCorrMap", "TPC", "CorrMap", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalCorrMap), {}, 1)}); // time-dependent + addInput(inputs, {"tpcCorrMapRef", "TPC", "CorrMapRef", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalCorrDerivMap), {}, 1)}); // time-dependent + } else if (gloOpts.lumiMode == LumiScaleMode::DerivativeMapMC) { + // for MC corrections + addInput(inputs, {"tpcCorrMap", "TPC", "CorrMap", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalCorrMapMC), {}, 1)}); // time-dependent + addInput(inputs, {"tpcCorrMapRef", "TPC", "CorrMapRef", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalCorrDerivMapMC), {}, 1)}); // time-dependent + } else { + LOG(fatal) << "Correction mode unknown! Choose either 0 (default) or 1 (derivative map) for flag corrmap-lumi-mode."; + } + + if (gloOpts.requestCTPLumi) { + addInput(inputs, {"CTPLumi", "CTP", "LUMI", 0, Lifetime::Timeframe}); + } + + addInput(inputs, {"tpcCorrPar", "TPC", "CorrMapParam", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CorrMapParam), {}, 0)}); // load once +} + +//________________________________________________________ +void CorrectionMapsLoaderFull::addInput(std::vector& inputs, InputSpec&& isp) +{ + if (std::find(inputs.begin(), inputs.end(), isp) == inputs.end()) { + inputs.emplace_back(isp); + } +} + +//________________________________________________________ +void CorrectionMapsLoaderFull::addOption(std::vector& options, ConfigParamSpec&& osp) +{ + if (std::find(options.begin(), options.end(), osp) == options.end()) { + options.emplace_back(osp); + } +} + +//________________________________________________________ +bool CorrectionMapsLoaderFull::accountCCDBInputs(const ConcreteDataMatcher& matcher, void* obj) +{ + if (matcher == ConcreteDataMatcher("TPC", "CorrMap", 0)) { + setCorrMap((o2::gpu::TPCFastTransform*)obj); + mCorrMap->rectifyAfterReadingFromFile(); + mCorrMap->setCTP2IDCFallBackThreshold(o2::tpc::CorrMapParam::Instance().CTP2IDCFallBackThreshold); + if (getMeanLumiOverride() != 0) { + if (getLumiScaleType() == LumiScaleType::CTPLumi) { + mCorrMap->setLumi(getMeanLumiOverride()); + LOGP(info, "CorrMap mean lumi rate is overridden to {}", mCorrMap->getLumi()); + } else if (getLumiScaleType() == LumiScaleType::TPCScaler) { + mCorrMap->setIDC(getMeanLumiOverride()); + LOGP(info, "CorrMap mean IDC rate is overridden to {}", mCorrMap->getIDC()); + } + } + float mapMeanRate = 0; + if (getLumiScaleType() == LumiScaleType::CTPLumi) { + mapMeanRate = mCorrMap->getLumi(); + } else if (getLumiScaleType() == LumiScaleType::TPCScaler) { + mapMeanRate = mCorrMap->getIDC(); + } + if (mCheckCTPIDCConsistency) { + checkMeanScaleConsistency(mapMeanRate, mCorrMap->getCTP2IDCFallBackThreshold()); + } + if (getMeanLumiOverride() == 0 && mapMeanRate > 0.) { + setMeanLumi(mapMeanRate, false); + } + LOGP(debug, "MeanLumiOverride={} MeanLumiMap={} -> meanLumi = {}", getMeanLumiOverride(), mapMeanRate, getMeanLumi()); + setUpdatedMap(); + return true; + } + if (matcher == ConcreteDataMatcher("TPC", "CorrMapRef", 0)) { + setCorrMapRef((o2::gpu::TPCFastTransform*)obj); + mCorrMapRef->rectifyAfterReadingFromFile(); + mCorrMapRef->setCTP2IDCFallBackThreshold(o2::tpc::CorrMapParam::Instance().CTP2IDCFallBackThreshold); + if (getMeanLumiRefOverride() != 0) { + if (getLumiScaleType() == LumiScaleType::CTPLumi) { + mCorrMapRef->setLumi(getMeanLumiRefOverride()); + LOGP(info, "CorrMapRef mean lumi rate is overridden to {}", mCorrMapRef->getLumi()); + } else if (getLumiScaleType() == LumiScaleType::TPCScaler) { + mCorrMapRef->setIDC(getMeanLumiRefOverride()); + LOGP(info, "CorrMapRef mean IDC rate is overridden to {}", mCorrMapRef->getIDC()); + } + } + float mapRefMeanRate = 0; + if (getLumiScaleType() == LumiScaleType::CTPLumi) { + mapRefMeanRate = mCorrMapRef->getLumi(); + } else if (getLumiScaleType() == LumiScaleType::TPCScaler) { + mapRefMeanRate = mCorrMapRef->getIDC(); + } + if (mCheckCTPIDCConsistency) { + checkMeanScaleConsistency(mapRefMeanRate, mCorrMapRef->getCTP2IDCFallBackThreshold()); + } + if (getMeanLumiRefOverride() == 0) { + setMeanLumiRef(mapRefMeanRate); + } + LOGP(debug, "MeanLumiRefOverride={} MeanLumiMap={} -> meanLumi = {}", getMeanLumiRefOverride(), mapRefMeanRate, getMeanLumiRef()); + setUpdatedMapRef(); + return true; + } + if (matcher == ConcreteDataMatcher("TPC", "CorrMapParam", 0)) { + const auto& par = o2::tpc::CorrMapParam::Instance(); + mMeanLumiOverride = par.lumiMean; // negative value switches off corrections !!! + mMeanLumiRefOverride = par.lumiMeanRef; + mInstCTPLumiOverride = par.lumiInst; + mInstLumiCTPFactor = par.lumiInstFactor; + mLumiCTPSource = par.ctpLumiSource; + + if (mMeanLumiOverride != 0.) { + setMeanLumi(mMeanLumiOverride, false); + } + if (mMeanLumiRefOverride != 0.) { + setMeanLumiRef(mMeanLumiRefOverride); + } + if (mInstCTPLumiOverride != 0.) { + setInstLumiCTP(mInstCTPLumiOverride * mInstLumiCTPFactor); + if (getLumiScaleType() == LumiScaleType::CTPLumi) { + setInstLumi(getInstLumiCTP(), false); + } + } + setUpdatedLumi(); + int scaleType = static_cast(getLumiScaleType()); + const std::array lumiS{"OFF", "CTP", "TPC scaler"}; + if (scaleType >= lumiS.size()) { + LOGP(fatal, "Wrong corrmap-lumi-mode provided!"); + } + + LOGP(info, "TPC correction map params updated: SP corrections: {} (corr.map scaling type={}, override values: lumiMean={} lumiRefMean={} lumiScaleMode={}), CTP Lumi: source={} lumiInstOverride={} , LumiInst scale={} ", + canUseCorrections() ? "ON" : "OFF", + lumiS[scaleType], mMeanLumiOverride, mMeanLumiRefOverride, static_cast(getLumiScaleMode()), mLumiCTPSource, mInstCTPLumiOverride, mInstLumiCTPFactor); + } + return false; +} + +//________________________________________________________ +void CorrectionMapsLoaderFull::init(o2::framework::InitContext& ic, bool idcsAvailable) +{ + if (getLumiScaleMode() == LumiScaleMode::Unset) { + LOGP(fatal, "TPC correction lumi scaling mode is not set"); + } + const auto& inputRouts = ic.services().get().inputs; + bool foundCTP = false; + for (const auto& route : inputRouts) { + if (route.matcher == InputSpec{"CTPLumi", "CTP", "LUMI", 0, Lifetime::Timeframe}) { + foundCTP = true; + } + } + setLumiCTPAvailable(foundCTP); + if ((getLumiScaleType() == LumiScaleType::CTPLumi && !foundCTP) || (getLumiScaleType() == LumiScaleType::TPCScaler && !idcsAvailable)) { + LOGP(fatal, "Lumi scaling source {}({}) is not available for TPC correction", static_cast(getLumiScaleType()), getLumiScaleType() == LumiScaleType::CTPLumi ? "CTP" : "TPCScaler"); + } +} + +void CorrectionMapsLoaderFull::checkMeanScaleConsistency(float meanLumi, float threshold) const +{ + if (getLumiScaleType() == LumiScaleType::CTPLumi) { + if (meanLumi < threshold) { + LOGP(fatal, "CTP Lumi scaling source is requested, but the map mean scale {} is below the threshold {}", meanLumi, threshold); + } + } else if (getLumiScaleType() == LumiScaleType::TPCScaler) { + if (meanLumi > threshold) { + LOGP(fatal, "IDC scaling source is requested, but the map mean scale {} is above the threshold {}", meanLumi, threshold); + } + } +} diff --git a/Detectors/TPC/calibration/src/TrackDump.cxx b/Detectors/TPC/calibration/src/TrackDump.cxx index 421750a5cb22b..f78d958a54bd3 100644 --- a/Detectors/TPC/calibration/src/TrackDump.cxx +++ b/Detectors/TPC/calibration/src/TrackDump.cxx @@ -237,9 +237,8 @@ float TrackDump::ClusterNativeAdd::zc(float vertexTime) const void TrackDump::ClusterNativeAdd::loadCorrMaps(std::string_view corrMapFile, std::string_view corrMapFileRef) { - sCorrHelper.setOwner(true); - sCorrHelper.setCorrMap(gpu::TPCFastTransform::loadFromFile(corrMapFile.data())); - if (!corrMapFileRef.empty()) { - sCorrHelper.setCorrMapRef(gpu::TPCFastTransform::loadFromFile(corrMapFileRef.data())); - } + auto fastTransformTmp = gpu::TPCFastTransform::loadFromFile(corrMapFile.data()); + std::vector buffer; + gpu::TPCFastTransformPOD::create(buffer, *fastTransformTmp); + sCorrHelper.setCorrMap(std::move(buffer)); } diff --git a/Detectors/TPC/reconstruction/include/TPCReconstruction/TPCFastTransformHelperO2.h b/Detectors/TPC/reconstruction/include/TPCReconstruction/TPCFastTransformHelperO2.h index f94bff0acc076..aa042ab16b627 100644 --- a/Detectors/TPC/reconstruction/include/TPCReconstruction/TPCFastTransformHelperO2.h +++ b/Detectors/TPC/reconstruction/include/TPCReconstruction/TPCFastTransformHelperO2.h @@ -21,9 +21,8 @@ #ifndef ALICEO2_TPC_TPCFASTTRANSFORMHELPERO2_H_ #define ALICEO2_TPC_TPCFASTTRANSFORMHELPERO2_H_ -#include "TPCFastTransform.h" +#include "TPCFastTransformPOD.h" #include "Rtypes.h" -#include namespace o2 { @@ -61,7 +60,15 @@ class TPCFastTransformHelperO2 std::unique_ptr create(Long_t TimeStamp, const TPCFastSpaceChargeCorrection& correction); /// Updates the transformation with the new time stamp - int updateCalibration(TPCFastTransform& transform, Long_t TimeStamp, float vDriftFactor = 1.f, float vDriftRef = 0.f, float driftTimeOffset = 0.f); + int updateCalibration(TPCFastTransform& fastTransform, Long_t TimeStamp, float vDriftFactor = 1.f, float vDriftRef = 0.f, float driftTimeOffset = 0.f) + { + return updateCalibrationImpl(fastTransform, TimeStamp, vDriftFactor, vDriftRef, driftTimeOffset); + } + + int updateCalibration(TPCFastTransformPOD& fastTransform, Long_t TimeStamp, float vDriftFactor = 1.f, float vDriftRef = 0.f, float driftTimeOffset = 0.f) + { + return updateCalibrationImpl(fastTransform, TimeStamp, vDriftFactor, vDriftRef, driftTimeOffset); + } /// _______________ Utilities ________________________ @@ -73,6 +80,9 @@ class TPCFastTransformHelperO2 /// initialization void init(); + template + int updateCalibrationImpl(T& transform, Long_t TimeStamp, float vDriftFactor, float vDriftRef, float driftTimeOffset); + static TPCFastTransformHelperO2* sInstance; ///< singleton instance bool mIsInitialized = 0; ///< initialization flag TPCFastTransformGeo mGeo; ///< geometry parameters diff --git a/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx b/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx index 419ced9fa978e..6e0bccbdc40fe 100644 --- a/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx +++ b/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx @@ -22,8 +22,6 @@ #include "TPCBase/Sector.h" #include "DataFormatsTPC/Defs.h" #include "TPCFastTransform.h" -#include "Spline2DHelper.h" -#include "Riostream.h" #include using namespace o2::gpu; @@ -136,7 +134,8 @@ std::unique_ptr TPCFastTransformHelperO2::create(Long_t TimeSt return create(TimeStamp, correction); } -int TPCFastTransformHelperO2::updateCalibration(TPCFastTransform& fastTransform, Long_t TimeStamp, float vDriftFactor, float vDriftRef, float driftTimeOffset) +template +int TPCFastTransformHelperO2::updateCalibrationImpl(T& fastTransform, Long_t TimeStamp, float vDriftFactor, float vDriftRef, float driftTimeOffset) { // Update the calibration with the new time stamp LOGP(debug, "Updating calibration: timestamp:{} vdriftFactor:{} vdriftRef:{}", TimeStamp, vDriftFactor, vDriftRef); @@ -150,7 +149,6 @@ int TPCFastTransformHelperO2::updateCalibration(TPCFastTransform& fastTransform, // search for the calibration database ... - auto& detParam = ParameterDetector::Instance(); auto& gasParam = ParameterGas::Instance(); auto& elParam = ParameterElectronics::Instance(); // start the initialization @@ -228,5 +226,9 @@ void TPCFastTransformHelperO2::testGeometry(const TPCFastTransformGeo& geo) cons << " max Dx " << maxDx << " max Dy " << maxDy << std::endl; } } + +template int TPCFastTransformHelperO2::updateCalibrationImpl(TPCFastTransform&, Long_t, float, float, float); +template int TPCFastTransformHelperO2::updateCalibrationImpl(TPCFastTransformPOD&, Long_t, float, float, float); + } // namespace tpc } // namespace o2 diff --git a/Detectors/TPC/reconstruction/test/testGPUCATracking.cxx b/Detectors/TPC/reconstruction/test/testGPUCATracking.cxx index 0debfa72dd7fa..2b21053bda1ff 100644 --- a/Detectors/TPC/reconstruction/test/testGPUCATracking.cxx +++ b/Detectors/TPC/reconstruction/test/testGPUCATracking.cxx @@ -25,7 +25,7 @@ #include "TPCReconstruction/TPCFastTransformHelperO2.h" #include "CorrectionMapsHelper.h" -#include "TPCFastTransform.h" +#include "TPCFastTransformPOD.h" #include "GPUO2Interface.h" #include "GPUO2InterfaceUtils.h" #include "GPUO2InterfaceConfiguration.h" @@ -74,10 +74,13 @@ BOOST_AUTO_TEST_CASE(CATracking_test1) config.configWorkflow.inputs.set(gpudatatypes::InOutType::TPCClusters); config.configWorkflow.outputs.set(gpudatatypes::InOutType::TPCMergedTracks); - std::unique_ptr fastTransform(TPCFastTransformHelperO2::instance()->create(0)); + auto fastTransformTmp = TPCFastTransformHelperO2::instance()->create(0); + std::vector fastTransformBuf; + TPCFastTransformPOD::create(fastTransformBuf, *fastTransformTmp); + std::unique_ptr fastTransformHelper(new CorrectionMapsHelper()); - fastTransformHelper->setCorrMap(fastTransform.get()); - config.configCalib.fastTransform = fastTransform.get(); + fastTransformHelper->setCorrMap(std::move(fastTransformBuf)); + config.configCalib.fastTransform = fastTransformHelper->getCorrMap(); config.configCalib.fastTransformHelper = fastTransformHelper.get(); auto dEdxCalibContainer = GPUO2InterfaceUtils::getCalibdEdxContainerDefault(); config.configCalib.dEdxCalibContainer = dEdxCalibContainer.get(); diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TPCCalibPadGainTracksSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCCalibPadGainTracksSpec.h index 516ea128acfe7..8e88a27d51e7f 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/TPCCalibPadGainTracksSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TPCCalibPadGainTracksSpec.h @@ -26,7 +26,6 @@ #include "TPCWorkflow/ProcessingHelpers.h" #include "Framework/CCDBParamSpec.h" #include "TPCBaseRecSim/CDBInterface.h" -#include "TPCCalibration/VDriftHelper.h" #include "TPCCalibration/CorrectionMapsLoader.h" #include "DetectorsBase/GRPGeomHelper.h" #include "GPUO2InterfaceUtils.h" @@ -45,22 +44,18 @@ namespace tpc class TPCCalibPadGainTracksDevice : public o2::framework::Task { public: - TPCCalibPadGainTracksDevice(std::shared_ptr dr, std::shared_ptr req, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, const uint32_t publishAfterTFs, const bool debug, const bool useLastExtractedMapAsReference, const std::string polynomialsFile, const bool disablePolynomialsCCDB) : mDataRequest(dr), mPublishAfter(publishAfterTFs), mDebug(debug), mUseLastExtractedMapAsReference(useLastExtractedMapAsReference), mDisablePolynomialsCCDB(disablePolynomialsCCDB), mCCDBRequest(req) + TPCCalibPadGainTracksDevice(std::shared_ptr dr, std::shared_ptr req, const uint32_t publishAfterTFs, const bool debug, const bool useLastExtractedMapAsReference, const std::string polynomialsFile, const bool disablePolynomialsCCDB) : mDataRequest(dr), mPublishAfter(publishAfterTFs), mDebug(debug), mUseLastExtractedMapAsReference(useLastExtractedMapAsReference), mDisablePolynomialsCCDB(disablePolynomialsCCDB), mCCDBRequest(req) { if (!polynomialsFile.empty()) { LOGP(info, "Loading polynomials from file {}", polynomialsFile); mPadGainTracks.loadPolTopologyCorrectionFromFile(polynomialsFile.data()); mDisablePolynomialsCCDB = true; } - mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); - mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); - mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); } void init(o2::framework::InitContext& ic) final { o2::base::GRPGeomHelper::instance().setRequest(mCCDBRequest); - mTPCCorrMapsLoader.init(ic); // setting up the histogram ranges const auto nBins = ic.options().get("nBins"); auto reldEdxMin = ic.options().get("reldEdxMin"); @@ -151,8 +146,6 @@ class TPCCalibPadGainTracksDevice : public o2::framework::Task LOGP(info, "Updating Q topology correction from CCDB"); const auto* topologyCorr = static_cast(obj); mPadGainTracks.setPolTopologyCorrectionFromContainer(*topologyCorr); - } else if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { - } else if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { } else if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { const auto field = o2::gpu::GPUO2InterfaceUtils::getNominalGPUBz(*o2::base::GRPGeomHelper::instance().getGRPMagField()); LOGP(info, "Setting magnetic field to {} kG", field); @@ -188,7 +181,6 @@ class TPCCalibPadGainTracksDevice : public o2::framework::Task LOGP(info, "fetching residual gain map"); pc.inputs().get>*>("tpcresidualgainmap"); } - mTPCVDriftHelper.extractCCDBInputs(pc); mTPCCorrMapsLoader.extractCCDBInputs(pc); bool updateMaps = false; if (mTPCCorrMapsLoader.isUpdated()) { @@ -196,19 +188,6 @@ class TPCCalibPadGainTracksDevice : public o2::framework::Task updateMaps = true; } mPadGainTracks.setTPCCorrMaps(&mTPCCorrMapsLoader); - if (mTPCVDriftHelper.isUpdated()) { - LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", - mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, - mTPCVDriftHelper.getVDriftObject().timeOffsetCorr, mTPCVDriftHelper.getVDriftObject().refTimeOffset, - mTPCVDriftHelper.getSourceName()); - mPadGainTracks.setTPCVDrift(mTPCVDriftHelper.getVDriftObject()); - mTPCVDriftHelper.acknowledgeUpdate(); - updateMaps = true; - } - if (updateMaps) { - mTPCCorrMapsLoader.updateVDrift(mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().getTimeOffset()); - } - mPadGainTracks.setMembers(&tracks, &clRefs, clusters->clusterIndex, recoData.clusterShMapTPC, recoData.occupancyMapTPC); mPadGainTracks.processTracks(mMaxTracksPerTF); ++mProcessedTFs; @@ -237,7 +216,6 @@ class TPCCalibPadGainTracksDevice : public o2::framework::Task unsigned int mUseEveryNthTF{1}; ///< process every Nth TF only unsigned int mFirstTFSend{1}; ///< first TF for which the data will be send (initialized randomly) int mMaxTracksPerTF{-1}; ///< max number of tracks processed per TF - o2::tpc::VDriftHelper mTPCVDriftHelper{}; o2::tpc::CorrectionMapsLoader mTPCCorrMapsLoader{}; void sendOutput(DataAllocator& output) @@ -247,16 +225,16 @@ class TPCCalibPadGainTracksDevice : public o2::framework::Task } }; -DataProcessorSpec getTPCCalibPadGainTracksSpec(const uint32_t publishAfterTFs, const bool debug, const bool useLastExtractedMapAsReference, const std::string polynomialsFile, bool disablePolynomialsCCDB, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts) +DataProcessorSpec getTPCCalibPadGainTracksSpec(const uint32_t publishAfterTFs, const bool debug, const bool useLastExtractedMapAsReference, const std::string polynomialsFile, bool disablePolynomialsCCDB) { std::vector inputs; auto dataRequest = std::make_shared(); dataRequest->requestTracks(o2::dataformats::GlobalTrackID::getSourceMask(o2::dataformats::GlobalTrackID::TPC), false); dataRequest->requestClusters(o2::dataformats::GlobalTrackID::getSourceMask(o2::dataformats::GlobalTrackID::TPC), false); - if (sclOpts.lumiType == 1) { - dataRequest->inputs.emplace_back("CTPLumi", "CTP", "LUMI", 0, Lifetime::Timeframe); - } + // if (sclOpts.lumiType == 1) { + // dataRequest->inputs.emplace_back("CTPLumi", "CTP", "LUMI", 0, Lifetime::Timeframe); + // } if (!polynomialsFile.empty()) { disablePolynomialsCCDB = true; @@ -270,7 +248,6 @@ DataProcessorSpec getTPCCalibPadGainTracksSpec(const uint32_t publishAfterTFs, c dataRequest->inputs.emplace_back("tpcresidualgainmap", gDataOriginTPC, "RESIDUALGAINMAP", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalPadGainResidual))); } - o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); Options opts{ {"nBins", VariantType::Int, 20, {"Number of bins per histogram"}}, {"reldEdxMin", VariantType::Int, 0, {"Minimum x coordinate of the histogram for Q/(dE/dx)"}}, @@ -293,7 +270,7 @@ DataProcessorSpec getTPCCalibPadGainTracksSpec(const uint32_t publishAfterTFs, c {"useEveryNthTF", VariantType::Int, 10, {"Using only a fraction of the data: 1: Use every TF, 10: Use only every tenth TF."}}, {"maxTracksPerTF", VariantType::Int, 10000, {"Maximum number of processed tracks per TF (-1 for processing all tracks)"}}, }; - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); + o2::tpc::CorrectionMapsLoader::requestInputs(dataRequest->inputs, opts); auto ccdbRequest = std::make_shared(false, // orbitResetTime false, // GRPECS=true @@ -310,7 +287,7 @@ DataProcessorSpec getTPCCalibPadGainTracksSpec(const uint32_t publishAfterTFs, c "calib-tpc-gainmap-tracks", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ccdbRequest, sclOpts, publishAfterTFs, debug, useLastExtractedMapAsReference, polynomialsFile, disablePolynomialsCCDB)}, + AlgorithmSpec{adaptFromTask(dataRequest, ccdbRequest, publishAfterTFs, debug, useLastExtractedMapAsReference, polynomialsFile, disablePolynomialsCCDB)}, opts}; // end DataProcessorSpec } diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TPCRefitter.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCRefitter.h index 31a5ce756142a..e2d8a2de3912d 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/TPCRefitter.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TPCRefitter.h @@ -23,7 +23,7 @@ struct CorrectionMapsLoaderGloOpts; namespace o2::trackstudy { /// create a processor spec -o2::framework::DataProcessorSpec getTPCRefitterSpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, bool requestCosmics = false); +o2::framework::DataProcessorSpec getTPCRefitterSpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool useMC, bool requestCosmics = false); } // namespace o2::trackstudy diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TPCScalerSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCScalerSpec.h index b85a882870ecb..950ef9a248443 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/TPCScalerSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TPCScalerSpec.h @@ -13,13 +13,14 @@ #define O2_TPC_TPCSCALER_SPEC #include "Framework/DataProcessorSpec.h" +#include "TPCCalibration/CorrectionMapsLoader.h" namespace o2 { namespace tpc { -o2::framework::DataProcessorSpec getTPCScalerSpec(bool enableIDCs, bool enableMShape); +o2::framework::DataProcessorSpec getTPCScalerSpec(bool enableIDCs, bool enableMShape, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts); } // end namespace tpc } // end namespace o2 diff --git a/Detectors/TPC/workflow/src/RecoWorkflow.cxx b/Detectors/TPC/workflow/src/RecoWorkflow.cxx index 3054dd5d61519..fb9b09329bfab 100644 --- a/Detectors/TPC/workflow/src/RecoWorkflow.cxx +++ b/Detectors/TPC/workflow/src/RecoWorkflow.cxx @@ -201,9 +201,7 @@ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vecto laneConfiguration, &hook}, propagateMC)); - if (sclOpts.needTPCScalersWorkflow()) { // for standalone tpc-reco workflow - specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpts.lumiType == 2, sclOpts.enableMShapeCorrection)); - } + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpts.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpts.enableMShapeCorrection, sclOpts)); if (produceTracks && sclOpts.requestCTPLumi) { // need CTP digits (lumi) reader specs.emplace_back(o2::ctp::getDigitsReaderSpec(false)); } @@ -225,9 +223,7 @@ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vecto if (!getenv("DPL_DISABLE_TPC_TRIGGER_READER") || atoi(getenv("DPL_DISABLE_TPC_TRIGGER_READER")) != 1) { specs.emplace_back(o2::tpc::getTPCTriggerReaderSpec()); } - if (sclOpts.needTPCScalersWorkflow()) { // for standalone tpc-reco workflow - specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpts.lumiType == 2, sclOpts.enableMShapeCorrection)); - } + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpts.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpts.enableMShapeCorrection, sclOpts)); if (sclOpts.requestCTPLumi) { // need CTP digits (lumi) reader specs.emplace_back(o2::ctp::getDigitsReaderSpec(false)); } @@ -461,11 +457,6 @@ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vecto if (runGPUReco) { o2::gpu::GPURecoWorkflowSpec::Config cfg; cfg.runTPCTracking = true; - cfg.lumiScaleType = sclOpts.lumiType; - cfg.lumiScaleMode = sclOpts.lumiMode; - cfg.checkCTPIDCconsistency = sclOpts.checkCTPIDCconsistency; - cfg.enableMShape = sclOpts.enableMShapeCorrection; - cfg.enableCTPLumi = sclOpts.requestCTPLumi; cfg.decompressTPC = decompressTPC; cfg.decompressTPCFromROOT = decompressTPC && inputType == InputType::CompClustersRoot; cfg.caClusterer = caClusterer; diff --git a/Detectors/TPC/workflow/src/TPCRefitter.cxx b/Detectors/TPC/workflow/src/TPCRefitter.cxx index 43a55526246fe..0db87bc8edbae 100644 --- a/Detectors/TPC/workflow/src/TPCRefitter.cxx +++ b/Detectors/TPC/workflow/src/TPCRefitter.cxx @@ -63,13 +63,8 @@ class TPCRefitterSpec final : public Task Streamer = 0x1, ///< Write per track streamer information TFVectors = 0x2, ///< Writer vectors per TF }; - TPCRefitterSpec(std::shared_ptr dr, std::shared_ptr gr, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, GTrackID::mask_t src, bool useMC) - : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mUseMC(useMC) - { - mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); - mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); - mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); - } + TPCRefitterSpec(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool useMC) + : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mUseMC(useMC) {} ~TPCRefitterSpec() final = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -184,7 +179,6 @@ void TPCRefitterSpec::init(InitContext& ic) mXRef = 0.; } mGenerator = std::mt19937(std::random_device{}()); - mTPCCorrMapsLoader.init(ic); } void TPCRefitterSpec::run(ProcessingContext& pc) @@ -219,21 +213,8 @@ void TPCRefitterSpec::updateTimeDependentParams(ProcessingContext& pc) // none at the moment } // we may have other params which need to be queried regularly - bool updateMaps = false; if (mTPCCorrMapsLoader.isUpdated()) { mTPCCorrMapsLoader.acknowledgeUpdate(); - updateMaps = true; - } - if (mTPCVDriftHelper.isUpdated()) { - LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", - mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, - mTPCVDriftHelper.getVDriftObject().timeOffsetCorr, mTPCVDriftHelper.getVDriftObject().refTimeOffset, - mTPCVDriftHelper.getSourceName()); - mTPCVDriftHelper.acknowledgeUpdate(); - updateMaps = true; - } - if (updateMaps) { - mTPCCorrMapsLoader.updateVDrift(mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().getTimeOffset()); } } @@ -413,9 +394,6 @@ void TPCRefitterSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { return; } - if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { - return; - } } bool TPCRefitterSpec::getDCAs(const o2::track::TrackPar& track, float& dcar, float& dcaz) @@ -740,7 +718,7 @@ void TPCRefitterSpec::processCosmics(o2::globaltracking::RecoContainer& recoData } } -DataProcessorSpec getTPCRefitterSpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, bool requestCosmics) +DataProcessorSpec getTPCRefitterSpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC, bool requestCosmics) { std::vector outputs; Options opts{ @@ -778,13 +756,13 @@ DataProcessorSpec getTPCRefitterSpec(GTrackID::mask_t srcTracks, GTrackID::mask_ dataRequest->inputs, true); o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); + o2::tpc::CorrectionMapsLoader::requestInputs(dataRequest->inputs, opts); return DataProcessorSpec{ "tpc-refitter", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, sclOpts, srcTracks, useMC)}, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, useMC)}, opts}; } diff --git a/Detectors/TPC/workflow/src/TPCScalerSpec.cxx b/Detectors/TPC/workflow/src/TPCScalerSpec.cxx index f185b5e08c7e7..61cbb988d672e 100644 --- a/Detectors/TPC/workflow/src/TPCScalerSpec.cxx +++ b/Detectors/TPC/workflow/src/TPCScalerSpec.cxx @@ -27,6 +27,8 @@ #include "TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h" #include "TPCSpaceCharge/SpaceCharge.h" #include "CommonUtils/TreeStreamRedirector.h" +#include "TPCCalibration/CorrectionMapsLoaderFull.h" +#include "TPCCalibration/VDriftHelper.h" using namespace o2::framework; @@ -38,7 +40,12 @@ namespace tpc class TPCScalerSpec : public Task { public: - TPCScalerSpec(std::shared_ptr req, bool enableIDCs, bool enableMShape) : mCCDBRequest(req), mEnableIDCs(enableIDCs), mEnableMShape(enableMShape){}; + TPCScalerSpec(std::shared_ptr req, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, bool enableIDCs, bool enableMShape) : mCCDBRequest(req), mEnableIDCs(enableIDCs), mEnableMShape(enableMShape), mGlobOpts(sclOpts) + { + mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); + mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); + mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); + }; void init(framework::InitContext& ic) final { @@ -57,6 +64,7 @@ class TPCScalerSpec : public Task if (enableStreamer) { mStreamer = std::make_unique("M_Shape.root", "recreate"); } + mTPCCorrMapsLoader.init(ic, mEnableIDCs); } void endOfStream(EndOfStreamContext& eos) final @@ -69,6 +77,11 @@ class TPCScalerSpec : public Task void run(ProcessingContext& pc) final { o2::base::GRPGeomHelper::instance().checkUpdates(pc); + mTPCVDriftHelper.extractCCDBInputs(pc); + if (mTPCVDriftHelper.isUpdated()) { + mTPCVDriftHelper.acknowledgeUpdate(); + } + if (mEnableIDCs && pc.inputs().isValid("tpcscaler")) { pc.inputs().get("tpcscaler"); } @@ -122,12 +135,7 @@ class TPCScalerSpec : public Task std::unique_ptr spCorrection = TPCFastSpaceChargeCorrectionHelper::instance()->createFromGlobalCorrection(getCorrections, mKnotsYMshape, mKnotsZMshape); std::unique_ptr fastTransform(TPCFastTransformHelperO2::instance()->create(0, *spCorrection)); - pc.outputs().snapshot(Output{header::gDataOriginTPC, "TPCMSHAPE"}, *fastTransform); - } else { - // send empty dummy object - LOGP(info, "Sending default (no) M-shape correction"); - auto fastTransform = o2::tpc::TPCFastTransformHelperO2::instance()->create(0); - pc.outputs().snapshot(Output{header::gDataOriginTPC, "TPCMSHAPE"}, *fastTransform); + mTPCCorrMapsLoader.setCorrMapMShape(std::move(fastTransform)); } if (mStreamer) { @@ -140,6 +148,7 @@ class TPCScalerSpec : public Task } } + float tpcScaler = -1.f; if (mEnableIDCs) { static int runWarningIDC = -1; if (pc.services().get().runNumber != mTPCScaler.getRun() && runWarningIDC != currRun) { @@ -149,8 +158,7 @@ class TPCScalerSpec : public Task float scalerA = mTPCScaler.getMeanScaler(timestamp, o2::tpc::Side::A); float scalerC = mTPCScaler.getMeanScaler(timestamp, o2::tpc::Side::C); float meanScaler = (scalerA + scalerC) / 2; - LOGP(info, "Publishing TPC scaler: {} for timestamp: {}, firstTFOrbit: {}", meanScaler, timestamp, firstTFOrbit); - pc.outputs().snapshot(Output{header::gDataOriginTPC, "TPCSCALER"}, meanScaler); + tpcScaler = meanScaler; if (mStreamer) { (*mStreamer) << "treeIDC" << "scalerA=" << scalerA @@ -160,11 +168,67 @@ class TPCScalerSpec : public Task << "\n"; } } + // check for Maps update + mTPCCorrMapsLoader.extractCCDBInputs(pc, tpcScaler); + + const float lumiCTP = mTPCCorrMapsLoader.getInstLumiCTP(); + // if CTP lumi was notrequest - defualt of 0 is published, otherwise the value is scaled with the provided factor + LOGP(info, "Publishing CTP Lumi: {} for timestamp: {}, firstTFOrbit: {}", lumiCTP, timestamp, firstTFOrbit); + pc.outputs().snapshot(Output{header::gDataOriginCTP, "LUMICTP"}, lumiCTP); + + buildMap(pc); + } + + void buildMap(ProcessingContext& pc) + { + // reference map + auto* corrMap = mTPCCorrMapsLoader.getCorrMap(); + + // // new correction map + o2::gpu::TPCFastTransform finalMap; + finalMap.cloneFromObject(*corrMap, nullptr); + finalMap.setApplyCorrectionOn(); + + const auto* corrMapRef = mTPCCorrMapsLoader.getCorrMapRef(); + const float lumiScale = mTPCCorrMapsLoader.getLumiScale(); + std::vector> additionalCorrections; + + // if standard scaling is used: map(lumi) = (mean_map - ref_map) * lumiScale + ref_map + if (mTPCCorrMapsLoader.getLumiScaleMode() == LumiScaleMode::Linear) { + const std::vector> step0{{&(corrMapRef->getCorrection()), -1.f}}; + // finalMap = (mean_map - finalMap) + TPCFastSpaceChargeCorrectionHelper::instance()->mergeCorrections(finalMap.getCorrection(), 1, step0, true); + + // finalMap = finalMap * lumiScale + ref_map + const std::vector> step1{{&(corrMapRef->getCorrection()), 1.f}}; + TPCFastSpaceChargeCorrectionHelper::instance()->mergeCorrections(finalMap.getCorrection(), lumiScale, step1, true); + + } else if (mTPCCorrMapsLoader.getLumiScaleMode() == LumiScaleMode::DerivativeMap || mTPCCorrMapsLoader.getLumiScaleMode() == LumiScaleMode::DerivativeMapMC) { + additionalCorrections.emplace_back(&(corrMapRef->getCorrection()), lumiScale); + } + + // if mshape map valid + if (!mTPCCorrMapsLoader.isCorrMapMShapeDummy()) { + LOGP(info, "Adding M-shape correction to the final map with scaling factor {}", mMShapeScalingFac); + additionalCorrections.emplace_back(&(mTPCCorrMapsLoader.getCorrMapMShape()->getCorrection()), 1.f); + } + + if (!additionalCorrections.empty()) { + TPCFastSpaceChargeCorrectionHelper::instance()->mergeCorrections(finalMap.getCorrection(), 1, additionalCorrections, true); + } + + Output corrMapOutput{header::gDataOriginTPC, "TPCCORRMAP", 0}; + auto outputBuffer = o2::pmr::vector(pc.outputs().getMemoryResource(corrMapOutput)); + auto* pod = TPCFastTransformPOD::create(outputBuffer, finalMap.getCorrection()); + const auto& vd = mTPCVDriftHelper.getVDriftObject(); + o2::tpc::TPCFastTransformHelperO2::instance()->updateCalibration(*pod, 0, vd.corrFact, vd.refVDrift, vd.getTimeOffset()); + pc.outputs().adoptContainer(corrMapOutput, std::move(outputBuffer)); } void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) final { o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj); + mTPCVDriftHelper.accountCCDBInputs(matcher, obj); if (matcher == ConcreteDataMatcher(o2::header::gDataOriginTPC, "TPCSCALERCCDB", 0)) { LOGP(info, "Updating TPC scaler"); mTPCScaler.setFromTree(*((TTree*)obj)); @@ -198,12 +262,16 @@ class TPCScalerSpec : public Task LOGP(info, "Loaded default M-Shape correction object from CCDB"); } } + if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { + return; + } } private: std::shared_ptr mCCDBRequest; ///< info for CCDB request const bool mEnableIDCs{true}; ///< enable IDCs const bool mEnableMShape{false}; ///< enable v shape scalers + const o2::tpc::CorrectionMapsLoaderGloOpts mGlobOpts; ///< global options for the correction map loader, needed to decide which maps to load from CCDB bool mEnableWeights{false}; ///< use weights for TPC scalers TPCScalerWeights mScalerWeights{}; ///< scaler weights float mIonDriftTimeMS{-1}; ///< ion drift time @@ -214,6 +282,8 @@ class TPCScalerSpec : public Task int mKnotsYMshape{4}; ///< number of knots used for the spline object for M-Shape distortions int mKnotsZMshape{4}; ///< number of knots used for the spline object for M-Shape distortions std::unique_ptr mStreamer; ///< streamer + o2::tpc::CorrectionMapsLoaderFull mTPCCorrMapsLoader{}; + o2::tpc::VDriftHelper mTPCVDriftHelper{}; ///< helper for v-drift void overWriteIntegrationTime() { @@ -229,7 +299,7 @@ class TPCScalerSpec : public Task } }; -o2::framework::DataProcessorSpec getTPCScalerSpec(bool enableIDCs, bool enableMShape) +o2::framework::DataProcessorSpec getTPCScalerSpec(bool enableIDCs, bool enableMShape, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts) { std::vector inputs; if (enableIDCs) { @@ -251,18 +321,16 @@ o2::framework::DataProcessorSpec getTPCScalerSpec(bool enableIDCs, bool enableMS inputs); std::vector outputs; - if (enableIDCs) { - outputs.emplace_back(o2::header::gDataOriginTPC, "TPCSCALER", 0, Lifetime::Timeframe); - } - if (enableMShape) { - outputs.emplace_back(o2::header::gDataOriginTPC, "TPCMSHAPE", 0, Lifetime::Timeframe); - } + outputs.emplace_back(o2::header::gDataOriginTPC, "TPCCORRMAP", 0, Lifetime::Timeframe); + outputs.emplace_back(o2::header::gDataOriginCTP, "LUMICTP", 0, Lifetime::Timeframe); + o2::tpc::VDriftHelper::requestCCDBInputs(inputs); + o2::tpc::CorrectionMapsLoaderFull::requestCCDBInputs(inputs, sclOpts); return DataProcessorSpec{ "tpc-scaler", inputs, outputs, - AlgorithmSpec{adaptFromTask(ccdbRequest, enableIDCs, enableMShape)}, + AlgorithmSpec{adaptFromTask(ccdbRequest, sclOpts, enableIDCs, enableMShape)}, Options{ {"ion-drift-time", VariantType::Float, -1.f, {"Overwrite ion drift time if a value >0 is provided"}}, {"max-time-for-weights", VariantType::Float, 500.f, {"Maximum possible integration time in ms when weights are used"}}, diff --git a/Detectors/TPC/workflow/src/tpc-calib-gainmap-tracks.cxx b/Detectors/TPC/workflow/src/tpc-calib-gainmap-tracks.cxx index 5475995437113..06f1f2633fb71 100644 --- a/Detectors/TPC/workflow/src/tpc-calib-gainmap-tracks.cxx +++ b/Detectors/TPC/workflow/src/tpc-calib-gainmap-tracks.cxx @@ -65,9 +65,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) const auto disablePolynomialsCCDB = config.options().get("disablePolynomialsCCDB"); const auto sclOpt = o2::tpc::CorrectionMapsLoader::parseGlobalOptions(config.options()); WorkflowSpec workflow; - if (sclOpt.needTPCScalersWorkflow()) { - workflow.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == 2, sclOpt.enableMShapeCorrection)); - } - workflow.emplace_back(o2::tpc::getTPCCalibPadGainTracksSpec(publishAfterTFs, debug, useLastExtractedMapAsReference, polynomialsFile, disablePolynomialsCCDB, sclOpt)); + workflow.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpt.enableMShapeCorrection, sclOpt)); + workflow.emplace_back(o2::tpc::getTPCCalibPadGainTracksSpec(publishAfterTFs, debug, useLastExtractedMapAsReference, polynomialsFile, disablePolynomialsCCDB)); return workflow; } diff --git a/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx b/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx index f3d4d639ddfd2..b9c8cde98273b 100644 --- a/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx +++ b/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx @@ -24,6 +24,7 @@ #include "TPCWorkflow/RecoWorkflow.h" #include "TPCReaderWorkflow/TPCSectorCompletionPolicy.h" #include "TPCCalibration/CorrectionMapsLoader.h" +#include "TPCCalibration/CorrectionMapsLoaderFull.h" #include "Framework/CustomWorkflowTerminationHook.h" #include "DataFormatsTPC/TPCSectorHeader.h" #include "Algorithm/RangeTokenizer.h" @@ -75,8 +76,8 @@ void customize(std::vector& workflowOptions) {"tpc-deadMap-sources", VariantType::Int, -1, {"Sources to consider for TPC dead channel map creation; -1=all, 0=deactivated"}}, {"tpc-mc-time-gain", VariantType::Bool, false, {"use time gain calibration for MC (true) or for data (false)"}}, }; - o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); + o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); std::swap(workflowOptions, options); } diff --git a/Detectors/TPC/workflow/src/tpc-refitter-workflow.cxx b/Detectors/TPC/workflow/src/tpc-refitter-workflow.cxx index 78bf63a44d60f..1700750f8aa4b 100644 --- a/Detectors/TPC/workflow/src/tpc-refitter-workflow.cxx +++ b/Detectors/TPC/workflow/src/tpc-refitter-workflow.cxx @@ -79,18 +79,16 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) srcCls = srcCls | GID::getSourcesMask("CTP"); } - if (sclOpt.lumiType == 2) { - const auto enableMShape = configcontext.options().get("enable-M-shape-correction"); - const auto enableIDCs = !configcontext.options().get("disable-IDC-scalers"); - specs.emplace_back(o2::tpc::getTPCScalerSpec(enableIDCs, enableMShape)); - } + const auto enableMShape = configcontext.options().get("enable-M-shape-correction"); + const auto enableIDCs = !configcontext.options().get("disable-IDC-scalers"); + specs.emplace_back(o2::tpc::getTPCScalerSpec(enableIDCs, enableMShape, sclOpt)); o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC); o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); // P-vertex is always needed if (enableCosmics) { o2::globaltracking::InputHelper::addInputSpecsCosmics(configcontext, specs, useMC); } - specs.emplace_back(o2::trackstudy::getTPCRefitterSpec(srcTrc, srcCls, useMC, sclOpt, enableCosmics)); + specs.emplace_back(o2::trackstudy::getTPCRefitterSpec(srcTrc, srcCls, useMC, enableCosmics)); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); diff --git a/Detectors/TPC/workflow/src/tpc-scaler.cxx b/Detectors/TPC/workflow/src/tpc-scaler.cxx index 598687c7dff41..d4b994f6eb275 100644 --- a/Detectors/TPC/workflow/src/tpc-scaler.cxx +++ b/Detectors/TPC/workflow/src/tpc-scaler.cxx @@ -15,6 +15,7 @@ #include "TPCWorkflow/TPCScalerSpec.h" #include "CommonUtils/ConfigurableParam.h" #include "Framework/ConfigParamSpec.h" +#include "TPCCalibration/CorrectionMapsLoader.h" using namespace o2::framework; @@ -25,7 +26,7 @@ void customize(std::vector& workflowOptions) ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, {"enable-M-shape-correction", VariantType::Bool, false, {"Enable M-shape distortion correction"}}, {"disable-IDC-scalers", VariantType::Bool, false, {"Disable TPC scalers for space-charge distortion fluctuation correction"}}}; - + o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); std::swap(workflowOptions, options); } @@ -37,6 +38,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) o2::conf::ConfigurableParam::updateFromString(config.options().get("configKeyValues")); const auto enableMShape = config.options().get("enable-M-shape-correction"); const auto enableIDCs = !config.options().get("disable-IDC-scalers"); - workflow.emplace_back(o2::tpc::getTPCScalerSpec(enableIDCs, enableMShape)); + auto sclOpt = o2::tpc::CorrectionMapsLoader::parseGlobalOptions(config.options()); + workflow.emplace_back(o2::tpc::getTPCScalerSpec(enableIDCs, enableMShape, sclOpt)); return workflow; } diff --git a/Detectors/TRD/workflow/include/TRDWorkflow/TRDGlobalTrackingSpec.h b/Detectors/TRD/workflow/include/TRDWorkflow/TRDGlobalTrackingSpec.h index 93f07dd58445e..9a7ac5a161694 100644 --- a/Detectors/TRD/workflow/include/TRDWorkflow/TRDGlobalTrackingSpec.h +++ b/Detectors/TRD/workflow/include/TRDWorkflow/TRDGlobalTrackingSpec.h @@ -52,13 +52,8 @@ namespace trd class TRDGlobalTracking : public o2::framework::Task { public: - TRDGlobalTracking(bool useMC, bool withPID, PIDPolicy policy, std::shared_ptr dataRequest, std::shared_ptr gr, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, - o2::dataformats::GlobalTrackID::mask_t src, bool trigRecFilterActive, bool strict) : mUseMC(useMC), mWithPID(withPID), mDataRequest(dataRequest), mGGCCDBRequest(gr), mTrkMask(src), mTrigRecFilter(trigRecFilterActive), mStrict(strict), mPolicy(policy) - { - mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); - mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); - mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); - } + TRDGlobalTracking(bool useMC, bool withPID, PIDPolicy policy, std::shared_ptr dataRequest, std::shared_ptr gr, + o2::dataformats::GlobalTrackID::mask_t src, bool trigRecFilterActive, bool strict) : mUseMC(useMC), mWithPID(withPID), mDataRequest(dataRequest), mGGCCDBRequest(gr), mTrkMask(src), mTrigRecFilter(trigRecFilterActive), mStrict(strict), mPolicy(policy) {} ~TRDGlobalTracking() override = default; void init(o2::framework::InitContext& ic) final; void fillMCTruthInfo(const TrackTRD& trk, o2::MCCompLabel lblSeed, std::vector& lblContainerTrd, std::vector& lblContainerMatch, const o2::dataformats::MCTruthContainer* trkltLabels) const; @@ -115,7 +110,7 @@ class TRDGlobalTracking : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getTRDGlobalTrackingSpec(bool useMC, o2::dataformats::GlobalTrackID::mask_t src, bool trigRecFilterActive, bool strict /* = false*/, bool withPID /* = false*/, PIDPolicy policy /* = PIDPolicy::DEFAULT*/, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts); +framework::DataProcessorSpec getTRDGlobalTrackingSpec(bool useMC, o2::dataformats::GlobalTrackID::mask_t src, bool trigRecFilterActive, bool strict /* = false*/, bool withPID /* = false*/, PIDPolicy policy /* = PIDPolicy::DEFAULT*/); } // namespace trd } // namespace o2 diff --git a/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx b/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx index 0f578efd3aa5b..a080f85524684 100644 --- a/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx +++ b/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx @@ -82,7 +82,6 @@ using TrackTunePar = o2::globaltracking::TrackTuneParams; void TRDGlobalTracking::init(InitContext& ic) { o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); - mTPCCorrMapsLoader.init(ic); mTimer.Stop(); mTimer.Reset(); } @@ -175,10 +174,6 @@ void TRDGlobalTracking::updateTimeDependentParams(ProcessingContext& pc) mTPCVDriftHelper.acknowledgeUpdate(); updateCalib = true; } - if (updateCalib) { - auto& vd = mTPCVDriftHelper.getVDriftObject(); - mTPCCorrMapsLoader.updateVDrift(vd.corrFact, vd.refVDrift, vd.getTimeOffset()); - } } void TRDGlobalTracking::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) @@ -189,9 +184,6 @@ void TRDGlobalTracking::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { return; } - if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { - return; - } if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { LOG(info) << "cluster dictionary updated"; mITSDict = (const o2::itsmft::TopologyDictionary*)obj; @@ -862,7 +854,7 @@ void TRDGlobalTracking::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getTRDGlobalTrackingSpec(bool useMC, GTrackID::mask_t src, bool trigRecFilterActive, bool strict, bool withPID, PIDPolicy policy, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts) +DataProcessorSpec getTRDGlobalTrackingSpec(bool useMC, GTrackID::mask_t src, bool trigRecFilterActive, bool strict, bool withPID, PIDPolicy policy) { std::vector outputs; uint32_t ss = o2::globaltracking::getSubSpec(strict ? o2::globaltracking::MatchingType::Strict : o2::globaltracking::MatchingType::Standard); @@ -899,7 +891,7 @@ DataProcessorSpec getTRDGlobalTrackingSpec(bool useMC, GTrackID::mask_t src, boo true); o2::tpc::VDriftHelper::requestCCDBInputs(inputs); Options opts; - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(inputs, opts, sclOpts); + o2::tpc::CorrectionMapsLoader::requestInputs(inputs, opts); // Request PID policy data if (withPID) { @@ -962,7 +954,7 @@ DataProcessorSpec getTRDGlobalTrackingSpec(bool useMC, GTrackID::mask_t src, boo processorName, inputs, outputs, - AlgorithmSpec{adaptFromTask(useMC, withPID, policy, dataRequest, ggRequest, sclOpts, src, trigRecFilterActive, strict)}, + AlgorithmSpec{adaptFromTask(useMC, withPID, policy, dataRequest, ggRequest, src, trigRecFilterActive, strict)}, opts}; } diff --git a/Detectors/TRD/workflow/src/trd-tracking-workflow.cxx b/Detectors/TRD/workflow/src/trd-tracking-workflow.cxx index 7781b5ed187cb..0bff7dd94d8a2 100644 --- a/Detectors/TRD/workflow/src/trd-tracking-workflow.cxx +++ b/Detectors/TRD/workflow/src/trd-tracking-workflow.cxx @@ -115,10 +115,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) // processing devices o2::framework::WorkflowSpec specs; - if (sclOpt.needTPCScalersWorkflow() && !configcontext.options().get("disable-root-input")) { - specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == 2, sclOpt.enableMShapeCorrection)); + if (!configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpt.enableMShapeCorrection, sclOpt)); } - specs.emplace_back(o2::trd::getTRDGlobalTrackingSpec(useMC, srcTRD, trigRecFilterActive, strict, pid, policy, sclOpt)); + specs.emplace_back(o2::trd::getTRDGlobalTrackingSpec(useMC, srcTRD, trigRecFilterActive, strict, pid, policy)); if (vdexb || gain) { specs.emplace_back(o2::trd::getTRDTrackBasedCalibSpec(srcTRD, vdexb, gain)); } diff --git a/GPU/GPUTracking/Base/GPUReconstructionConvert.cxx b/GPU/GPUTracking/Base/GPUReconstructionConvert.cxx index a4b17b81bf5ac..0aabb30f60e1a 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionConvert.cxx +++ b/GPU/GPUTracking/Base/GPUReconstructionConvert.cxx @@ -20,7 +20,7 @@ #endif #include "GPUReconstructionConvert.h" -#include "TPCFastTransform.h" +#include "TPCFastTransformPOD.h" #include "GPUTPCClusterData.h" #include "GPUO2DataTypes.h" #include "GPUDataTypesIO.h" @@ -48,7 +48,7 @@ using namespace o2::tpc; using namespace o2::tpc::constants; using namespace std::string_literals; -void GPUReconstructionConvert::ConvertNativeToClusterData(o2::tpc::ClusterNativeAccess* native, std::unique_ptr* clusters, uint32_t* nClusters, const TPCFastTransform* transform, int32_t continuousMaxTimeBin) +void GPUReconstructionConvert::ConvertNativeToClusterData(o2::tpc::ClusterNativeAccess* native, std::unique_ptr* clusters, uint32_t* nClusters, const TPCFastTransformPOD* transform, int32_t continuousMaxTimeBin) { memset(nClusters, 0, NSECTORS * sizeof(nClusters[0])); uint32_t offset = 0; diff --git a/GPU/GPUTracking/Base/GPUReconstructionConvert.h b/GPU/GPUTracking/Base/GPUReconstructionConvert.h index a24eb52a3a47c..3bb8a2b3df3c2 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionConvert.h +++ b/GPU/GPUTracking/Base/GPUReconstructionConvert.h @@ -41,7 +41,7 @@ namespace o2::gpu { struct GPUParam; struct GPUTPCClusterData; -class TPCFastTransform; +class TPCFastTransformPOD; struct GPUTrackingInOutDigits; struct GPUTrackingInOutZS; @@ -49,7 +49,7 @@ class GPUReconstructionConvert { public: constexpr static uint32_t NSECTORS = GPUCA_NSECTORS; - static void ConvertNativeToClusterData(o2::tpc::ClusterNativeAccess* native, std::unique_ptr* clusters, uint32_t* nClusters, const TPCFastTransform* transform, int32_t continuousMaxTimeBin = 0); + static void ConvertNativeToClusterData(o2::tpc::ClusterNativeAccess* native, std::unique_ptr* clusters, uint32_t* nClusters, const TPCFastTransformPOD* transform, int32_t continuousMaxTimeBin = 0); static void ConvertRun2RawToNative(o2::tpc::ClusterNativeAccess& native, std::unique_ptr& nativeBuffer, const AliHLTTPCRawCluster** rawClusters, uint32_t* nRawClusters); template static void RunZSEncoder(const S& in, std::unique_ptr* outBuffer, uint32_t* outSizes, o2::raw::RawFileWriter* raw, const o2::InteractionRecord* ir, const GPUParam& param, int32_t version, bool verify, float threshold = 0.f, bool padding = false, std::function&)> digitsFilter = nullptr); diff --git a/GPU/GPUTracking/Base/GPUReconstructionTimeframe.cxx b/GPU/GPUTracking/Base/GPUReconstructionTimeframe.cxx index fefcd0ac925fe..b605c99e393b1 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionTimeframe.cxx +++ b/GPU/GPUTracking/Base/GPUReconstructionTimeframe.cxx @@ -22,7 +22,7 @@ #include "GPUTPCMCInfo.h" #include "GPUTPCClusterData.h" #include "AliHLTTPCRawCluster.h" -#include "TPCFastTransform.h" +#include "TPCFastTransformPOD.h" #include "CorrectionMapsHelper.h" #include "GPUO2DataTypes.h" #include "GPUSettings.h" diff --git a/GPU/GPUTracking/DataTypes/GPUDataTypesIO.h b/GPU/GPUTracking/DataTypes/GPUDataTypesIO.h index 76fa569a16824..acd91939245d0 100644 --- a/GPU/GPUTracking/DataTypes/GPUDataTypesIO.h +++ b/GPU/GPUTracking/DataTypes/GPUDataTypesIO.h @@ -91,7 +91,7 @@ class ORTRootSerializer; namespace o2::gpu { class CorrectionMapsHelper; -class TPCFastTransform; +class TPCFastTransformPOD; struct TPCPadGainCalib; struct TPCZSLinkMapping; @@ -125,9 +125,7 @@ struct ConstPtr { template