diff --git a/Common/DCAFitter/README.md b/Common/DCAFitter/README.md index bbb079d144748..1699ffb4f8aca 100644 --- a/Common/DCAFitter/README.md +++ b/Common/DCAFitter/README.md @@ -2,7 +2,7 @@ \page refDetectorsVertexing DCAFitter /doxy --> -## DCAFitterN +# DCAFitterN Templated class to fit the Point of Closest Approach (PCA) of secondary vertex with N prongs. Allows minimization of either absolute or weighted Distances of Closest Approach (DCA) of N tracks to their common PCA. @@ -74,7 +74,22 @@ Extra method `setWeightedFinalPCA(bool)` is provided for the "mixed" mode: if `s but the final V0 position will be calculated using weighted average. One can also recalculate the V0 position by the weighted average method by calling explicitly `ft.recalculatePCAWithErrors(int icand=0)`, w/o prior call of `setWeightedFinalPCA(true)`: this will update the position returned by the `getPCACandidate(int cand = 0)`. -The covariance matrix of the V0 position is calculated as an inversed sum of tracks inversed covariances at respective `X_dca` points. +The covariance matrix of the V0 position is calculated as an inverted sum of tracks inversed covariances at respective `X_dca` points. See ``O2/Common/DCAFitter/test/testDCAFitterN.cxx`` for more extended example. Currently only 2 and 3 prongs permitted, thought this can be changed by modifying ``DCAFitterN::NMax`` constant. + +## Error handling + +It may happen that the track propagation to the the proximity of the PCA fails at the various stage of the fit. In this case the fit is abandoned and the failure flag is set, it can be checked using +isPropagationFailure(int cand = 0)` method. + +Also, due to the linearization errors the covariance matrix of the track propagated to some point may become non-positive defined. +In this case the relevant correlation coefficient of the cov.matrix is redefined to cure the position part of the cov.matrix and further program flow depends on the user settings for `DCAFitterN::setBadCovPolicy(v)`: + +`DCAFitterN::setBadCovPolicy(DCAFitterN::Discard);` : abandon fit (default) + +`DCAFitterN::setBadCovPolicy(DCAFitterN::Override);` : continue fit with overridden cov.matrix + +`DCAFitterN::setBadCovPolicy(DCAFitterN::OverrideAnFlag);` continue fit with overridden cov.matrix but set the propagation failure flag (can be checked using the same `isPropagationFailure(int cand = 0)` method). + diff --git a/Common/DCAFitter/include/DCAFitter/DCAFitterN.h b/Common/DCAFitter/include/DCAFitter/DCAFitterN.h index 2e36f7588e8be..6bd143eae44d6 100644 --- a/Common/DCAFitter/include/DCAFitter/DCAFitterN.h +++ b/Common/DCAFitter/include/DCAFitter/DCAFitterN.h @@ -26,34 +26,32 @@ namespace o2 { namespace vertexing { + ///__________________________________________________________________________________ ///< Inverse cov matrix (augmented by a dummy X error) of the point defined by the track struct TrackCovI { float sxx, syy, syz, szz; - GPUd() TrackCovI(const o2::track::TrackParCov& trc, float xerrFactor = 1.) { set(trc, xerrFactor); } - GPUdDefault() TrackCovI() = default; - GPUd() void set(const o2::track::TrackParCov& trc, float xerrFactor = 1) + GPUd() bool set(const o2::track::TrackParCov& trc, float xerrFactor = 1.f) { // we assign Y error to X for DCA calculation // (otherwise for quazi-collinear tracks the X will not be constrained) float cyy = trc.getSigmaY2(), czz = trc.getSigmaZ2(), cyz = trc.getSigmaZY(), cxx = cyy * xerrFactor; float detYZ = cyy * czz - cyz * cyz; - if (detYZ > 0.) { - auto detYZI = 1. / detYZ; - sxx = 1. / cxx; - syy = czz * detYZI; - syz = -cyz * detYZI; - szz = cyy * detYZI; - } else { -#ifndef GPUCA_GPUCODE - throw std::runtime_error("invalid track covariance"); -#else - printf("invalid track covariance\n"); -#endif + bool res = true; + if (detYZ <= 0.) { + cyz = o2::gpu::GPUCommonMath::Sqrt(cyy * czz) * (cyz > 0 ? 0.98f : -0.98f); + detYZ = cyy * czz - cyz * cyz; + res = false; } + auto detYZI = 1. / detYZ; + sxx = 1. / cxx; + syy = czz * detYZI; + syz = -cyz * detYZI; + szz = cyy * detYZI; + return res; } }; @@ -73,6 +71,27 @@ struct TrackDeriv { } }; +///__________________________________________________________________________ +///< Log log-throttling helper +struct LogLogThrottler { + size_t evCount{0}; + size_t evCountPrev{0}; + size_t logCount{0}; + + GPUdi() bool needToLog() + { + if (size_t(o2::gpu::GPUCommonMath::Log(++evCount)) + 1 > logCount) { + logCount++; + return true; + } + return false; + } + + GPUdi() size_t getNMuted() const { return evCount - evCountPrev - 1; } + + GPUdi() void clear() { evCount = evCountPrev = logCount = 0; } +}; + template class DCAFitterN { @@ -99,6 +118,12 @@ class DCAFitterN using ArrTrPos = o2::gpu::gpustd::array; // container of Track positions public: + enum BadCovPolicy { // if encountering non-positive defined cov. matrix, the choice is: + Discard = 0, // stop evaluation + Override = 1, // override correlation coef. to have cov.matrix pos.def and continue + OverrideAndFlag = 2 // override correlation coef. to have cov.matrix pos.def, set mPropFailed flag of corresponding candidate to true and continue (up to the user to check the flag) + }; + static constexpr int getNProngs() { return N; } DCAFitterN() = default; @@ -299,6 +324,9 @@ class DCAFitterN pnt[2] = tr.getZ(); } + void setBadCovPolicy(BadCovPolicy v) { mBadCovPolicy = v; } + BadCovPolicy getBadCovPolicy() const { return mBadCovPolicy; } + private: // vectors of 1st derivatives of track local residuals over X parameters o2::gpu::gpustd::array, N> mDResidDx; @@ -324,11 +352,15 @@ class DCAFitterN o2::gpu::gpustd::array mNIters; // number of iterations for each seed o2::gpu::gpustd::array mTrPropDone{}; // Flag that the tracks are fully propagated to PCA o2::gpu::gpustd::array mPropFailed{}; // Flag that some propagation failed for this PCA candidate + LogLogThrottler mLoggerBadCov{}; + LogLogThrottler mLoggerBadInv{}; + LogLogThrottler mLoggerBadProp{}; MatSym3D mWeightInv; // inverse weight of single track, [sum{M^T E M}]^-1 in EQ.T o2::gpu::gpustd::array mOrder{0}; int mCurHyp = 0; int mCrossIDCur = 0; int mCrossIDAlt = -1; + BadCovPolicy mBadCovPolicy{BadCovPolicy::Discard}; // what to do in case of non-pos-def. cov. matrix, see BadCovPolicy enum bool mAllowAltPreference = true; // if the fit converges to alternative PCA seed, abandon the current one bool mUseAbsDCA = false; // use abs. distance minimization rather than chi2 bool mWeightedFinalPCA = false; // recalculate PCA as a cov-matrix weighted mean, even if absDCA method was used @@ -677,7 +709,23 @@ GPUd() bool DCAFitterN::recalculatePCAWithErrors(int cand) mCurHyp = mOrder[cand]; if (mUseAbsDCA) { for (int i = N; i--;) { - mTrcEInv[mCurHyp][i].set(mCandTr[mCurHyp][i], XerrFactor); // prepare inverse cov.matrices at starting point + if (!mTrcEInv[mCurHyp][i].set(mCandTr[mCurHyp][i], XerrFactor)) { // prepare inverse cov.matrices at starting point + if (mLoggerBadCov.needToLog()) { +#ifndef GPUCA_GPUCODE + printf("fitter %d: error (%ld muted): overrode invalid track covariance from %s\n", + mFitterID, mLoggerBadCov.getNMuted(), mCandTr[mCurHyp][i].asString().c_str()); +#else + printf("fitter %d: error (%ld muted): overrode invalid track covariance cyy:%e czz:%e cyz:%e\n", + mFitterID, mLoggerBadCov.getNMuted(), mCandTr[mCurHyp][i].getSigmaY2(), mCandTr[mCurHyp][i].getSigmaZ2(), mCandTr[mCurHyp][i].getSigmaZY()); +#endif + mLoggerBadCov.evCountPrev = mLoggerBadCov.evCount; + } + if (mBadCovPolicy == Discard) { + return false; + } else if (mBadCovPolicy == OverrideAndFlag) { + mPropFailed[mCurHyp] = true; + } // otherwise, just use overridden errors w/o flagging + } } if (!calcPCACoefs()) { mCurHyp = saveCurHyp; @@ -884,7 +932,23 @@ GPUd() bool DCAFitterN::minimizeChi2() return false; } setTrackPos(mTrPos[mCurHyp][i], mCandTr[mCurHyp][i]); // prepare positions - mTrcEInv[mCurHyp][i].set(mCandTr[mCurHyp][i], XerrFactor); // prepare inverse cov.matrices at starting point + if (!mTrcEInv[mCurHyp][i].set(mCandTr[mCurHyp][i], XerrFactor)) { // prepare inverse cov.matrices at starting point + if (mLoggerBadCov.needToLog()) { +#ifndef GPUCA_GPUCODE + printf("fitter %d: error (%ld muted): overrode invalid track covariance from %s\n", + mFitterID, mLoggerBadCov.getNMuted(), mCandTr[mCurHyp][i].asString().c_str()); +#else + printf("fitter %d: error (%ld muted): overrode invalid track covariance cyy:%e czz:%e cyz:%e\n", + mFitterID, mLoggerBadCov.getNMuted(), mCandTr[mCurHyp][i].getSigmaY2(), mCandTr[mCurHyp][i].getSigmaZ2(), mCandTr[mCurHyp][i].getSigmaZY()); +#endif + mLoggerBadCov.evCountPrev = mLoggerBadCov.evCount; + } + if (mBadCovPolicy == Discard) { + return false; + } else if (mBadCovPolicy == OverrideAndFlag) { + mPropFailed[mCurHyp] = true; + } // otherwise, just use overridden errors w/o flagging + } } if (mMaxDZIni > 0 && !roughDZCut()) { // apply rough cut on tracks Z difference @@ -904,11 +968,10 @@ GPUd() bool DCAFitterN::minimizeChi2() // do Newton-Rapson iteration with corrections = - dchi2/d{x0..xN} * [ d^2chi2/d{x0..xN}^2 ]^-1 if (!mD2Chi2Dx2.Invert()) { -#ifndef GPUCA_GPUCODE_DEVICE - LOG(error) << "InversionFailed"; -#else - printf("InversionFailed\n"); -#endif + if (mLoggerBadInv.needToLog()) { + printf("fitter %d: error (%ld muted): Inversion failed\n", mFitterID, mLoggerBadCov.getNMuted()); + mLoggerBadInv.evCountPrev = mLoggerBadInv.evCount; + } return false; } VecND dx = mD2Chi2Dx2 * mDChi2Dx; @@ -961,11 +1024,10 @@ GPUd() bool DCAFitterN::minimizeChi2NoErr() // do Newton-Rapson iteration with corrections = - dchi2/d{x0..xN} * [ d^2chi2/d{x0..xN}^2 ]^-1 if (!mD2Chi2Dx2.Invert()) { -#ifndef GPUCA_GPUCODE_DEVICE - LOG(error) << "InversionFailed"; -#else - printf("InversionFailed\n"); -#endif + if (mLoggerBadInv.needToLog()) { + printf("itter %d: error (%ld muted): Inversion failed\n", mFitterID, mLoggerBadCov.getNMuted()); + mLoggerBadInv.evCountPrev = mLoggerBadInv.evCount; + } return false; } VecND dx = mD2Chi2Dx2 * mDChi2Dx; @@ -1108,6 +1170,14 @@ GPUdi() bool DCAFitterN::propagateParamToX(o2::track::TrackPar& t, f } if (!res) { mPropFailed[mCurHyp] = true; + if (mLoggerBadProp.needToLog()) { +#ifndef GPUCA_GPUCODE + printf("fitter %d: error (%ld muted): propagation failed for %s\n", mFitterID, mLoggerBadProp.getNMuted(), t.asString().c_str()); +#else + printf("fitter %d: error (%ld muted): propagation failed\n", mFitterID, mLoggerBadProp.getNMuted()); +#endif + mLoggerBadProp.evCountPrev = mLoggerBadProp.evCount; + } } return res; } @@ -1126,6 +1196,14 @@ GPUdi() bool DCAFitterN::propagateToX(o2::track::TrackParCov& t, flo } if (!res) { mPropFailed[mCurHyp] = true; + if (mLoggerBadProp.needToLog()) { +#ifndef GPUCA_GPUCODE + printf("fitter %d: error (%ld muted): propagation failed for %s\n", mFitterID, mLoggerBadProp.getNMuted(), t.asString().c_str()); +#else + printf("fitter %d: error (%ld muted): propagation failed\n", mFitterID, mLoggerBadProp.getNMuted()); +#endif + mLoggerBadProp.evCountPrev = mLoggerBadProp.evCount; + } } return res; } diff --git a/Common/ML/CMakeLists.txt b/Common/ML/CMakeLists.txt index 74287e774efa1..74be306c8b6a5 100644 --- a/Common/ML/CMakeLists.txt +++ b/Common/ML/CMakeLists.txt @@ -9,7 +9,21 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. +# Pass ORT variables as a preprocessor definition +if(DEFINED ENV{ORT_ROCM_BUILD}) + add_compile_definitions(ORT_ROCM_BUILD=$ENV{ORT_ROCM_BUILD}) +endif() +if(DEFINED ENV{ORT_CUDA_BUILD}) + add_compile_definitions(ORT_CUDA_BUILD=$ENV{ORT_CUDA_BUILD}) +endif() +if(DEFINED ENV{ORT_MIGRAPHX_BUILD}) + add_compile_definitions(ORT_MIGRAPHX_BUILD=$ENV{ORT_MIGRAPHX_BUILD}) +endif() +if(DEFINED ENV{ORT_TENSORRT_BUILD}) + add_compile_definitions(ORT_TENSORRT_BUILD=$ENV{ORT_TENSORRT_BUILD}) +endif() + o2_add_library(ML - SOURCES src/ort_interface.cxx + SOURCES src/OrtInterface.cxx TARGETVARNAME targetName PRIVATE_LINK_LIBRARIES O2::Framework ONNXRuntime::ONNXRuntime) diff --git a/Common/ML/include/ML/ort_interface.h b/Common/ML/include/ML/OrtInterface.h similarity index 96% rename from Common/ML/include/ML/ort_interface.h rename to Common/ML/include/ML/OrtInterface.h index e2049b8508cb4..89631d59a3846 100644 --- a/Common/ML/include/ML/ort_interface.h +++ b/Common/ML/include/ML/OrtInterface.h @@ -9,12 +9,12 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file ort_interface.h +/// \file OrtInterface.h /// \author Christian Sonnabend /// \brief A header library for loading ONNX models and inferencing them on CPU and GPU -#ifndef O2_ML_ONNX_INTERFACE_H -#define O2_ML_ONNX_INTERFACE_H +#ifndef O2_ML_ORTINTERFACE_H +#define O2_ML_ORTINTERFACE_H // C++ and system includes #include @@ -89,4 +89,4 @@ class OrtModel } // namespace o2 -#endif // O2_ML_ORT_INTERFACE_H +#endif // O2_ML_ORTINTERFACE_H diff --git a/Common/ML/src/ort_interface.cxx b/Common/ML/src/OrtInterface.cxx similarity index 87% rename from Common/ML/src/ort_interface.cxx rename to Common/ML/src/OrtInterface.cxx index 27ac8eee16b7b..eb124ff6f12c9 100644 --- a/Common/ML/src/ort_interface.cxx +++ b/Common/ML/src/OrtInterface.cxx @@ -9,11 +9,11 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file ort_interface.cxx +/// \file OrtInterface.cxx /// \author Christian Sonnabend /// \brief A header library for loading ONNX models and inferencing them on CPU and GPU -#include "ML/ort_interface.h" +#include "ML/OrtInterface.h" #include "ML/3rdparty/GPUORTFloat16.h" // ONNX includes @@ -50,29 +50,35 @@ void OrtModel::reset(std::unordered_map optionsMap) deviceId = (optionsMap.contains("device-id") ? std::stoi(optionsMap["device-id"]) : 0); allocateDeviceMemory = (optionsMap.contains("allocate-device-memory") ? std::stoi(optionsMap["allocate-device-memory"]) : 0); intraOpNumThreads = (optionsMap.contains("intra-op-num-threads") ? std::stoi(optionsMap["intra-op-num-threads"]) : 0); - loggingLevel = (optionsMap.contains("logging-level") ? std::stoi(optionsMap["logging-level"]) : 0); + loggingLevel = (optionsMap.contains("logging-level") ? std::stoi(optionsMap["logging-level"]) : 2); enableProfiling = (optionsMap.contains("enable-profiling") ? std::stoi(optionsMap["enable-profiling"]) : 0); enableOptimizations = (optionsMap.contains("enable-optimizations") ? std::stoi(optionsMap["enable-optimizations"]) : 0); std::string dev_mem_str = "Hip"; -#ifdef ORT_ROCM_BUILD +#if defined(ORT_ROCM_BUILD) +#if ORT_ROCM_BUILD == 1 if (device == "ROCM") { Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_ROCM(pImplOrt->sessionOptions, deviceId)); LOG(info) << "(ORT) ROCM execution provider set"; } #endif -#ifdef ORT_MIGRAPHX_BUILD +#endif +#if defined(ORT_MIGRAPHX_BUILD) +#if ORT_MIGRAPHX_BUILD == 1 if (device == "MIGRAPHX") { Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_MIGraphX(pImplOrt->sessionOptions, deviceId)); LOG(info) << "(ORT) MIGraphX execution provider set"; } #endif -#ifdef ORT_CUDA_BUILD +#endif +#if defined(ORT_CUDA_BUILD) +#if ORT_CUDA_BUILD == 1 if (device == "CUDA") { Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_CUDA(pImplOrt->sessionOptions, deviceId)); LOG(info) << "(ORT) CUDA execution provider set"; dev_mem_str = "Cuda"; } +#endif #endif if (allocateDeviceMemory) { @@ -106,7 +112,27 @@ void OrtModel::reset(std::unordered_map optionsMap) (pImplOrt->sessionOptions).SetGraphOptimizationLevel(GraphOptimizationLevel(enableOptimizations)); (pImplOrt->sessionOptions).SetLogSeverityLevel(OrtLoggingLevel(loggingLevel)); - pImplOrt->env = std::make_shared(OrtLoggingLevel(loggingLevel), (optionsMap["onnx-environment-name"].empty() ? "onnx_model_inference" : optionsMap["onnx-environment-name"].c_str())); + pImplOrt->env = std::make_shared( + OrtLoggingLevel(loggingLevel), + (optionsMap["onnx-environment-name"].empty() ? "onnx_model_inference" : optionsMap["onnx-environment-name"].c_str()), + // Integrate ORT logging into Fairlogger + [](void* param, OrtLoggingLevel severity, const char* category, const char* logid, const char* code_location, const char* message) { + if (severity == ORT_LOGGING_LEVEL_VERBOSE) { + LOG(debug) << "(ORT) [" << logid << "|" << category << "|" << code_location << "]: " << message; + } else if (severity == ORT_LOGGING_LEVEL_INFO) { + LOG(info) << "(ORT) [" << logid << "|" << category << "|" << code_location << "]: " << message; + } else if (severity == ORT_LOGGING_LEVEL_WARNING) { + LOG(warning) << "(ORT) [" << logid << "|" << category << "|" << code_location << "]: " << message; + } else if (severity == ORT_LOGGING_LEVEL_ERROR) { + LOG(error) << "(ORT) [" << logid << "|" << category << "|" << code_location << "]: " << message; + } else if (severity == ORT_LOGGING_LEVEL_FATAL) { + LOG(fatal) << "(ORT) [" << logid << "|" << category << "|" << code_location << "]: " << message; + } else { + LOG(info) << "(ORT) [" << logid << "|" << category << "|" << code_location << "]: " << message; + } + }, + (void*)3); + (pImplOrt->env)->DisableTelemetryEvents(); // Disable telemetry events pImplOrt->session = std::make_shared(*(pImplOrt->env), modelPath.c_str(), pImplOrt->sessionOptions); for (size_t i = 0; i < (pImplOrt->session)->GetInputCount(); ++i) { @@ -130,16 +156,14 @@ void OrtModel::reset(std::unordered_map optionsMap) [&](const std::string& str) { return str.c_str(); }); // Print names - if (loggingLevel > 1) { - LOG(info) << "Input Nodes:"; - for (size_t i = 0; i < mInputNames.size(); i++) { - LOG(info) << "\t" << mInputNames[i] << " : " << printShape(mInputShapes[i]); - } + LOG(info) << "\tInput Nodes:"; + for (size_t i = 0; i < mInputNames.size(); i++) { + LOG(info) << "\t\t" << mInputNames[i] << " : " << printShape(mInputShapes[i]); + } - LOG(info) << "Output Nodes:"; - for (size_t i = 0; i < mOutputNames.size(); i++) { - LOG(info) << "\t" << mOutputNames[i] << " : " << printShape(mOutputShapes[i]); - } + LOG(info) << "\tOutput Nodes:"; + for (size_t i = 0; i < mOutputNames.size(); i++) { + LOG(info) << "\t\t" << mOutputNames[i] << " : " << printShape(mOutputShapes[i]); } } diff --git a/Common/SimConfig/src/SimConfig.cxx b/Common/SimConfig/src/SimConfig.cxx index be21c38c5efc8..de494d565fd6a 100644 --- a/Common/SimConfig/src/SimConfig.cxx +++ b/Common/SimConfig/src/SimConfig.cxx @@ -487,8 +487,8 @@ void SimConfig::adjustFromCollContext(std::string const& collcontextfile, std::s // we take what is specified in the context mConfigData.mNEvents = collisionmap.size(); } else { - LOG(warning) << "The number of events on the command line " << mConfigData.mNEvents << " and in the collision context differ. Taking the min of the 2"; - mConfigData.mNEvents = std::min((size_t)mConfigData.mNEvents, collisionmap.size()); + LOG(warning) << "The number of events on the command line " << mConfigData.mNEvents << " and in the collision context differ. We take the one from collision context " << collisionmap.size(); + mConfigData.mNEvents = collisionmap.size(); } LOG(info) << "Setting number of events to simulate to " << mConfigData.mNEvents; } diff --git a/Common/Utils/CMakeLists.txt b/Common/Utils/CMakeLists.txt index 786ccc8f784fe..18f2aa7c1b6ed 100644 --- a/Common/Utils/CMakeLists.txt +++ b/Common/Utils/CMakeLists.txt @@ -81,6 +81,12 @@ o2_add_test(MemFileHelper SOURCES test/testMemFileHelper.cxx PUBLIC_LINK_LIBRARIES O2::CommonUtils) +o2_add_test(EnumFlags + COMPONENT_NAME CommonUtils + LABELS utils + SOURCES test/testEnumFlags.cxx + PUBLIC_LINK_LIBRARIES O2::CommonUtils) + o2_add_executable(treemergertool COMPONENT_NAME CommonUtils SOURCES src/TreeMergerTool.cxx diff --git a/Common/Utils/include/CommonUtils/EnumBitOperators.h b/Common/Utils/include/CommonUtils/EnumBitOperators.h deleted file mode 100644 index 3369a8eacf615..0000000000000 --- a/Common/Utils/include/CommonUtils/EnumBitOperators.h +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -#ifndef O2_FRAMEWORK_ENUM_BIT_OPERATORS_H_ -#define O2_FRAMEWORK_ENUM_BIT_OPERATORS_H_ - -#include - -#define O2_DEFINE_ENUM_BIT_OPERATORS(enum_t) \ - constexpr auto operator|(enum_t lhs, enum_t rhs) \ - { \ - return static_cast( \ - static_cast>(lhs) | \ - static_cast>(rhs)); \ - } \ - \ - constexpr auto operator&(enum_t lhs, enum_t rhs) \ - { \ - return static_cast( \ - static_cast>(lhs) & \ - static_cast>(rhs)); \ - } \ - \ - constexpr auto operator^(enum_t lhs, enum_t rhs) \ - { \ - return static_cast( \ - static_cast>(lhs) ^ \ - static_cast>(rhs)); \ - } \ - \ - constexpr auto operator~(enum_t op) \ - { \ - return static_cast( \ - ~static_cast>(op)); \ - } \ - \ - constexpr auto& operator|=(enum_t& lhs, enum_t rhs) \ - { \ - lhs = lhs | rhs; \ - return lhs; \ - } \ - \ - constexpr auto& operator&=(enum_t& lhs, enum_t rhs) \ - { \ - lhs = lhs & rhs; \ - return lhs; \ - } \ - \ - constexpr enum_t& operator^=(enum_t& lhs, enum_t rhs) \ - { \ - lhs = lhs ^ rhs; \ - return lhs; \ - } - -#define O2_ENUM_TEST_BIT(mask, value) ((mask & value) == value) -#define O2_ENUM_SET_BIT(bit) ((1 << bit)) -#define O2_ENUM_ANY_BIT(enum) ((static_cast>(enum) != 0)) - -#endif diff --git a/Common/Utils/include/CommonUtils/EnumFlags.h b/Common/Utils/include/CommonUtils/EnumFlags.h new file mode 100644 index 0000000000000..c4dba607d7804 --- /dev/null +++ b/Common/Utils/include/CommonUtils/EnumFlags.h @@ -0,0 +1,699 @@ +// 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_FRAMEWORK_FLAGS_H_ +#define O2_FRAMEWORK_FLAGS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CommonUtils/StringUtils.h" + +namespace o2::utils +{ + +namespace details::enum_flags +{ + +// Require that an enum with an underlying unsigned type. +template +concept EnumFlagHelper = requires { + requires std::is_enum_v; + requires std::is_unsigned_v>; + requires std::same_as>; +}; + +// Static constexpr only helper struct to implement modicum of enum reflection +// functions and also check via concepts expected properties of the enum. +// This is very much inspired by much more extensive libraries like magic_enum. +// Inspiration by its c++20 version (https://github.com/fix8mt/conjure_enum). +template +struct FlagsHelper final { + using U = std::underlying_type_t; + + static constexpr bool isScoped() noexcept + { + return std::is_enum_v && !std::is_convertible_v>; + } + + // Return line at given position. + template + static consteval const char* tpeek() noexcept + { + return std::source_location::current().function_name(); + } + // string_view value of function above + template + static constexpr std::string_view tpeek_v{tpeek()}; + + // Compiler Specifics + static constexpr auto CSpecifics{std::to_array< + std::tuple>({ +#if defined __clang__ + {"e = ", ']', "(anonymous namespace)", '('}, + {"T = ", ']', "(anonymous namespace)", '('}, +#else // assuming __GNUC__ + {"e = ", ';', "", '<'}, + {"T = ", ']', "{anonymous}", '{'}, +#endif + })}; + enum class SVal : uint8_t { Start, + End, + AnonStr, + AnonStart }; + enum class SType : uint8_t { Enum_t, + Type_t, + eT0, + eT1, + eT2, + eT3 }; + // Extract a compiler specification. + template + static constexpr auto getSpec() noexcept + { + return std::get(v)>(CSpecifics[static_cast(t)]); + } + + // Range that is scanned by the compiler + static constexpr size_t MinScan{0}; + static constexpr size_t MarginScan{1}; // Scan one past to check for overpopulation + static constexpr size_t MaxUnderScan{std::numeric_limits::digits}; // Maximum digits the underlying type has + static constexpr size_t MaxScan{MaxUnderScan + MarginScan}; + + // Checks if a given 'localation' contains an enum. + template + static constexpr bool isValid() noexcept + { + constexpr auto tp{tpeek_v.rfind(getSpec())}; + if constexpr (tp == std::string_view::npos) { + return false; + } +#if defined __clang__ + else if constexpr (tpeek_v[tp + getSpec().size()] == '(') { + if constexpr (tpeek_v[tp + getSpec().size() + 1] == '(') { + return false; + } + if constexpr (tpeek_v.find(getSpec(), tp + getSpec().size()) != std::string_view::npos) { + return true; + } + } else if constexpr (tpeek_v.find_first_of(getSpec(), tp + getSpec().size()) != std::string_view::npos) { + // check if this is an anonymous enum + return true; + } + return false; +#else + else if constexpr (tpeek_v[tp + getSpec().size()] != '(' && tpeek_v.find_first_of(getSpec(), tp + getSpec().size()) != std::string_view::npos) { + return true; + } else { + return false; + } +#endif + } + + // Extract which values are present in the enum by checking all values in + // the min-max-range above. + template + static constexpr auto getValues(std::index_sequence /*unused*/) noexcept + { + constexpr std::array valid{isValid(MinScan + I)>()...}; + constexpr auto count{std::count_if(valid.cbegin(), valid.cend(), [](bool v) noexcept { return v; })}; + static_assert(count > 0, "Requiring non-empty enum!"); + static_assert(count <= MaxUnderScan, "Underlying type of enum has less digits than given expected!"); + std::array values{}; + for (size_t idx{}, n{}; n < count; ++idx) { + if (valid[idx]) { + values[n++] = static_cast(MinScan + idx); + } + } + return values; + } + static constexpr auto Values{getValues(std::make_index_sequence())}; // Enum Values + static constexpr auto count() noexcept { return Values.size(); } // Number of enum members + static constexpr auto Min_v{Values.front()}; // Enum first entry + static constexpr auto Max_v{Values.back()}; // Enum last entry + static constexpr auto Min_u_v{static_cast(Min_v)}; // Enum first entry as size_t + static constexpr auto Max_u_v{static_cast(Max_v)}; // Enum last entry as size_t + static constexpr bool isContinuous() noexcept { return (Max_u_v - Min_u_v + 1) == count(); } // Is the enum continuous + static constexpr uint64_t MaxRep{(Max_u_v >= 64) ? std::numeric_limits::max() : (1ULL << Max_u_v) - 1}; // largest representable value + + template + static constexpr std::string_view getName() + { + constexpr auto tp{tpeek_v.rfind(getSpec())}; + if constexpr (tp == std::string_view::npos) { + return {}; + } + if constexpr (tpeek_v[tp + getSpec().size()] == getSpec()) { +#if defined __clang__ + if constexpr (tpeek_v[tp + getSpec().size() + 1] == getSpec()) { + return {}; + } +#endif + if (constexpr auto lstr{tpeek_v.substr(tp + getSpec().size())}; lstr.find(getSpec()) != std::string_view::npos) { // is anon + if constexpr (constexpr auto lc{lstr.find_first_of(getSpec())}; lc != std::string_view::npos) { + return lstr.substr(getSpec().size() + 2, lc - (getSpec().size() + 2)); + } + } + } + constexpr std::string_view result{tpeek_v.substr(tp + getSpec().size())}; + if constexpr (constexpr auto lc{result.find_first_of(getSpec())}; lc != std::string_view::npos) { + return result.substr(0, lc); + } else { + return {}; + } + } + + static constexpr std::string_view removeScope(std::string_view s) + { + if (const auto lc{s.find_last_of(':')}; lc != std::string_view::npos) { + return s.substr(lc + 1); + } + return s; + } + + static constexpr std::string_view findScope(std::string_view s) + { + const auto pos1 = s.rfind("::"); + if (pos1 == std::string_view::npos) { + return s; + } + const auto pos2 = s.rfind("::", pos1 - 1); + if (pos2 == std::string_view::npos) { + return s.substr(0, pos1); + } + return s.substr(pos2 + 2, pos1 - pos2 - 2); + } + + template + static constexpr auto getNameValue{getName()}; + + template + static constexpr auto getNames(std::index_sequence /*unused*/) + { + if constexpr (with_scope) { + return std::array{getNameValue...}; + } else { + return std::array{removeScope(getNameValue)...}; + } + } + + static constexpr auto Names{getNames(std::make_index_sequence())}; // Enum names without scope + static constexpr auto NamesScoped{getNames(std::make_index_sequence())}; // Enum names with scope + static constexpr auto Scope{findScope(NamesScoped.front())}; // Enum scope + + static constexpr auto getLongestName() noexcept + { + size_t max{0}; + for (size_t i{0}; i < count(); ++i) { + max = std::max(max, Names[i].size()); + } + return max; + } + + static constexpr auto NamesLongest{getLongestName()}; // Size of longest name + + template + static constexpr std::string_view toString() noexcept + { + return getNameValue(); + } + + static constexpr std::optional fromString(std::string_view str) noexcept + { + for (std::size_t i{0}; i < count(); ++i) { + if (Names[i] == str || NamesScoped[i] == str) { + return Values[i]; + } + } + return std::nullopt; + } + + // Convert char to lower. + static constexpr unsigned char toLower(const unsigned char c) noexcept + { + return (c >= 'A' && c <= 'Z') ? (c - 'A' + 'a') : c; + } + + // Are these chars equal (case-insensitive). + static constexpr bool isIEqual(const unsigned char a, const unsigned char b) noexcept + { + return toLower(a) == toLower(b); + } + + // Case-insensitive comparision for string_view. + static constexpr bool isIEqual(std::string_view s1, std::string_view s2) noexcept + { + if (s1.size() != s2.size()) { + return false; + } + for (size_t i{0}; i < s1.size(); ++i) { + if (!isIEqual(s1[i], s2[i])) { + return false; + } + } + return true; + } + + static constexpr std::string_view None{"none"}; + static constexpr bool hasNone() noexcept + { + // check that enum does not contain memeber named 'none' + for (size_t i{0}; i < count(); ++i) { + if (isIEqual(Names[i], None)) { + return true; + } + } + return false; + } + + static constexpr std::string_view All{"all"}; + static constexpr bool hasAll() noexcept + { + // check that enum does not contain memeber named 'all' + for (size_t i{0}; i < count(); ++i) { + if (isIEqual(Names[i], All)) { + return true; + } + } + return false; + } +}; + +} // namespace details::enum_flags + +// Require an enum to fullfil what one would except from a bitset. +template +concept EnumFlag = requires { + // range checks + requires details::enum_flags::FlagsHelper::Min_u_v == 0; // the first bit should be at position 0 + requires details::enum_flags::FlagsHelper::Max_u_v < details::enum_flags::FlagsHelper::count(); // the maximum is less than the total + requires details::enum_flags::FlagsHelper::isContinuous(); // do not allow missing bits + + // type checks + requires !details::enum_flags::FlagsHelper::hasNone(); // added automatically + requires !details::enum_flags::FlagsHelper::hasAll(); // added automatically +}; + +/** + * \brief Classs to aggregate and manage enum-based on-off flags. + * + * This class manages flags as bits in the underlying type of an enum, allowing + * manipulation via enum member names. It supports operations akin to std::bitset + * but is fully constexpr and is ideal for aggregating multiple on-off booleans, + * e.g., enabling/disabling algorithm features. + * + * Example: + * enum class AlgoOptions { + * Feature1, + * Feature2, + * Feature3, + * }; + * ... + * EnumFlags opts; + * opts.set("Feature1 | Feature3"); // Set Feature1 and Feature3. + * if (opts[AlgoOptions::Feature1]) { // Do some work. } // Check if Feature1 is set. + * + * Additional examples of how to use this class are in testEnumFlags.cxx. + */ +template +class EnumFlags +{ + using H = details::enum_flags::FlagsHelper; + using U = std::underlying_type_t; + U mBits{0}; + + // Converts enum to its underlying type. + constexpr auto to_underlying(E e) const noexcept + { + return static_cast(e); + } + + // Returns the bit representation of a flag. + constexpr auto to_bit(E e) const noexcept + { + return U(1) << to_underlying(e); + } + + public: + // Default constructor. + constexpr explicit EnumFlags() = default; + // Constructor to initialize with a single flag. + constexpr explicit EnumFlags(E e) : mBits(to_bit(e)) {} + // Copy constructor. + constexpr EnumFlags(const EnumFlags&) = default; + // Move constructor. + constexpr EnumFlags(EnumFlags&&) = default; + // Constructor to initialize with the underlyiny type. + constexpr explicit EnumFlags(U u) : mBits(u) {} + // Initialize with a list of flags. + constexpr EnumFlags(std::initializer_list flags) noexcept + { + std::for_each(flags.begin(), flags.end(), [this](const E f) noexcept { mBits |= to_bit(f); }); + } + // Destructor. + constexpr ~EnumFlags() = default; + + static constexpr U None{0}; // Represents no flags set. + static constexpr U All{H::MaxRep}; // Represents all flags set. + + // Return list of all enum values + static constexpr auto getValues() noexcept + { + return H::Values; + } + + // Return list of all enum Names + static constexpr auto getNames() noexcept + { + return H::Names; + } + + // Sets flags from a string representation. + // This can be either from a number representation (binary or digits) or + // a concatenation of the enums members name e.g., 'Enum1|Enum2|...' + void set(const std::string& s, int base = 2) + { + // on throw restore previous state and rethrow + const U prev = mBits; + reset(); + try { + setImpl(s, base); + } catch (const std::exception& e) { + mBits = prev; + throw; + } + } + // Returns the raw bitset value. + constexpr auto value() const noexcept + { + return mBits; + } + + // Resets all flags. + constexpr void reset() noexcept + { + mBits = U(0); + } + + // Resets a specific flag. + template + requires std::is_same_v + constexpr void reset(T t) + { + mBits &= ~to_bit(t); + } + + // Tests if a specific flag is set. + template + requires std::is_same_v + [[nodiscard]] constexpr bool test(T t) const noexcept + { + return (mBits & to_bit(t)) != None; + } + + // Sets a specific flag. + template + requires std::is_same_v + constexpr void set(T t) noexcept + { + mBits |= to_bit(t); + } + + // Toggles a specific flag. + template + requires std::is_same_v + constexpr void toggle(T t) noexcept + { + mBits ^= to_bit(t); + } + + // Checks if any flag is set. + [[nodiscard]] constexpr bool any() const noexcept + { + return mBits != None; + } + + // Returns the bitset as a binary string. + [[nodiscard]] std::string string() const + { + std::ostringstream oss; + oss << std::bitset(mBits); + return oss.str(); + } + + // Returns the bitset as a pretty multiline binary string. + [[nodiscard]] std::string pstring(bool withNewline = false) const + { + std::ostringstream oss; + if (withNewline) { + oss << '\n'; + } + oss << "0b"; + const std::bitset bits(mBits); + oss << bits; + if constexpr (H::isScoped()) { + oss << " " << H::Scope; + } + oss << '\n'; + for (size_t i = 0; i < H::count(); ++i) { + oss << " "; + for (size_t j = 0; j < H::count() - i - 1; ++j) { + oss << "┃"; + } + oss << "┗"; + for (size_t a{2 + i}; --a != 0U;) { + oss << "━"; + } + oss << " " << std::setw(H::NamesLongest) << std::left + << H::Names[i] << " " << (bits[i] ? "[Active]" : "[Inactive]"); + if (i != H::count() - 1) { + oss << "\n"; + } + } + return oss.str(); + } + + // Checks if any flag is set (Boolean context). + constexpr explicit operator bool() const noexcept + { + return any(); + } + + // Check if given flag is set. + template + requires std::is_same_v + constexpr bool operator[](const T t) noexcept + { + return test(t); + } + + // Checks if two flag sets are equal. + constexpr bool operator==(const EnumFlags& o) const noexcept + { + return mBits == o.mBits; + } + + // Checks if two flag sets are not equal. + constexpr bool operator!=(const EnumFlags& o) const noexcept + { + return mBits != o.mBits; + } + + // Copy assignment operator + constexpr EnumFlags& operator=(const EnumFlags& o) = default; + + // Move assignment operator + constexpr EnumFlags& operator=(EnumFlags&& o) = default; + + // Performs a bitwise OR with a flag. + template + requires std::is_same_v + constexpr EnumFlags& operator|=(T t) noexcept + { + mBits |= to_bit(t); + return *this; + } + + // Performs a bitwise AND with a flag. + template + requires std::is_same_v + constexpr EnumFlags& operator&=(T t) noexcept + { + mBits &= to_bit(t); + return *this; + } + + // Returns a flag set with a bitwise AND. + template + requires std::is_same_v + constexpr EnumFlags operator&(T t) const noexcept + { + return EnumFlags(mBits & to_bit(t)); + } + + // Returns a flag set with all bits inverted. + constexpr EnumFlags operator~() const noexcept + { + return EnumFlags(~mBits); + } + + // Performs a bitwise OR with another flag set. + constexpr EnumFlags operator|(const EnumFlags& o) const noexcept + { + return EnumFlags(mBits | o.mBits); + } + + // Performs a bitwise OR assignment. + constexpr EnumFlags& operator|=(const EnumFlags& o) noexcept + { + mBits |= o.mBits; + return *this; + } + + // Performs a bitwise XOR with another flag set. + constexpr EnumFlags operator^(const EnumFlags& o) const noexcept + { + return Flags(mBits ^ o.mBits); + } + + // Performs a bitwise XOR assignment. + constexpr EnumFlags& operator^=(const EnumFlags& o) noexcept + { + mBits ^= o.mBits; + return *this; + } + + // Checks if all specified flags are set. + template + constexpr bool all_of(Ts... flags) const noexcept + { + return ((test(flags) && ...)); + } + + // Checks if none of the specified flags are set. + template + constexpr bool none_of(Ts... flags) const noexcept + { + return (!(test(flags) || ...)); + } + + // Serializes the flag set to a string. + [[nodiscard]] std::string serialize() const + { + return std::to_string(mBits); + } + + // Deserializes a string into the flag set. + void deserialize(const std::string& data) + { + uint64_t v = std::stoul(data); + if (v > H::MaxRep) { + throw std::out_of_range("Values exceeds enum range."); + } + mBits = static_cast(v); + } + + // Counts the number of set bits (active flags). + [[nodiscard]] constexpr size_t count() const noexcept + { + size_t c{0}; + for (size_t i{H::Min_u_v}; i < H::Max_u_v; ++i) { + if ((mBits & (U(1) << i)) != U(0)) { + ++c; + } + } + return c; + } + + // Returns the union of two flag sets. + constexpr EnumFlags union_with(const EnumFlags& o) const noexcept + { + return EnumFlags(mBits | o.mBits); + } + + // Returns the intersection of two flag sets. + constexpr EnumFlags intersection_with(const EnumFlags& o) const noexcept + { + return EnumFlags(mBits & o.mBits); + } + + // Checks if all flags in another Flags object are present in the current object. + constexpr bool contains(const EnumFlags& other) const noexcept + { + return (mBits & other.mBits) == other.mBits; + } + + private: + // Set implemnetation, bits was zeroed before. + void setImpl(const std::string& s, int base = 2) + { + if (std::all_of(s.begin(), s.end(), [](unsigned char c) { return std::isdigit(c); })) { + if (base == 2) { // check of only 0 and 1 in string + if (!std::all_of(s.begin(), s.end(), [](char c) { return c == '0' || c == '1'; })) { + throw std::invalid_argument("Invalid binary string."); + } + } + uint64_t v = std::stoul(s, nullptr, base); + if (v > H::MaxRep) { + throw std::out_of_range("Values exceeds enum range."); + } + mBits = static_cast(v); + } else if (std::all_of(s.begin(), s.end(), [](unsigned char c) { return std::isalnum(c) != 0 || c == '|' || c == ' ' || c == ':'; })) { + std::string cs{s}; + std::transform(cs.begin(), cs.end(), cs.begin(), [](unsigned char c) { return std::tolower(c); }); + if (cs == H::All) { + mBits = All; + } else if (cs == H::None) { + mBits = None; + } else { + for (const auto& tok : Str::tokenize(s, '|')) { + if (auto e = H::fromString(tok)) { + mBits |= to_bit(*e); + } else { + throw std::invalid_argument(tok + " is not a valid enum value!"); + } + } + } + } else { + throw std::invalid_argument("Cannot parse string!"); + } + } +}; + +template +std::ostream& operator<<(std::ostream& os, const EnumFlags& f) +{ + os << f.pstring(true); + return os; +} + +} // namespace o2::utils + +#endif diff --git a/Common/Utils/include/CommonUtils/IRFrameSelector.h b/Common/Utils/include/CommonUtils/IRFrameSelector.h index 6312ae8314c3a..a4365030b6a12 100644 --- a/Common/Utils/include/CommonUtils/IRFrameSelector.h +++ b/Common/Utils/include/CommonUtils/IRFrameSelector.h @@ -46,6 +46,8 @@ class IRFrameSelector auto getIRFrames() const { return mFrames; } bool isSet() const { return mIsSet; } + void setOwnList(const std::vector& lst, bool toBeSorted); + private: gsl::span mFrames{}; // externally provided span of IRFrames, must be sorted in IRFrame.getMin() o2::dataformats::IRFrame mLastIRFrameChecked{}; // last frame which was checked diff --git a/Common/Utils/src/FileFetcher.cxx b/Common/Utils/src/FileFetcher.cxx index 41265764246da..048402712ccd7 100644 --- a/Common/Utils/src/FileFetcher.cxx +++ b/Common/Utils/src/FileFetcher.cxx @@ -345,6 +345,9 @@ bool FileFetcher::copyFile(size_t id) gSystem->Setenv("XRD_LOGLEVEL", "Dump"); logsToClean.push_back(fmt::format("log_xrd_{}.txt", uuid)); gSystem->Setenv("XRD_LOGFILE", logsToClean.back().c_str()); + LOGP(info, "debug log files for {}: ALIENPY_DEBUG_FILE={} XRD_LOGFILE={}", mInputFiles[id].getOrigName(), + getenv("ALIENPY_DEBUG_FILE") ? getenv("ALIENPY_DEBUG_FILE") : "", + getenv("XRD_LOGFILE") ? getenv("XRD_LOGFILE") : ""); } auto realCmd = std::regex_replace(std::regex_replace(mCopyCmd, std::regex(R"(\?src)"), mInputFiles[id].getOrigName()), std::regex(R"(\?dst)"), mInputFiles[id].getLocalName()); auto fullCmd = fmt::format(R"(sh -c "{}" >> {} 2>&1)", realCmd, mCopyCmdLogFile); diff --git a/Common/Utils/src/IRFrameSelector.cxx b/Common/Utils/src/IRFrameSelector.cxx index 8122484659f45..abc0ee1ee6ce3 100644 --- a/Common/Utils/src/IRFrameSelector.cxx +++ b/Common/Utils/src/IRFrameSelector.cxx @@ -167,6 +167,16 @@ size_t IRFrameSelector::loadIRFrames(const std::string& fname) return mOwnList.size(); } +void IRFrameSelector::setOwnList(const std::vector& lst, bool toBeSorted) +{ + clear(); + mOwnList.insert(mOwnList.end(), lst.begin(), lst.end()); + if (toBeSorted) { + std::sort(mOwnList.begin(), mOwnList.end(), [](const auto& a, const auto& b) { return a.getMin() < b.getMin(); }); + } + setSelectedIRFrames(mOwnList, 0, 0, 0, false); +} + void IRFrameSelector::print(bool lst) const { LOGP(info, "Last query stopped at entry {} for IRFrame {}:{}", mLastBoundID, @@ -183,6 +193,8 @@ void IRFrameSelector::clear() { mIsSet = false; mOwnList.clear(); + mLastIRFrameChecked.getMin().clear(); // invalidate + mLastBoundID = -1; mFrames = {}; } diff --git a/Common/Utils/test/testEnumFlags.cxx b/Common/Utils/test/testEnumFlags.cxx new file mode 100644 index 0000000000000..2838d09b2e6a3 --- /dev/null +++ b/Common/Utils/test/testEnumFlags.cxx @@ -0,0 +1,244 @@ +// 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. + +#define BOOST_TEST_MODULE Test Flags +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +#include +#include +#include + +#include "CommonUtils/EnumFlags.h" + +// Example enum to use with EnumFlags +enum class TestEnum : uint8_t { + Bit1, + Bit2, + Bit3, + Bit4, + Bit5VeryLongName, +}; + +BOOST_AUTO_TEST_CASE(Flags_test) +{ + using EFlags = o2::utils::EnumFlags; + + // Test default initialization + EFlags flags; + BOOST_TEST(flags.value() == 0); + BOOST_TEST(!flags.any()); + + // Test initialization with a single flag + EFlags flag1(TestEnum::Bit1); + BOOST_TEST(flag1.test(TestEnum::Bit1)); + BOOST_TEST(!flag1.test(TestEnum::Bit2)); + BOOST_TEST(flag1.value() == (1 << static_cast(TestEnum::Bit1))); + + // Test initialization with initializer list + EFlags multipleFlags({TestEnum::Bit1, TestEnum::Bit3}); + BOOST_TEST(multipleFlags.test(TestEnum::Bit1)); + BOOST_TEST(multipleFlags.test(TestEnum::Bit3)); + BOOST_TEST(!multipleFlags.test(TestEnum::Bit2)); + BOOST_TEST(multipleFlags.any()); + + // Test reset + multipleFlags.reset(TestEnum::Bit1); + BOOST_TEST(!multipleFlags.test(TestEnum::Bit1)); + BOOST_TEST(multipleFlags.test(TestEnum::Bit3)); + multipleFlags.reset(); + BOOST_TEST(!multipleFlags.any()); + + // Test operator| + EFlags combinedFlags = flag1 | EFlags(TestEnum::Bit2); + BOOST_TEST(combinedFlags.test(TestEnum::Bit1)); + BOOST_TEST(combinedFlags.test(TestEnum::Bit2)); + BOOST_TEST(!combinedFlags.test(TestEnum::Bit3)); + + // Test operator[] + BOOST_TEST(combinedFlags[TestEnum::Bit1]); + BOOST_TEST(combinedFlags[TestEnum::Bit2]); + BOOST_TEST(!combinedFlags[TestEnum::Bit3]); + + // Test operator|= + combinedFlags |= TestEnum::Bit3; + BOOST_TEST(combinedFlags.test(TestEnum::Bit3)); + + // Test operator& + EFlags intersection = combinedFlags & TestEnum::Bit1; + BOOST_TEST(intersection.test(TestEnum::Bit1)); + BOOST_TEST(!intersection.test(TestEnum::Bit2)); + BOOST_TEST(intersection.value() == (1 << static_cast(TestEnum::Bit1))); + + // Test operator&= + combinedFlags &= TestEnum::Bit1; + BOOST_TEST(combinedFlags.test(TestEnum::Bit1)); + BOOST_TEST(!combinedFlags.test(TestEnum::Bit2)); + BOOST_TEST(!combinedFlags.test(TestEnum::Bit3)); + + // Test operator~ (complement) + EFlags complement = ~EFlags(TestEnum::Bit1); + BOOST_TEST(!complement.test(TestEnum::Bit1)); + BOOST_TEST(complement.test(TestEnum::Bit2)); + BOOST_TEST(complement.test(TestEnum::Bit3)); + + // Test string() method + { + std::string flagString = flag1.string(); + BOOST_TEST(flagString.back() == '1'); // Ensure the least significant bit is set for flag1 + } + + // Test set with binary string + { + std::string binaryStr = "101"; + flags.set(binaryStr, 2); + BOOST_TEST(flags.test(TestEnum::Bit1)); + BOOST_TEST(!flags.test(TestEnum::Bit2)); + BOOST_TEST(flags.test(TestEnum::Bit3)); + } + + // Test invalid binary string in set + BOOST_CHECK_THROW(flags.set(std::string("invalid"), 2), std::invalid_argument); + + // Test range validation in set + BOOST_CHECK_THROW(flags.set(std::string("100000000"), 2), std::out_of_range); + + { // Test that return lists are sensible + const auto n = flags.getNames(); + const auto v = flags.getValues(); + BOOST_CHECK(n.size() == v.size()); + } + + { // print test + std::cout << flags; + } + + // Test flag tokenization and parsing + { + { // only one scoped flag + std::string str = "TestEnum::Bit2"; + flags.set(str); + BOOST_TEST(flags.test(TestEnum::Bit2)); + BOOST_TEST(flags.none_of(TestEnum::Bit1, TestEnum::Bit3, TestEnum::Bit4)); + } + + { // test with ws-triming and scope mixing + std::string str = "Bit4|TestEnum::Bit2 | Bit1 "; + flags.set(str); + BOOST_TEST(flags.test(TestEnum::Bit1)); + BOOST_TEST(flags.test(TestEnum::Bit2)); + BOOST_TEST(!flags.test(TestEnum::Bit3)); + BOOST_TEST(flags.test(TestEnum::Bit4)); + } + + { // test throw + std::string str = "Invalid"; + BOOST_CHECK_THROW(flags.set(str), std::invalid_argument); + } + } + + // Test all_of and none_of + { + EFlags allFlags({TestEnum::Bit1, TestEnum::Bit2, TestEnum::Bit3}); + BOOST_TEST(allFlags.all_of(TestEnum::Bit1, TestEnum::Bit2)); + BOOST_TEST(!allFlags.all_of(TestEnum::Bit4)); + BOOST_TEST(allFlags.none_of(TestEnum::Bit4)); + } + + // Test toggle + { + EFlags toggleFlags; + toggleFlags.toggle(TestEnum::Bit4); + BOOST_TEST(toggleFlags.test(TestEnum::Bit4)); + toggleFlags.toggle(TestEnum::Bit4); + BOOST_TEST(!toggleFlags.test(TestEnum::Bit4)); + } + + // Create a flag set and serialize it + { + EFlags serializedFlags{TestEnum::Bit1, TestEnum::Bit3}; + std::string serialized = serializedFlags.serialize(); + BOOST_CHECK_EQUAL(serialized, "5"); // 5 in binary is 0101, meaning Bit1 and Bit3 are set. + + // Deserialize back into a flag set + EFlags deserializedFlags; + deserializedFlags.deserialize(serialized); + BOOST_CHECK(deserializedFlags == serializedFlags); // Ensure the deserialized flags match the original + } + + // Test with an empty flag set + { + EFlags emptyFlags; + std::string serialized = emptyFlags.serialize(); + BOOST_CHECK_EQUAL(serialized, "0"); + + EFlags deserialized; + deserialized.deserialize(serialized); + BOOST_CHECK(deserialized == emptyFlags); + + // Test with all flags set + EFlags allFlags(EFlags::All); + serialized = allFlags.serialize(); + BOOST_CHECK_EQUAL(serialized, std::to_string(EFlags::All)); + + deserialized.deserialize(serialized); + BOOST_CHECK(deserialized == allFlags); + } + + // check throw deserializng out of range + { + EFlags flag; + std::string str = "999999"; + BOOST_CHECK_THROW(flag.deserialize(str), std::out_of_range); + } + + // Create two flag sets + { + EFlags flags1{TestEnum::Bit1, TestEnum::Bit2}; + EFlags flags2{TestEnum::Bit3, TestEnum::Bit4}; + + // Perform a union operation + EFlags unionFlags = flags1.union_with(flags2); + BOOST_CHECK(unionFlags.test(TestEnum::Bit1)); + BOOST_CHECK(unionFlags.test(TestEnum::Bit2)); + BOOST_CHECK(unionFlags.test(TestEnum::Bit3)); + BOOST_CHECK(unionFlags.test(TestEnum::Bit4)); + BOOST_CHECK_EQUAL(unionFlags.value(), 15); // 1111 in binary + } + + // Create two overlapping flag sets + { + EFlags flags3{TestEnum::Bit1, TestEnum::Bit2, TestEnum::Bit3}; + EFlags flags4{TestEnum::Bit2, TestEnum::Bit3, TestEnum::Bit4}; + + // Perform an intersection operation + EFlags intersectionFlags = flags3.intersection_with(flags4); + BOOST_CHECK(intersectionFlags.test(TestEnum::Bit2)); + BOOST_CHECK(intersectionFlags.test(TestEnum::Bit3)); + BOOST_CHECK(!intersectionFlags.test(TestEnum::Bit1)); + BOOST_CHECK(!intersectionFlags.test(TestEnum::Bit4)); + BOOST_CHECK_EQUAL(intersectionFlags.value(), 6); // 0110 in binary + } + + { + // Create two flag sets + EFlags flags1{TestEnum::Bit1, TestEnum::Bit2, TestEnum::Bit3}; + EFlags flags2{TestEnum::Bit2, TestEnum::Bit3}; + + // Check containment + BOOST_CHECK(flags1.contains(flags2)); // flags1 contains all flags in flags2 + BOOST_CHECK(!flags2.contains(flags1)); // flags2 does not contain all flags in flags1 + + // Test with disjoint sets + EFlags flags3{TestEnum::Bit4}; + BOOST_CHECK(!flags1.contains(flags3)); // flags1 does not contain flags3 + } +} diff --git a/DataFormats/Detectors/CTP/include/DataFormatsCTP/CTPRateFetcher.h b/DataFormats/Detectors/CTP/include/DataFormatsCTP/CTPRateFetcher.h index 89605fbd28e1f..d47e53419bdf1 100644 --- a/DataFormats/Detectors/CTP/include/DataFormatsCTP/CTPRateFetcher.h +++ b/DataFormats/Detectors/CTP/include/DataFormatsCTP/CTPRateFetcher.h @@ -32,6 +32,9 @@ class CTPRateFetcher double fetchNoPuCorr(o2::ccdb::BasicCCDBManager* ccdb, uint64_t timeStamp, int runNumber, const std::string sourceName); void setupRun(int runNumber, o2::ccdb::BasicCCDBManager* ccdb, uint64_t timeStamp, bool initScalers); void updateScalers(ctp::CTPRunScalers& scalers); + int getRates(std::array& rates, o2::ccdb::BasicCCDBManager* ccdb, int runNumber, const std::string sourceName); // rates at start,stop and middle of the run + void setOrbit(bool orb) { mOrbit = orb; } + void setOutsideLimits(bool qc) { mOutsideLimits = qc; } private: double fetchCTPratesInputs(uint64_t timeStamp, int input); @@ -41,6 +44,8 @@ class CTPRateFetcher double pileUpCorrection(double rate); int mRunNumber = -1; + bool mOutsideLimits = 0; + bool mOrbit = 0; o2::ctp::CTPConfiguration mConfig{}; o2::ctp::CTPRunScalers mScalers{}; o2::parameters::GRPLHCIFData mLHCIFdata{}; diff --git a/DataFormats/Detectors/CTP/include/DataFormatsCTP/Scalers.h b/DataFormats/Detectors/CTP/include/DataFormatsCTP/Scalers.h index eacbadbe9bedc..c10ac070d4d35 100644 --- a/DataFormats/Detectors/CTP/include/DataFormatsCTP/Scalers.h +++ b/DataFormats/Detectors/CTP/include/DataFormatsCTP/Scalers.h @@ -125,10 +125,10 @@ class CTPRunScalers void printLMBRateVsT() const; // prints LMB interaction rate vs time for debugging // returns the pair of global (levelled) interaction rate, as well as interpolated // rate in Hz at a certain orbit number within the run - std::pair getRate(uint32_t orbit, int classindex, int type) const; + std::pair getRate(uint32_t orbit, int classindex, int type, bool qc = 0) const; /// same with absolute timestamp (not orbit) as argument - std::pair getRateGivenT(double timestamp, int classindex, int type) const; + std::pair getRateGivenT(double timestamp, int classindex, int type, bool qc = 0) const; /// retrieves integral for class std::array getIntegralForClass(int i) const diff --git a/DataFormats/Detectors/CTP/src/CTPRateFetcher.cxx b/DataFormats/Detectors/CTP/src/CTPRateFetcher.cxx index d9fc250bdc2ac..28da2033e7b29 100644 --- a/DataFormats/Detectors/CTP/src/CTPRateFetcher.cxx +++ b/DataFormats/Detectors/CTP/src/CTPRateFetcher.cxx @@ -61,6 +61,23 @@ void CTPRateFetcher::updateScalers(ctp::CTPRunScalers& scalers) mScalers.convertRawToO2(); } // +int CTPRateFetcher::getRates(std::array& rates, o2::ccdb::BasicCCDBManager* ccdb, int runNumber, const std::string sourceName) // rates at start,stop and middle of the run +{ + setupRun(runNumber, ccdb, 0, 1); + mOrbit = 1; + mOutsideLimits = 1; + auto orbitlimits = mScalers.getOrbitLimit(); + // std::cout << "1st orbit:" << orbitlimits.first << " last:" << orbitlimits.second << " Middle:" << (orbitlimits.first + orbitlimits.second)/2 << std::endl; + double rate0 = fetch(ccdb, orbitlimits.first, mRunNumber, sourceName); + double rateLast = fetch(ccdb, orbitlimits.second, mRunNumber, sourceName); + double rateM = fetch(ccdb, (orbitlimits.first + orbitlimits.second) / 2, mRunNumber, sourceName); + // std::cout << rate0 << " " << rateLast << " " << rateM << std::endl; + rates[0] = rate0; + rates[1] = rateLast; + rates[2] = rateM; + return 0; +} +// double CTPRateFetcher::fetchCTPratesClasses(uint64_t timeStamp, const std::string& className, int inputType) { auto triggerRate = fetchCTPratesClassesNoPuCorr(timeStamp, className, inputType); @@ -84,14 +101,23 @@ double CTPRateFetcher::fetchCTPratesClassesNoPuCorr(uint64_t timeStamp, const st LOG(warn) << "Trigger class " << className << " not found in CTPConfiguration"; return -2.; } - auto rate{mScalers.getRateGivenT(timeStamp * 1.e-3, classIndex, inputType)}; - return rate.second; + if (mOrbit) { + auto rate{mScalers.getRate((uint32_t)timeStamp, classIndex, inputType, mOutsideLimits)}; + return rate.second; + } else { + auto rate{mScalers.getRateGivenT(timeStamp * 1.e-3, classIndex, inputType, mOutsideLimits)}; + return rate.second; + } } double CTPRateFetcher::fetchCTPratesInputs(uint64_t timeStamp, int input) { std::vector& recs = mScalers.getScalerRecordO2(); if (recs[0].scalersInps.size() == 48) { - return pileUpCorrection(mScalers.getRateGivenT(timeStamp * 1.e-3, input, 7).second); + if (mOrbit) { + return pileUpCorrection(mScalers.getRate((uint32_t)timeStamp, input, 7, mOutsideLimits).second); + } else { + return pileUpCorrection(mScalers.getRateGivenT(timeStamp * 1.e-3, input, 7, mOutsideLimits).second); + } } else { LOG(error) << "Inputs not available"; return -1.; @@ -101,7 +127,11 @@ double CTPRateFetcher::fetchCTPratesInputsNoPuCorr(uint64_t timeStamp, int input { std::vector& recs = mScalers.getScalerRecordO2(); if (recs[0].scalersInps.size() == 48) { - return mScalers.getRateGivenT(timeStamp * 1.e-3, input, 7).second; + if (mOrbit) { + return mScalers.getRate((uint32_t)timeStamp, input, 7, mOutsideLimits).second; + } else { + return mScalers.getRateGivenT(timeStamp * 1.e-3, input, 7, mOutsideLimits).second; // qc flag implemented only for time + } } else { LOG(error) << "Inputs not available"; return -1.; @@ -127,13 +157,13 @@ void CTPRateFetcher::setupRun(int runNumber, o2::ccdb::BasicCCDBManager* ccdb, u return; } mRunNumber = runNumber; - LOG(debug) << "Setting up CTP scalers for run " << mRunNumber; - std::map metadata; - auto ptrLHCIFdata = ccdb->getSpecific("GLO/Config/GRPLHCIF", timeStamp, metadata); + LOG(info) << "Setting up CTP scalers for run " << mRunNumber; + auto ptrLHCIFdata = ccdb->getSpecific("GLO/Config/GRPLHCIF", timeStamp); if (ptrLHCIFdata == nullptr) { LOG(fatal) << "GRPLHCIFData not in database, timestamp:" << timeStamp; } mLHCIFdata = *ptrLHCIFdata; + std::map metadata; metadata["runNumber"] = std::to_string(mRunNumber); auto ptrConfig = ccdb->getSpecific("CTP/Config/Config", timeStamp, metadata); if (ptrConfig == nullptr) { diff --git a/DataFormats/Detectors/CTP/src/Configuration.cxx b/DataFormats/Detectors/CTP/src/Configuration.cxx index 57272d70ed9e3..2ae8fc1b03ffc 100644 --- a/DataFormats/Detectors/CTP/src/Configuration.cxx +++ b/DataFormats/Detectors/CTP/src/Configuration.cxx @@ -608,9 +608,16 @@ int CTPConfiguration::processConfigurationLineRun3v2(std::string& line, int& lev break; } case DESCRIPTORS: { - if ((tokens.size() < 2) && (line.find("DTRUE") == std::string::npos)) { - LOG(warning) << "Dsecriptor:" << line; - break; + if ((tokens.size() < 2)) { + if (line.find("TRUE") != std::string::npos) { + CTPDescriptor desc; + desc.name = tokens[0]; + mDescriptors.push_back(desc); + break; + } else { + LOG(warning) << "Unexpected Descriptor:" << line; + break; + } } CTPDescriptor desc; desc.name = tokens[0]; diff --git a/DataFormats/Detectors/CTP/src/Scalers.cxx b/DataFormats/Detectors/CTP/src/Scalers.cxx index 51242829f4f1e..f70a035427ade 100644 --- a/DataFormats/Detectors/CTP/src/Scalers.cxx +++ b/DataFormats/Detectors/CTP/src/Scalers.cxx @@ -662,7 +662,7 @@ void CTPRunScalers::printLMBRateVsT() const // rate in Hz at a certain orbit number within the run // type - 7 : inputs // type - 1..6 : lmb,lma,l0b,l0a,l1b,l1a -std::pair CTPRunScalers::getRate(uint32_t orbit, int classindex, int type) const +std::pair CTPRunScalers::getRate(uint32_t orbit, int classindex, int type, bool qc) const { if (mScalerRecordO2.size() <= 1) { LOG(error) << "not enough data"; @@ -674,46 +674,59 @@ std::pair CTPRunScalers::getRate(uint32_t orbit, int classindex, // then we can use binary search to find the right entries auto iter = std::lower_bound(mScalerRecordO2.begin(), mScalerRecordO2.end(), orbit, [&](CTPScalerRecordO2 const& a, uint32_t value) { return a.intRecord.orbit <= value; }); - auto nextindex = iter - mScalerRecordO2.begin(); // this points to the first index that has orbit greater or equal to given orbit + auto nextindex = std::distance(mScalerRecordO2.begin(), iter); // this points to the first index that has orbit greater or equal to given orbit auto calcRate = [&](auto index1, auto index2) -> double { - auto next = &mScalerRecordO2[index2]; - auto prev = &mScalerRecordO2[index1]; - auto timedelta = (next->intRecord.orbit - prev->intRecord.orbit) * 88.e-6; // converts orbits into time + const auto& snext = mScalerRecordO2[index2]; + const auto& sprev = mScalerRecordO2[index1]; + auto timedelta = (snext.intRecord.orbit - sprev.intRecord.orbit) * 88.e-6; // converts orbits into time if (type < 7) { - auto s0 = &(prev->scalers[classindex]); // type CTPScalerO2* - auto s1 = &(next->scalers[classindex]); + const auto& s0 = sprev.scalers[classindex]; // type CTPScalerO2* + const auto& s1 = snext.scalers[classindex]; switch (type) { case 1: - return (s1->lmBefore - s0->lmBefore) / timedelta; + return (s1.lmBefore - s0.lmBefore) / timedelta; case 2: - return (s1->lmAfter - s0->lmAfter) / timedelta; + return (s1.lmAfter - s0.lmAfter) / timedelta; case 3: - return (s1->l0Before - s0->l0Before) / timedelta; + return (s1.l0Before - s0.l0Before) / timedelta; case 4: - return (s1->l0After - s0->l0After) / timedelta; + return (s1.l0After - s0.l0After) / timedelta; case 5: - return (s1->l1Before - s0->l1Before) / timedelta; + return (s1.l1Before - s0.l1Before) / timedelta; case 6: - return (s1->l1After - s0->l1After) / timedelta; + return (s1.l1After - s0.l1After) / timedelta; default: LOG(error) << "Wrong type:" << type; return -1; // wrong type } } else if (type == 7) { - auto s0 = &(prev->scalersInps[classindex]); // type CTPScalerO2* - auto s1 = &(next->scalersInps[classindex]); + auto s0 = sprev.scalersInps[classindex]; // type CTPScalerO2* + auto s1 = snext.scalersInps[classindex]; return (s1 - s0) / timedelta; } else { LOG(error) << "Wrong type:" << type; return -1; // wrong type } }; - - if (nextindex == 0 || nextindex == mScalerRecordO2.size()) { + // qc flag decides what to return if time outside run + if (nextindex == 0) { // orbit is out of bounds - LOG(info) << "query orbit " << orbit << " out of bounds; Just returning the global rate"; - return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* current rate */ -1); + if (qc == 0) { + LOG(info) << "query orbit " << orbit << " before first record; Just returning the global rate"; + return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* current rate */ -1); + } else { + LOG(info) << "query orbit " << orbit << " before first record; Returning the first rate"; + return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* first rate */ calcRate(0, 1)); + } + } else if (nextindex == mScalerRecordO2.size()) { + if (qc == 0) { + LOG(info) << "query orbit " << orbit << " after last record; Just returning the global rate"; + return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* current rate */ -1); + } else { + LOG(info) << "query orbit " << orbit << " after last record; Returning the last rate"; + return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* last rate */ calcRate(mScalerRecordO2.size() - 2, mScalerRecordO2.size() - 1)); + } } else { return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* current rate */ calcRate(nextindex - 1, nextindex)); } @@ -723,7 +736,7 @@ std::pair CTPRunScalers::getRate(uint32_t orbit, int classindex, // rate in Hz at a certain orbit number within the run // type - 7 : inputs // type - 1..6 : lmb,lma,l0b,l0a,l1b,l1a -std::pair CTPRunScalers::getRateGivenT(double timestamp, int classindex, int type) const +std::pair CTPRunScalers::getRateGivenT(double timestamp, int classindex, int type, bool qc) const { if (mScalerRecordO2.size() <= 1) { LOG(error) << "not enough data"; @@ -738,49 +751,61 @@ std::pair CTPRunScalers::getRateGivenT(double timestamp, int cla // this points to the first index that has orbit greater to given orbit; // If this is 0, it means that the above condition was false from the beginning, basically saying that the timestamp is below any of the ScalerRecords' orbits. // If this is mScalerRecordO2.size(), it means mScalerRecordO2.end() was returned, condition was met throughout all ScalerRecords, basically saying the timestamp is above any of the ScalarRecordss orbits. - auto nextindex = iter - mScalerRecordO2.begin(); + auto nextindex = std::distance(mScalerRecordO2.begin(), iter); auto calcRate = [&](auto index1, auto index2) -> double { - auto next = &mScalerRecordO2[index2]; - auto prev = &mScalerRecordO2[index1]; - auto timedelta = (next->intRecord.orbit - prev->intRecord.orbit) * 88.e-6; // converts orbits into time + const auto& snext = mScalerRecordO2[index2]; + const auto& sprev = mScalerRecordO2[index1]; + auto timedelta = (snext.intRecord.orbit - sprev.intRecord.orbit) * 88.e-6; // converts orbits into time // std::cout << "timedelta:" << timedelta << std::endl; if (type < 7) { - auto s0 = &(prev->scalers[classindex]); // type CTPScalerO2* - auto s1 = &(next->scalers[classindex]); + const auto& s0 = sprev.scalers[classindex]; // type CTPScalerO2* + const auto& s1 = snext.scalers[classindex]; switch (type) { case 1: - return (s1->lmBefore - s0->lmBefore) / timedelta; + return (s1.lmBefore - s0.lmBefore) / timedelta; case 2: - return (s1->lmAfter - s0->lmAfter) / timedelta; + return (s1.lmAfter - s0.lmAfter) / timedelta; case 3: - return (s1->l0Before - s0->l0Before) / timedelta; + return (s1.l0Before - s0.l0Before) / timedelta; case 4: - return (s1->l0After - s0->l0After) / timedelta; + return (s1.l0After - s0.l0After) / timedelta; case 5: - return (s1->l1Before - s0->l1Before) / timedelta; + return (s1.l1Before - s0.l1Before) / timedelta; case 6: - return (s1->l1After - s0->l1After) / timedelta; + return (s1.l1After - s0.l1After) / timedelta; default: LOG(error) << "Wrong type:" << type; return -1; // wrong type } } else if (type == 7) { // LOG(info) << "doing input:"; - auto s0 = prev->scalersInps[classindex]; // type CTPScalerO2* - auto s1 = next->scalersInps[classindex]; + auto s0 = sprev.scalersInps[classindex]; // type CTPScalerO2* + auto s1 = snext.scalersInps[classindex]; return (s1 - s0) / timedelta; } else { LOG(error) << "Wrong type:" << type; return -1; // wrong type } }; - if (nextindex == 0 || nextindex == mScalerRecordO2.size()) { + if (nextindex == 0) { // orbit is out of bounds - LOG(info) << "query timestamp " << (long)timestamp << " out of bounds; Just returning the global rate"; - return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* current rate */ -1); + if (qc == 0) { + LOG(info) << "query timestamp " << (long)timestamp << " before first record; Just returning the global rate"; + return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* current rate */ -1); + } else { + LOG(info) << "query timestamp " << (long)timestamp << " before first record; Returning the first rate"; + return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* first rate */ calcRate(0, 1)); + } + } else if (nextindex == mScalerRecordO2.size()) { + if (qc == 0) { + LOG(info) << "query timestamp " << (long)timestamp << " after last record; Just returning the global rate"; + return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* current rate */ -1); + } else { + LOG(info) << "query timestamp " << (long)timestamp << " after last record; Returning the last rate"; + return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* last rate */ calcRate(mScalerRecordO2.size() - 2, mScalerRecordO2.size() - 1)); + } } else { - return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* current rate */ calcRate(nextindex - 1, nextindex)); } return std::make_pair(-1., -1.); diff --git a/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/DCSDPValues.h b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/DCSDPValues.h index 26e1969fca968..97bbe982e6aac 100644 --- a/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/DCSDPValues.h +++ b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/DCSDPValues.h @@ -14,22 +14,23 @@ #include #include "Framework/Logger.h" +#include "Framework/O2LongInt.h" namespace o2 { namespace fit { struct DCSDPValues { - std::vector> values; + std::vector> values; DCSDPValues() { - values = std::vector>(); + values = std::vector>(); } void add(uint64_t timestamp, int64_t value) { - values.push_back(std::pair(timestamp, value)); + values.push_back(std::pair(timestamp, value)); } bool empty() @@ -63,4 +64,4 @@ struct DCSDPValues { } // namespace fit } // namespace o2 -#endif \ No newline at end of file +#endif diff --git a/DataFormats/Detectors/TOF/include/DataFormatsTOF/Cluster.h b/DataFormats/Detectors/TOF/include/DataFormatsTOF/Cluster.h index 589afc8a2cde9..f36150e18fbbc 100644 --- a/DataFormats/Detectors/TOF/include/DataFormatsTOF/Cluster.h +++ b/DataFormats/Detectors/TOF/include/DataFormatsTOF/Cluster.h @@ -53,7 +53,7 @@ class Cluster : public o2::BaseCluster Cluster() = default; - Cluster(std::int16_t sensid, float x, float y, float z, float sy2, float sz2, float syz, double timeRaw, double time, float tot, int L0L1latency, int deltaBC); + Cluster(std::int16_t sensid, float x, float y, float z, float sy2, float sz2, float syz, double timeRaw, double time, float tot, int L0L1latency, int deltaBC, float geanttime = 0.0, double t0 = 0.0); ~Cluster() = default; @@ -134,6 +134,10 @@ class Cluster : public o2::BaseCluster int getDigitInfoCH(int idig) const { return mDigitInfoCh[idig]; } double getDigitInfoT(int idig) const { return mDigitInfoT[idig]; } float getDigitInfoTOT(int idig) const { return mDigitInfoTOT[idig]; } + float getTgeant() const { return mTgeant; } + void setTgeant(float val) { mTgeant = val; } + double getT0true() const { return mT0true; } + void setT0true(double val) { mT0true = val; } private: #if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) @@ -153,8 +157,10 @@ class Cluster : public o2::BaseCluster int mDigitInfoCh[6] = {0, 0, 0, 0, 0, 0}; double mDigitInfoT[6] = {0., 0., 0., 0., 0., 0.}; float mDigitInfoTOT[6] = {0., 0., 0., 0., 0., 0.}; + float mTgeant = 0.0; + double mT0true = 0.0; - ClassDefNV(Cluster, 4); + ClassDefNV(Cluster, 5); }; #ifndef GPUCA_GPUCODE diff --git a/DataFormats/Detectors/TOF/src/Cluster.cxx b/DataFormats/Detectors/TOF/src/Cluster.cxx index 2ca3edeb19f0a..a7f3473e0b61c 100644 --- a/DataFormats/Detectors/TOF/src/Cluster.cxx +++ b/DataFormats/Detectors/TOF/src/Cluster.cxx @@ -23,7 +23,7 @@ using namespace o2::tof; ClassImp(o2::tof::Cluster); -Cluster::Cluster(std::int16_t sensid, float x, float y, float z, float sy2, float sz2, float syz, double timeRaw, double time, float tot, int L0L1Latency, int deltaBC) : o2::BaseCluster(sensid, x, y, z, sy2, sz2, syz), mTimeRaw(timeRaw), mTime(time), mTot(tot), mL0L1Latency(L0L1Latency), mDeltaBC(deltaBC) +Cluster::Cluster(std::int16_t sensid, float x, float y, float z, float sy2, float sz2, float syz, double timeRaw, double time, float tot, int L0L1Latency, int deltaBC, float geanttime, double t0) : o2::BaseCluster(sensid, x, y, z, sy2, sz2, syz), mTimeRaw(timeRaw), mTime(time), mTot(tot), mL0L1Latency(L0L1Latency), mDeltaBC(deltaBC), mTgeant(geanttime), mT0true(t0) { // caching R and phi diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CalibdEdxCorrection.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CalibdEdxCorrection.h index 22ee80992f432..8a731a61c8a2d 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CalibdEdxCorrection.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CalibdEdxCorrection.h @@ -49,9 +49,9 @@ class CalibdEdxCorrection } CalibdEdxCorrection(std::string_view fileName) { loadFromFile(fileName); } #else - CalibdEdxCorrection() CON_DEFAULT; + CalibdEdxCorrection() = default; #endif - ~CalibdEdxCorrection() CON_DEFAULT; + ~CalibdEdxCorrection() = default; GPUd() float getCorrection(const StackID& stack, ChargeType charge, float tgl = 0, float snp = 0) const { diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/ClusterNative.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ClusterNative.h index a996f59f51f9e..f3070d456afb1 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/ClusterNative.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ClusterNative.h @@ -76,7 +76,7 @@ struct ClusterNative { GPUd() static float unpackPad(uint16_t pad) { return float(pad) * (1.f / scalePadPacked); } GPUd() static float unpackTime(uint32_t time) { return float(time) * (1.f / scaleTimePacked); } - GPUdDefault() ClusterNative() CON_DEFAULT; + GPUdDefault() ClusterNative() = default; GPUd() ClusterNative(uint32_t time, uint8_t flags, uint16_t pad, uint8_t sigmaTime, uint8_t sigmaPad, uint16_t qmax, uint16_t qtot) : padPacked(pad), sigmaTimePacked(sigmaTime), sigmaPadPacked(sigmaPad), qMax(qmax), qTot(qtot) { setTimePackedFlags(time, flags); diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CompressedClusters.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CompressedClusters.h index 46da2da2a702e..18ad5c6819344 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CompressedClusters.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CompressedClusters.h @@ -77,8 +77,8 @@ struct CompressedClustersOffsets : public CompressedClustersPtrs_x ref; o2::InteractionRecord ir; std::array moduleTriggers{}; + // N.B. channels and triggers have geographical addressing (0x1 << (NChPerModule * im + ic) uint32_t channels = 0; // pattern of channels it refers to - uint32_t triggers = 0; // pattern of triggered channels (not necessarily stored) in this BC + uint32_t triggers = 0; // pattern of triggered channels (not necessarily stored) in this BC (i.e. with Hit bit on) uint8_t ext_triggers = 0; // pattern of ALICE triggers BCData() = default; diff --git a/DataFormats/Headers/include/Headers/DataHeaderHelpers.h b/DataFormats/Headers/include/Headers/DataHeaderHelpers.h index f264460890494..aa93414cfb99f 100644 --- a/DataFormats/Headers/include/Headers/DataHeaderHelpers.h +++ b/DataFormats/Headers/include/Headers/DataHeaderHelpers.h @@ -38,7 +38,7 @@ struct fmt::formatter::value, c } template - auto format(const T& p, FormatContext& ctx) + auto format(const T& p, FormatContext& ctx) const { return fmt::format_to(ctx.out(), "{}", p.template as()); } @@ -67,7 +67,7 @@ struct fmt::formatter { } template - auto format(const o2::header::DataHeader& h, FormatContext& ctx) + auto format(const o2::header::DataHeader& h, FormatContext& ctx) const { if (presentation == 's') { auto res = fmt::format("Data header version {}, flags: {}\n", h.headerVersion, h.flags) + diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOF.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOF.h index 1a29be70ec990..3b872374dd35a 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOF.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOF.h @@ -28,7 +28,7 @@ class MatchInfoTOF using GTrackID = o2::dataformats::GlobalTrackID; public: - MatchInfoTOF(int idLocal, int idxTOFCl, double time, float chi2, o2::track::TrackLTIntegral trkIntLT, GTrackID idxTrack, float dt = 0, float z = 0, float dx = 0, float dz = 0) : mIdLocal(idLocal), mIdxTOFCl(idxTOFCl), mSignal(time), mChi2(chi2), mIntLT(trkIntLT), mIdxTrack(idxTrack), mDeltaT(dt), mZatTOF(z), mDXatTOF(dx), mDZatTOF(dz){}; + MatchInfoTOF(int idLocal, int idxTOFCl, double time, float chi2, o2::track::TrackLTIntegral trkIntLT, GTrackID idxTrack, float dt = 0, float z = 0, float dx = 0, float dz = 0, float dy = 0, float geanttime = 0.0, double t0 = 0.0) : mIdLocal(idLocal), mIdxTOFCl(idxTOFCl), mSignal(time), mChi2(chi2), mIntLT(trkIntLT), mIdxTrack(idxTrack), mDeltaT(dt), mZatTOF(z), mDXatTOF(dx), mDZatTOF(dz), mDYatTOF(dy), mTgeant(geanttime), mT0true(t0){}; MatchInfoTOF() = default; void setIdxTOFCl(int index) { mIdxTOFCl = index; } void setIdxTrack(GTrackID index) { mIdxTrack = index; } @@ -59,6 +59,8 @@ class MatchInfoTOF float getDZatTOF() const { return mDZatTOF; } void setDXatTOF(float val) { mDXatTOF = val; } float getDXatTOF() const { return mDXatTOF; } + void setDYatTOF(float val) { mDYatTOF = val; } + float getDYatTOF() const { return mDYatTOF; } void setSignal(double time) { mSignal = time; } double getSignal() const { return mSignal; } @@ -68,6 +70,10 @@ class MatchInfoTOF void setVz(float val) { mVz = val; } int getChannel() const { return mChannel; } void setChannel(int val) { mChannel = val; } + float getTgeant() const { return mTgeant; } + void setTgeant(float val) { mTgeant = val; } + double getT0true() const { return mT0true; } + void setT0true(double val) { mT0true = val; } private: int mIdLocal; // track id in sector of the pair track-TOFcluster @@ -78,6 +84,7 @@ class MatchInfoTOF float mZatTOF = 0.0; ///< Z position at TOF float mDXatTOF = 0.0; ///< DX position at TOF float mDZatTOF = 0.0; ///< DZ position at TOF + float mDYatTOF = 0.0; ///< DY position at TOF float mDeltaT = 0.0; ///< tTOF - TPC (microsec) double mSignal = 0.0; ///< TOF time in ps float mVz = 0.0; ///< Vz from TOF match @@ -85,8 +92,10 @@ class MatchInfoTOF // Hit pattern information bool mHitUpDown = false; ///< hit pattern in TOF up-down bool mHitLeftRight = false; ///< hit pattern in TOF left-right + float mTgeant = 0.0; ///< geant time in MC + double mT0true = 0.0; ///< t0true - ClassDefNV(MatchInfoTOF, 7); + ClassDefNV(MatchInfoTOF, 8); }; } // namespace dataformats } // namespace o2 diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOFReco.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOFReco.h index 188b7b3ab121a..f1b555301bf80 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOFReco.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOFReco.h @@ -35,7 +35,7 @@ class MatchInfoTOFReco : public MatchInfoTOF ITSTPCTRD, SIZEALL }; - MatchInfoTOFReco(int idLocal, int idxTOFCl, double time, float chi2, o2::track::TrackLTIntegral trkIntLT, GTrackID idxTrack, TrackType trkType, float dt = 0, float z = 0, float dx = 0, float dz = 0) : MatchInfoTOF(idLocal, idxTOFCl, time, chi2, trkIntLT, idxTrack, dt, z, dx, dz), mTrackType(trkType){}; + MatchInfoTOFReco(int idLocal, int idxTOFCl, double time, float chi2, o2::track::TrackLTIntegral trkIntLT, GTrackID idxTrack, TrackType trkType, float dt = 0, float z = 0, float dx = 0, float dz = 0, float dy = 0) : MatchInfoTOF(idLocal, idxTOFCl, time, chi2, trkIntLT, idxTrack, dt, z, dx, dz, dy), mTrackType(trkType){}; MatchInfoTOFReco() = default; diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrizationWithError.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrizationWithError.h index dd155e7f55569..cc783298e14cd 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrizationWithError.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrizationWithError.h @@ -100,6 +100,8 @@ class TrackParametrizationWithError : public TrackParametrization template GPUd() value_t getPredictedChi2(const BaseCluster& p) const; + template + GPUd() value_t getPredictedChi2Quiet(const BaseCluster& p) const; GPUd() void buildCombinedCovMatrix(const TrackParametrizationWithError& rhs, MatrixDSym5& cov) const; GPUd() value_t getPredictedChi2(const TrackParametrizationWithError& rhs, MatrixDSym5& covToSet) const; @@ -315,6 +317,16 @@ GPUdi() auto TrackParametrizationWithError::getPredictedChi2(const Base return getPredictedChi2(pyz, cov); } +//__________________________________________________________________________ +template +template +GPUdi() auto TrackParametrizationWithError::getPredictedChi2Quiet(const BaseCluster& p) const -> value_t +{ + const dim2_t pyz = {value_T(p.getY()), value_T(p.getZ())}; + const dim3_t cov = {value_T(p.getSigmaY2()), value_T(p.getSigmaYZ()), value_T(p.getSigmaZ2())}; + return getPredictedChi2Quiet(pyz, cov); +} + //______________________________________________ template GPUdi() auto TrackParametrizationWithError::getPredictedChi2(const dim2_t& p, const dim3_t& cov) const -> value_t diff --git a/DataFormats/common/include/CommonDataFormat/TimeStamp.h b/DataFormats/common/include/CommonDataFormat/TimeStamp.h index 354c937c1a24b..56a71414c6b86 100644 --- a/DataFormats/common/include/CommonDataFormat/TimeStamp.h +++ b/DataFormats/common/include/CommonDataFormat/TimeStamp.h @@ -25,8 +25,8 @@ template class TimeStamp { public: - GPUhdDefault() TimeStamp() CON_DEFAULT; - GPUhdDefault() ~TimeStamp() CON_DEFAULT; + GPUhdDefault() TimeStamp() = default; + GPUhdDefault() ~TimeStamp() = default; GPUdi() TimeStamp(T time) { mTimeStamp = time; } GPUhdi() T getTimeStamp() const { return mTimeStamp; } GPUdi() void setTimeStamp(T t) { mTimeStamp = t; } diff --git a/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h b/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h index 4bd5dfa2ab76c..5e1f5f66b3f65 100644 --- a/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h +++ b/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h @@ -209,6 +209,9 @@ inline void DigitizationContext::retrieveHits(std::vector const& chains int entryID, std::vector* hits) const { + if (chains.size() <= sourceID) { + return; + } auto br = chains[sourceID]->GetBranch(brname); if (!br) { LOG(error) << "No branch found with name " << brname; diff --git a/DataFormats/simulation/src/DigitizationContext.cxx b/DataFormats/simulation/src/DigitizationContext.cxx index f3c993c9508b7..975458c41fcb3 100644 --- a/DataFormats/simulation/src/DigitizationContext.cxx +++ b/DataFormats/simulation/src/DigitizationContext.cxx @@ -99,6 +99,14 @@ bool DigitizationContext::initSimChains(o2::detectors::DetID detid, std::vector< return false; } + // check that all files are present, otherwise quit + for (int source = 0; source < mSimPrefixes.size(); ++source) { + if (!std::filesystem::exists(o2::base::DetectorNameConf::getHitsFileName(detid, mSimPrefixes[source].data()))) { + LOG(info) << "Not hit file present for " << detid.getName() << " (exiting SimChain setup)"; + return false; + } + } + simchains.emplace_back(new TChain("o2sim")); // add the main (background) file simchains.back()->AddFile(o2::base::DetectorNameConf::getHitsFileName(detid, mSimPrefixes[0].data()).c_str()); diff --git a/Detectors/AOD/include/AODProducerWorkflow/AODProducerHelpers.h b/Detectors/AOD/include/AODProducerWorkflow/AODProducerHelpers.h index 9ef05096b2fd2..5351504443269 100644 --- a/Detectors/AOD/include/AODProducerWorkflow/AODProducerHelpers.h +++ b/Detectors/AOD/include/AODProducerWorkflow/AODProducerHelpers.h @@ -18,8 +18,6 @@ #include #include #include -#include -#include #include namespace o2::aodhelpers @@ -55,7 +53,7 @@ auto createTableCursor(framework::ProcessingContext& pc) framework::Produces c; c.resetCursor(pc.outputs() .make(framework::OutputForTable::ref())); - c.setLabel(o2::aod::MetadataTrait::metadata::tableLabel()); + c.setLabel(aod::label()); return c; } } // namespace o2::aodhelpers diff --git a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h index d9481917f9a05..241846f1a9270 100644 --- a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h +++ b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h @@ -21,7 +21,6 @@ #include "DataFormatsTRD/TrackTRD.h" #include "DetectorsBase/GRPGeomHelper.h" #include "DetectorsBase/Propagator.h" -#include "Framework/AnalysisHelpers.h" #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" #include "ReconstructionDataFormats/GlobalTrackID.h" @@ -31,12 +30,11 @@ #include "ZDCBase/Constants.h" #include "GlobalTracking/MatchGlobalFwd.h" #include "CommonUtils/TreeStreamRedirector.h" -#include "CommonUtils/EnumBitOperators.h" +#include "CommonUtils/EnumFlags.h" #include #include #include -#include #include #include using namespace o2::framework; @@ -210,12 +208,9 @@ class BunchCrossings }; // end internal class // Steering bits for additional output during AOD production -enum struct AODProducerStreamerMask : uint8_t { - None = 0, - TrackQA = O2_ENUM_SET_BIT(0), - All = std::numeric_limits>::max(), +enum struct AODProducerStreamerFlags : uint8_t { + TrackQA, }; -O2_DEFINE_ENUM_BIT_OPERATORS(AODProducerStreamerMask) class AODProducerWorkflowDPL : public Task { @@ -253,7 +248,7 @@ class AODProducerWorkflowDPL : public Task std::unordered_set mGIDUsedBySVtx; std::unordered_set mGIDUsedByStr; - AODProducerStreamerMask mStreamerMask; + o2::utils::EnumFlags mStreamerFlags; std::shared_ptr mStreamer; int mNThreads = 1; @@ -435,6 +430,8 @@ class AODProducerWorkflowDPL : public Task int8_t dRefGloSnp{std::numeric_limits::min()}; int8_t dRefGloTgl{std::numeric_limits::min()}; int8_t dRefGloQ2Pt{std::numeric_limits::min()}; + int8_t dTofdX{std::numeric_limits::min()}; + int8_t dTofdZ{std::numeric_limits::min()}; }; // helper struct for addToFwdTracksTable() @@ -523,8 +520,8 @@ class AODProducerWorkflowDPL : public Task GIndex trackID, const o2::globaltracking::RecoContainer& data, int collisionID, std::uint64_t collisionBC, const std::map& bcsMap); - template - void addToFwdTracksTable(fwdTracksCursorType& fwdTracksCursor, fwdTracksCovCursorType& fwdTracksCovCursor, AmbigFwdTracksCursorType& ambigFwdTracksCursor, + template + void addToFwdTracksTable(fwdTracksCursorType& fwdTracksCursor, fwdTracksCovCursorType& fwdTracksCovCursor, AmbigFwdTracksCursorType& ambigFwdTracksCursor, mftTracksCovCursorType& mftTracksCovCursor, GIndex trackID, const o2::globaltracking::RecoContainer& data, int collisionID, std::uint64_t collisionBC, const std::map& bcsMap); TrackExtraInfo processBarrelTrack(int collisionID, std::uint64_t collisionBC, GIndex trackIndex, const o2::globaltracking::RecoContainer& data, const std::map& bcsMap); @@ -538,7 +535,7 @@ class AODProducerWorkflowDPL : public Task // * fills tables collision by collision // * interaction time is for TOF information template void fillTrackTablesPerCollision(int collisionID, std::uint64_t collisionBC, @@ -551,6 +548,7 @@ class AODProducerWorkflowDPL : public Task TracksQACursorType& tracksQACursor, AmbigTracksCursorType& ambigTracksCursor, MFTTracksCursorType& mftTracksCursor, + MFTTracksCovCursorType& mftTracksCovCursor, AmbigMFTTracksCursorType& ambigMFTTracksCursor, FwdTracksCursorType& fwdTracksCursor, FwdTracksCovCursorType& fwdTracksCovCursor, diff --git a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx index 8a2443b57c7ff..6083052eb1168 100644 --- a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx +++ b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx @@ -337,7 +337,7 @@ void AODProducerWorkflowDPL::addToTracksExtraTable(TracksExtraCursorType& tracks extraInfoHolder.itsClusterSizes, extraInfoHolder.tpcNClsFindable, extraInfoHolder.tpcNClsFindableMinusFound, - // extraInfoHolder.tpcNClsFindableMinusPID, + extraInfoHolder.tpcNClsFindableMinusPID, extraInfoHolder.tpcNClsFindableMinusCrossedRows, extraInfoHolder.tpcNClsShared, extraInfoHolder.trdPattern, @@ -358,47 +358,32 @@ void AODProducerWorkflowDPL::addToTracksExtraTable(TracksExtraCursorType& tracks template void AODProducerWorkflowDPL::addToTracksQATable(TracksQACursorType& tracksQACursor, TrackQA& trackQAInfoHolder) { - if constexpr (std::is_same_v) { // TODO remove remove once version changes - tracksQACursor( - trackQAInfoHolder.trackID, - truncateFloatFraction(trackQAInfoHolder.tpcTime0, mTPCTime0), - trackQAInfoHolder.tpcdcaR, - trackQAInfoHolder.tpcdcaZ, - trackQAInfoHolder.tpcClusterByteMask, - trackQAInfoHolder.tpcdEdxMax0R, - trackQAInfoHolder.tpcdEdxMax1R, - trackQAInfoHolder.tpcdEdxMax2R, - trackQAInfoHolder.tpcdEdxMax3R, - trackQAInfoHolder.tpcdEdxTot0R, - trackQAInfoHolder.tpcdEdxTot1R, - trackQAInfoHolder.tpcdEdxTot2R, - trackQAInfoHolder.tpcdEdxTot3R, - trackQAInfoHolder.dRefContY, - trackQAInfoHolder.dRefContZ, - trackQAInfoHolder.dRefContSnp, - trackQAInfoHolder.dRefContTgl, - trackQAInfoHolder.dRefContQ2Pt, - trackQAInfoHolder.dRefGloY, - trackQAInfoHolder.dRefGloZ, - trackQAInfoHolder.dRefGloSnp, - trackQAInfoHolder.dRefGloTgl, - trackQAInfoHolder.dRefGloQ2Pt); - } else { - tracksQACursor( - trackQAInfoHolder.trackID, - trackQAInfoHolder.tpcTime0, - trackQAInfoHolder.tpcdcaR, - trackQAInfoHolder.tpcdcaZ, - trackQAInfoHolder.tpcClusterByteMask, - trackQAInfoHolder.tpcdEdxMax0R, - trackQAInfoHolder.tpcdEdxMax1R, - trackQAInfoHolder.tpcdEdxMax2R, - trackQAInfoHolder.tpcdEdxMax3R, - trackQAInfoHolder.tpcdEdxTot0R, - trackQAInfoHolder.tpcdEdxTot1R, - trackQAInfoHolder.tpcdEdxTot2R, - trackQAInfoHolder.tpcdEdxTot3R); - } + tracksQACursor( + trackQAInfoHolder.trackID, + truncateFloatFraction(trackQAInfoHolder.tpcTime0, mTPCTime0), + trackQAInfoHolder.tpcdcaR, + trackQAInfoHolder.tpcdcaZ, + trackQAInfoHolder.tpcClusterByteMask, + trackQAInfoHolder.tpcdEdxMax0R, + trackQAInfoHolder.tpcdEdxMax1R, + trackQAInfoHolder.tpcdEdxMax2R, + trackQAInfoHolder.tpcdEdxMax3R, + trackQAInfoHolder.tpcdEdxTot0R, + trackQAInfoHolder.tpcdEdxTot1R, + trackQAInfoHolder.tpcdEdxTot2R, + trackQAInfoHolder.tpcdEdxTot3R, + trackQAInfoHolder.dRefContY, + trackQAInfoHolder.dRefContZ, + trackQAInfoHolder.dRefContSnp, + trackQAInfoHolder.dRefContTgl, + trackQAInfoHolder.dRefContQ2Pt, + trackQAInfoHolder.dRefGloY, + trackQAInfoHolder.dRefGloZ, + trackQAInfoHolder.dRefGloSnp, + trackQAInfoHolder.dRefGloTgl, + trackQAInfoHolder.dRefGloQ2Pt, + trackQAInfoHolder.dTofdX, + trackQAInfoHolder.dTofdZ); } template @@ -443,7 +428,7 @@ void AODProducerWorkflowDPL::addToMFTTracksTable(mftTracksCursorType& mftTracksC } template void AODProducerWorkflowDPL::fillTrackTablesPerCollision(int collisionID, std::uint64_t collisionBC, @@ -456,6 +441,7 @@ void AODProducerWorkflowDPL::fillTrackTablesPerCollision(int collisionID, TracksQACursorType& tracksQACursor, AmbigTracksCursorType& ambigTracksCursor, MFTTracksCursorType& mftTracksCursor, + MFTTracksCovCursorType& mftTracksCovCursor, AmbigMFTTracksCursorType& ambigMFTTracksCursor, FwdTracksCursorType& fwdTracksCursor, FwdTracksCovCursorType& fwdTracksCovCursor, @@ -475,6 +461,9 @@ void AODProducerWorkflowDPL::fillTrackTablesPerCollision(int collisionID, } else if (src == GIndex::Source::MCH || src == GIndex::Source::MFTMCH || src == GIndex::Source::MCHMID) { fwdTracksCursor.reserve(nToReserve + fwdTracksCursor.lastIndex()); fwdTracksCovCursor.reserve(nToReserve + fwdTracksCovCursor.lastIndex()); + if (src == GIndex::Source::MFTMCH) { + mftTracksCovCursor.reserve(nToReserve + mftTracksCovCursor.lastIndex()); + } } else { tracksCursor.reserve(nToReserve + tracksCursor.lastIndex()); tracksCovCursor.reserve(nToReserve + tracksCovCursor.lastIndex()); @@ -494,7 +483,7 @@ void AODProducerWorkflowDPL::fillTrackTablesPerCollision(int collisionID, if (trackIndex.isAmbiguous() && mGIDToTableFwdID.find(trackIndex) != mGIDToTableFwdID.end()) { // was it already stored ? continue; } - addToFwdTracksTable(fwdTracksCursor, fwdTracksCovCursor, ambigFwdTracksCursor, trackIndex, data, collisionID, collisionBC, bcsMap); + addToFwdTracksTable(fwdTracksCursor, fwdTracksCovCursor, ambigFwdTracksCursor, mftTracksCovCursor, trackIndex, data, collisionID, collisionBC, bcsMap); mGIDToTableFwdID.emplace(trackIndex, mTableTrFwdID); addClustersToFwdTrkClsTable(data, fwdTrkClsCursor, trackIndex, mTableTrFwdID); mTableTrFwdID++; @@ -615,9 +604,9 @@ void AODProducerWorkflowDPL::fillIndexTablesPerCollision(const o2::dataformats:: } } -template +template void AODProducerWorkflowDPL::addToFwdTracksTable(FwdTracksCursorType& fwdTracksCursor, FwdTracksCovCursorType& fwdTracksCovCursor, - AmbigFwdTracksCursorType& ambigFwdTracksCursor, GIndex trackID, + AmbigFwdTracksCursorType& ambigFwdTracksCursor, mftTracksCovCursorType& mftTracksCovCursor, GIndex trackID, const o2::globaltracking::RecoContainer& data, int collisionID, std::uint64_t collisionBC, const std::map& bcsMap) { @@ -759,6 +748,8 @@ void AODProducerWorkflowDPL::addToFwdTracksTable(FwdTracksCursorType& fwdTracksC fwdInfo.trackTimeRes = time.getTimeStampError() * 1.e3; } else { // This is a GlobalMuonTrack or a GlobalForwardTrack const auto& track = data.getGlobalFwdTrack(trackID); + const auto& mftTracks = data.getMFTTracks(); + const auto& mfttrack = mftTracks[track.getMFTTrackID()]; if (!extrapMCHTrack(track.getMCHTrackID())) { LOGF(warn, "Unable to extrapolate MCH track with ID %d! Dummy parameters will be used", track.getMCHTrackID()); } @@ -798,6 +789,26 @@ void AODProducerWorkflowDPL::addToFwdTracksTable(FwdTracksCursorType& fwdTracksC fwdCovInfo.rho1PtTgl = (Char_t)(128. * track.getCovariances()(3, 4) / (fwdCovInfo.sig1Pt * fwdCovInfo.sigTgl)); fwdInfo.trackTypeId = (fwdInfo.chi2matchmchmid >= 0) ? o2::aod::fwdtrack::GlobalMuonTrack : o2::aod::fwdtrack::GlobalForwardTrack; + + float sX = TMath::Sqrt(mfttrack.getSigma2X()), sY = TMath::Sqrt(mfttrack.getSigma2Y()), sPhi = TMath::Sqrt(mfttrack.getSigma2Phi()), + sTgl = TMath::Sqrt(mfttrack.getSigma2Tanl()), sQ2Pt = TMath::Sqrt(mfttrack.getSigma2InvQPt()); + + mftTracksCovCursor(fwdInfo.matchmfttrackid, + truncateFloatFraction(sX, mTrackCovDiag), + truncateFloatFraction(sY, mTrackCovDiag), + truncateFloatFraction(sPhi, mTrackCovDiag), + truncateFloatFraction(sTgl, mTrackCovDiag), + truncateFloatFraction(sQ2Pt, mTrackCovDiag), + (Char_t)(128. * mfttrack.getCovariances()(0, 1) / (sX * sY)), + (Char_t)(128. * mfttrack.getCovariances()(0, 2) / (sPhi * sX)), + (Char_t)(128. * mfttrack.getCovariances()(1, 2) / (sPhi * sY)), + (Char_t)(128. * mfttrack.getCovariances()(0, 3) / (sTgl * sX)), + (Char_t)(128. * mfttrack.getCovariances()(1, 3) / (sTgl * sY)), + (Char_t)(128. * mfttrack.getCovariances()(2, 3) / (sTgl * sPhi)), + (Char_t)(128. * mfttrack.getCovariances()(0, 4) / (sQ2Pt * sX)), + (Char_t)(128. * mfttrack.getCovariances()(1, 4) / (sQ2Pt * sY)), + (Char_t)(128. * mfttrack.getCovariances()(2, 4) / (sQ2Pt * sPhi)), + (Char_t)(128. * mfttrack.getCovariances()(3, 4) / (sQ2Pt * sTgl))); } std::uint64_t bcOfTimeRef; @@ -1690,9 +1701,10 @@ void AODProducerWorkflowDPL::init(InitContext& ic) mPropTracks = ic.options().get("propagate-tracks"); mPropMuons = ic.options().get("propagate-muons"); if (auto s = ic.options().get("with-streamers"); !s.empty()) { - mStreamerMask = static_cast(std::stoul(s, nullptr, 2)); - if (O2_ENUM_ANY_BIT(mStreamerMask)) { - LOGP(info, "Writing streamer data with mask {:0{}b}", static_cast>(mStreamerMask), std::numeric_limits>::digits); + mStreamerFlags.set(s); + if (mStreamerFlags) { + LOGP(info, "Writing streamer data with mask:"); + LOG(info) << mStreamerFlags; } else { LOGP(warn, "Specified non-default empty streamer mask!"); } @@ -1783,7 +1795,7 @@ void AODProducerWorkflowDPL::init(InitContext& ic) mTimer.Reset(); - if (O2_ENUM_ANY_BIT(mStreamerMask)) { + if (mStreamerFlags) { mStreamer = std::make_unique("AO2DStreamer.root", "RECREATE"); } } @@ -1851,6 +1863,7 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) auto fwdTracksCovCursor = createTableCursor(pc); auto fwdTrkClsCursor = createTableCursor(pc); auto mftTracksCursor = createTableCursor(pc); + auto mftTracksCovCursor = createTableCursor(pc); auto tracksCursor = createTableCursor(pc); auto tracksCovCursor = createTableCursor(pc); auto tracksExtraCursor = createTableCursor(pc); @@ -2185,7 +2198,7 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) auto& trackRef = primVer2TRefs.back(); // references to unassigned tracks are at the end // fixme: interaction time is undefined for unassigned tracks (?) fillTrackTablesPerCollision(-1, std::uint64_t(-1), trackRef, primVerGIs, recoData, tracksCursor, tracksCovCursor, tracksExtraCursor, tracksQACursor, - ambigTracksCursor, mftTracksCursor, ambigMFTTracksCursor, + ambigTracksCursor, mftTracksCursor, mftTracksCovCursor, ambigMFTTracksCursor, fwdTracksCursor, fwdTracksCovCursor, ambigFwdTracksCursor, fwdTrkClsCursor, bcsMap); // filling collisions and tracks into tables @@ -2227,7 +2240,7 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) auto& trackRef = primVer2TRefs[collisionID]; // passing interaction time in [ps] fillTrackTablesPerCollision(collisionID, globalBC, trackRef, primVerGIs, recoData, tracksCursor, tracksCovCursor, tracksExtraCursor, tracksQACursor, ambigTracksCursor, - mftTracksCursor, ambigMFTTracksCursor, + mftTracksCursor, mftTracksCovCursor, ambigMFTTracksCursor, fwdTracksCursor, fwdTracksCovCursor, ambigFwdTracksCursor, fwdTrkClsCursor, bcsMap); collisionID++; } @@ -2584,6 +2597,12 @@ AODProducerWorkflowDPL::TrackQA AODProducerWorkflowDPL::processBarrelTrackQA(int trackQAHolder.tpcdcaR = 100. * dcaInfo[0] / sqrt(1. + trackPar.getQ2Pt() * trackPar.getQ2Pt()); trackQAHolder.tpcdcaZ = 100. * dcaInfo[1] / sqrt(1. + trackPar.getQ2Pt() * trackPar.getQ2Pt()); } + // This allows to safely clamp any float to one byte, using the + // minmal/maximum values as under-/overflow borders and rounding to the nearest integer + auto safeInt8Clamp = [](auto value) -> int8_t { + using ValType = decltype(value); + return static_cast(TMath::Nint(std::clamp(value, static_cast(std::numeric_limits::min()), static_cast(std::numeric_limits::max())))); + }; /// get tracklet byteMask uint8_t clusterCounters[8] = {0}; { @@ -2614,96 +2633,101 @@ AODProducerWorkflowDPL::TrackQA AODProducerWorkflowDPL::processBarrelTrackQA(int trackQAHolder.tpcdEdxTot1R = uint8_t(tpcOrig.getdEdx().dEdxTotOROC1 * dEdxNorm); trackQAHolder.tpcdEdxTot2R = uint8_t(tpcOrig.getdEdx().dEdxTotOROC2 * dEdxNorm); trackQAHolder.tpcdEdxTot3R = uint8_t(tpcOrig.getdEdx().dEdxTotOROC3 * dEdxNorm); - - if constexpr (std::is_same_v) { // TODO remove remove once version changes - // Add matching information at a reference point (defined by - // o2::aod::track::trackQARefRadius) in the same frame as the global track - // without material corrections and error propagation - if (auto itsContGID = data.getITSContributorGID(trackIndex); itsContGID.isIndexSet() && itsContGID.getSource() != GIndex::ITSAB) { - const auto& itsOrig = data.getITSTrack(itsContGID); - o2::track::TrackPar gloCopy = trackPar; - o2::track::TrackPar itsCopy = itsOrig; - o2::track::TrackPar tpcCopy = tpcOrig; - if (prop->propagateToX(gloCopy, o2::aod::track::trackQARefRadius, prop->getNominalBz(), o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, mMatCorr) && - prop->propagateToAlphaX(tpcCopy, gloCopy.getAlpha(), o2::aod::track::trackQARefRadius, false, o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, 1, mMatCorr) && - prop->propagateToAlphaX(itsCopy, gloCopy.getAlpha(), o2::aod::track::trackQARefRadius, false, o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, 1, mMatCorr)) { - // All tracks are now at the same radius and in the same frame and we can calculate the deltas wrt. to the global track - // The scale is defined by the global track scaling depending on beta0 - const float beta0 = std::sqrt(std::min(50.f / tpcOrig.getdEdx().dEdxMaxTPC, 1.f)); - const float qpt = gloCopy.getQ2Pt(); - const float x = qpt / beta0; - // scaling is defined as sigmaBins/sqrt(p0^2 + (p1 * q/pt / beta)^2) - auto scaleCont = [&x](int i) -> float { - return o2::aod::track::trackQAScaleBins / std::sqrt(o2::aod::track::trackQAScaleContP0[i] * o2::aod::track::trackQAScaleContP0[i] + (o2::aod::track::trackQAScaleContP1[i] * x) * (o2::aod::track::trackQAScaleContP1[i] * x)); - }; - auto scaleGlo = [&x](int i) -> float { - return o2::aod::track::trackQAScaleBins / std::sqrt(o2::aod::track::trackQAScaleGloP0[i] * o2::aod::track::trackQAScaleGloP0[i] + (o2::aod::track::trackQAScaleGloP1[i] * x) * (o2::aod::track::trackQAScaleGloP1[i] * x)); - }; - - // This allows to safely clamp any float to one byte, using the - // minmal/maximum values as under-/overflow borders and rounding to the nearest integer - auto safeInt8Clamp = [](auto value) -> int8_t { - using ValType = decltype(value); - return static_cast(TMath::Nint(std::clamp(value, static_cast(std::numeric_limits::min()), static_cast(std::numeric_limits::max())))); - }; - - // Calculate deltas for contributors - trackQAHolder.dRefContY = safeInt8Clamp((itsCopy.getY() - tpcCopy.getY()) * scaleCont(0)); - trackQAHolder.dRefContZ = safeInt8Clamp((itsCopy.getZ() - tpcCopy.getZ()) * scaleCont(1)); - trackQAHolder.dRefContSnp = safeInt8Clamp((itsCopy.getSnp() - tpcCopy.getSnp()) * scaleCont(2)); - trackQAHolder.dRefContTgl = safeInt8Clamp((itsCopy.getTgl() - tpcCopy.getTgl()) * scaleCont(3)); - trackQAHolder.dRefContQ2Pt = safeInt8Clamp((itsCopy.getQ2Pt() - tpcCopy.getQ2Pt()) * scaleCont(4)); - // Calculate deltas for global track against averaged contributors - trackQAHolder.dRefGloY = safeInt8Clamp(((itsCopy.getY() + tpcCopy.getY()) * 0.5f - gloCopy.getY()) * scaleGlo(0)); - trackQAHolder.dRefGloZ = safeInt8Clamp(((itsCopy.getZ() + tpcCopy.getZ()) * 0.5f - gloCopy.getZ()) * scaleGlo(1)); - trackQAHolder.dRefGloSnp = safeInt8Clamp(((itsCopy.getSnp() + tpcCopy.getSnp()) * 0.5f - gloCopy.getSnp()) * scaleGlo(2)); - trackQAHolder.dRefGloTgl = safeInt8Clamp(((itsCopy.getTgl() + tpcCopy.getTgl()) * 0.5f - gloCopy.getTgl()) * scaleGlo(3)); - trackQAHolder.dRefGloQ2Pt = safeInt8Clamp(((itsCopy.getQ2Pt() + tpcCopy.getQ2Pt()) * 0.5f - gloCopy.getQ2Pt()) * scaleGlo(4)); - - if (O2_ENUM_TEST_BIT(mStreamerMask, AODProducerStreamerMask::TrackQA)) { - (*mStreamer) << "trackQA" - << "trackITSOrig=" << itsOrig - << "trackTPCOrig=" << tpcOrig - << "trackITSTPCOrig=" << trackPar - << "trackITSProp=" << itsCopy - << "trackTPCProp=" << tpcCopy - << "trackITSTPCProp=" << gloCopy - << "refRadius=" << o2::aod::track::trackQARefRadius - << "scaleBins=" << o2::aod::track::trackQAScaleBins - << "scaleCont0=" << scaleCont(0) - << "scaleCont1=" << scaleCont(1) - << "scaleCont2=" << scaleCont(2) - << "scaleCont3=" << scaleCont(3) - << "scaleCont4=" << scaleCont(4) - << "scaleGlo0=" << scaleGlo(0) - << "scaleGlo1=" << scaleGlo(1) - << "scaleGlo2=" << scaleGlo(2) - << "scaleGlo3=" << scaleGlo(3) - << "scaleGlo4=" << scaleGlo(4) - << "trackQAHolder.tpcTime0=" << trackQAHolder.tpcTime0 - << "trackQAHolder.tpcdcaR=" << trackQAHolder.tpcdcaR - << "trackQAHolder.tpcdcaZ=" << trackQAHolder.tpcdcaZ - << "trackQAHolder.tpcdcaClusterByteMask=" << trackQAHolder.tpcClusterByteMask - << "trackQAHolder.tpcdEdxMax0R=" << trackQAHolder.tpcdEdxMax0R - << "trackQAHolder.tpcdEdxMax1R=" << trackQAHolder.tpcdEdxMax1R - << "trackQAHolder.tpcdEdxMax2R=" << trackQAHolder.tpcdEdxMax2R - << "trackQAHolder.tpcdEdxMax3R=" << trackQAHolder.tpcdEdxMax3R - << "trackQAHolder.tpcdEdxTot0R=" << trackQAHolder.tpcdEdxTot0R - << "trackQAHolder.tpcdEdxTot1R=" << trackQAHolder.tpcdEdxTot1R - << "trackQAHolder.tpcdEdxTot2R=" << trackQAHolder.tpcdEdxTot2R - << "trackQAHolder.tpcdEdxTot3R=" << trackQAHolder.tpcdEdxTot3R - << "trackQAHolder.dRefContY=" << trackQAHolder.dRefContY - << "trackQAHolder.dRefContZ=" << trackQAHolder.dRefContZ - << "trackQAHolder.dRefContSnp=" << trackQAHolder.dRefContSnp - << "trackQAHolder.dRefContTgl=" << trackQAHolder.dRefContTgl - << "trackQAHolder.dRefContQ2Pt=" << trackQAHolder.dRefContQ2Pt - << "trackQAHolder.dRefGloY=" << trackQAHolder.dRefGloY - << "trackQAHolder.dRefGloZ=" << trackQAHolder.dRefGloZ - << "trackQAHolder.dRefGloSnp=" << trackQAHolder.dRefGloSnp - << "trackQAHolder.dRefGloTgl=" << trackQAHolder.dRefGloTgl - << "trackQAHolder.dRefGloQ2Pt=" << trackQAHolder.dRefGloQ2Pt - << "\n"; - } + /// + float scaleTOF{0}; + auto contributorsGIDA = data.getSingleDetectorRefs(trackIndex); + if (contributorsGIDA[GIndex::Source::TOF].isIndexSet()) { // ITS-TPC-TRD-TOF, ITS-TPC-TOF, TPC-TRD-TOF, TPC-TOF + const auto& tofMatch = data.getTOFMatch(trackIndex); + const float qpt = trackPar.getQ2Pt(); + scaleTOF = std::sqrt(o2::aod::track::trackQAScaledTOF[0] * o2::aod::track::trackQAScaledTOF[0] + qpt * qpt * o2::aod::track::trackQAScaledTOF[1] * o2::aod::track::trackQAScaledTOF[1]) / (2. * o2::aod::track::trackQAScaleBins); + trackQAHolder.dTofdX = safeInt8Clamp(tofMatch.getDXatTOF() / scaleTOF); + trackQAHolder.dTofdZ = safeInt8Clamp(tofMatch.getDZatTOF() / scaleTOF); + } + + // Add matching information at a reference point (defined by + // o2::aod::track::trackQARefRadius) in the same frame as the global track + // without material corrections and error propagation + if (auto itsContGID = data.getITSContributorGID(trackIndex); itsContGID.isIndexSet() && itsContGID.getSource() != GIndex::ITSAB) { + const auto& itsOrig = data.getITSTrack(itsContGID); + o2::track::TrackPar gloCopy = trackPar; + o2::track::TrackPar itsCopy = itsOrig; + o2::track::TrackPar tpcCopy = tpcOrig; + if (prop->propagateToX(gloCopy, o2::aod::track::trackQARefRadius, prop->getNominalBz(), o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, mMatCorr) && + prop->propagateToAlphaX(tpcCopy, gloCopy.getAlpha(), o2::aod::track::trackQARefRadius, false, o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, 1, mMatCorr) && + prop->propagateToAlphaX(itsCopy, gloCopy.getAlpha(), o2::aod::track::trackQARefRadius, false, o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, 1, mMatCorr)) { + // All tracks are now at the same radius and in the same frame and we can calculate the deltas wrt. to the global track + // The scale is defined by the global track scaling depending on beta0 + const float beta0 = std::sqrt(std::min(50.f / tpcOrig.getdEdx().dEdxMaxTPC, 1.f)); + const float qpt = gloCopy.getQ2Pt(); + const float x = qpt / beta0; + // scaling is defined as sigmaBins/sqrt(p0^2 + (p1 * q/pt / beta)^2) + auto scaleCont = [&x](int i) -> float { + return o2::aod::track::trackQAScaleBins / std::sqrt(o2::aod::track::trackQAScaleContP0[i] * o2::aod::track::trackQAScaleContP0[i] + (o2::aod::track::trackQAScaleContP1[i] * x) * (o2::aod::track::trackQAScaleContP1[i] * x)); + }; + auto scaleGlo = [&x](int i) -> float { + return o2::aod::track::trackQAScaleBins / std::sqrt(o2::aod::track::trackQAScaleGloP0[i] * o2::aod::track::trackQAScaleGloP0[i] + (o2::aod::track::trackQAScaleGloP1[i] * x) * (o2::aod::track::trackQAScaleGloP1[i] * x)); + }; + + // Calculate deltas for contributors + trackQAHolder.dRefContY = safeInt8Clamp((itsCopy.getY() - tpcCopy.getY()) * scaleCont(0)); + trackQAHolder.dRefContZ = safeInt8Clamp((itsCopy.getZ() - tpcCopy.getZ()) * scaleCont(1)); + trackQAHolder.dRefContSnp = safeInt8Clamp((itsCopy.getSnp() - tpcCopy.getSnp()) * scaleCont(2)); + trackQAHolder.dRefContTgl = safeInt8Clamp((itsCopy.getTgl() - tpcCopy.getTgl()) * scaleCont(3)); + trackQAHolder.dRefContQ2Pt = safeInt8Clamp((itsCopy.getQ2Pt() - tpcCopy.getQ2Pt()) * scaleCont(4)); + // Calculate deltas for global track against averaged contributors + trackQAHolder.dRefGloY = safeInt8Clamp(((itsCopy.getY() + tpcCopy.getY()) * 0.5f - gloCopy.getY()) * scaleGlo(0)); + trackQAHolder.dRefGloZ = safeInt8Clamp(((itsCopy.getZ() + tpcCopy.getZ()) * 0.5f - gloCopy.getZ()) * scaleGlo(1)); + trackQAHolder.dRefGloSnp = safeInt8Clamp(((itsCopy.getSnp() + tpcCopy.getSnp()) * 0.5f - gloCopy.getSnp()) * scaleGlo(2)); + trackQAHolder.dRefGloTgl = safeInt8Clamp(((itsCopy.getTgl() + tpcCopy.getTgl()) * 0.5f - gloCopy.getTgl()) * scaleGlo(3)); + trackQAHolder.dRefGloQ2Pt = safeInt8Clamp(((itsCopy.getQ2Pt() + tpcCopy.getQ2Pt()) * 0.5f - gloCopy.getQ2Pt()) * scaleGlo(4)); + // + + if (mStreamerFlags[AODProducerStreamerFlags::TrackQA]) { + (*mStreamer) << "trackQA" + << "trackITSOrig=" << itsOrig + << "trackTPCOrig=" << tpcOrig + << "trackITSTPCOrig=" << trackPar + << "trackITSProp=" << itsCopy + << "trackTPCProp=" << tpcCopy + << "trackITSTPCProp=" << gloCopy + << "refRadius=" << o2::aod::track::trackQARefRadius + << "scaleBins=" << o2::aod::track::trackQAScaleBins + << "scaleCont0=" << scaleCont(0) + << "scaleCont1=" << scaleCont(1) + << "scaleCont2=" << scaleCont(2) + << "scaleCont3=" << scaleCont(3) + << "scaleCont4=" << scaleCont(4) + << "scaleGlo0=" << scaleGlo(0) + << "scaleGlo1=" << scaleGlo(1) + << "scaleGlo2=" << scaleGlo(2) + << "scaleGlo3=" << scaleGlo(3) + << "scaleGlo4=" << scaleGlo(4) + << "trackQAHolder.tpcTime0=" << trackQAHolder.tpcTime0 + << "trackQAHolder.tpcdcaR=" << trackQAHolder.tpcdcaR + << "trackQAHolder.tpcdcaZ=" << trackQAHolder.tpcdcaZ + << "trackQAHolder.tpcdcaClusterByteMask=" << trackQAHolder.tpcClusterByteMask + << "trackQAHolder.tpcdEdxMax0R=" << trackQAHolder.tpcdEdxMax0R + << "trackQAHolder.tpcdEdxMax1R=" << trackQAHolder.tpcdEdxMax1R + << "trackQAHolder.tpcdEdxMax2R=" << trackQAHolder.tpcdEdxMax2R + << "trackQAHolder.tpcdEdxMax3R=" << trackQAHolder.tpcdEdxMax3R + << "trackQAHolder.tpcdEdxTot0R=" << trackQAHolder.tpcdEdxTot0R + << "trackQAHolder.tpcdEdxTot1R=" << trackQAHolder.tpcdEdxTot1R + << "trackQAHolder.tpcdEdxTot2R=" << trackQAHolder.tpcdEdxTot2R + << "trackQAHolder.tpcdEdxTot3R=" << trackQAHolder.tpcdEdxTot3R + << "trackQAHolder.dRefContY=" << trackQAHolder.dRefContY + << "trackQAHolder.dRefContZ=" << trackQAHolder.dRefContZ + << "trackQAHolder.dRefContSnp=" << trackQAHolder.dRefContSnp + << "trackQAHolder.dRefContTgl=" << trackQAHolder.dRefContTgl + << "trackQAHolder.dRefContQ2Pt=" << trackQAHolder.dRefContQ2Pt + << "trackQAHolder.dRefGloY=" << trackQAHolder.dRefGloY + << "trackQAHolder.dRefGloZ=" << trackQAHolder.dRefGloZ + << "trackQAHolder.dRefGloSnp=" << trackQAHolder.dRefGloSnp + << "trackQAHolder.dRefGloTgl=" << trackQAHolder.dRefGloTgl + << "trackQAHolder.dRefGloQ2Pt=" << trackQAHolder.dRefGloQ2Pt + << "trackQAHolder.dTofdX=" << trackQAHolder.dTofdX + << "trackQAHolder.dTofdZ=" << trackQAHolder.dTofdZ + << "scaleTOF=" << scaleTOF + << "\n"; } } } @@ -3142,6 +3166,7 @@ DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, boo OutputForTable::spec(), OutputForTable::spec(), OutputForTable::spec(), + OutputForTable::spec(), OutputForTable::spec(), OutputForTable::spec(), OutputForTable::spec(), diff --git a/Detectors/AOD/src/StandaloneAODProducer.cxx b/Detectors/AOD/src/StandaloneAODProducer.cxx index a9f5a09141d8c..7ac59a556ac08 100644 --- a/Detectors/AOD/src/StandaloneAODProducer.cxx +++ b/Detectors/AOD/src/StandaloneAODProducer.cxx @@ -94,7 +94,7 @@ void fillMCollisionTable(o2::steer::MCKinematicsReader const& mcreader) TFile outfile("aod.root", "UPDATE"); { - TableToTree t2t(mccoltable, &outfile, aod::MetadataTrait::metadata::tableLabel()); + TableToTree t2t(mccoltable, &outfile, aod::description_str(aod::signature()).data()); t2t.addAllBranches(); t2t.process(); } @@ -200,12 +200,12 @@ void fillCollisionAndTrackTable() f.Close(); TFile outfile("aod.root", "RECREATE"); { - TableToTree t2t(colltable, &outfile, aod::MetadataTrait::metadata::tableLabel()); + TableToTree t2t(colltable, &outfile, aod::description_str(aod::signature()).data()); t2t.addAllBranches(); t2t.process(); } { - TableToTree t2t(tracktable, &outfile, "Tracks" /* aod::MetadataTrait::metadata::tableLabel() */); + TableToTree t2t(tracktable, &outfile, aod::description_str(aod::signature()).data()); t2t.addAllBranches(); t2t.process(); } diff --git a/Detectors/Base/include/DetectorsBase/MatCell.h b/Detectors/Base/include/DetectorsBase/MatCell.h index 88143ddf44b03..40c5fd3db1f69 100644 --- a/Detectors/Base/include/DetectorsBase/MatCell.h +++ b/Detectors/Base/include/DetectorsBase/MatCell.h @@ -31,7 +31,7 @@ struct MatCell { float meanX2X0; ///< fraction of radiaton lenght GPUd() MatCell() : meanRho(0.f), meanX2X0(0.f) {} - GPUdDefault() MatCell(const MatCell& src) CON_DEFAULT; + GPUdDefault() MatCell(const MatCell& src) = default; GPUd() void set(const MatCell& c) { @@ -55,7 +55,7 @@ struct MatBudget : MatCell { float length; ///< length in material GPUd() MatBudget() : length(0.f) {} - GPUdDefault() MatBudget(const MatBudget& src) CON_DEFAULT; + GPUdDefault() MatBudget(const MatBudget& src) = default; GPUd() void scale(float scale) { diff --git a/Detectors/Base/include/DetectorsBase/MatLayerCyl.h b/Detectors/Base/include/DetectorsBase/MatLayerCyl.h index 869234e03f6c1..ca015fa457a1a 100644 --- a/Detectors/Base/include/DetectorsBase/MatLayerCyl.h +++ b/Detectors/Base/include/DetectorsBase/MatLayerCyl.h @@ -56,8 +56,8 @@ class MatLayerCyl : public o2::gpu::FlatObject #ifndef GPUCA_GPUCODE MatLayerCyl(); - MatLayerCyl(const MatLayerCyl& src) CON_DELETE; - ~MatLayerCyl() CON_DEFAULT; + MatLayerCyl(const MatLayerCyl& src) = delete; + ~MatLayerCyl() = default; #endif #ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version diff --git a/Detectors/Base/include/DetectorsBase/MatLayerCylSet.h b/Detectors/Base/include/DetectorsBase/MatLayerCylSet.h index 83fed8caf42eb..c74ce365d378f 100644 --- a/Detectors/Base/include/DetectorsBase/MatLayerCylSet.h +++ b/Detectors/Base/include/DetectorsBase/MatLayerCylSet.h @@ -52,9 +52,9 @@ class MatLayerCylSet : public o2::gpu::FlatObject public: #ifndef GPUCA_GPUCODE - MatLayerCylSet() CON_DEFAULT; - ~MatLayerCylSet() CON_DEFAULT; - MatLayerCylSet(const MatLayerCylSet& src) CON_DELETE; + MatLayerCylSet() = default; + ~MatLayerCylSet() = default; + MatLayerCylSet(const MatLayerCylSet& src) = delete; #endif GPUd() const MatLayerCylSetLayout* get() const { return reinterpret_cast(mFlatBufferPtr); } diff --git a/Detectors/Base/include/DetectorsBase/Ray.h b/Detectors/Base/include/DetectorsBase/Ray.h index 304ad5f00b03f..a72208c41af0d 100644 --- a/Detectors/Base/include/DetectorsBase/Ray.h +++ b/Detectors/Base/include/DetectorsBase/Ray.h @@ -49,7 +49,7 @@ class Ray GPUd() Ray() : mP{0.f}, mD{0.f}, mDistXY2(0.f), mDistXY2i(0.f), mDistXYZ(0.f), mXDxPlusYDy(0.f), mXDxPlusYDyRed(0.f), mXDxPlusYDy2(0.f), mR02(0.f), mR12(0.f) { } - GPUdDefault() ~Ray() CON_DEFAULT; + GPUdDefault() ~Ray() = default; #ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version Ray(const math_utils::Point3D point0, const math_utils::Point3D point1); diff --git a/Detectors/Base/src/GRPGeomHelper.cxx b/Detectors/Base/src/GRPGeomHelper.cxx index 2a76e52b3679e..e7e5248493548 100644 --- a/Detectors/Base/src/GRPGeomHelper.cxx +++ b/Detectors/Base/src/GRPGeomHelper.cxx @@ -235,7 +235,7 @@ void GRPGeomHelper::checkUpdates(ProcessingContext& pc) for (auto id = DetID::First; id <= DetID::Last; id++) { std::string binding = fmt::format("align{}", DetID::getName(id)); if (pc.inputs().getPos(binding.c_str()) < 0) { - return; + continue; } else { pc.inputs().get*>(binding); } diff --git a/Detectors/CTF/README.md b/Detectors/CTF/README.md index e1e65060db523..47ce765de289a 100644 --- a/Detectors/CTF/README.md +++ b/Detectors/CTF/README.md @@ -95,6 +95,14 @@ comma-separated list of detectors to read, Overrides skipDet ``` comma-separated list of detectors to skip + +By default an exception will be thrown if detector is requested but missing in the CTF. To enable injection of the empty output in such case one should use option `--allow-missing-detectors`. + +``` +--ctf-data-subspec arg (=0) +``` +allows to alter the `subSpecification` used to send the CTFDATA from the reader to decoders. Non-0 value must be used in case the data extracted by the CTF-reader should be processed and stored in new CTFs (in order to avoid clash of CTFDATA messages of the reader and writer). + ``` --max-tf arg (=-1) ``` @@ -141,6 +149,8 @@ There is a possibility to read remote root files directly, w/o caching them loca 2) provide proper regex to define remote files, e.g. for the example above: `--remote-regex "^root://.+/eos/aliceo2/.+"`. 3) pass an option `--copy-cmd no-copy`. +## Selective TF reading + ``` --select-ctf-ids ``` @@ -148,24 +158,25 @@ This is a `ctf-reader` device local option allowing selective reading of particu Note that the index corresponds not to the entry of the TF in the CTF tree but to the reader own counter incremented throught all input files (e.g. if the 10 CTF files with 20 TFs each are provided for the input and the selection of TFs `0,2,22,66` is provided, the reader will inject to the DPL the TFs at entries 0 and 2 from the 1st CTF file, entry 5 of the second file, entry 6 of the 3d and will finish the job. -For the ITS and MFT entropy decoding one can request either to decompose clusters to digits and send them instead of clusters (via `o2-ctf-reader-workflow` global options `--its-digits` and `--mft-digits` respectively) -or to apply the noise mask to decoded clusters (or decoded digits). If the masking (e.g. via option `--its-entropy-decoder " --mask-noise "`) is requested, user should provide to the entropy decoder the noise mask file (eventually will be loaded from CCDB) and cluster patterns decoding dictionary (if the clusters were encoded with patterns IDs). -For example, ``` -o2-ctf-reader-workflow --ctf-input --onlyDet ITS,MFT --its-entropy-decoder ' --mask-noise' | ... +--ir-frames-files --skip-skimmed-out-tf ``` -will decode ITS and MFT data, decompose on the fly ITS clusters to digits, mask the noisy pixels with the provided masks, recluster remaining ITS digits and send the new clusters out, together with unchanged MFT clusters. +This option (used for skimming) allow to push to DPL only those TFs which overlap with selected BC-ranges provided via input root file (for various formats see `o2::utils::IRFrameSelector::loadIRFrames` method). + ``` -o2-ctf-reader-workflow --ctf-input --onlyDet ITS,MFT --mft-digits --mft-entropy-decoder ' --mask-noise' | ... +--ir-frames-files ``` -will send decompose clusters to digits and send ben out after masking the noise for the MFT, while ITS clusters will be sent as decoded. - -By default an exception will be thrown if detector is requested but missing in the CTF. To enable injection of the empty output in such case one should use option `--allow-missing-detectors`. +This option allows to push to DPL only those TFs which overlap with the ` ` (separators can be any whitespace, comma or semicolon) records provided via text file (assuming that there are some entries for a given run, otherwise the option is ignored). +Multiple ranges per run and multiple runs can be mentioned in a single input file. The range limits can be indicated either as a UNIX timestamp in `ms` or as an orbit number (in the fill the run belongs to). +In case an option ``` ---ctf-data-subspec arg (=0) +--invert-irframe-selection ``` -allows to alter the `subSpecification` used to send the CTFDATA from the reader to decoders. Non-0 value must be used in case the data extracted by the CTF-reader should be processed and stored in new CTFs (in order to avoid clash of CTFDATA messages of the reader and writer). +is provided, the selections above are inverted: TFs matching some of the provided ranges will be discarded, while the rest will be pushed to the DPL + +At the end of the processing the `ctf-writer` will create a local file `ctf_read_ntf.txt` containing only the number of TFs pushed to the DPL. +In case no TF passed the selections above, this file will contain 0. ## Support for externally provided encoding dictionaries @@ -201,3 +212,18 @@ Additionally, one may throttle on the free SHM by providing an option to the rea Note that by default the reader reads into the memory the CTF data and prepares all output messages but injects them only once the rate-limiter allows that. With the option `--limit-tf-before-reading` set also the preparation of the data to inject will be conditioned by the green light from the rate-limiter. + + +## Modifying ITS/MFT CTF output + +For the ITS and MFT entropy decoding one can request either to decompose clusters to digits and send them instead of clusters (via `o2-ctf-reader-workflow` global options `--its-digits` and `--mft-digits` respectively) +or to apply the noise mask to decoded clusters (or decoded digits). If the masking (e.g. via option `--its-entropy-decoder " --mask-noise "`) is requested, user should provide to the entropy decoder the noise mask file (eventually will be loaded from CCDB) and cluster patterns decoding dictionary (if the clusters were encoded with patterns IDs). +For example, +``` +o2-ctf-reader-workflow --ctf-input --onlyDet ITS,MFT --its-entropy-decoder ' --mask-noise' | ... +``` +will decode ITS and MFT data, decompose on the fly ITS clusters to digits, mask the noisy pixels with the provided masks, recluster remaining ITS digits and send the new clusters out, together with unchanged MFT clusters. +``` +o2-ctf-reader-workflow --ctf-input --onlyDet ITS,MFT --mft-digits --mft-entropy-decoder ' --mask-noise' | ... +``` +will send decompose clusters to digits and send ben out after masking the noise for the MFT, while ITS clusters will be sent as decoded. diff --git a/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h b/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h index 997572e0371b2..b202013a6eea1 100644 --- a/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h +++ b/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h @@ -31,8 +31,10 @@ struct CTFReaderInp { std::string remoteRegex{}; std::string metricChannel{}; std::string fileIRFrames{}; + std::string fileRunTimeSpans{}; std::vector ctfIDs{}; bool skipSkimmedOutTF = false; + bool invertIRFramesSelection = false; bool allowMissingDetectors = false; bool checkTFLimitBeforeReading = false; bool sup0xccdb = false; diff --git a/Detectors/CTF/workflow/src/CTFReaderSpec.cxx b/Detectors/CTF/workflow/src/CTFReaderSpec.cxx index 70bb589e8836a..bcf3b5d975b74 100644 --- a/Detectors/CTF/workflow/src/CTFReaderSpec.cxx +++ b/Detectors/CTF/workflow/src/CTFReaderSpec.cxx @@ -45,6 +45,9 @@ #include "DataFormatsZDC/CTF.h" #include "DataFormatsHMP/CTF.h" #include "DataFormatsCTP/CTF.h" +#include "DataFormatsParameters/AggregatedRunInfo.h" +#include "CCDB/BasicCCDBManager.h" +#include "CommonConstants/LHCConstants.h" #include "Algorithm/RangeTokenizer.h" #include #include @@ -81,6 +84,8 @@ class CTFReaderSpec : public o2::framework::Task void run(o2::framework::ProcessingContext& pc) final; private: + void runTimeRangesToIRFrameSelector(const o2::framework::TimingInfo& timingInfo); + void loadRunTimeSpans(const std::string& flname); void openCTFFile(const std::string& flname); bool processTF(ProcessingContext& pc); void checkTreeEntries(); @@ -91,16 +96,20 @@ class CTFReaderSpec : public o2::framework::Task void tryToFixCTFHeader(CTFHeader& ctfHeader) const; CTFReaderInp mInput{}; o2::utils::IRFrameSelector mIRFrameSelector; // optional IR frames selector + std::map>> mRunTimeRanges; std::unique_ptr mFileFetcher; std::unique_ptr mCTFFile; std::unique_ptr mCTFTree; bool mRunning = false; bool mUseLocalTFCounter = false; + int mConvRunTimeRangesToOrbits = -1; // not defined yet int mCTFCounter = 0; + int mCTFCounterAcc = 0; int mNFailedFiles = 0; int mFilesRead = 0; int mTFLength = 128; int mNWaits = 0; + int mRunNumberPrev = -1; long mTotalWaitTime = 0; long mLastSendTime = 0L; long mCurrTreeEntry = 0L; @@ -129,8 +138,8 @@ void CTFReaderSpec::stopReader() return; } LOGP(info, "CTFReader stops processing, {} files read, {} files failed", mFilesRead - mNFailedFiles, mNFailedFiles); - LOGP(info, "CTF reading total timing: Cpu: {:.3f} Real: {:.3f} s for {} TFs in {} loops, spent {:.2} s in {} data waiting states", - mTimer.CpuTime(), mTimer.RealTime(), mCTFCounter, mFileFetcher->getNLoops(), 1e-6 * mTotalWaitTime, mNWaits); + LOGP(info, "CTF reading total timing: Cpu: {:.3f} Real: {:.3f} s for {} TFs ({} accepted) in {} loops, spent {:.2} s in {} data waiting states", + mTimer.CpuTime(), mTimer.RealTime(), mCTFCounter, mCTFCounterAcc, mFileFetcher->getNLoops(), 1e-6 * mTotalWaitTime, mNWaits); mRunning = false; mFileFetcher->stop(); mFileFetcher.reset(); @@ -164,6 +173,111 @@ void CTFReaderSpec::init(InitContext& ic) mTFLength = hbfu.nHBFPerTF; LOGP(info, "IRFrames will be selected from {}, assumed TF length: {} HBF", mInput.fileIRFrames, mTFLength); } + if (!mInput.fileRunTimeSpans.empty()) { + loadRunTimeSpans(mInput.fileRunTimeSpans); + } +} + +void CTFReaderSpec::runTimeRangesToIRFrameSelector(const o2::framework::TimingInfo& timingInfo) +{ + // convert entries in the runTimeRanges to IRFrameSelector, if needed, convert time to orbit + mIRFrameSelector.clear(); + auto ent = mRunTimeRanges.find(timingInfo.runNumber); + if (ent == mRunTimeRanges.end()) { + LOGP(info, "RunTimeRanges selection was provided but run {} has no entries, all TFs will be processed", timingInfo.runNumber); + return; + } + o2::parameters::AggregatedRunInfo rinfo; + auto& ccdb = o2::ccdb::BasicCCDBManager::instance(); + rinfo = o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(ccdb, timingInfo.runNumber); + if (rinfo.runNumber != timingInfo.runNumber || rinfo.orbitsPerTF < 1) { + LOGP(fatal, "failed to extract AggregatedRunInfo for run {}", timingInfo.runNumber); + } + mTFLength = rinfo.orbitsPerTF; + std::vector frames; + for (const auto& rng : ent->second) { + long orbMin = 0, orbMax = 0; + if (mConvRunTimeRangesToOrbits > 0) { + orbMin = rinfo.orbitSOR + (rng.first - rinfo.sor) / (o2::constants::lhc::LHCOrbitMUS * 0.001); + orbMax = rinfo.orbitSOR + (rng.second - rinfo.sor) / (o2::constants::lhc::LHCOrbitMUS * 0.001); + } else { + orbMin = rng.first; + orbMax = rng.second; + } + if (orbMin < 0) { + orbMin = 0; + } + if (orbMax < 0) { + orbMax = 0; + } + if (timingInfo.runNumber > 523897) { + orbMin = (orbMin / rinfo.orbitsPerTF) * rinfo.orbitsPerTF; + orbMax = (orbMax / rinfo.orbitsPerTF + 1) * rinfo.orbitsPerTF - 1; + } + LOGP(info, "TFs overlapping with orbits {}:{} will be {}", orbMin, orbMax, mInput.invertIRFramesSelection ? "rejected" : "selected"); + frames.emplace_back(InteractionRecord{0, uint32_t(orbMin)}, InteractionRecord{o2::constants::lhc::LHCMaxBunches, uint32_t(orbMax)}); + } + mIRFrameSelector.setOwnList(frames, true); +} + +void CTFReaderSpec::loadRunTimeSpans(const std::string& flname) +{ + std::ifstream inputFile(flname); + if (!inputFile) { + LOGP(fatal, "Failed to open selected run/timespans file {}", mInput.fileRunTimeSpans); + } + std::string line; + size_t cntl = 0, cntr = 0; + while (std::getline(inputFile, line)) { + cntl++; + for (char& ch : line) { // Replace semicolons and tabs with spaces for uniform processing + if (ch == ';' || ch == '\t' || ch == ',') { + ch = ' '; + } + } + o2::utils::Str::trim(line); + if (line.size() < 1 || line[0] == '#') { + continue; + } + auto tokens = o2::utils::Str::tokenize(line, ' '); + auto logError = [&cntl, &line]() { LOGP(error, "Expected format for selection is tripplet , failed on line#{}: {}", cntl, line); }; + if (tokens.size() >= 3) { + int run = 0; + long rmin, rmax; + try { + run = std::stoi(tokens[0]); + rmin = std::stol(tokens[1]); + rmax = std::stol(tokens[2]); + } catch (...) { + logError(); + continue; + } + + constexpr long ISTimeStamp = 1514761200000L; + int convmn = rmin > ISTimeStamp ? 1 : 0, convmx = rmax > ISTimeStamp ? 1 : 0; // values above ISTimeStamp are timestamps (need to be converted to orbits) + if (rmin > rmax) { + LOGP(fatal, "Provided range limits are not in increasing order, entry is {}", line); + } + if (mConvRunTimeRangesToOrbits == -1) { + if (convmn != convmx) { + LOGP(fatal, "Provided range limits should be both consistent either with orbit number or with unix timestamp in ms, entry is {}", line); + } + mConvRunTimeRangesToOrbits = convmn; // need to convert to orbit if time + LOGP(info, "Interpret selected time-spans input as {}", mConvRunTimeRangesToOrbits == 1 ? "timstamps(ms)" : "orbits"); + } else { + if (mConvRunTimeRangesToOrbits != convmn || mConvRunTimeRangesToOrbits != convmx) { + LOGP(fatal, "Provided range limits should are not consistent with previously determined {} input, entry is {}", mConvRunTimeRangesToOrbits == 1 ? "timestamps" : "orbits", line); + } + } + + mRunTimeRanges[run].emplace_back(rmin, rmax); + cntr++; + } else { + logError(); + } + } + LOGP(info, "Read {} time-spans for {} runs from {}", cntr, mRunTimeRanges.size(), mInput.fileRunTimeSpans); + inputFile.close(); } ///_______________________________________ @@ -256,6 +370,17 @@ void CTFReaderSpec::run(ProcessingContext& pc) pc.services().get().endOfStream(); pc.services().get().readyToQuit(QuitRequest::Me); stopReader(); + const std::string dummy{"ctf_read_ntf.txt"}; + if (mCTFCounterAcc == 0) { + LOGP(warn, "No TF passed selection, writing a 0 to file {}", dummy); + } + try { + std::ofstream outfile; + outfile.open(dummy, std::ios::out | std::ios::trunc); + outfile << mCTFCounterAcc << std::endl; + } catch (...) { + LOGP(error, "Failed to write {}", dummy); + } } } @@ -278,7 +403,7 @@ bool CTFReaderSpec::processTF(ProcessingContext& pc) } if (mUseLocalTFCounter) { - ctfHeader.tfCounter = mCTFCounter; + ctfHeader.tfCounter = mCTFCounterAcc; } LOG(info) << ctfHeader; @@ -289,19 +414,26 @@ bool CTFReaderSpec::processTF(ProcessingContext& pc) timingInfo.tfCounter = ctfHeader.tfCounter; timingInfo.runNumber = ctfHeader.run; + if (mRunTimeRanges.size() && timingInfo.runNumber != mRunNumberPrev) { + runTimeRangesToIRFrameSelector(timingInfo); + } + mRunNumberPrev = timingInfo.runNumber; + if (mIRFrameSelector.isSet()) { o2::InteractionRecord ir0(0, timingInfo.firstTForbit); - // we cannot have GRPECS via DPL CCDB fetcher in the CTFReader, so we use mTFLength extracted from the HBFUtils o2::InteractionRecord ir1(o2::constants::lhc::LHCMaxBunches - 1, timingInfo.firstTForbit < 0xffffffff - (mTFLength - 1) ? timingInfo.firstTForbit + (mTFLength - 1) : 0xffffffff); auto irSpan = mIRFrameSelector.getMatchingFrames({ir0, ir1}); - if (irSpan.size() == 0 && mInput.skipSkimmedOutTF) { - LOGP(info, "Skimming did not define any selection for TF [{}] : [{}]", ir0.asString(), ir1.asString()); + bool acc = true; + if (mInput.skipSkimmedOutTF) { + acc = (irSpan.size() > 0) ? !mInput.invertIRFramesSelection : mInput.invertIRFramesSelection; + LOGP(info, "IRFrame selection contains {} frames for TF [{}] : [{}]: {}use this TF (selection inversion mode is {})", + irSpan.size(), ir0.asString(), ir1.asString(), acc ? "" : "do not ", mInput.invertIRFramesSelection ? "ON" : "OFF"); + } + if (!acc) { return false; - } else { - if (mInput.checkTFLimitBeforeReading) { - limiter.check(pc, mInput.tfRateLimit, mInput.minSHM); - } - LOGP(info, "{} IR-Frames are selected for TF [{}] : [{}]", irSpan.size(), ir0.asString(), ir1.asString()); + } + if (mInput.checkTFLimitBeforeReading) { + limiter.check(pc, mInput.tfRateLimit, mInput.minSHM); } auto outVec = pc.outputs().make>(OutputRef{"selIRFrames"}, irSpan.begin(), irSpan.end()); } else { @@ -329,6 +461,7 @@ bool CTFReaderSpec::processTF(ProcessingContext& pc) processDetector(DetID::CPV, ctfHeader, pc); processDetector(DetID::ZDC, ctfHeader, pc); processDetector(DetID::CTP, ctfHeader, pc); + mCTFCounterAcc++; // send sTF acknowledge message if (!mInput.sup0xccdb) { @@ -466,7 +599,7 @@ DataProcessorSpec getCTFReaderSpec(const CTFReaderInp& inp) outputs.emplace_back(OutputLabel{det.getName()}, det.getDataOrigin(), "CTFDATA", inp.subspec, Lifetime::Timeframe); } } - if (!inp.fileIRFrames.empty()) { + if (!inp.fileIRFrames.empty() || !inp.fileRunTimeSpans.empty()) { outputs.emplace_back(OutputLabel{"selIRFrames"}, "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } if (!inp.sup0xccdb) { diff --git a/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx b/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx index 90d259f4e3a5c..1f0ef9a3b871b 100644 --- a/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx +++ b/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx @@ -66,7 +66,9 @@ void customize(std::vector& workflowOptions) options.push_back(ConfigParamSpec{"ctf-data-subspec", VariantType::Int, 0, {"subspec to use for decoded CTF messages (use non-0 if CTF writer will be attached downstream)"}}); options.push_back(ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}); options.push_back(ConfigParamSpec{"ir-frames-files", VariantType::String, "", {"If non empty, inject selected IRFrames from this file"}}); + options.push_back(ConfigParamSpec{"run-time-span-file", VariantType::String, "", {"If non empty, inject selected IRFrames from this text file (run, min/max orbit or unix time)"}}); options.push_back(ConfigParamSpec{"skip-skimmed-out-tf", VariantType::Bool, false, {"Do not process TFs with empty IR-Frame coverage"}}); + options.push_back(ConfigParamSpec{"invert-irframe-selection", VariantType::Bool, false, {"Select only frames mentioned in ir-frames-file (skip-skimmed-out-tf applied to TF not selected!)"}}); // options.push_back(ConfigParamSpec{"its-digits", VariantType::Bool, false, {"convert ITS clusters to digits"}}); options.push_back(ConfigParamSpec{"mft-digits", VariantType::Bool, false, {"convert MFT clusters to digits"}}); @@ -125,7 +127,9 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) ctfInput.sup0xccdb = !configcontext.options().get("send-diststf-0xccdb"); ctfInput.minSHM = std::stoul(configcontext.options().get("timeframes-shm-limit")); ctfInput.fileIRFrames = configcontext.options().get("ir-frames-files"); + ctfInput.fileRunTimeSpans = configcontext.options().get("run-time-span-file"); ctfInput.skipSkimmedOutTF = configcontext.options().get("skip-skimmed-out-tf"); + ctfInput.invertIRFramesSelection = configcontext.options().get("invert-irframe-selection"); int verbosity = configcontext.options().get("ctf-reader-verbosity"); int rateLimitingIPCID = std::stoi(configcontext.options().get("timeframes-rate-limit-ipcid")); @@ -133,6 +137,12 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) if (rateLimitingIPCID > -1 && !chanFmt.empty()) { ctfInput.metricChannel = fmt::format(fmt::runtime(chanFmt), o2::framework::ChannelSpecHelpers::defaultIPCFolder(), rateLimitingIPCID); } + if (!ctfInput.fileRunTimeSpans.empty()) { + ctfInput.skipSkimmedOutTF = true; + } + if (!ctfInput.fileIRFrames.empty() && !ctfInput.fileRunTimeSpans.empty()) { + LOGP(fatal, "One cannot provide --ir-frames-files and --run-time-span-file options simultaneously"); + } specs.push_back(o2::ctf::getCTFReaderSpec(ctfInput)); diff --git a/Detectors/CTP/macro/CMakeLists.txt b/Detectors/CTP/macro/CMakeLists.txt index 96f336c840241..8608c1a8b7846 100644 --- a/Detectors/CTP/macro/CMakeLists.txt +++ b/Detectors/CTP/macro/CMakeLists.txt @@ -73,4 +73,15 @@ o2_add_test_root_macro(CreateBKForRun.C PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP O2::CCDB LABELS ctp) - +o2_add_test_root_macro(CheckCTPConfig.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + O2::CCDB + LABELS ctp) +o2_add_test_root_macro(GetRates.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + O2::CCDB + LABELS ctp) +o2_add_test_root_macro(TestGetRates.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + O2::CCDB + LABELS ctp) \ No newline at end of file diff --git a/Detectors/CTP/macro/CheckCTPConfig.C b/Detectors/CTP/macro/CheckCTPConfig.C new file mode 100644 index 0000000000000..24a5e354f3fcd --- /dev/null +++ b/Detectors/CTP/macro/CheckCTPConfig.C @@ -0,0 +1,58 @@ +// 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 CreateCTPConfig.C +/// \brief create CTP config, test it and add to database +/// \author Roman Lietava + +#if !defined(__CLING__) || defined(__ROOTCLING__) + +#include +#include "CCDB/CcdbApi.h" +#include "CCDB/BasicCCDBManager.h" +#include "DataFormatsCTP/Configuration.h" +#include +#include +#include +#endif +using namespace o2::ctp; +int CheckCTPConfig(std::string cfgRun3str = "/home/rl/backup24/runs/559781.rcfg2", int writeToFile = 0) +{ + // + // run3 config + // + if (cfgRun3str.find(".rcfg") == std::string::npos) { + std::cout << "No file name:" << cfgRun3str << std::endl; + return 1; + } else { + std::string filename = cfgRun3str; + std::ifstream in; + in.open(filename); + if (!in) { + std::cout << "Can not open file:" << filename << std::endl; + return 2; + } + std::stringstream buffer; + buffer << in.rdbuf(); + cfgRun3str = buffer.str(); + } + // + CTPConfiguration ctpcfg; + int ret = ctpcfg.loadConfigurationRun3(cfgRun3str); + ctpcfg.printStream(std::cout); + std::cout << "CTP config done" << std::endl; + // ctpcfg.checkConfigConsistency(); + auto ctpclasses = ctpcfg.getCTPClasses(); + for (auto const& cls : ctpclasses) { + std::cout << cls.descriptor->name << ":" << std::hex << cls.descriptor->getInputsMask() << std::endl; + } + return ret; +} diff --git a/Detectors/CTP/macro/GetRates.C b/Detectors/CTP/macro/GetRates.C new file mode 100644 index 0000000000000..d2b65d821114a --- /dev/null +++ b/Detectors/CTP/macro/GetRates.C @@ -0,0 +1,28 @@ +// 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 +#endif +using namespace o2::ctp; + +void TestFetcher(int runNumber = 535087) +{ + auto& ccdb = o2::ccdb::BasicCCDBManager::instance(); + std::pair pp = ccdb.getRunDuration(runNumber); + long ts = pp.first + 60; + std::cout << "Run duration:" << pp.first << " " << pp.second << std::endl; + // Opening run + CTPRateFetcher fetcher; + fetcher.setupRun(runNumber, &ccdb, ts, 1); +} diff --git a/Detectors/CTP/macro/TestGetRates.C b/Detectors/CTP/macro/TestGetRates.C new file mode 100644 index 0000000000000..47790426d66c7 --- /dev/null +++ b/Detectors/CTP/macro/TestGetRates.C @@ -0,0 +1,32 @@ +// 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 +#endif +using namespace o2::ctp; + +void TestGetRates(int runNumber = 557251) +{ + auto& ccdb = o2::ccdb::BasicCCDBManager::instance(); + // Opening run + std::pair pp = ccdb.getRunDuration(runNumber); + long ts = pp.first + 60; + std::cout << "Run duration:" << pp.first << " " << pp.second << std::endl; + CTPRateFetcher fetcher; + fetcher.setupRun(runNumber, &ccdb, ts, 1); + fetcher.setOrbit(1); + std::array rates; + fetcher.getRates(rates, &ccdb, runNumber, "T0VTX"); + std::cout << "Start:" << rates[0] << " End:" << rates[1] << " Middle:" << rates[2] << std::endl; +} diff --git a/Detectors/CTP/simulation/src/Digitizer.cxx b/Detectors/CTP/simulation/src/Digitizer.cxx index 7f8c00a0ace69..2f033b8a01462 100644 --- a/Detectors/CTP/simulation/src/Digitizer.cxx +++ b/Detectors/CTP/simulation/src/Digitizer.cxx @@ -170,7 +170,7 @@ void Digitizer::calculateClassMask(const std::bitset ctpinpmask, st // } } } else { - if ((ctpinpmask.to_ullong() & tcl.descriptor->getInputsMask()) == tcl.descriptor->getInputsMask()) { + if (tcl.descriptor && ((ctpinpmask.to_ullong() & tcl.descriptor->getInputsMask()) == tcl.descriptor->getInputsMask())) { classmask |= tcl.classMask; } } diff --git a/Detectors/CTP/workflowIO/src/DigitReaderSpec.cxx b/Detectors/CTP/workflowIO/src/DigitReaderSpec.cxx index ecf1c2e19b660..81e6f53f42dcc 100644 --- a/Detectors/CTP/workflowIO/src/DigitReaderSpec.cxx +++ b/Detectors/CTP/workflowIO/src/DigitReaderSpec.cxx @@ -17,7 +17,10 @@ #include "DataFormatsCTP/LumiInfo.h" #include "Headers/DataHeader.h" #include "DetectorsCommonDataFormats/DetID.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" #include "CommonUtils/NameConf.h" +#include "CommonUtils/IRFrameSelector.h" #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" #include "Framework/ControlService.h" @@ -50,6 +53,7 @@ class DigitReader : public Task std::unique_ptr mTree; bool mUseMC = false; // use MC truth + bool mUseIRFrames = false; // selected IRFrames mode std::string mDigTreeName = "o2sim"; std::string mDigitBranchName = "CTPDigits"; std::string mLumiBranchName = "CTPLumi"; @@ -58,7 +62,7 @@ class DigitReader : public Task DigitReader::DigitReader(bool useMC) { if (useMC) { - LOG(info) << "CTP does not support MC truth at the moment"; + LOG(info) << "CTP : truth = data as CTP inputs are already digital"; } } @@ -66,21 +70,70 @@ void DigitReader::init(InitContext& ic) { auto filename = o2::utils::Str::concat_string(o2::utils::Str::rectifyDirectory(ic.options().get("input-dir")), ic.options().get("ctp-digit-infile")); + if (ic.options().hasOption("ignore-irframes") && !ic.options().get("ignore-irframes")) { + mUseIRFrames = true; + } connectTree(filename); } void DigitReader::run(ProcessingContext& pc) { - auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen - - mTree->GetEntry(ent); - LOG(info) << "DigitReader pushes " << mDigits.size() << " digits at entry " << ent; - pc.outputs().snapshot(Output{"CTP", "DIGITS", 0}, mDigits); - pc.outputs().snapshot(Output{"CTP", "LUMI", 0}, mLumi); - if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { - pc.services().get().endOfStream(); - pc.services().get().readyToQuit(QuitRequest::Me); + gsl::span irFrames{}; + // LOG(info) << "Using IRs:" << mUseIRFrames; + if (mUseIRFrames) { + irFrames = pc.inputs().get>("driverInfo"); + } + auto ent = mTree->GetReadEntry(); + if (!mUseIRFrames) { + ent++; + assert(ent < mTree->GetEntries()); // this should not happen + mTree->GetEntry(ent); + LOG(info) << "DigitReader pushes " << mDigits.size() << " digits at entry " << ent; + pc.outputs().snapshot(Output{"CTP", "DIGITS", 0}, mDigits); + pc.outputs().snapshot(Output{"CTP", "LUMI", 0}, mLumi); + if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + } + } else { + std::vector digitSel; + if (irFrames.size()) { // we assume the IRFrames are in the increasing order + if (ent < 0) { + ent++; + } + o2::utils::IRFrameSelector irfSel; + // MC digits are already aligned + irfSel.setSelectedIRFrames(irFrames, 0, 0, 0, true); + const auto irMin = irfSel.getIRFrames().front().getMin(); // use processed IRframes for rough comparisons (possible shift!) + const auto irMax = irfSel.getIRFrames().back().getMax(); + LOGP(info, "Selecting IRFrame {}-{}", irMin.asString(), irMax.asString()); + while (ent < mTree->GetEntries()) { + if (ent > mTree->GetReadEntry()) { + mTree->GetEntry(ent); + } + if (mDigits.front().intRecord <= irMax && mDigits.back().intRecord >= irMin) { // THere is overlap + for (int i = 0; i < (int)mDigits.size(); i++) { + const auto& dig = mDigits[i]; + // if(irfSel.check(dig.intRecord)) { // adding selected digit + if (dig.intRecord >= irMin && dig.intRecord <= irMax) { + digitSel.push_back(dig); + LOG(info) << "adding:" << dig.intRecord << " ent:" << ent; + } + } + } + if (mDigits.back().intRecord < irMax) { // need to check the next entry + ent++; + continue; + } + break; // push collected data + } + } + pc.outputs().snapshot(Output{"CTP", "DIGITS", 0}, digitSel); + pc.outputs().snapshot(Output{"CTP", "LUMI", 0}, mLumi); // add full lumi for this TF + if (!irFrames.size() || irFrames.back().isLast()) { + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + } } } diff --git a/Detectors/CTP/workflowScalers/src/ctp-ccdb-orbit.cxx b/Detectors/CTP/workflowScalers/src/ctp-ccdb-orbit.cxx index 13c06730d18ce..0f4203d994402 100644 --- a/Detectors/CTP/workflowScalers/src/ctp-ccdb-orbit.cxx +++ b/Detectors/CTP/workflowScalers/src/ctp-ccdb-orbit.cxx @@ -57,6 +57,7 @@ int main(int argc, char** argv) add_option("run-number,r", bpo::value()->default_value(123), "run number"); add_option("testReset,t", bpo::value()->default_value(0), "0 = CTP/Calib/OrbitReset; 1 = CTP/Calib/OrbitResetTest"); add_option("sox-orbit,x", bpo::value()->default_value(0), "SOX orbit"); + add_option("timestamp,s", bpo::value()->default_value(0), "timestamp of SOX/orbit reading; if 0 timestamp is calulated inside this code"); // opt_all.add(opt_general).add(opt_hidden); @@ -78,8 +79,11 @@ int main(int argc, char** argv) std::string action = vm["action"].as(); std::vector vect; std::string ccdbPath; - auto now = std::chrono::system_clock::now(); - long tt = std::chrono::duration_cast(now.time_since_epoch()).count(); + long tt = vm["timestamp"].as(); + if (tt == 0) { + auto now = std::chrono::system_clock::now(); + tt = std::chrono::duration_cast(now.time_since_epoch()).count(); + } vect.push_back(tt); if (action == "sox") { // write to CTP/Calib/FirstRunOrbit @@ -115,10 +119,10 @@ int main(int argc, char** argv) if (action == "sox") { int64_t runnum = vm["run-number"].as(); metadata["runNumber"] = std::to_string(runnum); - std::cout << "Storing:" << ccdbPath << " " << metadata["runNumber"] << " tmin:" << tmin << " tmax:" << tmax << std::endl; + std::cout << "Storing:" << ccdbPath << " " << metadata["runNumber"] << " tmin:" << tmin << " tmax:" << tmax << " ts:" << tt << std::endl; api.storeAsTFileAny(&(vect), ccdbPath, metadata, tmin, tmax); } else { - std::cout << "Storing:" << ccdbPath << " tmin:" << tmin << " tmax:" << tmax << std::endl; + std::cout << "Storing:" << ccdbPath << " tmin:" << tmin << " tmax:" << tmax << " ts:" << tt << std::endl; api.storeAsTFileAny(&(vect), ccdbPath, metadata, tmin, tmax); } } diff --git a/Detectors/Calibration/workflow/CCDBPopulatorSpec.h b/Detectors/Calibration/workflow/CCDBPopulatorSpec.h index cdd576adf67eb..990d70e17e050 100644 --- a/Detectors/Calibration/workflow/CCDBPopulatorSpec.h +++ b/Detectors/Calibration/workflow/CCDBPopulatorSpec.h @@ -207,6 +207,9 @@ void CCDBPopulator::doUpload(const CcdbObjectInfo& wrp, const gsl::span -#include +#include #include #include "DetectorsDCS/GenericFunctions.h" -namespace o2 -{ -namespace dcs +namespace o2::dcs { -/** - * This regular expression matches with strings representing payload types. - */ -static const std::regex REGEX_PT( - "^(Raw|DPVAL)/(Int|Uint|Float|Double|Bool|Char|String|Time|Binary)$"); - /** *

DeliveryType is a piece of meta-information used for deducing types of * DPVAL payloads and DIM service description strings used with services @@ -406,8 +398,8 @@ inline size_t dim_buffer_size(const DeliveryType type) throw std::domain_error("Illegal DeliveryType."); } } -} // namespace dcs +} // namespace o2::dcs + -} // namespace o2 #endif /* O2_DCS_DELIVERY_TYPE */ diff --git a/Detectors/DCS/testWorkflow/src/dcs-config-proxy.cxx b/Detectors/DCS/testWorkflow/src/dcs-config-proxy.cxx index 9b697da428ad2..b660093fb6029 100644 --- a/Detectors/DCS/testWorkflow/src/dcs-config-proxy.cxx +++ b/Detectors/DCS/testWorkflow/src/dcs-config-proxy.cxx @@ -34,7 +34,7 @@ using namespace o2::framework; using DetID = o2::detectors::DetID; -std::array exceptionsDetID{"GRP"}; +std::array exceptionsDetID{"GRP", "AGD"}; void sendAnswer(const std::string& what, const std::string& ack_chan, fair::mq::Device& device) { diff --git a/Detectors/FOCAL/simulation/data/simcuts.dat b/Detectors/FOCAL/simulation/data/simcuts.dat index 1ef7260bc34be..744f67c3c81f4 100644 --- a/Detectors/FOCAL/simulation/data/simcuts.dat +++ b/Detectors/FOCAL/simulation/data/simcuts.dat @@ -6,11 +6,11 @@ FOC 0 5.e-5 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 -1. -1 -1 -1 -1 1 -1 3 -1 -1 -1 -1 -1 * Si sensor FOC 1 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* Si pixel +FOC 2 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 * G10 plate -FOC 2 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 -1. -1 -1 -1 -1 1 -1 3 -1 -1 -1 -1 -1 +FOC 3 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 -1. -1 -1 -1 -1 1 -1 3 -1 -1 -1 -1 -1 * Alloy -FOC 5 5.e-5 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 -1. -1 -1 -1 -1 1 -1 3 -1 -1 -1 -1 -1 +FOC 6 5.e-5 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 -1. -1 -1 -1 -1 1 -1 3 -1 -1 -1 -1 -1 * Aluminium -FOC 10 5.e-5 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 -1. -1 -1 -1 -1 1 -1 3 -1 -1 -1 -1 -1 -* G10 plate -FOC 12 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 -1. -1 -1 -1 -1 1 -1 3 -1 -1 -1 -1 -1 +FOC 11 5.e-5 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 -1. -1 -1 -1 -1 1 -1 3 -1 -1 -1 -1 -1 diff --git a/Detectors/FOCAL/simulation/geometryFiles/geometry_Spaghetti.txt b/Detectors/FOCAL/simulation/geometryFiles/geometry_Spaghetti.txt index ff24f0bfec34a..5209204cc5eca 100644 --- a/Detectors/FOCAL/simulation/geometryFiles/geometry_Spaghetti.txt +++ b/Detectors/FOCAL/simulation/geometryFiles/geometry_Spaghetti.txt @@ -58,7 +58,7 @@ COMMAND_INSERT_PIX_AT_L9 GLOBAL_TOWER_TOL 0. Air GLOBAL_TOWER_TOLX 0.02 Air GLOBAL_TOWER_TOLY 0.8 Al - GLOBAL_FOCAL_Z 764.47 + GLOBAL_FOCAL_Z 763.5 GLOBAL_Tower_NX 2 GLOBAL_Tower_NY 11 GLOBAL_MIDDLE_TOWER_OFFSET 5 diff --git a/Detectors/FOCAL/simulation/src/Detector.cxx b/Detectors/FOCAL/simulation/src/Detector.cxx index 58db515b2e719..08df253d49f83 100644 --- a/Detectors/FOCAL/simulation/src/Detector.cxx +++ b/Detectors/FOCAL/simulation/src/Detector.cxx @@ -406,7 +406,7 @@ void Detector::addAlignableVolumes() const //____________________________________________________________________________ void Detector::addAlignableVolumesHCAL() const { - const std::string vpsector = "/cave_1/caveRB24_1/FOCAL_1/HCAL_1"; + const std::string vpsector = "/cave_1/barrel_1/FOCAL_1/HCAL_1"; const std::string snsector = "FOCAL/HCAL"; if (!gGeoManager->SetAlignableEntry(snsector.c_str(), vpsector.c_str())) { @@ -417,7 +417,7 @@ void Detector::addAlignableVolumesHCAL() const //____________________________________________________________________________ void Detector::addAlignableVolumesECAL() const { - const std::string vpsector = "/cave_1/caveRB24_1/FOCAL_1/ECAL_1"; + const std::string vpsector = "/cave_1/barrel_1/FOCAL_1/ECAL_1"; const std::string snsector = "FOCAL/ECAL"; if (!gGeoManager->SetAlignableEntry(snsector.c_str(), vpsector.c_str())) { @@ -485,8 +485,8 @@ void Detector::ConstructGeometry() } else { CreateHCALSpaghetti(); } - const float z0 = 1312.5; // center of caveRB24 mother volume - TVirtualMC::GetMC()->Gspos("FOCAL", 1, "caveRB24", 0, 0, mGeometry->getFOCALZ0() - (mGeometry->getInsertFrontPadLayers() ? 2.0 : 0.0) + (mGeometry->getInsertHCalReadoutMaterial() ? 1.5 : 0.0) - z0, 0, "ONLY"); + // const float z0 = 1312.5; // center of barrel mother volume + TVirtualMC::GetMC()->Gspos("FOCAL", 1, "barrel", 0, 30., mGeometry->getFOCALZ0() - (mGeometry->getInsertFrontPadLayers() ? 2.0 : 0.0) + (mGeometry->getInsertHCalReadoutMaterial() ? 1.5 : 0.0), 0, "ONLY"); } void Detector::CreateHCALSpaghetti() @@ -795,8 +795,8 @@ void Detector::CreateECALGeometry() // gMC->Gsvolu("EMSC1", "BOX", idtmed[3698], pars, 4);//Left towers (pixels shifted right) // gMC->Gsvolu("EMSC2", "BOX", idtmed[3698], pars, 4);//Right towers (pixels shifted left) - TVirtualMC::GetMC()->Gsvolu("EMSC1", "BOX", ID_AIR, pars, 4); // Left towers (pixels shifted right) - TVirtualMC::GetMC()->Gsvolu("EMSC2", "BOX", ID_AIR, pars, 4); // Right towers (pixels shifted left) + TVirtualMC::GetMC()->Gsvolu("EMSC1", "BOX", getMediumID(ID_AIR), pars, 4); // Left towers (pixels shifted right) + TVirtualMC::GetMC()->Gsvolu("EMSC2", "BOX", getMediumID(ID_AIR), pars, 4); // Right towers (pixels shifted left) // mSensitiveECALPad.push_back("EMSC1"); // mSensitiveECALPad.push_back("EMSC2"); mSensitive.push_back("EMSC1"); @@ -816,7 +816,7 @@ void Detector::CreateECALGeometry() if (icomp->material() == "PureW") { // TVirtualMC::GetMC()->Gsvolu("EW1", "BOX", idtmed[3599], pars, 4); - TVirtualMC::GetMC()->Gsvolu("EW1", "BOX", ID_TUNGSTEN, pars, 4); + TVirtualMC::GetMC()->Gsvolu("EW1", "BOX", getMediumID(ID_TUNGSTEN), pars, 4); // mSensitiveECALPad.push_back("EW1"); mSensitive.push_back("EW1"); gGeoManager->GetVolume("EW1")->SetLineColor(kBlue); @@ -827,7 +827,7 @@ void Detector::CreateECALGeometry() } if (icomp->material() == "Alloy") { // TVirtualMC::GetMC()->Gsvolu("EW1", "BOX", idtmed[3604], pars, 4); - TVirtualMC::GetMC()->Gsvolu("EW1", "BOX", ID_ALLOY, pars, 4); + TVirtualMC::GetMC()->Gsvolu("EW1", "BOX", getMediumID(ID_ALLOY), pars, 4); // mSensitiveECALPad.push_back("EW1"); mSensitive.push_back("EW1"); TVirtualMC::GetMC()->Gspos("EW1", icomp->id() + 1, "EMSC1", @@ -838,7 +838,7 @@ void Detector::CreateECALGeometry() if (icomp->material() == "G10") { // TVirtualMC::GetMC()->Gsvolu("G10RO1", "BOX", idtmed[3601], pars, 4); - TVirtualMC::GetMC()->Gsvolu("G10RO1", "BOX", ID_G10, pars, 4); + TVirtualMC::GetMC()->Gsvolu("G10RO1", "BOX", getMediumID(ID_G10), pars, 4); // mSensitiveECALPad.push_back("G10RO1"); mSensitive.push_back("G10RO1"); gGeoManager->GetVolume("G10RO1")->SetLineColor(kGreen); @@ -850,7 +850,7 @@ void Detector::CreateECALGeometry() if (icomp->material() == "Cu") { // TVirtualMC::GetMC()->Gsvolu("EWCU", "BOX", idtmed[3602], pars, 4); - TVirtualMC::GetMC()->Gsvolu("EWCU", "BOX", ID_COPPER, pars, 4); + TVirtualMC::GetMC()->Gsvolu("EWCU", "BOX", getMediumID(ID_COPPER), pars, 4); // mSensitiveECALPad.push_back("EWCU"); mSensitive.push_back("EWCU"); gGeoManager->GetVolume("EWCU")->SetLineColor(kViolet); @@ -862,7 +862,7 @@ void Detector::CreateECALGeometry() if (icomp->material() == "Air") { // TVirtualMC::GetMC()->Gsvolu("EWAIR1", "BOX", idtmed[3698], pars, 4); - TVirtualMC::GetMC()->Gsvolu("EWAIR1", "BOX", ID_AIR, pars, 4); + TVirtualMC::GetMC()->Gsvolu("EWAIR1", "BOX", getMediumID(ID_AIR), pars, 4); // mSensitiveECALPad.push_back("EWAIR1"); mSensitive.push_back("EWAIR1"); gGeoManager->GetVolume("EWAIR1")->SetLineColor(kGray); @@ -874,7 +874,7 @@ void Detector::CreateECALGeometry() if (icomp->material() == "Ceramic") { // TVirtualMC::GetMC()->Gsvolu("EWAIR1", "BOX", idtmed[3607], pars, 4); - TVirtualMC::GetMC()->Gsvolu("EWAIR1", "BOX", ID_CERAMIC, pars, 4); + TVirtualMC::GetMC()->Gsvolu("EWAIR1", "BOX", getMediumID(ID_CERAMIC), pars, 4); // mSensitiveECALPad.push_back("EWAIR1"); mSensitive.push_back("EWAIR1"); TVirtualMC::GetMC()->Gspos("EWAIR1", icomp->id() + 1, "EMSC1", @@ -885,7 +885,7 @@ void Detector::CreateECALGeometry() if (icomp->material() == "SiPad") { // TVirtualMC::GetMC()->Gsvolu("EWSIPAD1", "BOX", idtmed[3600], pars, 4); - TVirtualMC::GetMC()->Gsvolu("EWSIPAD1", "BOX", ID_SIPAD, pars, 4); + TVirtualMC::GetMC()->Gsvolu("EWSIPAD1", "BOX", getMediumID(ID_SIPAD), pars, 4); // mSensitiveECALPad.push_back("EWSIPAD1"); mSensitive.push_back("EWSIPAD1"); gGeoManager->GetVolume("EWSIPAD1")->SetLineColor(kOrange - 7); @@ -900,7 +900,7 @@ void Detector::CreateECALGeometry() // Pixels (sensitive layer) if (icomp->material() == "SiPix") { // TVirtualMC::GetMC()->Gsvolu("EWSIPIX1", "BOX", idtmed[3600], pars, 4); - TVirtualMC::GetMC()->Gsvolu("EWSIPIX1", "BOX", ID_SIPIX, pars, 4); + TVirtualMC::GetMC()->Gsvolu("EWSIPIX1", "BOX", getMediumID(ID_SIPIX), pars, 4); // mSensitiveECALPix.push_back("EWSIPIX1"); mSensitive.push_back("EWSIPIX1"); gGeoManager->GetVolume("EWSIPIX1")->SetLineColor(kPink); @@ -915,7 +915,7 @@ void Detector::CreateECALGeometry() // Passive silicon if (icomp->material() == "Si") { // TVirtualMC::GetMC()->Gsvolu("EWSI1", "BOX", idtmed[3610], pars, 4); - TVirtualMC::GetMC()->Gsvolu("EWSI1", "BOX", ID_SIINSENS, pars, 4); + TVirtualMC::GetMC()->Gsvolu("EWSI1", "BOX", getMediumID(ID_SIINSENS), pars, 4); // mSensitiveECALPix.push_back("EWSI1"); mSensitive.push_back("EWSI1"); gGeoManager->GetVolume("EWSI1")->SetLineColor(kPink); @@ -954,7 +954,7 @@ void Detector::CreateECALGeometry() fcal_pars[3] = 0.; // TVirtualMC::GetMC()->Gsvolu("ECAL", "BOX", idtmed[3698], fcal_pars, 4); - TVirtualMC::GetMC()->Gsvolu("ECAL", "BOX", ID_AIR, fcal_pars, 4); + TVirtualMC::GetMC()->Gsvolu("ECAL", "BOX", getMediumID(ID_AIR), fcal_pars, 4); // mSensitiveECALPad.push_back("ECAL"); mSensitive.push_back("ECAL"); @@ -964,7 +964,9 @@ void Detector::CreateECALGeometry() TGeoVolume* volumeSiPad = new TGeoVolume("volSiPad", siPadBox, gGeoManager->GetMedium(getMediumID(ID_SIPAD))); volumeSiPad->SetLineColor(kOrange + 7); // mSensitiveECALPad.push_back(volumeSiPad->GetName()); - mSensitive.push_back(volumeSiPad->GetName()); + if (geom->getInsertFrontPadLayers()) { + mSensitive.push_back(volumeSiPad->GetName()); + } double xp, yp, zp; int itowerx, itowery; diff --git a/Detectors/GLOQC/src/MatchITSTPCQC.cxx b/Detectors/GLOQC/src/MatchITSTPCQC.cxx index f0345175b9a59..6e14f9eb16c8b 100644 --- a/Detectors/GLOQC/src/MatchITSTPCQC.cxx +++ b/Detectors/GLOQC/src/MatchITSTPCQC.cxx @@ -477,6 +477,21 @@ void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) if (mTimestamp == -1 && mDoK0QC) { // we have not yet initialized the SVertexer params; let's do it ctx.inputs().get("SVParam"); + const auto& svparam = o2::vertexing::SVertexerParams::Instance(); + mFitterV0.setUseAbsDCA(svparam.useAbsDCA); + mFitterV0.setMaxR(svparam.maxRIni); + mFitterV0.setMinParamChange(svparam.minParamChange); + mFitterV0.setMinRelChi2Change(svparam.minRelChi2Change); + mFitterV0.setMaxDZIni(svparam.maxDZIni); + mFitterV0.setMaxDXYIni(svparam.maxDXYIni); + mFitterV0.setMaxChi2(svparam.maxChi2); + mFitterV0.setMatCorrType(o2::base::Propagator::MatCorrType(svparam.matCorr)); + mFitterV0.setUsePropagator(svparam.usePropagator); + mFitterV0.setRefitWithMatCorr(svparam.refitWithMatCorr); + mFitterV0.setMaxStep(svparam.maxStep); + mFitterV0.setMaxSnp(svparam.maxSnp); + mFitterV0.setMinXSeed(svparam.minXSeed); + mTimestamp = ctx.services().get().creation; auto grplhcif = o2::base::GRPGeomHelper::instance().getGRPLHCIF(); if (grplhcif->getBeamZ(0) != 1 || grplhcif->getBeamZ(1) != 1) { @@ -962,6 +977,7 @@ void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) if (mDoK0QC && mRecoCont.getPrimaryVertices().size() > 0) { // now doing K0S + mFitterV0.setBz(mBz); const auto pvertices = mRecoCont.getPrimaryVertices(); LOG(info) << "****** Number of PVs = " << pvertices.size(); diff --git a/Detectors/GRP/calibration/include/GRPCalibration/GRPDCSDPsProcessor.h b/Detectors/GRP/calibration/include/GRPCalibration/GRPDCSDPsProcessor.h index 98075051356e7..5043f36ef1433 100644 --- a/Detectors/GRP/calibration/include/GRPCalibration/GRPDCSDPsProcessor.h +++ b/Detectors/GRP/calibration/include/GRPCalibration/GRPDCSDPsProcessor.h @@ -16,6 +16,7 @@ #include #include #include "Framework/Logger.h" +#include "Framework/O2LongInt.h" #include "DetectorsDCS/DataPointCompositeObject.h" #include "DetectorsDCS/DataPointIdentifier.h" #include "DetectorsDCS/DataPointValue.h" @@ -37,11 +38,11 @@ using DPID = o2::dcs::DataPointIdentifier; using DPVAL = o2::dcs::DataPointValue; using DPCOM = o2::dcs::DataPointCompositeObject; -inline unsigned long llu2lu(std::uint64_t v) { return (unsigned long)v; } +inline O2LongUInt llu2lu(std::uint64_t v) { return (O2LongUInt)v; } struct GRPEnvVariables { - std::unordered_map>> mEnvVars; + std::unordered_map>> mEnvVars; size_t totalEntries() const { size_t s = 0; @@ -60,7 +61,7 @@ struct GRPEnvVariables { } } - ClassDefNV(GRPEnvVariables, 1); + ClassDefNV(GRPEnvVariables, 2); }; struct MagFieldHelper { @@ -122,7 +123,7 @@ struct MagFieldHelper { struct GRPCollimators { - std::unordered_map>> mCollimators; + std::unordered_map>> mCollimators; size_t totalEntries() const { size_t s = 0; @@ -141,7 +142,7 @@ struct GRPCollimators { } } - ClassDefNV(GRPCollimators, 1); + ClassDefNV(GRPCollimators, 2); }; struct GRPLHCInfo { @@ -191,19 +192,19 @@ struct GRPLHCInfo { static constexpr std::string_view lhcStringAliases[NLHCStringAliases] = {"ALI_Lumi_Source_Name", "BEAM_MODE", "MACHINE_MODE"}; static constexpr int nAliasesLHC = (int)NCollimatorAliases + (int)NBeamAliases + (int)NBkgAliases + (int)NBPTXAliases + (int)NBPTXPhaseAliases + (int)NBPTXPhaseRMSAliases + (int)NBPTXPhaseShiftAliases + (int)NLumiAliases + (int)NLHCStringAliases; - std::array>, 2> mIntensityBeam; - std::array>, 3> mBackground; - std::vector> mInstLumi; - std::vector> mBPTXdeltaT; - std::vector> mBPTXdeltaTRMS; - std::array>, 2> mBPTXPhase; - std::array>, 2> mBPTXPhaseRMS; - std::array>, 2> mBPTXPhaseShift; - std::pair mLumiSource; // only one value per object: when there is a change, a new object is stored - std::pair mMachineMode; // only one value per object: when there is a change, a new object is stored - std::pair mBeamMode; // only one value per object: when there is a change, a new object is stored - - void resetAndKeepLastVector(std::vector>& vect) + std::array>, 2> mIntensityBeam; + std::array>, 3> mBackground; + std::vector> mInstLumi; + std::vector> mBPTXdeltaT; + std::vector> mBPTXdeltaTRMS; + std::array>, 2> mBPTXPhase; + std::array>, 2> mBPTXPhaseRMS; + std::array>, 2> mBPTXPhaseShift; + std::pair mLumiSource; // only one value per object: when there is a change, a new object is stored + std::pair mMachineMode; // only one value per object: when there is a change, a new object is stored + std::pair mBeamMode; // only one value per object: when there is a change, a new object is stored + + void resetAndKeepLastVector(std::vector>& vect) { // always check that the size is > 0 (--> begin != end) for all vectors if (vect.begin() != vect.end()) { @@ -291,16 +292,16 @@ class GRPDCSDPsProcessor void init(const std::vector& pids); int process(const gsl::span dps); int processDP(const DPCOM& dpcom); - uint64_t processFlags(uint64_t flag, const char* pid) { return 0; } // for now it is not really implemented + O2LongUInt processFlags(O2LongUInt flag, const char* pid) { return 0; } // for now it is not really implemented bool processCollimators(const DPCOM& dpcom); bool processEnvVar(const DPCOM& dpcom); - bool processPairD(const DPCOM& dpcom, const std::string& alias, std::unordered_map>>& mapToUpdate); - bool processPairS(const DPCOM& dpcom, const std::string& alias, std::pair& p, bool& flag); - bool compareToLatest(std::pair& p, double val); + bool processPairD(const DPCOM& dpcom, const std::string& alias, std::unordered_map>>& mapToUpdate); + bool processPairS(const DPCOM& dpcom, const std::string& alias, std::pair& p, bool& flag); + bool compareToLatest(std::pair& p, double val); bool processLHCIFDPs(const DPCOM& dpcom); void resetAndKeepLastLHCIFDPs() { mLHCInfo.resetAndKeepLast(); } - void resetAndKeepLast(std::unordered_map>>& mapToReset) + void resetAndKeepLast(std::unordered_map>>& mapToReset) { // keep only the latest measurement for (auto& el : mapToReset) { @@ -366,8 +367,8 @@ class GRPDCSDPsProcessor void useVerboseMode() { mVerbose = true; } void clearVectors() { mClearVectors = true; } - void printVectorInfo(const std::vector>& vect, bool afterUpdate); - void updateVector(const DPID& dpid, std::vector>& vect, std::string alias, uint64_t timestamp, double val); + void printVectorInfo(const std::vector>& vect, bool afterUpdate); + void updateVector(const DPID& dpid, std::vector>& vect, std::string alias, O2LongUInt timestamp, double val); private: std::unordered_map mPids; // contains all PIDs for the processor, the bool diff --git a/Detectors/GRP/calibration/src/GRPDCSDPsProcessor.cxx b/Detectors/GRP/calibration/src/GRPDCSDPsProcessor.cxx index f3f96794095d7..c8fa7c2bff38b 100644 --- a/Detectors/GRP/calibration/src/GRPDCSDPsProcessor.cxx +++ b/Detectors/GRP/calibration/src/GRPDCSDPsProcessor.cxx @@ -185,7 +185,7 @@ bool GRPDCSDPsProcessor::processEnvVar(const DPCOM& dpcom) } //______________________________________________________________________ -bool GRPDCSDPsProcessor::processPairD(const DPCOM& dpcom, const std::string& alias, std::unordered_map>>& mapToUpdate) +bool GRPDCSDPsProcessor::processPairD(const DPCOM& dpcom, const std::string& alias, std::unordered_map>>& mapToUpdate) { // function to process Data Points that is stored in a pair @@ -207,7 +207,7 @@ bool GRPDCSDPsProcessor::processPairD(const DPCOM& dpcom, const std::string& ali } //______________________________________________________________________ -bool GRPDCSDPsProcessor::processPairS(const DPCOM& dpcom, const std::string& alias, std::pair& p, bool& flag) +bool GRPDCSDPsProcessor::processPairS(const DPCOM& dpcom, const std::string& alias, std::pair& p, bool& flag) { // function to process string Data Points that is stored in a pair @@ -237,7 +237,7 @@ bool GRPDCSDPsProcessor::processPairS(const DPCOM& dpcom, const std::string& ali //______________________________________________________________________ -bool GRPDCSDPsProcessor::compareToLatest(std::pair& p, double val) +bool GRPDCSDPsProcessor::compareToLatest(std::pair& p, double val) { // check if the content of the pair should be updated @@ -408,7 +408,7 @@ void GRPDCSDPsProcessor::updateCollimatorsCCDB() //______________________________________________________________________ -void GRPDCSDPsProcessor::printVectorInfo(const std::vector>& vect, bool afterUpdate) +void GRPDCSDPsProcessor::printVectorInfo(const std::vector>& vect, bool afterUpdate) { std::string stage = afterUpdate ? "after update" : "before update"; @@ -422,7 +422,7 @@ void GRPDCSDPsProcessor::printVectorInfo(const std::vector>& vect, std::string alias, uint64_t timestamp, double val) +void GRPDCSDPsProcessor::updateVector(const DPID& dpid, std::vector>& vect, std::string alias, O2LongUInt timestamp, double val) { printVectorInfo(vect, 0); bool updateFlag = false; diff --git a/Detectors/GlobalTracking/include/GlobalTracking/MatchTOF.h b/Detectors/GlobalTracking/include/GlobalTracking/MatchTOF.h index 957ae07544cf4..b66e5b143a898 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/MatchTOF.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/MatchTOF.h @@ -220,6 +220,8 @@ class MatchTOF void BestMatchesHP(std::vector& matchedTracksPairs, std::vector* matchedTracks, std::vector* matchedTracksIndex, int* matchedClustersIndex, const gsl::span& FITRecPoints, const std::vector& TOFClusWork, std::vector& CalibInfoTOF, unsigned long Timestamp, bool MCTruthON, const o2::dataformats::MCTruthContainer* TOFClusLabels, const std::vector* TracksLblWork, std::vector* OutTOFLabels); bool propagateToRefX(o2::track::TrackParCov& trc, float xRef /*in cm*/, float stepInCm /*in cm*/, o2::track::TrackLTIntegral& intLT); bool propagateToRefXWithoutCov(const o2::track::TrackParCov& trc, float xRef /*in cm*/, float stepInCm /*in cm*/, float bz); + bool propagateToRefXWithoutCov(const o2::track::TrackParCov& trc, float xRef /*in cm*/, float stepInCm /*in cm*/, float bz, float pos[3]); + void updateTL(o2::track::TrackLTIntegral& intLT, float deltal); void updateTimeDependentParams(); diff --git a/Detectors/GlobalTracking/src/MatchTOF.cxx b/Detectors/GlobalTracking/src/MatchTOF.cxx index 8a90251353123..89d6f8347373d 100644 --- a/Detectors/GlobalTracking/src/MatchTOF.cxx +++ b/Detectors/GlobalTracking/src/MatchTOF.cxx @@ -932,6 +932,44 @@ void MatchTOF::doMatching(int sec) } //Printf("nStepsInsideSameStrip[nStripsCrossedInPropagation-1] = %d", nStepsInsideSameStrip[nStripsCrossedInPropagation - 1]); if (nStepsInsideSameStrip[nStripsCrossedInPropagation - 1] == 0) { + // fine propagation inside the strip -> 1 mm step + trkLTInt[nStripsCrossedInPropagation - 1] = intLT; + // temporary variables since propagation can fail + int detIdTemp2[5] = {0, 0, 0, 0, 0}; + float deltaPosTemp2[3] = {deltaPosTemp[0], deltaPosTemp[1], deltaPosTemp[2]}; + int nstep = 0; + const int maxnstep = 50; + float xStart = trefTrk.getX(); + float xStop = xStart; + trefTrk.getXYZGlo(pos); + for (int ii = 0; ii < 3; ii++) { // we need to change the type... + posFloat[ii] = pos[ii]; + } + while (deltaPosTemp2[1] < -0.05 && detIdTemp2[2] != -1 && nstep < maxnstep) { // continuing propagation if dy is negative and we are still inside the strip volume + nstep++; + xStop += 0.1; + propagateToRefXWithoutCov(trefTrk, xStop, 0.1, mBz, posFloat); + + Geo::getPadDxDyDz(posFloat, detIdTemp2, deltaPosTemp2, sec); + if (detIdTemp2[2] != -1) { // if propation was succesful -> update params + float dx = deltaPosTemp2[0] - deltaPosTemp[0]; + float dy = deltaPosTemp2[1] - deltaPosTemp[1]; + float dz = deltaPosTemp2[2] - deltaPosTemp[2]; + updateTL(trkLTInt[nStripsCrossedInPropagation - 1], sqrt(dx * dx + dy * dy + dz * dz)); + detIdTemp[0] = detIdTemp2[0]; + detIdTemp[1] = detIdTemp2[1]; + detIdTemp[2] = detIdTemp2[2]; + detIdTemp[3] = detIdTemp2[3]; + detIdTemp[4] = detIdTemp2[4]; + deltaPosTemp[0] = deltaPosTemp2[0]; + deltaPosTemp[1] = deltaPosTemp2[1]; + deltaPosTemp[2] = deltaPosTemp2[2]; + } + } + + // adjust accordingly to DeltaY + updateTL(trkLTInt[nStripsCrossedInPropagation - 1], -deltaPosTemp[1]); + detId[nStripsCrossedInPropagation - 1][0] = detIdTemp[0]; detId[nStripsCrossedInPropagation - 1][1] = detIdTemp[1]; detId[nStripsCrossedInPropagation - 1][2] = detIdTemp[2]; @@ -940,16 +978,18 @@ void MatchTOF::doMatching(int sec) deltaPos[nStripsCrossedInPropagation - 1][0] = deltaPosTemp[0]; deltaPos[nStripsCrossedInPropagation - 1][1] = deltaPosTemp[1]; deltaPos[nStripsCrossedInPropagation - 1][2] = deltaPosTemp[2]; - trkLTInt[nStripsCrossedInPropagation - 1] = intLT; // Printf("intLT (after matching to strip %d): length = %f, time (Pion) = %f", nStripsCrossedInPropagation - 1, trkLTInt[nStripsCrossedInPropagation - 1].getL(), trkLTInt[nStripsCrossedInPropagation - 1].getTOF(o2::track::PID::Pion)); nStepsInsideSameStrip[nStripsCrossedInPropagation - 1]++; - } else { // a further propagation step in the same strip -> update info (we sum up on all matching with strip - we will divide for the number of steps a bit below) + } + /* // obsolete + else { // a further propagation step in the same strip -> update info (we sum up on all matching with strip - we will divide for the number of steps a bit below) // N.B. the integrated length and time are taken (at least for now) from the first time we crossed the strip, so here we do nothing with those deltaPos[nStripsCrossedInPropagation - 1][0] += deltaPosTemp[0] + (detIdTemp[4] - detId[nStripsCrossedInPropagation - 1][4]) * Geo::XPAD; // residual in x deltaPos[nStripsCrossedInPropagation - 1][1] += deltaPosTemp[1]; // residual in y deltaPos[nStripsCrossedInPropagation - 1][2] += deltaPosTemp[2] + (detIdTemp[3] - detId[nStripsCrossedInPropagation - 1][3]) * Geo::ZPAD; // residual in z nStepsInsideSameStrip[nStripsCrossedInPropagation - 1]++; } + */ } for (Int_t imatch = 0; imatch < nStripsCrossedInPropagation; imatch++) { @@ -1048,6 +1088,7 @@ void MatchTOF::doMatching(int sec) LOG(debug) << "Propagated Track [" << itrk << "]: detId[" << iPropagation << "] = " << detId[iPropagation][0] << ", " << detId[iPropagation][1] << ", " << detId[iPropagation][2] << ", " << detId[iPropagation][3] << ", " << detId[iPropagation][4]; float resX = deltaPos[iPropagation][0] - (indices[4] - detId[iPropagation][4]) * Geo::XPAD + posCorr[0]; // readjusting the residuals due to the fact that the propagation fell in a pad that was not exactly the one of the cluster float resZ = deltaPos[iPropagation][2] - (indices[3] - detId[iPropagation][3]) * Geo::ZPAD + posCorr[2]; // readjusting the residuals due to the fact that the propagation fell in a pad that was not exactly the one of the cluster + float resY = deltaPos[iPropagation][1]; float resXor = resX; float resZor = resZ; float res = TMath::Sqrt(resX * resX + resZ * resZ); @@ -1085,7 +1126,7 @@ void MatchTOF::doMatching(int sec) foundCluster = true; // set event indexes (to be checked) int eventIndexTOFCluster = mTOFClusSectIndexCache[indices[0]][itof]; - mMatchedTracksPairsSec[sec].emplace_back(cacheTrk[itrk], eventIndexTOFCluster, mTOFClusWork[cacheTOF[itof]].getTime(), chi2, trkLTInt[iPropagation], mTrackGid[sec][type][cacheTrk[itrk]], type, (trefTOF.getTime() - (minTrkTime + maxTrkTime - 100E3) * 0.5) * 1E-6, trefTOF.getZ(), resXor, resZor); // subracting 100 ns to max track which was artificially added + mMatchedTracksPairsSec[sec].emplace_back(cacheTrk[itrk], eventIndexTOFCluster, mTOFClusWork[cacheTOF[itof]].getTime(), chi2, trkLTInt[iPropagation], mTrackGid[sec][type][cacheTrk[itrk]], type, (trefTOF.getTime() - (minTrkTime + maxTrkTime - 100E3) * 0.5) * 1E-6, trefTOF.getZ(), resXor, resZor, resY); // subracting 100 ns to max track which was artificially added mMatchedTracksPairsSec[sec][mMatchedTracksPairsSec[sec].size() - 1].setPt(pt); mMatchedTracksPairsSec[sec][mMatchedTracksPairsSec[sec].size() - 1].setResX(sqrt(1. / errXinv2)); mMatchedTracksPairsSec[sec][mMatchedTracksPairsSec[sec].size() - 1].setResZ(sqrt(1. / errZinv2)); @@ -1290,6 +1331,46 @@ void MatchTOF::doMatchingForTPC(int sec) //Printf("nStepsInsideSameStrip[nStripsCrossedInPropagation-1] = %d", nStepsInsideSameStrip[nStripsCrossedInPropagation - 1]); if (nStepsInsideSameStrip[ibc][nStripsCrossedInPropagation[ibc] - 1] == 0) { + trkLTInt[ibc][nStripsCrossedInPropagation[ibc] - 1] = intLT; + // temporary variables since propagation can fail + int detIdTemp2[5] = {0, 0, 0, 0, 0}; + float deltaPosTemp2[3] = {deltaPosTemp[0], deltaPosTemp[1], deltaPosTemp[2]}; + int nstep = 0; + const int maxnstep = 50; + float xStart = trefTrk.getX(); + float xStop = xStart; + trefTrk.getXYZGlo(pos); + for (int ii = 0; ii < 3; ii++) { // we need to change the type... + posFloat[ii] = pos[ii]; + } + + while (deltaPosTemp2[1] < -0.05 && detIdTemp2[2] != -1 && nstep < maxnstep) { // continuing propagation if dy is negative and we are still inside the strip volume + nstep++; + xStop += 0.1; + propagateToRefXWithoutCov(trefTrk, xStop, 0.1, mBz, posFloat); + + posFloat[2] += ZshiftCurrent; + + Geo::getPadDxDyDz(posFloat, detIdTemp2, deltaPosTemp2, sec); + if (detIdTemp2[2] != -1) { // if propation was succesful -> update params + float dx = deltaPosTemp2[0] - deltaPosTemp[0]; + float dy = deltaPosTemp2[1] - deltaPosTemp[1]; + float dz = deltaPosTemp2[2] - deltaPosTemp[2]; + updateTL(trkLTInt[ibc][nStripsCrossedInPropagation[ibc] - 1], sqrt(dx * dx + dy * dy + dz * dz)); + detIdTemp[0] = detIdTemp2[0]; + detIdTemp[1] = detIdTemp2[1]; + detIdTemp[2] = detIdTemp2[2]; + detIdTemp[3] = detIdTemp2[3]; + detIdTemp[4] = detIdTemp2[4]; + deltaPosTemp[0] = deltaPosTemp2[0]; + deltaPosTemp[1] = deltaPosTemp2[1]; + deltaPosTemp[2] = deltaPosTemp2[2]; + } + } + + // adjust accordingly to DeltaY + updateTL(trkLTInt[ibc][nStripsCrossedInPropagation[ibc] - 1], -deltaPosTemp[1]); + detId[ibc][nStripsCrossedInPropagation[ibc] - 1][0] = detIdTemp[0]; detId[ibc][nStripsCrossedInPropagation[ibc] - 1][1] = detIdTemp[1]; detId[ibc][nStripsCrossedInPropagation[ibc] - 1][2] = detIdTemp[2]; @@ -1299,17 +1380,19 @@ void MatchTOF::doMatchingForTPC(int sec) deltaPos[ibc][nStripsCrossedInPropagation[ibc] - 1][1] = deltaPosTemp[1]; deltaPos[ibc][nStripsCrossedInPropagation[ibc] - 1][2] = deltaPosTemp[2]; - trkLTInt[ibc][nStripsCrossedInPropagation[ibc] - 1] = intLT; Zshift[ibc][nStripsCrossedInPropagation[ibc] - 1] = ZshiftCurrent; // Printf("intLT (after matching to strip %d): length = %f, time (Pion) = %f", nStripsCrossedInPropagation - 1, trkLTInt[nStripsCrossedInPropagation - 1].getL(), trkLTInt[nStripsCrossedInPropagation - 1].getTOF(o2::track::PID::Pion)); nStepsInsideSameStrip[ibc][nStripsCrossedInPropagation[ibc] - 1]++; - } else { // a further propagation step in the same strip -> update info (we sum up on all matching with strip - we will divide for the number of steps a bit below) - // N.B. the integrated length and time are taken (at least for now) from the first time we crossed the strip, so here we do nothing with those - deltaPos[ibc][nStripsCrossedInPropagation[ibc] - 1][0] += deltaPosTemp[0] + (detIdTemp[4] - detId[ibc][nStripsCrossedInPropagation[ibc] - 1][4]) * Geo::XPAD; // residual in x - deltaPos[ibc][nStripsCrossedInPropagation[ibc] - 1][1] += deltaPosTemp[1]; // residual in y - deltaPos[ibc][nStripsCrossedInPropagation[ibc] - 1][2] += deltaPosTemp[2] + (detIdTemp[3] - detId[ibc][nStripsCrossedInPropagation[ibc] - 1][3]) * Geo::ZPAD; // residual in z - nStepsInsideSameStrip[ibc][nStripsCrossedInPropagation[ibc] - 1]++; } + /* // obsolete + else { // a further propagation step in the same strip -> update info (we sum up on all matching with strip - we will divide for the number of steps a bit below) + // N.B. the integrated length and time are taken (at least for now) from the first time we crossed the strip, so here we do nothing with those + deltaPos[ibc][nStripsCrossedInPropagation[ibc] - 1][0] += deltaPosTemp[0] + (detIdTemp[4] - detId[ibc][nStripsCrossedInPropagation[ibc] - 1][4]) * Geo::XPAD; // residual in x + deltaPos[ibc][nStripsCrossedInPropagation[ibc] - 1][1] += deltaPosTemp[1]; // residual in y + deltaPos[ibc][nStripsCrossedInPropagation[ibc] - 1][2] += deltaPosTemp[2] + (detIdTemp[3] - detId[ibc][nStripsCrossedInPropagation[ibc] - 1][3]) * Geo::ZPAD; // residual in z + nStepsInsideSameStrip[ibc][nStripsCrossedInPropagation[ibc] - 1]++; + } + */ } } for (int ibc = 0; ibc < BCcand.size(); ibc++) { @@ -1436,6 +1519,7 @@ void MatchTOF::doMatchingForTPC(int sec) LOG(debug) << "Propagated Track [" << itrk << "]: detId[" << iPropagation << "] = " << detId[ibc][iPropagation][0] << ", " << detId[ibc][iPropagation][1] << ", " << detId[ibc][iPropagation][2] << ", " << detId[ibc][iPropagation][3] << ", " << detId[ibc][iPropagation][4]; float resX = deltaPos[ibc][iPropagation][0] - (indices[4] - detId[ibc][iPropagation][4]) * Geo::XPAD + posCorr[0]; // readjusting the residuals due to the fact that the propagation fell in a pad that was not exactly the one of the cluster float resZ = deltaPos[ibc][iPropagation][2] - (indices[3] - detId[ibc][iPropagation][3]) * Geo::ZPAD + posCorr[2]; // readjusting the residuals due to the fact that the propagation fell in a pad that was not exactly the one of the cluster + float resY = deltaPos[ibc][iPropagation][1]; if (BCcand[ibc] > bcClus) { resZ += (BCcand[ibc] - bcClus) * vdriftInBC * side; // add bc correction } else { @@ -1480,7 +1564,7 @@ void MatchTOF::doMatchingForTPC(int sec) // set event indexes (to be checked) int eventIndexTOFCluster = mTOFClusSectIndexCache[indices[0]][itof]; - mMatchedTracksPairsSec[sec].emplace_back(cacheTrk[itrk], eventIndexTOFCluster, mTOFClusWork[cacheTOF[itof]].getTime(), chi2, trkLTInt[ibc][iPropagation], mTrackGid[sec][trkType::UNCONS][cacheTrk[itrk]], trkType::UNCONS, trefTOF.getTime() * 1E-6 - tpctime, trefTOF.getZ(), resXor, resZor); // TODO: check if this is correct! + mMatchedTracksPairsSec[sec].emplace_back(cacheTrk[itrk], eventIndexTOFCluster, mTOFClusWork[cacheTOF[itof]].getTime(), chi2, trkLTInt[ibc][iPropagation], mTrackGid[sec][trkType::UNCONS][cacheTrk[itrk]], trkType::UNCONS, trefTOF.getTime() * 1E-6 - tpctime, trefTOF.getZ(), resXor, resZor, resY); // TODO: check if this is correct! mMatchedTracksPairsSec[sec][mMatchedTracksPairsSec[sec].size() - 1].setPt(pt); mMatchedTracksPairsSec[sec][mMatchedTracksPairsSec[sec].size() - 1].setResX(sqrt(1. / errXinv2)); mMatchedTracksPairsSec[sec][mMatchedTracksPairsSec[sec].size() - 1].setResZ(sqrt(1. / errZinv2)); @@ -1599,6 +1683,10 @@ void MatchTOF::BestMatches(std::vector& match if (std::abs(timeNew - timeOld) < 200) { // update time information averaging the two (the second one corrected for the difference in the track length) prevMatching.setSignal((timeNew + timeOld) * 0.5); + float geanttime = (TOFClusWork[matchingPair.getTOFClIndex()].getTgeant() + TOFClusWork[prevMatching.getTOFClIndex()].getTgeant() - deltaT * 1E-3) * 0.5; + double t0 = (TOFClusWork[matchingPair.getTOFClIndex()].getT0true() + TOFClusWork[prevMatching.getTOFClIndex()].getT0true()) * 0.5; + prevMatching.setTgeant(geanttime); + prevMatching.setT0true(t0); prevMatching.setChi2(0); // flag such cases with chi2 equal to zero matchedClustersIndex[matchingPair.getTOFClIndex()] = matchedTracksIndex[trkType][itrk]; // flag also the second cluster as already used } @@ -1610,6 +1698,9 @@ void MatchTOF::BestMatches(std::vector& match matchedTracksIndex[trkType][itrk] = matchedTracks[trkTypeSplitted].size(); // index of the MatchInfoTOF correspoding to this track matchedClustersIndex[matchingPair.getTOFClIndex()] = matchedTracksIndex[trkType][itrk]; // index of the track that was matched to this cluster + matchingPair.setTgeant(TOFClusWork[matchingPair.getTOFClIndex()].getTgeant()); + matchingPair.setT0true(TOFClusWork[matchingPair.getTOFClIndex()].getT0true()); + // let's check if cluster has multiple-hits (noferini) if (TOFClusWork[matchingPair.getTOFClIndex()].getNumOfContributingChannels() > 1) { const auto& tofcl = TOFClusWork[matchingPair.getTOFClIndex()]; @@ -1857,7 +1948,6 @@ bool MatchTOF::propagateToRefX(o2::track::TrackParCov& trc, float xRef, float st //Printf("propagateToRefX: snp of teh track is %f (--> %f grad)", trc.getSnp(), TMath::ASin(trc.getSnp())*TMath::RadToDeg()); return refReached && std::abs(trc.getSnp()) < 0.95; // Here we need to put MAXSNP } - //______________________________________________ bool MatchTOF::propagateToRefXWithoutCov(const o2::track::TrackParCov& trc, float xRef, float stepInCm, float bzField) { @@ -1897,6 +1987,61 @@ bool MatchTOF::propagateToRefXWithoutCov(const o2::track::TrackParCov& trc, floa return refReached && std::abs(trcNoCov.getSnp()) < 0.95 && std::abs(trcNoCov.getZ()) < Geo::MAXHZTOF; // Here we need to put MAXSNP } +//______________________________________________ +void MatchTOF::updateTL(o2::track::TrackLTIntegral& intLT, float deltal) +{ + for (int i = 0; i < intLT.getNTOFs(); i++) { + float betainv = intLT.getTOF(i) / intLT.getL(); + intLT.setTOF(intLT.getTOF(i) + deltal * betainv, i); + } + intLT.setL(intLT.getL() + deltal); +} + +//______________________________________________ +bool MatchTOF::propagateToRefXWithoutCov(const o2::track::TrackParCov& trc, float xRef, float stepInCm, float bzField, float pos[3]) +{ + // propagate track to matching reference X without using the covariance matrix + // we create the copy of the track in a TrackPar object (no cov matrix) + o2::track::TrackPar trcNoCov(trc); + const float tanHalfSector = tan(o2::constants::math::SectorSpanRad / 2); + bool refReached = false; + float xStart = trcNoCov.getX(); + // the first propagation will be from 2m, if the track is not at least at 2m + if (xStart < 50.) { + xStart = 50.; + } + int istep = 1; + bool hasPropagated = trcNoCov.propagateParamTo(xStart + istep * stepInCm, bzField); + while (hasPropagated) { + if (trcNoCov.getX() > xRef) { + refReached = true; // we reached the 371cm reference + } + istep++; + if (fabs(trcNoCov.getY()) > trcNoCov.getX() * tanHalfSector) { // we are still in the same sector + // we need to rotate the track to go to the new sector + // Printf("propagateToRefX: changing sector"); + auto alphaNew = o2::math_utils::angle2Alpha(trcNoCov.getPhiPos()); + if (!trcNoCov.rotateParam(alphaNew) != 0) { + // Printf("propagateToRefX: failed to rotate"); + break; // failed (this line is taken from MatchTPCITS and the following comment too: RS: check effect on matching tracks to neighbouring sector) + } + } + if (refReached) { + break; + } + hasPropagated = trcNoCov.propagateParamTo(xStart + istep * stepInCm, bzField); + } + // if (std::abs(trc.getSnp()) > MAXSNP) Printf("propagateToRefX: condition on snp not ok, returning false"); + // Printf("propagateToRefX: snp of teh track is %f (--> %f grad)", trcNoCov.getSnp(), TMath::ASin(trcNoCov.getSnp())*TMath::RadToDeg()); + + o2::track::TrackParametrization::dim3_t xyz; + trcNoCov.getXYZGlo(xyz); + pos[0] = xyz[0]; + pos[1] = xyz[1]; + pos[2] = xyz[2]; + + return refReached && std::abs(trcNoCov.getSnp()) < 0.95 && TMath::Abs(trcNoCov.getZ()) < Geo::MAXHZTOF; // Here we need to put MAXSNP +} //______________________________________________ void MatchTOF::setDebugFlag(UInt_t flag, bool on) diff --git a/Detectors/GlobalTracking/src/MatchTPCITS.cxx b/Detectors/GlobalTracking/src/MatchTPCITS.cxx index eb2a2212edb30..8bf3449dd52dc 100644 --- a/Detectors/GlobalTracking/src/MatchTPCITS.cxx +++ b/Detectors/GlobalTracking/src/MatchTPCITS.cxx @@ -661,7 +661,8 @@ bool MatchTPCITS::prepareITSData() auto pattID = clus.getPatternID(); unsigned int npix; #ifdef ENABLE_UPGRADES - if ((pattID == o2::itsmft::CompCluster::InvalidPatternID) || ((withITS3) ? mIT3Dict->isGroup(pattID) : mITSDict->isGroup(pattID))) { // braces guarantee evaluation order + auto ib = o2::its3::constants::detID::isDetITS3(clus.getChipID()); + if ((pattID == o2::itsmft::CompCluster::InvalidPatternID) || ((withITS3) ? mIT3Dict->isGroup(pattID, ib) : mITSDict->isGroup(pattID))) { // braces guarantee evaluation order #else if (pattID == o2::itsmft::CompCluster::InvalidPatternID || mITSDict->isGroup(pattID)) { #endif @@ -671,7 +672,7 @@ bool MatchTPCITS::prepareITSData() } else { #ifdef ENABLE_UPGRADES if (withITS3) { - npix = mIT3Dict->getNpixels(pattID); + npix = mIT3Dict->getNpixels(pattID, ib); } else { npix = mITSDict->getNpixels(pattID); } @@ -1434,8 +1435,7 @@ void MatchTPCITS::refitWinners(pmr::vector& matche #ifdef WITH_OPENMP #pragma omp parallel for schedule(dynamic) num_threads(mNThreads) \ - reduction(+ \ - : nFailedRefit) + reduction(+ : nFailedRefit) #endif for (int ifit = 0; ifit < nToFit; ifit++) { int iTPC = tpcToFit[ifit], iITS; diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/SVStudy.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/SVStudy.h index 9c9453215c9a0..d54513cb07a60 100644 --- a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/SVStudy.h +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/SVStudy.h @@ -22,7 +22,7 @@ namespace o2::svstudy { /// create a processor spec -o2::framework::DataProcessorSpec getSVStudySpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, bool useMC); +o2::framework::DataProcessorSpec getSVStudySpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcCls, bool useMC); } // namespace o2::svstudy diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyTypes.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyTypes.h index c13ecf266fa3e..5b1217f9a6539 100644 --- a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyTypes.h +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyTypes.h @@ -86,6 +86,28 @@ struct RecTrack { ClassDefNV(RecTrack, 1); }; +struct TrackPairInfo { + RecTrack tr0; + RecTrack tr1; + uint8_t nshTPC = 0; + uint8_t nshTPCRow = 0; + + int getComb() const { return tr0.track.getSign() != tr1.track.getSign() ? 0 : (tr0.track.getSign() > 0 ? 1 : 2); } + float getDPhi() const + { + float dphi = tr0.track.getPhi() - tr1.track.getPhi(); + if (dphi < -o2::constants::math::PI) { + dphi += o2::constants::math::TwoPI; + } else if (dphi > o2::constants::math::PI) { + dphi -= o2::constants::math::TwoPI; + } + return dphi; + } + float getDTgl() const { return tr0.track.getTgl() - tr1.track.getTgl(); } + + ClassDefNV(TrackPairInfo, 1) +}; + struct TrackFamily { // set of tracks related to the same MC label MCTrackInfo mcTrackInfo{}; std::vector recTracks{}; diff --git a/Detectors/GlobalTrackingWorkflow/study/src/GlobalTrackingStudyLinkDef.h b/Detectors/GlobalTrackingWorkflow/study/src/GlobalTrackingStudyLinkDef.h index 1e8fd688ca2fa..f666132c9c1cf 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/GlobalTrackingStudyLinkDef.h +++ b/Detectors/GlobalTrackingWorkflow/study/src/GlobalTrackingStudyLinkDef.h @@ -36,5 +36,7 @@ #pragma link C++ class o2::trackstudy::ClResTPC + ; #pragma link C++ class o2::trackstudy::ClResTPCCont + ; #pragma link C++ class std::vector < o2::trackstudy::ClResTPCCont> + ; +#pragma link C++ class o2::trackstudy::TrackPairInfo + ; +#pragma link C++ class std::vector < o2::trackstudy::TrackPairInfo> + ; #endif diff --git a/Detectors/GlobalTrackingWorkflow/study/src/SVStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/SVStudy.cxx index 17b33c86e61ad..12a883ec991f6 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/SVStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/SVStudy.cxx @@ -44,6 +44,12 @@ #include "DCAFitter/DCAFitterN.h" #include "MathUtils/fit.h" #include "GlobalTrackingStudy/V0Ext.h" +#include "GPUO2InterfaceConfiguration.h" +// #include "GPUSettingsO2.h" +#include "GPUParam.h" +#include "GPUParam.inc" +#include "GPUO2InterfaceRefit.h" +#include "GPUO2InterfaceUtils.h" namespace o2::svstudy { @@ -64,8 +70,8 @@ using timeEst = o2::dataformats::TimeStampWithError; class SVStudySpec : public Task { public: - SVStudySpec(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool useMC) - : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mUseMC(useMC) {} + SVStudySpec(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool useTPCCl, bool useMC) + : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mUseTPCCl(useTPCCl), mUseMC(useMC) {} ~SVStudySpec() final = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -83,11 +89,18 @@ class SVStudySpec : public Task std::unique_ptr mDBGOut; float mSelK0 = -1; bool mRefit = false; + bool mUseTPCCl = false; float mMaxEta = 0.8; float mBz = 0; + int mNHBPerTF = 0; + int mNTPCOccBinLength = 0; ///< TPC occ. histo bin length in TBs + float mNTPCOccBinLengthInv; + float mTPCTBinMUSInv = 0.f; GTrackID::mask_t mTracksSrc{}; o2::vertexing::DCAFitterN<2> mFitterV0; + std::vector mTBinClOccAft, mTBinClOccBef; std::unique_ptr mcReader; // reader of MC information + std::shared_ptr mParam = nullptr; }; void SVStudySpec::init(InitContext& ic) @@ -107,6 +120,48 @@ void SVStudySpec::run(ProcessingContext& pc) o2::globaltracking::RecoContainer recoData; recoData.collectData(pc, *mDataRequest.get()); // select tracks of needed type, with minimal cuts, the real selected will be done in the vertexer updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions + + size_t occupancyMapSizeBytes = o2::gpu::GPUO2InterfaceRefit::fillOccupancyMapGetSize(mNHBPerTF, mParam.get()); + gsl::span TPCRefitterOccMap = recoData.occupancyMapTPC; + o2::gpu::GPUO2InterfaceUtils::paramUseExternalOccupancyMap(mParam.get(), mNHBPerTF, TPCRefitterOccMap.data(), occupancyMapSizeBytes); + + mTBinClOccBef.resize(1); + mTBinClOccAft.resize(1); + if (recoData.inputsTPCclusters && mUseTPCCl) { + mNTPCOccBinLength = mParam->rec.tpc.occupancyMapTimeBins; + mTBinClOccBef.clear(); + mTBinClOccAft.clear(); + // prepare TPC occupancy data + if (mNTPCOccBinLength > 1 && recoData.occupancyMapTPC.size()) { + mNTPCOccBinLengthInv = 1. / mNTPCOccBinLength; + int nTPCBins = mNHBPerTF * o2::constants::lhc::LHCMaxBunches / 8, ninteg = 0; + int nTPCOccBins = nTPCBins * mNTPCOccBinLengthInv, sumBins = std::max(1, int(o2::constants::lhc::LHCMaxBunches / 8 * mNTPCOccBinLengthInv)); + mTBinClOccAft.resize(nTPCOccBins); + mTBinClOccBef.resize(nTPCOccBins); + float sm = 0., tb = 0.5 * mNTPCOccBinLength; + std::vector mltHistTB(nTPCOccBins); + for (int i = 0; i < nTPCOccBins; i++) { + mltHistTB[i] = mParam->GetUnscaledMult(tb); + tb += mNTPCOccBinLength; + } + for (int i = nTPCOccBins; i--;) { + sm += mltHistTB[i]; + if (i + sumBins < nTPCOccBins) { + sm -= mltHistTB[i + sumBins]; + } + mTBinClOccAft[i] = sm; + } + sm = 0; + for (int i = 0; i < nTPCOccBins; i++) { + sm += mltHistTB[i]; + if (i - sumBins > 0) { + sm -= mltHistTB[i - sumBins]; + } + mTBinClOccBef[i] = sm; + } + } + } + process(recoData); } @@ -133,6 +188,12 @@ void SVStudySpec::updateTimeDependentParams(ProcessingContext& pc) mFitterV0.setMaxStep(svparam.maxStep); mFitterV0.setMaxSnp(svparam.maxSnp); mFitterV0.setMinXSeed(svparam.minXSeed); + + mNHBPerTF = o2::base::GRPGeomHelper::instance().getGRPECS()->getNHBFPerTF(); + if (!mParam) { + // for occupancy estimator + mParam = o2::gpu::GPUO2InterfaceUtils::getFullParamShared(0.f, mNHBPerTF); + } } mBz = o2::base::Propagator::Instance()->getNominalBz(); mFitterV0.setBz(mBz); @@ -268,8 +329,13 @@ void SVStudySpec::process(o2::globaltracking::RecoContainer& recoData) } if (v0extVec.size()) { const auto& pv = recoData.getPrimaryVertex(pvID); + float tpcOccBef = 0., tpcOccAft = 0.; + int tb = pv.getTimeStamp().getTimeStamp() * mTPCTBinMUSInv * mNTPCOccBinLengthInv; + tpcOccBef = tb < 0 ? mTBinClOccBef[0] : (tb >= mTBinClOccBef.size() ? mTBinClOccBef.back() : mTBinClOccBef[tb]); + tpcOccAft = tb < 0 ? mTBinClOccAft[0] : (tb >= mTBinClOccAft.size() ? mTBinClOccAft.back() : mTBinClOccAft[tb]); + (*mDBGOut) << "v0" - << "orbit=" << recoData.startIR.orbit << "tfID=" << tfID + << "orbit=" << recoData.startIR.orbit << "tfID=" << tfID << "tpcOccBef=" << tpcOccBef << "tpcOccAft=" << tpcOccAft << "v0Ext=" << v0extVec << "pv=" << pv << "\n"; @@ -334,29 +400,30 @@ void SVStudySpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) } } -DataProcessorSpec getSVStudySpec(GTrackID::mask_t srcTracks, bool useMC) +DataProcessorSpec getSVStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcCls, bool useMC) { std::vector outputs; auto dataRequest = std::make_shared(); dataRequest->requestTracks(srcTracks, useMC); + dataRequest->requestClusters(srcCls, false); dataRequest->requestPrimaryVertices(useMC); dataRequest->requestSecondaryVertices(useMC); dataRequest->inputs.emplace_back("meanvtx", "GLO", "MEANVERTEX", 0, Lifetime::Condition, ccdbParamSpec("GLO/Calib/MeanVertex", {}, 1)); - auto ggRequest = std::make_shared(false, // orbitResetTime - false, // GRPECS=true + auto ggRequest = std::make_shared(true, // orbitResetTime + true, // GRPECS=true false, // GRPLHCIF true, // GRPMagField true, // askMatLUT o2::base::GRPGeomRequest::None, // geometry dataRequest->inputs, true); - + bool useTPCcl = srcCls[GTrackID::TPC]; return DataProcessorSpec{ "sv-study", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, useMC)}, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, useTPCcl, useMC)}, Options{ {"refit", VariantType::Bool, false, {"refit SVertices"}}, {"sel-k0", VariantType::Float, -1.f, {"If positive, select K0s with this mass margin"}}, diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx index ba453b944a742..b3ef78bd2eabf 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx @@ -33,6 +33,7 @@ #include "DetectorsBase/GRPGeomHelper.h" #include "GlobalTrackingStudy/TrackingStudy.h" #include "GlobalTrackingStudy/TrackInfoExt.h" +#include "GlobalTrackingStudy/TrackMCStudyTypes.h" #include "TPCBase/ParameterElectronics.h" #include "ReconstructionDataFormats/PrimaryVertex.h" #include "ReconstructionDataFormats/PrimaryVertexExt.h" @@ -48,6 +49,7 @@ #include "GPUParam.inc" #include "Steer/MCKinematicsReader.h" #include "MathUtils/fit.h" +#include namespace o2::trackstudy { @@ -92,7 +94,8 @@ class TrackingStudySpec : public Task std::unique_ptr mDBGOut; std::unique_ptr mDBGOutVtx; std::unique_ptr mTPCRefitter; ///< TPC refitter used for TPC tracks refit during the reconstruction - std::vector mTBinClOccAft, mTBinClOccBef; ///< TPC occupancy histo: i-th entry is the integrated occupancy for ~1 orbit starting/preceding from the TB = i*mNTPCOccBinLength + std::vector mTBinClOccAft, mTBinClOccBef, mTBinClOccWgh; ///< TPC occupancy histo: i-th entry is the integrated occupancy for ~1 orbit starting/preceding from the TB = i*mNTPCOccBinLength + std::unique_ptr mOccWghFun; float mITSROFrameLengthMUS = 0.f; float mTPCTBinMUS = 0.f; // TPC bin in microseconds float mTPCTBinMUSInv = 0.f; @@ -108,6 +111,7 @@ class TrackingStudySpec : public Task int mNHBPerTF = 0; float mNTPCOccBinLengthInv; bool mStoreWithITSOnly = false; + bool mDoPairsCorr = false; std::string mDCAYFormula = "0.0105 + 0.0350 / pow(x, 1.1)"; std::string mDCAZFormula = "0.0105 + 0.0350 / pow(x, 1.1)"; GTrackID::mask_t mTracksSrc{}; @@ -136,6 +140,11 @@ void TrackingStudySpec::init(InitContext& ic) mMinTPCClusters = ic.options().get("min-tpc-clusters"); mDCAYFormula = ic.options().get("dcay-vs-pt"); mDCAZFormula = ic.options().get("dcaz-vs-pt"); + mDoPairsCorr = ic.options().get("pair-correlations"); + auto str = ic.options().get("occ-weight-fun"); + if (!str.empty()) { + mOccWghFun = std::make_unique("occFun", str.c_str(), -100., 100.); + } } void TrackingStudySpec::run(ProcessingContext& pc) @@ -151,7 +160,9 @@ void TrackingStudySpec::run(ProcessingContext& pc) mNTPCOccBinLength = mTPCRefitter->getParam()->rec.tpc.occupancyMapTimeBins; mTBinClOccBef.clear(); mTBinClOccAft.clear(); + mTBinClOccWgh.clear(); } + // prepare TPC occupancy data if (mNTPCOccBinLength > 1 && recoData.occupancyMapTPC.size()) { mNTPCOccBinLengthInv = 1. / mNTPCOccBinLength; @@ -159,8 +170,27 @@ void TrackingStudySpec::run(ProcessingContext& pc) int nTPCOccBins = nTPCBins * mNTPCOccBinLengthInv, sumBins = std::max(1, int(o2::constants::lhc::LHCMaxBunches / 8 * mNTPCOccBinLengthInv)); mTBinClOccAft.resize(nTPCOccBins); mTBinClOccBef.resize(nTPCOccBins); - std::vector mltHistTB(nTPCOccBins); float sm = 0., tb = 0.5 * mNTPCOccBinLength; + /* // at the moment not used + if (mOccWghFun) { + mTBinClOccWgh.resize(nTPCBins); + float occBin2MUS = 8 * o2::constants::lhc::LHCBunchSpacingMUS; + int covWghTB = TMath::NInt(100./occBin2MUS); // coverage of weighted occ. in TBins + for (int i = 0; i < nTPCBins; i++) { + sm = 0.; + for (int j=-covWghTB;j=nTPCBins) { + continue; + } + sm += mOccWghFun->Eval(j*occBin2MUS)*mTPCRefitter->getParam()->GetUnscaledMult(j+i); + } + mTBinClOccWgh[i] = sm; + } + } else { + mTBinClOccWgh.resize(1); + } + */ + std::vector mltHistTB(nTPCOccBins); for (int i = 0; i < nTPCOccBins; i++) { mltHistTB[i] = mTPCRefitter->getParam()->GetUnscaledMult(tb); tb += mNTPCOccBinLength; @@ -245,12 +275,14 @@ void TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) float tBiasITS = alpParams.roFrameBiasInBC * o2::constants::lhc::LHCBunchSpacingMUS; const o2::ft0::InteractionTag& ft0Params = o2::ft0::InteractionTag::Instance(); std::vector trcExtVec; + std::vector trcPairsVec; auto vdrit = mTPCVDriftHelper.getVDriftObject().getVDrift(); bool tpcTrackOK = recoData.isTrackSourceLoaded(GTrackID::TPC); auto fillTPCClInfo = [&recoData, this](const o2::tpc::TrackTPC& trc, o2::dataformats::TrackInfoExt& trExt, float timestampTB = -1e9) { const auto clRefs = recoData.getTPCTracksClusterRefs(); - const auto shMap = recoData.clusterShMapTPC.data(); + const auto tpcClusAcc = recoData.getTPCClusters(); + const auto shMap = recoData.clusterShMapTPC; if (recoData.inputsTPCclusters) { uint8_t clSect = 0, clRow = 0, clRowP = -1; uint32_t clIdx = 0; @@ -260,13 +292,14 @@ void TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) trExt.rowCountTPC++; clRowP = clRow; } - if (shMap[clRefs[ic + trc.getClusterRef().getFirstEntry()]]) { + unsigned int absoluteIndex = tpcClusAcc.clusterOffset[clSect][clRow] + clIdx; + if (shMap[absoluteIndex] & GPUCA_NAMESPACE::gpu::GPUTPCGMMergedTrackHit::flagShared) { trExt.nClTPCShared++; } } trc.getClusterReference(clRefs, trc.getNClusterReferences() - 1, clSect, clRow, clIdx); trExt.rowMinTPC = clRow; - const auto& clus = recoData.inputsTPCclusters->clusterIndex.clusters[clSect][clRow][clIdx]; + const auto& clus = tpcClusAcc.clusters[clSect][clRow][clIdx]; this->mTPCCorrMapsLoader.Transform(clSect, clRow, clus.getPad(), clus.getTime(), trExt.innerTPCPos0[0], trExt.innerTPCPos0[1], trExt.innerTPCPos0[2], trc.getTime0()); // nominal time of the track if (timestampTB > -1e8) { this->mTPCCorrMapsLoader.Transform(clSect, clRow, clus.getPad(), clus.getTime(), trExt.innerTPCPos[0], trExt.innerTPCPos[1], trExt.innerTPCPos[2], timestampTB); // time assigned from the global track track @@ -278,6 +311,81 @@ void TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) } }; + auto getTPCPairSharing = [&recoData, this](const o2::tpc::TrackTPC& trc0, const o2::tpc::TrackTPC& trc1) { + const auto clRefs = recoData.getTPCTracksClusterRefs(); + uint8_t nsh = 0, nshRows = 0, lastSharedRow = -1; + if (recoData.inputsTPCclusters) { + uint8_t clSect0 = 0, clRow0 = 0, clSect1 = 0, clRow1 = 0; + uint32_t clIdx0 = 0, clIdx1 = 0; + int ic1Start = 0; + for (int ic0 = 0; ic0 < trc0.getNClusterReferences(); ic0++) { // outside -> inside + trc0.getClusterReference(clRefs, ic0, clSect0, clRow0, clIdx0); + for (int ic1 = ic1Start; ic1 < trc1.getNClusterReferences(); ic1++) { // outside -> inside + trc1.getClusterReference(clRefs, ic1, clSect1, clRow1, clIdx1); + if (clRow1 > clRow0) { + ic1Start = ic1 + 1; + continue; // catch up ic0 + } + if (clRow1 == clRow0) { + if (clSect0 == clSect1 && clIdx0 == clIdx1) { + nsh++; + if (lastSharedRow != clRow0) { + lastSharedRow = clRow0; + nshRows++; + } + ic1Start = ic1 + 1; + break; // check next ic0 + } + } + } + } + } + return std::make_pair(nsh, nshRows); + }; + + auto assignRecTrack = [&recoData, this](const o2::dataformats::TrackInfoExt& src, o2::trackstudy::RecTrack& dst) { + dst.track = src.track; + dst.gid = src.gid; + dst.ts.setTimeStamp(src.ttime); + dst.ts.setTimeStampError(src.ttimeE); + dst.nClITS = src.nClITS; + dst.nClTPC = src.nClTPC; + dst.pattITS = src.pattITS; + if (src.q2ptITS == 0. && dst.nClITS > 0) { + dst.pattITS |= 0x1 << 7; + } + dst.lowestPadRow = src.rowMinTPC; + if (this->mUseMC) { + auto gidSet = recoData.getSingleDetectorRefs(src.gid); + if (recoData.getTrackMCLabel(src.gid).isFake()) { + dst.flags |= RecTrack::FakeGLO; + } + auto msk = src.gid.getSourceDetectorsMask(); + if (msk[DetID::ITS]) { + if (gidSet[GTrackID::ITS].isSourceSet()) { // has ITS track rather than AB tracklet + auto lblITS = recoData.getTrackMCLabel(gidSet[GTrackID::ITS]); + if (lblITS.isFake()) { + dst.flags |= RecTrack::FakeITS; + } + } else { // AB ITS tracklet + if (recoData.getTrackMCLabel(gidSet[GTrackID::ITSAB]).isFake()) { + dst.flags |= RecTrack::FakeITS; + } + } + if (msk[DetID::TPC]) { // has both ITS and TPC contribution + if (recoData.getTrackMCLabel(gidSet[GTrackID::ITSTPC]).isFake()) { + dst.flags |= RecTrack::FakeITSTPC; + } + } + } + if (msk[DetID::TPC]) { + if (recoData.getTrackMCLabel(gidSet[GTrackID::TPC]).isFake()) { + dst.flags |= RecTrack::FakeTPC; + } + } + } + }; + for (int iv = 0; iv < nv; iv++) { LOGP(debug, "processing PV {} of {}", iv, nv); const auto& vtref = vtxRefs[iv]; @@ -309,6 +417,7 @@ void TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) pve.VtxID = iv; } trcExtVec.clear(); + trcPairsVec.clear(); float q2ptITS, q2ptTPC, q2ptITSTPC, q2ptITSTPCTRD; for (int is = 0; is < GTrackID::NSources; is++) { DetID::mask_t dm = GTrackID::getSourceDetectorsMask(is); @@ -444,6 +553,42 @@ void TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) << "orbit=" << recoData.startIR.orbit << "tfID=" << TFCount << "tpcOccBef=" << tpcOccBef << "tpcOccAft=" << tpcOccAft << "pve=" << pveVec[iv] << "trc=" << trcExtVec << "\n"; + + if (mDoPairsCorr) { + for (int it0 = 0; it0 < (int)trcExtVec.size(); it0++) { + const auto& tr0 = trcExtVec[it0]; + if (tr0.nClTPC < 1) { + continue; + } + for (int it1 = it0 + 1; it1 < (int)trcExtVec.size(); it1++) { + const auto& tr1 = trcExtVec[it1]; + if (tr1.nClTPC < 1) { + continue; + } + + if (std::abs(tr0.track.getTgl() - tr1.track.getTgl()) > 0.25) { + continue; + } + auto dphi = tr0.track.getPhi() - tr1.track.getPhi(); + if (dphi < -o2::constants::math::PI) { + dphi += o2::constants::math::TwoPI; + } else if (dphi > o2::constants::math::PI) { + dphi -= o2::constants::math::TwoPI; + } + if (std::abs(dphi) > 0.25) { + continue; + } + auto& pr = trcPairsVec.emplace_back(); + assignRecTrack(tr0, pr.tr0); + assignRecTrack(tr1, pr.tr1); + auto shinfo = getTPCPairSharing(recoData.getTPCTrack(recoData.getTPCContributorGID(tr0.gid)), recoData.getTPCTrack(recoData.getTPCContributorGID(tr1.gid))); + pr.nshTPC = shinfo.first; + pr.nshTPCRow = shinfo.second; + } + } + } + (*mDBGOut) << "pairs" + << "pr=" << trcPairsVec << "\n"; } int nvtot = mMaxNeighbours < 0 ? -1 : (int)pveVec.size(); @@ -600,6 +745,8 @@ DataProcessorSpec getTrackingStudySpec(GTrackID::mask_t srcTracks, GTrackID::mas {"max-eta", VariantType::Float, 1.0f, {"Cut on track eta"}}, {"min-pt", VariantType::Float, 0.1f, {"Cut on track pT"}}, {"with-its-only", VariantType::Bool, false, {"Store tracks with ITS only"}}, + {"pair-correlations", VariantType::Bool, false, {"Do pairs correlation"}}, + {"occ-weight-fun", VariantType::String, "(x>=-40&&x<-5) ? (1./1225*pow(x+40,2)) : ((x>-5&&x<15) ? 1. : ((x>=15&&x<40) ? (-0.4/25*x+1.24 ) : ( (x>40&&x<100) ? -0.4/60*x+0.6+0.8/3 : 0)))", {"Occupancy weighting f-n vs time in musec"}}, {"min-x-prop", VariantType::Float, 100.f, {"track should be propagated to this X at least"}}, }; o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); diff --git a/Detectors/GlobalTrackingWorkflow/study/src/sv-study-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/sv-study-workflow.cxx index fba5e67452f1f..7e104b82f4854 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/sv-study-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/sv-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"}}, {"disable-root-input", VariantType::Bool, false, {"disable root-files input reader"}}, + {"ignore-tpc-occ", VariantType::Bool, false, {"do not fill TPC occupancy (needs TPC clusters)"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); @@ -61,10 +62,14 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) GID::mask_t srcTrc = allowedSourcesTrc & GID::getSourcesMask(configcontext.options().get("track-sources")); GID::mask_t srcCls{}; + bool fillTPCOcc = !configcontext.options().get("ignore-tpc-occ"); + if (fillTPCOcc) { + srcCls = srcCls | GID::getSourcesMask("TPC"); + } o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC); o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); // P-vertex is always needed o2::globaltracking::InputHelper::addInputSpecsSVertex(configcontext, specs); // S-vertex is always needed - specs.emplace_back(o2::svstudy::getSVStudySpec(srcTrc, useMC)); + specs.emplace_back(o2::svstudy::getSVStudySpec(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/ITSMFT/ITS/base/src/GeometryTGeo.cxx b/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx index b52fd8f58320f..3efbae0c698ca 100644 --- a/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx +++ b/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx @@ -24,8 +24,6 @@ #ifdef ENABLE_UPGRADES #include "ITS3Base/SpecsV2.h" -#include "ITS3Base/SegmentationSuperAlpide.h" -using SuperSegmentation = o2::its3::SegmentationSuperAlpide; #endif #include // for TGeoBBox @@ -420,33 +418,20 @@ TGeoHMatrix* GeometryTGeo::extractMatrixSensor(int index) const static int chipInGlo{0}; // account for the difference between physical sensitive layer (where charge collection is simulated) and effective sensor thicknesses + // in the ITS3 case this accounted by specialized functions double delta = Segmentation::SensorLayerThickness - Segmentation::SensorLayerThicknessEff; -#ifdef ENABLE_UPGRADES - if (mIsLayerITS3[getLayer(index)]) { - delta = its3::SegmentationSuperAlpide::mSensorLayerThickness - its3::SegmentationSuperAlpide::mSensorLayerThicknessEff; - } -#endif - static TGeoTranslation tra(0., 0.5 * delta, 0.); - +#ifdef ENABLE_UPGRADES // only apply for non ITS3 OB layers + if (!mIsLayerITS3[getLayer(index)]) { + matTmp *= tra; + } +#else matTmp *= tra; +#endif return &matTmp; } -//__________________________________________________________________________ -const o2::math_utils::Transform3D GeometryTGeo::getT2LMatrixITS3(int isn, float alpha) -{ - // create for sensor isn the TGeo matrix for Tracking to Local frame transformations - static TGeoHMatrix t2l; - t2l.Clear(); - t2l.RotateZ(alpha * RadToDeg()); // rotate in direction of normal to the tangent to the cylinder - const TGeoHMatrix& matL2G = getMatrixL2G(isn); - const auto& matL2Gi = matL2G.Inverse(); - t2l.MultiplyLeft(&matL2Gi); - return Mat3D(t2l); -} - //__________________________________________________________________________ void GeometryTGeo::Build(int loadTrans) { @@ -492,23 +477,6 @@ void GeometryTGeo::Build(int loadTrans) mLastChipIndex[i] = numberOfChips - 1; } - LOGP(debug, "Summary of extracted Geometry:"); - LOGP(debug, " There are {} Layers and {} HalfBarrels", mNumberOfLayers, mNumberOfHalfBarrels); - for (int i = 0; i < mNumberOfLayers; i++) { - LOGP(debug, " Layer {}: {:*^30}", i, "START"); - LOGP(debug, " - mNumberOfStaves={}", mNumberOfStaves[i]); - LOGP(debug, " - mNumberOfChipsPerStave={}", mNumberOfChipsPerStave[i]); - LOGP(debug, " - mNumberOfHalfStaves={}", mNumberOfHalfStaves[i]); - LOGP(debug, " - mNumberOfChipsPerHalfStave={}", mNumberOfChipsPerHalfStave[i]); - LOGP(debug, " - mNumberOfModules={}", mNumberOfModules[i]); - LOGP(debug, " - mNumberOfChipsPerModules={}", mNumberOfChipsPerModule[i]); - LOGP(debug, " - mNumberOfChipsPerLayer={}", mNumberOfChipsPerLayer[i]); - LOGP(debug, " - mNumberOfChipsPerHalfBarrel={}", mNumberOfChipsPerHalfBarrel[i]); - LOGP(debug, " - mLastChipIndex={}", mLastChipIndex[i]); - LOGP(debug, " Layer {}: {:*^30}", i, "END"); - } - LOGP(debug, "In total there {} chips registered", numberOfChips); - #ifdef ENABLE_UPGRADES if (std::any_of(mIsLayerITS3.cbegin(), mIsLayerITS3.cend(), [](auto b) { return b; })) { LOGP(info, "Found active IT3 layers -> Renaming Detector ITS to IT3"); @@ -880,34 +848,30 @@ void GeometryTGeo::extractSensorXAlpha(int isn, float& x, float& alp) const TGeoHMatrix* matL2G = extractMatrixSensor(isn); double locA[3] = {-100., 0., 0.}, locB[3] = {100., 0., 0.}, gloA[3], gloB[3]; - int iLayer = getLayer(isn); + double xp{0}, yp{0}; -#ifdef ENABLE_UPGRADES - if (mIsLayerITS3[iLayer]) { - // We need to calcualte the line tangent at the mid-point in the geometry + if (int iLayer = getLayer(isn); mIsLayerITS3[iLayer]) { + // For a TGeoTubeSeg the local coordinate system is defined at the origin + // of the circle of the side, since in our implementation we rotated the geometry a bit const auto radius = o2::its3::constants::radii[iLayer]; const auto phi1 = o2::its3::constants::tile::width / radius; const auto phi2 = o2::its3::constants::pixelarray::width / radius + phi1; const auto phi3 = (phi2 - phi1) / 2.; // mid-point in phi - const auto x = radius * std::cos(phi3); - const auto y = radius * std::sin(phi3); - // For the tangent we make the parametric line equation y = m * x - c - const auto m = x / y; - const auto c = y - m * x; - // Now we can given any x calulate points along this line, we pick points far away, - // the calculation of the normal should work then below. - locA[1] = m * locA[0] + c; - locB[1] = m * locB[0] + c; - } -#endif - - matL2G->LocalToMaster(locA, gloA); - matL2G->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; - x = Sqrt(xp * xp + yp * yp); - alp = ATan2(yp, xp); + locA[0] = radius * std::cos(phi3); + locA[1] = radius * std::sin(phi3); + matL2G->LocalToMaster(locA, gloA); + xp = gloA[0]; + yp = gloA[1]; + } else { + matL2G->LocalToMaster(locA, gloA); + matL2G->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); + xp = gloB[0] - dx * t; + yp = gloB[1] - dy * t; + } + x = std::hypot(xp, yp); + alp = std::atan2(yp, xp); o2::math_utils::bringTo02Pi(alp); } @@ -926,6 +890,19 @@ TGeoHMatrix& GeometryTGeo::createT2LMatrix(int isn) return t2l; } +//__________________________________________________________________________ +const o2::math_utils::Transform3D GeometryTGeo::getT2LMatrixITS3(int isn, float alpha) +{ + // create for sensor isn the TGeo matrix for Tracking to Local frame transformations with correction for effective thickness + static TGeoHMatrix t2l; + t2l.Clear(); + t2l.RotateZ(alpha * RadToDeg()); // rotate in direction of normal to the tangent to the cylinder + const TGeoHMatrix& matL2G = getMatrixL2G(isn); + const auto& matL2Gi = matL2G.Inverse(); + t2l.MultiplyLeft(&matL2Gi); + return Mat3D(t2l); +} + //__________________________________________________________________________ int GeometryTGeo::extractVolumeCopy(const char* name, const char* prefix) const { diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckSquasher.C b/Detectors/ITSMFT/ITS/macros/test/CheckSquasher.C index 70bdb46abfe37..4e4dfcc41b928 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CheckSquasher.C +++ b/Detectors/ITSMFT/ITS/macros/test/CheckSquasher.C @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -32,17 +33,38 @@ #include #endif -void getClusterPatterns(std::vector& pattVec, std::vector* ITSclus, std::vector* ITSpatt, o2::itsmft::TopologyDictionary& mdict); +static bool invPal = false; +void getClusterPatterns(std::vector& pattVec, + std::vector* ITSclus, + std::vector* ITSpatt, + o2::itsmft::TopologyDictionary& mdict); -void CheckSquasher(const uint chipId = 0, const uint startingROF = 0, const unsigned int nRofs = 3, const string fname = "o2clus_its.root") +void drawClustersInChipInRof(const uint chipId, + gsl::span clustersInFrame, + gsl::span patternsInFrame, + TH2D* hHitMapsVsFrame, + TH2D* hHitMapSuperimposed = nullptr); + +void drawClustersInStaveInRof(const int staveId, + const int layerId, + gsl::span clustersInFrame, + gsl::span patternsInFrame, + TH2D* hHitMapsVsFrame); + +void CheckSquasher1(const uint chipId = 0, const uint startingROF = 0, const uint nRofs = 3, bool showSuperimposed = false, const string fname = "o2clus_its.root") { - TColor::InvertPalette(); + if (!invPal) { + TColor::InvertPalette(); + invPal = true; + } + gStyle->SetOptStat(0); // Geometry auto& cc = o2::ccdb::BasicCCDBManager::instance(); cc.setTimestamp(o2::ccdb::getCurrentTimestamp()); auto* gman = cc.get("ITS/Config/Geometry"); gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); + // Topology dictionary auto mdict = cc.get("ITS/Calib/ClusterDictionary"); auto fITSclus = TFile::Open(fname.data(), "r"); @@ -78,55 +100,7 @@ void CheckSquasher(const uint chipId = 0, const uint startingROF = 0, const unsi const auto& rof = (*ITSrof)[startingROF + iR]; auto clustersInFrame = rof.getROFData(*ITSclus); auto patternsInFrame = rof.getROFData(pattVec); - // auto pattIt = ITSpatt->cbegin(); - - for (unsigned int clusInd{0}; clusInd < clustersInFrame.size(); clusInd++) { - const auto& clus = clustersInFrame[clusInd]; - auto sID = clus.getSensorID(); - - if (sID == chipId) { - LOGP(info, "Processing cluster {}", clusInd); - clus.print(); - - // auto labels = clusLabArr->getLabels(clusInd); - // extract pattern info - auto col = clus.getCol(); - auto row = clus.getRow(); - - std::cout << patternsInFrame[clusInd]; - - std::cout << std::endl; - int ic = 0, ir = 0; - - auto colSpan = patternsInFrame[clusInd].getColumnSpan(); - auto rowSpan = patternsInFrame[clusInd].getRowSpan(); - auto nBits = rowSpan * colSpan; - - for (int i = 2; i < patternsInFrame[clusInd].getUsedBytes() + 2; i++) { - unsigned char tempChar = patternsInFrame[clusInd].getByte(i); - int s = 128; // 0b10000000 - while (s > 0) { - if ((tempChar & s) != 0) // checking active pixels - { - hHitMapsVsFrame[iR]->Fill(col + ic, row + ir); - hHitMapSuperimposed->Fill(col + ic, row + ir); - } - ic++; - s >>= 1; - if ((ir + 1) * ic == nBits) { - break; - } - if (ic == colSpan) { - ic = 0; - ir++; - } - if ((ir + 1) * ic == nBits) { - break; - } - } - } - } - } + drawClustersInChipInRof(chipId, clustersInFrame, patternsInFrame, hHitMapsVsFrame[iR], hHitMapSuperimposed); } auto canvas = new TCanvas(Form("chip%d", chipId), Form("chip%d", chipId), nRofs * 1000, 600); auto canvasSuperimposition = new TCanvas(Form("chip%d_superimposed", chipId), Form("chip%d_superimposed", chipId), 600, 600); @@ -141,7 +115,81 @@ void CheckSquasher(const uint chipId = 0, const uint startingROF = 0, const unsi canvasSuperimposition->cd(); gPad->SetGridx(); gPad->SetGridy(); - hHitMapSuperimposed->Draw("colz"); + if (showSuperimposed) { + hHitMapSuperimposed->Draw("colz"); + } else { + delete canvasSuperimposition; + } +} + +void CheckSquasher(const int staveId, const uint layerId, const uint startingROF = 0, const uint nRofs = 3, const string fname = "o2clus_its.root") +{ + std::array staves{12, 14, 16}; + if (!invPal) { + TColor::InvertPalette(); + invPal = true; + } + + gStyle->SetOptStat(0); + // Geometry + auto& cc = o2::ccdb::BasicCCDBManager::instance(); + cc.setTimestamp(o2::ccdb::getCurrentTimestamp()); + auto* gman = cc.get("ITS/Config/Geometry"); + gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); + + // Topology dictionary + auto mdict = cc.get("ITS/Calib/ClusterDictionary"); + auto fITSclus = TFile::Open(fname.data(), "r"); + auto treeITSclus = (TTree*)fITSclus->Get("o2sim"); + + std::vector* ITSclus = nullptr; + std::vector* ITSrof = nullptr; + std::vector* ITSpatt = nullptr; + o2::dataformats::MCTruthContainer* clusLabArr = nullptr; + + treeITSclus->SetBranchAddress("ITSClusterComp", &ITSclus); + treeITSclus->SetBranchAddress("ITSClustersROF", &ITSrof); + treeITSclus->SetBranchAddress("ITSClusterPatt", &ITSpatt); + + auto clSpan = gsl::span(ITSclus->data(), ITSclus->size()); + std::vector hHitMapsVsFrame(nRofs); + TH2D* hHitMapSuperimposed = nullptr; + std::vector hHitMapStaves; + hHitMapStaves.resize(staveId < 0 ? staves[layerId] : 1); + for (int iStave{0}; iStave < (staveId < 0 ? staves[layerId] : 1); ++iStave) { + hHitMapStaves[staveId < 0 ? iStave : 0] = new TH2D(Form("stave_%d", staveId < 0 ? iStave : staveId), "", 1024 * 9, -0.5, 1023.5 * 9, 512, -0.5, 511.5); + } + + treeITSclus->GetEvent(0); + LOGP(info, "there are {} rofs in this TF", ITSrof->size()); + + // Get patterns + std::vector pattVec; + getClusterPatterns(pattVec, ITSclus, ITSpatt, *mdict); + + for (unsigned int iR{0}; iR < nRofs; iR++) { + LOGP(info, " ===============\n \tProcessing rof {} \n\t===============", iR + startingROF); + // work on data + const auto& rof = (*ITSrof)[startingROF + iR]; + auto clustersInFrame = rof.getROFData(*ITSclus); + auto patternsInFrame = rof.getROFData(pattVec); + for (int iStave{0}; iStave < (staveId < 0 ? staves[layerId] : 1); ++iStave) { + drawClustersInStaveInRof(staveId < 0 ? iStave : staveId, layerId, clustersInFrame, patternsInFrame, hHitMapStaves[staveId < 0 ? iStave : 0]); + } + } + + auto canvas = new TCanvas(Form("stave%d", staveId), Form("stave%d", staveId), 9 * 1000, (staveId < 0 ? staves[layerId] : 1) * 1000); + if (staveId < 0) { + canvas->Divide(1, staves[layerId]); + } + for (int iStave{0}; iStave < (staveId < 0 ? staves[layerId] : 1); ++iStave) { + if (staveId < 0) { + canvas->cd(iStave + 1); + } + gPad->SetGridx(); + gPad->SetGridy(); + hHitMapStaves[staveId < 0 ? iStave : 0]->Draw("colz"); + } } void getClusterPatterns(std::vector& pattVec, std::vector* ITSclus, std::vector* ITSpatt, o2::itsmft::TopologyDictionary& mdict) @@ -168,3 +216,113 @@ void getClusterPatterns(std::vector& pattVec, std::v pattVec.push_back(patt); } } + +void drawClustersInChipInRof(const uint chipId, + gsl::span clustersInFrame, + gsl::span patternsInFrame, + TH2D* hHitMapsVsFrame, + TH2D* hHitMapSuperimposed) +{ + for (unsigned int clusInd{0}; clusInd < clustersInFrame.size(); clusInd++) { + const auto& clus = clustersInFrame[clusInd]; + auto sID = clus.getSensorID(); + + if (sID == chipId) { + LOGP(info, "Processing cluster {}", clusInd); + clus.print(); + + // extract pattern info + auto col = clus.getCol(); + auto row = clus.getRow(); + + std::cout << patternsInFrame[clusInd]; + + std::cout << std::endl; + int ic = 0, ir = 0; + + auto colSpan = patternsInFrame[clusInd].getColumnSpan(); + auto rowSpan = patternsInFrame[clusInd].getRowSpan(); + auto nBits = rowSpan * colSpan; + + for (int i = 2; i < patternsInFrame[clusInd].getUsedBytes() + 2; i++) { + unsigned char tempChar = patternsInFrame[clusInd].getByte(i); + int s = 128; // 0b10000000 + while (s > 0) { + if ((tempChar & s) != 0) // checking active pixels + { + hHitMapsVsFrame->Fill(col + ic, row + ir); + hHitMapSuperimposed == nullptr ?: hHitMapSuperimposed->Fill(col + ic, row + ir); + } + ic++; + s >>= 1; + if ((ir + 1) * ic == nBits) { + break; + } + if (ic == colSpan) { + ic = 0; + ir++; + } + if ((ir + 1) * ic == nBits) { + break; + } + } + } + } + } +} + +void drawClustersInStaveInRof(const int staveId, + const int layerId, + gsl::span clustersInFrame, + gsl::span patternsInFrame, + TH2D* hHitMapsVsFrame) +{ + o2::itsmft::ChipMappingITS chipMapping; + int lay, sta, ssta, mod, chipInMod; + std::array sensorIDs; // sIDs in the staves of the IB. + for (unsigned int clusInd{0}; clusInd < clustersInFrame.size(); clusInd++) { + const auto& clus = clustersInFrame[clusInd]; + auto sID = clus.getSensorID(); + + chipMapping.expandChipInfoHW(sID, lay, sta, ssta, mod, chipInMod); + + if (sta == staveId && lay == layerId) { + // extract pattern info + auto col = clus.getCol(); + auto row = clus.getRow(); + + int ic = 0, ir = 0; + + auto colSpan = patternsInFrame[clusInd].getColumnSpan(); + auto rowSpan = patternsInFrame[clusInd].getRowSpan(); + auto nBits = rowSpan * colSpan; + + for (int i = 2; i < patternsInFrame[clusInd].getUsedBytes() + 2; i++) { + unsigned char tempChar = patternsInFrame[clusInd].getByte(i); + int s = 128; // 0b10000000 + while (s > 0) { + if ((tempChar & s) != 0) // checking active pixels + { + // Normalize the chip ID to be within the range [0, 8] + int normalizedChipID = sID % 9; // Hardcode for the ITS IB + double x = col + ic + normalizedChipID * 1024; + double y = row + ir; + hHitMapsVsFrame->Fill(x, y); + } + ic++; + s >>= 1; + if ((ir + 1) * ic == nBits) { + break; + } + if (ic == colSpan) { + ic = 0; + ir++; + } + if ((ir + 1) * ic == nBits) { + break; + } + } + } + } + } +} \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckTracksCA.C b/Detectors/ITSMFT/ITS/macros/test/CheckTracksCA.C index c00e0ccbfe016..7c128ce34d538 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CheckTracksCA.C +++ b/Detectors/ITSMFT/ITS/macros/test/CheckTracksCA.C @@ -87,6 +87,7 @@ void CheckTracksCA(bool doFakeClStud = false, TTree* mcTree = (TTree*)gFile->Get("o2sim"); mcTree->SetBranchStatus("*", 0); // disable all branches mcTree->SetBranchStatus("MCTrack*", 1); + mcTree->SetBranchStatus("MCEventHeader*", 1); std::vector* mcArr = nullptr; mcTree->SetBranchAddress("MCTrack", &mcArr); @@ -115,10 +116,13 @@ void CheckTracksCA(bool doFakeClStud = false, std::cout << "** Filling particle table ... " << std::flush; int lastEventIDcl = -1, cf = 0; int nev = mcTree->GetEntriesFast(); - std::vector> info(nev); + std::vector> info; + info.resize(nev); + TH1D* hZvertex = new TH1D("hZvertex", "Z vertex", 100, -20, 20); for (int n = 0; n < nev; n++) { // loop over MC events mcTree->GetEvent(n); info[n].resize(mcArr->size()); + hZvertex->Fill(mcEvent->GetZ()); for (unsigned int mcI{0}; mcI < mcArr->size(); ++mcI) { auto part = mcArr->at(mcI); info[n][mcI].event = n; @@ -196,7 +200,6 @@ void CheckTracksCA(bool doFakeClStud = false, info[evID][trackID].track.getImpactParams(info[evID][trackID].pvx, info[evID][trackID].pvy, info[evID][trackID].pvz, bz, ip); info[evID][trackID].dcaxy = ip[0]; info[evID][trackID].dcaz = ip[1]; - Info("", "dcaxy=%f dcaz=%f bz=%f", ip[0], ip[1], bz); } fakes += fake; @@ -286,6 +289,10 @@ void CheckTracksCA(bool doFakeClStud = false, clone->Divide(clone, den, 1, 1, "b"); clone->SetLineColor(3); clone->Draw("histesame"); + TCanvas* c2 = new TCanvas; + c2->SetGridx(); + c2->SetGridy(); + hZvertex->DrawClone(); std::cout << "** Streaming output TTree to file ... " << std::flush; TFile file("CheckTracksCA.root", "recreate"); diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/macros/PostTrackExtension.notest b/Detectors/ITSMFT/ITS/postprocessing/studies/macros/PostTrackExtension.notest index 4a7c9c4159a4b..29f94086aae4c 100644 --- a/Detectors/ITSMFT/ITS/postprocessing/studies/macros/PostTrackExtension.notest +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/macros/PostTrackExtension.notest @@ -587,7 +587,7 @@ void setStyle() gStyle->Reset("Plain"); gStyle->SetOptTitle(0); gStyle->SetOptStat(0); - gStyle->SetPalette(kRainbow); + gStyle->SetPalette(kRainBow); gStyle->SetCanvasColor(10); gStyle->SetCanvasBorderMode(0); gStyle->SetFrameLineWidth(1); diff --git a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEst.h b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEst.h index 457381862cc42..9e8299e89b404 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEst.h +++ b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEst.h @@ -45,7 +45,7 @@ struct FastMultEst { static uint32_t getCurrentRandomSeed(); int selectROFs(const gsl::span rofs, const gsl::span clus, - const gsl::span trig, std::vector& sel); + const gsl::span trig, std::vector& sel); void fillNClPerLayer(const gsl::span& clusters); float process(const std::array ncl) diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/FastMultEst.cxx b/Detectors/ITSMFT/ITS/reconstruction/src/FastMultEst.cxx index a55fafdf60409..c547996c6f356 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/src/FastMultEst.cxx +++ b/Detectors/ITSMFT/ITS/reconstruction/src/FastMultEst.cxx @@ -125,7 +125,7 @@ float FastMultEst::processNoiseImposed(const std::array ncl) } int FastMultEst::selectROFs(const gsl::span rofs, const gsl::span clus, - const gsl::span trig, std::vector& sel) + const gsl::span trig, std::vector& sel) { int nrof = rofs.size(), nsel = 0; const auto& multEstConf = FastMultEstConfig::Instance(); // parameters for mult estimation and cuts diff --git a/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx b/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx index bf2e997794ee4..2304a9102092a 100644 --- a/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx +++ b/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx @@ -190,7 +190,7 @@ Detector::Detector(Bool_t active, TString name) } else { mLayerName[j].Form("%s%d", GeometryTGeo::getITSSensorPattern(), j); // See V3Layer } - LOGP(info, "{}: mLayerName={}", j, mLayerName[j].Data()); + LOGP(debug, "{}: mLayerName={}", j, mLayerName[j].Data()); } if (mNumberLayers > 0) { // if not, we'll Fatal-ize in CreateGeometry @@ -723,8 +723,8 @@ void Detector::defineLayer(Int_t nlay, Double_t phi0, Double_t r, Int_t nstav, I // Return: // none. - LOG(info) << "L# " << nlay << " Phi:" << phi0 << " R:" << r << " Nst:" << nstav << " Nunit:" << nunit - << " Lthick:" << lthick << " Dthick:" << dthick << " DetID:" << dettypeID << " B:" << buildLevel; + LOG(debug) << "L# " << nlay << " Phi:" << phi0 << " R:" << r << " Nst:" << nstav << " Nunit:" << nunit + << " Lthick:" << lthick << " Dthick:" << dthick << " DetID:" << dettypeID << " B:" << buildLevel; if (nlay >= mNumberLayers || nlay < 0) { LOG(error) << "Wrong layer number " << nlay; diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h index ad8724f315ec8..4ac22607a580b 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h @@ -51,9 +51,19 @@ class TimeFrameGPU : public TimeFrame void initialise(const int, const TrackingParameters&, const int, IndexTableUtils* utils = nullptr, const TimeFrameGPUParameters* pars = nullptr); void initDevice(IndexTableUtils*, const TrackingParameters& trkParam, const TimeFrameGPUParameters&, const int, const int); void initDeviceSAFitting(); + void loadIndexTableUtils(const int); void loadTrackingFrameInfoDevice(const int); void loadUnsortedClustersDevice(const int); void loadClustersDevice(const int); + void loadClustersIndexTables(const int iteration); + void createUsedClustersDevice(const int); + void loadUsedClustersDevice(); + void loadROframeClustersDevice(const int); + void loadMultiplicityCutMask(const int); + void loadVertices(const int); + + /// + void createTrackletsLUTDevice(const int); void loadTrackletsDevice(); void loadTrackletsLUTDevice(); void loadCellsDevice(); @@ -62,12 +72,14 @@ class TimeFrameGPU : public TimeFrame void loadTrackSeedsChi2Device(); void loadRoadsDevice(); void loadTrackSeedsDevice(std::vector&); + void createTrackletsBuffers(); void createCellsBuffers(const int); void createCellsDevice(); void createCellsLUTDevice(); - void createNeighboursDevice(); + void createNeighboursIndexTablesDevice(); void createNeighboursDevice(const unsigned int& layer, std::vector>& neighbours); void createNeighboursLUTDevice(const int, const unsigned int); + void createNeighboursDeviceArray(); void createTrackITSExtDevice(std::vector&); void downloadTrackITSExtDevice(std::vector&); void downloadCellsNeighboursDevice(std::vector>>&, const int); @@ -93,7 +105,7 @@ class TimeFrameGPU : public TimeFrame std::vector>& getLabelsInChunks() { return mLabelsInChunks; } int getNAllocatedROFs() const { return mNrof; } // Allocated means maximum nROF for each chunk while populated is the number of loaded ones. StaticTrackingParameters* getDeviceTrackingParameters() { return mTrackingParamsDevice; } - Vertex* getDeviceVertices() { return mVerticesDevice; } + Vertex* getDeviceVertices() { return mPrimaryVerticesDevice; } int* getDeviceROFramesPV() { return mROFramesPVDevice; } unsigned char* getDeviceUsedClusters(const int); const o2::base::Propagator* getChainPropagator(); @@ -102,13 +114,20 @@ class TimeFrameGPU : public TimeFrame Road* getDeviceRoads() { return mRoadsDevice; } TrackITSExt* getDeviceTrackITSExt() { return mTrackITSExtDevice; } int* getDeviceNeighboursLUT(const int layer) { return mNeighboursLUTDevice[layer]; } - gpuPair* getDeviceNeighbours(const int layer) { return mNeighboursDevice[layer]; } + gsl::span getDeviceNeighboursLUTs() { return mNeighboursLUTDevice; } + gpuPair* getDeviceNeighbourPairs(const int layer) { return mNeighbourPairsDevice[layer]; } + int* getDeviceNeighbours(const int layer) { return mNeighboursDevice[layer]; } + int** getDeviceNeighboursArray() { return mNeighboursDeviceArray; } TrackingFrameInfo* getDeviceTrackingFrameInfo(const int); const TrackingFrameInfo** getDeviceArrayTrackingFrameInfo() const { return mTrackingFrameInfoDeviceArray; } const Cluster** getDeviceArrayClusters() const { return mClustersDeviceArray; } const Cluster** getDeviceArrayUnsortedClusters() const { return mUnsortedClustersDeviceArray; } - const Tracklet** getDeviceArrayTracklets() const { return mTrackletsDeviceArray; } - const int** getDeviceArrayTrackletsLUT() const { return mTrackletsLUTDeviceArray; } + const int** getDeviceArrayClustersIndexTables() const { return mClustersIndexTablesDeviceArray; } + std::vector getClusterSizes(); + const unsigned char** getDeviceArrayUsedClusters() const { return mUsedClustersDeviceArray; } + const int** getDeviceROframeClusters() const { return mROFrameClustersDeviceArray; } + Tracklet** getDeviceArrayTracklets() { return mTrackletsDeviceArray; } + int** getDeviceArrayTrackletsLUT() const { return mTrackletsLUTDeviceArray; } int** getDeviceArrayCellsLUT() const { return mCellsLUTDeviceArray; } int** getDeviceArrayNeighboursCellLUT() const { return mNeighboursCellLUTDeviceArray; } CellSeed** getDeviceArrayCells() const { return mCellsDeviceArray; } @@ -116,17 +135,19 @@ class TimeFrameGPU : 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; } void setDevicePropagator(const o2::base::PropagatorImpl*) override; // Host-specific getters - gsl::span getHostNTracklets(const int chunkId); - gsl::span getHostNCells(const int chunkId); + gsl::span getNTracklets() { return mNTracklets; } + gsl::span getNCells() { return mNCells; } // Host-available device getters + gsl::span getDeviceTrackletsLUTs() { return mTrackletsLUTDevice; } gsl::span getDeviceCellLUTs() { return mCellsLUTDevice; } + gsl::span getDeviceTracklet() { return mTrackletsDevice; } gsl::span getDeviceCells() { return mCellsDevice; } - gsl::span getNCellsDevice() { return mNCells; } private: void allocMemAsync(void**, size_t, Stream*, bool); // Abstract owned and unowned memory allocations @@ -136,31 +157,37 @@ class TimeFrameGPU : public TimeFrame StaticTrackingParameters mStaticTrackingParams; // Host-available device buffer sizes + std::array mNTracklets; std::array mNCells; // Device pointers StaticTrackingParameters* mTrackingParamsDevice; IndexTableUtils* mIndexTableUtilsDevice; - std::array mROFramesClustersDevice; - std::array mUsedClustersDevice; - Vertex* mVerticesDevice; - int* mROFramesPVDevice; // Hybrid pref + uint8_t* mMultMaskDevice; + Vertex* mPrimaryVerticesDevice; + int* mROFramesPVDevice; std::array mClustersDevice; std::array mUnsortedClustersDevice; + std::array mClustersIndexTablesDevice; + std::array mUsedClustersDevice; + std::array mROFramesClustersDevice; const Cluster** mClustersDeviceArray; const Cluster** mUnsortedClustersDeviceArray; + const int** mClustersIndexTablesDeviceArray; + const unsigned char** mUsedClustersDeviceArray; + const int** mROFrameClustersDeviceArray; std::array mTrackletsDevice; - const Tracklet** mTrackletsDeviceArray; - const int** mTrackletsLUTDeviceArray; - std::array mTrackletsLUTDevice; + Tracklet** mTrackletsDeviceArray; + std::array mTrackletsLUTDevice; std::array mCellsLUTDevice; std::array mNeighboursLUTDevice; int** mCellsLUTDeviceArray; int** mNeighboursCellDeviceArray; int** mNeighboursCellLUTDeviceArray; + int** mTrackletsLUTDeviceArray; std::array mCellsDevice; std::array mNeighboursIndexTablesDevice; CellSeed* mTrackSeedsDevice; @@ -172,7 +199,9 @@ class TimeFrameGPU : public TimeFrame Road* mRoadsDevice; TrackITSExt* mTrackITSExtDevice; - std::array*, nLayers - 2> mNeighboursDevice; + std::array*, nLayers - 2> mNeighbourPairsDevice; + std::array mNeighboursDevice; + int** mNeighboursDeviceArray; std::array mTrackingFrameInfoDevice; const TrackingFrameInfo** mTrackingFrameInfoDeviceArray; @@ -186,10 +215,6 @@ class TimeFrameGPU : public TimeFrame std::vector> mNVerticesInChunks; std::vector> mLabelsInChunks; - // Host memory used only in GPU tracking - std::vector mHostNTracklets; - std::vector mHostNCells; - // Temporary buffer for storing output tracks from GPU tracking std::vector mTrackITSExt; }; @@ -215,6 +240,16 @@ inline int TimeFrameGPU::getNClustersInRofSpan(const int rofIdstart, co { return static_cast(mROFramesClusters[layerId][(rofIdstart + rofSpanSize) < mROFramesClusters.size() ? rofIdstart + rofSpanSize : mROFramesClusters.size() - 1] - mROFramesClusters[layerId][rofIdstart]); } + +template +inline std::vector TimeFrameGPU::getClusterSizes() +{ + std::vector sizes(mUnsortedClusters.size()); + std::transform(mUnsortedClusters.begin(), mUnsortedClusters.end(), sizes.begin(), + [](const auto& v) { return static_cast(v.size()); }); + return sizes; +} + } // namespace gpu } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h index 34e6165b9530f..f50a11a83805f 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h @@ -40,6 +40,7 @@ GPUg() void fitTrackSeedsKernel( CellSeed* trackSeeds, const TrackingFrameInfo** foundTrackingFrameInfo, o2::its::TrackITSExt* tracks, + const float* minPts, const unsigned int nSeeds, const float Bz, const int startLevel, @@ -50,11 +51,74 @@ GPUg() void fitTrackSeedsKernel( #endif } // namespace gpu +template +void countTrackletsInROFsHandler(const IndexTableUtils* utils, + const uint8_t* multMask, + const int startROF, + const int endROF, + const int maxROF, + const int deltaROF, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const int nVertices, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const int iteration, + const float NSigmaCut, + std::vector& phiCuts, + const float resolutionPV, + std::vector& minR, + std::vector& maxR, + std::vector& resolutions, + std::vector& radii, + std::vector& mulScatAng, + const int nBlocks, + const int nThreads); + +template +void computeTrackletsInROFsHandler(const IndexTableUtils* utils, + const uint8_t* multMask, + const int startROF, + const int endROF, + const int maxROF, + const int deltaROF, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const int nVertices, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + Tracklet** tracklets, + gsl::span spanTracklets, + gsl::span nTracklets, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const int iteration, + const float NSigmaCut, + std::vector& phiCuts, + const float resolutionPV, + std::vector& minR, + std::vector& maxR, + std::vector& resolutions, + std::vector& radii, + std::vector& mulScatAng, + const int nBlocks, + const int nThreads); + void countCellsHandler(const Cluster** sortedClusters, const Cluster** unsortedClusters, const TrackingFrameInfo** tfInfo, - const Tracklet** tracklets, - const int** trackletsLUT, + Tracklet** tracklets, + int** trackletsLUT, const int nTracklets, const int layer, CellSeed* cells, @@ -70,8 +134,8 @@ void countCellsHandler(const Cluster** sortedClusters, void computeCellsHandler(const Cluster** sortedClusters, const Cluster** unsortedClusters, const TrackingFrameInfo** tfInfo, - const Tracklet** tracklets, - const int** trackletsLUT, + Tracklet** tracklets, + int** trackletsLUT, const int nTracklets, const int layer, CellSeed* cells, @@ -112,13 +176,36 @@ void computeCellNeighboursHandler(CellSeed** cellsLayersDevice, const int nBlocks, const int nThreads); -void filterCellNeighboursHandler(std::vector&, - gpuPair*, - unsigned int); +int filterCellNeighboursHandler(std::vector&, + gpuPair*, + int*, + unsigned int); + +template +void processNeighboursHandler(const int startLayer, + const int startLevel, + CellSeed** allCellSeeds, + CellSeed* currentCellSeeds, + const unsigned int nCurrentCells, + const unsigned char** usedClusters, + int* neighbours, + gsl::span neighboursDeviceLUTs, + const TrackingFrameInfo** foundTrackingFrameInfo, + const float bz, + const float MaxChi2ClusterAttachment, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + const std::vector& lastCellIdHost, // temporary host vector + const std::vector& lastCellSeedHost, // temporary host vector + std::vector& updatedCellIdHost, // temporary host vector + std::vector& updatedCellSeedHost, // temporary host vector + const int nBlocks, + const int nThreads); void trackSeedHandler(CellSeed* trackSeeds, const TrackingFrameInfo** foundTrackingFrameInfo, o2::its::TrackITSExt* tracks, + std::vector& minPtsHost, const unsigned int nSeeds, const float Bz, const int startLevel, diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Utils.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Utils.h index 66244bf854b5f..a88e51742e84a 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Utils.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Utils.h @@ -31,6 +31,49 @@ struct gpuPair { namespace gpu { +// Poor man implementation of a span-like struct. It is very limited. +template +struct gpuSpan { + using value_type = T; + using ptr = T*; + using ref = T&; + + GPUd() gpuSpan() : _data(nullptr), _size(0) {} + GPUd() gpuSpan(ptr data, unsigned int dim) : _data(data), _size(dim) {} + GPUd() ref operator[](unsigned int idx) const { return _data[idx]; } + GPUd() unsigned int size() const { return _size; } + GPUd() bool empty() const { return _size == 0; } + GPUd() ref front() const { return _data[0]; } + GPUd() ref back() const { return _data[_size - 1]; } + GPUd() ptr begin() const { return _data; } + GPUd() ptr end() const { return _data + _size; } + + protected: + ptr _data; + unsigned int _size; +}; + +template +struct gpuSpan { + using value_type = T; + using ptr = const T*; + using ref = const T&; + + GPUd() gpuSpan() : _data(nullptr), _size(0) {} + GPUd() gpuSpan(ptr data, unsigned int dim) : _data(data), _size(dim) {} + GPUd() gpuSpan(const gpuSpan& other) : _data(other._data), _size(other._size) {} + GPUd() ref operator[](unsigned int idx) const { return _data[idx]; } + GPUd() unsigned int size() const { return _size; } + GPUd() bool empty() const { return _size == 0; } + GPUd() ref front() const { return _data[0]; } + GPUd() ref back() const { return _data[_size - 1]; } + GPUd() ptr begin() const { return _data; } + GPUd() ptr end() const { return _data + _size; } + + protected: + ptr _data; + unsigned int _size; +}; enum class Task { Tracker = 0, diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu index 67144ba2c98ea..fd067b9930fd0 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu @@ -92,6 +92,19 @@ void TimeFrameGPU::setDevicePropagator(const o2::base::PropagatorImpl +void TimeFrameGPU::loadIndexTableUtils(const int iteration) +{ + START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "loading indextable utils"); + if (!iteration) { + LOGP(debug, "gpu-allocation: allocating IndexTableUtils buffer, for {} MB.", sizeof(IndexTableUtils) / MB); + allocMemAsync(reinterpret_cast(&mIndexTableUtilsDevice), sizeof(IndexTableUtils), nullptr, getExtAllocator()); + } + LOGP(debug, "gpu-transfer: loading IndexTableUtils object, for {} MB.", sizeof(IndexTableUtils) / MB); + checkGPUError(cudaMemcpyAsync(mIndexTableUtilsDevice, &mIndexTableUtils, sizeof(IndexTableUtils), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); +} + template void TimeFrameGPU::loadUnsortedClustersDevice(const int iteration) { @@ -128,6 +141,65 @@ void TimeFrameGPU::loadClustersDevice(const int iteration) } } +template +void TimeFrameGPU::loadClustersIndexTables(const int iteration) +{ + if (!iteration) { + START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "loading sorted clusters"); + for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { + LOGP(debug, "gpu-transfer: loading clusters indextable for layer {} with {} elements, for {} MB.", iLayer, mIndexTables[iLayer].size(), mIndexTables[iLayer].size() * sizeof(int) / MB); + allocMemAsync(reinterpret_cast(&mClustersIndexTablesDevice[iLayer]), mIndexTables[iLayer].size() * sizeof(int), nullptr, getExtAllocator()); + checkGPUError(cudaMemcpyAsync(mClustersIndexTablesDevice[iLayer], mIndexTables[iLayer].data(), mIndexTables[iLayer].size() * sizeof(int), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + } + allocMemAsync(reinterpret_cast(&mClustersIndexTablesDeviceArray), nLayers * sizeof(int), nullptr, getExtAllocator()); + checkGPUError(cudaMemcpyAsync(mClustersIndexTablesDeviceArray, mClustersIndexTablesDevice.data(), nLayers * sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); + } +} + +template +void TimeFrameGPU::createUsedClustersDevice(const int iteration) +{ + if (!iteration) { + START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "creating used clusters flags"); + for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { + LOGP(debug, "gpu-transfer: creating {} used clusters flags on layer {}, for {} MB.", mUsedClusters[iLayer].size(), iLayer, mUsedClusters[iLayer].size() * sizeof(unsigned char) / MB); + allocMemAsync(reinterpret_cast(&mUsedClustersDevice[iLayer]), mUsedClusters[iLayer].size() * sizeof(unsigned char), nullptr, getExtAllocator()); + checkGPUError(cudaMemsetAsync(mUsedClustersDevice[iLayer], 0, mUsedClusters[iLayer].size() * sizeof(unsigned char), mGpuStreams[0].get())); + } + allocMemAsync(reinterpret_cast(&mUsedClustersDeviceArray), nLayers * sizeof(unsigned char*), nullptr, getExtAllocator()); + checkGPUError(cudaMemcpyAsync(mUsedClustersDeviceArray, mUsedClustersDevice.data(), nLayers * sizeof(unsigned char*), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); + } +} + +template +void TimeFrameGPU::loadUsedClustersDevice() +{ + START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "loading used clusters flags"); + for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { + LOGP(debug, "gpu-transfer: loading {} used clusters flags on layer {}, for {} MB.", mUsedClusters[iLayer].size(), iLayer, mClusters[iLayer].size() * sizeof(unsigned char) / MB); + checkGPUError(cudaMemcpyAsync(mUsedClustersDevice[iLayer], mUsedClusters[iLayer].data(), mUsedClusters[iLayer].size() * sizeof(unsigned char), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + } + STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); +} + +template +void TimeFrameGPU::loadROframeClustersDevice(const int iteration) +{ + if (!iteration) { + START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "loading ROframe clusters"); + for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { + LOGP(debug, "gpu-transfer: loading {} ROframe clusters info on layer {}, for {} MB.", mROFramesClusters[iLayer].size(), iLayer, mROFramesClusters[iLayer].size() * sizeof(int) / MB); + allocMemAsync(reinterpret_cast(&mROFramesClustersDevice[iLayer]), mROFramesClusters[iLayer].size() * sizeof(int), nullptr, getExtAllocator()); + checkGPUError(cudaMemcpyAsync(mROFramesClustersDevice[iLayer], mROFramesClusters[iLayer].data(), mROFramesClusters[iLayer].size() * sizeof(int), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + } + allocMemAsync(reinterpret_cast(&mROFrameClustersDeviceArray), nLayers * sizeof(int*), nullptr, getExtAllocator()); + checkGPUError(cudaMemcpyAsync(mROFrameClustersDeviceArray, mROFramesClustersDevice.data(), nLayers * sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); + } +} + template void TimeFrameGPU::loadTrackingFrameInfoDevice(const int iteration) { @@ -146,19 +218,76 @@ void TimeFrameGPU::loadTrackingFrameInfoDevice(const int iteration) STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); } +template +void TimeFrameGPU::loadMultiplicityCutMask(const int iteration) +{ + if (!iteration) { + START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "loading multiplicity cut mask"); + LOGP(debug, "gpu-transfer: loading multiplicity cut mask with {} elements, for {} MB.", mMultiplicityCutMask.size(), mMultiplicityCutMask.size() * sizeof(bool) / MB); + allocMemAsync(reinterpret_cast(&mMultMaskDevice), mMultiplicityCutMask.size() * sizeof(uint8_t), nullptr, getExtAllocator()); + checkGPUError(cudaMemcpyAsync(mMultMaskDevice, mMultiplicityCutMask.data(), mMultiplicityCutMask.size() * sizeof(uint8_t), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); + } +} + +template +void TimeFrameGPU::loadVertices(const int iteration) +{ + if (!iteration) { + START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "loading seeding vertices"); + LOGP(debug, "gpu-transfer: loading {} ROframes vertices, for {} MB.", mROFramesPV.size(), mROFramesPV.size() * sizeof(int) / MB); + allocMemAsync(reinterpret_cast(&mROFramesPVDevice), mROFramesPV.size() * sizeof(int), nullptr, getExtAllocator()); + checkGPUError(cudaMemcpyAsync(mROFramesPVDevice, mROFramesPV.data(), mROFramesPV.size() * sizeof(int), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + LOGP(debug, "gpu-transfer: loading {} seeding vertices, for {} MB.", mPrimaryVertices.size(), mPrimaryVertices.size() * sizeof(Vertex) / MB); + allocMemAsync(reinterpret_cast(&mPrimaryVerticesDevice), mPrimaryVertices.size() * sizeof(Vertex), nullptr, getExtAllocator()); + checkGPUError(cudaMemcpyAsync(mPrimaryVerticesDevice, mPrimaryVertices.data(), mPrimaryVertices.size() * sizeof(Vertex), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); + } +} + +template +void TimeFrameGPU::createTrackletsLUTDevice(const int iteration) +{ + START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "creating tracklets LUTs"); + for (auto iLayer{0}; iLayer < nLayers - 1; ++iLayer) { + if (!iteration) { + LOGP(debug, "gpu-transfer: creating tracklets LUT for {} elements on layer {}, for {} MB.", mClusters[iLayer].size() + 1, iLayer, (mClusters[iLayer].size() + 1) * sizeof(int) / MB); + allocMemAsync(reinterpret_cast(&mTrackletsLUTDevice[iLayer]), (mClusters[iLayer].size() + 1) * sizeof(int), nullptr, getExtAllocator()); + } + checkGPUError(cudaMemsetAsync(mTrackletsLUTDevice[iLayer], 0, (mClusters[iLayer].size() + 1) * sizeof(int), mGpuStreams[0].get())); + } + if (!iteration) { + allocMemAsync(reinterpret_cast(&mTrackletsLUTDeviceArray), (nLayers - 1) * sizeof(int*), nullptr, getExtAllocator()); + checkGPUError(cudaMemcpyAsync(mTrackletsLUTDeviceArray, mTrackletsLUTDevice.data(), mTrackletsLUTDevice.size() * sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + } + STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); +} + +template +void TimeFrameGPU::createTrackletsBuffers() +{ + START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "creating cells buffers"); + for (auto iLayer{0}; iLayer < nLayers - 1; ++iLayer) { + mNTracklets[iLayer] = 0; + checkGPUError(cudaMemcpyAsync(&mNTracklets[iLayer], mTrackletsLUTDevice[iLayer] + mClusters[iLayer].size(), sizeof(int), cudaMemcpyDeviceToHost)); + LOGP(debug, "gpu-transfer: creating tracklets buffer for {} elements on layer {}, for {} MB.", mNTracklets[iLayer], iLayer, mNTracklets[iLayer] * sizeof(Tracklet) / MB); + allocMemAsync(reinterpret_cast(&mTrackletsDevice[iLayer]), mNTracklets[iLayer] * sizeof(Tracklet), nullptr, getExtAllocator()); + } + allocMemAsync(reinterpret_cast(&mTrackletsDeviceArray), (nLayers - 1) * sizeof(Tracklet*), nullptr, getExtAllocator()); + checkGPUError(cudaHostRegister(mTrackletsDevice.data(), (nLayers - 1) * sizeof(Tracklet*), cudaHostRegisterPortable)); + checkGPUError(cudaMemcpyAsync(mTrackletsDeviceArray, mTrackletsDevice.data(), (nLayers - 1) * sizeof(Tracklet*), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); +} + template void TimeFrameGPU::loadTrackletsDevice() { START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "loading tracklets"); for (auto iLayer{0}; iLayer < nLayers - 1; ++iLayer) { LOGP(debug, "gpu-transfer: loading {} tracklets on layer {}, for {} MB.", mTracklets[iLayer].size(), iLayer, mTracklets[iLayer].size() * sizeof(Tracklet) / MB); - allocMemAsync(reinterpret_cast(&mTrackletsDevice[iLayer]), mTracklets[iLayer].size() * sizeof(Tracklet), nullptr, getExtAllocator()); checkGPUError(cudaHostRegister(mTracklets[iLayer].data(), mTracklets[iLayer].size() * sizeof(Tracklet), cudaHostRegisterPortable)); checkGPUError(cudaMemcpyAsync(mTrackletsDevice[iLayer], mTracklets[iLayer].data(), mTracklets[iLayer].size() * sizeof(Tracklet), cudaMemcpyHostToDevice, mGpuStreams[0].get())); } - allocMemAsync(reinterpret_cast(&mTrackletsDeviceArray), (nLayers - 1) * sizeof(Tracklet*), nullptr, getExtAllocator()); - checkGPUError(cudaHostRegister(mTrackletsDevice.data(), (nLayers - 1) * sizeof(Tracklet*), cudaHostRegisterPortable)); - checkGPUError(cudaMemcpyAsync(mTrackletsDeviceArray, mTrackletsDevice.data(), (nLayers - 1) * sizeof(Tracklet*), cudaMemcpyHostToDevice, mGpuStreams[0].get())); STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); } @@ -167,29 +296,38 @@ void TimeFrameGPU::loadTrackletsLUTDevice() { START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "loading tracklets"); for (auto iLayer{0}; iLayer < nLayers - 2; ++iLayer) { - LOGP(debug, "gpu-transfer: loading tracklets LUT for {} elements on layer {}, for {} MB", mTrackletsLookupTable[iLayer].size(), iLayer, mTrackletsLookupTable[iLayer].size() * sizeof(int) / MB); - allocMemAsync(reinterpret_cast(&mTrackletsLUTDevice[iLayer]), mTrackletsLookupTable[iLayer].size() * sizeof(int), nullptr, getExtAllocator()); + LOGP(debug, "gpu-transfer: loading tracklets LUT for {} elements on layer {}, for {} MB", mTrackletsLookupTable[iLayer].size(), iLayer + 1, mTrackletsLookupTable[iLayer].size() * sizeof(int) / MB); checkGPUError(cudaHostRegister(mTrackletsLookupTable[iLayer].data(), mTrackletsLookupTable[iLayer].size() * sizeof(int), cudaHostRegisterPortable)); - checkGPUError(cudaMemcpyAsync(mTrackletsLUTDevice[iLayer], mTrackletsLookupTable[iLayer].data(), mTrackletsLookupTable[iLayer].size() * sizeof(int), cudaMemcpyHostToDevice)); + checkGPUError(cudaMemcpyAsync(mTrackletsLUTDevice[iLayer + 1], mTrackletsLookupTable[iLayer].data(), mTrackletsLookupTable[iLayer].size() * sizeof(int), cudaMemcpyHostToDevice)); } - allocMemAsync(reinterpret_cast(&mTrackletsLUTDeviceArray), (nLayers - 2) * sizeof(int*), nullptr, getExtAllocator()); - checkGPUError(cudaHostRegister(mTrackletsLUTDevice.data(), (nLayers - 2) * sizeof(int*), cudaHostRegisterPortable)); - checkGPUError(cudaMemcpyAsync(mTrackletsLUTDeviceArray, mTrackletsLUTDevice.data(), (nLayers - 2) * sizeof(int*), cudaMemcpyHostToDevice)); + checkGPUError(cudaHostRegister(mTrackletsLUTDevice.data(), (nLayers - 1) * sizeof(int*), cudaHostRegisterPortable)); + checkGPUError(cudaMemcpyAsync(mTrackletsLUTDeviceArray, mTrackletsLUTDevice.data(), (nLayers - 1) * sizeof(int*), cudaMemcpyHostToDevice)); STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); } template -void TimeFrameGPU::createNeighboursDevice() +void TimeFrameGPU::createNeighboursIndexTablesDevice() { - START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "loading cell seeds"); + START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "creating cells neighbours"); + // Here we do also the creation of the CellsDeviceArray, as the cells buffers are populated separately in the previous steps. + allocMemAsync(reinterpret_cast(&mCellsDeviceArray), (nLayers - 2) * sizeof(CellSeed*), nullptr, getExtAllocator()); + checkGPUError(cudaHostRegister(mCellsDevice.data(), (nLayers - 2) * sizeof(CellSeed*), cudaHostRegisterPortable)); + checkGPUError(cudaMemcpyAsync(mCellsDeviceArray, mCellsDevice.data(), (nLayers - 2) * sizeof(CellSeed*), cudaMemcpyHostToDevice, mGpuStreams[0].get())); for (auto iLayer{0}; iLayer < nLayers - 2; ++iLayer) { LOGP(debug, "gpu-transfer: loading neighbours LUT for {} elements on layer {}, for {} MB.", mNCells[iLayer], iLayer, mNCells[iLayer] * sizeof(CellSeed) / MB); allocMemAsync(reinterpret_cast(&mNeighboursIndexTablesDevice[iLayer]), (mNCells[iLayer] + 1) * sizeof(int), nullptr, getExtAllocator()); checkGPUError(cudaMemsetAsync(mNeighboursIndexTablesDevice[iLayer], 0, (mNCells[iLayer] + 1) * sizeof(int), mGpuStreams[0].get())); } - allocMemAsync(reinterpret_cast(&mCellsDeviceArray), (nLayers - 2) * sizeof(CellSeed*), nullptr, getExtAllocator()); - checkGPUError(cudaHostRegister(mCellsDevice.data(), (nLayers - 2) * sizeof(CellSeed*), cudaHostRegisterPortable)); - checkGPUError(cudaMemcpyAsync(mCellsDeviceArray, mCellsDevice.data(), (nLayers - 2) * sizeof(CellSeed*), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); +} + +template +void TimeFrameGPU::createNeighboursLUTDevice(const int layer, const unsigned int nCells) +{ + START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "reserving neighboursLUT"); + LOGP(debug, "gpu-allocation: reserving neighbours LUT for {} elements on layer {} , for {} MB.", nCells + 1, layer, (nCells + 1) * sizeof(int) / MB); + allocMemAsync(reinterpret_cast(&mNeighboursLUTDevice[layer]), (nCells + 1) * sizeof(int), nullptr, getExtAllocator()); // We need one element more to move exc -> inc + checkGPUError(cudaMemsetAsync(mNeighboursLUTDevice[layer], 0, (nCells + 1) * sizeof(int), mGpuStreams[0].get())); STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); } @@ -214,9 +352,9 @@ void TimeFrameGPU::createCellsLUTDevice() { START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "creating cells LUTs"); for (auto iLayer{0}; iLayer < nLayers - 2; ++iLayer) { - LOGP(debug, "gpu-transfer: creating cell LUT for {} elements on layer {}, for {} MB.", mTracklets[iLayer].size() + 1, iLayer, (mTracklets[iLayer].size() + 1) * sizeof(int) / MB); - allocMemAsync(reinterpret_cast(&mCellsLUTDevice[iLayer]), (mTracklets[iLayer].size() + 1) * sizeof(int), nullptr, getExtAllocator()); - checkGPUError(cudaMemsetAsync(mCellsLUTDevice[iLayer], 0, (mTracklets[iLayer].size() + 1) * sizeof(int), mGpuStreams[0].get())); + LOGP(debug, "gpu-transfer: creating cell LUT for {} elements on layer {}, for {} MB.", mNTracklets[iLayer] + 1, iLayer, (mNTracklets[iLayer] + 1) * sizeof(int) / MB); + allocMemAsync(reinterpret_cast(&mCellsLUTDevice[iLayer]), (mNTracklets[iLayer] + 1) * sizeof(int), nullptr, getExtAllocator()); + checkGPUError(cudaMemsetAsync(mCellsLUTDevice[iLayer], 0, (mNTracklets[iLayer] + 1) * sizeof(int), mGpuStreams[0].get())); } allocMemAsync(reinterpret_cast(&mCellsLUTDeviceArray), (nLayers - 2) * sizeof(int*), nullptr, getExtAllocator()); checkGPUError(cudaMemcpyAsync(mCellsLUTDeviceArray, mCellsLUTDevice.data(), mCellsLUTDevice.size() * sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[0].get())); @@ -228,7 +366,7 @@ void TimeFrameGPU::createCellsBuffers(const int layer) { START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "creating cells buffers"); mNCells[layer] = 0; - checkGPUError(cudaMemcpyAsync(&mNCells[layer], mCellsLUTDevice[layer] + mTracklets[layer].size(), sizeof(int), cudaMemcpyDeviceToHost)); + checkGPUError(cudaMemcpyAsync(&mNCells[layer], mCellsLUTDevice[layer] + mNTracklets[layer], sizeof(int), cudaMemcpyDeviceToHost)); LOGP(debug, "gpu-transfer: creating cell buffer for {} elements on layer {}, for {} MB.", mNCells[layer], layer, mNCells[layer] * sizeof(CellSeed) / MB); allocMemAsync(reinterpret_cast(&mCellsDevice[layer]), mNCells[layer] * sizeof(CellSeed), nullptr, getExtAllocator()); @@ -273,19 +411,20 @@ void TimeFrameGPU::createNeighboursDevice(const unsigned int& layer, st START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "reserving neighbours"); mCellsNeighbours[layer].clear(); mCellsNeighbours[layer].resize(neighbours.size()); + LOGP(debug, "gpu-allocation: reserving {} neighbours (pairs), for {} MB.", neighbours.size(), neighbours.size() * sizeof(gpuPair) / MB); + allocMemAsync(reinterpret_cast(&mNeighbourPairsDevice[layer]), neighbours.size() * sizeof(gpuPair), &(mGpuStreams[0]), getExtAllocator()); + checkGPUError(cudaMemsetAsync(mNeighbourPairsDevice[layer], -1, neighbours.size() * sizeof(gpuPair), mGpuStreams[0].get())); LOGP(debug, "gpu-allocation: reserving {} neighbours, for {} MB.", neighbours.size(), neighbours.size() * sizeof(gpuPair) / MB); - allocMemAsync(reinterpret_cast(&mNeighboursDevice[layer]), neighbours.size() * sizeof(gpuPair), &(mGpuStreams[0]), getExtAllocator()); - checkGPUError(cudaMemsetAsync(mNeighboursDevice[layer], -1, neighbours.size() * sizeof(gpuPair), mGpuStreams[0].get())); + allocMemAsync(reinterpret_cast(&mNeighboursDevice[layer]), neighbours.size() * sizeof(int), &(mGpuStreams[0]), getExtAllocator()); STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); } template -void TimeFrameGPU::createNeighboursLUTDevice(const int layer, const unsigned int nCells) +void TimeFrameGPU::createNeighboursDeviceArray() { - START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "reserving neighboursLUT"); - LOGP(debug, "gpu-allocation: reserving neighbours LUT for {} elements on layer {} , for {} MB.", nCells + 1, layer, (nCells + 1) * sizeof(int) / MB); - allocMemAsync(reinterpret_cast(&mNeighboursLUTDevice[layer]), (nCells + 1) * sizeof(int), nullptr, getExtAllocator()); // We need one element more to move exc -> inc - checkGPUError(cudaMemsetAsync(mNeighboursLUTDevice[layer], 0, (nCells + 1) * sizeof(int), mGpuStreams[0].get())); + START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "reserving neighbours"); + allocMemAsync(reinterpret_cast(&mNeighboursDeviceArray), (nLayers - 2) * sizeof(int*), &(mGpuStreams[0]), getExtAllocator()); + checkGPUError(cudaMemcpyAsync(mNeighboursDeviceArray, mNeighboursDevice.data(), (nLayers - 2) * sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[0].get())); STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); } @@ -319,9 +458,9 @@ void TimeFrameGPU::downloadCellsLUTDevice() { START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "downloading cell luts"); for (auto iLayer{0}; iLayer < nLayers - 3; ++iLayer) { - LOGP(debug, "gpu-transfer: downloading cells lut on layer {} for {} elements", iLayer, (mTracklets[iLayer + 1].size() + 1)); - mCellsLookupTable[iLayer].resize(mTracklets[iLayer + 1].size() + 1); - checkGPUError(cudaMemcpyAsync(mCellsLookupTable[iLayer].data(), mCellsLUTDevice[iLayer + 1], (mTracklets[iLayer + 1].size() + 1) * sizeof(int), cudaMemcpyDeviceToHost, mGpuStreams[0].get())); + LOGP(debug, "gpu-transfer: downloading cells lut on layer {} for {} elements", iLayer, (mNTracklets[iLayer + 1] + 1)); + mCellsLookupTable[iLayer].resize(mNTracklets[iLayer + 1] + 1); + checkGPUError(cudaMemcpyAsync(mCellsLookupTable[iLayer].data(), mCellsLUTDevice[iLayer + 1], (mNTracklets[iLayer + 1] + 1) * sizeof(int), cudaMemcpyDeviceToHost, mGpuStreams[0].get())); } STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); } @@ -332,7 +471,7 @@ void TimeFrameGPU::downloadCellsNeighboursDevice(std::vector) / MB); // TODO: something less dangerous than assuming the same memory layout of std::pair and gpuPair... or not? :) - checkGPUError(cudaMemcpyAsync(neighbours[layer].data(), mNeighboursDevice[layer], neighbours[layer].size() * sizeof(gpuPair), cudaMemcpyDeviceToHost, mGpuStreams[0].get())); + checkGPUError(cudaMemcpyAsync(neighbours[layer].data(), mNeighbourPairsDevice[layer], neighbours[layer].size() * sizeof(gpuPair), cudaMemcpyDeviceToHost, mGpuStreams[0].get())); } template @@ -362,13 +501,6 @@ void TimeFrameGPU::unregisterRest() LOGP(debug, "unregistering rest of the host memory..."); checkGPUError(cudaHostUnregister(mCellsDevice.data())); checkGPUError(cudaHostUnregister(mTrackletsDevice.data())); - checkGPUError(cudaHostUnregister(mTrackletsLUTDevice.data())); - for (auto iLayer{0}; iLayer < nLayers - 1; ++iLayer) { - if (iLayer < nLayers - 2) { - checkGPUError(cudaHostUnregister(mTrackletsLookupTable[iLayer].data())); - } - checkGPUError(cudaHostUnregister(mTracklets[iLayer].data())); - } STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); } diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx index 3c6a307fc4ff6..395aab3a470ac 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx @@ -31,241 +31,18 @@ void TrackerTraitsGPU::initialiseTimeFrame(const int iteration) mTimeFrameGPU->initialise(iteration, mTrkParams[iteration], nLayers); mTimeFrameGPU->loadClustersDevice(iteration); mTimeFrameGPU->loadUnsortedClustersDevice(iteration); + mTimeFrameGPU->loadClustersIndexTables(iteration); mTimeFrameGPU->loadTrackingFrameInfoDevice(iteration); + mTimeFrameGPU->loadMultiplicityCutMask(iteration); + mTimeFrameGPU->loadVertices(iteration); + mTimeFrameGPU->loadROframeClustersDevice(iteration); + mTimeFrameGPU->createUsedClustersDevice(iteration); + mTimeFrameGPU->loadIndexTableUtils(iteration); } template void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int, int) { - // if (!mTimeFrameGPU->getClusters().size()) { - // return; - // } - // 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); - // gsl::span diamondSpan(&diamondVert, 1); - // std::vector threads(mTimeFrameGPU->getNChunks()); - - // for (int chunkId{0}; chunkId < mTimeFrameGPU->getNChunks(); ++chunkId) { - // int maxTracklets{static_cast(mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->clustersPerROfCapacity) * - // static_cast(mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->maxTrackletsPerCluster)}; - // int maxRofPerChunk{mTimeFrameGPU->mNrof / (int)mTimeFrameGPU->getNChunks()}; - // // Define workload - // auto doTrackReconstruction = [&, chunkId, maxRofPerChunk, iteration]() -> void { - // auto offset = chunkId * maxRofPerChunk; - // auto maxROF = offset + maxRofPerChunk; - // while (offset < maxROF) { - // auto rofs = mTimeFrameGPU->loadChunkData(chunkId, offset, maxROF); - // //////////////////// - // /// Tracklet finding - - // for (int iLayer{0}; iLayer < nLayers - 1; ++iLayer) { - // auto nclus = mTimeFrameGPU->getTotalClustersPerROFrange(offset, rofs, iLayer); - // const float meanDeltaR{mTrkParams[iteration].LayerRadii[iLayer + 1] - mTrkParams[iteration].LayerRadii[iLayer]}; - // gpu::computeLayerTrackletsKernelMultipleRof<<getStream(chunkId).get()>>>( - // iLayer, // const int layerIndex, - // iteration, // const int iteration, - // offset, // const unsigned int startRofId, - // rofs, // const unsigned int rofSize, - // 0, // const unsigned int deltaRof, - // mTimeFrameGPU->getChunk(chunkId).getDeviceClusters(iLayer), // const Cluster* clustersCurrentLayer, - // mTimeFrameGPU->getChunk(chunkId).getDeviceClusters(iLayer + 1), // const Cluster* clustersNextLayer, - // mTimeFrameGPU->getDeviceROframesClusters(iLayer), // const int* roFrameClustersCurrentLayer, // Number of clusters on layer 0 per ROF - // mTimeFrameGPU->getDeviceROframesClusters(iLayer + 1), // const int* roFrameClustersNextLayer, // Number of clusters on layer 1 per ROF - // mTimeFrameGPU->getChunk(chunkId).getDeviceIndexTables(iLayer + 1), // const int* indexTableNextLayer, - // mTimeFrameGPU->getDeviceUsedClusters(iLayer), // const int* usedClustersCurrentLayer, - // mTimeFrameGPU->getDeviceUsedClusters(iLayer + 1), // const int* usedClustersNextLayer, - // mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer), // Tracklet* tracklets, // output data - // mTimeFrameGPU->getDeviceVertices(), // const Vertex* vertices, - // mTimeFrameGPU->getDeviceROframesPV(), // const int* pvROFrame, - // mTimeFrameGPU->getPhiCut(iLayer), // const float phiCut, - // mTimeFrameGPU->getMinR(iLayer + 1), // const float minR, - // mTimeFrameGPU->getMaxR(iLayer + 1), // const float maxR, - // meanDeltaR, // const float meanDeltaR, - // mTimeFrameGPU->getPositionResolution(iLayer), // const float positionResolution, - // mTimeFrameGPU->getMSangle(iLayer), // const float mSAngle, - // mTimeFrameGPU->getDeviceTrackingParameters(), // const StaticTrackingParameters* trkPars, - // mTimeFrameGPU->getDeviceIndexTableUtils(), // const IndexTableUtils* utils - // mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->clustersPerROfCapacity, // const int clustersPerROfCapacity, - // mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->maxTrackletsPerCluster); // const int maxTrackletsPerCluster - - // // Remove empty tracklets due to striding. - // auto nulltracklet = o2::its::Tracklet{}; - // auto thrustTrackletsBegin = thrust::device_ptr(mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer)); - // auto thrustTrackletsEnd = thrust::device_ptr(mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer) + (int)rofs * maxTracklets); - // auto thrustTrackletsAfterEraseEnd = thrust::remove(THRUST_NAMESPACE::par.on(mTimeFrameGPU->getStream(chunkId).get()), - // thrustTrackletsBegin, - // thrustTrackletsEnd, - // nulltracklet); - // // Sort tracklets by first cluster index. - // thrust::sort(THRUST_NAMESPACE::par.on(mTimeFrameGPU->getStream(chunkId).get()), - // thrustTrackletsBegin, - // thrustTrackletsAfterEraseEnd, - // gpu::trackletSortIndexFunctor()); - - // // Remove duplicates. - // auto thrustTrackletsAfterUniqueEnd = thrust::unique(THRUST_NAMESPACE::par.on(mTimeFrameGPU->getStream(chunkId).get()), thrustTrackletsBegin, thrustTrackletsAfterEraseEnd); - - // discardResult(cudaStreamSynchronize(mTimeFrameGPU->getStream(chunkId).get())); - // mTimeFrameGPU->getHostNTracklets(chunkId)[iLayer] = thrustTrackletsAfterUniqueEnd - thrustTrackletsBegin; - // // Compute tracklet lookup table. - // gpu::compileTrackletsLookupTableKernel<<getStream(chunkId).get()>>>(mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer), - // mTimeFrameGPU->getChunk(chunkId).getDeviceTrackletsLookupTables(iLayer), - // mTimeFrameGPU->getHostNTracklets(chunkId)[iLayer]); - // discardResult(cub::DeviceScan::ExclusiveSum(mTimeFrameGPU->getChunk(chunkId).getDeviceCUBTmpBuffer(), // d_temp_storage - // mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->tmpCUBBufferSize, // temp_storage_bytes - // mTimeFrameGPU->getChunk(chunkId).getDeviceTrackletsLookupTables(iLayer), // d_in - // mTimeFrameGPU->getChunk(chunkId).getDeviceTrackletsLookupTables(iLayer), // d_out - // nclus, // num_items - // mTimeFrameGPU->getStream(chunkId).get())); - - // // Create tracklets labels, at the moment on the host - // if (mTimeFrameGPU->hasMCinformation()) { - // std::vector tracklets(mTimeFrameGPU->getHostNTracklets(chunkId)[iLayer]); - // checkGPUError(cudaHostRegister(tracklets.data(), tracklets.size() * sizeof(o2::its::Tracklet), cudaHostRegisterDefault)); - // checkGPUError(cudaMemcpyAsync(tracklets.data(), mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer), tracklets.size() * sizeof(o2::its::Tracklet), cudaMemcpyDeviceToHost, mTimeFrameGPU->getStream(chunkId).get())); - // for (auto& trk : tracklets) { - // MCCompLabel label; - // int currentId{mTimeFrameGPU->mClusters[iLayer][trk.firstClusterIndex].clusterId}; // This is not yet offsetted to the index of the first cluster of the chunk - // int nextId{mTimeFrameGPU->mClusters[iLayer + 1][trk.secondClusterIndex].clusterId}; // This is not yet offsetted to the index of the first cluster of the chunk - // for (auto& lab1 : mTimeFrameGPU->getClusterLabels(iLayer, currentId)) { - // for (auto& lab2 : mTimeFrameGPU->getClusterLabels(iLayer + 1, nextId)) { - // if (lab1 == lab2 && lab1.isValid()) { - // label = lab1; - // break; - // } - // } - // if (label.isValid()) { - // break; - // } - // } - // // TODO: implment label merging. - // // mTimeFrameGPU->getTrackletsLabel(iLayer).emplace_back(label); - // } - // checkGPUError(cudaHostUnregister(tracklets.data())); - // } - // } - - // //////////////// - // /// Cell finding - // for (int iLayer{0}; iLayer < nLayers - 2; ++iLayer) { - // // Compute layer cells. - // gpu::computeLayerCellsKernel<<<10, 1024, 0, mTimeFrameGPU->getStream(chunkId).get()>>>( - // mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer), - // mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer + 1), - // mTimeFrameGPU->getChunk(chunkId).getDeviceTrackletsLookupTables(iLayer + 1), - // mTimeFrameGPU->getHostNTracklets(chunkId)[iLayer], - // nullptr, - // mTimeFrameGPU->getChunk(chunkId).getDeviceCellsLookupTables(iLayer), - // mTimeFrameGPU->getDeviceTrackingParameters()); - - // // Compute number of found Cells - // checkGPUError(cub::DeviceReduce::Sum(mTimeFrameGPU->getChunk(chunkId).getDeviceCUBTmpBuffer(), // d_temp_storage - // mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->tmpCUBBufferSize, // temp_storage_bytes - // mTimeFrameGPU->getChunk(chunkId).getDeviceCellsLookupTables(iLayer), // d_in - // mTimeFrameGPU->getChunk(chunkId).getDeviceNFoundCells() + iLayer, // d_out - // mTimeFrameGPU->getHostNTracklets(chunkId)[iLayer], // num_items - // mTimeFrameGPU->getStream(chunkId).get())); - // // Compute LUT - // discardResult(cub::DeviceScan::ExclusiveSum(mTimeFrameGPU->getChunk(chunkId).getDeviceCUBTmpBuffer(), // d_temp_storage - // mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->tmpCUBBufferSize, // temp_storage_bytes - // mTimeFrameGPU->getChunk(chunkId).getDeviceCellsLookupTables(iLayer), // d_in - // mTimeFrameGPU->getChunk(chunkId).getDeviceCellsLookupTables(iLayer), // d_out - // mTimeFrameGPU->getHostNTracklets(chunkId)[iLayer], // num_items - // mTimeFrameGPU->getStream(chunkId).get())); - - // gpu::computeLayerCellsKernel<<<10, 1024, 0, mTimeFrameGPU->getStream(chunkId).get()>>>( - // mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer), - // mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer + 1), - // mTimeFrameGPU->getChunk(chunkId).getDeviceTrackletsLookupTables(iLayer + 1), - // mTimeFrameGPU->getHostNTracklets(chunkId)[iLayer], - // mTimeFrameGPU->getChunk(chunkId).getDeviceCells(iLayer), - // mTimeFrameGPU->getChunk(chunkId).getDeviceCellsLookupTables(iLayer), - // mTimeFrameGPU->getDeviceTrackingParameters()); - // } - // checkGPUError(cudaMemcpyAsync(mTimeFrameGPU->getHostNCells(chunkId).data(), - // mTimeFrameGPU->getChunk(chunkId).getDeviceNFoundCells(), - // (nLayers - 2) * sizeof(int), - // cudaMemcpyDeviceToHost, - // mTimeFrameGPU->getStream(chunkId).get())); - - // // Create cells labels - // // TODO: make it work after fixing the tracklets labels - // if (mTimeFrameGPU->hasMCinformation()) { - // for (int iLayer{0}; iLayer < nLayers - 2; ++iLayer) { - // std::vector cells(mTimeFrameGPU->getHostNCells(chunkId)[iLayer]); - // // Async with not registered memory? - // checkGPUError(cudaMemcpyAsync(cells.data(), mTimeFrameGPU->getChunk(chunkId).getDeviceCells(iLayer), mTimeFrameGPU->getHostNCells(chunkId)[iLayer] * sizeof(o2::its::Cell), cudaMemcpyDeviceToHost)); - // for (auto& cell : cells) { - // MCCompLabel currentLab{mTimeFrameGPU->getTrackletsLabel(iLayer)[cell.getFirstTrackletIndex()]}; - // MCCompLabel nextLab{mTimeFrameGPU->getTrackletsLabel(iLayer + 1)[cell.getSecondTrackletIndex()]}; - // mTimeFrameGPU->getCellsLabel(iLayer).emplace_back(currentLab == nextLab ? currentLab : MCCompLabel()); - // } - // } - // } - - // ///////////////////// - // /// Neighbour finding - // for (int iLayer{0}; iLayer < nLayers - 3; ++iLayer) { - // gpu::computeLayerCellNeighboursKernel<<<10, 1024, 0, mTimeFrameGPU->getStream(chunkId).get()>>>( - // mTimeFrameGPU->getChunk(chunkId).getDeviceCells(iLayer), - // mTimeFrameGPU->getChunk(chunkId).getDeviceCells(iLayer + 1), - // iLayer, - // mTimeFrameGPU->getChunk(chunkId).getDeviceCellsLookupTables(iLayer + 1), - // mTimeFrameGPU->getChunk(chunkId).getDeviceCellNeigboursLookupTables(iLayer), - // nullptr, - // mTimeFrameGPU->getChunk(chunkId).getDeviceNFoundCells(), - // mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->maxNeighboursSize); - - // // Compute Cell Neighbours LUT - // checkGPUError(cub::DeviceScan::ExclusiveSum(mTimeFrameGPU->getChunk(chunkId).getDeviceCUBTmpBuffer(), // d_temp_storage - // mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->tmpCUBBufferSize, // temp_storage_bytes - // mTimeFrameGPU->getChunk(chunkId).getDeviceCellNeigboursLookupTables(iLayer), // d_in - // mTimeFrameGPU->getChunk(chunkId).getDeviceCellNeigboursLookupTables(iLayer), // d_out - // mTimeFrameGPU->getHostNCells(chunkId)[iLayer + 1], // num_items - // mTimeFrameGPU->getStream(chunkId).get())); - - // gpu::computeLayerCellNeighboursKernel<<<10, 1024, 0, mTimeFrameGPU->getStream(chunkId).get()>>>( - // mTimeFrameGPU->getChunk(chunkId).getDeviceCells(iLayer), - // mTimeFrameGPU->getChunk(chunkId).getDeviceCells(iLayer + 1), - // iLayer, - // mTimeFrameGPU->getChunk(chunkId).getDeviceCellsLookupTables(iLayer + 1), - // mTimeFrameGPU->getChunk(chunkId).getDeviceCellNeigboursLookupTables(iLayer), - // mTimeFrameGPU->getChunk(chunkId).getDeviceCellNeighbours(iLayer), - // mTimeFrameGPU->getChunk(chunkId).getDeviceNFoundCells(), - // mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->maxNeighboursSize); - - // // if (!chunkId) { - // // gpu::printBufferLayerOnThread<<<1, 1, 0, mTimeFrameGPU->getStream(chunkId).get()>>>(iLayer, - // // mTimeFrameGPU->getChunk(chunkId).getDeviceCellNeighbours(iLayer), - // // mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->maxNeighboursSize * rofs); - // // } - // } - // // Download cells into vectors - - // for (int iLevel{nLayers - 2}; iLevel >= mTrkParams[iteration].CellMinimumLevel(); --iLevel) { - // const int minimumLevel{iLevel - 1}; - // for (int iLayer{nLayers - 3}; iLayer >= minimumLevel; --iLayer) { - // // gpu::computeLayerRoadsKernel<<<1, 1, 0, mTimeFrameGPU->getStream(chunkId).get()>>>(iLevel, // const int level, - // // iLayer, // const int layerIndex, - // // mTimeFrameGPU->getChunk(chunkId).getDeviceArrayCells(), // const CellSeed** cells, - // // mTimeFrameGPU->getChunk(chunkId).getDeviceNFoundCells(), // const int* nCells, - // // mTimeFrameGPU->getChunk(chunkId).getDeviceArrayNeighboursCell(), // const int** neighbours, - // // mTimeFrameGPU->getChunk(chunkId).getDeviceArrayNeighboursCellLUT(), // const int** neighboursLUT, - // // mTimeFrameGPU->getChunk(chunkId).getDeviceRoads(), // Road* roads, - // // mTimeFrameGPU->getChunk(chunkId).getDeviceRoadsLookupTables(iLayer)); // int* roadsLookupTable - // } - // } - - // // End of tracking for this chunk - // offset += rofs; - // } - // }; - // threads[chunkId] = std::thread(doTrackReconstruction); - // } - // for (auto& thread : threads) { - // thread.join(); - // } - - // mTimeFrameGPU->wipe(nLayers); } template @@ -299,7 +76,7 @@ int TrackerTraitsGPU::getTFNumberOfClusters() const template int TrackerTraitsGPU::getTFNumberOfTracklets() const { - return mTimeFrameGPU->getNumberOfTracklets(); + return std::accumulate(mTimeFrameGPU->getNTracklets().begin(), mTimeFrameGPU->getNTracklets().end(), 0); } template @@ -313,31 +90,93 @@ int TrackerTraitsGPU::getTFNumberOfCells() const template void TrackerTraitsGPU::computeTrackletsHybrid(const int iteration, int iROFslice, int iVertex) { - TrackerTraits::computeLayerTracklets(iteration, iROFslice, iVertex); + auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); + mTimeFrameGPU->createTrackletsLUTDevice(iteration); + + 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); + gsl::span diamondSpan(&diamondVert, 1); + int startROF{mTrkParams[iteration].nROFsPerIterations > 0 ? iROFslice * mTrkParams[iteration].nROFsPerIterations : 0}; + int endROF{o2::gpu::CAMath::Min(mTrkParams[iteration].nROFsPerIterations > 0 ? (iROFslice + 1) * mTrkParams[iteration].nROFsPerIterations + mTrkParams[iteration].DeltaROF : mTimeFrameGPU->getNrof(), mTimeFrameGPU->getNrof())}; + + countTrackletsInROFsHandler(mTimeFrameGPU->getDeviceIndexTableUtils(), + mTimeFrameGPU->getDeviceMultCutMask(), + startROF, + endROF, + mTimeFrameGPU->getNrof(), + mTrkParams[iteration].DeltaROF, + iVertex, + mTimeFrameGPU->getDeviceVertices(), + mTimeFrameGPU->getDeviceROFramesPV(), + mTimeFrameGPU->getPrimaryVerticesNum(), + mTimeFrameGPU->getDeviceArrayClusters(), + mTimeFrameGPU->getClusterSizes(), + mTimeFrameGPU->getDeviceROframeClusters(), + mTimeFrameGPU->getDeviceArrayUsedClusters(), + mTimeFrameGPU->getDeviceArrayClustersIndexTables(), + mTimeFrameGPU->getDeviceArrayTrackletsLUT(), + mTimeFrameGPU->getDeviceTrackletsLUTs(), // Required for the exclusive sums + iteration, + mTrkParams[iteration].NSigmaCut, + mTimeFrameGPU->getPhiCuts(), + mTrkParams[iteration].PVres, + mTimeFrameGPU->getMinRs(), + mTimeFrameGPU->getMaxRs(), + mTimeFrameGPU->getPositionResolutions(), + mTrkParams[iteration].LayerRadii, + mTimeFrameGPU->getMSangles(), + conf.nBlocks, + conf.nThreads); + mTimeFrameGPU->createTrackletsBuffers(); + computeTrackletsInROFsHandler(mTimeFrameGPU->getDeviceIndexTableUtils(), + mTimeFrameGPU->getDeviceMultCutMask(), + startROF, + endROF, + mTimeFrameGPU->getNrof(), + mTrkParams[iteration].DeltaROF, + iVertex, + mTimeFrameGPU->getDeviceVertices(), + mTimeFrameGPU->getDeviceROFramesPV(), + mTimeFrameGPU->getPrimaryVerticesNum(), + mTimeFrameGPU->getDeviceArrayClusters(), + mTimeFrameGPU->getClusterSizes(), + mTimeFrameGPU->getDeviceROframeClusters(), + mTimeFrameGPU->getDeviceArrayUsedClusters(), + mTimeFrameGPU->getDeviceArrayClustersIndexTables(), + mTimeFrameGPU->getDeviceArrayTracklets(), + mTimeFrameGPU->getDeviceTracklet(), + mTimeFrameGPU->getNTracklets(), + mTimeFrameGPU->getDeviceArrayTrackletsLUT(), + mTimeFrameGPU->getDeviceTrackletsLUTs(), + iteration, + mTrkParams[iteration].NSigmaCut, + mTimeFrameGPU->getPhiCuts(), + mTrkParams[iteration].PVres, + mTimeFrameGPU->getMinRs(), + mTimeFrameGPU->getMaxRs(), + mTimeFrameGPU->getPositionResolutions(), + mTrkParams[iteration].LayerRadii, + mTimeFrameGPU->getMSangles(), + conf.nBlocks, + conf.nThreads); } template void TrackerTraitsGPU::computeCellsHybrid(const int iteration) { - mTimeFrameGPU->loadTrackletsDevice(); - mTimeFrameGPU->loadTrackletsLUTDevice(); mTimeFrameGPU->createCellsLUTDevice(); auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); - // #pragma omp parallel for num_threads(nLayers) for (int iLayer = 0; iLayer < mTrkParams[iteration].CellsPerRoad(); ++iLayer) { - if (mTimeFrameGPU->getTracklets()[iLayer + 1].empty() || - mTimeFrameGPU->getTracklets()[iLayer].empty()) { + if (!mTimeFrameGPU->getNTracklets()[iLayer + 1] || !mTimeFrameGPU->getNTracklets()[iLayer]) { continue; } - - const int currentLayerTrackletsNum{static_cast(mTimeFrameGPU->getTracklets()[iLayer].size())}; + const int currentLayerTrackletsNum{static_cast(mTimeFrameGPU->getNTracklets()[iLayer])}; countCellsHandler(mTimeFrameGPU->getDeviceArrayClusters(), mTimeFrameGPU->getDeviceArrayUnsortedClusters(), mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), mTimeFrameGPU->getDeviceArrayTracklets(), mTimeFrameGPU->getDeviceArrayTrackletsLUT(), - mTimeFrameGPU->getTracklets()[iLayer].size(), + mTimeFrameGPU->getNTracklets()[iLayer], iLayer, nullptr, mTimeFrameGPU->getDeviceArrayCellsLUT(), @@ -354,7 +193,7 @@ void TrackerTraitsGPU::computeCellsHybrid(const int iteration) mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), mTimeFrameGPU->getDeviceArrayTracklets(), mTimeFrameGPU->getDeviceArrayTrackletsLUT(), - mTimeFrameGPU->getTracklets()[iLayer].size(), + mTimeFrameGPU->getNTracklets()[iLayer], iLayer, mTimeFrameGPU->getDeviceCells()[iLayer], mTimeFrameGPU->getDeviceArrayCellsLUT(), @@ -374,11 +213,11 @@ void TrackerTraitsGPU::computeCellsHybrid(const int iteration) template void TrackerTraitsGPU::findCellsNeighboursHybrid(const int iteration) { - mTimeFrameGPU->createNeighboursDevice(); + mTimeFrameGPU->createNeighboursIndexTablesDevice(); auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); std::vector>> cellsNeighboursLayer(mTrkParams[iteration].CellsPerRoad() - 1); for (int iLayer{0}; iLayer < mTrkParams[iteration].CellsPerRoad() - 1; ++iLayer) { - const int nextLayerCellsNum{static_cast(mTimeFrameGPU->getNCellsDevice()[iLayer + 1])}; + const int nextLayerCellsNum{static_cast(mTimeFrameGPU->getNCells()[iLayer + 1])}; mTimeFrameGPU->getCellsNeighboursLUT()[iLayer].clear(); mTimeFrameGPU->getCellsNeighboursLUT()[iLayer].resize(nextLayerCellsNum, 0); @@ -388,17 +227,16 @@ void TrackerTraitsGPU::findCellsNeighboursHybrid(const int iteration) continue; } - int layerCellsNum{static_cast(mTimeFrameGPU->getCells()[iLayer].size())}; mTimeFrameGPU->createNeighboursLUTDevice(iLayer, nextLayerCellsNum); countCellNeighboursHandler(mTimeFrameGPU->getDeviceArrayCells(), mTimeFrameGPU->getDeviceNeighboursLUT(iLayer), // LUT is initialised here. mTimeFrameGPU->getDeviceArrayCellsLUT(), - mTimeFrameGPU->getDeviceNeighbours(iLayer), + mTimeFrameGPU->getDeviceNeighbourPairs(iLayer), mTimeFrameGPU->getDeviceNeighboursIndexTables(iLayer), mTrkParams[0].MaxChi2ClusterAttachment, mBz, iLayer, - layerCellsNum, + mTimeFrameGPU->getNCells()[iLayer], nextLayerCellsNum, 1e2, conf.nBlocks, @@ -410,12 +248,12 @@ void TrackerTraitsGPU::findCellsNeighboursHybrid(const int iteration) computeCellNeighboursHandler(mTimeFrameGPU->getDeviceArrayCells(), mTimeFrameGPU->getDeviceNeighboursLUT(iLayer), mTimeFrameGPU->getDeviceArrayCellsLUT(), - mTimeFrameGPU->getDeviceNeighbours(iLayer), + mTimeFrameGPU->getDeviceNeighbourPairs(iLayer), mTimeFrameGPU->getDeviceNeighboursIndexTables(iLayer), mTrkParams[0].MaxChi2ClusterAttachment, mBz, iLayer, - layerCellsNum, + mTimeFrameGPU->getNCells()[iLayer], nextLayerCellsNum, 1e2, conf.nBlocks, @@ -424,9 +262,11 @@ void TrackerTraitsGPU::findCellsNeighboursHybrid(const int iteration) mTimeFrameGPU->getCellsNeighbours()[iLayer].reserve(cellsNeighboursLayer[iLayer].size()); filterCellNeighboursHandler(mTimeFrameGPU->getCellsNeighbours()[iLayer], + mTimeFrameGPU->getDeviceNeighbourPairs(iLayer), mTimeFrameGPU->getDeviceNeighbours(iLayer), cellsNeighboursLayer[iLayer].size()); } + mTimeFrameGPU->createNeighboursDeviceArray(); mTimeFrameGPU->downloadCellsDevice(); mTimeFrameGPU->unregisterRest(); }; @@ -434,14 +274,36 @@ void TrackerTraitsGPU::findCellsNeighboursHybrid(const int iteration) template void TrackerTraitsGPU::findRoads(const int iteration) { + auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); for (int startLevel{mTrkParams[iteration].CellsPerRoad()}; startLevel >= mTrkParams[iteration].CellMinimumLevel(); --startLevel) { const int minimumLayer{startLevel - 1}; std::vector trackSeeds; for (int startLayer{mTrkParams[iteration].CellsPerRoad() - 1}; startLayer >= minimumLayer; --startLayer) { + if ((mTrkParams[iteration].StartLayerMask & (1 << (startLayer + 2))) == 0) { + continue; + } std::vector lastCellId, updatedCellId; std::vector lastCellSeed, updatedCellSeed; - processNeighbours(startLayer, startLevel, mTimeFrame->getCells()[startLayer], lastCellId, updatedCellSeed, updatedCellId); + processNeighboursHandler(startLayer, + startLevel, + mTimeFrameGPU->getDeviceArrayCells(), + mTimeFrameGPU->getDeviceCells()[startLayer], + mTimeFrameGPU->getNCells()[startLayer], + mTimeFrameGPU->getDeviceArrayUsedClusters(), + mTimeFrameGPU->getDeviceNeighbours(startLayer - 1), + mTimeFrameGPU->getDeviceNeighboursLUTs(), + mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), + mBz, + mTrkParams[0].MaxChi2ClusterAttachment, // float maxChi2ClusterAttachment + mTimeFrameGPU->getDevicePropagator(), + mCorrType, + lastCellId, // temporary host vector + lastCellSeed, // temporary host vector + updatedCellId, // temporary host vectors + updatedCellSeed, // temporary host vectors + conf.nBlocks, + conf.nThreads); int level = startLevel; for (int iLayer{startLayer - 1}; iLayer > 0 && level > 2; --iLayer) { @@ -449,7 +311,25 @@ void TrackerTraitsGPU::findRoads(const int iteration) lastCellId.swap(updatedCellId); std::vector().swap(updatedCellSeed); /// tame the memory peaks updatedCellId.clear(); - processNeighbours(iLayer, --level, lastCellSeed, lastCellId, updatedCellSeed, updatedCellId); + processNeighboursHandler(iLayer, + --level, + mTimeFrameGPU->getDeviceArrayCells(), + mTimeFrameGPU->getDeviceCells()[iLayer], + mTimeFrameGPU->getNCells()[iLayer], + mTimeFrameGPU->getDeviceArrayUsedClusters(), + mTimeFrameGPU->getDeviceNeighbours(iLayer - 1), + mTimeFrameGPU->getDeviceNeighboursLUTs(), + mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), + mBz, + mTrkParams[0].MaxChi2ClusterAttachment, // float maxChi2ClusterAttachment + mTimeFrameGPU->getDevicePropagator(), + mCorrType, + lastCellId, // temporary host vector + lastCellSeed, // temporary host vector + updatedCellId, // temporary host vectors + updatedCellSeed, // temporary host vectors + conf.nBlocks, + conf.nThreads); } for (auto& seed : updatedCellSeed) { if (seed.getQ2Pt() > 1.e3 || seed.getChi2() > mTrkParams[0].MaxChi2NDF * ((startLevel + 2) * 2 - 5)) { @@ -464,15 +344,16 @@ void TrackerTraitsGPU::findRoads(const int iteration) } mTimeFrameGPU->createTrackITSExtDevice(trackSeeds); mTimeFrameGPU->loadTrackSeedsDevice(trackSeeds); - auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); - trackSeedHandler(mTimeFrameGPU->getDeviceTrackSeeds(), // CellSeed* trackSeeds, - mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), // TrackingFrameInfo** foundTrackingFrameInfo, - mTimeFrameGPU->getDeviceTrackITSExt(), // o2::its::TrackITSExt* tracks, - trackSeeds.size(), // const size_t nSeeds, - mBz, // const float Bz, + + trackSeedHandler(mTimeFrameGPU->getDeviceTrackSeeds(), // CellSeed* trackSeeds + mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), // TrackingFrameInfo** foundTrackingFrameInfo + mTimeFrameGPU->getDeviceTrackITSExt(), // o2::its::TrackITSExt* tracks + mTrkParams[iteration].MinPt, // std::vector& minPtsHost, + trackSeeds.size(), // const size_t nSeeds + mBz, // const float Bz startLevel, // const int startLevel, - mTrkParams[0].MaxChi2ClusterAttachment, // float maxChi2ClusterAttachment, - mTrkParams[0].MaxChi2NDF, // float maxChi2NDF, + mTrkParams[0].MaxChi2ClusterAttachment, // float maxChi2ClusterAttachment + mTrkParams[0].MaxChi2NDF, // float maxChi2NDF mTimeFrameGPU->getDevicePropagator(), // const o2::base::Propagator* propagator mCorrType, // o2::base::PropagatorImpl::MatCorrType conf.nBlocks, @@ -495,8 +376,8 @@ void TrackerTraitsGPU::findRoads(const int iteration) if (track.getClusterIndex(iLayer) == UnusedIndex) { continue; } - nShared += int(mTimeFrame->isClusterUsed(iLayer, track.getClusterIndex(iLayer))); - isFirstShared |= !iLayer && mTimeFrame->isClusterUsed(iLayer, track.getClusterIndex(iLayer)); + nShared += int(mTimeFrameGPU->isClusterUsed(iLayer, track.getClusterIndex(iLayer))); + isFirstShared |= !iLayer && mTimeFrameGPU->isClusterUsed(iLayer, track.getClusterIndex(iLayer)); } if (nShared > mTrkParams[0].ClusterSharing) { @@ -508,8 +389,8 @@ void TrackerTraitsGPU::findRoads(const int iteration) if (track.getClusterIndex(iLayer) == UnusedIndex) { continue; } - mTimeFrame->markUsedCluster(iLayer, track.getClusterIndex(iLayer)); - int currentROF = mTimeFrame->getClusterROF(iLayer, track.getClusterIndex(iLayer)); + 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; @@ -525,8 +406,9 @@ void TrackerTraitsGPU::findRoads(const int iteration) if (rofs[1] != INT_MAX) { track.setNextROFbit(); } - mTimeFrame->getTracks(std::min(rofs[0], rofs[1])).emplace_back(track); + mTimeFrameGPU->getTracks(std::min(rofs[0], rofs[1])).emplace_back(track); } + mTimeFrameGPU->loadUsedClustersDevice(); } if (iteration == mTrkParams.size() - 1) { mTimeFrameGPU->unregisterHostMemory(0); diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu index 73dcf3bcb4894..19edef6c40346 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu @@ -32,6 +32,7 @@ #include "ITStracking/IndexTableUtils.h" #include "ITStracking/MathUtils.h" #include "DataFormatsITS/TrackITS.h" +#include "ReconstructionDataFormats/Vertex.h" #include "ITStrackingGPU/TrackerTraitsGPU.h" #include "ITStrackingGPU/TrackingKernels.h" @@ -70,12 +71,39 @@ inline void gpuAssert(cudaError_t code, const char* file, int line, bool abort = } namespace o2::its - { using namespace constants::its2; +using Vertex = o2::dataformats::Vertex>; + +GPUd() float Sq(float v) +{ + return v * v; +} namespace gpu { + +GPUd() const int4 getBinsRect(const Cluster& currentCluster, const int layerIndex, + const o2::its::IndexTableUtils& utils, + const float z1, const float z2, float maxdeltaz, float maxdeltaphi) +{ + const float zRangeMin = o2::gpu::CAMath::Min(z1, z2) - maxdeltaz; + const float phiRangeMin = (maxdeltaphi > constants::math::Pi) ? 0.f : currentCluster.phi - maxdeltaphi; + const float zRangeMax = o2::gpu::CAMath::Max(z1, z2) + maxdeltaz; + const float phiRangeMax = (maxdeltaphi > constants::math::Pi) ? constants::math::TwoPi : currentCluster.phi + maxdeltaphi; + + if (zRangeMax < -LayersZCoordinate()[layerIndex + 1] || + zRangeMin > LayersZCoordinate()[layerIndex + 1] || zRangeMin > zRangeMax) { + + return getEmptyBinsRect(); + } + + return int4{o2::gpu::CAMath::Max(0, utils.getZBinIndex(layerIndex + 1, zRangeMin)), + utils.getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMin)), + o2::gpu::CAMath::Min(ZBins - 1, utils.getZBinIndex(layerIndex + 1, zRangeMax)), + utils.getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMax))}; +} + GPUd() bool fitTrack(TrackITSExt& track, int start, int end, @@ -84,7 +112,7 @@ GPUd() bool fitTrack(TrackITSExt& track, float chi2ndfcut, float maxQoverPt, int nCl, - float Bz, + float bz, const TrackingFrameInfo** tfInfos, const o2::base::Propagator* prop, o2::base::PropagatorF::MatCorrType matCorrType) @@ -100,7 +128,7 @@ GPUd() bool fitTrack(TrackITSExt& track, if (!prop->propagateToX(track, trackingHit.xTrackingFrame, - Bz, + bz, o2::base::PropagatorImpl::MAX_SIN_PHI, o2::base::PropagatorImpl::MAX_STEP, matCorrType)) { @@ -127,7 +155,7 @@ GPUd() bool fitTrack(TrackITSExt& track, } nCl++; } - return o2::gpu::GPUCommonMath::Abs(track.getQ2Pt()) < maxQoverPt && track.getChi2() < chi2ndfcut * (nCl * 2 - 5); + return o2::gpu::CAMath::Abs(track.getQ2Pt()) < maxQoverPt && track.getChi2() < chi2ndfcut * (nCl * 2 - 5); } GPUd() o2::track::TrackParCov buildTrackSeed(const Cluster& cluster1, @@ -146,7 +174,7 @@ GPUd() o2::track::TrackParCov buildTrackSeed(const Cluster& cluster1, const float y3 = tf3.positionTrackingFrame[0]; const float z3 = tf3.positionTrackingFrame[1]; - const bool zeroField{o2::gpu::GPUCommonMath::Abs(bz) < o2::constants::math::Almost0}; + const bool zeroField{o2::gpu::CAMath::Abs(bz) < o2::constants::math::Almost0}; const float tgp = zeroField ? o2::gpu::CAMath::ATan2(y3 - y1, x3 - x1) : 1.f; const float crv = zeroField ? 1.f : math_utils::computeCurvature(x3, y3, x2, y2, x1, y1); const float snp = zeroField ? tgp / o2::gpu::CAMath::Sqrt(1.f + tgp * tgp) : crv * (x3 - math_utils::computeCurvatureCentreX(x3, y3, x2, y2, x1, y1)); @@ -164,6 +192,14 @@ GPUd() o2::track::TrackParCov buildTrackSeed(const Cluster& cluster1, 0.f, 0.f, 0.f, 0.f, sg2q2pt}); } +struct sort_tracklets { + GPUhd() bool operator()(const Tracklet& a, const Tracklet& b) { return a.firstClusterIndex < b.firstClusterIndex || (a.firstClusterIndex == b.firstClusterIndex && a.secondClusterIndex < b.secondClusterIndex); } +}; + +struct equal_tracklets { + GPUhd() bool operator()(const Tracklet& a, const Tracklet& b) { return a.firstClusterIndex == b.firstClusterIndex && a.secondClusterIndex == b.secondClusterIndex; } +}; + template struct pair_to_first : public thrust::unary_function, T1> { GPUhd() int operator()(const gpuPair& a) const @@ -196,13 +232,41 @@ struct is_valid_pair { } }; +GPUd() gpuSpan getPrimaryVertices(const int rof, + const int* roframesPV, + const int nROF, + const uint8_t* mask, + const Vertex* vertices) +{ + const int start_pv_id = roframesPV[rof]; + const int stop_rof = rof >= nROF - 1 ? nROF : rof + 1; + size_t delta = mask[rof] ? roframesPV[stop_rof] - start_pv_id : 0; // return empty span if ROF is excluded + return gpuSpan(&vertices[start_pv_id], delta); +}; + +GPUd() gpuSpan getClustersOnLayer(const int rof, + const int totROFs, + const int layer, + const int** roframesClus, + const Cluster** clusters) +{ + if (rof < 0 || rof >= totROFs) { + return gpuSpan(); + } + const int start_clus_id{roframesClus[layer][rof]}; + const int stop_rof = rof >= totROFs - 1 ? totROFs : rof + 1; + const unsigned int delta = roframesClus[layer][stop_rof] - start_clus_id; + return gpuSpan(&(clusters[layer][start_clus_id]), delta); +} + template GPUg() void fitTrackSeedsKernel( CellSeed* trackSeeds, const TrackingFrameInfo** foundTrackingFrameInfo, o2::its::TrackITSExt* tracks, + const float* minPts, const unsigned int nSeeds, - const float Bz, + const float bz, const int startLevel, float maxChi2ClusterAttachment, float maxChi2NDF, @@ -228,7 +292,7 @@ GPUg() void fitTrackSeedsKernel( maxChi2NDF, // float maxChi2NDF, o2::constants::math::VeryBig, // float maxQoverPt, 0, // nCl, - Bz, // float Bz, + bz, // float bz, foundTrackingFrameInfo, // TrackingFrameInfo** trackingFrameInfo, propagator, // const o2::base::Propagator* propagator, matCorrType); // o2::base::PropagatorF::MatCorrType matCorrType @@ -247,11 +311,11 @@ GPUg() void fitTrackSeedsKernel( maxChi2NDF, // float maxChi2NDF, 50.f, // float maxQoverPt, 0, // nCl, - Bz, // float Bz, + bz, // float bz, foundTrackingFrameInfo, // TrackingFrameInfo** trackingFrameInfo, propagator, // const o2::base::Propagator* propagator, matCorrType); // o2::base::PropagatorF::MatCorrType matCorrType - if (!fitSuccess) { + if (!fitSuccess || temporaryTrack.getPt() < minPts[nLayers - temporaryTrack.getNClusters()]) { continue; } tracks[iCurrentTrackSeedIndex] = temporaryTrack; @@ -301,7 +365,6 @@ GPUg() void computeLayerCellNeighboursKernel( // FIXME: this is prone to race conditions: check on level is not atomic const int currentCellLevel{currentCellSeed.getLevel()}; if (currentCellLevel >= nextCellSeed.getLevel()) { - // atomicExch(cellSeedArray[layerIndex + 1][iNextCell].getLevelPtr(), currentCellLevel + 1); // Update level on corresponding cell cellSeedArray[layerIndex + 1][iNextCell].setLevel(currentCellLevel + 1); } } @@ -314,8 +377,8 @@ GPUg() void computeLayerCellsKernel( const Cluster** sortedClusters, const Cluster** unsortedClusters, const TrackingFrameInfo** tfInfo, - const Tracklet** tracklets, - const int** trackletsLUT, + Tracklet** tracklets, + int** trackletsLUT, const int nTrackletsCurrent, const int layer, CellSeed* cells, @@ -331,8 +394,8 @@ GPUg() void computeLayerCellsKernel( for (int iCurrentTrackletIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentTrackletIndex < nTrackletsCurrent; iCurrentTrackletIndex += blockDim.x * gridDim.x) { const Tracklet& currentTracklet = tracklets[layer][iCurrentTrackletIndex]; const int nextLayerClusterIndex{currentTracklet.secondClusterIndex}; - const int nextLayerFirstTrackletIndex{trackletsLUT[layer][nextLayerClusterIndex]}; - const int nextLayerLastTrackletIndex{trackletsLUT[layer][nextLayerClusterIndex + 1]}; + const int nextLayerFirstTrackletIndex{trackletsLUT[layer + 1][nextLayerClusterIndex]}; + const int nextLayerLastTrackletIndex{trackletsLUT[layer + 1][nextLayerClusterIndex + 1]}; if (nextLayerFirstTrackletIndex == nextLayerLastTrackletIndex) { continue; } @@ -342,7 +405,7 @@ GPUg() void computeLayerCellsKernel( break; } const Tracklet& nextTracklet = tracklets[layer + 1][iNextTrackletIndex]; - const float deltaTanLambda{o2::gpu::GPUCommonMath::Abs(currentTracklet.tanLambda - nextTracklet.tanLambda)}; + const float deltaTanLambda{o2::gpu::CAMath::Abs(currentTracklet.tanLambda - nextTracklet.tanLambda)}; if (deltaTanLambda / cellDeltaTanLambdaSigma < nSigmaCut) { const int clusId[3]{ @@ -394,40 +457,231 @@ GPUg() void computeLayerCellsKernel( } } -///////////////////////////////////////// -// Debug Kernels -///////////////////////////////////////// -GPUd() const int4 getBinsRect(const Cluster& currentCluster, const int layerIndex, - const o2::its::IndexTableUtils& utils, - const float z1, const float z2, float maxdeltaz, float maxdeltaphi) +template +GPUg() void computeLayerTrackletsMultiROFKernel( + const IndexTableUtils* utils, + const uint8_t* multMask, + const int layerIndex, + const int startROF, + const int endROF, + const int totalROFs, + const int deltaROF, + 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()}; + for (unsigned int iROF{blockIdx.x}; iROF < endROF - startROF; iROF += gridDim.x) { + const short rof0 = iROF + startROF; + auto primaryVertices = getPrimaryVertices(rof0, rofPV, totalROFs, multMask, vertices); + 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())}; + const short minROF = o2::gpu::CAMath::Max(startROF, static_cast(rof0 - deltaROF)); + const short maxROF = o2::gpu::CAMath::Min(endROF - 1, static_cast(rof0 + deltaROF)); + auto clustersCurrentLayer = getClustersOnLayer(rof0, totalROFs, layerIndex, ROFClusters, clusters); + if (clustersCurrentLayer.empty()) { + continue; + } - if (zRangeMax < -LayersZCoordinate()[layerIndex + 1] || - zRangeMin > LayersZCoordinate()[layerIndex + 1] || zRangeMin > zRangeMax) { + for (int currentClusterIndex = threadIdx.x; currentClusterIndex < clustersCurrentLayer.size(); currentClusterIndex += blockDim.x) { + unsigned int storedTracklets{0}; + auto currentCluster{clustersCurrentLayer[currentClusterIndex]}; + const int currentSortedIndex{ROFClusters[layerIndex][rof0] + currentClusterIndex}; + if (usedClusters[layerIndex][currentCluster.clusterId]) { + continue; + } - return getEmptyBinsRect(); + const float inverseR0{1.f / currentCluster.radius}; + for (int iV{startVtx}; iV < endVtx; ++iV) { + auto& primaryVertex{primaryVertices[iV]}; + if (primaryVertex.isFlagSet(2) && iteration != 3) { + continue; + } + const float resolution = o2::gpu::CAMath::Sqrt(Sq(resolutionPV) / primaryVertex.getNContributors() + Sq(positionResolution)); + const float tanLambda{(currentCluster.zCoordinate - primaryVertex.getZ()) * inverseR0}; + const float zAtRmin{tanLambda * (minR - currentCluster.radius) + currentCluster.zCoordinate}; + const float zAtRmax{tanLambda * (maxR - currentCluster.radius) + currentCluster.zCoordinate}; + const float sqInverseDeltaZ0{1.f / (Sq(currentCluster.zCoordinate - primaryVertex.getZ()) + 2.e-8f)}; /// protecting from overflows adding the detector resolution + const float sigmaZ{o2::gpu::CAMath::Sqrt(Sq(resolution) * Sq(tanLambda) * ((Sq(inverseR0) + sqInverseDeltaZ0) * Sq(meanDeltaR) + 1.f) + Sq(meanDeltaR * MSAngle))}; + const int4 selectedBinsRect{getBinsRect(currentCluster, layerIndex, *utils, zAtRmin, zAtRmax, sigmaZ * NSigmaCut, phiCut)}; + 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 += phiBins; + } + + const int tableSize{phiBins * zBins + 1}; + for (short rof1{minROF}; rof1 <= maxROF; ++rof1) { + auto clustersNextLayer = getClustersOnLayer(rof1, totalROFs, layerIndex + 1, ROFClusters, clusters); + if (clustersNextLayer.empty()) { + continue; + } + for (int iPhiCount{0}; iPhiCount < phiBinsNum; iPhiCount++) { + int iPhiBin = (selectedBinsRect.y + iPhiCount) % phiBins; + const int firstBinIndex{utils->getBinIndex(selectedBinsRect.x, iPhiBin)}; + const int maxBinIndex{firstBinIndex + selectedBinsRect.z - selectedBinsRect.x + 1}; + const int firstRowClusterIndex = indexTables[layerIndex + 1][(rof1 - startROF) * tableSize + firstBinIndex]; + const int maxRowClusterIndex = indexTables[layerIndex + 1][(rof1 - startROF) * tableSize + maxBinIndex]; + for (int nextClusterIndex{firstRowClusterIndex}; nextClusterIndex < maxRowClusterIndex; ++nextClusterIndex) { + if (nextClusterIndex >= clustersNextLayer.size()) { + break; + } + const Cluster& nextCluster{clustersNextLayer[nextClusterIndex]}; + if (usedClusters[layerIndex + 1][nextCluster.clusterId]) { + continue; + } + const float deltaPhi{o2::gpu::CAMath::Abs(currentCluster.phi - nextCluster.phi)}; + const float deltaZ{o2::gpu::CAMath::Abs(tanLambda * (nextCluster.radius - currentCluster.radius) + currentCluster.zCoordinate - nextCluster.zCoordinate)}; + const int nextSortedIndex{ROFClusters[layerIndex + 1][rof1] + nextClusterIndex}; + if (deltaZ / sigmaZ < NSigmaCut && (deltaPhi < phiCut || o2::gpu::CAMath::Abs(deltaPhi - constants::math::TwoPi) < phiCut)) { + if constexpr (initRun) { + trackletsLUT[layerIndex][currentSortedIndex]++; // we need l0 as well for usual exclusive sums. + } else { + 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)}; + new (tracklets[layerIndex] + trackletsLUT[layerIndex][currentSortedIndex] + storedTracklets) Tracklet{currentSortedIndex, nextSortedIndex, tanL, phi, rof0, rof1}; + } + ++storedTracklets; + } + } + } + } + } + } } +} - return int4{o2::gpu::GPUCommonMath::Max(0, utils.getZBinIndex(layerIndex + 1, zRangeMin)), - utils.getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMin)), - o2::gpu::GPUCommonMath::Min(ZBins - 1, utils.getZBinIndex(layerIndex + 1, zRangeMax)), - utils.getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMax))}; +template +GPUg() void compileTrackletsLookupTableKernel(const Tracklet* tracklets, + int* trackletsLookUpTable, + const int nTracklets) +{ + for (int currentTrackletIndex = blockIdx.x * blockDim.x + threadIdx.x; currentTrackletIndex < nTracklets; currentTrackletIndex += blockDim.x * gridDim.x) { + atomicAdd(&trackletsLookUpTable[tracklets[currentTrackletIndex].firstClusterIndex], 1); + } } -GPUhd() float Sq(float q) +template +GPUg() void processNeighboursKernel(const int layer, + const int level, + CellSeed** allCellSeeds, + CellSeed* currentCellSeeds, + const int* currentCellIds, + const unsigned int nCurrentCells, + 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 + int* neighbours, + int* neighboursLUT, + const TrackingFrameInfo** foundTrackingFrameInfo, + const float bz, + const float MaxChi2ClusterAttachment, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType) { - return q * q; + constexpr float radl = 9.36f; // Radiation length of Si [cm]. + constexpr float rho = 2.33f; // Density of Si [g/cm^3]. + constexpr float layerxX0[7] = {5.e-3f, 5.e-3f, 5.e-3f, 1.e-2f, 1.e-2f, 1.e-2f, 1.e-2f}; // Hardcoded here for the moment. + for (unsigned int iCurrentCell = blockIdx.x * blockDim.x + threadIdx.x; iCurrentCell < nCurrentCells; iCurrentCell += blockDim.x * gridDim.x) { + int foundSeeds{0}; + const auto& currentCell{currentCellSeeds[iCurrentCell]}; + if (currentCell.getLevel() != level) { + continue; + } + if (currentCellIds == nullptr && (usedClusters[layer][currentCell.getFirstClusterIndex()] || + usedClusters[layer + 1][currentCell.getSecondClusterIndex()] || + usedClusters[layer + 2][currentCell.getThirdClusterIndex()])) { + continue; + } + const int cellId = currentCellIds == nullptr ? iCurrentCell : currentCellIds[iCurrentCell]; + + const int startNeighbourId{cellId ? neighboursLUT[cellId - 1] : 0}; + const int endNeighbourId{neighboursLUT[cellId]}; + + for (int iNeighbourCell{startNeighbourId}; iNeighbourCell < endNeighbourId; ++iNeighbourCell) { + const int neighbourCellId = neighbours[iNeighbourCell]; + const CellSeed& neighbourCell = allCellSeeds[layer - 1][neighbourCellId]; + + if (neighbourCell.getSecondTrackletIndex() != currentCell.getFirstTrackletIndex()) { + continue; + } + if (usedClusters[layer - 1][neighbourCell.getFirstClusterIndex()]) { + continue; + } + if (currentCell.getLevel() - 1 != neighbourCell.getLevel()) { + continue; + } + CellSeed seed{currentCell}; + auto& trHit = foundTrackingFrameInfo[layer - 1][neighbourCell.getFirstClusterIndex()]; + + if (!seed.rotate(trHit.alphaTrackingFrame)) { + continue; + } + + if (!propagator->propagateToX(seed, trHit.xTrackingFrame, bz, o2::base::PropagatorImpl::MAX_SIN_PHI, o2::base::PropagatorImpl::MAX_STEP, matCorrType)) { + continue; + } + + if (matCorrType == o2::base::PropagatorF::MatCorrType::USEMatCorrNONE) { + if (!seed.correctForMaterial(layerxX0[layer - 1], layerxX0[layer - 1] * radl * rho, true)) { + continue; + } + } + + auto predChi2{seed.getPredictedChi2Quiet(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)}; + if ((predChi2 > MaxChi2ClusterAttachment) || predChi2 < 0.f) { + continue; + } + seed.setChi2(seed.getChi2() + predChi2); + if (!seed.o2::track::TrackParCov::update(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)) { + continue; + } + seed.getClusters()[layer - 1] = neighbourCell.getFirstClusterIndex(); + seed.setLevel(neighbourCell.getLevel()); + seed.setFirstTrackletIndex(neighbourCell.getFirstTrackletIndex()); + seed.setSecondTrackletIndex(neighbourCell.getSecondTrackletIndex()); + if constexpr (dryRun) { + foundSeedsTable[iCurrentCell]++; + } else { + updatedCellsIds[foundSeedsTable[iCurrentCell] + foundSeeds] = neighbourCellId; + updatedCellSeeds[foundSeedsTable[iCurrentCell] + foundSeeds] = seed; + } + foundSeeds++; + } + } } +///////////////////////////////////////// +// Debug Kernels +///////////////////////////////////////// + template GPUd() void pPointer(T* ptr) { printf("[%p]\t", ptr); } + template GPUg() void printPointersKernel(std::tuple args) { @@ -437,7 +691,6 @@ GPUg() void printPointersKernel(std::tuple args) std::apply(print_all, args); } -// Functors to sort tracklets template struct trackletSortEmptyFunctor : public thrust::binary_function { GPUhd() bool operator()(const T& lhs, const T& rhs) const @@ -454,7 +707,6 @@ struct trackletSortIndexFunctor : public thrust::binary_function { } }; -// Print layer buffer GPUg() void printBufferLayerOnThread(const int layer, const int* v, unsigned int size, const int len = 150, const unsigned int tId = 0) { if (blockIdx.x * blockDim.x + threadIdx.x == tId) { @@ -494,52 +746,12 @@ GPUg() void printBufferPointersLayerOnThread(const int layer, void** v, unsigned } } -// Dump vertices GPUg() void printVertices(const Vertex* v, unsigned int size, const unsigned int tId = 0) { if (blockIdx.x * blockDim.x + threadIdx.x == tId) { - printf("vertices: "); + printf("vertices: \n"); for (int i{0}; i < size; ++i) { - printf("x=%f y=%f z=%f\n", v[i].getX(), v[i].getY(), v[i].getZ()); - } - } -} - -// Dump tracklets -GPUg() void printTracklets(const Tracklet* t, - const int offset, - const int startRof, - const int nrof, - const int* roFrameClustersCurrentLayer, // Number of clusters on layer 0 per ROF - const int* roFrameClustersNextLayer, // Number of clusters on layer 1 per ROF - const int maxClustersPerRof = 5e2, - const int maxTrackletsPerCluster = 50, - const unsigned int tId = 0) -{ - if (threadIdx.x == tId) { - auto offsetCurrent{roFrameClustersCurrentLayer[offset]}; - auto offsetNext{roFrameClustersNextLayer[offset]}; - auto offsetChunk{(startRof - offset) * maxClustersPerRof * maxTrackletsPerCluster}; - for (int i{offsetChunk}; i < offsetChunk + nrof * maxClustersPerRof * maxTrackletsPerCluster; ++i) { - if (t[i].firstClusterIndex != -1) { - t[i].dump(offsetCurrent, offsetNext); - } - } - } -} - -GPUg() void printTrackletsNotStrided(const Tracklet* t, - const int offset, - const int* roFrameClustersCurrentLayer, // Number of clusters on layer 0 per ROF - const int* roFrameClustersNextLayer, // Number of clusters on layer 1 per ROF - const int ntracklets, - const unsigned int tId = 0) -{ - if (threadIdx.x == tId) { - auto offsetCurrent{roFrameClustersCurrentLayer[offset]}; - auto offsetNext{roFrameClustersNextLayer[offset]}; - for (int i{0}; i < ntracklets; ++i) { - t[i].dump(offsetCurrent, offsetNext); + printf("\tx=%f y=%f z=%f\n", v[i].getX(), v[i].getY(), v[i].getZ()); } } } @@ -556,259 +768,208 @@ GPUg() void printNeighbours(const gpuPair* neighbours, } } -// Compute the tracklets for a given layer -template -GPUg() void computeLayerTrackletsKernelSingleRof( - const short rof0, - const short maxRofs, - const int layerIndex, - const Cluster* clustersCurrentLayer, // input data rof0 - const Cluster* clustersNextLayer, // input data rof0-delta * trkPars, - const IndexTableUtils* utils, - const unsigned int maxTrackletsPerCluster = 50) +GPUg() void printTrackletsLUTPerROF(const int layerId, + const int** ROFClusters, + int** luts, + const int tId = 0) { - for (int currentClusterIndex = blockIdx.x * blockDim.x + threadIdx.x; currentClusterIndex < currentLayerClustersSize; currentClusterIndex += blockDim.x * gridDim.x) { - unsigned int storedTracklets{0}; - const Cluster& currentCluster{clustersCurrentLayer[currentClusterIndex]}; - const int currentSortedIndex{roFrameClusters[rof0] + currentClusterIndex}; - if (usedClustersLayer[currentSortedIndex]) { - continue; - } - short minRof = (rof0 >= trkPars->DeltaROF) ? rof0 - trkPars->DeltaROF : 0; - short maxRof = (rof0 == static_cast(maxRofs - trkPars->DeltaROF)) ? rof0 : rof0 + trkPars->DeltaROF; - const float inverseR0{1.f / currentCluster.radius}; - for (int iPrimaryVertex{0}; iPrimaryVertex < nVertices; iPrimaryVertex++) { - const auto& primaryVertex{vertices[iPrimaryVertex]}; - if (primaryVertex.getX() == 0.f && primaryVertex.getY() == 0.f && primaryVertex.getZ() == 0.f) { - continue; - } - const float resolution{o2::gpu::GPUCommonMath::Sqrt(Sq(trkPars->PVres) / primaryVertex.getNContributors() + Sq(positionResolution))}; - const float tanLambda{(currentCluster.zCoordinate - primaryVertex.getZ()) * inverseR0}; - const float zAtRmin{tanLambda * (minR - currentCluster.radius) + currentCluster.zCoordinate}; - const float zAtRmax{tanLambda * (maxR - currentCluster.radius) + currentCluster.zCoordinate}; - const float sqInverseDeltaZ0{1.f / (Sq(currentCluster.zCoordinate - primaryVertex.getZ()) + 2.e-8f)}; /// protecting from overflows adding the detector resolution - const float sigmaZ{o2::gpu::CAMath::Sqrt(Sq(resolution) * Sq(tanLambda) * ((Sq(inverseR0) + sqInverseDeltaZ0) * Sq(meanDeltaR) + 1.f) + Sq(meanDeltaR * mSAngle))}; - - const int4 selectedBinsRect{getBinsRect(currentCluster, layerIndex, *utils, zAtRmin, zAtRmax, sigmaZ * trkPars->NSigmaCut, phiCut)}; - if (selectedBinsRect.x == 0 && selectedBinsRect.y == 0 && selectedBinsRect.z == 0 && selectedBinsRect.w == 0) { + if (blockIdx.x * blockDim.x + threadIdx.x == tId) { + for (auto rofId{0}; rofId < 2304; ++rofId) { + int nClus = ROFClusters[layerId][rofId + 1] - ROFClusters[layerId][rofId]; + if (!nClus) { continue; } - int phiBinsNum{selectedBinsRect.w - selectedBinsRect.y + 1}; - if (phiBinsNum < 0) { - phiBinsNum += trkPars->PhiBins; - } - constexpr int tableSize{256 * 128 + 1}; // hardcoded for the time being + printf("rof: %d (%d) ==> ", rofId, nClus); - for (short rof1{minRof}; rof1 <= maxRof; ++rof1) { - if (!(roFrameClustersNext[rof1 + 1] - roFrameClustersNext[rof1])) { // number of clusters on next layer > 0 - continue; - } - for (int iPhiCount{0}; iPhiCount < phiBinsNum; iPhiCount++) { - int iPhiBin = (selectedBinsRect.y + iPhiCount) % trkPars->PhiBins; - const int firstBinIndex{utils->getBinIndex(selectedBinsRect.x, iPhiBin)}; - const int maxBinIndex{firstBinIndex + selectedBinsRect.z - selectedBinsRect.x + 1}; - const int firstRowClusterIndex = indexTable[rof1 * tableSize + firstBinIndex]; - const int maxRowClusterIndex = indexTable[rof1 * tableSize + maxBinIndex]; - for (int iNextCluster{firstRowClusterIndex}; iNextCluster < maxRowClusterIndex; ++iNextCluster) { - if (iNextCluster >= (roFrameClustersNext[rof1 + 1] - roFrameClustersNext[rof1])) { - break; - } - const Cluster& nextCluster{getPtrFromRuler(rof1, clustersNextLayer, roFrameClustersNext)[iNextCluster]}; - if (usedClustersNextLayer[nextCluster.clusterId]) { - continue; - } - const float deltaPhi{o2::gpu::GPUCommonMath::Abs(currentCluster.phi - nextCluster.phi)}; - const float deltaZ{o2::gpu::GPUCommonMath::Abs(tanLambda * (nextCluster.radius - currentCluster.radius) + currentCluster.zCoordinate - nextCluster.zCoordinate)}; - - if (deltaZ / sigmaZ < trkPars->NSigmaCut && (deltaPhi < phiCut || o2::gpu::GPUCommonMath::Abs(deltaPhi - constants::math::TwoPi) < phiCut)) { - trackletsLookUpTable[currentSortedIndex]++; // Race-condition safe - const float phi{o2::gpu::GPUCommonMath::ATan2(currentCluster.yCoordinate - nextCluster.yCoordinate, currentCluster.xCoordinate - nextCluster.xCoordinate)}; - const float tanL{(currentCluster.zCoordinate - nextCluster.zCoordinate) / (currentCluster.radius - nextCluster.radius)}; - const unsigned int stride{currentClusterIndex * maxTrackletsPerCluster}; - new (tracklets + stride + storedTracklets) Tracklet{currentSortedIndex, roFrameClustersNext[rof1] + iNextCluster, tanL, phi, rof0, rof1}; - ++storedTracklets; - } - } - } + for (int iC{0}; iC < nClus; ++iC) { + int nT = luts[layerId][ROFClusters[layerId][rofId] + iC]; + printf("%d\t", nT); } + printf("\n"); } - // if (storedTracklets > maxTrackletsPerCluster) { - // printf("its-gpu-tracklet finder: found more tracklets per clusters (%d) than maximum set (%d), check the configuration!\n", maxTrackletsPerCluster, storedTracklets); - // } } } -template -GPUg() void compileTrackletsLookupTableKernel(const Tracklet* tracklets, - int* trackletsLookUpTable, - const int nTracklets) +GPUg() void printCellSeeds(CellSeed* seed, int nCells, const unsigned int tId = 0) { - for (int currentTrackletIndex = blockIdx.x * blockDim.x + threadIdx.x; currentTrackletIndex < nTracklets; currentTrackletIndex += blockDim.x * gridDim.x) { - auto& tracklet{tracklets[currentTrackletIndex]}; - if (tracklet.firstClusterIndex >= 0) { - atomicAdd(trackletsLookUpTable + tracklet.firstClusterIndex, 1); + for (unsigned int iCell{0}; iCell < nCells; ++iCell) { + if (threadIdx.x == tId) { + seed[iCell].printCell(); } } } +} // namespace gpu -template -GPUg() void computeLayerTrackletsKernelMultipleRof( - const int layerIndex, - const int iteration, - const unsigned int startRofId, - const unsigned int rofSize, - const int maxRofs, - const Cluster* clustersCurrentLayer, // input data rof0 - const Cluster* clustersNextLayer, // input data rof0-delta * trkPars, - const IndexTableUtils* utils, - const unsigned int maxClustersPerRof = 5e2, - const unsigned int maxTrackletsPerCluster = 50) +template +void countTrackletsInROFsHandler(const IndexTableUtils* utils, + const uint8_t* multMask, + const int startROF, + const int endROF, + const int maxROF, + const int deltaROF, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const int nVertices, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const int iteration, + const float NSigmaCut, + std::vector& phiCuts, + const float resolutionPV, + std::vector& minRs, + std::vector& maxRs, + std::vector& resolutions, + std::vector& radii, + std::vector& mulScatAng, + const int nBlocks, + const int nThreads) { - const int phiBins{utils->getNphiBins()}; - const int zBins{utils->getNzBins()}; - for (unsigned int iRof{blockIdx.x}; iRof < rofSize; iRof += gridDim.x) { - auto rof0 = iRof + startRofId; - auto nClustersCurrentLayerRof = o2::gpu::GPUCommonMath::Min(roFrameClustersCurrentLayer[rof0 + 1] - roFrameClustersCurrentLayer[rof0], (int)maxClustersPerRof); - // if (nClustersCurrentLayerRof > maxClustersPerRof) { - // printf("its-gpu-tracklet finder: on layer %d found more clusters per ROF (%d) than maximum set (%d), check the configuration!\n", layerIndex, nClustersCurrentLayerRof, maxClustersPerRof); - // } - auto* clustersCurrentLayerRof = clustersCurrentLayer + (roFrameClustersCurrentLayer[rof0] - roFrameClustersCurrentLayer[startRofId]); - auto nVerticesRof0 = nVertices[rof0 + 1] - nVertices[rof0]; - auto trackletsRof0 = tracklets + maxTrackletsPerCluster * maxClustersPerRof * iRof; - for (int currentClusterIndex = threadIdx.x; currentClusterIndex < nClustersCurrentLayerRof; currentClusterIndex += blockDim.x) { - unsigned int storedTracklets{0}; - const Cluster& currentCluster{clustersCurrentLayerRof[currentClusterIndex]}; - const int currentSortedIndex{roFrameClustersCurrentLayer[rof0] + currentClusterIndex}; - const int currentSortedIndexChunk{currentSortedIndex - roFrameClustersCurrentLayer[startRofId]}; - if (usedClustersLayer[currentSortedIndex]) { - continue; - } - - int minRof = (rof0 >= trkPars->DeltaROF) ? rof0 - trkPars->DeltaROF : 0; - int maxRof = (rof0 == maxRofs - trkPars->DeltaROF) ? rof0 : rof0 + trkPars->DeltaROF; // works with delta = {0, 1} - const float inverseR0{1.f / currentCluster.radius}; - - for (int iPrimaryVertex{0}; iPrimaryVertex < nVerticesRof0; iPrimaryVertex++) { - const auto& primaryVertex{vertices[nVertices[rof0] + iPrimaryVertex]}; - const float resolution{o2::gpu::GPUCommonMath::Sqrt(Sq(trkPars->PVres) / primaryVertex.getNContributors() + Sq(positionResolution))}; - const float tanLambda{(currentCluster.zCoordinate - primaryVertex.getZ()) * inverseR0}; - const float zAtRmin{tanLambda * (minR - currentCluster.radius) + currentCluster.zCoordinate}; - const float zAtRmax{tanLambda * (maxR - currentCluster.radius) + currentCluster.zCoordinate}; - const float sqInverseDeltaZ0{1.f / (Sq(currentCluster.zCoordinate - primaryVertex.getZ()) + 2.e-8f)}; /// protecting from overflows adding the detector resolution - const float sigmaZ{o2::gpu::CAMath::Sqrt(Sq(resolution) * Sq(tanLambda) * ((Sq(inverseR0) + sqInverseDeltaZ0) * Sq(meanDeltaR) + 1.f) + Sq(meanDeltaR * mSAngle))}; - - const int4 selectedBinsRect{getBinsRect(currentCluster, layerIndex, *utils, zAtRmin, zAtRmax, sigmaZ * trkPars->NSigmaCut, phiCut)}; - - 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 += trkPars->PhiBins; - } - const int tableSize{phiBins * zBins + 1}; - for (int rof1{minRof}; rof1 <= maxRof; ++rof1) { - auto nClustersNext{roFrameClustersNextLayer[rof1 + 1] - roFrameClustersNextLayer[rof1]}; - if (!nClustersNext) { // number of clusters on next layer > 0 - continue; - } - for (int iPhiCount{0}; iPhiCount < phiBinsNum; iPhiCount++) { - int iPhiBin = (selectedBinsRect.y + iPhiCount) % trkPars->PhiBins; - const int firstBinIndex{utils->getBinIndex(selectedBinsRect.x, iPhiBin)}; - const int maxBinIndex{firstBinIndex + selectedBinsRect.z - selectedBinsRect.x + 1}; - const int firstRowClusterIndex = indexTablesNext[(rof1 - startRofId) * tableSize + firstBinIndex]; - const int maxRowClusterIndex = indexTablesNext[(rof1 - startRofId) * tableSize + maxBinIndex]; - for (int iNextCluster{firstRowClusterIndex}; iNextCluster < maxRowClusterIndex; ++iNextCluster) { - if (iNextCluster >= nClustersNext) { - break; - } - auto nextClusterIndex{roFrameClustersNextLayer[rof1] - roFrameClustersNextLayer[startRofId] + iNextCluster}; - const Cluster& nextCluster{clustersNextLayer[nextClusterIndex]}; - if (usedClustersNextLayer[nextCluster.clusterId]) { - continue; - } - const float deltaPhi{o2::gpu::GPUCommonMath::Abs(currentCluster.phi - nextCluster.phi)}; - const float deltaZ{o2::gpu::GPUCommonMath::Abs(tanLambda * (nextCluster.radius - currentCluster.radius) + currentCluster.zCoordinate - nextCluster.zCoordinate)}; - - if ((deltaZ / sigmaZ < trkPars->NSigmaCut && (deltaPhi < phiCut || o2::gpu::GPUCommonMath::Abs(deltaPhi - constants::math::TwoPi) < phiCut))) { - const float phi{o2::gpu::GPUCommonMath::ATan2(currentCluster.yCoordinate - nextCluster.yCoordinate, currentCluster.xCoordinate - nextCluster.xCoordinate)}; - const float tanL{(currentCluster.zCoordinate - nextCluster.zCoordinate) / (currentCluster.radius - nextCluster.radius)}; - const unsigned int stride{currentClusterIndex * maxTrackletsPerCluster}; - if (storedTracklets < maxTrackletsPerCluster) { - new (trackletsRof0 + stride + storedTracklets) Tracklet{currentSortedIndexChunk, nextClusterIndex, tanL, phi, static_cast(rof0), static_cast(rof1)}; - } - // else { - // printf("its-gpu-tracklet-finder: on rof %d layer: %d: found more tracklets (%d) than maximum allowed per cluster. This is lossy!\n", rof0, layerIndex, storedTracklets); - // } - ++storedTracklets; - } - } - } - } - } - } + for (int iLayer = 0; iLayer < nLayers - 1; ++iLayer) { + gpu::computeLayerTrackletsMultiROFKernel<<>>( + utils, + multMask, + iLayer, + startROF, + endROF, + maxROF, + deltaROF, + vertices, + rofPV, + nVertices, + vertexId, + clusters, + ROFClusters, + usedClusters, + clustersIndexTables, + nullptr, + trackletsLUTs, + iteration, + NSigmaCut, + phiCuts[iLayer], + resolutionPV, + minRs[iLayer + 1], + maxRs[iLayer + 1], + resolutions[iLayer], + radii[iLayer + 1] - radii[iLayer], + mulScatAng[iLayer]); + void* d_temp_storage = nullptr; + size_t temp_storage_bytes = 0; + gpuCheckError(cub::DeviceScan::ExclusiveSum(d_temp_storage, // d_temp_storage + temp_storage_bytes, // temp_storage_bytes + trackletsLUTsHost[iLayer], // d_in + trackletsLUTsHost[iLayer], // d_out + nClusters[iLayer] + 1, // num_items + 0)); // NOLINT: this is the offset of the sum, not a pointer + discardResult(cudaMalloc(&d_temp_storage, temp_storage_bytes)); + gpuCheckError(cub::DeviceScan::ExclusiveSum(d_temp_storage, // d_temp_storage + temp_storage_bytes, // temp_storage_bytes + trackletsLUTsHost[iLayer], // d_in + trackletsLUTsHost[iLayer], // d_out + nClusters[iLayer] + 1, // num_items + 0)); // NOLINT: this is the offset of the sum, not a pointer + gpuCheckError(cudaFree(d_temp_storage)); } } -// Decrease LUT entries corresponding to duplicated tracklets. NB: duplicate tracklets are removed separately (see const Tracklets*). -GPUg() void removeDuplicateTrackletsEntriesLUTKernel( - int* trackletsLookUpTable, - const Tracklet* tracklets, - const int* nTracklets, - const int layerIndex) +template +void computeTrackletsInROFsHandler(const IndexTableUtils* utils, + const uint8_t* multMask, + const int startROF, + const int endROF, + const int maxROF, + const int deltaROF, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const int nVertices, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + Tracklet** tracklets, + gsl::span spanTracklets, + gsl::span nTracklets, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const int iteration, + const float NSigmaCut, + std::vector& phiCuts, + const float resolutionPV, + std::vector& minRs, + std::vector& maxRs, + std::vector& resolutions, + std::vector& radii, + std::vector& mulScatAng, + const int nBlocks, + const int nThreads) { - int id0{-1}, id1{-1}; - for (int iTracklet{0}; iTracklet < nTracklets[layerIndex]; ++iTracklet) { - auto& trk = tracklets[iTracklet]; - if (trk.firstClusterIndex == id0 && trk.secondClusterIndex == id1) { - trackletsLookUpTable[id0]--; - } else { - id0 = trk.firstClusterIndex; - id1 = trk.secondClusterIndex; + for (int iLayer = 0; iLayer < nLayers - 1; ++iLayer) { + gpu::computeLayerTrackletsMultiROFKernel<<>>(utils, + multMask, + iLayer, + startROF, + endROF, + maxROF, + deltaROF, + vertices, + rofPV, + nVertices, + vertexId, + clusters, + ROFClusters, + usedClusters, + clustersIndexTables, + tracklets, + trackletsLUTs, + iteration, + NSigmaCut, + phiCuts[iLayer], + resolutionPV, + minRs[iLayer + 1], + maxRs[iLayer + 1], + resolutions[iLayer], + radii[iLayer + 1] - radii[iLayer], + mulScatAng[iLayer]); + thrust::device_ptr tracklets_ptr(spanTracklets[iLayer]); + thrust::sort(thrust::device, tracklets_ptr, tracklets_ptr + nTracklets[iLayer], gpu::sort_tracklets()); + auto unique_end = thrust::unique(thrust::device, tracklets_ptr, tracklets_ptr + nTracklets[iLayer], gpu::equal_tracklets()); + nTracklets[iLayer] = unique_end - tracklets_ptr; + if (iLayer > 0) { + gpuCheckError(cudaMemset(trackletsLUTsHost[iLayer], 0, nClusters[iLayer] * sizeof(int))); + gpu::compileTrackletsLookupTableKernel<<>>(spanTracklets[iLayer], trackletsLUTsHost[iLayer], nTracklets[iLayer]); + void* d_temp_storage = nullptr; + size_t temp_storage_bytes = 0; + gpuCheckError(cub::DeviceScan::ExclusiveSum(d_temp_storage, // d_temp_storage + temp_storage_bytes, // temp_storage_bytes + trackletsLUTsHost[iLayer], // d_in + trackletsLUTsHost[iLayer], // d_out + nClusters[iLayer] + 1, // num_items + 0)); // NOLINT: this is the offset of the sum, not a pointer + discardResult(cudaMalloc(&d_temp_storage, temp_storage_bytes)); + gpuCheckError(cub::DeviceScan::ExclusiveSum(d_temp_storage, // d_temp_storage + temp_storage_bytes, // temp_storage_bytes + trackletsLUTsHost[iLayer], // d_in + trackletsLUTsHost[iLayer], // d_out + nClusters[iLayer] + 1, // num_items + 0)); // NOLINT: this is the offset of the sum, not a pointer + gpuCheckError(cudaFree(d_temp_storage)); } } } -} // namespace gpu - void countCellsHandler( const Cluster** sortedClusters, const Cluster** unsortedClusters, const TrackingFrameInfo** tfInfo, - const Tracklet** tracklets, - const int** trackletsLUT, + Tracklet** tracklets, + int** trackletsLUT, const int nTracklets, const int layer, CellSeed* cells, @@ -850,7 +1011,6 @@ void countCellsHandler( cellsLUTsHost, // d_out nTracklets + 1, // num_items 0)); // NOLINT: this is the offset of the sum, not a pointer - // gpu::printBufferLayerOnThread<<<1, 1>>>(layer, cellsLUTsHost, nTracklets + 1); gpuCheckError(cudaFree(d_temp_storage)); } @@ -858,8 +1018,8 @@ void computeCellsHandler( const Cluster** sortedClusters, const Cluster** unsortedClusters, const TrackingFrameInfo** tfInfo, - const Tracklet** tracklets, - const int** trackletsLUT, + Tracklet** tracklets, + int** trackletsLUT, const int nTracklets, const int layer, CellSeed* cells, @@ -913,8 +1073,8 @@ void countCellNeighboursHandler(CellSeed** cellsLayersDevice, layerIndex, nCells, maxCellNeighbours); - gpuCheckError(cudaPeekAtLastError()); - gpuCheckError(cudaDeviceSynchronize()); + // gpuCheckError(cudaPeekAtLastError()); + // gpuCheckError(cudaDeviceSynchronize()); void *d_temp_storage = nullptr, *d_temp_storage_2 = nullptr; size_t temp_storage_bytes = 0, temp_storage_bytes_2 = 0; gpuCheckError(cub::DeviceScan::InclusiveSum(d_temp_storage, // d_temp_storage @@ -963,8 +1123,8 @@ void computeCellNeighboursHandler(CellSeed** cellsLayersDevice, const int nThreads) { - gpu::computeLayerCellNeighboursKernel<<>>( + gpu::computeLayerCellNeighboursKernel<<>>( cellsLayersDevice, neighboursLUT, neighboursIndexTable, @@ -979,36 +1139,190 @@ void computeCellNeighboursHandler(CellSeed** cellsLayersDevice, gpuCheckError(cudaDeviceSynchronize()); } -void filterCellNeighboursHandler(std::vector& neighHost, - gpuPair* cellNeighbours, - unsigned int nNeigh) +int filterCellNeighboursHandler(std::vector& neighHost, // TODO: eventually remove this! + gpuPair* cellNeighbourPairs, + int* cellNeighbours, + unsigned int nNeigh) { - thrust::device_ptr> neighVector(cellNeighbours); + thrust::device_ptr> neighVectorPairs(cellNeighbourPairs); + thrust::device_ptr validNeighs(cellNeighbours); thrust::device_vector keys(nNeigh); // TODO: externally allocate. thrust::device_vector vals(nNeigh); // TODO: externally allocate. - thrust::copy(thrust::make_transform_iterator(neighVector, gpu::pair_to_second()), - thrust::make_transform_iterator(neighVector + nNeigh, gpu::pair_to_second()), + thrust::copy(thrust::make_transform_iterator(neighVectorPairs, gpu::pair_to_second()), + thrust::make_transform_iterator(neighVectorPairs + nNeigh, gpu::pair_to_second()), keys.begin()); thrust::sequence(vals.begin(), vals.end()); thrust::sort_by_key(keys.begin(), keys.end(), vals.begin()); thrust::device_vector> sortedNeigh(nNeigh); - thrust::copy(thrust::make_permutation_iterator(neighVector, vals.begin()), - thrust::make_permutation_iterator(neighVector, vals.end()), + thrust::copy(thrust::make_permutation_iterator(neighVectorPairs, vals.begin()), + thrust::make_permutation_iterator(neighVectorPairs, vals.end()), sortedNeigh.begin()); discardResult(cudaDeviceSynchronize()); auto trimmedBegin = thrust::find_if(sortedNeigh.begin(), sortedNeigh.end(), gpu::is_valid_pair()); // trim leading -1s auto trimmedSize = sortedNeigh.end() - trimmedBegin; - thrust::device_vector validNeigh(trimmedSize); neighHost.resize(trimmedSize); - thrust::transform(trimmedBegin, sortedNeigh.end(), validNeigh.begin(), gpu::pair_to_first()); - gpuCheckError(cudaMemcpy(neighHost.data(), thrust::raw_pointer_cast(validNeigh.data()), trimmedSize * sizeof(int), cudaMemcpyDeviceToHost)); + thrust::transform(trimmedBegin, sortedNeigh.end(), validNeighs, gpu::pair_to_first()); + gpuCheckError(cudaMemcpy(neighHost.data(), cellNeighbours, trimmedSize * sizeof(int), cudaMemcpyDeviceToHost)); + + return trimmedSize; +} + +template +void processNeighboursHandler(const int startLayer, + const int startLevel, + CellSeed** allCellSeeds, + CellSeed* currentCellSeeds, + const unsigned int nCurrentCells, + const unsigned char** usedClusters, + int* neighbours, + gsl::span neighboursDeviceLUTs, + const TrackingFrameInfo** foundTrackingFrameInfo, + const float bz, + const float MaxChi2ClusterAttachment, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + const std::vector& lastCellIdHost, // temporary host vector + const std::vector& lastCellSeedHost, // temporary host vector + std::vector& updatedCellIdHost, // temporary host vector + std::vector& updatedCellSeedHost, // temporary host vector + const int nBlocks, + const int nThreads) +{ + thrust::device_vector foundSeedsTable(nCurrentCells + 1); // Shortcut: device_vector skips central memory management, we are relying on the contingency. TODO: fix this. + thrust::device_vector lastCellIds(lastCellIdHost); + thrust::device_vector lastCellSeed(lastCellSeedHost); + gpu::processNeighboursKernel<<>>(startLayer, + startLevel, + allCellSeeds, + lastCellIdHost.empty() ? currentCellSeeds : thrust::raw_pointer_cast(&lastCellSeed[0]), // lastCellSeeds + lastCellIdHost.empty() ? nullptr : thrust::raw_pointer_cast(&lastCellIds[0]), // lastCellIds, + lastCellIdHost.empty() ? nCurrentCells : lastCellSeedHost.size(), + nullptr, // updatedCellSeeds, + nullptr, // updatedCellsIds, + thrust::raw_pointer_cast(&foundSeedsTable[0]), // auxiliary only in GPU code to compute the number of cells per iteration + usedClusters, // Used clusters + neighbours, + neighboursDeviceLUTs[startLayer - 1], + foundTrackingFrameInfo, + bz, + MaxChi2ClusterAttachment, + propagator, + matCorrType); + void *d_temp_storage = nullptr, *d_temp_storage_2 = nullptr; + size_t temp_storage_bytes = 0, temp_storage_bytes_2 = 0; + gpuCheckError(cub::DeviceScan::ExclusiveSum(d_temp_storage, // d_temp_storage + temp_storage_bytes, // temp_storage_bytes + thrust::raw_pointer_cast(&foundSeedsTable[0]), // d_in + thrust::raw_pointer_cast(&foundSeedsTable[0]), // d_out + nCurrentCells + 1, // num_items + 0)); // NOLINT: failure in clang-tidy + discardResult(cudaMalloc(&d_temp_storage, temp_storage_bytes)); + gpuCheckError(cub::DeviceScan::ExclusiveSum(d_temp_storage, // d_temp_storage + temp_storage_bytes, // temp_storage_bytes + thrust::raw_pointer_cast(&foundSeedsTable[0]), // d_in + thrust::raw_pointer_cast(&foundSeedsTable[0]), // d_out + nCurrentCells + 1, // num_items + 0)); // NOLINT: failure in clang-tidy + + thrust::device_vector updatedCellIds(foundSeedsTable.back()) /*, lastCellIds(foundSeedsTable.back())*/; + thrust::device_vector updatedCellSeeds(foundSeedsTable.back()) /*, lastCellSeeds(foundSeedsTable.back())*/; + + gpu::processNeighboursKernel<<>>(startLayer, + startLevel, + allCellSeeds, + lastCellIdHost.empty() ? currentCellSeeds : thrust::raw_pointer_cast(&lastCellSeed[0]), // lastCellSeeds + lastCellIdHost.empty() ? nullptr : thrust::raw_pointer_cast(&lastCellIds[0]), // lastCellIds, + lastCellIdHost.empty() ? nCurrentCells : lastCellSeedHost.size(), + thrust::raw_pointer_cast(&updatedCellSeeds[0]), // updatedCellSeeds + thrust::raw_pointer_cast(&updatedCellIds[0]), // updatedCellsIds + thrust::raw_pointer_cast(&foundSeedsTable[0]), // auxiliary only in GPU code to compute the number of cells per iteration + usedClusters, // Used clusters + neighbours, + neighboursDeviceLUTs[startLayer - 1], + foundTrackingFrameInfo, + bz, + MaxChi2ClusterAttachment, + propagator, + matCorrType); + + // Temporary copyback to host to validate the kernel + updatedCellIdHost.resize(updatedCellIds.size()); + updatedCellSeedHost.resize(updatedCellSeeds.size()); + thrust::copy(updatedCellIds.begin(), updatedCellIds.end(), updatedCellIdHost.begin()); + thrust::copy(updatedCellSeeds.begin(), updatedCellSeeds.end(), updatedCellSeedHost.begin()); + + // int level = startLevel; + // for (int iLayer{startLayer - 1}; iLayer > 0 && level > 2; --iLayer) { + // --level; + // lastCellSeeds.swap(updatedCellSeeds); + // lastCellIds.swap(updatedCellIds); + // foundSeedsTable.resize(lastCellSeeds.size() + 1); + // thrust::fill(foundSeedsTable.begin(), foundSeedsTable.end(), 0); + + // gpu::processNeighboursKernel<<<1, 1>>>(iLayer, + // level, + // allCellSeeds, + // thrust::raw_pointer_cast(&lastCellSeeds[0]), + // thrust::raw_pointer_cast(&lastCellIds[0]), // currentCellIds, + // lastCellSeeds.size(), + // nullptr, // updatedCellSeeds, + // nullptr, // updatedCellsIds, + // thrust::raw_pointer_cast(&foundSeedsTable[0]), // auxiliary only in GPU code to compute the number of cells per iteration + // usedClusters, // Used clusters + // neighbours, + // neighboursDeviceLUTs[iLayer - 1], + // foundTrackingFrameInfo, + // bz, + // MaxChi2ClusterAttachment, + // propagator, + // matCorrType); + + // gpuCheckError(cub::DeviceScan::ExclusiveSum(d_temp_storage_2, // d_temp_storage + // temp_storage_bytes, // temp_storage_bytes + // thrust::raw_pointer_cast(&foundSeedsTable[0]), // d_in + // thrust::raw_pointer_cast(&foundSeedsTable[0]), // d_out + // foundSeedsTable.size(), // num_items + // 0)); + // discardResult(cudaMalloc(&d_temp_storage, temp_storage_bytes)); + // gpuCheckError(cub::DeviceScan::ExclusiveSum(d_temp_storage_2, // d_temp_storage + // temp_storage_bytes_2, // temp_storage_bytes + // thrust::raw_pointer_cast(&foundSeedsTable[0]), // d_in + // thrust::raw_pointer_cast(&foundSeedsTable[0]), // d_out + // foundSeedsTable.size(), // num_items + // 0)); + // updatedCellIds.resize(foundSeedsTable.back(), 0); + // updatedCellSeeds.resize(foundSeedsTable.back(), CellSeed()); + + // gpu::processNeighboursKernel<<<1, 1>>>(iLayer, + // level, + // allCellSeeds, + // thrust::raw_pointer_cast(&lastCellSeeds[0]), + // thrust::raw_pointer_cast(&lastCellIds[0]), // currentCellIds, + // lastCellSeeds.size(), + // thrust::raw_pointer_cast(&updatedCellSeeds[0]), // updatedCellSeeds + // thrust::raw_pointer_cast(&updatedCellIds[0]), // updatedCellsIds + // thrust::raw_pointer_cast(&foundSeedsTable[0]), // auxiliary only in GPU code to compute the number of cells per iteration + // usedClusters, // Used clusters + // neighbours, + // neighboursDeviceLUTs[iLayer - 1], + // foundTrackingFrameInfo, + // bz, + // MaxChi2ClusterAttachment, + // propagator, + // matCorrType); + // gpu::printCellSeeds<<<1, 1>>>(thrust::raw_pointer_cast(&updatedCellSeeds[0]), updatedCellSeeds.size()); + // } + + gpuCheckError(cudaFree(d_temp_storage)); + gpuCheckError(cudaFree(d_temp_storage_2)); } void trackSeedHandler(CellSeed* trackSeeds, const TrackingFrameInfo** foundTrackingFrameInfo, o2::its::TrackITSExt* tracks, + std::vector& minPtsHost, const unsigned int nSeeds, - const float Bz, + const float bz, const int startLevel, float maxChi2ClusterAttachment, float maxChi2NDF, @@ -1017,19 +1331,102 @@ void trackSeedHandler(CellSeed* trackSeeds, const int nBlocks, const int nThreads) { + thrust::device_vector minPts(minPtsHost); gpu::fitTrackSeedsKernel<<>>( - trackSeeds, // CellSeed* - foundTrackingFrameInfo, // TrackingFrameInfo** - tracks, // TrackITSExt* - nSeeds, // const unsigned int - Bz, // const float - startLevel, // const int - maxChi2ClusterAttachment, // float - maxChi2NDF, // float - propagator, // const o2::base::Propagator* - matCorrType); // o2::base::PropagatorF::MatCorrType + trackSeeds, // CellSeed* + foundTrackingFrameInfo, // TrackingFrameInfo** + tracks, // TrackITSExt* + thrust::raw_pointer_cast(&minPts[0]), // const float* minPts, + nSeeds, // const unsigned int + bz, // const float + startLevel, // const int + maxChi2ClusterAttachment, // float + maxChi2NDF, // float + propagator, // const o2::base::Propagator* + matCorrType); // o2::base::PropagatorF::MatCorrType gpuCheckError(cudaPeekAtLastError()); gpuCheckError(cudaDeviceSynchronize()); } -} // namespace o2::its + +template void countTrackletsInROFsHandler<7>(const IndexTableUtils* utils, + const uint8_t* multMask, + const int startROF, + const int endROF, + const int maxROF, + const int deltaROF, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const int nVertices, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const int iteration, + const float NSigmaCut, + std::vector& phiCuts, + const float resolutionPV, + std::vector& minRs, + std::vector& maxRs, + std::vector& resolutions, + std::vector& radii, + std::vector& mulScatAng, + const int nBlocks, + const int nThreads); + +template void computeTrackletsInROFsHandler<7>(const IndexTableUtils* utils, + const uint8_t* multMask, + const int startROF, + const int endROF, + const int maxROF, + const int deltaROF, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const int nVertices, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + Tracklet** tracklets, + gsl::span spanTracklets, + gsl::span nTracklets, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const int iteration, + const float NSigmaCut, + std::vector& phiCuts, + const float resolutionPV, + std::vector& minRs, + std::vector& maxRs, + std::vector& resolutions, + std::vector& radii, + std::vector& mulScatAng, + const int nBlocks, + const int nThreads); + +template void processNeighboursHandler<7>(const int startLayer, + const int startLevel, + CellSeed** allCellSeeds, + CellSeed* currentCellSeeds, + const unsigned int nCurrentCells, + const unsigned char** usedClusters, + int* neighbours, + gsl::span neighboursDeviceLUTs, + const TrackingFrameInfo** foundTrackingFrameInfo, + const float bz, + const float MaxChi2ClusterAttachment, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + const std::vector& lastCellIdHost, // temporary host vector + const std::vector& lastCellSeedHost, // temporary host vector + std::vector& updatedCellIdHost, // temporary host vector + std::vector& updatedCellSeedHost, // temporary host vector + const int nBlocks, + const int nThreads); +} // namespace o2::its \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h index cb9f28665cf07..fa0473ae88462 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h @@ -15,10 +15,10 @@ #ifndef TRACKINGITSU_INCLUDE_CACELL_H_ #define TRACKINGITSU_INCLUDE_CACELL_H_ - #ifndef GPUCA_GPUCODE_DEVICE #include #include +#include #endif #include "GPUCommonDef.h" @@ -104,6 +104,7 @@ class CellSeed final : public o2::track::TrackParCovF GPUhd() int* getLevelPtr() { return &mLevel; } GPUhd() int* getClusters() { return mClusters; } GPUhd() int getCluster(int i) const { return mClusters[i]; } + GPUhdi() void printCell() const; private: int mClusters[7] = {-1, -1, -1, -1, -1, -1, -1}; @@ -112,6 +113,11 @@ class CellSeed final : public o2::track::TrackParCovF float mChi2 = 0.f; }; +GPUhdi() void CellSeed::printCell() const +{ + printf("trkl: %d, %d\t lvl: %d\t chi2: %f\n", mTracklets[0], mTracklets[1], mLevel, mChi2); +} + } // namespace its } // namespace o2 #endif /* TRACKINGITSU_INCLUDE_CACELL_H_ */ diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h index 976d01f1d476b..e99f0c1f4d13f 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h @@ -94,10 +94,14 @@ struct TrackingParameters { unsigned long MaxMemory = 12000000000UL; float MaxChi2ClusterAttachment = 60.f; float MaxChi2NDF = 30.f; + std::vector MinPt = {0.f, 0.f, 0.f, 0.f}; + unsigned char StartLayerMask = 0x7F; bool FindShortTracks = false; bool PerPrimaryVertexProcessing = false; bool SaveTimeBenchmarks = false; bool DoUPCIteration = false; + bool FataliseUponFailure = true; + bool DropTFUponFailure = false; /// Cluster attachment bool UseTrackFollower = false; bool UseTrackFollowerTop = false; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h index 906eb0fa5c21e..46c4a8e19fa47 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h @@ -106,12 +106,16 @@ class TimeFrame float getBeamX() const; float getBeamY() const; - + std::vector& getMinRs() { return mMinR; } + std::vector& getMaxRs() { return mMaxR; } float getMinR(int layer) const { return mMinR[layer]; } float getMaxR(int layer) const { return mMaxR[layer]; } float getMSangle(int layer) const { return mMSangles[layer]; } + std::vector& getMSangles() { return mMSangles; } float getPhiCut(int layer) const { return mPhiCuts[layer]; } + std::vector& getPhiCuts() { return mPhiCuts; } float getPositionResolution(int layer) const { return mPositionResolution[layer]; } + std::vector& getPositionResolutions() { return mPositionResolution; } gsl::span getClustersOnLayer(int rofId, int layerId); gsl::span getClustersOnLayer(int rofId, int layerId) const; @@ -209,8 +213,8 @@ class TimeFrame const unsigned long long& getRoadLabel(int i) const; bool isRoadFake(int i) const; - void setMultiplicityCutMask(const std::vector& cutMask) { mMultiplicityCutMask = cutMask; } - void setROFMask(const std::vector& rofMask) { mROFMask = rofMask; } + 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); } @@ -255,6 +259,7 @@ class TimeFrame void printCellLUTonLayer(int i); void printTrackletLUTs(); void printCellLUTs(); + void printSliceInfo(const int, const int); IndexTableUtils mIndexTableUtils; @@ -289,8 +294,16 @@ class TimeFrame std::vector> mTracks; std::vector> mCellsNeighbours; std::vector> mCellsLookupTable; + std::vector mMultiplicityCutMask; const o2::base::PropagatorImpl* mPropagatorDevice = nullptr; // Needed only for GPU + void dropTracks() + { + for (auto& v : mTracks) { + deepVectorClear(v); + } + } + protected: template void deepVectorClear(std::vector& vec) @@ -311,8 +324,8 @@ class TimeFrame std::vector mPhiCuts; std::vector mPositionResolution; std::vector mClusterSize; - std::vector mMultiplicityCutMask; - std::vector mROFMask; + + std::vector mROFMask; std::vector> mPValphaX; /// PV x and alpha for track propagation std::vector> mTrackletLabels; std::vector> mCellLabels; @@ -439,33 +452,33 @@ inline gsl::span TimeFrame::getClustersPerROFrange(int rofMin, in return gsl::span(); } int startIdx{mROFramesClusters[layerId][rofMin]}; // First cluster of rofMin - int endIdx{mROFramesClusters[layerId][std::min(rofMin + range, mNrof)]}; + int endIdx{mROFramesClusters[layerId][o2::gpu::CAMath::Min(rofMin + range, mNrof)]}; return {&mClusters[layerId][startIdx], static_cast::size_type>(endIdx - startIdx)}; } inline gsl::span TimeFrame::getROFramesClustersPerROFrange(int rofMin, int range, int layerId) const { - int chkdRange{std::min(range, mNrof - rofMin)}; + int chkdRange{o2::gpu::CAMath::Min(range, mNrof - rofMin)}; return {&mROFramesClusters[layerId][rofMin], static_cast::size_type>(chkdRange)}; } inline gsl::span TimeFrame::getNClustersROFrange(int rofMin, int range, int layerId) const { - int chkdRange{std::min(range, mNrof - rofMin)}; + int chkdRange{o2::gpu::CAMath::Min(range, mNrof - rofMin)}; return {&mNClustersPerROF[layerId][rofMin], static_cast::size_type>(chkdRange)}; } inline int TimeFrame::getTotalClustersPerROFrange(int rofMin, int range, int layerId) const { int startIdx{rofMin}; // First cluster of rofMin - int endIdx{std::min(rofMin + range, mNrof)}; + int endIdx{o2::gpu::CAMath::Min(rofMin + range, mNrof)}; return mROFramesClusters[layerId][endIdx] - mROFramesClusters[layerId][startIdx]; } inline gsl::span TimeFrame::getIndexTablePerROFrange(int rofMin, int range, int layerId) const { const int iTableSize{mIndexTableUtils.getNphiBins() * mIndexTableUtils.getNzBins() + 1}; - int chkdRange{std::min(range, mNrof - rofMin)}; + int chkdRange{o2::gpu::CAMath::Min(range, mNrof - rofMin)}; return {&mIndexTables[layerId][rofMin * iTableSize], static_cast::size_type>(chkdRange * iTableSize)}; } diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h index 68bfdb51170b5..20e9a4362b77c 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h @@ -22,13 +22,12 @@ namespace its struct VertexerParamConfig : public o2::conf::ConfigurableParamHelper { - 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; - // Number of ROFs to be considered for the vertexing - int deltaRof = 0; + 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. - // geometrical cuts + // geometrical cuts for tracklet selection float zCut = 0.002f; float phiCut = 0.005f; float pairCut = 0.04f; @@ -42,12 +41,12 @@ struct VertexerParamConfig : public o2::conf::ConfigurableParamHelper { // Use TGeo for mat. budget - bool useMatCorrTGeo = false; - bool useFastMaterial = false; - int deltaRof = 0; - 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 deltaRof = 0; // configure the width of the window in ROFs to be considered for the tracking. + 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; @@ -69,23 +68,25 @@ 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 diamondPos[3] = {0.f, 0.f, 0.f}; // override the position of the vertex + bool useDiamond = false; // enable overriding the vertex position + unsigned long maxMemory = 0; // override default protections on the maximum memory to be used by the tracking + int useTrackFollower = -1; // bit 0: allow mixing implies bits 1&2; bit 1: topwards; bit2: downwards; => 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 nThreads = 1; - int nOrbitsPerIterations = 0; - int nROFsPerIterations = 0; - bool perPrimaryVertexProcessing = false; - bool saveTimeBenchmarks = false; - bool overrideBeamEstimation = false; // used by gpuwf only - int trackingMode = -1; // -1: unset, 0=sync, 1=async, 2=cosmics used by gpuwf only - bool doUPCIteration = false; + int nThreads = 1; // number of threads to perform the operations in parallel. + 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. + bool perPrimaryVertexProcessing = false; // perform the full tracking considering the vertex hypotheses one at the time. + bool saveTimeBenchmarks = false; // dump metrics on file + bool overrideBeamEstimation = false; // use beam position from meanVertex CCDB object + int trackingMode = -1; // -1: unset, 0=sync, 1=async, 2=cosmics used by gpuwf only + bool doUPCIteration = false; // Perform an additional iteration for UPC events on tagged vertices. You want to combine this config with VertexerParamConfig.nIterations=2 + bool fataliseUponFailure = true; // granular management of the fatalisation in async mode + bool dropTFUponFailure = false; O2ParamDef(TrackerParamConfig, "ITSCATrackerParam"); }; diff --git a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx index f614de9b9f26a..506656e2777d1 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx @@ -608,5 +608,22 @@ void TimeFrame::printNClsPerROF() std::cout << std::endl; } } + +void TimeFrame::printSliceInfo(const int startROF, const int sliceSize) +{ + std::cout << "Dumping slice of " << sliceSize << " rofs:" << std::endl; + for (int iROF{startROF}; iROF < startROF + sliceSize; ++iROF) { + std::cout << "ROF " << iROF << " dump:" << std::endl; + for (unsigned int iLayer{0}; iLayer < mClusters.size(); ++iLayer) { + std::cout << "Layer " << iLayer << " has: " << getClustersOnLayer(iROF, iLayer).size() << " clusters." << std::endl; + } + std::cout << "Number of seeding vertices: " << getPrimaryVertices(iROF).size() << std::endl; + int iVertex{0}; + for (auto& v : getPrimaryVertices(iROF)) { + std::cout << "\t vertex " << iVertex++ << ": x=" << v.getX() << " " << " y=" << v.getY() << " z=" << v.getZ() << " has " << v.getNContributors() << " contributors." << std::endl; + } + } +} + } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx b/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx index 721452bf0361d..bc642015b1fcd 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx @@ -35,6 +35,7 @@ namespace o2 { namespace its { +using o2::its::constants::GB; Tracker::Tracker(o2::its::TrackerTraits* traits) { @@ -56,6 +57,7 @@ void Tracker::clustersToTracks(std::function logger, std::f } } + bool dropTF = false; for (int iteration = 0; iteration < (int)mTrkParams.size(); ++iteration) { if (iteration == 3 && mTrkParams[0].DoUPCIteration) { mTimeFrame->swapMasks(); @@ -74,12 +76,18 @@ void Tracker::clustersToTracks(std::function logger, std::f &Tracker::computeTracklets, "Tracklet finding", [](std::string) {}, iteration, iROFs, iVertex); nTracklets += mTraits->getTFNumberOfTracklets(); if (!mTimeFrame->checkMemory(mTrkParams[iteration].MaxMemory)) { - error(fmt::format("Too much memory used during trackleting in iteration {}, check the detector status and/or the selections.", iteration)); + mTimeFrame->printSliceInfo(iROFs, mTrkParams[iteration].nROFsPerIterations); + error(fmt::format("Too much memory used during trackleting in iteration {} in ROF span {}-{}: {:.2f} GB. Current limit is {:.2f} GB, check the detector status and/or the selections.", + iteration, iROFs, iROFs + mTrkParams[iteration].nROFsPerIterations, mTimeFrame->getArtefactsMemory() / GB, mTrkParams[iteration].MaxMemory / GB)); + if (mTrkParams[iteration].DropTFUponFailure) { + dropTF = true; + } break; } float trackletsPerCluster = mTraits->getTFNumberOfClusters() > 0 ? float(mTraits->getTFNumberOfTracklets()) / mTraits->getTFNumberOfClusters() : 0.f; if (trackletsPerCluster > mTrkParams[iteration].TrackletsPerClusterLimit) { - error(fmt::format("Too many tracklets per cluster ({}) in iteration {}, check the detector status and/or the selections. Current limit is {}", trackletsPerCluster, iteration, mTrkParams[iteration].TrackletsPerClusterLimit)); + error(fmt::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; } @@ -87,12 +95,18 @@ void Tracker::clustersToTracks(std::function logger, std::f &Tracker::computeCells, "Cell finding", [](std::string) {}, iteration); nCells += mTraits->getTFNumberOfCells(); if (!mTimeFrame->checkMemory(mTrkParams[iteration].MaxMemory)) { - error(fmt::format("Too much memory used during cell finding in iteration {}, check the detector status and/or the selections.", iteration)); + mTimeFrame->printSliceInfo(iROFs, mTrkParams[iteration].nROFsPerIterations); + error(fmt::format("Too much memory used during cell finding in iteration {} in ROF span {}-{}: {:.2f} GB. Current limit is {:.2f} GB, check the detector status and/or the selections.", + iteration, iROFs, iROFs + mTrkParams[iteration].nROFsPerIterations, mTimeFrame->getArtefactsMemory() / GB, mTrkParams[iteration].MaxMemory / GB)); + if (mTrkParams[iteration].DropTFUponFailure) { + dropTF = true; + } break; } float cellsPerCluster = mTraits->getTFNumberOfClusters() > 0 ? float(mTraits->getTFNumberOfCells()) / mTraits->getTFNumberOfClusters() : 0.f; if (cellsPerCluster > mTrkParams[iteration].CellsPerClusterLimit) { - error(fmt::format("Too many cells per cluster ({}) in iteration {}, check the detector status and/or the selections. Current limit is {}", cellsPerCluster, iteration, mTrkParams[iteration].CellsPerClusterLimit)); + error(fmt::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; } @@ -103,7 +117,7 @@ void Tracker::clustersToTracks(std::function logger, std::f &Tracker::findRoads, "Road finding", [](std::string) {}, iteration); } iVertex++; - } while (iVertex < maxNvertices); + } while (iVertex < maxNvertices && !dropTF); logger(fmt::format(" - Tracklet finding: {} tracklets found in {:.2f} ms", nTracklets, timeTracklets)); logger(fmt::format(" - Cell finding: {} cells found in {:.2f} ms", nCells, timeCells)); logger(fmt::format(" - Neighbours finding: {} neighbours found in {:.2f} ms", nNeighbours, timeNeighbours)); @@ -115,6 +129,11 @@ void Tracker::clustersToTracks(std::function logger, std::f total += timeExtending; logger(fmt::format(" - Extending Tracks: {} extended tracks using {} clusters found in {:.2f} ms", nExtendedTracks + mTimeFrame->mNExtendedTracks, nExtendedClusters + mTimeFrame->mNExtendedUsedClusters, timeExtending)); } + if (dropTF) { + error(fmt::format("...Dropping Timeframe...")); + mTimeFrame->dropTracks(); + break; // breaking out the iterations loop + } } total += evaluateTask(&Tracker::findShortPrimaries, "Short primaries finding", logger); @@ -495,6 +514,8 @@ void Tracker::getGlobalConfiguration() params.nROFsPerIterations = nROFsPerIterations; params.PerPrimaryVertexProcessing = tc.perPrimaryVertexProcessing; params.SaveTimeBenchmarks = tc.saveTimeBenchmarks; + params.FataliseUponFailure = tc.fataliseUponFailure; + params.DropTFUponFailure = tc.dropTFUponFailure; for (int iD{0}; iD < 3; ++iD) { params.Diamond[iD] = tc.diamondPos[iD]; } diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx index da0abbae9dc1f..8dcb7bfd315c1 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx @@ -71,13 +71,13 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iROFslice, in 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); gsl::span diamondSpan(&diamondVert, 1); int startROF{mTrkParams[iteration].nROFsPerIterations > 0 ? iROFslice * mTrkParams[iteration].nROFsPerIterations : 0}; - int endROF{mTrkParams[iteration].nROFsPerIterations > 0 ? (iROFslice + 1) * mTrkParams[iteration].nROFsPerIterations + mTrkParams[iteration].DeltaROF : tf->getNrof()}; + int endROF{gpu::GPUCommonMath::Min(mTrkParams[iteration].nROFsPerIterations > 0 ? (iROFslice + 1) * mTrkParams[iteration].nROFsPerIterations + mTrkParams[iteration].DeltaROF : tf->getNrof(), tf->getNrof())}; for (int rof0{startROF}; rof0 < endROF; ++rof0) { gsl::span primaryVertices = mTrkParams[iteration].UseDiamond ? diamondSpan : tf->getPrimaryVertices(rof0); const int startVtx{iVertex >= 0 ? iVertex : 0}; - const int endVtx{iVertex >= 0 ? std::min(iVertex + 1, static_cast(primaryVertices.size())) : static_cast(primaryVertices.size())}; - int minRof = std::max(startROF, rof0 - mTrkParams[iteration].DeltaROF); - int maxRof = std::min(endROF - 1, rof0 + mTrkParams[iteration].DeltaROF); + const int endVtx{iVertex >= 0 ? o2::gpu::CAMath::Min(iVertex + 1, static_cast(primaryVertices.size())) : static_cast(primaryVertices.size())}; + int minRof = o2::gpu::CAMath::Max(startROF, rof0 - mTrkParams[iteration].DeltaROF); + int maxRof = o2::gpu::CAMath::Min(endROF - 1, rof0 + mTrkParams[iteration].DeltaROF); #pragma omp parallel for num_threads(mNThreads) for (int iLayer = 0; iLayer < mTrkParams[iteration].TrackletsPerRoad(); ++iLayer) { gsl::span layer0 = tf->getClustersOnLayer(rof0, iLayer); @@ -128,7 +128,6 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iROFslice, in if (layer1.empty()) { continue; } - for (int iPhiCount{0}; iPhiCount < phiBinsNum; iPhiCount++) { int iPhiBin = (selectedBinsRect.y + iPhiCount) % mTrkParams[iteration].PhiBins; const int firstBinIndex{tf->mIndexTableUtils.getBinIndex(selectedBinsRect.x, iPhiBin)}; @@ -145,9 +144,7 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iROFslice, in } const int firstRowClusterIndex = tf->getIndexTable(rof1, iLayer + 1)[firstBinIndex]; const int maxRowClusterIndex = tf->getIndexTable(rof1, iLayer + 1)[maxBinIndex]; - for (int iNextCluster{firstRowClusterIndex}; iNextCluster < maxRowClusterIndex; ++iNextCluster) { - if (iNextCluster >= (int)layer1.size()) { break; } @@ -576,6 +573,9 @@ void TrackerTraits::findRoads(const int iteration) const int minimumLayer{startLevel - 1}; std::vector trackSeeds; for (int startLayer{mTrkParams[iteration].CellsPerRoad() - 1}; startLayer >= minimumLayer; --startLayer) { + if ((mTrkParams[iteration].StartLayerMask & (1 << (startLayer + 2))) == 0) { + continue; + } CA_DEBUGGER(std::cout << "\t\t > Starting processing layer " << startLayer << std::endl); std::vector lastCellId, updatedCellId; std::vector lastCellSeed, updatedCellSeed; @@ -618,7 +618,7 @@ void TrackerTraits::findRoads(const int iteration) temporaryTrack.resetCovariance(); temporaryTrack.setChi2(0); fitSuccess = fitTrack(temporaryTrack, mTrkParams[0].NLayers - 1, -1, -1, mTrkParams[0].MaxChi2ClusterAttachment, mTrkParams[0].MaxChi2NDF, 50.f); - if (!fitSuccess) { + if (!fitSuccess || temporaryTrack.getPt() < mTrkParams[iteration].MinPt[mTrkParams[iteration].NLayers - temporaryTrack.getNClusters()]) { continue; } tracks[trackIndex++] = temporaryTrack; @@ -668,7 +668,7 @@ void TrackerTraits::findRoads(const int iteration) if (rofs[1] != INT_MAX) { track.setNextROFbit(); } - mTimeFrame->getTracks(std::min(rofs[0], rofs[1])).emplace_back(track); + mTimeFrame->getTracks(o2::gpu::CAMath::Min(rofs[0], rofs[1])).emplace_back(track); } } } diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx index f00d87164d7d6..7305e205ecb3a 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx @@ -47,6 +47,8 @@ void ITSTrackingInterface::initialise() trackParams[2].TrackletMinPt = 0.1f; trackParams[2].CellDeltaTanLambdaSigma *= 4.; trackParams[2].MinTrackLength = 4; + trackParams[2].MinPt[3] = 0.2f; + trackParams[2].StartLayerMask = (1 << 6) + (1 << 3); if (o2::its::TrackerParamConfig::Instance().doUPCIteration) { trackParams[3].TrackletMinPt = 0.1f; trackParams[3].CellDeltaTanLambdaSigma *= 4.; @@ -174,7 +176,7 @@ void ITSTrackingInterface::run(framework::ProcessingContext& pc) auto errorLogger = [&](std::string s) { LOG(error) << s; }; FastMultEst multEst; // mult estimator - std::vector processingMask, processUPCMask; + 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); @@ -269,13 +271,13 @@ void ITSTrackingInterface::run(framework::ProcessingContext& pc) mTimeFrame->setROFMask(processUPCMask); // Run CA tracker if constexpr (isGPU) { - if (mMode == o2::its::TrackingMode::Async) { + if (mMode == o2::its::TrackingMode::Async && o2::its::TrackerParamConfig::Instance().fataliseUponFailure) { mTracker->clustersToTracksHybrid(logger, fatalLogger); } else { mTracker->clustersToTracksHybrid(logger, errorLogger); } } else { - if (mMode == o2::its::TrackingMode::Async) { + if (mMode == o2::its::TrackingMode::Async && o2::its::TrackerParamConfig::Instance().fataliseUponFailure) { mTracker->clustersToTracks(logger, fatalLogger); } else { mTracker->clustersToTracks(logger, errorLogger); diff --git a/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx b/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx index 4eaddc8385b8a..e87e2289b49e7 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx @@ -90,7 +90,7 @@ float Vertexer::clustersToVerticesHybrid(std::function logg auto timeVertexingIteration = evaluateTask( &Vertexer::findVerticesHybrid, "Hybrid Vertexer vertex finding", [](std::string) {}, iteration); - printEpilog(logger, true, nTracklets01, nTracklets12, mTimeFrame->getNLinesTotal(), mTimeFrame->getTotVertIteration().size(), timeInit, timeTracklet, timeSelection, timeVertexing); + printEpilog(logger, true, nTracklets01, nTracklets12, mTimeFrame->getNLinesTotal(), mTimeFrame->getTotVertIteration()[iteration], timeInitIteration, timeTrackletIteration, timeSelectionIteration, timeVertexingIteration); timeInit += timeInitIteration; timeTracklet += timeTrackletIteration; timeSelection += timeSelectionIteration; @@ -142,9 +142,9 @@ void Vertexer::printEpilog(std::function logger, const float initT, const float trackletT, const float selecT, const float vertexT) { float total = initT + trackletT + selecT + vertexT; - logger(fmt::format(" - {}Vertexer: found {} | {} tracklets in: {} ms", isHybrid ? "Hybrid" : "", trackletN01, trackletN12, trackletT)); - logger(fmt::format(" - {}Vertexer: selected {} tracklets in: {} ms", isHybrid ? "Hybrid" : "", selectedN, selecT)); - logger(fmt::format(" - {}Vertexer: found {} vertices in: {} ms", isHybrid ? "Hybrid" : "", vertexN, vertexT)); + logger(fmt::format(" - {}Vertexer: found {} | {} tracklets in: {} ms", isHybrid ? "Hybrid " : "", trackletN01, trackletN12, trackletT)); + logger(fmt::format(" - {}Vertexer: selected {} tracklets in: {} ms", isHybrid ? "Hybrid " : "", selectedN, selecT)); + logger(fmt::format(" - {}Vertexer: found {} vertices in: {} ms", isHybrid ? "Hybrid " : "", vertexN, vertexT)); // logger(fmt::format(" - Timeframe {} vertexing completed in: {} ms, using {} thread(s).", mTimeFrameCounter++, total, mTraits->getNThreads())); } diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ThresholdCalibratorSpec.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ThresholdCalibratorSpec.h index d97e1b78982eb..028ce3d0687fb 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ThresholdCalibratorSpec.h +++ b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ThresholdCalibratorSpec.h @@ -234,7 +234,7 @@ class ITSThresholdCalibrator : public Task short int mRunTypeRU[N_RU] = {0}; short int mRunTypeRUCopy[N_RU] = {0}; short int mCdwCntRU[N_RU][N_ROW] = {{0}}; - short int mRowRU[N_RU] = {0}; + short int mLoopVal[N_RU][N_ROW] = {{0}}; bool mActiveLinks[N_RU][3] = {{false}}; std::set mRuSet; // Either "T" for threshold, "V" for VCASN, or "I" for ITHR diff --git a/Detectors/ITSMFT/ITS/workflow/src/ClustererSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/ClustererSpec.cxx index 16e2c65a37e33..d58e4f5d915c1 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/ClustererSpec.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/ClustererSpec.cxx @@ -121,8 +121,9 @@ void ClustererDPL::updateTimeDependentParams(ProcessingContext& pc) // settings for the fired pixel overflow masking const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); const auto& clParams = o2::itsmft::ClustererParam::Instance(); + mClusterer->setDropHugeClusters(clParams.dropHugeClusters); if (clParams.maxBCDiffToMaskBias > 0 && clParams.maxBCDiffToSquashBias > 0) { - LOGP(fatal, "maxBCDiffToMaskBias = {} and maxBCDiffToMaskBias = {} cannot be set at the same time. Either set masking or squashing with a BCDiff > 0", clParams.maxBCDiffToMaskBias, clParams.maxBCDiffToSquashBias); + LOGP(fatal, "maxBCDiffToMaskBias = {} and maxBCDiffToSquashBias = {} cannot be set at the same time. Either set masking or squashing with a BCDiff > 0", clParams.maxBCDiffToMaskBias, clParams.maxBCDiffToSquashBias); } auto nbc = clParams.maxBCDiffToMaskBias; nbc += mClusterer->isContinuousReadOut() ? alpParams.roFrameLengthInBC : (alpParams.roFrameLengthTrig / o2::constants::lhc::LHCBunchSpacingNS); diff --git a/Detectors/ITSMFT/ITS/workflow/src/CookedTrackerSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/CookedTrackerSpec.cxx index 01e649f982896..4a0470adcf07a 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/CookedTrackerSpec.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/CookedTrackerSpec.cxx @@ -132,7 +132,7 @@ void CookedTrackerDPL::run(ProcessingContext& pc) const auto& multEstConf = FastMultEstConfig::Instance(); // parameters for mult estimation and cuts FastMultEst multEst; // mult estimator - std::vector processingMask; + std::vector processingMask; int cutVertexMult{0}, cutRandomMult = int(rofsinput.size()) - multEst.selectROFs(rofsinput, compClusters, physTriggers, processingMask); // auto processingMask_ephemeral = processingMask; diff --git a/Detectors/ITSMFT/ITS/workflow/src/ThresholdCalibratorSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/ThresholdCalibratorSpec.cxx index a93ef3c4f4242..e5e4ea016e26e 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/ThresholdCalibratorSpec.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/ThresholdCalibratorSpec.cxx @@ -744,41 +744,39 @@ void ITSThresholdCalibrator::extractThresholdRow(const short int& chipID, const } else { // threshold, vcasn, ithr - for (int scan_i = 0; scan_i < ((mScanType == 'r') ? N_RANGE : N_RANGE2); scan_i++) { - + short int iRU = getRUID(chipID); #ifdef WITH_OPENMP - omp_set_num_threads(mNThreads); + omp_set_num_threads(mNThreads); #pragma omp parallel for schedule(dynamic) #endif - // Loop over all columns (pixels) in the row - for (short int col_i = 0; col_i < this->N_COL; col_i++) { - - // Do the threshold fit - float thresh = 0., noise = 0.; - bool success = false; - int spoints = 0; - if (isDumpS) { // already protected for multi-thread in the init - mFitHist->SetName(Form("scurve_chip%d_row%d_col%d_scani%d", chipID, row, col_i, scan_i)); - } + // Loop over all columns (pixels) in the row + for (short int col_i = 0; col_i < this->N_COL; col_i++) { + // Do the threshold fit + float thresh = 0., noise = 0.; + bool success = false; + int spoints = 0; + int scan_i = mScanType == 'r' ? (mLoopVal[iRU][row] - mMin) / mStep : 0; + if (isDumpS) { // already protected for multi-thread in the init + mFitHist->SetName(Form("scurve_chip%d_row%d_col%d_scani%d", chipID, row, col_i, scan_i)); + } - success = this->findThreshold(chipID, mPixelHits[chipID][row][col_i], - this->mX, mScanType == 'r' ? N_RANGE2 : N_RANGE, thresh, noise, spoints, scan_i); + success = this->findThreshold(chipID, mPixelHits[chipID][row][col_i], + this->mX, mScanType == 'r' ? N_RANGE2 : N_RANGE, thresh, noise, spoints, scan_i); - vChipid[col_i] = chipID; - vRow[col_i] = row; - vThreshold[col_i] = (mScanType == 'T' || mScanType == 'r') ? (short int)(thresh * 10.) : (short int)(thresh); - vNoise[col_i] = (float)(noise * 10.); // always factor 10 also for ITHR/VCASN to not have all zeros - vSuccess[col_i] = success; - vPoints[col_i] = spoints > 0 ? (unsigned char)(spoints) : 0; + vChipid[col_i] = chipID; + vRow[col_i] = row; + vThreshold[col_i] = (mScanType == 'T' || mScanType == 'r') ? (short int)(thresh * 10.) : (short int)(thresh); + vNoise[col_i] = (float)(noise * 10.); // always factor 10 also for ITHR/VCASN to not have all zeros + vSuccess[col_i] = success; + vPoints[col_i] = spoints > 0 ? (unsigned char)(spoints) : 0; - if (mScanType == 'r') { - vMixData[col_i] = (scan_i * this->mStep) + mMin; - } - } if (mScanType == 'r') { - this->saveThreshold(); // save before moving to the next vresetd + vMixData[col_i] = mLoopVal[iRU][row]; } } + if (mScanType == 'r') { + this->saveThreshold(); // save before moving to the next vresetd + } // Fill the ScTree tree if (mScanType == 'T') { // TODO: store also for other scans? @@ -1311,10 +1309,12 @@ void ITSThresholdCalibrator::run(ProcessingContext& pc) short int row = -1; short int cwcnt = -1; bool isAllZero = true; + short int ruIndex = -1; for (short int iRU = 0; iRU < this->N_RU; iRU++) { const auto& calib = calibs[iROF * this->N_RU + iRU]; if (calib.calibUserField != 0) { mRuSet.insert(iRU); + ruIndex = iRU; isAllZero = false; if (loopval >= 0) { @@ -1363,9 +1363,9 @@ void ITSThresholdCalibrator::run(ProcessingContext& pc) cwcnt = (short int)(calib.calibCounter); // count the last N injections short int checkVal = (mScanType == 'I') ? mMin : mMax; - if (loopval == checkVal && realcharge == mMin2) { // the second condition is relevant only for mScanType=p + if ((mScanType != 'r' && loopval == checkVal) || (mScanType == 'r' && realcharge == mMax2)) { mCdwCntRU[iRU][row]++; - mRowRU[iRU] = row; // keep the row + mLoopVal[iRU][row] = loopval; // keep loop val (relevant for VRESET2D scan only) } if (this->mVerboseOutput) { LOG(info) << "RU: " << iRU << " CDWcounter: " << cwcnt << " row: " << row << " Loopval: " << loopval << " realcharge: " << realcharge << " confDBv: " << mCdwVersion; @@ -1425,7 +1425,7 @@ void ITSThresholdCalibrator::run(ProcessingContext& pc) short int ru = getRUID(chipID); mActiveLinks[ru][getLinkID(chipID, ru)] = true; // check rows and allocate memory - if (mForbiddenRows.count(chipID)) { + if (mScanType != 'r' && mForbiddenRows.count(chipID)) { for (int iforb = mForbiddenRows[chipID].size() - 1; iforb >= 0; iforb--) { if (mForbiddenRows[chipID][iforb] == row) { mChipsForbRows[chipID] = true; @@ -1467,30 +1467,32 @@ void ITSThresholdCalibrator::run(ProcessingContext& pc) } } } // if (charge) - } // for (ROFs) - // Prepare the ChipDone object for QC + extract data if the row is completed - for (auto& iRU : mRuSet) { + //// + // Prepare the ChipDone object for QC + extract data if the row is completed + if (ruIndex < 0) { + continue; + } short int nL = 0; for (int iL = 0; iL < 3; iL++) { - if (mActiveLinks[iRU][iL]) { + if (mActiveLinks[ruIndex][iL]) { nL++; // count active links } } - std::vector chipEnabled = getChipListFromRu(iRU, mActiveLinks[iRU]); // chip boundaries + std::vector chipEnabled = getChipListFromRu(ruIndex, mActiveLinks[ruIndex]); // chip boundaries // Fill the chipDone info string - if (mRunTypeRUCopy[iRU] == nInjScaled * nL) { + if (mRunTypeRUCopy[ruIndex] == nInjScaled * nL) { for (short int iChip = 0; iChip < chipEnabled.size(); iChip++) { if ((chipEnabled[iChip] % mChipModBase) != mChipModSel) { continue; } addDatabaseEntry(chipEnabled[iChip], "", std::vector(), true); } - mRunTypeRUCopy[iRU] = 0; // reset here is safer (the other counter is reset in finalize) + mRunTypeRUCopy[ruIndex] = 0; // reset here is safer (the other counter is reset in finalize) } // Check if scan of a row is finished: only for specific scans! - bool passCondition = (mCdwCntRU[iRU][mRowRU[iRU]] >= nInjScaled * nL); - if (mScanType != 'D' && mScanType != 'A' && mScanType != 'P' && mScanType != 'p' && mScanType != 'R' && mScanType != 'r' && passCondition) { + bool passCondition = (mCdwCntRU[ruIndex][row] >= nInjScaled * nL); + if (mScanType != 'D' && mScanType != 'A' && mScanType != 'P' && mScanType != 'p' && mScanType != 'R' && passCondition) { // extract data from the row for (short int iChip = 0; iChip < chipEnabled.size(); iChip++) { short int chipID = chipEnabled[iChip]; @@ -1499,17 +1501,19 @@ void ITSThresholdCalibrator::run(ProcessingContext& pc) } if (!isDumpS || (std::find(chipDumpList.begin(), chipDumpList.end(), chipID) != chipDumpList.end() || !chipDumpList.size())) { // to dump s-curves as histograms if (mPixelHits.count(chipID)) { - if (mPixelHits[chipID].count(mRowRU[iRU])) { // make sure the row exists - extractAndUpdate(chipID, mRowRU[iRU]); - mPixelHits[chipID].erase(mRowRU[iRU]); - mForbiddenRows[chipID].push_back(mRowRU[iRU]); + if (mPixelHits[chipID].count(row)) { // make sure the row exists + extractAndUpdate(chipID, row); + if (mScanType != 'r' || (mScanType == 'r' && mLoopVal[ruIndex][row] == mMax)) { + mPixelHits[chipID].erase(row); + } + mForbiddenRows[chipID].push_back(row); } } } } - mCdwCntRU[iRU][mRowRU[iRU]] = 0; // reset + mCdwCntRU[ruIndex][row] = 0; // reset } - } // end loop on RuSet + } // for (ROFs) if (!(this->mRunTypeUp)) { finalize(); @@ -1871,9 +1875,8 @@ void ITSThresholdCalibrator::finalize() this->addDatabaseEntry(it_ineff->first, name, std::vector(), false); it_ineff = this->mIneffPixID.erase(it_ineff); } - } else if (this->mScanType == 'P' || this->mScanType == 'p' || this->mScanType == 'r' || mScanType == 'R') { // pulse length scan 1D and 2D, vresetd scan 1D & 2D + } else if (this->mScanType == 'P' || this->mScanType == 'p' || mScanType == 'R') { // pulse length scan 1D and 2D, vresetd scan 1D (2D already extracted in run()) name = "Pulse"; - std::set thisRUs; // extract hits for the available row(s) auto itchip = this->mPixelHits.cbegin(); while (itchip != mPixelHits.cend()) { diff --git a/Detectors/ITSMFT/MFT/condition/include/MFTCondition/MFTDCSProcessor.h b/Detectors/ITSMFT/MFT/condition/include/MFTCondition/MFTDCSProcessor.h index 4fb411c0b73b5..e5bcf07ee083e 100644 --- a/Detectors/ITSMFT/MFT/condition/include/MFTCondition/MFTDCSProcessor.h +++ b/Detectors/ITSMFT/MFT/condition/include/MFTCondition/MFTDCSProcessor.h @@ -22,6 +22,7 @@ #include "DetectorsDCS/DataPointIdentifier.h" #include "DetectorsDCS/DataPointValue.h" #include "DetectorsDCS/DeliveryType.h" +#include "Framework/O2LongInt.h" #include "CCDB/CcdbObjectInfo.h" #include "CommonUtils/MemFileHelper.h" #include "CCDB/CcdbApi.h" @@ -40,13 +41,13 @@ using DPCOM = o2::dcs::DataPointCompositeObject; struct MFTDCSinfo { - std::pair firstValue; // first value seen by the MFT DCS processor - std::pair lastValue; // last value seen by the MFT DCS processor - std::pair meanValue; // mean value seen by the MFT DCS processor - std::pair stddevValue; // standard deviation value seen by the MFT DCS processor - std::pair midValue; // mid value seen by the MFT DCS processor - std::pair maxChange; // maximum variation seen by the MFT DCS processor - std::pair summary; // number of entries by the MFT DCS processor + std::pair firstValue; // first value seen by the MFT DCS processor + std::pair lastValue; // last value seen by the MFT DCS processor + std::pair meanValue; // mean value seen by the MFT DCS processor + std::pair stddevValue; // standard deviation value seen by the MFT DCS processor + std::pair midValue; // mid value seen by the MFT DCS processor + std::pair maxChange; // maximum variation seen by the MFT DCS processor + std::pair summary; // number of entries by the MFT DCS processor MFTDCSinfo() { diff --git a/Detectors/ITSMFT/MFT/workflow/src/ClustererSpec.cxx b/Detectors/ITSMFT/MFT/workflow/src/ClustererSpec.cxx index bc8568112e447..766d7c1a0729e 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/ClustererSpec.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/ClustererSpec.cxx @@ -122,8 +122,9 @@ void ClustererDPL::updateTimeDependentParams(ProcessingContext& pc) const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); const auto& clParams = o2::itsmft::ClustererParam::Instance(); if (clParams.maxBCDiffToMaskBias > 0 && clParams.maxBCDiffToSquashBias > 0) { - LOGP(fatal, "maxBCDiffToMaskBias = {} and maxBCDiffToMaskBias = {} cannot be set at the same time. Either set masking or squashing with a BCDiff > 0", clParams.maxBCDiffToMaskBias, clParams.maxBCDiffToSquashBias); + LOGP(fatal, "maxBCDiffToMaskBias = {} and maxBCDiffToSquashBias = {} cannot be set at the same time. Either set masking or squashing with a BCDiff > 0", clParams.maxBCDiffToMaskBias, clParams.maxBCDiffToSquashBias); } + mClusterer->setDropHugeClusters(clParams.dropHugeClusters); auto nbc = clParams.maxBCDiffToMaskBias; nbc += mClusterer->isContinuousReadOut() ? alpParams.roFrameLengthInBC : (alpParams.roFrameLengthTrig / o2::constants::lhc::LHCBunchSpacingNS); mClusterer->setMaxBCSeparationToMask(nbc); diff --git a/Detectors/ITSMFT/common/data/AlpideResponseData/AlpideResponse.cxx b/Detectors/ITSMFT/common/data/AlpideResponseData/AlpideResponse.cxx index f4c71d85df24e..86ffb24b239ed 100644 --- a/Detectors/ITSMFT/common/data/AlpideResponseData/AlpideResponse.cxx +++ b/Detectors/ITSMFT/common/data/AlpideResponseData/AlpideResponse.cxx @@ -14,55 +14,87 @@ #include "ITSMFTSimulation/AlpideSimResponse.h" #include #include +#include #include #include #include #include #include -void alpideResponse(const std::string& inpath = "./", - const std::string& outpath = "./", - const std::string& response_file = "AlpideResponseData.root") +void alpideResponse(const std::string& inpath, const std::string& outpath, const std::string& chip_name) { + // Check input path validity + if (gSystem->AccessPathName(inpath.c_str())) { + throw std::invalid_argument("Input path does not exist or is inaccessible: " + inpath); + } + + // Check output path validity + if (gSystem->AccessPathName(outpath.c_str(), kWritePermission)) { + throw std::invalid_argument("Output path is not writable: " + outpath); + } o2::itsmft::AlpideSimResponse resp0, resp1; - resp0.initData(0, inpath.data()); - resp1.initData(1, inpath.data()); + if (chip_name == "Alpide") { + resp0.initData(0, inpath.c_str()); + resp1.initData(1, inpath.c_str()); + } else if (chip_name == "APTS") { + resp1.setColMax(1.5e-4); + resp1.setRowMax(1.5e-4); + resp1.initData(1, inpath.c_str()); + } else { + throw std::invalid_argument("Unknown chip name: " + chip_name); + } - auto file = TFile::Open((outpath + response_file).data(), "recreate"); - file->WriteObjectAny(&resp0, "o2::itsmft::AlpideSimResponse", "response0"); + std::string output_file = outpath + "/" + chip_name + "ResponseData.root"; + auto file = TFile::Open(output_file.c_str(), "recreate"); + + if (!file || file->IsZombie()) { + throw std::runtime_error("Failed to create output file: " + output_file); + } else if (chip_name == "Alpide") { + file->WriteObjectAny(&resp0, "o2::itsmft::AlpideSimResponse", "response0"); + } file->WriteObjectAny(&resp1, "o2::itsmft::AlpideSimResponse", "response1"); file->Close(); + delete file; } int main(int argc, const char* argv[]) { namespace bpo = boost::program_options; bpo::variables_map vm; - bpo::options_description options("Alpide reponse generator options"); - options.add_options()( - "inputdir,i", bpo::value()->default_value("./"), "Path where Vbb-0.0V and Vbb-3.0V are located.")( - "outputdir,o", bpo::value()->default_value("./"), "Path where to store the output.")( - "name,n", bpo::value()->default_value("AlpideResponseData.root"), "Output file name."); + bpo::options_description options("Alpide response generator options"); + options.add_options()("inputdir,i", bpo::value()->default_value("./"), "Path where Vbb-0.0V and Vbb-3.0V are located.")("outputdir,o", bpo::value()->default_value("./"), "Path where to store the output.")("chip,c", bpo::value()->default_value("Alpide"), "Chip name (Alpide or APTS)."); try { bpo::store(parse_command_line(argc, argv, options), vm); + if (vm.count("help")) { std::cout << options << std::endl; - return 1; + return 0; } + bpo::notify(vm); } catch (const bpo::error& e) { std::cerr << e.what() << "\n\n"; std::cerr << "Error parsing command line arguments. Available options:\n"; - std::cerr << options << std::endl; return 2; } - std::cout << "Generating " << vm["inputdir"].as() + vm["name"].as() << std::endl; - alpideResponse(vm["inputdir"].as(), vm["outputdir"].as(), vm["name"].as()); + try { + std::cout << "Generating response for chip: " << vm["chip"].as() << std::endl; + std::cout << "Input directory: " << vm["inputdir"].as() << std::endl; + std::cout << "Output directory: " << vm["outputdir"].as() << std::endl; + + alpideResponse(vm["inputdir"].as(), + vm["outputdir"].as(), + vm["chip"].as()); + std::cout << "Response file generated successfully." << std::endl; + } catch (const std::exception& e) { + std::cerr << "Error: " << e.what() << std::endl; + return 1; + } return 0; -} \ No newline at end of file +} diff --git a/Detectors/ITSMFT/common/data/AlpideResponseData/CMakeLists.txt b/Detectors/ITSMFT/common/data/AlpideResponseData/CMakeLists.txt index ad26d9e658e16..88ec19a543db5 100644 --- a/Detectors/ITSMFT/common/data/AlpideResponseData/CMakeLists.txt +++ b/Detectors/ITSMFT/common/data/AlpideResponseData/CMakeLists.txt @@ -20,10 +20,10 @@ set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${CMAKE_SOURCE_DI if(ITSRESPONSE) message(STATUS "ITSRESPONSE option provided, setting ITSRESPONSE_DIR from it: " ${ITSRESPONSE}) - set(ITSRESPONSE_DIR ${ITSRESPONSE}) + set(ITSRESPONSE_DIR ${ITSRESPONSE} CACHE PATH "ITSResponse directory") else() message(STATUS "ITSRESPONSE option not provided, setting ITSRESPONSE_DIR from environment ITSRESPONSE_ROOT: " $ENV{ITSRESPONSE_ROOT}) - set(ITSRESPONSE_DIR $ENV{ITSRESPONSE_ROOT}) + set(ITSRESPONSE_DIR $ENV{ITSRESPONSE_ROOT} CACHE PATH "ITSResponse directory") endif() add_custom_command(TARGET O2exe-alpide-response-generator POST_BUILD diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h index d79eb4ac2b460..960ce2ca33d5b 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h @@ -204,6 +204,9 @@ class Clusterer bool isContinuousReadOut() const { return mContinuousReadout; } void setContinuousReadOut(bool v) { mContinuousReadout = v; } + bool isDropHugeClusters() const { return mDropHugeClusters; } + void setDropHugeClusters(bool v) { mDropHugeClusters = v; } + int getMaxBCSeparationToMask() const { return mMaxBCSeparationToMask; } void setMaxBCSeparationToMask(int n) { mMaxBCSeparationToMask = n; } @@ -238,6 +241,7 @@ class Clusterer // clusterization options bool mContinuousReadout = true; ///< flag continuous readout + bool mDropHugeClusters = false; ///< don't include clusters that would be split in more than one ///< mask continuosly fired pixels in frames separated by less than this amount of BCs (fired from hit in prev. ROF) int mMaxBCSeparationToMask = 6000. / o2::constants::lhc::LHCBunchSpacingNS + 10; diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ClustererParam.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ClustererParam.h index b922dbd421431..a71e5f3095b06 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ClustererParam.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ClustererParam.h @@ -38,6 +38,7 @@ struct ClustererParam : public o2::conf::ConfigurableParamHelper> chipErrorsTF{}; // vector of chip decoding errors seen in the given TF + std::vector errMsgVecTF; // Specific errors info collected for sending for the whole TF + const RUInfo* ruInfo = nullptr; RUDecodeData() diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelDecoder.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelDecoder.h index 810bff1037513..3a53253da2b42 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelDecoder.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelDecoder.h @@ -69,8 +69,8 @@ class RawPixelDecoder final : public PixelReader template void fillCalibData(CalibContainer& calib); - template - void collectDecodingErrors(LinkErrors& linkErrors, DecErrors& decErrors); + template + void collectDecodingErrors(LinkErrors& linkErrors, DecErrors& decErrors, ErrMsgs& errInfos); const RUDecodeData* getRUDecode(int ruSW) const { return mRUEntry[ruSW] < 0 ? nullptr : &mRUDecodeVec[mRUEntry[ruSW]]; } const GBTLink* getGBTLink(int i) const { return i < 0 ? nullptr : &mGBTLinks[i]; } @@ -267,8 +267,8 @@ void RawPixelDecoder::fillCalibData(CalibContainer& calib) ///______________________________________________________________________ template -template -void RawPixelDecoder::collectDecodingErrors(LinkErrors& linkErrors, DecErrors& decErrors) +template +void RawPixelDecoder::collectDecodingErrors(LinkErrors& linkErrors, DecErrors& decErrors, ErrMsgs& errInfos) { for (auto& lnk : mGBTLinks) { if (lnk.gbtErrStatUpadated) { @@ -276,11 +276,24 @@ void RawPixelDecoder::collectDecodingErrors(LinkErrors& linkErrors, Dec lnk.gbtErrStatUpadated = false; } } + size_t nerr = 0, nerrMsg = 0; for (auto& ru : mRUDecodeVec) { - for (const auto& err : ru.chipErrorsTF) { - decErrors.emplace_back(ChipError{err.first, err.second.first, err.second.second}); // id, nerrors, errorFlags + nerr += ru.chipErrorsTF.size(); + nerrMsg += ru.errMsgVecTF.size(); + } + if (nerr || nerrMsg) { + decErrors.reserve(nerr); + errInfos.reserve(nerrMsg); + for (auto& ru : mRUDecodeVec) { + for (const auto& err : ru.chipErrorsTF) { + decErrors.emplace_back(ChipError{err.first, err.second.first, err.second.second}); // id, nerrors, errorFlags + } + for (auto& err : ru.errMsgVecTF) { + errInfos.push_back(err); + } + ru.chipErrorsTF.clear(); + ru.errMsgVecTF.clear(); } - ru.chipErrorsTF.clear(); } } diff --git a/Detectors/ITSMFT/common/reconstruction/src/Clusterer.cxx b/Detectors/ITSMFT/common/reconstruction/src/Clusterer.cxx index b1b5c257dc98a..15dcc67a8967b 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/Clusterer.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/Clusterer.cxx @@ -263,36 +263,38 @@ void Clusterer::ClustererThread::finishChip(ChipPixelData* curChipData, CompClus parent->streamCluster(pixArrBuff, &labelsBuff, bbox, parent->mPattIdConverter, compClusPtr, patternsPtr, labelsClusPtr, nlab); } else { auto warnLeft = MaxHugeClusWarn - parent->mNHugeClus; - if (warnLeft > 0) { - LOGP(warn, "Splitting a huge cluster: chipID {}, rows {}:{} cols {}:{}{}", bbox.chipID, bbox.rowMin, bbox.rowMax, bbox.colMin, bbox.colMax, - warnLeft == 1 ? " (Further warnings will be muted)" : ""); + if (!parent->mDropHugeClusters) { + if (warnLeft > 0) { + LOGP(warn, "Splitting a huge cluster: chipID {}, rows {}:{} cols {}:{}{}", bbox.chipID, bbox.rowMin, bbox.rowMax, bbox.colMin, bbox.colMax, + warnLeft == 1 ? " (Further warnings will be muted)" : ""); #ifdef WITH_OPENMP #pragma omp critical #endif - { - parent->mNHugeClus++; + { + parent->mNHugeClus++; + } } - } - BBox bboxT(bbox); // truncated box - std::vector pixbuf; - do { - bboxT.rowMin = bbox.rowMin; - bboxT.colMax = std::min(bbox.colMax, uint16_t(bboxT.colMin + o2::itsmft::ClusterPattern::MaxColSpan - 1)); - do { // Select a subset of pixels fitting the reduced bounding box - bboxT.rowMax = std::min(bbox.rowMax, uint16_t(bboxT.rowMin + o2::itsmft::ClusterPattern::MaxRowSpan - 1)); - for (const auto& pix : pixArrBuff) { - if (bboxT.isInside(pix.getRowDirect(), pix.getCol())) { - pixbuf.push_back(pix); + BBox bboxT(bbox); // truncated box + std::vector pixbuf; + do { + bboxT.rowMin = bbox.rowMin; + bboxT.colMax = std::min(bbox.colMax, uint16_t(bboxT.colMin + o2::itsmft::ClusterPattern::MaxColSpan - 1)); + do { // Select a subset of pixels fitting the reduced bounding box + bboxT.rowMax = std::min(bbox.rowMax, uint16_t(bboxT.rowMin + o2::itsmft::ClusterPattern::MaxRowSpan - 1)); + for (const auto& pix : pixArrBuff) { + if (bboxT.isInside(pix.getRowDirect(), pix.getCol())) { + pixbuf.push_back(pix); + } } - } - if (!pixbuf.empty()) { // Stream a piece of cluster only if the reduced bounding box is not empty - parent->streamCluster(pixbuf, &labelsBuff, bboxT, parent->mPattIdConverter, compClusPtr, patternsPtr, labelsClusPtr, nlab, true); - pixbuf.clear(); - } - bboxT.rowMin = bboxT.rowMax + 1; - } while (bboxT.rowMin < bbox.rowMax); - bboxT.colMin = bboxT.colMax + 1; - } while (bboxT.colMin < bbox.colMax); + if (!pixbuf.empty()) { // Stream a piece of cluster only if the reduced bounding box is not empty + parent->streamCluster(pixbuf, &labelsBuff, bboxT, parent->mPattIdConverter, compClusPtr, patternsPtr, labelsClusPtr, nlab, true); + pixbuf.clear(); + } + bboxT.rowMin = bboxT.rowMax + 1; + } while (bboxT.rowMin < bbox.rowMax); + bboxT.colMin = bboxT.colMax + 1; + } while (bboxT.colMin < bbox.colMax); + } } } } @@ -451,8 +453,8 @@ void Clusterer::print() const { // print settings LOGP(info, "Clusterizer squashes overflow pixels separated by {} BC and <= {} in row/col seeking down to {} neighbour ROFs", mMaxBCSeparationToSquash, mMaxRowColDiffToMask, mSquashingDepth); - LOG(info) << "Clusterizer masks overflow pixels separated by < " << mMaxBCSeparationToMask << " BC and <= " - << mMaxRowColDiffToMask << " in row/col"; + LOGP(info, "Clusterizer masks overflow pixels separated by < {} BC and <= {} in row/col", mMaxBCSeparationToMask, mMaxRowColDiffToMask); + LOGP(info, "Clusterizer does {} drop huge clusters", mDropHugeClusters ? "" : "not"); #ifdef _PERFORM_TIMING_ auto& tmr = const_cast(mTimer); // ugly but this is what root does internally diff --git a/Detectors/ITSMFT/common/reconstruction/src/ITSMFTReconstructionLinkDef.h b/Detectors/ITSMFT/common/reconstruction/src/ITSMFTReconstructionLinkDef.h index e6785e4402f37..19f4ca06d0220 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/ITSMFTReconstructionLinkDef.h +++ b/Detectors/ITSMFT/common/reconstruction/src/ITSMFTReconstructionLinkDef.h @@ -55,4 +55,7 @@ #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::itsmft::ClustererParam < o2::detectors::DetID::ITS>> + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::itsmft::ClustererParam < o2::detectors::DetID::MFT>> + ; +#pragma link C++ class o2::itsmft::ErrorMessage + ; +#pragma link C++ class std::vector < o2::itsmft::ErrorMessage> + ; + #endif diff --git a/Detectors/ITSMFT/common/reconstruction/src/RUDecodeData.cxx b/Detectors/ITSMFT/common/reconstruction/src/RUDecodeData.cxx index e81194666fcb8..a9ed2748ec004 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/RUDecodeData.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/RUDecodeData.cxx @@ -62,6 +62,14 @@ void RUDecodeData::fillChipStatistics(int icab, const ChipPixelData* chipData) auto& chErr = chipErrorsTF[compid]; chErr.first++; chErr.second |= chipData->getErrorFlags(); + + if (chipData->isErrorSet(ChipStat::RepeatingPixel)) { + auto& errMsg = errMsgVecTF.emplace_back(); + errMsg.id = chipData->getChipID(); + errMsg.errType = ChipStat::RepeatingPixel; + errMsg.errInfo0 = chipData->getErrorInfo() & 0xffff; // row + errMsg.errInfo1 = (chipData->getErrorInfo() >> 16) & 0xffff; // row + } } if (action & ChipStat::ErrActDump) { linkHBFToDump[(uint64_t(cableLinkPtr[icab]->subSpec) << 32) + cableLinkPtr[icab]->hbfEntry] = cableLinkPtr[icab]->irHBF.orbit; diff --git a/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/AlpideSimResponse.h b/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/AlpideSimResponse.h index 0462115d3bfc6..25cad6d27b9a5 100644 --- a/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/AlpideSimResponse.h +++ b/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/AlpideSimResponse.h @@ -38,7 +38,7 @@ class AlpideRespSimMat static int constexpr getNPix() { return NPix; } AlpideRespSimMat() = default; - ~AlpideRespSimMat() = default; + virtual ~AlpideRespSimMat() = default; void adopt(const AlpideRespSimMat& src, bool flipRow = false, bool flipCol = false) { @@ -91,6 +91,7 @@ class AlpideSimResponse int getDepthBin(float pos) const; std::string composeDataName(int colBin, int rowBin); + protected: int mNBinCol = 0; /// number of bins in X(col direction) int mNBinRow = 0; /// number of bins in Y(row direction) int mNBinDpt = 0; /// number of bins in Z(sensor dept) @@ -132,6 +133,8 @@ class AlpideSimResponse float getStepCol() const { return mStepInvCol ? 1. / mStepInvCol : 0.f; } float getStepRow() const { return mStepInvRow ? 1. / mStepInvRow : 0.f; } float getStepDepth() const { return mStepInvDpt ? 1. / mStepInvDpt : 0.f; } + void setColMax(float v) noexcept { mColMax = v; } + void setRowMax(float v) noexcept { mRowMax = v; } void setDataPath(const std::string pth) { mDataPath = pth; } void setGridColName(const std::string nm) { mGridColName = nm; } void setGridRowName(const std::string nm) { mGridRowName = nm; } @@ -142,7 +145,7 @@ class AlpideSimResponse const std::string& getColRowDataFmt() const { return mColRowDataFmt; } void print() const; - ClassDefNV(AlpideSimResponse, 1); + ClassDefNV(AlpideSimResponse, 2); }; //----------------------------------------------------- diff --git a/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/DigiParams.h b/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/DigiParams.h index 7772c47237ae8..6ee56fbbe4dd5 100644 --- a/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/DigiParams.h +++ b/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/DigiParams.h @@ -96,7 +96,7 @@ class DigiParams const SignalShape& getSignalShape() const { return mSignalShape; } SignalShape& getSignalShape() { return (SignalShape&)mSignalShape; } - void print() const; + virtual void print() const; private: static constexpr double infTime = 1e99; diff --git a/Detectors/ITSMFT/common/workflow/src/STFDecoderSpec.cxx b/Detectors/ITSMFT/common/workflow/src/STFDecoderSpec.cxx index 76bd1ec7454a0..7042cb7433ac5 100644 --- a/Detectors/ITSMFT/common/workflow/src/STFDecoderSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/STFDecoderSpec.cxx @@ -247,7 +247,8 @@ void STFDecoder::run(ProcessingContext& pc) } auto& linkErrors = pc.outputs().make>(Output{orig, "LinkErrors", 0}); auto& decErrors = pc.outputs().make>(Output{orig, "ChipErrors", 0}); - mDecoder->collectDecodingErrors(linkErrors, decErrors); + auto& errMessages = pc.outputs().make>(Output{orig, "ErrorInfo", 0}); + mDecoder->collectDecodingErrors(linkErrors, decErrors, errMessages); pc.outputs().snapshot(Output{orig, "PHYSTRIG", 0}, mDecoder->getExternalTriggers()); @@ -398,6 +399,7 @@ DataProcessorSpec getSTFDecoderSpec(const STFDecoderInp& inp) 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); outputs.emplace_back(inp.origin, "CHIPSSTATUS", 0, Lifetime::Timeframe); if (inp.askSTFDist) { diff --git a/Detectors/MUON/MCH/Geometry/CMakeLists.txt b/Detectors/MUON/MCH/Geometry/CMakeLists.txt index 5e367fa87bbf4..000364af028c6 100644 --- a/Detectors/MUON/MCH/Geometry/CMakeLists.txt +++ b/Detectors/MUON/MCH/Geometry/CMakeLists.txt @@ -12,7 +12,7 @@ add_subdirectory(MisAligner) add_subdirectory(Transformer) add_subdirectory(Creator) -if (BUILD_TESTING) +if (BUILD_TESTING AND GLFW_FOUND) add_subdirectory(Test) endif() diff --git a/Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx b/Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx index 58a2a775537d4..ef59d94b26048 100644 --- a/Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx +++ b/Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx @@ -431,7 +431,7 @@ void TFReaderSpec::TFBuilder() } else { break; } - if (mInput.maxTFsPerFile > 0 && mInput.maxTFsPerFile >= locID) { // go to next file + if (mInput.maxTFsPerFile > 0 && locID >= mInput.maxTFsPerFile) { // go to next file break; } } diff --git a/Detectors/TOF/base/include/TOFBase/Digit.h b/Detectors/TOF/base/include/TOFBase/Digit.h index d23c138012e81..eef03ef84b97c 100644 --- a/Detectors/TOF/base/include/TOFBase/Digit.h +++ b/Detectors/TOF/base/include/TOFBase/Digit.h @@ -32,8 +32,8 @@ class Digit public: Digit() = default; - Digit(Int_t channel, Int_t tdc, Int_t tot, uint64_t bc, Int_t label = -1, uint32_t triggerorbit = 0, uint16_t triggerbunch = 0); - Digit(Int_t channel, Int_t tdc, Int_t tot, uint32_t orbit, uint16_t bc, Int_t label = -1, uint32_t triggerorbit = 0, uint16_t triggerbunch = 0); + Digit(Int_t channel, Int_t tdc, Int_t tot, uint64_t bc, Int_t label = -1, uint32_t triggerorbit = 0, uint16_t triggerbunch = 0, float geanttime = 0, double t0 = 0); + Digit(Int_t channel, Int_t tdc, Int_t tot, uint32_t orbit, uint16_t bc, Int_t label = -1, uint32_t triggerorbit = 0, uint16_t triggerbunch = 0, float geanttime = 0, double t0 = 0); ~Digit() = default; /// Get global ordering key made of @@ -66,7 +66,7 @@ class Digit void printStream(std::ostream& stream) const; - void merge(Int_t tdc, Int_t tot); + bool merge(Int_t tdc, Int_t tot); void getPhiAndEtaIndex(int& phi, int& eta) const; @@ -93,6 +93,11 @@ class Digit void setTriggerBunch(uint16_t value) { mTriggerBunch = value; } uint16_t getTriggerBunch() const { return mTriggerBunch; } + float getTgeant() const { return mTgeant; } + void setTgeant(float val) { mTgeant = val; } + double getT0true() const { return mT0true; } + void setT0true(double val) { mT0true = val; } + private: friend class boost::serialization::access; @@ -107,8 +112,10 @@ class Digit uint16_t mTriggerBunch = 0; //!< bunch id of trigger event Bool_t mIsUsedInCluster; //!/< flag to declare that the digit was used to build a cluster Bool_t mIsProblematic = false; //!< flag to tell whether the channel of the digit was problemati; not persistent; default = ok + float mTgeant = 0.0; ///< geant time in MC + double mT0true = 0.0; ///< t0true - ClassDefNV(Digit, 4); + ClassDefNV(Digit, 5); }; std::ostream& operator<<(std::ostream& stream, const Digit& dig); diff --git a/Detectors/TOF/base/include/TOFBase/Strip.h b/Detectors/TOF/base/include/TOFBase/Strip.h index 3d23545552f34..f1152e25ab294 100644 --- a/Detectors/TOF/base/include/TOFBase/Strip.h +++ b/Detectors/TOF/base/include/TOFBase/Strip.h @@ -80,7 +80,7 @@ class Strip /// reset points container o2::tof::Digit* findDigit(ULong64_t key); - Int_t addDigit(Int_t channel, Int_t tdc, Int_t tot, uint64_t bc, Int_t lbl = 0, uint32_t triggerorbit = 0, uint16_t triggerbunch = 0); // returns the MC label + Int_t addDigit(Int_t channel, Int_t tdc, Int_t tot, uint64_t bc, Int_t lbl = 0, uint32_t triggerorbit = 0, uint16_t triggerbunch = 0, float geanttime = 0, double t0 = 0); // returns the MC label void fillOutputContainer(std::vector& digits); diff --git a/Detectors/TOF/base/src/Digit.cxx b/Detectors/TOF/base/src/Digit.cxx index ed58623877e8d..00f96a0007cec 100644 --- a/Detectors/TOF/base/src/Digit.cxx +++ b/Detectors/TOF/base/src/Digit.cxx @@ -17,14 +17,14 @@ using namespace o2::tof; ClassImp(o2::tof::Digit); -Digit::Digit(Int_t channel, Int_t tdc, Int_t tot, uint64_t bc, Int_t label, uint32_t triggerorbit, uint16_t triggerbunch) - : mChannel(channel), mTDC(tdc), mTOT(tot), mIR(0, 0), mLabel(label), mTriggerOrbit(triggerorbit), mTriggerBunch(triggerbunch), mIsUsedInCluster(kFALSE) +Digit::Digit(Int_t channel, Int_t tdc, Int_t tot, uint64_t bc, Int_t label, uint32_t triggerorbit, uint16_t triggerbunch, float geanttime, double t0) + : mChannel(channel), mTDC(tdc), mTOT(tot), mIR(0, 0), mLabel(label), mTriggerOrbit(triggerorbit), mTriggerBunch(triggerbunch), mIsUsedInCluster(kFALSE), mTgeant(geanttime), mT0true(t0) { mIR.setFromLong(bc); } //______________________________________________________________________ -Digit::Digit(Int_t channel, Int_t tdc, Int_t tot, uint32_t orbit, uint16_t bc, Int_t label, uint32_t triggerorbit, uint16_t triggerbunch) - : mChannel(channel), mTDC(tdc), mTOT(tot), mIR(bc, orbit), mLabel(label), mTriggerOrbit(triggerorbit), mTriggerBunch(triggerbunch), mIsUsedInCluster(kFALSE) +Digit::Digit(Int_t channel, Int_t tdc, Int_t tot, uint32_t orbit, uint16_t bc, Int_t label, uint32_t triggerorbit, uint16_t triggerbunch, float geanttime, double t0) + : mChannel(channel), mTDC(tdc), mTOT(tot), mIR(bc, orbit), mLabel(label), mTriggerOrbit(triggerorbit), mTriggerBunch(triggerbunch), mIsUsedInCluster(kFALSE), mTgeant(geanttime), mT0true(t0) { } //______________________________________________________________________ @@ -44,16 +44,18 @@ std::ostream& operator<<(std::ostream& stream, const Digit& digi) //______________________________________________________________________ -void Digit::merge(Int_t tdc, Int_t tot) +bool Digit::merge(Int_t tdc, Int_t tot) { // merging two digits if (tdc < mTDC) { mTDC = tdc; + return 1; // new came first // TODO: adjust TOT } else { // TODO: adjust TOT + return 0; } } diff --git a/Detectors/TOF/base/src/Strip.cxx b/Detectors/TOF/base/src/Strip.cxx index a008776c2690f..e72bd5183c78e 100644 --- a/Detectors/TOF/base/src/Strip.cxx +++ b/Detectors/TOF/base/src/Strip.cxx @@ -34,7 +34,7 @@ Strip::Strip(Int_t index) { } //_______________________________________________________________________ -Int_t Strip::addDigit(Int_t channel, Int_t tdc, Int_t tot, uint64_t bc, Int_t lbl, uint32_t triggerorbit, uint16_t triggerbunch) +Int_t Strip::addDigit(Int_t channel, Int_t tdc, Int_t tot, uint64_t bc, Int_t lbl, uint32_t triggerorbit, uint16_t triggerbunch, float geanttime, double t0) { // return the MC label. We pass it also as argument, but it can change in @@ -44,10 +44,13 @@ Int_t Strip::addDigit(Int_t channel, Int_t tdc, Int_t tot, uint64_t bc, Int_t lb auto dig = findDigit(key); if (dig) { lbl = dig->getLabel(); // getting the label from the already existing digit - dig->merge(tdc, tot); // merging to the existing digit + if (dig->merge(tdc, tot)) { // merging to the existing digit (if new came first upload also MC truth) + dig->setTgeant(geanttime); + dig->setT0true(t0); + } mDigitMerged++; } else { - mDigits.emplace(std::make_pair(key, Digit(channel, tdc, tot, bc, lbl, triggerorbit, triggerbunch))); + mDigits.emplace(std::make_pair(key, Digit(channel, tdc, tot, bc, lbl, triggerorbit, triggerbunch, geanttime, t0))); } return lbl; diff --git a/Detectors/TOF/calibration/include/TOFCalibration/TOFDCSProcessor.h b/Detectors/TOF/calibration/include/TOFCalibration/TOFDCSProcessor.h index 6279565f4ef63..bb7e1fc7f5971 100644 --- a/Detectors/TOF/calibration/include/TOFCalibration/TOFDCSProcessor.h +++ b/Detectors/TOF/calibration/include/TOFCalibration/TOFDCSProcessor.h @@ -18,6 +18,7 @@ #include #include #include "Framework/Logger.h" +#include "Framework/O2LongInt.h" #include "DetectorsDCS/DataPointCompositeObject.h" #include "DetectorsDCS/DataPointIdentifier.h" #include "DetectorsDCS/DataPointValue.h" @@ -40,19 +41,19 @@ using DPVAL = o2::dcs::DataPointValue; using DPCOM = o2::dcs::DataPointCompositeObject; struct TOFDCSinfo { - std::pair firstValue; // first value seen by the TOF DCS processor - std::pair lastValue; // last value seen by the TOF DCS processor - std::pair midValue; // mid value seen by the TOF DCS processor - std::pair, double> maxChange; // maximum variation seen by the TOF DCS processor (previous - subsequent value) - std::pair minValue; // smallest measurement seen by the TOF DCS processor - std::pair maxValue; // largest measurement seen by the TOF DCS processor + std::pair firstValue; // first value seen by the TOF DCS processor + std::pair lastValue; // last value seen by the TOF DCS processor + std::pair midValue; // mid value seen by the TOF DCS processor + std::pair, double> maxChange; // maximum variation seen by the TOF DCS processor (previous - subsequent value) + std::pair minValue; // smallest measurement seen by the TOF DCS processor + std::pair maxValue; // largest measurement seen by the TOF DCS processor bool updated = false; TOFDCSinfo() { firstValue = std::make_pair(0, -999999999); lastValue = std::make_pair(0, -999999999); midValue = std::make_pair(0, -999999999); - std::array atmp = {0, 0}; + std::array atmp = {0, 0}; maxChange = std::make_pair(atmp, 0); minValue = std::make_pair(0, 99999999999); maxValue = std::make_pair(0, -99999999999); diff --git a/Detectors/TOF/reconstruction/src/Clusterer.cxx b/Detectors/TOF/reconstruction/src/Clusterer.cxx index 47dcbd805b162..0b393bfd45e78 100644 --- a/Detectors/TOF/reconstruction/src/Clusterer.cxx +++ b/Detectors/TOF/reconstruction/src/Clusterer.cxx @@ -173,6 +173,8 @@ void Clusterer::buildCluster(Cluster& c, MCLabelContainer const* digitMCTruth) } c.setMainContributingChannel(mContributingDigit[0]->getChannel()); + c.setTgeant(mContributingDigit[0]->getTgeant()); + c.setT0true(mContributingDigit[0]->getT0true()); c.setTime(mContributingDigit[0]->getCalibratedTime()); // time in ps (for now we assume it calibrated) c.setTimeRaw(mContributingDigit[0]->getTDC() * Geo::TDCBIN + mContributingDigit[0]->getBC() * o2::constants::lhc::LHCBunchSpacingNS * 1E3); // time in ps (for now we assume it calibrated) diff --git a/Detectors/TOF/simulation/include/TOFSimulation/Digitizer.h b/Detectors/TOF/simulation/include/TOFSimulation/Digitizer.h index 15a71b9e57c1f..5153f168f176f 100644 --- a/Detectors/TOF/simulation/include/TOFSimulation/Digitizer.h +++ b/Detectors/TOF/simulation/include/TOFSimulation/Digitizer.h @@ -135,11 +135,11 @@ class Digitizer : public WindowFiller CalibApi* mCalibApi = nullptr; //! calib api to handle the TOF calibration - void fillDigitsInStrip(std::vector* strips, o2::dataformats::MCTruthContainer* mcTruthContainer, int channel, int tdc, int tot, uint64_t nbc, UInt_t istrip, Int_t trackID, Int_t eventID, Int_t sourceID); + void fillDigitsInStrip(std::vector* strips, o2::dataformats::MCTruthContainer* mcTruthContainer, int channel, int tdc, int tot, uint64_t nbc, UInt_t istrip, Int_t trackID, Int_t eventID, Int_t sourceID, float geanttime = 0, double t0 = 0.0); Int_t processHit(const HitType& hit, Double_t event_time); void addDigit(Int_t channel, UInt_t istrip, Double_t time, Float_t x, Float_t z, Float_t charge, Int_t iX, Int_t iZ, Int_t padZfired, - Int_t trackID); + Int_t trackID, float geanttime = 0, double t0 = 0.0); void checkIfReuseFutureDigits(); diff --git a/Detectors/TOF/simulation/src/Digitizer.cxx b/Detectors/TOF/simulation/src/Digitizer.cxx index 50ea2c194616c..ec899bd35fbff 100644 --- a/Detectors/TOF/simulation/src/Digitizer.cxx +++ b/Detectors/TOF/simulation/src/Digitizer.cxx @@ -170,7 +170,7 @@ Int_t Digitizer::processHit(const HitType& hit, Double_t event_time) Float_t charge = getCharge(hit.GetEnergyLoss()); // NOTE: FROM NOW ON THE TIME IS IN PS ... AND NOT IN NS - Double_t time = getShowerTimeSmeared((double(hit.GetTime()) + event_time) * 1E3, charge); + Double_t time = getShowerTimeSmeared((double(hit.GetTime()) + event_time) * 1E3 + 0.5 * Geo::TDCBIN, charge); Float_t xLocal = deltapos[0]; Float_t zLocal = deltapos[2]; @@ -192,7 +192,7 @@ Int_t Digitizer::processHit(const HitType& hit, Double_t event_time) ndigits++; mXLastShift[mNLastHit] = 0; mZLastShift[mNLastHit] = 0; - addDigit(channel, istrip, time, xLocal, zLocal, charge, 0, 0, detInd[3], trackID); + addDigit(channel, istrip, time, xLocal, zLocal, charge, 0, 0, detInd[3], trackID, hit.GetTime(), event_time * 1E3); } // check PAD 2 @@ -209,7 +209,7 @@ Int_t Digitizer::processHit(const HitType& hit, Double_t event_time) ndigits++; mXLastShift[mNLastHit] = 0; mZLastShift[mNLastHit] = iZshift; - addDigit(channel, istrip, time, xLocal, zLocal, charge, 0, iZshift, detInd[3], trackID); + addDigit(channel, istrip, time, xLocal, zLocal, charge, 0, iZshift, detInd[3], trackID, hit.GetTime(), event_time * 1E3); } // check PAD 3 @@ -223,7 +223,7 @@ Int_t Digitizer::processHit(const HitType& hit, Double_t event_time) ndigits++; mXLastShift[mNLastHit] = -1; mZLastShift[mNLastHit] = 0; - addDigit(channel, istrip, time, xLocal, zLocal, charge, -1, 0, detInd[3], trackID); + addDigit(channel, istrip, time, xLocal, zLocal, charge, -1, 0, detInd[3], trackID, hit.GetTime(), event_time * 1E3); } } @@ -238,7 +238,7 @@ Int_t Digitizer::processHit(const HitType& hit, Double_t event_time) ndigits++; mXLastShift[mNLastHit] = 1; mZLastShift[mNLastHit] = 0; - addDigit(channel, istrip, time, xLocal, zLocal, charge, 1, 0, detInd[3], trackID); + addDigit(channel, istrip, time, xLocal, zLocal, charge, 1, 0, detInd[3], trackID, hit.GetTime(), event_time * 1E3); } } @@ -257,7 +257,7 @@ Int_t Digitizer::processHit(const HitType& hit, Double_t event_time) ndigits++; mXLastShift[mNLastHit] = -1; mZLastShift[mNLastHit] = iZshift; - addDigit(channel, istrip, time, xLocal, zLocal, charge, -1, iZshift, detInd[3], trackID); + addDigit(channel, istrip, time, xLocal, zLocal, charge, -1, iZshift, detInd[3], trackID, hit.GetTime(), event_time * 1E3); } } @@ -276,7 +276,7 @@ Int_t Digitizer::processHit(const HitType& hit, Double_t event_time) ndigits++; mXLastShift[mNLastHit] = 1; mZLastShift[mNLastHit] = iZshift; - addDigit(channel, istrip, time, xLocal, zLocal, charge, 1, iZshift, detInd[3], trackID); + addDigit(channel, istrip, time, xLocal, zLocal, charge, 1, iZshift, detInd[3], trackID, hit.GetTime(), event_time * 1E3); } } return ndigits; @@ -284,7 +284,7 @@ Int_t Digitizer::processHit(const HitType& hit, Double_t event_time) //______________________________________________________________________ void Digitizer::addDigit(Int_t channel, UInt_t istrip, Double_t time, Float_t x, Float_t z, Float_t charge, Int_t iX, Int_t iZ, - Int_t padZfired, Int_t trackID) + Int_t padZfired, Int_t trackID, float geanttime, double t0) { // TOF digit requires: channel, time and time-over-threshold @@ -297,10 +297,11 @@ void Digitizer::addDigit(Int_t channel, UInt_t istrip, Double_t time, Float_t x, charge *= getFractionOfCharge(x, z); // tot tuned to reproduce 0.8% of orphans tot(=0) - Float_t tot = gRandom->Gaus(12., 1.5); // time-over-threshold - if (tot < 8.4) { - tot = 0; + Float_t totf = gRandom->Gaus(12. * Geo::NTOTBIN_PER_NS, 1.5 * Geo::NTOTBIN_PER_NS); // time-over-threshold + if (totf < 172) { + totf = 0; } + int tot = int(totf); Float_t xborder = Geo::XPAD * 0.5 - std::abs(x); Float_t zborder = Geo::ZPAD * 0.5 - std::abs(z); @@ -309,7 +310,7 @@ void Digitizer::addDigit(Int_t channel, UInt_t istrip, Double_t time, Float_t x, Float_t timewalkX = x * mTimeWalkeSlope; Float_t timewalkZ = (z - (padZfired - 0.5) * Geo::ZPAD) * mTimeWalkeSlope; - if (border < 0) { // keep the effect onlu if hit out of pad + if (border < 0) { // keep the effect only if hit out of pad border *= -1; Float_t extraTimeSmear = border * mTimeSlope; time += gRandom->Gaus(mTimeDelay, extraTimeSmear); @@ -325,7 +326,7 @@ void Digitizer::addDigit(Int_t channel, UInt_t istrip, Double_t time, Float_t x, time += TMath::Sqrt(timewalkX * timewalkX + timewalkZ * timewalkZ) - mTimeDelayCorr - mTimeWalkeSlope * 2; // Decalibrate - float tsCorr = mCalibApi->getTimeDecalibration(channel, tot); + float tsCorr = mCalibApi->getTimeDecalibration(channel, tot * Geo::TOTBIN_NS); if (std::abs(tsCorr) > 200E3) { // accept correction up to 200 ns LOG(error) << "Wrong de-calibration correction for ch = " << channel << ", tot = " << tot << " (Skip it)"; return; @@ -344,8 +345,10 @@ void Digitizer::addDigit(Int_t channel, UInt_t istrip, Double_t time, Float_t x, int tdc = int((time - Geo::BC_TIME_INPS * nbc) * Geo::NTDCBIN_PER_PS); + static long firstlongbc = long(o2::raw::HBFUtils::Instance().orbitFirstSampled) * o2::constants::lhc::LHCMaxBunches; // add orbit and bc nbc += mEventTime.toLong(); + t0 += (mEventTime.toLong() - firstlongbc - Geo::LATENCYWINDOW_IN_BC) * Geo::BC_TIME_INPS; // printf("orbit = %d -- bc = %d -- nbc = (%d) %d\n",mEventTime.orbit,mEventTime.bc, mEventTime.toLong(),nbc); @@ -386,7 +389,7 @@ void Digitizer::addDigit(Int_t channel, UInt_t istrip, Double_t time, Float_t x, mFutureItrackID.push_back(trackID); // fill temporary digits array - insertDigitInFuture(channel, tdc, tot * Geo::NTOTBIN_PER_NS, nbc, lblCurrent); + insertDigitInFuture(channel, tdc, tot, nbc, lblCurrent); return; // don't fill if doesn't match any available readout window } else if (isIfOverlap == MAXWINDOWS) { // add in future digits but also in one of the current readout windows (beacuse of windows overlap) lblCurrent = mFutureIevent.size(); @@ -395,7 +398,7 @@ void Digitizer::addDigit(Int_t channel, UInt_t istrip, Double_t time, Float_t x, mFutureItrackID.push_back(trackID); // fill temporary digits array - insertDigitInFuture(channel, tdc, tot * Geo::NTOTBIN_PER_NS, nbc, lblCurrent); + insertDigitInFuture(channel, tdc, tot, nbc, lblCurrent); } if (isnext) { @@ -416,7 +419,7 @@ void Digitizer::addDigit(Int_t channel, UInt_t istrip, Double_t time, Float_t x, mcTruthContainer = mMCTruthContainerNext[isnext - 1]; } - fillDigitsInStrip(strips, mcTruthContainer, channel, tdc, tot, nbc, istrip, trackID, mEventID, mSrcID); + fillDigitsInStrip(strips, mcTruthContainer, channel, tdc, tot, nbc, istrip, trackID, mEventID, mSrcID, geanttime, t0); if (isIfOverlap > -1 && isIfOverlap < MAXWINDOWS) { // fill also a second readout window because of the overlap if (!isIfOverlap) { @@ -427,18 +430,18 @@ void Digitizer::addDigit(Int_t channel, UInt_t istrip, Double_t time, Float_t x, mcTruthContainer = mMCTruthContainerNext[isIfOverlap - 1]; } - fillDigitsInStrip(strips, mcTruthContainer, channel, tdc, tot, nbc, istrip, trackID, mEventID, mSrcID); + fillDigitsInStrip(strips, mcTruthContainer, channel, tdc, tot, nbc, istrip, trackID, mEventID, mSrcID, geanttime, t0); } } //______________________________________________________________________ -void Digitizer::fillDigitsInStrip(std::vector* strips, o2::dataformats::MCTruthContainer* mcTruthContainer, int channel, int tdc, int tot, uint64_t nbc, UInt_t istrip, Int_t trackID, Int_t eventID, Int_t sourceID) +void Digitizer::fillDigitsInStrip(std::vector* strips, o2::dataformats::MCTruthContainer* mcTruthContainer, int channel, int tdc, int tot, uint64_t nbc, UInt_t istrip, Int_t trackID, Int_t eventID, Int_t sourceID, float geanttime, double t0) { int lblCurrent; if (mcTruthContainer) { lblCurrent = mcTruthContainer->getIndexedSize(); // this is the size of mHeaderArray; } - Int_t lbl = (*strips)[istrip].addDigit(channel, tdc, tot * Geo::NTOTBIN_PER_NS, nbc, lblCurrent); + Int_t lbl = (*strips)[istrip].addDigit(channel, tdc, tot, nbc, lblCurrent, 0, 0, geanttime, t0); if (mcTruthContainer) { if (lbl == lblCurrent) { // it means that the digit was a new one --> we have to add the info in the MC container @@ -1099,7 +1102,7 @@ void Digitizer::checkIfReuseFutureDigits() int trackID = mFutureItrackID[digit->getLabel()]; int sourceID = mFutureIsource[digit->getLabel()]; int eventID = mFutureIevent[digit->getLabel()]; - fillDigitsInStrip(strips, mcTruthContainer, digit->getChannel(), digit->getTDC(), digit->getTOT(), digit->getBC(), digit->getChannel() / Geo::NPADS, trackID, eventID, sourceID); + fillDigitsInStrip(strips, mcTruthContainer, digit->getChannel(), digit->getTDC(), digit->getTOT(), digit->getBC(), digit->getChannel() / Geo::NPADS, trackID, eventID, sourceID, digit->getTgeant()); if (isIfOverlap < 0) { // if there is no overlap candidate // remove digit from array in the future diff --git a/Detectors/TPC/base/CMakeLists.txt b/Detectors/TPC/base/CMakeLists.txt index c13fec6f03ab7..d4c1bc4602d54 100644 --- a/Detectors/TPC/base/CMakeLists.txt +++ b/Detectors/TPC/base/CMakeLists.txt @@ -38,6 +38,7 @@ o2_add_library(TPCBase src/IonTailSettings.cxx src/FEEConfig.cxx src/DeadChannelMapCreator.cxx + src/CommonModeCorrection.cxx PUBLIC_LINK_LIBRARIES Vc::Vc Boost::boost O2::DataFormatsTPC O2::DetectorsRaw O2::CCDB FairRoot::Base) @@ -70,6 +71,7 @@ o2_target_root_dictionary(TPCBase include/TPCBase/IonTailSettings.h include/TPCBase/FEEConfig.h include/TPCBase/DeadChannelMapCreator.h + include/TPCBase/CommonModeCorrection.h include/TPCBase/CDBTypes.h) o2_add_test(Base COMPONENT_NAME tpc diff --git a/Detectors/TPC/base/include/TPCBase/CommonModeCorrection.h b/Detectors/TPC/base/include/TPCBase/CommonModeCorrection.h new file mode 100644 index 0000000000000..a222327d2b434 --- /dev/null +++ b/Detectors/TPC/base/include/TPCBase/CommonModeCorrection.h @@ -0,0 +1,246 @@ +// 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 CommonModeCorrection.h +/// \brief Calculate the common mode correction factor +/// \author Jens Wiechula, Jens.Wiechula@ikf.uni-frankfurt.de + +#ifndef AliceO2_TPC_CommonModeCorrection_H_ +#define AliceO2_TPC_CommonModeCorrection_H_ + +#include +#include +#include + +#include "DataFormatsTPC/Digit.h" +#include "TPCBase/FEEConfig.h" + +namespace o2::tpc +{ + +/// Class to calculate the common mode correction +/// +/// Calculation of the common mode correction, based on the algorithm propsed by Marian Ivanov +/// The calculation is done for one single CRU and time bin +class CommonModeCorrection +{ + public: + struct CMdata { + std::vector adcValues; + std::vector cmKValues; + std::vector pedestals; + + void resize(size_t newSize) + { + adcValues.resize(newSize); + cmKValues.resize(newSize); + pedestals.resize(newSize); + } + + void clear() + { + adcValues.clear(); + cmKValues.clear(); + pedestals.clear(); + } + }; + + struct CMInfo { + float cmValue{}; ///< common mode value from pseudo code + float cmValueStd{}; ///< std dev of common mode values from pseudo code + float cmValueCRU{}; ///< common mode value from firmware, if available + float sumPos{}; ///< sum of positive signals > mSumPosThreshold + float sumNeg{}; ///< sum of negative signals <= mSumPosThreshold, corrected for k-factor + uint16_t nPadsUsed{}; ///< number of pads used for CM calculation + uint16_t nNeg{}; ///< number of pads used for sumNeg + uint16_t nOccupancy{}; ///< number of CM corrected pads larger than mOccupancyThreshold + uint16_t nSaturation{}; ///< number of pads in saturation + }; + + struct CMDebug { + std::vector nPadsOk{}; + std::vector adcDist{}; + }; + + using CalPadMapType = std::unordered_map; + + /// Calculation of the common mode value + /// + /// \param value pad-by-pad charge values + /// \param cmKValues corresponding pad-by-pad common mode k-factors + /// \param pedestals corresponding pad-by-pad pedestals + /// \param + CMInfo getCommonMode(gsl::span values, gsl::span cmKValues, gsl::span pedestals, CMDebug* cmDebug = nullptr) const; + CMInfo getCommonMode(const std::vector& values, const std::vector& cmKValues, const std::vector& pedestals) const { return getCommonMode(gsl::span(values), gsl::span(cmKValues), gsl::span(pedestals)); } + + CMInfo getCommonMode(const CMdata& cmData) const { return getCommonMode(std::span(cmData.adcValues), std::span(cmData.cmKValues), std::span(cmData.pedestals)); } + + void setNPadsCompRandom(int n) { mNPadsCompRamdom = n; } + int getNPadsCompRandom() const { return mNPadsCompRamdom; } + + void setNPadsCompMin(int n) { mNPadsCompMin = n; } + int getNPadsCompMin() const { return mNPadsCompMin; } + + /// Minimum number of pads required in the CM calculation to be used for digit correction + void setNPadsMinCM(int n) { mNPadsMinCM = n; } + int getNPadsMinCM() const { return mNPadsMinCM; } + + void setQEmpty(float q) { mQEmpty = q; } + float getQEmpty() const { return mQEmpty; } + + void setQComp(float q) { mQComp = q; } + float getQComp() const { return mQComp; } + + /// The mQComp will be set to (cm - mQCompScaleThreshold) * mQCompScale, if cm > mQCompScaleThreshold + void setQCompScaleThreshold(float q) { mQCompScaleThreshold = q; } + float getQCompScaleThreshold() const { return mQCompScaleThreshold; } + + /// The mQComp will be set to (cm - mQCompScaleThreshold) * mQCompScale, if cm > mQCompScaleThreshold + void setQCompScale(float q) { mQCompScale = q; } + float getQCompScale() const { return mQCompScale; } + + /// Threshold above which a signal is considered for sumPos, if debug information is used + void setSumPosThreshold(float threshold) { mSumPosThreshold = threshold; } + float getSumPosThreshold() const { return mSumPosThreshold; } + + /// Threshold above which a signal is considered for the occupancy + void setOccupancyThreshold(float threshold) { mOccupancyThreshold = threshold; } + float getOccupancyThreshold() const { return mOccupancyThreshold; } + + /// Pad maps loaded from FEEConfig + void setPadMaps(CalPadMapType& padMaps) { mPadMaps = padMaps; } + + /// load a CalPad from file and add it to the local mPadMaps + /// \param fileName input file name + /// \param nameInFile name of the CalPad object in the file + /// \param namePadMap name under which to store the object in the mPadMaps, if empty use the same as nameInFile + void loadCalPad(std::string_view fileName, std::string_view nameInFile, std::string_view namePadMap = ""); + + /// load CMkValues from file, assuming it is stored under the name "CMkValues + void loadCMkValues(std::string_view fileName) { loadCalPad(fileName, "CMkValues"); } + + /// load Pedestals from file, assuming it is stored under the name "Pedestals + void loadPedestals(std::string_view fileName) { loadCalPad(fileName, "Pedestals"); } + + /// Custom setting of CalPad, overwriting what was set in mPadMaps + void setCalPad(const CalPad& calPad, std::string_view name) { mPadMaps[name.data()] = calPad; } + + /// cmk value + float getCMkValue(int sector, int row, int pad) { return mPadMaps["CMkValues"].getValue(sector, row, pad); } + + /// pedestal value + float getPedestalValue(int sector, int row, int pad) { return mPadMaps["Pedestals"].getValue(sector, row, pad); } + + /// load the Pad maps from CCDB + void + loadDefaultPadMaps(FEEConfig::Tags feeTag = FEEConfig::Tags::Physics30sigma); + + CMdata collectCMdata(const std::vector& digits, int cru, int timeBin); + + int getCommonMode(std::vector& digits, std::vector>& cmValues, bool negativeOnly = false, bool hasInjectedCMValue = false, std::vector>* cmDebug = nullptr, int minTimeBin = -1, int maxTimeBin = -1) const; + + /// corret digits for common mode + /// \param cmValues will contain CM information for each CRU and time bin + /// \param negativeOnly only correct negative common mode signals + /// \return maximum + int correctDigits(std::vector& digits, std::vector>& cmValues, bool negativeOnly = false, bool hasInjectedCMValue = false, std::vector>* cmDebug = nullptr, int minTimeBin = -1, int maxTimeBin = -1) const; + + void correctDigits(std::string_view digiFileIn, Long64_t maxEntries = -1, std::string_view digitFileOut = "tpcdigit_cmcorr.root", std::string_view cmFileOut = "CommonModeValues.root", bool negativeOnly = false, int nThreads = 1, bool writeOnlyCM = false, bool writeDebug = false, bool hasInjectedCMValue = false, int minTimeBin = -1, int maxTimeBin = -1); + + void limitKFactorPrecision(bool limit = true) { mLimitKFactor = limit; } + void limitPedestalPrecision(bool limit = true) { mLimitPedestal = limit; } + + /// set the number of threads used for CM calculation + /// \param nThreads number of threads + static void setNThreads(const int nThreads) { sNThreads = nThreads; } + + /// \return returns the number of threads used for decoding + static int getNThreads() { return sNThreads; } + + /// add artificial common mode, only works when using the 'correctDigits' function + void addCommonMode(float cm) { mArtificialCM = cm; } + + void setCorrectOutputForPedestal(bool corret = true) { mCorrectOutputForPedestal = corret; } + bool getCorrectOutputForPedestal() const { return mCorrectOutputForPedestal; } + + /// Add zeros for pads without signal + void setAddSubthreshold(bool addSubthreshold) { mSubthreshold = addSubthreshold; } + bool getAddSubthreshold() const { return mSubthreshold; } + + static float decodeInjectedCMValue(float lower, float upper); + + private: + inline static int sNThreads{1}; ///< Number of parallel threads for the CM calculation + int mNPadsCompRamdom{10}; ///< Number of random pads to compare with to check if the present pad is empty + int mNPadsCompMin{7}; ///< Minimum number of neighbouring pads with q close to present pad to define this as empty + int mNPadsMinCM{0}; ///< Minimum number of pads required in the CM calculation to be used for digit correction + float mQEmpty{2}; ///< Threshold to enter check for empty pad + float mQComp{1}; ///< Threshold for comparison with random pads + float mQCompScaleThreshold{0}; ///< Charge threshold from which on to increase mQComp + float mQCompScale{0}; ///< Slope with which to increase mQComp if below mQCompScaleThreshold + float mSumPosThreshold{2}; ///< calculate sumPos > mSumPosThreshold, sumNeg M<= mSumPosThreshold + float mOccupancyThreshold{3}; ///< calculate number of pads > mQCompScaleThreshold after CM correction + bool mLimitKFactor{false}; ///< Limit the k-factor precision to 2I6F + bool mLimitPedestal{false}; ///< Limit the preestal precision to 10I2F + int mSubthreshold{0}; ///< Add data for pads without signal. 1 = add zeros; 2 = add random noise + float mArtificialCM{}; ///< artificial common mode signals + bool mCorrectOutputForPedestal{false}; ///< correct the writte out ADC for the pedestal value + + CalPadMapType mPadMaps; ///< Pad-by-pad CRU configuration values (Pedestal, Noise, ITF + CM parameters) + + struct pos { + int row; + int pad; + }; + + // positions of lower words per CRU in sector + const std::array mCMInjectIDLower{ + // row0 pad0 row1 pad1 + pos{0, 2}, + pos{20, 1}, + pos{32, 2}, + pos{51, 1}, + pos{63, 1}, + pos{84, 1}, + pos{97, 1}, + pos{116, 2}, + pos{127, 2}, + pos{142, 0}, + }; + + // positions of upper words per CRU in sector + const std::array mCMInjectIDUpper{ + // row0 pad0 row1 pad1 + pos{0, 3}, + pos{20, 3}, + pos{32, 3}, + pos{51, 3}, + pos{63, 2}, + pos{84, 4}, + pos{97, 2}, + pos{115, 5}, + pos{127, 3}, + pos{142, 4}, + }; + + /// Return the value stored in mPadMaps["calibName"] + /// \param calibName name of calibraion in mPadMaps + /// \param cru CRU number + /// \param pad Pad number within the CRU + float getCalPadValue(const std::string calibName, int icru, int pad) const; + + bool padMapExists(const std::string& calibName); + + ClassDefNV(CommonModeCorrection, 2); +}; + +} // namespace o2::tpc +#endif diff --git a/Detectors/TPC/base/include/TPCBase/Mapper.h b/Detectors/TPC/base/include/TPCBase/Mapper.h index cee3d76db85b2..f2ff425675df6 100644 --- a/Detectors/TPC/base/include/TPCBase/Mapper.h +++ b/Detectors/TPC/base/include/TPCBase/Mapper.h @@ -396,6 +396,14 @@ class Mapper bool isOutOfSector(GlobalPosition3D posEle, const Sector& sector, const float margin = 0.f) const; + static bool isEdgePad(int rowInSector, int padInRow); + static bool isFirstOrLastRowInStack(int rowInSector); + static bool isBelowSpacerCross(int rowInSector, int padInRow); + static bool isHighCouplingPad(int rowInSector, int padInRow) + { + return isEdgePad(rowInSector, padInRow) || isFirstOrLastRowInStack(rowInSector) || isBelowSpacerCross(rowInSector, padInRow); + } + static constexpr unsigned short getNumberOfIROCs() { return 36; } static constexpr unsigned short getNumberOfOROCs() { return 36; } static constexpr unsigned short getPadsInIROC() { return mPadsInIROC; } @@ -523,6 +531,7 @@ class Mapper static constexpr unsigned int GLOBALPADOFFSET[NREGIONS]{0, 1200, 2400, 3840, 5280, 6720, 8160, 9760, 11360, 12960}; ///< offset of number of pads for region static constexpr unsigned int ROWSPERREGION[NREGIONS]{17, 15, 16, 15, 18, 16, 16, 14, 13, 12}; ///< number of pad rows for region static constexpr unsigned int ROWOFFSET[NREGIONS]{0, 17, 32, 48, 63, 81, 97, 113, 127, 140}; ///< offset to calculate local row from global row + static constexpr unsigned int ROWOFFSETSTACK[4]{0, 63, 97, 127}; ///< offset to calculate local row from global row static constexpr float REGIONAREA[NREGIONS]{374.4f, 378.f, 453.6f, 470.88f, 864.f, 864.f, 1167.36f, 1128.96f, 1449.6f, 1456.8f}; ///< volume of each region in cm^2 static constexpr float INVPADAREA[NREGIONS]{1 / 0.312f, 1 / 0.315f, 1 / 0.315f, 1 / 0.327f, 1 / 0.6f, 1 / 0.6f, 1 / 0.7296f, 1 / 0.7056f, 1 / 0.906f, 1 / 0.9105f}; ///< inverse size of the pad area padwidth*padLength static constexpr unsigned REGION[PADROWS] = { @@ -542,7 +551,7 @@ class Mapper {0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4}, // region 7 {0, 0, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5}, // region 8 {0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5} // region 9 - }; ///< additional pads per row compared to first row + }; ///< additional pads per row compared to first row const inline static std::vector OFFSETCRULOCAL[NREGIONS]{ {0, 66, 132, 198, 266, 334, 402, 472, 542, 612, 684, 756, 828, 902, 976, 1050, 1124}, // region 0 {0, 76, 152, 228, 306, 384, 462, 542, 622, 702, 784, 866, 948, 1032, 1116}, // region 1 @@ -554,7 +563,7 @@ class Mapper {0, 110, 220, 332, 444, 556, 670, 784, 898, 1014, 1130, 1246, 1364, 1482}, // region 7 {0, 118, 236, 356, 476, 598, 720, 844, 968, 1092, 1218, 1344, 1472}, // region 8 {0, 128, 258, 388, 520, 652, 784, 918, 1052, 1188, 1324, 1462} // region 9 - }; ///< row offset in cru for given local pad row + }; ///< row offset in cru for given local pad row const inline static std::vector PADSPERROW[NREGIONS]{ {66, 66, 66, 68, 68, 68, 70, 70, 70, 72, 72, 72, 74, 74, 74, 74, 76}, // region 0 {76, 76, 76, 78, 78, 78, 80, 80, 80, 82, 82, 82, 84, 84, 84}, // region 1 @@ -566,7 +575,7 @@ class Mapper {110, 110, 112, 112, 112, 114, 114, 114, 116, 116, 116, 118, 118, 118}, // region 7 {118, 118, 120, 120, 122, 122, 124, 124, 124, 126, 126, 128, 128}, // region 8 {128, 130, 130, 132, 132, 132, 134, 134, 136, 136, 138, 138} // region 9 - }; ///< number of pads per row in region + }; ///< number of pads per row in region static constexpr unsigned int OFFSETCRUGLOBAL[PADROWS]{ 0, 66, 132, 198, 266, 334, 402, 472, 542, 612, 684, 756, 828, 902, 976, 1050, 1124, // region 0 0, 76, 152, 228, 306, 384, 462, 542, 622, 702, 784, 866, 948, 1032, 1116, // region 1 @@ -578,7 +587,7 @@ class Mapper 0, 110, 220, 332, 444, 556, 670, 784, 898, 1014, 1130, 1246, 1364, 1482, // region 7 0, 118, 236, 356, 476, 598, 720, 844, 968, 1092, 1218, 1344, 1472, // region 8 0, 128, 258, 388, 520, 652, 784, 918, 1052, 1188, 1324, 1462 // region 9 - }; ///< row offset in cru for given global pad row + }; ///< row offset in cru for given global pad row static constexpr unsigned int LinksPerRegionPerEndpoint[NREGIONS][NENDPOINTS]{ {8, 7}, // region 0 @@ -591,7 +600,7 @@ class Mapper {10, 10}, // region 7 {10, 10}, // region 8 {10, 10}, // region 9 - }; ///< number of links per region per end point + }; ///< number of links per region per end point private: Mapper(const std::string& mappingDir); diff --git a/Detectors/TPC/base/src/CommonModeCorrection.cxx b/Detectors/TPC/base/src/CommonModeCorrection.cxx new file mode 100644 index 0000000000000..729fb408eb204 --- /dev/null +++ b/Detectors/TPC/base/src/CommonModeCorrection.cxx @@ -0,0 +1,568 @@ +// 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 CommonModeCorrection.cxx +/// \brief Calculate the common mode correction factor + +// #include +#include +#include +#include +#include "CCDB/CcdbApi.h" +#include "TPCBase/CommonModeCorrection.h" +#include "TPCBase/Mapper.h" +#include "TPCBase/Utils.h" +#include "TPCBase/CRUCalibHelpers.h" +#include "TChain.h" +#include "TROOT.h" +#include "TFile.h" +#include "MathUtils/RandomRing.h" +#include "CommonUtils/TreeStreamRedirector.h" + +using namespace o2::tpc; +using namespace o2::tpc::cru_calib_helpers; +CommonModeCorrection::CMInfo CommonModeCorrection::getCommonMode(gsl::span values, gsl::span cmKValues, gsl::span pedestals, CMDebug* cmDebug) const +{ + if (values.size() == 0) { + return CMInfo{}; + } + // sanity check + if (values.size() != cmKValues.size() || values.size() != pedestals.size()) { + LOGP(error, "vector sizes of input values, cmKValues and pedestals don't match: {}, {}, {}", values.size(), cmKValues.size(), pedestals.size()); + return CMInfo{}; + } + static math_utils::RandomRing random(math_utils::RandomRing<>::RandomType::Flat); + std::vector adcCM; //< ADC values used for common mode calculation + + CMInfo cmInfo; + if (cmDebug) { + cmDebug->nPadsOk.resize(mNPadsCompRamdom + 1); + cmDebug->adcDist.resize(10); + } + + for (size_t iPad = 0; iPad < values.size(); ++iPad) { + const float kCM = mLimitKFactor ? fixedSizeToFloat<6>(floatToFixedSize<8, 6>(cmKValues[iPad])) : cmKValues[iPad]; + const float pedestal = mLimitPedestal ? fixedSizeToFloat(floatToFixedSize(pedestals[iPad])) : pedestals[iPad]; + const float adcPadRaw = values[iPad]; + const float adcPad = adcPadRaw - pedestal; + const float adcPadNorm = (kCM > 0) ? adcPad / kCM : 0; + + if (adcPadRaw > 1023.7) { + ++cmInfo.nSaturation; + } + + if (adcPad > mQEmpty) { + continue; + } + + float qCompAdd = 0; + if ((mQCompScaleThreshold < 0) && (adcPadNorm < mQCompScaleThreshold)) { + qCompAdd = (mQCompScaleThreshold - adcPadNorm) * mQCompScale; + LOGP(info, "Setting qCompAdd to {} for {}", qCompAdd, adcPadNorm); + } + + int nPadsOK = 0; + + for (int iRnd = 0; iRnd < mNPadsCompRamdom; ++iRnd) { + int padRnd = 0; + do { + padRnd = int(random.getNextValue() * (values.size() - 1)); + } while (padRnd == iPad); + const float kCMRnd = mLimitKFactor ? fixedSizeToFloat<6>(floatToFixedSize<8, 6>(cmKValues[padRnd])) : cmKValues[padRnd]; + const float pedestalRnd = mLimitPedestal ? fixedSizeToFloat(floatToFixedSize(pedestals[padRnd])) : pedestals[padRnd]; + const float adcPadRnd = values[padRnd] - pedestalRnd; + const float adcPadRndNorm = (kCMRnd > 0) ? adcPadRnd / kCMRnd : 0; + const float adcDist = std::abs(adcPadNorm - adcPadRndNorm); + if (cmDebug) { + const size_t distPos = std::min(cmDebug->adcDist.size() - 1, size_t(adcDist / 0.5)); + ++cmDebug->adcDist[distPos]; + } + if (adcDist < mQComp) { + ++nPadsOK; + } + } + + if (cmDebug) { + ++cmDebug->nPadsOk[nPadsOK]; + } + + if (nPadsOK >= mNPadsCompMin) { + adcCM.emplace_back(adcPadNorm); + } + } + + const int entriesCM = int(adcCM.size()); + float commonMode = 0; // std::accumulate(adcCM.begin(), adcCM.end(), 0.f); + float commonModeStd = 0; + + if (entriesCM > 0) { + std::for_each(adcCM.begin(), adcCM.end(), [&commonMode, &commonModeStd](const auto val) { + commonMode += val; + commonModeStd += val * val; + }); + commonMode /= float(entriesCM); + commonModeStd = std::sqrt(std::abs(commonModeStd / entriesCM - commonMode * commonMode)); + } + cmInfo.cmValue = commonMode; + cmInfo.cmValueStd = commonModeStd; + cmInfo.nPadsUsed = entriesCM; + + for (size_t iPad = 0; iPad < values.size(); ++iPad) { + const float kCM = mLimitKFactor ? fixedSizeToFloat<6>(floatToFixedSize<8, 6>(cmKValues[iPad])) : cmKValues[iPad]; + const float pedestal = mLimitPedestal ? fixedSizeToFloat(floatToFixedSize(pedestals[iPad])) : pedestals[iPad]; + const float adcPadRaw = values[iPad]; + const float adcPad = adcPadRaw - pedestal; + const float adcPadNorm = (kCM > 0) ? adcPad / kCM : 0; + const float adcPadCorr = adcPad - kCM * commonMode; + + if (adcPadCorr > mSumPosThreshold) { + cmInfo.sumPos += adcPadCorr; + } else { + cmInfo.sumNeg += adcPadNorm; + ++cmInfo.nNeg; + } + + if (mOccupancyThreshold > 0) { + if (adcPadCorr > mOccupancyThreshold) { + ++cmInfo.nOccupancy; + } + } + } + + return cmInfo; +} + +void CommonModeCorrection::loadDefaultPadMaps(FEEConfig::Tags tag) +{ + o2::ccdb::CcdbApi cdbApi; + cdbApi.init("http://alice-ccdb.cern.ch"); + const auto feeConfig = cdbApi.retrieveFromTFileAny("TPC/Config/FEE", {}, long(tag)); + if (!feeConfig) { + LOGP(error, "Could not retrieve pad maps"); + return; + } + mPadMaps = feeConfig->padMaps; + delete feeConfig; +} + +CommonModeCorrection::CMdata CommonModeCorrection::collectCMdata(const std::vector& digits, int cru, int timeBin) +{ + + CMdata data; + if (!padMapExists("CMkValues") || padMapExists("Pedestals")) { + return data; + } + + for (const auto& digit : digits) { + if (digit.getTimeStamp() < timeBin) { + continue; + } + + if (digit.getTimeStamp() > timeBin) { + break; + } + + if (digit.getCRU() < cru) { + continue; + } + + if (digit.getCRU() > cru) { + break; + } + + const auto sector = CRU(digit.getCRU()).sector(); + data.adcValues.emplace_back(digit.getChargeFloat()); + data.cmKValues.emplace_back(mPadMaps["CMkValues"].getValue(sector, digit.getRow(), digit.getPad())); + data.pedestals.emplace_back(mPadMaps["Pedestals"].getValue(sector, digit.getRow(), digit.getPad())); + } + return data; +} + +int CommonModeCorrection::getCommonMode(std::vector& digits, std::vector>& cmValues, bool negativeOnly, bool hasInjectedCMValue, std::vector>* cmDebug, int minTimeBin, int maxTimeBin) const +{ + // calculation common mode values + int maxTimeBinProcessed = -1; + int lastCRU = -1; + int lastTimeBin = -1; + CMdata data; + const auto& cmkValues = mPadMaps.at("CMkValues"); + const auto& pedestals = mPadMaps.at("Pedestals"); + + bool doArtificialCM = std::abs(mArtificialCM) > 0; + + // for decoding of the injected common mode signals + float cmInjectedLower{}; + float cmInjectedUpper{}; + + for (size_t iDigit = 0; iDigit < digits.size(); ++iDigit) { + auto& digit = digits[iDigit]; + const auto timeBin = digit.getTimeStamp(); + if ((minTimeBin > -1) && (timeBin < minTimeBin)) { + continue; + } + if ((maxTimeBin > -1) && (timeBin > maxTimeBin)) { + continue; + } + if ((lastCRU > -1) && ((digit.getCRU() != lastCRU) || (digit.getTimeStamp() != lastTimeBin))) { + auto& cmValuesCRU = cmValues[lastCRU]; + if (cmValuesCRU.size() <= lastTimeBin) { + cmValuesCRU.resize(lastTimeBin + 500); + if (cmDebug) { + (*cmDebug)[lastCRU].resize(lastTimeBin + 500); + } + } + if (mSubthreshold > 0) { + const size_t nPadsCRU = Mapper::PADSPERREGION[lastCRU % 10]; + const auto dataSize = data.adcValues.size(); + if (dataSize < nPadsCRU) { + data.resize(nPadsCRU); + if (mSubthreshold == 2) { + for (size_t i = dataSize; i < nPadsCRU; ++i) { + data.adcValues[i] = gRandom->Gaus(); + } + } + } + } + cmValuesCRU[lastTimeBin] = getCommonMode(data.adcValues, data.cmKValues, data.pedestals, cmDebug ? &((*cmDebug)[lastCRU][lastTimeBin]) : nullptr); + if (hasInjectedCMValue) { + cmValuesCRU[lastTimeBin].cmValueCRU = decodeInjectedCMValue(cmInjectedLower, cmInjectedUpper); + } + // LOGP(info, "processing CRU {}, timeBin {}, CM = {}", lastCRU, lastTimeBin, cmValuesCRU[lastTimeBin].cmValue); + + data.clear(); + } + const auto sector = CRU(digit.getCRU()).sector(); + const auto cmkValue = cmkValues.getValue(sector, digit.getRow(), digit.getPad()); + const auto pedestal = pedestals.getValue(sector, digit.getRow(), digit.getPad()); + float charge = digit.getChargeFloat(); + if (doArtificialCM) { + charge = std::clamp(charge + mArtificialCM * cmkValue, 0.f, 1023.f); + } + lastCRU = digit.getCRU(); + lastTimeBin = timeBin; + maxTimeBinProcessed = std::max(lastTimeBin, maxTimeBinProcessed); + + bool isInjectedCMPad = false; + if (hasInjectedCMValue) { + const auto posLow = mCMInjectIDLower[lastCRU % 10]; + const auto posUpper = mCMInjectIDUpper[lastCRU % 10]; + const auto row = digit.getRow(); + const auto pad = digit.getPad(); + if (row == posLow.row) { + if (pad == posLow.pad) { + cmInjectedLower = digit.getChargeFloat(); + isInjectedCMPad = true; + // LOGP(info, "setting lower CM value cru {}, row {}, pad {}: {:012b}", digit.getCRU(), row, pad, floatToFixedSize(digit.getChargeFloat())); + } + } + if (row == posUpper.row) { + if (pad == posUpper.pad) { + cmInjectedUpper = digit.getChargeFloat(); + isInjectedCMPad = true; + // LOGP(info, "setting upper CM value cru {}, row {}, pad {}: {:012b}", digit.getCRU(), row, pad, floatToFixedSize(digit.getChargeFloat())); + if (cmInjectedUpper == 0) { + LOGP(info, "cm upper = 0 cru {}, row {}, pad {}", digit.getCRU(), row, pad); + } + } + } + } + + if (!isInjectedCMPad) { + data.adcValues.emplace_back(charge); + data.cmKValues.emplace_back(cmkValue); + data.pedestals.emplace_back(pedestal); + } + } + { + auto& cmValuesCRU = cmValues[lastCRU]; + if (cmValuesCRU.size() <= lastTimeBin) { + cmValuesCRU.resize(lastTimeBin + 500); + if (cmDebug) { + (*cmDebug)[lastCRU].resize(lastTimeBin + 500); + } + } + cmValuesCRU[lastTimeBin] = getCommonMode(data.adcValues, data.cmKValues, data.pedestals, cmDebug ? &((*cmDebug)[lastCRU][lastTimeBin]) : nullptr); + // LOGP(info, "processing CRU {}, timeBin {}, CM = {}", lastCRU, lastTimeBin, cmValuesCRU[lastTimeBin].cmValue); + + if (hasInjectedCMValue) { + cmValuesCRU[lastTimeBin].cmValueCRU = decodeInjectedCMValue(cmInjectedLower, cmInjectedUpper); + } + + data.clear(); + } + return maxTimeBinProcessed; +} + +int CommonModeCorrection::correctDigits(std::vector& digits, std::vector>& cmValues, bool negativeOnly, bool hasInjectedCMValue, std::vector>* cmDebug, int minTimeBin, int maxTimeBin) const +{ + const auto maxTimeBinProcessed = getCommonMode(digits, cmValues, negativeOnly, hasInjectedCMValue, cmDebug, minTimeBin, maxTimeBin); + const auto& cmkValues = mPadMaps.at("CMkValues"); + const auto& pedestals = mPadMaps.at("Pedestals"); + // ===| apply correction |==== + for (auto& digit : digits) { + const auto timeBin = digit.getTimeStamp(); + if ((minTimeBin > -1) && (timeBin < minTimeBin)) { + continue; + } + if ((maxTimeBin > -1) && (timeBin > maxTimeBin)) { + continue; + } + const auto sector = CRU(digit.getCRU()).sector(); + const auto cmKValue = cmkValues.getValue(sector, digit.getRow(), digit.getPad()); + // LOGP(info, "correcting value for CRU {}, time bin {}", digit.getCRU(), digit.getTimeStamp()); + const auto cmValue = cmValues[digit.getCRU()][digit.getTimeStamp()].cmValue; + const auto cmNPads = cmValues[digit.getCRU()][digit.getTimeStamp()].nPadsUsed; + if ((!negativeOnly || cmValue < 0) && (cmNPads > mNPadsMinCM)) { + digit.setCharge(digit.getCharge() - cmValue * cmKValue); + if (mCorrectOutputForPedestal) { + const auto sector = CRU(digit.getCRU()).sector(); + const auto pedestal = pedestals.getValue(sector, digit.getRow(), digit.getPad()); + digit.setCharge(digit.getChargeFloat() - pedestal); + } + } + } + + return maxTimeBinProcessed; +} + +void CommonModeCorrection::correctDigits(std::string_view digiFileIn, Long64_t maxEntries, std::string_view digitFileOut, std::string_view cmFileOut, bool negativeOnly, int nThreads, bool writeOnlyCM, bool writeDebug, bool hasInjectedCMValue, int minTimeBin, int maxTimeBin) +{ + ROOT::EnableThreadSafety(); + + TChain* tree = o2::tpc::utils::buildChain(fmt::format("ls {}", digiFileIn), "o2sim", "o2sim"); + Long64_t nEntries = tree->GetEntries(); + if (maxEntries > 0) { + nEntries = std::min(nEntries, maxEntries); + } + + if (mPadMaps.find("Pedestals") == mPadMaps.end()) { + LOGP(info, "Using empty pedestals"); + mPadMaps["Pedestals"] = CalPad("Pedestals"); + } + + std::unique_ptr fOut; + std::unique_ptr tOut; + if (!writeOnlyCM) { + fOut.reset(TFile::Open(digitFileOut.data(), "RECREATE")); + fOut->SetCompressionLevel(5); // zstd default level + fOut->SetCompressionAlgorithm(5); // zstd + tOut = std::make_unique("o2sim", "o2sim"); + } + + std::array*, 36> digitizedSignal; + std::array outBranches{}; + for (size_t iSec = 0; iSec < digitizedSignal.size(); ++iSec) { + digitizedSignal[iSec] = nullptr; + tree->SetBranchAddress(Form("TPCDigit_%zu", iSec), &digitizedSignal[iSec]); + if (tOut) { + outBranches[iSec] = tOut->Branch(Form("TPCDigit_%zu", iSec), &digitizedSignal[iSec]); + } + } + + o2::utils::TreeStreamRedirector pcstream(cmFileOut.data(), "recreate"); + pcstream.GetFile()->SetCompressionAlgorithm(5); + pcstream.GetFile()->SetCompressionLevel(5); + + for (Long64_t iTF = 0; iTF < nEntries; ++iTF) { + tree->GetEntry(iTF); + LOGP(info, "Processing entry {}/{}", iTF + 1, nEntries); + + std::vector> cmValues; // CRU * timeBin + std::vector> cmDebug; // CRU * timeBin + + cmValues.resize(CRU::MaxCRU); + if (writeDebug) { + cmDebug.resize(CRU::MaxCRU); + } + int maxTimeBinSeen = -1; + + auto worker = [&](int iTread) { + // for (size_t iSector = 0; iSector < 36; ++iSector) { + for (size_t iSector = iTread; iSector < 36; iSector += nThreads) { + LOGP(info, "Processing entry {}/{}, starting sector {}", iTF + 1, nEntries, iSector); + auto digits = digitizedSignal[iSector]; + int maxTimeBinSector = 0; + if (digits && (digits->size() > 0)) { + maxTimeBinSector = correctDigits(*digits, cmValues, negativeOnly, hasInjectedCMValue, writeDebug ? &cmDebug : nullptr, minTimeBin, maxTimeBin); + } + { + static std::mutex maxMutex; + std::lock_guard lock{maxMutex}; + maxTimeBinSeen = std::max(maxTimeBinSeen, maxTimeBinSector); + if (outBranches[iSector]) { + outBranches[iSector]->Fill(); + LOGP(info, "Filling branch for sector {}", iSector); + } + } + } + }; + + std::vector threads(nThreads); + + for (int i = 0; i < threads.size(); i++) { + threads[i] = std::thread(worker, i); + } + + // wait for the threads to finish + for (auto& th : threads) { + th.join(); + } + + size_t maxTimeCRU = 0; + for (int iCRU = 0; iCRU < cmValues.size(); ++iCRU) { + maxTimeCRU = std::max(maxTimeCRU, cmValues[iCRU].size()); + } + const int maxTBCRU = std::min(maxTimeBinSeen, int(maxTimeCRU)); + + for (int iTimeBin = 0; iTimeBin < maxTBCRU; ++iTimeBin) { + + std::vector cm(CRU::MaxCRU); + std::vector cmD(CRU::MaxCRU); + std::vector sumPosStack(36 * 4); + std::vector nPosStack(36 * 4); + std::vector nSaturationStack(36 * 4); + std::vector sumPosStackCRU(CRU::MaxCRU); + std::vector sumPosStackCRUCorr(CRU::MaxCRU); + std::vector nSaturationStackCRU(CRU::MaxCRU); + + for (int iCRU = 0; iCRU < cmValues.size(); ++iCRU) { + if (cmValues[iCRU].size() == 0) { + continue; + } + cm[iCRU] = cmValues[iCRU][iTimeBin]; + if (writeDebug) { + cmD[iCRU] = cmDebug[iCRU][iTimeBin]; + } + const CRU cru(iCRU); + const StackID stackID{cru.sector(), cru.gemStack()}; + const auto index = stackID.getIndex(); + sumPosStack[index] += cm[iCRU].sumPos; + nPosStack[index] += (Mapper::PADSPERREGION[cru.region()] - cm[iCRU].nNeg); + nSaturationStack[index] += cm[iCRU].nSaturation; + } + + for (int iCRU = 0; iCRU < cmValues.size(); ++iCRU) { + if (cmValues[iCRU].size() == 0) { + continue; + } + const CRU cru(iCRU); + const StackID stackID{cru.sector(), cru.gemStack()}; + const auto index = stackID.getIndex(); + sumPosStackCRU[iCRU] = sumPosStack[index]; + sumPosStackCRUCorr[iCRU] = sumPosStack[index] - nPosStack[index] * cm[iCRU].cmValue; + nSaturationStackCRU[iCRU] = nSaturationStack[index]; + } + + pcstream << "cm" + << "iTF=" << iTF + << "iTimeBin=" << iTimeBin + << "cmInfo=" << cm + << "sumPosStack=" << sumPosStackCRU + << "sumPosStackCorr=" << sumPosStackCRUCorr + << "nSaturationStack=" << nSaturationStackCRU; + + if (writeDebug) { + pcstream << "cm" + << "cmDebug=" << cmD; + } + + pcstream << "cm" + << "\n"; + } + + // if (tOut) { + // tOut->Fill(); + // } + } + + pcstream.Close(); + if (fOut && tOut) { + tOut->SetEntries(nEntries); + fOut->cd(); + tOut->Write(); + tOut.reset(); + fOut->Close(); + } +} + +float CommonModeCorrection::decodeInjectedCMValue(float lower, float upper) +{ + // CRU row0 pad0 row1 pad1 + // 0 0 2 0 3 + // 1 20 1 20 3 + // 2 32 2 32 3 + // 3 51 1 51 3 + // 4 62 1 62 2 + // 5 84 1 84 4 + // 6 97 1 97 2 + // 7 116 2 115 5 + // 8 127 2 127 3 + // 9 142 0 142 4 + // + // CM Value encoding: + // Kanal 0 : Bit 11 ... 8 = 0x8. Bit 7..0 CM-Werte Bits 7...0 + // Kanal 1 : Bit 11.. 9 = "100". Bit 8 = CM Positive, Bits 6..0 = CM-Wert Bits 14..8 + const int ilower = floatToFixedSize(lower); + const int iupper = floatToFixedSize(upper); + if (!(ilower & 0x800) || !(iupper & 0x800)) { + LOGP(error, "Not a CM word: lower: {:012b} upper: {:012b}", ilower, iupper); + return 0; + } + const int fixedSizeCM = ((iupper & 0x7F) << 8) + (ilower & 0xFF); + const float floatCM = fixedSizeToFloat<8>(fixedSizeCM); + + // bit 8 of upper word is the sign 1 = positive + return (iupper & 0x100) ? floatCM : -floatCM; +} + +float CommonModeCorrection::getCalPadValue(const std::string calibName, int icru, int pad) const +{ + if (mPadMaps.find(calibName) == mPadMaps.end()) { + LOGP(error, "{} not set, cannot be used", calibName); + return 0; + } + const auto& calPad = mPadMaps.at(calibName); + const CRU cru(icru); + const int roc = cru.roc(); + const int padOffset = (cru.isIROC()) ? Mapper::GLOBALPADOFFSET[cru.region()] : Mapper::GLOBALPADOFFSET[cru.region()] - Mapper::GLOBALPADOFFSET[4]; + + const auto& calArray = calPad.getCalArray(roc); + + return calArray.getValue(padOffset + pad); +} + +bool CommonModeCorrection::padMapExists(const std::string& calibName) +{ + if (mPadMaps.find(calibName) == mPadMaps.end()) { + LOGP(error, "{} not in mPadMaps", calibName); + return false; + } + return true; +} + +void CommonModeCorrection::loadCalPad(std::string_view fileName, std::string_view nameInFile, std::string_view namePadMap) +{ + if (fileName.size() == 0) { + return; + } + + auto pads = o2::tpc::utils::readCalPads(fileName, nameInFile); + if ((pads.size() == 0) || (pads.at(0) == nullptr)) { + LOGP(error, "Could not load object {} from file {}", nameInFile, fileName); + return; + } + + if (namePadMap.size() == 0) { + namePadMap = nameInFile; + } + + mPadMaps[namePadMap.data()] = *pads[0]; +} diff --git a/Detectors/TPC/base/src/Mapper.cxx b/Detectors/TPC/base/src/Mapper.cxx index 56ce283178da0..2796d488f014d 100644 --- a/Detectors/TPC/base/src/Mapper.cxx +++ b/Detectors/TPC/base/src/Mapper.cxx @@ -298,5 +298,41 @@ void Mapper::setTraceLengths(std::string_view inputFile, std::vector& len } } +bool Mapper::isEdgePad(int rowInSector, int padInRow) +{ + const auto& mapper = instance(); + return (padInRow == 0) || (padInRow == mapper.getNumberOfPadsInRowSector(rowInSector) - 1); +} + +bool Mapper::isFirstOrLastRowInStack(int rowInSector) +{ + if (rowInSector == 0 || rowInSector == PADROWS - 1) { + return true; + } + + const auto& mapper = instance(); + for (int i = 1; i < 4; ++i) { + if (rowInSector == ROWOFFSETSTACK[i] || rowInSector == ROWOFFSETSTACK[i] - 1) { + return true; + } + } + return false; +} + +bool Mapper::isBelowSpacerCross(int rowInSector, int padInRow) +{ + static std::vector ROWSBELOWCROSS{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + if (ROWSBELOWCROSS[rowInSector]) { + return true; + } + + const auto& mapper = instance(); + const auto padCenter = mapper.getNumberOfPadsInRowSector(rowInSector) / 2; + if (padInRow == padCenter || padInRow == padCenter - 1) { + return true; + } + return false; +} + } // namespace tpc } // namespace o2 diff --git a/Detectors/TPC/base/src/Painter.cxx b/Detectors/TPC/base/src/Painter.cxx index 863547a666611..9f143d3fa45ce 100644 --- a/Detectors/TPC/base/src/Painter.cxx +++ b/Detectors/TPC/base/src/Painter.cxx @@ -334,9 +334,9 @@ TCanvas* painter::draw(const CalDet& calDet, int nbins1D, float xMin1D, float const GlobalPosition2D pos = mapper.getPadCentre(PadROCPos(roc, irow, ipad)); const int bin = hist2D->FindBin(pos.X(), pos.Y()); if (!hist2D->GetBinContent(bin)) { - hist2D->SetBinContent(bin, val); + hist2D->SetBinContent(bin, double(val)); } - hist1D->Fill(val); + hist1D->Fill(double(val)); } } } @@ -430,7 +430,7 @@ void painter::fillHistogram2D(TH2& h2D, const CalDet& calDet, Side side) const GlobalPosition2D pos = mapper.getPadCentre(PadROCPos(roc, irow, ipad)); const int bin = h2D.FindBin(pos.X(), pos.Y()); if (!h2D.GetBinContent(bin)) { - h2D.SetBinContent(bin, val); + h2D.SetBinContent(bin, double(val)); } } } @@ -454,7 +454,7 @@ void painter::fillHistogram2D(TH2& h2D, const CalArray& calArray) const GlobalPadNumber pad = mapper.getPadNumber(padSubset, position, irow, ipad); const auto val = calArray.getValue(pad); const int cpad = ipad - padsInRow / 2; - h2D.Fill(irow, cpad, val); + h2D.Fill(irow, cpad, double(val)); } } } @@ -523,6 +523,17 @@ std::enable_if_t::value, bool> hasData(const CalArray& ca return cal.getSum() > T{0}; } +template +std::enable_if_t::value, bool> hasData(const CalArray& cal) +{ + for (const auto v : cal.getData()) { + if (int(v) > 0) { + return true; + } + } + return false; +} + template std::vector painter::makeSummaryCanvases(const CalDet& calDet, int nbins1D, float xMin1D, float xMax1D, bool onlyFilled, std::vector* outputCanvases) { @@ -589,7 +600,7 @@ std::vector painter::makeSummaryCanvases(const CalDet& calDet, int // ===| 1D histogram |=== auto h1D = new TH1F(fmt::format("h1_{}_{:02d}", calName, iroc).data(), fmt::format("{} distribution ROC {:02d} ({});ADC value", calName, iroc, getROCTitle(iroc)).data(), nbins1D, xMin1D, xMax1D); for (const auto& val : roc.getData()) { - h1D->Fill(val); + h1D->Fill(double(val)); } // ===| 2D histogram |=== @@ -1342,6 +1353,9 @@ void painter::adjustPalette(TH1* h, float x2ndc, float tickLength) gPad->Modified(); gPad->Update(); auto palette = (TPaletteAxis*)h->GetListOfFunctions()->FindObject("palette"); + if (!palette) { + return; + } palette->SetX2NDC(x2ndc); auto ax = h->GetZaxis(); ax->SetTickLength(tickLength); @@ -1425,6 +1439,12 @@ template TCanvas* painter::draw(const CalArray& calArray); template TH2* painter::getHistogram2D(const CalDet& calDet, Side side); template TH2* painter::getHistogram2D(const CalArray& calArray); +template TCanvas* painter::draw(const CalDet& calDet, int, float, float, TCanvas*); +template std::vector painter::makeSummaryCanvases(const CalDet& calDet, int, float, float, bool, std::vector*); +template TCanvas* painter::draw(const CalArray& calArray); +template TH2* painter::getHistogram2D(const CalDet& calDet, Side side); +template TH2* painter::getHistogram2D(const CalArray& calArray); + template TCanvas* painter::draw(const CalDet& calDet, int, float, float, TCanvas*); template std::vector painter::makeSummaryCanvases(const CalDet& calDet, int, float, float, bool, std::vector*); template TCanvas* painter::draw(const CalArray& calArray); diff --git a/Detectors/TPC/base/src/TPCBaseLinkDef.h b/Detectors/TPC/base/src/TPCBaseLinkDef.h index 33b6cf2c03392..60924db3953e2 100644 --- a/Detectors/TPC/base/src/TPCBaseLinkDef.h +++ b/Detectors/TPC/base/src/TPCBaseLinkDef.h @@ -66,6 +66,9 @@ #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::tpc::IonTailSettings> + ; #pragma link C++ class o2::tpc::FEEConfig + ; #pragma link C++ class o2::tpc::CRUConfig + ; +#pragma link C++ class o2::tpc::CommonModeCorrection + ; +#pragma link C++ class std::vector < o2::tpc::CommonModeCorrection::CMInfo> + ; +#pragma link C++ class std::vector < o2::tpc::CommonModeCorrection::CMDebug> + ; #pragma link C++ namespace o2::tpc::utils; #pragma link C++ function o2::tpc::utils::tokenize(const std::string_view, const std::string_view); diff --git a/Detectors/TPC/base/src/Utils.cxx b/Detectors/TPC/base/src/Utils.cxx index 8879e8ab342d2..d8a420ea4f03c 100644 --- a/Detectors/TPC/base/src/Utils.cxx +++ b/Detectors/TPC/base/src/Utils.cxx @@ -133,7 +133,7 @@ void utils::addFECInfo() h->SetTitle(title.data()); } -void utils::saveCanvases(TObjArray& arr, std::string_view outDir, std::string_view types, std::string_view rootFileName, std::string nameAdd) +void utils::saveCanvases(TObjArray& arr, std::string_view outDir, std::string_view types, std::string_view singleOutFileName, std::string nameAdd) { if (types.size()) { for (auto c : arr) { @@ -141,21 +141,39 @@ void utils::saveCanvases(TObjArray& arr, std::string_view outDir, std::string_vi } } - if (rootFileName.size()) { - std::unique_ptr outFile(TFile::Open(fmt::format("{}/{}", outDir, rootFileName).data(), "recreate")); - arr.Write(arr.GetName(), TObject::kSingleKey); - outFile->Close(); + if (singleOutFileName.size()) { + const auto outFileNames = o2::utils::Str::tokenize(singleOutFileName.data(), ','); + for (const auto& outFileName : outFileNames) { + auto fileName = fmt::format("{}/{}", outDir, outFileName); + if (o2::utils::Str::endsWith(outFileName, ".root")) { + std::unique_ptr outFile(TFile::Open(fileName.data(), "recreate")); + arr.Write(arr.GetName(), TObject::kSingleKey); + outFile->Close(); + } else if (o2::utils::Str::endsWith(outFileName, ".pdf")) { + const auto nCanv = arr.GetEntries(); + for (int i = 0; i < nCanv; ++i) { + auto fileName2 = fileName; + if (i == 0) { + fileName2 += "("; + } else if (i == nCanv - 1) { + fileName2 += ")"; + } + auto c = static_cast(arr.UncheckedAt(i)); + c->Print(fileName2.data(), fmt::format("Title:{}", c->GetTitle()).data()); + } + } + } } } -void utils::saveCanvases(std::vector& canvases, std::string_view outDir, std::string_view types, std::string_view rootFileName, std::string nameAdd) +void utils::saveCanvases(std::vector& canvases, std::string_view outDir, std::string_view types, std::string_view singleOutFileName, std::string nameAdd) { TObjArray arr; for (auto c : canvases) { arr.Add(c); } - saveCanvases(arr, outDir, types, rootFileName, nameAdd); + saveCanvases(arr, outDir, types, singleOutFileName, nameAdd); } void utils::saveCanvas(TCanvas& c, std::string_view outDir, std::string_view types, std::string nameAdd) diff --git a/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/SpacePointsCalibConfParam.h b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/SpacePointsCalibConfParam.h index 2465cbf512d2b..819ca7b0ae07f 100644 --- a/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/SpacePointsCalibConfParam.h +++ b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/SpacePointsCalibConfParam.h @@ -29,35 +29,35 @@ struct SpacePointsCalibConfParam : public o2::conf::ConfigurableParamHelpermaxZ2X; mIsInitialized = true; + + if (doBinning) { + // initialize binning + initBinning(); + } + LOG(info) << "Initialization complete"; } @@ -182,10 +184,10 @@ void TrackResiduals::initBinning() } // // Z/X binning - mDZ2XI = mNZ2XBins / sMaxZ2X; + mDZ2XI = mNZ2XBins / mMaxZ2X; mDZ2X = 1.0f / mDZ2XI; // for uniform case only if (mUniformBins[VoxZ]) { - LOGF(info, "Z/X-binning is uniform with %i bins from 0 to %f", mNZ2XBins, sMaxZ2X); + LOGF(info, "Z/X-binning is uniform with %i bins from 0 to %f", mNZ2XBins, mMaxZ2X); for (int iz = 0; iz < mNZ2XBins; ++iz) { mZ2XBinsDH.push_back(.5f * mDZ2X); mZ2XBinsDI.push_back(mDZ2XI); @@ -265,7 +267,7 @@ int TrackResiduals::getRowID(float x) const bool TrackResiduals::findVoxelBin(int secID, float x, float y, float z, std::array& bvox) const { // Z/X bin - if (fabs(z / x) > sMaxZ2X) { + if (fabs(z / x) > mMaxZ2X) { return false; } int bz = getZ2XBinExact(secID < SECTORSPERSIDE ? z / x : -z / x); @@ -601,7 +603,7 @@ int TrackResiduals::validateVoxels(int iSec) resVox.flags |= Masked; } } // loop over Z - } // loop over Y/X + } // loop over Y/X mValidFracXBins[iSec][ix] = static_cast(cntValid) / (mNY2XBins * mNZ2XBins); LOGP(debug, "Sector {}: xBin {} has {} % of voxels valid. Total masked due to fit: {} ,and sigma: {}", iSec, ix, mValidFracXBins[iSec][ix] * 100., cntMaskedFit, cntMaskedSigma); diff --git a/Detectors/TPC/calibration/macro/prepareCMFiles.C b/Detectors/TPC/calibration/macro/prepareCMFiles.C index dc14bc61aa793..08880ccbe4862 100644 --- a/Detectors/TPC/calibration/macro/prepareCMFiles.C +++ b/Detectors/TPC/calibration/macro/prepareCMFiles.C @@ -28,7 +28,10 @@ using namespace o2::tpc::cru_calib_helpers; using namespace o2::tpc; -void prepareCMFiles(const std::string_view pulserFile, std::string outputDir = "./") +/// \param limitHighCouplingPads if > 0 limit pads in the high coupling region to this value +/// \param replaceHighCouplingPads if > 0 replace pads in the high coupling region by this value (take preceedence over limitHighCouplingPads) +/// \param maxValue if > 0 limit to this maximum value +void prepareCMFiles(const std::string_view pulserFile, std::string outputDir = "./", float limitHighCouplingPads = 0, float replaceHighCouplingPads = 0, float maxValue = 0) { constexpr uint32_t DataBits = 8; constexpr uint32_t FractionalBits = 6; @@ -115,6 +118,7 @@ void prepareCMFiles(const std::string_view pulserFile, std::string outputDir = " const int fecInPartition = fecInfo.getIndex() - partInfo.getSectorFECOffset(); const int dataWrapperID = fecInPartition >= fecOffset; const int globalLinkID = (fecInPartition % fecOffset) + dataWrapperID * 12; + const auto& padPos = mapper.padPos(globalPad); float pulserVal = rocPulserQtot.getValue(ipad); @@ -128,6 +132,20 @@ void prepareCMFiles(const std::string_view pulserFile, std::string outputDir = " pulserVal = MaxVal; } + if (replaceHighCouplingPads > 0) { + if (Mapper::isHighCouplingPad(padPos.getRow(), padPos.getPad())) { + pulserVal = replaceHighCouplingPads; + } + } else if (limitHighCouplingPads > 0) { + if (Mapper::isHighCouplingPad(padPos.getRow(), padPos.getPad())) { + pulserVal = std::min(pulserVal, limitHighCouplingPads); + } + } + + if (maxValue > 0) { + pulserVal = std::min(pulserVal, maxValue); + } + const int hwChannel = getHWChannel(sampa, sampaChannel, region % 2); // for debugging // printf("%4d %4d %4d %4d %4d: %u\n", cru.number(), globalLinkID, hwChannel, fecInfo.getSampaChip(), fecInfo.getSampaChannel(), getADCValue(pedestal)); @@ -143,22 +161,36 @@ void prepareCMFiles(const std::string_view pulserFile, std::string outputDir = " const bool onlyFilled = false; // ===| k-Values full float precision |=== - const auto outFileFloatTxt = (outputDir + "/commonMode_K_values_float.txt"); - const auto outFileFloatRoot = (outputDir + "/commonMode_K_values_float.root"); + string nameAdd; + if (replaceHighCouplingPads > 0) { + nameAdd = fmt::format(".replaceHC_{:.2}", replaceHighCouplingPads); + } else if (limitHighCouplingPads > 0) { + nameAdd = fmt::format(".limitHC_{:.2}", limitHighCouplingPads); + } + + if (maxValue > 0) { + nameAdd += fmt::format(".maxValue_{:.2}", maxValue); + } + + string outNameBase = "commonMode_K_values" + nameAdd; + string outNameInvBase = "commonMode_inv_K_values" + nameAdd; + + const auto outFileFloatTxt = (outputDir + "/" + outNameBase + "_float.txt"); + const auto outFileFloatRoot = (outputDir + "/" + outNameBase + "_float.root"); writeValues(outFileFloatTxt, commonModeKValuesFloat, onlyFilled); - getCalPad(outFileFloatTxt, outFileFloatRoot, "CMkValues"); + getCalPad<0>(outFileFloatTxt, outFileFloatRoot, "CMkValues"); // ===| k-Values limited precision 2I6F |=== - const auto outFileTxt = (outputDir + "/commonMode_K_values.txt"); - const auto outFileRoot = (outputDir + "/commonMode_K_values.root"); + const auto outFileTxt = (outputDir + "/" + outNameBase + ".txt"); + const auto outFileRoot = (outputDir + "/" + outNameBase + ".root"); writeValues(outFileTxt, commonModeKValues, onlyFilled); getCalPad(outFileTxt, outFileRoot, "CMkValues"); // ===| inverse k-Values limited precision 2I6F |=== - const auto outFileInvTxt = (outputDir + "/commonMode_inv_K_values.txt"); - const auto outFileInvRoot = (outputDir + "/commonMode_inv_K_values.root"); + const auto outFileInvTxt = (outputDir + "/" + outNameInvBase + ".txt"); + const auto outFileInvRoot = (outputDir + "/" + outNameInvBase + ".root"); writeValues(outFileInvTxt, commonModeInvKValues, onlyFilled); getCalPad(outFileInvTxt, outFileInvRoot, "InvCMkValues"); diff --git a/Detectors/TPC/calibration/macro/prepareITFiles.C b/Detectors/TPC/calibration/macro/prepareITFiles.C index ff58d57583342..eac0355e0ddfd 100644 --- a/Detectors/TPC/calibration/macro/prepareITFiles.C +++ b/Detectors/TPC/calibration/macro/prepareITFiles.C @@ -31,7 +31,7 @@ using namespace o2::tpc::cru_calib_helpers; using namespace o2::tpc; -void prepareCMFiles(const std::string_view itDataFile, std::string outputDir = "./") +void prepareITFiles(const std::string_view itDataFile, std::string outputDir = "./", float orocFractionScale = 1.f) { const auto& mapper = Mapper::instance(); @@ -102,6 +102,10 @@ void prepareCMFiles(const std::string_view itDataFile, std::string outputDir = " float fractionVal = rocFraction.getValue(ipad); float expLambdaVal = rocExpLambda.getValue(ipad); + if (roc.isOROC()) { + fractionVal *= orocFractionScale; + } + if ((fractionVal <= 0) || (fractionVal > 0.6)) { LOGP(error, "Too fraction value in ROC {:2}, CRU {:3}, fec in CRU: {:2}, SAMPA: {}, channel: {:2}: {:.4f}, setting value to roc mean {}", iroc, cruID, fecInPartition, sampa, sampaChannel, fractionVal, meanFraction); fractionVal = meanFraction; diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TPCTimeSeriesSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCTimeSeriesSpec.h index 96de43308d117..d7da0b9acb343 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/TPCTimeSeriesSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TPCTimeSeriesSpec.h @@ -14,6 +14,7 @@ #include "Framework/DataProcessorSpec.h" #include "DetectorsBase/Propagator.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" namespace o2 { @@ -22,7 +23,7 @@ namespace tpc static constexpr header::DataDescription getDataDescriptionTimeSeries() { return header::DataDescription{"TIMESERIES"}; } static constexpr header::DataDescription getDataDescriptionTPCTimeSeriesTFId() { return header::DataDescription{"ITPCTSTFID"}; } -o2::framework::DataProcessorSpec getTPCTimeSeriesSpec(const bool disableWriter, const o2::base::Propagator::MatCorrType matType, const bool enableUnbinnedWriter, bool tpcOnly); +o2::framework::DataProcessorSpec getTPCTimeSeriesSpec(const bool disableWriter, const o2::base::Propagator::MatCorrType matType, const bool enableUnbinnedWriter, o2::dataformats::GlobalTrackID::mask_t src); } // end namespace tpc } // end namespace o2 diff --git a/Detectors/TPC/workflow/src/CalibdEdxSpec.cxx b/Detectors/TPC/workflow/src/CalibdEdxSpec.cxx index a32a4a1bb3089..97b69156a2a6d 100644 --- a/Detectors/TPC/workflow/src/CalibdEdxSpec.cxx +++ b/Detectors/TPC/workflow/src/CalibdEdxSpec.cxx @@ -18,17 +18,18 @@ // o2 includes #include "CCDB/CcdbApi.h" #include "CCDB/CcdbObjectInfo.h" -#include "CommonUtils/NameConf.h" +// #include "CommonUtils/NameConf.h" #include "DataFormatsTPC/TrackTPC.h" -#include "DataFormatsParameters/GRPObject.h" +// #include "DataFormatsParameters/GRPObject.h" #include "DetectorsCalibration/Utils.h" #include "Framework/Task.h" #include "Framework/DataProcessorSpec.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" +#include "GPUO2InterfaceConfigurableParam.h" #include "TPCCalibration/CalibdEdx.h" #include "TPCWorkflow/ProcessingHelpers.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBase/CDBTypes.h" #include "TPCBase/Utils.h" #include "DetectorsBase/GRPGeomHelper.h" @@ -68,6 +69,29 @@ class CalibdEdxDevice : public Task mCalib->set2DFitThreshold(minEntries2D); mCalib->setElectronCut(fitThreshold, fitPasses, fitThresholdLowFactor); mCalib->setMaterialType(mMatType); + + mCustomdEdxFileName = o2::gpu::GPUConfigurableParamGPUSettingsO2::Instance().dEdxCorrFile; + mDisableTimeGain = o2::gpu::GPUConfigurableParamGPUSettingsO2::Instance().dEdxDisableResidualGain; + + if (mDisableTimeGain) { + LOGP(info, "TimeGain correction was disabled via GPU_global.dEdxDisableResidualGain=1"); + } + + if (!mDisableTimeGain && !mCustomdEdxFileName.empty()) { + std::unique_ptr fdEdxCustom(TFile::Open(mCustomdEdxFileName.data())); + if (!fdEdxCustom || !fdEdxCustom->IsOpen() || fdEdxCustom->IsZombie()) { + LOGP(error, "Could not open custom TimeGain file {}", mCustomdEdxFileName); + } else { + const auto timeGain = fdEdxCustom->Get("CalibdEdxCorrection"); + if (!timeGain) { + LOGP(error, "Could not load 'CalibdEdxCorrection' from file {}", mCustomdEdxFileName); + } else { + const auto meanParamTot = timeGain->getMeanParams(ChargeType::Tot); + LOGP(info, "Loaded custom TimeGain from file {} with {} dimensions and mean qTot Params {}", mCustomdEdxFileName, timeGain->getDims(), utils::elementsToString(meanParamTot)); + mCalib->setCalibrationInput(*timeGain); + } + } + } } void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) final @@ -75,7 +99,7 @@ class CalibdEdxDevice : public Task if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { return; } - if (matcher == ConcreteDataMatcher("TPC", "TIMEGAIN", 0)) { + if ((mDisableTimeGain == 0) && mCustomdEdxFileName.empty() && (matcher == ConcreteDataMatcher("TPC", "TIMEGAIN", 0))) { mCalib->setCalibrationInput(*(o2::tpc::CalibdEdxCorrection*)obj); const auto meanParamTot = mCalib->getCalibrationInput().getMeanParams(ChargeType::Tot); LOGP(info, "Updating TimeGain with {} dimensions and mean qTot Params {}", mCalib->getCalibrationInput().getDims(), utils::elementsToString(meanParamTot)); @@ -143,7 +167,9 @@ class CalibdEdxDevice : public Task uint64_t mRunNumber{0}; ///< processed run number uint64_t mTimeStampStart{0}; ///< time stamp for first TF for CCDB output std::unique_ptr mCalib; - bool mMakeGaussianFits{true}; ///< make gaussian fits or take the mean + bool mMakeGaussianFits{true}; ///< make gaussian fits or take the mean + bool mDisableTimeGain{false}; ///< if time gain is disabled via GPU_global.dEdxDisableResidualGain=1 + std::string mCustomdEdxFileName{}; ///< name of the custom dE/dx file configured via GPU_global.dEdxCorrFile }; DataProcessorSpec getCalibdEdxSpec(const o2::base::Propagator::MatCorrType matType) diff --git a/Detectors/TPC/workflow/src/CalibratordEdxSpec.cxx b/Detectors/TPC/workflow/src/CalibratordEdxSpec.cxx index 6e477084d992c..ce45356aa28c8 100644 --- a/Detectors/TPC/workflow/src/CalibratordEdxSpec.cxx +++ b/Detectors/TPC/workflow/src/CalibratordEdxSpec.cxx @@ -21,18 +21,19 @@ // o2 includes #include "CCDB/CcdbApi.h" #include "CCDB/CcdbObjectInfo.h" -#include "CommonUtils/NameConf.h" +// #include "CommonUtils/NameConf.h" #include "DataFormatsTPC/TrackTPC.h" -#include "DataFormatsParameters/GRPObject.h" +// #include "DataFormatsParameters/GRPObject.h" #include "DetectorsCalibration/Utils.h" #include "Framework/Task.h" #include "Framework/DataProcessorSpec.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" +#include "GPUO2InterfaceConfigurableParam.h" #include "TPCCalibration/CalibratordEdx.h" #include "TPCWorkflow/ProcessingHelpers.h" #include "DetectorsBase/GRPGeomHelper.h" -#include "TPCBase/CDBInterface.h" +#include "TPCBase/CDBTypes.h" #include "TPCBase/Utils.h" using namespace o2::framework; @@ -85,6 +86,29 @@ class CalibratordEdxDevice : public Task mCalibrator->setTrackDebug(trackDebug); mCalibrator->setMakeGaussianFits(makeGaussianFits); + mCustomdEdxFileName = o2::gpu::GPUConfigurableParamGPUSettingsO2::Instance().dEdxCorrFile; + mDisableTimeGain = o2::gpu::GPUConfigurableParamGPUSettingsO2::Instance().dEdxDisableResidualGain; + + if (mDisableTimeGain) { + LOGP(info, "TimeGain correction was disabled via GPU_global.dEdxDisableResidualGain=1"); + } + + if (!mDisableTimeGain && !mCustomdEdxFileName.empty()) { + std::unique_ptr fdEdxCustom(TFile::Open(mCustomdEdxFileName.data())); + if (!fdEdxCustom || !fdEdxCustom->IsOpen() || fdEdxCustom->IsZombie()) { + LOGP(error, "Could not open custom TimeGain file {}", mCustomdEdxFileName); + } else { + const auto timeGain = fdEdxCustom->Get("CalibdEdxCorrection"); + if (!timeGain) { + LOGP(error, "Could not load 'CalibdEdxCorrection' from file {}", mCustomdEdxFileName); + } else { + mTimeGain = *timeGain; + const auto meanParamTot = mTimeGain.getMeanParams(ChargeType::Tot); + LOGP(info, "Loaded custom TimeGain from file {} with {} dimensions and mean qTot Params {}", mCustomdEdxFileName, mTimeGain.getDims(), utils::elementsToString(meanParamTot)); + } + } + } + if (dumpData) { const auto dumpDataName = ic.options().get("file-dump-name"); mCalibrator->enableDebugOutput(dumpDataName); @@ -96,7 +120,7 @@ class CalibratordEdxDevice : public Task if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { return; } - if (matcher == ConcreteDataMatcher("TPC", "TIMEGAIN", 0)) { + if ((mDisableTimeGain == 0) && mCustomdEdxFileName.empty() && (matcher == ConcreteDataMatcher("TPC", "TIMEGAIN", 0))) { mTimeGain = *(o2::tpc::CalibdEdxCorrection*)obj; const auto meanParamTot = mTimeGain.getMeanParams(ChargeType::Tot); LOGP(info, "Updating TimeGain with {} dimensions and mean qTot Params {}", mTimeGain.getDims(), utils::elementsToString(meanParamTot)); @@ -181,6 +205,8 @@ class CalibratordEdxDevice : public Task uint32_t mRunNumber{0}; ///< processed run number long mCalibIntervalExtensionMS{0}; ///< Extension of the calibration interval end in ms o2::tpc::CalibdEdxCorrection mTimeGain{}; ///< currently valid TimeGain + bool mDisableTimeGain{false}; ///< if time gain is disabled via GPU_global.dEdxDisableResidualGain=1 + std::string mCustomdEdxFileName{}; ///< name of the custom dE/dx file configured via GPU_global.dEdxCorrFile }; DataProcessorSpec getCalibratordEdxSpec(const o2::base::Propagator::MatCorrType matType) diff --git a/Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx b/Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx index 280fd965c4185..c34cc0b7e9863 100644 --- a/Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx +++ b/Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx @@ -61,7 +61,7 @@ class TPCTimeSeries : public Task { public: /// \constructor - TPCTimeSeries(std::shared_ptr req, const bool disableWriter, const o2::base::Propagator::MatCorrType matType, const bool enableUnbinnedWriter, const bool tpcOnly, std::shared_ptr dr) : mCCDBRequest(req), mDisableWriter(disableWriter), mMatType(matType), mUnbinnedWriter(enableUnbinnedWriter), mTPCOnly(tpcOnly), mDataRequest(dr){}; + TPCTimeSeries(std::shared_ptr req, const bool disableWriter, const o2::base::Propagator::MatCorrType matType, const bool enableUnbinnedWriter, const bool tpcOnly, std::shared_ptr dr) : mCCDBRequest(req), mDisableWriter(disableWriter), mMatType(matType), mUnbinnedWriter(enableUnbinnedWriter), mTPCOnly(tpcOnly), mDataRequest(dr) {}; void init(framework::InitContext& ic) final { @@ -206,11 +206,14 @@ class TPCTimeSeries : public Task indicesITSTPC[tracksITSTPC[i].getRefTPC().getIndex()] = {i, idxVtx}; } - std::vector> idxTPCTrackToTOFCluster; // store for each tpc track index the index to the TOF cluster + std::vector> idxTPCTrackToTOFCluster; // store for each tpc track index the index to the TOF cluster // get matches to TOF in case skimmed data is produced if (mUnbinnedWriter) { - idxTPCTrackToTOFCluster = std::vector>(tracksTPC.size(), {-1, -999, -999}); + // getLTIntegralOut(), ///< L,TOF integral calculated during the propagation + // getSignal() mSignal = 0.0; ///< TOF time in ps + o2::track::TrackLTIntegral defLT; + idxTPCTrackToTOFCluster = std::vector>(tracksTPC.size(), {-1, -999, -999, defLT, 0, 0}); const std::vector> tofMatches{recoData.getTPCTOFMatches(), recoData.getTPCTRDTOFMatches(), recoData.getITSTPCTOFMatches(), recoData.getITSTPCTRDTOFMatches()}; // loop over ITS-TPC-TRD-TOF and ITS-TPC-TOF tracks an store for each ITS-TPC track the TOF track index @@ -218,7 +221,10 @@ class TPCTimeSeries : public Task for (const auto& tpctofmatch : tofMatch) { auto refTPC = recoData.getTPCContributorGID(tpctofmatch.getTrackRef()); if (refTPC.isIndexSet()) { - idxTPCTrackToTOFCluster[refTPC] = {tpctofmatch.getIdxTOFCl(), tpctofmatch.getDXatTOF(), tpctofmatch.getDZatTOF()}; + o2::track::TrackLTIntegral ltIntegral = tpctofmatch.getLTIntegralOut(); + double signal = tpctofmatch.getSignal(); + float deltaT = tpctofmatch.getDeltaT(); + idxTPCTrackToTOFCluster[refTPC] = {tpctofmatch.getIdxTOFCl(), tpctofmatch.getDXatTOF(), tpctofmatch.getDZatTOF(), ltIntegral, signal, deltaT}; } } } @@ -1049,7 +1055,7 @@ class TPCTimeSeries : public Task return isGoodTrack; } - void fillDCA(const gsl::span tracksTPC, const gsl::span tracksITSTPC, const gsl::span vertices, const int iTrk, const int iThread, const std::unordered_map>& indicesITSTPC, const gsl::span tracksITS, const std::vector>& idxTPCTrackToTOFCluster, const gsl::span tofClusters) + void fillDCA(const gsl::span tracksTPC, const gsl::span tracksITSTPC, const gsl::span vertices, const int iTrk, const int iThread, const std::unordered_map>& indicesITSTPC, const gsl::span tracksITS, const std::vector>& idxTPCTrackToTOFCluster, const gsl::span tofClusters) { const auto& trackFull = tracksTPC[iTrk]; const bool isGoodTrack = checkTrack(trackFull); @@ -1354,7 +1360,8 @@ class TPCTimeSeries : public Task } } } - + double vertexTime = vertex.getTimeStamp().getTimeStamp(); + double trackTime0 = trackFull.getTime0(); *mStreamer[iThread] << "treeTimeSeries" // DCAs << "triggerMask=" << triggerMask @@ -1432,6 +1439,11 @@ class TPCTimeSeries : public Task << "tpcZDeltaAtTOF=" << tpcZDeltaAtTOF << "mDXatTOF=" << std::get<1>(idxTPCTrackToTOFCluster[iTrk]) << "mDZatTOF=" << std::get<2>(idxTPCTrackToTOFCluster[iTrk]) + << "mTOFLength=" << std::get<3>(idxTPCTrackToTOFCluster[iTrk]) + << "mTOFSignal=" << std::get<4>(idxTPCTrackToTOFCluster[iTrk]) + << "mDeltaTTOFTPC=" << std::get<5>(idxTPCTrackToTOFCluster[iTrk]) /// delta T- TPC TOF + << "vertexTime=" << vertexTime /// time stamp assigned to the vertex + << "trackTime0=" << trackTime0 /// time stamp assigned to the track // TPC delta param << "deltaTPCParamInOutTgl=" << deltaTPCParamInOutTgl << "deltaTPCParamInOutQPt=" << deltaTPCParamInOutQPt @@ -1739,14 +1751,15 @@ class TPCTimeSeries : public Task } }; -o2::framework::DataProcessorSpec getTPCTimeSeriesSpec(const bool disableWriter, const o2::base::Propagator::MatCorrType matType, const bool enableUnbinnedWriter, bool tpcOnly) +o2::framework::DataProcessorSpec getTPCTimeSeriesSpec(const bool disableWriter, const o2::base::Propagator::MatCorrType matType, const bool enableUnbinnedWriter, GTrackID::mask_t src) { - using GID = o2::dataformats::GlobalTrackID; auto dataRequest = std::make_shared(); bool useMC = false; - GID::mask_t srcTracks = tpcOnly ? GID::getSourcesMask("TPC") : GID::getSourcesMask("TPC,ITS,ITS-TPC,ITS-TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD-TOF"); + GTrackID::mask_t srcTracks = GTrackID::getSourcesMask("TPC,ITS,ITS-TPC,ITS-TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD-TOF") & src; + srcTracks.set(GTrackID::TPC); // TPC must be always there dataRequest->requestTracks(srcTracks, useMC); - dataRequest->requestClusters(GID::getSourcesMask("TPC"), useMC); + dataRequest->requestClusters(GTrackID::getSourcesMask("TPC"), useMC); + bool tpcOnly = srcTracks == GTrackID::getSourcesMask("TPC"); if (!tpcOnly) { dataRequest->requestPrimaryVertices(useMC); } diff --git a/Detectors/TPC/workflow/src/tpc-integrate-idc.cxx b/Detectors/TPC/workflow/src/tpc-integrate-idc.cxx index cd7e777a293e0..4aae6683f29b0 100644 --- a/Detectors/TPC/workflow/src/tpc-integrate-idc.cxx +++ b/Detectors/TPC/workflow/src/tpc-integrate-idc.cxx @@ -11,6 +11,7 @@ #include #include +#include #include "Algorithm/RangeTokenizer.h" #include "Framework/WorkflowSpec.h" #include "Framework/ConfigParamSpec.h" diff --git a/Detectors/TPC/workflow/src/tpc-krypton-clusterer.cxx b/Detectors/TPC/workflow/src/tpc-krypton-clusterer.cxx index e815a4fc85e3a..d7c1839f94c6b 100644 --- a/Detectors/TPC/workflow/src/tpc-krypton-clusterer.cxx +++ b/Detectors/TPC/workflow/src/tpc-krypton-clusterer.cxx @@ -12,6 +12,7 @@ #include #include #include +#include #include "Algorithm/RangeTokenizer.h" #include "Framework/WorkflowSpec.h" diff --git a/Detectors/TPC/workflow/src/tpc-time-series.cxx b/Detectors/TPC/workflow/src/tpc-time-series.cxx index 6fdab2eeb713d..f7bcf00cb27ea 100644 --- a/Detectors/TPC/workflow/src/tpc-time-series.cxx +++ b/Detectors/TPC/workflow/src/tpc-time-series.cxx @@ -28,9 +28,8 @@ void customize(std::vector& workflowOptions) ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, {"disable-root-output", VariantType::Bool, false, {"disable root-files output writers"}}, {"enable-unbinned-root-output", VariantType::Bool, false, {"writing out unbinned track data"}}, - {"tpc-only", VariantType::Bool, false, {"use only tpc tracks as input"}}, + {"track-sources", VariantType::String, std::string{o2::dataformats::GlobalTrackID::ALL}, {"comma-separated list of sources to use"}}, {"material-type", VariantType::Int, 2, {"Type for the material budget during track propagation: 0=None, 1=Geo, 2=LUT"}}}; - std::swap(workflowOptions, options); } @@ -42,9 +41,9 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) o2::conf::ConfigurableParam::updateFromString(config.options().get("configKeyValues")); const bool disableWriter = config.options().get("disable-root-output"); const bool enableUnbinnedWriter = config.options().get("enable-unbinned-root-output"); - const bool tpcOnly = config.options().get("tpc-only"); + auto src = o2::dataformats::GlobalTrackID::getSourcesMask(config.options().get("track-sources")); auto materialType = static_cast(config.options().get("material-type")); - workflow.emplace_back(o2::tpc::getTPCTimeSeriesSpec(disableWriter, materialType, enableUnbinnedWriter, tpcOnly)); + workflow.emplace_back(o2::tpc::getTPCTimeSeriesSpec(disableWriter, materialType, enableUnbinnedWriter, src)); if (!disableWriter) { workflow.emplace_back(o2::tpc::getTPCTimeSeriesWriterSpec()); } diff --git a/Detectors/Upgrades/ITS3/CMakeLists.txt b/Detectors/Upgrades/ITS3/CMakeLists.txt index 6965061571da6..73ad4b9d53e37 100644 --- a/Detectors/Upgrades/ITS3/CMakeLists.txt +++ b/Detectors/Upgrades/ITS3/CMakeLists.txt @@ -9,11 +9,13 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. -#add_compile_options(-O0 -g -fPIC) +#add_compile_options(-O0 -g -fPIC -fsanitize=address) +#add_link_options(-fsanitize=address) -add_subdirectory(macros) +add_subdirectory(data) add_subdirectory(simulation) add_subdirectory(alignment) add_subdirectory(base) add_subdirectory(workflow) add_subdirectory(reconstruction) +add_subdirectory(macros) diff --git a/Detectors/Upgrades/ITS3/README.md b/Detectors/Upgrades/ITS3/README.md index 6d3b0d8d821fb..afcea6a5c3e17 100644 --- a/Detectors/Upgrades/ITS3/README.md +++ b/Detectors/Upgrades/ITS3/README.md @@ -35,7 +35,7 @@ export ALICEO2_CCDB_LOCALCACHE=${PWD}/ccdb Simulate diamond -``` bash +```bash # append to o2-sim --configKeyValues="Diamond.width[2]=6.;"" ``` @@ -86,13 +86,27 @@ TODO ```bash # Create Full Geometry -o2-sim -g pythia8pp -j10 --detectorList ALICE2.1 --run 303901 -n0 +o2-sim --detectorList ALICE2.1 --run 303901 -n0 cp o2sim_geometry.root ${ALICEO2_CCDB_LOCALCACHE}/GLO/Config/Geometry/snapshot.root o2-create-aligned-geometry-workflow -b --configKeyValues "HBFUtils.startTime=1547978230000" --condition-remap="file://${ALICEO2_CCDB_LOCALCACHE}=GLO/Config/Geometry" cp o2sim_geometry-aligned.root ${ALICEO2_CCDB_LOCALCACHE}/GLO/Config/GeometryAligned/snapshot.root cp its_GeometryTGeo.root ${ALICEO2_CCDB_LOCALCACHE}/ITS/Config/Geometry/snapshot.root ``` +or copying the ideal geometry to the aligned one and: + +```cpp +{ + o2::base::GeometryManager::loadGeometry(""); + auto itsTGeo = o2::its::GeometryTGeo::Instance(); + itsTGeo->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G, o2::math_utils::TransformType::T2GRot)); + TFile outF("its_GeometryTGeo.root", "recreate"); + outF.WriteObjectAny(itsTGeo, "o2::its::GeometryTGeo", "ccdb_object"); + outF.Close(); + itsTGeo->destroy(); +} +``` + ### Regenerating the TopologyDictionary 1. Clusterization w/o tracking @@ -158,7 +172,7 @@ The file `hijing.C` can be found [here](https://alice.its.cern.ch/jira/browse/AO 2. (optional) Run the macro `CreateITS3StaticDeadMap.C` and/or visualize with `CheckTileNumbering.C` 3. Move the ccdb object into `${ALICEO2_CCDB_LOCALCACHE}/IT3/Calib/DeadMap`, this is not optional since there is no default object uploaded 4. Run digitizer with `ITS3Params.useDeadChannelMap=true;`, e.g.: -``` bash +```bash o2-sim-digitizer-workflow --configKeyValues="ITS3Params.useDeadChannelMap=true;" ``` @@ -168,6 +182,89 @@ o2-sim-digitizer-workflow --configKeyValues="ITS3Params.useDeadChannelMap=true;" 1. Create misalignment parameters with `CreateMisalignmentITS3.C` 2. Visualize with `ShowCoefficients.C` 3. Run digitizer -``` bash +```bash o2-sim-digitizer-workflow -b --configKeyValues="ITS3Params.applyMisalignmentHits=true;ITS3Params.misalignmentHitsParams=misparams.root" ``` + + +### Misc +#### Setup to run SIM+DIGIT+TRACKING +```bash + +#!/bin/bash + +export IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE=1 +export ALICEO2_CCDB_LOCALCACHE=$PWD/ccdb + +BASE_DIR="batch_" +TOTAL_DIRS=4 +SIM_CMD="o2-sim -g pythia8pp --detectorList ALICE2.1 -m IT3 --run 303901 -n2000 --field ccdb -j8" +DIGIT_CMD="o2-sim-digitizer-workflow -b --interactionRate 675000 --run --configKeyValues=\"HBFUtils.runNumber=303901;HBFUtils.nHBFPerTF=32;ITSAlpideParam.roFrameLengthInBC=198\"" +RECO_CMD="o2-its3-reco-workflow -b --run --configKeyValues=\"ITSVertexerParam.phiCut=0.5;ITSVertexerParam.clusterContributorsCut=3;ITSVertexerParam.tanLambdaCut=0.2;ITSCATrackerParam.useTrackFollower=0;ITSCATrackerParam.findShortTracks=1;HBFUtils.runNumber=303901;HBFUtils.nHBFPerTF=32;ITSAlpideParam.roFrameLengthInBC=198\" --tracking-mode async" + +for ((i = 1; i <= TOTAL_DIRS; i++)); do + DIR="${BASE_DIR}${i}" + + if [ ! -d "$DIR" ]; then + mkdir "$DIR" + fi + + if [ -f "${DIR}/sim_done" ]; then + echo "Skipping SIM ${DIR} because _done exists." + continue + fi + + cd "$DIR" + + echo "Executing SIM command in ${DIR}..." + eval $SIM_CMD >sim.log + + touch sim_done + + cd .. +done + +for ((i = 1; i <= TOTAL_DIRS; i++)); do + DIR="${BASE_DIR}${i}" + + if [ ! -d "$DIR" ]; then + mkdir "$DIR" + fi + + if [ -f "${DIR}/digit_done" ]; then + echo "Skipping DIGIT ${DIR} because _done exists." + continue + fi + + cd "$DIR" + + echo "Executing DIGIT command in ${DIR}..." + eval $DIGIT_CMD >digit.log + + touch digit_done + + cd .. +done + +for ((i = 1; i <= TOTAL_DIRS; i++)); do + DIR="${BASE_DIR}${i}" + + if [ ! -d "$DIR" ]; then + mkdir "$DIR" + fi + + if [ -f "${DIR}/reco_done" ]; then + echo "Skipping RECO ${DIR} because _done exists." + continue + fi + + cd "$DIR" + + echo "Executing RECO command in ${DIR}..." + eval $RECO_CMD >reco.log + + touch reco_done + + cd .. +done +``` diff --git a/Detectors/Upgrades/ITS3/alignment/src/MisalignmentHits.cxx b/Detectors/Upgrades/ITS3/alignment/src/MisalignmentHits.cxx index fbc0b5d623dca..66ab4c8090b54 100644 --- a/Detectors/Upgrades/ITS3/alignment/src/MisalignmentHits.cxx +++ b/Detectors/Upgrades/ITS3/alignment/src/MisalignmentHits.cxx @@ -10,7 +10,6 @@ // or submit itself to any jurisdiction. #include "ITS3Align/MisalignmentHits.h" -#include "ITS3Base/SegmentationSuperAlpide.h" #include "ITS3Base/ITS3Params.h" #include "SimConfig/DigiParams.h" #include "DetectorsBase/Propagator.h" diff --git a/Detectors/Upgrades/ITS3/base/CMakeLists.txt b/Detectors/Upgrades/ITS3/base/CMakeLists.txt index 8695e2323bbab..306226e5088cf 100644 --- a/Detectors/Upgrades/ITS3/base/CMakeLists.txt +++ b/Detectors/Upgrades/ITS3/base/CMakeLists.txt @@ -9,11 +9,9 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. -o2_add_library(ITS3Base - SOURCES src/SegmentationSuperAlpide.cxx - src/ITS3Params.cxx - PUBLIC_LINK_LIBRARIES O2::CommonConstants O2::MathUtils O2::DetectorsBase) +o2_add_library( + ITS3Base + SOURCES src/ITS3Params.cxx + PUBLIC_LINK_LIBRARIES O2::CommonConstants O2::MathUtils O2::DetectorsBase) -o2_target_root_dictionary(ITS3Base - HEADERS include/ITS3Base/SegmentationSuperAlpide.h - include/ITS3Base/ITS3Params.h) +o2_target_root_dictionary(ITS3Base HEADERS include/ITS3Base/ITS3Params.h) diff --git a/Detectors/Upgrades/ITS3/base/include/ITS3Base/ITS3Params.h b/Detectors/Upgrades/ITS3/base/include/ITS3Base/ITS3Params.h index c685bf0f085d6..0bd548cef953d 100644 --- a/Detectors/Upgrades/ITS3/base/include/ITS3Base/ITS3Params.h +++ b/Detectors/Upgrades/ITS3/base/include/ITS3Base/ITS3Params.h @@ -25,7 +25,8 @@ struct ITS3Params : public o2::conf::ConfigurableParamHelper { 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 + 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) O2ParamDef(ITS3Params, "ITS3Params"); }; diff --git a/Detectors/Upgrades/ITS3/base/include/ITS3Base/SegmentationMosaix.h b/Detectors/Upgrades/ITS3/base/include/ITS3Base/SegmentationMosaix.h new file mode 100644 index 0000000000000..43cfd75a90c26 --- /dev/null +++ b/Detectors/Upgrades/ITS3/base/include/ITS3Base/SegmentationMosaix.h @@ -0,0 +1,231 @@ +// 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 SegmentationMosaix.h +/// \brief Definition of the SegmentationMosaix class +/// \author felix.schlepper@cern.ch + +#ifndef ALICEO2_ITS3_SEGMENTATIONMOSAIX_H_ +#define ALICEO2_ITS3_SEGMENTATIONMOSAIX_H_ + +#include + +#include "MathUtils/Cartesian.h" +#include "ITS3Base/SpecsV2.h" + +namespace o2::its3 +{ + +/// Segmentation and response for pixels in ITS3 upgrade +class SegmentationMosaix +{ + // This class defines the segmenation of the pixelArray in the tile. We define + // two coordinate systems, one width x,z detector local coordianates (cm) and + // the more natural row,col layout: Also all the transformation between these + // two. The class provides the transformation from the tile to TGeo + // coordinates. + // In fact there exist three coordinate systems and one is transient. + // 1. The curved coordinate system. The chip's local coordinate system is + // defined with its center at the the mid-point of the tube. + // 2. The flat coordinate system. This is the tube segment projected onto a flat + // surface. In the projection we implicitly assume that the inner and outer + // stretch does not depend on the radius. + // Additionally, there is a difference between the flat geometrical center + // and the phyiscal center defined by the metal layer. + // 3. The detector coordinate system. Defined by the row and column segmentation + // defined at the upper edge in the flat coord. + + // O----------------------| + // | | | + // | | | ^ x + // | | | | + // | | | | + // | | | | + // | | | X----> z X marks (x,z)=(0,0) + // |-----------X----------| + // | | | O----> col O marks (row,col)=(0,0) + // | | | | + // | | | | + // | | | v + // | | | row + // | | | + // |----------------------| + + public: + constexpr SegmentationMosaix(int layer) : mRadius(static_cast(constants::radiiMiddle[layer])) {} + constexpr ~SegmentationMosaix() = default; + constexpr SegmentationMosaix(const SegmentationMosaix&) = default; + constexpr SegmentationMosaix(SegmentationMosaix&&) = delete; + constexpr SegmentationMosaix& operator=(const SegmentationMosaix&) = default; + constexpr SegmentationMosaix& operator=(SegmentationMosaix&&) = delete; + + static constexpr int NCols{constants::pixelarray::nCols}; + static constexpr int NRows{constants::pixelarray::nRows}; + static constexpr int NPixels{NCols * NRows}; + static constexpr float Length{constants::pixelarray::length}; + static constexpr float LengthH{Length / 2.f}; + static constexpr float Width{constants::pixelarray::width}; + static constexpr float WidthH{Width / 2.f}; + static constexpr float PitchCol{constants::pixelarray::pixels::mosaix::pitchZ}; + static constexpr float PitchRow{constants::pixelarray::pixels::mosaix::pitchX}; + static constexpr float SensorLayerThickness{constants::totalThickness}; + static constexpr float NominalYShift{0.0f}; + + /// Transformation from the curved surface to a flat surface. + /// Additionally a shift in the flat coordinates must be applied because + /// the center of the TGeoShap when projected will be higher than the + /// physical thickness of the chip (we add an additional hull to account for + /// the copper metal interconnection which is in reality part of the chip but in our + /// simulation the silicon and metal layer are separated). Thus we shift the projected center + /// down by this difference to align the coordinate systems. + /// \param xCurved Detector local curved coordinate x in cm with respect to + /// the center of the sensitive volume. + /// \param yCurved Detector local curved coordinate y in cm with respect to + /// the center of the sensitive volume. + /// \param xFlat Detector local flat coordinate x in cm with respect to + /// the center of the sensitive volume. + /// \param yFlat Detector local flat coordinate y in cm with respect to + /// the center of the sensitive volume. + constexpr void curvedToFlat(const float xCurved, const float yCurved, float& xFlat, float& yFlat) const noexcept + { + // MUST align the flat surface with the curved surface with the original pixel array is on and account for metal + // stack + float dist = std::hypot(xCurved, yCurved); + float phi = std::atan2(yCurved, xCurved); + // the y position is in the silicon volume however we need the chip volume (silicon+metalstack) + // this is accounted by a y shift + xFlat = WidthH - mRadius * phi; + yFlat = dist - mRadius + NominalYShift; + } + + /// Transformation from the flat surface to a curved surface + /// It works only if the detector is not rototraslated. + /// \param xFlat Detector local flat coordinate x in cm with respect to + /// the center of the sensitive volume. + /// \param yFlat Detector local flat coordinate y in cm with respect to + /// the center of the sensitive volume. + /// \param xCurved Detector local curved coordinate x in cm with respect to + /// the center of the sensitive volume. + /// \param yCurved Detector local curved coordinate y in cm with respect to + /// the center of the sensitive volume. + constexpr void flatToCurved(float xFlat, float yFlat, float& xCurved, float& yCurved) const noexcept + { + // MUST align the flat surface with the curved surface with the original pixel array is on and account for metal + // stack + float dist = yFlat - NominalYShift + mRadius; + float phi = (WidthH - xFlat) / mRadius; + // the y position is in the chip volume however we need the silicon volume + // this is accounted by a -y shift + xCurved = dist * std::cos(phi); + yCurved = dist * std::sin(phi); + } + + /// Transformation from Geant detector centered local coordinates (cm) to + /// Pixel cell numbers iRow and iCol. + /// Returns true if point x,z is inside sensitive volume, false otherwise. + /// A value of -1 for iRow or iCol indicates that this point is outside of the + /// detector segmentation as defined. + /// \param float x Detector local coordinate x in cm with respect to + /// the center of the sensitive volume. + /// \param float z Detector local coordinate z in cm with respect to + /// the center of the sensitive volume. + /// \param int iRow Detector x cell coordinate. + /// \param int iCol Detector z cell coordinate. + constexpr bool localToDetector(float const xRow, float const zCol, int& iRow, int& iCol) const noexcept + { + if (!isValidLoc(xRow, zCol)) { + return false; + } + localToDetectorUnchecked(xRow, zCol, iRow, iCol); + if (!isValidDet(iRow, iCol)) { + iRow = iCol = -1; + return false; + } + return true; + } + + // Same as localToDetector w.o. checks. + constexpr void localToDetectorUnchecked(float const xRow, float const zCol, int& iRow, int& iCol) const noexcept + { + iRow = static_cast(std::floor((WidthH - xRow) / PitchRow)); + iCol = static_cast(std::floor((zCol + LengthH) / PitchCol)); + } + + /// Transformation from Detector cell coordinates to Geant detector centered + /// local coordinates (cm) + /// \param int iRow Detector x cell coordinate. + /// \param int iCol Detector z cell coordinate. + /// \param float x Detector local coordinate x in cm with respect to the + /// center of the sensitive volume. + /// \param float z Detector local coordinate z in cm with respect to the + /// 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. + template + constexpr bool detectorToLocal(T const row, T const col, float& xRow, float& zCol) const noexcept + { + if (!isValidDet(row, col)) { + return false; + } + detectorToLocalUnchecked(row, col, xRow, zCol); + return isValidLoc(xRow, zCol); + } + + // Same as detectorToLocal w.o. checks. + // We position ourself in the middle of the pixel. + template + constexpr void detectorToLocalUnchecked(T const row, T const col, float& xRow, float& zCol) const noexcept + { + xRow = -(static_cast(row) + 0.5f) * PitchRow + WidthH; + zCol = (static_cast(col) + 0.5f) * PitchCol - LengthH; + } + + template + constexpr bool detectorToLocal(T const row, T const col, math_utils::Point3D& loc) const noexcept + { + float xRow{0.}, zCol{0.}; + if (!detectorToLocal(row, col, xRow, zCol)) { + return false; + } + loc.SetCoordinates(xRow, NominalYShift, zCol); + return true; + } + + template + constexpr void detectorToLocalUnchecked(T const row, T const col, math_utils::Point3D& loc) const noexcept + { + float xRow{0.}, zCol{0.}; + detectorToLocalUnchecked(row, col, xRow, zCol); + loc.SetCoordinates(xRow, NominalYShift, zCol); + } + + private: + // Check local coordinates (cm) validity. + template + constexpr bool isValidLoc(T const x, T const z) const noexcept + { + return (-WidthH < x && x < WidthH && -LengthH < z && z < LengthH); + } + + // Check detector coordinates validity. + template + constexpr bool isValidDet(T const row, T const col) const noexcept + { + return (row >= 0 && row < static_cast(NRows) && + col >= 0 && col < static_cast(NCols)); + } + + float mRadius; +}; + +} // namespace o2::its3 + +#endif diff --git a/Detectors/Upgrades/ITS3/base/include/ITS3Base/SegmentationSuperAlpide.h b/Detectors/Upgrades/ITS3/base/include/ITS3Base/SegmentationSuperAlpide.h deleted file mode 100644 index dbdf90574ce5d..0000000000000 --- a/Detectors/Upgrades/ITS3/base/include/ITS3Base/SegmentationSuperAlpide.h +++ /dev/null @@ -1,207 +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 SegmentationSuperAlpide.h -/// \brief Definition of the SegmentationSuperAlpide class -/// \author felix.schlepper@cern.ch - -#ifndef ALICEO2_ITS3_SEGMENTATIONSUPERALPIDE_H_ -#define ALICEO2_ITS3_SEGMENTATIONSUPERALPIDE_H_ - -#include "MathUtils/Cartesian.h" -#include "ITS3Base/SpecsV2.h" -#include "Rtypes.h" - -#include - -namespace o2::its3 -{ - -/// Segmentation and response for pixels in ITS3 upgrade -class SegmentationSuperAlpide -{ - // This class defines the segmenation of the pixelArray in the tile. We define - // two coordinate systems, one width x,z detector local coordianates (cm) and - // the more natural row,col layout: Also all the transformation between these - // two. The class provides the transformation from the tile to TGeo - // coordinates. - - // row,col=0 - // | - // v - // x----------------------x - // | | | - // | | | - // | | | ^ x - // | | | | - // | | | | - // | | | | - // |-----------X----------| X marks (x,z)=(0,0) X----> z - // | | | - // | | | - // | | | - // | | | - // | | | - // | | | - // x----------------------x - public: - virtual ~SegmentationSuperAlpide() = default; - SegmentationSuperAlpide(const SegmentationSuperAlpide&) = default; - SegmentationSuperAlpide(SegmentationSuperAlpide&&) = delete; - SegmentationSuperAlpide& operator=(const SegmentationSuperAlpide&) = delete; - SegmentationSuperAlpide& operator=(SegmentationSuperAlpide&&) = delete; - constexpr SegmentationSuperAlpide(int layer) : mLayer{layer} {} - - static constexpr int mNCols{constants::pixelarray::nCols}; - static constexpr int mNRows{constants::pixelarray::nRows}; - static constexpr int nPixels{mNCols * mNRows}; - static constexpr float mLength{constants::pixelarray::length}; - static constexpr float mWidth{constants::pixelarray::width}; - static constexpr float mPitchCol{constants::pixelarray::length / static_cast(mNCols)}; - static constexpr float mPitchRow{constants::pixelarray::width / static_cast(mNRows)}; - static constexpr float mSensorLayerThickness{constants::thickness}; - static constexpr float mSensorLayerThicknessEff{constants::effThickness}; - static constexpr std::array mRadii{constants::radii}; - - /// Transformation from the curved surface to a flat surface - /// \param xCurved Detector local curved coordinate x in cm with respect to - /// the center of the sensitive volume. - /// \param yCurved Detector local curved coordinate y in cm with respect to - /// the center of the sensitive volume. - /// \param xFlat Detector local flat coordinate x in cm with respect to - /// the center of the sensitive volume. - /// \param yFlat Detector local flat coordinate y in cm with respect to - /// the center of the sensitive volume. - void curvedToFlat(const float xCurved, const float yCurved, float& xFlat, float& yFlat) const noexcept - { - // MUST align the flat surface with the curved surface with the original pixel array is on - float dist = std::hypot(xCurved, yCurved); - float phiReadout = constants::tile::readout::width / constants::radii[mLayer]; - float phi = std::atan2(yCurved, xCurved); - xFlat = mRadii[mLayer] * (phi - phiReadout) - constants::pixelarray::width / 2.; - yFlat = dist - mRadii[mLayer]; - } - - /// Transformation from the flat surface to a curved surface - /// It works only if the detector is not rototraslated - /// \param xFlat Detector local flat coordinate x in cm with respect to - /// the center of the sensitive volume. - /// \param yFlat Detector local flat coordinate y in cm with respect to - /// the center of the sensitive volume. - /// \param xCurved Detector local curved coordinate x in cm with respect to - /// the center of the sensitive volume. - /// \param yCurved Detector local curved coordinate y in cm with respect to - /// the center of the sensitive volume. - void flatToCurved(float xFlat, float yFlat, float& xCurved, float& yCurved) const noexcept - { - // MUST align the flat surface with the curved surface with the original pixel array is on - float dist = yFlat + mRadii[mLayer]; - float phiReadout = constants::tile::readout::width / mRadii[mLayer]; - xCurved = dist * std::cos(phiReadout + (xFlat + constants::pixelarray::width / 2.) / mRadii[mLayer]); - yCurved = dist * std::sin(phiReadout + (xFlat + constants::pixelarray::width / 2.) / mRadii[mLayer]); - } - - /// Transformation from Geant detector centered local coordinates (cm) to - /// Pixel cell numbers iRow and iCol. - /// Returns true if point x,z is inside sensitive volume, false otherwise. - /// A value of -1 for iRow or iCol indicates that this point is outside of the - /// detector segmentation as defined. - /// \param float x Detector local coordinate x in cm with respect to - /// the center of the sensitive volume. - /// \param float z Detector local coordinate z in cm with respect to - /// the center of the sensitive volume. - /// \param int iRow Detector x cell coordinate. - /// \param int iCol Detector z cell coordinate. - bool localToDetector(float const xRow, float const zCol, int& iRow, int& iCol) const noexcept - { - localToDetectorUnchecked(xRow, zCol, iRow, iCol); - if (!isValid(iRow, iCol)) { - iRow = iCol = -1; - return false; - } - return true; - } - - // Same as localToDetector w.o. checks. - void localToDetectorUnchecked(float const xRow, float const zCol, int& iRow, int& iCol) const noexcept - { - namespace cp = constants::pixelarray; - iRow = std::floor((cp::width / 2. - xRow) / mPitchRow); - iCol = std::floor((zCol + cp::length / 2.) / mPitchCol); - } - - /// Transformation from Detector cell coordinates to Geant detector centered - /// local coordinates (cm) - /// \param int iRow Detector x cell coordinate. - /// \param int iCol Detector z cell coordinate. - /// \param float x Detector local coordinate x in cm with respect to the - /// center of the sensitive volume. - /// \param float z Detector local coordinate z in cm with respect to the - /// 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(int const iRow, int const iCol, float& xRow, float& zCol) const noexcept - { - if (!isValid(iRow, iCol)) { - return false; - } - detectorToLocalUnchecked(iRow, iCol, xRow, zCol); - return isValid(xRow, zCol); - } - - // Same as detectorToLocal w.o. checks. - // We position ourself in the middle of the pixel. - void detectorToLocalUnchecked(int const iRow, int const iCol, float& xRow, float& zCol) const noexcept - { - namespace cp = constants::pixelarray; - xRow = -(iRow + 0.5) * mPitchRow + cp::width / 2.; - zCol = (iCol + 0.5) * mPitchCol - cp::length / 2.; - } - - bool detectorToLocal(int const row, int const col, math_utils::Point3D& loc) const noexcept - { - float xRow{0.}, zCol{0.}; - if (!detectorToLocal(row, col, xRow, zCol)) { - return false; - } - loc.SetCoordinates(xRow, 0., zCol); - return true; - } - - void detectorToLocalUnchecked(int const row, int const col, math_utils::Point3D& loc) const noexcept - { - float xRow{0.}, zCol{0.}; - detectorToLocalUnchecked(row, col, xRow, zCol); - loc.SetCoordinates(xRow, 0., zCol); - } - - private: - template - [[nodiscard]] bool isValid(T const row, T const col) const noexcept - { - if constexpr (std::is_floating_point_v) { // compares in local coord. - namespace cp = constants::pixelarray; - return !static_cast(row <= -cp::width / 2. || cp::width / 2. <= row || col <= -cp::length / 2. || cp::length / 2. <= col); - } else { // compares in rows/cols - return !static_cast(row < 0 || row >= static_cast(mNRows) || col < 0 || col >= static_cast(mNCols)); - } - } - - const int mLayer{0}; ///< chip layer - - ClassDef(SegmentationSuperAlpide, 1); -}; - -/// Segmentation array -extern const std::array SuperSegmentations; -} // namespace o2::its3 - -#endif diff --git a/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h b/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h index d3efde58d0e0d..fedaad9182cce 100644 --- a/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h +++ b/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h @@ -21,114 +21,164 @@ #include +// This files defines the design specifications of the chip. +// Each TGeoShape has the following properties +// length: dimension in z-axis +// width: dimension in xy-axes +// color: for visulisation namespace o2::its3::constants { -constexpr float cm{1e+2}; // This is the default unit of TGeo so we use this as scale -constexpr float mu{1e-6 * cm}; -constexpr float mm{1e-3 * cm}; +constexpr double cm{1e+2}; // This is the default unit of TGeo so we use this as scale +constexpr double mu{1e-6 * cm}; +constexpr double mm{1e-3 * cm}; namespace pixelarray { -constexpr float width{9.197 * mm}; -constexpr float length{3.571 * mm}; +constexpr double width{9.197 * mm}; +constexpr double length{3.571 * mm}; constexpr int nCols{156}; constexpr int nRows{442}; constexpr int nPixels{nRows * nCols}; constexpr EColor color{kGreen}; -constexpr float area{width * length}; +constexpr double area{width * length}; } // namespace pixelarray namespace tile { namespace biasing { -constexpr float width{0.06 * mm}; -constexpr float length{3.571 * mm}; +constexpr double width{0.06 * mm}; +constexpr double length{3.571 * mm}; constexpr EColor color{kYellow}; static_assert(length == pixelarray::length); } // namespace biasing namespace powerswitches { -constexpr float width{9.257 * mm}; -constexpr float length{0.02 * mm}; -constexpr float z{pixelarray::width}; +constexpr double width{9.257 * mm}; +constexpr double length{0.02 * mm}; +constexpr double z{pixelarray::width}; constexpr EColor color{kBlue}; } // namespace powerswitches namespace readout { -constexpr float width{0.525 * mm}; -constexpr float length{3.591 * mm}; +constexpr double width{0.525 * mm}; +constexpr double length{3.591 * mm}; constexpr EColor color{kMagenta}; static_assert(length == (biasing::length + powerswitches::length)); } // namespace readout -constexpr float length{readout::length}; -constexpr float width{powerswitches::width + readout::width}; +constexpr double length{readout::length}; +constexpr double width{powerswitches::width + readout::width}; } // namespace tile namespace rsu { namespace databackbone { -constexpr float width{9.782 * mm}; -constexpr float length{0.06 * mm}; +constexpr double width{9.782 * mm}; +constexpr double length{0.06 * mm}; constexpr EColor color{kRed}; } // namespace databackbone -constexpr float width{19.564 * mm}; -constexpr float length{21.666 * mm}; +constexpr double width{19.564 * mm}; +constexpr double length{21.666 * mm}; constexpr unsigned int nTiles{12}; } // namespace rsu namespace segment { -constexpr float width{rsu::width}; +constexpr double width{rsu::width}; namespace lec { -constexpr float width{segment::width}; -constexpr float length{4.5 * mm}; +constexpr double width{segment::width}; +constexpr double length{4.5 * mm}; constexpr EColor color{kCyan}; } // namespace lec namespace rec { -constexpr float width{segment::width}; -constexpr float length{1.5 * mm}; +constexpr double width{segment::width}; +constexpr double length{1.5 * mm}; constexpr EColor color{kCyan}; } // namespace rec constexpr unsigned int nRSUs{12}; constexpr unsigned int nTilesPerSegment{nRSUs * rsu::nTiles}; -constexpr float length{nRSUs * rsu::length + lec::length + rec::length}; -constexpr float lengthSensitive{nRSUs * rsu::length}; +constexpr double length{(nRSUs * rsu::length) + lec::length + rec::length}; +constexpr double lengthSensitive{nRSUs * rsu::length}; } // namespace segment namespace carbonfoam { // TODO: Waiting for the further information from WP5(Corrado) -constexpr float longeronsWidth{2.0 * mm}; // what is the height of the longerons? -constexpr float longeronsLength{263 * mm}; // from blueprint -constexpr float HringLength{6.0 * mm}; // from blueprint -constexpr float edgeBetwChipAndFoam{1.0 * mm}; // from blueprint but not used cause forms are already overlapping -constexpr float gapBetwHringsLongerons{0.05 * mm}; // from blueprint -constexpr std::array nHoles{11, 11, 11}; // how many holes for each layer? -constexpr std::array radiusHoles{1.0 * mm, 1.0 * mm, 2.0 * mm}; // what is the radius of the holes for each layer? +constexpr double longeronsWidth{2.0 * mm}; // what is the height of the longerons? +constexpr double longeronsLength{263 * mm}; // from blueprint +constexpr double HringLength{6.0 * mm}; // from blueprint +constexpr double edgeBetwChipAndFoam{1.0 * mm}; // from blueprint but not used cause forms are already overlapping +constexpr double gapBetwHringsLongerons{0.05 * mm}; // from blueprint +constexpr std::array nHoles{11, 11, 11}; // how many holes for each layer? +constexpr std::array radiusHoles{1.0 * mm, 1.0 * mm, 2.0 * mm}; // what is the radius of the holes for each layer? constexpr EColor color{kGray}; } // namespace carbonfoam +namespace metalstack +{ +constexpr double thickness{5 * mu}; // physical thickness of the copper metal stack +constexpr double length{segment::length}; +constexpr double width{segment::width}; +constexpr EColor color{kBlack}; +} // namespace metalstack +namespace silicon +{ +constexpr double thickness{45 * mu}; // thickness of silicon +constexpr double thicknessIn{(thickness + metalstack::thickness) / 2.}; // inner silicon thickness +constexpr double thicknessOut{(thickness - metalstack::thickness) / 2.}; // outer silicon thickness +} // namespace silicon constexpr unsigned int nLayers{3}; constexpr unsigned int nTotLayers{7}; constexpr unsigned int nSensorsIB{2 * nLayers}; -constexpr float equatorialGap{1 * mm}; +constexpr double equatorialGap{1 * mm}; constexpr std::array nSegments{3, 4, 5}; -constexpr float thickness{50 * mu}; //< Physical Thickness of chip -constexpr float effThickness{66 * mu}; //< Physical thickness + metal substrate -constexpr std::array radii{19.0006 * mm, 25.228 * mm, 31.4554 * mm}; // middle radius e.g. inner radius+thickness/2. -constexpr std::array radiiInner{radii[0] - thickness / 2.f, radii[1] - thickness / 2.f, radii[2] - thickness / 2.f}; // inner radius -constexpr std::array radiiOuter{radii[0] + thickness / 2.f, radii[1] + thickness / 2.f, radii[2] + thickness / 2.f}; // inner radius +constexpr double totalThickness{silicon::thickness + metalstack::thickness}; // total chip thickness +constexpr std::array radii{19.0006 * mm, 25.228 * mm, 31.4554 * mm}; // nominal radius +constexpr std::array radiiInner{radii[0] - silicon::thicknessIn, radii[1] - silicon::thicknessIn, radii[2] - silicon::thicknessIn}; // inner silicon radius +constexpr std::array radiiOuter{radii[0] + silicon::thicknessOut, radii[1] + silicon::thicknessOut, radii[2] + silicon::thicknessOut}; // outer silicon radius +constexpr std::array radiiMiddle{(radiiInner[0] + radiiOuter[0]) / 2., (radiiInner[1] + radiiOuter[1]) / 2., (radiiInner[2] + radiiOuter[2]) / 2.}; // middle silicon radius +constexpr double nominalYShift{-metalstack::thickness / 2.}; // shift to position in silicion volume to the chip volume (silicon+metalstack) + +// extra information of pixels and their response functions +namespace pixelarray::pixels +{ +namespace mosaix +{ +constexpr double pitchX{width / static_cast(nRows)}; +constexpr double pitchZ{length / static_cast(nCols)}; +} // namespace mosaix +namespace apts +{ +constexpr double pitchX{15.0 * mu}; +constexpr double pitchZ{15.0 * mu}; +constexpr double responseUpperLimit{10 * mu}; +constexpr double responseYShift{responseUpperLimit - silicon::thicknessOut}; +} // namespace apts +namespace moss +{ +namespace top +{ +constexpr double pitchX{22.5 * mu}; +constexpr double pitchZ{22.5 * mu}; +} // namespace top +namespace bot +{ +constexpr double pitchX{18.0 * mu}; +constexpr double pitchZ{18.0 * mu}; +} // namespace bot +} // namespace moss +} // namespace pixelarray::pixels + namespace detID { -constexpr unsigned int mDetIDs{2 * 12 * 12 * 12}; //< 2 Hemispheres * (3,4,5=12 segments in a layer) * 12 RSUs in a segment * 12 Tiles in a RSU -constexpr unsigned int l0IDStart{0}; //< Start DetID layer 0 -constexpr unsigned int l0IDEnd{2 * 3 * 12 * 12 - 1}; //< End First DetID layer 0; inclusive range -constexpr unsigned int l0IDTot{2 * 3 * 12 * 12}; //< Total DetID in Layer 0 -constexpr unsigned int l1IDStart{l0IDEnd + 1}; //< Start DetID layer 1 -constexpr unsigned int l1IDEnd{l1IDStart + 2 * 4 * 12 * 12 - 1}; //< End First DetID layer 1; inclusive range -constexpr unsigned int l1IDTot{2 * 4 * 12 * 12}; //< Total DetID in Layer 1 -constexpr unsigned int l2IDStart{l1IDEnd + 1}; //< Start DetID layer 2 -constexpr unsigned int l2IDEnd{l2IDStart + 2 * 5 * 12 * 12 - 1}; //< End First DetID layer 2; inclusive range -constexpr unsigned int l2IDTot{2 * 5 * 12 * 12}; //< Total DetID in Layer 2 -constexpr unsigned int nChips{l2IDEnd + 1}; //< number of Chips (PixelArrays) in IB +constexpr unsigned int mDetIDs{2 * 12 * 12 * 12}; //< 2 Hemispheres * (3,4,5=12 segments in a layer) * 12 RSUs in a segment * 12 Tiles in a RSU +constexpr unsigned int l0IDStart{0}; //< Start DetID layer 0 +constexpr unsigned int l0IDEnd{(2 * 3 * 12 * 12) - 1}; //< End First DetID layer 0; inclusive range +constexpr unsigned int l0IDTot{2 * 3 * 12 * 12}; //< Total DetID in Layer 0 +constexpr unsigned int l1IDStart{l0IDEnd + 1}; //< Start DetID layer 1 +constexpr unsigned int l1IDEnd{l1IDStart + (2 * 4 * 12 * 12) - 1}; //< End First DetID layer 1; inclusive range +constexpr unsigned int l1IDTot{2 * 4 * 12 * 12}; //< Total DetID in Layer 1 +constexpr unsigned int l2IDStart{l1IDEnd + 1}; //< Start DetID layer 2 +constexpr unsigned int l2IDEnd{l2IDStart + (2 * 5 * 12 * 12) - 1}; //< End First DetID layer 2; inclusive range +constexpr unsigned int l2IDTot{2 * 5 * 12 * 12}; //< Total DetID in Layer 2 +constexpr unsigned int nChips{l2IDEnd + 1}; //< number of Chips (PixelArrays) in IB template inline T getDetID2Layer(T detID) diff --git a/Detectors/Upgrades/ITS3/base/src/ITS3BaseLinkDef.h b/Detectors/Upgrades/ITS3/base/src/ITS3BaseLinkDef.h index dc0557824e0f8..144711b052a1b 100644 --- a/Detectors/Upgrades/ITS3/base/src/ITS3BaseLinkDef.h +++ b/Detectors/Upgrades/ITS3/base/src/ITS3BaseLinkDef.h @@ -15,7 +15,6 @@ #pragma link off all classes; #pragma link off all functions; -#pragma link C++ class o2::its3::SegmentationSuperAlpide + ; #pragma link C++ class o2::its3::ITS3Params + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its3::ITS3Params> + ; diff --git a/Detectors/Upgrades/ITS3/data/CMakeLists.txt b/Detectors/Upgrades/ITS3/data/CMakeLists.txt new file mode 100644 index 0000000000000..3107f0b1fa8ce --- /dev/null +++ b/Detectors/Upgrades/ITS3/data/CMakeLists.txt @@ -0,0 +1,19 @@ +# 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_custom_target(GenerateAPTSResponse + ALL + COMMAND ${CMAKE_BINARY_DIR}/stage/bin/o2-alpide-response-generator -c APTS -i ${ITSRESPONSE_DIR}/response/ITS3ChipResponseData/APTSResponseData/ -o ${CMAKE_CURRENT_BINARY_DIR}/ + BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/APTSResponseData.root + DEPENDS GenerateAlpideResponse + COMMENT "Generating APTSResponseData.root" +) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/APTSResponseData.root" DESTINATION "${CMAKE_INSTALL_PREFIX}/share/Detectors/Upgrades/ITS3/data/ITS3ChipResponseData/") diff --git a/Detectors/Upgrades/ITS3/macros/test/CMakeLists.txt b/Detectors/Upgrades/ITS3/macros/test/CMakeLists.txt index bdd0329c55ecd..cb6812445283c 100644 --- a/Detectors/Upgrades/ITS3/macros/test/CMakeLists.txt +++ b/Detectors/Upgrades/ITS3/macros/test/CMakeLists.txt @@ -19,10 +19,13 @@ its3_add_macro(CheckHits.C) its3_add_macro(CheckDigitsDensity.C) its3_add_macro(CheckClusterSize.C) its3_add_macro(CompareClusterSize.C) -its3_add_macro(CheckSuperAlpideSegment.C) -its3_add_macro(CheckSuperAlpideSegmentTrans.C) +its3_add_macro(CheckMosaixSegment.C) +its3_add_macro(CheckMosaixSegmentTrans.C) its3_add_macro(CompareClustersAndDigits.C) +its3_add_macro(CompareClustersAndDigitsOnChip.C) its3_add_macro(CheckROFs.C) its3_add_macro(CheckTileNumbering.C) its3_add_macro(CreateITS3StaticDeadMap.C) its3_add_macro(TestSensorGeometry.C) +its3_add_macro(CorrTracksClusters.C) +its3_add_macro(CheckChipResponseFile.C) diff --git a/Detectors/Upgrades/ITS3/macros/test/CheckChipResponseFile.C b/Detectors/Upgrades/ITS3/macros/test/CheckChipResponseFile.C new file mode 100644 index 0000000000000..996a99d87ecbc --- /dev/null +++ b/Detectors/Upgrades/ITS3/macros/test/CheckChipResponseFile.C @@ -0,0 +1,192 @@ +// 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 CheckChipResponseFile.C +/// \brief Simple macro to check the chip response files + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include +#include +#include +#include + +#define ENABLE_UPGRADES +#include "ITSMFTSimulation/AlpideSimResponse.h" + +#include "ITS3Base/SegmentationMosaix.h" +#include "fairlogger/Logger.h" +#endif + +using SegmentationMosaix = o2::its3::SegmentationMosaix; + +double um2cm(double um) { return um * 1e-4; } +double cm2um(double cm) { return cm * 1e+4; } + +o2::itsmft::AlpideSimResponse *mAlpSimResp0 = nullptr, + *mAlpSimResp1 = nullptr, + *mAptSimResp1 = nullptr; + +o2::itsmft::AlpideSimResponse* loadResponse(const std::string& fileName, const std::string& respName) +{ + TFile* f = TFile::Open(fileName.data()); + if (!f) { + std::cerr << fileName << " not found" << std::endl; + return nullptr; + } + auto resp = (o2::itsmft::AlpideSimResponse*)f->Get(respName.data()); + if (!resp) + std::cerr << respName << " not found in " << fileName << std::endl; + return resp; +} + +void LoadRespFunc() +{ + std::string AptsFile = "$(O2_ROOT)/share/Detectors/Upgrades/ITS3/data/ITS3ChipResponseData/APTSResponseData.root"; + std::string AlpideFile = "$(O2_ROOT)/share/Detectors/ITSMFT/data/AlpideResponseData/AlpideResponseData.root"; + + mAlpSimResp0 = loadResponse(AlpideFile, "response0"); // Vbb=0V + LOG(info) << "ALPIDE Vbb=0V response" << std::endl; + mAlpSimResp0->print(); + mAlpSimResp1 = loadResponse(AlpideFile, "response1"); // Vbb=-3V + LOG(info) << "ALPIDE Vbb=-3V response" << std::endl; + mAlpSimResp1->print(); + mAptSimResp1 = loadResponse(AptsFile, "response1"); // APTS + LOG(info) << "APTS response" << std::endl; + mAptSimResp1->print(); +} + +std::vector getCollectionSeediciencies(o2::itsmft::AlpideSimResponse* resp, + const std::vector& depths) +{ + std::vector seed; + bool flipRow = false, flipCol = false; + for (auto depth : depths) { + auto rspmat = resp->getResponse(0.0, 0.0, + um2cm(depth) + resp->getDepthMin() + 1.e-9, + flipRow, flipCol); + seed.push_back(rspmat ? rspmat->getValue(2, 2) : 0.f); + } + return seed; +} + +std::vector getShareValues(o2::itsmft::AlpideSimResponse* resp, + const std::vector& depths) +{ + std::vector share; + bool flipRow = false, flipCol = false; + for (auto depth : depths) { + auto rspmat = resp->getResponse(0.0, 0.0, + um2cm(depth) + resp->getDepthMin() + 1.e-9, + flipRow, flipCol); + float s = 0; + int npix = resp->getNPix(); + if (rspmat) { + for (int i = 0; i < npix; ++i) + for (int j = 0; j < npix; ++j) + if (!(i == npix / 2 && j == npix / 2)) + s += rspmat->getValue(i, j); + } + share.push_back(s); + } + return share; +} + +std::vector getEffValues(o2::itsmft::AlpideSimResponse* resp, + const std::vector& depths) +{ + std::vector all; + bool flipRow = false, flipCol = false; + for (auto depth : depths) { + auto rspmat = resp->getResponse(0.0, 0.0, + um2cm(depth) + resp->getDepthMin() + 1.e-9, + flipRow, flipCol); + float s = 0; + int npix = resp->getNPix(); + if (rspmat) { + for (int i = 0; i < npix; ++i) + for (int j = 0; j < npix; ++j) + s += rspmat->getValue(i, j); + } + all.push_back(s); + } + return all; +} + +void CheckChipResponseFile() +{ + LoadRespFunc(); + LOG(info) << "Response function loaded" << std::endl; + + std::vector vecDepth(50); + for (int i = 0; i < 50; ++i) + vecDepth[i] = i; + + int colors[] = {kOrange + 7, kRed + 1, kAzure + 4}; + struct RespInfo { + o2::itsmft::AlpideSimResponse* resp; + std::string title; + int color; + }; + std::vector responses = { + {mAptSimResp1, "APTS", colors[0]}, + {mAlpSimResp0, "ALPIDE Vbb=0V", colors[1]}, + {mAlpSimResp1, "ALPIDE Vbb=-3V", colors[2]}}; + + TCanvas* c1 = new TCanvas("c1", "c1", 800, 600); + TH1* frame = c1->DrawFrame(-1, -0.049, 50, 1.049); + frame->SetTitle(";Depth(um);Charge Collection Seed / Share / Eff"); + TLegend* leg = new TLegend(0.15, 0.5, 0.4, 0.85); + leg->SetFillStyle(0); + leg->SetBorderSize(0); + + for (auto& r : responses) { + if (!r.resp) + continue; + auto seed = getCollectionSeediciencies(r.resp, vecDepth); + auto shr = getShareValues(r.resp, vecDepth); + auto all = getEffValues(r.resp, vecDepth); + + TGraph* grSeed = new TGraph(vecDepth.size(), vecDepth.data(), seed.data()); + grSeed->SetTitle(Form("%s seed", r.title.c_str())); + grSeed->SetLineColor(r.color); + grSeed->SetLineWidth(2); + grSeed->SetMarkerColor(r.color); + grSeed->SetMarkerStyle(kFullCircle); + grSeed->SetMarkerSize(0.8); + grSeed->Draw("SAME LP"); + leg->AddEntry(grSeed, Form("%s seed", r.title.c_str()), "lp"); + + TGraph* grShare = new TGraph(vecDepth.size(), vecDepth.data(), shr.data()); + grShare->SetLineColor(r.color); + grShare->SetLineWidth(2); + grShare->SetMarkerColor(r.color); + grShare->SetMarkerStyle(kOpenSquare); + grShare->SetMarkerSize(1); + grShare->Draw("SAME LP"); + leg->AddEntry(grShare, Form("%s share", r.title.c_str()), "p"); + + TGraph* grEff = new TGraph(vecDepth.size(), vecDepth.data(), all.data()); + grEff->SetLineColor(r.color); + grEff->SetLineWidth(2); + grEff->SetMarkerColor(r.color); + grEff->SetMarkerStyle(kFullDiamond); + grEff->SetMarkerSize(1); + grEff->Draw("SAME LP"); + leg->AddEntry(grEff, Form("%s eff", r.title.c_str()), "p"); + } + leg->Draw(); + + c1->SaveAs("ChipResponse.pdf"); +} diff --git a/Detectors/Upgrades/ITS3/macros/test/CheckClusterSize.C b/Detectors/Upgrades/ITS3/macros/test/CheckClusterSize.C index addaaf47269d2..564b20350b883 100755 --- a/Detectors/Upgrades/ITS3/macros/test/CheckClusterSize.C +++ b/Detectors/Upgrades/ITS3/macros/test/CheckClusterSize.C @@ -43,6 +43,7 @@ #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCEventHeader.h" #include "SimulationDataFormat/MCTrack.h" +#include "ITS3Base/SpecsV2.h" #endif #define ENABLE_UPGRADES #include "SimulationDataFormat/MCTruthContainer.h" @@ -65,7 +66,11 @@ void checkFile(const std::unique_ptr& file); inline auto hist_map(unsigned short id) { - return std::clamp(id, static_cast(0), static_cast(6)) / 2; + int lay = o2::its3::constants::detID::getDetID2Layer(id); + if (lay == -1) { + return nLayers - 1; + } + return lay; } void CheckClusterSize(std::string clusFileName = "o2clus_its.root", @@ -133,7 +138,7 @@ void CheckClusterSize(std::string clusFileName = "o2clus_its.root", std::vector hOtherSecondaryEta; std::vector hOtherSecondaryPt; std::vector hOtherSecondaryPhi; - for (int i = 0; i < 4; ++i) { + for (int i = 0; i < nLayers; ++i) { hPrimary.emplace_back(Form("primary/L%d", i), Form("L%d Primary Cluster Size", i), maxClusterSize, 0, maxClusterSize); hPrimaryEta.emplace_back(Form("primary/EtaL%d", i), Form("L%d Primary Cluster Size vs Eta", i), maxClusterSize, 0, maxClusterSize, 100, -3.0, 3.0); hPrimaryPt.emplace_back(Form("primary/Pt%d", i), Form("L%d Primary Cluster Size vs Pt", i), maxClusterSize, 0, maxClusterSize, 100, 0.0, 10.0); @@ -238,35 +243,39 @@ void CheckClusterSize(std::string clusFileName = "o2clus_its.root", int nROFRec = (int)rofRecVec.size(); auto pattIt = patternsPtr->cbegin(); + int cInvalid{0}, cGood{0}; for (int irof = 0; irof < nROFRec; irof++) { const auto& rofRec = rofRecVec[irof]; - // rofRec.print(); + /*rofRec.print();*/ for (int icl = 0; icl < rofRec.getNEntries(); icl++) { int clEntry = rofRec.getFirstEntry() + icl; const auto& cluster = clusArr[clEntry]; - // cluster.print(); + /*cluster.print();*/ auto pattId = cluster.getPatternID(); auto id = cluster.getSensorID(); + auto ib = o2::its3::constants::detID::isDetITS3(id); int clusterSize{-1}; - if (pattId == o2::itsmft::CompCluster::InvalidPatternID || dict.isGroup(pattId)) { + if (pattId == o2::itsmft::CompCluster::InvalidPatternID || dict.isGroup(pattId, ib)) { o2::itsmft::ClusterPattern patt(pattIt); clusterSize = patt.getNPixels(); continue; } else { - clusterSize = dict.getNpixels(pattId); + clusterSize = dict.getNpixels(pattId, ib); } const auto& label = (clusLabArr->getLabels(clEntry))[0]; if (!label.isValid() || label.getSourceID() != 0 || !label.isCorrect()) { + ++cInvalid; continue; } + ++cGood; const int trackID = label.getTrackID(); int evID = label.getEventID(); const auto& pInfo = info[evID][trackID]; - if (id > 6) { + if (!o2::its3::constants::detID::isDetITS3(id)) { hOuterBarrel.Fill(clusterSize); } @@ -332,6 +341,7 @@ void CheckClusterSize(std::string clusFileName = "o2clus_its.root", } } } + std::cout << "Good labels: " << cGood << "; invalid: " << cInvalid << '\n'; std::cout << "Done measuring cluster sizes:" << std::endl; for (int i = 0; i < nLayers; ++i) { std::cout << "* Layer " << i << ":\n"; diff --git a/Detectors/Upgrades/ITS3/macros/test/CheckClustersITS3.C b/Detectors/Upgrades/ITS3/macros/test/CheckClustersITS3.C index af03ed7a9877b..006271a1ea7bd 100644 --- a/Detectors/Upgrades/ITS3/macros/test/CheckClustersITS3.C +++ b/Detectors/Upgrades/ITS3/macros/test/CheckClustersITS3.C @@ -25,7 +25,7 @@ #define ENABLE_UPGRADES #include "DetectorsCommonDataFormats/DetID.h" #include "ITSMFTBase/SegmentationAlpide.h" -#include "ITS3Base/SegmentationSuperAlpide.h" +#include "ITS3Base/SegmentationMosaix.h" #include "ITS3Base/SpecsV2.h" #include "ITSBase/GeometryTGeo.h" #include "DataFormatsITSMFT/CompCluster.h" @@ -50,22 +50,24 @@ void CheckClustersITS3(const std::string& clusfile = "o2clus_its.root", using namespace o2::base; using namespace o2::its; - using SuperSegmentation = o2::its3::SegmentationSuperAlpide; + using MosaixSegmentation = o2::its3::SegmentationMosaix; using Segmentation = o2::itsmft::SegmentationAlpide; using o2::itsmft::CompClusterExt; using o2::itsmft::Hit; using ROFRec = o2::itsmft::ROFRecord; using MC2ROF = o2::itsmft::MC2ROFRecord; using HitVec = std::vector; - using MC2HITS_map = std::unordered_map; // maps (track_ID<<16 + chip_ID) to entry in the hit vector + using MC2HITS_map = std::unordered_map; // maps (track_ID<<32 + chip_ID) to entry in the hit vector + std::array mMosaixSegmentations{0, 1, 2}; std::vector hitVecPool; std::vector mc2hitVec; - ULong_t cPattValid{0}, cPattInvalid{0}, cLabelInvalid{0}, cNoMC{0}; + ULong_t cPattValidIB{0}, cPattInvalidIB{0}, cLabelInvalidIB{0}, cNoMCIB{0}; + ULong_t cPattValidOB{0}, cPattInvalidOB{0}, cLabelInvalidOB{0}, cNoMCOB{0}; TFile fout("CheckClusters.root", "recreate"); - TNtuple nt("ntc", "cluster ntuple", "ev:lab:hlx:hlz:hgx:hgz:tx:tz:cgx:cgy:cgz:clx:cly:clz:dx:dy:dz:ex:ez:patid:rof:npx:id"); + TNtuple nt("ntc", "cluster ntuple", "ev:lab:hlx:hlz:hgx:hgz:tx:tz:cgx:cgy:cgz:clx:cly:clz:dx:dy:dz:ex:ez:patid:rof:npx:id:eta:row:col:lay"); // Geometry o2::base::GeometryManager::loadGeometry(inputGeom); @@ -102,6 +104,7 @@ void CheckClustersITS3(const std::string& clusfile = "o2clus_its.root", } else { LOG(info) << "Running without dictionary !"; } + dict.print(); // ROFrecords std::vector rofRecVec, *rofRecVecP = &rofRecVec; @@ -174,20 +177,18 @@ void CheckClustersITS3(const std::string& clusfile = "o2clus_its.root", auto isIB = o2::its3::constants::detID::isDetITS3(chipID); auto layer = o2::its3::constants::detID::getDetID2Layer(chipID); auto clusterSize{-1}; - if (pattID == o2::itsmft::CompCluster::InvalidPatternID || dict.isGroup(pattID)) { + if (pattID == o2::itsmft::CompCluster::InvalidPatternID || dict.isGroup(pattID, isIB)) { o2::itsmft::ClusterPattern patt(pattIt); locC = dict.getClusterCoordinates(cluster, patt, false); LOGP(debug, "I am invalid and I am on chip {}", chipID); - ++cPattInvalid; + (isIB) ? ++cPattInvalidIB : ++cPattInvalidOB; continue; } else { locC = dict.getClusterCoordinates(cluster); - errX = dict.getErrX(pattID); - errZ = dict.getErrZ(pattID); - errX *= (isIB) ? SuperSegmentation::mPitchRow : Segmentation::PitchRow; - errZ *= (isIB) ? SuperSegmentation::mPitchCol : Segmentation::PitchCol; - npix = dict.getNpixels(pattID); - ++cPattValid; + errX = dict.getErrX(pattID, isIB); + errZ = dict.getErrZ(pattID, isIB); + npix = dict.getNpixels(pattID, isIB); + (isIB) ? ++cPattValidIB : ++cPattValidOB; } // Transformation to the local --> global @@ -195,7 +196,7 @@ void CheckClustersITS3(const std::string& clusfile = "o2clus_its.root", const auto& lab = (clusLabArr->getLabels(clEntry))[0]; if (!lab.isValid()) { - ++cLabelInvalid; + (isIB) ? ++cLabelInvalidIB : ++cLabelInvalidOB; continue; } @@ -207,7 +208,7 @@ void CheckClustersITS3(const std::string& clusfile = "o2clus_its.root", auto hitEntry = mc2hit.find(key); if (hitEntry == mc2hit.end()) { LOG(debug) << "Failed to find MC hit entry for Tr" << trID << " chipID" << chipID; - ++cNoMC; + (isIB) ? ++cNoMCIB : ++cNoMCOB; continue; } const auto& hit = (*hitArray)[hitEntry->second]; @@ -234,25 +235,22 @@ void CheckClustersITS3(const std::string& clusfile = "o2clus_its.root", } else { // compare in local flat coordinates float xFlatEnd{0.}, yFlatEnd{0.}; - o2::its3::SuperSegmentations[layer].curvedToFlat(locH.X(), locH.Y(), xFlatEnd, yFlatEnd); + mMosaixSegmentations[layer].curvedToFlat(locH.X(), locH.Y(), xFlatEnd, yFlatEnd); locH.SetXYZ(xFlatEnd, yFlatEnd, locH.Z()); float xFlatSta{0.}, yFlatSta{0.}; - o2::its3::SuperSegmentations[layer].curvedToFlat(locHsta.X(), locHsta.Y(), xFlatSta, yFlatSta); + mMosaixSegmentations[layer].curvedToFlat(locHsta.X(), locHsta.Y(), xFlatSta, yFlatSta); locHsta.SetXYZ(xFlatSta, yFlatSta, locHsta.Z()); - // recalculate x/y in flat - // x0 = xFlatSta, dltx = xFlatEnd - x0; - // y0 = yFlatSta, dlty = yFlatEnd - y0; - // r = (0.5 * (SuperSegmentation::mSensorLayerThickness - SuperSegmentation::mSensorLayerThicknessEff) - y0) / dlty; - // locH.SetXYZ(x0 + r * dltx, y0 + r * dlty, z0 + r * dltz); // not really precise, but okish locH.SetXYZ(0.5f * (locH.X() + locHsta.X()), 0.5f * (locH.Y() + locHsta.Y()), 0.5f * (locH.Z() + locHsta.Z())); - o2::its3::SuperSegmentations[layer].curvedToFlat(locC.X(), locC.Y(), xFlatSta, yFlatSta); + mMosaixSegmentations[layer].curvedToFlat(locC.X(), locC.Y(), xFlatSta, yFlatSta); locC.SetXYZ(xFlatSta, yFlatSta, locC.Z()); } + float theta = std::acos(gloC.Z() / gloC.Rho()); + float eta = -std::log(std::tan(theta / 2)); - std::array data = {(float)lab.getEventID(), (float)trID, + std::array data = {(float)lab.getEventID(), (float)trID, locH.X(), locH.Z(), gloH.X(), gloH.Z(), dltx / dlty, dltz / dlty, @@ -260,13 +258,15 @@ void CheckClustersITS3(const std::string& clusfile = "o2clus_its.root", locC.X(), locC.Y(), locC.Z(), locC.X() - locH.X(), locC.Y() - locH.Y(), locC.Z() - locH.Z(), errX, errZ, (float)pattID, - (float)rofRec.getROFrame(), (float)npix, (float)chipID}; + (float)rofRec.getROFrame(), (float)npix, (float)chipID, eta, (float)cluster.getRow(), (float)cluster.getCol(), (float)layer}; nt.Fill(data.data()); } } - LOGP(info, "There were {} valid PatternIDs and {} ({:.1f}%) invalid ones", cPattValid, cPattInvalid, ((float)cPattInvalid / (float)(cPattInvalid + cPattValid)) * 100); - LOGP(info, "There were {} invalid Labels and {} with No MC Hit information ", cLabelInvalid, cNoMC); + LOGP(info, "IB {} valid PatternIDs and {} ({:.1f}%) invalid ones", cPattValidIB, cPattInvalidIB, ((float)cPattInvalidIB / (float)(cPattInvalidIB + cPattValidIB)) * 100); + LOGP(info, "IB {} invalid Labels and {} with No MC Hit information ", cLabelInvalidIB, cNoMCIB); + LOGP(info, "OB {} valid PatternIDs and {} ({:.1f}%) invalid ones", cPattValidOB, cPattInvalidOB, ((float)cPattInvalidOB / (float)(cPattInvalidOB + cPattValidOB)) * 100); + LOGP(info, "OB {} invalid Labels and {} with No MC Hit information ", cLabelInvalidOB, cNoMCOB); auto canvCgXCgY = new TCanvas("canvCgXCgY", "", 1600, 1600); canvCgXCgY->Divide(2, 2); @@ -292,6 +292,18 @@ void CheckClustersITS3(const std::string& clusfile = "o2clus_its.root", nt.Draw("dx:dz>>h_dx_vs_dz_OB_z(1000, -0.01, 0.01, 1000, -0.01, 0.01)", "id >= 3456 && abs(cgz) < 2", "colz"); canvdXdZ->SaveAs("it3clusters_dx_vs_dz.pdf"); + auto canvCHXZ = new TCanvas("canvCHXZ", "", 1600, 1600); + canvCHXZ->Divide(2, 2); + canvCHXZ->cd(1); + nt.Draw("(cgx-hgx)*10000:eta>>h_chx_IB(101,-1.4,1.4,101,-50,50)", "id<3456", "prof"); + canvCHXZ->cd(2); + nt.Draw("(cgx-hgx)*10000:eta>>h_chx_OB(101,-1.4,1.4,101,-50,50)", "id>=3456", "prof"); + canvCHXZ->cd(3); + nt.Draw("(cgz-hgz)*10000:eta>>h_chz_IB(101,-1.4,1.4,101,-50,50)", "id<3456", "prof"); + canvCHXZ->cd(4); + nt.Draw("(cgz-hgz)*10000:eta>>h_chz_OB(101,-1.4,1.4,101,-50,50)", "id>=3456", "prof"); + canvCgXCgY->SaveAs("it3clusters_xz_eta.pdf"); + auto c1 = new TCanvas("p1", "pullX"); c1->cd(); c1->SetLogy(); diff --git a/Detectors/Upgrades/ITS3/macros/test/CheckDigitsDensity.C b/Detectors/Upgrades/ITS3/macros/test/CheckDigitsDensity.C index 0c8d9c3bdfbec..67b75e33bc430 100755 --- a/Detectors/Upgrades/ITS3/macros/test/CheckDigitsDensity.C +++ b/Detectors/Upgrades/ITS3/macros/test/CheckDigitsDensity.C @@ -37,7 +37,7 @@ #include "ITS3Base/SpecsV2.h" #include "CommonConstants/MathConstants.h" #include "DataFormatsITSMFT/Digit.h" -#include "ITS3Base/SegmentationSuperAlpide.h" +#include "ITS3Base/SegmentationMosaix.h" #include "DetectorsBase/GeometryManager.h" #include "ITSBase/GeometryTGeo.h" #include "fairlogger/Logger.h" @@ -56,7 +56,7 @@ constexpr double qedRate = qedXSection / hadXSection * interaction_rate; // Hz constexpr double qedFactor = qedRate * integration_time; // a.u. using o2::itsmft::Digit; namespace its3 = o2::its3; -using SSAlpide = its3::SegmentationSuperAlpide; +using Mosaix = its3::SegmentationMosaix; void checkFile(const std::unique_ptr& file); @@ -64,7 +64,7 @@ void CheckDigitsDensity(int nEvents = 10000, std::string digitFileName = "it3dig { gROOT->SetBatch(batch); LOGP(debug, "Checking Digit ITS3 Density"); - // Vars + std::array mMosaixSegmentations{0, 1, 2}; // Geometry o2::base::GeometryManager::loadGeometry(geomFileName); @@ -80,8 +80,8 @@ void CheckDigitsDensity(int nEvents = 10000, std::string digitFileName = "it3dig digitTree->SetBranchAddress("IT3Digit", &digitArrayPtr); std::array hists; for (int i{3}; i--;) { - double rmin = its3::constants::radii[i] - its3::constants::thickness; - double rmax = its3::constants::radii[i] + its3::constants::thickness; + double rmin = its3::constants::radiiInner[i]; + double rmax = its3::constants::radiiOuter[i]; hists[i] = new TH2F(Form("h_digits_dens_L%d", i), Form("Digit Density L%d in %d Events; Z_{Glo} [cm]; R_{Glo} [cm]", i, nEvents), 100, -15, 15, 100, rmin, rmax); } @@ -103,8 +103,8 @@ void CheckDigitsDensity(int nEvents = 10000, std::string digitFileName = "it3dig // goto curved coordinates float x{0.f}, y{0.f}, z{0.f}; float xFlat{0.f}, yFlat{0.f}; - its3::SuperSegmentations[layer].detectorToLocal(row, col, xFlat, z); - its3::SuperSegmentations[layer].flatToCurved(xFlat, 0., x, y); + mMosaixSegmentations[layer].detectorToLocal(row, col, xFlat, z); + mMosaixSegmentations[layer].flatToCurved(xFlat, 0., x, y); const o2::math_utils::Point3D locD(x, y, z); const auto gloD = gman->getMatrixL2G(id)(locD); // convert to global const auto R = std::hypot(gloD.X(), gloD.Y()); @@ -115,7 +115,7 @@ void CheckDigitsDensity(int nEvents = 10000, std::string digitFileName = "it3dig std::unique_ptr oFile(TFile::Open("checkDigitsDensity.root", "RECREATE")); checkFile(oFile); for (const auto& h : hists) { - h->Scale(1. / (SSAlpide::mPitchCol * SSAlpide::mPitchRow * nEvents)); + h->Scale(1. / (Mosaix::PitchCol * Mosaix::PitchRow * nEvents)); h->ProjectionX()->Write(); h->Write(); } diff --git a/Detectors/Upgrades/ITS3/macros/test/CheckDigitsITS3.C b/Detectors/Upgrades/ITS3/macros/test/CheckDigitsITS3.C index 16aa3adc8101c..240b1bd344af5 100644 --- a/Detectors/Upgrades/ITS3/macros/test/CheckDigitsITS3.C +++ b/Detectors/Upgrades/ITS3/macros/test/CheckDigitsITS3.C @@ -27,7 +27,7 @@ #define ENABLE_UPGRADES #include "ITSBase/GeometryTGeo.h" #include "DataFormatsITSMFT/Digit.h" -#include "ITS3Base/SegmentationSuperAlpide.h" +#include "ITS3Base/SegmentationMosaix.h" #include "ITSMFTBase/SegmentationAlpide.h" #include "ITSMFTSimulation/Hit.h" #include "MathUtils/Utils.h" @@ -51,6 +51,7 @@ void CheckDigitsITS3(std::string digifile = "it3digits.root", std::string hitfil using o2::itsmft::Hit; using o2::itsmft::SegmentationAlpide; + std::array mMosaixSegmentations{0, 1, 2}; TFile* f = TFile::Open("CheckDigits.root", "recreate"); TNtuple* nt = new TNtuple("ntd", "digit ntuple", "id:x:y:z:rowD:colD:rowH:colH:xlH:zlH:xlcH:zlcH:dx:dz"); @@ -79,8 +80,6 @@ void CheckDigitsITS3(std::string digifile = "it3digits.root", std::string hitfil int nevD = digTree->GetEntries(); // digits in cont. readout may be grouped as few events per entry - int lastReadHitEv = -1; - int nDigitReadIB{0}, nDigitReadOB{0}; int nDigitFilledIB{0}, nDigitFilledOB{0}; @@ -165,8 +164,8 @@ void CheckDigitsITS3(std::string digifile = "it3digits.root", std::string hitfil if (isIB) { // ITS3 IB float xFlat{0.f}, yFlat{0.f}; - its3::SuperSegmentations[layer].detectorToLocal(ix, iz, xFlat, z); - its3::SuperSegmentations[layer].flatToCurved(xFlat, 0., x, y); + mMosaixSegmentations[layer].detectorToLocal(ix, iz, xFlat, z); + mMosaixSegmentations[layer].flatToCurved(xFlat, 0., x, y); } else { // ITS2 OB SegmentationAlpide::detectorToLocal(ix, iz, x, z); @@ -184,7 +183,7 @@ void CheckDigitsITS3(std::string digifile = "it3digits.root", std::string hitfil const auto* mc2hit = &mc2hitVec[lab.getEventID()]; const auto& hitEntry = mc2hit->find(key); if (hitEntry == mc2hit->end()) { - LOGP(error, "Failed to find MC hit entry for Tr {} chipID {}", trID, chipID); + LOGP(debug, "Failed to find MC hit entry for Tr {} chipID {}", trID, chipID); continue; } @@ -196,18 +195,18 @@ void CheckDigitsITS3(std::string digifile = "it3digits.root", std::string hitfil auto xyzLocE = gman->getMatrixL2G(chipID) ^ (hit.GetPos()); // inverse conversion from global to local auto xyzLocS = gman->getMatrixL2G(chipID) ^ (hit.GetPosStart()); o2::math_utils::Vector3D xyzLocM; - xyzLocM.SetCoordinates(0.5 * (xyzLocE.X() + xyzLocS.X()), 0.5 * (xyzLocE.Y() + xyzLocS.Y()), 0.5 * (xyzLocE.Z() + xyzLocS.Z())); + xyzLocM.SetCoordinates(0.5f * (xyzLocE.X() + xyzLocS.X()), 0.5f * (xyzLocE.Y() + xyzLocS.Y()), 0.5f * (xyzLocE.Z() + xyzLocS.Z())); float xlc = 0., zlc = 0.; int row = 0, col = 0; if (isIB) { float xFlat{0.}, yFlat{0.}; - its3::SuperSegmentations[layer].curvedToFlat(xyzLocM.X(), xyzLocM.Y(), xFlat, yFlat); + mMosaixSegmentations[layer].curvedToFlat(xyzLocM.X(), xyzLocM.Y(), xFlat, yFlat); xyzLocM.SetCoordinates(xFlat, yFlat, xyzLocM.Z()); - its3::SuperSegmentations[layer].curvedToFlat(locD.X(), locD.Y(), xFlat, yFlat); + mMosaixSegmentations[layer].curvedToFlat(locD.X(), locD.Y(), xFlat, yFlat); locD.SetCoordinates(xFlat, yFlat, locD.Z()); - if (auto v1 = !its3::SuperSegmentations[layer].localToDetector(xyzLocM.X(), xyzLocM.Z(), row, col), - v2 = !its3::SuperSegmentations[layer].detectorToLocal(row, col, xlc, zlc); + if (auto v1 = !mMosaixSegmentations[layer].localToDetector(xyzLocM.X(), xyzLocM.Z(), row, col), + v2 = !mMosaixSegmentations[layer].detectorToLocal(row, col, xlc, zlc); v1 || v2) { continue; } @@ -223,7 +222,7 @@ void CheckDigitsITS3(std::string digifile = "it3digits.root", std::string hitfil (isIB) ? ++nDigitFilledIB : ++nDigitFilledOB; } // end loop on digits array - } // end loop on ROFRecords array + } // end loop on ROFRecords array auto canvXY = new TCanvas("canvXY", "", 1600, 1600); canvXY->Divide(2, 2); diff --git a/Detectors/Upgrades/ITS3/macros/test/CheckHits.C b/Detectors/Upgrades/ITS3/macros/test/CheckHits.C index 7833b7c205f4a..00ac0a992ba39 100644 --- a/Detectors/Upgrades/ITS3/macros/test/CheckHits.C +++ b/Detectors/Upgrades/ITS3/macros/test/CheckHits.C @@ -31,7 +31,6 @@ #define ENABLE_UPGRADES #include "CommonConstants/MathConstants.h" -#include "ITS3Base/SegmentationSuperAlpide.h" #include "ITS3Base/SpecsV2.h" #include "ITSMFTSimulation/Hit.h" #include "SimulationDataFormat/MCTrack.h" @@ -39,7 +38,6 @@ namespace it3c = o2::its3::constants; namespace it3d = it3c::detID; -using SSAlpide = o2::its3::SegmentationSuperAlpide; using o2::itsmft::Hit; constexpr double interaction_rate = 50e3; // Hz diff --git a/Detectors/Upgrades/ITS3/macros/test/CheckSuperAlpideSegment.C b/Detectors/Upgrades/ITS3/macros/test/CheckMosaixSegment.C similarity index 78% rename from Detectors/Upgrades/ITS3/macros/test/CheckSuperAlpideSegment.C rename to Detectors/Upgrades/ITS3/macros/test/CheckMosaixSegment.C index 76ac02959415d..12e1ab3a7280d 100644 --- a/Detectors/Upgrades/ITS3/macros/test/CheckSuperAlpideSegment.C +++ b/Detectors/Upgrades/ITS3/macros/test/CheckMosaixSegment.C @@ -9,9 +9,6 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file CheckTracksITS3.C -/// \brief Simple macro to check ITS3 tracks - #if !defined(__CLING__) || defined(__ROOTCLING__) #include "Rtypes.h" @@ -24,14 +21,6 @@ #include "TGLViewer.h" #include "TMath.h" -#include "TEveGeoNode.h" -#include "TEveManager.h" -#include "TEveViewer.h" -#include "TEvePointSet.h" -#include "TEveTrackPropagator.h" -#include "TEveTrack.h" -#include "TEveVSDStructs.h" - #include "TFile.h" #include "TGraph.h" #include "TH1D.h" @@ -49,40 +38,41 @@ #include "MathUtils/Cartesian.h" #include "ITS3Base/SpecsV2.h" -#include "ITS3Base/SegmentationSuperAlpide.h" +#include "ITS3Base/SegmentationMosaix.h" #include "ITSBase/GeometryTGeo.h" #endif using gITS = o2::its::GeometryTGeo; -void CheckSuperAlpideSegment(bool isTestDetectorToLocal = false, - bool isTestFlatToCurved = false, - bool isTestLocalToGlobal = false) +void CheckMosaixSegment(bool isTestDetectorToLocal = false, + bool isTestFlatToCurved = false, + bool isTestLocalToGlobal = false) { using namespace o2::its3; - static constexpr unsigned int mNCols{SegmentationSuperAlpide::mNCols}; - static constexpr unsigned int mNRows{SegmentationSuperAlpide::mNRows}; + static constexpr unsigned int mNCols{SegmentationMosaix::NCols}; + static constexpr unsigned int mNRows{SegmentationMosaix::NRows}; static constexpr unsigned int nPixels{mNCols * mNRows}; + std::array mMosaixSegmentations{0, 1, 2}; if (isTestDetectorToLocal || isTestFlatToCurved) { namespace cp = constants::pixelarray; - TH2I* h_raw_col = new TH2I("h_raw_col", "raws and cols sown;raw;col", mNRows, 0, mNRows, mNCols, 0, mNCols); - TH2D* h_xLocal_zLocal = new TH2D("h_xLocal_zLocal", "x and z from raws and cols;xLocal;zLocal", mNRows, -cp::length / 2, cp::length / 2, mNCols, -cp::width / 2, cp::width / 2); - TH2I* h_raw_col_translate = new TH2I("h_raw_col_translate", "raws and cols from x and z;raw;col", mNRows, 0, mNRows, mNCols, 0, mNCols); - TGraph* g_raw_xLocal = new TGraph(); - g_raw_xLocal->SetMarkerStyle(20); - g_raw_xLocal->SetMarkerSize(0.2); + TH2I* h_row_col = new TH2I("h_row_col", "rows and cols sown;row;col", mNRows, 0, mNRows, mNCols, 0, mNCols); + TH2D* h_xLocal_zLocal = new TH2D("h_xLocal_zLocal", "x and z from rows and cols;xLocal;zLocal", mNRows, -cp::length / 2, cp::length / 2, mNCols, -cp::width / 2, cp::width / 2); + TH2I* h_row_col_translate = new TH2I("h_row_col_translate", "rows and cols from x and z;row;col", mNRows, 0, mNRows, mNCols, 0, mNCols); + TGraph* g_row_xLocal = new TGraph(); + g_row_xLocal->SetMarkerStyle(20); + g_row_xLocal->SetMarkerSize(0.2); TGraph* g_col_zLocal = new TGraph(); g_col_zLocal->SetMarkerStyle(20); g_col_zLocal->SetMarkerSize(0.2); - TGraph* g_raw_xLocal_translate = new TGraph(); - g_raw_xLocal_translate->SetMarkerStyle(20); - g_raw_xLocal_translate->SetMarkerSize(0.2); + TGraph* g_row_xLocal_translate = new TGraph(); + g_row_xLocal_translate->SetMarkerStyle(20); + g_row_xLocal_translate->SetMarkerSize(0.2); TGraph* g_col_zLocal_translate = new TGraph(); g_col_zLocal_translate->SetMarkerStyle(20); - SegmentationSuperAlpide seg(0); + SegmentationMosaix seg(0); int nPoint = 0; for (UInt_t i = 0; i < mNRows; ++i) { for (UInt_t j = 0; j < mNCols; ++j) { @@ -92,16 +82,16 @@ void CheckSuperAlpideSegment(bool isTestDetectorToLocal = false, int col_trans = -1; seg.detectorToLocal(i, j, xLocal, zLocal); seg.localToDetector(xLocal, zLocal, row_trans, col_trans); - g_raw_xLocal->SetPoint(nPoint, i, xLocal); + g_row_xLocal->SetPoint(nPoint, i, xLocal); g_col_zLocal->SetPoint(nPoint, j, zLocal); - g_raw_xLocal_translate->SetPoint(nPoint, xLocal, row_trans); + g_row_xLocal_translate->SetPoint(nPoint, xLocal, row_trans); g_col_zLocal_translate->SetPoint(nPoint++, zLocal, col_trans); bool pattern = ((i >= 50 && i <= 100) || (i >= 250 && i <= 350)) && ((j >= 30 && j <= 70) || (j >= 100 && j <= 120)); if (pattern) { - h_raw_col->Fill(i, j); + h_row_col->Fill(i, j); h_xLocal_zLocal->Fill(xLocal, zLocal); - h_raw_col_translate->Fill(row_trans, col_trans); + h_row_col_translate->Fill(row_trans, col_trans); } } } @@ -110,29 +100,30 @@ void CheckSuperAlpideSegment(bool isTestDetectorToLocal = false, // gStyle->SetPalette(kCMYK); c1->Divide(3, 1); c1->cd(1); - h_raw_col->Draw("colz"); + h_row_col->Draw("colz"); c1->cd(2); h_xLocal_zLocal->Draw("colz"); c1->cd(3); - h_raw_col_translate->Draw("colz"); + h_row_col_translate->Draw("colz"); TCanvas* c2 = new TCanvas("c2", "c2", 1600, 400); c2->Divide(4, 1); c2->cd(1); - g_raw_xLocal->SetTitle("xLocal vs raw;raw;xLocal"); - g_raw_xLocal->Draw("same ap"); + g_row_xLocal->SetTitle("xLocal vs row;row;xLocal"); + g_row_xLocal->Draw("same ap"); c2->cd(2); g_col_zLocal->SetTitle("zLocal vs col;col;zLocal"); g_col_zLocal->Draw("same ap"); c2->cd(3); - g_raw_xLocal_translate->SetTitle("raw_translate vs xLocal;xLocal;raw_translate"); - g_raw_xLocal_translate->Draw("same ap"); + g_row_xLocal_translate->SetTitle("row_translate vs xLocal;xLocal;row_translate"); + g_row_xLocal_translate->Draw("same ap"); c2->cd(4); g_col_zLocal_translate->SetTitle("col_translate vs zLocal;zLocal;col_translate"); g_col_zLocal_translate->Draw("same ap"); } if (isTestLocalToGlobal) { + o2::base::GeometryManager::loadGeometry(); namespace cp = constants::pixelarray; TH2D* h_xCurved_yCurved = new TH2D("h_xCurved_yCurved", "from flat to curved;x;y", 200, -1, 4, 200, -2, 3); TH2D* h_xFlat_yFlat = new TH2D("h_xFlat_yFlat", "from curved to flat ;x;y", 200, -1, 4, 200, -2, 3); @@ -170,11 +161,11 @@ void CheckSuperAlpideSegment(bool isTestDetectorToLocal = false, float xLocal_translate = 0; float yLocal_translate = 0; - SuperSegmentations[iLayer].detectorToLocal(row, col, xLocal, zLocal); - SuperSegmentations[iLayer].flatToCurved(xLocal, 0., xCurved, yCurved); + mMosaixSegmentations[iLayer].detectorToLocal(row, col, xLocal, zLocal); + mMosaixSegmentations[iLayer].flatToCurved(xLocal, 0., xCurved, yCurved); double posLocal[3] = {xCurved, yCurved, zLocal}; double posGlobal[3] = {0, 0, 0}; - SuperSegmentations[iLayer].curvedToFlat(xCurved, yCurved, xLocal_translate, yLocal_translate); + mMosaixSegmentations[iLayer].curvedToFlat(xCurved, yCurved, xLocal_translate, yLocal_translate); matrix->LocalToMaster(posLocal, posGlobal); h_xCurved_yCurved->Fill(xLocal, 0); @@ -195,8 +186,7 @@ void CheckSuperAlpideSegment(bool isTestDetectorToLocal = false, TArc* arc[3]; h_xCurved_yCurved->Draw("colz"); for (int i = 0; i < 3; i++) { - arc[i] = new TArc(-0, 0, constants::radii[i] + constants::thickness / 2., -5, 40); - arc[i]->SetLineColor(kRed); + arc[i] = new TArc(-0, 0, constants::radiiOuter[i], -5, 40); arc[i]->SetFillStyle(0); } diff --git a/Detectors/Upgrades/ITS3/macros/test/CheckSuperAlpideSegmentTrans.C b/Detectors/Upgrades/ITS3/macros/test/CheckMosaixSegmentTrans.C similarity index 85% rename from Detectors/Upgrades/ITS3/macros/test/CheckSuperAlpideSegmentTrans.C rename to Detectors/Upgrades/ITS3/macros/test/CheckMosaixSegmentTrans.C index 64937f2ad2855..1a723bd6017bb 100644 --- a/Detectors/Upgrades/ITS3/macros/test/CheckSuperAlpideSegmentTrans.C +++ b/Detectors/Upgrades/ITS3/macros/test/CheckMosaixSegmentTrans.C @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file CheckSuperAlpideSegmentTrans.C +/// \file CheckMosaixSegmentTrans.C /// \brief Simple macro to check ITS3 Alpide Trans #if !defined(__CLING__) || defined(__ROOTCLING__) @@ -26,7 +26,7 @@ #include "TStyle.h" #include "TTree.h" -#include "ITS3Base/SegmentationSuperAlpide.h" +#include "ITS3Base/SegmentationMosaix.h" #include "ITS3Base/SpecsV2.h" #endif @@ -37,10 +37,11 @@ constexpr float PI = 3.14159274101257324e+00f; constexpr float Rad2Deg = 180.f / PI; constexpr float Deg2Rad = 1. / Rad2Deg; -constexpr auto nRows{SegmentationSuperAlpide::mNRows}; -constexpr auto nCols{SegmentationSuperAlpide::mNCols}; -constexpr auto fLength{SegmentationSuperAlpide::mLength}; -constexpr auto fWidth{SegmentationSuperAlpide::mWidth}; +constexpr auto nRows{SegmentationMosaix::NRows}; +constexpr auto nCols{SegmentationMosaix::NCols}; +constexpr auto fLength{SegmentationMosaix::Length}; +constexpr auto fWidth{SegmentationMosaix::Width}; +const std::array mMosaixSegmentations{0, 1, 2}; TH2* DrawReverseBins(TH2* h) { @@ -83,13 +84,13 @@ void DrawXAxisCol(TH1* h) newaxis->Draw(); } -void CheckSuperAlpideSegmentTrans() +void CheckMosaixSegmentTrans() { gStyle->SetOptStat(1111111); for (int iLayer{0}; iLayer < 3; ++iLayer) { - float r_inner = constants::radii[iLayer] - constants::thickness / 2.; - float r_outer = constants::radii[iLayer] + constants::thickness / 2.; + float r_inner = constants::radiiInner[iLayer]; + float r_outer = constants::radiiOuter[iLayer]; float phiReadout_inner = constants::tile::readout::width / r_inner * Rad2Deg; float phiReadout_outer = @@ -140,10 +141,10 @@ void CheckSuperAlpideSegmentTrans() g_arc_inner->AddPoint(x_inner, y_inner); g_arc_outer->AddPoint(x_outer, y_outer); // Test Segmentation - SuperSegmentations[iLayer].curvedToFlat(x_inner, y_inner, x_inner_flat, y_inner_flat); - SuperSegmentations[iLayer].flatToCurved(x_inner_flat, y_inner_flat, x_inner_curved, y_inner_curved); - SuperSegmentations[iLayer].curvedToFlat(x_outer, y_outer, x_outer_flat, y_outer_flat); - SuperSegmentations[iLayer].flatToCurved(x_outer_flat, y_outer_flat, x_outer_curved, y_outer_curved); + mMosaixSegmentations[iLayer].curvedToFlat(x_inner, y_inner, x_inner_flat, y_inner_flat); + mMosaixSegmentations[iLayer].flatToCurved(x_inner_flat, y_inner_flat, x_inner_curved, y_inner_curved); + mMosaixSegmentations[iLayer].curvedToFlat(x_outer, y_outer, x_outer_flat, y_outer_flat); + mMosaixSegmentations[iLayer].flatToCurved(x_outer_flat, y_outer_flat, x_outer_curved, y_outer_curved); g_arc_inner_flat->AddPoint(x_inner_flat, y_inner_flat); g_arc_outer_flat->AddPoint(x_outer_flat, y_outer_flat); h_f2c_res->Fill(x_inner - x_inner_curved, y_inner - y_inner_curved); @@ -201,15 +202,12 @@ void CheckSuperAlpideSegmentTrans() for (int iCol{0}; iCol < nCols; ++iCol) { float xRow{0}, zCol{0}; int iiRow{0}, iiCol{0}; - auto v1 = - SuperSegmentations[iLayer].detectorToLocal(iRow, iCol, xRow, zCol); - auto v2 = SuperSegmentations[iLayer].localToDetector(xRow, zCol, iiRow, - iiCol); - // Info("L2D", - // "iRow=%d, iCol=%d --d2l(%s)--> xRow=%f, zCol=%f --l2d(%s)--> " - // "iiRow=%d, iiCol=%d", - // iRow, iCol, v1 ? "good" : "bad", xRow, zCol, v2 ? "good" : - // "bad", iiRow, iiCol); + auto v1 = mMosaixSegmentations[iLayer].detectorToLocal(iRow, iCol, xRow, zCol); + auto v2 = mMosaixSegmentations[iLayer].localToDetector(xRow, zCol, iiRow, iiCol); + Info("L2D", + "iRow=%d, iCol=%d --d2l(%s)--> xRow=%f, zCol=%f --l2d(%s)--> " + "iiRow=%d, iiCol=%d", + iRow, iCol, v1 ? "good" : "bad", xRow, zCol, v2 ? "good" : "bad", iiRow, iiCol); if (!v1 || !v2) { Error("LOOP", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Layer %d", iLayer); return; diff --git a/Detectors/Upgrades/ITS3/macros/test/CheckTileNumbering.C b/Detectors/Upgrades/ITS3/macros/test/CheckTileNumbering.C index 3a01960b1859d..220b1d39ad42b 100644 --- a/Detectors/Upgrades/ITS3/macros/test/CheckTileNumbering.C +++ b/Detectors/Upgrades/ITS3/macros/test/CheckTileNumbering.C @@ -25,7 +25,7 @@ #include "ITSBase/GeometryTGeo.h" #include "ITS3Base/SpecsV2.h" -#include "ITS3Base/SegmentationSuperAlpide.h" +#include "ITS3Base/SegmentationMosaix.h" #include "MathUtils/Cartesian.h" #include "MathUtils/Utils.h" #include "DataFormatsITSMFT/NoiseMap.h" @@ -102,6 +102,8 @@ void CheckTileNumbering(const std::string& inputGeom = "", const std::string& de Int_t colors[NRGBs] = {kWhite, kRed, kGray}; TColor::SetPalette(NRGBs, colors, 1.0); + std::array mMosaixSegmentations{0, 1, 2}; + const float phiOffsetL0 = std::asin(o2::its3::constants::equatorialGap / 2.f / o2::its3::constants::radii[0]); const float phiOffsetL1 = std::asin(o2::its3::constants::equatorialGap / 2.f / o2::its3::constants::radii[1]); const float phiOffsetL2 = std::asin(o2::its3::constants::equatorialGap / 2.f / o2::its3::constants::radii[2]); @@ -142,7 +144,7 @@ void CheckTileNumbering(const std::string& inputGeom = "", const std::string& de for (unsigned int iDet{0}; iDet <= o2::its3::constants::detID::l2IDEnd; ++iDet) { int sensorID = o2::its3::constants::detID::getSensorID(iDet); int layerID = o2::its3::constants::detID::getDetID2Layer(iDet); - o2::its3::SuperSegmentations[layerID].flatToCurved(xFlat, 0., x, y); + mMosaixSegmentations[layerID].flatToCurved(xFlat, 0., x, y); o2::math_utils::Point3D locC{x, y, z}; auto gloC = gman->getMatrixL2G(iDet)(locC); float phi = o2::math_utils::to02Pi(std::atan2(gloC.Y(), gloC.X())); diff --git a/Detectors/Upgrades/ITS3/macros/test/CompareClustersAndDigits.C b/Detectors/Upgrades/ITS3/macros/test/CompareClustersAndDigits.C index f151de72c8ac1..c124481cc6f76 100644 --- a/Detectors/Upgrades/ITS3/macros/test/CompareClustersAndDigits.C +++ b/Detectors/Upgrades/ITS3/macros/test/CompareClustersAndDigits.C @@ -31,7 +31,7 @@ #include "DataFormatsITSMFT/ROFRecord.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsCommonDataFormats/DetectorNameConf.h" -#include "ITS3Base/SegmentationSuperAlpide.h" +#include "ITS3Base/SegmentationMosaix.h" #include "ITS3Base/SpecsV2.h" #include "ITS3Reconstruction/TopologyDictionary.h" #include "ITSBase/GeometryTGeo.h" @@ -86,7 +86,6 @@ void CompareClustersAndDigits(std::string clusfile = "o2clus_it3.root", using namespace o2::base; using o2::itsmft::Hit; - using SuperSegmentation = o2::its3::SegmentationSuperAlpide; using Segmentation = o2::itsmft::SegmentationAlpide; using o2::itsmft::CompClusterExt; using ROFRec = o2::itsmft::ROFRecord; @@ -97,6 +96,8 @@ void CompareClustersAndDigits(std::string clusfile = "o2clus_it3.root", std::vector hitVecPool; std::vector mc2hitVec; + std::array mMosaixSegmentations{0, 1, 2}; + // Geometry o2::base::GeometryManager::loadGeometry(inputGeom); auto gman = o2::its::GeometryTGeo::Instance(); @@ -124,9 +125,9 @@ void CompareClustersAndDigits(std::string clusfile = "o2clus_it3.root", TFile fileC(clusfile.data()); auto* clusTree = dynamic_cast(fileC.Get("o2sim")); std::vector* clusArr = nullptr; - clusTree->SetBranchAddress("IT3ClusterComp", &clusArr); + clusTree->SetBranchAddress("ITSClusterComp", &clusArr); std::vector* patternsPtr = nullptr; - auto pattBranch = clusTree->GetBranch("IT3ClusterPatt"); + auto pattBranch = clusTree->GetBranch("ITSClusterPatt"); if (pattBranch != nullptr) { pattBranch->SetAddress(&patternsPtr); } @@ -144,14 +145,14 @@ void CompareClustersAndDigits(std::string clusfile = "o2clus_it3.root", // ROFrecords std::vector rofRecVec, *rofRecVecP = &rofRecVec; - clusTree->SetBranchAddress("IT3ClustersROF", &rofRecVecP); + clusTree->SetBranchAddress("ITSClustersROF", &rofRecVecP); // Cluster MC labels o2::dataformats::MCTruthContainer* clusLabArr = nullptr; std::vector mc2rofVec, *mc2rofVecP = &mc2rofVec; - if ((hitTree != nullptr) && (clusTree->GetBranch("IT3ClusterMCTruth") != nullptr)) { - clusTree->SetBranchAddress("IT3ClusterMCTruth", &clusLabArr); - clusTree->SetBranchAddress("IT3ClustersMC2ROF", &mc2rofVecP); + if ((hitTree != nullptr) && (clusTree->GetBranch("ITSClusterMCTruth") != nullptr)) { + clusTree->SetBranchAddress("ITSClusterMCTruth", &clusLabArr); + clusTree->SetBranchAddress("ITSClustersMC2ROF", &mc2rofVecP); } clusTree->GetEntry(0); @@ -188,7 +189,7 @@ void CompareClustersAndDigits(std::string clusfile = "o2clus_it3.root", std::vector data(nChips); for (int iChip{0}; iChip < nChips; ++iChip) { auto& dat = data[iChip]; - int col{o2::its3::SegmentationSuperAlpide::mNCols}, row{o2::its3::SegmentationSuperAlpide::mNRows}; + int col{o2::its3::SegmentationMosaix::NCols}, row{o2::its3::SegmentationMosaix::NRows}; if (!o2::its3::constants::detID::isDetITS3(iChip)) { col = o2::itsmft::SegmentationAlpide::NCols; row = o2::itsmft::SegmentationAlpide::NRows; @@ -259,7 +260,7 @@ void CompareClustersAndDigits(std::string clusfile = "o2clus_it3.root", const auto pattID = cluster.getPatternID(); const auto isIB = o2::its3::constants::detID::isDetITS3(chipID); const auto layer = gman->getLayer(chipID); - if (pattID == o2::itsmft::CompCluster::InvalidPatternID || dict.isGroup(pattID)) { + if (pattID == o2::itsmft::CompCluster::InvalidPatternID || dict.isGroup(pattID, isIB)) { continue; } const auto& lab = (clusLabArr->getLabels(clEntry))[0]; @@ -282,9 +283,9 @@ void CompareClustersAndDigits(std::string clusfile = "o2clus_it3.root", o2::math_utils::Point3D locHMiddle; if (isIB) { float xFlat{0.}, yFlat{0.}; - o2::its3::SuperSegmentations[layer].curvedToFlat(locHEnd.X(), locHEnd.Y(), xFlat, yFlat); + mMosaixSegmentations[layer].curvedToFlat(locHEnd.X(), locHEnd.Y(), xFlat, yFlat); locHEnd.SetXYZ(xFlat, yFlat, locHEnd.Z()); - o2::its3::SuperSegmentations[layer].curvedToFlat(locHStart.X(), locHStart.Y(), xFlat, yFlat); + mMosaixSegmentations[layer].curvedToFlat(locHStart.X(), locHStart.Y(), xFlat, yFlat); locHStart.SetXYZ(xFlat, yFlat, locHStart.Z()); } locHMiddle.SetXYZ(0.5f * (locHEnd.X() + locHStart.X()), 0.5f * (locHEnd.Y() + locHStart.Y()), 0.5f * (locHEnd.Z() + locHStart.Z())); @@ -292,10 +293,10 @@ void CompareClustersAndDigits(std::string clusfile = "o2clus_it3.root", int rowHS, colHS, rowHM, colHM, rowHE, colHE, colC, rowC; bool v1, v2, v3, v4; if (isIB) { - v1 = o2::its3::SuperSegmentations[layer].localToDetector(locHStart.X(), locHStart.Z(), rowHS, colHS); - v2 = o2::its3::SuperSegmentations[layer].localToDetector(locHMiddle.X(), locHMiddle.Z(), rowHM, colHM); - v3 = o2::its3::SuperSegmentations[layer].localToDetector(locHEnd.X(), locHEnd.Z(), rowHE, colHE); - v4 = o2::its3::SuperSegmentations[layer].localToDetector(locC.X(), locC.Z(), rowC, colC); + v1 = mMosaixSegmentations[layer].localToDetector(locHStart.X(), locHStart.Z(), rowHS, colHS); + v2 = mMosaixSegmentations[layer].localToDetector(locHMiddle.X(), locHMiddle.Z(), rowHM, colHM); + v3 = mMosaixSegmentations[layer].localToDetector(locHEnd.X(), locHEnd.Z(), rowHE, colHE); + v4 = mMosaixSegmentations[layer].localToDetector(locC.X(), locC.Z(), rowC, colC); } else { v1 = o2::itsmft::SegmentationAlpide::localToDetector(locHStart.X(), locHStart.Z(), rowHS, colHS); v2 = o2::itsmft::SegmentationAlpide::localToDetector(locHMiddle.X(), locHMiddle.Z(), rowHM, colHM); @@ -315,7 +316,7 @@ void CompareClustersAndDigits(std::string clusfile = "o2clus_it3.root", data[chipID].cog->AddPoint(colC, rowC); constexpr float delta = 1e-2; - const auto& patt = dict.getPattern(cluster.getPatternID()); + const auto& patt = dict.getPattern(cluster.getPatternID(), isIB); auto box = new TBox( cluster.getCol() - delta - 0.5, cluster.getRow() - delta - 0.5, @@ -338,8 +339,8 @@ void CompareClustersAndDigits(std::string clusfile = "o2clus_it3.root", } auto& dat = data[iChip]; gFile->cd(); - /* auto path = gman->getMatrixPath(iChip); */ - TString path; // TODO wrong use above + auto path = gman->getMatrixPath(iChip); + /*TString path; // TODO wrong use above*/ const std::string cpath{path.Data() + 39, path.Data() + path.Length()}; const std::filesystem::path p{cpath}; if (oFile->mkdir(p.parent_path().c_str(), "", true) == nullptr) { diff --git a/Detectors/Upgrades/ITS3/macros/test/CompareClustersAndDigitsOnChip.C b/Detectors/Upgrades/ITS3/macros/test/CompareClustersAndDigitsOnChip.C new file mode 100644 index 0000000000000..310be8c5858ef --- /dev/null +++ b/Detectors/Upgrades/ITS3/macros/test/CompareClustersAndDigitsOnChip.C @@ -0,0 +1,579 @@ +// 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 CompareClustersAndDigitsOnChip.C +/// \brief Macro to compare ITS3 clusters and digits on a pixel array, + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#define ENABLE_UPGRADES +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITSMFT/Digit.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DetectorsCommonDataFormats/DetectorNameConf.h" +#include "ITS3Base/SegmentationMosaix.h" +#include "ITS3Base/SpecsV2.h" +#include "ITS3Reconstruction/TopologyDictionary.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITSMFT/ClusterTopology.h" +#include "ITSBase/GeometryTGeo.h" +#include "ITSMFTBase/SegmentationAlpide.h" +#include "ITSMFTSimulation/Hit.h" +#include "MathUtils/Cartesian.h" +#include "MathUtils/Utils.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" +#include "SimulationDataFormat/IOMCTruthContainerView.h" + +struct Data { + TH2F* pixelArray; + TGraph* hitS; + TGraph* hitM; + TGraph* hitE; + TGraph* clusS; + TGraph* cog; + TLegend* leg; + std::vector* vClusBox; + void clear() + { + delete pixelArray; + delete hitS; + delete hitM; + delete hitE; + delete clusS; + delete cog; + delete leg; + for (auto& b : *vClusBox) { + delete b; + } + delete vClusBox; + } +}; + +void CompareClustersAndDigitsOnChip(std::string clusfile = "o2clus_its.root", + std::string digifile = "it3digits.root", + std::string dictfile = "", + std::string hitfile = "o2sim_HitsIT3.root", + std::string inputGeom = "o2sim_geometry.root", + bool batch = true) +{ + TH1::AddDirectory(kFALSE); + gROOT->SetBatch(batch); + gStyle->SetPalette(kRainBow); + gStyle->SetOptStat(0); + + using namespace o2::base; + using namespace o2::its; + using o2::itsmft::Hit; + using Segmentation = o2::itsmft::SegmentationAlpide; + using o2::itsmft::ClusterTopology; + using o2::itsmft::CompClusterExt; + using ROFRec = o2::itsmft::ROFRecord; + using MC2ROF = o2::itsmft::MC2ROFRecord; + using HitVec = std::vector; + using MC2HITS_map = std::unordered_map; // maps (track_ID<<16 + chip_ID) to entry in the hit vector + std::vector hitVecPool; + std::vector mc2hitVec; + + std::array mMosaixSegmentations{0, 1, 2}; + + // Geometry + o2::base::GeometryManager::loadGeometry(inputGeom); + auto gman = o2::its::GeometryTGeo::Instance(); + gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, + o2::math_utils::TransformType::T2GRot, + o2::math_utils::TransformType::L2G)); // request cached transforms + const int nChips = gman->getNumberOfChips(); + + LOGP(info, "Total number of chips is {} in ITS3 (IB and OB)", nChips); + + // Create all plots + LOGP(info, "Selecting chips to be visualised"); + std::set selectedChips; + std::map> chipGroups; + + for (int chipID{0}; chipID < nChips; ++chipID) { + TString tpath = gman->getMatrixPath(chipID); + std::string path = tpath.Data(); + + std::vector tokens; + std::istringstream iss(path); + std::string token; + while (std::getline(iss, token, '/')) { + if (!token.empty()) { + tokens.push_back(token); + } + } + + std::string segmentName, staveName, carbonFormName; + for (const auto& t : tokens) { + if (t.find("ITS3Segment") != std::string::npos) + segmentName = t; + if (t.find("ITSUStave") != std::string::npos) + staveName = t; + if (t.find("ITS3CarbonForm") != std::string::npos) + carbonFormName = t; + } + + std::string groupKey; + if (!segmentName.empty()) { + groupKey = segmentName + "_" + carbonFormName; + } else if (!staveName.empty()) { + groupKey = staveName; + } else { + continue; + } + + chipGroups[groupKey].push_back(chipID); + } + + LOGP(info, "From each IB Segment or OB Stave, 10 chipIDs are uniformly selected"); + LOGP(info, "Selected chipID: "); + for (auto& [groupName, ids] : chipGroups) { + std::vector sampled; + if (ids.size() <= 10) { + for (auto id : ids) { + selectedChips.insert(id); + sampled.push_back(id); + } + } else { + for (int i{0}; i < 10; ++i) { + int idx = i * (ids.size() - 1) / 9; // 9 intervals for 10 points + int id = ids[idx]; + if (selectedChips.insert(id).second) { + sampled.push_back(id); + } + } + } + + std::ostringstream oss; + std::string topOrBot = "N/A"; + std::smatch match; + std::regex rgxSegment(R"(Segment(\d+)_(\d+)_ITS3CarbonForm\d+_(\d+))"); + std::regex rgxStave(R"(Stave(\d+)_(\d+))"); + if (std::regex_search(groupName, match, rgxSegment)) { + int layer = std::stoi(match[1]); + int segment = std::stoi(match[2]); + int carbonForm = std::stoi(match[3]); + topOrBot = (carbonForm == 0 ? "TOP" : "BOT"); + oss << topOrBot << " segment " << segment << " at layer " << layer << ": "; + } else if (std::regex_search(groupName, match, rgxStave)) { + int layer = std::stoi(match[1]); + int stave = std::stoi(match[2]); + oss << "Stave " << stave << " at layer " << layer << ": "; + } else { + LOGP(error, "Cannot select the correct chipID in OB or IB"); + return; + } + for (auto id : sampled) { + oss << id << " "; + } + LOG(info) << oss.str(); + } + LOGP(info, "{} selected chips will be visualized and analyzed.", chipGroups.size()); + + // Hits + TFile fileH(hitfile.data()); + auto* hitTree = dynamic_cast(fileH.Get("o2sim")); + std::vector* hitArray = nullptr; + hitTree->SetBranchAddress("IT3Hit", &hitArray); + mc2hitVec.resize(hitTree->GetEntries()); + hitVecPool.resize(hitTree->GetEntries(), nullptr); + + // Digits + TFile* digFile = TFile::Open(digifile.data()); + TTree* digTree = (TTree*)digFile->Get("o2sim"); + std::vector* digArr = nullptr; + digTree->SetBranchAddress("IT3Digit", &digArr); + o2::dataformats::IOMCTruthContainerView* plabels = nullptr; + digTree->SetBranchAddress("IT3DigitMCTruth", &plabels); + + // Clusters + TFile fileC(clusfile.data()); + auto* clusTree = dynamic_cast(fileC.Get("o2sim")); + std::vector* clusArr = nullptr; + clusTree->SetBranchAddress("ITSClusterComp", &clusArr); + std::vector* patternsPtr = nullptr; + auto pattBranch = clusTree->GetBranch("ITSClusterPatt"); + if (pattBranch != nullptr) { + pattBranch->SetAddress(&patternsPtr); + } + + // Topology dictionary + o2::its3::TopologyDictionary dict; + bool hasAvailableDict = false; + if (!dictfile.empty()) { + std::ifstream file(dictfile.c_str()); + if (file.good()) { + LOGP(info, "Running with external topology dictionary: {}", dictfile); + dict.readFromFile(dictfile); + LOGP(info, "The IB dictionary size is {}, and the OB dictionary size is {}", dict.getSize(true), dict.getSize(false)); + hasAvailableDict = dict.getSize(true) != 0 && dict.getSize(false) != 0; + if (hasAvailableDict) { + LOGP(info, "Dictionaries is vaild."); + } else { + LOGP(info, "Dictionaries is NOT vaild!"); + } + } else { + LOGP(info, "Cannot open dictionary file: {}. Running without external dictionary!", dictfile); + dictfile = ""; + } + } else { + LOGP(info, "Running without external topology dictionary!"); + } + + // ROFrecords + std::vector rofRecVec, *rofRecVecP = &rofRecVec; + clusTree->SetBranchAddress("ITSClustersROF", &rofRecVecP); + + // Cluster MC labels + o2::dataformats::MCTruthContainer* clusLabArr = nullptr; + std::vector mc2rofVec, *mc2rofVecP = &mc2rofVec; + if ((hitTree != nullptr) && (clusTree->GetBranch("ITSClusterMCTruth") != nullptr)) { + clusTree->SetBranchAddress("ITSClusterMCTruth", &clusLabArr); + clusTree->SetBranchAddress("ITSClustersMC2ROF", &mc2rofVecP); + } + + clusTree->GetEntry(0); + unsigned int nROFRec = (int)rofRecVec.size(); + std::vector mcEvMin(nROFRec, hitTree->GetEntries()); + std::vector mcEvMax(nROFRec, -1); + + // Build min and max MC events used by each ROF + for (int imc = mc2rofVec.size(); imc--;) { + const auto& mc2rof = mc2rofVec[imc]; + if (mc2rof.rofRecordID < 0) { + continue; // this MC event did not contribute to any ROF + } + for (unsigned int irfd = mc2rof.maxROF - mc2rof.minROF + 1; irfd--;) { + unsigned int irof = mc2rof.rofRecordID + irfd; + if (irof >= nROFRec) { + LOGP(error, "ROF = {} from MC2ROF record is >= N ROFs = {}", irof, nROFRec); + } + if (mcEvMin[irof] > imc) { + mcEvMin[irof] = imc; + } + if (mcEvMax[irof] < imc) { + mcEvMax[irof] = imc; + } + } + } + + // Create all plots + LOGP(info, "Creating plots"); + std::unordered_map data; + auto initData = [&](int chipID, Data& dat) { + if (dat.pixelArray) + return; + + int nCol{0}, nRow{0}; + float lengthPixArr{0}, widthPixArr{0}; + bool isIB = o2::its3::constants::detID::isDetITS3(chipID); + int layer = gman->getLayer(chipID); + if (isIB) { + nCol = o2::its3::SegmentationMosaix::NCols; + nRow = o2::its3::SegmentationMosaix::NRows; + lengthPixArr = o2::its3::constants::pixelarray::pixels::mosaix::pitchZ * nCol; + widthPixArr = o2::its3::constants::pixelarray::pixels::mosaix::pitchX * nRow; + } else { + nCol = o2::itsmft::SegmentationAlpide::NCols; + nRow = o2::itsmft::SegmentationAlpide::NRows; + lengthPixArr = o2::itsmft::SegmentationAlpide::PitchCol * nCol; + widthPixArr = o2::itsmft::SegmentationAlpide::PitchRow * nRow; + } + + dat.pixelArray = new TH2F(Form("histSensor_%d", chipID), Form("SensorID=%d;z(cm);x(cm)", chipID), + nCol, -0.5 * lengthPixArr, 0.5 * lengthPixArr, + nRow, -0.5 * widthPixArr, 0.5 * widthPixArr); + dat.hitS = new TGraph(); + dat.hitS->SetMarkerStyle(kFullTriangleDown); + dat.hitS->SetMarkerColor(kGreen); + dat.hitM = new TGraph(); + dat.hitM->SetMarkerStyle(kFullCircle); + dat.hitM->SetMarkerColor(kGreen + 3); + dat.hitE = new TGraph(); + dat.hitE->SetMarkerStyle(kFullTriangleUp); + dat.hitE->SetMarkerColor(kGreen + 5); + dat.clusS = new TGraph(); + dat.clusS->SetMarkerStyle(kFullSquare); + dat.clusS->SetMarkerColor(kBlue); + dat.cog = new TGraph(); + dat.cog->SetMarkerStyle(kFullDiamond); + dat.cog->SetMarkerColor(kRed); + dat.leg = new TLegend(0.7, 0.7, 0.92, 0.92); + dat.leg->AddEntry(dat.hitS, "Hit Start"); + dat.leg->AddEntry(dat.hitM, "Hit Middle"); + dat.leg->AddEntry(dat.hitE, "Hit End"); + dat.leg->AddEntry(dat.clusS, "Cluster Start"); + dat.leg->AddEntry(dat.cog, "Cluster COG"); + dat.vClusBox = new std::vector; + }; + + LOGP(info, "Filling digits"); + for (int iDigit{0}; digTree->LoadTree(iDigit) >= 0; ++iDigit) { + digTree->GetEntry(iDigit); + for (const auto& digit : *digArr) { + const auto chipID = digit.getChipIndex(); + if (!selectedChips.count(chipID)) + continue; + const auto layer = gman->getLayer(chipID); + bool isIB = layer < 3; + float locDigiX{0}, locDigiZ{0}; + if (isIB) { + mMosaixSegmentations[layer].detectorToLocal(digit.getRow(), digit.getColumn(), locDigiX, locDigiZ); + } else { + o2::itsmft::SegmentationAlpide::detectorToLocal(digit.getRow(), digit.getColumn(), locDigiX, locDigiZ); + } + auto& dat = data[chipID]; + initData(chipID, dat); + data[chipID].pixelArray->Fill(locDigiZ, locDigiX); + } + } + + LOGP(info, "Building min and max MC events used by each ROF, total ROFs {}", nROFRec); + auto pattIt = patternsPtr->cbegin(); + bool isAllPattIDInvaild{true}; + for (unsigned int irof{0}; irof < nROFRec; irof++) { + const auto& rofRec = rofRecVec[irof]; + // >> read and map MC events contributing to this ROF + for (int im = mcEvMin[irof]; im <= mcEvMax[irof]; im++) { + if (hitVecPool[im] == nullptr) { + hitTree->SetBranchAddress("IT3Hit", &hitVecPool[im]); + hitTree->GetEntry(im); + auto& mc2hit = mc2hitVec[im]; + const auto* hitArray = hitVecPool[im]; + for (int ih = hitArray->size(); ih--;) { + const auto& hit = (*hitArray)[ih]; + uint64_t key = (uint64_t(hit.GetTrackID()) << 32) + hit.GetDetectorID(); + mc2hit.emplace(key, ih); + } + } + } + + // Clusters in this ROF + for (int icl{0}; icl < rofRec.getNEntries(); icl++) { + int clEntry = rofRec.getFirstEntry() + icl; // entry of icl-th cluster of this ROF in the vector of clusters + const auto& cluster = (*clusArr)[clEntry]; + const auto chipID = cluster.getSensorID(); + if (!selectedChips.count(chipID)) { + // Even if not selected, advance pattIt if patternID is InvalidPatternID + if (cluster.getPatternID() == o2::itsmft::CompCluster::InvalidPatternID) { + o2::itsmft::ClusterPattern::skipPattern(pattIt); + } + continue; + } + const auto pattID = cluster.getPatternID(); + const bool isIB = o2::its3::constants::detID::isDetITS3(chipID); + const auto layer = gman->getLayer(chipID); + auto& dat = data[chipID]; + initData(chipID, dat); + o2::itsmft::ClusterPattern pattern; + // Pattern extraction + if (cluster.getPatternID() != o2::itsmft::CompCluster::InvalidPatternID) { + isAllPattIDInvaild = false; + if (!hasAvailableDict) { + LOGP(error, "Encountered pattern ID {}, which is not equal to the invalid pattern ID {}", cluster.getPatternID(), o2::itsmft::CompCluster::InvalidPatternID); + LOGP(error, "Clusters have already been generated with a dictionary which was not provided properly!"); + return; + } + if (dict.isGroup(cluster.getPatternID(), isIB)) { + pattern.acquirePattern(pattIt); + } else { + pattern = dict.getPattern(cluster.getPatternID(), isIB); + } + } else { + pattern.acquirePattern(pattIt); + } + + // Hits + const auto& lab = (clusLabArr->getLabels(clEntry))[0]; + if (!lab.isValid()) + continue; + const int trID = lab.getTrackID(); + const auto& mc2hit = mc2hitVec[lab.getEventID()]; + const auto* hitArray = hitVecPool[lab.getEventID()]; + uint64_t key = (uint64_t(trID) << 32) + chipID; + auto hitEntry = mc2hit.find(key); + if (hitEntry == mc2hit.end()) + continue; + o2::math_utils::Point3D locHMiddle; + const auto& hit = (*hitArray)[hitEntry->second]; + auto locHEnd = gman->getMatrixL2G(chipID) ^ (hit.GetPos()); + auto locHStart = gman->getMatrixL2G(chipID) ^ (hit.GetPosStart()); + if (isIB) { + float xFlat{0.}, yFlat{0.}; + mMosaixSegmentations[layer].curvedToFlat(locHEnd.X(), locHEnd.Y(), xFlat, yFlat); + locHEnd.SetXYZ(xFlat, yFlat, locHEnd.Z()); + mMosaixSegmentations[layer].curvedToFlat(locHStart.X(), locHStart.Y(), xFlat, yFlat); + locHStart.SetXYZ(xFlat, yFlat, locHStart.Z()); + } + locHMiddle.SetXYZ(0.5f * (locHEnd.X() + locHStart.X()), + 0.5f * (locHEnd.Y() + locHStart.Y()), + 0.5f * (locHEnd.Z() + locHStart.Z())); + data[chipID].hitS->AddPoint(locHStart.Z(), locHStart.X()); + data[chipID].hitM->AddPoint(locHMiddle.Z(), locHMiddle.X()); + data[chipID].hitE->AddPoint(locHEnd.Z(), locHEnd.X()); + + // Cluster Start point + float locCluX{0}, locCluZ{0}; + if (isIB) { + mMosaixSegmentations[layer].detectorToLocal(cluster.getRow(), cluster.getCol(), locCluX, locCluZ); + } else { + o2::itsmft::SegmentationAlpide::detectorToLocal(cluster.getRow(), cluster.getCol(), locCluX, locCluZ); + } + data[chipID].clusS->AddPoint(locCluZ, locCluX); + + // COG + o2::math_utils::Point3D locCOG; + // Cluster COG using dictionary (if available) + if (hasAvailableDict && (pattID != o2::itsmft::CompCluster::InvalidPatternID && !dict.isGroup(pattID, isIB))) { + locCOG = dict.getClusterCoordinates(cluster); + } else { + if (isIB) { + locCOG = o2::its3::TopologyDictionary::getClusterCoordinates(cluster, pattern, false); + } else { + locCOG = o2::itsmft::TopologyDictionary::getClusterCoordinates(cluster, pattern, false); + } + } + if (isIB) { + float flatX{0}, flatY{0}; + mMosaixSegmentations[layer].curvedToFlat(locCOG.X(), locCOG.Y(), flatX, flatY); + locCOG.SetCoordinates(flatX, flatY, locCOG.Z()); + } + data[chipID].cog->AddPoint(locCOG.Z(), locCOG.X()); + + // Cluster Box using dictionary if available, otherwise use raw pattern + float lowLeftX{0}, lowLeftZ{0}, topRightX{0}, topRightZ{0}; + // Use dictionary-based cluster box + if (isIB) { + mMosaixSegmentations[layer].detectorToLocal(cluster.getRow(), cluster.getCol(), lowLeftX, lowLeftZ); + mMosaixSegmentations[layer].detectorToLocal(cluster.getRow() + pattern.getRowSpan() - 1, + cluster.getCol() + pattern.getColumnSpan() - 1, + topRightX, topRightZ); + lowLeftX += 0.5 * o2::its3::constants::pixelarray::pixels::mosaix::pitchX; + lowLeftZ -= 0.5 * o2::its3::constants::pixelarray::pixels::mosaix::pitchZ; + topRightX -= 0.5 * o2::its3::constants::pixelarray::pixels::mosaix::pitchX; + topRightZ += 0.5 * o2::its3::constants::pixelarray::pixels::mosaix::pitchZ; + } else { + o2::itsmft::SegmentationAlpide::detectorToLocal(cluster.getRow(), cluster.getCol(), lowLeftX, lowLeftZ); + o2::itsmft::SegmentationAlpide::detectorToLocal(cluster.getRow() + pattern.getRowSpan() - 1, + cluster.getCol() + pattern.getColumnSpan() - 1, + topRightX, topRightZ); + lowLeftX += 0.5 * o2::itsmft::SegmentationAlpide::PitchRow; + lowLeftZ -= 0.5 * o2::itsmft::SegmentationAlpide::PitchCol; + topRightX -= 0.5 * o2::itsmft::SegmentationAlpide::PitchRow; + topRightZ += 0.5 * o2::itsmft::SegmentationAlpide::PitchCol; + } + auto clusBox = new TBox(lowLeftZ, lowLeftX, topRightZ, topRightX); + clusBox->SetFillColorAlpha(0, 0); + clusBox->SetFillStyle(0); + clusBox->SetLineWidth(4); + clusBox->SetLineColor(kBlack); + data[chipID].vClusBox->push_back(clusBox); + } + } + + if (isAllPattIDInvaild) { + LOGP(info, "Verified input cluster file was generated w/o topology dictionary"); + if (!dictfile.empty()) { + LOGP(error, "Non-dictionary cluster file processed by external dictionary! Please adjust input."); + return; + } + } + + LOGP(info, "Writing to root file"); + double x1, y1, x2, y2; + auto oFileOut = TFile::Open("CompareClustersAndDigitsOnChip.root", "RECREATE"); + oFileOut->cd(); + for (int chipID{0}; chipID < nChips; chipID++) { + if (!selectedChips.count(chipID)) + continue; + auto& dat = data[chipID]; + TString tpath = gman->getMatrixPath(chipID); + const std::string cpath{tpath.Data() + 39, tpath.Data() + tpath.Length()}; + const std::filesystem::path p{cpath}; + std::string nestedDir = p.parent_path().string(); + TDirectory* currentDir = oFileOut; + std::istringstream iss(nestedDir); + std::string token; + while (std::getline(iss, token, '/')) { + if (token.empty()) + continue; + TDirectory* nextDir = currentDir->GetDirectory(token.c_str()); + if (!nextDir) { + nextDir = currentDir->mkdir(token.c_str()); + } + if (!nextDir) { + LOGP(error, "Cannot create subdirectory: %s", token.c_str()); + break; + } + currentDir = nextDir; + currentDir->cd(); + } + if (!currentDir) { + LOGP(error, "Failed to create nested directory for chip %d", chipID); + continue; + } + + auto canv = new TCanvas(Form("%s_%d", p.filename().c_str(), chipID)); + canv->SetTitle(Form("%s_%d", p.filename().c_str(), chipID)); + canv->cd(); + gPad->SetGrid(1, 1); + dat.pixelArray->Draw("colz"); + dat.hitS->Draw("p;same"); + dat.hitM->Draw("p;same"); + dat.hitE->Draw("p;same"); + auto arr = new TArrow(); + arr->SetArrowSize(0.01); + for (int i{0}; i < dat.hitS->GetN(); ++i) { + dat.hitS->GetPoint(i, x1, y1); + dat.hitE->GetPoint(i, x2, y2); + arr->DrawArrow(x1, y1, x2, y2); + } + dat.clusS->Draw("p;same"); + if (dat.cog->GetN() != 0) + dat.cog->Draw("p;same"); + for (const auto& clusBox : *dat.vClusBox) { + clusBox->Draw(); + } + dat.leg->Draw(); + canv->SetEditable(false); + + currentDir->WriteTObject(canv, canv->GetName()); + dat.clear(); + delete canv; + delete arr; + printf("\rWriting chip %05d", chipID); + } + printf("\n"); + oFileOut->Write(); + oFileOut->Close(); + LOGP(info, "Finished writing selected chip visualizations."); +} \ No newline at end of file diff --git a/Detectors/Upgrades/ITS3/macros/test/CorrTracksClusters.C b/Detectors/Upgrades/ITS3/macros/test/CorrTracksClusters.C new file mode 100644 index 0000000000000..634d761366920 --- /dev/null +++ b/Detectors/Upgrades/ITS3/macros/test/CorrTracksClusters.C @@ -0,0 +1,638 @@ +// 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 "TEfficiency.h" +#include +#include +#include + +#include "ITSMFTSimulation/Hit.h" +#include "DataFormatsITS/TrackITS.h" +#include "DetectorsBase/Propagator.h" +#include "Field/MagneticField.h" +#include "ITSBase/GeometryTGeo.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCEventHeader.h" +#include "SimulationDataFormat/MCTrack.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "SimulationDataFormat/MCTruthContainer.h" +#include "SimulationDataFormat/TrackReference.h" +#include "ITS3Reconstruction/TopologyDictionary.h" +#include "ITSMFTBase/SegmentationAlpide.h" +#include "ITS3Base/SegmentationMosaix.h" + +#include +#include +#include +#include +#endif + +using namespace std; +using namespace o2::itsmft; +using namespace o2::its; +using SegmentationIB = o2::its3::SegmentationMosaix; +using SegmentationOB = o2::itsmft::SegmentationAlpide; +static constexpr int kNLayer = 7; +static constexpr int INVALID_INT = -99; +static constexpr float INVALID_FLOAT = -99.f; + +//______________________________________________________________________________ +// ParticleInfo structure +struct ParticleInfo { + int event{}; + int pdg{}; + float pt{}; + float recpt{}; + float eta{}; + float phi{}; + float pvx{}; + float pvy{}; + float pvz{}; + float dcaxy{}; + float dcaz{}; + int mother{}; + int first{}; + unsigned short clusters = 0u; + unsigned char isReco = 0u; + unsigned char isFake = 0u; + bool isPrimary = false; + unsigned char storedStatus = 2; /// not stored = 2, fake = 1, good = 0 + std::array clusterSize; + std::array clusterPattern; + std::array clusterLocX; + std::array clusterLocZ; + std::array hitLocX; + std::array hitLocY; + std::array hitLocZ; + o2::its::TrackITS track; + ParticleInfo() + { + clusterSize.fill(INVALID_INT); + clusterPattern.fill(INVALID_INT); + clusterLocX.fill(INVALID_FLOAT); + clusterLocZ.fill(INVALID_FLOAT); + hitLocX.fill(INVALID_FLOAT); + hitLocY.fill(INVALID_FLOAT); + hitLocZ.fill(INVALID_FLOAT); + } +}; + +//______________________________________________________________________________ +// Convert curved local coordinates to flat coordinates +void CurvedLocalToFlat(o2::math_utils::Point3D& point, const SegmentationIB& seg) +{ + float xFlat = 0.f, yFlat = 0.f; + seg.curvedToFlat(point.X(), point.Y(), xFlat, yFlat); + point.SetXYZ(xFlat, yFlat, point.Z()); +} + +//______________________________________________________________________________ +// Resolve pattern from patternID and iterator +bool resolvePattern(const o2::itsmft::CompClusterExt& cluster, + decltype(std::declval>().cbegin())& pattIt, + const o2::its3::TopologyDictionary& dict, + bool isIB, + o2::itsmft::ClusterPattern& pattOut) +{ + auto pattID = cluster.getPatternID(); + if (pattID != o2::itsmft::CompCluster::InvalidPatternID) { + if (!dict.getSize(true) && !dict.getSize(false)) { + LOGP(error, "Encountered non-invalid pattern ID {} but dictionary is missing!", pattID); + return false; + } + if (dict.isGroup(pattID, isIB)) { + pattOut.acquirePattern(pattIt); + } else { + pattOut = dict.getPattern(pattID, isIB); + } + } else { + pattOut.acquirePattern(pattIt); + } + return true; +} + +//______________________________________________________________________________ +// Function to analyze reconstructed tracks +void analyzeRecoTracks(TTree* recTree, + const std::vector* recArr, + const std::vector* trkLabArr, + std::vector>& info, + float bz, + ULong_t& unaccounted, + ULong_t& good, + ULong_t& fakes, + ULong_t& total) +{ + unaccounted = good = fakes = total = 0; + for (int frame = 0; frame < recTree->GetEntriesFast(); frame++) { // reco tracks frames + if (recTree->GetEvent(frame) == 0) + continue; + total += trkLabArr->size(); + for (unsigned int iTrack = 0; iTrack < trkLabArr->size(); ++iTrack) { + auto lab = trkLabArr->at(iTrack); + if (!lab.isSet()) { + unaccounted++; + continue; + } + int trackID, evID, srcID; + bool fake; + lab.get(trackID, evID, srcID, fake); + if (evID < 0 || evID >= (int)info.size()) { + unaccounted++; + continue; + } + if (trackID < 0 || trackID >= (int)info[evID].size()) { + unaccounted++; + continue; + } + info[evID][trackID].isReco += !fake; + info[evID][trackID].isFake += fake; + if (recArr->at(iTrack).isBetter(info[evID][trackID].track, 1.e9)) { + info[evID][trackID].storedStatus = fake; + info[evID][trackID].track = recArr->at(iTrack); + float ip[2]{0., 0.}; + info[evID][trackID].track.getImpactParams(info[evID][trackID].pvx, + info[evID][trackID].pvy, + info[evID][trackID].pvz, bz, ip); + info[evID][trackID].dcaxy = ip[0]; + info[evID][trackID].dcaz = ip[1]; + info[evID][trackID].recpt = info[evID][trackID].track.getPt(); + } + fakes += static_cast(fake); + good += static_cast(!fake); + } + } + LOGP(info, "** Some statistics:"); + LOGP(info, "\t- Total number of tracks: {}", total); + LOGP(info, "\t- Total number of tracks not corresponding to particles: {} ({:.2f}%)", unaccounted, unaccounted * 100. / total); + LOGP(info, "\t- Total number of fakes: {} ({:.2f}%)", fakes, fakes * 100. / total); + LOGP(info, "\t- Total number of good: {} ({:.2f}%)", good, good * 100. / total); +} + +//______________________________________________________________________________ +// Read and map hit information from hitTree +void mapHitsForMCEvents(TTree* hitTree, + std::vector*>& hitVecPool, + std::vector>& mc2hitVec, + const std::vector& mcEvMin, + const std::vector& mcEvMax, + size_t nROFRec) +{ + for (unsigned int irof = 0; irof < nROFRec; irof++) { + for (int im = mcEvMin[irof]; im <= mcEvMax[irof]; im++) { + if (!hitVecPool[im]) { + hitTree->SetBranchAddress("IT3Hit", &hitVecPool[im]); + hitTree->GetEntry(im); + auto& mc2hit = mc2hitVec[im]; + const auto* hitArray = hitVecPool[im]; + for (int ih = hitArray->size(); ih--;) { + const auto& hit = (*hitArray)[ih]; + uint64_t key = (uint64_t(hit.GetTrackID()) << 32) + hit.GetDetectorID(); + mc2hit.emplace(key, ih); + } + } + } + } +} + +//______________________________________________________________________________ +// Load geometry and magnetic field information +void loadGeometryAndField(const std::string& magfile, const std::string& inputGeom, float& bz, o2::its::GeometryTGeo*& gman) +{ + o2::base::Propagator::initFieldFromGRP(magfile); + bz = o2::base::Propagator::Instance()->getNominalBz(); + o2::base::GeometryManager::loadGeometry(inputGeom); + gman = o2::its::GeometryTGeo::Instance(); + gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, + o2::math_utils::TransformType::T2GRot, + o2::math_utils::TransformType::L2G)); +} + +//______________________________________________________________________________ +// Load topology dictionary +void loadTopologyDictionary(const std::string& dictfile, o2::its3::TopologyDictionary& dict) +{ + std::ifstream iofile(dictfile); + if (iofile.good()) { + LOG(info) << "Running with dictionary: " << dictfile; + dict.readFromFile(dictfile); + } else { + LOG(info) << "Dictionary file not found: " << dictfile; + } +} + +//______________________________________________________________________________ +// Build ROF +void buildMcEvRangePerROF(const std::vector& mc2rofVec, + size_t nROFRec, + std::vector& mcEvMin, + std::vector& mcEvMax) +{ + for (size_t imc = 0; imc < mc2rofVec.size(); ++imc) { + const auto& mc2rof = mc2rofVec[imc]; + if (mc2rof.rofRecordID < 0) + continue; + for (size_t i = mc2rof.minROF; i <= mc2rof.maxROF; ++i) { + if (i >= nROFRec) + continue; + mcEvMin[i] = std::min(mcEvMin[i], static_cast(imc)); + mcEvMax[i] = std::max(mcEvMax[i], static_cast(imc)); + } + } +} + +//______________________________________________________________________________ +// Load Hits data +void prepareHitAccess(const std::string& hitfile, + TTree*& hitTree, + std::vector*>& hitVecPool, + std::vector>& mc2hitVec) +{ + TFile* fHit = TFile::Open(hitfile.data()); + hitTree = (TTree*)fHit->Get("o2sim"); + mc2hitVec.resize(hitTree->GetEntries()); + hitVecPool.resize(hitTree->GetEntries(), nullptr); +} + +void loadCluster(const std::string& clusfile, + TTree*& clusTree, + std::vector*& clusArr, + o2::dataformats::MCTruthContainer*& clusLabArr, + std::vector& mc2rofVec, + std::vector*& patternsPtr, + std::vector& rofRecVec) +{ + // Open file and let it persist + TFile* fileC = TFile::Open(clusfile.data()); + // Get tree + clusTree = dynamic_cast(fileC->Get("o2sim")); + // Cluster array + clusArr = nullptr; + clusTree->SetBranchAddress("ITSClusterComp", &clusArr); + // MC truth + clusLabArr = nullptr; + clusTree->SetBranchAddress("ITSClusterMCTruth", &clusLabArr); + clusTree->SetBranchAddress("ITSClusterPatt", &patternsPtr); + // ROF records + std::vector* rofRecVecP = &rofRecVec; + clusTree->SetBranchAddress("ITSClustersROF", &rofRecVecP); + // MC2ROF mapping + std::vector* mc2rofVecP = &mc2rofVec; + clusTree->SetBranchAddress("ITSClustersMC2ROF", &mc2rofVecP); + clusTree->GetEntry(0); + // After setting all branch addresses, trigger preload of the first entr +} + +//______________________________________________________________________________ +// Load Reconstructed Tracks data +void loadRecoTracks(const std::string& tracfile, + TTree*& recTree, + std::vector*& recArr, + std::vector*& trkLabArr) +{ + TFile* fTrk = TFile::Open(tracfile.data()); + recTree = (TTree*)fTrk->Get("o2sim"); + recTree->SetBranchAddress("ITSTrack", &recArr); + recTree->SetBranchAddress("ITSTrackMCTruth", &trkLabArr); +} + +//______________________________________________________________________________ +// Load MC Track information +void loadMCTrackInfo(const std::string& kinefile, + std::vector>& info, + std::vector*& mcArr, + o2::dataformats::MCEventHeader*& mcEvent, + TTree*& mcTree) +{ + TFile* kineFile = TFile::Open(kinefile.data()); + mcTree = (TTree*)kineFile->Get("o2sim"); + mcTree->SetBranchStatus("*", 0); + mcTree->SetBranchStatus("MCTrack*", 1); + mcTree->SetBranchStatus("MCEventHeader*", 1); + mcTree->SetBranchAddress("MCTrack", &mcArr); + mcTree->SetBranchAddress("MCEventHeader.", &mcEvent); + + int nev = mcTree->GetEntriesFast(); + info.resize(nev); + for (int n = 0; n < nev; n++) { + mcTree->GetEvent(n); + info[n].resize(mcArr->size()); + for (unsigned int mcI = 0; mcI < mcArr->size(); ++mcI) { + auto part = mcArr->at(mcI); + info[n][mcI].pvx = mcEvent->GetX(); + info[n][mcI].pvy = mcEvent->GetY(); + info[n][mcI].pvz = mcEvent->GetZ(); + info[n][mcI].event = n; + info[n][mcI].pdg = part.GetPdgCode(); + info[n][mcI].pt = part.GetPt(); + info[n][mcI].phi = part.GetPhi(); + info[n][mcI].eta = part.GetEta(); + info[n][mcI].isPrimary = part.isPrimary(); + } + } +} + +//______________________________________________________________________________ +// Main function CorrTracksClusters +void CorrTracksClusters(const std::string& tracfile = "o2trac_its.root", + const std::string& clusfile = "o2clus_its.root", + const std::string& kinefile = "o2sim_Kine.root", + const std::string& magfile = "o2sim_grp.root", + const std::string& hitfile = "o2sim_HitsIT3.root", + const std::string& dictfile = "IT3dictionary.root", + const std::string& inputGeom = "", + bool batch = false) +{ + gROOT->SetBatch(batch); + + // Geo and Field + LOGP(info, "Geo and Field loading"); + float bz{0.f}; + o2::its::GeometryTGeo* gman = nullptr; + loadGeometryAndField(magfile, inputGeom, bz, gman); + LOGP(info, "Finished Geo and Field loading"); + + // MC tracks + LOGP(info, "MC Track Info loading"); + std::vector* mcArr = nullptr; + o2::dataformats::MCEventHeader* mcEvent = nullptr; + TTree* mcTree = nullptr; + std::vector> info; + loadMCTrackInfo(kinefile, info, mcArr, mcEvent, mcTree); + LOGP(info, "Finished MC Track Info loading"); + + // Reconstructed tracks + LOGP(info, "Reco Tracks loading"); + TTree* recTree = nullptr; + std::vector* recArr = nullptr; + std::vector* trkLabArr = nullptr; + loadRecoTracks(tracfile, recTree, recArr, trkLabArr); + LOGP(info, "Finished Reco Tracks loading"); + + // Run analyzeRecoTracks + LOGP(info, "Track analysis (analyzeRecoTracks)"); + ULong_t unaccounted{0}, good{0}, fakes{0}, total{0}; + analyzeRecoTracks(recTree, recArr, trkLabArr, info, bz, unaccounted, good, fakes, total); + LOGP(info, "Finished track analysis (analyzeRecoTracks)"); + + // Topology dictionary + LOGP(info, "Topology Dictionary loading"); + o2::its3::TopologyDictionary dict; + loadTopologyDictionary(dictfile, dict); + LOGP(info, "Finished Topology Dictionary loading"); + + // Clusters + LOGP(info, "Cluster Data loading"); + TTree* clusTree = nullptr; + std::vector* clusArr = nullptr; + o2::dataformats::MCTruthContainer* clusLabArr = nullptr; + std::vector* patternsPtr = nullptr; + std::vector mc2rofVec; + std::vector rofRecVec; + loadCluster(clusfile, clusTree, clusArr, clusLabArr, mc2rofVec, patternsPtr, rofRecVec); + LOGP(info, "Finished Cluster Data loading"); + // clusTree->GetEntry(0); + + // Hits + LOGP(info, "Hits loading"); + TTree* hitTree = nullptr; + std::vector*> hitVecPool; + std::vector> mc2hitVec; + prepareHitAccess(hitfile, hitTree, hitVecPool, mc2hitVec); + LOGP(info, "Finished Hits loading"); + + // Build min and max MC events used by each ROF + LOGP(info, "Building MC event ranges"); + std::vector mcEvMin, mcEvMax; + mcEvMin.assign(rofRecVec.size(), hitTree->GetEntries()); + mcEvMax.assign(rofRecVec.size(), -1); + buildMcEvRangePerROF(mc2rofVec, rofRecVec.size(), mcEvMin, mcEvMax); + LOGP(info, "Initial MC event ranges built"); + unsigned int nROFRec = rofRecVec.size(); + + // Map hits for MC events + LOGP(info, "Map hits for MC events"); + mapHitsForMCEvents(hitTree, hitVecPool, mc2hitVec, mcEvMin, mcEvMax, nROFRec); + LOGP(info, "Mapped hits for MC events"); + + // Run cluster particle matching + auto pattIt = patternsPtr->cbegin(); + for (unsigned int iClus = 0; iClus < clusArr->size(); ++iClus) { + auto lab = (clusLabArr->getLabels(iClus))[0]; + const auto& c = (*clusArr)[iClus]; + // Ensure pattIt is advanced even if cluster is skipped + if (!lab.isValid() || lab.getSourceID() != 0 || !lab.isCorrect()) { + if (c.getPatternID() == CompCluster::InvalidPatternID) { + o2::itsmft::ClusterPattern::skipPattern(pattIt); + } + continue; + } + + int trackID{0}, evID{0}, srcID{0}; + bool fake{false}; + lab.get(trackID, evID, srcID, fake); + if (evID < 0 || static_cast(evID) >= info.size() || trackID < 0 || static_cast(trackID) >= info[evID].size()) { + if (c.getPatternID() == CompCluster::InvalidPatternID) { + o2::itsmft::ClusterPattern::skipPattern(pattIt); + } + continue; + } + UShort_t chipID = c.getSensorID(); + int layer = gman->getLayer(chipID); + bool isIB = layer < 3; + info[evID][trackID].clusters |= 1 << layer; + + o2::math_utils::Point3D clusterPos; + int clusterSize; + auto pattID = c.getPatternID(); + o2::itsmft::ClusterPattern patt; + if (!resolvePattern(c, pattIt, dict, isIB, patt)) { + continue; + } + clusterSize = patt.getNPixels(); + clusterPos = dict.getClusterCoordinates(c, patt, false); + + if (isIB) { + CurvedLocalToFlat(clusterPos, SegmentationIB(layer)); + } + + info[evID][trackID].clusterSize[layer] = clusterSize; + info[evID][trackID].clusterPattern[layer] = pattID; + info[evID][trackID].clusterLocX[layer] = clusterPos.X(); + info[evID][trackID].clusterLocZ[layer] = clusterPos.Z(); + + const auto& mc2hit = mc2hitVec[lab.getEventID()]; + const auto* hitArray = hitVecPool[lab.getEventID()]; + uint64_t key = (uint64_t(trackID) << 32) + c.getSensorID(); + auto hitIt = mc2hit.find(key); + if (hitIt == mc2hit.end()) + continue; + const auto& hit = (*hitArray)[hitIt->second]; + + auto hitLocSta = gman->getMatrixL2G(chipID) ^ hit.GetPosStart(); + auto hitLocEnd = gman->getMatrixL2G(chipID) ^ hit.GetPos(); + + if (isIB) { + CurvedLocalToFlat(hitLocSta, SegmentationIB(layer)); + CurvedLocalToFlat(hitLocEnd, SegmentationIB(layer)); + info[evID][trackID].hitLocX[layer] = 0.5f * (hitLocSta.X() + hitLocEnd.X()); + info[evID][trackID].hitLocY[layer] = 0.5f * (hitLocSta.Y() + hitLocEnd.Y()); + info[evID][trackID].hitLocZ[layer] = 0.5f * (hitLocSta.Z() + hitLocEnd.Z()); + } else { + auto x0 = hitLocSta.X(), dx = hitLocEnd.X() - x0; + auto y0 = hitLocSta.Y(), dy = hitLocEnd.Y() - y0; + auto z0 = hitLocSta.Z(), dz = hitLocEnd.Z() - z0; + auto r = (0.5f * (SegmentationOB::SensorLayerThickness - SegmentationOB::SensorLayerThicknessEff) - y0) / dy; + info[evID][trackID].hitLocX[layer] = x0 + r * dx; + info[evID][trackID].hitLocY[layer] = y0 + r * dy; + info[evID][trackID].hitLocZ[layer] = z0 + r * dz; + } + } + + LOGP(info, "Finished cluster-to-particle matching"); + + // The following part generates statistical histograms and outputs a TTree + int nb = 100; + double xbins[nb + 1], ptcutl = 0.01, ptcuth = 10.; + double a = std::log(ptcuth / ptcutl) / nb; + for (int i = 0; i <= nb; ++i) { + xbins[i] = ptcutl * std::exp(i * a); + } + auto* h_pt_num = new TH1D("h_pt_num", ";#it{p}_{T} (GeV/#it{c});Number of tracks", nb, xbins); + auto* h_pt_den = new TH1D("h_pt_den", ";#it{p}_{T} (GeV/#it{c});Number of generated primary particles", nb, xbins); + auto* h_pt_eff = new TEfficiency("h_pt_eff", "Tracking Efficiency;#it{p}_{T} (GeV/#it{c});Eff.", nb, xbins); + + auto* h_eta_num = new TH1D("h_eta_num", ";#it{#eta};Number of tracks", 60, -3, 3); + auto* h_eta_den = new TH1D("h_eta_den", ";#it{#eta};Number of generated particles", 60, -3, 3); + auto* h_eta_eff = new TEfficiency("h_eta_eff", "Tracking Efficiency;#it{#eta};Eff.", 60, -3, 3); + + auto* h_phi_num = new TH1D("h_phi_num", ";#varphi;Number of tracks", 360, 0., 2 * TMath::Pi()); + auto* h_phi_den = new TH1D("h_phi_den", ";#varphi;Number of generated particles", 360, 0., 2 * TMath::Pi()); + auto* h_phi_eff = new TEfficiency("h_phi_eff", "Tracking Efficiency;#varphi;Eff.", 360, 0., 2 * TMath::Pi()); + + auto* h_pt_fake = new TH1D("h_pt_fake", ";#it{p}_{T} (GeV/#it{c});Number of fake tracks", nb, xbins); + auto* h_pt_multifake = new TH1D("h_pt_multifake", ";#it{p}_{T} (GeV/#it{c});Number of multifake tracks", nb, xbins); + auto* h_pt_clones = new TH1D("h_pt_clones", ";#it{p}_{T} (GeV/#it{c});Number of cloned tracks", nb, xbins); + auto* h_dcaxy_vs_pt = new TH2D("h_dcaxy_vs_pt", ";#it{p}_{T} (GeV/#it{c});DCA_{xy} (#mum)", nb, xbins, 2000, -500., 500.); + auto* h_dcaxy_vs_eta = new TH2D("h_dcaxy_vs_eta", ";#it{#eta};DCA_{xy} (#mum)", 60, -3, 3, 2000, -500., 500.); + auto* h_dcaxy_vs_phi = new TH2D("h_dcaxy_vs_phi", ";#varphi;DCA_{xy} (#mum)", 360, 0., 2 * TMath::Pi(), 2000, -500., 500.); + auto* h_dcaz_vs_pt = new TH2D("h_dcaz_vs_pt", ";#it{p}_{T} (GeV/#it{c});DCA_{z} (#mum)", nb, xbins, 2000, -500., 500.); + auto* h_dcaz_vs_eta = new TH2D("h_dcaz_vs_eta", ";#it{#eta};DCA_{z} (#mum)", 60, -3, 3, 2000, -500., 500.); + auto* h_dcaz_vs_phi = new TH2D("h_dcaz_vs_phi", ";#varphi;DCA_{z} (#mum)", 360, 0., 2 * TMath::Pi(), 2000, -500., 500.); + auto* h_chi2 = new TH2D("h_chi2", ";#it{p}_{T} (GeV/#it{c});#chi^{2};Number of tracks", nb, xbins, 200, 0., 100.); + + for (auto& evInfo : info) { + for (auto& part : evInfo) { + if ((part.clusters & 0x7f) != 0x7f) { + // part.clusters != 0x3f && part.clusters != 0x3f << 1 && + // part.clusters != 0x1f && part.clusters != 0x1f << 1 && part.clusters + // != 0x1f << 2 && part.clusters != 0x0f && part.clusters != 0x0f << 1 + // && part.clusters != 0x0f << 2 && part.clusters != 0x0f << 3) { + continue; + } + if (!part.isPrimary) { + continue; + } + + h_pt_den->Fill(part.pt); + h_eta_den->Fill(part.eta); + h_phi_den->Fill(part.phi); + + if (part.isReco != 0u) { + h_pt_num->Fill(part.pt); + h_eta_num->Fill(part.eta); + h_phi_num->Fill(part.phi); + if (std::abs(part.eta) < 0.5) { + h_dcaxy_vs_pt->Fill(part.pt, part.dcaxy * 10000); + h_dcaz_vs_pt->Fill(part.pt, part.dcaz * 10000); + } + h_dcaz_vs_eta->Fill(part.eta, part.dcaz * 10000); + h_dcaxy_vs_eta->Fill(part.eta, part.dcaxy * 10000); + h_dcaxy_vs_phi->Fill(part.phi, part.dcaxy * 10000); + h_dcaz_vs_phi->Fill(part.phi, part.dcaz * 10000); + + h_chi2->Fill(part.pt, part.track.getChi2()); + + if (part.isReco > 1) { + for (int _i{0}; _i < part.isReco - 1; ++_i) { + h_pt_clones->Fill(part.pt); + } + } + } + if (part.isFake != 0u) { + h_pt_fake->Fill(part.pt); + if (part.isFake > 1) { + for (int _i{0}; _i < part.isFake - 1; ++_i) { + h_pt_multifake->Fill(part.pt); + } + } + } + } + } + + LOGP(info, "Streaming output TTree to file"); + TFile file("CorrTracksClusters.root", "recreate"); + TTree tree("ParticleInfo", "ParticleInfo"); + ParticleInfo pInfo; + tree.Branch("particle", &pInfo); + for (auto& event : info) { + for (auto& part : event) { + int nCl{0}; + for (unsigned int bit{0}; bit < sizeof(pInfo.clusters) * 8; ++bit) { + nCl += bool(part.clusters & (1 << bit)); + } + if (nCl < 3) { + continue; + } + pInfo = part; + tree.Fill(); + } + } + tree.Write(); + h_pt_num->Write(); + h_eta_num->Write(); + h_phi_num->Write(); + h_pt_den->Write(); + h_eta_den->Write(); + h_phi_den->Write(); + h_pt_multifake->Write(); + h_pt_fake->Write(); + h_dcaxy_vs_pt->Write(); + h_dcaz_vs_pt->Write(); + h_dcaxy_vs_eta->Write(); + h_dcaxy_vs_phi->Write(); + h_dcaz_vs_eta->Write(); + h_dcaz_vs_phi->Write(); + h_pt_clones->Write(); + h_chi2->Write(); + + h_pt_eff->SetTotalHistogram(*h_pt_den, ""); + h_pt_eff->SetPassedHistogram(*h_pt_num, ""); + h_pt_eff->SetTitle("Tracking Efficiency;#it{p}_{T} (GeV/#it{c});Eff."); + h_pt_eff->Write(); + + h_phi_eff->SetTotalHistogram(*h_phi_den, ""); + h_phi_eff->SetPassedHistogram(*h_phi_num, ""); + h_phi_eff->SetTitle("Tracking Efficiency;#varphi;Eff."); + h_phi_eff->Write(); + + h_eta_eff->SetTotalHistogram(*h_eta_den, ""); + h_eta_eff->SetPassedHistogram(*h_eta_num, ""); + h_eta_eff->SetTitle("Tracking Efficiency;#it{#eta};Eff."); + h_eta_eff->Write(); + + file.Close(); + LOGP(info, "Finished streaming output TTree to file"); + LOGP(info, "done."); +} diff --git a/Detectors/Upgrades/ITS3/macros/test/CreateDictionariesITS3.C b/Detectors/Upgrades/ITS3/macros/test/CreateDictionariesITS3.C index d8783ba7c8fb9..76d7bf09de77f 100644 --- a/Detectors/Upgrades/ITS3/macros/test/CreateDictionariesITS3.C +++ b/Detectors/Upgrades/ITS3/macros/test/CreateDictionariesITS3.C @@ -34,7 +34,7 @@ #include "DetectorsCommonDataFormats/DetID.h" #include "ITSBase/GeometryTGeo.h" #include "ITSMFTBase/SegmentationAlpide.h" -#include "ITS3Base/SegmentationSuperAlpide.h" +#include "ITS3Base/SegmentationMosaix.h" #include "DataFormatsITSMFT/CompCluster.h" #include "DataFormatsITSMFT/ClusterTopology.h" #include "ITS3Reconstruction/TopologyDictionary.h" @@ -52,7 +52,7 @@ #endif -void CreateDictionariesITS3(bool saveDeltas = false, +void CreateDictionariesITS3(bool saveDeltas = true, float probThreshold = 1e-6, std::string clusDictFile = "", std::string clusfile = "o2clus_its.root", @@ -60,14 +60,13 @@ void CreateDictionariesITS3(bool saveDeltas = false, std::string collContextfile = "collisioncontext.root", std::string inputGeom = "", float checkOutliers = 2., // reject outliers (MC dX or dZ exceeds row/col span by a factor above the threshold) - float minPtMC = 0.01) // account only MC hits with pT above threshold + float minPtMC = 0.1) // account only MC hits with pT above threshold { const int QEDSourceID = 99; // Clusters from this MC source correspond to QED electrons using namespace o2::base; using namespace o2::its; - using o2::its3::SegmentationSuperAlpide; using Segmentation = o2::itsmft::SegmentationAlpide; using o2::its3::BuildTopologyDictionary; using o2::itsmft::ClusterTopology; @@ -82,18 +81,20 @@ void CreateDictionariesITS3(bool saveDeltas = false, std::vector hitVecPool; std::vector mc2hitVec; o2::its3::TopologyDictionary clusDictOld; + std::array mMosaixSegmentations{0, 1, 2}; if (!clusDictFile.empty()) { clusDictOld.readFromFile(clusDictFile); - LOGP(info, "Loaded external cluster dictionary with {} entries from {}", clusDictOld.getSize(), clusDictFile); + LOGP(info, "Loaded external cluster dictionary with {} IB/{} OBentries from {}", clusDictOld.getSize(true), clusDictOld.getSize(false), clusDictFile); } - ULong_t cOk{0}, cOutliers{0}, cFailedMC{0}; + ULong_t cOkIB{0}, cOutliersIB{0}, cFailedMCIB{0}; + ULong_t cOkOB{0}, cOutliersOB{0}, cFailedMCOB{0}; TFile* fout = nullptr; TNtuple* nt = nullptr; if (saveDeltas) { fout = TFile::Open("CreateDictionaries.root", "recreate"); - nt = new TNtuple("nt", "hashes ntuple", "hash:dx:dz"); + nt = new TNtuple("nt", "hashes ntuple", "hash:layer:chipID:xhf:zhf:xcf:zcf:dx:dz:outlimDx:outlimDz"); } const o2::steer::DigitizationContext* digContext = nullptr; @@ -233,17 +234,18 @@ void CreateDictionariesITS3(bool saveDeltas = false, const auto& cluster = (*clusArr)[clEntry]; o2::itsmft::ClusterPattern pattern; + bool ib = o2::its3::constants::detID::isDetITS3(cluster.getChipID()); if (cluster.getPatternID() != CompCluster::InvalidPatternID) { - if (clusDictOld.getSize() == 0) { + if (clusDictOld.getSize(ib) == 0) { LOG(error) << "Encountered patternID = " << cluster.getPatternID() << " != " << CompCluster::InvalidPatternID; LOG(error) << "Clusters have already been generated with a dictionary which was not provided"; return; } - if (clusDictOld.isGroup(cluster.getPatternID())) { + if (clusDictOld.isGroup(cluster.getPatternID(), ib)) { pattern.acquirePattern(pattIdx); } else { - pattern = clusDictOld.getPattern(cluster.getPatternID()); + pattern = clusDictOld.getPattern(cluster.getPatternID(), ib); } } else { pattern.acquirePattern(pattIdx); @@ -270,44 +272,49 @@ void CreateDictionariesITS3(bool saveDeltas = false, o2::math_utils::Vector3D xyzLocM; xyzLocM.SetCoordinates(0.5f * (xyzLocE.X() + xyzLocS.X()), 0.5f * (xyzLocE.Y() + xyzLocS.Y()), 0.5f * (xyzLocE.Z() + xyzLocS.Z())); auto locC = o2::its3::TopologyDictionary::getClusterCoordinates(cluster, pattern, false); - bool isIB = o2::its3::constants::detID::isDetITS3(chipID); int layer = gman->getLayer(chipID); - if (isIB) { + if (ib) { float xFlat{0.}, yFlat{0.}; - o2::its3::SuperSegmentations[layer].curvedToFlat(xyzLocM.X(), xyzLocM.Y(), xFlat, yFlat); + mMosaixSegmentations[layer].curvedToFlat(xyzLocM.X(), xyzLocM.Y(), xFlat, yFlat); xyzLocM.SetCoordinates(xFlat, yFlat, xyzLocM.Z()); - o2::its3::SuperSegmentations[layer].curvedToFlat(locC.X(), locC.Y(), xFlat, yFlat); + mMosaixSegmentations[layer].curvedToFlat(locC.X(), locC.Y(), xFlat, yFlat); locC.SetCoordinates(xFlat, yFlat, locC.Z()); } dX = xyzLocM.X() - locC.X(); dZ = xyzLocM.Z() - locC.Z(); - dX /= (isIB) ? o2::its3::SegmentationSuperAlpide::mPitchRow : o2::itsmft::SegmentationAlpide::PitchRow; - dZ /= (isIB) ? o2::its3::SegmentationSuperAlpide::mPitchCol : o2::itsmft::SegmentationAlpide::PitchCol; - if (saveDeltas) { - nt->Fill(topology.getHash(), dX, dZ); - } + dX /= (ib) ? o2::its3::SegmentationMosaix::PitchRow : o2::itsmft::SegmentationAlpide::PitchRow; + dZ /= (ib) ? o2::its3::SegmentationMosaix::PitchCol : o2::itsmft::SegmentationAlpide::PitchCol; + + float outLimitDx{-1}, outLimitDz{-1}; if (checkOutliers > 0.) { - if (bool bX = std::abs(dX) > topology.getRowSpan() * checkOutliers, bZ = std::abs(dZ) > topology.getColumnSpan() * checkOutliers; bX || bZ) { // ignore outlier - ++cOutliers; - LOGP(debug, "Ignored Value dX={} > {} * {} -> {}", dX, topology.getRowSpan(), checkOutliers, bX); - LOGP(debug, "Ignored Value dZ={} > {} * {} -> {}", dZ, topology.getColumnSpan(), checkOutliers, bZ); + outLimitDx = topology.getRowSpan() * checkOutliers; + outLimitDz = topology.getColumnSpan() * checkOutliers; + bool isOutDx = std::abs(dX) > outLimitDx; + bool isOutDz = std::abs(dZ) > outLimitDz; + if (isOutDx || isOutDz) { // ignore outlier + (ib) ? ++cOutliersIB : ++cOutliersOB; + LOGP(debug, "Ignored Value dX={} > {} * {} -> {}", dX, topology.getRowSpan(), checkOutliers, isOutDx); + LOGP(debug, "Ignored Value dZ={} > {} * {} -> {}", dZ, topology.getColumnSpan(), checkOutliers, isOutDz); dX = dZ = BuildTopologyDictionary::IgnoreVal; } else { - ++cOk; + (ib) ? ++cOkIB : ++cOkOB; } } + if (saveDeltas) { + nt->Fill(topology.getHash(), layer, chipID, xyzLocM.X(), xyzLocM.Z(), locC.X(), locC.Z(), dX, dZ, outLimitDx, outLimitDz); + } } } else { /* LOGP(info, " Failed to find MC hit entry for Tr: {} chipID: {}", trID, chipID); */ /* lab.print(); */ - ++cFailedMC; + (ib) ? ++cFailedMCIB : ++cFailedMCOB; } - signalDictionary.accountTopology(topology, dX, dZ); + signalDictionary.accountTopology(topology, ib, dX, dZ); } else { - noiseDictionary.accountTopology(topology, dX, dZ); + noiseDictionary.accountTopology(topology, ib, dX, dZ); } } - completeDictionary.accountTopology(topology, dX, dZ); + completeDictionary.accountTopology(topology, ib, dX, dZ); } // clean MC cache for events which are not needed anymore @@ -323,12 +330,14 @@ void CreateDictionariesITS3(bool saveDeltas = false, } } - LOGP(info, "Clusters: {} okay (failed MCHit2Clus {}); outliers {}", cOk, cFailedMC, cOutliers); + LOGP(info, "IB Clusters: {} okay (failed MCHit2Clus {}); outliers {}", cOkIB, cFailedMCIB, cOutliersIB); + LOGP(info, "OB Clusters: {} okay (failed MCHit2Clus {}); outliers {}", cOkOB, cFailedMCOB, cOutliersOB); auto dID = o2::detectors::DetID::IT3; LOGP(info, "Complete Dictionary:"); - completeDictionary.setThreshold(probThreshold); + completeDictionary.setThreshold(probThreshold, true); + completeDictionary.setThreshold(probThreshold, false); completeDictionary.groupRareTopologies(); completeDictionary.printDictionaryBinary(o2::base::DetectorNameConf::getAlpideClusterDictionaryFileName(dID, "")); completeDictionary.printDictionary(o2::base::DetectorNameConf::getAlpideClusterDictionaryFileName(dID, "", "txt")); @@ -336,24 +345,34 @@ void CreateDictionariesITS3(bool saveDeltas = false, TFile histogramOutput("histograms.root", "recreate"); TCanvas* cComplete = new TCanvas("cComplete", "Distribution of all the topologies"); - cComplete->cd(); - cComplete->SetLogy(); - TH1F* hComplete = completeDictionary.getDictionary().getTopologyDistribution("hComplete"); - hComplete->SetDirectory(nullptr); - hComplete->Draw("hist"); - hComplete->Write(); + cComplete->Divide(2, 1); + cComplete->cd(1); + TH1F* hCompleteIB = completeDictionary.getDictionary().getTopologyDistribution("hCompleteInnerBarrel", true); + hCompleteIB->SetDirectory(nullptr); + hCompleteIB->Draw("hist"); + gPad->SetLogy(); + cComplete->cd(2); + TH1F* hCompleteOB = completeDictionary.getDictionary().getTopologyDistribution("hCompleteOuterBarrel", false); + hCompleteOB->SetDirectory(nullptr); + hCompleteOB->Draw("hist"); + gPad->SetLogy(); + histogramOutput.cd(); + hCompleteIB->Write(); + hCompleteOB->Write(); cComplete->Write(); if (clusLabArr) { LOGP(info, "Noise Dictionary:"); - noiseDictionary.setThreshold(0.0001); + noiseDictionary.setThreshold(0.0001, true); + noiseDictionary.setThreshold(0.0001, false); noiseDictionary.groupRareTopologies(); noiseDictionary.printDictionaryBinary(o2::base::DetectorNameConf::getAlpideClusterDictionaryFileName(dID, "noiseClusTopo")); noiseDictionary.printDictionary(o2::base::DetectorNameConf::getAlpideClusterDictionaryFileName(dID, "noiseClusTopo", "txt")); noiseDictionary.saveDictionaryRoot(o2::base::DetectorNameConf::getAlpideClusterDictionaryFileName(dID, "noiseClusTopo", "root")); LOGP(info, "Signal Dictionary:"); - signalDictionary.setThreshold(0.0001); + signalDictionary.setThreshold(0.0001, true); + signalDictionary.setThreshold(0.0001, false); signalDictionary.groupRareTopologies(); signalDictionary.printDictionaryBinary(o2::base::DetectorNameConf::getAlpideClusterDictionaryFileName(dID, "signal")); signalDictionary.printDictionary(o2::base::DetectorNameConf::getAlpideClusterDictionaryFileName(dID, "signal", "txt")); @@ -361,26 +380,42 @@ void CreateDictionariesITS3(bool saveDeltas = false, LOGP(info, "Plotting Channels"); auto cNoise = new TCanvas("cNoise", "Distribution of noise topologies"); - cNoise->cd(); - cNoise->SetLogy(); - auto hNoise = noiseDictionary.getDictionary().getTopologyDistribution("hNoise"); - hNoise->SetDirectory(nullptr); - hNoise->Draw("hist"); + cNoise->Divide(2, 1); + cNoise->cd(1); + auto hNoiseIB = noiseDictionary.getDictionary().getTopologyDistribution("hNoiseInnerBarrel", true); + hNoiseIB->SetDirectory(nullptr); + hNoiseIB->Draw("hist"); + gPad->SetLogy(); + cNoise->cd(2); + auto hNoiseOB = noiseDictionary.getDictionary().getTopologyDistribution("hNoiseOuterBarrel", false); + hNoiseOB->SetDirectory(nullptr); + hNoiseOB->Draw("hist"); + gPad->SetLogy(); histogramOutput.cd(); - hNoise->Write(); + hNoiseIB->Write(); + hNoiseOB->Write(); cNoise->Write(); + auto cSignal = new TCanvas("cSignal", "cSignal"); - cSignal->cd(); + cSignal->Divide(2, 1); + cSignal->cd(1); + auto hSignalIB = signalDictionary.getDictionary().getTopologyDistribution("hSignalInnerBarrel", true); + hSignalIB->SetDirectory(nullptr); + hSignalIB->Draw("hist"); + gPad->SetLogy(); + cSignal->cd(2); cSignal->SetLogy(); - auto hSignal = signalDictionary.getDictionary().getTopologyDistribution("hSignal"); - hSignal->SetDirectory(nullptr); - hSignal->Draw("hist"); + auto hSignalOB = signalDictionary.getDictionary().getTopologyDistribution("hSignalOuterBarrel", false); + hSignalOB->SetDirectory(nullptr); + hSignalOB->Draw("hist"); + gPad->SetLogy(); histogramOutput.cd(); - hSignal->Write(); + hSignalIB->Write(); + hSignalOB->Write(); cSignal->Write(); - sw.Stop(); - sw.Print(); } + sw.Stop(); + sw.Print(); if (saveDeltas) { fout->cd(); nt->Write(); diff --git a/Detectors/Upgrades/ITS3/macros/test/TestSensorGeometry.C b/Detectors/Upgrades/ITS3/macros/test/TestSensorGeometry.C index 1a0ec73e34f31..4b54bbced2929 100644 --- a/Detectors/Upgrades/ITS3/macros/test/TestSensorGeometry.C +++ b/Detectors/Upgrades/ITS3/macros/test/TestSensorGeometry.C @@ -21,7 +21,7 @@ #include "TList.h" #endif -void TestSensorGeometry(bool checkFull = false) +void TestSensorGeometry(bool draw = false, bool checkFull = false) { gGeoManager = new TGeoManager("simple", "Simple geometry"); TGeoMaterial* matVacuum = new TGeoMaterial("Vacuum", 0, 0, 0); @@ -30,8 +30,7 @@ void TestSensorGeometry(bool checkFull = false) auto top = gGeoManager->MakeBox("TOP", Vacuum, 270., 270., 120.); gGeoManager->SetTopVolume(top); - o2::its3::ITS3Layer layer0{0, top, nullptr, - o2::its3::ITS3Layer::BuildLevel::kLayer, true}; + o2::its3::ITS3Layer layer0{2, top, nullptr, o2::its3::ITS3Layer::BuildLevel::kLayer, true}; // Print available medias TIter next{gGeoManager->GetListOfMedia()}; @@ -42,13 +41,17 @@ void TestSensorGeometry(bool checkFull = false) gGeoManager->CloseGeometry(); gGeoManager->SetVisLevel(99); + if (draw) { + gGeoManager->Draw("ogl"); + } + if (checkFull) { gGeoManager->CheckGeometryFull(); - } - gGeoManager->CheckOverlaps(0.0001); - TIter nextOverlap{gGeoManager->GetListOfOverlaps()}; - while ((obj = (TObject*)nextOverlap())) { - LOGP(info, "Overlap in {}", obj->GetName()); + gGeoManager->CheckOverlaps(0.00001); + TIter nextOverlap{gGeoManager->GetListOfOverlaps()}; + while ((obj = (TObject*)nextOverlap())) { + LOGP(info, "Overlap in {}", obj->GetName()); + } } std::unique_ptr f{TFile::Open("geo.root", "RECREATE")}; diff --git a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/BuildTopologyDictionary.h b/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/BuildTopologyDictionary.h index 7df603bb29fb2..662c58aeb2cd8 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/BuildTopologyDictionary.h +++ b/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/BuildTopologyDictionary.h @@ -24,31 +24,47 @@ namespace o2::its3 class BuildTopologyDictionary { + using TopoInfo = std::unordered_map; + using TopoStat = std::map; + using TopoFreq = std::vector>; + public: static constexpr float IgnoreVal = 999.; - void accountTopology(const itsmft::ClusterTopology& cluster, float dX = IgnoreVal, float dZ = IgnoreVal); - void setNCommon(unsigned int nCommon); // set number of common topologies - void setThreshold(double thr); - void setThresholdCumulative(double cumulative); // Considering the integral + void accountTopology(const itsmft::ClusterTopology& cluster, bool IB, float dX = IgnoreVal, float dZ = IgnoreVal); + void setNCommon(unsigned int nCommon, bool IB); // set number of common topologies + void setThreshold(double thr, bool IB); + void setThresholdCumulative(double cumulative, bool IB); // Considering the integral void groupRareTopologies(); - friend std::ostream& operator<<(std::ostream& os, const BuildTopologyDictionary& BD); void printDictionary(const std::string& fname); void printDictionaryBinary(const std::string& fname); void saveDictionaryRoot(const std::string& fname); - unsigned int getTotClusters() const { return mTotClusters; } - unsigned int getNotInGroups() const { return mNCommonTopologies; } - TopologyDictionary getDictionary() const { return mDictionary; } + [[nodiscard]] unsigned int getTotClusters(bool IB) const { return (IB) ? mTotClustersIB : mTotClustersOB; } + [[nodiscard]] unsigned int getNotInGroups(bool IB) const { return (IB) ? mNCommonTopologiesIB : mNCommonTopologiesOB; } + [[nodiscard]] const TopologyDictionary& getDictionary() const { return mDictionary; } + + friend std::ostream& operator<<(std::ostream& os, const BuildTopologyDictionary& BD); private: - TopologyDictionary mDictionary; ///< Dictionary of topologies - std::map mTopologyMap; //! Temporary map of type - std::vector> mTopologyFrequency; //! , needed to define threshold - unsigned int mTotClusters{0}; - unsigned int mNCommonTopologies{0}; - double mFrequencyThreshold{0.}; - - std::unordered_map mMapInfo; + void accountTopologyImpl(const itsmft::ClusterTopology& cluster, TopoInfo& tinfo, TopoStat& tstat, unsigned int& ntot, float sigmaX, float sigmaZ, float dX, float dZ); + void setNCommonImpl(unsigned int ncom, TopoFreq& tfreq, TopoStat& tstat, unsigned int& ncommon, unsigned int ntot); + void setThresholdImpl(double thr, TopoFreq& tfreq, TopoInfo& tinfo, TopoStat& tstat, unsigned int& ncommon, double& freqthres, unsigned int ntot); + void setThresholdCumulativeImpl(double cumulative, TopoFreq& tfreq, unsigned int& ncommon, double& freqthres, unsigned int ntot); + void groupRareTopologiesImpl(TopoFreq& tfreq, TopoInfo& tinfo, TopoStat& tstat, unsigned int& ncommon, double& freqthres, TopologyDictionaryData& data, unsigned int ntot); + + TopologyDictionary mDictionary; ///< Dictionary of topologies + unsigned int mTotClustersIB{0}; + unsigned int mTotClustersOB{0}; + unsigned int mNCommonTopologiesIB{0}; + unsigned int mNCommonTopologiesOB{0}; + double mFrequencyThresholdIB{0.}; + double mFrequencyThresholdOB{0.}; + TopoInfo mMapInfoIB; + TopoInfo mMapInfoOB; + TopoStat mTopologyMapIB; //! IB Temporary map of type + TopoStat mTopologyMapOB; //! OB Temporary map of type + TopoFreq mTopologyFrequencyIB; //! IB , needed to define threshold + TopoFreq mTopologyFrequencyOB; //! OB , needed to define threshold ClassDefNV(BuildTopologyDictionary, 3); }; diff --git a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/Clusterer.h b/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/Clusterer.h index 20acf07d4f547..a81db09217e9b 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/Clusterer.h +++ b/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/Clusterer.h @@ -207,7 +207,7 @@ class Clusterer template static void streamCluster(const std::vector& pixbuf, const std::array* lblBuff, const BBox& bbox, const its3::LookUp& pattIdConverter, - VCLUS* compClusPtr, VPAT* patternsPtr, MCTruth* labelsClusPtr, int nlab, bool isHuge = false); + VCLUS* compClusPtr, VPAT* patternsPtr, MCTruth* labelsClusPtr, int nlab, bool isIB, bool isHuge = false); bool isContinuousReadOut() const { return mContinuousReadout; } void setContinuousReadOut(bool v) { mContinuousReadout = v; } @@ -230,7 +230,7 @@ class Clusterer ///< load the dictionary of cluster topologies void setDictionary(const its3::TopologyDictionary* dict) { - LOGP(info, "Setting TopologyDictionary with size={}", dict->getSize()); + LOGP(info, "Setting TopologyDictionary with IB size={} & OB size={}", dict->getSize(true), dict->getSize(false)); mPattIdConverter.setDictionary(dict); // dict->print(); } @@ -274,7 +274,7 @@ class Clusterer template void Clusterer::streamCluster(const std::vector& pixbuf, const std::array* lblBuff, const Clusterer::BBox& bbox, const its3::LookUp& pattIdConverter, - VCLUS* compClusPtr, VPAT* patternsPtr, MCTruth* labelsClusPtr, int nlab, bool isHuge) + VCLUS* compClusPtr, VPAT* patternsPtr, MCTruth* labelsClusPtr, int nlab, bool isIB, bool isHuge) { if (labelsClusPtr && lblBuff) { // MC labels were requested auto cnt = compClusPtr->size(); @@ -291,10 +291,10 @@ void Clusterer::streamCluster(const std::vector& pixbuf, const std::a int nbits = ir * colSpanW + ic; patt[nbits >> 3] |= (0x1 << (7 - (nbits % 8))); } - uint16_t pattID = (isHuge || pattIdConverter.size() == 0) ? CompCluster::InvalidPatternID : pattIdConverter.findGroupID(rowSpanW, colSpanW, patt.data()); + uint16_t pattID = (isHuge || pattIdConverter.size(isIB) == 0) ? CompCluster::InvalidPatternID : pattIdConverter.findGroupID(rowSpanW, colSpanW, isIB, patt.data()); uint16_t row = bbox.rowMin, col = bbox.colMin; LOGP(debug, "PattID: findGroupID({},{},{})={}", row, col, patt[0], pattID); - if (pattID == CompCluster::InvalidPatternID || pattIdConverter.isGroup(pattID)) { + if (pattID == CompCluster::InvalidPatternID || pattIdConverter.isGroup(pattID, isIB)) { if (pattID != CompCluster::InvalidPatternID) { // For groupped topologies, the reference pixel is the COG pixel float xCOG = 0., zCOG = 0.; diff --git a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/IOUtils.h b/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/IOUtils.h index 2407344aa0193..b9e7fd0f6ec39 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/IOUtils.h +++ b/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/IOUtils.h @@ -16,14 +16,13 @@ #include "ITS3Reconstruction/TopologyDictionary.h" #include "ITStracking/TimeFrame.h" #include "ITStracking/IOUtils.h" -#include "ITS3Base/SegmentationSuperAlpide.h" +#include "ITS3Base/SegmentationMosaix.h" #include "ITS3Base/SpecsV2.h" namespace o2::its3::ioutils { -using SSAlpide = o2::its3::SegmentationSuperAlpide; -constexpr float DefClusErrorRow = o2::its3::SegmentationSuperAlpide::mPitchRow * 0.5; -constexpr float DefClusErrorCol = o2::its3::SegmentationSuperAlpide::mPitchCol * 0.5; +constexpr float DefClusErrorRow = o2::its3::SegmentationMosaix::PitchRow * 0.5; +constexpr float DefClusErrorCol = o2::its3::SegmentationMosaix::PitchCol * 0.5; constexpr float DefClusError2Row = DefClusErrorRow * DefClusErrorRow; constexpr float DefClusError2Col = DefClusErrorCol * DefClusErrorCol; @@ -31,13 +30,14 @@ template o2::math_utils::Point3D extractClusterData(const itsmft::CompClusterExt& c, iterator& iter, const its3::TopologyDictionary* dict, T& sig2y, T& sig2z) { auto pattID = c.getPatternID(); + auto ib = constants::detID::isDetITS3(c.getSensorID()); // Dummy COG errors (about half pixel size) - sig2y = (constants::detID::isDetITS3(c.getSensorID())) ? DefClusError2Row : o2::its::ioutils::DefClusError2Row; - sig2z = (constants::detID::isDetITS3(c.getSensorID())) ? DefClusError2Col : o2::its::ioutils::DefClusError2Col; + sig2y = (ib) ? DefClusError2Row : o2::its::ioutils::DefClusError2Row; + sig2z = (ib) ? DefClusError2Col : o2::its::ioutils::DefClusError2Col; if (pattID != itsmft::CompCluster::InvalidPatternID) { - sig2y = dict->getErr2X(pattID) * sig2y; // Error is given in detector coordinates - sig2z = dict->getErr2Z(pattID) * sig2z; - if (!dict->isGroup(pattID)) { + sig2y = dict->getErr2X(pattID, ib); + sig2z = dict->getErr2Z(pattID, ib); + if (!dict->isGroup(pattID, ib)) { return dict->getClusterCoordinates(c); } else { o2::itsmft::ClusterPattern patt(iter); @@ -53,13 +53,14 @@ template o2::math_utils::Point3D extractClusterData(const itsmft::CompClusterExt& c, iterator& iter, const its3::TopologyDictionary* dict, T& sig2y, T& sig2z, uint8_t& cls) { auto pattID = c.getPatternID(); + auto ib = constants::detID::isDetITS3(c.getSensorID()); auto iterC = iter; unsigned int clusterSize{999}; - if (pattID == itsmft::CompCluster::InvalidPatternID || dict->isGroup(pattID)) { + if (pattID == itsmft::CompCluster::InvalidPatternID || dict->isGroup(pattID, ib)) { o2::itsmft::ClusterPattern patt(iterC); clusterSize = patt.getNPixels(); } else { - clusterSize = dict->getNpixels(pattID); + clusterSize = dict->getNpixels(pattID, ib); } cls = static_cast(std::clamp(clusterSize, static_cast(std::numeric_limits::min()), static_cast(std::numeric_limits::max()))); return extractClusterData(c, iter, dict, sig2y, sig2z); diff --git a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/LookUp.h b/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/LookUp.h index 0fbecb41393ff..809a129a0debf 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/LookUp.h +++ b/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/LookUp.h @@ -21,7 +21,6 @@ #ifndef ALICEO2_ITS3_LOOKUP_H #define ALICEO2_ITS3_LOOKUP_H -#include "DataFormatsITSMFT/ClusterTopology.h" #include "ITS3Reconstruction/TopologyDictionary.h" namespace o2::its3 @@ -32,20 +31,21 @@ class LookUp LookUp() = default; LookUp(std::string fileName); static int groupFinder(int nRow, int nCol); - int findGroupID(int nRow, int nCol, const unsigned char patt[itsmft::ClusterPattern::MaxPatternBytes]) const; - int getTopologiesOverThreshold() const { return mTopologiesOverThreshold; } + int findGroupID(int nRow, int nCol, bool IB, const unsigned char patt[itsmft::ClusterPattern::MaxPatternBytes]) const; + int getTopologiesOverThreshold(bool IB) const { return (IB) ? mTopologiesOverThresholdIB : mTopologiesOverThresholdOB; } void loadDictionary(std::string fileName); void setDictionary(const TopologyDictionary* dict); - bool isGroup(int id) const { return mDictionary.isGroup(id); } - int size() const { return mDictionary.getSize(); } - auto getPattern(int id) const { return mDictionary.getPattern(id); } - auto getDictionaty() const { return mDictionary; } + auto getDictionary() const { return mDictionary; } + bool isGroup(int id, bool IB) const { return mDictionary.isGroup(id, IB); } + int size(bool IB) const { return mDictionary.getSize(IB); } + auto getPattern(int id, bool IB) const { return mDictionary.getPattern(id, IB); } private: - TopologyDictionary mDictionary{}; - int mTopologiesOverThreshold{0}; + TopologyDictionary mDictionary; + int mTopologiesOverThresholdIB{0}; + int mTopologiesOverThresholdOB{0}; - ClassDefNV(LookUp, 2); + ClassDefNV(LookUp, 3); }; } // namespace o2::its3 diff --git a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/TopologyDictionary.h b/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/TopologyDictionary.h index a11131ed9f61f..31f1c023999b8 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/TopologyDictionary.h +++ b/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/TopologyDictionary.h @@ -24,6 +24,18 @@ namespace o2::its3 class BuildTopologyDictionary; class LookUp; +struct TopologyDictionaryData { + static constexpr int STopoSize{(8 * 255) + 1}; + std::array mSmallTopologiesLUT{}; ///< Look-Up Table for the topologies with 1-byte linearised matrix + std::vector mVectorOfIDs; ///< Vector of topologies and groups + std::unordered_map mCommonMap; ///< Map of pair + std::unordered_map mGroupMap; ///< Map of pair + + void print() const noexcept; + + ClassDefNV(TopologyDictionaryData, 1); +}; + class TopologyDictionary { public: @@ -32,91 +44,108 @@ class TopologyDictionary /// constexpr for the definition of the groups of rare topologies. /// The attritbution of the group ID is stringly dependent on the following parameters: it must be a power of 2. - static constexpr int RowClassSpan = 4; ///< Row span of the classes of rare topologies - static constexpr int ColClassSpan = 4; ///< Column span of the classes of rare topologies - static constexpr int MaxNumberOfRowClasses = 1 + (itsmft::ClusterPattern::MaxRowSpan - 1) / RowClassSpan; ///< Maximum number of row classes for the groups of rare topologies - static constexpr int MaxNumberOfColClasses = 1 + (itsmft::ClusterPattern::MaxColSpan - 1) / ColClassSpan; ///< Maximum number of col classes for the groups of rare topologies - static constexpr int NumberOfRareGroups = MaxNumberOfRowClasses * MaxNumberOfColClasses; ///< Number of entries corresponding to groups of rare topologies (those whos matrix exceed the max number of bytes are empty). + static constexpr int RowClassSpan = 4; ///< Row span of the classes of rare topologies + static constexpr int ColClassSpan = 4; ///< Column span of the classes of rare topologies + static constexpr int MaxNumberOfRowClasses = 1 + ((itsmft::ClusterPattern::MaxRowSpan - 1) / RowClassSpan); ///< Maximum number of row classes for the groups of rare topologies + static constexpr int MaxNumberOfColClasses = 1 + ((itsmft::ClusterPattern::MaxColSpan - 1) / ColClassSpan); ///< Maximum number of col classes for the groups of rare topologies + static constexpr int NumberOfRareGroups = MaxNumberOfRowClasses * MaxNumberOfColClasses; ///< Number of entries corresponding to groups of rare topologies (those whos matrix exceed the max number of bytes are empty). + /// Resets internal structures + void reset() noexcept; + void resetMaps(bool IB) noexcept; /// Prints the dictionary friend std::ostream& operator<<(std::ostream& os, const its3::TopologyDictionary& dictionary); /// Prints the dictionary in a binary file void writeBinaryFile(const std::string& outputFile); /// Reads the dictionary from a binary file - int readBinaryFile(const std::string& fileName); - - int readFromFile(const std::string& fileName); + void readBinaryFile(const std::string& fileName); + void readFromFile(const std::string& fileName); + void print() const noexcept; /// Returns the x position of the COG for the n_th element - inline float getXCOG(int n) const + [[nodiscard]] float getXCOG(int n, bool IB) const { - assert(n >= 0 || n < (int)mVectorOfIDs.size()); - return mVectorOfIDs[n].mXCOG; + const auto& data = (IB) ? mDataIB : mDataOB; + assert(n >= 0 || n < (int)data.mVectorOfIDs.size()); + return data.mVectorOfIDs[n].mXCOG; } /// Returns the error on the x position of the COG for the n_th element - inline float getErrX(int n) const + [[nodiscard]] float getErrX(int n, bool IB) const { - assert(n >= 0 || n < (int)mVectorOfIDs.size()); - return mVectorOfIDs[n].mErrX; + const auto& data = (IB) ? mDataIB : mDataOB; + assert(n >= 0 || n < (int)data.mVectorOfIDs.size()); + return data.mVectorOfIDs[n].mErrX; } /// Returns the z position of the COG for the n_th element - inline float getZCOG(int n) const + [[nodiscard]] float getZCOG(int n, bool IB) const { - assert(n >= 0 || n < (int)mVectorOfIDs.size()); - return mVectorOfIDs[n].mZCOG; + const auto& data = (IB) ? mDataIB : mDataOB; + assert(n >= 0 || n < (int)data.mVectorOfIDs.size()); + return data.mVectorOfIDs[n].mZCOG; } /// Returns the error on the z position of the COG for the n_th element - inline float getErrZ(int n) const + [[nodiscard]] float getErrZ(int n, bool IB) const { - assert(n >= 0 || n < (int)mVectorOfIDs.size()); - return mVectorOfIDs[n].mErrZ; + const auto& data = (IB) ? mDataIB : mDataOB; + assert(n >= 0 || n < (int)data.mVectorOfIDs.size()); + return data.mVectorOfIDs[n].mErrZ; } /// Returns the error^2 on the x position of the COG for the n_th element - inline float getErr2X(int n) const + [[nodiscard]] float getErr2X(int n, bool IB) const { - assert(n >= 0 || n < (int)mVectorOfIDs.size()); - return mVectorOfIDs[n].mErr2X; + const auto& data = (IB) ? mDataIB : mDataOB; + assert(n >= 0 || n < (int)data.mVectorOfIDs.size()); + return data.mVectorOfIDs[n].mErr2X; } /// Returns the error^2 on the z position of the COG for the n_th element - inline float getErr2Z(int n) const + [[nodiscard]] float getErr2Z(int n, bool IB) const { - assert(n >= 0 || n < (int)mVectorOfIDs.size()); - return mVectorOfIDs[n].mErr2Z; + const auto& data = (IB) ? mDataIB : mDataOB; + assert(n >= 0 || n < (int)data.mVectorOfIDs.size()); + return data.mVectorOfIDs[n].mErr2Z; } /// Returns the hash of the n_th element - inline unsigned long getHash(int n) const + [[nodiscard]] unsigned long getHash(int n, bool IB) const { - assert(n >= 0 || n < (int)mVectorOfIDs.size()); - return mVectorOfIDs[n].mHash; + const auto& data = (IB) ? mDataIB : mDataOB; + assert(n >= 0 || n < (int)data.mVectorOfIDs.size()); + return data.mVectorOfIDs[n].mHash; } /// Returns the number of fired pixels of the n_th element - inline int getNpixels(int n) const + [[nodiscard]] int getNpixels(int n, bool IB) const { - assert(n >= 0 || n < (int)mVectorOfIDs.size()); - return mVectorOfIDs[n].mNpixels; + const auto& data = (IB) ? mDataIB : mDataOB; + assert(n >= 0 || n < (int)data.mVectorOfIDs.size()); + return data.mVectorOfIDs[n].mNpixels; } /// Returns the frequency of the n_th element; - inline double getFrequency(int n) const + [[nodiscard]] double getFrequency(int n, bool IB) const { - assert(n >= 0 || n < (int)mVectorOfIDs.size()); - return mVectorOfIDs[n].mFrequency; + const auto& data = (IB) ? mDataIB : mDataOB; + assert(n >= 0 || n < (int)data.mVectorOfIDs.size()); + return data.mVectorOfIDs[n].mFrequency; } /// Returns true if the element corresponds to a group of rare topologies - inline bool isGroup(int n) const + [[nodiscard]] bool isGroup(int n, bool IB) const { - assert(n >= 0 || n < (int)mVectorOfIDs.size()); - return mVectorOfIDs[n].mIsGroup; + const auto& data = (IB) ? mDataIB : mDataOB; + assert(n >= 0 || n < (int)data.mVectorOfIDs.size()); + return data.mVectorOfIDs[n].mIsGroup; } /// Returns the pattern of the topology - inline const itsmft::ClusterPattern& getPattern(int n) const + [[nodiscard]] const itsmft::ClusterPattern& getPattern(int n, bool IB) const { - assert(n >= 0 || n < (int)mVectorOfIDs.size()); - return mVectorOfIDs[n].mPattern; + const auto& data = (IB) ? mDataIB : mDataOB; + assert(n >= 0 || n < (int)data.mVectorOfIDs.size()); + return data.mVectorOfIDs[n].mPattern; } /// Fills a hostogram with the distribution of the IDs - TH1F* getTopologyDistribution(const std::string_view hname = "h_topo_dist") const; + [[nodiscard]] TH1F* getTopologyDistribution(const std::string_view hname, bool IB) const; /// Returns the number of elements in the dicionary; - int getSize() const { return (int)mVectorOfIDs.size(); } + [[nodiscard]] int getSize(bool IB) const + { + return static_cast((IB) ? mDataIB.mVectorOfIDs.size() : mDataOB.mVectorOfIDs.size()); + } /// Returns the local position of a compact cluster /// Returns the local position of a compact cluster @@ -133,13 +162,10 @@ class TopologyDictionary friend its3::LookUp; private: - static constexpr int STopoSize{8 * 255 + 1}; - std::unordered_map mCommonMap{}; ///< Map of pair - std::unordered_map mGroupMap{}; ///< Map of pair - int mSmallTopologiesLUT[STopoSize]{}; ///< Look-Up Table for the topologies with 1-byte linearised matrix - std::vector mVectorOfIDs{}; ///< Vector of topologies and groups + TopologyDictionaryData mDataIB; + TopologyDictionaryData mDataOB; - ClassDefNV(TopologyDictionary, 3); + ClassDefNV(TopologyDictionary, 4); }; } // namespace o2::its3 diff --git a/Detectors/Upgrades/ITS3/reconstruction/src/BuildTopologyDictionary.cxx b/Detectors/Upgrades/ITS3/reconstruction/src/BuildTopologyDictionary.cxx index 87ad450eecd9e..f7eec52f9434a 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/src/BuildTopologyDictionary.cxx +++ b/Detectors/Upgrades/ITS3/reconstruction/src/BuildTopologyDictionary.cxx @@ -14,7 +14,9 @@ #include "ITS3Reconstruction/BuildTopologyDictionary.h" #include "ITS3Reconstruction/LookUp.h" #include "DataFormatsITSMFT/CompCluster.h" -#include "ITS3Base/SegmentationSuperAlpide.h" + +#include "ITSMFTBase/SegmentationAlpide.h" +#include "ITS3Base/SegmentationMosaix.h" #include "TFile.h" @@ -22,14 +24,25 @@ ClassImp(o2::its3::BuildTopologyDictionary); namespace o2::its3 { -void BuildTopologyDictionary::accountTopology(const itsmft::ClusterTopology& cluster, float dX, float dZ) +void BuildTopologyDictionary::accountTopology(const itsmft::ClusterTopology& cluster, bool IB, float dX, float dZ) { - mTotClusters++; + accountTopologyImpl(cluster, + ((IB) ? mMapInfoIB : mMapInfoOB), + ((IB) ? mTopologyMapIB : mTopologyMapOB), + ((IB) ? mTotClustersIB : mTotClustersOB), + ((IB) ? SegmentationMosaix::PitchRow : itsmft::SegmentationAlpide::PitchRow), + ((IB) ? SegmentationMosaix::PitchCol : itsmft::SegmentationAlpide::PitchCol), + dX, dZ); +} + +void BuildTopologyDictionary::accountTopologyImpl(const itsmft::ClusterTopology& cluster, TopoInfo& tinfo, TopoStat& tstat, unsigned int& tot, float sigmaX, float sigmaZ, float dX, float dZ) +{ + ++tot; bool useDf = dX < IgnoreVal / 2; // we may need to account the frequency but to not update the centroid // std::pair::iterator,bool> ret; // auto ret = mTopologyMap.insert(std::make_pair(cluster.getHash(), std::make_pair(cluster, 1))); - auto& topoStat = mTopologyMap[cluster.getHash()]; + auto& topoStat = tstat[cluster.getHash()]; topoStat.countsTotal++; if (topoStat.countsTotal == 1) { // a new topology is inserted topoStat.topology = cluster; @@ -45,14 +58,14 @@ void BuildTopologyDictionary::accountTopology(const itsmft::ClusterTopology& clu topInf.mZmean = dZ; topoStat.countsWithBias = 1; } else { // assign expected sigmas from the pixel X, Z sizes - topInf.mXsigma2 = 1.f / 12.f / (float)std::min(10, topInf.mSizeX); - topInf.mZsigma2 = 1.f / 12.f / (float)std::min(10, topInf.mSizeZ); + topInf.mXsigma2 = sigmaX * sigmaX / 12.f / (float)std::min(10, topInf.mSizeX); + topInf.mZsigma2 = sigmaZ * sigmaZ / (float)std::min(10, topInf.mSizeZ); } - mMapInfo.emplace(cluster.getHash(), topInf); + tinfo.emplace(cluster.getHash(), topInf); } else { if (useDf) { auto num = topoStat.countsWithBias++; - auto ind = mMapInfo.find(cluster.getHash()); + auto ind = tinfo.find(cluster.getHash()); float tmpxMean = ind->second.mXmean; float newxMean = ind->second.mXmean = ((tmpxMean)*num + dX) / (num + 1); float tmpxSigma2 = ind->second.mXsigma2; @@ -65,101 +78,135 @@ void BuildTopologyDictionary::accountTopology(const itsmft::ClusterTopology& clu } } -void BuildTopologyDictionary::setThreshold(double thr) +void BuildTopologyDictionary::setNCommon(unsigned int nCommon, bool IB) +{ + mDictionary.resetMaps(IB); + + auto& freqTopo = ((IB) ? mTopologyFrequencyIB : mTopologyFrequencyOB); + auto& freqThres = ((IB) ? mFrequencyThresholdIB : mFrequencyThresholdOB); + auto& comTopo = ((IB) ? mNCommonTopologiesIB : mNCommonTopologiesOB); + auto ntot = ((IB) ? mTotClustersIB : mTotClustersOB); + + setNCommonImpl(nCommon, + freqTopo, + ((IB) ? mTopologyMapIB : mTopologyMapOB), + comTopo, + ntot); + // Recaculate also the threshold + freqThres = ((double)freqTopo[comTopo - 1].first) / ntot; +} + +void BuildTopologyDictionary::setNCommonImpl(unsigned int ncom, TopoFreq& tfreq, TopoStat& tstat, unsigned int& ncommon, unsigned int ntot) { - mTopologyFrequency.clear(); - for (auto&& p : mTopologyMap) { // p is pair - mTopologyFrequency.emplace_back(p.second.countsTotal, p.first); + if (ncom >= itsmft::CompCluster::InvalidPatternID) { + LOGP(warning, "Redefining nCommon from {} to {} to be below InvalidPatternID", ncom, itsmft::CompCluster::InvalidPatternID - 1); + ncom = itsmft::CompCluster::InvalidPatternID - 1; + } + tfreq.clear(); + for (auto&& p : tstat) { // p os pair + tfreq.emplace_back(p.second.countsTotal, p.first); } - std::sort(mTopologyFrequency.begin(), mTopologyFrequency.end(), + std::sort(tfreq.begin(), tfreq.end(), [](const std::pair& couple1, const std::pair& couple2) { return (couple1.first > couple2.first); }); - mNCommonTopologies = 0; - mDictionary.mCommonMap.clear(); - mDictionary.mGroupMap.clear(); - mFrequencyThreshold = thr; - for (auto& q : mTopologyFrequency) { - if (((double)q.first) / mTotClusters > thr) { - mNCommonTopologies++; + ncommon = ncom; +} + +void BuildTopologyDictionary::setThreshold(double thr, bool IB) +{ + mDictionary.resetMaps(IB); + setThresholdImpl(thr, + ((IB) ? mTopologyFrequencyIB : mTopologyFrequencyOB), + ((IB) ? mMapInfoIB : mMapInfoOB), + ((IB) ? mTopologyMapIB : mTopologyMapOB), + ((IB) ? mNCommonTopologiesIB : mNCommonTopologiesOB), + ((IB) ? mFrequencyThresholdIB : mFrequencyThresholdOB), + ((IB) ? mTotClustersIB : mTotClustersOB)); +} + +void BuildTopologyDictionary::setThresholdImpl(double thr, TopoFreq& tfreq, TopoInfo& tinfo, TopoStat& tstat, unsigned int& ncommon, double& freqthres, unsigned int ntot) +{ + setNCommonImpl(0, tfreq, tstat, ncommon, ntot); + freqthres = thr; + for (auto& q : tfreq) { + if (((double)q.first) / ntot > thr) { + ++ncommon; } else { break; } } - if (mNCommonTopologies >= itsmft::CompCluster::InvalidPatternID) { - mFrequencyThreshold = ((double)mTopologyFrequency[itsmft::CompCluster::InvalidPatternID - 1].first) / mTotClusters; - LOGP(warning, "Redefining prob. threshould from {} to {} to be below InvalidPatternID (was {})", thr, mFrequencyThreshold, mNCommonTopologies); - mNCommonTopologies = itsmft::CompCluster::InvalidPatternID - 1; + if (ncommon >= itsmft::CompCluster::InvalidPatternID) { + freqthres = ((double)tfreq[itsmft::CompCluster::InvalidPatternID - 1].first) / ntot; + LOGP(warning, "Redefining prob. threshold from {} to {} to be below InvalidPatternID (was {})", thr, freqthres, ntot); + ncommon = itsmft::CompCluster::InvalidPatternID - 1; } } -void BuildTopologyDictionary::setNCommon(unsigned int nCommon) +void BuildTopologyDictionary::setThresholdCumulative(double cumulative, bool IB) { - if (nCommon >= itsmft::CompCluster::InvalidPatternID) { - LOGP(warning, "Redefining nCommon from {} to {} to be below InvalidPatternID", nCommon, itsmft::CompCluster::InvalidPatternID - 1); - nCommon = itsmft::CompCluster::InvalidPatternID - 1; - } - mTopologyFrequency.clear(); - for (auto&& p : mTopologyMap) { // p os pair - mTopologyFrequency.emplace_back(p.second.countsTotal, p.first); + if (cumulative <= 0. || cumulative >= 1.) { + cumulative = 0.99; } - std::sort(mTopologyFrequency.begin(), mTopologyFrequency.end(), - [](const std::pair& couple1, - const std::pair& couple2) { return (couple1.first > couple2.first); }); - mNCommonTopologies = nCommon; - mDictionary.mCommonMap.clear(); - mDictionary.mGroupMap.clear(); - mFrequencyThreshold = ((double)mTopologyFrequency[mNCommonTopologies - 1].first) / mTotClusters; + + auto& freqTopo = ((IB) ? mTopologyFrequencyIB : mTopologyFrequencyOB); + auto& freqThres = ((IB) ? mFrequencyThresholdIB : mFrequencyThresholdOB); + auto& statTopo = ((IB) ? mTopologyMapIB : mTopologyMapOB); + auto& comTopo = ((IB) ? mNCommonTopologiesIB : mNCommonTopologiesOB); + auto ntot = ((IB) ? mTotClustersIB : mTotClustersOB); + + mDictionary.resetMaps(IB); + setNCommonImpl(0, freqTopo, statTopo, comTopo, ntot); + setThresholdCumulativeImpl(cumulative, freqTopo, comTopo, freqThres, ntot); } -void BuildTopologyDictionary::setThresholdCumulative(double cumulative) +void BuildTopologyDictionary::setThresholdCumulativeImpl(double cumulative, TopoFreq& tfreq, unsigned int& ncommon, double& freqthres, unsigned int ntot) { - mTopologyFrequency.clear(); - if (cumulative <= 0. || cumulative >= 1.) { - cumulative = 0.99; - } double totFreq = 0.; - for (auto&& p : mTopologyMap) { // p os pair - mTopologyFrequency.emplace_back(p.second.countsTotal, p.first); - } - std::sort(mTopologyFrequency.begin(), mTopologyFrequency.end(), - [](const std::pair& couple1, - const std::pair& couple2) { return (couple1.first > couple2.first); }); - mNCommonTopologies = 0; - mDictionary.mCommonMap.clear(); - mDictionary.mGroupMap.clear(); - for (auto& q : mTopologyFrequency) { - totFreq += ((double)(q.first)) / mTotClusters; + for (auto& q : tfreq) { + totFreq += ((double)(q.first)) / ntot; if (totFreq < cumulative) { - mNCommonTopologies++; - if (mNCommonTopologies >= itsmft::CompCluster::InvalidPatternID) { - totFreq -= ((double)(q.first)) / mTotClusters; - mNCommonTopologies--; + ++ncommon; + if (ncommon >= itsmft::CompCluster::InvalidPatternID) { + totFreq -= ((double)(q.first)) / ntot; + --ncommon; LOGP(warning, "Redefining cumulative threshould from {} to {} to be below InvalidPatternID)", cumulative, totFreq); } } else { break; } } - mFrequencyThreshold = ((double)(mTopologyFrequency[--mNCommonTopologies].first)) / mTotClusters; - while (std::fabs(((double)mTopologyFrequency[mNCommonTopologies].first) / mTotClusters - mFrequencyThreshold) < 1.e-15) { - mNCommonTopologies--; + freqthres = ((double)(tfreq[--ncommon].first)) / ntot; + while (std::fabs(((double)tfreq[ncommon--].first) / ntot - freqthres) < 1.e-15) { } - mFrequencyThreshold = ((double)mTopologyFrequency[mNCommonTopologies++].first) / mTotClusters; + freqthres = ((double)tfreq[ncommon++].first) / ntot; } void BuildTopologyDictionary::groupRareTopologies() { LOG(info) << "Dictionary finalisation"; - LOG(info) << "Number of clusters: " << mTotClusters; + LOG(info) << "Number of IB clusters: " << mTotClustersIB; + LOG(info) << "Number of OB clusters: " << mTotClustersOB; + + groupRareTopologiesImpl(mTopologyFrequencyIB, mMapInfoIB, mTopologyMapIB, mNCommonTopologiesIB, mFrequencyThresholdIB, mDictionary.mDataIB, mNCommonTopologiesIB); + groupRareTopologiesImpl(mTopologyFrequencyOB, mMapInfoOB, mTopologyMapOB, mNCommonTopologiesOB, mFrequencyThresholdOB, mDictionary.mDataOB, mNCommonTopologiesOB); + + LOG(info) << "Dictionay finalised"; + LOG(info) << "IB:"; + mDictionary.mDataIB.print(); + LOG(info) << "OB:"; + mDictionary.mDataOB.print(); +} +void BuildTopologyDictionary::groupRareTopologiesImpl(TopoFreq& tfreq, TopoInfo& tinfo, TopoStat& tstat, unsigned int& ncommon, double& freqthres, TopologyDictionaryData& data, unsigned int ntot) +{ double totFreq = 0.; - for (unsigned int j = 0; j < mNCommonTopologies; j++) { + for (unsigned int j = 0; j < ncommon; j++) { itsmft::GroupStruct gr; - gr.mHash = mTopologyFrequency[j].second; - gr.mFrequency = ((double)(mTopologyFrequency[j].first)) / mTotClusters; + gr.mHash = tfreq[j].second; + gr.mFrequency = ((double)(tfreq[j].first)) / ntot; totFreq += gr.mFrequency; // rough estimation for the error considering a8 uniform distribution - const auto& topo = mMapInfo.find(gr.mHash)->second; + const auto& topo = tinfo.find(gr.mHash)->second; gr.mErrX = std::sqrt(topo.mXsigma2); gr.mErrZ = std::sqrt(topo.mZsigma2); gr.mErr2X = topo.mXsigma2; @@ -169,11 +216,11 @@ void BuildTopologyDictionary::groupRareTopologies() gr.mNpixels = topo.mNpixels; gr.mPattern = topo.mPattern; gr.mIsGroup = false; - mDictionary.mVectorOfIDs.push_back(gr); + data.mVectorOfIDs.push_back(gr); if (j == int(itsmft::CompCluster::InvalidPatternID - 1)) { LOGP(warning, "Limiting N unique topologies to {}, threshold freq. to {}, cumulative freq. to {} to be below InvalidPatternID", j, gr.mFrequency, totFreq); - mNCommonTopologies = j; - mFrequencyThreshold = gr.mFrequency; + ncommon = j; + freqthres = gr.mFrequency; break; } } @@ -193,8 +240,8 @@ void BuildTopologyDictionary::groupRareTopologies() // Create a structure for a group of rare topologies itsmft::GroupStruct gr; gr.mHash = (((unsigned long)(grNum)) << 32) & 0xffffffff00000000; - gr.mErrX = its3::TopologyDictionary::RowClassSpan / std::sqrt(12 * std::min(10, rowBinEdge)); - gr.mErrZ = its3::TopologyDictionary::ColClassSpan / std::sqrt(12 * std::min(10, colBinEdge)); + gr.mErrX = its3::TopologyDictionary::RowClassSpan / std::sqrt(12.f * (float)std::min(10, rowBinEdge)); + gr.mErrZ = its3::TopologyDictionary::ColClassSpan / std::sqrt(12.f * (float)std::min(10, colBinEdge)); gr.mErr2X = gr.mErrX * gr.mErrX; gr.mErr2Z = gr.mErrZ * gr.mErrZ; gr.mXCOG = 0; @@ -228,58 +275,65 @@ void BuildTopologyDictionary::groupRareTopologies() int rs{}, cs{}, index{}; // Updating the counts for the groups of rare topologies - for (auto j{mNCommonTopologies}; j < mTopologyFrequency.size(); j++) { - unsigned long hash1 = mTopologyFrequency[j].second; - rs = mTopologyMap.find(hash1)->second.topology.getRowSpan(); - cs = mTopologyMap.find(hash1)->second.topology.getColumnSpan(); + for (auto j{ncommon}; j < tfreq.size(); j++) { + unsigned long hash1 = tfreq[j].second; + rs = tstat.find(hash1)->second.topology.getRowSpan(); + cs = tstat.find(hash1)->second.topology.getColumnSpan(); index = its3::LookUp::groupFinder(rs, cs); - tmp_GroupMap[index].second += mTopologyFrequency[j].first; + tmp_GroupMap[index].second += tfreq[j].first; } for (auto&& p : tmp_GroupMap) { itsmft::GroupStruct& group = p.second.first; - group.mFrequency = ((double)p.second.second) / mTotClusters; - mDictionary.mVectorOfIDs.push_back(group); + group.mFrequency = ((double)p.second.second) / ntot; + data.mVectorOfIDs.push_back(group); } // Sorting the dictionary preserving all unique topologies - std::sort(mDictionary.mVectorOfIDs.begin(), mDictionary.mVectorOfIDs.end(), [](const itsmft::GroupStruct& a, const itsmft::GroupStruct& b) { + std::sort(data.mVectorOfIDs.begin(), data.mVectorOfIDs.end(), [](const itsmft::GroupStruct& a, const itsmft::GroupStruct& b) { return (!a.mIsGroup) && b.mIsGroup ? true : (a.mIsGroup && (!b.mIsGroup) ? false : (a.mFrequency > b.mFrequency)); }); - if (mDictionary.mVectorOfIDs.size() >= itsmft::CompCluster::InvalidPatternID - 1) { + if (data.mVectorOfIDs.size() >= itsmft::CompCluster::InvalidPatternID - 1) { LOGP(warning, "Max allowed {} patterns is reached, stopping", itsmft::CompCluster::InvalidPatternID - 1); - mDictionary.mVectorOfIDs.resize(itsmft::CompCluster::InvalidPatternID - 1); + data.mVectorOfIDs.resize(itsmft::CompCluster::InvalidPatternID - 1); } // Sorting the dictionary to final form - std::sort(mDictionary.mVectorOfIDs.begin(), mDictionary.mVectorOfIDs.end(), [](const itsmft::GroupStruct& a, const itsmft::GroupStruct& b) { return a.mFrequency > b.mFrequency; }); + std::sort(data.mVectorOfIDs.begin(), data.mVectorOfIDs.end(), [](const itsmft::GroupStruct& a, const itsmft::GroupStruct& b) { return a.mFrequency > b.mFrequency; }); // Creating the map for common topologies - for (int iKey = 0; iKey < mDictionary.getSize(); iKey++) { - itsmft::GroupStruct& gr = mDictionary.mVectorOfIDs[iKey]; + for (int iKey = 0; iKey < data.mVectorOfIDs.size(); iKey++) { + itsmft::GroupStruct& gr = data.mVectorOfIDs[iKey]; if (!gr.mIsGroup) { - mDictionary.mCommonMap.emplace(gr.mHash, iKey); + data.mCommonMap.emplace(gr.mHash, iKey); if (gr.mPattern.getUsedBytes() == 1) { - mDictionary.mSmallTopologiesLUT[(gr.mPattern.getColumnSpan() - 1) * 255 + (int)gr.mPattern.getByte(2)] = iKey; + data.mSmallTopologiesLUT[(gr.mPattern.getColumnSpan() - 1) * 255 + (int)gr.mPattern.getByte(2)] = iKey; } } else { - mDictionary.mGroupMap.emplace((int)(gr.mHash >> 32) & 0x00000000ffffffff, iKey); + data.mGroupMap.emplace((int)(gr.mHash >> 32) & 0x00000000ffffffff, iKey); } } - LOG(info) << "Dictionay finalised"; - LOG(info) << "Number of keys: " << mDictionary.getSize(); - LOG(info) << "Number of common topologies: " << mDictionary.mCommonMap.size(); - LOG(info) << "Number of groups of rare topologies: " << mDictionary.mGroupMap.size(); } std::ostream& operator<<(std::ostream& os, const BuildTopologyDictionary& DB) { - for (unsigned int i = 0; i < DB.mNCommonTopologies; i++) { - const unsigned long& hash = DB.mTopologyFrequency[i].second; + os << "--- InnerBarrel\n"; + for (unsigned int i = 0; i < DB.mNCommonTopologiesIB; i++) { + const unsigned long& hash = DB.mTopologyFrequencyIB[i].second; + os << "Hash: " << hash << '\n'; + os << "counts: " << DB.mTopologyMapIB.find(hash)->second.countsTotal; + os << " (with bias provided: " << DB.mTopologyMapIB.find(hash)->second.countsWithBias << ")" << '\n'; + os << "sigmaX: " << std::sqrt(DB.mMapInfoIB.find(hash)->second.mXsigma2) << '\n'; + os << "sigmaZ: " << std::sqrt(DB.mMapInfoIB.find(hash)->second.mZsigma2) << '\n'; + os << DB.mTopologyMapIB.find(hash)->second.topology; + } + os << "--- OuterBarrel\n"; + for (unsigned int i = 0; i < DB.mNCommonTopologiesOB; i++) { + const unsigned long& hash = DB.mTopologyFrequencyOB[i].second; os << "Hash: " << hash << '\n'; - os << "counts: " << DB.mTopologyMap.find(hash)->second.countsTotal; - os << " (with bias provided: " << DB.mTopologyMap.find(hash)->second.countsWithBias << ")" << '\n'; - os << "sigmaX: " << std::sqrt(DB.mMapInfo.find(hash)->second.mXsigma2) << '\n'; - os << "sigmaZ: " << std::sqrt(DB.mMapInfo.find(hash)->second.mZsigma2) << '\n'; - os << DB.mTopologyMap.find(hash)->second.topology; + os << "counts: " << DB.mTopologyMapOB.find(hash)->second.countsTotal; + os << " (with bias provided: " << DB.mTopologyMapOB.find(hash)->second.countsWithBias << ")" << '\n'; + os << "sigmaX: " << std::sqrt(DB.mMapInfoOB.find(hash)->second.mXsigma2) << '\n'; + os << "sigmaZ: " << std::sqrt(DB.mMapInfoOB.find(hash)->second.mZsigma2) << '\n'; + os << DB.mTopologyMapOB.find(hash)->second.topology; } return os; } diff --git a/Detectors/Upgrades/ITS3/reconstruction/src/Clusterer.cxx b/Detectors/Upgrades/ITS3/reconstruction/src/Clusterer.cxx index 90f5245bcef58..bce17b3759340 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/src/Clusterer.cxx +++ b/Detectors/Upgrades/ITS3/reconstruction/src/Clusterer.cxx @@ -12,15 +12,14 @@ /// \file Clusterer.cxx /// \brief Implementation of the ITS cluster finder -#include "ITS3Reconstruction/Clusterer.h" +#include -#include -#include "Framework/Logger.h" -#include "ITS3Base/SegmentationSuperAlpide.h" +#include "ITS3Reconstruction/Clusterer.h" +#include "ITS3Base/SegmentationMosaix.h" #include "SimulationDataFormat/MCTruthContainer.h" #include "CommonDataFormat/InteractionRecord.h" -#include +#include "TTree.h" #ifdef WITH_OPENMP #include @@ -252,7 +251,7 @@ void Clusterer::ClustererThread::finishChip(ChipPixelData* curChipData, CompClus preClusterIndices[i2] = -1; } if (bbox.isAcceptableSize()) { - parent->streamCluster(pixArrBuff, &labelsBuff, bbox, parent->mPattIdConverter, compClusPtr, patternsPtr, labelsClusPtr, nlab); + parent->streamCluster(pixArrBuff, &labelsBuff, bbox, parent->mPattIdConverter, compClusPtr, patternsPtr, labelsClusPtr, nlab, constants::detID::isDetITS3(curChipData->getChipID())); } else { auto warnLeft = MaxHugeClusWarn - parent->mNHugeClus; if (warnLeft > 0) { @@ -278,7 +277,7 @@ void Clusterer::ClustererThread::finishChip(ChipPixelData* curChipData, CompClus } } if (!pixbuf.empty()) { // Stream a piece of cluster only if the reduced bounding box is not empty - parent->streamCluster(pixbuf, &labelsBuff, bboxT, parent->mPattIdConverter, compClusPtr, patternsPtr, labelsClusPtr, nlab, true); + parent->streamCluster(pixbuf, &labelsBuff, bboxT, parent->mPattIdConverter, compClusPtr, patternsPtr, labelsClusPtr, nlab, constants::detID::isDetITS3(curChipData->getChipID()), true); pixbuf.clear(); } bboxT.rowMin = bboxT.rowMax + 1; @@ -305,10 +304,12 @@ void Clusterer::ClustererThread::finishChipSingleHitFast(uint32_t hit, ChipPixel } } + auto ib = constants::detID::isDetITS3(curChipData->getChipID()); + // add to compact clusters, which must be always filled unsigned char patt[ClusterPattern::MaxPatternBytes]{0x1 << (7 - (0 % 8))}; // unrolled 1 hit version of full loop in finishChip - uint16_t pattID = (parent->mPattIdConverter.size() == 0) ? CompCluster::InvalidPatternID : parent->mPattIdConverter.findGroupID(1, 1, patt); - if ((pattID == CompCluster::InvalidPatternID || parent->mPattIdConverter.isGroup(pattID)) && patternsPtr) { + uint16_t pattID = (parent->mPattIdConverter.size(ib) == 0) ? CompCluster::InvalidPatternID : parent->mPattIdConverter.findGroupID(1, 1, ib, patt); + if ((pattID == CompCluster::InvalidPatternID || parent->mPattIdConverter.isGroup(pattID, ib)) && patternsPtr) { patternsPtr->emplace_back(1); // rowspan patternsPtr->emplace_back(1); // colspan patternsPtr->insert(patternsPtr->end(), std::begin(patt), std::begin(patt) + 1); @@ -334,7 +335,7 @@ void Clusterer::ClustererThread::initChip(const ChipPixelData* curChipData, uint size = itsmft::SegmentationAlpide::NRows + 2; int chipId = curChipData->getChipID(); if (its3::constants::detID::isDetITS3(chipId)) { - size = its3::SegmentationSuperAlpide::mNRows + 2; + size = its3::SegmentationMosaix::NRows + 2; } delete[] column1; diff --git a/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx b/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx index 50e651f7f5675..58dd56ac41f95 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx +++ b/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx @@ -16,8 +16,6 @@ #include "DataFormatsITSMFT/ROFRecord.h" #include "ITS3Reconstruction/TopologyDictionary.h" #include "ITSBase/GeometryTGeo.h" -#include "ITSMFTBase/SegmentationAlpide.h" -#include "ITS3Base/SegmentationSuperAlpide.h" #include "ITS3Base/SpecsV2.h" #include "ITStracking/TrackingConfigParam.h" #include "Framework/Logger.h" @@ -80,7 +78,6 @@ int loadROFrameDataITS3(its::TimeFrame* tf, auto isITS3 = its3::constants::detID::isDetITS3(sensorID); auto layer = geom->getLayer(sensorID); - auto pattID = c.getPatternID(); float sigmaY2{0}, sigmaZ2{0}, sigmaYZ{0}; uint8_t clusterSize{0}; auto locXYZ = extractClusterData(c, pattIt, dict, sigmaY2, sigmaZ2, clusterSize); diff --git a/Detectors/Upgrades/ITS3/reconstruction/src/ITS3ReconstructionLinkDef.h b/Detectors/Upgrades/ITS3/reconstruction/src/ITS3ReconstructionLinkDef.h index f19a7fcaba9ca..2ebd89970d9a1 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/src/ITS3ReconstructionLinkDef.h +++ b/Detectors/Upgrades/ITS3/reconstruction/src/ITS3ReconstructionLinkDef.h @@ -16,6 +16,7 @@ #pragma link off all functions; #pragma link C++ class o2::its3::Clusterer + ; +#pragma link C++ class o2::its3::TopologyDictionaryData + ; #pragma link C++ class o2::its3::TopologyDictionary + ; #pragma link C++ class o2::its3::BuildTopologyDictionary + ; #pragma link C++ class o2::its3::LookUp + ; diff --git a/Detectors/Upgrades/ITS3/reconstruction/src/LookUp.cxx b/Detectors/Upgrades/ITS3/reconstruction/src/LookUp.cxx index caabfa6f2decb..e137e091dc631 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/src/LookUp.cxx +++ b/Detectors/Upgrades/ITS3/reconstruction/src/LookUp.cxx @@ -31,7 +31,8 @@ LookUp::LookUp(std::string fileName) void LookUp::loadDictionary(std::string fileName) { mDictionary.readFromFile(fileName); - mTopologiesOverThreshold = mDictionary.mCommonMap.size(); + mTopologiesOverThresholdIB = mDictionary.mDataIB.mCommonMap.size(); + mTopologiesOverThresholdOB = mDictionary.mDataOB.mCommonMap.size(); } void LookUp::setDictionary(const its3::TopologyDictionary* dict) @@ -39,7 +40,8 @@ void LookUp::setDictionary(const its3::TopologyDictionary* dict) if (dict != nullptr) { mDictionary = *dict; } - mTopologiesOverThreshold = mDictionary.mCommonMap.size(); + mTopologiesOverThresholdIB = mDictionary.mDataIB.mCommonMap.size(); + mTopologiesOverThresholdOB = mDictionary.mDataOB.mCommonMap.size(); } int LookUp::groupFinder(int nRow, int nCol) @@ -61,25 +63,26 @@ int LookUp::groupFinder(int nRow, int nCol) return grNum; } -int LookUp::findGroupID(int nRow, int nCol, const unsigned char patt[itsmft::ClusterPattern::MaxPatternBytes]) const +int LookUp::findGroupID(int nRow, int nCol, bool IB, const unsigned char patt[itsmft::ClusterPattern::MaxPatternBytes]) const { + const auto& data = (IB) ? mDictionary.mDataIB : mDictionary.mDataOB; int nBits = nRow * nCol; if (nBits < 9) { // Small unique topology - int ID = mDictionary.mSmallTopologiesLUT[(nCol - 1) * 255 + (int)patt[0]]; + int ID = data.mSmallTopologiesLUT[(nCol - 1) * 255 + (int)patt[0]]; if (ID >= 0) { return ID; } } else { // Big unique topology unsigned long hash = itsmft::ClusterTopology::getCompleteHash(nRow, nCol, patt); - auto ret = mDictionary.mCommonMap.find(hash); - if (ret != mDictionary.mCommonMap.end()) { + auto ret = data.mCommonMap.find(hash); + if (ret != data.mCommonMap.end()) { return ret->second; } } - if (!mDictionary.mGroupMap.empty()) { // rare valid topology group + if (!data.mGroupMap.empty()) { // rare valid topology group int index = groupFinder(nRow, nCol); - auto res = mDictionary.mGroupMap.find(index); - return res == mDictionary.mGroupMap.end() ? itsmft::CompCluster::InvalidPatternID : res->second; + auto res = data.mGroupMap.find(index); + return res == data.mGroupMap.end() ? itsmft::CompCluster::InvalidPatternID : res->second; } return itsmft::CompCluster::InvalidPatternID; } diff --git a/Detectors/Upgrades/ITS3/reconstruction/src/TopologyDictionary.cxx b/Detectors/Upgrades/ITS3/reconstruction/src/TopologyDictionary.cxx index 66a4b0a6878cd..61ab051ffb565 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/src/TopologyDictionary.cxx +++ b/Detectors/Upgrades/ITS3/reconstruction/src/TopologyDictionary.cxx @@ -12,7 +12,7 @@ /// \file TopologyDictionary.cxx #include "ITS3Reconstruction/TopologyDictionary.h" -#include "ITS3Base/SegmentationSuperAlpide.h" +#include "ITS3Base/SegmentationMosaix.h" #include "ITSMFTBase/SegmentationAlpide.h" #include "CommonUtils/StringUtils.h" #include @@ -23,9 +23,16 @@ ClassImp(o2::its3::TopologyDictionary); namespace o2::its3 { +void TopologyDictionaryData::print() const noexcept +{ + LOG(info) << "Number of keys: " << mVectorOfIDs.size(); + LOG(info) << "Number of common topologies: " << mCommonMap.size(); + LOG(info) << "Number of groups of rare topologies: " << mGroupMap.size(); +} + TopologyDictionary::TopologyDictionary() { - memset(mSmallTopologiesLUT, -1, STopoSize * sizeof(int)); + reset(); } TopologyDictionary::TopologyDictionary(const std::string& fileName) @@ -33,10 +40,43 @@ TopologyDictionary::TopologyDictionary(const std::string& fileName) readFromFile(fileName); } +void TopologyDictionary::print() const noexcept +{ + LOG(info) << "ITS3 TopologyDictionary"; + LOG(info) << "InnerBarrel"; + mDataIB.print(); + LOG(info) << "OuterBarrel"; + mDataOB.print(); +} + +void TopologyDictionary::reset() noexcept +{ + mDataIB.mSmallTopologiesLUT.fill(-1); + mDataOB.mSmallTopologiesLUT.fill(-1); + mDataIB.mVectorOfIDs.clear(); + mDataOB.mVectorOfIDs.clear(); +} + +void TopologyDictionary::resetMaps(bool IB) noexcept +{ + auto& data = (IB) ? mDataIB : mDataOB; + data.mCommonMap.clear(); + data.mGroupMap.clear(); +} + std::ostream& operator<<(std::ostream& os, const its3::TopologyDictionary& dict) { int ID = 0; - for (auto& p : dict.mVectorOfIDs) { + os << "--- InnerBarrel:\n"; + for (auto& p : dict.mDataIB.mVectorOfIDs) { + os << "ID: " << ID++ << " Hash: " << p.mHash << " ErrX: " << p.mErrX << " ErrZ : " << p.mErrZ << " xCOG: " << p.mXCOG << " zCOG: " << p.mZCOG << " Npixles: " << p.mNpixels << " Frequency: " << p.mFrequency << " isGroup : " << std::boolalpha << p.mIsGroup << '\n' + << p.mPattern << '\n' + << "*********************************************************" << '\n' + << '\n'; + } + ID = 0; + os << "--- OuterBarrel:\n"; + for (auto& p : dict.mDataOB.mVectorOfIDs) { os << "ID: " << ID++ << " Hash: " << p.mHash << " ErrX: " << p.mErrX << " ErrZ : " << p.mErrZ << " xCOG: " << p.mXCOG << " zCOG: " << p.mZCOG << " Npixles: " << p.mNpixels << " Frequency: " << p.mFrequency << " isGroup : " << std::boolalpha << p.mIsGroup << '\n' << p.mPattern << '\n' << "*********************************************************" << '\n' @@ -48,24 +88,36 @@ std::ostream& operator<<(std::ostream& os, const its3::TopologyDictionary& dict) void TopologyDictionary::writeBinaryFile(const std::string& outputfile) { std::ofstream file_output(outputfile, std::ios::out | std::ios::binary); - for (auto& p : mVectorOfIDs) { - file_output.write(reinterpret_cast(&p.mHash), sizeof(unsigned long)); - file_output.write(reinterpret_cast(&p.mErrX), sizeof(float)); - file_output.write(reinterpret_cast(&p.mErrZ), sizeof(float)); - file_output.write(reinterpret_cast(&p.mErr2X), sizeof(float)); - file_output.write(reinterpret_cast(&p.mErr2Z), sizeof(float)); - file_output.write(reinterpret_cast(&p.mXCOG), sizeof(float)); - file_output.write(reinterpret_cast(&p.mZCOG), sizeof(float)); - file_output.write(reinterpret_cast(&p.mNpixels), sizeof(int)); - file_output.write(reinterpret_cast(&p.mFrequency), sizeof(double)); - file_output.write(reinterpret_cast(&p.mIsGroup), sizeof(bool)); - file_output.write(const_cast(reinterpret_cast(&p.mPattern.getPattern())), - sizeof(unsigned char) * (itsmft::ClusterPattern::kExtendedPatternBytes)); + if (!file_output) { + throw std::runtime_error(fmt::format("Cannot open output file %s!", outputfile)); } + + auto writeData = [](auto& file_output, auto& data) { + auto size = data.mVectorOfIDs.size(); + file_output.write(reinterpret_cast(&size), sizeof(size)); + for (auto& p : data.mVectorOfIDs) { + file_output.write(reinterpret_cast(&p.mHash), sizeof(unsigned long)); + file_output.write(reinterpret_cast(&p.mErrX), sizeof(float)); + file_output.write(reinterpret_cast(&p.mErrZ), sizeof(float)); + file_output.write(reinterpret_cast(&p.mErr2X), sizeof(float)); + file_output.write(reinterpret_cast(&p.mErr2Z), sizeof(float)); + file_output.write(reinterpret_cast(&p.mXCOG), sizeof(float)); + file_output.write(reinterpret_cast(&p.mZCOG), sizeof(float)); + file_output.write(reinterpret_cast(&p.mNpixels), sizeof(int)); + file_output.write(reinterpret_cast(&p.mFrequency), sizeof(double)); + file_output.write(reinterpret_cast(&p.mIsGroup), sizeof(bool)); + file_output.write(const_cast(reinterpret_cast(&p.mPattern.getPattern())), + sizeof(unsigned char) * (itsmft::ClusterPattern::kExtendedPatternBytes)); + } + }; + + writeData(file_output, mDataIB); + writeData(file_output, mDataOB); + file_output.close(); } -int TopologyDictionary::readFromFile(const std::string& fname) +void TopologyDictionary::readFromFile(const std::string& fname) { LOGP(info, "Reading TopologyDictionary from File '{}'", fname); if (o2::utils::Str::endsWith(fname, ".root")) { @@ -76,59 +128,63 @@ int TopologyDictionary::readFromFile(const std::string& fname) } else { throw std::runtime_error(fmt::format("Unrecognized format {}", fname)); } - return 0; } -int TopologyDictionary::readBinaryFile(const std::string& fname) +void TopologyDictionary::readBinaryFile(const std::string& fname) { - mVectorOfIDs.clear(); - mCommonMap.clear(); - for (auto& p : mSmallTopologiesLUT) { - p = -1; - } + reset(); + std::ifstream in(fname.data(), std::ios::in | std::ios::binary); - itsmft::GroupStruct gr; - int groupID = 0; if (!in.is_open()) { LOG(error) << "The file " << fname << " coud not be opened"; throw std::runtime_error("The file coud not be opened"); } else { - while (in.read(reinterpret_cast(&gr.mHash), sizeof(unsigned long))) { - in.read(reinterpret_cast(&gr.mErrX), sizeof(float)); - in.read(reinterpret_cast(&gr.mErrZ), sizeof(float)); - in.read(reinterpret_cast(&gr.mErr2X), sizeof(float)); - in.read(reinterpret_cast(&gr.mErr2Z), sizeof(float)); - in.read(reinterpret_cast(&gr.mXCOG), sizeof(float)); - in.read(reinterpret_cast(&gr.mZCOG), sizeof(float)); - in.read(reinterpret_cast(&gr.mNpixels), sizeof(int)); - in.read(reinterpret_cast(&gr.mFrequency), sizeof(double)); - in.read(reinterpret_cast(&gr.mIsGroup), sizeof(bool)); - in.read(const_cast(reinterpret_cast(&gr.mPattern.getPattern())), sizeof(unsigned char) * (itsmft::ClusterPattern::kExtendedPatternBytes)); - mVectorOfIDs.push_back(gr); - if (!gr.mIsGroup) { - mCommonMap.insert(std::make_pair(gr.mHash, groupID)); - if (gr.mPattern.getUsedBytes() == 1) { - mSmallTopologiesLUT[(gr.mPattern.getColumnSpan() - 1) * 255 + (int)gr.mPattern.getByte(2)] = groupID; + + auto readData = [](auto& in, auto& data) { + int groupID = 0; + std::size_t size{}, cur{}; + itsmft::GroupStruct gr; + in.read(reinterpret_cast(&size), sizeof(std::size_t)); + while (cur++ != size) { + in.read(reinterpret_cast(&gr.mHash), sizeof(unsigned long)); + in.read(reinterpret_cast(&gr.mErrX), sizeof(float)); + in.read(reinterpret_cast(&gr.mErrZ), sizeof(float)); + in.read(reinterpret_cast(&gr.mErr2X), sizeof(float)); + in.read(reinterpret_cast(&gr.mErr2Z), sizeof(float)); + in.read(reinterpret_cast(&gr.mXCOG), sizeof(float)); + in.read(reinterpret_cast(&gr.mZCOG), sizeof(float)); + in.read(reinterpret_cast(&gr.mNpixels), sizeof(int)); + in.read(reinterpret_cast(&gr.mFrequency), sizeof(double)); + in.read(reinterpret_cast(&gr.mIsGroup), sizeof(bool)); + in.read(const_cast(reinterpret_cast(&gr.mPattern.getPattern())), sizeof(unsigned char) * (itsmft::ClusterPattern::kExtendedPatternBytes)); + data.mVectorOfIDs.push_back(gr); + if (!gr.mIsGroup) { + data.mCommonMap.insert(std::make_pair(gr.mHash, groupID)); + if (gr.mPattern.getUsedBytes() == 1) { + data.mSmallTopologiesLUT[(gr.mPattern.getColumnSpan() - 1) * 255 + (int)gr.mPattern.getByte(2)] = groupID; + } + } else { + data.mGroupMap.insert(std::make_pair((int)(gr.mHash >> 32) & 0x00000000ffffffff, groupID)); } - } else { - mGroupMap.insert(std::make_pair((int)(gr.mHash >> 32) & 0x00000000ffffffff, groupID)); + groupID++; } - groupID++; - } + }; + + readData(in, mDataIB); + readData(in, mDataOB); } in.close(); - return 0; } -TH1F* TopologyDictionary::getTopologyDistribution(const std::string_view hname) const +TH1F* TopologyDictionary::getTopologyDistribution(const std::string_view hname, bool IB) const { - int dictSize = getSize(); - auto* histo = new TH1F(hname.data(), ";Topology ID;Frequency", dictSize, -0.5, dictSize - 0.5); + int dictSize = getSize(IB); + auto* histo = new TH1F(hname.data(), Form("%s;Topology ID;Frequency", (IB) ? "InnerBarrel" : "OuterBarrel"), dictSize, -0.5, dictSize - 0.5); histo->SetFillColor(kRed); histo->SetFillStyle(3005); histo->SetDrawOption("histo"); for (int i = 0; i < dictSize; i++) { - histo->Fill(i, getFrequency(i)); + histo->Fill(i, getFrequency(i, IB)); } return histo; } @@ -136,18 +192,19 @@ TH1F* TopologyDictionary::getTopologyDistribution(const std::string_view hname) template math_utils::Point3D TopologyDictionary::getClusterCoordinates(const itsmft::CompClusterExt& cl) const { + static std::array mIBSegmentations{0, 1, 2}; math_utils::Point3D locCl; if (!its3::constants::detID::isDetITS3(cl.getSensorID())) { o2::itsmft::SegmentationAlpide::detectorToLocalUnchecked(cl.getRow(), cl.getCol(), locCl); - locCl.SetX(locCl.X() + this->getXCOG(cl.getPatternID()) * itsmft::SegmentationAlpide::PitchRow); - locCl.SetZ(locCl.Z() + this->getZCOG(cl.getPatternID()) * itsmft::SegmentationAlpide::PitchCol); + locCl.SetX(locCl.X() + this->getXCOG(cl.getPatternID(), false) * itsmft::SegmentationAlpide::PitchRow); + locCl.SetZ(locCl.Z() + this->getZCOG(cl.getPatternID(), false) * itsmft::SegmentationAlpide::PitchCol); } else { auto layer = its3::constants::detID::getDetID2Layer(cl.getSensorID()); - its3::SuperSegmentations[layer].detectorToLocalUnchecked(cl.getRow(), cl.getCol(), locCl); - locCl.SetX(locCl.X() + this->getXCOG(cl.getPatternID()) * its3::SegmentationSuperAlpide::mPitchRow); - locCl.SetZ(locCl.Z() + this->getZCOG(cl.getPatternID()) * its3::SegmentationSuperAlpide::mPitchCol); + mIBSegmentations[layer].detectorToLocalUnchecked(cl.getRow(), cl.getCol(), locCl); + locCl.SetX(locCl.X() + this->getXCOG(cl.getPatternID(), true) * its3::SegmentationMosaix::PitchRow); + locCl.SetZ(locCl.Z() + this->getZCOG(cl.getPatternID(), true) * its3::SegmentationMosaix::PitchCol); float xCurved{0.f}, yCurved{0.f}; - its3::SuperSegmentations[layer].flatToCurved(locCl.X(), locCl.Y(), xCurved, yCurved); + mIBSegmentations[layer].flatToCurved(locCl.X(), locCl.Y(), xCurved, yCurved); locCl.SetXYZ(xCurved, yCurved, locCl.Z()); } return locCl; @@ -156,6 +213,7 @@ math_utils::Point3D TopologyDictionary::getClusterCoordinates(const itsmft::C template math_utils::Point3D TopologyDictionary::getClusterCoordinates(const itsmft::CompClusterExt& cl, const itsmft::ClusterPattern& patt, bool isGroup) { + static std::array mIBSegmentations{0, 1, 2}; auto refRow = cl.getRow(); auto refCol = cl.getCol(); float xCOG = 0, zCOG = 0; @@ -169,9 +227,9 @@ math_utils::Point3D TopologyDictionary::getClusterCoordinates(const itsmft::C o2::itsmft::SegmentationAlpide::detectorToLocalUnchecked(refRow + xCOG, refCol + zCOG, locCl); } else { auto layer = its3::constants::detID::getDetID2Layer(cl.getSensorID()); - its3::SuperSegmentations[layer].detectorToLocalUnchecked(refRow + xCOG, refCol + zCOG, locCl); + mIBSegmentations[layer].detectorToLocalUnchecked(refRow + xCOG, refCol + zCOG, locCl); float xCurved{0.f}, yCurved{0.f}; - its3::SuperSegmentations[layer].flatToCurved(locCl.X(), locCl.Y(), xCurved, yCurved); + mIBSegmentations[layer].flatToCurved(locCl.X(), locCl.Y(), xCurved, yCurved); locCl.SetXYZ(xCurved, yCurved, locCl.Z()); } return locCl; diff --git a/Detectors/Upgrades/ITS3/simulation/CMakeLists.txt b/Detectors/Upgrades/ITS3/simulation/CMakeLists.txt index 17a8fd2748b87..8c4722012224d 100644 --- a/Detectors/Upgrades/ITS3/simulation/CMakeLists.txt +++ b/Detectors/Upgrades/ITS3/simulation/CMakeLists.txt @@ -14,8 +14,12 @@ o2_add_library(ITS3Simulation src/ITS3Services.cxx src/DescriptorInnerBarrelITS3.cxx src/Digitizer.cxx + src/DigiParams.cxx + src/ITS3DPLDigitizerParam.cxx + src/ChipDigitsContainer.cxx + src/ChipSimResponse.cxx PUBLIC_LINK_LIBRARIES O2::SimulationDataFormat - O2::ITSBase O2::ITSMFTSimulation + O2::ITSBase O2::ITSMFTSimulation O2::ITSMFTBase ROOT::Physics) o2_target_root_dictionary(ITS3Simulation @@ -23,6 +27,10 @@ o2_target_root_dictionary(ITS3Simulation include/ITS3Simulation/ITS3Services.h include/ITS3Simulation/DescriptorInnerBarrelITS3.h include/ITS3Simulation/Digitizer.h + include/ITS3Simulation/DigiParams.h + include/ITS3Simulation/ITS3DPLDigitizerParam.h + include/ITS3Simulation/ChipDigitsContainer.h + include/ITS3Simulation/ChipSimResponse.h ) -o2_data_file(COPY data DESTINATION Detectors/ITS3/simulation) \ No newline at end of file +o2_data_file(COPY data DESTINATION Detectors/ITS3/simulation) diff --git a/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/ChipDigitsContainer.h b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/ChipDigitsContainer.h new file mode 100644 index 0000000000000..ae2fd487ece0e --- /dev/null +++ b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/ChipDigitsContainer.h @@ -0,0 +1,63 @@ +// 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_CHIPDIGITSCONTAINER_ +#define ALICEO2_ITS3_CHIPDIGITSCONTAINER_ + +#include "ITSMFTBase/SegmentationAlpide.h" // Base class in o2::itsmft namespace +#include "ITSMFTSimulation/ChipDigitsContainer.h" // Base class in o2::itsmft namespace +#include "ITS3Base/SegmentationMosaix.h" // OB segmentation implementation +#include "ITS3Base/SpecsV2.h" // Provides SpecsV2::isDetITS3() interface +#include "ITS3Simulation/DigiParams.h" // ITS3-specific DigiParams interface +#include + +namespace o2 +{ +namespace its3 +{ + +// IB uses the Alpide segmentation, +// OB uses the Mosaix segmentation. +using SegmentationIB = SegmentationMosaix; +using SegmentationOB = o2::itsmft::SegmentationAlpide; +class ChipDigitsContainer : public o2::itsmft::ChipDigitsContainer +{ + private: + bool innerBarrel; ///< true if the chip belongs to the inner barrel (IB), false if outer barrel (OB) + int maxRows; ///< maximum number of rows + int maxCols; ///< maximum number of columns + + public: + explicit ChipDigitsContainer(UShort_t idx = 0); + + /// Returns whether the chip is in the inner barrel (IB) + void setChipIndex(UShort_t idx) + { + o2::itsmft::ChipDigitsContainer::setChipIndex(idx); + innerBarrel = constants::detID::isDetITS3(getChipIndex()); + maxRows = innerBarrel ? SegmentationIB::NRows : SegmentationOB::NRows; + maxCols = innerBarrel ? SegmentationIB::NCols : SegmentationOB::NCols; + } + + int getMaxRows() const { return maxRows; } + int getMaxCols() const { return maxCols; } + bool isIB() const; + /// Adds noise digits, deleted the one using the itsmft::DigiParams interface + void addNoise(UInt_t rofMin, UInt_t rofMax, const o2::itsmft::DigiParams* params, int maxRows = o2::itsmft::SegmentationAlpide::NRows, int maxCols = o2::itsmft::SegmentationAlpide::NCols) = delete; + void addNoise(UInt_t rofMin, UInt_t rofMax, const o2::its3::DigiParams* params); + + ClassDefNV(ChipDigitsContainer, 1); +}; + +} // namespace its3 +} // namespace o2 + +#endif // ALICEO2_ITS3_CHIPDIGITSCONTAINER_ \ No newline at end of file diff --git a/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/ChipSimResponse.h b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/ChipSimResponse.h new file mode 100644 index 0000000000000..e04b65c132b88 --- /dev/null +++ b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/ChipSimResponse.h @@ -0,0 +1,30 @@ +#ifndef ALICEO2_ITS3SIMULATION_CHIPSIMRESPONSE_H +#define ALICEO2_ITS3SIMULATION_CHIPSIMRESPONSE_H + +#include "ITSMFTSimulation/AlpideSimResponse.h" + +namespace o2 +{ +namespace its3 +{ + +class ChipSimResponse : public o2::itsmft::AlpideSimResponse +{ + public: + ChipSimResponse() = default; + ChipSimResponse(const ChipSimResponse& other) = default; + + float getRespCentreDep() const { return mRespCentreDep; } + void computeCentreFromData(); + void initData(int tableNumber, std::string dataPath, const bool quiet = true); + + private: + float mRespCentreDep = 0.f; + + ClassDefNV(ChipSimResponse, 1); +}; + +} // namespace its3 +} // namespace o2 + +#endif // ALICEO2_ITS3SIMULATION_CHIPSIMRESPONSE_H \ No newline at end of file diff --git a/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/DescriptorInnerBarrelITS3.h b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/DescriptorInnerBarrelITS3.h index 80536a14d99c2..d1b54f81face4 100644 --- a/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/DescriptorInnerBarrelITS3.h +++ b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/DescriptorInnerBarrelITS3.h @@ -45,9 +45,9 @@ class DescriptorInnerBarrelITS3 : public o2::its::DescriptorInnerBarrel int mNumLayers{constants::nLayers}; // wrapper volume properties - double mWrapperMinRadiusITS3{1.8}; - double mWrapperMaxRadiusITS3{4.}; - double mWrapperZSpanITS3{20.}; + static constexpr double mWrapperMinRadiusITS3{1.8}; + static constexpr double mWrapperMaxRadiusITS3{4.}; + static constexpr double mWrapperZSpanITS3{constants::segment::length + 5.}; private: std::array, constants::nLayers> mIBLayers; diff --git a/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/DigiParams.h b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/DigiParams.h new file mode 100644 index 0000000000000..751bb84ad036b --- /dev/null +++ b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/DigiParams.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 ITS3_DIGIPARAMS_H +#define ITS3_DIGIPARAMS_H + +#include "ITSMFTSimulation/DigiParams.h" +#include "ITS3Simulation/ChipSimResponse.h" + +namespace o2::its3 +{ + +class DigiParams final : public o2::itsmft::DigiParams +{ + private: + float mIBNoisePerPixel = 1.e-8; + int mIBChargeThreshold = 150; ///< charge threshold in Nelectrons + int mIBMinChargeToAccount = 15; ///< minimum charge contribution to account + int mIBNSimSteps = 18; ///< number of steps in response simulation + float mIBNSimStepsInv = 0; ///< its inverse + + public: + DigiParams(); + + void setIBNoisePerPixel(float v) { mIBNoisePerPixel = v; } + float getIBNoisePerPixel() const { return mIBNoisePerPixel; } + + void setIBChargeThreshold(int v, float frac2Account = 0.1); + int getIBChargeThreshold() const { return mIBChargeThreshold; } + + void setIBNSimSteps(int v); + int getIBNSimSteps() const { return mIBNSimSteps; } + float getIBNSimStepsInv() const { return mIBNSimStepsInv; } + + int getIBMinChargeToAccount() const { return mIBMinChargeToAccount; } + + const o2::itsmft::AlpideSimResponse* getAlpSimResponse() const = delete; + void setAlpSimResponse(const o2::itsmft::AlpideSimResponse* par) = delete; + + const o2::itsmft::AlpideSimResponse* getOBSimResponse() const { return mOBSimResponse; } + void setOBSimResponse(const o2::itsmft::AlpideSimResponse* response) { mOBSimResponse = response; } + + const o2::itsmft::AlpideSimResponse* getIBSimResponse() const { return mIBSimResponse; } + void setIBSimResponse(const o2::itsmft::AlpideSimResponse* response); + + bool hasResponseFunctions() const { return mIBSimResponse != nullptr && mOBSimResponse != nullptr; } + + void print() const final; + + private: + const o2::itsmft::AlpideSimResponse* mOBSimResponse = nullptr; //!< pointer to external response + const o2::its3::ChipSimResponse* mIBSimResponse = nullptr; //!< pointer to external response + + ClassDefNV(DigiParams, 1); +}; + +} // namespace o2::its3 + +#endif diff --git a/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/Digitizer.h b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/Digitizer.h index 7ece842b6f61f..edc5583c03d5a 100644 --- a/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/Digitizer.h +++ b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/Digitizer.h @@ -18,15 +18,16 @@ #include #include -#include "Rtypes.h" // for Digitizer::Class -#include "TObject.h" // for TObject +#include "Rtypes.h" +#include "TObject.h" -#include "ITSMFTSimulation/ChipDigitsContainer.h" #include "ITSMFTSimulation/AlpideSimResponse.h" -#include "ITSMFTSimulation/DigiParams.h" #include "ITSMFTSimulation/Hit.h" #include "ITSBase/GeometryTGeo.h" -#include "ITS3Base/SegmentationSuperAlpide.h" +#include "ITS3Base/SegmentationMosaix.h" +#include "ITS3Simulation/DigiParams.h" +#include "ITS3Simulation/ChipDigitsContainer.h" +#include "ITS3Simulation/ChipSimResponse.h" #include "DataFormatsITSMFT/Digit.h" #include "DataFormatsITSMFT/ROFRecord.h" #include "CommonDataFormat/InteractionRecord.h" @@ -35,6 +36,7 @@ namespace o2::its3 { + class Digitizer : public TObject { using ExtraDig = std::vector; ///< container for extra contributions to PreDigits @@ -44,8 +46,8 @@ class Digitizer : public TObject void setMCLabels(o2::dataformats::MCTruthContainer* mclb) { mMCLabels = mclb; } void setROFRecords(std::vector* rec) { mROFRecords = rec; } - o2::itsmft::DigiParams& getParams() { return (o2::itsmft::DigiParams&)mParams; } - const o2::itsmft::DigiParams& getParams() const { return mParams; } + o2::its3::DigiParams& getParams() { return mParams; } + const o2::its3::DigiParams& getParams() const { return mParams; } void init(); @@ -62,9 +64,6 @@ class Digitizer : public TObject bool isContinuous() const { return mParams.isContinuous(); } void fillOutputContainer(uint32_t maxFrame = 0xffffffff); - void setDigiParams(const o2::itsmft::DigiParams& par) { mParams = par; } - const o2::itsmft::DigiParams& getDigitParams() const { return mParams; } - // provide the common itsmft::GeometryTGeo to access matrices and segmentation void setGeometry(const o2::its::GeometryTGeo* gm) { mGeometry = gm; } @@ -80,7 +79,7 @@ class Digitizer : public TObject private: void processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID, int srcID); - void registerDigits(o2::itsmft::ChipDigitsContainer& chip, uint32_t roFrame, float tInROF, int nROF, + void registerDigits(o2::its3::ChipDigitsContainer& chip, uint32_t roFrame, float tInROF, int nROF, uint16_t row, uint16_t col, int nEle, o2::MCCompLabel& lbl); ExtraDig* getExtraDigBuffer(uint32_t roFrame) @@ -97,7 +96,7 @@ class Digitizer : public TObject static constexpr float sec2ns = 1e9; - o2::itsmft::DigiParams mParams; ///< digitization parameters + o2::its3::DigiParams mParams; ///< digitization parameters o2::InteractionTimeRecord mEventTime; ///< global event time and interaction record o2::InteractionRecord mIRFirstSampledTF; ///< IR of the 1st sampled IR, noise-only ROFs will be inserted till this IR only double mCollisionTimeWrtROF{}; @@ -108,11 +107,19 @@ class Digitizer : public TObject uint32_t mEventROFrameMin = 0xffffffff; ///< lowest RO frame for processed events (w/o automatic noise ROFs) uint32_t mEventROFrameMax = 0; ///< highest RO frame forfor processed events (w/o automatic noise ROFs) - o2::itsmft::AlpideSimResponse* mAlpSimResp = nullptr; // simulated response + static constexpr std::array mIBSegmentations{0, 1, 2}; + + o2::its3::ChipSimResponse* mSimRespIB = nullptr; // simulated response for IB + o2::itsmft::AlpideSimResponse* mSimRespOB = nullptr; // simulated response for OB + bool mSimRespIBOrientation{false}; // wether the orientation in the IB response function is flipped + float mSimRespIBShift{0.f}; // adjusting the Y-shift in the IB response function to match sensor local coord. + float mSimRespIBScaleX{1.f}; // scale x-local coordinate to response function x-coordinate + float mSimRespIBScaleZ{1.f}; // scale z-local coordinate to response function z-coordinate + float mSimRespOBShift{0.f}; // adjusting the Y-shift in the OB response function to match sensor local coord. const o2::its::GeometryTGeo* mGeometry = nullptr; ///< ITS3 geometry - std::vector mChips; ///< Array of chips digits containers + std::vector mChips; ///< Array of chips digits containers std::deque> mExtraBuff; ///< burrer (per roFrame) for extra digits std::vector* mDigits = nullptr; //! output digits @@ -121,8 +128,9 @@ class Digitizer : public TObject const o2::itsmft::NoiseMap* mDeadChanMap = nullptr; - ClassDef(Digitizer, 4); + ClassDef(Digitizer, 5); }; + } // namespace o2::its3 #endif /* ALICEO2_ITS3_DIGITIZER_H */ diff --git a/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/ITS3DPLDigitizerParam.h b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/ITS3DPLDigitizerParam.h new file mode 100644 index 0000000000000..4081d365a6dac --- /dev/null +++ b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/ITS3DPLDigitizerParam.h @@ -0,0 +1,31 @@ +// 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". + +#ifndef ALICEO2_ITS3DPLDIGITIZERPARAM_H_ +#define ALICEO2_ITS3DPLDIGITIZERPARAM_H_ + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2 +{ +namespace its3 +{ + +struct ITS3DPLDigitizerParam : public o2::conf::ConfigurableParamHelper { + float IBNoisePerPixel = 1.e-8; ///< MOSAIX Noise per channel + int IBChargeThreshold = 150; ///< charge threshold in Nelectrons for IB + int IBMinChargeToAccount = 15; ///< minimum charge contribution to account for IB + int nIBSimSteps = 18; ///< number of steps in response for IB + + O2ParamDef(ITS3DPLDigitizerParam, "ITS3DPLDigitizerParam"); +}; + +} // namespace its3 +} // namespace o2 + +#endif \ No newline at end of file diff --git a/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/ITS3Layer.h b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/ITS3Layer.h index 7543650e04a71..fd9195f9ee228 100644 --- a/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/ITS3Layer.h +++ b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/ITS3Layer.h @@ -20,13 +20,12 @@ #include #include -#include "Framework/Logger.h" -#include +#include "ITS3Base/SpecsV2.h" namespace o2::its3 { -/// This class defines the Geometry for the ITS3 using TGeo. +/// This class defines the geometry for the ITS3 IB layers. class ITS3Layer { // The hierarchy will be the following: @@ -45,8 +44,8 @@ class ITS3Layer kTile, kRSU, kSegment, - kCarbonForm, kChip, + kCarbonForm, kLayer, kAll, }; @@ -56,11 +55,10 @@ class ITS3Layer return mNames[static_cast((b == BuildLevel::kAll) ? BuildLevel::kLayer : b)]; } - explicit ITS3Layer(int layer = 0) : mNLayer(layer) - { - LOGP(debug, "Called on {} layer {}", layer, mNLayer); - init(); - } + explicit ITS3Layer(int layer = 0) : mNLayer(layer), + mR(o2::its3::constants::radii[mNLayer]), + mRmin(o2::its3::constants::radiiInner[mNLayer]), + mRmax(o2::its3::constants::radiiOuter[mNLayer]) {} explicit ITS3Layer(TGeoVolume* motherVolume, int layer = 0) : ITS3Layer(layer) { @@ -82,6 +80,7 @@ class ITS3Layer TGeoMedium* mSilicon{nullptr}; TGeoMedium* mAir{nullptr}; TGeoMedium* mCarbon{nullptr}; + TGeoMedium* mCopper{nullptr}; void getMaterials(bool create = false); TGeoMedium* getMaterial(const char* matName, bool create = false); @@ -97,10 +96,12 @@ class ITS3Layer uint8_t mNLayer{0}; // Layer number double mR{0}; // Middle Radius - double mRmin{}; // Minimum Radius + double mRmin{0}; // Minimum Radius double mRmax{0}; // Maximum Radius - // Individual Pieces + // Individual pieces + // since TGeo manages the resources itself one should not use these pointers + // after initializition anymore! TGeoVolume* mPixelArray{nullptr}; TGeoVolumeAssembly* mTile{nullptr}; TGeoVolumeAssembly* mRSU{nullptr}; @@ -109,7 +110,7 @@ class ITS3Layer TGeoVolumeAssembly* mCarbonForm{nullptr}; TGeoVolumeAssembly* mLayer{nullptr}; - ClassDef(ITS3Layer, 2); + ClassDef(ITS3Layer, 3); }; } // namespace o2::its3 diff --git a/Detectors/Upgrades/ITS3/simulation/src/ChipDigitsContainer.cxx b/Detectors/Upgrades/ITS3/simulation/src/ChipDigitsContainer.cxx new file mode 100644 index 0000000000000..308fbf506946c --- /dev/null +++ b/Detectors/Upgrades/ITS3/simulation/src/ChipDigitsContainer.cxx @@ -0,0 +1,63 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details. +// 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 "ITS3Simulation/ChipDigitsContainer.h" + +namespace o2 +{ +namespace its3 +{ + +ChipDigitsContainer::ChipDigitsContainer(UShort_t idx) + : o2::itsmft::ChipDigitsContainer(idx) {} + +bool ChipDigitsContainer::isIB() const +{ + return innerBarrel; +} + +void ChipDigitsContainer::addNoise(UInt_t rofMin, UInt_t rofMax, const o2::its3::DigiParams* params) +{ + UInt_t row = 0; + UInt_t col = 0; + Int_t nhits = 0; + constexpr float ns2sec = 1e-9; + float mean = 0.f; + int nel = 0; + + if (isIB()) { + // Inner barrel: use ITS3-specific noise interface with OB segmentation. + mean = params->getIBNoisePerPixel() * SegmentationOB::NPixels; + nel = static_cast(params->getIBChargeThreshold() * 1.1); + } else { + // Outer barrel: use base class noise interface with IB segmentation. + mean = params->getNoisePerPixel() * SegmentationIB::NPixels; + nel = static_cast(params->getChargeThreshold() * 1.1); + } + + for (UInt_t rof = rofMin; rof <= rofMax; ++rof) { + nhits = gRandom->Poisson(mean); + for (Int_t i = 0; i < nhits; ++i) { + row = gRandom->Integer(maxRows); + col = gRandom->Integer(maxCols); + if (mNoiseMap && mNoiseMap->isNoisy(mChipIndex, row, col)) + continue; + if (mDeadChanMap && mDeadChanMap->isNoisy(mChipIndex, row, col)) + continue; + auto key = getOrderingKey(rof, row, col); + if (!findDigit(key)) + addDigit(key, rof, row, col, nel, o2::MCCompLabel(true)); + } + } +} + +} // namespace its3 +} // namespace o2 \ No newline at end of file diff --git a/Detectors/Upgrades/ITS3/simulation/src/ChipSimResponse.cxx b/Detectors/Upgrades/ITS3/simulation/src/ChipSimResponse.cxx new file mode 100644 index 0000000000000..67a416bf9d42e --- /dev/null +++ b/Detectors/Upgrades/ITS3/simulation/src/ChipSimResponse.cxx @@ -0,0 +1,51 @@ +#include "ITS3Simulation/ChipSimResponse.h" +#include +#include + +using namespace o2::its3; + +ClassImp(o2::its3::ChipSimResponse); + +void ChipSimResponse::initData(int tableNumber, std::string dataPath, const bool quiet) +{ + AlpideSimResponse::initData(tableNumber, dataPath, quiet); + computeCentreFromData(); +} + +void ChipSimResponse::computeCentreFromData() +{ + std::vector zVec, qVec; + const int npix = o2::itsmft::AlpideRespSimMat::getNPix(); + + for (int iz = 0; iz < mNBinDpt; ++iz) { + size_t bin = iz + mNBinDpt * (0 + mNBinRow * 0); + const auto& mat = mData[bin]; + float val = mat.getValue(npix / 2, npix / 2); + float gz = mDptMin + iz / mStepInvDpt; + zVec.push_back(gz); + qVec.push_back(val); + } + + std::vector> zqPairs; + for (size_t i = 0; i < zVec.size(); ++i) { + zqPairs.emplace_back(zVec[i], qVec[i]); + } + std::sort(zqPairs.begin(), zqPairs.end()); + zVec.clear(); + qVec.clear(); + for (auto& p : zqPairs) { + zVec.push_back(p.first); + qVec.push_back(p.second); + } + + float intQ = 0.f, intZQ = 0.f; + for (size_t i = 0; i + 1 < zVec.size(); ++i) { + float z0 = zVec[i], z1 = zVec[i + 1]; + float q0 = qVec[i], q1 = qVec[i + 1]; + float dz = z1 - z0; + intQ += 0.5f * (q0 + q1) * dz; + intZQ += 0.5f * (z0 * q0 + z1 * q1) * dz; + } + + mRespCentreDep = (intQ > 0.f) ? intZQ / intQ : 0.f; +} diff --git a/Detectors/Upgrades/ITS3/simulation/src/DescriptorInnerBarrelITS3.cxx b/Detectors/Upgrades/ITS3/simulation/src/DescriptorInnerBarrelITS3.cxx index 6d1bc621b5287..540e1d41f1c62 100644 --- a/Detectors/Upgrades/ITS3/simulation/src/DescriptorInnerBarrelITS3.cxx +++ b/Detectors/Upgrades/ITS3/simulation/src/DescriptorInnerBarrelITS3.cxx @@ -18,14 +18,14 @@ ClassImp(DescriptorInnerBarrelITS3); void DescriptorInnerBarrelITS3::createLayer(int iLayer, TGeoVolume* dest) { - LOGP(info, "ITS3-IB: Creating Layer {}", iLayer); + LOGP(debug, "ITS3-IB: Creating Layer {}", iLayer); mIBLayers[iLayer] = std::make_unique(iLayer); mIBLayers[iLayer]->createLayer(dest); } void DescriptorInnerBarrelITS3::createServices(TGeoVolume* dest) { - LOGP(info, "ITS3-IB: Creating Services"); + LOGP(debug, "ITS3-IB: Creating Services"); mServices = std::make_unique(); mServices->createCYSSAssembly(dest); } diff --git a/Detectors/Upgrades/ITS3/simulation/src/DigiParams.cxx b/Detectors/Upgrades/ITS3/simulation/src/DigiParams.cxx new file mode 100644 index 0000000000000..7e7af98d7088d --- /dev/null +++ b/Detectors/Upgrades/ITS3/simulation/src/DigiParams.cxx @@ -0,0 +1,86 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file DigiParams.cxx +/// \brief Implementation of the ITS3 digitization steering params + +#include "Framework/Logger.h" +#include "ITS3Simulation/DigiParams.h" + +ClassImp(o2::its3::DigiParams); + +namespace o2::its3 +{ + +DigiParams::DigiParams() +{ + // make sure the defaults are consistent + setIBNSimSteps(mIBNSimSteps); +} + +void DigiParams::setIBNSimSteps(int v) +{ + // set number of sampling steps in silicon + mIBNSimSteps = v > 0 ? v : 1; + mIBNSimStepsInv = 1.f / mIBNSimSteps; +} + +void DigiParams::setIBChargeThreshold(int v, float frac2Account) +{ + // set charge threshold for digits creation and its fraction to account + // contribution from single hit + mIBChargeThreshold = v; + mIBMinChargeToAccount = v * frac2Account; + if (mIBMinChargeToAccount < 0 || mIBMinChargeToAccount > mIBChargeThreshold) { + mIBMinChargeToAccount = mIBChargeThreshold; + } + LOG(info) << "Set Mosaix charge threshold to " << mIBChargeThreshold + << ", single hit will be accounted from " << mIBMinChargeToAccount + << " electrons"; +} + +void DigiParams::print() const +{ + // print settings + LOGF(info, "ITS3 DigiParams settings:"); + LOGF(info, "Continuous readout : %s", isContinuous() ? "ON" : "OFF"); + LOGF(info, "Readout Frame Length(ns) : %f", getROFrameLength()); + LOGF(info, "Strobe delay (ns) : %f", getStrobeDelay()); + LOGF(info, "Strobe length (ns) : %f", getStrobeLength()); + LOGF(info, "IB Threshold (N electrons) : %d", getIBChargeThreshold()); + LOGF(info, "OB Threshold (N electrons) : %d", getChargeThreshold()); + LOGF(info, "Min N electrons to account for IB : %d", getIBMinChargeToAccount()); + LOGF(info, "Min N electrons to account for OB : %d", getMinChargeToAccount()); + LOGF(info, "Number of charge sharing steps of IB : %d", getIBNSimSteps()); + LOGF(info, "Number of charge sharing steps of OB : %d", getNSimSteps()); + LOGF(info, "ELoss to N electrons factor : %e", getEnergyToNElectrons()); + LOGF(info, "Noise level per pixel of IB : %e", getIBNoisePerPixel()); + LOGF(info, "Noise level per pixel of OB : %e", getNoisePerPixel()); + LOGF(info, "Charge time-response:\n"); + getSignalShape().print(); +} + +void DigiParams::setIBSimResponse(const o2::itsmft::AlpideSimResponse* response) +{ + // This method is compatible with ChipSimResponse and will automatically handle center computation. + auto chipResp = static_cast(response); + if (chipResp) { + mIBSimResponse = chipResp; + if (mIBSimResponse->getRespCentreDep() == 0.f) { + const_cast(mIBSimResponse)->computeCentreFromData(); + } + } else { + LOG(error) << "Provided response is not of type ChipSimResponse. Cannot proceed."; + return; + } +} + +} // namespace o2::its3 diff --git a/Detectors/Upgrades/ITS3/simulation/src/Digitizer.cxx b/Detectors/Upgrades/ITS3/simulation/src/Digitizer.cxx index f1519c1d04063..1d1d15a91f89b 100644 --- a/Detectors/Upgrades/ITS3/simulation/src/Digitizer.cxx +++ b/Detectors/Upgrades/ITS3/simulation/src/Digitizer.cxx @@ -14,6 +14,7 @@ #include "ITSMFTBase/SegmentationAlpide.h" #include "ITS3Simulation/Digitizer.h" +#include "ITS3Base/ITS3Params.h" #include "MathUtils/Cartesian.h" #include "SimulationDataFormat/MCTruthContainer.h" #include "DetectorsRaw/HBFUtils.h" @@ -21,12 +22,13 @@ #include "Framework/Logger.h" #include +#include #include #include using o2::itsmft::Hit; -using Segmentation = o2::itsmft::SegmentationAlpide; -using SuperSegmentation = o2::its3::SegmentationSuperAlpide; +using SegmentationOB = o2::itsmft::SegmentationAlpide; +using SegmentationIB = o2::its3::SegmentationMosaix; using o2::itsmft::AlpideRespSimMat; using o2::itsmft::PreDigit; @@ -44,14 +46,46 @@ void Digitizer::init() } } - if (mParams.getAlpSimResponse() == nullptr) { - std::string responseFile = "$(O2_ROOT)/share/Detectors/ITSMFT/data/AlpideResponseData/AlpideResponseData.root"; - LOGP(info, "Loading AlpideSimRespnse from file: {}", responseFile); - auto file = TFile::Open(responseFile.data()); - mAlpSimResp = (o2::itsmft::AlpideSimResponse*)file->Get("response0"); // We use by default the alpide response for Vbb=0V - mParams.setAlpSimResponse(mAlpSimResp); + if (!mParams.hasResponseFunctions()) { + auto loadSetResponseFunc = [&](const char* fileIB, const char* nameIB, const char* fileOB, const char* nameOB) { + LOGP(info, "Loading response function IB={}:{} ; OB={}:{}", nameIB, fileIB, nameOB, fileOB); + auto fIB = TFile::Open(fileIB, "READ"); + if (!fIB || fIB->IsZombie() || !fIB->IsOpen()) { + LOGP(fatal, "Cannot open file {}", fileIB); + } + auto fOB = TFile::Open(fileOB, "READ"); + if (!fOB || fOB->IsZombie() || !fOB->IsOpen()) { + LOGP(fatal, "Cannot open file {}", fileOB); + } + mParams.setIBSimResponse(mSimRespIB = fIB->Get(nameIB)); + mParams.setOBSimResponse(mSimRespOB = fOB->Get(nameOB)); + fIB->Close(); + fOB->Close(); + }; + + if (const auto& func = ITS3Params::Instance().chipResponseFunction; func == "Alpide") { + constexpr const char* responseFile = "$(O2_ROOT)/share/Detectors/ITSMFT/data/AlpideResponseData/AlpideResponseData.root"; + loadSetResponseFunc(responseFile, "response0", responseFile, "response0"); + mSimRespIBScaleX = o2::itsmft::SegmentationAlpide::PitchRow / SegmentationIB::PitchRow; + mSimRespIBScaleZ = o2::itsmft::SegmentationAlpide::PitchCol / SegmentationIB::PitchCol; + } else if (func == "APTS") { + constexpr const char* responseFileIB = "$(O2_ROOT)/share/Detectors/Upgrades/ITS3/data/ITS3ChipResponseData/APTSResponseData.root"; + constexpr const char* responseFileOB = "$(O2_ROOT)/share/Detectors/ITSMFT/data/AlpideResponseData/AlpideResponseData.root"; + loadSetResponseFunc(responseFileIB, "response1", responseFileOB, "response0"); + mSimRespIBScaleX = constants::pixelarray::pixels::apts::pitchX / SegmentationIB::PitchRow; + mSimRespIBScaleZ = constants::pixelarray::pixels::apts::pitchZ / SegmentationIB::PitchCol; + mSimRespIBOrientation = true; + } else { + LOGP(fatal, "ResponseFunction '{}' not implemented!", func); + } + mSimRespIBShift = mSimRespIB->getDepthMax() - constants::silicon::thickness / 2.f; + mSimRespOBShift = mSimRespOB->getDepthMax() - SegmentationOB::SensorLayerThickness / 2.f; } + mParams.print(); + LOGP(info, "IB shift = {} ; OB shift = {}", mSimRespIBShift, mSimRespOBShift); + LOGP(info, "IB pixel scale on x = {} ; z = {}", mSimRespIBScaleX, mSimRespIBScaleZ); + LOGP(info, "IB response orientation: {}", mSimRespIBOrientation ? "flipped" : "normal"); mIRFirstSampledTF = o2::raw::HBFUtils::Instance().getFirstSampledTFIR(); } @@ -142,11 +176,7 @@ void Digitizer::fillOutputContainer(uint32_t frameLast) auto& extra = *(mExtraBuff.front().get()); for (size_t iChip{0}; iChip < mChips.size(); ++iChip) { auto& chip = mChips[iChip]; - if (constants::detID::isDetITS3(iChip)) { // Check if this is a chip of ITS3 - chip.addNoise(mROFrameMin, mROFrameMin, &mParams, SuperSegmentation::mNRows, SuperSegmentation::mNCols); - } else { - chip.addNoise(mROFrameMin, mROFrameMin, &mParams); - } + chip.addNoise(mROFrameMin, mROFrameMin, &mParams); auto& buffer = chip.getPreDigits(); if (buffer.empty()) { continue; @@ -159,7 +189,7 @@ void Digitizer::fillOutputContainer(uint32_t frameLast) break; // is the digit ROFrame from the key > the max requested frame } auto& preDig = iter->second; // preDigit - if (preDig.charge >= mParams.getChargeThreshold()) { + if (preDig.charge >= (chip.isIB() ? mParams.getIBChargeThreshold() : mParams.getChargeThreshold())) { int digID = mDigits->size(); mDigits->emplace_back(chip.getChipIndex(), preDig.row, preDig.col, preDig.charge); mMCLabels->addElement(digID, preDig.labelRef.label); @@ -226,20 +256,19 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID } // here we start stepping in the depth of the sensor to generate charge diffision - float nStepsInv = mParams.getNSimStepsInv(); - int nSteps = mParams.getNSimSteps(); int detID{hit.GetDetectorID()}; int layer = mGeometry->getLayer(detID); const auto& matrix = mGeometry->getMatrixL2G(detID); - bool innerBarrel{layer < 3}; + int nSteps = chip.isIB() ? mParams.getIBNSimSteps() : mParams.getNSimSteps(); + float nStepsInv = chip.isIB() ? mParams.getIBNSimStepsInv() : mParams.getNSimStepsInv(); math_utils::Vector3D xyzLocS, xyzLocE; xyzLocS = matrix ^ (hit.GetPosStart()); // Global hit coordinates to local detector coordinates xyzLocE = matrix ^ (hit.GetPos()); - if (innerBarrel) { + if (chip.isIB()) { // transform the point on the curved surface to a flat one float xFlatE{0.f}, yFlatE{0.f}, xFlatS{0.f}, yFlatS{0.f}; - SuperSegmentations[layer].curvedToFlat(xyzLocS.X(), xyzLocS.Y(), xFlatS, yFlatS); - SuperSegmentations[layer].curvedToFlat(xyzLocE.X(), xyzLocE.Y(), xFlatE, yFlatE); + mIBSegmentations[layer].curvedToFlat(xyzLocS.X(), xyzLocS.Y(), xFlatS, yFlatS); + mIBSegmentations[layer].curvedToFlat(xyzLocE.X(), xyzLocE.Y(), xFlatE, yFlatE); // update the local coordinates with the flattened ones xyzLocS.SetXYZ(xFlatS, yFlatS, xyzLocS.Z()); xyzLocE.SetXYZ(xFlatE, yFlatE, xyzLocE.Z()); @@ -253,16 +282,16 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID xyzLocS += stepH; // Adjust start position to the middle of the first step xyzLocE -= stepH; // Adjust end position to the middle of the last step int rowS = -1, colS = -1, rowE = -1, colE = -1, nSkip = 0; - if (innerBarrel) { + if (chip.isIB()) { // get entrance pixel row and col - while (!SuperSegmentations[layer].localToDetector(xyzLocS.X(), xyzLocS.Z(), rowS, colS)) { // guard-ring ? + while (!mIBSegmentations[layer].localToDetector(xyzLocS.X(), xyzLocS.Z(), rowS, colS)) { // guard-ring ? if (++nSkip >= nSteps) { return; // did not enter to sensitive matrix } xyzLocS += step; } // get exit pixel row and col - while (!SuperSegmentations[layer].localToDetector(xyzLocE.X(), xyzLocE.Z(), rowE, colE)) { // guard-ring ? + while (!mIBSegmentations[layer].localToDetector(xyzLocE.X(), xyzLocE.Z(), rowE, colE)) { // guard-ring ? if (++nSkip >= nSteps) { return; // did not enter to sensitive matrix } @@ -270,14 +299,14 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID } } else { // get entrance pixel row and col - while (!Segmentation::localToDetector(xyzLocS.X(), xyzLocS.Z(), rowS, colS)) { // guard-ring ? + while (!SegmentationOB::localToDetector(xyzLocS.X(), xyzLocS.Z(), rowS, colS)) { // guard-ring ? if (++nSkip >= nSteps) { return; // did not enter to sensitive matrix } xyzLocS += step; } // get exit pixel row and col - while (!Segmentation::localToDetector(xyzLocE.X(), xyzLocE.Z(), rowE, colE)) { // guard-ring ? + while (!SegmentationOB::localToDetector(xyzLocE.X(), xyzLocE.Z(), rowE, colE)) { // guard-ring ? if (++nSkip >= nSteps) { return; // did not enter to sensitive matrix } @@ -294,23 +323,17 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID } rowS -= AlpideRespSimMat::NPix / 2; rowE += AlpideRespSimMat::NPix / 2; - if (rowS < 0) { - rowS = 0; - } + rowS = std::max(rowS, 0); - int maxNrows{innerBarrel ? SuperSegmentation::mNRows : Segmentation::NRows}; - int maxNcols{innerBarrel ? SuperSegmentation::mNCols : Segmentation::NCols}; - if (rowE >= maxNrows) { - rowE = maxNrows - 1; - } + const int maxNrows{chip.isIB() ? SegmentationIB::NRows : SegmentationOB::NRows}; + const int maxNcols{chip.isIB() ? SegmentationIB::NCols : SegmentationOB::NCols}; + + rowE = std::min(rowE, maxNrows - 1); colS -= AlpideRespSimMat::NPix / 2; colE += AlpideRespSimMat::NPix / 2; - if (colS < 0) { - colS = 0; - } - if (colE >= maxNcols) { - colE = maxNcols - 1; - } + colS = std::max(colS, 0); + colE = std::min(colE, maxNcols - 1); + int rowSpan = rowE - rowS + 1, colSpan = colE - colS + 1; // size of plaquet where some response is expected float respMatrix[rowSpan][colSpan]; // response accumulated here std::fill(&respMatrix[0][0], &respMatrix[0][0] + rowSpan * colSpan, 0.f); @@ -327,22 +350,22 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID // take into account that the AlpideSimResponse depth defintion has different min/max boundaries // although the max should coincide with the surface of the epitaxial layer, which in the chip // local coordinates has Y = +SensorLayerThickness/2 - float thickness = innerBarrel ? SuperSegmentation::mSensorLayerThickness : Segmentation::SensorLayerThickness; - xyzLocS.SetY(xyzLocS.Y() + mAlpSimResp->getDepthMax() - thickness / 2.); + xyzLocS.SetY(xyzLocS.Y() + ((chip.isIB()) ? mSimRespIBShift : mSimRespOBShift)); + // collect charge in evey pixel which might be affected by the hit for (int iStep = nSteps; iStep--;) { // Get the pixel ID - if (innerBarrel) { - SuperSegmentations[layer].localToDetector(xyzLocS.X(), xyzLocS.Z(), row, col); + if (chip.isIB()) { + mIBSegmentations[layer].localToDetector(xyzLocS.X(), xyzLocS.Z(), row, col); } else { - Segmentation::localToDetector(xyzLocS.X(), xyzLocS.Z(), row, col); + SegmentationOB::localToDetector(xyzLocS.X(), xyzLocS.Z(), row, col); } if (row != rowPrev || col != colPrev) { // update pixel and coordinates of its center - if (innerBarrel) { - if (!SuperSegmentations[layer].detectorToLocal(row, col, cRowPix, cColPix)) { + if (chip.isIB()) { + if (!mIBSegmentations[layer].detectorToLocal(row, col, cRowPix, cColPix)) { continue; } - } else if (!Segmentation::detectorToLocal(row, col, cRowPix, cColPix)) { + } else if (!SegmentationOB::detectorToLocal(row, col, cRowPix, cColPix)) { continue; // should not happen } rowPrev = row; @@ -350,9 +373,17 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID } bool flipCol = false, flipRow = false; // note that response needs coordinates along column row (locX) (locZ) then depth (locY) - double rowMax{0.5f * (innerBarrel ? SuperSegmentation::mPitchRow : Segmentation::PitchRow)}; - double colMax{0.5f * (innerBarrel ? SuperSegmentation::mPitchCol : Segmentation::PitchCol)}; - auto rspmat = mAlpSimResp->getResponse(xyzLocS.X() - cRowPix, xyzLocS.Z() - cColPix, xyzLocS.Y(), flipRow, flipCol, rowMax, colMax); + float rowMax{}, colMax{}; + const AlpideRespSimMat* rspmat{nullptr}; + if (chip.isIB()) { + rowMax = 0.5f * SegmentationIB::PitchRow * mSimRespIBScaleX; + colMax = 0.5f * SegmentationIB::PitchCol * mSimRespIBScaleZ; + rspmat = mSimRespIB->getResponse(mSimRespIBScaleX * (xyzLocS.X() - cRowPix), mSimRespIBScaleZ * (xyzLocS.Z() - cColPix), xyzLocS.Y(), flipRow, flipCol, rowMax, colMax); + } else { + rowMax = 0.5f * SegmentationOB::PitchRow; + colMax = 0.5f * SegmentationOB::PitchCol; + rspmat = mSimRespOB->getResponse(xyzLocS.X() - cRowPix, xyzLocS.Z() - cColPix, xyzLocS.Y(), flipRow, flipCol, rowMax, colMax); + } xyzLocS += step; if (rspmat == nullptr) { @@ -369,7 +400,7 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID if (colDest < 0 || colDest >= colSpan) { continue; } - respMatrix[rowDest][colDest] += rspmat->getValue(irow, icol, flipRow, flipCol); + respMatrix[rowDest][colDest] += rspmat->getValue(irow, icol, ((chip.isIB() && mSimRespIBOrientation) ? !flipRow : flipRow), flipCol); } } } @@ -386,7 +417,7 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID } int nEle = gRandom->Poisson(nElectrons * nEleResp); // total charge in given pixel // ignore charge which have no chance to fire the pixel - if (nEle < mParams.getMinChargeToAccount()) { + if (nEle < (chip.isIB() ? mParams.getIBChargeThreshold() : mParams.getChargeThreshold())) { continue; } uint16_t colIS = icol + colS; @@ -395,7 +426,7 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID } } -void Digitizer::registerDigits(o2::itsmft::ChipDigitsContainer& chip, uint32_t roFrame, float tInROF, int nROF, +void Digitizer::registerDigits(o2::its3::ChipDigitsContainer& chip, uint32_t roFrame, float tInROF, int nROF, uint16_t row, uint16_t col, int nEle, o2::MCCompLabel& lbl) { // Register digits for given pixel, accounting for the possible signal contribution to @@ -409,7 +440,7 @@ void Digitizer::registerDigits(o2::itsmft::ChipDigitsContainer& chip, uint32_t r tStrobe += mParams.getROFrameLength(); // for the next ROF // discard too small contributions, they have no chance to produce a digit - if (nEleROF < mParams.getMinChargeToAccount()) { + if (nEleROF < (chip.isIB() ? mParams.getIBChargeThreshold() : mParams.getChargeThreshold())) { continue; } if (roFr > mEventROFrameMax) { diff --git a/GPU/GPUTracking/Base/opencl2/GPUReconstructionOCL2Internals.h b/Detectors/Upgrades/ITS3/simulation/src/ITS3DPLDigitizerParam.cxx similarity index 59% rename from GPU/GPUTracking/Base/opencl2/GPUReconstructionOCL2Internals.h rename to Detectors/Upgrades/ITS3/simulation/src/ITS3DPLDigitizerParam.cxx index 8debdc47be8e8..003372759644c 100644 --- a/GPU/GPUTracking/Base/opencl2/GPUReconstructionOCL2Internals.h +++ b/Detectors/Upgrades/ITS3/simulation/src/ITS3DPLDigitizerParam.cxx @@ -9,20 +9,13 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file GPUReconstructionOCL2Internals.h -/// \author David Rohr, Sergey Gorbunov +#include "ITS3Simulation/ITS3DPLDigitizerParam.h" -#ifndef GPUTPCGPUTRACKEROPENCLINTERNALS2_H -#define GPUTPCGPUTRACKEROPENCLINTERNALS2_H - -#include "GPUReconstructionOCLInternals.h" - -namespace GPUCA_NAMESPACE::gpu +namespace o2 { - -struct GPUReconstructionOCL2Internals : public GPUReconstructionOCLInternals { -}; - -} // namespace GPUCA_NAMESPACE::gpu - -#endif +namespace its3 +{ +static auto& sDigitizerParamITS3 = o2::its3::ITS3DPLDigitizerParam::Instance(); +} // namespace its3 +} // namespace o2 +O2ParamImpl(o2::its3::ITS3DPLDigitizerParam) \ No newline at end of file diff --git a/Detectors/Upgrades/ITS3/simulation/src/ITS3Layer.cxx b/Detectors/Upgrades/ITS3/simulation/src/ITS3Layer.cxx index 26e47e03057c2..8dc94e339c793 100644 --- a/Detectors/Upgrades/ITS3/simulation/src/ITS3Layer.cxx +++ b/Detectors/Upgrades/ITS3/simulation/src/ITS3Layer.cxx @@ -18,11 +18,11 @@ #include "TGeoVolume.h" #include "TGeoCompositeShape.h" +#include "Framework/Logger.h" #include "CommonConstants/MathConstants.h" #include "ITSBase/GeometryTGeo.h" #include "ITS3Base/SpecsV2.h" #include "ITS3Simulation/ITS3Layer.h" -#include "fairlogger/Logger.h" namespace o2m = o2::constants::math; namespace its3c = o2::its3::constants; @@ -31,13 +31,6 @@ namespace o2::its3 { using its3TGeo = o2::its::GeometryTGeo; -void ITS3Layer::init() -{ - mR = its3c::radii[mNLayer]; - mRmin = its3c::radiiInner[mNLayer]; - mRmax = its3c::radiiOuter[mNLayer]; -} - void ITS3Layer::getMaterials(bool create) { if (gGeoManager == nullptr) { @@ -47,6 +40,7 @@ void ITS3Layer::getMaterials(bool create) mSilicon = getMaterial("IT3_SI$", create); mAir = getMaterial("IT3_AIR$", create); mCarbon = getMaterial("IT3_CARBON$", create); + mCopper = getMaterial("IT3_COPPER$", create); } TGeoMedium* ITS3Layer::getMaterial(const char* matName, bool create) @@ -58,11 +52,11 @@ TGeoMedium* ITS3Layer::getMaterial(const char* matName, bool create) } else { // create dummy auto matDummy = gGeoManager->GetMaterial("MAT_DUMMY$"); if (matDummy == nullptr) { - LOGP(info, "Created Dummy material"); + LOGP(warn, "Created Dummy material"); matDummy = new TGeoMaterial("MAT_DUMMY$", 26.98, 13, 2.7); } mat = new TGeoMedium(matName, 1, matDummy); - LOGP(info, "Created medium {}", matName); + LOGP(warn, "Created medium {}", matName); } } return mat; @@ -75,12 +69,10 @@ void ITS3Layer::createLayer(TGeoVolume* motherVolume) createLayerImpl(); mBuilt = true; - LOGP(info, "ITS3-Layer: Created Layer {} with mR={} (minR={}, maxR={})", mNLayer, mR, mRmin, mRmax); if (motherVolume == nullptr) { return; } // Add it to motherVolume - LOGP(debug, " `-> Attaching to motherVolume '{}'", motherVolume->GetName()); auto* trans = new TGeoTranslation(0, 0, -constants::segment::lengthSensitive / 2.); motherVolume->AddNode(mLayer, 0, trans); } @@ -91,15 +83,9 @@ void ITS3Layer::createPixelArray() return; } // A pixel array is pure silicon and the sensitive part of our detector. - // It will be segmented into a 442x156 matrix by the - // SuperSegmentationAlpide. - // Pixel Array is just a longer version of the biasing but starts in phi at - // biasPhi2. using namespace its3c::pixelarray; - double pixelArrayPhi1 = constants::tile::readout::width / mR * o2m::Rad2Deg; - double pixelArrayPhi2 = width / mR * o2m::Rad2Deg + pixelArrayPhi1; - auto pixelArray = new TGeoTubeSeg(mRmin, mRmax, length / 2., - pixelArrayPhi1, pixelArrayPhi2); + double pixelArrayPhi = width / mR * o2m::Rad2Deg; + auto pixelArray = new TGeoTubeSeg(mRmin, mRmax, length / 2., 0, pixelArrayPhi); mPixelArray = new TGeoVolume(its3TGeo::getITS3PixelArrayPattern(mNLayer), pixelArray, mSilicon); mPixelArray->SetLineColor(color); mPixelArray->RegisterYourself(); @@ -131,8 +117,9 @@ void ITS3Layer::createTile() mTile->AddNode(readoutVol, 0, zMoveReadout); // Pixel Array is just a longer version of the biasing but starts in phi at - // biasPhi2. - mTile->AddNode(mPixelArray, 0); + // readoutPhi2. + auto phiRotPixelArray = new TGeoRotation(Form("its3PhiPixelArrayOffset_%d", mNLayer), readoutPhi2, 0, 0); + mTile->AddNode(mPixelArray, 0, phiRotPixelArray); // Biasing double biasPhi1 = constants::pixelarray::width / mR * o2m::Rad2Deg + readoutPhi2; @@ -199,7 +186,7 @@ void ITS3Layer::createRSU() // Rotation for top half and vertical mirroring double phi = width / mR * o2m::Rad2Deg; - auto rot = new TGeoRotation("", 0, 0, -phi); + auto rot = new TGeoRotation(Form("its3RotHalfBarrel_%d", mNLayer), 0, 0, -phi); rot->ReflectY(true); // Upper Left @@ -276,11 +263,19 @@ void ITS3Layer::createChip() mChip = new TGeoVolumeAssembly(its3TGeo::getITS3ChipPattern(mNLayer)); mChip->VisibleDaughters(); + auto phiOffset = constants::segment::width / mR * o2m::Rad2Deg; for (unsigned int i{0}; i < constants::nSegments[mNLayer]; ++i) { - double phiOffset = constants::segment::width / mR * o2m::Rad2Deg; - auto rot = new TGeoRotation("", 0, 0, phiOffset * i); + auto rot = new TGeoRotation(Form("its3PhiSegmentOffset_%d_%d", mNLayer, i), 0, 0, phiOffset * i); mChip->AddNode(mSegment, i, rot); } + + // Add metal stack positioned radially outward + auto zMoveMetal = new TGeoTranslation(0, 0, constants::metalstack::length / 2. - constants::segment::lec::length); + auto metal = new TGeoTubeSeg(mRmax, mRmax + constants::metalstack::thickness, constants::metalstack::length / 2., 0, constants::nSegments[mNLayer] * phiOffset); + auto metalVol = new TGeoVolume(Form("metal%d", mNLayer), metal, mCopper); + metalVol->SetLineColor(constants::metalstack::color); + metalVol->RegisterYourself(); + mChip->AddNode(metalVol, 0, zMoveMetal); } void ITS3Layer::createCarbonForm() @@ -296,7 +291,7 @@ void ITS3Layer::createCarbonForm() mCarbonForm->VisibleDaughters(); double dRadius = -1; if (mNLayer < 2) { - dRadius = constants::radii[mNLayer + 1] - constants::radii[mNLayer] - constants::thickness; + dRadius = constants::radii[mNLayer + 1] - constants::radii[mNLayer] - constants::totalThickness; } else { dRadius = 0.7; // TODO: lack of carbon foam radius for layer 2, use 0.7mm as a temporary value } @@ -372,8 +367,8 @@ void ITS3Layer::createLayerImpl() // The offset is the right angle triangle of the middle radius with the // transverse axis. double phiOffset = std::asin(constants::equatorialGap / 2. / mR) * o2m::Rad2Deg; - auto rotTop = new TGeoRotation("", 0, 0, +phiOffset); - auto rotBot = new TGeoRotation("", 0, 0, phiOffset + 180); + auto rotTop = new TGeoRotation(Form("its3CarbonPhiOffsetTop_%d", mNLayer), 0, 0, +phiOffset); + auto rotBot = new TGeoRotation(Form("its3CarbonPhiOffsetBot_%d", mNLayer), 0, 0, phiOffset + 180); mLayer->AddNode(mCarbonForm, 0, rotTop); mLayer->AddNode(mCarbonForm, 1, rotBot); @@ -412,8 +407,7 @@ void ITS3Layer::buildPartial(TGeoVolume* motherVolume, TGeoMatrix* mat, BuildLev case BuildLevel::kLayer: [[fallthrough]]; default: - createLayerImpl(); - motherVolume->AddNode(mLayer, 0, mat); + createLayer(motherVolume); } LOGP(info, "Partially built ITS3-{}-{}", mNLayer, getName(level)); } diff --git a/Detectors/Upgrades/ITS3/simulation/src/ITS3SimulationLinkDef.h b/Detectors/Upgrades/ITS3/simulation/src/ITS3SimulationLinkDef.h index b9af595018a34..921512193f98b 100644 --- a/Detectors/Upgrades/ITS3/simulation/src/ITS3SimulationLinkDef.h +++ b/Detectors/Upgrades/ITS3/simulation/src/ITS3SimulationLinkDef.h @@ -18,6 +18,10 @@ #pragma link C++ class o2::its3::ITS3Layer + ; #pragma link C++ class o2::its3::ITS3Services + ; #pragma link C++ class o2::its3::DescriptorInnerBarrelITS3 + ; +#pragma link C++ class o2::its3::DigiParams + ; #pragma link C++ class o2::its3::Digitizer + ; +#pragma link C++ class o2::its3::ITS3DPLDigitizerParam + ; +#pragma link C++ class o2::its3::ChipDigitsContainer + ; +#pragma link C++ class o2::its3::ChipSimResponse + ; #endif diff --git a/Detectors/Vertexing/StrangenessTracking/include/StrangenessTracking/StrangenessTracker.h b/Detectors/Vertexing/StrangenessTracking/include/StrangenessTracking/StrangenessTracker.h index 302b1de201ab1..f8c3c0dcb670c 100644 --- a/Detectors/Vertexing/StrangenessTracking/include/StrangenessTracking/StrangenessTracker.h +++ b/Detectors/Vertexing/StrangenessTracking/include/StrangenessTracking/StrangenessTracker.h @@ -279,20 +279,20 @@ class StrangenessTracker for (unsigned int iClus{0}; iClus < ITSclus.size(); ++iClus) { auto& clus = ITSclus[iClus]; auto pattID = clus.getPatternID(); + auto ib = o2::its3::constants::detID::isDetITS3(clus.getChipID()); int npix; o2::itsmft::ClusterPattern patt; - if (pattID == o2::itsmft::CompCluster::InvalidPatternID || mdict->isGroup(pattID)) { + if (pattID == o2::itsmft::CompCluster::InvalidPatternID || mdict->isGroup(pattID, ib)) { patt.acquirePattern(pattIt); npix = patt.getNPixels(); } else { - npix = mdict->getNpixels(pattID); - patt = mdict->getPattern(pattID); + npix = mdict->getNpixels(pattID, ib); + patt = mdict->getPattern(pattID, ib); } clusSizeVec[iClus] = npix; } - // LOG(info) << " Patt Npixel: " << pattVec[0].getNPixels(); } #endif diff --git a/Detectors/Vertexing/StrangenessTracking/src/StrangenessTracker.cxx b/Detectors/Vertexing/StrangenessTracking/src/StrangenessTracker.cxx index d5d4951b1c707..720ef4c9d5d87 100644 --- a/Detectors/Vertexing/StrangenessTracking/src/StrangenessTracker.cxx +++ b/Detectors/Vertexing/StrangenessTracking/src/StrangenessTracker.cxx @@ -507,7 +507,7 @@ bool StrangenessTracker::updateTrack(const ITSCluster& clus, o2::track::TrackPar return false; } } - auto chi2 = std::abs(track.getPredictedChi2(clus)); // abs to be understood + auto chi2 = std::abs(track.getPredictedChi2Quiet(clus)); // abs to be understood LOG(debug) << "Chi2: " << chi2; if (chi2 > mStrParams->mMaxChi2 || chi2 < 0) { return false; diff --git a/Detectors/ZDC/calib/include/ZDCCalib/CalibParamZDC.h b/Detectors/ZDC/calib/include/ZDCCalib/CalibParamZDC.h index 38416a3ec9d99..2e2b91e07482f 100644 --- a/Detectors/ZDC/calib/include/ZDCCalib/CalibParamZDC.h +++ b/Detectors/ZDC/calib/include/ZDCCalib/CalibParamZDC.h @@ -27,6 +27,7 @@ namespace o2 namespace zdc { struct CalibParamZDC : public o2::conf::ConfigurableParamHelper { + bool dumpCalib = false; // Dump partial calibration object bool debugOutput = false; // Debug output bool rootOutput = true; // Output histograms to EOS std::string outputDir = "./"; // ROOT files output directory diff --git a/Detectors/ZDC/calib/include/ZDCCalib/InterCalib.h b/Detectors/ZDC/calib/include/ZDCCalib/InterCalib.h index d53d3ae1eec0f..6d625464c714b 100644 --- a/Detectors/ZDC/calib/include/ZDCCalib/InterCalib.h +++ b/Detectors/ZDC/calib/include/ZDCCalib/InterCalib.h @@ -80,6 +80,8 @@ class InterCalib void setInterCalibConfig(const InterCalibConfig* param) { mInterCalibConfig = param; }; const InterCalibConfig* getInterCalibConfig() const { return mInterCalibConfig; }; + InterCalibData& getData() { return mData; }; + void setVerbosity(int v) { mVerbosity = v; } int getVerbosity() const { return mVerbosity; } diff --git a/Detectors/ZDC/calib/include/ZDCCalib/InterCalibConfig.h b/Detectors/ZDC/calib/include/ZDCCalib/InterCalibConfig.h index d9e729cd57f3f..3bf1e488abb3e 100644 --- a/Detectors/ZDC/calib/include/ZDCCalib/InterCalibConfig.h +++ b/Detectors/ZDC/calib/include/ZDCCalib/InterCalibConfig.h @@ -36,9 +36,13 @@ struct InterCalibConfig { // Meaningful values are in the range of tower x centers i.e. from // 2.8 to 19.6 If one puts less than 2.8 then the computation will be // the same as for ZPA/ZPC with no cuts - double xcut_ZPA = 6; - double xcut_ZPC = 6; - double tower_cut_ZP = 0; + double xcut_ZPA = 0; + double xcut_ZPC = 0; + double rms_cut_ZP = 0; // RMS of ZP centroid can go from 0 to 8.4 cm + double towerCutLow_ZPA[4] = {0, 0, 0, 0}; // Applied to all ZP fits except ZPI + double towerCutLow_ZPC[4] = {0, 0, 0, 0}; // Applied to all ZP fits except ZPI + double towerCutHigh_ZPA[4] = {std::numeric_limits::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity()}; // Applied to all ZP fits except ZPI + double towerCutHigh_ZPC[4] = {std::numeric_limits::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity()}; // Applied to all ZP fits except ZPI bool cross_check = false; int nb1[NH] = {0}; /// 1D histogram: number of bins @@ -87,7 +91,7 @@ struct InterCalibConfig { enabled[7] = c7; enabled[8] = c8; } - ClassDefNV(InterCalibConfig, 4); + ClassDefNV(InterCalibConfig, 5); }; } // namespace zdc } // namespace o2 diff --git a/Detectors/ZDC/calib/include/ZDCCalib/WaveformCalibData.h b/Detectors/ZDC/calib/include/ZDCCalib/WaveformCalibData.h index 2818146d75f32..8701e3667b74a 100644 --- a/Detectors/ZDC/calib/include/ZDCCalib/WaveformCalibData.h +++ b/Detectors/ZDC/calib/include/ZDCCalib/WaveformCalibData.h @@ -87,6 +87,7 @@ struct WaveformCalibData { void setCreationTime(uint64_t ctime); void setN(int n); int saveDebugHistos(const std::string fn); + int dumpCalib(const std::string fn); ClassDefNV(WaveformCalibData, 1); }; diff --git a/Detectors/ZDC/calib/include/ZDCCalib/WaveformCalibEPN.h b/Detectors/ZDC/calib/include/ZDCCalib/WaveformCalibEPN.h index 929190f09d162..86dff268ee0ad 100644 --- a/Detectors/ZDC/calib/include/ZDCCalib/WaveformCalibEPN.h +++ b/Detectors/ZDC/calib/include/ZDCCalib/WaveformCalibEPN.h @@ -37,9 +37,11 @@ class WaveformCalibEPN const gsl::span& wave); int endOfRun(); int saveDebugHistos(const std::string fn = "ZDCWaveformCalibEPN.root"); + int dumpCalib(const std::string fn = "ZDCWaveformCalibEPNDump.root"); void setConfig(const WaveformCalibConfig* param) { mConfig = param; }; const WaveformCalibConfig* getConfig() const { return mConfig; }; void setSaveDebugHistos() { mSaveDebugHistos = true; } + void setDumpCalib() { mDumpCalib = true; } void setDontSaveDebugHistos() { mSaveDebugHistos = false; } void setVerbosity(int val) { mVerbosity = val; } WaveformCalibData mData; @@ -48,6 +50,7 @@ class WaveformCalibEPN private: bool mInitDone = false; bool mSaveDebugHistos = false; + bool mDumpCalib = false; int32_t mNBin = 0; int32_t mVerbosity = DbgMinimal; const WaveformCalibConfig* mConfig = nullptr; /// Configuration of intercalibration diff --git a/Detectors/ZDC/calib/include/ZDCCalib/ZDCDCSProcessor.h b/Detectors/ZDC/calib/include/ZDCCalib/ZDCDCSProcessor.h index 7f7bb40414781..ee5367376356f 100644 --- a/Detectors/ZDC/calib/include/ZDCCalib/ZDCDCSProcessor.h +++ b/Detectors/ZDC/calib/include/ZDCCalib/ZDCDCSProcessor.h @@ -15,6 +15,7 @@ #include #include #include "Framework/Logger.h" +#include "Framework/O2LongInt.h" #include "DetectorsDCS/DataPointCompositeObject.h" #include "DetectorsDCS/DataPointIdentifier.h" #include "DetectorsDCS/DataPointValue.h" @@ -39,10 +40,10 @@ using DPVAL = o2::dcs::DataPointValue; using DPCOM = o2::dcs::DataPointCompositeObject; struct ZDCDCSinfo { - std::pair firstValue; // first value seen by the ZDC DCS processor - std::pair lastValue; // last value seen by the ZDC DCS processor - std::pair midValue; // mid value seen by the ZDC DCS processor - std::pair maxChange; // maximum variation seen by the ZDC DCS processor + std::pair firstValue; // first value seen by the ZDC DCS processor + std::pair lastValue; // last value seen by the ZDC DCS processor + std::pair midValue; // mid value seen by the ZDC DCS processor + std::pair maxChange; // maximum variation seen by the ZDC DCS processor ZDCDCSinfo() { diff --git a/Detectors/ZDC/calib/src/InterCalib.cxx b/Detectors/ZDC/calib/src/InterCalib.cxx index dc75b172a3aca..f267be48761e5 100644 --- a/Detectors/ZDC/calib/src/InterCalib.cxx +++ b/Detectors/ZDC/calib/src/InterCalib.cxx @@ -210,12 +210,12 @@ void InterCalib::assign(int ih, bool ismod) } else if (ih == 5) { nid = 1; id = id_5; - LOG(warn) << "InterCalib::assign unimplemented coefficient ih = " << ih; + LOG(warn) << "InterCalib::assign is not implemented for coefficient ih = " << ih; return; } else if (ih == 6) { nid = 1; id = id_6; - LOG(warn) << "InterCalib::assign unimplemented coefficient ih = " << ih; + LOG(warn) << "InterCalib::assign is not implemented for coefficient ih = " << ih; return; } else if (ih == 7 || ih == 8) { nid = 4; @@ -246,15 +246,23 @@ void InterCalib::assign(int ih, bool ismod) if (oldval > 0) { val = val * mPar[ih][iid + 1]; } - if (mVerbosity > DbgZero) { + if (mTowerParamUpd.modified[ich]) { + LOGF(warn, "%s OVERWRITING MODIFIED PARAMETER %8.6f", ChannelNames[ich].data(), mTowerParamUpd.getTowerCalib(ich)); + LOGF(info, "%s updated %8.6f -> %8.6f", ChannelNames[ich].data(), oldval, val); + } else if (mVerbosity > DbgZero) { LOGF(info, "%s updated %8.6f -> %8.6f", ChannelNames[ich].data(), oldval, val); } mTowerParamUpd.setTowerCalib(ich, val, true); } else { - if (mVerbosity > DbgZero) { - LOGF(info, "%s NOT CHANGED %8.6f", ChannelNames[ich].data(), oldval); + // Check if another fit has already modified the parameters + if (mTowerParamUpd.modified[ich]) { + LOGF(warn, "%s NOT OVERWRITING MODIFIED PARAMETER %8.6f", ChannelNames[ich].data(), mTowerParamUpd.getTowerCalib(ich)); + } else { + if (mVerbosity > DbgZero) { + LOGF(info, "%s NOT CHANGED %8.6f", ChannelNames[ich].data(), oldval); + } + mTowerParamUpd.setTowerCalib(ich, oldval, false); } - mTowerParamUpd.setTowerCalib(ich, oldval, false); } } } @@ -294,6 +302,10 @@ int InterCalib::process(const char* hname, int ic) ih = HidZNI; } else if (hn.EqualTo("hZPI")) { ih = HidZPI; + } else if (hn.EqualTo("hZPAX")) { + ih = HidZPAX; + } else if (hn.EqualTo("hZPCX")) { + ih = HidZPCX; } else { LOGF(error, "Not recognized histogram name: %s\n", hname); return -1; @@ -434,18 +446,32 @@ void InterCalib::add(int ih, o2::dataformats::FlatHisto2D& h2) void InterCalib::cumulate(int ih, double tc, double t1, double t2, double t3, double t4, double w = 1) { + constexpr double minfty = -std::numeric_limits::infinity(); if (tc < mInterCalibConfig->cutLow[ih] || tc > mInterCalibConfig->cutHigh[ih]) { return; } - double val[NPAR] = {0, 0, 0, 0, 0, 1}; - val[0] = tc; - val[1] = t1; - val[2] = t2; - val[3] = t3; - val[4] = t4; - for (int32_t i = 0; i < NPAR; i++) { - for (int32_t j = i; j < NPAR; j++) { - mData.mSum[ih][i][j] += val[i] * val[j] * w; + if ((ih == HidZPA || ih == HidZPAX)) { + if (t1 < mInterCalibConfig->towerCutLow_ZPA[0] || t2 < mInterCalibConfig->towerCutLow_ZPA[1] || t3 < mInterCalibConfig->towerCutLow_ZPA[2] || t4 < mInterCalibConfig->towerCutLow_ZPA[3]) { + return; + } + if (t1 > mInterCalibConfig->towerCutHigh_ZPA[0] || t2 > mInterCalibConfig->towerCutHigh_ZPA[1] || t3 > mInterCalibConfig->towerCutHigh_ZPA[2] || t4 > mInterCalibConfig->towerCutHigh_ZPA[3]) { + return; + } + } + if (ih == HidZPC || ih == HidZPCX) { + if (t1 < mInterCalibConfig->towerCutLow_ZPC[0] || t2 < mInterCalibConfig->towerCutLow_ZPC[1] || t3 < mInterCalibConfig->towerCutLow_ZPC[2] || t4 < mInterCalibConfig->towerCutLow_ZPC[3]) { + return; + } + if (t1 > mInterCalibConfig->towerCutHigh_ZPC[0] || t2 > mInterCalibConfig->towerCutHigh_ZPC[1] || t3 > mInterCalibConfig->towerCutHigh_ZPC[2] || t4 > mInterCalibConfig->towerCutHigh_ZPC[3]) { + return; + } + } + double val[NPAR] = {tc, t1, t2, t3, t4, 1}; + if (tc > minfty && t1 > minfty && t2 > minfty && t3 > minfty && t4 > minfty) { + for (int32_t i = 0; i < NPAR; i++) { + for (int32_t j = i; j < NPAR; j++) { + mData.mSum[ih][i][j] += val[i] * val[j] * w; + } } } // mData.mSum[ih][5][5] contains the number of analyzed events @@ -470,6 +496,9 @@ void InterCalib::fcn(int& npar, double* gin, double& chi, double* par, int iflag chi += (i == 0 ? par[i] : -par[i]) * (j == 0 ? par[j] : -par[j]) * mAdd[i][j]; } } + // Following line modifies the chisquare computation (sum of squares of residuals) + // to perform orthogonal least squares instead of ordinary least squares minimization + chi = chi / (1 + par[1] * par[1] + par[2] * par[2] + par[3] * par[3] + par[4] * par[4]); } int InterCalib::mini(int ih) @@ -498,15 +527,11 @@ int InterCalib::mini(int ih) // Calibration cvoefficient is forced to and step is forced to zero mMn[ih]->mnparm(0, "c0", 1., 0., 1., 1., ierflg); - // Special fit for proton calorimeters: fit least exposed towers with using previous - // fit of all towers + // Special fit for proton calorimeters: fit least exposed towers + // starting from parameters of previous fit to all towers // Tower 1 - if (ih == HidZPCX) { - mMn[ih]->mnparm(1, "c1", mPar[HidZPC][1], 0, l_bnd, u_bnd, ierflg); - } else { - mMn[ih]->mnparm(1, "c1", start, step, l_bnd, u_bnd, ierflg); - } + mMn[ih]->mnparm(1, "c1", start, step, l_bnd, u_bnd, ierflg); // Tower 2 // Only two ZEM calorimeters: equalize response @@ -518,20 +543,11 @@ int InterCalib::mini(int ih) step = 0; } - if (ih == HidZPCX) { - mMn[ih]->mnparm(2, "c2", mPar[HidZPC][2], 0, l_bnd, u_bnd, ierflg); - } else { - mMn[ih]->mnparm(2, "c2", start, step, l_bnd, u_bnd, ierflg); - } + mMn[ih]->mnparm(2, "c2", start, step, l_bnd, u_bnd, ierflg); // Towers 3 and 4 - if (ih == HidZPAX) { - mMn[ih]->mnparm(3, "c3", mPar[HidZPA][3], 0, l_bnd, u_bnd, ierflg); - mMn[ih]->mnparm(4, "c4", mPar[HidZPA][4], 0, l_bnd, u_bnd, ierflg); - } else { - mMn[ih]->mnparm(3, "c3", start, step, l_bnd, u_bnd, ierflg); - mMn[ih]->mnparm(4, "c4", start, step, l_bnd, u_bnd, ierflg); - } + mMn[ih]->mnparm(3, "c3", start, step, l_bnd, u_bnd, ierflg); + mMn[ih]->mnparm(4, "c4", start, step, l_bnd, u_bnd, ierflg); // Offset l_bnd = mInterCalibConfig->l_bnd_o[ih]; @@ -551,13 +567,15 @@ int InterCalib::mini(int ih) l_bnd = mInterCalibConfig->l_bnd[ih]; u_bnd = mInterCalibConfig->u_bnd[ih]; for (int i = 1; i <= 4; i++) { - if (TMath::Abs(mPar[ih][i] - l_bnd) < 1e-3 || TMath::Abs(mPar[ih][i] - u_bnd) < 1e-3) { + if (TMath::Abs(mPar[ih][i] - l_bnd) < 1e-2 || TMath::Abs(mPar[ih][i] - u_bnd) < 1e-2) { retry = true; LOG(warn) << "ih=" << ih << " par " << i << " too close to boundaries"; if (ih == 1 || ih == 7) { - mMn[ih]->mnparm(i, parn[i], mTowerParam->tower_calib[IdZPAC + i], 0, l_bnd, u_bnd, ierflg); + // mMn[ih]->mnparm(i, parn[i], mTowerParam->tower_calib[IdZPAC + i], 0, l_bnd, u_bnd, ierflg); + mMn[ih]->mnparm(i, parn[i], mInterCalibConfig->start[ih], 0, l_bnd, u_bnd, ierflg); } else if (ih == 3 || ih == 8) { - mMn[ih]->mnparm(i, parn[i], mTowerParam->tower_calib[IdZPCC + i], 0, l_bnd, u_bnd, ierflg); + // mMn[ih]->mnparm(i, parn[i], mTowerParam->tower_calib[IdZPCC + i], 0, l_bnd, u_bnd, ierflg); + mMn[ih]->mnparm(i, parn[i], mInterCalibConfig->start[ih], 0, l_bnd, u_bnd, ierflg); } else { LOG(fatal) << "ERROR on InterCalib minimization ih=" << ih; } diff --git a/Detectors/ZDC/calib/src/InterCalibConfig.cxx b/Detectors/ZDC/calib/src/InterCalibConfig.cxx index f70420eb6b67a..b65a060255be7 100644 --- a/Detectors/ZDC/calib/src/InterCalibConfig.cxx +++ b/Detectors/ZDC/calib/src/InterCalibConfig.cxx @@ -28,7 +28,11 @@ void InterCalibConfig::print() const } LOG(info) << "xcut_ZPA = " << xcut_ZPA; LOG(info) << "xcut_ZPC = " << xcut_ZPC; - LOG(info) << "tower_cut_ZP = " << tower_cut_ZP; + LOG(info) << "towerCutLow_ZPA = {" << towerCutLow_ZPA[0] << ", " << towerCutLow_ZPA[1] << ", " << towerCutLow_ZPA[2] << ", " << towerCutLow_ZPA[3] << "};"; + LOG(info) << "towerCutHigh_ZPA = {" << towerCutHigh_ZPA[0] << ", " << towerCutHigh_ZPA[1] << ", " << towerCutHigh_ZPA[2] << ", " << towerCutHigh_ZPA[3] << "};"; + LOG(info) << "towerCutLow_ZPC = {" << towerCutLow_ZPC[0] << ", " << towerCutLow_ZPC[1] << ", " << towerCutLow_ZPC[2] << ", " << towerCutLow_ZPC[3] << "};"; + LOG(info) << "towerCutHigh_ZPC = {" << towerCutHigh_ZPC[0] << ", " << towerCutHigh_ZPC[1] << ", " << towerCutHigh_ZPC[2] << ", " << towerCutHigh_ZPC[3] << "};"; + LOG(info) << "rms_cut_ZP = " << rms_cut_ZP; if (cross_check) { LOG(warn) << "THIS IS A CROSS CHECK CONFIGURATION (vs SUM)"; } diff --git a/Detectors/ZDC/calib/src/InterCalibEPN.cxx b/Detectors/ZDC/calib/src/InterCalibEPN.cxx index 3f17d256c042f..2f7fd5f26ebf9 100644 --- a/Detectors/ZDC/calib/src/InterCalibEPN.cxx +++ b/Detectors/ZDC/calib/src/InterCalibEPN.cxx @@ -106,7 +106,7 @@ int InterCalibEPN::process(const gsl::span& RecBC, float x, rms; ev.centroidZPA(x, rms); cumulate(HidZPA, ev.EZDC(IdZPAC), ev.EZDC(IdZPA1), ev.EZDC(IdZPA2), ev.EZDC(IdZPA3), ev.EZDC(IdZPA4), 1.); - if (x < -(mInterCalibConfig->xcut_ZPA)) { + if (x < -(mInterCalibConfig->xcut_ZPA) && rms >= mInterCalibConfig->rms_cut_ZP) { cumulate(HidZPAX, ev.EZDC(IdZPAC), ev.EZDC(IdZPA1), ev.EZDC(IdZPA2), ev.EZDC(IdZPA3), ev.EZDC(IdZPA4), 1.); } } @@ -117,7 +117,7 @@ int InterCalibEPN::process(const gsl::span& RecBC, float x, rms; ev.centroidZPC(x, rms); cumulate(HidZPC, ev.EZDC(IdZPCC), ev.EZDC(IdZPC1), ev.EZDC(IdZPC2), ev.EZDC(IdZPC3), ev.EZDC(IdZPC4), 1.); - if (x > (mInterCalibConfig->xcut_ZPC)) { + if (x > (mInterCalibConfig->xcut_ZPC) && rms >= mInterCalibConfig->rms_cut_ZP) { cumulate(HidZPCX, ev.EZDC(IdZPCC), ev.EZDC(IdZPC1), ev.EZDC(IdZPC2), ev.EZDC(IdZPC3), ev.EZDC(IdZPC4), 1.); } } @@ -266,22 +266,33 @@ void InterCalibEPN::clear(int ih) void InterCalibEPN::cumulate(int ih, double tc, double t1, double t2, double t3, double t4, double w = 1) { + constexpr double minfty = -std::numeric_limits::infinity(); // printf("%s: ih=%d tc=%g t1=%g t2=%g t3=%g t4=%g w=%g\n",__func__,ih, tc, t1, t2, t3, t4, w); fflush(stdout); if (tc < mInterCalibConfig->cutLow[ih] || tc > mInterCalibConfig->cutHigh[ih]) { return; } - if ((ih == 7 || ih == 8) && (t1 < mInterCalibConfig->tower_cut_ZP || t2 < mInterCalibConfig->tower_cut_ZP || t3 < mInterCalibConfig->tower_cut_ZP || t4 < mInterCalibConfig->tower_cut_ZP)) { - return; + if ((ih == HidZPA || ih == HidZPAX)) { + if (t1 < mInterCalibConfig->towerCutLow_ZPA[0] || t2 < mInterCalibConfig->towerCutLow_ZPA[1] || t3 < mInterCalibConfig->towerCutLow_ZPA[2] || t4 < mInterCalibConfig->towerCutLow_ZPA[3]) { + return; + } + if (t1 > mInterCalibConfig->towerCutHigh_ZPA[0] || t2 > mInterCalibConfig->towerCutHigh_ZPA[1] || t3 > mInterCalibConfig->towerCutHigh_ZPA[2] || t4 > mInterCalibConfig->towerCutHigh_ZPA[3]) { + return; + } } - double val[NPAR] = {0, 0, 0, 0, 0, 1}; - val[0] = tc; - val[1] = t1; - val[2] = t2; - val[3] = t3; - val[4] = t4; - for (int32_t i = 0; i < NPAR; i++) { - for (int32_t j = i; j < NPAR; j++) { - mData.mSum[ih][i][j] += val[i] * val[j] * w; + if (ih == HidZPC || ih == HidZPCX) { + if (t1 < mInterCalibConfig->towerCutLow_ZPC[0] || t2 < mInterCalibConfig->towerCutLow_ZPC[1] || t3 < mInterCalibConfig->towerCutLow_ZPC[2] || t4 < mInterCalibConfig->towerCutLow_ZPC[3]) { + return; + } + if (t1 > mInterCalibConfig->towerCutHigh_ZPC[0] || t2 > mInterCalibConfig->towerCutHigh_ZPC[1] || t3 > mInterCalibConfig->towerCutHigh_ZPC[2] || t4 > mInterCalibConfig->towerCutHigh_ZPC[3]) { + return; + } + } + double val[NPAR] = {tc, t1, t2, t3, t4, 1}; + if (tc > minfty && t1 > minfty && t2 > minfty && t3 > minfty && t4 > minfty) { + for (int32_t i = 0; i < NPAR; i++) { + for (int32_t j = i; j < NPAR; j++) { + mData.mSum[ih][i][j] += val[i] * val[j] * w; + } } } // mData.mSum[ih][5][5] contains the number of analyzed events diff --git a/Detectors/ZDC/calib/src/WaveformCalibConfig.cxx b/Detectors/ZDC/calib/src/WaveformCalibConfig.cxx index f0aeff5d53fc7..923d53f27f734 100644 --- a/Detectors/ZDC/calib/src/WaveformCalibConfig.cxx +++ b/Detectors/ZDC/calib/src/WaveformCalibConfig.cxx @@ -20,9 +20,10 @@ WaveformCalibConfig::WaveformCalibConfig() cutLow[isig] = -std::numeric_limits::infinity(); cutHigh[isig] = std::numeric_limits::infinity(); } + // Firmware aligns signals within one sample for (int itdc = 0; itdc < NTDCChannels; itdc++) { - cutTimeLow[itdc] = -1.25; - cutTimeHigh[itdc] = 1.25; + cutTimeHigh[itdc] = o2::constants::lhc::LHCBunchSpacingNS / NTimeBinsPerBC; + cutTimeLow[itdc] = -cutTimeHigh[itdc]; } } diff --git a/Detectors/ZDC/calib/src/WaveformCalibData.cxx b/Detectors/ZDC/calib/src/WaveformCalibData.cxx index 759d85a6a0f88..a326242e21433 100644 --- a/Detectors/ZDC/calib/src/WaveformCalibData.cxx +++ b/Detectors/ZDC/calib/src/WaveformCalibData.cxx @@ -187,6 +187,21 @@ int WaveformCalibData::saveDebugHistos(const std::string fn) return 0; } +//______________________________________________________________________________ +int WaveformCalibData::dumpCalib(const std::string fn) +{ + TDirectory* cwd = gDirectory; + TFile* f = new TFile(fn.data(), "recreate"); + if (f->IsZombie()) { + LOG(error) << "Cannot create file: " << fn; + return 1; + } + f->WriteObjectAny((void*)this, o2::zdc::WaveformCalibData::Class(), "WaveformCalibData"); + f->Close(); + cwd->cd(); + return 0; +} + //______________________________________________________________________________ void WaveformCalibData::clear() { diff --git a/Detectors/ZDC/calib/src/WaveformCalibEPN.cxx b/Detectors/ZDC/calib/src/WaveformCalibEPN.cxx index cda158e9f5b6a..02e9bc15a933f 100644 --- a/Detectors/ZDC/calib/src/WaveformCalibEPN.cxx +++ b/Detectors/ZDC/calib/src/WaveformCalibEPN.cxx @@ -41,6 +41,10 @@ int WaveformCalibEPN::init() setSaveDebugHistos(); } + if (opt.dumpCalib == true) { + setDumpCalib(); + } + mQueue.configure(cfg); if (mVerbosity > DbgZero) { mQueue.printConf(); @@ -99,6 +103,7 @@ int WaveformCalibEPN::process(const gsl::span& RecBC, #endif if (mask != 0) { #ifdef O2_ZDC_WAVEFORMCALIB_DEBUG + // Print last recorded event. Not the event at peak position ev.print(); ev.printDecodedMessages(); mQueue.print(); @@ -122,17 +127,25 @@ int WaveformCalibEPN::endOfRun() if (mVerbosity > DbgZero) { LOGF(info, "WaveformCalibEPN::endOfRun ts (%llu:%llu)", mData.mCTimeBeg, mData.mCTimeEnd); for (int is = 0; is < NChannels; is++) { + int itdc = SignalTDC[is]; if (mData.getEntries(is) > 0) { - int itdc = SignalTDC[is]; LOGF(info, "Waveform %2d %s with %10d events and cuts AMP:(%g:%g) TDC:%d:(%g:%g) Valid:[%d:%d:%d]", is, ChannelNames[is].data(), mData.getEntries(is), mConfig->cutLow[is], mConfig->cutHigh[is], itdc, mConfig->cutTimeLow[itdc], mConfig->cutTimeHigh[itdc], mData.getFirstValid(is), mData.mPeak, mData.getLastValid(is)); + } else { + LOGF(info, "Waveform %2d %s with %10d events and cuts AMP:(%g:%g) TDC:%d:(%g:%g)", is, ChannelNames[is].data(), + mData.getEntries(is), mConfig->cutLow[is], mConfig->cutHigh[is], + itdc, mConfig->cutTimeLow[itdc], mConfig->cutTimeHigh[itdc]); } } } + const auto& opt = CalibParamZDC::Instance(); if (mSaveDebugHistos) { - saveDebugHistos(); + saveDebugHistos(opt.outputDir + "ZDCWaveformCalibEPN.root"); + } + if (mDumpCalib) { + dumpCalib(opt.outputDir + "ZDCWaveformCalibEPNDump.root"); } return 0; } @@ -142,3 +155,9 @@ int WaveformCalibEPN::saveDebugHistos(const std::string fn) { return mData.saveDebugHistos(fn); } + +//______________________________________________________________________________ +int WaveformCalibEPN::dumpCalib(const std::string fn) +{ + return mData.dumpCalib(fn); +} diff --git a/Detectors/ZDC/calib/src/WaveformCalibQueue.cxx b/Detectors/ZDC/calib/src/WaveformCalibQueue.cxx index c62306f21b1ad..76d66c7577029 100644 --- a/Detectors/ZDC/calib/src/WaveformCalibQueue.cxx +++ b/Detectors/ZDC/calib/src/WaveformCalibQueue.cxx @@ -201,6 +201,9 @@ int WaveformCalibQueue::hasData(int isig, const gsl::span& wave, WaveformCalibData& data) { +#ifdef O2_ZDC_WAVEFORMCALIB_DEBUG + LOG(info) << "WaveformCalibQueue::" << __func__ << " isig=" << isig << " " << ChannelNames[isig] << " tdcid=" << SignalTDC[isig] << " tdc_sig=" << TDCSignal[SignalTDC[isig]] << " " << ChannelNames[TDCSignal[SignalTDC[isig]]]; +#endif int ipkb = -1; // Bunch where peak is found int ipk = -1; // peak position within bunch float min = std::numeric_limits::infinity(); @@ -213,7 +216,7 @@ int WaveformCalibQueue::addData(int isig, const gsl::spancutLow[isig] || amp > mCfg->cutHigh[isig]) { // No warning messages for amplitude cuts on towers +#ifdef O2_ZDC_WAVEFORMCALIB_DEBUG + LOG(info) << " isig = " << isig << " amplitude " << amp << " not in range " << mCfg->cutLow[isig] << " : " << mCfg->cutHigh[isig]; +#endif return -1; } if ((ppos - mPeak) < mTimeLow[itdc] || (ppos - mPeak) > mTimeHigh[itdc]) { diff --git a/Detectors/ZDC/reconstruction/CMakeLists.txt b/Detectors/ZDC/reconstruction/CMakeLists.txt index f06819f8e2cf8..ea4b4b60d22b5 100644 --- a/Detectors/ZDC/reconstruction/CMakeLists.txt +++ b/Detectors/ZDC/reconstruction/CMakeLists.txt @@ -13,6 +13,7 @@ o2_add_library(ZDCReconstruction SOURCES src/CTFCoder.cxx src/CTFHelper.cxx src/DigiReco.cxx + src/DigiParser.cxx src/RecoParamZDC.cxx src/ZDCTDCParam.cxx src/ZDCEnergyParam.cxx diff --git a/Detectors/ZDC/reconstruction/include/ZDCReconstruction/DigiParser.h b/Detectors/ZDC/reconstruction/include/ZDCReconstruction/DigiParser.h new file mode 100644 index 0000000000000..41e389403aa73 --- /dev/null +++ b/Detectors/ZDC/reconstruction/include/ZDCReconstruction/DigiParser.h @@ -0,0 +1,98 @@ +// 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 "Framework/Logger.h" +#include "ZDCBase/Constants.h" +#include "ZDCSimulation/ZDCSimParam.h" +#include "ZDCReconstruction/RecoParamZDC.h" +#include "ZDCReconstruction/ZDCTDCParam.h" +#include "ZDCReconstruction/ZDCTDCCorr.h" +#include "ZDCReconstruction/ZDCEnergyParam.h" +#include "ZDCReconstruction/ZDCTowerParam.h" +#include "ZDCReconstruction/BaselineParam.h" +#include "ZDCReconstruction/RecoConfigZDC.h" +#include "ZDCBase/ModuleConfig.h" +#include "CommonDataFormat/InteractionRecord.h" +#include "DataFormatsZDC/BCData.h" +#include "DataFormatsZDC/ChannelData.h" +#include "DataFormatsZDC/OrbitData.h" +#include "DataFormatsZDC/RecEvent.h" +#include "DataFormatsZDC/RecEventAux.h" + +#ifndef ALICEO2_ZDC_DIGI_PARSER_H +#define ALICEO2_ZDC_DIGI_PARSER_H + +namespace o2 +{ +namespace zdc +{ + +class DigiParser +{ + public: + DigiParser() = default; + ~DigiParser() = default; + void init(); + int process(const gsl::span& orbitdata, + const gsl::span& bcdata, + const gsl::span& chdata); + void setVerbosity(int v) + { + mVerbosity = v; + } + int getVerbosity() const { return mVerbosity; } + void setOutput(std::string output) + { + mOutput = output; + } + void setRejectPileUp(bool op = true) + { + mRejectPileUp = op; + } + void eor(); + + void setModuleConfig(const ModuleConfig* moduleConfig) { mModuleConfig = moduleConfig; }; + const ModuleConfig* getModuleConfig() { return mModuleConfig; }; + + private: + const ModuleConfig* mModuleConfig = nullptr; /// Trigger/readout configuration object + const RecoParamZDC* mRopt = nullptr; + + void setStat(TH1* h); + void setModuleLabel(TH1* h); + void setModuleLabel(TAxis* ax); + + int32_t mVerbosity = DbgMinimal; + bool mRejectPileUp = true; + std::string mOutput = "ZDCDigiParser.root"; + uint32_t mTriggerMask = 0; /// Mask of triggering channels + uint32_t mChMask[NChannels] = {0}; /// Identify all channels in readout pattern + + std::unique_ptr mTransmitted = nullptr; + std::unique_ptr mFired = nullptr; + std::unique_ptr mBaseline[NChannels] = {nullptr}; + std::unique_ptr mCounts[NChannels] = {nullptr}; + std::unique_ptr mSignalTH[NChannels] = {nullptr}; + std::unique_ptr mBunchH[NChannels] = {nullptr}; // Bunch pattern Hit + std::unique_ptr mAlignment; + + int mNBC = 0; +}; +} // namespace zdc +} // namespace o2 +#endif diff --git a/Detectors/ZDC/reconstruction/src/DigiParser.cxx b/Detectors/ZDC/reconstruction/src/DigiParser.cxx new file mode 100644 index 0000000000000..1259c9e6e1150 --- /dev/null +++ b/Detectors/ZDC/reconstruction/src/DigiParser.cxx @@ -0,0 +1,298 @@ +// 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 "Framework/Logger.h" +#include "CommonConstants/LHCConstants.h" +#include "ZDCReconstruction/DigiParser.h" +#include "ZDCReconstruction/RecoParamZDC.h" + +namespace o2 +{ +namespace zdc +{ + +void DigiParser::init() +{ + LOG(info) << "Initialization of ZDC DigiParser"; + if (!mModuleConfig) { + LOG(fatal) << "Missing ModuleConfig configuration object"; + return; + } + + mTriggerMask = mModuleConfig->getTriggerMask(); + + // Update reconstruction parameters + o2::zdc::RecoParamZDC& ropt = const_cast(RecoParamZDC::Instance()); + ropt.print(); + mRopt = (o2::zdc::RecoParamZDC*)&ropt; + + // Fill maps channel maps for integration + for (int ich = 0; ich < NChannels; ich++) { + // If the reconstruction parameters were not manually set + if (ropt.amod[ich] < 0 || ropt.ach[ich] < 0) { + for (int im = 0; im < NModules; im++) { + for (uint32_t ic = 0; ic < NChPerModule; ic++) { + if (mModuleConfig->modules[im].channelID[ic] == ich && mModuleConfig->modules[im].readChannel[ic]) { + ropt.amod[ich] = im; + ropt.ach[ich] = ic; + // Fill mask to identify all channels + mChMask[ich] = (0x1 << (4 * im + ic)); + goto next_ich; + } + } + } + } else { + // Fill mask to identify all channels + mChMask[ich] = (0x1 << (4 * ropt.amod[ich] + ropt.ach[ich])); + } + next_ich:; + if (mVerbosity > DbgZero) { + LOG(info) << "Channel " << ich << "(" << ChannelNames[ich] << ") mod " << ropt.amod[ich] << " ch " << ropt.ach[ich] << " bit " << (4 * ropt.amod[ich] + ropt.ach[ich]); + } + } + + double xmin = -3 * NTimeBinsPerBC - 0.5; + double xmax = 2 * NTimeBinsPerBC - 0.5; + int nbx = std::round(xmax - xmin); + + if (mTransmitted == nullptr) { + mTransmitted = std::make_unique("ht", "Transmitted channels", NChannels, -0.5, NChannels - 0.5); + } + if (mFired == nullptr) { + mFired = std::make_unique("hfired", "Fired channels", NChannels, -0.5, NChannels - 0.5); + } + if (mAlignment == nullptr) { + mAlignment = std::make_unique("hmap", "Map of fired channels", o2::constants::lhc::LHCMaxBunches, -0.5, o2::constants::lhc::LHCMaxBunches - 0.5, NChannels, -0.5, NChannels - 0.5); + } + for (uint32_t ich = 0; ich < NChannels; ich++) { + if (mBaseline[ich] == nullptr) { + TString hname = TString::Format("hp_%s", ChannelNames[ich].data()); + TString htit = TString::Format("Baseline %s;Average orbit baseline", ChannelNames[ich].data()); + mBaseline[ich] = std::make_unique(hname, htit, 65536, -32768.5, 32767.5); + } + if (mCounts[ich] == nullptr) { + TString hname = TString::Format("hc_%s", ChannelNames[ich].data()); + TString htit = TString::Format("Counts %s; Orbit hits", ChannelNames[ich].data()); + mCounts[ich] = std::make_unique(hname, htit, o2::constants::lhc::LHCMaxBunches + 1, -0.5, o2::constants::lhc::LHCMaxBunches + 0.5); + } + if (mSignalTH[ich] == nullptr) { + TString hname = TString::Format("hsth_%s", ChannelNames[ich].data()); + TString htit = TString::Format("Signal %s AUTOT & Hit; Sample; ADC", ChannelNames[ich].data()); + if (mRejectPileUp) { + mSignalTH[ich] = std::make_unique(hname, htit, 3 * NTimeBinsPerBC, -0.5 - 1 * NTimeBinsPerBC, 2 * NTimeBinsPerBC - 0.5, ADCRange, ADCMin - 0.5, ADCMax + 0.5); + } else { + mSignalTH[ich] = std::make_unique(hname, htit, 5 * NTimeBinsPerBC, -0.5 - 3 * NTimeBinsPerBC, 2 * NTimeBinsPerBC - 0.5, ADCRange, ADCMin - 0.5, ADCMax + 0.5); + } + } + if (mBunchH[ich] == nullptr) { + TString hname = TString::Format("hbh_%s", ChannelNames[ich].data()); + TString htit = TString::Format("Bunch %s AUTOT Hit; BC units; - BC hundreds", ChannelNames[ich].data()); + mBunchH[ich] = std::make_unique(hname, htit, 100, -0.5, 99.5, 36, -35.5, 0.5); + } + } +} // init + +void DigiParser::eor() +{ + TFile* f = new TFile(mOutput.data(), "recreate"); + if (f->IsZombie()) { + LOG(fatal) << "Cannot write to file " << f->GetName(); + return; + } + for (uint32_t i = 0; i < NChannels; i++) { + setStat(mBunchH[i].get()); + mBunchH[i]->Write(); + } + for (uint32_t i = 0; i < NChannels; i++) { + setStat(mBaseline[i].get()); + mBaseline[i]->Write(); + } + for (uint32_t i = 0; i < NChannels; i++) { + setStat(mCounts[i].get()); + mCounts[i]->Write(); + } + for (uint32_t i = 0; i < NChannels; i++) { + setStat(mSignalTH[i].get()); + mSignalTH[i]->Write(); + } + setModuleLabel(mTransmitted.get()); + mTransmitted->SetMinimum(0); + mTransmitted->Write(); + setModuleLabel(mFired.get()); + mFired->SetMinimum(0); + mFired->Write(); + setModuleLabel((mAlignment.get())->GetYaxis()); + mAlignment->SetMinimum(0); + mAlignment->Write(); + f->Close(); +} + +int DigiParser::process(const gsl::span& orbitdata, const gsl::span& bcdata, const gsl::span& chdata) +{ + // We assume that vectors contain data from a full time frame + int norb = orbitdata.size(); + + uint32_t scaler[NChannels] = {0}; + for (int iorb = 0; iorb < norb; iorb++) { + for (int ich = 0; ich < NChannels; ich++) { + if (orbitdata[iorb].scaler[ich] <= o2::constants::lhc::LHCMaxBunches) { + scaler[ich] += orbitdata[iorb].scaler[ich]; + mCounts[ich]->Fill(orbitdata[iorb].scaler[ich]); + auto myped = float(orbitdata[iorb].data[ich]) * mModuleConfig->baselineFactor; + if (myped >= ADCMin && myped <= ADCMax) { + // Pedestal information is present for this channel + mBaseline[ich]->Fill(myped); + } + } else { + LOG(warn) << "Corrupted scaler data for orbit " << orbitdata[iorb].ir.orbit; + } + } + } + + mNBC = bcdata.size(); + std::vector> chRef; /// Cache of references + chRef.resize(mNBC); + + // Assign data references + for (int ibc = 0; ibc < mNBC; ibc++) { + auto& bcd = bcdata[ibc]; + int chEnt = bcd.ref.getFirstEntry(); + for (int ich = 0; ich < NChannels; ich++) { + chRef[ibc][ich] = ZDCRefInitVal; + } + for (int ic = 0; ic < bcd.ref.getEntries(); ic++) { + auto& chd = chdata[chEnt]; + if (chd.id > IdDummy && chd.id < NChannels) { + chRef[ibc][chd.id] = chEnt; + mTransmitted->Fill(chd.id); + if ((bcdata[ibc].triggers & mChMask[chd.id]) != 0) { + mFired->Fill(chd.id); + } + } + chEnt++; + } + } + + for (uint32_t isig = 0; isig < NChannels; isig++) { + for (int ibc = 0; ibc < mNBC; ibc++) { + auto& ir = bcdata[ibc].ir; + // Identify pile-up + if (mRejectPileUp) { + int nsig = 0; + // Check previous bunches + for (int ibn = -4; ibn < 5; ibn++) { + int ibt = ibc + ibn; + if (ibt >= 0) { // Check backward and current bunch + if (ibt < mNBC) { + auto bcd = bcdata[ibt].ir.differenceInBC(ir); + if (bcd == ibn) { + if ((bcdata[ibt].triggers & mChMask[isig]) != 0) { + nsig++; + } + } + } else { + break; + } + } + } + if (nsig > 1) { + continue; + } + } + // Check previous, current and next bunch crossings + for (int ibn = -1; ibn < 4; ibn++) { + int ibt = ibc + ibn; + if (ibt >= 0) { // Check backward and current bunch + if (ibt < mNBC) { // Check forward bunches + auto bcd = bcdata[ibt].ir.differenceInBC(ir); + if (bcd == 0) { + // Fill bunch map + if ((bcdata[ibc].triggers & mChMask[isig]) != 0) { + double bc_d = uint32_t(ir.bc / 100); + double bc_m = uint32_t(ir.bc % 100); + mBunchH[isig]->Fill(bc_m, -bc_d); + mFired->Fill(isig); + mAlignment->Fill(ir.bc, isig); + } + } + if (bcd == ibn) { + if ((bcdata[ibt].triggers & mChMask[isig]) != 0) { + // Fill waveform + auto ref = chRef[ibc][isig]; + if (ref != ZDCRefInitVal) { + for (int is = 0; is < NTimeBinsPerBC; is++) { + mSignalTH[isig]->Fill(-ibn * NTimeBinsPerBC + is, chdata[ref].data[is]); + } + } + } + } + } else { + break; + } + } + } + } + } + return 0; +} // process + +void DigiParser::setStat(TH1* h) +{ + TString hn = h->GetName(); + h->Draw(); + gPad->Update(); + TPaveStats* st = (TPaveStats*)h->GetListOfFunctions()->FindObject("stats"); + st->SetFillStyle(1001); + st->SetBorderSize(1); + if (hn.BeginsWith("hp")) { + st->SetOptStat(111111); + st->SetX1NDC(0.1); + st->SetX2NDC(0.3); + st->SetY1NDC(0.640); + st->SetY2NDC(0.9); + } else if (hn.BeginsWith("hc")) { + st->SetOptStat(1111); + st->SetX1NDC(0.799); + st->SetX2NDC(0.999); + st->SetY1NDC(0.829); + st->SetY2NDC(0.999); + } else if (hn.BeginsWith("hs") || hn.BeginsWith("hb")) { + st->SetOptStat(11); + st->SetX1NDC(0.799); + st->SetX2NDC(0.9995); + st->SetY1NDC(0.904); + st->SetY2NDC(0.999); + } +} + +void DigiParser::setModuleLabel(TH1* h) +{ + for (uint32_t isig = 0; isig < NChannels; isig++) { + h->GetXaxis()->SetBinLabel(isig + 1, ChannelNames[isig].data()); + } +} + +void DigiParser::setModuleLabel(TAxis* ax) +{ + for (uint32_t isig = 0; isig < NChannels; isig++) { + ax->SetBinLabel(isig + 1, ChannelNames[isig].data()); + } +} + +} // namespace zdc +} // namespace o2 diff --git a/Detectors/ZDC/reconstruction/src/DigiReco.cxx b/Detectors/ZDC/reconstruction/src/DigiReco.cxx index 397e2aef63f1c..50a8ceeb13691 100644 --- a/Detectors/ZDC/reconstruction/src/DigiReco.cxx +++ b/Detectors/ZDC/reconstruction/src/DigiReco.cxx @@ -68,6 +68,9 @@ void DigiReco::init() } } } + } else { + // Fill mask to identify TDC channels + mTDCMask[itdc] = (0x1 << (4 * ropt.tmod[itdc] + ropt.tch[itdc])); } next_itdc:; if (mVerbosity > DbgZero) { @@ -356,6 +359,9 @@ void DigiReco::init() } } } + } else { + // Fill mask to identify all channels + mChMask[ich] = (0x1 << (4 * ropt.amod[ich] + ropt.ach[ich])); } next_ich:; if (mVerbosity > DbgZero) { diff --git a/Detectors/ZDC/workflow/CMakeLists.txt b/Detectors/ZDC/workflow/CMakeLists.txt index 90de7e1bc2659..21de8322a81fe 100644 --- a/Detectors/ZDC/workflow/CMakeLists.txt +++ b/Detectors/ZDC/workflow/CMakeLists.txt @@ -20,7 +20,9 @@ o2_add_library(ZDCWorkflow src/RecoWorkflow.cxx src/DigitRecoSpec.cxx src/DigitReaderSpec.cxx + src/DigitParserSpec.cxx src/RecoReaderSpec.cxx + src/ParserWorkflow.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::DataFormatsZDC O2::ZDCRaw @@ -64,3 +66,9 @@ o2_add_executable(digits-writer COMPONENT_NAME zdc SOURCES src/digits-writer-workflow.cxx PUBLIC_LINK_LIBRARIES O2::ZDCWorkflow O2::ZDCReconstruction) + +o2_add_executable(digits-parser + COMPONENT_NAME zdc + SOURCES src/zdc-parser-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::ZDCWorkflow O2::ZDCReconstruction) + diff --git a/Detectors/ZDC/workflow/include/ZDCWorkflow/DigitParserSpec.h b/Detectors/ZDC/workflow/include/ZDCWorkflow/DigitParserSpec.h new file mode 100644 index 0000000000000..e7e64b52862e7 --- /dev/null +++ b/Detectors/ZDC/workflow/include/ZDCWorkflow/DigitParserSpec.h @@ -0,0 +1,58 @@ +// 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 DigitRecoSpec.h +/// @brief Run ZDC digits reconstruction +/// @author pietro.cortese@cern.ch + +#ifndef O2_ZDC_DIGITPARSER_SPEC +#define O2_ZDC_DIGITPARSER_SPEC + +#include "Framework/Logger.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/DataAllocator.h" +#include "Framework/DataSpecUtils.h" +#include "Framework/Task.h" +#include +#include "CommonUtils/NameConf.h" +#include "ZDCReconstruction/DigiParser.h" + +namespace o2 +{ +namespace zdc +{ + +class DigitParserSpec : public o2::framework::Task +{ + public: + DigitParserSpec(); + DigitParserSpec(const int verbosity); + ~DigitParserSpec() override = default; + void init(o2::framework::InitContext& ic) final; + void updateTimeDependentParams(o2::framework::ProcessingContext& pc); + void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) final; + void run(o2::framework::ProcessingContext& pc) final; + void endOfStream(o2::framework::EndOfStreamContext& ec) final; + + private: + DigiParser mWorker; // Reconstruction object + int mVerbosity = 0; // Verbosity level during recostruction + bool mInitialized = false; // Connect once to CCDB during initialization + TStopwatch mTimer; +}; + +/// create a processor spec +framework::DataProcessorSpec getDigitParserSpec(const int verbosity); + +} // namespace zdc +} // namespace o2 + +#endif diff --git a/Detectors/ZDC/workflow/include/ZDCWorkflow/DigitRecoSpec.h b/Detectors/ZDC/workflow/include/ZDCWorkflow/DigitRecoSpec.h index 8141fdeb46dfe..1d7f6ccfbc50b 100644 --- a/Detectors/ZDC/workflow/include/ZDCWorkflow/DigitRecoSpec.h +++ b/Detectors/ZDC/workflow/include/ZDCWorkflow/DigitRecoSpec.h @@ -34,8 +34,7 @@ class DigitRecoSpec : public o2::framework::Task { public: DigitRecoSpec(); - DigitRecoSpec(const int verbosity, const bool debugOut, - const bool enableZDCTDCCorr, const bool enableZDCEnergyParam, const bool enableZDCTowerParam, const bool enableBaselineParam); + DigitRecoSpec(const int verbosity, const bool debugOut, const bool enableZDCTDCCorr, const bool enableZDCEnergyParam, const bool enableZDCTowerParam, const bool enableBaselineParam); ~DigitRecoSpec() override = default; void init(o2::framework::InitContext& ic) final; void updateTimeDependentParams(o2::framework::ProcessingContext& pc); diff --git a/GPU/GPUTracking/Base/opencl/GPUReconstructionOCL1Internals.h b/Detectors/ZDC/workflow/include/ZDCWorkflow/ParserWorkflow.h similarity index 60% rename from GPU/GPUTracking/Base/opencl/GPUReconstructionOCL1Internals.h rename to Detectors/ZDC/workflow/include/ZDCWorkflow/ParserWorkflow.h index 997a108ac26d0..0aced0b444983 100644 --- a/GPU/GPUTracking/Base/opencl/GPUReconstructionOCL1Internals.h +++ b/Detectors/ZDC/workflow/include/ZDCWorkflow/ParserWorkflow.h @@ -9,20 +9,18 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file GPUReconstructionOCL1Internals.h -/// \author David Rohr, Sergey Gorbunov +#ifndef O2_ZDC_PARSERWORKFLOW_H +#define O2_ZDC_PARSERWORKFLOW_H -#ifndef GPUTPCGPUTRACKEROPENCLINTERNALS1_H -#define GPUTPCGPUTRACKEROPENCLINTERNALS1_H +/// @file ParserWorkflow.h -#include "GPUReconstructionOCLInternals.h" +#include "Framework/WorkflowSpec.h" -namespace GPUCA_NAMESPACE::gpu +namespace o2 { - -struct GPUReconstructionOCL1Internals : public GPUReconstructionOCLInternals { -}; - -} // namespace GPUCA_NAMESPACE::gpu - +namespace zdc +{ +framework::WorkflowSpec getParserWorkflow(const int verbosity); +} // namespace zdc +} // namespace o2 #endif diff --git a/Detectors/ZDC/workflow/include/ZDCWorkflow/RecoWorkflow.h b/Detectors/ZDC/workflow/include/ZDCWorkflow/RecoWorkflow.h index a06e768377ad9..3df76af188c1b 100644 --- a/Detectors/ZDC/workflow/include/ZDCWorkflow/RecoWorkflow.h +++ b/Detectors/ZDC/workflow/include/ZDCWorkflow/RecoWorkflow.h @@ -20,8 +20,7 @@ namespace o2 { namespace zdc { -framework::WorkflowSpec getRecoWorkflow(const bool useMC, const bool disableRootInp, const bool disableRootOut, const int verbosity, const bool enableDebugOut, - const bool enableZDCTDCCorr, const bool enableZDCEnergyParam, const bool enableZDCTowerParam, const bool enableBaselineParam); +framework::WorkflowSpec getRecoWorkflow(const bool useMC, const bool disableRootInp, const bool disableRootOut, const int verbosity, const bool enableDebugOut, const bool enableZDCTDCCorr, const bool enableZDCEnergyParam, const bool enableZDCTowerParam, const bool enableBaselineParam); } // namespace zdc } // namespace o2 #endif diff --git a/Detectors/ZDC/workflow/src/DigitParserSpec.cxx b/Detectors/ZDC/workflow/src/DigitParserSpec.cxx new file mode 100644 index 0000000000000..b761de6d2d9ff --- /dev/null +++ b/Detectors/ZDC/workflow/src/DigitParserSpec.cxx @@ -0,0 +1,128 @@ +// 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 DigitParserSpec.cxx +/// @brief ZDC digits parser +/// @author pietro.cortese@cern.ch + +#include +#include +#include +#include +#include "CCDB/BasicCCDBManager.h" +#include "CCDB/CCDBTimeStampUtils.h" +#include "Framework/Logger.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/CCDBParamSpec.h" +#include "ZDCWorkflow/DigitParserSpec.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DataFormatsZDC/BCData.h" +#include "DataFormatsZDC/ChannelData.h" +#include "DataFormatsZDC/OrbitData.h" +#include "ZDCBase/ModuleConfig.h" +#include "CommonUtils/NameConf.h" +#include "CCDB/BasicCCDBManager.h" +#include "CCDB/CCDBTimeStampUtils.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace zdc +{ + +DigitParserSpec::DigitParserSpec() +{ + mTimer.Stop(); + mTimer.Reset(); +} + +DigitParserSpec::DigitParserSpec(const int verbosity) : mVerbosity(verbosity) +{ + mTimer.Stop(); + mTimer.Reset(); +} + +void DigitParserSpec::init(o2::framework::InitContext& ic) +{ + mWorker.setOutput(ic.options().get("parser-output")); + mWorker.setRejectPileUp((ic.options().get("reject-pileup")) != 0); +} + +void DigitParserSpec::updateTimeDependentParams(ProcessingContext& pc) +{ + // we call these methods just to trigger finaliseCCDB callback + pc.inputs().get("moduleconfig"); +} + +void DigitParserSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) +{ + if (matcher == ConcreteDataMatcher("ZDC", "MODULECONFIG", 0)) { + auto* config = (const o2::zdc::ModuleConfig*)obj; + if (mVerbosity > DbgZero) { + config->print(); + } + mWorker.setModuleConfig(config); + } +} + +void DigitParserSpec::run(ProcessingContext& pc) +{ + if (!mInitialized) { + LOG(info) << "DigitParserSpec::run initialization"; + mInitialized = true; + updateTimeDependentParams(pc); + mWorker.setVerbosity(mVerbosity); + mWorker.init(); + } + auto cput = mTimer.CpuTime(); + mTimer.Start(false); + + auto bcdata = pc.inputs().get>("trig"); + auto chans = pc.inputs().get>("chan"); + auto peds = pc.inputs().get>("peds"); + + int rval = mWorker.process(peds, bcdata, chans); + if (rval != 0) { + LOG(warning) << bcdata.size() << " BC " << chans.size() << " CH " << peds.size() << " OD -> processing ended in ERROR @ line " << rval; + } + mTimer.Stop(); +} + +void DigitParserSpec::endOfStream(EndOfStreamContext& ec) +{ + mWorker.eor(); + LOGF(info, "ZDC digits parsing total time: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); +} + +framework::DataProcessorSpec getDigitParserSpec(const int verbosity = 0) +{ + std::vector inputs; + inputs.emplace_back("trig", "ZDC", "DIGITSBC", 0, Lifetime::Timeframe); + inputs.emplace_back("chan", "ZDC", "DIGITSCH", 0, Lifetime::Timeframe); + inputs.emplace_back("peds", "ZDC", "DIGITSPD", 0, Lifetime::Timeframe); + inputs.emplace_back("moduleconfig", "ZDC", "MODULECONFIG", 0, Lifetime::Condition, o2::framework::ccdbParamSpec(o2::zdc::CCDBPathConfigModule.data())); + + std::vector outputs; + + return DataProcessorSpec{ + "zdc-digi-parser", + inputs, + outputs, + AlgorithmSpec{adaptFromTask(verbosity)}, + o2::framework::Options{{"parser-output", o2::framework::VariantType::String, "ZDCDigiParser.root", {"Output file name"}}, + {"reject-pileup", o2::framework::VariantType::Int, 1, {"Reject pile-up for signal shapes 0/1"}}}}; +} + +} // namespace zdc +} // namespace o2 diff --git a/Detectors/ZDC/workflow/src/DigitRecoSpec.cxx b/Detectors/ZDC/workflow/src/DigitRecoSpec.cxx index e9b63c5b49d5b..8f0ab82fa5b9b 100644 --- a/Detectors/ZDC/workflow/src/DigitRecoSpec.cxx +++ b/Detectors/ZDC/workflow/src/DigitRecoSpec.cxx @@ -275,12 +275,10 @@ void DigitRecoSpec::run(ProcessingContext& pc) void DigitRecoSpec::endOfStream(EndOfStreamContext& ec) { mWorker.eor(); - LOGF(info, "ZDC Reconstruction total timing: Cpu: %.3e Real: %.3e s in %d slots", - mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); + LOGF(info, "ZDC Reconstruction total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -framework::DataProcessorSpec getDigitRecoSpec(const int verbosity = 0, const bool enableDebugOut = true, - const bool enableZDCTDCCorr = true, const bool enableZDCEnergyParam = true, const bool enableZDCTowerParam = true, const bool enableBaselineParam = true) +framework::DataProcessorSpec getDigitRecoSpec(const int verbosity = 0, const bool enableDebugOut = true, const bool enableZDCTDCCorr = true, const bool enableZDCEnergyParam = true, const bool enableZDCTowerParam = true, const bool enableBaselineParam = true) { std::vector inputs; inputs.emplace_back("trig", "ZDC", "DIGITSBC", 0, Lifetime::Timeframe); diff --git a/Detectors/ZDC/workflow/src/ParserWorkflow.cxx b/Detectors/ZDC/workflow/src/ParserWorkflow.cxx new file mode 100644 index 0000000000000..bb8c193262627 --- /dev/null +++ b/Detectors/ZDC/workflow/src/ParserWorkflow.cxx @@ -0,0 +1,31 @@ +// 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 ParserWorkflow.cxx + +#include "ZDCWorkflow/ParserWorkflow.h" +#include "ZDCWorkflow/DigitReaderSpec.h" +#include "ZDCWorkflow/DigitParserSpec.h" + +namespace o2 +{ +namespace zdc +{ + +framework::WorkflowSpec getParserWorkflow(const int verbosity) +{ + framework::WorkflowSpec specs; + specs.emplace_back(o2::zdc::getDigitParserSpec(verbosity)); + return specs; +} + +} // namespace zdc +} // namespace o2 diff --git a/Detectors/ZDC/workflow/src/RecoReaderSpec.cxx b/Detectors/ZDC/workflow/src/RecoReaderSpec.cxx index ebea51c932c5c..33b2b59d8247b 100644 --- a/Detectors/ZDC/workflow/src/RecoReaderSpec.cxx +++ b/Detectors/ZDC/workflow/src/RecoReaderSpec.cxx @@ -22,6 +22,8 @@ #include "DataFormatsZDC/BCData.h" #include "DataFormatsZDC/ChannelData.h" #include "DataFormatsZDC/RecEvent.h" +#include "DataFormatsZDC/RecEvent.h" +#include "DataFormatsZDC/ZDCWaveform.h" #include "CommonUtils/NameConf.h" using namespace o2::framework; @@ -33,8 +35,7 @@ namespace zdc void RecoReader::init(InitContext& ic) { - auto filename = o2::utils::Str::concat_string(o2::utils::Str::rectifyDirectory(ic.options().get("input-dir")), - ic.options().get("zdc-reco-infile")); + auto filename = o2::utils::Str::concat_string(o2::utils::Str::rectifyDirectory(ic.options().get("input-dir")), ic.options().get("zdc-reco-infile")); mFile.reset(TFile::Open(filename.c_str())); if (!mFile->IsOpen()) { LOG(error) << "Cannot open the " << filename.c_str() << " file !"; @@ -54,20 +55,24 @@ void RecoReader::run(ProcessingContext& pc) std::vector Energy, *EnergyPtr = &Energy; std::vector TDCData, *TDCDataPtr = &TDCData; std::vector Info, *InfoPtr = &Info; + std::vector WaveformData, *WaveformDataPtr = &WaveformData; mTree->SetBranchAddress("ZDCRecBC", &RecBCPtr); mTree->SetBranchAddress("ZDCRecE", &EnergyPtr); mTree->SetBranchAddress("ZDCRecTDC", &TDCDataPtr); mTree->SetBranchAddress("ZDCRecInfo", &InfoPtr); + mTree->SetBranchAddress("ZDCWaveform", &WaveformDataPtr); auto ent = mTree->GetReadEntry() + 1; assert(ent < mTree->GetEntries()); // this should not happen mTree->GetEntry(ent); - LOG(info) << "ZDCRecoReader pushed " << RecBC.size() << " b.c. " << Energy.size() << " Energies " << TDCData.size() << " TDCs " << Info.size() << " Infos"; + LOG(info) << "ZDCRecoReader pushed " << RecBC.size() << " b.c. " << Energy.size() << " Energies " << TDCData.size() << " TDCs " << Info.size() << " Infos " << WaveformData.size() << " Waveform chunks"; pc.outputs().snapshot(Output{"ZDC", "BCREC", 0}, RecBC); pc.outputs().snapshot(Output{"ZDC", "ENERGY", 0}, Energy); pc.outputs().snapshot(Output{"ZDC", "TDCDATA", 0}, TDCData); pc.outputs().snapshot(Output{"ZDC", "INFO", 0}, Info); + pc.outputs().snapshot(Output{"ZDC", "WAVE", 0}, WaveformData); + if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { pc.services().get().endOfStream(); pc.services().get().readyToQuit(QuitRequest::Me); @@ -81,6 +86,8 @@ DataProcessorSpec getRecoReaderSpec() outputs.emplace_back("ZDC", "ENERGY", 0, Lifetime::Timeframe); outputs.emplace_back("ZDC", "TDCDATA", 0, Lifetime::Timeframe); outputs.emplace_back("ZDC", "INFO", 0, Lifetime::Timeframe); + outputs.emplace_back("ZDC", "WAVE", 0, Lifetime::Timeframe); + return DataProcessorSpec{ "zdc-reco-reader", Inputs{}, @@ -88,6 +95,7 @@ DataProcessorSpec getRecoReaderSpec() AlgorithmSpec{adaptFromTask()}, Options{ {"zdc-reco-infile", VariantType::String, "zdcreco.root", {"Name of the input file"}}, + {"enable-waveform", VariantType::Bool, false, {"Read waveform data"}}, {"input-dir", VariantType::String, "none", {"Input directory"}}}}; } diff --git a/Detectors/ZDC/workflow/src/RecoWorkflow.cxx b/Detectors/ZDC/workflow/src/RecoWorkflow.cxx index b93a86f6237b5..3004f045d8237 100644 --- a/Detectors/ZDC/workflow/src/RecoWorkflow.cxx +++ b/Detectors/ZDC/workflow/src/RecoWorkflow.cxx @@ -22,8 +22,7 @@ namespace o2 namespace zdc { -framework::WorkflowSpec getRecoWorkflow(const bool useMC, const bool disableRootInp, const bool disableRootOut, const int verbosity, const bool enableDebugOut, - const bool enableZDCTDCCorr, const bool enableZDCEnergyParam, const bool enableZDCTowerParam, const bool enableBaselineParam) +framework::WorkflowSpec getRecoWorkflow(const bool useMC, const bool disableRootInp, const bool disableRootOut, const int verbosity, const bool enableDebugOut, const bool enableZDCTDCCorr, const bool enableZDCEnergyParam, const bool enableZDCTowerParam, const bool enableBaselineParam) { framework::WorkflowSpec specs; if (!disableRootInp) { diff --git a/Detectors/ZDC/workflow/src/zdc-parser-workflow.cxx b/Detectors/ZDC/workflow/src/zdc-parser-workflow.cxx new file mode 100644 index 0000000000000..19e31dfa48129 --- /dev/null +++ b/Detectors/ZDC/workflow/src/zdc-parser-workflow.cxx @@ -0,0 +1,60 @@ +// 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 "ZDCWorkflow/ParserWorkflow.h" +#include "CommonUtils/ConfigurableParam.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" +#include "Framework/CallbacksPolicy.h" +#include "Framework/CompletionPolicyHelpers.h" + +using namespace o2::framework; + +// ------------------------------------------------------------------ +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +void customize(std::vector& policies) +{ + // ordered policies for the writers + policies.push_back(CompletionPolicyHelpers::consumeWhenAllOrdered(".*(?:ZDC|zdc).*[W,w]riter.*")); +} + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + // option allowing to set parameters + workflowOptions.push_back(ConfigParamSpec{"verbosity-level", VariantType::Int, 0, {"verbosity level"}}); + std::string keyvaluehelp("Semicolon separated key=value strings ..."); + workflowOptions.push_back(ConfigParamSpec{"configKeyValues", VariantType::String, "", {keyvaluehelp}}); + o2::raw::HBFUtilsInitializer::addConfigOption(workflowOptions); +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + LOG(info) << "WorkflowSpec defineDataProcessing"; + // Update the (declared) parameters if changed from the command line + o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); + + auto verbosity = configcontext.options().get("verbosity-level"); + + auto wf = o2::zdc::getParserWorkflow(verbosity); + + // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit + o2::raw::HBFUtilsInitializer hbfIni(configcontext, wf); + + return std::move(wf); +} diff --git a/EventVisualisation/Workflow/src/O2DPLDisplay.cxx b/EventVisualisation/Workflow/src/O2DPLDisplay.cxx index 414d70c443541..e02e1ee20ce58 100644 --- a/EventVisualisation/Workflow/src/O2DPLDisplay.cxx +++ b/EventVisualisation/Workflow/src/O2DPLDisplay.cxx @@ -229,7 +229,8 @@ void O2DPLDisplaySpec::run(ProcessingContext& pc) } } - LOGP(info, "Tracks: {}", fmt::join(sourceStats, ", ")); + // FIXME: find out why this does not work with 11.1.1 +// LOGP(info, "Tracks: {}", fmt::join(sourceStats, ", ")); } void O2DPLDisplaySpec::endOfStream(EndOfStreamContext& ec) diff --git a/Framework/AnalysisSupport/CMakeLists.txt b/Framework/AnalysisSupport/CMakeLists.txt index 5fb1282469711..dedbf8cb590b2 100644 --- a/Framework/AnalysisSupport/CMakeLists.txt +++ b/Framework/AnalysisSupport/CMakeLists.txt @@ -24,6 +24,16 @@ o2_add_library(FrameworkAnalysisSupport PRIVATE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_LIST_DIR}/src PUBLIC_LINK_LIBRARIES O2::Framework ${EXTRA_TARGETS} ROOT::TreePlayer) +o2_add_library(FrameworkAnalysisRNTupleSupport + SOURCES src/RNTuplePlugin.cxx + PRIVATE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_LIST_DIR}/src + PUBLIC_LINK_LIBRARIES O2::Framework ${EXTRA_TARGETS} ROOT::ROOTNTuple ROOT::ROOTNTupleUtil) + +o2_add_library(FrameworkAnalysisTTreeSupport + SOURCES src/TTreePlugin.cxx + PRIVATE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_LIST_DIR}/src + PUBLIC_LINK_LIBRARIES O2::Framework ${EXTRA_TARGETS} ROOT::TreePlayer) + o2_add_test(DataInputDirector NAME test_Framework_test_DataInputDirector SOURCES test/test_DataInputDirector.cxx COMPONENT_NAME Framework diff --git a/Framework/AnalysisSupport/src/Plugin.cxx b/Framework/AnalysisSupport/src/Plugin.cxx index 52435375d7e9e..e3a39761e8049 100644 --- a/Framework/AnalysisSupport/src/Plugin.cxx +++ b/Framework/AnalysisSupport/src/Plugin.cxx @@ -155,12 +155,14 @@ struct DiscoverMetadataInAOD : o2::framework::ConfigDiscoveryPlugin { LOGP(fatal, "Couldn't open file \"{}\"!", filename); } std::vector results = readMetadata(currentFile); + const bool metaDataEmpty = results.empty(); + auto tables = getListOfTables(currentFile); + if (tables.empty() == false) { + results.push_back(ConfigParamSpec{"aod-metadata-tables", VariantType::ArrayString, tables, {"Tables in first AOD"}}); + } + // Found metadata already in the main file. - if (!results.empty()) { - auto tables = getListOfTables(currentFile); - if (tables.empty() == false) { - results.push_back(ConfigParamSpec{"aod-metadata-tables", VariantType::ArrayString, tables, {"Tables in first AOD"}}); - } + if (!metaDataEmpty) { results.push_back(ConfigParamSpec{"aod-metadata-source", VariantType::String, filename, {"File from which the metadata was extracted."}}); return results; } diff --git a/Framework/AnalysisSupport/src/RNTuplePlugin.cxx b/Framework/AnalysisSupport/src/RNTuplePlugin.cxx new file mode 100644 index 0000000000000..f66723419c24e --- /dev/null +++ b/Framework/AnalysisSupport/src/RNTuplePlugin.cxx @@ -0,0 +1,852 @@ +// 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 "Framework/RuntimeError.h" +#include "Framework/RootArrowFilesystem.h" +#include "Framework/Plugins.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +template class + std::unique_ptr; + +namespace o2::framework +{ + +class RNTupleFileWriteOptions : public arrow::dataset::FileWriteOptions +{ + public: + RNTupleFileWriteOptions(std::shared_ptr format) + : FileWriteOptions(format) + { + } +}; + +// A filesystem which allows me to get a RNTuple +class RNTupleFileSystem : public VirtualRootFileSystemBase +{ + public: + ~RNTupleFileSystem() override; + + std::shared_ptr GetSubFilesystem(arrow::dataset::FileSource source) override + { + return std::dynamic_pointer_cast(shared_from_this()); + }; + virtual ROOT::Experimental::RNTuple* GetRNTuple(arrow::dataset::FileSource source) = 0; +}; + +class SingleRNTupleFileSystem : public RNTupleFileSystem +{ + public: + SingleRNTupleFileSystem(ROOT::Experimental::RNTuple* tuple) + : RNTupleFileSystem(), + mTuple(tuple) + { + } + + arrow::Result GetFileInfo(std::string const& path) override; + + std::string type_name() const override + { + return "rntuple"; + } + + ROOT::Experimental::RNTuple* GetRNTuple(arrow::dataset::FileSource) override + { + // Simply return the only TTree we have + return mTuple; + } + + private: + ROOT::Experimental::RNTuple* mTuple; +}; + +arrow::Result SingleRNTupleFileSystem::GetFileInfo(std::string const& path) +{ + arrow::dataset::FileSource source(path, shared_from_this()); + arrow::fs::FileInfo result; + result.set_path(path); + result.set_type(arrow::fs::FileType::File); + return result; +} + +class RNTupleFileFragment : public arrow::dataset::FileFragment +{ + public: + RNTupleFileFragment(arrow::dataset::FileSource source, + std::shared_ptr format, + arrow::compute::Expression partition_expression, + std::shared_ptr physical_schema) + : FileFragment(std::move(source), std::move(format), std::move(partition_expression), std::move(physical_schema)) + { + } +}; + +class RNTupleFileFormat : public arrow::dataset::FileFormat +{ + size_t& mTotCompressedSize; + size_t& mTotUncompressedSize; + + public: + RNTupleFileFormat(size_t& totalCompressedSize, size_t& totalUncompressedSize) + : FileFormat({}), + mTotCompressedSize(totalCompressedSize), + mTotUncompressedSize(totalUncompressedSize) + { + } + + ~RNTupleFileFormat() override = default; + + std::string type_name() const override + { + return "rntuple"; + } + + bool Equals(const FileFormat& other) const override + { + return other.type_name() == this->type_name(); + } + + arrow::Result IsSupported(const arrow::dataset::FileSource& source) const override + { + auto fs = std::dynamic_pointer_cast(source.filesystem()); + auto subFs = fs->GetSubFilesystem(source); + if (std::dynamic_pointer_cast(subFs)) { + return true; + } + return false; + } + + arrow::Result> Inspect(const arrow::dataset::FileSource& source) const override; + + arrow::Result ScanBatchesAsync( + const std::shared_ptr& options, + const std::shared_ptr& fragment) const override; + + std::shared_ptr DefaultWriteOptions() override; + + arrow::Result> MakeWriter(std::shared_ptr destination, + std::shared_ptr schema, + std::shared_ptr options, + arrow::fs::FileLocator destination_locator) const override; + arrow::Result> MakeFragment( + arrow::dataset::FileSource source, arrow::compute::Expression partition_expression, + std::shared_ptr physical_schema) override; +}; + +struct RootNTupleVisitor : public ROOT::Experimental::Detail::RFieldVisitor { + void VisitArrayField(const ROOT::Experimental::RArrayField& field) override + { + int size = field.GetLength(); + RootNTupleVisitor valueVisitor{}; + auto valueField = field.GetSubFields()[0]; + valueField->AcceptVisitor(valueVisitor); + auto type = valueVisitor.datatype; + this->datatype = arrow::fixed_size_list(type, size); + } + + void VisitRVecField(const ROOT::Experimental::RRVecField& field) override + { + RootNTupleVisitor valueVisitor{}; + auto valueField = field.GetSubFields()[0]; + valueField->AcceptVisitor(valueVisitor); + auto type = valueVisitor.datatype; + this->datatype = arrow::list(type); + } + + void VisitField(const ROOT::Experimental::RFieldBase& field) override + { + throw o2::framework::runtime_error_f("Unknown field %s with type %s", field.GetFieldName().c_str(), field.GetTypeName().c_str()); + } + + void VisitIntField(const ROOT::Experimental::RField& field) override + { + this->datatype = arrow::int32(); + } + + void VisitInt8Field(const ROOT::Experimental::RField& field) override + { + this->datatype = arrow::int8(); + } + + void VisitInt16Field(const ROOT::Experimental::RField& field) override + { + this->datatype = arrow::int16(); + } + + void VisitUInt32Field(const ROOT::Experimental::RField& field) override + { + this->datatype = arrow::uint32(); + } + + void VisitUInt8Field(const ROOT::Experimental::RField& field) override + { + this->datatype = arrow::uint8(); + } + + void VisitUInt16Field(const ROOT::Experimental::RField& field) override + { + this->datatype = arrow::int16(); + } + + void VisitBoolField(const ROOT::Experimental::RField& field) override + { + this->datatype = arrow::boolean(); + } + + void VisitFloatField(const ROOT::Experimental::RField& field) override + { + this->datatype = arrow::float32(); + } + + void VisitDoubleField(const ROOT::Experimental::RField& field) override + { + this->datatype = arrow::float64(); + } + std::shared_ptr datatype; +}; +} // namespace o2::framework + +auto arrowTypeFromRNTuple(ROOT::Experimental::RFieldBase const& field, int size) +{ + o2::framework::RootNTupleVisitor visitor; + field.AcceptVisitor(visitor); + return visitor.datatype; +} + +namespace o2::framework +{ +std::unique_ptr rootFieldFromArrow(std::shared_ptr field, std::string name) +{ + using namespace ROOT::Experimental; + switch (field->type()->id()) { + case arrow::Type::BOOL: + return std::make_unique>(name); + case arrow::Type::UINT8: + return std::make_unique>(name); + case arrow::Type::UINT16: + return std::make_unique>(name); + case arrow::Type::UINT32: + return std::make_unique>(name); + case arrow::Type::UINT64: + return std::make_unique>(name); + case arrow::Type::INT8: + return std::make_unique>(name); + case arrow::Type::INT16: + return std::make_unique>(name); + case arrow::Type::INT32: + return std::make_unique>(name); + case arrow::Type::INT64: + return std::make_unique>(name); + case arrow::Type::FLOAT: + return std::make_unique>(name); + case arrow::Type::DOUBLE: + return std::make_unique>(name); + case arrow::Type::STRING: + return std::make_unique>(name); + default: + throw runtime_error("Unsupported arrow column type"); + } +} + +class RNTupleFileWriter : public arrow::dataset::FileWriter +{ + std::shared_ptr mWriter; + bool firstBatch = true; + std::vector> valueArrays; + std::vector> valueTypes; + std::vector valueCount; + + public: + RNTupleFileWriter(std::shared_ptr schema, std::shared_ptr options, + std::shared_ptr destination, + arrow::fs::FileLocator destination_locator) + : FileWriter(schema, options, destination, destination_locator) + { + using namespace ROOT::Experimental; + + auto model = RNTupleModel::CreateBare(); + // Let's create a model from the physical schema + for (auto i = 0u; i < schema->fields().size(); ++i) { + auto& field = schema->field(i); + + // Construct all the needed branches. + switch (field->type()->id()) { + case arrow::Type::FIXED_SIZE_LIST: { + auto list = std::static_pointer_cast(field->type()); + auto valueField = field->type()->field(0); + model->AddField(std::make_unique(field->name(), rootFieldFromArrow(valueField, "_0"), list->list_size())); + } break; + case arrow::Type::LIST: { + auto valueField = field->type()->field(0); + model->AddField(std::make_unique(field->name(), rootFieldFromArrow(valueField, "_0"))); + } break; + default: { + model->AddField(rootFieldFromArrow(field, field->name())); + } break; + } + } + auto fileStream = std::dynamic_pointer_cast(destination_); + auto* file = dynamic_cast(fileStream->GetDirectory()); + mWriter = RNTupleWriter::Append(std::move(model), destination_locator_.path, *file, {}); + } + + arrow::Status Write(const std::shared_ptr& batch) override + { + if (firstBatch) { + firstBatch = false; + } + + // Support writing empty tables + if (batch->columns().empty() || batch->num_rows() == 0) { + return arrow::Status::OK(); + } + + for (auto i = 0u; i < batch->columns().size(); ++i) { + auto column = batch->column(i); + auto& field = batch->schema()->field(i); + + valueArrays.push_back(nullptr); + valueTypes.push_back(nullptr); + valueCount.push_back(1); + + switch (field->type()->id()) { + case arrow::Type::FIXED_SIZE_LIST: { + auto list = std::static_pointer_cast(column); + auto listType = std::static_pointer_cast(field->type()); + if (field->type()->field(0)->type()->id() == arrow::Type::BOOL) { + auto boolArray = std::static_pointer_cast(list->values()); + int64_t length = boolArray->length(); + arrow::UInt8Builder builder; + auto ok = builder.Reserve(length); + + for (int64_t i = 0; i < length; ++i) { + if (boolArray->IsValid(i)) { + // Expand each boolean value (true/false) to uint8 (1/0) + uint8_t value = boolArray->Value(i) ? 1 : 0; + auto ok = builder.Append(value); + } else { + // Append null for invalid entries + auto ok = builder.AppendNull(); + } + } + valueArrays.back() = *builder.Finish(); + valueTypes.back() = valueArrays.back()->type(); + } else { + valueArrays.back() = list->values(); + valueTypes.back() = field->type()->field(0)->type(); + } + valueCount.back() = listType->list_size(); + } break; + case arrow::Type::LIST: { + auto list = std::static_pointer_cast(column); + valueArrays.back() = list; + valueTypes.back() = field->type()->field(0)->type(); + valueCount.back() = -1; + } break; + case arrow::Type::BOOL: { + // We unpack the array + auto boolArray = std::static_pointer_cast(column); + int64_t length = boolArray->length(); + arrow::UInt8Builder builder; + auto ok = builder.Reserve(length); + + for (int64_t i = 0; i < length; ++i) { + if (boolArray->IsValid(i)) { + // Expand each boolean value (true/false) to uint8 (1/0) + uint8_t value = boolArray->Value(i) ? 1 : 0; + auto ok = builder.Append(value); + } else { + // Append null for invalid entries + auto ok = builder.AppendNull(); + } + } + valueArrays.back() = *builder.Finish(); + valueTypes.back() = valueArrays.back()->type(); + } break; + default: + valueArrays.back() = column; + valueTypes.back() = field->type(); + break; + } + } + + int64_t pos = 0; + + auto entry = mWriter->CreateEntry(); + std::vector tokens; + tokens.reserve(batch->num_columns()); + std::vector typeIds; + typeIds.reserve(batch->num_columns()); + + for (size_t ci = 0; ci < batch->num_columns(); ++ci) { + auto& field = batch->schema()->field(ci); + typeIds.push_back(batch->column(ci)->type()->id()); + tokens.push_back(entry->GetToken(field->name())); + } + + while (pos < batch->num_rows()) { + for (size_t ci = 0; ci < batch->num_columns(); ++ci) { + auto typeId = typeIds[ci]; + auto token = tokens[ci]; + + switch (typeId) { + case arrow::Type::LIST: { + auto list = std::static_pointer_cast(valueArrays[ci]); + auto value_slice = list->value_slice(pos); + + valueCount[ci] = value_slice->length(); + auto bindValue = [&vc = valueCount, ci, token](auto array, std::unique_ptr& entry) -> void { + using value_type = std::decay_t::value_type; + auto v = std::make_shared>((value_type*)array->raw_values(), vc[ci]); + entry->BindValue(token, v); + }; + switch (valueTypes[ci]->id()) { + case arrow::Type::FLOAT: { + bindValue(std::static_pointer_cast(value_slice), entry); + } break; + case arrow::Type::DOUBLE: { + bindValue(std::static_pointer_cast(value_slice), entry); + } break; + case arrow::Type::INT8: { + bindValue(std::static_pointer_cast(value_slice), entry); + } break; + case arrow::Type::INT16: { + bindValue(std::static_pointer_cast(value_slice), entry); + } break; + case arrow::Type::INT32: { + bindValue(std::static_pointer_cast(value_slice), entry); + } break; + case arrow::Type::INT64: { + bindValue(std::static_pointer_cast(value_slice), entry); + } break; + case arrow::Type::UINT8: { + bindValue(std::static_pointer_cast(value_slice), entry); + } break; + case arrow::Type::UINT16: { + bindValue(std::static_pointer_cast(value_slice), entry); + } break; + case arrow::Type::UINT32: { + bindValue(std::static_pointer_cast(value_slice), entry); + } break; + case arrow::Type::UINT64: { + bindValue(std::static_pointer_cast(value_slice), entry); + } break; + default: { + throw runtime_error("Unsupported kind of VLA"); + } break; + } + } break; + case arrow::Type::FIXED_SIZE_LIST: { + entry->BindRawPtr(token, (void*)(valueArrays[ci]->data()->buffers[1]->data() + pos * valueCount[ci] * valueTypes[ci]->byte_width())); + } break; + case arrow::Type::BOOL: { + // Not sure we actually need this + entry->BindRawPtr(token, (bool*)(valueArrays[ci]->data()->buffers[1]->data() + pos * 1)); + } break; + default: + // By default we consider things scalars. + entry->BindRawPtr(token, (void*)(valueArrays[ci]->data()->buffers[1]->data() + pos * valueTypes[ci]->byte_width())); + break; + } + } + mWriter->Fill(*entry); + ++pos; + } + // mWriter->CommitCluster(); + + return arrow::Status::OK(); + } + + arrow::Future<> + FinishInternal() override + { + return {}; + }; +}; + +arrow::Result> RNTupleFileFormat::Inspect(const arrow::dataset::FileSource& source) const +{ + + auto fs = std::dynamic_pointer_cast(source.filesystem()); + // Actually get the TTree from the ROOT file. + auto ntupleFs = std::dynamic_pointer_cast(fs->GetSubFilesystem(source)); + if (!ntupleFs.get()) { + throw runtime_error_f("Unknown filesystem %s\n", source.filesystem()->type_name().c_str()); + } + ROOT::Experimental::RNTuple* rntuple = ntupleFs->GetRNTuple(source); + + auto inspector = ROOT::Experimental::RNTupleInspector::Create(rntuple); + + auto reader = ROOT::Experimental::RNTupleReader::Open(rntuple); + + auto& tupleField0 = reader->GetModel().GetFieldZero(); + std::vector> fields; + for (auto& tupleField : tupleField0.GetSubFields()) { + auto field = std::make_shared(tupleField->GetFieldName(), arrowTypeFromRNTuple(*tupleField, tupleField->GetValueSize())); + fields.push_back(field); + } + + return std::make_shared(fields); +} + +arrow::Result RNTupleFileFormat::ScanBatchesAsync( + const std::shared_ptr& options, + const std::shared_ptr& fragment) const +{ + auto dataset_schema = options->dataset_schema; + auto ntupleFragment = std::dynamic_pointer_cast(fragment); + + auto generator = [pool = options->pool, ntupleFragment, dataset_schema, &totalCompressedSize = mTotCompressedSize, + &totalUncompressedSize = mTotUncompressedSize]() -> arrow::Future> { + using namespace ROOT::Experimental; + std::vector> columns; + std::vector> fields = dataset_schema->fields(); + + auto containerFS = std::dynamic_pointer_cast(ntupleFragment->source().filesystem()); + auto fs = std::dynamic_pointer_cast(containerFS->GetSubFilesystem(ntupleFragment->source())); + + int64_t rows = -1; + ROOT::Experimental::RNTuple* rntuple = fs->GetRNTuple(ntupleFragment->source()); + auto reader = ROOT::Experimental::RNTupleReader::Open(rntuple); + auto& model = reader->GetModel(); + for (auto& physicalField : fields) { + auto bulk = model.CreateBulk(physicalField->name()); + + auto listType = std::dynamic_pointer_cast(physicalField->type()); + + auto& descriptor = reader->GetDescriptor(); + auto totalEntries = reader->GetNEntries(); + + if (rows == -1) { + rows = totalEntries; + } + if (rows != totalEntries) { + throw runtime_error_f("Unmatching number of rows for branch %s", physicalField->name().c_str()); + } + arrow::Status status; + int readEntries = 0; + std::shared_ptr array; + if (physicalField->type() == arrow::boolean() || + (listType && physicalField->type()->field(0)->type() == arrow::boolean())) { + if (listType) { + std::unique_ptr builder = nullptr; + auto status = arrow::MakeBuilder(pool, physicalField->type()->field(0)->type(), &builder); + if (!status.ok()) { + throw runtime_error("Cannot create value builder"); + } + auto listBuilder = std::make_unique(pool, std::move(builder), listType->list_size()); + auto valueBuilder = listBuilder.get()->value_builder(); + // boolean array special case: we need to use builder to create the bitmap + status = valueBuilder->Reserve(totalEntries * listType->list_size()); + status &= listBuilder->Reserve(totalEntries); + if (!status.ok()) { + throw runtime_error("Failed to reserve memory for array builder"); + } + auto clusterIt = descriptor.FindClusterId(0, 0); + // No adoption for now... + // bulk.AdoptBuffer(buffer, totalEntries) + while (clusterIt != kInvalidDescriptorId) { + auto& index = descriptor.GetClusterDescriptor(clusterIt); + auto mask = std::make_unique(index.GetNEntries()); + std::fill(mask.get(), mask.get() + index.GetNEntries(), true); + void* ptr = bulk.ReadBulk(RClusterIndex(clusterIt, index.GetFirstEntryIndex()), mask.get(), index.GetNEntries()); + int readLast = index.GetNEntries(); + readEntries += readLast; + status &= static_cast(valueBuilder)->AppendValues(reinterpret_cast(ptr), readLast * listType->list_size()); + clusterIt = descriptor.FindNextClusterId(clusterIt); + } + status &= static_cast(listBuilder.get())->AppendValues(readEntries); + if (!status.ok()) { + throw runtime_error("Failed to append values to array"); + } + status &= listBuilder->Finish(&array); + if (!status.ok()) { + throw runtime_error("Failed to create array"); + } + } else if (listType == nullptr) { + std::unique_ptr builder = nullptr; + auto status = arrow::MakeBuilder(pool, physicalField->type(), &builder); + if (!status.ok()) { + throw runtime_error("Cannot create builder"); + } + auto valueBuilder = static_cast(builder.get()); + // boolean array special case: we need to use builder to create the bitmap + status = valueBuilder->Reserve(totalEntries); + if (!status.ok()) { + throw runtime_error("Failed to reserve memory for array builder"); + } + auto clusterIt = descriptor.FindClusterId(0, 0); + while (clusterIt != kInvalidDescriptorId) { + auto& index = descriptor.GetClusterDescriptor(clusterIt); + auto mask = std::make_unique(index.GetNEntries()); + std::fill(mask.get(), mask.get() + index.GetNEntries(), true); + void* ptr = bulk.ReadBulk(RClusterIndex(clusterIt, index.GetFirstEntryIndex()), mask.get(), index.GetNEntries()); + int readLast = index.GetNEntries(); + readEntries += readLast; + status &= valueBuilder->AppendValues(reinterpret_cast(ptr), readLast); + clusterIt = descriptor.FindNextClusterId(clusterIt); + } + if (!status.ok()) { + throw runtime_error("Failed to append values to array"); + } + status &= valueBuilder->Finish(&array); + if (!status.ok()) { + throw runtime_error("Failed to create array"); + } + } + } else { + // other types: use serialized read to build arrays directly. + auto typeSize = physicalField->type()->byte_width(); + // FIXME: for now... + auto bytes = 0; + auto branchSize = bytes ? bytes : 1000000; + auto&& result = arrow::AllocateResizableBuffer(branchSize, pool); + if (!result.ok()) { + throw runtime_error("Cannot allocate values buffer"); + } + std::shared_ptr arrowValuesBuffer = std::move(result).ValueUnsafe(); + auto ptr = arrowValuesBuffer->mutable_data(); + if (ptr == nullptr) { + throw runtime_error("Invalid buffer"); + } + + std::unique_ptr offsetBuffer = nullptr; + + std::shared_ptr arrowOffsetBuffer; + std::span offsets; + int size = 0; + uint32_t totalSize = 0; + int64_t listSize = 1; + if (auto fixedSizeList = std::dynamic_pointer_cast(physicalField->type())) { + listSize = fixedSizeList->list_size(); + typeSize = fixedSizeList->field(0)->type()->byte_width(); + auto clusterIt = descriptor.FindClusterId(0, 0); + while (clusterIt != kInvalidDescriptorId) { + auto& index = descriptor.GetClusterDescriptor(clusterIt); + auto mask = std::make_unique(index.GetNEntries()); + std::fill(mask.get(), mask.get() + index.GetNEntries(), true); + void* inPtr = bulk.ReadBulk(RClusterIndex(clusterIt, index.GetFirstEntryIndex()), mask.get(), index.GetNEntries()); + + int readLast = index.GetNEntries(); + if (listSize == -1) { + size = offsets[readEntries + readLast] - offsets[readEntries]; + } else { + size = readLast * listSize; + } + readEntries += readLast; + memcpy(ptr, inPtr, size * typeSize); + ptr += (ptrdiff_t)(size * typeSize); + clusterIt = descriptor.FindNextClusterId(clusterIt); + } + } else if (auto vlaListType = std::dynamic_pointer_cast(physicalField->type())) { + listSize = -1; + typeSize = vlaListType->field(0)->type()->byte_width(); + offsetBuffer = std::make_unique(TBuffer::EMode::kWrite, 4 * 1024 * 1024); + result = arrow::AllocateResizableBuffer((totalEntries + 1) * (int64_t)sizeof(int), pool); + if (!result.ok()) { + throw runtime_error("Cannot allocate offset buffer"); + } + arrowOffsetBuffer = std::move(result).ValueUnsafe(); + + // Offset bulk + auto offsetBulk = model.CreateBulk(physicalField->name()); + // Actual values are in a different place... + bulk = model.CreateBulk(physicalField->name()); + auto clusterIt = descriptor.FindClusterId(0, 0); + auto* ptrOffset = reinterpret_cast(arrowOffsetBuffer->mutable_data()); + auto* tPtrOffset = reinterpret_cast(ptrOffset); + offsets = std::span{tPtrOffset, tPtrOffset + totalEntries + 1}; + + auto copyOffsets = [&arrowValuesBuffer, &pool, &ptrOffset, &ptr, &totalSize](auto inPtr, size_t total) { + using value_type = typename std::decay_t::value_type; + for (size_t i = 0; i < total; i++) { + *ptrOffset++ = totalSize; + totalSize += inPtr[i].size(); + } + *ptrOffset = totalSize; + auto&& result = arrow::AllocateResizableBuffer(totalSize * sizeof(value_type), pool); + if (!result.ok()) { + throw runtime_error("Cannot allocate values buffer"); + } + arrowValuesBuffer = std::move(result).ValueUnsafe(); + ptr = (uint8_t*)(arrowValuesBuffer->mutable_data()); + // Calculate the size of the buffer here. + for (size_t i = 0; i < total; i++) { + int vlaSizeInBytes = inPtr[i].size() * sizeof(value_type); + if (vlaSizeInBytes == 0) { + continue; + } + memcpy(ptr, inPtr[i].data(), vlaSizeInBytes); + ptr += vlaSizeInBytes; + } + }; + + while (clusterIt != kInvalidDescriptorId) { + auto& index = descriptor.GetClusterDescriptor(clusterIt); + auto mask = std::make_unique(index.GetNEntries()); + std::fill(mask.get(), mask.get() + index.GetNEntries(), true); + int readLast = index.GetNEntries(); + switch (vlaListType->field(0)->type()->id()) { + case arrow::Type::FLOAT: { + copyOffsets((ROOT::Internal::VecOps::RVec*)offsetBulk.ReadBulk(RClusterIndex(clusterIt, index.GetFirstEntryIndex()), mask.get(), index.GetNEntries()), readLast); + } break; + case arrow::Type::DOUBLE: { + copyOffsets((ROOT::Internal::VecOps::RVec*)offsetBulk.ReadBulk(RClusterIndex(clusterIt, index.GetFirstEntryIndex()), mask.get(), index.GetNEntries()), readLast); + } break; + case arrow::Type::INT8: { + copyOffsets((ROOT::Internal::VecOps::RVec*)offsetBulk.ReadBulk(RClusterIndex(clusterIt, index.GetFirstEntryIndex()), mask.get(), index.GetNEntries()), readLast); + } break; + case arrow::Type::INT16: { + copyOffsets((ROOT::Internal::VecOps::RVec*)offsetBulk.ReadBulk(RClusterIndex(clusterIt, index.GetFirstEntryIndex()), mask.get(), index.GetNEntries()), readLast); + } break; + case arrow::Type::INT32: { + copyOffsets((ROOT::Internal::VecOps::RVec*)offsetBulk.ReadBulk(RClusterIndex(clusterIt, index.GetFirstEntryIndex()), mask.get(), index.GetNEntries()), readLast); + } break; + case arrow::Type::INT64: { + copyOffsets((ROOT::Internal::VecOps::RVec*)offsetBulk.ReadBulk(RClusterIndex(clusterIt, index.GetFirstEntryIndex()), mask.get(), index.GetNEntries()), readLast); + } break; + case arrow::Type::UINT8: { + copyOffsets((ROOT::Internal::VecOps::RVec*)offsetBulk.ReadBulk(RClusterIndex(clusterIt, index.GetFirstEntryIndex()), mask.get(), index.GetNEntries()), readLast); + } break; + case arrow::Type::UINT16: { + copyOffsets((ROOT::Internal::VecOps::RVec*)offsetBulk.ReadBulk(RClusterIndex(clusterIt, index.GetFirstEntryIndex()), mask.get(), index.GetNEntries()), readLast); + } break; + case arrow::Type::UINT32: { + copyOffsets((ROOT::Internal::VecOps::RVec*)offsetBulk.ReadBulk(RClusterIndex(clusterIt, index.GetFirstEntryIndex()), mask.get(), index.GetNEntries()), readLast); + } break; + case arrow::Type::UINT64: { + copyOffsets((ROOT::Internal::VecOps::RVec*)offsetBulk.ReadBulk(RClusterIndex(clusterIt, index.GetFirstEntryIndex()), mask.get(), index.GetNEntries()), readLast); + } break; + default: { + throw runtime_error("Unsupported kind of VLA"); + } break; + } + + readEntries += readLast; + clusterIt = descriptor.FindNextClusterId(clusterIt); + } + } else { + auto clusterIt = descriptor.FindClusterId(0, 0); + while (clusterIt != kInvalidDescriptorId) { + auto& index = descriptor.GetClusterDescriptor(clusterIt); + auto mask = std::make_unique(index.GetNEntries()); + std::fill(mask.get(), mask.get() + index.GetNEntries(), true); + void* inPtr = bulk.ReadBulk(RClusterIndex(clusterIt, index.GetFirstEntryIndex()), mask.get(), index.GetNEntries()); + + int readLast = index.GetNEntries(); + if (listSize == -1) { + size = offsets[readEntries + readLast] - offsets[readEntries]; + } else { + size = readLast * listSize; + } + readEntries += readLast; + memcpy(ptr, inPtr, size * typeSize); + ptr += (ptrdiff_t)(size * typeSize); + clusterIt = descriptor.FindNextClusterId(clusterIt); + } + } + switch (listSize) { + case -1: { + auto varray = std::make_shared(physicalField->type()->field(0)->type(), totalSize, arrowValuesBuffer); + array = std::make_shared(physicalField->type(), readEntries, arrowOffsetBuffer, varray); + } break; + case 1: { + totalSize = readEntries * listSize; + array = std::make_shared(physicalField->type(), readEntries, arrowValuesBuffer); + + } break; + default: { + totalSize = readEntries * listSize; + auto varray = std::make_shared(physicalField->type()->field(0)->type(), totalSize, arrowValuesBuffer); + array = std::make_shared(physicalField->type(), readEntries, varray); + } + } + } + columns.push_back(array); + } + + auto batch = arrow::RecordBatch::Make(dataset_schema, rows, columns); + return batch; + }; + + return generator; +} + +arrow::Result> RNTupleFileFormat::MakeWriter(std::shared_ptr destination, + std::shared_ptr schema, + std::shared_ptr options, + arrow::fs::FileLocator destination_locator) const +{ + auto writer = std::make_shared(schema, options, destination, destination_locator); + return std::dynamic_pointer_cast(writer); +} + +arrow::Result> RNTupleFileFormat::MakeFragment( + arrow::dataset::FileSource source, arrow::compute::Expression partition_expression, + std::shared_ptr physical_schema) +{ + std::shared_ptr format = std::make_shared(mTotCompressedSize, mTotUncompressedSize); + + auto fragment = std::make_shared(std::move(source), std::move(format), + std::move(partition_expression), + std::move(physical_schema)); + return std::dynamic_pointer_cast(fragment); +} + +RNTupleFileSystem::~RNTupleFileSystem() = default; + +std::shared_ptr + RNTupleFileFormat::DefaultWriteOptions() +{ + return std::make_shared(shared_from_this()); +} + +struct RNTuplePluginContext { + size_t totalCompressedSize = 0; + size_t totalUncompressedSize = 0; + std::shared_ptr format = nullptr; +}; + +struct RNTupleObjectReadingImplementation : public RootArrowFactoryPlugin { + RootArrowFactory* create() override + { + auto context = new RNTuplePluginContext; + context->format = std::make_shared(context->totalCompressedSize, context->totalUncompressedSize); + return new RootArrowFactory{ + .options = [context]() { return context->format->DefaultWriteOptions(); }, + .format = [context]() { return context->format; }, + .getSubFilesystem = [](void* handle) { + auto rntuple = (ROOT::Experimental::RNTuple*)handle; + return std::shared_ptr(new SingleRNTupleFileSystem(rntuple)); }, + }; + } +}; + +DEFINE_DPL_PLUGINS_BEGIN +DEFINE_DPL_PLUGIN_INSTANCE(RNTupleObjectReadingImplementation, RootObjectReadingImplementation); +DEFINE_DPL_PLUGINS_END +} // namespace o2::framework diff --git a/Framework/AnalysisSupport/src/TTreePlugin.cxx b/Framework/AnalysisSupport/src/TTreePlugin.cxx new file mode 100644 index 0000000000000..e84a053d58d60 --- /dev/null +++ b/Framework/AnalysisSupport/src/TTreePlugin.cxx @@ -0,0 +1,864 @@ +// 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 "Framework/RootArrowFilesystem.h" +#include "Framework/Plugins.h" +#include "Framework/Signpost.h" +#include "Framework/Endian.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +O2_DECLARE_DYNAMIC_LOG(root_arrow_fs); + +namespace o2::framework +{ + +class TTreeFileWriteOptions : public arrow::dataset::FileWriteOptions +{ + public: + TTreeFileWriteOptions(std::shared_ptr format) + : FileWriteOptions(format) + { + } +}; + +// A filesystem which allows me to get a TTree +class TTreeFileSystem : public VirtualRootFileSystemBase +{ + public: + ~TTreeFileSystem() override; + + std::shared_ptr GetSubFilesystem(arrow::dataset::FileSource source) override + { + return std::dynamic_pointer_cast(shared_from_this()); + }; + + arrow::Result> OpenOutputStream( + const std::string& path, + const std::shared_ptr& metadata) override; + + virtual std::unique_ptr& GetTree(arrow::dataset::FileSource source) = 0; +}; + +class SingleTreeFileSystem : public TTreeFileSystem +{ + public: + SingleTreeFileSystem(TTree* tree) + : TTreeFileSystem(), + mTree(tree) + { + } + + arrow::Result GetFileInfo(std::string const& path) override; + + std::string type_name() const override + { + return "ttree"; + } + + std::unique_ptr& GetTree(arrow::dataset::FileSource) override + { + // Simply return the only TTree we have + return mTree; + } + + private: + std::unique_ptr mTree; +}; + +arrow::Result SingleTreeFileSystem::GetFileInfo(std::string const& path) +{ + arrow::dataset::FileSource source(path, shared_from_this()); + arrow::fs::FileInfo result; + result.set_path(path); + result.set_type(arrow::fs::FileType::File); + return result; +} + +class TTreeFileFragment : public arrow::dataset::FileFragment +{ + public: + TTreeFileFragment(arrow::dataset::FileSource source, + std::shared_ptr format, + arrow::compute::Expression partition_expression, + std::shared_ptr physical_schema) + : FileFragment(std::move(source), std::move(format), std::move(partition_expression), std::move(physical_schema)) + { + } +}; + +class TTreeFileFormat : public arrow::dataset::FileFormat +{ + size_t& mTotCompressedSize; + size_t& mTotUncompressedSize; + + public: + TTreeFileFormat(size_t& totalCompressedSize, size_t& totalUncompressedSize) + : FileFormat({}), + mTotCompressedSize(totalCompressedSize), + mTotUncompressedSize(totalUncompressedSize) + { + } + + ~TTreeFileFormat() override = default; + + std::string type_name() const override + { + return "ttree"; + } + + bool Equals(const FileFormat& other) const override + { + return other.type_name() == this->type_name(); + } + + arrow::Result IsSupported(const arrow::dataset::FileSource& source) const override + { + auto fs = std::dynamic_pointer_cast(source.filesystem()); + auto subFs = fs->GetSubFilesystem(source); + if (std::dynamic_pointer_cast(subFs)) { + return true; + } + return false; + } + + arrow::Result> Inspect(const arrow::dataset::FileSource& source) const override; + /// \brief Create a FileFragment for a FileSource. + arrow::Result> MakeFragment( + arrow::dataset::FileSource source, arrow::compute::Expression partition_expression, + std::shared_ptr physical_schema) override; + + arrow::Result> MakeWriter(std::shared_ptr destination, std::shared_ptr schema, std::shared_ptr options, arrow::fs::FileLocator destination_locator) const override; + + std::shared_ptr DefaultWriteOptions() override; + + arrow::Result ScanBatchesAsync( + const std::shared_ptr& options, + const std::shared_ptr& fragment) const override; +}; + +// An arrow outputstream which allows to write to a TTree. Eventually +// with a prefix for the branches. +class TTreeOutputStream : public arrow::io::OutputStream +{ + public: + // Using a pointer means that the tree itself is owned by another + // class + TTreeOutputStream(TTree *, std::string branchPrefix); + + arrow::Status Close() override; + + arrow::Result Tell() const override; + + arrow::Status Write(const void* data, int64_t nbytes) override; + + bool closed() const override; + + TBranch* CreateBranch(char const* branchName, char const* sizeBranch); + + TTree* GetTree() + { + return mTree; + } + + private: + TTree* mTree; + std::string mBranchPrefix; +}; + +// An arrow outputstream which allows to write to a ttree +// @a branch prefix is to be used to identify a set of branches which all belong to +// the same table. +TTreeOutputStream::TTreeOutputStream(TTree* f, std::string branchPrefix) + : mTree(f), + mBranchPrefix(std::move(branchPrefix)) +{ +} + +arrow::Status TTreeOutputStream::Close() +{ + if (mTree->GetCurrentFile() == nullptr) { + return arrow::Status::Invalid("Cannot close a tree not attached to a file"); + } + mTree->GetCurrentFile()->Close(); + return arrow::Status::OK(); +} + +arrow::Result TTreeOutputStream::Tell() const +{ + return arrow::Result(arrow::Status::NotImplemented("Cannot move")); +} + +arrow::Status TTreeOutputStream::Write(const void* data, int64_t nbytes) +{ + return arrow::Status::NotImplemented("Cannot write raw bytes to a TTree"); +} + +bool TTreeOutputStream::closed() const +{ + // A standalone tree is never closed. + if (mTree->GetCurrentFile() == nullptr) { + return false; + } + return mTree->GetCurrentFile()->IsOpen() == false; +} + +TBranch* TTreeOutputStream::CreateBranch(char const* branchName, char const* sizeBranch) +{ + return mTree->Branch((mBranchPrefix + "/" + branchName).c_str(), (char*)nullptr, (mBranchPrefix + sizeBranch).c_str()); +} + +struct TTreePluginContext { + size_t totalCompressedSize = 0; + size_t totalUncompressedSize = 0; + std::shared_ptr format = nullptr; +}; + +struct TTreeObjectReadingImplementation : public RootArrowFactoryPlugin { + RootArrowFactory* create() override + { + auto context = new TTreePluginContext; + context->format = std::make_shared(context->totalCompressedSize, context->totalUncompressedSize); + return new RootArrowFactory{ + .options = [context]() { return context->format->DefaultWriteOptions(); }, + .format = [context]() { return context->format; }, + .getSubFilesystem = [](void* handle) { + auto tree = (TTree*)handle; + return std::shared_ptr(new SingleTreeFileSystem(tree)); }, + }; + } +}; + +arrow::Result TTreeFileFormat::ScanBatchesAsync( + const std::shared_ptr& options, + const std::shared_ptr& fragment) const +{ + // Get the fragment as a TTreeFragment. This might be PART of a TTree. + auto treeFragment = std::dynamic_pointer_cast(fragment); + // This is the schema we want to read + auto dataset_schema = options->dataset_schema; + + auto generator = [pool = options->pool, treeFragment, dataset_schema, &totalCompressedSize = mTotCompressedSize, + &totalUncompressedSize = mTotUncompressedSize]() -> arrow::Future> { + auto schema = treeFragment->format()->Inspect(treeFragment->source()); + + std::vector> columns; + std::vector> fields = dataset_schema->fields(); + auto physical_schema = *treeFragment->ReadPhysicalSchema(); + + static TBufferFile buffer{TBuffer::EMode::kWrite, 4 * 1024 * 1024}; + auto containerFS = std::dynamic_pointer_cast(treeFragment->source().filesystem()); + auto fs = std::dynamic_pointer_cast(containerFS->GetSubFilesystem(treeFragment->source())); + + int64_t rows = -1; + auto& tree = fs->GetTree(treeFragment->source()); + for (auto& field : fields) { + // The field actually on disk + auto physicalField = physical_schema->GetFieldByName(field->name()); + TBranch* branch = tree->GetBranch(physicalField->name().c_str()); + assert(branch); + buffer.Reset(); + auto totalEntries = branch->GetEntries(); + if (rows == -1) { + rows = totalEntries; + } + if (rows != totalEntries) { + throw runtime_error_f("Unmatching number of rows for branch %s", branch->GetName()); + } + arrow::Status status; + int readEntries = 0; + std::shared_ptr array; + auto listType = std::dynamic_pointer_cast(physicalField->type()); + if (physicalField->type() == arrow::boolean() || + (listType && physicalField->type()->field(0)->type() == arrow::boolean())) { + if (listType) { + std::unique_ptr builder = nullptr; + auto status = arrow::MakeBuilder(pool, physicalField->type()->field(0)->type(), &builder); + if (!status.ok()) { + throw runtime_error("Cannot create value builder"); + } + auto listBuilder = std::make_unique(pool, std::move(builder), listType->list_size()); + auto valueBuilder = listBuilder.get()->value_builder(); + // boolean array special case: we need to use builder to create the bitmap + status = valueBuilder->Reserve(totalEntries * listType->list_size()); + status &= listBuilder->Reserve(totalEntries); + if (!status.ok()) { + throw runtime_error("Failed to reserve memory for array builder"); + } + while (readEntries < totalEntries) { + auto readLast = branch->GetBulkRead().GetBulkEntries(readEntries, buffer); + readEntries += readLast; + status &= static_cast(valueBuilder)->AppendValues(reinterpret_cast(buffer.GetCurrent()), readLast * listType->list_size()); + } + status &= static_cast(listBuilder.get())->AppendValues(readEntries); + if (!status.ok()) { + throw runtime_error("Failed to append values to array"); + } + status &= listBuilder->Finish(&array); + if (!status.ok()) { + throw runtime_error("Failed to create array"); + } + } else if (listType == nullptr) { + std::unique_ptr builder = nullptr; + auto status = arrow::MakeBuilder(pool, physicalField->type(), &builder); + if (!status.ok()) { + throw runtime_error("Cannot create builder"); + } + auto valueBuilder = static_cast(builder.get()); + // boolean array special case: we need to use builder to create the bitmap + status = valueBuilder->Reserve(totalEntries); + if (!status.ok()) { + throw runtime_error("Failed to reserve memory for array builder"); + } + while (readEntries < totalEntries) { + auto readLast = branch->GetBulkRead().GetBulkEntries(readEntries, buffer); + readEntries += readLast; + status &= valueBuilder->AppendValues(reinterpret_cast(buffer.GetCurrent()), readLast); + } + if (!status.ok()) { + throw runtime_error("Failed to append values to array"); + } + status &= valueBuilder->Finish(&array); + if (!status.ok()) { + throw runtime_error("Failed to create array"); + } + } + } else { + // other types: use serialized read to build arrays directly. + auto typeSize = physicalField->type()->byte_width(); + // This is needed for branches which have not been persisted. + auto bytes = branch->GetTotBytes(); + auto branchSize = bytes ? bytes : 1000000; + auto&& result = arrow::AllocateResizableBuffer(branchSize, pool); + if (!result.ok()) { + throw runtime_error("Cannot allocate values buffer"); + } + std::shared_ptr arrowValuesBuffer = std::move(result).ValueUnsafe(); + auto ptr = arrowValuesBuffer->mutable_data(); + if (ptr == nullptr) { + throw runtime_error("Invalid buffer"); + } + + std::unique_ptr offsetBuffer = nullptr; + + uint32_t offset = 0; + int count = 0; + std::shared_ptr arrowOffsetBuffer; + std::span offsets; + int size = 0; + uint32_t totalSize = 0; + TBranch* mSizeBranch = nullptr; + int64_t listSize = 1; + if (auto fixedSizeList = std::dynamic_pointer_cast(physicalField->type())) { + listSize = fixedSizeList->list_size(); + typeSize = fixedSizeList->field(0)->type()->byte_width(); + } else if (auto vlaListType = std::dynamic_pointer_cast(physicalField->type())) { + listSize = -1; + typeSize = vlaListType->field(0)->type()->byte_width(); + } + if (listSize == -1) { + mSizeBranch = branch->GetTree()->GetBranch((std::string{branch->GetName()} + "_size").c_str()); + offsetBuffer = std::make_unique(TBuffer::EMode::kWrite, 4 * 1024 * 1024); + result = arrow::AllocateResizableBuffer((totalEntries + 1) * (int64_t)sizeof(int), pool); + if (!result.ok()) { + throw runtime_error("Cannot allocate offset buffer"); + } + arrowOffsetBuffer = std::move(result).ValueUnsafe(); + unsigned char* ptrOffset = arrowOffsetBuffer->mutable_data(); + auto* tPtrOffset = reinterpret_cast(ptrOffset); + offsets = std::span{tPtrOffset, tPtrOffset + totalEntries + 1}; + + // read sizes first + while (readEntries < totalEntries) { + auto readLast = mSizeBranch->GetBulkRead().GetEntriesSerialized(readEntries, *offsetBuffer); + readEntries += readLast; + for (auto i = 0; i < readLast; ++i) { + offsets[count++] = (int)offset; + offset += swap32_(reinterpret_cast(offsetBuffer->GetCurrent())[i]); + } + } + offsets[count] = (int)offset; + totalSize = offset; + readEntries = 0; + } + + while (readEntries < totalEntries) { + auto readLast = branch->GetBulkRead().GetEntriesSerialized(readEntries, buffer); + if (listSize == -1) { + size = offsets[readEntries + readLast] - offsets[readEntries]; + } else { + size = readLast * listSize; + } + readEntries += readLast; + swapCopy(ptr, buffer.GetCurrent(), size, typeSize); + ptr += (ptrdiff_t)(size * typeSize); + } + if (listSize >= 1) { + totalSize = readEntries * listSize; + } + std::shared_ptr varray; + switch (listSize) { + case -1: + varray = std::make_shared(physicalField->type()->field(0)->type(), totalSize, arrowValuesBuffer); + array = std::make_shared(physicalField->type(), readEntries, arrowOffsetBuffer, varray); + break; + case 1: + array = std::make_shared(physicalField->type(), readEntries, arrowValuesBuffer); + break; + default: + varray = std::make_shared(physicalField->type()->field(0)->type(), totalSize, arrowValuesBuffer); + array = std::make_shared(physicalField->type(), readEntries, varray); + } + } + + branch->SetStatus(false); + branch->DropBaskets("all"); + branch->Reset(); + branch->GetTransientBuffer(0)->Expand(0); + + columns.push_back(array); + } + auto batch = arrow::RecordBatch::Make(dataset_schema, rows, columns); + totalCompressedSize += tree->GetZipBytes(); + totalUncompressedSize += tree->GetTotBytes(); + return batch; + }; + return generator; +} + +char const* rootSuffixFromArrow(arrow::Type::type id) +{ + switch (id) { + case arrow::Type::BOOL: + return "/O"; + case arrow::Type::UINT8: + return "/b"; + case arrow::Type::UINT16: + return "/s"; + case arrow::Type::UINT32: + return "/i"; + case arrow::Type::UINT64: + return "/l"; + case arrow::Type::INT8: + return "/B"; + case arrow::Type::INT16: + return "/S"; + case arrow::Type::INT32: + return "/I"; + case arrow::Type::INT64: + return "/L"; + case arrow::Type::FLOAT: + return "/F"; + case arrow::Type::DOUBLE: + return "/D"; + default: + throw runtime_error("Unsupported arrow column type"); + } +} + +arrow::Result> TTreeFileSystem::OpenOutputStream( + const std::string& path, + const std::shared_ptr& metadata) +{ + arrow::dataset::FileSource source{path, shared_from_this()}; + auto prefix = metadata->Get("branch_prefix"); + if (prefix.ok()) { + return std::make_shared(GetTree(source).get(), *prefix); + } + return std::make_shared(GetTree(source).get(), ""); +} + +namespace +{ +struct BranchInfo { + std::string name; + TBranch* ptr; + bool mVLA; +}; +} // namespace + +auto arrowTypeFromROOT(EDataType type, int size) +{ + auto typeGenerator = [](std::shared_ptr const& type, int size) -> std::shared_ptr { + switch (size) { + case -1: + return arrow::list(type); + case 1: + return std::move(type); + default: + return arrow::fixed_size_list(type, size); + } + }; + + switch (type) { + case EDataType::kBool_t: + return typeGenerator(arrow::boolean(), size); + case EDataType::kUChar_t: + return typeGenerator(arrow::uint8(), size); + case EDataType::kUShort_t: + return typeGenerator(arrow::uint16(), size); + case EDataType::kUInt_t: + return typeGenerator(arrow::uint32(), size); + case EDataType::kULong64_t: + return typeGenerator(arrow::uint64(), size); + case EDataType::kChar_t: + return typeGenerator(arrow::int8(), size); + case EDataType::kShort_t: + return typeGenerator(arrow::int16(), size); + case EDataType::kInt_t: + return typeGenerator(arrow::int32(), size); + case EDataType::kLong64_t: + return typeGenerator(arrow::int64(), size); + case EDataType::kFloat_t: + return typeGenerator(arrow::float32(), size); + case EDataType::kDouble_t: + return typeGenerator(arrow::float64(), size); + default: + throw o2::framework::runtime_error_f("Unsupported branch type: %d", static_cast(type)); + } +} + +arrow::Result> TTreeFileFormat::Inspect(const arrow::dataset::FileSource& source) const +{ + arrow::Schema schema{{}}; + auto fs = std::dynamic_pointer_cast(source.filesystem()); + // Actually get the TTree from the ROOT file. + auto treeFs = std::dynamic_pointer_cast(fs->GetSubFilesystem(source)); + if (!treeFs.get()) { + throw runtime_error_f("Unknown filesystem %s\n", source.filesystem()->type_name().c_str()); + } + auto& tree = treeFs->GetTree(source); + + auto branches = tree->GetListOfBranches(); + auto n = branches->GetEntries(); + + std::vector branchInfos; + for (auto i = 0; i < n; ++i) { + auto branch = static_cast(branches->At(i)); + auto name = std::string{branch->GetName()}; + auto pos = name.find("_size"); + if (pos != std::string::npos) { + name.erase(pos); + branchInfos.emplace_back(BranchInfo{name, (TBranch*)nullptr, true}); + } else { + auto lookup = std::find_if(branchInfos.begin(), branchInfos.end(), [&](BranchInfo const& bi) { + return bi.name == name; + }); + if (lookup == branchInfos.end()) { + branchInfos.emplace_back(BranchInfo{name, branch, false}); + } else { + lookup->ptr = branch; + } + } + } + + std::vector> fields; + tree->SetCacheSize(25000000); + for (auto& bi : branchInfos) { + static TClass* cls; + EDataType type; + bi.ptr->GetExpectedType(cls, type); + auto listSize = -1; + if (!bi.mVLA) { + listSize = static_cast(bi.ptr->GetListOfLeaves()->At(0))->GetLenStatic(); + } + auto field = std::make_shared(bi.ptr->GetName(), arrowTypeFromROOT(type, listSize)); + fields.push_back(field); + + tree->AddBranchToCache(bi.ptr); + if (strncmp(bi.ptr->GetName(), "fIndexArray", strlen("fIndexArray")) == 0) { + std::string sizeBranchName = bi.ptr->GetName(); + sizeBranchName += "_size"; + auto* sizeBranch = (TBranch*)tree->GetBranch(sizeBranchName.c_str()); + if (sizeBranch) { + tree->AddBranchToCache(sizeBranch); + } + } + } + tree->StopCacheLearningPhase(); + + return std::make_shared(fields); +} + +/// \brief Create a FileFragment for a FileSource. +arrow::Result> TTreeFileFormat::MakeFragment( + arrow::dataset::FileSource source, arrow::compute::Expression partition_expression, + std::shared_ptr physical_schema) +{ + std::shared_ptr format = std::make_shared(mTotCompressedSize, mTotUncompressedSize); + + auto fragment = std::make_shared(std::move(source), std::move(format), + std::move(partition_expression), + std::move(physical_schema)); + return std::dynamic_pointer_cast(fragment); +} + +class TTreeFileWriter : public arrow::dataset::FileWriter +{ + std::vector branches; + std::vector sizesBranches; + std::vector> valueArrays; + std::vector> sizeArrays; + std::vector> valueTypes; + + std::vector valuesIdealBasketSize; + std::vector sizeIdealBasketSize; + + std::vector typeSizes; + std::vector listSizes; + bool firstBasket = true; + + // This is to create a batsket size according to the first batch. + void finaliseBasketSize(std::shared_ptr firstBatch) + { + O2_SIGNPOST_ID_FROM_POINTER(sid, root_arrow_fs, this); + O2_SIGNPOST_START(root_arrow_fs, sid, "finaliseBasketSize", "First batch with %lli rows received and %zu columns", + firstBatch->num_rows(), firstBatch->columns().size()); + for (size_t i = 0; i < branches.size(); i++) { + auto* branch = branches[i]; + auto* sizeBranch = sizesBranches[i]; + + int valueSize = valueTypes[i]->byte_width(); + if (listSizes[i] == 1) { + O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, sid, "finaliseBasketSize", "Branch %s exists and uses %d bytes per entry for %lli entries.", + branch->GetName(), valueSize, firstBatch->num_rows()); + assert(sizeBranch == nullptr); + branch->SetBasketSize(1024 + firstBatch->num_rows() * valueSize); + } else if (listSizes[i] == -1) { + O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, sid, "finaliseBasketSize", "Branch %s exists and uses %d bytes per entry.", + branch->GetName(), valueSize); + // This should probably lookup the + auto column = firstBatch->GetColumnByName(schema_->field(i)->name()); + auto list = std::static_pointer_cast(column); + O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, sid, "finaliseBasketSize", "Branch %s needed. Associated size branch %s and there are %lli entries of size %d in that list.", + branch->GetName(), sizeBranch->GetName(), list->length(), valueSize); + branch->SetBasketSize(1024 + firstBatch->num_rows() * valueSize * list->length()); + sizeBranch->SetBasketSize(1024 + firstBatch->num_rows() * 4); + } else { + O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, sid, "finaliseBasketSize", "Branch %s needed. There are %lli entries per array of size %d in that list.", + branch->GetName(), listSizes[i], valueSize); + assert(sizeBranch == nullptr); + branch->SetBasketSize(1024 + firstBatch->num_rows() * valueSize * listSizes[i]); + } + + auto field = firstBatch->schema()->field(i); + if (field->name().starts_with("fIndexArray")) { + // One int per array to keep track of the size + int idealBasketSize = 4 * firstBatch->num_rows() + 1024 + field->type()->byte_width() * firstBatch->num_rows(); // minimal additional size needed, otherwise we get 2 baskets + int basketSize = std::max(32000, idealBasketSize); // keep a minimum value + sizeBranch->SetBasketSize(basketSize); + branch->SetBasketSize(basketSize); + } + } + O2_SIGNPOST_END(root_arrow_fs, sid, "finaliseBasketSize", "Done"); + } + + public: + // Create the TTree based on the physical_schema, not the one in the batch. + // The write method will have to reconcile the two schemas. + TTreeFileWriter(std::shared_ptr schema, std::shared_ptr options, + std::shared_ptr destination, + arrow::fs::FileLocator destination_locator) + : FileWriter(schema, options, destination, destination_locator) + { + // Batches have the same number of entries for each column. + auto directoryStream = std::dynamic_pointer_cast(destination_); + auto treeStream = std::dynamic_pointer_cast(destination_); + + if (directoryStream.get()) { + TDirectoryFile* dir = directoryStream->GetDirectory(); + dir->cd(); + auto* tree = new TTree(destination_locator_.path.c_str(), ""); + treeStream = std::make_shared(tree, ""); + } else if (treeStream.get()) { + // We already have a tree stream, let's derive a new one + // with the destination_locator_.path as prefix for the branches + // This way we can multiplex multiple tables in the same tree. + auto* tree = treeStream->GetTree(); + treeStream = std::make_shared(tree, destination_locator_.path); + } else { + // I could simply set a prefix here to merge to an already existing tree. + throw std::runtime_error("Unsupported backend."); + } + + for (auto i = 0u; i < schema->fields().size(); ++i) { + auto& field = schema->field(i); + listSizes.push_back(1); + + int valuesIdealBasketSize = 0; + // Construct all the needed branches. + switch (field->type()->id()) { + case arrow::Type::FIXED_SIZE_LIST: { + listSizes.back() = std::static_pointer_cast(field->type())->list_size(); + valuesIdealBasketSize = 1024 + valueTypes.back()->byte_width() * listSizes.back(); + valueTypes.push_back(field->type()->field(0)->type()); + sizesBranches.push_back(nullptr); + std::string leafList = fmt::format("{}[{}]{}", field->name(), listSizes.back(), rootSuffixFromArrow(valueTypes.back()->id())); + branches.push_back(treeStream->CreateBranch(field->name().c_str(), leafList.c_str())); + } break; + case arrow::Type::LIST: { + valueTypes.push_back(field->type()->field(0)->type()); + std::string leafList = fmt::format("{}[{}_size]{}", field->name(), field->name(), rootSuffixFromArrow(valueTypes.back()->id())); + listSizes.back() = -1; // VLA, we need to calculate it on the fly; + std::string sizeLeafList = field->name() + "_size/I"; + sizesBranches.push_back(treeStream->CreateBranch((field->name() + "_size").c_str(), sizeLeafList.c_str())); + branches.push_back(treeStream->CreateBranch(field->name().c_str(), leafList.c_str())); + // Notice that this could be replaced by a better guess of the + // average size of the list elements, but this is not trivial. + } break; + default: { + valueTypes.push_back(field->type()); + std::string leafList = field->name() + rootSuffixFromArrow(valueTypes.back()->id()); + sizesBranches.push_back(nullptr); + branches.push_back(treeStream->CreateBranch(field->name().c_str(), leafList.c_str())); + } break; + } + } + // We create the branches from the schema + } + + arrow::Status Write(const std::shared_ptr& batch) override + { + if (firstBasket) { + firstBasket = false; + finaliseBasketSize(batch); + } + + // Support writing empty tables + if (batch->columns().empty() || batch->num_rows() == 0) { + return arrow::Status::OK(); + } + + // Batches have the same number of entries for each column. + auto directoryStream = std::dynamic_pointer_cast(destination_); + TTree* tree = nullptr; + if (directoryStream.get()) { + TDirectoryFile* dir = directoryStream->GetDirectory(); + tree = (TTree*)dir->Get(destination_locator_.path.c_str()); + } + auto treeStream = std::dynamic_pointer_cast(destination_); + + if (!tree) { + // I could simply set a prefix here to merge to an already existing tree. + throw std::runtime_error("Unsupported backend."); + } + + for (auto i = 0u; i < batch->columns().size(); ++i) { + auto column = batch->column(i); + auto& field = batch->schema()->field(i); + + valueArrays.push_back(nullptr); + + switch (field->type()->id()) { + case arrow::Type::FIXED_SIZE_LIST: { + auto list = std::static_pointer_cast(column); + valueArrays.back() = list->values(); + } break; + case arrow::Type::LIST: { + auto list = std::static_pointer_cast(column); + valueArrays.back() = list; + } break; + case arrow::Type::BOOL: { + // In case of arrays of booleans, we need to go back to their + // char based representation for ROOT to save them. + auto boolArray = std::static_pointer_cast(column); + + int64_t length = boolArray->length(); + arrow::UInt8Builder builder; + auto ok = builder.Reserve(length); + + for (int64_t i = 0; i < length; ++i) { + if (boolArray->IsValid(i)) { + // Expand each boolean value (true/false) to uint8 (1/0) + uint8_t value = boolArray->Value(i) ? 1 : 0; + auto ok = builder.Append(value); + } else { + // Append null for invalid entries + auto ok = builder.AppendNull(); + } + } + valueArrays.back() = *builder.Finish(); + } break; + default: + valueArrays.back() = column; + } + } + + int64_t pos = 0; + while (pos < batch->num_rows()) { + for (size_t bi = 0; bi < branches.size(); ++bi) { + auto* branch = branches[bi]; + auto* sizeBranch = sizesBranches[bi]; + auto array = batch->column(bi); + auto& field = batch->schema()->field(bi); + auto& listSize = listSizes[bi]; + auto valueType = valueTypes[bi]; + auto valueArray = valueArrays[bi]; + + switch (field->type()->id()) { + case arrow::Type::LIST: { + auto list = std::static_pointer_cast(array); + listSize = list->value_length(pos); + uint8_t const* buffer = std::static_pointer_cast(valueArray)->values()->data() + array->offset() + list->value_offset(pos) * valueType->byte_width(); + branch->SetAddress((void*)buffer); + sizeBranch->SetAddress(&listSize); + }; + break; + case arrow::Type::FIXED_SIZE_LIST: + default: { + uint8_t const* buffer = std::static_pointer_cast(valueArray)->values()->data() + array->offset() + pos * listSize * valueType->byte_width(); + branch->SetAddress((void*)buffer); + }; + } + } + tree->Fill(); + ++pos; + } + return arrow::Status::OK(); + } + + arrow::Future<> FinishInternal() override + { + auto treeStream = std::dynamic_pointer_cast(destination_); + auto* tree = treeStream->GetTree(); + tree->Write("", TObject::kOverwrite); + tree->SetDirectory(nullptr); + + return {}; + }; +}; +arrow::Result> TTreeFileFormat::MakeWriter(std::shared_ptr destination, std::shared_ptr schema, std::shared_ptr options, arrow::fs::FileLocator destination_locator) const +{ + auto writer = std::make_shared(schema, options, destination, destination_locator); + return std::dynamic_pointer_cast(writer); +} + +std::shared_ptr TTreeFileFormat::DefaultWriteOptions() +{ + std::shared_ptr options( + new TTreeFileWriteOptions(shared_from_this())); + return options; +} + +TTreeFileSystem::~TTreeFileSystem() = default; + +DEFINE_DPL_PLUGINS_BEGIN +DEFINE_DPL_PLUGIN_INSTANCE(TTreeObjectReadingImplementation, RootObjectReadingImplementation); +DEFINE_DPL_PLUGINS_END +} // namespace o2::framework diff --git a/Framework/Core/CMakeLists.txt b/Framework/Core/CMakeLists.txt index 02367afdcc556..103b559f642e2 100644 --- a/Framework/Core/CMakeLists.txt +++ b/Framework/Core/CMakeLists.txt @@ -18,6 +18,7 @@ o2_add_library(Framework src/AnalysisDataModel.cxx src/AnalysisSupportHelpers.cxx src/ASoA.cxx + src/ASoAHelpers.cxx src/AsyncQueue.cxx src/AnalysisDataModelHelpers.cxx src/BoostOptionsRetriever.cxx @@ -51,6 +52,7 @@ o2_add_library(Framework src/ConfigParamDiscovery.cxx src/ConfigParamStore.cxx src/ConfigParamsHelper.cxx + src/ConfigParamRegistry.cxx src/ChannelParamSpec.cxx src/DDSConfigHelpers.cxx src/DataAllocator.cxx @@ -137,6 +139,7 @@ o2_add_library(Framework src/Task.cxx src/Array2D.cxx src/Variant.cxx + src/VariantPropertyTreeHelpers.cxx src/WorkflowCustomizationHelpers.cxx src/WorkflowHelpers.cxx src/WorkflowSerializationHelpers.cxx @@ -236,6 +239,7 @@ add_executable(o2-test-framework-core test/test_Services.cxx test/test_StringHelpers.cxx test/test_StaticFor.cxx + test/test_TableSpawner.cxx test/test_TMessageSerializer.cxx test/test_TableBuilder.cxx test/test_TimeParallelPipelining.cxx @@ -269,6 +273,10 @@ o2_add_test(Timers NAME test_Framework_test_Timers LABELS framework PUBLIC_LINK_LIBRARIES O2::Framework) +o2_add_executable(framework-ao2d-to-ao3d + SOURCES test/o2AO2DToAO3D.cxx + PUBLIC_LINK_LIBRARIES O2::Framework) + # FIXME: make this a proper test, when it actually does not hang. o2_add_executable(test-framework-ConsumeWhenAllOrdered SOURCES test/test_ConsumeWhenAllOrdered.cxx @@ -298,6 +306,7 @@ add_executable(o2-test-framework-root target_link_libraries(o2-test-framework-root PRIVATE O2::Framework) target_link_libraries(o2-test-framework-root PRIVATE O2::Catch2) target_link_libraries(o2-test-framework-root PRIVATE ROOT::ROOTDataFrame) +target_link_libraries(o2-test-framework-root PRIVATE ROOT::ROOTNTuple) set_property(TARGET o2-test-framework-root PROPERTY RUNTIME_OUTPUT_DIRECTORY ${outdir}) add_test(NAME framework:root COMMAND o2-test-framework-root --skip-benchmarks) add_test(NAME framework:crash COMMAND sh -e -c "PATH=${CMAKE_RUNTIME_OUTPUT_DIRECTORY}:$PATH ${CMAKE_CURRENT_LIST_DIR}/test/test_AllCrashTypes.sh") diff --git a/Framework/Core/include/Framework/ASoA.h b/Framework/Core/include/Framework/ASoA.h index 34d18476e483d..8af872a64176d 100644 --- a/Framework/Core/include/Framework/ASoA.h +++ b/Framework/Core/include/Framework/ASoA.h @@ -15,6 +15,7 @@ #include "Framework/Pack.h" #include "Framework/FunctionalHelpers.h" #include "Headers/DataHeader.h" +#include "Headers/DataHeaderHelpers.h" #include "Framework/CompilerBuiltins.h" #include "Framework/Traits.h" #include "Framework/Expressions.h" @@ -26,152 +27,364 @@ #include #include #include +#include #include #include +#include +#include #include #include -#define DECLARE_SOA_METADATA() \ - template \ - struct MetadataTrait { \ - using metadata = std::void_t; \ - }; - -#define DECLARE_SOA_ITERATOR_METADATA() \ - template \ - requires(o2::soa::is_iterator) \ - struct MetadataTrait { \ - using metadata = typename MetadataTrait::metadata; \ - }; - namespace o2::framework { using ListVector = std::vector>; std::string cutString(std::string&& str); +std::string strToUpper(std::string&& str); +} // namespace o2::framework -struct OriginEnc { - static constexpr size_t size = 4; - uint32_t value; - consteval OriginEnc(uint32_t v) : value{v} - { - } - consteval OriginEnc(std::string_view in) : value{0} +namespace o2::soa +{ +void accessingInvalidIndexFor(const char* getter); +void dereferenceWithWrongType(); +void missingFilterDeclaration(int hash, int ai); +void notBoundTable(const char* tableName); +} // namespace o2::soa + +namespace o2::soa +{ +/// Generic identifier for a table type +struct TableRef { + consteval TableRef() + : label_hash{0}, + desc_hash{0}, + origin_hash{0}, + version{0} { - for (auto i = 0U; i < (size < in.size() ? size : in.size()); ++i) { - value |= ((uint8_t)in[i]) << (8 * i); - } } - operator const char*() const + consteval TableRef(uint32_t _label, uint32_t _desc, uint32_t _origin, uint32_t _version) + : label_hash{_label}, + desc_hash{_desc}, + origin_hash{_origin}, + version{_version} { - return (const char*)(&value); } + uint32_t label_hash; + uint32_t desc_hash; + uint32_t origin_hash; + uint32_t version; - consteval operator o2::header::DataOrigin() + constexpr bool operator==(TableRef const& other) const noexcept { - return o2::header::DataOrigin{value}; + return (this->label_hash == other.label_hash) && + (this->desc_hash == other.desc_hash) && + (this->origin_hash == other.origin_hash) && + (this->version == other.version); } - consteval OriginEnc(OriginEnc const& other) = default; - consteval OriginEnc(OriginEnc&& other) noexcept = default; - consteval OriginEnc& operator=(OriginEnc const& other) = default; - consteval OriginEnc& operator=(OriginEnc&& other) noexcept = default; -}; -} // namespace o2::framework - -template <> -struct fmt::formatter { - char presentation = 's'; - constexpr auto parse(format_parse_context& ctx) + constexpr bool descriptionCompatible(TableRef const& other) const noexcept { - auto it = ctx.begin(), end = ctx.end(); - if (it != end && (*it == 's')) { - presentation = *it++; - } - - // Check if reached the end of the range: - if (it != end && *it != '}') { - throw format_error("invalid pick format"); - } - - // Return an iterator past the end of the parsed range: - return it; + return this->desc_hash == other.desc_hash; } - template - auto format(o2::framework::OriginEnc const& origin, FormatContext& ctx) + constexpr bool descriptionCompatible(uint32_t _desc_hash) const noexcept { - return fmt::format_to(ctx.out(), "{}", (std::string_view)origin); + return this->desc_hash == _desc_hash; } + + constexpr TableRef(TableRef const&) = default; + constexpr TableRef& operator=(TableRef const&) = default; + constexpr TableRef(TableRef&&) = default; + constexpr TableRef& operator=(TableRef&&) = default; }; -namespace o2::aod +/// Helpers to manipulate TableRef arrays +template ar1, std::array ar2> +consteval auto merge() { -DECLARE_SOA_METADATA(); + constexpr const int duplicates = std::ranges::count_if(ar2.begin(), ar2.end(), [&](TableRef const& a) { return std::any_of(ar1.begin(), ar1.end(), [&](TableRef const& e) { return e == a; }); }); + std::array out; + + auto pos = std::copy(ar1.begin(), ar1.end(), out.begin()); + std::copy_if(ar2.begin(), ar2.end(), pos, [&](TableRef const& a) { return std::none_of(ar1.begin(), ar1.end(), [&](TableRef const& e) { return e == a; }); }); + return out; } -namespace o2::soa +template ar1, std::array ar2, typename L> +consteval auto merge_if(L l) { -/// special case for the template with origin -template class Ref> -struct is_specialization_origin : std::false_type { -}; + constexpr const int to_remove = std::ranges::count_if(ar1.begin(), ar1.end(), [&](TableRef const& a) { return !l(a); }); + constexpr const int duplicates = std::ranges::count_if(ar2.begin(), ar2.end(), [&](TableRef const& a) { return std::any_of(ar1.begin(), ar1.end(), [&](TableRef const& e) { return e == a; }) || !l(a); }); + std::array out; -template