diff --git a/.clang-tidy b/.clang-tidy index da768906bcc32..080c35ba31df3 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,3 +1,42 @@ +--- +Checks: + - -* + - modernize-avoid-bind + - modernize-deprecated-headers + - modernize-make-shared + - modernize-raw-string-literal + - modernize-redundant-void-arg + - modernize-replace-auto-ptr + - modernize-replace-random-shuffle + - modernize-shrink-to-fit + - modernize-unary-static-assert + - modernize-use-equals-default + - modernize-use-noexcept + - modernize-use-nullptr + - modernize-use-override + - modernize-use-transparent-functors + - modernize-use-uncaught-exceptions + - readability-braces-around-statements + - -clang-diagnostic-vla-cxx-extension CheckOptions: - - key: CheckPathRegex - value: '.*/O2/.*' + # Naming conventions + readability-identifier-naming.ClassCase: CamelCase + readability-identifier-naming.ClassMemberPrefix: m + readability-identifier-naming.ConceptCase: CamelCase + readability-identifier-naming.ConstexprVariableCase: CamelCase + readability-identifier-naming.EnumCase: CamelCase + readability-identifier-naming.EnumConstantCase: CamelCase + readability-identifier-naming.EnumConstantIgnoredRegexp: "^k?[A-Z][a-zA-Z0-9_]*$" # Allow "k" prefix and non-trailing underscores in PDG names. + readability-identifier-naming.FunctionCase: camelBack + readability-identifier-naming.MacroDefinitionCase: UPPER_CASE + readability-identifier-naming.MacroDefinitionIgnoredRegexp: "^[A-Z][A-Z0-9_]*_$" # Allow the trailing underscore in header guards. + readability-identifier-naming.MemberCase: camelBack + readability-identifier-naming.NamespaceCase: lower_case + readability-identifier-naming.ParameterCase: camelBack + readability-identifier-naming.StructCase: CamelCase + readability-identifier-naming.TemplateParameterCase: CamelCase + readability-identifier-naming.TypeAliasCase: CamelCase + readability-identifier-naming.TypedefCase: CamelCase + readability-identifier-naming.TypeTemplateParameterCase: CamelCase + readability-identifier-naming.VariableCase: camelBack +... diff --git a/CCDB/src/CcdbApi.cxx b/CCDB/src/CcdbApi.cxx index 42bc13904bf61..93a79ad56c477 100644 --- a/CCDB/src/CcdbApi.cxx +++ b/CCDB/src/CcdbApi.cxx @@ -213,8 +213,7 @@ void CcdbApi::init(std::string const& host) snapshotReport += ')'; } - mNeedAlienToken = (host.find("https://") != std::string::npos) || (host.find("alice-ccdb.cern.ch") != std::string::npos); - + mNeedAlienToken = (host.find("https://") != std::string::npos) || (host.find("alice-ccdb.cern.ch") != std::string::npos) || (host.find("ccdb-test.cern.ch") != std::string::npos); // Set the curl timeout. It can be forced with an env var or it has different defaults based on the deployment mode. if (getenv("ALICEO2_CCDB_CURL_TIMEOUT_DOWNLOAD")) { auto timeout = atoi(getenv("ALICEO2_CCDB_CURL_TIMEOUT_DOWNLOAD")); diff --git a/Common/Utils/include/CommonUtils/EnumFlags.h b/Common/Utils/include/CommonUtils/EnumFlags.h index e7481c903e666..5032b6c0f1483 100644 --- a/Common/Utils/include/CommonUtils/EnumFlags.h +++ b/Common/Utils/include/CommonUtils/EnumFlags.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -34,7 +35,9 @@ #include #include +#ifndef GPUCA_GPUCODE #include "CommonUtils/StringUtils.h" +#endif namespace o2::utils { @@ -55,6 +58,7 @@ concept EnumFlagHelper = requires { // 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). // NOTE: Cannot detect if bit values past the underlying type are defined. +#ifndef GPUCA_GPUCODE template struct FlagsHelper final { using U = std::underlying_type_t; @@ -317,10 +321,12 @@ struct FlagsHelper final { return false; } }; +#endif } // namespace details::enum_flags // Require an enum to fullfil what one would except from a bitset. +#ifndef GPUCA_GPUCODE template concept EnumFlag = requires { // range checks @@ -332,6 +338,10 @@ concept EnumFlag = requires { requires !details::enum_flags::FlagsHelper::hasNone(); // added automatically requires !details::enum_flags::FlagsHelper::hasAll(); // added automatically }; +#else +template +concept EnumFlag = details::enum_flags::EnumFlagHelper; +#endif /** * \brief Class to aggregate and manage enum-based on-off flags. @@ -358,7 +368,9 @@ template class EnumFlags { static constexpr int DefaultBase{2}; +#ifndef GPUCA_GPUCODE using H = details::enum_flags::FlagsHelper; +#endif using U = std::underlying_type_t; U mBits{0}; @@ -388,18 +400,21 @@ class EnumFlags // 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); }); + for (const E f : flags) { + mBits |= to_bit(f); + } } +#ifndef GPUCA_GPUCODE // Init from a string. // explicit EnumFlags(const std::string& str, int base = DefaultBase) { set(str, base); } - // Destructor. - constexpr ~EnumFlags() = default; +#endif - static constexpr U None{0}; // Represents no flags set. + static constexpr U None{0}; // Represents no flags set. +#ifndef GPUCA_GPUCODE static constexpr U All{H::MaxRep}; // Represents all flags set. // Return list of all enum values @@ -432,6 +447,7 @@ class EnumFlags throw; } } +#endif // Returns the raw bitset value. [[nodiscard]] constexpr auto value() const noexcept { @@ -493,6 +509,7 @@ class EnumFlags } // Checks if all flags are set. +#ifndef GPUCA_GPUCODE [[nodiscard]] constexpr bool all() const noexcept { return mBits == All; @@ -537,6 +554,7 @@ class EnumFlags } return oss.str(); } +#endif // Checks if any flag is set (Boolean context). [[nodiscard]] constexpr explicit operator bool() const noexcept @@ -645,6 +663,7 @@ class EnumFlags } // Serializes the flag set to a string. +#ifndef GPUCA_GPUCODE [[nodiscard]] std::string serialize() const { return std::to_string(mBits); @@ -659,6 +678,7 @@ class EnumFlags } mBits = static_cast(v); } +#endif // Counts the number of set bits (active flags). [[nodiscard]] constexpr size_t count() const noexcept @@ -686,6 +706,7 @@ class EnumFlags private: // Set implementation, bits was zeroed before. +#ifndef GPUCA_GPUCODE void setImpl(const std::string& s, int base = 2) { // Helper to check if character is valid for given base @@ -782,14 +803,17 @@ class EnumFlags throw std::invalid_argument("Cannot parse string!"); } } +#endif }; +#ifndef GPUCA_GPUCODE template std::ostream& operator<<(std::ostream& os, const EnumFlags& f) { os << f.pstring(true); return os; } +#endif } // namespace o2::utils diff --git a/Common/Utils/include/CommonUtils/NameConf.h b/Common/Utils/include/CommonUtils/NameConf.h index fb10f929c9782..8d4c0a2c1c4f8 100644 --- a/Common/Utils/include/CommonUtils/NameConf.h +++ b/Common/Utils/include/CommonUtils/NameConf.h @@ -100,6 +100,9 @@ class NameConf : public o2::conf::ConfigurableParamHelper // CTF Dictionary static std::string getCTFDictFileName(); + // O2 Raw TF Filename + static std::string getRawTFFileName(uint32_t run, uint32_t orb, uint32_t id, const std::string& host, const std::string_view prefix = "o2_rawtf_dump"); + // Default CCDB server static std::string getCCDBServer(); diff --git a/Common/Utils/src/NameConf.cxx b/Common/Utils/src/NameConf.cxx index 45646284a878b..48cefacaf14c7 100644 --- a/Common/Utils/src/NameConf.cxx +++ b/Common/Utils/src/NameConf.cxx @@ -95,6 +95,11 @@ std::string NameConf::getCTFFileName(uint32_t run, uint32_t orb, uint32_t id, co return o2::utils::Str::concat_string(prefix, '_', fmt::format("run{:08d}_orbit{:010d}_tf{:010d}_{}", run, orb, id, host), ".root"); } +std::string NameConf::getRawTFFileName(uint32_t run, uint32_t orb, uint32_t id, const std::string& host, const std::string_view prefix) +{ + return o2::utils::Str::concat_string(prefix, '_', fmt::format("run{:08d}_orbit{:010d}_tf{:010d}_{}", run, orb, id, host), ".tf"); +} + std::string NameConf::getCTFDictFileName() { return o2::utils::Str::concat_string(CTFDICT, ".root"); diff --git a/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h b/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h index 5d13ad753b8bc..89f6416c6e177 100644 --- a/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h +++ b/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h @@ -192,7 +192,13 @@ class TrackITSExt : public TrackITS getClusterRefs().setEntries(ncl); } - GPUhdi() const int& getClusterIndex(int lr) const { return mIndex[lr]; } + GPUhdi() const int getClusterIndex(int lr) const { return mIndex[lr]; } + + GPUh() const int getFirstLayerClusterIndex() const + { + int firstLayer = getFirstClusterLayer(); + return getClusterIndex(firstLayer); + } GPUhdi() void setExternalClusterIndex(int layer, int idx, bool newCluster = false) { @@ -210,6 +216,33 @@ class TrackITSExt : public TrackITS return mIndex; } +#ifndef GPUCA_GPUCODE + // build order-independent hash via the external cluster idx (unique within a TF) for the selected layers + // cluster indices are either sorted inward or outward + size_t hash(uint16_t layerMask = 0xFFFF, bool inward = true) const noexcept + { + size_t h1 = 0, h2 = 0; + int from = (int)getLastClusterLayer(), to = -1, step = -1; + if (inward) { + from = (int)getFirstClusterLayer(); + to = MaxClusters; + step = 1; + } + // clusters are stored continously but they do not necesarrily correspond to the layers + for (int layer = from, slot{0}; layer != to; layer += step) { + if (hasHitOnLayer(layer)) { + int idx = mIndex[slot++]; + if (layerMask & (uint16_t(1) << layer)) { + size_t v = std::hash{}(idx); + h1 ^= v; + h2 += v * 0x9e3779b97f4a7c15ULL; // boost's hash_combine + } + } + } + return h1 ^ (h2 << 1); + } +#endif + private: std::array mIndex = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; ///< Indices of associated clusters ClassDefNV(TrackITSExt, 3); diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CMV.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CMV.h index 109eff2654466..8195b3e39c689 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CMV.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CMV.h @@ -85,11 +85,13 @@ struct Data { return positive ? magnitude : -magnitude; } - // Encode from float: clamps magnitude to 15 bits, range ±255.992 + // Encode from float: truncates magnitude to 15 bits, range ±255.992 void setCMVFloat(float value) { const bool positive = (value >= 0.f); - const uint16_t magnitude = static_cast(std::abs(value) * 128.f + 0.5f) & 0x7FFF; + const uint16_t magnitude = static_cast( + std::lround(std::abs(value) * 128.f)) & + 0x7FFF; cmv = (positive ? 0x8000 : 0x0000) | magnitude; } }; @@ -119,4 +121,4 @@ struct Container { } // namespace o2::tpc::cmv -#endif \ No newline at end of file +#endif diff --git a/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h b/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h index 23dc30119aa7a..ef259e5322bb8 100644 --- a/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h +++ b/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h @@ -524,6 +524,48 @@ inline void O2DatabasePDG::addALICEParticles(TDatabasePDG* db) db->AddParticle("f2_1525", "f2_1525", 1.525, kFALSE, 0.073, 0, "Resonance", ionCode); } + // when using hadronic rescattering in Pythia8 + ionCode = 9000221; + if (!db->GetParticle(ionCode)) { + db->AddParticle("f0_500", "f0_500", 0.500, kFALSE, 0.350, 0.0, "Resonance", ionCode); + } + ionCode = -100313; + if (!db->GetParticle(ionCode)) { + db->AddParticle("K*(1410)bar0", "K*(1410)bar0", 1.414, kFALSE, 0.232, 0, "Resonance", ionCode); + } + ionCode = 100313; + if (!db->GetParticle(ionCode)) { + db->AddParticle("K*(1410)0", "K*(1410)0", 1.414, kFALSE, 0.232, 0, "Resonance", ionCode); + } + ionCode = 100323; + if (!db->GetParticle(ionCode)) { + db->AddParticle("K*(1410)+", "K*(1410)+", 1.414, kFALSE, 0.232, +1, "Resonance", ionCode); + } + ionCode = -100323; + if (!db->GetParticle(ionCode)) { + db->AddParticle("K*(1410)-", "K*(1410)-", 1.414, kFALSE, 0.232, -1, "Resonance", ionCode); + } + ionCode = 100211; + if (!db->GetParticle(ionCode)) { + db->AddParticle("pi(1300)+", "pi(1300)+", 1.165, kFALSE, 0.400, +1, "Resonance", ionCode); + } + ionCode = -100211; + if (!db->GetParticle(ionCode)) { + db->AddParticle("pi(1300)-", "pi(1300)-", 1.165, kFALSE, 0.400, -1, "Resonance", ionCode); + } + ionCode = 202112; + if (!db->GetParticle(ionCode)) { + db->AddParticle("n(1440)0", "n(1440)0", 1.358, kFALSE, 0.350, 0, "Ion", ionCode); + } + ionCode = -202212; + if (!db->GetParticle(ionCode)) { + db->AddParticle("p(1440)bar-", "p(1440)bar-", 1.793, kFALSE, 0.350, -1, "Ion", ionCode); + } + ionCode = 202212; + if (!db->GetParticle(ionCode)) { + db->AddParticle("p(1440)+", "p(1440)+", 1.793, kFALSE, 0.350, 1, "Ion", ionCode); + } + // Xi-/+ (1820) ionCode = 123314; if (!db->GetParticle(ionCode)) { diff --git a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h index 8947a50fe42cd..02f1b2582d74b 100644 --- a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h +++ b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h @@ -249,6 +249,7 @@ class AODProducerWorkflowDPL : public Task bool mThinTracks{false}; bool mPropTracks{false}; bool mPropMuons{false}; + bool mStoreAllMFTCov{false}; float mTrackQCKeepGlobalTracks{false}; float mTrackQCRetainOnlydEdx{false}; float mTrackQCFraction{0.00}; @@ -547,8 +548,8 @@ class AODProducerWorkflowDPL : public Task template void addToTRDsExtra(const o2::globaltracking::RecoContainer& recoData, TRDsExtraCursorType& trdExtraCursor, const GIndex& trkIdx, int trkTableIdx); - template - void addToMFTTracksTable(mftTracksCursorType& mftTracksCursor, AmbigMFTTracksCursorType& ambigMFTTracksCursor, + template + void addToMFTTracksTable(mftTracksCursorType& mftTracksCursor, mftTracksCovCursorType& mftTracksCovCursor, AmbigMFTTracksCursorType& ambigMFTTracksCursor, GIndex trackID, const o2::globaltracking::RecoContainer& data, int collisionID, std::uint64_t collisionBC, const std::map& bcsMap); diff --git a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx index 03f38206b2a47..8365628f1644b 100644 --- a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx +++ b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx @@ -507,8 +507,8 @@ void AODProducerWorkflowDPL::addToTRDsExtra(const o2::globaltracking::RecoContai trdExtraCursor(trkTableIdx, q0s, q1s, q2s, q0sCor, q1sCor, q2sCor, ttgls, tphis); } -template -void AODProducerWorkflowDPL::addToMFTTracksTable(mftTracksCursorType& mftTracksCursor, AmbigMFTTracksCursorType& ambigMFTTracksCursor, +template +void AODProducerWorkflowDPL::addToMFTTracksTable(mftTracksCursorType& mftTracksCursor, mftTracksCovCursorType& mftTracksCovCursor, AmbigMFTTracksCursorType& ambigMFTTracksCursor, GIndex trackID, const o2::globaltracking::RecoContainer& data, int collisionID, std::uint64_t collisionBC, const std::map& bcsMap) { @@ -543,6 +543,30 @@ void AODProducerWorkflowDPL::addToMFTTracksTable(mftTracksCursorType& mftTracksC truncateFloatFraction(track.getTrackChi2(), mTrackChi2), truncateFloatFraction(trackTime, mTrackTime), truncateFloatFraction(trackTimeRes, mTrackTimeError)); + if (mStoreAllMFTCov) { + float sX = TMath::Sqrt(track.getSigma2X()); + float sY = TMath::Sqrt(track.getSigma2Y()); + float sPhi = TMath::Sqrt(track.getSigma2Phi()); + float sTgl = TMath::Sqrt(track.getSigma2Tanl()); + float sQ2Pt = TMath::Sqrt(track.getSigma2InvQPt()); + + mftTracksCovCursor(mTableTrMFTID, + truncateFloatFraction(sX, mTrackCovDiag), + truncateFloatFraction(sY, mTrackCovDiag), + truncateFloatFraction(sPhi, mTrackCovDiag), + truncateFloatFraction(sTgl, mTrackCovDiag), + truncateFloatFraction(sQ2Pt, mTrackCovDiag), + (Char_t)(128. * track.getCovariances()(0, 1) / (sX * sY)), + (Char_t)(128. * track.getCovariances()(0, 2) / (sPhi * sX)), + (Char_t)(128. * track.getCovariances()(1, 2) / (sPhi * sY)), + (Char_t)(128. * track.getCovariances()(0, 3) / (sTgl * sX)), + (Char_t)(128. * track.getCovariances()(1, 3) / (sTgl * sY)), + (Char_t)(128. * track.getCovariances()(2, 3) / (sTgl * sPhi)), + (Char_t)(128. * track.getCovariances()(0, 4) / (sQ2Pt * sX)), + (Char_t)(128. * track.getCovariances()(1, 4) / (sQ2Pt * sY)), + (Char_t)(128. * track.getCovariances()(2, 4) / (sQ2Pt * sPhi)), + (Char_t)(128. * track.getCovariances()(3, 4) / (sQ2Pt * sTgl))); + } if (needBCSlice) { ambigMFTTracksCursor(mTableTrMFTID, bcSlice); } @@ -579,10 +603,13 @@ void AODProducerWorkflowDPL::fillTrackTablesPerCollision(int collisionID, int nToReserve = end - start; // + last index for a given table if (src == GIndex::Source::MFT) { mftTracksCursor.reserve(nToReserve + mftTracksCursor.lastIndex()); + if (mStoreAllMFTCov) { + mftTracksCovCursor.reserve(nToReserve + mftTracksCovCursor.lastIndex()); + } } 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) { + if (!mStoreAllMFTCov && src == GIndex::Source::MFTMCH) { mftTracksCovCursor.reserve(nToReserve + mftTracksCovCursor.lastIndex()); } } else { @@ -597,7 +624,7 @@ void AODProducerWorkflowDPL::fillTrackTablesPerCollision(int collisionID, if (trackIndex.isAmbiguous() && mGIDToTableMFTID.find(trackIndex) != mGIDToTableMFTID.end()) { // was it already stored ? continue; } - addToMFTTracksTable(mftTracksCursor, ambigMFTTracksCursor, trackIndex, data, collisionID, collisionBC, bcsMap); + addToMFTTracksTable(mftTracksCursor, mftTracksCovCursor, ambigMFTTracksCursor, trackIndex, data, collisionID, collisionBC, bcsMap); mGIDToTableMFTID.emplace(trackIndex, mTableTrMFTID); mTableTrMFTID++; } else if (src == GIndex::Source::MCH || src == GIndex::Source::MFTMCH || src == GIndex::Source::MCHMID) { // FwdTracks tracks are treated separately since they are stored in a different table @@ -929,22 +956,24 @@ void AODProducerWorkflowDPL::addToFwdTracksTable(FwdTracksCursorType& fwdTracksC 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))); + if (!mStoreAllMFTCov) { + 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; @@ -1830,6 +1859,7 @@ void AODProducerWorkflowDPL::init(InitContext& ic) mPropTracks = ic.options().get("propagate-tracks"); mMaxPropXiu = ic.options().get("propagate-tracks-max-xiu"); mPropMuons = ic.options().get("propagate-muons"); + mStoreAllMFTCov = ic.options().get("store-all-mft-cov"); if (auto s = ic.options().get("with-streamers"); !s.empty()) { mStreamerFlags.set(s); if (mStreamerFlags) { @@ -3524,6 +3554,7 @@ DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, boo ConfigParamSpec{"propagate-tracks-max-xiu", VariantType::Float, 5.0f, {"Propagate tracks to IP if X_IU smaller than this value (and if propagate tracks enabled)"}}, ConfigParamSpec{"hepmc-update", VariantType::String, "always", {"When to update HepMC Aux tables: always - force update, never - never update, all - if all keys are present, any - when any key is present (not valid yet)"}}, ConfigParamSpec{"propagate-muons", VariantType::Bool, false, {"Propagate muons to IP"}}, + ConfigParamSpec{"store-all-mft-cov", VariantType::Bool, false, {"Store covariance matrices for all MFT tracks"}}, ConfigParamSpec{"thin-tracks", VariantType::Bool, false, {"Produce thinned track tables"}}, ConfigParamSpec{"trackqc-keepglobaltracks", VariantType::Bool, false, {"Always keep TrackQA for global tracks"}}, ConfigParamSpec{"trackqc-retainonlydedx", VariantType::Bool, false, {"Keep only dEdx information, zero out everything else"}}, diff --git a/Detectors/CTF/workflow/src/CTFWriterSpec.cxx b/Detectors/CTF/workflow/src/CTFWriterSpec.cxx index 5d6db7d613674..f175bf4c2e5d3 100644 --- a/Detectors/CTF/workflow/src/CTFWriterSpec.cxx +++ b/Detectors/CTF/workflow/src/CTFWriterSpec.cxx @@ -310,7 +310,7 @@ size_t CTFWriterSpec::processDet(o2::framework::ProcessingContext& pc, DetID det if (det == DetID::ITS) { nLayers = mInput.doITSStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; } else if (det == DetID::MFT) { - nLayers = mInput.doMFTStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + nLayers = mInput.doMFTStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; } for (uint32_t iLayer = 0; iLayer < nLayers; iLayer++) { auto binding = getBinding(det.getName(), iLayer); @@ -431,7 +431,7 @@ size_t CTFWriterSpec::estimateCTFSize(ProcessingContext& pc) if (det == DetID::ITS) { nLayers = mInput.doITSStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; } else if (det == DetID::MFT) { - nLayers = mInput.doMFTStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + nLayers = mInput.doMFTStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; } for (uint32_t iLayer = 0; iLayer < nLayers; iLayer++) { auto binding = getBinding(det.getName(), iLayer); @@ -818,7 +818,7 @@ DataProcessorSpec getCTFWriterSpec(const o2::ctf::CTFWriterInp& inp) if (det == DetID::ITS) { nLayers = inp.doITSStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; } else if (det == DetID::MFT) { - nLayers = inp.doMFTStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + nLayers = inp.doMFTStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; } for (uint32_t iLayer = 0; iLayer < nLayers; iLayer++) { inputs.emplace_back(CTFWriterSpec::getBinding(det.getName(), iLayer), det.getDataOrigin(), "CTFDATA", iLayer, Lifetime::Timeframe); diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/TRUDataHandler.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/TRUDataHandler.h index 811faf13a05ff..ea9820563dee6 100644 --- a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/TRUDataHandler.h +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/TRUDataHandler.h @@ -13,6 +13,7 @@ #include #include +#include #include #include #include diff --git a/Detectors/GlobalTrackingWorkflow/study/src/CheckResidSpec.cxx b/Detectors/GlobalTrackingWorkflow/study/src/CheckResidSpec.cxx index 01ec999fce1eb..6a1915791a911 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/CheckResidSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/CheckResidSpec.cxx @@ -132,7 +132,7 @@ void CheckResidSpec::init(InitContext& ic) int maxLanes = ic.services().get().maxInputTimeslices; std::string nm = params.outname; if (maxLanes > 1) { - o2::conf::ConfigurableParam::updateFromString(fmt::format("checkresid.outname={}_{}", nm, lane)); + o2::conf::ConfigurableParam::updateFromString(fmt::format("checkresid.outname={}_t{}", nm, lane)); } if (mDraw) { mFillHistos = true; @@ -173,8 +173,7 @@ void CheckResidSpec::init(InitContext& ic) mNThreads = 1; #endif if (mFillTree) { - nm += ".root"; - mDBGOut = std::make_unique(nm.c_str(), "recreate"); + mDBGOut = std::make_unique(fmt::format("{}.root", params.outname).c_str(), "recreate"); } } diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h index c87b3d36b9a6a..5f56e3f272473 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h @@ -25,47 +25,53 @@ namespace o2::its::gpu { template -class TimeFrameGPU final : public TimeFrame +class TimeFrameGPU : public TimeFrame { using typename TimeFrame::IndexTableUtilsN; using typename TimeFrame::ROFOverlapTableN; using typename TimeFrame::ROFVertexLookupTableN; using typename TimeFrame::ROFMaskTableN; + using typename TimeFrame::TrackingTopologyN; using typename TimeFrame::TrackSeedN; + static constexpr int MaxTransitions = TrackingTopologyN::MaxTransitions; + static constexpr int MaxCells = TrackingTopologyN::MaxCells; + static constexpr int MaxStreams = MaxCells > NLayers ? MaxCells : NLayers; public: TimeFrameGPU() = default; - ~TimeFrameGPU() final = default; + ~TimeFrameGPU() override = default; /// Most relevant operations void pushMemoryStack(const int); void popMemoryStack(const int); void registerHostMemory(const int); void unregisterHostMemory(const int); - void initialise(const int, const TrackingParameters&, const int); - void loadIndexTableUtils(const int); - void loadTrackingFrameInfoDevice(const int, const int); - void createTrackingFrameInfoDeviceArray(const int); - void loadUnsortedClustersDevice(const int, const int); - void createUnsortedClustersDeviceArray(const int, const int = NLayers); - void loadClustersDevice(const int, const int); - void createClustersDeviceArray(const int, const int = NLayers); - void loadClustersIndexTables(const int, const int); - void createClustersIndexTablesArray(const int); - void createUsedClustersDevice(const int, const int); - void createUsedClustersDeviceArray(const int, const int = NLayers); + void initialise(const TrackingParameters&, int maxLayers); + void initialise(const TrackingParameters&, int maxLayers, int iteration); + void loadIndexTableUtils(); + void loadTrackingTopologies(); + void loadTrackingFrameInfoDevice(const int); + void createTrackingFrameInfoDeviceArray(); + void loadUnsortedClustersDevice(const int); + void createUnsortedClustersDeviceArray(const int = NLayers); + void loadClustersDevice(const int); + void createClustersDeviceArray(const int = NLayers); + void loadClustersIndexTables(const int); + void createClustersIndexTablesArray(); + void createUsedClustersDevice(const int); + void createUsedClustersDeviceArray(const int = NLayers); void loadUsedClustersDevice(); - void loadROFrameClustersDevice(const int, const int); - void createROFrameClustersDeviceArray(const int); + void loadROFrameClustersDevice(const int); + void createROFrameClustersDeviceArray(); void loadROFCutMask(const int); - void loadVertices(const int); - void loadROFOverlapTable(const int); - void loadROFVertexLookupTable(const int); - void updateROFVertexLookupTable(const int); + void loadVertices(); + void loadROFOverlapTable(); + void loadROFVertexLookupTable(); + void updateROFVertexLookupTable(); /// - void createTrackletsLUTDevice(const int, const int); - void createTrackletsLUTDeviceArray(const int); + void createTrackletsLUTDevice(bool, const int); + void createTrackletsLUTDeviceArray(); void loadTrackletsDevice(); void loadTrackletsLUTDevice(); void loadCellsDevice(); @@ -74,18 +80,18 @@ class TimeFrameGPU final : public TimeFrame void loadTrackSeedsChi2Device(); void loadTrackSeedsDevice(bounded_vector&); void createTrackletsBuffers(const int); - void createTrackletsBuffersArray(const int); + void createTrackletsBuffersArray(); void createCellsBuffers(const int); - void createCellsBuffersArray(const int); + void createCellsBuffersArray(); void createCellsDevice(); void createCellsLUTDevice(const int); - void createCellsLUTDeviceArray(const int); + void createCellsLUTDeviceArray(); void createNeighboursIndexTablesDevice(const int); void createNeighboursDevice(const unsigned int layer); void createNeighboursLUTDevice(const int, const unsigned int); void createTrackITSExtDevice(const size_t); void downloadTrackITSExtDevice(); - void downloadCellsNeighboursDevice(std::vector>>&, const int); + void downloadCellsNeighboursDevice(std::vector>&, const int); void downloadNeighboursLUTDevice(bounded_vector&, const int); void downloadCellsDevice(); void downloadCellsLUTDevice(); @@ -104,11 +110,12 @@ class TimeFrameGPU final : public TimeFrame /// interface virtual bool isGPU() const noexcept final { return true; } - virtual const char* getName() const noexcept { return "GPU"; } + virtual const char* getName() const noexcept override final { return "GPU"; } IndexTableUtilsN* getDeviceIndexTableUtils() { return mIndexTableUtilsDevice; } const auto getDeviceROFOverlapTableView() { return mDeviceROFOverlapTableView; } const auto getDeviceROFVertexLookupTableView() { return mDeviceROFVertexLookupTableView; } const auto getDeviceROFMaskTableView() { return mDeviceROFMaskTableView; } + const auto getDeviceTrackingTopologyView() const { return mDeviceTrackingTopologyView; } int* getDeviceROFramesClusters(const int layer) { return mROFramesClustersDevice[layer]; } auto& getTrackITSExt() { return mTrackITSExt; } Vertex* getDeviceVertices() { return mPrimaryVerticesDevice; } @@ -120,10 +127,9 @@ class TimeFrameGPU final : public TimeFrame TrackITSExt* getDeviceTrackITSExt() { return mTrackITSExtDevice; } int* getDeviceNeighboursLUT(const int layer) { return mNeighboursLUTDevice[layer]; } gsl::span getDeviceNeighboursLUTs() { return mNeighboursLUTDevice; } - gpuPair* getDeviceNeighbourPairs(const int layer) { return mNeighbourPairsDevice[layer]; } - std::array& getDeviceNeighboursAll() { return mNeighboursDevice; } - int* getDeviceNeighbours(const int layer) { return mNeighboursDevice[layer]; } - int** getDeviceNeighboursArray() { return mNeighboursDevice.data(); } + CellNeighbour** getDeviceArrayNeighbours() { return mNeighboursDeviceArray; } + std::array& getDeviceNeighboursAll() { return mNeighboursDevice; } + CellNeighbour* getDeviceNeighbours(const int layer) { return mNeighboursDevice[layer]; } TrackingFrameInfo* getDeviceTrackingFrameInfo(const int); const TrackingFrameInfo** getDeviceArrayTrackingFrameInfo() const { return mTrackingFrameInfoDeviceArray; } const Cluster** getDeviceArrayClusters() const { return mClustersDeviceArray; } @@ -147,10 +153,10 @@ class TimeFrameGPU final : public TimeFrame void setDevicePropagator(const o2::base::PropagatorImpl* p) final { this->mPropagatorDevice = p; } // Host-specific getters - gsl::span getNTracklets() { return mNTracklets; } - gsl::span getNCells() { return mNCells; } + gsl::span getNTracklets() { return {mNTracklets.data(), static_cast::size_type>(this->mTrackingTopologyView.nTransitions)}; } + gsl::span getNCells() { return {mNCells.data(), static_cast::size_type>(this->mTrackingTopologyView.nCells)}; } auto& getArrayNCells() { return mNCells; } - gsl::span getNNeighbours() { return mNNeighbours; } + gsl::span getNNeighbours() { return {mNNeighbours.data(), static_cast::size_type>(this->mTrackingTopologyView.nCells)}; } auto& getArrayNNeighbours() { return mNNeighbours; } // Host-available device getters @@ -169,9 +175,9 @@ class TimeFrameGPU final : public TimeFrame void allocMem(void**, size_t, bool, int32_t = o2::gpu::GPUMemoryResource::MEMORY_GPU); // Abstract owned and unowned memory allocations on default stream // Host-available device buffer sizes - std::array mNTracklets; - std::array mNCells; - std::array mNNeighbours; + std::array mNTracklets{}; + std::array mNCells{}; + std::array mNNeighbours{}; // Device pointers IndexTableUtilsN* mIndexTableUtilsDevice; @@ -179,6 +185,8 @@ class TimeFrameGPU final : public TimeFrame ROFOverlapTableN::View mDeviceROFOverlapTableView; ROFVertexLookupTableN::View mDeviceROFVertexLookupTableView; ROFMaskTableN::View mDeviceROFMaskTableView; + std::vector mDeviceTrackerTopologyViews; + typename TrackingTopologyN::View mDeviceTrackingTopologyView; // Hybrid pref Vertex* mPrimaryVerticesDevice; @@ -193,30 +201,29 @@ class TimeFrameGPU final : public TimeFrame const int** mClustersIndexTablesDeviceArray; uint8_t** mUsedClustersDeviceArray; const int** mROFramesClustersDeviceArray; - std::array mTrackletsDevice; - std::array mTrackletsLUTDevice; - std::array mCellsLUTDevice; - std::array mNeighboursLUTDevice; + std::array mTrackletsDevice{}; + std::array mTrackletsLUTDevice{}; + std::array mCellsLUTDevice{}; + std::array mNeighboursLUTDevice{}; Tracklet** mTrackletsDeviceArray{nullptr}; int** mCellsLUTDeviceArray{nullptr}; - int** mNeighboursCellDeviceArray{nullptr}; int** mNeighboursCellLUTDeviceArray{nullptr}; int** mTrackletsLUTDeviceArray{nullptr}; - std::array mCellsDevice; + std::array mCellsDevice{}; CellSeed** mCellsDeviceArray; - std::array mNeighboursIndexTablesDevice; + std::array mNeighboursIndexTablesDevice{}; TrackSeedN* mTrackSeedsDevice{nullptr}; int* mTrackSeedsLUTDevice{nullptr}; unsigned int mNTracks{0}; - std::array mCellSeedsDevice; + std::array mCellSeedsDevice{}; o2::track::TrackParCovF** mCellSeedsDeviceArray; - std::array mCellSeedsChi2Device; + std::array mCellSeedsChi2Device{}; float** mCellSeedsChi2DeviceArray; TrackITSExt* mTrackITSExtDevice; - std::array*, NLayers - 2> mNeighbourPairsDevice; - std::array mNeighboursDevice; + std::array mNeighboursDevice{}; + CellNeighbour** mNeighboursDeviceArray{nullptr}; std::array mTrackingFrameInfoDevice; const TrackingFrameInfo** mTrackingFrameInfoDeviceArray; @@ -245,19 +252,19 @@ inline std::vector TimeFrameGPU::getClusterSizes() template inline size_t TimeFrameGPU::getNumberOfTracklets() const { - return std::accumulate(mNTracklets.begin(), mNTracklets.end(), 0); + return std::accumulate(mNTracklets.begin(), mNTracklets.begin() + this->mTrackingTopologyView.nTransitions, 0); } template inline size_t TimeFrameGPU::getNumberOfCells() const { - return std::accumulate(mNCells.begin(), mNCells.end(), 0); + return std::accumulate(mNCells.begin(), mNCells.begin() + this->mTrackingTopologyView.nCells, 0); } template inline size_t TimeFrameGPU::getNumberOfNeighbours() const { - return std::accumulate(mNNeighbours.begin(), mNNeighbours.end(), 0); + return std::accumulate(mNNeighbours.begin(), mNNeighbours.begin() + this->mTrackingTopologyView.nCells, 0); } } // namespace o2::its::gpu diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h index bf004426f9134..161283db2a2bc 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h @@ -17,14 +17,14 @@ #include "ITStracking/BoundedAllocator.h" #include "ITStracking/ROFLookupTables.h" -#include "ITStracking/Definitions.h" +#include "ITStracking/TrackingTopology.h" #include "ITStrackingGPU/Utils.h" #include "DetectorsBase/Propagator.h" -#include "GPUCommonDef.h" namespace o2::its { class CellSeed; +struct CellNeighbour; template class TrackSeed; class TrackingFrameInfo; @@ -38,7 +38,9 @@ class ExternalAllocator; template void countTrackletsInROFsHandler(const IndexTableUtils* utils, const typename ROFMaskTable::View& rofMask, - const int layer, + const int transitionId, + const int fromLayer, + const int toLayer, const typename ROFOverlapTable::View& rofOverlaps, const typename ROFVertexLookupTable::View& vertexLUT, const int vertexId, @@ -51,22 +53,25 @@ void countTrackletsInROFsHandler(const IndexTableUtils* utils, const int** clustersIndexTables, int** trackletsLUTs, gsl::span trackletsLUTsHost, - const int iteration, + const bool selectUPCVertices, const float NSigmaCut, - bounded_vector& phiCuts, + const typename TrackingTopology::View topology, + bounded_vector& transitionPhiCuts, const float resolutionPV, std::array& minR, std::array& maxR, bounded_vector& resolutions, std::vector& radii, - bounded_vector& mulScatAng, + bounded_vector& transitionMSAngles, o2::its::ExternalAllocator* alloc, gpu::Streams& streams); template void computeTrackletsInROFsHandler(const IndexTableUtils* utils, const typename ROFMaskTable::View& rofMask, - const int layer, + const int transitionId, + const int fromLayer, + const int toLayer, const typename ROFOverlapTable::View& rofOverlaps, const typename ROFVertexLookupTable::View& vertexLUT, const int vertexId, @@ -82,15 +87,16 @@ void computeTrackletsInROFsHandler(const IndexTableUtils* utils, gsl::span nTracklets, int** trackletsLUTs, gsl::span trackletsLUTsHost, - const int iteration, + const bool selectUPCVertices, const float NSigmaCut, - bounded_vector& phiCuts, + const typename TrackingTopology::View topology, + bounded_vector& transitionPhiCuts, const float resolutionPV, std::array& minR, std::array& maxR, bounded_vector& resolutions, std::vector& radii, - bounded_vector& mulScatAng, + bounded_vector& transitionMSAngles, o2::its::ExternalAllocator* alloc, gpu::Streams& streams); @@ -101,7 +107,8 @@ void countCellsHandler(const Cluster** sortedClusters, Tracklet** tracklets, int** trackletsLUT, const int nTracklets, - const int layer, + const int cellTopologyId, + const typename TrackingTopology::View topology, CellSeed* cells, int** cellsLUTsDeviceArray, int* cellsLUTsHost, @@ -120,7 +127,8 @@ void computeCellsHandler(const Cluster** sortedClusters, Tracklet** tracklets, int** trackletsLUT, const int nTracklets, - const int layer, + const int cellTopologyId, + const typename TrackingTopology::View topology, CellSeed* cells, int** cellsLUTsDeviceArray, int* cellsLUTsHost, @@ -133,33 +141,31 @@ void computeCellsHandler(const Cluster** sortedClusters, template void countCellNeighboursHandler(CellSeed** cellsLayersDevice, - int* neighboursLUTs, + int* neighboursCursor, int** cellsLUTs, - gpuPair* cellNeighbours, - int* neighboursIndexTable, - const Tracklet** tracklets, + const int sourceCellTopologyId, + const int targetCellTopologyId, const float maxChi2ClusterAttachment, const float bz, - const int layerIndex, const unsigned int nCells, - const unsigned int nCellsNext, - const int maxCellNeighbours, - o2::its::ExternalAllocator* alloc, gpu::Stream& stream); +void scanCellNeighboursHandler(int* neighboursCursor, + int* neighboursLUT, + const unsigned int nCells, + o2::its::ExternalAllocator* alloc, + gpu::Stream& stream); + template void computeCellNeighboursHandler(CellSeed** cellsLayersDevice, - int* neighboursLUTs, + int* neighboursCursor, int** cellsLUTs, - gpuPair* cellNeighbours, - int* neighboursIndexTable, - const Tracklet** tracklets, + CellNeighbour* cellNeighbours, + const int sourceCellTopologyId, + const int targetCellTopologyId, const float maxChi2ClusterAttachment, const float bz, - const int layerIndex, const unsigned int nCells, - const unsigned int nCellsNext, - const int maxCellNeighbours, gpu::Stream& stream); int filterCellNeighboursHandler(gpuPair*, @@ -169,19 +175,24 @@ int filterCellNeighboursHandler(gpuPair*, o2::its::ExternalAllocator* = nullptr); template -void processNeighboursHandler(const int startLayer, - const int startLevel, +void processNeighboursHandler(const int startLevel, + const int defaultCellTopologyId, CellSeed** allCellSeeds, CellSeed* currentCellSeeds, - std::array& nCells, + const int* currentCellTopologyIds, + const int* currentCellIds, + const int* nCells, const unsigned char** usedClusters, - std::array& neighbours, - gsl::span neighboursDeviceLUTs, + CellNeighbour** neighbours, + int** neighboursDeviceLUTs, const TrackingFrameInfo** foundTrackingFrameInfo, bounded_vector>& seedsHost, const float bz, const float MaxChi2ClusterAttachment, const float maxChi2NDF, + const int maxHoles, + const int minTrackLength, + const LayerMask holeLayerMask, const std::vector& layerxX0Host, const o2::base::Propagator* propagator, const o2::base::PropagatorF::MatCorrType matCorrType, diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu index b9091eebde377..5fff30f5162b1 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu @@ -52,10 +52,10 @@ void TimeFrameGPU::allocMem(void** ptr, size_t size, bool extAllocator, } template -void TimeFrameGPU::loadIndexTableUtils(const int iteration) +void TimeFrameGPU::loadIndexTableUtils() { GPUTimer timer("loading indextable utils"); - if (!iteration) { + { GPULog("gpu-allocation: allocating IndexTableUtils buffer, for {:.2f} MB.", sizeof(IndexTableUtilsN) / constants::MB); allocMem(reinterpret_cast(&mIndexTableUtilsDevice), sizeof(IndexTableUtilsN), this->hasFrameworkAllocator()); } @@ -64,9 +64,9 @@ void TimeFrameGPU::loadIndexTableUtils(const int iteration) } template -void TimeFrameGPU::createUnsortedClustersDeviceArray(const int iteration, const int maxLayers) +void TimeFrameGPU::createUnsortedClustersDeviceArray(const int maxLayers) { - if (!iteration) { + { GPUTimer timer("creating unsorted clusters array"); allocMem(reinterpret_cast(&mUnsortedClustersDeviceArray), NLayers * sizeof(Cluster*), this->hasFrameworkAllocator()); GPUChkErrS(cudaHostRegister(mUnsortedClustersDevice.data(), NLayers * sizeof(Cluster*), cudaHostRegisterPortable)); @@ -81,9 +81,9 @@ void TimeFrameGPU::createUnsortedClustersDeviceArray(const int iteratio } template -void TimeFrameGPU::loadUnsortedClustersDevice(const int iteration, const int layer) +void TimeFrameGPU::loadUnsortedClustersDevice(const int layer) { - if (!iteration) { + { GPUTimer timer(mGpuStreams[layer], "loading unsorted clusters", layer); GPULog("gpu-transfer: loading {} unsorted clusters on layer {}, for {:.2f} MB.", this->mUnsortedClusters[layer].size(), layer, this->mUnsortedClusters[layer].size() * sizeof(Cluster) / constants::MB); allocMemAsync(reinterpret_cast(&mUnsortedClustersDevice[layer]), this->mUnsortedClusters[layer].size() * sizeof(Cluster), mGpuStreams[layer], this->hasFrameworkAllocator()); @@ -93,9 +93,9 @@ void TimeFrameGPU::loadUnsortedClustersDevice(const int iteration, cons } template -void TimeFrameGPU::createClustersDeviceArray(const int iteration, const int maxLayers) +void TimeFrameGPU::createClustersDeviceArray(const int maxLayers) { - if (!iteration) { + { GPUTimer timer("creating sorted clusters array"); allocMem(reinterpret_cast(&mClustersDeviceArray), NLayers * sizeof(Cluster*), this->hasFrameworkAllocator()); GPUChkErrS(cudaHostRegister(mClustersDevice.data(), NLayers * sizeof(Cluster*), cudaHostRegisterPortable)); @@ -110,9 +110,9 @@ void TimeFrameGPU::createClustersDeviceArray(const int iteration, const } template -void TimeFrameGPU::loadClustersDevice(const int iteration, const int layer) +void TimeFrameGPU::loadClustersDevice(const int layer) { - if (!iteration) { + { GPUTimer timer(mGpuStreams[layer], "loading sorted clusters", layer); GPULog("gpu-transfer: loading {} clusters on layer {}, for {:.2f} MB.", this->mClusters[layer].size(), layer, this->mClusters[layer].size() * sizeof(Cluster) / constants::MB); allocMemAsync(reinterpret_cast(&mClustersDevice[layer]), this->mClusters[layer].size() * sizeof(Cluster), mGpuStreams[layer], this->hasFrameworkAllocator()); @@ -122,9 +122,9 @@ void TimeFrameGPU::loadClustersDevice(const int iteration, const int la } template -void TimeFrameGPU::createClustersIndexTablesArray(const int iteration) +void TimeFrameGPU::createClustersIndexTablesArray() { - if (!iteration) { + { GPUTimer timer("creating clustersindextable array"); allocMem(reinterpret_cast(&mClustersIndexTablesDeviceArray), NLayers * sizeof(int*), this->hasFrameworkAllocator()); GPUChkErrS(cudaHostRegister(mClustersIndexTablesDevice.data(), NLayers * sizeof(int*), cudaHostRegisterPortable)); @@ -139,9 +139,9 @@ void TimeFrameGPU::createClustersIndexTablesArray(const int iteration) } template -void TimeFrameGPU::loadClustersIndexTables(const int iteration, const int layer) +void TimeFrameGPU::loadClustersIndexTables(const int layer) { - if (!iteration) { + { GPUTimer timer(mGpuStreams[layer], "loading sorted clusters", layer); GPULog("gpu-transfer: loading clusters indextable for layer {} with {} elements, for {:.2f} MB.", layer, this->mIndexTables[layer].size(), this->mIndexTables[layer].size() * sizeof(int) / constants::MB); allocMemAsync(reinterpret_cast(&mClustersIndexTablesDevice[layer]), this->mIndexTables[layer].size() * sizeof(int), mGpuStreams[layer], this->hasFrameworkAllocator()); @@ -151,9 +151,9 @@ void TimeFrameGPU::loadClustersIndexTables(const int iteration, const i } template -void TimeFrameGPU::createUsedClustersDeviceArray(const int iteration, const int maxLayers) +void TimeFrameGPU::createUsedClustersDeviceArray(const int maxLayers) { - if (!iteration) { + { GPUTimer timer("creating used clusters flags"); allocMem(reinterpret_cast(&mUsedClustersDeviceArray), NLayers * sizeof(uint8_t*), this->hasFrameworkAllocator()); GPUChkErrS(cudaHostRegister(mUsedClustersDevice.data(), NLayers * sizeof(uint8_t*), cudaHostRegisterPortable)); @@ -168,9 +168,9 @@ void TimeFrameGPU::createUsedClustersDeviceArray(const int iteration, c } template -void TimeFrameGPU::createUsedClustersDevice(const int iteration, const int layer) +void TimeFrameGPU::createUsedClustersDevice(const int layer) { - if (!iteration) { + { GPUTimer timer(mGpuStreams[layer], "creating used clusters flags", layer); GPULog("gpu-transfer: creating {} used clusters flags on layer {}, for {:.2f} MB.", this->mUsedClusters[layer].size(), layer, this->mUsedClusters[layer].size() * sizeof(unsigned char) / constants::MB); allocMemAsync(reinterpret_cast(&mUsedClustersDevice[layer]), this->mUsedClusters[layer].size() * sizeof(unsigned char), mGpuStreams[layer], this->hasFrameworkAllocator()); @@ -190,9 +190,9 @@ void TimeFrameGPU::loadUsedClustersDevice() } template -void TimeFrameGPU::createROFrameClustersDeviceArray(const int iteration) +void TimeFrameGPU::createROFrameClustersDeviceArray() { - if (!iteration) { + { GPUTimer timer("creating ROFrame clusters array"); allocMem(reinterpret_cast(&mROFramesClustersDeviceArray), NLayers * sizeof(int*), this->hasFrameworkAllocator()); GPUChkErrS(cudaHostRegister(mROFramesClustersDevice.data(), NLayers * sizeof(int*), cudaHostRegisterPortable)); @@ -207,9 +207,9 @@ void TimeFrameGPU::createROFrameClustersDeviceArray(const int iteration } template -void TimeFrameGPU::loadROFrameClustersDevice(const int iteration, const int layer) +void TimeFrameGPU::loadROFrameClustersDevice(const int layer) { - if (!iteration) { + { GPUTimer timer(mGpuStreams[layer], "loading ROframe clusters", layer); GPULog("gpu-transfer: loading {} ROframe clusters info on layer {}, for {:.2f} MB.", this->mROFramesClusters[layer].size(), layer, this->mROFramesClusters[layer].size() * sizeof(int) / constants::MB); allocMemAsync(reinterpret_cast(&mROFramesClustersDevice[layer]), this->mROFramesClusters[layer].size() * sizeof(int), mGpuStreams[layer], this->hasFrameworkAllocator()); @@ -219,9 +219,9 @@ void TimeFrameGPU::loadROFrameClustersDevice(const int iteration, const } template -void TimeFrameGPU::createTrackingFrameInfoDeviceArray(const int iteration) +void TimeFrameGPU::createTrackingFrameInfoDeviceArray() { - if (!iteration) { + { GPUTimer timer("creating trackingframeinfo array"); allocMem(reinterpret_cast(&mTrackingFrameInfoDeviceArray), NLayers * sizeof(TrackingFrameInfo*), this->hasFrameworkAllocator()); GPUChkErrS(cudaHostRegister(mTrackingFrameInfoDevice.data(), NLayers * sizeof(TrackingFrameInfo*), cudaHostRegisterPortable)); @@ -236,9 +236,9 @@ void TimeFrameGPU::createTrackingFrameInfoDeviceArray(const int iterati } template -void TimeFrameGPU::loadTrackingFrameInfoDevice(const int iteration, const int layer) +void TimeFrameGPU::loadTrackingFrameInfoDevice(const int layer) { - if (!iteration) { + { GPUTimer timer(mGpuStreams[layer], "loading trackingframeinfo", layer); GPULog("gpu-transfer: loading {} tfinfo on layer {}, for {:.2f} MB.", this->mTrackingFrameInfo[layer].size(), layer, this->mTrackingFrameInfo[layer].size() * sizeof(TrackingFrameInfo) / constants::MB); allocMemAsync(reinterpret_cast(&mTrackingFrameInfoDevice[layer]), this->mTrackingFrameInfo[layer].size() * sizeof(TrackingFrameInfo), mGpuStreams[layer], this->hasFrameworkAllocator()); @@ -250,7 +250,7 @@ void TimeFrameGPU::loadTrackingFrameInfoDevice(const int iteration, con template void TimeFrameGPU::loadROFCutMask(const int iteration) { - if (!iteration || iteration == 3) { // we need to re-load the swapped mult-mask in upc iteration + { GPUTimer timer("loading multiplicity cut mask"); const auto& hostTable = *(this->mROFMask); const auto hostView = hostTable.getView(); @@ -270,9 +270,9 @@ void TimeFrameGPU::loadROFCutMask(const int iteration) } template -void TimeFrameGPU::loadVertices(const int iteration) +void TimeFrameGPU::loadVertices() { - if (!iteration) { + { GPUTimer timer("loading seeding vertices"); GPULog("gpu-transfer: loading {} seeding vertices, for {:.2f} MB.", this->mPrimaryVertices.size(), this->mPrimaryVertices.size() * sizeof(Vertex) / constants::MB); allocMem(reinterpret_cast(&mPrimaryVerticesDevice), this->mPrimaryVertices.size() * sizeof(Vertex), this->hasFrameworkAllocator()); @@ -281,9 +281,9 @@ void TimeFrameGPU::loadVertices(const int iteration) } template -void TimeFrameGPU::loadROFOverlapTable(const int iteration) +void TimeFrameGPU::loadROFOverlapTable() { - if (!iteration) { + { GPUTimer timer("initialising device view of ROFOverlapTable"); const auto& hostTable = this->getROFOverlapTable(); const auto& hostView = this->getROFOverlapTableView(); @@ -305,9 +305,9 @@ void TimeFrameGPU::loadROFOverlapTable(const int iteration) } template -void TimeFrameGPU::loadROFVertexLookupTable(const int iteration) +void TimeFrameGPU::loadROFVertexLookupTable() { - if (!iteration) { + { GPUTimer timer("initialising device view of ROFVertexLookupTable"); const auto& hostTable = this->getROFVertexLookupTable(); const auto& hostView = this->getROFVertexLookupTableView(); @@ -329,10 +329,41 @@ void TimeFrameGPU::loadROFVertexLookupTable(const int iteration) } template -void TimeFrameGPU::updateROFVertexLookupTable(const int iteration) +void TimeFrameGPU::loadTrackingTopologies() +{ + GPUTimer timer("initialising device views of TrackingTopology"); + const auto& hostTopologies = this->getTrackerTopologies(); + mDeviceTrackerTopologyViews.resize(hostTopologies.size()); + using LayerTransition = typename TrackingTopologyN::LayerTransition; + using CellTopology = typename TrackingTopologyN::CellTopology; + using Range = typename TrackingTopologyN::Range; + using Id = typename TrackingTopologyN::Id; + for (size_t iteration = 0; iteration < hostTopologies.size(); ++iteration) { + const auto& topology = hostTopologies[iteration]; + LayerTransition* dTransitions{nullptr}; + CellTopology* dCells{nullptr}; + Range* dCellsByFirstTransitionIndex{nullptr}; + Id* dCellsByFirstTransition{nullptr}; + allocMem(reinterpret_cast(&dTransitions), topology.getNTransitions() * sizeof(LayerTransition), this->hasFrameworkAllocator()); + allocMem(reinterpret_cast(&dCells), topology.getNCells() * sizeof(CellTopology), this->hasFrameworkAllocator()); + allocMem(reinterpret_cast(&dCellsByFirstTransitionIndex), topology.getNTransitions() * sizeof(Range), this->hasFrameworkAllocator()); + allocMem(reinterpret_cast(&dCellsByFirstTransition), topology.getNCellsByFirstTransition() * sizeof(Id), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(dTransitions, topology.getTransitions().data(), topology.getNTransitions() * sizeof(LayerTransition), cudaMemcpyHostToDevice)); + GPUChkErrS(cudaMemcpy(dCells, topology.getCells().data(), topology.getNCells() * sizeof(CellTopology), cudaMemcpyHostToDevice)); + GPUChkErrS(cudaMemcpy(dCellsByFirstTransitionIndex, topology.getCellsByFirstTransitionIndex().data(), topology.getNTransitions() * sizeof(Range), cudaMemcpyHostToDevice)); + GPUChkErrS(cudaMemcpy(dCellsByFirstTransition, topology.getCellsByFirstTransition().data(), topology.getNCellsByFirstTransition() * sizeof(Id), cudaMemcpyHostToDevice)); + mDeviceTrackerTopologyViews[iteration] = topology.getDeviceView(dTransitions, dCells, dCellsByFirstTransitionIndex, dCellsByFirstTransition); + } + if (!mDeviceTrackerTopologyViews.empty()) { + mDeviceTrackingTopologyView = mDeviceTrackerTopologyViews.front(); + } +} + +template +void TimeFrameGPU::updateROFVertexLookupTable() { const auto& hostTable = this->getROFVertexLookupTable(); - if (!iteration) { + { GPUTimer timer("updating device view of ROFVertexLookupTable"); const auto& hostView = this->getROFVertexLookupTableView(); using TableEntry = ROFVertexLookupTable::TableEntry; @@ -345,19 +376,20 @@ void TimeFrameGPU::updateROFVertexLookupTable(const int iteration) } template -void TimeFrameGPU::createTrackletsLUTDeviceArray(const int iteration) +void TimeFrameGPU::createTrackletsLUTDeviceArray() { - if (!iteration) { - allocMem(reinterpret_cast(&mTrackletsLUTDeviceArray), (NLayers - 1) * sizeof(int*), this->hasFrameworkAllocator()); + { + allocMem(reinterpret_cast(&mTrackletsLUTDeviceArray), MaxTransitions * sizeof(int*), this->hasFrameworkAllocator()); } } template -void TimeFrameGPU::createTrackletsLUTDevice(const int iteration, const int layer) +void TimeFrameGPU::createTrackletsLUTDevice(bool allocate, const int layer) { GPUTimer timer(mGpuStreams[layer], "creating tracklets LUTs", layer); - const int ncls = this->mClusters[layer].size() + 1; - if (!iteration) { + const int fromLayer = this->mTrackingTopologyView.getTransition(layer).fromLayer; + const int ncls = this->mClusters[fromLayer].size() + 1; + if (allocate || mTrackletsLUTDevice[layer] == nullptr) { GPULog("gpu-allocation: creating tracklets LUT for {} elements on layer {}, for {:.2f} MB.", ncls, layer, ncls * sizeof(int) / constants::MB); allocMemAsync(reinterpret_cast(&mTrackletsLUTDevice[layer]), ncls * sizeof(int), mGpuStreams[layer], this->hasFrameworkAllocator()); GPUChkErrS(cudaMemcpyAsync(&mTrackletsLUTDeviceArray[layer], &mTrackletsLUTDevice[layer], sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); @@ -366,11 +398,11 @@ void TimeFrameGPU::createTrackletsLUTDevice(const int iteration, const } template -void TimeFrameGPU::createTrackletsBuffersArray(const int iteration) +void TimeFrameGPU::createTrackletsBuffersArray() { - if (!iteration) { + { GPUTimer timer("creating tracklet buffers array"); - allocMem(reinterpret_cast(&mTrackletsDeviceArray), (NLayers - 1) * sizeof(Tracklet*), this->hasFrameworkAllocator()); + allocMem(reinterpret_cast(&mTrackletsDeviceArray), MaxTransitions * sizeof(Tracklet*), this->hasFrameworkAllocator()); } } @@ -379,7 +411,8 @@ void TimeFrameGPU::createTrackletsBuffers(const int layer) { GPUTimer timer(mGpuStreams[layer], "creating tracklet buffers", layer); mNTracklets[layer] = 0; - GPUChkErrS(cudaMemcpyAsync(&mNTracklets[layer], mTrackletsLUTDevice[layer] + this->mClusters[layer].size(), sizeof(int), cudaMemcpyDeviceToHost, mGpuStreams[layer].get())); + const int fromLayer = this->mTrackingTopologyView.getTransition(layer).fromLayer; + GPUChkErrS(cudaMemcpyAsync(&mNTracklets[layer], mTrackletsLUTDevice[layer] + this->mClusters[fromLayer].size(), sizeof(int), cudaMemcpyDeviceToHost, mGpuStreams[layer].get())); mGpuStreams[layer].sync(); // ensure number of tracklets is correct GPULog("gpu-transfer: creating tracklets buffer for {} elements on layer {}, for {:.2f} MB.", mNTracklets[layer], layer, mNTracklets[layer] * sizeof(Tracklet) / constants::MB); allocMemAsync(reinterpret_cast(&mTrackletsDevice[layer]), mNTracklets[layer] * sizeof(Tracklet), mGpuStreams[layer], this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); @@ -426,6 +459,7 @@ void TimeFrameGPU::createNeighboursLUTDevice(const int layer, const uns GPULog("gpu-allocation: reserving neighbours LUT for {} elements on layer {} , for {:.2f} MB.", nCells + 1, layer, (nCells + 1) * sizeof(int) / constants::MB); allocMemAsync(reinterpret_cast(&mNeighboursLUTDevice[layer]), (nCells + 1) * sizeof(int), mGpuStreams[layer], this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); // We need one element more to move exc -> inc GPUChkErrS(cudaMemsetAsync(mNeighboursLUTDevice[layer], 0, (nCells + 1) * sizeof(int), mGpuStreams[layer].get())); + GPUChkErrS(cudaMemcpyAsync(&mNeighboursCellLUTDeviceArray[layer], &mNeighboursLUTDevice[layer], sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); } template @@ -442,11 +476,13 @@ void TimeFrameGPU::loadCellsDevice() } template -void TimeFrameGPU::createCellsLUTDeviceArray(const int iteration) +void TimeFrameGPU::createCellsLUTDeviceArray() { - if (!iteration) { + { GPUTimer timer("creating cells LUTs array"); - allocMem(reinterpret_cast(&mCellsLUTDeviceArray), (NLayers - 2) * sizeof(int*), this->hasFrameworkAllocator()); + allocMem(reinterpret_cast(&mCellsLUTDeviceArray), MaxCells * sizeof(int*), this->hasFrameworkAllocator()); + allocMem(reinterpret_cast(&mNeighboursCellLUTDeviceArray), MaxCells * sizeof(int*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemset(mNeighboursCellLUTDeviceArray, 0, MaxCells * sizeof(int*))); } } @@ -454,18 +490,21 @@ template void TimeFrameGPU::createCellsLUTDevice(const int layer) { GPUTimer timer(mGpuStreams[layer], "creating cells LUTs", layer); - GPULog("gpu-transfer: creating cell LUT for {} elements on layer {}, for {:.2f} MB.", mNTracklets[layer] + 1, layer, (mNTracklets[layer] + 1) * sizeof(int) / constants::MB); - allocMemAsync(reinterpret_cast(&mCellsLUTDevice[layer]), (mNTracklets[layer] + 1) * sizeof(int), mGpuStreams[layer], this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); - GPUChkErrS(cudaMemsetAsync(mCellsLUTDevice[layer], 0, (mNTracklets[layer] + 1) * sizeof(int), mGpuStreams[layer].get())); + const int firstTransition = this->mTrackingTopologyView.getCell(layer).firstTransition; + GPULog("gpu-transfer: creating cell LUT for {} elements on layer {}, for {:.2f} MB.", mNTracklets[firstTransition] + 1, layer, (mNTracklets[firstTransition] + 1) * sizeof(int) / constants::MB); + allocMemAsync(reinterpret_cast(&mCellsLUTDevice[layer]), (mNTracklets[firstTransition] + 1) * sizeof(int), mGpuStreams[layer], this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + GPUChkErrS(cudaMemsetAsync(mCellsLUTDevice[layer], 0, (mNTracklets[firstTransition] + 1) * sizeof(int), mGpuStreams[layer].get())); GPUChkErrS(cudaMemcpyAsync(&mCellsLUTDeviceArray[layer], &mCellsLUTDevice[layer], sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); } template -void TimeFrameGPU::createCellsBuffersArray(const int iteration) +void TimeFrameGPU::createCellsBuffersArray() { - if (!iteration) { + { GPUTimer timer("creating cells buffers array"); - allocMem(reinterpret_cast(&mCellsDeviceArray), (NLayers - 2) * sizeof(CellSeed*), this->hasFrameworkAllocator()); + allocMem(reinterpret_cast(&mCellsDeviceArray), MaxCells * sizeof(CellSeed*), this->hasFrameworkAllocator()); + allocMem(reinterpret_cast(&mNeighboursDeviceArray), MaxCells * sizeof(CellNeighbour*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemset(mNeighboursDeviceArray, 0, MaxCells * sizeof(CellNeighbour*))); GPUChkErrS(cudaMemcpy(mCellsDeviceArray, mCellsDevice.data(), mCellsDevice.size() * sizeof(CellSeed*), cudaMemcpyHostToDevice)); } } @@ -475,7 +514,8 @@ void TimeFrameGPU::createCellsBuffers(const int layer) { GPUTimer timer(mGpuStreams[layer], "creating cells buffers"); mNCells[layer] = 0; - GPUChkErrS(cudaMemcpyAsync(&mNCells[layer], mCellsLUTDevice[layer] + mNTracklets[layer], sizeof(int), cudaMemcpyDeviceToHost, mGpuStreams[layer].get())); + const int firstTransition = this->mTrackingTopologyView.getCell(layer).firstTransition; + GPUChkErrS(cudaMemcpyAsync(&mNCells[layer], mCellsLUTDevice[layer] + mNTracklets[firstTransition], sizeof(int), cudaMemcpyDeviceToHost, mGpuStreams[layer].get())); mGpuStreams[layer].sync(); // ensure number of cells is correct GPULog("gpu-transfer: creating cell buffer for {} elements on layer {}, for {:.2f} MB.", mNCells[layer], layer, mNCells[layer] * sizeof(CellSeed) / constants::MB); allocMemAsync(reinterpret_cast(&mCellsDevice[layer]), mNCells[layer] * sizeof(CellSeed), mGpuStreams[layer], this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); @@ -511,13 +551,22 @@ void TimeFrameGPU::createNeighboursDevice(const unsigned int layer) { GPUTimer timer(mGpuStreams[layer], "reserving neighbours", layer); this->mNNeighbours[layer] = 0; - GPUChkErrS(cudaMemcpyAsync(&(this->mNNeighbours[layer]), &(mNeighboursLUTDevice[layer][this->mNCells[layer + 1] - 1]), sizeof(unsigned int), cudaMemcpyDeviceToHost, mGpuStreams[layer].get())); + if (this->mNCells[layer] == 0) { + mNeighboursDevice[layer] = nullptr; + GPUChkErrS(cudaMemcpyAsync(&mNeighboursDeviceArray[layer], &mNeighboursDevice[layer], sizeof(CellNeighbour*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); + return; + } + GPUChkErrS(cudaMemcpyAsync(&(this->mNNeighbours[layer]), &(mNeighboursLUTDevice[layer][this->mNCells[layer]]), sizeof(unsigned int), cudaMemcpyDeviceToHost, mGpuStreams[layer].get())); mGpuStreams[layer].sync(); // ensure number of neighbours is correct - GPULog("gpu-allocation: reserving {} neighbours (pairs), for {:.2f} MB.", this->mNNeighbours[layer], (this->mNNeighbours[layer]) * sizeof(gpuPair) / constants::MB); - allocMemAsync(reinterpret_cast(&mNeighbourPairsDevice[layer]), (this->mNNeighbours[layer]) * sizeof(gpuPair), mGpuStreams[layer], this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); - GPUChkErrS(cudaMemsetAsync(mNeighbourPairsDevice[layer], -1, (this->mNNeighbours[layer]) * sizeof(gpuPair), mGpuStreams[layer].get())); - GPULog("gpu-allocation: reserving {} neighbours, for {:.2f} MB.", this->mNNeighbours[layer], (this->mNNeighbours[layer]) * sizeof(gpuPair) / constants::MB); - allocMemAsync(reinterpret_cast(&mNeighboursDevice[layer]), (this->mNNeighbours[layer]) * sizeof(int), mGpuStreams[layer], this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + if (this->mNNeighbours[layer] == 0) { + mNeighboursDevice[layer] = nullptr; + GPUChkErrS(cudaMemcpyAsync(&mNeighboursDeviceArray[layer], &mNeighboursDevice[layer], sizeof(CellNeighbour*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); + return; + } + GPULog("gpu-allocation: reserving {} neighbours, for {:.2f} MB.", this->mNNeighbours[layer], (this->mNNeighbours[layer]) * sizeof(CellNeighbour) / constants::MB); + allocMemAsync(reinterpret_cast(&mNeighboursDevice[layer]), (this->mNNeighbours[layer]) * sizeof(CellNeighbour), mGpuStreams[layer], this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + GPUChkErrS(cudaMemsetAsync(mNeighboursDevice[layer], -1, (this->mNNeighbours[layer]) * sizeof(CellNeighbour), mGpuStreams[layer].get())); + GPUChkErrS(cudaMemcpyAsync(&mNeighboursDeviceArray[layer], &mNeighboursDevice[layer], sizeof(CellNeighbour*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); } template @@ -555,11 +604,11 @@ void TimeFrameGPU::downloadCellsLUTDevice() } template -void TimeFrameGPU::downloadCellsNeighboursDevice(std::vector>>& neighbours, const int layer) +void TimeFrameGPU::downloadCellsNeighboursDevice(std::vector>& neighbours, const int layer) { GPUTimer timer(mGpuStreams[layer], "downloading neighbours from layer", layer); - GPULog("gpu-transfer: downloading {} neighbours, for {:.2f} MB.", neighbours[layer].size(), neighbours[layer].size() * sizeof(std::pair) / constants::MB); - GPUChkErrS(cudaMemcpyAsync(neighbours[layer].data(), mNeighbourPairsDevice[layer], neighbours[layer].size() * sizeof(gpuPair), cudaMemcpyDeviceToHost, mGpuStreams[layer].get())); + GPULog("gpu-transfer: downloading {} neighbours, for {:.2f} MB.", neighbours[layer].size(), neighbours[layer].size() * sizeof(CellNeighbour) / constants::MB); + GPUChkErrS(cudaMemcpyAsync(neighbours[layer].data(), mNeighboursDevice[layer], neighbours[layer].size() * sizeof(CellNeighbour), cudaMemcpyDeviceToHost, mGpuStreams[layer].get())); } template @@ -646,12 +695,20 @@ void TimeFrameGPU::popMemoryStack(const int iteration) } template -void TimeFrameGPU::initialise(const int iteration, - const TrackingParameters& trkParam, - const int maxLayers) +void TimeFrameGPU::initialise(const TrackingParameters& trkParam, int maxLayers) +{ + mGpuStreams.resize(MaxStreams); + o2::its::TimeFrame::initialise(trkParam, maxLayers); +} + +template +void TimeFrameGPU::initialise(const TrackingParameters& trkParam, int maxLayers, int iteration) { - mGpuStreams.resize(NLayers); - o2::its::TimeFrame::initialise(iteration, trkParam, maxLayers, false); + mGpuStreams.resize(MaxStreams); + o2::its::TimeFrame::initialise(trkParam, maxLayers, iteration); + if (iteration != constants::UnusedIndex && iteration < static_cast(mDeviceTrackerTopologyViews.size())) { + mDeviceTrackingTopologyView = mDeviceTrackerTopologyViews[iteration]; + } } template @@ -694,4 +751,8 @@ void TimeFrameGPU::wipe() } template class TimeFrameGPU<7>; +// ALICE3 upgrade +#ifdef ENABLE_UPGRADES +template class TimeFrameGPU<11>; +#endif } // namespace o2::its::gpu diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx index 67b6b0bf5cdfe..32c46e2ea55d2 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx @@ -10,16 +10,11 @@ // or submit itself to any jurisdiction. /// -#include -#include #include -#include "DataFormatsITS/TrackITS.h" - -#include "ITStracking/TrackHelpers.h" #include "ITStrackingGPU/TrackerTraitsGPU.h" #include "ITStrackingGPU/TrackingKernels.h" -#include "ITStracking/Constants.h" +#include "ITStracking/Configuration.h" namespace o2::its { @@ -27,28 +22,34 @@ namespace o2::its template void TrackerTraitsGPU::initialiseTimeFrame(const int iteration) { - mTimeFrameGPU->initialise(iteration, this->mTrkParams[iteration], NLayers); - // on default stream - mTimeFrameGPU->loadVertices(iteration); - // TODO these tables can be put in persistent memory - mTimeFrameGPU->loadROFOverlapTable(iteration); // this can be put in constant memory actually - mTimeFrameGPU->loadROFVertexLookupTable(iteration); - // once the tables are in persistent memory just update the vertex one - // mTimeFrameGPU->updateROFVertexLookupTable(iteration); - mTimeFrameGPU->loadIndexTableUtils(iteration); - mTimeFrameGPU->loadROFCutMask(iteration); - // pinned on host - mTimeFrameGPU->createUsedClustersDeviceArray(iteration); - mTimeFrameGPU->createClustersDeviceArray(iteration); - mTimeFrameGPU->createUnsortedClustersDeviceArray(iteration); - mTimeFrameGPU->createClustersIndexTablesArray(iteration); - mTimeFrameGPU->createTrackingFrameInfoDeviceArray(iteration); - mTimeFrameGPU->createROFrameClustersDeviceArray(iteration); - // device array - mTimeFrameGPU->createTrackletsLUTDeviceArray(iteration); - mTimeFrameGPU->createTrackletsBuffersArray(iteration); - mTimeFrameGPU->createCellsBuffersArray(iteration); - mTimeFrameGPU->createCellsLUTDeviceArray(iteration); + mTimeFrameGPU->initialise(this->mTrkParams[iteration], NLayers, iteration); + + if (this->mTrkParams[iteration].PassFlags[IterationStep::FirstPass]) { + // on default stream + mTimeFrameGPU->loadVertices(); + // TODO these tables can be put in persistent memory + mTimeFrameGPU->loadROFOverlapTable(); // this can be put in constant memory actually + mTimeFrameGPU->loadROFVertexLookupTable(); + mTimeFrameGPU->loadTrackingTopologies(); + // once the tables are in persistent memory just update the vertex one + // mTimeFrameGPU->updateROFVertexLookupTable(); + mTimeFrameGPU->loadIndexTableUtils(); + // pinned on host + mTimeFrameGPU->createUsedClustersDeviceArray(); + mTimeFrameGPU->createClustersDeviceArray(); + mTimeFrameGPU->createUnsortedClustersDeviceArray(); + mTimeFrameGPU->createClustersIndexTablesArray(); + mTimeFrameGPU->createTrackingFrameInfoDeviceArray(); + mTimeFrameGPU->createROFrameClustersDeviceArray(); + // device array + mTimeFrameGPU->createTrackletsLUTDeviceArray(); + mTimeFrameGPU->createTrackletsBuffersArray(); + mTimeFrameGPU->createCellsBuffersArray(); + mTimeFrameGPU->createCellsLUTDeviceArray(); + } + if (this->mTrkParams[iteration].PassFlags[IterationStep::FirstPass] || this->mTrkParams[iteration].PassFlags[IterationStep::UseUPCMask]) { + mTimeFrameGPU->loadROFCutMask(iteration); + } // push every create artefact on the stack mTimeFrameGPU->pushMemoryStack(iteration); } @@ -63,28 +64,28 @@ void TrackerTraitsGPU::adoptTimeFrame(TimeFrame* tf) template void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int iVertex) { - // start by queuing loading needed of two last layers - for (int iLayer{NLayers}; iLayer-- > NLayers - 2;) { - mTimeFrameGPU->createUsedClustersDevice(iteration, iLayer); - mTimeFrameGPU->loadClustersDevice(iteration, iLayer); - mTimeFrameGPU->loadClustersIndexTables(iteration, iLayer); - mTimeFrameGPU->loadROFrameClustersDevice(iteration, iLayer); + const auto topology = mTimeFrameGPU->getDeviceTrackingTopologyView(); + const auto hostTopology = mTimeFrameGPU->getTrackingTopologyView(); + for (int iLayer{0}; iLayer < this->mTrkParams[iteration].NLayers; ++iLayer) { + if (this->mTrkParams[iteration].PassFlags[IterationStep::FirstPass]) { + mTimeFrameGPU->createUsedClustersDevice(iLayer); + mTimeFrameGPU->loadClustersDevice(iLayer); + mTimeFrameGPU->loadClustersIndexTables(iLayer); + mTimeFrameGPU->loadROFrameClustersDevice(iLayer); + } mTimeFrameGPU->recordEvent(iLayer); } - for (int iLayer{this->mTrkParams[iteration].TrackletsPerRoad()}; iLayer--;) { - if (iLayer) { // queue loading data of next layer in parallel, this the copies are overlapping with computation kernels - mTimeFrameGPU->createUsedClustersDevice(iteration, iLayer - 1); - mTimeFrameGPU->loadClustersDevice(iteration, iLayer - 1); - mTimeFrameGPU->loadClustersIndexTables(iteration, iLayer - 1); - mTimeFrameGPU->loadROFrameClustersDevice(iteration, iLayer - 1); - mTimeFrameGPU->recordEvent(iLayer - 1); - } - mTimeFrameGPU->createTrackletsLUTDevice(iteration, iLayer); - mTimeFrameGPU->waitEvent(iLayer, iLayer + 1); // wait stream until all data is available + for (int transitionId{0}; transitionId < hostTopology.nTransitions; ++transitionId) { + const auto transition = hostTopology.getTransition(transitionId); + mTimeFrameGPU->createTrackletsLUTDevice(this->mTrkParams[iteration].PassFlags[IterationStep::FirstPass], transitionId); + mTimeFrameGPU->waitEvent(transitionId, transition.fromLayer); + mTimeFrameGPU->waitEvent(transitionId, transition.toLayer); countTrackletsInROFsHandler(mTimeFrameGPU->getDeviceIndexTableUtils(), mTimeFrameGPU->getDeviceROFMaskTableView(), - iLayer, + transitionId, + transition.fromLayer, + transition.toLayer, mTimeFrameGPU->getDeviceROFOverlapTableView(), mTimeFrameGPU->getDeviceROFVertexLookupTableView(), iVertex, @@ -97,24 +98,28 @@ void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int i mTimeFrameGPU->getDeviceArrayClustersIndexTables(), mTimeFrameGPU->getDeviceArrayTrackletsLUT(), mTimeFrameGPU->getDeviceTrackletsLUTs(), - iteration, + this->mTrkParams[iteration].PassFlags[IterationStep::SelectUPCVertices], this->mTrkParams[iteration].NSigmaCut, - mTimeFrameGPU->getPhiCuts(), + topology, + mTimeFrameGPU->getTransitionPhiCuts(), this->mTrkParams[iteration].PVres, mTimeFrameGPU->getMinRs(), mTimeFrameGPU->getMaxRs(), mTimeFrameGPU->getPositionResolutions(), this->mTrkParams[iteration].LayerRadii, - mTimeFrameGPU->getMSangles(), + mTimeFrameGPU->getTransitionMSAngles(), mTimeFrameGPU->getFrameworkAllocator(), mTimeFrameGPU->getStreams()); - mTimeFrameGPU->createTrackletsBuffers(iLayer); - if (mTimeFrameGPU->getNTracklets()[iLayer] == 0) { + mTimeFrameGPU->createTrackletsBuffers(transitionId); + if (mTimeFrameGPU->getNTracklets()[transitionId] == 0) { + mTimeFrameGPU->recordEvent(transitionId); continue; } computeTrackletsInROFsHandler(mTimeFrameGPU->getDeviceIndexTableUtils(), mTimeFrameGPU->getDeviceROFMaskTableView(), - iLayer, + transitionId, + transition.fromLayer, + transition.toLayer, mTimeFrameGPU->getDeviceROFOverlapTableView(), mTimeFrameGPU->getDeviceROFVertexLookupTableView(), iVertex, @@ -130,57 +135,62 @@ void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int i mTimeFrameGPU->getNTracklets(), mTimeFrameGPU->getDeviceArrayTrackletsLUT(), mTimeFrameGPU->getDeviceTrackletsLUTs(), - iteration, + this->mTrkParams[iteration].PassFlags[IterationStep::SelectUPCVertices], this->mTrkParams[iteration].NSigmaCut, - mTimeFrameGPU->getPhiCuts(), + topology, + mTimeFrameGPU->getTransitionPhiCuts(), this->mTrkParams[iteration].PVres, mTimeFrameGPU->getMinRs(), mTimeFrameGPU->getMaxRs(), mTimeFrameGPU->getPositionResolutions(), this->mTrkParams[iteration].LayerRadii, - mTimeFrameGPU->getMSangles(), + mTimeFrameGPU->getTransitionMSAngles(), mTimeFrameGPU->getFrameworkAllocator(), mTimeFrameGPU->getStreams()); + mTimeFrameGPU->recordEvent(transitionId); } } template void TrackerTraitsGPU::computeLayerCells(const int iteration) { - // start by queuing loading needed of three last layers - for (int iLayer{NLayers}; iLayer-- > NLayers - 3;) { - mTimeFrameGPU->loadUnsortedClustersDevice(iteration, iLayer); - mTimeFrameGPU->loadTrackingFrameInfoDevice(iteration, iLayer); + const auto topology = mTimeFrameGPU->getDeviceTrackingTopologyView(); + const auto hostTopology = mTimeFrameGPU->getTrackingTopologyView(); + for (int iLayer{0}; iLayer < this->mTrkParams[iteration].NLayers; ++iLayer) { + if (this->mTrkParams[iteration].PassFlags[IterationStep::FirstPass]) { + mTimeFrameGPU->loadUnsortedClustersDevice(iLayer); + mTimeFrameGPU->loadTrackingFrameInfoDevice(iLayer); + } mTimeFrameGPU->recordEvent(iLayer); } - for (int iLayer{this->mTrkParams[iteration].CellsPerRoad()}; iLayer--;) { - if (iLayer) { - mTimeFrameGPU->loadUnsortedClustersDevice(iteration, iLayer - 1); - mTimeFrameGPU->loadTrackingFrameInfoDevice(iteration, iLayer - 1); - mTimeFrameGPU->recordEvent(iLayer - 1); - } - - // if there are no tracklets skip entirely - const int currentLayerTrackletsNum{static_cast(mTimeFrameGPU->getNTracklets()[iLayer])}; - if (!currentLayerTrackletsNum || !mTimeFrameGPU->getNTracklets()[iLayer + 1]) { - mTimeFrameGPU->getNCells()[iLayer] = 0; + for (int cellTopologyId{hostTopology.nCells}; cellTopologyId--;) { + const auto cellTopology = hostTopology.getCell(cellTopologyId); + const auto first = hostTopology.getTransition(cellTopology.firstTransition); + const auto second = hostTopology.getTransition(cellTopology.secondTransition); + const int currentLayerTrackletsNum{static_cast(mTimeFrameGPU->getNTracklets()[cellTopology.firstTransition])}; + if (!currentLayerTrackletsNum || !mTimeFrameGPU->getNTracklets()[cellTopology.secondTransition]) { + mTimeFrameGPU->getNCells()[cellTopologyId] = 0; continue; } - mTimeFrameGPU->createCellsLUTDevice(iLayer); - mTimeFrameGPU->waitEvent(iLayer, iLayer + 1); // wait stream until all data is available - mTimeFrameGPU->waitEvent(iLayer, iLayer + 2); // wait stream until all data is available + mTimeFrameGPU->createCellsLUTDevice(cellTopologyId); + mTimeFrameGPU->waitEvent(cellTopologyId, cellTopology.firstTransition); + mTimeFrameGPU->waitEvent(cellTopologyId, cellTopology.secondTransition); + mTimeFrameGPU->waitEvent(cellTopologyId, first.fromLayer); + mTimeFrameGPU->waitEvent(cellTopologyId, first.toLayer); + mTimeFrameGPU->waitEvent(cellTopologyId, second.toLayer); countCellsHandler(mTimeFrameGPU->getDeviceArrayClusters(), mTimeFrameGPU->getDeviceArrayUnsortedClusters(), mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), mTimeFrameGPU->getDeviceArrayTracklets(), mTimeFrameGPU->getDeviceArrayTrackletsLUT(), currentLayerTrackletsNum, - iLayer, + cellTopologyId, + topology, nullptr, mTimeFrameGPU->getDeviceArrayCellsLUT(), - mTimeFrameGPU->getDeviceCellLUTs()[iLayer], + mTimeFrameGPU->getDeviceCellLUTs()[cellTopologyId], this->mBz, this->mTrkParams[iteration].MaxChi2ClusterAttachment, this->mTrkParams[iteration].CellDeltaTanLambdaSigma, @@ -188,8 +198,9 @@ void TrackerTraitsGPU::computeLayerCells(const int iteration) this->mTrkParams[iteration].LayerxX0, mTimeFrameGPU->getFrameworkAllocator(), mTimeFrameGPU->getStreams()); - mTimeFrameGPU->createCellsBuffers(iLayer); - if (mTimeFrameGPU->getNCells()[iLayer] == 0) { + mTimeFrameGPU->createCellsBuffers(cellTopologyId); + if (mTimeFrameGPU->getNCells()[cellTopologyId] == 0) { + mTimeFrameGPU->recordEvent(cellTopologyId); continue; } computeCellsHandler(mTimeFrameGPU->getDeviceArrayClusters(), @@ -198,16 +209,18 @@ void TrackerTraitsGPU::computeLayerCells(const int iteration) mTimeFrameGPU->getDeviceArrayTracklets(), mTimeFrameGPU->getDeviceArrayTrackletsLUT(), currentLayerTrackletsNum, - iLayer, - mTimeFrameGPU->getDeviceCells()[iLayer], + cellTopologyId, + topology, + mTimeFrameGPU->getDeviceCells()[cellTopologyId], mTimeFrameGPU->getDeviceArrayCellsLUT(), - mTimeFrameGPU->getDeviceCellLUTs()[iLayer], + mTimeFrameGPU->getDeviceCellLUTs()[cellTopologyId], this->mBz, this->mTrkParams[iteration].MaxChi2ClusterAttachment, this->mTrkParams[iteration].CellDeltaTanLambdaSigma, this->mTrkParams[iteration].NSigmaCut, this->mTrkParams[iteration].LayerxX0, mTimeFrameGPU->getStreams()); + mTimeFrameGPU->recordEvent(cellTopologyId); } mTimeFrameGPU->syncStreams(false); } @@ -215,58 +228,71 @@ void TrackerTraitsGPU::computeLayerCells(const int iteration) template void TrackerTraitsGPU::findCellsNeighbours(const int iteration) { - for (int iLayer{0}; iLayer < this->mTrkParams[iteration].NeighboursPerRoad(); ++iLayer) { - if (iLayer > 0) { - // Previous layer updates levels in this layer's cells. - mTimeFrameGPU->waitEvent(iLayer, iLayer - 1); - } - const int currentLayerCellsNum{static_cast(mTimeFrameGPU->getNCells()[iLayer])}; - const int nextLayerCellsNum{static_cast(mTimeFrameGPU->getNCells()[iLayer + 1])}; - if (!nextLayerCellsNum || !currentLayerCellsNum) { - mTimeFrameGPU->getNNeighbours()[iLayer] = 0; - mTimeFrameGPU->recordEvent(iLayer); - continue; - } - mTimeFrameGPU->createNeighboursIndexTablesDevice(iLayer); - mTimeFrameGPU->createNeighboursLUTDevice(iLayer, nextLayerCellsNum); - countCellNeighboursHandler(mTimeFrameGPU->getDeviceArrayCells(), - mTimeFrameGPU->getDeviceNeighboursLUT(iLayer), // LUT is initialised here. - mTimeFrameGPU->getDeviceArrayCellsLUT(), - mTimeFrameGPU->getDeviceNeighbourPairs(iLayer), - mTimeFrameGPU->getDeviceNeighboursIndexTables(iLayer), - (const Tracklet**)mTimeFrameGPU->getDeviceArrayTracklets(), - this->mTrkParams[iteration].MaxChi2ClusterAttachment, - this->mBz, - iLayer, - currentLayerCellsNum, - nextLayerCellsNum, - 1e2, - mTimeFrameGPU->getFrameworkAllocator(), - mTimeFrameGPU->getStream(iLayer)); - mTimeFrameGPU->createNeighboursDevice(iLayer); - if (mTimeFrameGPU->getNNeighbours()[iLayer] == 0) { - mTimeFrameGPU->recordEvent(iLayer); - continue; + const auto hostTopology = mTimeFrameGPU->getTrackingTopologyView(); + for (int outerLayer{0}; outerLayer < NLayers; ++outerLayer) { + for (int targetCellTopologyId{0}; targetCellTopologyId < hostTopology.nCells; ++targetCellTopologyId) { + const auto targetCellTopology = hostTopology.getCell(targetCellTopologyId); + if (targetCellTopology.hitLayerMask.last() != outerLayer) { + continue; + } + const int targetCellsNum{static_cast(mTimeFrameGPU->getNCells()[targetCellTopologyId])}; + if (!targetCellsNum) { + mTimeFrameGPU->getNNeighbours()[targetCellTopologyId] = 0; + mTimeFrameGPU->recordEvent(targetCellTopologyId); + continue; + } + mTimeFrameGPU->createNeighboursIndexTablesDevice(targetCellTopologyId); + mTimeFrameGPU->createNeighboursLUTDevice(targetCellTopologyId, targetCellsNum); + + for (int sourceCellTopologyId{0}; sourceCellTopologyId < hostTopology.nCells; ++sourceCellTopologyId) { + const auto sourceCellTopology = hostTopology.getCell(sourceCellTopologyId); + const int sourceCellsNum{static_cast(mTimeFrameGPU->getNCells()[sourceCellTopologyId])}; + if (!sourceCellsNum || sourceCellTopology.secondTransition != targetCellTopology.firstTransition) { + continue; + } + mTimeFrameGPU->waitEvent(targetCellTopologyId, sourceCellTopologyId); + countCellNeighboursHandler(mTimeFrameGPU->getDeviceArrayCells(), + mTimeFrameGPU->getDeviceNeighboursIndexTables(targetCellTopologyId), + mTimeFrameGPU->getDeviceArrayCellsLUT(), + sourceCellTopologyId, + targetCellTopologyId, + this->mTrkParams[iteration].MaxChi2ClusterAttachment, + this->mBz, + sourceCellsNum, + mTimeFrameGPU->getStream(targetCellTopologyId)); + } + + scanCellNeighboursHandler(mTimeFrameGPU->getDeviceNeighboursIndexTables(targetCellTopologyId), + mTimeFrameGPU->getDeviceNeighboursLUT(targetCellTopologyId), + targetCellsNum, + mTimeFrameGPU->getFrameworkAllocator(), + mTimeFrameGPU->getStream(targetCellTopologyId)); + + mTimeFrameGPU->createNeighboursDevice(targetCellTopologyId); + if (mTimeFrameGPU->getNNeighbours()[targetCellTopologyId] == 0) { + mTimeFrameGPU->recordEvent(targetCellTopologyId); + continue; + } + + for (int sourceCellTopologyId{0}; sourceCellTopologyId < hostTopology.nCells; ++sourceCellTopologyId) { + const auto sourceCellTopology = hostTopology.getCell(sourceCellTopologyId); + const int sourceCellsNum{static_cast(mTimeFrameGPU->getNCells()[sourceCellTopologyId])}; + if (!sourceCellsNum || sourceCellTopology.secondTransition != targetCellTopology.firstTransition) { + continue; + } + computeCellNeighboursHandler(mTimeFrameGPU->getDeviceArrayCells(), + mTimeFrameGPU->getDeviceNeighboursIndexTables(targetCellTopologyId), + mTimeFrameGPU->getDeviceArrayCellsLUT(), + mTimeFrameGPU->getDeviceNeighbours(targetCellTopologyId), + sourceCellTopologyId, + targetCellTopologyId, + this->mTrkParams[iteration].MaxChi2ClusterAttachment, + this->mBz, + sourceCellsNum, + mTimeFrameGPU->getStream(targetCellTopologyId)); + } + mTimeFrameGPU->recordEvent(targetCellTopologyId); } - computeCellNeighboursHandler(mTimeFrameGPU->getDeviceArrayCells(), - mTimeFrameGPU->getDeviceNeighboursLUT(iLayer), - mTimeFrameGPU->getDeviceArrayCellsLUT(), - mTimeFrameGPU->getDeviceNeighbourPairs(iLayer), - mTimeFrameGPU->getDeviceNeighboursIndexTables(iLayer), - (const Tracklet**)mTimeFrameGPU->getDeviceArrayTracklets(), - this->mTrkParams[iteration].MaxChi2ClusterAttachment, - this->mBz, - iLayer, - currentLayerCellsNum, - nextLayerCellsNum, - 1e2, - mTimeFrameGPU->getStream(iLayer)); - mTimeFrameGPU->getArrayNNeighbours()[iLayer] = filterCellNeighboursHandler(mTimeFrameGPU->getDeviceNeighbourPairs(iLayer), - mTimeFrameGPU->getDeviceNeighbours(iLayer), - mTimeFrameGPU->getArrayNNeighbours()[iLayer], - mTimeFrameGPU->getStream(iLayer), - mTimeFrameGPU->getFrameworkAllocator()); - mTimeFrameGPU->recordEvent(iLayer); } mTimeFrameGPU->syncStreams(false); } @@ -278,26 +304,33 @@ void TrackerTraitsGPU::findRoads(const int iteration) bounded_vector> sharedFirstClusters(this->mTrkParams[iteration].NLayers, bounded_vector(this->getMemoryPool().get()), this->getMemoryPool().get()); firstClusters.resize(this->mTrkParams[iteration].NLayers); sharedFirstClusters.resize(this->mTrkParams[iteration].NLayers); + const auto hostTopology = mTimeFrameGPU->getTrackingTopologyView(); for (int startLevel{this->mTrkParams[iteration].CellsPerRoad()}; startLevel >= this->mTrkParams[iteration].CellMinimumLevel(); --startLevel) { - const int minimumLayer{startLevel - 1}; bounded_vector> trackSeeds(this->getMemoryPool().get()); - for (int startLayer{this->mTrkParams[iteration].CellsPerRoad() - 1}; startLayer >= minimumLayer; --startLayer) { - if ((this->mTrkParams[iteration].StartLayerMask & (1 << (startLayer + 2))) == 0) { + for (int startCellTopologyId{0}; startCellTopologyId < hostTopology.nCells; ++startCellTopologyId) { + const int startLayer = hostTopology.getCell(startCellTopologyId).hitLayerMask.last(); + if ((this->mTrkParams[iteration].StartLayerMask & (1 << startLayer)) == 0 || + mTimeFrameGPU->getNCells()[startCellTopologyId] == 0) { continue; } - processNeighboursHandler(startLayer, - startLevel, + processNeighboursHandler(startLevel, + startCellTopologyId, mTimeFrameGPU->getDeviceArrayCells(), - mTimeFrameGPU->getDeviceCells()[startLayer], - mTimeFrameGPU->getArrayNCells(), + mTimeFrameGPU->getDeviceCells()[startCellTopologyId], + nullptr, + nullptr, + mTimeFrameGPU->getArrayNCells().data(), (const uint8_t**)mTimeFrameGPU->getDeviceArrayUsedClusters(), - mTimeFrameGPU->getDeviceNeighboursAll(), - mTimeFrameGPU->getDeviceNeighboursLUTs(), + mTimeFrameGPU->getDeviceArrayNeighbours(), + mTimeFrameGPU->getDeviceArrayNeighboursCellLUT(), mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), trackSeeds, this->mBz, this->mTrkParams[iteration].MaxChi2ClusterAttachment, this->mTrkParams[iteration].MaxChi2NDF, + this->mTrkParams[iteration].MaxHoles, + this->mTrkParams[iteration].MinTrackLength, + this->mTrkParams[iteration].HoleLayerMask, this->mTrkParams[iteration].LayerxX0, mTimeFrameGPU->getDevicePropagator(), this->mTrkParams[iteration].CorrType, @@ -354,10 +387,10 @@ void TrackerTraitsGPU::findRoads(const int iteration) mTimeFrameGPU->downloadTrackITSExtDevice(); auto& tracks = mTimeFrameGPU->getTrackITSExt(); - this->acceptTracks(iteration, tracks, firstClusters, sharedFirstClusters); + this->acceptTracks(iteration, tracks, firstClusters); mTimeFrameGPU->loadUsedClustersDevice(); } - this->markTracks(iteration, sharedFirstClusters); + this->markTracks(iteration); // wipe the artefact memory mTimeFrameGPU->popMemoryStack(iteration); }; @@ -388,4 +421,7 @@ void TrackerTraitsGPU::setBz(float bz) } template class TrackerTraitsGPU<7>; +#ifdef ENABLE_UPGRADES +template class TrackerTraitsGPU<11>; +#endif } // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu index 49b8f19d68ea6..571afe08fc209 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu @@ -86,20 +86,25 @@ struct is_valid_pair { template struct seed_selector { - float maxQ2Pt; - float maxChi2; + float mMaxQ2Pt; + float mMaxChi2; + int mMaxHoles; + int mMinTrackLength; + LayerMask mHoleLayerMask; - GPUhd() seed_selector(float maxQ2Pt, float maxChi2) : maxQ2Pt(maxQ2Pt), maxChi2(maxChi2) {} + GPUhd() seed_selector(float maxQ2Pt, float maxChi2, int maxHoles, int minTrackLength, LayerMask holeLayerMask) : mMaxQ2Pt(maxQ2Pt), mMaxChi2(maxChi2), mMaxHoles(maxHoles), mMinTrackLength(minTrackLength), mHoleLayerMask(holeLayerMask) {} GPUhd() bool operator()(const TrackSeed& seed) const { - return !(seed.getQ2Pt() > maxQ2Pt || seed.getChi2() > maxChi2); + return !(seed.getQ2Pt() > mMaxQ2Pt || seed.getChi2() > mMaxChi2) && + seed.getHitLayerMask().length() >= mMinTrackLength && + seed.getHitLayerMask().isAllowed(mMaxHoles, mHoleLayerMask); } }; struct compare_track_chi2 { GPUhd() bool operator()(const TrackITSExt& a, const TrackITSExt& b) const { - return a.getChi2() < b.getChi2(); + return o2::its::track::isBetter(a, b); } }; @@ -160,30 +165,22 @@ GPUg() void __launch_bounds__(256, 1) fitTrackSeedsKernel( template GPUg() void __launch_bounds__(256, 1) computeLayerCellNeighboursKernel( CellSeed** cellSeedArray, - int* neighboursLUT, - int* neighboursIndexTable, + int* neighboursCursor, int** cellsLUTs, - gpuPair* cellNeighbours, - const Tracklet** tracklets, + CellNeighbour* cellNeighbours, + const int sourceCellTopologyId, + const int targetCellTopologyId, const float maxChi2ClusterAttachment, const float bz, - const int layerIndex, - const unsigned int nCells, - const int maxCellNeighbours = 1e2) + const unsigned int nCells) { for (int iCurrentCellIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentCellIndex < nCells; iCurrentCellIndex += blockDim.x * gridDim.x) { - if constexpr (!initRun) { - if (neighboursIndexTable[iCurrentCellIndex] == neighboursIndexTable[iCurrentCellIndex + 1]) { - continue; - } - } - const auto& currentCellSeed{cellSeedArray[layerIndex][iCurrentCellIndex]}; + const auto& currentCellSeed{cellSeedArray[sourceCellTopologyId][iCurrentCellIndex]}; const int nextLayerTrackletIndex{currentCellSeed.getSecondTrackletIndex()}; - const int nextLayerFirstCellIndex{cellsLUTs[layerIndex + 1][nextLayerTrackletIndex]}; - const int nextLayerLastCellIndex{cellsLUTs[layerIndex + 1][nextLayerTrackletIndex + 1]}; - int foundNeighbours{0}; + const int nextLayerFirstCellIndex{cellsLUTs[targetCellTopologyId][nextLayerTrackletIndex]}; + const int nextLayerLastCellIndex{cellsLUTs[targetCellTopologyId][nextLayerTrackletIndex + 1]}; for (int iNextCell{nextLayerFirstCellIndex}; iNextCell < nextLayerLastCellIndex; ++iNextCell) { - auto nextCellSeed{cellSeedArray[layerIndex + 1][iNextCell]}; // Copy + auto nextCellSeed{cellSeedArray[targetCellTopologyId][iNextCell]}; // Copy if (nextCellSeed.getFirstTrackletIndex() != nextLayerTrackletIndex || !currentCellSeed.getTimeStamp().isCompatible(nextCellSeed.getTimeStamp())) { break; } @@ -199,14 +196,13 @@ GPUg() void __launch_bounds__(256, 1) computeLayerCellNeighboursKernel( } if constexpr (initRun) { - atomicAdd(neighboursLUT + iNextCell, 1); - neighboursIndexTable[iCurrentCellIndex]++; + atomicAdd(neighboursCursor + iNextCell, 1); } else { - cellNeighbours[neighboursIndexTable[iCurrentCellIndex] + foundNeighbours] = {iCurrentCellIndex, iNextCell}; - foundNeighbours++; + const int offset = atomicAdd(neighboursCursor + iNextCell, 1); + cellNeighbours[offset] = {sourceCellTopologyId, iCurrentCellIndex, targetCellTopologyId, iNextCell, currentCellSeed.getLevel() + 1}; const int currentCellLevel{currentCellSeed.getLevel()}; if (currentCellLevel >= nextCellSeed.getLevel()) { - atomicMax(cellSeedArray[layerIndex + 1][iNextCell].getLevelPtr(), currentCellLevel + 1); + atomicMax(cellSeedArray[targetCellTopologyId][iNextCell].getLevelPtr(), currentCellLevel + 1); } } } @@ -221,7 +217,8 @@ GPUg() void __launch_bounds__(256, 1) computeLayerCellsKernel( Tracklet** tracklets, int** trackletsLUT, const int nTrackletsCurrent, - const int layer, + const int cellTopologyId, + const typename TrackingTopology::View topology, CellSeed* cells, int** cellsLUTs, const float* layerxX0, @@ -230,25 +227,29 @@ GPUg() void __launch_bounds__(256, 1) computeLayerCellsKernel( const float cellDeltaTanLambdaSigma, const float nSigmaCut) { + const auto cellTopology = topology.getCell(cellTopologyId); + const auto first = topology.getTransition(cellTopology.firstTransition); + const auto second = topology.getTransition(cellTopology.secondTransition); + const int layers[3] = {first.fromLayer, first.toLayer, second.toLayer}; for (int iCurrentTrackletIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentTrackletIndex < nTrackletsCurrent; iCurrentTrackletIndex += blockDim.x * gridDim.x) { if constexpr (!initRun) { - if (cellsLUTs[layer][iCurrentTrackletIndex] == cellsLUTs[layer][iCurrentTrackletIndex + 1]) { + if (cellsLUTs[cellTopologyId][iCurrentTrackletIndex] == cellsLUTs[cellTopologyId][iCurrentTrackletIndex + 1]) { continue; } } - const Tracklet& currentTracklet = tracklets[layer][iCurrentTrackletIndex]; + const Tracklet& currentTracklet = tracklets[cellTopology.firstTransition][iCurrentTrackletIndex]; const int nextLayerClusterIndex{currentTracklet.secondClusterIndex}; - const int nextLayerFirstTrackletIndex{trackletsLUT[layer + 1][nextLayerClusterIndex]}; - const int nextLayerLastTrackletIndex{trackletsLUT[layer + 1][nextLayerClusterIndex + 1]}; + const int nextLayerFirstTrackletIndex{trackletsLUT[cellTopology.secondTransition][nextLayerClusterIndex]}; + const int nextLayerLastTrackletIndex{trackletsLUT[cellTopology.secondTransition][nextLayerClusterIndex + 1]}; if (nextLayerFirstTrackletIndex == nextLayerLastTrackletIndex) { continue; } int foundCells{0}; for (int iNextTrackletIndex{nextLayerFirstTrackletIndex}; iNextTrackletIndex < nextLayerLastTrackletIndex; ++iNextTrackletIndex) { - if (tracklets[layer + 1][iNextTrackletIndex].firstClusterIndex != nextLayerClusterIndex) { + if (tracklets[cellTopology.secondTransition][iNextTrackletIndex].firstClusterIndex != nextLayerClusterIndex) { break; } - const Tracklet& nextTracklet = tracklets[layer + 1][iNextTrackletIndex]; + const Tracklet& nextTracklet = tracklets[cellTopology.secondTransition][iNextTrackletIndex]; if (!currentTracklet.getTimeStamp().isCompatible(nextTracklet.getTimeStamp())) { continue; } @@ -256,18 +257,18 @@ GPUg() void __launch_bounds__(256, 1) computeLayerCellsKernel( if (deltaTanLambda / cellDeltaTanLambdaSigma < nSigmaCut) { const int clusId[3]{ - sortedClusters[layer][currentTracklet.firstClusterIndex].clusterId, - sortedClusters[layer + 1][nextTracklet.firstClusterIndex].clusterId, - sortedClusters[layer + 2][nextTracklet.secondClusterIndex].clusterId}; + sortedClusters[layers[0]][currentTracklet.firstClusterIndex].clusterId, + sortedClusters[layers[1]][nextTracklet.firstClusterIndex].clusterId, + sortedClusters[layers[2]][nextTracklet.secondClusterIndex].clusterId}; - const auto& cluster1_glo = unsortedClusters[layer][clusId[0]]; - const auto& cluster2_glo = unsortedClusters[layer + 1][clusId[1]]; - const auto& cluster3_tf = tfInfo[layer + 2][clusId[2]]; + const auto& cluster1_glo = unsortedClusters[layers[0]][clusId[0]]; + const auto& cluster2_glo = unsortedClusters[layers[1]][clusId[1]]; + const auto& cluster3_tf = tfInfo[layers[2]][clusId[2]]; auto track{o2::its::track::buildTrackSeed(cluster1_glo, cluster2_glo, cluster3_tf, bz)}; float chi2{0.f}; bool good{false}; for (int iC{2}; iC--;) { - const TrackingFrameInfo& trackingHit = tfInfo[layer + iC][clusId[iC]]; + const TrackingFrameInfo& trackingHit = tfInfo[layers[iC]][clusId[iC]]; if (!track.rotate(trackingHit.alphaTrackingFrame)) { break; } @@ -275,7 +276,7 @@ GPUg() void __launch_bounds__(256, 1) computeLayerCellsKernel( break; } - if (!track.correctForMaterial(layerxX0[layer + iC], layerxX0[layer + iC] * constants::Radl * constants::Rho, true)) { + if (!track.correctForMaterial(layerxX0[layers[iC]], layerxX0[layers[iC]] * constants::Radl * constants::Rho, true)) { break; } @@ -295,13 +296,13 @@ GPUg() void __launch_bounds__(256, 1) computeLayerCellsKernel( if constexpr (!initRun) { TimeEstBC ts = currentTracklet.getTimeStamp(); ts += nextTracklet.getTimeStamp(); - new (cells + cellsLUTs[layer][iCurrentTrackletIndex] + foundCells) CellSeed{layer, clusId[0], clusId[1], clusId[2], iCurrentTrackletIndex, iNextTrackletIndex, track, chi2, ts}; + new (cells + cellsLUTs[cellTopologyId][iCurrentTrackletIndex] + foundCells) CellSeed{cellTopology.hitLayerMask, clusId[0], clusId[1], clusId[2], iCurrentTrackletIndex, iNextTrackletIndex, track, chi2, ts}; } ++foundCells; } } if constexpr (initRun) { - cellsLUTs[layer][iCurrentTrackletIndex] = foundCells; + cellsLUTs[cellTopologyId][iCurrentTrackletIndex] = foundCells; } } } @@ -310,7 +311,8 @@ template GPUg() void __launch_bounds__(256, 1) computeLayerTrackletsMultiROFKernel( const IndexTableUtils* utils, const typename ROFMaskTable::View rofMask, - const int layerIndex, + const int transitionId, + const typename TrackingTopology::View topology, const typename ROFOverlapTable::View rofOverlaps, const typename ROFVertexLookupTable::View vertexLUT, const Vertex* vertices, @@ -322,7 +324,7 @@ GPUg() void __launch_bounds__(256, 1) computeLayerTrackletsMultiROFKernel( const int** indexTables, Tracklet** tracklets, int** trackletsLUT, - const int iteration, + const bool selectUPCVertices, const float NSigmaCut, const float phiCut, const float resolutionPV, @@ -332,17 +334,20 @@ GPUg() void __launch_bounds__(256, 1) computeLayerTrackletsMultiROFKernel( const float meanDeltaR, const float MSAngle) { + const auto transition = topology.getTransition(transitionId); + const int fromLayer = transition.fromLayer; + const int toLayer = transition.toLayer; const int phiBins{utils->getNphiBins()}; const int zBins{utils->getNzBins()}; const int tableSize{phiBins * zBins + 1}; - const int totalROFs0 = rofOverlaps.getLayer(layerIndex).mNROFsTF; - const int totalROFs1 = rofOverlaps.getLayer(layerIndex + 1).mNROFsTF; + const int totalROFs0 = rofOverlaps.getLayer(fromLayer).mNROFsTF; + const int totalROFs1 = rofOverlaps.getLayer(toLayer).mNROFsTF; for (unsigned int pivotROF{blockIdx.x}; pivotROF < totalROFs0; pivotROF += gridDim.x) { - if (!rofMask.isROFEnabled(layerIndex, pivotROF)) { + if (!rofMask.isROFEnabled(fromLayer, pivotROF)) { continue; } - const auto& pvs = vertexLUT.getVertices(layerIndex, pivotROF); + const auto& pvs = vertexLUT.getVertices(fromLayer, pivotROF); auto primaryVertices = gpuSpan(&vertices[pvs.getFirstEntry()], pvs.getEntries()); if (primaryVertices.empty()) { continue; @@ -353,12 +358,12 @@ GPUg() void __launch_bounds__(256, 1) computeLayerTrackletsMultiROFKernel( continue; } - const auto& rofOverlap = rofOverlaps.getOverlap(layerIndex, layerIndex + 1, pivotROF); + const auto& rofOverlap = rofOverlaps.getOverlap(fromLayer, toLayer, pivotROF); if (!rofOverlap.getEntries()) { continue; } - auto clustersCurrentLayer = getClustersOnLayer(pivotROF, totalROFs0, layerIndex, ROFClusters, clusters); + auto clustersCurrentLayer = getClustersOnLayer(pivotROF, totalROFs0, fromLayer, ROFClusters, clusters); if (clustersCurrentLayer.empty()) { continue; } @@ -367,12 +372,12 @@ GPUg() void __launch_bounds__(256, 1) computeLayerTrackletsMultiROFKernel( unsigned int storedTracklets{0}; const auto& currentCluster{clustersCurrentLayer[currentClusterIndex]}; - const int currentSortedIndex{ROFClusters[layerIndex][pivotROF] + currentClusterIndex}; - if (usedClusters[layerIndex][currentCluster.clusterId]) { + const int currentSortedIndex{ROFClusters[fromLayer][pivotROF] + currentClusterIndex}; + if (usedClusters[fromLayer][currentCluster.clusterId]) { continue; } if constexpr (!initRun) { - if (trackletsLUT[layerIndex][currentSortedIndex] == trackletsLUT[layerIndex][currentSortedIndex + 1]) { + if (trackletsLUT[transitionId][currentSortedIndex] == trackletsLUT[transitionId][currentSortedIndex + 1]) { continue; } } @@ -380,10 +385,10 @@ GPUg() void __launch_bounds__(256, 1) computeLayerTrackletsMultiROFKernel( const float inverseR0{1.f / currentCluster.radius}; for (int iV{startVtx}; iV < endVtx; ++iV) { auto& primaryVertex{primaryVertices[iV]}; - if (!vertexLUT.isVertexCompatible(layerIndex, pivotROF, primaryVertex)) { + if (!vertexLUT.isVertexCompatible(fromLayer, pivotROF, primaryVertex)) { continue; } - if ((primaryVertex.isFlagSet(Vertex::Flags::UPCMode) && iteration != 3) || (iteration == 3 && !primaryVertex.isFlagSet(Vertex::Flags::UPCMode))) { + if (primaryVertex.isFlagSet(Vertex::Flags::UPCMode) != selectUPCVertices) { continue; } @@ -393,7 +398,7 @@ GPUg() void __launch_bounds__(256, 1) computeLayerTrackletsMultiROFKernel( const float zAtRmax{tanLambda * (maxR - currentCluster.radius) + currentCluster.zCoordinate}; const float sqInverseDeltaZ0{1.f / (math_utils::Sq(currentCluster.zCoordinate - primaryVertex.getZ()) + constants::Tolerance)}; /// protecting from overflows adding the detector resolution const float sigmaZ{o2::gpu::CAMath::Sqrt(math_utils::Sq(resolution) * math_utils::Sq(tanLambda) * ((math_utils::Sq(inverseR0) + sqInverseDeltaZ0) * math_utils::Sq(meanDeltaR) + 1.f) + math_utils::Sq(meanDeltaR * MSAngle))}; - const int4 selectedBinsRect{o2::its::getBinsRect(currentCluster, layerIndex + 1, zAtRmin, zAtRmax, sigmaZ * NSigmaCut, phiCut, *utils)}; + const int4 selectedBinsRect{o2::its::getBinsRect(currentCluster, toLayer, zAtRmin, zAtRmax, sigmaZ * NSigmaCut, phiCut, *utils)}; if (selectedBinsRect.x < 0) { continue; } @@ -404,14 +409,14 @@ GPUg() void __launch_bounds__(256, 1) computeLayerTrackletsMultiROFKernel( } for (short targetROF = rofOverlap.getFirstEntry(); targetROF < rofOverlap.getEntriesBound(); ++targetROF) { - if (!rofMask.isROFEnabled(layerIndex + 1, targetROF)) { + if (!rofMask.isROFEnabled(toLayer, targetROF)) { continue; } - auto clustersNextLayer = getClustersOnLayer(targetROF, totalROFs1, layerIndex + 1, ROFClusters, clusters); + auto clustersNextLayer = getClustersOnLayer(targetROF, totalROFs1, toLayer, ROFClusters, clusters); if (clustersNextLayer.empty()) { continue; } - const auto ts = rofOverlaps.getTimeStamp(layerIndex, pivotROF, layerIndex + 1, targetROF); + const auto ts = rofOverlaps.getTimeStamp(fromLayer, pivotROF, toLayer, targetROF); if (!ts.isCompatible(primaryVertex.getTimeStamp())) { continue; } @@ -419,26 +424,26 @@ GPUg() void __launch_bounds__(256, 1) computeLayerTrackletsMultiROFKernel( 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][(targetROF)*tableSize + firstBinIndex]; - const int maxRowClusterIndex = indexTables[layerIndex + 1][(targetROF)*tableSize + maxBinIndex]; + const int firstRowClusterIndex = indexTables[toLayer][(targetROF)*tableSize + firstBinIndex]; + const int maxRowClusterIndex = indexTables[toLayer][(targetROF)*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]) { + if (usedClusters[toLayer][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)}; if (deltaZ / sigmaZ < NSigmaCut && (deltaPhi < phiCut || o2::gpu::CAMath::Abs(deltaPhi - o2::constants::math::TwoPI) < phiCut)) { if constexpr (initRun) { - trackletsLUT[layerIndex][currentSortedIndex]++; // we need l0 as well for usual exclusive sums. + trackletsLUT[transitionId][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)}; - const int nextSortedIndex{ROFClusters[layerIndex + 1][targetROF] + nextClusterIndex}; - new (tracklets[layerIndex] + trackletsLUT[layerIndex][currentSortedIndex] + storedTracklets) Tracklet{currentSortedIndex, nextSortedIndex, tanL, phi, ts}; + const int nextSortedIndex{ROFClusters[toLayer][targetROF] + nextClusterIndex}; + new (tracklets[transitionId] + trackletsLUT[transitionId][currentSortedIndex] + storedTracklets) Tracklet{currentSortedIndex, nextSortedIndex, tanL, phi, ts}; } ++storedTracklets; } @@ -462,18 +467,20 @@ GPUg() void __launch_bounds__(256, 1) compileTrackletsLookupTableKernel( template GPUg() void __launch_bounds__(256, 1) processNeighboursKernel( - const int layer, + const int defaultCellTopologyId, const int level, CellSeed** allCellSeeds, CurrentSeed* currentCellSeeds, const int* currentCellIds, + const int* currentCellTopologyIds, const unsigned int nCurrentCells, TrackSeed* updatedCellSeeds, int* updatedCellsIds, + int* updatedCellTopologyIds, 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, + CellNeighbour** neighbours, + int** neighboursLUT, const TrackingFrameInfo** foundTrackingFrameInfo, const float* layerxX0, const float bz, @@ -489,22 +496,33 @@ GPUg() void __launch_bounds__(256, 1) processNeighboursKernel( } int foundSeeds{0}; const auto& currentCell{currentCellSeeds[iCurrentCell]}; + const int cellTopologyId = currentCellTopologyIds == nullptr ? defaultCellTopologyId : currentCellTopologyIds[iCurrentCell]; if (currentCell.getLevel() != level) { continue; } - if (currentCellIds == nullptr && (usedClusters[layer][currentCell.getFirstClusterIndex()] || - usedClusters[layer + 1][currentCell.getSecondClusterIndex()] || - usedClusters[layer + 2][currentCell.getThirdClusterIndex()])) { - continue; + if (currentCellIds == nullptr) { + bool used = false; + for (int layer = 0; layer < NLayers; ++layer) { + const int clusterIndex = currentCell.getCluster(layer); + used |= clusterIndex != constants::UnusedIndex && usedClusters[layer][clusterIndex]; + } + if (used) { + continue; + } } const int cellId = currentCellIds == nullptr ? iCurrentCell : currentCellIds[iCurrentCell]; + if (cellTopologyId < 0 || neighboursLUT[cellTopologyId] == nullptr || neighbours[cellTopologyId] == nullptr) { + continue; + } - const int startNeighbourId{cellId ? neighboursLUT[cellId - 1] : 0}; - const int endNeighbourId{neighboursLUT[cellId]}; + const int startNeighbourId{neighboursLUT[cellTopologyId][cellId]}; + const int endNeighbourId{neighboursLUT[cellTopologyId][cellId + 1]}; for (int iNeighbourCell{startNeighbourId}; iNeighbourCell < endNeighbourId; ++iNeighbourCell) { - const int neighbourCellId = neighbours[iNeighbourCell]; - const auto& neighbourCell = allCellSeeds[layer - 1][neighbourCellId]; + const auto& neighbourRef = neighbours[cellTopologyId][iNeighbourCell]; + const int neighbourCellTopologyId = neighbourRef.cellTopology; + const int neighbourCellId = neighbourRef.cell; + const auto& neighbourCell = allCellSeeds[neighbourCellTopologyId][neighbourCellId]; if (neighbourCell.getSecondTrackletIndex() != currentCell.getFirstTrackletIndex()) { continue; @@ -515,11 +533,13 @@ GPUg() void __launch_bounds__(256, 1) processNeighboursKernel( if (currentCell.getLevel() - 1 != neighbourCell.getLevel()) { continue; } - if (usedClusters[layer - 1][neighbourCell.getFirstClusterIndex()]) { + const int neighbourLayer = neighbourCell.getInnerLayer(); + const int neighbourCluster = neighbourCell.getFirstClusterIndex(); + if (usedClusters[neighbourLayer][neighbourCluster]) { continue; } TrackSeed seed{currentCell}; - auto& trHit = foundTrackingFrameInfo[layer - 1][neighbourCell.getFirstClusterIndex()]; + auto& trHit = foundTrackingFrameInfo[neighbourLayer][neighbourCluster]; if (!seed.rotate(trHit.alphaTrackingFrame)) { continue; @@ -530,7 +550,7 @@ GPUg() void __launch_bounds__(256, 1) processNeighboursKernel( } if (matCorrType == o2::base::PropagatorF::MatCorrType::USEMatCorrNONE) { - if (!seed.correctForMaterial(layerxX0[layer - 1], layerxX0[layer - 1] * constants::Radl * constants::Rho, true)) { + if (!seed.correctForMaterial(layerxX0[neighbourLayer], layerxX0[neighbourLayer] * constants::Radl * constants::Rho, true)) { continue; } } @@ -546,11 +566,15 @@ GPUg() void __launch_bounds__(256, 1) processNeighboursKernel( if constexpr (dryRun) { foundSeedsTable[iCurrentCell]++; } else { - seed.getClusters()[layer - 1] = neighbourCell.getFirstClusterIndex(); + seed.getClusters()[neighbourLayer] = neighbourCluster; + auto mask = seed.getHitLayerMask(); + mask.set(neighbourLayer); + seed.setHitLayerMask(mask); seed.setLevel(neighbourCell.getLevel()); seed.setFirstTrackletIndex(neighbourCell.getFirstTrackletIndex()); seed.setSecondTrackletIndex(neighbourCell.getSecondTrackletIndex()); updatedCellsIds[foundSeedsTable[iCurrentCell] + foundSeeds] = neighbourCellId; + updatedCellTopologyIds[foundSeedsTable[iCurrentCell] + foundSeeds] = neighbourCellTopologyId; updatedCellSeeds[foundSeedsTable[iCurrentCell] + foundSeeds] = seed; } foundSeeds++; @@ -563,7 +587,9 @@ GPUg() void __launch_bounds__(256, 1) processNeighboursKernel( template void countTrackletsInROFsHandler(const IndexTableUtils* utils, const typename ROFMaskTable::View& rofMask, - const int layer, + const int transitionId, + const int fromLayer, + const int toLayer, const typename ROFOverlapTable::View& rofOverlaps, const typename ROFVertexLookupTable::View& vertexLUT, const int vertexId, @@ -576,22 +602,24 @@ void countTrackletsInROFsHandler(const IndexTableUtils* utils, const int** clustersIndexTables, int** trackletsLUTs, gsl::span trackletsLUTsHost, - const int iteration, + const bool selectUPCVertices, const float NSigmaCut, - bounded_vector& phiCuts, + const typename TrackingTopology::View topology, + bounded_vector& transitionPhiCuts, const float resolutionPV, std::array& minRs, std::array& maxRs, bounded_vector& resolutions, std::vector& radii, - bounded_vector& mulScatAng, + bounded_vector& transitionMSAngles, o2::its::ExternalAllocator* alloc, gpu::Streams& streams) { - gpu::computeLayerTrackletsMultiROFKernel<<<60, 256, 0, streams[layer].get()>>>( + gpu::computeLayerTrackletsMultiROFKernel<<<60, 256, 0, streams[transitionId].get()>>>( utils, rofMask, - layer, + transitionId, + topology, rofOverlaps, vertexLUT, vertices, @@ -603,23 +631,25 @@ void countTrackletsInROFsHandler(const IndexTableUtils* utils, clustersIndexTables, nullptr, trackletsLUTs, - iteration, + selectUPCVertices, NSigmaCut, - phiCuts[layer], + transitionPhiCuts[transitionId], resolutionPV, - minRs[layer + 1], - maxRs[layer + 1], - resolutions[layer], - radii[layer + 1] - radii[layer], - mulScatAng[layer]); - auto nosync_policy = THRUST_NAMESPACE::par_nosync(gpu::TypedAllocator(alloc)).on(streams[layer].get()); - thrust::exclusive_scan(nosync_policy, trackletsLUTsHost[layer], trackletsLUTsHost[layer] + nClusters[layer] + 1, trackletsLUTsHost[layer]); + minRs[toLayer], + maxRs[toLayer], + resolutions[fromLayer], + radii[toLayer] - radii[fromLayer], + transitionMSAngles[transitionId]); + auto nosync_policy = THRUST_NAMESPACE::par_nosync(gpu::TypedAllocator(alloc)).on(streams[transitionId].get()); + thrust::exclusive_scan(nosync_policy, trackletsLUTsHost[transitionId], trackletsLUTsHost[transitionId] + nClusters[fromLayer] + 1, trackletsLUTsHost[transitionId]); } template void computeTrackletsInROFsHandler(const IndexTableUtils* utils, const typename ROFMaskTable::View& rofMask, - const int layer, + const int transitionId, + const int fromLayer, + const int toLayer, const typename ROFOverlapTable::View& rofOverlaps, const typename ROFVertexLookupTable::View& vertexLUT, const int vertexId, @@ -635,22 +665,24 @@ void computeTrackletsInROFsHandler(const IndexTableUtils* utils, gsl::span nTracklets, int** trackletsLUTs, gsl::span trackletsLUTsHost, - const int iteration, + const bool selectUPCVertices, const float NSigmaCut, - bounded_vector& phiCuts, + const typename TrackingTopology::View topology, + bounded_vector& transitionPhiCuts, const float resolutionPV, std::array& minRs, std::array& maxRs, bounded_vector& resolutions, std::vector& radii, - bounded_vector& mulScatAng, + bounded_vector& transitionMSAngles, o2::its::ExternalAllocator* alloc, gpu::Streams& streams) { - gpu::computeLayerTrackletsMultiROFKernel<<<60, 256, 0, streams[layer].get()>>>( + gpu::computeLayerTrackletsMultiROFKernel<<<60, 256, 0, streams[transitionId].get()>>>( utils, rofMask, - layer, + transitionId, + topology, rofOverlaps, vertexLUT, vertices, @@ -662,27 +694,27 @@ void computeTrackletsInROFsHandler(const IndexTableUtils* utils, clustersIndexTables, tracklets, trackletsLUTs, - iteration, + selectUPCVertices, NSigmaCut, - phiCuts[layer], + transitionPhiCuts[transitionId], resolutionPV, - minRs[layer + 1], - maxRs[layer + 1], - resolutions[layer], - radii[layer + 1] - radii[layer], - mulScatAng[layer]); - thrust::device_ptr tracklets_ptr(spanTracklets[layer]); - auto nosync_policy = THRUST_NAMESPACE::par_nosync(gpu::TypedAllocator(alloc)).on(streams[layer].get()); - thrust::sort(nosync_policy, tracklets_ptr, tracklets_ptr + nTracklets[layer]); - auto unique_end = thrust::unique(nosync_policy, tracklets_ptr, tracklets_ptr + nTracklets[layer]); - nTracklets[layer] = unique_end - tracklets_ptr; - if (layer) { - GPUChkErrS(cudaMemsetAsync(trackletsLUTsHost[layer], 0, (nClusters[layer] + 1) * sizeof(int), streams[layer].get())); - gpu::compileTrackletsLookupTableKernel<<<60, 256, 0, streams[layer].get()>>>( - spanTracklets[layer], - trackletsLUTsHost[layer], - nTracklets[layer]); - thrust::exclusive_scan(nosync_policy, trackletsLUTsHost[layer], trackletsLUTsHost[layer] + nClusters[layer] + 1, trackletsLUTsHost[layer]); + minRs[toLayer], + maxRs[toLayer], + resolutions[fromLayer], + radii[toLayer] - radii[fromLayer], + transitionMSAngles[transitionId]); + thrust::device_ptr tracklets_ptr(spanTracklets[transitionId]); + auto nosync_policy = THRUST_NAMESPACE::par_nosync(gpu::TypedAllocator(alloc)).on(streams[transitionId].get()); + thrust::sort(nosync_policy, tracklets_ptr, tracklets_ptr + nTracklets[transitionId]); + auto unique_end = thrust::unique(nosync_policy, tracklets_ptr, tracklets_ptr + nTracklets[transitionId]); + nTracklets[transitionId] = unique_end - tracklets_ptr; + if (fromLayer > 0) { + GPUChkErrS(cudaMemsetAsync(trackletsLUTsHost[transitionId], 0, (nClusters[fromLayer] + 1) * sizeof(int), streams[transitionId].get())); + gpu::compileTrackletsLookupTableKernel<<<60, 256, 0, streams[transitionId].get()>>>( + spanTracklets[transitionId], + trackletsLUTsHost[transitionId], + nTracklets[transitionId]); + thrust::exclusive_scan(nosync_policy, trackletsLUTsHost[transitionId], trackletsLUTsHost[transitionId] + nClusters[fromLayer] + 1, trackletsLUTsHost[transitionId]); } } @@ -694,7 +726,8 @@ void countCellsHandler( Tracklet** tracklets, int** trackletsLUT, const int nTracklets, - const int layer, + const int cellTopologyId, + const typename TrackingTopology::View topology, CellSeed* cells, int** cellsLUTsArrayDevice, int* cellsLUTsHost, @@ -707,14 +740,15 @@ void countCellsHandler( gpu::Streams& streams) { thrust::device_vector layerxX0(layerxX0Host); - gpu::computeLayerCellsKernel<<<60, 256, 0, streams[layer].get()>>>( - sortedClusters, // const Cluster** - unsortedClusters, // const Cluster** - tfInfo, // const TrackingFrameInfo** - tracklets, // const Tracklets** - trackletsLUT, // const int** - nTracklets, // const int - layer, // const int + gpu::computeLayerCellsKernel<<<60, 256, 0, streams[cellTopologyId].get()>>>( + sortedClusters, // const Cluster** + unsortedClusters, // const Cluster** + tfInfo, // const TrackingFrameInfo** + tracklets, // const Tracklets** + trackletsLUT, // const int** + nTracklets, // const int + cellTopologyId, // const int + topology, cells, // CellSeed* cellsLUTsArrayDevice, // int** thrust::raw_pointer_cast(&layerxX0[0]), @@ -722,7 +756,7 @@ void countCellsHandler( maxChi2ClusterAttachment, // const float cellDeltaTanLambdaSigma, // const float nSigmaCut); // const float - auto nosync_policy = THRUST_NAMESPACE::par_nosync(gpu::TypedAllocator(alloc)).on(streams[layer].get()); + auto nosync_policy = THRUST_NAMESPACE::par_nosync(gpu::TypedAllocator(alloc)).on(streams[cellTopologyId].get()); thrust::exclusive_scan(nosync_policy, cellsLUTsHost, cellsLUTsHost + nTracklets + 1, cellsLUTsHost); } @@ -734,7 +768,8 @@ void computeCellsHandler( Tracklet** tracklets, int** trackletsLUT, const int nTracklets, - const int layer, + const int cellTopologyId, + const typename TrackingTopology::View topology, CellSeed* cells, int** cellsLUTsArrayDevice, int* cellsLUTsHost, @@ -746,14 +781,15 @@ void computeCellsHandler( gpu::Streams& streams) { thrust::device_vector layerxX0(layerxX0Host); - gpu::computeLayerCellsKernel<<<60, 256, 0, streams[layer].get()>>>( - sortedClusters, // const Cluster** - unsortedClusters, // const Cluster** - tfInfo, // const TrackingFrameInfo** - tracklets, // const Tracklets** - trackletsLUT, // const int** - nTracklets, // const int - layer, // const int + gpu::computeLayerCellsKernel<<<60, 256, 0, streams[cellTopologyId].get()>>>( + sortedClusters, // const Cluster** + unsortedClusters, // const Cluster** + tfInfo, // const TrackingFrameInfo** + tracklets, // const Tracklets** + trackletsLUT, // const int** + nTracklets, // const int + cellTopologyId, // const int + topology, cells, // CellSeed* cellsLUTsArrayDevice, // int** thrust::raw_pointer_cast(&layerxX0[0]), @@ -765,64 +801,60 @@ void computeCellsHandler( template void countCellNeighboursHandler(CellSeed** cellsLayersDevice, - int* neighboursLUT, + int* neighboursCursor, int** cellsLUTs, - gpuPair* cellNeighbours, - int* neighboursIndexTable, - const Tracklet** tracklets, + const int sourceCellTopologyId, + const int targetCellTopologyId, const float maxChi2ClusterAttachment, const float bz, - const int layerIndex, const unsigned int nCells, - const unsigned int nCellsNext, - const int maxCellNeighbours, - o2::its::ExternalAllocator* alloc, gpu::Stream& stream) { gpu::computeLayerCellNeighboursKernel<<<60, 256, 0, stream.get()>>>( cellsLayersDevice, - neighboursLUT, - neighboursIndexTable, + neighboursCursor, cellsLUTs, - cellNeighbours, - tracklets, + nullptr, + sourceCellTopologyId, + targetCellTopologyId, maxChi2ClusterAttachment, bz, - layerIndex, - nCells, - maxCellNeighbours); + nCells); +} + +void scanCellNeighboursHandler(int* neighboursCursor, + int* neighboursLUT, + const unsigned int nCells, + o2::its::ExternalAllocator* alloc, + gpu::Stream& stream) +{ auto nosync_policy = THRUST_NAMESPACE::par_nosync(gpu::TypedAllocator(alloc)).on(stream.get()); - thrust::inclusive_scan(nosync_policy, neighboursLUT, neighboursLUT + nCellsNext, neighboursLUT); - thrust::exclusive_scan(nosync_policy, neighboursIndexTable, neighboursIndexTable + nCells + 1, neighboursIndexTable); + thrust::exclusive_scan(nosync_policy, neighboursCursor, neighboursCursor + nCells + 1, neighboursCursor); + GPUChkErrS(cudaMemcpyAsync(neighboursLUT, neighboursCursor, (nCells + 1) * sizeof(int), cudaMemcpyDeviceToDevice, stream.get())); } template void computeCellNeighboursHandler(CellSeed** cellsLayersDevice, - int* neighboursLUT, + int* neighboursCursor, int** cellsLUTs, - gpuPair* cellNeighbours, - int* neighboursIndexTable, - const Tracklet** tracklets, + CellNeighbour* cellNeighbours, + const int sourceCellTopologyId, + const int targetCellTopologyId, const float maxChi2ClusterAttachment, const float bz, - const int layerIndex, const unsigned int nCells, - const unsigned int nCellsNext, - const int maxCellNeighbours, gpu::Stream& stream) { gpu::computeLayerCellNeighboursKernel<<<60, 256, 0, stream.get()>>>( cellsLayersDevice, - neighboursLUT, - neighboursIndexTable, + neighboursCursor, cellsLUTs, cellNeighbours, - tracklets, + sourceCellTopologyId, + targetCellTopologyId, maxChi2ClusterAttachment, bz, - layerIndex, - nCells, - maxCellNeighbours); + nCells); } int filterCellNeighboursHandler(gpuPair* cellNeighbourPairs, @@ -842,19 +874,24 @@ int filterCellNeighboursHandler(gpuPair* cellNeighbourPairs, } template -void processNeighboursHandler(const int startLayer, - const int startLevel, +void processNeighboursHandler(const int startLevel, + const int defaultCellTopologyId, CellSeed** allCellSeeds, CellSeed* currentCellSeeds, - std::array& nCells, + const int* currentCellTopologyIds, + const int* currentCellIds, + const int* nCells, const unsigned char** usedClusters, - std::array& neighbours, - gsl::span neighboursDeviceLUTs, + CellNeighbour** neighbours, + int** neighboursDeviceLUTs, const TrackingFrameInfo** foundTrackingFrameInfo, bounded_vector>& seedsHost, const float bz, const float maxChi2ClusterAttachment, const float maxChi2NDF, + const int maxHoles, + const int minTrackLength, + const LayerMask holeLayerMask, const std::vector& layerxX0Host, const o2::base::Propagator* propagator, const o2::base::PropagatorF::MatCorrType matCorrType, @@ -865,22 +902,24 @@ void processNeighboursHandler(const int startLayer, auto allocInt = gpu::TypedAllocator(alloc); auto allocTrackSeed = gpu::TypedAllocator>(alloc); thrust::device_vector layerxX0(layerxX0Host); - thrust::device_vector> foundSeedsTable(nCells[startLayer] + 1, 0, allocInt); + thrust::device_vector> foundSeedsTable(nCells[defaultCellTopologyId] + 1, 0, allocInt); auto nosync_policy = THRUST_NAMESPACE::par_nosync(gpu::TypedAllocator(alloc)).on(gpu::Stream::DefaultStream); gpu::processNeighboursKernel<<<60, 256>>>( - startLayer, + defaultCellTopologyId, startLevel, allCellSeeds, currentCellSeeds, nullptr, - nCells[startLayer], + nullptr, + nCells[defaultCellTopologyId], + nullptr, nullptr, nullptr, thrust::raw_pointer_cast(&foundSeedsTable[0]), usedClusters, - neighbours[startLayer - 1], - neighboursDeviceLUTs[startLayer - 1], + neighbours, + neighboursDeviceLUTs, foundTrackingFrameInfo, thrust::raw_pointer_cast(&layerxX0[0]), bz, @@ -890,20 +929,23 @@ void processNeighboursHandler(const int startLayer, thrust::exclusive_scan(nosync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), foundSeedsTable.begin()); thrust::device_vector> updatedCellId(foundSeedsTable.back(), 0, allocInt); + thrust::device_vector> updatedCellTopologyId(foundSeedsTable.back(), 0, allocInt); thrust::device_vector, gpu::TypedAllocator>> updatedCellSeed(foundSeedsTable.back(), allocTrackSeed); gpu::processNeighboursKernel<<<60, 256>>>( - startLayer, + defaultCellTopologyId, startLevel, allCellSeeds, currentCellSeeds, nullptr, - nCells[startLayer], + nullptr, + nCells[defaultCellTopologyId], thrust::raw_pointer_cast(&updatedCellSeed[0]), thrust::raw_pointer_cast(&updatedCellId[0]), + thrust::raw_pointer_cast(&updatedCellTopologyId[0]), thrust::raw_pointer_cast(&foundSeedsTable[0]), usedClusters, - neighbours[startLayer - 1], - neighboursDeviceLUTs[startLayer - 1], + neighbours, + neighboursDeviceLUTs, foundTrackingFrameInfo, thrust::raw_pointer_cast(&layerxX0[0]), bz, @@ -914,29 +956,35 @@ void processNeighboursHandler(const int startLayer, int level = startLevel; thrust::device_vector> lastCellId(allocInt); + thrust::device_vector> lastCellTopologyId(allocInt); thrust::device_vector, gpu::TypedAllocator>> lastCellSeed(allocTrackSeed); - for (int iLayer{startLayer - 1}; iLayer > 0 && level > 2; --iLayer) { + while (level > 2 && !updatedCellSeed.empty()) { lastCellSeed.swap(updatedCellSeed); lastCellId.swap(updatedCellId); + lastCellTopologyId.swap(updatedCellTopologyId); thrust::device_vector, gpu::TypedAllocator>>(allocTrackSeed).swap(updatedCellSeed); thrust::device_vector>(allocInt).swap(updatedCellId); + thrust::device_vector>(allocInt).swap(updatedCellTopologyId); auto lastCellSeedSize{lastCellSeed.size()}; foundSeedsTable.resize(lastCellSeedSize + 1); thrust::fill(nosync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), 0); + --level; gpu::processNeighboursKernel><<<60, 256>>>( - iLayer, - --level, + constants::UnusedIndex, + level, allCellSeeds, thrust::raw_pointer_cast(&lastCellSeed[0]), thrust::raw_pointer_cast(&lastCellId[0]), + thrust::raw_pointer_cast(&lastCellTopologyId[0]), lastCellSeedSize, nullptr, nullptr, + nullptr, thrust::raw_pointer_cast(&foundSeedsTable[0]), usedClusters, - neighbours[iLayer - 1], - neighboursDeviceLUTs[iLayer - 1], + neighbours, + neighboursDeviceLUTs, foundTrackingFrameInfo, thrust::raw_pointer_cast(&layerxX0[0]), bz, @@ -948,22 +996,26 @@ void processNeighboursHandler(const int startLayer, auto foundSeeds{foundSeedsTable.back()}; updatedCellId.resize(foundSeeds); thrust::fill(nosync_policy, updatedCellId.begin(), updatedCellId.end(), 0); + updatedCellTopologyId.resize(foundSeeds); + thrust::fill(nosync_policy, updatedCellTopologyId.begin(), updatedCellTopologyId.end(), 0); updatedCellSeed.resize(foundSeeds); thrust::fill(nosync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), TrackSeed()); gpu::processNeighboursKernel><<<60, 256>>>( - iLayer, + constants::UnusedIndex, level, allCellSeeds, thrust::raw_pointer_cast(&lastCellSeed[0]), thrust::raw_pointer_cast(&lastCellId[0]), + thrust::raw_pointer_cast(&lastCellTopologyId[0]), lastCellSeedSize, thrust::raw_pointer_cast(&updatedCellSeed[0]), thrust::raw_pointer_cast(&updatedCellId[0]), + thrust::raw_pointer_cast(&updatedCellTopologyId[0]), thrust::raw_pointer_cast(&foundSeedsTable[0]), usedClusters, - neighbours[iLayer - 1], - neighboursDeviceLUTs[iLayer - 1], + neighbours, + neighboursDeviceLUTs, foundTrackingFrameInfo, thrust::raw_pointer_cast(&layerxX0[0]), bz, @@ -973,7 +1025,7 @@ void processNeighboursHandler(const int startLayer, } GPUChkErrS(cudaStreamSynchronize(gpu::Stream::DefaultStream)); thrust::device_vector, gpu::TypedAllocator>> outSeeds(updatedCellSeed.size(), allocTrackSeed); - auto end = thrust::copy_if(nosync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), outSeeds.begin(), gpu::seed_selector(1.e3, maxChi2NDF * ((startLevel + 2) * 2 - 5))); + auto end = thrust::copy_if(nosync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), outSeeds.begin(), gpu::seed_selector(1.e3, maxChi2NDF * ((startLevel + 2) * 2 - 5), maxHoles, minTrackLength, holeLayerMask)); auto s{end - outSeeds.begin()}; seedsHost.reserve(seedsHost.size() + s); thrust::copy(outSeeds.begin(), outSeeds.begin() + s, std::back_inserter(seedsHost)); @@ -1081,7 +1133,9 @@ void computeTrackSeedHandler(TrackSeed* trackSeeds, /// Explicit instantiation of ITS2 handlers template void countTrackletsInROFsHandler<7>(const IndexTableUtils<7>* utils, const ROFMaskTable<7>::View& rofMask, - const int layer, + const int transitionId, + const int fromLayer, + const int toLayer, const ROFOverlapTable<7>::View& rofOverlaps, const ROFVertexLookupTable<7>::View& vertexLUT, const int vertexId, @@ -1094,21 +1148,24 @@ template void countTrackletsInROFsHandler<7>(const IndexTableUtils<7>* utils, const int** clustersIndexTables, int** trackletsLUTs, gsl::span trackletsLUTsHost, - const int iteration, + const bool selectUPCVertices, const float NSigmaCut, - bounded_vector& phiCuts, + const TrackingTopology<7>::View topology, + bounded_vector& transitionPhiCuts, const float resolutionPV, std::array& minRs, std::array& maxRs, bounded_vector& resolutions, std::vector& radii, - bounded_vector& mulScatAng, + bounded_vector& transitionMSAngles, o2::its::ExternalAllocator* alloc, gpu::Streams& streams); template void computeTrackletsInROFsHandler<7>(const IndexTableUtils<7>* utils, const ROFMaskTable<7>::View& rofMask, - const int layer, + const int transitionId, + const int fromLayer, + const int toLayer, const ROFOverlapTable<7>::View& rofOverlaps, const ROFVertexLookupTable<7>::View& vertexLUT, const int vertexId, @@ -1124,15 +1181,16 @@ template void computeTrackletsInROFsHandler<7>(const IndexTableUtils<7>* utils, gsl::span nTracklets, int** trackletsLUTs, gsl::span trackletsLUTsHost, - const int iteration, + const bool selectUPCVertices, const float NSigmaCut, - bounded_vector& phiCuts, + const TrackingTopology<7>::View topology, + bounded_vector& transitionPhiCuts, const float resolutionPV, std::array& minRs, std::array& maxRs, bounded_vector& resolutions, std::vector& radii, - bounded_vector& mulScatAng, + bounded_vector& transitionMSAngles, o2::its::ExternalAllocator* alloc, gpu::Streams& streams); @@ -1142,7 +1200,8 @@ template void countCellsHandler<7>(const Cluster** sortedClusters, Tracklet** tracklets, int** trackletsLUT, const int nTracklets, - const int layer, + const int cellTopologyId, + const TrackingTopology<7>::View topology, CellSeed* cells, int** cellsLUTsArrayDevice, int* cellsLUTsHost, @@ -1160,7 +1219,8 @@ template void computeCellsHandler<7>(const Cluster** sortedClusters, Tracklet** tracklets, int** trackletsLUT, const int nTracklets, - const int layer, + const int cellTopologyId, + const TrackingTopology<7>::View topology, CellSeed* cells, int** cellsLUTsArrayDevice, int* cellsLUTsHost, @@ -1172,47 +1232,44 @@ template void computeCellsHandler<7>(const Cluster** sortedClusters, gpu::Streams& streams); template void countCellNeighboursHandler<7>(CellSeed** cellsLayersDevice, - int* neighboursLUT, + int* neighboursCursor, int** cellsLUTs, - gpuPair* cellNeighbours, - int* neighboursIndexTable, - const Tracklet** tracklets, + const int sourceCellTopologyId, + const int targetCellTopologyId, const float maxChi2ClusterAttachment, const float bz, - const int layerIndex, const unsigned int nCells, - const unsigned int nCellsNext, - const int maxCellNeighbours, - o2::its::ExternalAllocator* alloc, gpu::Stream& stream); template void computeCellNeighboursHandler<7>(CellSeed** cellsLayersDevice, - int* neighboursLUT, + int* neighboursCursor, int** cellsLUTs, - gpuPair* cellNeighbours, - int* neighboursIndexTable, - const Tracklet** tracklets, + CellNeighbour* cellNeighbours, + const int sourceCellTopologyId, + const int targetCellTopologyId, const float maxChi2ClusterAttachment, const float bz, - const int layerIndex, const unsigned int nCells, - const unsigned int nCellsNext, - const int maxCellNeighbours, gpu::Stream& stream); -template void processNeighboursHandler<7>(const int startLayer, - const int startLevel, +template void processNeighboursHandler<7>(const int startLevel, + const int defaultCellTopologyId, CellSeed** allCellSeeds, CellSeed* currentCellSeeds, - std::array& nCells, + const int* currentCellTopologyIds, + const int* currentCellIds, + const int* nCells, const unsigned char** usedClusters, - std::array& neighbours, - gsl::span neighboursDeviceLUTs, + CellNeighbour** neighbours, + int** neighboursDeviceLUTs, const TrackingFrameInfo** foundTrackingFrameInfo, bounded_vector>& seedsHost, const float bz, const float maxChi2ClusterAttachment, const float maxChi2NDF, + const int maxHoles, + const int minTrackLength, + const LayerMask holeLayerMask, const std::vector& layerxX0Host, const o2::base::Propagator* propagator, const o2::base::PropagatorF::MatCorrType matCorrType, @@ -1258,4 +1315,190 @@ template void computeTrackSeedHandler(TrackSeed<7>* trackSeeds, const o2::base::PropagatorF::MatCorrType matCorrType, o2::its::ExternalAllocator* alloc); +/// Explicit instantiation of ALICE3 handlers +#ifdef ENABLE_UPGRADES +template void countTrackletsInROFsHandler<11>(const IndexTableUtils<11>* utils, + const ROFMaskTable<11>::View& rofMask, + const int transitionId, + const int fromLayer, + const int toLayer, + const ROFOverlapTable<11>::View& rofOverlaps, + const ROFVertexLookupTable<11>::View& vertexLUT, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const bool selectUPCVertices, + const float NSigmaCut, + const TrackingTopology<11>::View topology, + bounded_vector& transitionPhiCuts, + const float resolutionPV, + std::array& minRs, + std::array& maxRs, + bounded_vector& resolutions, + std::vector& radii, + bounded_vector& transitionMSAngles, + o2::its::ExternalAllocator* alloc, + gpu::Streams& streams); + +template void computeTrackletsInROFsHandler<11>(const IndexTableUtils<11>* utils, + const ROFMaskTable<11>::View& rofMask, + const int transitionId, + const int fromLayer, + const int toLayer, + const ROFOverlapTable<11>::View& rofOverlaps, + const ROFVertexLookupTable<11>::View& vertexLUT, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + Tracklet** tracklets, + gsl::span spanTracklets, + gsl::span nTracklets, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const bool selectUPCVertices, + const float NSigmaCut, + const TrackingTopology<11>::View topology, + bounded_vector& transitionPhiCuts, + const float resolutionPV, + std::array& minRs, + std::array& maxRs, + bounded_vector& resolutions, + std::vector& radii, + bounded_vector& transitionMSAngles, + o2::its::ExternalAllocator* alloc, + gpu::Streams& streams); + +template void countCellsHandler<11>(const Cluster** sortedClusters, + const Cluster** unsortedClusters, + const TrackingFrameInfo** tfInfo, + Tracklet** tracklets, + int** trackletsLUT, + const int nTracklets, + const int cellTopologyId, + const TrackingTopology<11>::View topology, + CellSeed* cells, + int** cellsLUTsArrayDevice, + int* cellsLUTsHost, + const float bz, + const float maxChi2ClusterAttachment, + const float cellDeltaTanLambdaSigma, + const float nSigmaCut, + const std::vector& layerxX0Host, + o2::its::ExternalAllocator* alloc, + gpu::Streams& streams); + +template void computeCellsHandler<11>(const Cluster** sortedClusters, + const Cluster** unsortedClusters, + const TrackingFrameInfo** tfInfo, + Tracklet** tracklets, + int** trackletsLUT, + const int nTracklets, + const int cellTopologyId, + const TrackingTopology<11>::View topology, + CellSeed* cells, + int** cellsLUTsArrayDevice, + int* cellsLUTsHost, + const float bz, + const float maxChi2ClusterAttachment, + const float cellDeltaTanLambdaSigma, + const float nSigmaCut, + const std::vector& layerxX0Host, + gpu::Streams& streams); + +template void countCellNeighboursHandler<11>(CellSeed** cellsLayersDevice, + int* neighboursCursor, + int** cellsLUTs, + const int sourceCellTopologyId, + const int targetCellTopologyId, + const float maxChi2ClusterAttachment, + const float bz, + const unsigned int nCells, + gpu::Stream& stream); + +template void computeCellNeighboursHandler<11>(CellSeed** cellsLayersDevice, + int* neighboursCursor, + int** cellsLUTs, + CellNeighbour* cellNeighbours, + const int sourceCellTopologyId, + const int targetCellTopologyId, + const float maxChi2ClusterAttachment, + const float bz, + const unsigned int nCells, + gpu::Stream& stream); + +template void processNeighboursHandler<11>(const int startLevel, + const int defaultCellTopologyId, + CellSeed** allCellSeeds, + CellSeed* currentCellSeeds, + const int* currentCellTopologyIds, + const int* currentCellIds, + const int* nCells, + const unsigned char** usedClusters, + CellNeighbour** neighbours, + int** neighboursDeviceLUTs, + const TrackingFrameInfo** foundTrackingFrameInfo, + bounded_vector>& seedsHost, + const float bz, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int maxHoles, + const int minTrackLength, + const LayerMask holeLayerMask, + const std::vector& layerxX0Host, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc); + +template void countTrackSeedHandler(TrackSeed<11>* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + int* seedLUT, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, + const std::vector& layerxX0Host, + const unsigned int nSeeds, + const float bz, + const int startLevel, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc); + +template void computeTrackSeedHandler(TrackSeed<11>* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + o2::its::TrackITSExt* tracks, + const int* seedLUT, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, + const std::vector& layerxX0Host, + const unsigned int nSeeds, + const unsigned int nTracks, + const float bz, + const int startLevel, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc); +#endif } // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h index c7718ee666311..4706977d08ba6 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h @@ -16,7 +16,10 @@ #ifndef TRACKINGITSU_INCLUDE_CACELL_H_ #define TRACKINGITSU_INCLUDE_CACELL_H_ +#include + #include "ITStracking/Constants.h" +#include "ITStracking/LayerMask.h" #include "DataFormatsITS/TimeEstBC.h" #include "ReconstructionDataFormats/Track.h" #include "GPUCommonDef.h" @@ -24,11 +27,21 @@ namespace o2::its { +struct CellNeighbour { + int cellTopology{-1}; + int cell{-1}; + int nextCellTopology{-1}; + int nextCell{-1}; + int level{-1}; +}; + template class SeedBase : public o2::track::TrackParCovF { public: - GPUhd() int getInnerLayer() const { return getUserField(); } + GPUhd() LayerMask getHitLayerMask() const { return LayerMask{static_cast(getUserField())}; } + GPUhd() void setHitLayerMask(LayerMask mask) { setUserField(mask.value()); } + GPUhd() int getInnerLayer() const { return getHitLayerMask().first(); } GPUhd() int getFirstTrackletIndex() const { return mTracklets[0]; }; GPUhd() void setFirstTrackletIndex(int trkl) { mTracklets[0] = trkl; }; GPUhd() int getSecondTrackletIndex() const { return mTracklets[1]; }; @@ -71,9 +84,13 @@ class CellSeed final : public SeedBase public: GPUhdDefault() CellSeed() = default; GPUhd() CellSeed(int innerL, int cl0, int cl1, int cl2, int trkl0, int trkl1, const o2::track::TrackParCovF& tpc, float chi2, const TimeEstBC& time) + : CellSeed(LayerMask(innerL, innerL + 1, innerL + 2), cl0, cl1, cl2, trkl0, trkl1, tpc, chi2, time) + { + } + GPUhd() CellSeed(LayerMask hitLayerMask, int cl0, int cl1, int cl2, int trkl0, int trkl1, const o2::track::TrackParCovF& tpc, float chi2, const TimeEstBC& time) : Base(tpc, chi2, 1, time) { - setUserField(innerL); + setHitLayerMask(hitLayerMask); auto& clusters = this->clustersRaw(); clusters[0] = cl0; clusters[1] = cl1; @@ -92,12 +109,12 @@ class CellSeed final : public SeedBase GPUhd() int getThirdClusterIndex() const { return this->clustersRaw()[2]; }; GPUhd() auto& getClusters() { return this->clustersRaw(); } GPUhd() const auto& getClusters() const { return this->clustersRaw(); } - /// getCluster takes an ABSOLUTE layer index and returns UnusedIndex if the - /// layer is outside the 3 stored slots (innerL, innerL+1, innerL+2). + /// getCluster takes an ABSOLUTE layer index. Compact cluster slots are + /// mapped to absolute layers by set-bit order in the hit-layer mask. GPUhd() int getCluster(int layer) const { - const int rel = layer - getInnerLayer(); - return (rel >= 0 && rel < constants::ClustersPerCell) ? this->clustersRaw()[rel] : constants::UnusedIndex; + const int slot = getHitLayerMask().slot(layer); + return (slot >= 0 && slot < constants::ClustersPerCell) ? this->clustersRaw()[slot] : constants::UnusedIndex; } }; @@ -114,14 +131,17 @@ class TrackSeed final : public SeedBase GPUhd() TrackSeed(const CellSeed& cs) : Base(static_cast(cs), cs.getChi2(), cs.getLevel(), cs.getTimeStamp()) { - this->setUserField(cs.getInnerLayer()); + this->setHitLayerMask(cs.getHitLayerMask()); this->setFirstTrackletIndex(cs.getFirstTrackletIndex()); this->setSecondTrackletIndex(cs.getSecondTrackletIndex()); - const int innerL = cs.getInnerLayer(); auto& clusters = this->clustersRaw(); - clusters[innerL + 0] = cs.getFirstClusterIndex(); - clusters[innerL + 1] = cs.getSecondClusterIndex(); - clusters[innerL + 2] = cs.getThirdClusterIndex(); + int slot = 0; + const auto hitMask = cs.getHitLayerMask(); + for (int layer = 0; layer < NLayers; ++layer) { + if (hitMask.has(layer)) { + clusters[layer] = cs.getClusters()[slot++]; + } + } } GPUhdDefault() TrackSeed(const TrackSeed&) = default; GPUhdDefault() ~TrackSeed() = default; @@ -129,14 +149,27 @@ class TrackSeed final : public SeedBase GPUhdDefault() TrackSeed& operator=(const TrackSeed&) = default; GPUhdDefault() TrackSeed& operator=(TrackSeed&&) = default; - /// Three-cluster view of the original cell — note: innerL (UserField) is not - /// updated when processNeighbours extends the cluster list leftward. - GPUhd() int getFirstClusterIndex() const { return this->clustersRaw()[this->getUserField()]; } - GPUhd() int getSecondClusterIndex() const { return this->clustersRaw()[this->getUserField() + 1]; } - GPUhd() int getThirdClusterIndex() const { return this->clustersRaw()[this->getUserField() + 2]; } + GPUhd() int getFirstClusterIndex() const { return getClusterBySlot(0); } + GPUhd() int getSecondClusterIndex() const { return getClusterBySlot(1); } + GPUhd() int getThirdClusterIndex() const { return getClusterBySlot(2); } GPUhd() auto& getClusters() { return this->clustersRaw(); } GPUhd() const auto& getClusters() const { return this->clustersRaw(); } GPUhd() int getCluster(int layer) const { return this->clustersRaw()[layer]; } + + private: + GPUhd() int getClusterBySlot(int requestedSlot) const + { + int slot = 0; + const auto hitMask = this->getHitLayerMask(); + for (int layer = 0; layer < NLayers; ++layer) { + if (hitMask.has(layer)) { + if (slot++ == requestedSlot) { + return this->clustersRaw()[layer]; + } + } + } + return constants::UnusedIndex; + } }; } // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h index dbce5e0dc08a7..852c5ecd24633 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h @@ -23,19 +23,38 @@ #include #endif +#include "CommonUtils/EnumFlags.h" #include "DetectorsBase/Propagator.h" #include "ITStracking/Constants.h" namespace o2::its { +// Steering of dedicated steps in an iteration +enum class IterationStep : uint8_t { + FirstPass = 0, + RebuildClusterLUT, + UseUPCMask, + SelectUPCVertices, + ResetVertices, + SkipROFsAboveThreshold, + MarkVerticesAsUPC, +}; +using IterationSteps = o2::utils::EnumFlags; + struct TrackingParameters { - int CellMinimumLevel() const noexcept { return MinTrackLength - constants::ClustersPerCell + 1; } + int CellMinimumLevel() const noexcept + { + const int minClusters = MinTrackLength - (MaxHoles > 0 ? MaxHoles : 0); + const int effectiveMinClusters = minClusters > constants::ClustersPerCell ? minClusters : constants::ClustersPerCell; + return effectiveMinClusters - constants::ClustersPerCell + 1; + } int NeighboursPerRoad() const noexcept { return NLayers - 3; } int CellsPerRoad() const noexcept { return NLayers - 2; } int TrackletsPerRoad() const noexcept { return NLayers - 1; } std::string asString() const; + IterationSteps PassFlags{IterationStep::FirstPass, IterationStep::RebuildClusterLUT}; int NLayers = 7; std::vector AddTimeError = {0, 0, 0, 0, 0, 0, 0}; std::vector LayerZ = {16.333f + 1, 16.333f + 1, 16.333f + 1, 42.140f + 1, 42.140f + 1, 73.745f + 1, 73.745f + 1}; @@ -51,9 +70,10 @@ struct TrackingParameters { float DiamondCov[6] = {25.e-6f, 0.f, 0.f, 25.e-6f, 0.f, 36.f}; /// General parameters - bool AllowSharingFirstCluster = false; int ClusterSharing = 0; int MinTrackLength = 7; + int MaxHoles = 0; + uint16_t HoleLayerMask = 0; float NSigmaCut = 5; float PVres = 1.e-2f; /// Trackleting cuts @@ -73,17 +93,22 @@ struct TrackingParameters { bool SaveTimeBenchmarks = false; bool DoUPCIteration = false; bool FataliseUponFailure = true; - - bool createArtefactLabels{false}; - + bool CreateArtefactLabels{false}; bool PrintMemory = false; // print allocator usage in epilog report size_t MaxMemory = std::numeric_limits::max(); bool DropTFUponFailure = false; + + // Selections on tracks sharing clusters + bool AllowSharingFirstCluster = false; + float SharedClusterMaxDeltaPhi = 0.05f; // For tracks sharing clusters, maximum allowed delta phi at the cluster position + float SharedClusterMaxDeltaEta = 0.03f; // For tracks sharing clusters, maximum allowed delta eta at the cluster position + bool SharedClusterOppositeSign = false; // For tracks sharing clusters, require opposite sign of the tracklets }; struct VertexingParameters { std::string asString() const; + IterationSteps PassFlags{IterationStep::FirstPass, IterationStep::ResetVertices}; std::vector LayerZ = {16.333f + 1, 16.333f + 1, 16.333f + 1, 42.140f + 1, 42.140f + 1, 73.745f + 1, 73.745f + 1}; std::vector LayerRadii = {2.33959f, 3.14076f, 3.91924f, 19.6213f, 24.5597f, 34.388f, 39.3329f}; int vertPerRofThreshold = 0; // Maximum number of vertices per ROF to trigger second a round diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/LayerMask.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/LayerMask.h new file mode 100644 index 0000000000000..9fe9894b3b457 --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/LayerMask.h @@ -0,0 +1,115 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef TRACKINGITSU_INCLUDE_LAYERMASK_H_ +#define TRACKINGITSU_INCLUDE_LAYERMASK_H_ + +#include +#include + +#ifndef GPUCA_GPUCODE +#include +#include +#endif + +#include "GPUCommonDef.h" +#include "GPUCommonMath.h" +#include "ITStracking/Constants.h" + +namespace o2::its +{ + +struct LayerMask { + GPUhdDefault() constexpr LayerMask() noexcept = default; + GPUhdDefault() constexpr LayerMask(uint16_t mask) noexcept : mBits{mask} {} + GPUhdDefault() constexpr LayerMask(int layer0, int layer1, int layer2) noexcept + : mBits{static_cast((uint16_t(1) << layer0) | (uint16_t(1) << layer1) | (uint16_t(1) << layer2))} + { + } + GPUhdi() constexpr operator uint16_t() const noexcept { return mBits; } + GPUhdi() constexpr uint16_t value() const noexcept { return mBits; } + GPUhdi() constexpr void set(int layer) noexcept { mBits |= (uint16_t(1) << layer); } + + GPUhdi() LayerMask operator~() const noexcept { return LayerMask{static_cast(~mBits)}; } + GPUhdi() LayerMask operator&(LayerMask other) const noexcept { return LayerMask{static_cast(mBits & other.mBits)}; } + GPUhdi() LayerMask operator|(LayerMask other) const noexcept { return LayerMask{static_cast(mBits | other.mBits)}; } + GPUhdi() LayerMask& operator&=(LayerMask other) noexcept + { + mBits &= other.mBits; + return *this; + } + GPUhdi() LayerMask& operator|=(LayerMask other) noexcept + { + mBits |= other.mBits; + return *this; + } + + GPUhdi() bool empty() const noexcept { return mBits == 0; } + GPUhdi() bool has(int layer) const noexcept { return mBits & (uint16_t(1) << layer); } + GPUhdi() bool isSubsetOf(LayerMask allowed) const noexcept { return (*this & ~allowed).empty(); } + GPUhdi() bool isAllowedHoleMask(int maxHoles, LayerMask allowedHoleMask) const noexcept + { + const int allowedHoles = maxHoles > 0 ? maxHoles : 0; + return count() <= allowedHoles && isSubsetOf(allowedHoleMask); + } + GPUhdi() bool isAllowed(int maxHoles, LayerMask allowedHoleMask) const noexcept + { + return holeMask().isAllowedHoleMask(maxHoles, allowedHoleMask); + } + GPUhdi() int length() const noexcept { return empty() ? 0 : last() - first() + 1; } + GPUhdi() int count() const noexcept { return static_cast(o2::gpu::GPUCommonMath::Popcount(mBits)); } + GPUhdi() int first() const noexcept { return mBits ? static_cast(o2::gpu::GPUCommonMath::Ctz(mBits)) : constants::UnusedIndex; } + GPUhdi() int last() const noexcept { return mBits ? 31 - static_cast(o2::gpu::GPUCommonMath::Clz(mBits)) : constants::UnusedIndex; } + GPUhdi() LayerMask holeMask() const noexcept + { + return empty() ? LayerMask{0} : (span(first(), last()) & ~(*this)); + } + + GPUhdi() int slot(int layer) const noexcept + { + if (!has(layer)) { + return constants::UnusedIndex; + } + const uint32_t lowerLayers = (uint32_t(1) << layer) - 1; + return static_cast(o2::gpu::GPUCommonMath::Popcount(static_cast(mBits) & lowerLayers)); + } + + static GPUhdi() LayerMask span(int fromLayer, int toLayer) noexcept + { + if (fromLayer > toLayer) { + return 0; + } + const uint32_t upper = (uint32_t(1) << (toLayer + 1)) - 1; + const uint32_t lower = (uint32_t(1) << fromLayer) - 1; + return static_cast(upper & ~lower); + } + + static GPUhdi() LayerMask skipped(int fromLayer, int toLayer) noexcept + { + return (toLayer - fromLayer <= 1) ? LayerMask{0} : span(fromLayer + 1, toLayer - 1); + } + +#ifndef GPUCA_GPUCODE + std::string asString() const { return fmt::format("{:016b}", mBits); } +#endif + + private: + uint16_t mBits{0}; +}; + +static_assert(std::is_standard_layout_v); +static_assert(std::is_trivially_copyable_v); +static_assert(sizeof(LayerMask) == sizeof(uint16_t)); +static_assert(alignof(LayerMask) == alignof(uint16_t)); + +} // namespace o2::its + +#endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/MathUtils.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/MathUtils.h index d276e27638dbd..950d8c0a9117f 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/MathUtils.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/MathUtils.h @@ -16,6 +16,8 @@ #ifndef O2_ITS_TRACKING_MATHUTILS_H_ #define O2_ITS_TRACKING_MATHUTILS_H_ +#include + #include "CommonConstants/MathConstants.h" #include "ITStracking/Constants.h" #include "MathUtils/Utils.h" diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h index 300abb2a3b10d..3fef2dc640cbc 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h @@ -32,6 +32,7 @@ #include "ITStracking/ExternalAllocator.h" #include "ITStracking/BoundedAllocator.h" #include "ITStracking/ROFLookupTables.h" +#include "ITStracking/TrackingTopology.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" @@ -66,6 +67,7 @@ struct TimeFrame { using ROFOverlapTableN = ROFOverlapTable; using ROFVertexLookupTableN = ROFVertexLookupTable; using ROFMaskTableN = ROFMaskTable; + using TrackingTopologyN = TrackingTopology; using TrackSeedN = TrackSeed; friend class gpu::TimeFrameGPU; @@ -112,10 +114,10 @@ struct TimeFrame { auto& 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]; } - auto& getMSangles() { return mMSangles; } - float getPhiCut(int layer) const { return mPhiCuts[layer]; } - auto& getPhiCuts() { return mPhiCuts; } + float getTransitionPhiCut(int transitionId) const { return mTransitionPhiCuts[transitionId]; } + float getTransitionMSAngle(int transitionId) const { return mTransitionMSAngles[transitionId]; } + auto& getTransitionPhiCuts() { return mTransitionPhiCuts; } + auto& getTransitionMSAngles() { return mTransitionMSAngles; } float getPositionResolution(int layer) const { return mPositionResolution[layer]; } auto& getPositionResolutions() { return mPositionResolution; } @@ -135,6 +137,8 @@ struct TimeFrame { const auto& getIndexTableUtils() const { return mIndexTableUtils; } const auto& getROFOverlapTable() const { return mROFOverlapTable; } const auto& getROFOverlapTableView() const { return mROFOverlapTableView; } + const auto& getTrackerTopologies() const { return mTrackerTopologies; } + const auto& getTrackingTopologyView() const { return mTrackingTopologyView; } void setROFOverlapTable(ROFOverlapTableN table) { mROFOverlapTable = std::move(table); @@ -177,7 +181,10 @@ struct TimeFrame { auto& getCellsLabel(int layer) { return mCellLabels[layer]; } bool hasMCinformation() const { return mClusterLabels[0] != nullptr; } - void initialise(const int iteration, const TrackingParameters& trkParam, const int maxLayers = NLayers, bool resetVertices = true); + void initVertexingTopology(const TrackingParameters& trkParam); + void initDefaultTrackingTopology(const TrackingParameters& trkParam, const int maxLayers = NLayers); + void initTrackerTopologies(gsl::span trkParams, const int maxLayers = NLayers); + void initialise(const TrackingParameters& trkParam, const int maxLayers = NLayers, const int iteration = constants::UnusedIndex); bool isClusterUsed(int layer, int clusterId) const { return mUsedClusters[layer][clusterId]; } void markUsedCluster(int layer, int clusterId) { mUsedClusters[layer][clusterId] = true; } @@ -193,6 +200,7 @@ struct TimeFrame { auto& getCellsLookupTable() { return mCellsLookupTable; } auto& getCellsNeighbours() { return mCellsNeighbours; } + auto& getCellsNeighboursTopology() { return mCellsNeighboursTopology; } auto& getCellsNeighboursLUT() { return mCellsNeighboursLUT; } auto& getTracks() { return mTracks; } auto& getTracksLabel() { return mTracksLabel; } @@ -273,6 +281,7 @@ struct TimeFrame { bounded_vector mTracks; bounded_vector mTracksLabel; std::vector> mCellsNeighbours; + std::vector> mCellsNeighboursTopology; std::vector> mCellsLookupTable; const o2::base::PropagatorImpl* mPropagatorDevice = nullptr; // Needed only for GPU @@ -292,8 +301,8 @@ struct TimeFrame { bool isBeamPositionOverridden = false; std::array mMinR; std::array mMaxR; - bounded_vector mMSangles; - bounded_vector mPhiCuts; + bounded_vector mTransitionPhiCuts; + bounded_vector mTransitionMSAngles; bounded_vector mPositionResolution; std::array, NLayers> mClusterSize; @@ -319,6 +328,10 @@ struct TimeFrame { IndexTableUtilsN mIndexTableUtils; ROFOverlapTableN mROFOverlapTable; ROFOverlapTableN::View mROFOverlapTableView; + TrackingTopologyN mVertexingTopology; + TrackingTopologyN mDefaultTrackingTopology; + std::vector mTrackerTopologies; + typename TrackingTopologyN::View mTrackingTopologyView; ROFVertexLookupTableN mROFVertexLookupTable; ROFVertexLookupTableN::View mROFVertexLookupTableView; ROFMaskTableN mMultiplicityCutMask; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackHelpers.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackHelpers.h index 885cb0f2b9ca5..d244b39ff9d11 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackHelpers.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackHelpers.h @@ -16,8 +16,6 @@ #ifndef O2_ITS_TRACKING_TRACKHELPERS_H_ #define O2_ITS_TRACKING_TRACKHELPERS_H_ -#include - #include "DataFormatsITS/TrackITS.h" #include "ITStracking/Cell.h" #include "ITStracking/Cluster.h" @@ -29,6 +27,16 @@ namespace o2::its::track { +// Prefer 1) longer track 2) sorted in chi2 +GPUhdi() bool isBetter(const o2::its::TrackITS& a, const o2::its::TrackITS& b) +{ + const auto ncla = a.getNumberOfClusters(); + const auto nclb = b.getNumberOfClusters(); + // is a as long as b ? then decide on chi2 + // otherwise prefer longer + return (ncla == nclb) ? (a.getChi2() < b.getChi2()) : ncla > nclb; +} + // Find the populated interior layer closest to the radial midpoint. // If no layer can be found, return constants::UnusedIndex. // Should minimize the sagitta bias. diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h index 1c3c642429686..f536e86fe95d5 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h @@ -18,7 +18,6 @@ #include -#include "DetectorsBase/Propagator.h" #include "ITStracking/Configuration.h" #include "ITStracking/IndexTableUtils.h" #include "ITStracking/TimeFrame.h" @@ -46,7 +45,7 @@ class TrackerTraits virtual ~TrackerTraits() = default; virtual void adoptTimeFrame(TimeFrame* tf) { mTimeFrame = tf; } - virtual void initialiseTimeFrame(const int iteration) { mTimeFrame->initialise(iteration, mTrkParams[iteration], mTrkParams[iteration].NLayers, false); } + virtual void initialiseTimeFrame(const int iteration) { mTimeFrame->initialise(mTrkParams[iteration], mTrkParams[iteration].NLayers, iteration); } virtual void computeLayerTracklets(const int iteration, int iVertex); virtual void computeLayerCells(const int iteration); @@ -54,10 +53,10 @@ class TrackerTraits virtual void findRoads(const int iteration); template - void processNeighbours(int iteration, int iLayer, int iLevel, const bounded_vector& currentCellSeed, const bounded_vector& currentCellId, bounded_vector& updatedCellSeed, bounded_vector& updatedCellId); + void processNeighbours(int iteration, int defaultCellTopologyId, int iLevel, const bounded_vector& currentCellSeed, const bounded_vector& currentCellId, const bounded_vector& currentCellTopologyId, bounded_vector& updatedCellSeed, bounded_vector& updatedCellId, bounded_vector& updatedCellTopologyId); - void acceptTracks(int iteration, bounded_vector& tracks, bounded_vector>& firstClusters, bounded_vector>& sharedFirstClusters); - void markTracks(int iteration, bounded_vector>& sharedFirstClusters); + void acceptTracks(int iteration, bounded_vector& tracks, bounded_vector>& firstClusters); + void markTracks(int iteration); void updateTrackingParameters(const std::vector& trkPars) { diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h index 1d997ef12147a..69aa3c5fdaf06 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h @@ -71,6 +71,8 @@ struct TrackerParamConfig : public o2::conf::ConfigurableParamHelper0, otherwise use code defaults uint8_t startLayerMask[constants::MaxIter] = {}; // mask of start layer for this iteration (if >0) + int maxHolesIter[constants::MaxIter] = {}; // maximum number of missing internal layers allowed in the CA topology for each iteration + uint16_t holeLayerMaskIter[constants::MaxIter] = {}; // layers that may be skipped by the CA topology for each iteration float minPtIterLgt[constants::MaxIter * (MaxTrackLength - MinTrackLength + 1)] = {}; // min.pT for given track length at this iteration, used only if >0, otherwise use code defaults float sysErrY2[7] = {0}; // systematic error^2 in Y per layer float sysErrZ2[7] = {0}; // systematic error^2 in Z per layer @@ -100,7 +102,12 @@ struct TrackerParamConfig : public o2::conf::ConfigurableParamHelper::max(); bool dropTFUponFailure = false; bool fataliseUponFailure = true; // granular management of the fatalisation in async mode + + // Selections on tracks sharing clusters bool allowSharingFirstCluster = false; // allow first cluster sharing among tracks + float sharedClusterMaxDeltaPhi = 0.05f; // Maximum allowed delta phi at the cluster position + float sharedClusterMaxDeltaEta = 0.03f; // Maximum allowed delta eta at the cluster position + bool sharedClusterOppositeSign = false; // Require opposite sign of the tracklets O2ParamDef(TrackerParamConfig, "ITSCATrackerParam"); }; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h index ac4b99a0a8cd8..14c5d6a62e0ad 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h @@ -34,6 +34,7 @@ namespace o2::its { class ITSTrackingInterface { + public: static constexpr int NLayers{7}; using VertexerN = Vertexer; using VertexerTraitsN = VertexerTraits; @@ -41,7 +42,6 @@ class ITSTrackingInterface using TrackerTraitsN = TrackerTraits; using TimeFrameN = TimeFrame; - public: ITSTrackingInterface(bool isMC, bool doStag, int trgType, @@ -80,6 +80,8 @@ class ITSTrackingInterface TimeFrameN* mTimeFrame = nullptr; protected: + virtual void overrideParameters(std::vector& t, std::vector& v) {} + virtual void requestTopologyDictionary(framework::ProcessingContext& pc); virtual void loadROF(gsl::span& trackROFspan, gsl::span clusters, gsl::span::iterator& pattIt, @@ -98,7 +100,7 @@ class ITSTrackingInterface const o2::itsmft::TopologyDictionary* mDict = nullptr; std::unique_ptr mTracker = nullptr; std::unique_ptr mVertexer = nullptr; - const o2::dataformats::MeanVertexObject* mMeanVertex; + const o2::dataformats::MeanVertexObject* mMeanVertex{}; std::shared_ptr mMemoryPool; std::shared_ptr mTaskArena; }; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingTopology.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingTopology.h new file mode 100644 index 0000000000000..2afb67609664f --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingTopology.h @@ -0,0 +1,219 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef TRACKINGITSU_INCLUDE_TRACKINGTOPOLOGY_H_ +#define TRACKINGITSU_INCLUDE_TRACKINGTOPOLOGY_H_ + +#include +#include +#include +#include + +#ifndef GPUCA_GPUCODE +#include +#include +#include "Framework/Logger.h" +#endif + +#include "CommonDataFormat/RangeReference.h" +#include "GPUCommonDef.h" +#include "GPUCommonMath.h" +#include "ITStracking/LayerMask.h" + +namespace o2::its +{ + +template +class TrackingTopology +{ + public: + using Id = uint8_t; + using Mask = LayerMask; + using Range = o2::dataformats::RangeReference; + static constexpr int MaxTransitions = (NLayers * (NLayers - 1)) / 2; + static constexpr int MaxCells = (NLayers * (NLayers - 1) * (NLayers - 2)) / 6; + static_assert(NLayers < std::numeric_limits::max()); + static_assert(MaxTransitions <= std::numeric_limits::max()); + static_assert(MaxCells <= std::numeric_limits::max()); + + // Describes from which layer to which layer the look-up happens + struct LayerTransition { + Id fromLayer{0}; + Id toLayer{0}; + }; + static_assert(std::is_standard_layout_v); + static_assert(std::is_trivially_copyable_v); + static_assert(sizeof(LayerTransition) == (2 * sizeof(Id))); + + // Describes from which LayerTransition a tracklet is allowed to originate + // and with which LayerTransition this can be combined additionally the hitMasked is cached + struct CellTopology { + Id firstTransition{0}; + Id secondTransition{0}; + Mask hitLayerMask{0}; + }; + static_assert(std::is_standard_layout_v); + static_assert(std::is_trivially_copyable_v); + static_assert(sizeof(CellTopology) == (2 * sizeof(Id)) + sizeof(Mask)); + + // GPU ready view of the underlying LUTs + struct View { + const LayerTransition* transitions{nullptr}; + const CellTopology* cells{nullptr}; + const Range* cellsByFirstTransitionIndex{nullptr}; + const Id* cellsByFirstTransition{nullptr}; + Id nTransitions{0}; + Id nCells{0}; + Id nCellsByFirstTransition{0}; + + GPUhdi() const LayerTransition& getTransition(Id id) const { return transitions[id]; } + GPUhdi() const CellTopology& getCell(Id id) const { return cells[id]; } + GPUhdi() Range getCellsStartingWithTransition(Id transitionId) const { return cellsByFirstTransitionIndex[transitionId]; } + +#ifndef GPUCA_GPUCODE + std::string asString() const + { + std::string out = fmt::format("TrackingTopology: transitions={} cells={}", nTransitions, nCells); + out += "\n transitions:"; + for (Id transitionId = 0; transitionId < nTransitions; ++transitionId) { + const auto& t = transitions[transitionId]; + out += fmt::format("\n {}: {} -> {}", transitionId, t.fromLayer, t.toLayer); + } + out += "\n cells:"; + for (Id cellId = 0; cellId < nCells; ++cellId) { + const auto& c = cells[cellId]; + const auto& first = transitions[c.firstTransition]; + const auto& second = transitions[c.secondTransition]; + out += fmt::format("\n {}: {} -> {} -> {} hitMask={} transitions=({}, {})", cellId, first.fromLayer, first.toLayer, second.toLayer, c.hitLayerMask.asString(), c.firstTransition, c.secondTransition); + } + return out; + } + + void print() const + { + LOGP(info, "{}", asString()); + } +#endif + }; + + void init(int maxLayers, int maxHoles, Mask holeLayerMask) + { + clear(); + mMaxLayers = o2::gpu::CAMath::Max(0, o2::gpu::CAMath::Min(maxLayers, NLayers)); + mMaxHoles = o2::gpu::CAMath::Max(maxHoles, 0); + mHoleLayerMask = holeLayerMask; + for (int fromLayer = 0; fromLayer < mMaxLayers; ++fromLayer) { + for (int toLayer = fromLayer + 1; toLayer < mMaxLayers; ++toLayer) { + if (Mask::skipped(fromLayer, toLayer).isAllowedHoleMask(mMaxHoles, mHoleLayerMask)) { + mTransitions[mNTransitions++] = LayerTransition{static_cast(fromLayer), static_cast(toLayer)}; + } + } + } + + for (Id firstId = 0; firstId < mNTransitions; ++firstId) { + const auto& first = mTransitions[firstId]; + for (Id secondId = 0; secondId < mNTransitions; ++secondId) { + const auto& second = mTransitions[secondId]; + if (first.toLayer != second.fromLayer) { + continue; + } + const Mask hitMask{first.fromLayer, first.toLayer, second.toLayer}; + if (hitMask.isAllowed(mMaxHoles, mHoleLayerMask)) { + mCells[mNCells++] = CellTopology{firstId, secondId, hitMask}; + } + } + } + + fillCellsByTransition(); + } + + View getView() const + { + return View{mTransitions.data(), + mCells.data(), + mCellsByFirstTransitionIndex.data(), + mCellsByFirstTransition.data(), + mNTransitions, + mNCells, + mNCellsByFirstTransition}; + } + + View getDeviceView(const LayerTransition* deviceTransitions, + const CellTopology* deviceCells, + const Range* deviceCellsByFirstTransitionIndex, + const Id* deviceCellsByFirstTransition) const + { + return View{deviceTransitions, + deviceCells, + deviceCellsByFirstTransitionIndex, + deviceCellsByFirstTransition, + mNTransitions, + mNCells, + mNCellsByFirstTransition}; + } + + const auto& getTransitions() const noexcept { return mTransitions; } + const auto& getCells() const noexcept { return mCells; } + const auto& getCellsByFirstTransitionIndex() const noexcept { return mCellsByFirstTransitionIndex; } + const auto& getCellsByFirstTransition() const noexcept { return mCellsByFirstTransition; } + Id getNTransitions() const noexcept { return mNTransitions; } + Id getNCells() const noexcept { return mNCells; } + Id getNCellsByFirstTransition() const noexcept { return mNCellsByFirstTransition; } + + private: + void clear() + { + mNTransitions = 0; + mNCells = 0; + mNCellsByFirstTransition = 0; + mTransitions.fill({}); + mCells.fill({}); + mCellsByFirstTransitionIndex.fill(Range{0, 0}); + mCellsByFirstTransition.fill(0); + } + + void fillCellsByTransition() + { + std::array counts{}; + for (Id cellId = 0; cellId < mNCells; ++cellId) { + ++counts[mCells[cellId].firstTransition]; + } + + Id offset = 0; + for (Id transitionId = 0; transitionId < mNTransitions; ++transitionId) { + mCellsByFirstTransitionIndex[transitionId].setFirstEntry(offset); + mCellsByFirstTransitionIndex[transitionId].setEntries(counts[transitionId]); + offset += counts[transitionId]; + } + + std::array cursor{}; + for (Id cellId = 0; cellId < mNCells; ++cellId) { + const Id transitionId = mCells[cellId].firstTransition; + mCellsByFirstTransition[mCellsByFirstTransitionIndex[transitionId].getFirstEntry() + cursor[transitionId]++] = cellId; + } + mNCellsByFirstTransition = offset; + } + + int mMaxLayers{0}; + int mMaxHoles{0}; + Mask mHoleLayerMask{0}; + Id mNTransitions{0}; + Id mNCells{0}; + Id mNCellsByFirstTransition{0}; + std::array mTransitions{}; + std::array mCells{}; + std::array mCellsByFirstTransitionIndex{}; + std::array mCellsByFirstTransition{}; +}; + +} // namespace o2::its + +#endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h index 3230737a0f87c..daf8d708e1e23 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h @@ -53,14 +53,11 @@ class VertexerTraits VertexerTraits() = default; virtual ~VertexerTraits() = default; - GPUhd() static const int2 getPhiBins(float phi, float deltaPhi, const IndexTableUtilsN&); - GPUhd() const int2 getPhiBins(float phi, float deltaPhi) { return getPhiBins(phi, deltaPhi, mIndexTableUtils); } - // virtual vertexer interface - virtual void initialise(const TrackingParameters& trackingParams, const int iteration = 0); - virtual void computeTracklets(const int iteration = 0); - virtual void computeTrackletMatching(const int iteration = 0); - virtual void computeVertices(const int iteration = 0); + virtual void initialise(const TrackingParameters& trackingParams); + virtual void computeTracklets(const int iteration); + virtual void computeTrackletMatching(const int iteration); + virtual void computeVertices(const int iteration); virtual void adoptTimeFrame(TimeFrameN* tf) noexcept { mTimeFrame = tf; } virtual void updateVertexingParameters(const std::vector& vrtPar); @@ -115,19 +112,6 @@ class VertexerTraits std::shared_ptr mTaskArena; }; -template -inline void VertexerTraits::initialise(const TrackingParameters& trackingParams, const int iteration) -{ - mTimeFrame->initialise(0, trackingParams, 3, (bool)(!iteration)); // iteration for initialisation must be 0 for correctly resetting the frame, we need to pass the non-reset flag for vertices as well, tho. -} - -template -GPUhdi() const int2 VertexerTraits::getPhiBins(float phi, float dPhi, const IndexTableUtilsN& utils) -{ - return int2{utils.getPhiBinIndex(math_utils::getNormalizedPhi(phi - dPhi)), - utils.getPhiBinIndex(math_utils::getNormalizedPhi(phi + dPhi))}; -} - } // namespace its } // namespace o2 #endif diff --git a/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx b/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx index 49bf9b5b1887d..0bf383c996a68 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx @@ -24,8 +24,8 @@ using namespace o2::its; std::string TrackingParameters::asString() const { - std::string str = std::format("NZb:{} NPhB:{} PerVtx:{} DropFail:{} ClSh:{} TtklMinPt:{:.2f} MinCl:{}", - ZBins, PhiBins, PerPrimaryVertexProcessing, DropTFUponFailure, ClusterSharing, TrackletMinPt, MinTrackLength); + std::string str = std::format("NZb:{} NPhB:{} PerVtx:{} DropFail:{} ClSh:{} TtklMinPt:{:.2f} MinCl:{} MaxHoles:{} HoleMask:{:#x}", + ZBins, PhiBins, PerPrimaryVertexProcessing, DropTFUponFailure, ClusterSharing, TrackletMinPt, MinTrackLength, MaxHoles, HoleLayerMask); bool first = true; for (int il = NLayers; il >= MinTrackLength; il--) { int slot = NLayers - il; @@ -143,7 +143,7 @@ std::vector TrackingMode::getTrackingParameters(TrackingMode // check if something was overridden via configurable params if (ip < constants::MaxIter) { if (tc.startLayerMask[ip] > 0) { - trackParams[2].StartLayerMask = tc.startLayerMask[ip]; + param.StartLayerMask = tc.startLayerMask[ip]; } if (tc.minTrackLgtIter[ip] > 0) { param.MinTrackLength = tc.minTrackLgtIter[ip]; @@ -174,6 +174,14 @@ std::vector TrackingMode::getTrackingParameters(TrackingMode LOGP(fatal, "Unsupported ITS tracking mode {} ", toString(mode)); } + for (auto& param : trackParams) { + param.PassFlags.reset(); + } + trackParams[0].PassFlags.set(IterationStep::FirstPass, IterationStep::RebuildClusterLUT); + if (trackParams.size() > 3 && tc.doUPCIteration) { + trackParams[3].PassFlags.set(IterationStep::UseUPCMask, IterationStep::RebuildClusterLUT, IterationStep::SelectUPCVertices); + } + float bFactor = std::abs(o2::base::Propagator::Instance()->getNominalBz()) / 5.0066791f; float bFactorTracklets = bFactor < 0.01f ? 1.f : bFactor; // for tracklets only @@ -188,7 +196,7 @@ std::vector TrackingMode::getTrackingParameters(TrackingMode p.ReseedIfShorter = tc.reseedIfShorter; p.RepeatRefitOut = tc.repeatRefitOut; p.ShiftRefToCluster = tc.shiftRefToCluster; - p.createArtefactLabels = tc.createArtefactLabels; + p.CreateArtefactLabels = tc.createArtefactLabels; p.PrintMemory = tc.printMemory; p.MaxMemory = tc.maxMemory; @@ -196,6 +204,14 @@ std::vector TrackingMode::getTrackingParameters(TrackingMode p.SaveTimeBenchmarks = tc.saveTimeBenchmarks; p.FataliseUponFailure = tc.fataliseUponFailure; p.AllowSharingFirstCluster = tc.allowSharingFirstCluster; + p.SharedClusterMaxDeltaPhi = tc.sharedClusterMaxDeltaPhi; + p.SharedClusterMaxDeltaEta = tc.sharedClusterMaxDeltaEta; + p.SharedClusterOppositeSign = tc.sharedClusterOppositeSign; + const auto iter = &p - trackParams.data(); + if (iter < constants::MaxIter) { + p.MaxHoles = tc.maxHolesIter[iter]; + p.HoleLayerMask = tc.holeLayerMaskIter[iter]; + } if (tc.useMatCorrTGeo) { p.CorrType = o2::base::PropagatorImpl::MatCorrType::USEMatCorrTGeo; @@ -241,6 +257,12 @@ std::vector TrackingMode::getVertexingParameters(TrackingMo { const auto& vc = o2::its::VertexerParamConfig::Instance(); std::vector vertParams(2); // The number of actual iterations will be set as a configKeyVal to allow for pp/PbPb choice + for (auto& param : vertParams) { + param.PassFlags.reset(); + } + vertParams[0].PassFlags.set(IterationStep::FirstPass, IterationStep::ResetVertices); + vertParams[1].PassFlags.set(IterationStep::SkipROFsAboveThreshold, IterationStep::MarkVerticesAsUPC); + // global parameters set for every iteration for (auto& p : vertParams) { p.vertPerRofThreshold = vc.vertPerRofThreshold; diff --git a/Detectors/ITSMFT/ITS/tracking/src/FastMultEst.cxx b/Detectors/ITSMFT/ITS/tracking/src/FastMultEst.cxx index cb831d7db71d0..cfbfdd8a9150e 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/FastMultEst.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/FastMultEst.cxx @@ -168,85 +168,41 @@ int FastMultEst::selectROFs(const std::array(rofs, clus, doStaggering, multLayer); + const int selectionLayer = multEstConf.isMultCutRequested() ? std::clamp(multEstConf.cutMultClusLayer, 0, NLayers - 1) : overlapView.getClock(); + const auto multCounts = buildMultiplicityCounts(rofs, clus, doStaggering, selectionLayer); const int selectionRofCount = doStaggering ? static_cast(rofs[selectionLayer].size()) : static_cast(rofs[0].size()); sel.resetMask(); lastRandomSeed = gRandom->GetSeed(); const o2::InteractionRecord tfStartIR{0, firstTForbit}; - - if (!trig.empty()) { + // mask ROFs which are not good from the multiplicity selection (if any) POV + struct ROFStatus { + int entry = 0, priority = 0; + }; + std::vector selROFs; + selROFs.reserve(selectionRofCount); + bool selmult = multEstConf.isMultCutRequested(); + for (int selectionRof = 0; selectionRof < selectionRofCount; ++selectionRof) { + selROFs.emplace_back(selectionRof, (selmult && !multEstConf.isPassingMultCut(process(multCounts[selectionRof]))) ? -1 : 0); + } + if (!trig.empty() && multEstConf.preferTriggered) { const auto& selectionLayerTiming = overlapView.getLayer(selectionLayer); - const auto& multLayerTiming = overlapView.getLayer(multLayer); - for (const auto& trigger : trig) { const int selectionRof = findROFForIR(trigger.ir, tfStartIR, selectionLayerTiming); - if (selectionRof < 0) { - continue; - } - if (multEstConf.cutRandomFraction > 0.f && gRandom->Rndm() < multEstConf.cutRandomFraction) { - continue; - } - if (multEstConf.isMultCutRequested()) { - const int triggerMultRof = doStaggering ? findROFForIR(trigger.ir, tfStartIR, multLayerTiming) : selectionRof; - if (triggerMultRof < 0 || triggerMultRof >= static_cast(multCounts.size())) { - continue; - } - if (!multEstConf.isPassingMultCut(process(multCounts[triggerMultRof]))) { - continue; - } - } - enableCompatibleROFs(selectionLayer, selectionRof, overlapView, sel); - } - } else { - LOGP(info, "FastMultEst received no physics/TRD triggers, falling back to ROF-driven filtering on layer {}", selectionLayer); - for (int selectionRof = 0; selectionRof < selectionRofCount; ++selectionRof) { - if (multEstConf.isMultCutRequested()) { - bool passes = false; - if (!doStaggering || selectionLayer == multLayer) { - if (selectionRof < static_cast(multCounts.size())) { - passes = multEstConf.isPassingMultCut(process(multCounts[selectionRof])); - } - } else { - const auto& overlap = overlapView.getOverlap(selectionLayer, multLayer, selectionRof); - for (int rof = overlap.getFirstEntry(); rof < overlap.getEntriesBound(); ++rof) { - if (rof < static_cast(multCounts.size())) { - if (multEstConf.isPassingMultCut(process(multCounts[rof]))) { - passes = true; - break; - } - } - } - } - if (!passes) { - continue; - } - } - if (multEstConf.cutRandomFraction > 0.f && gRandom->Rndm() < multEstConf.cutRandomFraction) { + if (selectionRof < 0 || selROFs[selectionRof].priority < 0) { continue; } - enableCompatibleROFs(selectionLayer, selectionRof, overlapView, sel); + selROFs[selectionRof].priority++; // increment trigger counter } + sort(selROFs.begin(), selROFs.end(), [](const ROFStatus& a, const ROFStatus& b) { return a.priority > b.priority; }); // order in number of triggers, masked will go to the end } - - const auto selView = sel.getView(); int nsel = 0; - for (int irof = 0; irof < selectionRofCount; ++irof) { - nsel += selView.isROFEnabled(selectionLayer, irof); - } - - if (!trig.empty() && multEstConf.preferTriggered) { - LOGP(debug, "FastMultEst preferTriggered is ignored in trigger-driven mask mode"); + for (auto& rof : selROFs) { + if (rof.priority >= 0 && (multEstConf.cutRandomFraction <= 0.f || (gRandom->Rndm() > multEstConf.cutRandomFraction))) { + enableCompatibleROFs(selectionLayer, rof.entry, overlapView, sel); + nsel++; + } } - LOGP(debug, "NSel = {} of {} rofs on layer {} Seeds: before {} after {}", nsel, selectionRofCount, selectionLayer, lastRandomSeed, gRandom->GetSeed()); - return nsel; } diff --git a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx index cafddfcc41a76..8375004cbfbad 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx @@ -241,26 +241,42 @@ void TimeFrame::prepareClusters(const TrackingParameters& trkParam, con } template -void TimeFrame::initialise(const int iteration, const TrackingParameters& trkParam, const int maxLayers, bool resetVertices) +void TimeFrame::initVertexingTopology(const TrackingParameters& trkParam) { - if (iteration == 0) { + mVertexingTopology.init(3, trkParam.MaxHoles, trkParam.HoleLayerMask); +} + +template +void TimeFrame::initDefaultTrackingTopology(const TrackingParameters& trkParam, const int maxLayers) +{ + mDefaultTrackingTopology.init(maxLayers, trkParam.MaxHoles, trkParam.HoleLayerMask); +} + +template +void TimeFrame::initTrackerTopologies(gsl::span trkParams, const int maxLayers) +{ + mTrackerTopologies.resize(trkParams.size()); + for (size_t iteration = 0; iteration < trkParams.size(); ++iteration) { + const int iterationMaxLayers = std::min(maxLayers, trkParams[iteration].NLayers); + mTrackerTopologies[iteration].init(iterationMaxLayers, trkParams[iteration].MaxHoles, trkParams[iteration].HoleLayerMask); + } +} + +template +void TimeFrame::initialise(const TrackingParameters& trkParam, const int maxLayers, const int iteration) +{ + mTrackingTopologyView = iteration != constants::UnusedIndex ? mTrackerTopologies[iteration].getView() : (maxLayers == 3 ? mVertexingTopology.getView() : mDefaultTrackingTopology.getView()); + + if (trkParam.PassFlags[IterationStep::FirstPass]) { deepVectorClear(mTracks); deepVectorClear(mTracksLabel); deepVectorClear(mLines); deepVectorClear(mLinesLabels); - if (resetVertices) { + if (trkParam.PassFlags[IterationStep::ResetVertices]) { deepVectorClear(mPrimaryVertices); deepVectorClear(mPrimaryVerticesLabels); } clearResizeBoundedVector(mLinesLabels, getNrof(1), mMemoryPool.get()); - clearResizeBoundedVector(mCells, trkParam.CellsPerRoad(), mMemoryPool.get()); - clearResizeBoundedVector(mCellsLookupTable, trkParam.CellsPerRoad() - 1, mMemoryPool.get()); - clearResizeBoundedVector(mCellsNeighbours, trkParam.CellsPerRoad() - 1, mMemoryPool.get()); - clearResizeBoundedVector(mCellsNeighboursLUT, trkParam.CellsPerRoad() - 1, mMemoryPool.get()); - clearResizeBoundedVector(mCellLabels, trkParam.CellsPerRoad(), mMemoryPool.get()); - clearResizeBoundedVector(mTracklets, std::min(trkParam.TrackletsPerRoad(), maxLayers - 1), mMemoryPool.get()); - clearResizeBoundedVector(mTrackletLabels, trkParam.TrackletsPerRoad(), mMemoryPool.get()); - clearResizeBoundedVector(mTrackletsLookupTable, trkParam.TrackletsPerRoad(), mMemoryPool.get()); mIndexTableUtils.setTrackingParameters(trkParam); clearResizeBoundedVector(mPositionResolution, trkParam.NLayers, mMemoryPool.get()); clearResizeBoundedVector(mBogusClusters, trkParam.NLayers, mMemoryPool.get()); @@ -289,11 +305,22 @@ void TimeFrame::initialise(const int iteration, const TrackingParameter mMinR.fill(std::numeric_limits::max()); mMaxR.fill(std::numeric_limits::min()); } + clearResizeBoundedVector(mCells, mTrackingTopologyView.nCells, mMemoryPool.get()); + clearResizeBoundedVector(mCellsLookupTable, mTrackingTopologyView.nCells, mMemoryPool.get()); + clearResizeBoundedVector(mCellsNeighbours, mTrackingTopologyView.nCells, mMemoryPool.get()); + clearResizeBoundedVector(mCellsNeighboursTopology, mTrackingTopologyView.nCells, mMemoryPool.get()); + clearResizeBoundedVector(mCellsNeighboursLUT, mTrackingTopologyView.nCells, mMemoryPool.get()); + clearResizeBoundedVector(mCellLabels, mTrackingTopologyView.nCells, mMemoryPool.get()); + clearResizeBoundedVector(mTracklets, mTrackingTopologyView.nTransitions, mMemoryPool.get()); + clearResizeBoundedVector(mTrackletLabels, mTrackingTopologyView.nTransitions, mMemoryPool.get()); + clearResizeBoundedVector(mTrackletsLookupTable, mTrackingTopologyView.nTransitions, mMemoryPool.get()); + clearResizeBoundedVector(mTransitionPhiCuts, mTrackingTopologyView.nTransitions, mMemoryPool.get()); + clearResizeBoundedVector(mTransitionMSAngles, mTrackingTopologyView.nTransitions, mMemoryPool.get()); mNTrackletsPerROF.resize(2); for (auto& v : mNTrackletsPerROF) { v = bounded_vector(getNrof(1) + 1, 0, mMemoryPool.get()); } - if (iteration == 0 || iteration == 3) { + if (trkParam.PassFlags[IterationStep::RebuildClusterLUT]) { prepareClusters(trkParam, maxLayers); } mTotalTracklets = {0, 0}; @@ -304,42 +331,48 @@ void TimeFrame::initialise(const int iteration, const TrackingParameter } } - mMSangles.resize(trkParam.NLayers); - mPhiCuts.resize(mClusters.size() - 1, 0.f); - float oneOverR{0.001f * 0.3f * std::abs(mBz) / trkParam.TrackletMinPt}; + // estimate MS per layer + std::array msAngles{}; for (unsigned int iLayer{0}; iLayer < NLayers; ++iLayer) { - mMSangles[iLayer] = math_utils::MSangle(0.14f, trkParam.TrackletMinPt, trkParam.LayerxX0[iLayer]); + msAngles[iLayer] = math_utils::MSangle(0.14f, trkParam.TrackletMinPt, trkParam.LayerxX0[iLayer]); mPositionResolution[iLayer] = o2::gpu::CAMath::Sqrt((0.5f * (trkParam.SystErrorZ2[iLayer] + trkParam.SystErrorY2[iLayer])) + (trkParam.LayerResolution[iLayer] * trkParam.LayerResolution[iLayer])); - if (iLayer < mClusters.size() - 1) { - const float& r1 = trkParam.LayerRadii[iLayer]; - const float& r2 = trkParam.LayerRadii[iLayer + 1]; - oneOverR = (0.5 * oneOverR >= 1.f / r2) ? (2.f / r2) - o2::constants::math::Almost0 : oneOverR; - const float res1 = o2::gpu::CAMath::Hypot(trkParam.PVres, mPositionResolution[iLayer]); - const float res2 = o2::gpu::CAMath::Hypot(trkParam.PVres, mPositionResolution[iLayer + 1]); - const float cosTheta1half = o2::gpu::CAMath::Sqrt(1.f - math_utils::Sq(0.5f * r1 * oneOverR)); - const float cosTheta2half = o2::gpu::CAMath::Sqrt(1.f - math_utils::Sq(0.5f * r2 * oneOverR)); - float x = (r2 * cosTheta1half) - (r1 * cosTheta2half); - float delta = o2::gpu::CAMath::Sqrt(1.f / (1.f - 0.25f * math_utils::Sq(x * oneOverR)) * (math_utils::Sq((0.25f * r1 * r2 * math_utils::Sq(oneOverR) / cosTheta2half) + cosTheta1half) * math_utils::Sq(res1) + math_utils::Sq((0.25f * r1 * r2 * math_utils::Sq(oneOverR) / cosTheta1half) + cosTheta2half) * math_utils::Sq(res2))); - /// the expression std::asin(0.5f * x * oneOverR) is equivalent to std::aCos(0.5f * r1 * oneOverR) - std::acos(0.5 * r2 * oneOverR) - mPhiCuts[iLayer] = std::min(o2::gpu::CAMath::ASin(0.5f * x * oneOverR) + 2.f * mMSangles[iLayer] + delta, o2::constants::math::PI * 0.5f); - } } - for (int iLayer{0}; iLayer < std::min((int)mTracklets.size(), maxLayers); ++iLayer) { - deepVectorClear(mTracklets[iLayer]); - deepVectorClear(mTrackletLabels[iLayer]); - if (iLayer < (int)mCells.size()) { - deepVectorClear(mCells[iLayer]); - deepVectorClear(mTrackletsLookupTable[iLayer]); - mTrackletsLookupTable[iLayer].resize(mClusters[iLayer + 1].size() + 1, 0); - deepVectorClear(mCellLabels[iLayer]); + // for each transition calculate the phi-cuts + integrated MS + float oneOverR{0.001f * 0.3f * std::abs(mBz) / trkParam.TrackletMinPt}; + for (int transitionId{0}; transitionId < (int)mTracklets.size(); ++transitionId) { + const auto& transition = mTrackingTopologyView.getTransition(transitionId); + float ms2 = 0.; + for (int layer = transition.fromLayer; layer < transition.toLayer; ++layer) { + ms2 += math_utils::Sq(msAngles[layer]); } + mTransitionMSAngles[transitionId] = o2::gpu::CAMath::Sqrt(ms2); + const float& r1 = trkParam.LayerRadii[transition.fromLayer]; + const float& r2 = trkParam.LayerRadii[transition.toLayer]; + oneOverR = (0.5 * oneOverR >= 1.f / r2) ? (2.f / r2) - o2::constants::math::Almost0 : oneOverR; + const float res1 = o2::gpu::CAMath::Hypot(trkParam.PVres, mPositionResolution[transition.fromLayer]); + const float res2 = o2::gpu::CAMath::Hypot(trkParam.PVres, mPositionResolution[transition.toLayer]); + const float cosTheta1half = o2::gpu::CAMath::Sqrt(1.f - math_utils::Sq(0.5f * r1 * oneOverR)); + const float cosTheta2half = o2::gpu::CAMath::Sqrt(1.f - math_utils::Sq(0.5f * r2 * oneOverR)); + float x = (r2 * cosTheta1half) - (r1 * cosTheta2half); + float delta = o2::gpu::CAMath::Sqrt(1.f / (1.f - 0.25f * math_utils::Sq(x * oneOverR)) * (math_utils::Sq((0.25f * r1 * r2 * math_utils::Sq(oneOverR) / cosTheta2half) + cosTheta1half) * math_utils::Sq(res1) + math_utils::Sq((0.25f * r1 * r2 * math_utils::Sq(oneOverR) / cosTheta1half) + cosTheta2half) * math_utils::Sq(res2))); + /// the expression std::asin(0.5f * x * oneOverR) is equivalent to std::aCos(0.5f * r1 * oneOverR) - std::acos(0.5 * r2 * oneOverR) + mTransitionPhiCuts[transitionId] = o2::gpu::CAMath::Min(o2::gpu::CAMath::ASin(0.5f * x * oneOverR) + 2.f * mTransitionMSAngles[transitionId] + delta, o2::constants::math::PI * 0.5f); + + // some cleanup + deepVectorClear(mTracklets[transitionId]); + deepVectorClear(mTrackletLabels[transitionId]); + deepVectorClear(mTrackletsLookupTable[transitionId]); + mTrackletsLookupTable[transitionId].resize(mClusters[transition.fromLayer].size() + 1, 0); + } - if (iLayer < (int)mCells.size() - 1) { - deepVectorClear(mCellsLookupTable[iLayer]); - deepVectorClear(mCellsNeighbours[iLayer]); - deepVectorClear(mCellsNeighboursLUT[iLayer]); - } + for (int cellId{0}; cellId < (int)mCells.size(); ++cellId) { + deepVectorClear(mCells[cellId]); + deepVectorClear(mCellsLookupTable[cellId]); + deepVectorClear(mCellsNeighbours[cellId]); + deepVectorClear(mCellsNeighboursTopology[cellId]); + deepVectorClear(mCellsNeighboursLUT[cellId]); + deepVectorClear(mCellLabels[cellId]); } } @@ -356,6 +389,9 @@ unsigned long TimeFrame::getArtefactsMemory() const for (const auto& cellsN : mCellsNeighbours) { size += sizeof(int) * cellsN.size(); } + for (const auto& cellsN : mCellsNeighboursTopology) { + size += sizeof(int) * cellsN.size(); + } return size; } @@ -401,8 +437,8 @@ void TimeFrame::setMemoryPool(std::shared_ptr po initContainers(mNTrackletsPerClusterSum); initContainers(mNClustersPerROF); initVector(mPrimaryVertices); - initVector(mMSangles); - initVector(mPhiCuts); + initVector(mTransitionPhiCuts); + initVector(mTransitionMSAngles); initVector(mPositionResolution); initContainers(mClusterSize); initVector(mPValphaX); @@ -442,6 +478,7 @@ void TimeFrame::wipe() deepVectorClear(mTracklets); deepVectorClear(mCells); deepVectorClear(mCellsNeighbours); + deepVectorClear(mCellsNeighboursTopology); deepVectorClear(mCellsLookupTable); deepVectorClear(mPrimaryVertices); deepVectorClear(mTrackletsLookupTable); @@ -449,8 +486,8 @@ void TimeFrame::wipe() deepVectorClear(mNTrackletsPerCluster); deepVectorClear(mNTrackletsPerClusterSum); deepVectorClear(mNClustersPerROF); - deepVectorClear(mMSangles); - deepVectorClear(mPhiCuts); + deepVectorClear(mTransitionPhiCuts); + deepVectorClear(mTransitionMSAngles); deepVectorClear(mPositionResolution); deepVectorClear(mClusterSize); deepVectorClear(mPValphaX); diff --git a/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx b/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx index 382f2314b2e6a..f17d961fc7bb7 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx @@ -68,7 +68,7 @@ float Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& e try { for (iteration = 0; iteration < (int)mTrkParams.size(); ++iteration) { mMemoryPool->setMaxMemory(mTrkParams[iteration].MaxMemory); - if (iteration == 3 && mTrkParams[0].DoUPCIteration) { + if (mTrkParams[iteration].PassFlags[IterationStep::UseUPCMask]) { mTimeFrame->useUPCMask(); } float timeFrame{0.}, timeTracklets{0.}, timeCells{0.}, timeNeighbours{0.}, timeRoads{0.}; diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx index dc2d6e8889973..3432b60162002 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx @@ -15,25 +15,23 @@ #include #include -#include #include #include #include -#include +#include -#include "CommonConstants/MathConstants.h" #include "DetectorsBase/Propagator.h" #include "GPUCommonMath.h" #include "ITStracking/BoundedAllocator.h" #include "ITStracking/Cell.h" #include "ITStracking/Constants.h" #include "ITStracking/IndexTableUtils.h" +#include "ITStracking/LayerMask.h" #include "ITStracking/ROFLookupTables.h" #include "ITStracking/TrackerTraits.h" #include "ITStracking/TrackHelpers.h" #include "ITStracking/Tracklet.h" -#include "ReconstructionDataFormats/Track.h" namespace o2::its { @@ -47,23 +45,23 @@ struct PassMode { template void TrackerTraits::computeLayerTracklets(const int iteration, int iVertex) { - for (int iLayer = 0; iLayer < mTrkParams[iteration].TrackletsPerRoad(); ++iLayer) { - mTimeFrame->getTracklets()[iLayer].clear(); - mTimeFrame->getTrackletsLabel(iLayer).clear(); - if (iLayer > 0) { - std::fill(mTimeFrame->getTrackletsLookupTable()[iLayer - 1].begin(), mTimeFrame->getTrackletsLookupTable()[iLayer - 1].end(), 0); - } + const auto topology = mTimeFrame->getTrackingTopologyView(); + for (int transitionId = 0; transitionId < topology.nTransitions; ++transitionId) { + mTimeFrame->getTracklets()[transitionId].clear(); + mTimeFrame->getTrackletsLabel(transitionId).clear(); + std::fill(mTimeFrame->getTrackletsLookupTable()[transitionId].begin(), mTimeFrame->getTrackletsLookupTable()[transitionId].end(), 0); } const Vertex diamondVert(mTrkParams[iteration].Diamond, mTrkParams[iteration].DiamondCov, 1, 1.f); gsl::span diamondSpan(&diamondVert, 1); mTaskArena->execute([&] { - auto forTracklets = [&](auto Tag, int iLayer, int pivotROF, int base, int& offset) -> int { - if (!mTimeFrame->getROFMaskView().isROFEnabled(iLayer, pivotROF)) { + auto forTracklets = [&](auto Tag, int transitionId, int pivotROF, int base, int& offset) -> int { + const auto& transition = topology.getTransition(transitionId); + if (!mTimeFrame->getROFMaskView().isROFEnabled(transition.fromLayer, pivotROF)) { return 0; } - gsl::span primaryVertices = mTrkParams[iteration].UseDiamond ? diamondSpan : mTimeFrame->getPrimaryVertices(iLayer, pivotROF); + gsl::span primaryVertices = mTrkParams[iteration].UseDiamond ? diamondSpan : mTimeFrame->getPrimaryVertices(transition.fromLayer, pivotROF); if (primaryVertices.empty()) { return 0; } @@ -73,46 +71,46 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iVer return 0; } - // does this layer have any overlap with the next layer - const auto& rofOverlap = mTimeFrame->getROFOverlapTableView().getOverlap(iLayer, iLayer + 1, pivotROF); + const auto& rofOverlap = mTimeFrame->getROFOverlapTableView().getOverlap(transition.fromLayer, transition.toLayer, pivotROF); if (!rofOverlap.getEntries()) { return 0; } int localCount = 0; - auto& tracklets = mTimeFrame->getTracklets()[iLayer]; - auto layer0 = mTimeFrame->getClustersOnLayer(pivotROF, iLayer); + auto& tracklets = mTimeFrame->getTracklets()[transitionId]; + auto layer0 = mTimeFrame->getClustersOnLayer(pivotROF, transition.fromLayer); if (layer0.empty()) { return 0; } - const float meanDeltaR = mTrkParams[iteration].LayerRadii[iLayer + 1] - mTrkParams[iteration].LayerRadii[iLayer]; + const float meanDeltaR = mTrkParams[iteration].LayerRadii[transition.toLayer] - mTrkParams[iteration].LayerRadii[transition.fromLayer]; + const float phiCut = mTimeFrame->getTransitionPhiCut(transitionId); + const float msAngle = mTimeFrame->getTransitionMSAngle(transitionId); for (int iCluster = 0; iCluster < int(layer0.size()); ++iCluster) { const Cluster& currentCluster = layer0[iCluster]; - const int currentSortedIndex = mTimeFrame->getSortedIndex(pivotROF, iLayer, iCluster); - if (mTimeFrame->isClusterUsed(iLayer, currentCluster.clusterId)) { + const int currentSortedIndex = mTimeFrame->getSortedIndex(pivotROF, transition.fromLayer, iCluster); + if (mTimeFrame->isClusterUsed(transition.fromLayer, currentCluster.clusterId)) { continue; } const float inverseR0 = 1.f / currentCluster.radius; for (int iV = startVtx; iV < endVtx; ++iV) { const auto& pv = primaryVertices[iV]; - if (!mTimeFrame->getROFVertexLookupTableView().isVertexCompatible(iLayer, pivotROF, pv)) { + if (!mTimeFrame->getROFVertexLookupTableView().isVertexCompatible(transition.fromLayer, pivotROF, pv)) { continue; } - if ((pv.isFlagSet(Vertex::Flags::UPCMode) && iteration != 3) || (iteration == 3 && !pv.isFlagSet(Vertex::Flags::UPCMode))) { + if (pv.isFlagSet(Vertex::Flags::UPCMode) != mTrkParams[iteration].PassFlags[IterationStep::SelectUPCVertices]) { continue; } - const float resolution = o2::gpu::CAMath::Sqrt(math_utils::Sq(mTimeFrame->getPositionResolution(iLayer)) + math_utils::Sq(mTrkParams[iteration].PVres) / float(pv.getNContributors())); + const float resolution = o2::gpu::CAMath::Sqrt(math_utils::Sq(mTimeFrame->getPositionResolution(transition.fromLayer)) + math_utils::Sq(mTrkParams[iteration].PVres) / float(pv.getNContributors())); const float tanLambda = (currentCluster.zCoordinate - pv.getZ()) * inverseR0; - const float zAtRmin = tanLambda * (mTimeFrame->getMinR(iLayer + 1) - currentCluster.radius) + currentCluster.zCoordinate; - const float zAtRmax = tanLambda * (mTimeFrame->getMaxR(iLayer + 1) - currentCluster.radius) + currentCluster.zCoordinate; + const float zAtRmin = tanLambda * (mTimeFrame->getMinR(transition.toLayer) - currentCluster.radius) + currentCluster.zCoordinate; + const float zAtRmax = tanLambda * (mTimeFrame->getMaxR(transition.toLayer) - currentCluster.radius) + currentCluster.zCoordinate; const float sqInvDeltaZ0 = 1.f / (math_utils::Sq(currentCluster.zCoordinate - pv.getZ()) + constants::Tolerance); - const float sigmaZ = o2::gpu::CAMath::Sqrt( - math_utils::Sq(resolution) * math_utils::Sq(tanLambda) * ((math_utils::Sq(inverseR0) + sqInvDeltaZ0) * math_utils::Sq(meanDeltaR) + 1.f) + math_utils::Sq(meanDeltaR * mTimeFrame->getMSangle(iLayer))); - const auto bins = o2::its::getBinsRect(currentCluster, iLayer + 1, zAtRmin, zAtRmax, - sigmaZ * mTrkParams[iteration].NSigmaCut, mTimeFrame->getPhiCut(iLayer), + const float sigmaZ = o2::gpu::CAMath::Sqrt((math_utils::Sq(resolution) * math_utils::Sq(tanLambda) * ((math_utils::Sq(inverseR0) + sqInvDeltaZ0) * math_utils::Sq(meanDeltaR) + 1.f)) + math_utils::Sq(meanDeltaR * msAngle)); + const auto bins = o2::its::getBinsRect(currentCluster, transition.toLayer, zAtRmin, zAtRmax, + sigmaZ * mTrkParams[iteration].NSigmaCut, phiCut, mTimeFrame->getIndexTableUtils()); if (bins.x < 0) { continue; @@ -123,18 +121,18 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iVer } for (int targetROF = rofOverlap.getFirstEntry(); targetROF < rofOverlap.getEntriesBound(); ++targetROF) { - if (!mTimeFrame->getROFMaskView().isROFEnabled(iLayer + 1, targetROF)) { + if (!mTimeFrame->getROFMaskView().isROFEnabled(transition.toLayer, targetROF)) { continue; } - auto layer1 = mTimeFrame->getClustersOnLayer(targetROF, iLayer + 1); + auto layer1 = mTimeFrame->getClustersOnLayer(targetROF, transition.toLayer); if (layer1.empty()) { continue; } - const auto ts = mTimeFrame->getROFOverlapTableView().getTimeStamp(iLayer, pivotROF, iLayer + 1, targetROF); + const auto ts = mTimeFrame->getROFOverlapTableView().getTimeStamp(transition.fromLayer, pivotROF, transition.toLayer, targetROF); if (!ts.isCompatible(pv.getTimeStamp())) { continue; } - const auto& targetIndexTable = mTimeFrame->getIndexTable(targetROF, iLayer + 1); + const auto& targetIndexTable = mTimeFrame->getIndexTable(targetROF, transition.toLayer); const int zBinRange = (bins.z - bins.x) + 1; for (int iPhi = 0; iPhi < phiBinsNum; ++iPhi) { const int iPhiBin = (bins.y + iPhi) % mTrkParams[iteration].PhiBins; @@ -147,22 +145,22 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iVer break; } const Cluster& nextCluster = layer1[iNext]; - if (mTimeFrame->isClusterUsed(iLayer + 1, nextCluster.clusterId)) { + if (mTimeFrame->isClusterUsed(transition.toLayer, nextCluster.clusterId)) { continue; } const float deltaZ = o2::gpu::CAMath::Abs((tanLambda * (nextCluster.radius - currentCluster.radius)) + currentCluster.zCoordinate - nextCluster.zCoordinate); if (deltaZ / sigmaZ < mTrkParams[iteration].NSigmaCut && - math_utils::isPhiDifferenceBelow(currentCluster.phi, nextCluster.phi, mTimeFrame->getPhiCut(iLayer))) { + math_utils::isPhiDifferenceBelow(currentCluster.phi, nextCluster.phi, phiCut)) { const float phi{o2::gpu::CAMath::ATan2(currentCluster.yCoordinate - nextCluster.yCoordinate, currentCluster.xCoordinate - nextCluster.xCoordinate)}; const float tanL = (currentCluster.zCoordinate - nextCluster.zCoordinate) / (currentCluster.radius - nextCluster.radius); if constexpr (decltype(Tag)::value == PassMode::OnePass::value) { - tracklets.emplace_back(currentSortedIndex, mTimeFrame->getSortedIndex(targetROF, iLayer + 1, iNext), tanL, phi, ts); + tracklets.emplace_back(currentSortedIndex, mTimeFrame->getSortedIndex(targetROF, transition.toLayer, iNext), tanL, phi, ts); } else if constexpr (decltype(Tag)::value == PassMode::TwoPassCount::value) { ++localCount; } else if constexpr (decltype(Tag)::value == PassMode::TwoPassInsert::value) { const int idx = base + offset++; - tracklets[idx] = Tracklet(currentSortedIndex, mTimeFrame->getSortedIndex(targetROF, iLayer + 1, iNext), tanL, phi, ts); + tracklets[idx] = Tracklet(currentSortedIndex, mTimeFrame->getSortedIndex(targetROF, transition.toLayer, iNext), tanL, phi, ts); } } } @@ -175,22 +173,24 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iVer int dummy{0}; if (mTaskArena->max_concurrency() <= 1) { - for (int iLayer{0}; iLayer < mTrkParams[iteration].TrackletsPerRoad(); ++iLayer) { - const int startROF = 0, endROF = mTimeFrame->getROFOverlapTableView().getLayer(iLayer).mNROFsTF; + for (int transitionId{0}; transitionId < topology.nTransitions; ++transitionId) { + const int fromLayer = topology.getTransition(transitionId).fromLayer; + const int startROF = 0, endROF = mTimeFrame->getROFOverlapTableView().getLayer(fromLayer).mNROFsTF; for (int pivotROF{startROF}; pivotROF < endROF; ++pivotROF) { - forTracklets(PassMode::OnePass{}, iLayer, pivotROF, 0, dummy); + forTracklets(PassMode::OnePass{}, transitionId, pivotROF, 0, dummy); } } } else { - tbb::parallel_for(0, mTrkParams[iteration].TrackletsPerRoad(), [&](const int iLayer) { - const int startROF = 0, endROF = mTimeFrame->getROFOverlapTableView().getLayer(iLayer).mNROFsTF; + tbb::parallel_for(0, static_cast(topology.nTransitions), [&](const int transitionId) { + const int fromLayer = topology.getTransition(transitionId).fromLayer; + const int startROF = 0, endROF = mTimeFrame->getROFOverlapTableView().getLayer(fromLayer).mNROFsTF; bounded_vector perROFCount((endROF - startROF) + 1, mMemoryPool.get()); tbb::parallel_for(startROF, endROF, [&](const int pivotROF) { - perROFCount[pivotROF - startROF] = forTracklets(PassMode::TwoPassCount{}, iLayer, pivotROF, 0, dummy); + perROFCount[pivotROF - startROF] = forTracklets(PassMode::TwoPassCount{}, transitionId, pivotROF, 0, dummy); }); std::exclusive_scan(perROFCount.begin(), perROFCount.end(), perROFCount.begin(), 0); const int nTracklets = perROFCount.back(); - mTimeFrame->getTracklets()[iLayer].resize(nTracklets); + mTimeFrame->getTracklets()[transitionId].resize(nTracklets); if (nTracklets == 0) { return; } @@ -200,38 +200,37 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iVer return; } int localIdx = 0; - forTracklets(PassMode::TwoPassInsert{}, iLayer, pivotROF, baseIdx, localIdx); + forTracklets(PassMode::TwoPassInsert{}, transitionId, pivotROF, baseIdx, localIdx); }); }); } - tbb::parallel_for(0, mTrkParams[iteration].TrackletsPerRoad(), [&](const int iLayer) { + tbb::parallel_for(0, static_cast(topology.nTransitions), [&](const int transitionId) { /// Sort tracklets & remove duplicates // duplicates can exist simply since we evaluate per vertex - auto& trkl{mTimeFrame->getTracklets()[iLayer]}; + auto& trkl{mTimeFrame->getTracklets()[transitionId]}; std::sort(trkl.begin(), trkl.end()); trkl.erase(std::unique(trkl.begin(), trkl.end()), trkl.end()); trkl.shrink_to_fit(); - if (iLayer > 0) { /// recalculate lut - auto& lut{mTimeFrame->getTrackletsLookupTable()[iLayer - 1]}; - if (!trkl.empty()) { - for (const auto& tkl : trkl) { - lut[tkl.firstClusterIndex + 1]++; - } - std::inclusive_scan(lut.begin(), lut.end(), lut.begin()); + auto& lut{mTimeFrame->getTrackletsLookupTable()[transitionId]}; + if (!trkl.empty()) { + for (const auto& tkl : trkl) { + lut[tkl.firstClusterIndex + 1]++; } + std::inclusive_scan(lut.begin(), lut.end(), lut.begin()); } }); /// Create tracklets labels - if (mTimeFrame->hasMCinformation() && mTrkParams[iteration].createArtefactLabels) { - tbb::parallel_for(0, mTrkParams[iteration].TrackletsPerRoad(), [&](const int iLayer) { - for (auto& trk : mTimeFrame->getTracklets()[iLayer]) { + if (mTimeFrame->hasMCinformation() && mTrkParams[iteration].CreateArtefactLabels) { + tbb::parallel_for(0, static_cast(topology.nTransitions), [&](const int transitionId) { + const auto& transition = topology.getTransition(transitionId); + for (auto& trk : mTimeFrame->getTracklets()[transitionId]) { MCCompLabel label; - int currentId{mTimeFrame->getClusters()[iLayer][trk.firstClusterIndex].clusterId}; - int nextId{mTimeFrame->getClusters()[iLayer + 1][trk.secondClusterIndex].clusterId}; - for (const auto& lab1 : mTimeFrame->getClusterLabels(iLayer, currentId)) { - for (const auto& lab2 : mTimeFrame->getClusterLabels(iLayer + 1, nextId)) { + int currentId{mTimeFrame->getClusters()[transition.fromLayer][trk.firstClusterIndex].clusterId}; + int nextId{mTimeFrame->getClusters()[transition.toLayer][trk.secondClusterIndex].clusterId}; + for (const auto& lab1 : mTimeFrame->getClusterLabels(transition.fromLayer, currentId)) { + for (const auto& lab2 : mTimeFrame->getClusterLabels(transition.toLayer, nextId)) { if (lab1 == lab2 && lab1.isValid()) { label = lab1; break; @@ -241,7 +240,7 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iVer break; } } - mTimeFrame->getTrackletsLabel(iLayer).emplace_back(label); + mTimeFrame->getTrackletsLabel(transitionId).emplace_back(label); } }); } @@ -251,26 +250,28 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iVer template void TrackerTraits::computeLayerCells(const int iteration) { - for (int iLayer = 0; iLayer < mTrkParams[iteration].CellsPerRoad(); ++iLayer) { - deepVectorClear(mTimeFrame->getCells()[iLayer]); - if (iLayer > 0) { - deepVectorClear(mTimeFrame->getCellsLookupTable()[iLayer - 1]); - } - if (mTimeFrame->hasMCinformation() && mTrkParams[iteration].createArtefactLabels) { - deepVectorClear(mTimeFrame->getCellsLabel(iLayer)); + const auto topology = mTimeFrame->getTrackingTopologyView(); + for (int cellTopologyId = 0; cellTopologyId < topology.nCells; ++cellTopologyId) { + deepVectorClear(mTimeFrame->getCells()[cellTopologyId]); + deepVectorClear(mTimeFrame->getCellsLookupTable()[cellTopologyId]); + if (mTimeFrame->hasMCinformation() && mTrkParams[iteration].CreateArtefactLabels) { + deepVectorClear(mTimeFrame->getCellsLabel(cellTopologyId)); } } mTaskArena->execute([&] { - auto forTrackletCells = [&](auto Tag, int iLayer, bounded_vector& layerCells, int iTracklet, int offset = 0) -> int { - const Tracklet& currentTracklet{mTimeFrame->getTracklets()[iLayer][iTracklet]}; + auto forTrackletCells = [&](auto Tag, int cellTopologyId, bounded_vector& layerCells, int iTracklet, int offset = 0) -> int { + const auto& cellTopology = topology.getCell(cellTopologyId); + const auto& firstTransition = topology.getTransition(cellTopology.firstTransition); + const auto& secondTransition = topology.getTransition(cellTopology.secondTransition); + const Tracklet& currentTracklet{mTimeFrame->getTracklets()[cellTopology.firstTransition][iTracklet]}; const int nextLayerClusterIndex{currentTracklet.secondClusterIndex}; - const int nextLayerFirstTrackletIndex{mTimeFrame->getTrackletsLookupTable()[iLayer][nextLayerClusterIndex]}; - const int nextLayerLastTrackletIndex{mTimeFrame->getTrackletsLookupTable()[iLayer][nextLayerClusterIndex + 1]}; + const int nextLayerFirstTrackletIndex{mTimeFrame->getTrackletsLookupTable()[cellTopology.secondTransition][nextLayerClusterIndex]}; + const int nextLayerLastTrackletIndex{mTimeFrame->getTrackletsLookupTable()[cellTopology.secondTransition][nextLayerClusterIndex + 1]}; int foundCells{0}; for (int iNextTracklet{nextLayerFirstTrackletIndex}; iNextTracklet < nextLayerLastTrackletIndex; ++iNextTracklet) { - const Tracklet& nextTracklet{mTimeFrame->getTracklets()[iLayer + 1][iNextTracklet]}; - if (mTimeFrame->getTracklets()[iLayer + 1][iNextTracklet].firstClusterIndex != nextLayerClusterIndex) { + const Tracklet& nextTracklet{mTimeFrame->getTracklets()[cellTopology.secondTransition][iNextTracklet]}; + if (nextTracklet.firstClusterIndex != nextLayerClusterIndex) { break; } if (!currentTracklet.getTimeStamp().isCompatible(nextTracklet.getTimeStamp())) { @@ -282,18 +283,20 @@ void TrackerTraits::computeLayerCells(const int iteration) /// Track seed preparation. Clusters are numbered progressively from the innermost going outward. const int clusId[3]{ - mTimeFrame->getClusters()[iLayer][currentTracklet.firstClusterIndex].clusterId, - mTimeFrame->getClusters()[iLayer + 1][nextTracklet.firstClusterIndex].clusterId, - mTimeFrame->getClusters()[iLayer + 2][nextTracklet.secondClusterIndex].clusterId}; - const auto& cluster1_glo = mTimeFrame->getUnsortedClusters()[iLayer][clusId[0]]; - const auto& cluster2_glo = mTimeFrame->getUnsortedClusters()[iLayer + 1][clusId[1]]; - const auto& cluster3_tf = mTimeFrame->getTrackingFrameInfoOnLayer(iLayer + 2)[clusId[2]]; + mTimeFrame->getClusters()[firstTransition.fromLayer][currentTracklet.firstClusterIndex].clusterId, + mTimeFrame->getClusters()[firstTransition.toLayer][nextTracklet.firstClusterIndex].clusterId, + mTimeFrame->getClusters()[secondTransition.toLayer][nextTracklet.secondClusterIndex].clusterId}; + const int hitLayers[3]{firstTransition.fromLayer, firstTransition.toLayer, secondTransition.toLayer}; + const auto& cluster1_glo = mTimeFrame->getUnsortedClusters()[firstTransition.fromLayer][clusId[0]]; + const auto& cluster2_glo = mTimeFrame->getUnsortedClusters()[firstTransition.toLayer][clusId[1]]; + const auto& cluster3_tf = mTimeFrame->getTrackingFrameInfoOnLayer(secondTransition.toLayer)[clusId[2]]; auto track{o2::its::track::buildTrackSeed(cluster1_glo, cluster2_glo, cluster3_tf, mBz)}; float chi2{0.f}; bool good{false}; for (int iC{2}; iC--;) { - const TrackingFrameInfo& trackingHit = mTimeFrame->getTrackingFrameInfoOnLayer(iLayer + iC)[clusId[iC]]; + const int hitLayer = hitLayers[iC]; + const TrackingFrameInfo& trackingHit = mTimeFrame->getTrackingFrameInfoOnLayer(hitLayer)[clusId[iC]]; if (!track.rotate(trackingHit.alphaTrackingFrame)) { break; @@ -303,7 +306,7 @@ void TrackerTraits::computeLayerCells(const int iteration) break; } - if (!track.correctForMaterial(mTrkParams[iteration].LayerxX0[iLayer + iC], mTrkParams[iteration].LayerxX0[iLayer + iC] * constants::Radl * constants::Rho, true)) { + if (!track.correctForMaterial(mTrkParams[iteration].LayerxX0[hitLayer], mTrkParams[iteration].LayerxX0[hitLayer] * constants::Radl * constants::Rho, true)) { break; } @@ -323,12 +326,13 @@ void TrackerTraits::computeLayerCells(const int iteration) TimeEstBC ts = currentTracklet.getTimeStamp(); ts += nextTracklet.getTimeStamp(); if constexpr (decltype(Tag)::value == PassMode::OnePass::value) { - layerCells.emplace_back(iLayer, clusId[0], clusId[1], clusId[2], iTracklet, iNextTracklet, track, chi2, ts); + layerCells.emplace_back(cellTopology.hitLayerMask, clusId[0], clusId[1], clusId[2], iTracklet, iNextTracklet, track, chi2, ts); ++foundCells; } else if constexpr (decltype(Tag)::value == PassMode::TwoPassCount::value) { ++foundCells; } else if constexpr (decltype(Tag)::value == PassMode::TwoPassInsert::value) { - layerCells[offset++] = CellSeed(iLayer, clusId[0], clusId[1], clusId[2], iTracklet, iNextTracklet, track, chi2, ts); + layerCells[offset++] = CellSeed(cellTopology.hitLayerMask, clusId[0], clusId[1], clusId[2], iTracklet, iNextTracklet, track, chi2, ts); + ++foundCells; } else { static_assert(false, "Unknown mode!"); } @@ -338,39 +342,32 @@ void TrackerTraits::computeLayerCells(const int iteration) return foundCells; }; - for (int iLayer = 0; iLayer < mTrkParams[iteration].CellsPerRoad(); ++iLayer) { - if (mTimeFrame->getTracklets()[iLayer + 1].empty() || - mTimeFrame->getTracklets()[iLayer].empty()) { - if (iLayer < mTrkParams[iteration].TrackletsPerRoad()) { - deepVectorClear(mTimeFrame->getTracklets()[iLayer]); - deepVectorClear(mTimeFrame->getTrackletsLabel(iLayer)); - } + for (int cellTopologyId = 0; cellTopologyId < topology.nCells; ++cellTopologyId) { + const auto& cellTopology = topology.getCell(cellTopologyId); + if (mTimeFrame->getTracklets()[cellTopology.firstTransition].empty() || + mTimeFrame->getTracklets()[cellTopology.secondTransition].empty()) { continue; } - auto& layerCells = mTimeFrame->getCells()[iLayer]; - const int currentLayerTrackletsNum{static_cast(mTimeFrame->getTracklets()[iLayer].size())}; + auto& layerCells = mTimeFrame->getCells()[cellTopologyId]; + const int currentLayerTrackletsNum{static_cast(mTimeFrame->getTracklets()[cellTopology.firstTransition].size())}; bounded_vector perTrackletCount(currentLayerTrackletsNum + 1, 0, mMemoryPool.get()); if (mTaskArena->max_concurrency() <= 1) { for (int iTracklet{0}; iTracklet < currentLayerTrackletsNum; ++iTracklet) { - perTrackletCount[iTracklet] = forTrackletCells(PassMode::OnePass{}, iLayer, layerCells, iTracklet); + perTrackletCount[iTracklet] = forTrackletCells(PassMode::OnePass{}, cellTopologyId, layerCells, iTracklet); } std::exclusive_scan(perTrackletCount.begin(), perTrackletCount.end(), perTrackletCount.begin(), 0); } else { tbb::parallel_for(0, currentLayerTrackletsNum, [&](const int iTracklet) { - perTrackletCount[iTracklet] = forTrackletCells(PassMode::TwoPassCount{}, iLayer, layerCells, iTracklet); + perTrackletCount[iTracklet] = forTrackletCells(PassMode::TwoPassCount{}, cellTopologyId, layerCells, iTracklet); }); std::exclusive_scan(perTrackletCount.begin(), perTrackletCount.end(), perTrackletCount.begin(), 0); auto totalCells{perTrackletCount.back()}; if (totalCells == 0) { - if (iLayer > 0) { - auto& lut = mTimeFrame->getCellsLookupTable()[iLayer - 1]; - lut.resize(currentLayerTrackletsNum + 1); - std::fill(lut.begin(), lut.end(), 0); - } - deepVectorClear(mTimeFrame->getTracklets()[iLayer]); - deepVectorClear(mTimeFrame->getTrackletsLabel(iLayer)); + auto& lut = mTimeFrame->getCellsLookupTable()[cellTopologyId]; + lut.resize(currentLayerTrackletsNum + 1); + std::fill(lut.begin(), lut.end(), 0); continue; } layerCells.resize(totalCells); @@ -380,181 +377,184 @@ void TrackerTraits::computeLayerCells(const int iteration) if (offset == perTrackletCount[iTracklet + 1]) { return; } - forTrackletCells(PassMode::TwoPassInsert{}, iLayer, layerCells, iTracklet, offset); + forTrackletCells(PassMode::TwoPassInsert{}, cellTopologyId, layerCells, iTracklet, offset); }); } - if (iLayer > 0) { - auto& lut = mTimeFrame->getCellsLookupTable()[iLayer - 1]; - lut.resize(currentLayerTrackletsNum + 1); - std::copy_n(perTrackletCount.begin(), currentLayerTrackletsNum + 1, lut.begin()); - } + auto& lut = mTimeFrame->getCellsLookupTable()[cellTopologyId]; + lut.resize(currentLayerTrackletsNum + 1); + std::copy_n(perTrackletCount.begin(), currentLayerTrackletsNum + 1, lut.begin()); - if (mTimeFrame->hasMCinformation() && mTrkParams[iteration].createArtefactLabels) { - auto& labels = mTimeFrame->getCellsLabel(iLayer); + if (mTimeFrame->hasMCinformation() && mTrkParams[iteration].CreateArtefactLabels) { + auto& labels = mTimeFrame->getCellsLabel(cellTopologyId); labels.reserve(layerCells.size()); for (const auto& cell : layerCells) { - MCCompLabel currentLab{mTimeFrame->getTrackletsLabel(iLayer)[cell.getFirstTrackletIndex()]}; - MCCompLabel nextLab{mTimeFrame->getTrackletsLabel(iLayer + 1)[cell.getSecondTrackletIndex()]}; + MCCompLabel currentLab{mTimeFrame->getTrackletsLabel(cellTopology.firstTransition)[cell.getFirstTrackletIndex()]}; + MCCompLabel nextLab{mTimeFrame->getTrackletsLabel(cellTopology.secondTransition)[cell.getSecondTrackletIndex()]}; labels.emplace_back(currentLab == nextLab ? currentLab : MCCompLabel()); } } - - // Once layer i cells are built and labelled, the corresponding tracklet artefacts are no longer needed. - deepVectorClear(mTimeFrame->getTracklets()[iLayer]); - deepVectorClear(mTimeFrame->getTrackletsLabel(iLayer)); } }); - // Clear the trailing tracklet artefacts that are not consumed as the first leg of a cell. - for (int iLayer = mTrkParams[iteration].CellsPerRoad(); iLayer < mTrkParams[iteration].TrackletsPerRoad(); ++iLayer) { - deepVectorClear(mTimeFrame->getTracklets()[iLayer]); - deepVectorClear(mTimeFrame->getTrackletsLabel(iLayer)); + for (int transitionId = 0; transitionId < topology.nTransitions; ++transitionId) { + deepVectorClear(mTimeFrame->getTracklets()[transitionId]); + deepVectorClear(mTimeFrame->getTrackletsLabel(transitionId)); } } template void TrackerTraits::findCellsNeighbours(const int iteration) { - struct Neighbor { - int cell{-1}, nextCell{-1}, level{-1}; - }; - + const auto topology = mTimeFrame->getTrackingTopologyView(); mTaskArena->execute([&] { - for (int iLayer{0}; iLayer < mTrkParams[iteration].NeighboursPerRoad(); ++iLayer) { - deepVectorClear(mTimeFrame->getCellsNeighbours()[iLayer]); - deepVectorClear(mTimeFrame->getCellsNeighboursLUT()[iLayer]); - if (mTimeFrame->getCells()[iLayer + 1].empty() || - mTimeFrame->getCellsLookupTable()[iLayer].empty()) { - continue; - } + std::vector> cellsNeighboursByTarget; + cellsNeighboursByTarget.reserve(topology.nCells); + for (int cellTopologyId{0}; cellTopologyId < topology.nCells; ++cellTopologyId) { + deepVectorClear(mTimeFrame->getCellsNeighbours()[cellTopologyId]); + deepVectorClear(mTimeFrame->getCellsNeighboursTopology()[cellTopologyId]); + deepVectorClear(mTimeFrame->getCellsNeighboursLUT()[cellTopologyId]); + cellsNeighboursByTarget.emplace_back(mMemoryPool.get()); + } - int nCells{static_cast(mTimeFrame->getCells()[iLayer].size())}; - bounded_vector cellsNeighbours(mMemoryPool.get()); + for (int outerLayer{0}; outerLayer < NLayers; ++outerLayer) { + for (int cellTopologyId{0}; cellTopologyId < topology.nCells; ++cellTopologyId) { + const auto& cellTopology = topology.getCell(cellTopologyId); + if (cellTopology.hitLayerMask.last() != outerLayer || + mTimeFrame->getCells()[cellTopologyId].empty()) { + continue; + } + const auto successors = topology.getCellsStartingWithTransition(cellTopology.secondTransition); + if (!successors.getEntries()) { + continue; + } - auto forCellNeighbour = [&](auto Tag, int iCell, int offset = 0) -> int { - const auto& currentCellSeed{mTimeFrame->getCells()[iLayer][iCell]}; - const int nextLayerTrackletIndex{currentCellSeed.getSecondTrackletIndex()}; - const int nextLayerFirstCellIndex{mTimeFrame->getCellsLookupTable()[iLayer][nextLayerTrackletIndex]}; - const int nextLayerLastCellIndex{mTimeFrame->getCellsLookupTable()[iLayer][nextLayerTrackletIndex + 1]}; - int foundNextCells{0}; - for (int iNextCell{nextLayerFirstCellIndex}; iNextCell < nextLayerLastCellIndex; ++iNextCell) { - auto nextCellSeed{mTimeFrame->getCells()[iLayer + 1][iNextCell]}; /// copy - if (nextCellSeed.getFirstTrackletIndex() != nextLayerTrackletIndex || !currentCellSeed.getTimeStamp().isCompatible(nextCellSeed.getTimeStamp())) { - break; - } + tbb::enumerable_thread_specific> sourceNeighbours([&]() { return bounded_vector{mMemoryPool.get()}; }); + tbb::parallel_for(0, static_cast(mTimeFrame->getCells()[cellTopologyId].size()), [&](const int iCell) { + auto& localNeighbours = sourceNeighbours.local(); + const auto& currentCellSeed{mTimeFrame->getCells()[cellTopologyId][iCell]}; + const int nextLayerTrackletIndex{currentCellSeed.getSecondTrackletIndex()}; + for (int iSuccessor{0}; iSuccessor < successors.getEntries(); ++iSuccessor) { + const int nextCellTopologyId = topology.cellsByFirstTransition[successors.getFirstEntry() + iSuccessor]; + if (mTimeFrame->getCells()[nextCellTopologyId].empty() || + mTimeFrame->getCellsLookupTable()[nextCellTopologyId].empty()) { + continue; + } + const auto& nextCellLUT = mTimeFrame->getCellsLookupTable()[nextCellTopologyId]; + if (nextLayerTrackletIndex + 1 >= static_cast(nextCellLUT.size())) { + continue; + } + const int nextLayerFirstCellIndex{nextCellLUT[nextLayerTrackletIndex]}; + const int nextLayerLastCellIndex{nextCellLUT[nextLayerTrackletIndex + 1]}; + for (int iNextCell{nextLayerFirstCellIndex}; iNextCell < nextLayerLastCellIndex; ++iNextCell) { + const auto& nextCellSeedRef{mTimeFrame->getCells()[nextCellTopologyId][iNextCell]}; + if (nextCellSeedRef.getFirstTrackletIndex() != nextLayerTrackletIndex || !currentCellSeed.getTimeStamp().isCompatible(nextCellSeedRef.getTimeStamp())) { + break; + } - if (!nextCellSeed.rotate(currentCellSeed.getAlpha()) || - !nextCellSeed.propagateTo(currentCellSeed.getX(), getBz())) { - continue; - } + auto nextCellSeed{mTimeFrame->getCells()[nextCellTopologyId][iNextCell]}; /// copy + if (!nextCellSeed.rotate(currentCellSeed.getAlpha()) || + !nextCellSeed.propagateTo(currentCellSeed.getX(), getBz())) { + continue; + } - float chi2 = currentCellSeed.getPredictedChi2(nextCellSeed); /// TODO: switch to the chi2 wrt cluster to avoid correlation - if (chi2 > mTrkParams[iteration].MaxChi2ClusterAttachment) { - continue; - } + float chi2 = currentCellSeed.getPredictedChi2(nextCellSeed); + if (chi2 > mTrkParams[iteration].MaxChi2ClusterAttachment) { + continue; + } - if constexpr (decltype(Tag)::value == PassMode::OnePass::value) { - cellsNeighbours.emplace_back(iCell, iNextCell, currentCellSeed.getLevel() + 1); - } else if constexpr (decltype(Tag)::value == PassMode::TwoPassCount::value) { - ++foundNextCells; - } else if constexpr (decltype(Tag)::value == PassMode::TwoPassInsert::value) { - cellsNeighbours[offset++] = {iCell, iNextCell, currentCellSeed.getLevel() + 1}; - } else { - static_assert(false, "Unknown mode!"); + const int nextLevel = currentCellSeed.getLevel() + 1; + localNeighbours.emplace_back(cellTopologyId, iCell, nextCellTopologyId, iNextCell, nextLevel); + } } - } - return foundNextCells; - }; - - if (mTaskArena->max_concurrency() <= 1) { - for (int iCell{0}; iCell < nCells; ++iCell) { - forCellNeighbour(PassMode::OnePass{}, iCell); - } - } else { - bounded_vector perCellCount(nCells + 1, 0, mMemoryPool.get()); - tbb::parallel_for(0, nCells, [&](const int iCell) { - perCellCount[iCell] = forCellNeighbour(PassMode::TwoPassCount{}, iCell); }); - std::exclusive_scan(perCellCount.begin(), perCellCount.end(), perCellCount.begin(), 0); - int totalCellNeighbours = perCellCount.back(); - if (totalCellNeighbours == 0) { - deepVectorClear(mTimeFrame->getCellsNeighbours()[iLayer]); - continue; + bounded_vector count(topology.nCells, 0, mMemoryPool.get()); + for (const auto& localNeighbours : sourceNeighbours) { + for (const auto& neigh : localNeighbours) { + ++count[neigh.nextCellTopology]; + } } - cellsNeighbours.resize(totalCellNeighbours); - - tbb::parallel_for(0, nCells, [&](const int iCell) { - int offset = perCellCount[iCell]; - if (offset == perCellCount[iCell + 1]) { - return; + for (size_t i{0}; i < topology.nCells; ++i) { + cellsNeighboursByTarget[i].reserve(count[i]); + } + for (const auto& localNeighbours : sourceNeighbours) { + for (const auto& neigh : localNeighbours) { + cellsNeighboursByTarget[neigh.nextCellTopology].emplace_back(neigh); + if (neigh.level > mTimeFrame->getCells()[neigh.nextCellTopology][neigh.nextCell].getLevel()) { + mTimeFrame->getCells()[neigh.nextCellTopology][neigh.nextCell].setLevel(neigh.level); + } } - forCellNeighbour(PassMode::TwoPassInsert{}, iCell, offset); - }); + } } + } + for (int cellTopologyId{0}; cellTopologyId < topology.nCells; ++cellTopologyId) { + auto& cellsNeighbours = cellsNeighboursByTarget[cellTopologyId]; if (cellsNeighbours.empty()) { continue; } - tbb::parallel_sort(cellsNeighbours.begin(), cellsNeighbours.end(), [](const auto& a, const auto& b) { + std::sort(cellsNeighbours.begin(), cellsNeighbours.end(), [](const auto& a, const auto& b) { return a.nextCell < b.nextCell; }); - auto& cellsNeighbourLUT = mTimeFrame->getCellsNeighboursLUT()[iLayer]; - cellsNeighbourLUT.assign(mTimeFrame->getCells()[iLayer + 1].size(), 0); + auto& cellsNeighbourLUT = mTimeFrame->getCellsNeighboursLUT()[cellTopologyId]; + cellsNeighbourLUT.assign(mTimeFrame->getCells()[cellTopologyId].size(), 0); for (const auto& neigh : cellsNeighbours) { ++cellsNeighbourLUT[neigh.nextCell]; } std::inclusive_scan(cellsNeighbourLUT.begin(), cellsNeighbourLUT.end(), cellsNeighbourLUT.begin()); - mTimeFrame->getCellsNeighbours()[iLayer].reserve(cellsNeighbours.size()); - std::ranges::transform(cellsNeighbours, std::back_inserter(mTimeFrame->getCellsNeighbours()[iLayer]), [](const auto& neigh) { return neigh.cell; }); - - for (auto it = cellsNeighbours.begin(); it != cellsNeighbours.end();) { - int cellIdx = it->nextCell; - int maxLvl = it->level; - while (++it != cellsNeighbours.end() && it->nextCell == cellIdx) { - maxLvl = std::max(maxLvl, it->level); - } - mTimeFrame->getCells()[iLayer + 1][cellIdx].setLevel(maxLvl); - } + mTimeFrame->getCellsNeighbours()[cellTopologyId].reserve(cellsNeighbours.size()); + mTimeFrame->getCellsNeighboursTopology()[cellTopologyId].reserve(cellsNeighbours.size()); + std::ranges::transform(cellsNeighbours, std::back_inserter(mTimeFrame->getCellsNeighbours()[cellTopologyId]), [](const auto& neigh) { return neigh.cell; }); + std::ranges::transform(cellsNeighbours, std::back_inserter(mTimeFrame->getCellsNeighboursTopology()[cellTopologyId]), [](const auto& neigh) { return neigh.cellTopology; }); + } - // clear cells LUT - deepVectorClear(mTimeFrame->getCellsLookupTable()[iLayer]); + // clean up LUTs + for (auto& cellLUT : mTimeFrame->getCellsLookupTable()) { + deepVectorClear(cellLUT); } }); } template template -void TrackerTraits::processNeighbours(int iteration, int iLayer, int iLevel, const bounded_vector& currentCellSeed, const bounded_vector& currentCellId, bounded_vector& updatedCellSeeds, bounded_vector& updatedCellsIds) +void TrackerTraits::processNeighbours(int iteration, int defaultCellTopologyId, int iLevel, const bounded_vector& currentCellSeed, const bounded_vector& currentCellId, const bounded_vector& currentCellTopologyId, bounded_vector& updatedCellSeeds, bounded_vector& updatedCellsIds, bounded_vector& updatedCellsTopologyIds) { auto propagator = o2::base::Propagator::Instance(); mTaskArena->execute([&] { auto forCellNeighbours = [&](auto Tag, int iCell, int offset = 0) -> int { const auto& currentCell{currentCellSeed[iCell]}; + const int cellTopologyId = currentCellTopologyId.empty() ? defaultCellTopologyId : currentCellTopologyId[iCell]; if constexpr (decltype(Tag)::value != PassMode::TwoPassInsert::value) { if (currentCell.getLevel() != iLevel) { return 0; } - if (currentCellId.empty() && (mTimeFrame->isClusterUsed(iLayer, currentCell.getFirstClusterIndex()) || - mTimeFrame->isClusterUsed(iLayer + 1, currentCell.getSecondClusterIndex()) || - mTimeFrame->isClusterUsed(iLayer + 2, currentCell.getThirdClusterIndex()))) { - return 0; /// this we do only on the first iteration, hence the check on currentCellId + if (currentCellId.empty()) { + for (int layer = 0; layer < NLayers; ++layer) { + const int clusterIndex = currentCell.getCluster(layer); + if (clusterIndex != constants::UnusedIndex && mTimeFrame->isClusterUsed(layer, clusterIndex)) { + return 0; /// this we do only on the first iteration, hence the check on currentCellId + } + } } } const int cellId = currentCellId.empty() ? iCell : currentCellId[iCell]; - const int startNeighbourId{cellId ? mTimeFrame->getCellsNeighboursLUT()[iLayer - 1][cellId - 1] : 0}; - const int endNeighbourId{mTimeFrame->getCellsNeighboursLUT()[iLayer - 1][cellId]}; + if (cellTopologyId < 0 || mTimeFrame->getCellsNeighboursLUT()[cellTopologyId].empty()) { + return 0; + } + const int startNeighbourId{cellId ? mTimeFrame->getCellsNeighboursLUT()[cellTopologyId][cellId - 1] : 0}; + const int endNeighbourId{mTimeFrame->getCellsNeighboursLUT()[cellTopologyId][cellId]}; int foundSeeds{0}; for (int iNeighbourCell{startNeighbourId}; iNeighbourCell < endNeighbourId; ++iNeighbourCell) { - const int neighbourCellId = mTimeFrame->getCellsNeighbours()[iLayer - 1][iNeighbourCell]; - const auto& neighbourCell = mTimeFrame->getCells()[iLayer - 1][neighbourCellId]; + const int neighbourCellTopologyId = mTimeFrame->getCellsNeighboursTopology()[cellTopologyId][iNeighbourCell]; + const int neighbourCellId = mTimeFrame->getCellsNeighbours()[cellTopologyId][iNeighbourCell]; + const auto& neighbourCell = mTimeFrame->getCells()[neighbourCellTopologyId][neighbourCellId]; if (neighbourCell.getSecondTrackletIndex() != currentCell.getFirstTrackletIndex()) { continue; } @@ -564,7 +564,9 @@ void TrackerTraits::processNeighbours(int iteration, int iLayer, int iL if (currentCell.getLevel() - 1 != neighbourCell.getLevel()) { continue; } - if (mTimeFrame->isClusterUsed(iLayer - 1, neighbourCell.getFirstClusterIndex())) { + const int neighbourLayer = neighbourCell.getInnerLayer(); + const int neighbourCluster = neighbourCell.getFirstClusterIndex(); + if (mTimeFrame->isClusterUsed(neighbourLayer, neighbourCluster)) { continue; } @@ -572,7 +574,7 @@ void TrackerTraits::processNeighbours(int iteration, int iLayer, int iL TrackSeedN seed{currentCell}; seed.getTimeStamp() = currentCell.getTimeStamp(); seed.getTimeStamp() += neighbourCell.getTimeStamp(); - const auto& trHit = mTimeFrame->getTrackingFrameInfoOnLayer(iLayer - 1)[neighbourCell.getFirstClusterIndex()]; + const auto& trHit = mTimeFrame->getTrackingFrameInfoOnLayer(neighbourLayer)[neighbourCluster]; if (!seed.rotate(trHit.alphaTrackingFrame)) { continue; @@ -583,7 +585,7 @@ void TrackerTraits::processNeighbours(int iteration, int iLayer, int iL } if (mTrkParams[iteration].CorrType == o2::base::PropagatorF::MatCorrType::USEMatCorrNONE) { - if (!seed.correctForMaterial(mTrkParams[iteration].LayerxX0[iLayer - 1], mTrkParams[iteration].LayerxX0[iLayer - 1] * constants::Radl * constants::Rho, true)) { + if (!seed.correctForMaterial(mTrkParams[iteration].LayerxX0[neighbourLayer], mTrkParams[iteration].LayerxX0[neighbourLayer] * constants::Radl * constants::Rho, true)) { continue; } } @@ -598,7 +600,10 @@ void TrackerTraits::processNeighbours(int iteration, int iLayer, int iL } if constexpr (decltype(Tag)::value != PassMode::TwoPassCount::value) { - seed.getClusters()[iLayer - 1] = neighbourCell.getFirstClusterIndex(); + seed.getClusters()[neighbourLayer] = neighbourCluster; + auto mask = seed.getHitLayerMask(); + mask.set(neighbourLayer); + seed.setHitLayerMask(mask); seed.setLevel(neighbourCell.getLevel()); seed.setFirstTrackletIndex(neighbourCell.getFirstTrackletIndex()); seed.setSecondTrackletIndex(neighbourCell.getSecondTrackletIndex()); @@ -607,11 +612,13 @@ void TrackerTraits::processNeighbours(int iteration, int iLayer, int iL if constexpr (decltype(Tag)::value == PassMode::OnePass::value) { updatedCellSeeds.push_back(seed); updatedCellsIds.push_back(neighbourCellId); + updatedCellsTopologyIds.push_back(neighbourCellTopologyId); } else if constexpr (decltype(Tag)::value == PassMode::TwoPassCount::value) { ++foundSeeds; } else if constexpr (decltype(Tag)::value == PassMode::TwoPassInsert::value) { updatedCellSeeds[offset] = seed; - updatedCellsIds[offset++] = neighbourCellId; + updatedCellsIds[offset] = neighbourCellId; + updatedCellsTopologyIds[offset++] = neighbourCellTopologyId; } else { static_assert(false, "Unknown mode!"); } @@ -637,6 +644,7 @@ void TrackerTraits::processNeighbours(int iteration, int iLayer, int iL } updatedCellSeeds.resize(totalNeighbours); updatedCellsIds.resize(totalNeighbours); + updatedCellsTopologyIds.resize(totalNeighbours); tbb::parallel_for(0, nCells, [&](const int iCell) { int offset = perCellCount[iCell]; @@ -653,9 +661,7 @@ template void TrackerTraits::findRoads(const int iteration) { bounded_vector> firstClusters(mTrkParams[iteration].NLayers, bounded_vector(mMemoryPool.get()), mMemoryPool.get()); - bounded_vector> sharedFirstClusters(mTrkParams[iteration].NLayers, bounded_vector(mMemoryPool.get()), mMemoryPool.get()); firstClusters.resize(mTrkParams[iteration].NLayers); - sharedFirstClusters.resize(mTrkParams[iteration].NLayers); const auto propagator = o2::base::Propagator::Instance(); const TrackingFrameInfo* tfInfos[NLayers]{}; const Cluster* unsortedClusters[NLayers]{}; @@ -663,33 +669,42 @@ void TrackerTraits::findRoads(const int iteration) tfInfos[iLayer] = mTimeFrame->getTrackingFrameInfoOnLayer(iLayer).data(); unsortedClusters[iLayer] = mTimeFrame->getUnsortedClusters()[iLayer].data(); } + const auto topology = mTimeFrame->getTrackingTopologyView(); for (int startLevel{mTrkParams[iteration].CellsPerRoad()}; startLevel >= mTrkParams[iteration].CellMinimumLevel(); --startLevel) { auto seedFilter = [&](const auto& seed) { - return seed.getQ2Pt() <= 1.e3 && seed.getChi2() <= mTrkParams[iteration].MaxChi2NDF * ((startLevel + 2) * 2 - 5); + return seed.getHitLayerMask().isAllowed(mTrkParams[iteration].MaxHoles, mTrkParams[iteration].HoleLayerMask) && + seed.getHitLayerMask().length() >= mTrkParams[iteration].MinTrackLength && + seed.getQ2Pt() <= 1.e3 && seed.getChi2() <= mTrkParams[iteration].MaxChi2NDF * ((startLevel + 2) * 2 - 5); }; bounded_vector trackSeeds(mMemoryPool.get()); - for (int startLayer{mTrkParams[iteration].NeighboursPerRoad()}; startLayer >= startLevel - 1; --startLayer) { - if ((mTrkParams[iteration].StartLayerMask & (1 << (startLayer + 2))) == 0) { + for (int startCellTopologyId{0}; startCellTopologyId < topology.nCells; ++startCellTopologyId) { + const int startLayer = topology.getCell(startCellTopologyId).hitLayerMask.last(); + if ((mTrkParams[iteration].StartLayerMask & (1 << startLayer)) == 0 || + mTimeFrame->getCells()[startCellTopologyId].empty()) { continue; } bounded_vector lastCellId(mMemoryPool.get()), updatedCellId(mMemoryPool.get()); + bounded_vector lastCellTopologyId(mMemoryPool.get()), updatedCellTopologyId(mMemoryPool.get()); bounded_vector lastCellSeed(mMemoryPool.get()), updatedCellSeed(mMemoryPool.get()); - processNeighbours(iteration, startLayer, startLevel, mTimeFrame->getCells()[startLayer], lastCellId, updatedCellSeed, updatedCellId); + processNeighbours(iteration, startCellTopologyId, startLevel, mTimeFrame->getCells()[startCellTopologyId], lastCellId, lastCellTopologyId, updatedCellSeed, updatedCellId, updatedCellTopologyId); int level = startLevel; - for (int iLayer{startLayer - 1}; iLayer > 0 && level > 2; --iLayer) { + while (level > 2 && !updatedCellSeed.empty()) { lastCellSeed.swap(updatedCellSeed); lastCellId.swap(updatedCellId); + lastCellTopologyId.swap(updatedCellTopologyId); deepVectorClear(updatedCellSeed); /// tame the memory peaks deepVectorClear(updatedCellId); /// tame the memory peaks - processNeighbours(iteration, iLayer, --level, lastCellSeed, lastCellId, updatedCellSeed, updatedCellId); + deepVectorClear(updatedCellTopologyId); + processNeighbours(iteration, constants::UnusedIndex, --level, lastCellSeed, lastCellId, lastCellTopologyId, updatedCellSeed, updatedCellId, updatedCellTopologyId); } - deepVectorClear(lastCellId); /// tame the memory peaks - deepVectorClear(lastCellSeed); /// tame the memory peaks + deepVectorClear(lastCellId); /// tame the memory peaks + deepVectorClear(lastCellTopologyId); /// tame the memory peaks + deepVectorClear(lastCellSeed); /// tame the memory peaks if (!updatedCellSeed.empty()) { trackSeeds.reserve(trackSeeds.size() + std::count_if(updatedCellSeed.begin(), updatedCellSeed.end(), seedFilter)); @@ -767,17 +782,19 @@ void TrackerTraits::findRoads(const int iteration) }); std::sort(tracks.begin(), tracks.end(), [](const auto& a, const auto& b) { - return a.getChi2() < b.getChi2(); + return track::isBetter(a, b); }); - acceptTracks(iteration, tracks, firstClusters, sharedFirstClusters); + acceptTracks(iteration, tracks, firstClusters); } - markTracks(iteration, sharedFirstClusters); + markTracks(iteration); } template -void TrackerTraits::acceptTracks(int iteration, bounded_vector& tracks, bounded_vector>& firstClusters, bounded_vector>& sharedFirstClusters) +void TrackerTraits::acceptTracks(int iteration, bounded_vector& tracks, bounded_vector>& firstClusters) { + auto& trks = mTimeFrame->getTracks(); + trks.reserve(trks.size() + tracks.size()); const float smallestROFHalf = mTimeFrame->getROFOverlapTableView().getClockLayer().mROFLength * 0.5f; for (auto& track : tracks) { int nShared = 0; @@ -837,38 +854,55 @@ void TrackerTraits::acceptTracks(int iteration, bounded_vectorgetTracks().emplace_back(track); + trks.emplace_back(track); if (mTrkParams[iteration].AllowSharingFirstCluster) { firstClusters[firstLayer].push_back(firstCluster); - if (isFirstShared) { - sharedFirstClusters[firstLayer].push_back(firstCluster); - } } } } template -void TrackerTraits::markTracks(int iteration, bounded_vector>& sharedFirstClusters) +void TrackerTraits::markTracks(int iteration) { if (mTrkParams[iteration].AllowSharingFirstCluster) { /// Now we have to set the shared cluster flag - for (int iLayer{0}; iLayer < mTrkParams[iteration].NLayers; ++iLayer) { - std::sort(sharedFirstClusters[iLayer].begin(), sharedFirstClusters[iLayer].end()); - } + auto& tracks = mTimeFrame->getTracks(); - for (auto& track : mTimeFrame->getTracks()) { - int firstLayer{mTrkParams[iteration].NLayers}, firstCluster{constants::UnusedIndex}; - for (int iLayer{0}; iLayer < mTrkParams[iteration].NLayers; ++iLayer) { - if (track.getClusterIndex(iLayer) == constants::UnusedIndex) { - continue; - } - firstLayer = iLayer; - firstCluster = track.getClusterIndex(iLayer); - break; + bounded_vector fclusSort(tracks.size(), mMemoryPool.get()); + std::iota(fclusSort.begin(), fclusSort.end(), 0); + std::sort(fclusSort.begin(), fclusSort.end(), [&tracks](int a, int b) { + return tracks[a].getFirstLayerClusterIndex() < tracks[b].getFirstLayerClusterIndex(); + }); + + auto areTracksSelected = [this, iteration](const TrackITSExt& t1, const TrackITSExt& t2) { + const auto t1FirstLayer{t1.getFirstClusterLayer()}, t2FirstLayer{t2.getFirstClusterLayer()}; + if (t1FirstLayer != t2FirstLayer) { + return false; + } + if (mTimeFrame->getClusterROF(t1FirstLayer, t1.getClusterIndex(t1FirstLayer)) != mTimeFrame->getClusterROF(t2FirstLayer, t2.getClusterIndex(t2FirstLayer))) { + return false; + } + if (!math_utils::isPhiDifferenceBelow(t1.getPhi(), t2.getPhi(), mTrkParams[iteration].SharedClusterMaxDeltaPhi)) { + return false; + } + if (std::abs(t1.getEta() - t2.getEta()) > mTrkParams[iteration].SharedClusterMaxDeltaEta) { + return false; } - if (std::binary_search(sharedFirstClusters[firstLayer].begin(), sharedFirstClusters[firstLayer].end(), firstCluster)) { - track.setSharedClusters(); + if (mTrkParams[iteration].SharedClusterOppositeSign && t1.getSign() == t2.getSign()) { + return false; + } + return true; + }; + + for (int i{0}; i < static_cast(fclusSort.size()); ++i) { + auto& track = tracks[fclusSort[i]]; + for (int j{i + 1}; j < static_cast(fclusSort.size()) && tracks[fclusSort[j]].getFirstLayerClusterIndex() == track.getFirstLayerClusterIndex(); ++j) { + auto& track2 = tracks[fclusSort[j]]; + if (areTracksSelected(track, track2)) { + track.setSharedClusters(); + track2.setSharedClusters(); + } } } } @@ -897,13 +931,13 @@ void TrackerTraits::setNThreads(int n, std::shared_ptr } template class TrackerTraits<7>; -template void TrackerTraits<7>::processNeighbours(int, int, int, const bounded_vector&, const bounded_vector&, bounded_vector>&, bounded_vector&); -template void TrackerTraits<7>::processNeighbours>(int, int, int, const bounded_vector>&, const bounded_vector&, bounded_vector>&, bounded_vector&); +template void TrackerTraits<7>::processNeighbours(int, int, int, const bounded_vector&, const bounded_vector&, const bounded_vector&, bounded_vector>&, bounded_vector&, bounded_vector&); +template void TrackerTraits<7>::processNeighbours>(int, int, int, const bounded_vector>&, const bounded_vector&, const bounded_vector&, bounded_vector>&, bounded_vector&, bounded_vector&); // ALICE3 upgrade #ifdef ENABLE_UPGRADES template class TrackerTraits<11>; -template void TrackerTraits<11>::processNeighbours(int, int, int, const bounded_vector&, const bounded_vector&, bounded_vector>&, bounded_vector&); -template void TrackerTraits<11>::processNeighbours>(int, int, int, const bounded_vector>&, const bounded_vector&, bounded_vector>&, bounded_vector&); +template void TrackerTraits<11>::processNeighbours(int, int, int, const bounded_vector&, const bounded_vector&, const bounded_vector&, bounded_vector>&, bounded_vector&, bounded_vector&); +template void TrackerTraits<11>::processNeighbours>(int, int, int, const bounded_vector>&, const bounded_vector&, const bounded_vector&, bounded_vector>&, bounded_vector&, bounded_vector&); #endif } // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx index a48f23c5eb8f1..7f10419d63fea 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx @@ -50,9 +50,16 @@ void ITSTrackingInterface::initialise() } auto trackParams = TrackingMode::getTrackingParameters(mMode); auto vertParams = TrackingMode::getVertexingParameters(mMode); + overrideParameters(trackParams, vertParams); LOGP(info, "Initializing tracker in {} phase reconstruction with {} passes for tracking and {}/{} for vertexing", TrackingMode::toString(mMode), trackParams.size(), o2::its::VertexerParamConfig::Instance().nIterations, vertParams.size()); mTracker->setParameters(trackParams); mVertexer->setParameters(vertParams); + TrackingParameters vertexTrackingParams; + mTimeFrame->initVertexingTopology(vertexTrackingParams); + if (!trackParams.empty()) { + mTimeFrame->initDefaultTrackingTopology(trackParams[0], NLayers); + mTimeFrame->initTrackerTopologies(gsl::span(trackParams.data(), trackParams.size())); + } if (mMode == TrackingMode::Cosmics) { mRunVertexer = false; @@ -372,7 +379,7 @@ void ITSTrackingInterface::updateTimeDependentParams(framework::ProcessingContex } if (!initOnceDone) { // this params need to be queried only once initOnceDone = true; - pc.inputs().get("itscldict"); // just to trigger the finaliseCCDB + requestTopologyDictionary(pc); pc.inputs().get*>("itsalppar"); if (pc.inputs().getPos("itsTGeo") >= 0) { pc.inputs().get("itsTGeo"); @@ -485,6 +492,11 @@ void ITSTrackingInterface::setTraitsFromProvider(VertexerTraitsN* vertexerTraits mVertexer->setMemoryPool(mMemoryPool); } +void ITSTrackingInterface::requestTopologyDictionary(framework::ProcessingContext& pc) +{ + pc.inputs().get("itscldict"); // just to trigger the finaliseCCDB +} + void ITSTrackingInterface::loadROF(gsl::span& trackROFspan, gsl::span clusters, gsl::span::iterator& pattIt, diff --git a/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx b/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx index 2acbec7fe8bc0..ba37275f87688 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx @@ -61,9 +61,14 @@ float Vertexer::clustersToVertices(LogFunc logger) mMemoryPool->setMaxMemory(mVertParams[iteration].MaxMemory); unsigned int nTracklets01{0}, nTracklets12{0}; logger(fmt::format("=== ITS {} Seeding vertexer iteration {} summary:", mTraits->getName(), iteration)); - trkPars.PhiBins = mTraits->getVertexingParameters()[0].PhiBins; - trkPars.ZBins = mTraits->getVertexingParameters()[0].ZBins; - auto timeInitIteration = evaluateTask(&Vertexer::initialiseVertexer, StateNames[mCurStep = Init], iteration, evalLog, trkPars, iteration); + const auto& currentVtxPars = mTraits->getVertexingParameters()[iteration]; + trkPars.PhiBins = currentVtxPars.PhiBins; + trkPars.ZBins = currentVtxPars.ZBins; + trkPars.LayerZ = currentVtxPars.LayerZ; + trkPars.LayerRadii = currentVtxPars.LayerRadii; + trkPars.PassFlags = mVertParams[iteration].PassFlags; + trkPars.PassFlags.set(IterationStep::FirstPass, IterationStep::RebuildClusterLUT); + auto timeInitIteration = evaluateTask(&Vertexer::initialiseVertexer, StateNames[mCurStep = Init], iteration, evalLog, trkPars); auto timeTrackletIteration = evaluateTask(&Vertexer::findTracklets, StateNames[mCurStep = Trackleting], iteration, evalLog, iteration); nTracklets01 = mTimeFrame->getTotalTrackletsTF(0); nTracklets12 = mTimeFrame->getTotalTrackletsTF(1); @@ -79,7 +84,6 @@ float Vertexer::clustersToVertices(LogFunc logger) // update LUT with all currently found vertices so in second iteration we can check vertPerROFThreshold sortVertices(); - mTimeFrame->updateROFVertexLookupTable(); } completed = true; } catch (const BoundedMemoryResource::MemoryLimitExceeded& err) { @@ -128,6 +132,8 @@ void Vertexer::sortVertices() } mc.swap(sortedMC); } + // update LUT after sorting + mTimeFrame->updateROFVertexLookupTable(); } template diff --git a/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx index 00674b715b97d..237e99e57e0da 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx @@ -156,6 +156,12 @@ void trackletSelectionKernelHost( } } // namespace +template +void VertexerTraits::initialise(const TrackingParameters& trackingParams) +{ + mTimeFrame->initialise(trackingParams, 3); +} + template void VertexerTraits::updateVertexingParameters(const std::vector& vrtPar) { @@ -528,7 +534,7 @@ void VertexerTraits::computeVertices(const int iteration) cluster.getRMS2(), (ushort)cluster.getSize(), cluster.getAvgDistance2()}; - if (iteration) { + if (mVrtParams[iteration].PassFlags[IterationStep::MarkVerticesAsUPC]) { vertex.setFlags(Vertex::UPCMode); } vertex.setTimeStamp(cluster.getTimeStamp()); @@ -629,7 +635,8 @@ void VertexerTraits::setNThreads(int n, std::shared_ptr bool VertexerTraits::skipROF(int iteration, int rof) const { - return iteration && (int)mTimeFrame->getROFVertexLookupTableView().getVertices(1, rof).getEntries() > mVrtParams[iteration].vertPerRofThreshold; + return mVrtParams[iteration].PassFlags[IterationStep::SkipROFsAboveThreshold] && + (int)mTimeFrame->getROFVertexLookupTableView().getVertices(1, rof).getEntries() > mVrtParams[iteration].vertPerRofThreshold; } template class VertexerTraits<7>; diff --git a/Detectors/ITSMFT/ITS/tracking/test/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/test/CMakeLists.txt index 063583b4cfa1b..f8fce10b78602 100644 --- a/Detectors/ITSMFT/ITS/tracking/test/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/test/CMakeLists.txt @@ -20,3 +20,9 @@ o2_add_test(roflookuptables COMPONENT_NAME its-tracking LABELS "its;tracking" PUBLIC_LINK_LIBRARIES O2::ITStracking) + +o2_add_test(trackingtopology + SOURCES testTrackingTopology.cxx + COMPONENT_NAME its-tracking + LABELS "its;tracking" + PUBLIC_LINK_LIBRARIES O2::ITStracking) diff --git a/Detectors/ITSMFT/ITS/tracking/test/testROFLookupTables.cxx b/Detectors/ITSMFT/ITS/tracking/test/testROFLookupTables.cxx index dd98a75efca7c..9626e42efd547 100644 --- a/Detectors/ITSMFT/ITS/tracking/test/testROFLookupTables.cxx +++ b/Detectors/ITSMFT/ITS/tracking/test/testROFLookupTables.cxx @@ -559,6 +559,7 @@ BOOST_AUTO_TEST_CASE(rofvertex_basic) vertices.push_back(vert1); table.update(vertices.data(), vertices.size()); const auto view = table.getView(); + view.printAll(); } BOOST_AUTO_TEST_CASE(rofvertex_init_with_vertices) diff --git a/Detectors/ITSMFT/ITS/tracking/test/testTrackingTopology.cxx b/Detectors/ITSMFT/ITS/tracking/test/testTrackingTopology.cxx new file mode 100644 index 0000000000000..4944d00b15fea --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/test/testTrackingTopology.cxx @@ -0,0 +1,119 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#define BOOST_TEST_MODULE ITS TrackingTopology +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +#include +#include "ITStracking/TrackingTopology.h" + +/// -------- Tests -------- +BOOST_AUTO_TEST_CASE(layermask_holes_and_length) +{ + using o2::its::LayerMask; + + const LayerMask layer3Hole{0x77}; // layers 0,1,2,4,5,6 + BOOST_CHECK_EQUAL(layer3Hole.count(), 6); + BOOST_CHECK_EQUAL(layer3Hole.length(), 7); + BOOST_CHECK_EQUAL(layer3Hole.holeMask().value(), 0x08); + BOOST_CHECK(layer3Hole.isAllowed(1, 0x08)); + BOOST_CHECK(!layer3Hole.isAllowed(0, 0x08)); + + const LayerMask missingLeadingLayer0{0x7e}; // layers 1..6 + BOOST_CHECK_EQUAL(missingLeadingLayer0.count(), 6); + BOOST_CHECK_EQUAL(missingLeadingLayer0.length(), 6); + BOOST_CHECK_EQUAL(missingLeadingLayer0.holeMask().value(), 0x00); + + const LayerMask missingTrailingLayer6{0x3f}; // layers 0..5 + BOOST_CHECK_EQUAL(missingTrailingLayer6.count(), 6); + BOOST_CHECK_EQUAL(missingTrailingLayer6.length(), 6); + BOOST_CHECK_EQUAL(missingTrailingLayer6.holeMask().value(), 0x00); +} + +BOOST_AUTO_TEST_CASE(layermask_topological_length_counts_internal_holes) +{ + using o2::its::LayerMask; + + BOOST_CHECK_GE(LayerMask{0x7f}.length(), 7); // 7 clusters + BOOST_CHECK_GE(LayerMask{0x77}.length(), 7); // 6 clusters + layer-3 hole + BOOST_CHECK_LT(LayerMask{0x7e}.length(), 7); // missing leading layer + BOOST_CHECK_LT(LayerMask{0x3f}.length(), 7); // missing trailing layer +} + +BOOST_AUTO_TEST_CASE(trackingtopology_basic) +{ + o2::its::TrackingTopology<4> topo; + topo.init(4, 0, 0); + const auto view = topo.getView(); + view.print(); + + BOOST_CHECK_EQUAL(view.nTransitions, 3); + for (int i{0}; i < 3; ++i) { + const auto& tra = view.getTransition(i); + BOOST_CHECK_EQUAL(tra.fromLayer, i); + BOOST_CHECK_EQUAL(tra.toLayer, i + 1); + } + + BOOST_CHECK_EQUAL(view.nCells, 2); + for (int i{0}; i < 2; ++i) { + const auto& cell = view.getCell(i); + BOOST_CHECK_EQUAL(cell.firstTransition, i); + BOOST_CHECK_EQUAL(cell.secondTransition, i + 1); + } +} + +BOOST_AUTO_TEST_CASE(trackingtopology_single_allowed_hole) +{ + o2::its::TrackingTopology<5> topo; + topo.init(5, 1, 1 << 2); + const auto view = topo.getView(); + view.print(); + + BOOST_CHECK_EQUAL(view.nTransitions, 5); + BOOST_CHECK_EQUAL(view.nCells, 5); + + bool hasHoleTransition = false; + for (int i{0}; i < view.nTransitions; ++i) { + const auto& transition = view.getTransition(i); + hasHoleTransition |= transition.fromLayer == 1 && transition.toLayer == 3; + BOOST_CHECK(o2::its::LayerMask::skipped(transition.fromLayer, transition.toLayer).isAllowedHoleMask(1, 1 << 2)); + } + BOOST_CHECK(hasHoleTransition); + + bool hasHoleCell = false; + for (int i{0}; i < view.nCells; ++i) { + const auto& cell = view.getCell(i); + hasHoleCell |= cell.hitLayerMask.value() == 0x0b; // layers 0,1,3 + BOOST_CHECK(cell.hitLayerMask.isAllowed(1, 1 << 2)); + } + BOOST_CHECK(hasHoleCell); +} + +BOOST_AUTO_TEST_CASE(trackingtopology_rejects_wrong_hole_layer) +{ + o2::its::TrackingTopology<5> topo; + topo.init(5, 1, 1 << 2); + const auto view = topo.getView(); + view.print(); + + for (int i{0}; i < view.nTransitions; ++i) { + const auto& transition = view.getTransition(i); + BOOST_CHECK(!(transition.fromLayer == 0 && transition.toLayer == 2)); + BOOST_CHECK(!(transition.fromLayer == 2 && transition.toLayer == 4)); + } + + for (int i{0}; i < view.nCells; ++i) { + const auto& cell = view.getCell(i); + BOOST_CHECK(cell.hitLayerMask.holeMask().isSubsetOf(1 << 2)); + } +} diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h index 4f9bc90c1c758..76ac8878562de 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h @@ -356,8 +356,10 @@ void CTFCoder::decompress(const CompressedClusters& compCl, VROF& rofRecVec, assert(chipCount == compCl.header.nChips); if (clCount != compCl.header.nClusters) { - LOG(error) << "expected " << compCl.header.nClusters << " but counted " << clCount << " in ROFRecords"; - throw std::runtime_error("mismatch between expected and counter number of clusters"); + LOGP(error, "expected {} but counted {} clusters in {} ROFRecords", compCl.header.nClusters, clCount, compCl.header.nROFs); + if (clCount > compCl.header.nClusters) { + throw std::runtime_error("mismatch between expected and counter number of clusters"); + } } } @@ -456,8 +458,10 @@ void CTFCoder::decompress(const CompressedClusters& compCl, VROF& rofRecVec, assert(chipCount == compCl.header.nChips); if (clCount != compCl.header.nClusters) { - LOG(error) << "expected " << compCl.header.nClusters << " but counted " << clCount << " in ROFRecords"; - throw std::runtime_error("mismatch between expected and counter number of clusters"); + LOGP(error, "expected {} but counted {} clusters in {} ROFRecords", compCl.header.nClusters, clCount, compCl.header.nROFs); + if (clCount > compCl.header.nClusters) { + throw std::runtime_error("mismatch between expected and counter number of clusters"); + } } } diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingMFT.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingMFT.h index eee9bdbb6a4dc..63d37a25ffbc9 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingMFT.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingMFT.h @@ -73,16 +73,15 @@ class ChipMappingMFT ///< total number of RUs static constexpr Int_t getNRUs() { return NRUs; } - ///< get FEEId of the RU (software id of the RU), read via given link + ///< get software id of the RU, from first 8 bits of FEEID (HW id of RU) uint8_t FEEId2RUSW(uint16_t hw) const { return mFEEId2RUSW[hw & 0xff]; } - ///< get HW id of the RU (software id of the RU) + ///< get FEEID, from software id of the RU and link number uint16_t RUSW2FEEId(uint16_t sw, uint16_t linkID = 0) const { return ((linkID << 8) + mRUInfo[sw].idHW); } ///< compose FEEid for given stave (ru) relative to layer and link, see documentation in the constructor uint16_t composeFEEId(uint16_t layer, uint16_t ruOnLayer, uint16_t link) const { - // only one link is used // ruOnLayer is 0, 1, 2, 3 for half = 0 // 4, 5, 6, 7 1 auto dhalf = std::div(ruOnLayer, 4); @@ -114,7 +113,7 @@ class ChipMappingMFT face = (feeID >> 2) & 0x1; } - ///< get info on sw RU + ///< get info on sw RU corresponding to given FEEID const RUInfo* getRUInfoFEEId(Int_t feeID) const { return &mRUInfo[FEEId2RUSW(feeID)]; } ///< get number of chips served by single cable on given RU type @@ -123,13 +122,13 @@ class ChipMappingMFT return ((0x1 << 7) + (cableHW & 0x1f)); } - ///< convert HW cable ID to its position on the ActiveLanes word in the GBT.header for given RU type + ///< convert HW cable ID to its position on the ActiveLanes word in the GBT.header for given RU type (note: this position is equal to the HW cable ID) uint8_t cableHW2Pos(uint8_t ruType, uint8_t hwid) const { return mCableHW2Pos[ruType][hwid]; } ///< convert HW cable ID to SW ID for give RU type uint8_t cableHW2SW(uint8_t ruType, uint8_t hwid) const { return hwid < mCableHW2SW[ruType].size() ? mCableHW2SW[ruType][hwid] : 0xff; } - ///< convert cable iterator ID to its position on the ActiveLanes word in the GBT.header for given RU type + ///< convert cable iterator ID (i.e. chipOnModule) to its position on the ActiveLanes word in the GBT.header for given RU type (note: this position is equal to the HW cable ID) uint8_t cablePos(uint8_t ruType, uint8_t id) const { return mCablePos[ruType][id]; } ///< get chipID on module from chip global SW ID, cable SW ID and stave (RU) info @@ -139,7 +138,7 @@ class ChipMappingMFT return 0xffff; } - ///< get chip global SW ID from chipID on module, cable SW ID and stave (RU) info + ///< get chip global SW ID from cable HW ID and stave (RU) info (note: chOnModuleHW is unused) uint16_t getGlobalChipID(uint16_t chOnModuleHW, int cableHW, const RUInfo& ruInfo) const { auto chipOnRU = cableHW2SW(ruInfo.ruType, cableHW); @@ -393,11 +392,11 @@ class ChipMappingMFT private: Int_t invalid() const; - static constexpr Int_t NRUs = NLayers * NZonesPerLayer; + static constexpr Int_t NRUs = NLayers * NZonesPerLayer; // 10 layers * 8 zones per layer static constexpr Int_t NModules = 280; static constexpr Int_t NChipsInfo = 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 16 + 17 + 18 + 19 + 14; static constexpr Int_t NChipsPerCable = 1; - static constexpr Int_t NLinks = 1; + static constexpr Int_t NLinks = 3; static constexpr Int_t NConnectors = 5; static constexpr Int_t NMaxChipsPerLadder = 5; static constexpr Int_t NRUCables = 25; diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h index 0bdbb701a9356..dd3052e2cc5bd 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h @@ -236,6 +236,8 @@ class Clusterer ///< load the dictionary of cluster topologies void loadDictionary(const std::string& fileName) { mPattIdConverter.loadDictionary(fileName); } void setDictionary(const TopologyDictionary* dict) { mPattIdConverter.setDictionary(dict); } + const TopologyDictionary& getDictionary() const { return mPattIdConverter.getDictionary(); } + auto& getPattIdConverter() const { return mPattIdConverter; } TStopwatch& getTimer() { return mTimer; } // cannot be const TStopwatch& getTimerMerge() { return mTimerMerge; } // cannot be const diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/LookUp.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/LookUp.h index 3537a1f408886..4f84a838efc70 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/LookUp.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/LookUp.h @@ -43,7 +43,7 @@ class LookUp 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; } private: TopologyDictionary mDictionary; diff --git a/Detectors/ITSMFT/common/reconstruction/src/ChipMappingMFT.cxx b/Detectors/ITSMFT/common/reconstruction/src/ChipMappingMFT.cxx index de2358469e894..b79c529bef803 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/ChipMappingMFT.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/ChipMappingMFT.cxx @@ -1624,7 +1624,7 @@ ChipMappingMFT::ChipMappingMFT() { // init chips info - uint32_t maxRUHW = composeFEEId(NLayers - 1, NZonesPerLayer - 1, NLinks - 1); // Max possible FEE ID + uint32_t maxRUHW = composeFEEId(NLayers - 1, NZonesPerLayer - 1, 0); // Max possible RU HW ID (first 8 bits of max FEEID, while link stored in 9th and 10th bit of FEEID) mFEEId2RUSW.resize(maxRUHW + 1, 0xff); int curLayer = -1, curZone = -1, curHalf = -1; @@ -1698,8 +1698,8 @@ ChipMappingMFT::ChipMappingMFT() auto& ruInfo = mRUInfo[ctrRU]; ruInfo.idSW = ctrRU++; - // map FEEIds (RU read out by at most 3 GBT links) to SW ID - ruInfo.idHW = composeFEEId(iLayer, iZone, 0); // FEEId for link 0 + // map RU HW ID (RU read out by at most 3 GBT links) to SW ID + ruInfo.idHW = composeFEEId(iLayer, iZone, 0); // RU HW ID (first 8 bits of FEEID) mFEEId2RUSW[ruInfo.idHW] = ruInfo.idSW; ruInfo.layer = iLayer; ruInfo.ruType = ZoneRUType[iZone % 4][iLayer / 2]; diff --git a/Detectors/ITSMFT/common/reconstruction/src/RawPixelDecoder.cxx b/Detectors/ITSMFT/common/reconstruction/src/RawPixelDecoder.cxx index 7158551e02e20..df877cd38c578 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/RawPixelDecoder.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/RawPixelDecoder.cxx @@ -235,15 +235,6 @@ void RawPixelDecoder::setupLinks(InputRecord& inputs) auto nLinks = mGBTLinks.size(); auto origin = (mUserDataOrigin == o2::header::gDataOriginInvalid) ? mMAP.getOrigin() : mUserDataOrigin; auto datadesc = (mUserDataDescription == o2::header::gDataDescriptionInvalid) ? o2::header::gDataDescriptionRawData : mUserDataDescription; - if (mUserDataDescription != o2::header::gDataDescriptionInvalid) { // overwrite data filter origin&descriptions with user defined ones if possible - for (auto& filt : mInputFilter) { - if (std::holds_alternative(filt.matcher)) { - std::get(filt.matcher).origin = origin; - std::get(filt.matcher).description = datadesc; - } - } - } - // if we see requested data type input with 0xDEADBEEF subspec and 0 payload this means that the "delayed message" // mechanism created it in absence of real data from upstream. Processor should send empty output to not block the workflow { diff --git a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/STFDecoderSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/STFDecoderSpec.h index 29b9f75bcbc4e..8c16759e16726 100644 --- a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/STFDecoderSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/STFDecoderSpec.h @@ -75,7 +75,9 @@ class STFDecoder : public Task void finalize(); void reset(); std::unique_ptr setupClusterer(const std::string& dictName); - void ensureContinuousROF(const std::vector& in, std::vector& out, int lr, int nROFsTF, const char* name); + bool ensureContinuousROF(const std::vector& in, std::vector& out, int lr, int nROFsTF, const char* name); + void rectifyDigits(std::vector& rofVec, std::vector& digVec); + void rectifyClusters(std::vector& rofVec, std::vector& clusVec, std::vector& pattVec); TStopwatch mTimer; bool mDoClusters = false; @@ -90,6 +92,8 @@ class STFDecoder : public Task bool mUseClusterDictionary = true; bool mVerifyDecoder = false; bool mDumpFrom1stPipeline = false; + bool mRunEnsureContinuousROF = true; + bool mDisableRectifyContinuousROF = false; int mDumpOnError = 0; int mNThreads = 1; int mVerbosity = 0; diff --git a/Detectors/ITSMFT/common/workflow/src/STFDecoderSpec.cxx b/Detectors/ITSMFT/common/workflow/src/STFDecoderSpec.cxx index 8fb6ba4e6aa97..79372160c6ade 100644 --- a/Detectors/ITSMFT/common/workflow/src/STFDecoderSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/STFDecoderSpec.cxx @@ -63,6 +63,7 @@ STFDecoder::STFDecoder(const STFDecoderInp& inp, std::shared_ptr void STFDecoder::init(InitContext& ic) { + int lane = ic.services().get().inputTimesliceId; o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); try { auto v0 = o2::utils::Str::tokenize(mInputSpec, ':'); @@ -72,11 +73,22 @@ void STFDecoder::init(InitContext& ic) header::DataDescription dataDesc; dataOrig.runtimeInit(v1[0].c_str()); dataDesc.runtimeInit(v2[0].c_str()); + Mapping map; for (int iLayer{0}; iLayer < mLayers; ++iLayer) { auto& dec = mDecoder.emplace_back(std::make_unique>()); dec->setUserDataOrigin(dataOrig); dec->setUserDataDescription(dataDesc); dec->init(); // is this no-op? + + if (mDoStaggering) { + std::vector filter; + for (const auto feeID : map.getLayer2FEEIDs(iLayer)) { + filter.emplace_back("filter", ConcreteDataMatcher{dataOrig, dataDesc, (o2::header::DataHeader::SubSpecificationType)feeID}); + } + dec->setInputFilter(filter); + } else { + dec->setInputFilter({InputSpec{"filter", ConcreteDataTypeMatcher(dataOrig, dataDesc)}}); + } } } catch (const std::exception& e) { LOG(error) << "exception was thrown in decoder creation: " << e.what(); @@ -126,22 +138,36 @@ void STFDecoder::init(InitContext& ic) LOG(error) << "non-std::exception was thrown in decoder configuration"; throw; } + if (mDoCalibData) { + std::string warnMsg; + bool enforceEnsureContinuousROFinCalib = ic.options().get("enforce-continuous-rof-with-calib"); + if (ic.options().get("enforce-continuous-rof-with-calib")) { + warnMsg = "Calibration data requested but the ensureContinuousROF is explicitly enforced!"; + } else { + mRunEnsureContinuousROF = false; + warnMsg = "Calibration data requested, disabling ensureContinuousROF!"; + } + if (lane == 0) { + LOGP(alarm, "{}", warnMsg); + } else { + LOGP(info, "{}", warnMsg); + } + } + + mDisableRectifyContinuousROF = ic.options().get("disable-rectify-continuous-rof"); + if (mDisableRectifyContinuousROF && mRunEnsureContinuousROF) { + std::string warnMsg = "Rectification of clusters/digits is explicitly disabled after the ensureContinuousROF!"; + if (lane == 0) { + LOGP(alarm, "{}", warnMsg); + } else { + LOGP(info, "{}", warnMsg); + } + } if (mDoClusters) { mClusterer = std::make_unique(); mClusterer->setNChips(Mapping::getNChips()); } - - if (mDoStaggering) { - Mapping map; - for (uint32_t iLayer{0}; iLayer < mLayers; ++iLayer) { - std::vector filter; - for (const auto feeID : map.getLayer2FEEIDs(iLayer)) { - filter.emplace_back("filter", ConcreteDataMatcher{Mapping::getOrigin(), o2::header::gDataDescriptionRawData, (o2::header::DataHeader::SubSpecificationType)feeID}); - } - mDecoder[iLayer]->setInputFilter(filter); - } - } } ///_______________________________________ @@ -258,21 +284,29 @@ void STFDecoder::run(ProcessingContext& pc) } } if (mDoDigits) { + std::vector expDigRofVec; + if (ensureContinuousROF(digROFVec, expDigRofVec, iLayer, nROFsTF, "digits") && !mDisableRectifyContinuousROF) { + auto oldNDig = digVec.size(); + rectifyDigits(expDigRofVec, digVec); + LOGP(warn, "Rectified {} digits out of original {} on layer {} following ensureContinuousROF", digVec.size(), oldNDig, iLayer); + } pc.outputs().snapshot(Output{orig, "DIGITS", iLayer}, digVec); - std::vector expDigRofVec(nROFsTF); - ensureContinuousROF(digROFVec, expDigRofVec, iLayer, nROFsTF, "digits"); - pc.outputs().snapshot(Output{orig, "DIGITSROF", iLayer}, digROFVec); + pc.outputs().snapshot(Output{orig, "DIGITSROF", iLayer}, expDigRofVec); mEstNDig[iLayer] = std::max(mEstNDig[iLayer], size_t(digVec.size() * 1.2)); if (mDoCalibData) { pc.outputs().snapshot(Output{orig, "GBTCALIB", iLayer}, calVec); mEstNCalib[iLayer] = std::max(mEstNCalib[iLayer], size_t(calVec.size() * 1.2)); } - LOG(debug) << mSelfName << " Decoded " << digVec.size() << " Digits in " << digROFVec.size() << " ROFs" << ((mDoStaggering) ? std::format(" on layer {}", iLayer) : ""); + LOG(debug) << mSelfName << " Decoded " << digVec.size() << " Digits in " << expDigRofVec.size() << " ROFs" << ((mDoStaggering) ? std::format(" on layer {}", iLayer) : ""); } if (mDoClusters) { // we are not obliged to create vectors which are not requested, but other devices might not know the options of this one - std::vector expClusRofVec(nROFsTF); - ensureContinuousROF(clusROFVec, expClusRofVec, iLayer, nROFsTF, "clusters"); + std::vector expClusRofVec; + if (ensureContinuousROF(clusROFVec, expClusRofVec, iLayer, nROFsTF, "clusters") && !mDisableRectifyContinuousROF) { + auto oldNClus = clusCompVec.size(), oldNPatt = clusPattVec.size(); + rectifyClusters(expClusRofVec, clusCompVec, clusPattVec); + LOGP(warn, "Rectified {} clusters and {} patterns out of original {} and {} on layer {} following ensureContinuousROF", clusCompVec.size(), clusPattVec.size(), oldNClus, oldNPatt, iLayer); + } pc.outputs().snapshot(Output{orig, "COMPCLUSTERS", iLayer}, clusCompVec); pc.outputs().snapshot(Output{orig, "PATTERNS", iLayer}, clusPattVec); pc.outputs().snapshot(Output{orig, "CLUSTERSROF", iLayer}, expClusRofVec); @@ -416,8 +450,12 @@ void STFDecoder::reset() ///_______________________________________ template -void STFDecoder::ensureContinuousROF(const std::vector& rofVec, std::vector& expROFVec, int lr, int nROFsTF, const char* name) +bool STFDecoder::ensureContinuousROF(const std::vector& rofVec, std::vector& expROFVec, int lr, int nROFsTF, const char* name) { + if (!mRunEnsureContinuousROF) { + expROFVec = rofVec; + return false; + } const auto& par = AlpideParam::Instance(); // ensure that the rof output is continuous // we will preserve the digits/clusters as they are but the stray ROFs will be removed (leaving their clusters/digits unaddressed). @@ -465,13 +503,82 @@ void STFDecoder::ensureContinuousROF(const std::vector& rofV } } } - int prevFirst{0}; + int prevLast{0}; + bool reReference = false; // in case a non-last ROF with non-0 entries is removed, ROF references need to be shifted and clusters/digits rewritten for (auto& rof : expROFVec) { if (rof.getFirstEntry() < 0) { - rof.setFirstEntry(prevFirst); + rof.setFirstEntry(prevLast); + } else if (rof.getFirstEntry() != prevLast) { + reReference = true; // there is jump + } + prevLast = rof.getFirstEntry() + rof.getNEntries(); + } + return reReference; +} + +///_______________________________________ +template +void STFDecoder::rectifyDigits(std::vector& rofVec, std::vector& digVec) +{ + // following ensureContinuousROF call some old ROFs might have been dropped, need to rebuild digits vector and rereference ROF + std::vector digVecTmp; + digVecTmp.reserve(digVec.size()); + auto beg0 = digVec.begin(); + for (auto& rof : rofVec) { + int firstEntry = digVecTmp.size(); + if (rof.getNEntries()) { + auto beg = beg0 + rof.getFirstEntry(), end = beg + rof.getNEntries(); + std::copy(beg, end, std::back_inserter(digVecTmp)); + } + rof.setFirstEntry(firstEntry); + } + digVec.swap(digVecTmp); +} + +///_______________________________________ +template +void STFDecoder::rectifyClusters(std::vector& rofVec, std::vector& clusVec, std::vector& pattVec) +{ + // following ensureContinuousROF call some old ROFs might have been dropped, need to rebuild clusters and patterns vectors and rereference ROF + std::vector clusVecTmp; + clusVecTmp.reserve(clusVec.size()); + std::vector pattVecTmp; + pattVecTmp.reserve(pattVec.size()); + const auto& dict = mClusterer->getDictionary(); + auto begCl0 = clusVec.begin(), begClForPatt = begCl0; + auto pattIt = pattVec.begin(); + + auto skipToLastPattern = [&begClForPatt, &pattIt, &dict](const decltype(begCl0) tgt) { + while (begClForPatt < tgt) { // iterate clusters skipping their patterns until we reach targed cluster + const auto& clp = *begClForPatt; + auto pattID = clp.getPatternID(); + if (pattID == itsmft::CompCluster::InvalidPatternID || dict.isGroup(pattID)) { + ClusterPattern::skipPattern(pattIt); + } + begClForPatt++; + } + }; + + for (auto& rof : rofVec) { + int firstEntry = clusVecTmp.size(); + if (rof.getNEntries()) { + auto begClROF = begCl0 + rof.getFirstEntry(), endClROF = begClROF + rof.getNEntries(); // clusters to copy start/end here + if (mDoPatterns) { + if (begClForPatt > begClROF) { // normally should no happen unless original ROFs were not ordered + begClForPatt = begCl0; // start from the beginning + } + skipToLastPattern(begClROF); // iterate clusters skipping their patterns until we reach the 1st cluster to be copied + auto begPattToCopy = pattIt; // the 1st pattern corresponding to the needed ROF + skipToLastPattern(endClROF); // iterate clusters skipping their patterns until we reach the last cluster to be copied + std::copy(begPattToCopy, pattIt, std::back_inserter(pattVecTmp)); + } + std::copy(begClROF, endClROF, std::back_inserter(clusVecTmp)); } - prevFirst = rof.getFirstEntry(); + // copy patterns corresponding to this ROF + rof.setFirstEntry(firstEntry); } + clusVec.swap(clusVecTmp); + pattVec.swap(pattVecTmp); } ///_______________________________________ @@ -544,6 +651,8 @@ DataProcessorSpec getSTFDecoderSpec(const STFDecoderInp& inp) {"unmute-extra-lanes", VariantType::Bool, false, {"allow extra lanes to be as verbose as 1st one"}}, {"allow-empty-rofs", VariantType::Bool, false, {"record ROFs w/o any hit"}}, {"ignore-noise-map", VariantType::Bool, false, {"do not mask pixels flagged in the noise map"}}, + {"enforce-continuous-rof-with-calib", VariantType::Bool, false, {"enforce ensureContinuousROF call even when calibration data is requested (not recommended)"}}, + {"disable-rectify-continuous-rof", VariantType::Bool, false, {"do not rectify clusters and digits after ensureContinuousROF (not recommended)"}}, {"accept-rof-rampup-data", VariantType::Bool, false, {"do not discard data during ROF ramp up"}}, {"rof-length-error-freq", VariantType::Float, 60.f, {"do not report ROF length error more frequently than this value, disable if negative"}}, {"ignore-cluster-dictionary", VariantType::Bool, false, {"do not use cluster dictionary, always store explicit patterns"}}}}; diff --git a/Detectors/Raw/README.md b/Detectors/Raw/README.md index 557245030b980..1fece239723ec 100644 --- a/Detectors/Raw/README.md +++ b/Detectors/Raw/README.md @@ -548,6 +548,87 @@ list of detectors for which raw outputs are discarded. The raw data will be propagated (if present) only if the detector is selected in `--onlyDet` and `NOT` selected in `--non-raw-only-det`. The non-raw data will be propagated (if defined for the given detector and present in the file) only if the detector is selected in `--onlyDet` and `NOT` selected in `--raw-only-det`. +## Raw TF (DD format) dumping workflow + +Use `o2-raw-tf-dump-workflow` to dump raw TF data in DD format. The options are: +``` +--dataspec arg (=tst:TST/A) +``` +Optional selection string for the data to be dumped, e.g. the same string supplied to the input raw proxy +``` +--triggerspec arg (="") +``` +Selection string for the external trigger to dump particular TF. Must be contained in the `--dataspec`. The workflow will loop over all available trigger inputs, interpreting them as span: any `span[0]==true` will trigger writing process (modulo throttling). +``` +--include-deadbeef (false) +``` +Include data with DPL-generated 0xdeadbeef subspecs (for data missing in the original TF). +``` +--exclude-trigger-specs (="") + +``` +Ignore trigger seen in these inputs of triggerspec (e.g. to suppress noisy trigger inputs) +``` +--max-dump-rate arg (=0) +``` +Fraction in (`%`) of TFs to dump. W/o external trigger: random(>0) or periodic(<0) rejection. With external trigger: throttle dumping to have the lowest estimated acceptance rate compatible with this rate. +``` +--rate-est-conf-limit arg (=0.05) +``` +Quantile for the lowest rate estimate confidence limit +``` +--max-warn arg (=5) +``` +If throttling, max allowed warnings +``` +--mute-warn-period arg (=100) +``` +Mute warnings about throttling for this number of TFs +``` +--output-dir arg (=none) +``` +Dumped TFs output directory, must exist. `none` means current dir., `/dev/null`: ignort writing (dry run) +``` +--meta-output-dir arg (=/dev/null) +``` +TF metadata output directory, must exist (if not /dev/null, in which case the metadata will not be created) +``` +--md5-for-meta (false) +``` +Fill CTF file MD5 sum in the metadata file +``` +--min-file-size arg (=0) +``` +Accumulate TFs until given file size reached +``` +--max-file-size arg (=0) +``` +If > 0, try to avoid exceeding given file size, also used for space check +``` +--max-tf-per-file arg (=0) +``` +If > 0, avoid storing more than requested CTFs per file +``` +--require-free-disk arg (=0) +``` +Pause writing op. if available disk space is below this margin, in bytes if >0, as a fraction of total if <0 +``` +--wait-for-free-disk arg (=10) +``` +If paused due to the low disk space, recheck after this time (in s) +``` +--max-wait-for-free-disk arg (=60) +``` +Produce fatal if paused due to the low disk space for more than time in seconds. +``` +--verbosity-level (=0) +``` +Verbose mode: 1: decision on every TF, 2: details of saved TF, 3: more details. +``` +--ignore-partition-run-dir +``` +Do not creare partition-run directory in output-dir + ## TF rate limiting To apply TF rate limiting (i.e. make sure that no more than N TFs are in processing) provide `--timeframes-rate-limit --timeframes-rate-limit-ipcid ` diff --git a/Detectors/Raw/TFReaderDD/CMakeLists.txt b/Detectors/Raw/TFReaderDD/CMakeLists.txt index 12ecc9ca8795d..f87d1b5a7704e 100644 --- a/Detectors/Raw/TFReaderDD/CMakeLists.txt +++ b/Detectors/Raw/TFReaderDD/CMakeLists.txt @@ -26,3 +26,10 @@ o2_add_executable(tf-reader-workflow SOURCES src/TFReaderSpec.cxx src/tf-reader-workflow.cxx PUBLIC_LINK_LIBRARIES O2::TFReaderDD) + + +o2_add_executable(tf-dump-workflow + COMPONENT_NAME raw + SOURCES src/RawTFDumpSpec.cxx + src/tf-data-dump-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::TFReaderDD) diff --git a/Detectors/Raw/TFReaderDD/include/TFReaderDD/SubTimeFrameFile.h b/Detectors/Raw/TFReaderDD/include/TFReaderDD/SubTimeFrameFile.h index 340027642b74c..eeabf8e8d4117 100644 --- a/Detectors/Raw/TFReaderDD/include/TFReaderDD/SubTimeFrameFile.h +++ b/Detectors/Raw/TFReaderDD/include/TFReaderDD/SubTimeFrameFile.h @@ -21,6 +21,8 @@ #include #include +#include "Framework/DataSpecUtils.h" +#include "Framework/OutputSpec.h" #include "Framework/Logger.h" namespace o2 @@ -151,13 +153,13 @@ struct SubTimeFrameFileMeta { /// std::uint64_t mWriteTimeMs; - auto getTimePoint() + auto getTimePoint() const { using namespace std::chrono; return time_point{milliseconds{mWriteTimeMs}}; } - std::string getTimeString() + std::string getTimeString() const { using namespace std::chrono; std::time_t lTime = system_clock::to_time_t(getTimePoint()); @@ -167,6 +169,11 @@ struct SubTimeFrameFileMeta { return lTimeStream.str(); } + const std::string info() const + { + return fmt::format("Size in file: {} Time: {} Version: {}", mStfSizeInFile, getTimeString(), mStfFileVersion); + } + SubTimeFrameFileMeta(const std::uint64_t pStfSize) : SubTimeFrameFileMeta() { @@ -220,6 +227,11 @@ struct SubTimeFrameFileDataIndex { static_assert(sizeof(DataIndexElem) == 48, "DataIndexElem changed -> Binary compatibility is lost!"); } + + const std::string info() const + { + return fmt::format("DH: {} Cnt:{} Size:{} Offset:{}", o2::framework::DataSpecUtils::describe(o2::framework::OutputSpec{mDataOrigin, mDataDescription, mSubSpecification}), mDataBlockCnt, mSize, mOffset); + } }; SubTimeFrameFileDataIndex() = default; @@ -240,6 +252,8 @@ struct SubTimeFrameFileDataIndex { return sizeof(o2::header::DataHeader) + (sizeof(DataIndexElem) * mDataIndex.size()); } + const std::vector& getDataIndex() const { return mDataIndex; } + friend std::ostream& operator<<(std::ostream& pStream, const SubTimeFrameFileDataIndex& pIndex); private: diff --git a/Detectors/Raw/TFReaderDD/include/TFReaderDD/SubTimeFrameFileReader.h b/Detectors/Raw/TFReaderDD/include/TFReaderDD/SubTimeFrameFileReader.h index 3b926e0a79206..2b7d2b7ab8e74 100644 --- a/Detectors/Raw/TFReaderDD/include/TFReaderDD/SubTimeFrameFileReader.h +++ b/Detectors/Raw/TFReaderDD/include/TFReaderDD/SubTimeFrameFileReader.h @@ -46,11 +46,11 @@ class SubTimeFrameFileReader public: SubTimeFrameFileReader() = delete; - SubTimeFrameFileReader(const std::string& pFileName, o2::detectors::DetID::mask_t detMask); + SubTimeFrameFileReader(const std::string& pFileName, o2::detectors::DetID::mask_t detMask, int verb, bool sup0xccdb, bool repaireHeaders, bool rejectDistSTF); ~SubTimeFrameFileReader(); /// Read a single TF from the file - std::unique_ptr read(fair::mq::Device* device, const std::vector& outputRoutes, const std::string& rawChannel, size_t slice, bool sup0xccdb, int verbosity); + std::unique_ptr read(fair::mq::Device* device, const std::vector& outputRoutes, const std::string& rawChannel, size_t slice); /// Tell the current position of the file inline std::uint64_t position() const { return mFileMapOffset; } @@ -76,6 +76,13 @@ class SubTimeFrameFileReader std::uint64_t mFileMapOffset = 0; std::uint64_t mFileSize = 0; + int mVerbosity = 0; + bool mSup0xccdb = true; + bool mRepaireHeaders = true; + bool mRejectDistSTF = true; + + const std::string describeHeader(const o2::header::DataHeader& hd, bool full = false) const; + // helper to make sure written chunks are buffered, only allow pointers template ::value>> diff --git a/Detectors/Raw/TFReaderDD/src/RawTFDumpSpec.cxx b/Detectors/Raw/TFReaderDD/src/RawTFDumpSpec.cxx new file mode 100644 index 0000000000000..03bd26ae0deb9 --- /dev/null +++ b/Detectors/Raw/TFReaderDD/src/RawTFDumpSpec.cxx @@ -0,0 +1,611 @@ +// 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/WorkflowSpec.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/RawDeviceService.h" +#include "Framework/DataProcessingHelpers.h" +#include "Framework/InputRecordWalker.h" +#include "Framework/Task.h" +#include "Framework/DataTakingContext.h" +#include "Framework/TimingInfo.h" +#include "DataFormatsParameters/GRPECSObject.h" +#include "DetectorsCommonDataFormats/FileMetaData.h" +#include "RawTFDumpSpec.h" +#include "TFReaderDD/SubTimeFrameFile.h" +#include "CommonUtils/NameConf.h" +#include "CommonUtils/FileSystemUtils.h" +#include "CommonUtils/StringUtils.h" +#include "Algorithm/RangeTokenizer.h" +#include +#include +#include +#include +#include + +namespace o2::rawdd +{ +namespace o2h = o2::header; +using namespace o2::framework; +using DataHeader = o2::header::DataHeader; +using DetID = o2::detectors::DetID; +using ios = std::ios_base; + +class RawTFDump : public Task +{ + public: + static constexpr o2h::DataDescription DESCRaw{"RAWDATA"}, DESCCRaw{"CRAWDATA"}; + + RawTFDump(const std::string& trigger); + void init(InitContext& ic) final; + void run(ProcessingContext& pc) final; + void endOfStream(EndOfStreamContext& ec) final; + + private: + bool triggerTF(ProcessingContext& pc); + void updateTimeDependentParams(ProcessingContext& pc); + void prepareTFForWriting(ProcessingContext& pc); + size_t getTFSizeInFile() const; + size_t getCurrentFileSize(); + void prepareTFFile(); + void closeTFFile(); + bool checkFreeSpace(ProcessingContext& pc); + std::string reportRates() const; + + SubTimeFrameFileDataIndex mTFDataIndex; + std::vector> mTFData; + std::map> mDataMap; + std::vector mFilter{}; + std::vector mTriggerFilter{}; + std::vector mExclTriggerFilter{}; + + size_t mTFSize = 0; + size_t mMinFileSize = 0; // if > 0, accumulate TFs in the same file until the total size exceeds this minimum + size_t mMaxFileSize = 0; // if > MinSize, and accumulated size will exceed this value, stop accumulation (even if mMinFileSize is not reached) + + int mNTFsSeen = 0; // total number of TFs seen + int mNTFsExtTrig = 0; // total nunber of TFs externally triggered + int mNTFsAccepted = 0; // total number of TFs written + int mNTFsInFile = 0; // total number of TFs accumulated in the current file + int mNTFFiles = 0; // total number of TF files written + int mLastWarned = 0; // TF when last warned about throttling + int mMaxTFPerFile = 0; // max TFs per files to store + int mNWarnThrottle = 0; // number of times we warned about the throttling + int mMaxWarnThrottle = 0; // max allowed warnings about the throttling + int mWarnThrottleTF = 0; // min period (in TFs) between the warnings about the throttling + int mWaitDiskFull = 0; // if mCheckDiskFull triggers, pause for this amount of ms before new attempt + int mWaitDiskFullMax = -1; // produce fatal mCheckDiskFull block the workflow for more than this time (in ms) + float mCheckDiskFull = 0.; // wait for if available abs. disk space is < mCheckDiskFull (if >0) or if its fraction is < -mCheckDiskFull (if <0) + float mMaxAccRate = 0.f; // max acceptance rate + float mConfLim = 0.05f; // confidence limit for rate esimate (lower quantile) + float mRateEstAccLow = 0.f; // lower limit on accepted TFs rate + float mRateEstAccUpp = 0.f; // upper limit on accepted TFs rate + float mRateEstTrgLow = 0.f; // lower limit on triggered TFs rate + float mRateEstTrgUpp = 0.f; // upper limit on triggered TFs rate + + bool mFillMD5 = false; + bool mWriteTF = true; // for dry run + bool mStoreMetaFile = false; + bool mCreateRunEnvDir = true; + bool mAcceptCurrentTF = false; + bool mRejectDEADBEEF = false; + bool mRejectDistSTF = true; + int mVerbose = 0; + std::vector mTFOrbits{}; // 1st orbits of TF accumulated in current file + o2::framework::DataTakingContext mDataTakingContext{}; + o2::framework::TimingInfo mTimingInfo{}; + + std::string mTrigger{}; // external trigger input + std::string mExclTriggerSpecs{}; // trigger specs to ignore + std::string mHostName{}; + std::string mTFDir{}; + std::string mTFMetaFileDir = "/dev/null"; + std::string mCurrentTFFileName{}; + std::string mCurrentTFFileNameFull{}; + std::string mCurrentTFFileNameFullTmp{}; + std::string mMetaDataType{}; + + static constexpr size_t MiB = 1ul << 20; + static constexpr std::streamsize sBuffSize = MiB; // 1 MiB + static constexpr std::streamsize sChunkSize = 512; + static const std::string TMPFileEnding; + std::unique_ptr mFileBuf; + std::ofstream mFile; + std::uniform_real_distribution mUniformDist{0.0, 100.0}; + std::default_random_engine mRGen; + + // helper to make sure the written blocks are buffered + template < + typename pointer, + typename std::enable_if< + std::is_pointer::value && // pointers only + (std::is_void>::value || // void* or standard layout! + std::is_standard_layout>::value)>::type* = nullptr> + void buffered_write(const pointer p, std::streamsize pCount) + { + // make sure we're not doing a short write + assert((pCount % sizeof(std::conditional_t>::value, + char, std::remove_pointer_t>) == + 0) && + "Performing short write?"); + + const char* lPtr = reinterpret_cast(p); + // avoid the optimization if the write is large enough + if (pCount >= sBuffSize) { + mFile.write(lPtr, pCount); + } else { + // split the write to smaller chunks + while (pCount > 0) { + const auto lToWrite = std::min(pCount, sChunkSize); + assert(lToWrite > 0 && lToWrite <= sChunkSize && lToWrite <= pCount); + + mFile.write(lPtr, lToWrite); + lPtr += lToWrite; + pCount -= lToWrite; + } + } + } +}; + +const std::string RawTFDump::TMPFileEnding{".part"}; + +//________________________________________ +RawTFDump::RawTFDump(const std::string& trigger) : mTrigger{trigger} +{ + mTriggerFilter = select(trigger.c_str()); + mFileBuf = std::make_unique(sBuffSize); + mFile.rdbuf()->pubsetbuf(mFileBuf.get(), sBuffSize); + mFile.clear(); + mFile.exceptions(std::fstream::failbit | std::fstream::badbit); +} + +//________________________________________ +void RawTFDump::init(InitContext& ic) +{ + mRGen = std::default_random_engine(getpid()); + mTFMetaFileDir = ic.options().get("meta-output-dir"); + if (mTFMetaFileDir != "/dev/null") { + mTFMetaFileDir = o2::utils::Str::rectifyDirectory(mTFMetaFileDir); + mStoreMetaFile = true; + mFillMD5 = ic.options().get("md5-for-meta"); + } + + mTFDir = ic.options().get("output-dir"); + if (mTFDir != "/dev/null") { + mTFDir = o2::utils::Str::rectifyDirectory(mTFDir); + mWriteTF = true; + } else { + mWriteTF = false; + mStoreMetaFile = false; + } + mRejectDistSTF = !ic.options().get("include-dist-stf"); + mRejectDEADBEEF = !ic.options().get("include-deadbeef"); + mCreateRunEnvDir = !ic.options().get("ignore-partition-run-dir"); + mMinFileSize = ic.options().get("min-file-size"); + mMaxFileSize = ic.options().get("max-file-size"); + mMaxTFPerFile = ic.options().get("max-tf-per-file"); + mMaxAccRate = ic.options().get("max-dump-rate"); + float cl = ic.options().get("rate-est-conf-limit"); + if (mConfLim < 0.001 || mConfLim > 0.32) { + LOGP(warn, "Bad confidence limit {} for rate estimate, setting to default {}", cl, mConfLim); + } else { + mConfLim = cl; + } + mMaxWarnThrottle = ic.options().get("max-warn"); + mWarnThrottleTF = ic.options().get("mute-warn-period"); + + mVerbose = ic.options().get("verbosity-level"); + mExclTriggerSpecs = ic.options().get("exclude-trigger-specs"); + if (!mExclTriggerSpecs.empty()) { + mExclTriggerFilter = select(mExclTriggerSpecs.c_str()); + } + if (mTrigger.empty()) { + if (mMaxAccRate >= 0.f) { + LOGP(info, "Will accept randomly {}% of TFs", mMaxAccRate); + } else { + LOGP(info, "Will accept every {}-th TF", int(std::ceil(-100.f / mMaxAccRate))); + } + } else { + mMaxAccRate = std::abs(mMaxAccRate); + LOGP(info, "Will limit TFs triggered with {} by {}% at most", mTrigger, mMaxAccRate); + if (!mExclTriggerFilter.empty()) { + LOGP(info, "Inputs excluded from the trigger: {}", mExclTriggerSpecs); + } + } + + if (mWriteTF) { + if (mMinFileSize > 0) { + LOGP(info, "Multiple TFs will be accumulated in the file until its size exceeds {}{}", + mMinFileSize, mMaxFileSize > mMinFileSize ? fmt::format(" but does not exceed {} B", mMaxFileSize) : std::string{}); + } + } + + mCheckDiskFull = ic.options().get("require-free-disk"); + mWaitDiskFull = 1000 * ic.options().get("wait-for-free-disk"); + mWaitDiskFullMax = 1000 * ic.options().get("max-wait-for-free-disk"); + + char hostname[_POSIX_HOST_NAME_MAX]; + gethostname(hostname, _POSIX_HOST_NAME_MAX); + mHostName = hostname; + mHostName = mHostName.substr(0, mHostName.find('.')); +} + +//________________________________________ +void RawTFDump::run(ProcessingContext& pc) +{ + mNTFsSeen++; + updateTimeDependentParams(pc); + mAcceptCurrentTF = triggerTF(pc); + if (mAcceptCurrentTF) { + prepareTFForWriting(pc); + } else { + return; + } + + prepareTFFile(); + if (mWriteTF && checkFreeSpace(pc)) { // write data + try { + size_t lTFSizeInFile = getTFSizeInFile(); + SubTimeFrameFileMeta lTFFileMeta(lTFSizeInFile); + lTFFileMeta.mWriteTimeMs = mTimingInfo.creation; + + mFile << lTFFileMeta; // Write DataHeader + SubTimeFrameFileMeta + mFile << mTFDataIndex; // Write DataHeader + SubTimeFrameFileDataIndex + + for (const auto& eqEntry : mDataMap) { + auto& [lSize, lCnt, lEntry] = eqEntry.second; + for (size_t part = 0; part < lCnt; part++) { + const auto& dataPtr = mTFData[lEntry + part]; + DataHeader hdToWrite = *reinterpret_cast(dataPtr.first); // make a local DataHeader copy to clear flagsNextHeader bit + hdToWrite.flagsNextHeader = 0; + hdToWrite.splitPayloadIndex = part; + if (mVerbose > 2) { + LOGP(info, "Writing part:{}/{} of {} | TFCounter:{} part{}/{}", part, lCnt, DataSpecUtils::describe(OutputSpec{hdToWrite.dataOrigin, hdToWrite.dataDescription, hdToWrite.subSpecification}), hdToWrite.firstTForbit, hdToWrite.splitPayloadIndex, hdToWrite.splitPayloadParts); + } + buffered_write(reinterpret_cast(&hdToWrite), sizeof(DataHeader)); + buffered_write(dataPtr.second, hdToWrite.payloadSize); + } + } + mFile.flush(); // flush the buffer and check the state + mTFOrbits.push_back(mTimingInfo.firstTForbit); + mNTFsInFile++; + } catch (const std::ios_base::failure& eFailExc) { + LOGP(error, "Writing of TF {} to file {} failed. error={}", mTimingInfo.tfCounter, mCurrentTFFileNameFullTmp, eFailExc.what()); + } + } + // cleanup + mTFData.clear(); + mDataMap.clear(); + mTFDataIndex.clear(); + mTFSize = 0; +} + +//____________________________________________________________ +void RawTFDump::endOfStream(EndOfStreamContext&) +{ + closeTFFile(); + LOGP(info, "Dumped {} TFs to {} files", mNTFsAccepted, mNTFFiles); + if (!mTriggerFilter.empty()) { + LOGP(info, "External trigger summary: {}", reportRates()); + } +} + +//________________________________________ +size_t RawTFDump::getTFSizeInFile() const +{ + return SubTimeFrameFileMeta::getSizeInFile() + mTFDataIndex.getSizeInFile() + mTFSize; +} + +//________________________________________ +size_t RawTFDump::getCurrentFileSize() +{ + return mFile.is_open() ? size_t(mFile.tellp()) : 0; +} + +//___________________________________________________________________ +void RawTFDump::prepareTFFile() +{ + if (!mWriteTF) { + return; + } + bool needToOpen; + if (!mFile.is_open()) { + needToOpen = true; + } else { + auto currSize = getCurrentFileSize(); + if ((mNTFsInFile >= mMaxTFPerFile) || + (currSize >= mMinFileSize) || // min size exceeded, may close the file. + (currSize && mMaxFileSize > mMinFileSize && ((currSize + mTFSize) > mMaxFileSize))) { // this is not the 1st TF in the file and the new size will exceed allowed max + needToOpen = true; + } else { + LOGP(info, "Will add new TF of size {} to existing file of size {} with {} TFs", mTFSize, currSize, mNTFsInFile); + needToOpen = false; + } + } + if (needToOpen) { + closeTFFile(); + auto TFDir = mTFDir.empty() ? o2::utils::Str::rectifyDirectory("./") : mTFDir; + if (mCreateRunEnvDir && !mDataTakingContext.envId.empty() && (mDataTakingContext.envId != o2::framework::DataTakingContext::UNKNOWN)) { + TFDir += fmt::format("{}_{}tf/", mDataTakingContext.envId, mDataTakingContext.runNumber); + if (!TFDir.empty()) { + o2::utils::createDirectoriesIfAbsent(TFDir); + LOGP(info, "Created {} directory for TFs output", TFDir); + } + } + mCurrentTFFileName = o2::base::NameConf::getRawTFFileName(mTimingInfo.runNumber, mTimingInfo.firstTForbit, mTimingInfo.tfCounter, mHostName); + mCurrentTFFileNameFull = fmt::format("{}{}", TFDir, mCurrentTFFileName); + mCurrentTFFileNameFullTmp = TMPFileEnding.empty() ? mCurrentTFFileNameFull : o2::utils::Str::concat_string(mCurrentTFFileNameFull, TMPFileEnding); + mFile.open(mCurrentTFFileNameFullTmp.c_str(), ios::binary | ios::trunc | ios::out | ios::ate); + LOGP(info, "Opened new raw-tf dump file {}[{}]", mCurrentTFFileNameFull, TMPFileEnding); + mNTFFiles++; + } +} + +//___________________________________________________________________ +void RawTFDump::updateTimeDependentParams(ProcessingContext& pc) +{ + namespace GRPECS = o2::parameters::GRPECS; + mTimingInfo = pc.services().get(); + if (mTimingInfo.globalRunNumberChanged) { + mDataTakingContext = pc.services().get(); + // determine the output type for the TF metadata + mMetaDataType = GRPECS::getRawDataPersistencyMode(mDataTakingContext.runType, mDataTakingContext.forcedRaw); + } +} + +//___________________________________________________________________ +void RawTFDump::closeTFFile() +{ + if (!mFile.is_open()) { + return; + } + try { + LOGP(info, "Closing output file {}[{}]", mCurrentTFFileNameFull, TMPFileEnding); + mFile.close(); + // write TF file metaFile data + if (mStoreMetaFile) { + o2::dataformats::FileMetaData TFMetaData; + if (!TFMetaData.fillFileData(mCurrentTFFileNameFullTmp, mFillMD5, TMPFileEnding)) { + throw std::runtime_error("metadata file was requested but not created"); + } + TFMetaData.setDataTakingContext(mDataTakingContext); + TFMetaData.type = mMetaDataType; + TFMetaData.priority = "high"; + TFMetaData.tfOrbits.swap(mTFOrbits); + auto metaFileNameTmp = fmt::format("{}{}.tmp", mTFMetaFileDir, mCurrentTFFileName); + auto metaFileName = fmt::format("{}{}.done", mTFMetaFileDir, mCurrentTFFileName); + try { + std::ofstream metaFileOut(metaFileNameTmp); + metaFileOut << TFMetaData; + metaFileOut.close(); + if (!TMPFileEnding.empty()) { + std::filesystem::rename(mCurrentTFFileNameFullTmp, mCurrentTFFileNameFull); + } + std::filesystem::rename(metaFileNameTmp, metaFileName); + LOGP(info, "wrote meta file {}", metaFileName); + } catch (std::exception const& e) { + LOGP(error, "Failed to store TF meta data file {}, reason {}", metaFileName, e.what()); + } + } else if (!TMPFileEnding.empty()) { + std::filesystem::rename(mCurrentTFFileNameFullTmp, mCurrentTFFileNameFull); + } + } catch (std::exception const& e) { + LOGP(error, "Failed to finalize TF file {}, reason: ", mCurrentTFFileNameFull, e.what()); + } + mTFOrbits.clear(); + mNTFsInFile = 0; +} + +//________________________________________ +bool RawTFDump::checkFreeSpace(ProcessingContext& pc) +{ + int totalWait = 0, nwaitCycles = 0; + while (mCheckDiskFull) { + constexpr int showFirstN = 10, prsecaleWarnings = 50; + try { + const auto si = std::filesystem::space(mCurrentTFFileNameFullTmp); + std::string wmsg{}; + if (mCheckDiskFull > 0.f && si.available < mCheckDiskFull) { + nwaitCycles++; + wmsg = fmt::format("Disk has {} MiB available while at least {} MiB is requested, wait for {} ms (on top of {} ms)", si.available / MiB, size_t(mCheckDiskFull) / MiB, mWaitDiskFull, totalWait); + } else if (mCheckDiskFull < 0.f && float(si.available) / si.capacity < -mCheckDiskFull) { // relative margin requested + nwaitCycles++; + wmsg = fmt::format("Disk has {:.3f}% available while at least {:.3f}% is requested, wait for {} ms (on top of {} ms)", si.capacity ? float(si.available) / si.capacity * 100.f : 0., -mCheckDiskFull, mWaitDiskFull, totalWait); + } else { + nwaitCycles = 0; + } + if (nwaitCycles) { + if (mWaitDiskFullMax > 0 && totalWait > mWaitDiskFullMax) { + closeTFFile(); // try to save whatever we have + LOGP(fatal, "Disk has {} MiB available out of {} MiB after waiting for {} ms", si.available / MiB, si.capacity / MiB, mWaitDiskFullMax); + } + if (nwaitCycles < showFirstN + 1 || (prsecaleWarnings && (nwaitCycles % prsecaleWarnings) == 0)) { + LOGP(alarm, "{}", wmsg); + } + pc.services().get().waitFor((unsigned int)(mWaitDiskFull)); + totalWait += mWaitDiskFull; + continue; + } + } catch (std::exception const& e) { + LOGP(fatal, "unable to query disk space info for path {}, reason {}", mCurrentTFFileNameFull, e.what()); // do we want this? + } + break; + } + return true; +} + +//________________________________________ +bool RawTFDump::triggerTF(ProcessingContext& pc) +{ + bool trig = false; + if (mTrigger.empty()) { // random + if (mMaxAccRate > 0.f) { + trig = (mUniformDist(mRGen) <= mMaxAccRate); + } else if (mMaxAccRate < 0.f) { + trig = (mTimingInfo.tfCounter % int(std::ceil(-100.f / mMaxAccRate))) == 0; + } + } else { + for (auto const& ref : InputRecordWalker(pc.inputs(), mTriggerFilter)) { + auto const* dh = DataRefUtils::getHeader(ref); + if (!dh) { + LOGP(error, "Failed to extract header for trigger input"); + continue; + } + auto extTrig = DataRefUtils::as(ref); + if (mVerbose > 0) { + LOGP(info, "trigger input {}, part: {} of {}, payload {}, 1stTFOrbit: {} TF: {} | span size: {} span[0]={}", + DataSpecUtils::describe(OutputSpec{dh->dataOrigin, dh->dataDescription, dh->subSpecification}), + dh->splitPayloadIndex, dh->splitPayloadParts, dh->payloadSize, dh->firstTForbit, dh->tfCounter, extTrig.size(), extTrig.size() > 0 ? extTrig[0] : false); + } + if (extTrig.size() && extTrig[0]) { + // is the input with this trigger vetoed? + bool veto = false; + for (const auto& excl : mExclTriggerFilter) { + if (DataRefUtils::match(ref, excl)) { + if (mVerbose > 0) { + LOGP(info, "ignoring trigger from black-listed {}", DataSpecUtils::describe(OutputSpec{dh->dataOrigin, dh->dataDescription, dh->subSpecification})); + } + veto = true; + break; + } + } + if (veto) { + continue; + } + trig = true; + break; + } + } + if (trig) { // do we need to throttle? + mNTFsExtTrig++; + mRateEstTrgLow = TMath::ChisquareQuantile(mConfLim, 2 * (mNTFsExtTrig)) / (2 * mNTFsSeen); + mRateEstTrgUpp = TMath::ChisquareQuantile(1. - mConfLim, 2 * (mNTFsExtTrig + 1)) / (2 * mNTFsSeen); + mRateEstAccLow = TMath::ChisquareQuantile(mConfLim, 2 * (mNTFsAccepted)) / (2 * mNTFsSeen); + mRateEstAccUpp = TMath::ChisquareQuantile(1. - mConfLim, 2 * (mNTFsAccepted + 1)) / (2 * mNTFsSeen); + if (mRateEstAccLow > 0.01 * mMaxAccRate) { // current lowest estimate on the acceptance rate exceeds desired limit -> ignore trigger + trig = false; + // do we need to warn? + if ((mNTFsSeen - mLastWarned) > mWarnThrottleTF && ((mNWarnThrottle < mMaxWarnThrottle) || mMaxWarnThrottle < 0)) { + mLastWarned = mNTFsSeen; + std::string swarn = reportRates(); + if (++mNWarnThrottle == mMaxWarnThrottle) { + swarn += " Will not warn anymore."; + } else { + swarn += fmt::format(" Will suppress this warnings for {} TFs", mWarnThrottleTF); + } + LOGP(alarm, "Ignoring TF triggered for dumping: {}", swarn); + } + } + } + } + if (trig) { + mNTFsAccepted++; + } + if (mVerbose > 0) { + LOGP(info, "TF#{} (slice#{}) will{} be written, {}", mTimingInfo.tfCounter, mTimingInfo.timeslice, trig ? "" : " not", reportRates()); + } + return trig; +} + +//________________________________________ +void RawTFDump::prepareTFForWriting(ProcessingContext& pc) +{ + for (auto const& ref : InputRecordWalker(pc.inputs(), mFilter)) { + auto const* dh = DataRefUtils::getHeader(ref); + if (!dh) { + LOGP(error, "Failed to extract header"); + continue; + } + if ((dh->subSpecification == 0xdeadbeef && mRejectDEADBEEF) || + (dh->dataOrigin == o2::header::gDataOriginFLP && dh->dataDescription == o2::header::gDataDescriptionDISTSTF && mRejectDistSTF)) { + if (mVerbose > 2) { + LOGP(info, "Rejecting {}", DataSpecUtils::describe(OutputSpec{dh->dataOrigin, dh->dataDescription, dh->subSpecification})); + } + continue; + } + const auto lHdrDataSize = sizeof(DataHeader) + dh->payloadSize; + mTFSize += lHdrDataSize; + + auto& [lSize, lCnt, lEntry] = mDataMap[EquipmentIdentifier(*dh)]; + if (!lCnt) { + lEntry = mTFData.size(); // flag where the data of this spec starts + } + lSize += lHdrDataSize; + lCnt++; + mTFData.push_back({ref.header, ref.payload}); + if (mVerbose > 2) { + const auto* dph = DataRefUtils::getHeader(ref); + LOGP(info, "{}, part: {} of {}, payload {}, 1stTFOrbit: {} TF: {}, creation: {} | counter:{} size:{} entry:{}", + DataSpecUtils::describe(OutputSpec{dh->dataOrigin, dh->dataDescription, dh->subSpecification}), + dh->splitPayloadIndex, dh->splitPayloadParts, dh->payloadSize, dh->firstTForbit, dh->tfCounter, dph ? dph->creation : -1UL, lCnt, lSize, lEntry); + } + } + + // build the index + { + LOGP(info, "Creating dump image for TF {} of run {}, starting orbit {}, size = {}", mTimingInfo.tfCounter, mTimingInfo.runNumber, mTimingInfo.firstTForbit, mTFSize); + std::uint64_t lCurrOff = 0; + for (const auto& eqEntry : mDataMap) { + const auto& eq = eqEntry.first; + auto& [lSize, lCnt, lEntry] = eqEntry.second; + assert(lSize > sizeof(DataHeader)); + + OutputSpec spec{eq.mDataOrigin, eq.mDataDescription, eq.mSubSpecification}; + if (mVerbose > 1) { + LOGP(info, "{} : {} parts of size {} entry {}| offset: {}", DataSpecUtils::describe(spec), lCnt, lSize, lEntry, lCurrOff); + } + mTFDataIndex.AddStfElement(eq, lCnt, lCurrOff, lSize); + lCurrOff += lSize; + } + } +} + +//____________________________________________________________ +std::string RawTFDump::reportRates() const +{ + std::string rep = fmt::format("{} TFs seen, {} accepted", mNTFsSeen, mNTFsAccepted); + if (!mTrigger.empty()) { + rep += fmt::format(", {} ext.triggered, est.rate: [{:.2e}:{:.2e}]/[{:.2e}:{:.2e}].", mNTFsExtTrig, mRateEstAccLow, mRateEstAccUpp, mRateEstTrgLow, mRateEstTrgUpp); + } + return rep; +} + +//__________________________________________________________ +DataProcessorSpec getRawTFDumpSpec(const std::string& inpconfig, const std::string& trigger) +{ + std::vector inputs = select(inpconfig.c_str()); + return DataProcessorSpec{ + "raw-tf-dump", + inputs, + {}, + AlgorithmSpec{adaptFromTask(trigger)}, + Options{ + {"include-deadbeef", VariantType::Bool, false, {"Include DPL-generated 0xdeadbeef subspecs for missing data"}}, + {"include-dist-stf", VariantType::Bool, false, {"Include FLP/DISTSUBTIMEFRAME input"}}, + {"exclude-trigger-specs", VariantType::String, "", {"Ignore trigger seen in these inputs of triggerspec"}}, + {"max-dump-rate", VariantType::Float, 0.f, {"%-age of TFs to dump. W/o external trigger: random(>0) or periodic(<0) rejection, with: max limit"}}, + {"rate-est-conf-limit", VariantType::Float, 0.05f, {"quantile for the lowest rate estimate confidence limit"}}, + {"max-warn", VariantType::Int, 5, {"max allowed warnings on throttling"}}, + {"mute-warn-period", VariantType::Int, 100, {"mute warnings on throttling for this number of TFs"}}, + {"output-dir", VariantType::String, "none", {"TF output directory, must exist"}}, + {"meta-output-dir", VariantType::String, "/dev/null", {"TF metadata output directory, must exist (if not /dev/null)"}}, + {"md5-for-meta", VariantType::Bool, false, {"fill CTF file MD5 sum in the metadata file"}}, + {"min-file-size", VariantType::Int64, 0l, {"accumulate TFs until given file size reached"}}, + {"max-file-size", VariantType::Int64, 0l, {"if > 0, try to avoid exceeding given file size, also used for space check"}}, + {"max-tf-per-file", VariantType::Int, 0, {"if > 0, avoid storing more than requested CTFs per file"}}, + {"require-free-disk", VariantType::Float, 0.f, {"pause writing op. if available disk space is below this margin, in bytes if >0, as a fraction of total if <0"}}, + {"wait-for-free-disk", VariantType::Float, 10.f, {"if paused due to the low disk space, recheck after this time (in s)"}}, + {"max-wait-for-free-disk", VariantType::Float, 60.f, {"produce fatal if paused due to the low disk space for more than this amount in s."}}, + {"verbosity-level", VariantType::Int, 0, {"Verbose mode: 1: decision on every TF, 2: details of saved TF, 3: more details"}}, + {"ignore-partition-run-dir", VariantType::Bool, false, {"Do not creare partition-run directory in output-dir"}}}}; +} + +} // namespace o2::rawdd diff --git a/Detectors/ITSMFT/common/base/include/ITSMFTBase/DPLAlpideParam.h b/Detectors/Raw/TFReaderDD/src/RawTFDumpSpec.h similarity index 58% rename from Detectors/ITSMFT/common/base/include/ITSMFTBase/DPLAlpideParam.h rename to Detectors/Raw/TFReaderDD/src/RawTFDumpSpec.h index e217808c06177..a39cfb026ed52 100644 --- a/Detectors/ITSMFT/common/base/include/ITSMFTBase/DPLAlpideParam.h +++ b/Detectors/Raw/TFReaderDD/src/RawTFDumpSpec.h @@ -1,4 +1,4 @@ -// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. // See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. // All rights not expressly granted are reserved. // @@ -9,5 +9,15 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -// FIXME: temporary shim to no not break O2Physics -#include "DataFormatsITSMFT/DPLAlpideParam.h" +#ifndef O2_RAW_TF_DUMP_SPEC_ +#define O2_RAW_TF_DUMP_SPEC_ + +#include "DetectorsCommonDataFormats/DetID.h" +#include "Framework/DeviceSpec.h" + +namespace o2::rawdd +{ +o2::framework::DataProcessorSpec getRawTFDumpSpec(const std::string& inpconfig, const std::string& trigger); +} + +#endif diff --git a/Detectors/Raw/TFReaderDD/src/SubTimeFrameFileReader.cxx b/Detectors/Raw/TFReaderDD/src/SubTimeFrameFileReader.cxx index f227390e67ef3..c8bc6ff374ead 100644 --- a/Detectors/Raw/TFReaderDD/src/SubTimeFrameFileReader.cxx +++ b/Detectors/Raw/TFReaderDD/src/SubTimeFrameFileReader.cxx @@ -45,8 +45,8 @@ namespace o2f = o2::framework; /// SubTimeFrameFileReader //////////////////////////////////////////////////////////////////////////////// -SubTimeFrameFileReader::SubTimeFrameFileReader(const std::string& pFileName, o2::detectors::DetID::mask_t detMask) - : mFileName(pFileName) +SubTimeFrameFileReader::SubTimeFrameFileReader(const std::string& pFileName, o2::detectors::DetID::mask_t detMask, int verb, bool sup0xccdb, bool repaireHeaders, bool rejectDistSTF) + : mFileName(pFileName), mVerbosity(verb), mSup0xccdb(sup0xccdb), mRepaireHeaders(repaireHeaders), mRejectDistSTF(rejectDistSTF) { mFileMap.open(mFileName); if (!mFileMap.is_open()) { @@ -115,7 +115,7 @@ std::size_t SubTimeFrameFileReader::getHeaderStackSize() // throws ios_base::fai LOGP(error, "FileReader: Reached max number of headers allowed: {}.", cMaxHeaders); return 0; } - + LOGP(debug, "getHeaderStackSize, pos = {}, size = {}", lFilePosStart, lStackSize); return lStackSize; } @@ -178,13 +178,21 @@ Stack SubTimeFrameFileReader::getHeaderStack(std::size_t& pOrigsize) return Stack(lStackMem); } +const std::string SubTimeFrameFileReader::describeHeader(const o2::header::DataHeader& hd, bool full) const +{ + std::string res = fmt::format("{}", o2f::DataSpecUtils::describe(o2::framework::OutputSpec{hd.dataOrigin, hd.dataDescription, hd.subSpecification})); + if (full) { + res += fmt::format(" part:{}/{} sz:{} TF:{} Orb:{} Run:{}", hd.splitPayloadIndex, hd.splitPayloadParts, hd.payloadSize, hd.tfCounter, hd.firstTForbit, hd.runNumber); + } + return res; +} + std::uint32_t sRunNumber = 0; // TODO: add id to files metadata std::uint32_t sFirstTForbit = 0; // TODO: add id to files metadata std::uint64_t sCreationTime = 0; std::mutex stfMtx; -std::unique_ptr SubTimeFrameFileReader::read(fair::mq::Device* device, const std::vector& outputRoutes, - const std::string& rawChannel, size_t slice, bool sup0xccdb, int verbosity) +std::unique_ptr SubTimeFrameFileReader::read(fair::mq::Device* device, const std::vector& outputRoutes, const std::string& rawChannel, size_t slice) { std::unique_ptr messagesPerRoute = std::make_unique(); auto& msgMap = *messagesPerRoute.get(); @@ -252,9 +260,15 @@ std::unique_ptr SubTimeFrameFileReader::read(fair::mq::Device* return nullptr; } lStfMetaDataHdr = o2::header::DataHeader::Get(lMetaHdrStack.first()); + if (mVerbosity > 0) { + LOGP(info, "read filemeta, pos = {}, size = {}", position(), sizeof(SubTimeFrameFileMeta)); + } if (!read_advance(&lStfFileMeta, sizeof(SubTimeFrameFileMeta))) { return nullptr; } + if (mVerbosity > 0) { + LOGP(info, "TFMeta : {}", lStfFileMeta.info()); + } if (lStfFileMeta.mWriteTimeMs == 0 && creationFallBack != 0) { if (!creation0Notified) { creation0Notified = true; @@ -318,9 +332,9 @@ std::unique_ptr SubTimeFrameFileReader::read(fair::mq::Device* std::int64_t lLeftToRead = lStfDataSize; STFHeader stfHeader{tfID, -1u, -1u}; + DataHeader prevHeader; // read pairs while (lLeftToRead > 0) { - // allocate and read the Headers std::size_t lDataHeaderStackSize = 0; Stack lDataHeaderStack = getHeaderStack(lDataHeaderStackSize); @@ -335,6 +349,25 @@ std::unique_ptr SubTimeFrameFileReader::read(fair::mq::Device* return nullptr; } DataHeader locDataHeader(*lDataHeader); + + if (mRepaireHeaders) { + if (locDataHeader == prevHeader) { + if (prevHeader.tfCounter == locDataHeader.tfCounter && (prevHeader.splitPayloadIndex + 1) != locDataHeader.splitPayloadIndex) { + if (mVerbosity > 3) { + LOGP(warn, "Repairing wrong part index for {} to {}", describeHeader(locDataHeader, true), (prevHeader.splitPayloadIndex + 1) % prevHeader.splitPayloadParts); + } + locDataHeader.splitPayloadIndex = (++prevHeader.splitPayloadIndex) % prevHeader.splitPayloadParts; + } + } else { // new header + if (locDataHeader.splitPayloadIndex != 0) { + if (mVerbosity > 2) { + LOGP(warn, "Repairing wrong part index for new {} to {}", describeHeader(locDataHeader, true), (prevHeader.splitPayloadIndex + 1) % prevHeader.splitPayloadParts); + } + locDataHeader.splitPayloadIndex = 0; + } + } + prevHeader = locDataHeader; + } // sanity check if (int(locDataHeader.firstTForbit) == -1) { if (!negativeOrbitNotified) { @@ -350,6 +383,18 @@ std::unique_ptr SubTimeFrameFileReader::read(fair::mq::Device* } locDataHeader.runNumber = runNumberFallBack; } + const std::uint64_t lDataSize = locDataHeader.payloadSize; + + if (locDataHeader.dataOrigin == o2::header::gDataOriginFLP && locDataHeader.dataDescription == o2::header::gDataDescriptionDISTSTF && mRejectDistSTF) { + if (mVerbosity > 0) { + LOGP(warn, "Ignoring stored {}", describeHeader(locDataHeader)); + } + if (!ignore_nbytes(lDataSize)) { + return nullptr; + } + lLeftToRead -= (lDataHeaderStackSize + lDataSize); // update the counter + continue; + } o2::header::Stack headerStack{locDataHeader, o2f::DataProcessingHeader{tfID, 1, lStfFileMeta.mWriteTimeMs}}; if (stfHeader.runNumber == -1) { stfHeader.id = locDataHeader.tfCounter; @@ -359,8 +404,6 @@ std::unique_ptr SubTimeFrameFileReader::read(fair::mq::Device* sRunNumber = stfHeader.runNumber; sFirstTForbit = stfHeader.firstOrbit; } - - const std::uint64_t lDataSize = locDataHeader.payloadSize; // do we accept these data? auto detOrigStatus = mDetOrigMap.find(locDataHeader.dataOrigin); if (detOrigStatus != mDetOrigMap.end() && !detOrigStatus->second) { // this is a detector data and we don't want to read it @@ -398,14 +441,15 @@ std::unique_ptr SubTimeFrameFileReader::read(fair::mq::Device* msgSW.Stop(); #endif memcpy(lHdrStackMsg->GetData(), headerStack.data(), headerStack.size()); + LOGP(debug, "read data, pos = {}, size = {} leftToRead {}", position(), lDataSize, lLeftToRead); if (!read_advance(lDataMsg->GetData(), lDataSize)) { return nullptr; } - if (verbosity > 0) { - if (verbosity > 1 || locDataHeader.splitPayloadIndex == 0) { + if (mVerbosity > 0) { + if (mVerbosity > 1 || locDataHeader.splitPayloadIndex == 0) { printStack(headerStack); - if (o2::raw::RDHUtils::checkRDH(lDataMsg->GetData()) && verbosity > 2) { + if (o2::raw::RDHUtils::checkRDH(lDataMsg->GetData()) && mVerbosity > 2) { o2::raw::RDHUtils::printRDH(lDataMsg->GetData()); } } @@ -413,6 +457,9 @@ std::unique_ptr SubTimeFrameFileReader::read(fair::mq::Device* #ifdef _RUN_TIMING_MEASUREMENT_ addPartSW.Start(false); #endif + if (mVerbosity > 2) { + LOGP(info, "addPart {} to {} | HdrSize:{} DataSize:{}", describeHeader(locDataHeader, true), fmqChannel, lHdrStackMsg->GetSize(), lDataMsg->GetSize()); + } addPart(std::move(lHdrStackMsg), std::move(lDataMsg), fmqChannel); #ifdef _RUN_TIMING_MEASUREMENT_ addPartSW.Stop(); @@ -434,7 +481,7 @@ std::unique_ptr SubTimeFrameFileReader::read(fair::mq::Device* } unsigned stfSS[2] = {0, 0xccdb}; - for (int iss = 0; iss < (sup0xccdb ? 1 : 2); iss++) { + for (int iss = 0; iss < (mSup0xccdb ? 1 : 2); iss++) { o2::header::DataHeader stfDistDataHeader(o2::header::gDataDescriptionDISTSTF, o2::header::gDataOriginFLP, stfSS[iss], sizeof(STFHeader), 0, 1); stfDistDataHeader.payloadSerializationMethod = o2::header::gSerializationMethodNone; stfDistDataHeader.firstTForbit = stfHeader.firstOrbit; @@ -444,7 +491,7 @@ std::unique_ptr SubTimeFrameFileReader::read(fair::mq::Device* if (!fmqChannel.empty()) { // no output channel auto fmqFactory = device->GetChannel(fmqChannel, 0).Transport(); o2::header::Stack headerStackSTF{stfDistDataHeader, o2f::DataProcessingHeader{tfID, 1, lStfFileMeta.mWriteTimeMs}}; - if (verbosity > 0) { + if (mVerbosity > 0) { printStack(headerStackSTF); } auto hdMessageSTF = fmqFactory->CreateMessage(headerStackSTF.size(), fair::mq::Alignment{64}); @@ -454,6 +501,9 @@ std::unique_ptr SubTimeFrameFileReader::read(fair::mq::Device* #ifdef _RUN_TIMING_MEASUREMENT_ addPartSW.Start(false); #endif + if (mVerbosity > 2) { + LOGP(info, "addPart forced {} to {} | HdrSize:{} DataSize:{}", describeHeader(stfDistDataHeader, true), fmqChannel, hdMessageSTF->GetSize(), plMessageSTF->GetSize()); + } addPart(std::move(hdMessageSTF), std::move(plMessageSTF), fmqChannel); #ifdef _RUN_TIMING_MEASUREMENT_ addPartSW.Stop(); diff --git a/Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx b/Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx index 919e76083f595..d0de5fb893e3d 100644 --- a/Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx +++ b/Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx @@ -118,6 +118,9 @@ void TFReaderSpec::init(o2f::InitContext& ic) mInput.maxTFsPerFile = mInput.maxTFsPerFile > 0 ? mInput.maxTFsPerFile : 0x7fffffff; mInput.maxTFCache = std::max(1, ic.options().get("max-cached-tf")); mInput.maxFileCache = std::max(1, ic.options().get("max-cached-files")); + mInput.repairHeaders = !ic.options().get("ignore-repair-headers"); + mInput.rejectDistSTF = !ic.options().get("read-dist-stf"); + if (!mInput.fileRunTimeSpans.empty()) { loadRunTimeSpans(mInput.fileRunTimeSpans); } @@ -263,7 +266,11 @@ void TFReaderSpec::run(o2f::ProcessingContext& ctx) setTimingInfo(*tfPtr.get()); size_t nparts = 0, dataSize = 0; if (mInput.sendDummyForMissing) { + int cntAck = 0; for (auto& msgIt : *tfPtr.get()) { // complete with empty output for the specs which were requested but were not seen in the data + if (mInput.verbosity > 0) { + LOGP(info, "acknowledgeOutput {}", cntAck++); + } acknowledgeOutput(*msgIt.second.get(), true); } addMissingParts(*tfPtr.get()); @@ -409,7 +416,7 @@ void TFReaderSpec::TFBuilder() } LOG(info) << "Processing file " << tfFileName; - SubTimeFrameFileReader reader(tfFileName, mInput.detMask); + SubTimeFrameFileReader reader(tfFileName, mInput.detMask, mInput.verbosity, mInput.sup0xccdb, mInput.repairHeaders, mInput.rejectDistSTF); size_t locID = 0; // try { @@ -421,7 +428,7 @@ void TFReaderSpec::TFBuilder() std::this_thread::sleep_for(sleepTime); continue; } - auto tf = reader.read(mDevice, mOutputRoutes, mInput.rawChannelConfig, mAccTFCounter, mInput.sup0xccdb, mInput.verbosity); + auto tf = reader.read(mDevice, mOutputRoutes, mInput.rawChannelConfig, mAccTFCounter); bool acceptTF = true; if (tf) { if (mRunTimeRanges.size()) { @@ -675,6 +682,8 @@ o2f::DataProcessorSpec o2::rawdd::getTFReaderSpec(o2::rawdd::TFReaderInp& rinp) } spec.options.emplace_back(o2f::ConfigParamSpec{"select-tf-ids", o2f::VariantType::String, "", {"comma-separated list TF IDs to inject (from cumulative counter of TFs seen)"}}); spec.options.emplace_back(o2f::ConfigParamSpec{"fetch-failure-threshold", o2f::VariantType::Float, 0.f, {"Fatil if too many failures( >0: fraction, <0: abs number, 0: no threshold)"}}); + spec.options.emplace_back(o2f::ConfigParamSpec{"ignore-repair-headers", o2f::VariantType::Bool, false, {"do not check/repair headers"}}); + spec.options.emplace_back(o2f::ConfigParamSpec{"read-dist-stf", o2f::VariantType::Bool, false, {"do not ignore stored FLP/DISTSUBTIMEFRAME (will clash with injected one)"}}); spec.options.emplace_back(o2f::ConfigParamSpec{"max-tf", o2f::VariantType::Int, -1, {"max TF ID to process (<= 0 : infinite)"}}); spec.options.emplace_back(o2f::ConfigParamSpec{"max-tf-per-file", o2f::VariantType::Int, -1, {"max TFs to process per raw-tf file (<= 0 : infinite)"}}); spec.options.emplace_back(o2f::ConfigParamSpec{"max-cached-tf", o2f::VariantType::Int, 3, {"max TFs to cache in memory"}}); diff --git a/Detectors/Raw/TFReaderDD/src/TFReaderSpec.h b/Detectors/Raw/TFReaderDD/src/TFReaderSpec.h index 2c1c62ecbb414..6ecce0d032c06 100644 --- a/Detectors/Raw/TFReaderDD/src/TFReaderSpec.h +++ b/Detectors/Raw/TFReaderDD/src/TFReaderSpec.h @@ -49,6 +49,8 @@ struct TFReaderInp { bool sendDummyForMissing = true; bool sup0xccdb = false; bool invertIRFramesSelection = false; + bool repairHeaders = true; + bool rejectDistSTF = true; std::vector hdVec; std::vector tfIDs{}; }; diff --git a/Detectors/Raw/TFReaderDD/src/tf-data-dump-workflow.cxx b/Detectors/Raw/TFReaderDD/src/tf-data-dump-workflow.cxx new file mode 100644 index 0000000000000..fbade100d202f --- /dev/null +++ b/Detectors/Raw/TFReaderDD/src/tf-data-dump-workflow.cxx @@ -0,0 +1,46 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/CompletionPolicyHelpers.h" + +using namespace o2::framework; + +void customize(std::vector& workflowOptions) +{ + std::vector options; + options.push_back(ConfigParamSpec{"dataspec", VariantType::String, "tst:TST/A", {"selection string for the data to be proxied"}}); + options.push_back(ConfigParamSpec{"triggerspec", VariantType::String, "", {"selection string for the trigger input (must be also in dataspec if non-empty)"}}); + options.push_back(ConfigParamSpec{"configKeyValues", VariantType::String, "", {"semicolon separated key=value strings"}}); + std::swap(workflowOptions, options); +} + +void customize(std::vector& policies) +{ + policies.push_back({CompletionPolicyHelpers::consumeWhenPastOldestPossibleTimeframe("raw-tf-dump", [](auto const&) -> bool { return true; })}); + // policies.push_back({CompletionPolicyHelpers::consumeWhenAllOrdered("raw-tf-dump", [](auto const&) -> bool { return true; })}); // RSTOREM +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" +#include "RawTFDumpSpec.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); + auto inpconfig = configcontext.options().get("dataspec"); + auto trigger = configcontext.options().get("triggerspec"); + WorkflowSpec specs{o2::rawdd::getRawTFDumpSpec(inpconfig, trigger)}; + return specs; +} diff --git a/Detectors/Raw/TFReaderDD/src/tf-reader-workflow.cxx b/Detectors/Raw/TFReaderDD/src/tf-reader-workflow.cxx index b424353531de7..a29b4dadfdb25 100644 --- a/Detectors/Raw/TFReaderDD/src/tf-reader-workflow.cxx +++ b/Detectors/Raw/TFReaderDD/src/tf-reader-workflow.cxx @@ -34,7 +34,7 @@ void customize(std::vector& workflowOptions) options.push_back(ConfigParamSpec{"copy-dir", VariantType::String, "/tmp/", {"copy base directory for remote files"}}); options.push_back(ConfigParamSpec{"tf-file-regex", VariantType::String, ".+\\.tf$", {"regex string to identify TF files"}}); options.push_back(ConfigParamSpec{"remote-regex", VariantType::String, "^(alien://|)/alice/data/.+", {"regex string to identify remote files"}}); // Use "^/eos/aliceo2/.+" for direct EOS access - options.push_back(ConfigParamSpec{"tf-reader-verbosity", VariantType::Int, 0, {"verbosity level (1 or 2: check RDH, print DH/DPH for 1st or all slices, >2 print RDH)"}}); + options.push_back(ConfigParamSpec{"tf-reader-verbosity", VariantType::Int, 0, {"verbosity level (1 or 2: check RDH, print DH/DPH for 1st or all slices, >2 print RDH), report repairs"}}); options.push_back(ConfigParamSpec{"raw-channel-config", VariantType::String, "", {"optional raw FMQ channel for non-DPL output"}}); options.push_back(ConfigParamSpec{"send-diststf-0xccdb", VariantType::Bool, false, {"send explicit FLP/DISTSUBTIMEFRAME/0xccdb output"}}); options.push_back(ConfigParamSpec{"disable-dummy-output", VariantType::Bool, false, {"Disable sending empty output if corresponding data is not found in the data"}}); diff --git a/Detectors/TPC/calibration/CMakeLists.txt b/Detectors/TPC/calibration/CMakeLists.txt index 675f15e89258b..6aeb497c1cf23 100644 --- a/Detectors/TPC/calibration/CMakeLists.txt +++ b/Detectors/TPC/calibration/CMakeLists.txt @@ -60,6 +60,7 @@ o2_add_library(TPCCalibration src/PressureTemperatureHelper.cxx src/CMVContainer.cxx src/CorrectionMapsLoader.cxx + src/CMVHelper.cxx PUBLIC_LINK_LIBRARIES O2::DataFormatsTPC O2::TPCBaseRecSim O2::TPCReconstruction ROOT::Minuit Microsoft.GSL::GSL @@ -119,7 +120,8 @@ o2_target_root_dictionary(TPCCalibration include/TPCCalibration/CorrectdEdxDistortions.h include/TPCCalibration/PressureTemperatureHelper.h include/TPCCalibration/CMVContainer.h - include/TPCCalibration/CorrectionMapsLoader.h) + include/TPCCalibration/CorrectionMapsLoader.h + include/TPCCalibration/CMVHelper.h) o2_add_test_root_macro(macro/comparePedestalsAndNoise.C PUBLIC_LINK_LIBRARIES O2::TPCBaseRecSim diff --git a/Detectors/TPC/calibration/include/TPCCalibration/CMVHelper.h b/Detectors/TPC/calibration/include/TPCCalibration/CMVHelper.h new file mode 100644 index 0000000000000..d687c6872b8df --- /dev/null +++ b/Detectors/TPC/calibration/include/TPCCalibration/CMVHelper.h @@ -0,0 +1,52 @@ +// 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 CMVHelper.h +/// @author Tuba Gündem, tuba.gundem@cern.ch +/// @brief Helper utilities for reading CMV ROOT files + +#ifndef ALICEO2_TPC_CMVHELPER_H_ +#define ALICEO2_TPC_CMVHELPER_H_ + +#include + +#include "TFile.h" +#include "TTree.h" + +namespace o2::tpc +{ + +struct CMVPerTF; +struct CMVPerTFCompressed; + +struct CMVFileHandle { + TFile* file{nullptr}; + TTree* tree{nullptr}; + bool isCompressed{false}; + CMVPerTFCompressed* tfCompressed{nullptr}; + CMVPerTF* tfRaw{nullptr}; + CMVPerTF* tfDecoded{nullptr}; ///< scratch buffer used when decompressing + long firstTFInTree{-1}; ///< first global TF index from tree UserInfo ("firstTF"); -1 if absent + long lastTFInTree{-1}; ///< last global TF index from tree UserInfo ("lastTF"); -1 if absent + + /// Open path and set up branch addresses. Returns false on any error + bool open(const std::string& path); + + /// Load entry iEntry and return a pointer to the decoded CMVPerTF, or nullptr on error + const CMVPerTF* getEntry(long long iEntry); + + /// Release all resources + void close(); +}; + +} // namespace o2::tpc + +#endif // ALICEO2_TPC_CMVHELPER_H_ diff --git a/Detectors/TPC/calibration/macro/drawCMV.C b/Detectors/TPC/calibration/macro/drawCMV.C index 4f74db16cecd6..78e951fcfd676 100644 --- a/Detectors/TPC/calibration/macro/drawCMV.C +++ b/Detectors/TPC/calibration/macro/drawCMV.C @@ -11,19 +11,19 @@ #if !defined(__CLING__) || defined(__ROOTCLING__) #include -#include #include +#include #include -#include "TFile.h" -#include "TParameter.h" #include "TTree.h" #include "TH1F.h" #include "TH2F.h" #include "TCanvas.h" -#include "TPCCalibration/CMVContainer.h" #include "TPCBase/Utils.h" +#include "TPCCalibration/CMVContainer.h" +#include "TPCCalibration/CMVHelper.h" + #endif using namespace o2::tpc; @@ -38,39 +38,20 @@ TObjArray* drawCMV(std::string_view filename, std::string_view outDir, std::stri arrCanvases->SetName("CMV"); // open file - TFile f(filename.data(), "READ"); - if (f.IsZombie()) { + CMVFileHandle fh; + if (!fh.open(std::string(filename))) { fmt::print("ERROR: cannot open '{}'\n", filename); return arrCanvases; } fmt::print("Opened file: {}\n", filename); + fmt::print("Tree 'ccdb_object' found, entries: {}\n", fh.tree->GetEntries()); - // get TTree - TTree* tree = nullptr; - f.GetObject("ccdb_object", tree); - if (!tree) { - fmt::print("ERROR: TTree 'ccdb_object' not found\n"); - return arrCanvases; - } - fmt::print("Tree 'ccdb_object' found, entries: {}\n", tree->GetEntries()); - - // read metadata - long firstTF = -1, lastTF = -1; - if (auto* userInfo = tree->GetUserInfo()) { - for (int i = 0; i < userInfo->GetSize(); ++i) { - if (auto* p = dynamic_cast*>(userInfo->At(i))) { - if (std::string(p->GetName()) == "firstTF") - firstTF = p->GetVal(); - if (std::string(p->GetName()) == "lastTF") - lastTF = p->GetVal(); - } - } - } - fmt::print("firstTF: {}, lastTF: {}\n", firstTF, lastTF); + fmt::print("firstTF: {}, lastTF: {}\n", fh.firstTFInTree, fh.lastTFInTree); - const int nEntries = tree->GetEntries(); + const int nEntries = fh.tree->GetEntries(); if (nEntries == 0) { fmt::print("ERROR: no entries in tree\n"); + fh.close(); return arrCanvases; } @@ -80,61 +61,62 @@ TObjArray* drawCMV(std::string_view filename, std::string_view outDir, std::stri TH2F* h2d = new TH2F("hCMVvsTimeBin", ";Timebin (200 ns);Common Mode Values (ADC)", 100, 0, nTimeBins, 110, -100.5, 9.5); + h2d->SetDirectory(nullptr); h2d->SetStats(1); TH1F* h1d = new TH1F("hCMV", ";Common Mode Values (ADC);Counts", 110, -100.5, 9.5); + h1d->SetDirectory(nullptr); h1d->SetStats(1); - // auto-detect branch format: compressed or raw - const bool isCompressed = (tree->GetBranch("CMVPerTFCompressed") != nullptr); - const bool isRaw = (tree->GetBranch("CMVPerTF") != nullptr); - if (!isCompressed && !isRaw) { - fmt::print("ERROR: no recognised branch found (expected 'CMVPerTFCompressed' or 'CMVPerTF')\n"); - return arrCanvases; - } - fmt::print("Branch format: {}\n", isCompressed ? "CMVPerTFCompressed" : "CMVPerTF (raw)"); - - o2::tpc::CMVPerTFCompressed* tfCompressed = nullptr; - o2::tpc::CMVPerTF* tfRaw = nullptr; - CMVPerTF* tfDecoded = isCompressed ? new CMVPerTF() : nullptr; + TH1F* h1dCRU = new TH1F("hCRU", ";CRU;Counts", + 360, -0.5, 359.5); + h1dCRU->SetDirectory(nullptr); + h1dCRU->SetStats(1); + TH2F* h2dCRU = new TH2F("hCMVvsCRU", ";CRU;Common Mode Values (ADC)", + 360, -0.5, 359.5, + 110, -100.5, 9.5); + h2dCRU->SetDirectory(nullptr); + h2dCRU->SetStats(0); - if (isCompressed) { - tree->SetBranchAddress("CMVPerTFCompressed", &tfCompressed); - } else { - tree->SetBranchAddress("CMVPerTF", &tfRaw); - } + fmt::print("Branch format: {}\n", fh.isCompressed ? "CMVPerTFCompressed" : "CMVPerTF (raw)"); long firstOrbit = -1; long firstOrbitDPL = -1; + // Pre-allocate fill arrays once; x-values (timebins) are constant across entries and CRUs + const int fillsPerEntry = nCRUs * nTimeBins; + std::vector xArr(fillsPerEntry), yArr(fillsPerEntry), wArr(fillsPerEntry, 1.0), cruArr(fillsPerEntry); + for (int cru = 0; cru < nCRUs; ++cru) { + for (int tb = 0; tb < nTimeBins; ++tb) { + xArr[cru * nTimeBins + tb] = tb; + cruArr[cru * nTimeBins + tb] = cru; + } + } + for (int i = 0; i < nEntries; ++i) { - tree->GetEntry(i); - - // Decompress if needed; resolve to a unified CMVPerTF pointer - const CMVPerTF* tf = nullptr; - if (isCompressed) { - tfCompressed->decompress(tfDecoded); - tf = tfDecoded; - } else { - tf = tfRaw; + const CMVPerTF* tf = fh.getEntry(i); + if (!tf) { + continue; } firstOrbit = tf->firstOrbit; firstOrbitDPL = tf->firstOrbitDPL; - fmt::print("firstOrbit: {}, firstOrbitDPL: {}\n", firstOrbit, firstOrbitDPL); + + fmt::print("Entry {}: firstOrbit: {}, firstOrbitDPL: {}\n", i, firstOrbit, firstOrbitDPL); for (int cru = 0; cru < nCRUs; ++cru) { for (int tb = 0; tb < nTimeBins; ++tb) { - const float cmvValue = tf->getCMVFloat(cru, tb); - h2d->Fill(tb, cmvValue); - h1d->Fill(cmvValue); + yArr[cru * nTimeBins + tb] = tf->getCMVFloat(cru, tb); + // fmt::print("Entry {}: cru: {}, tb: {}, cmv: {}\n", i, cru, tb, tf->getCMVFloat(cru, tb)); } } + h2d->FillN(fillsPerEntry, xArr.data(), yArr.data(), wArr.data()); + h1d->FillN(fillsPerEntry, yArr.data(), wArr.data()); + h2dCRU->FillN(fillsPerEntry, cruArr.data(), yArr.data(), wArr.data()); + h1dCRU->FillN(fillsPerEntry, cruArr.data(), wArr.data()); } - delete tfDecoded; - tree->ResetBranchAddresses(); - delete tfCompressed; + fh.close(); // draw auto* c = new TCanvas("cCMVvsTimeBin", ""); @@ -149,10 +131,20 @@ TObjArray* drawCMV(std::string_view filename, std::string_view outDir, std::stri arrCanvases->Add(c1); + auto* c2 = new TCanvas("cCRUDistribution", ""); + h1dCRU->Draw(); + + arrCanvases->Add(c2); + + auto* c3 = new TCanvas("cCMVvsCRU", ""); + c3->SetLogz(); + h2dCRU->Draw("colz"); + + arrCanvases->Add(c3); + if (outDir.size()) { utils::saveCanvases(*arrCanvases, outDir, "", rootFileName); } - f.Close(); return arrCanvases; } diff --git a/Detectors/TPC/calibration/src/CMVHelper.cxx b/Detectors/TPC/calibration/src/CMVHelper.cxx new file mode 100644 index 0000000000000..abcbd977a9acb --- /dev/null +++ b/Detectors/TPC/calibration/src/CMVHelper.cxx @@ -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. + +/// @file CMVHelper.cxx +/// @author Tuba Gündem, tuba.gundem@cern.ch +/// @brief Helper utilities for reading CMV ROOT files + +#include "TPCCalibration/CMVHelper.h" + +#include + +#include "TPCCalibration/CMVContainer.h" +#include "TParameter.h" + +namespace o2::tpc +{ + +bool CMVFileHandle::open(const std::string& path) +{ + file = TFile::Open(path.c_str()); + if (!file || file->IsZombie()) { + std::cerr << "CMVFileHandle: failed to open: " << path << "\n"; + return false; + } + file->GetObject("ccdb_object", tree); + if (!tree) { + std::cerr << "CMVFileHandle: TTree 'ccdb_object' not found in: " << path << "\n"; + close(); + return false; + } + + // Extract firstTF / lastTF from UserInfo if stored by the aggregation workflow + if (auto* ui = tree->GetUserInfo()) { + if (auto* p = dynamic_cast*>(ui->FindObject("firstTF"))) { + firstTFInTree = p->GetVal(); + } + if (auto* p = dynamic_cast*>(ui->FindObject("lastTF"))) { + lastTFInTree = p->GetVal(); + } + } + + isCompressed = (tree->GetBranch("CMVPerTFCompressed") != nullptr); + const bool isRaw = (tree->GetBranch("CMVPerTF") != nullptr); + if (!isCompressed && !isRaw) { + std::cerr << "CMVFileHandle: no recognised branch (CMVPerTFCompressed / CMVPerTF) in: " + << path << "\n"; + close(); + return false; + } + + if (isCompressed) { + tree->SetBranchAddress("CMVPerTFCompressed", &tfCompressed); + tfDecoded = new CMVPerTF(); + } else { + tree->SetBranchAddress("CMVPerTF", &tfRaw); + } + return true; +} + +const CMVPerTF* CMVFileHandle::getEntry(long long iEntry) +{ + tree->GetEntry(iEntry); + if (isCompressed) { + if (!tfCompressed) { + return nullptr; + } + tfCompressed->decompress(tfDecoded); + return tfDecoded; + } + return tfRaw; +} + +void CMVFileHandle::close() +{ + if (tree) { + tree->ResetBranchAddresses(); + tree = nullptr; + } + tfCompressed = nullptr; + tfRaw = nullptr; + delete tfDecoded; + tfDecoded = nullptr; + if (file) { + file->Close(); + delete file; + file = nullptr; + } +} + +} // namespace o2::tpc diff --git a/Detectors/TPC/calibration/src/CorrectionMapsLoader.cxx b/Detectors/TPC/calibration/src/CorrectionMapsLoader.cxx index 9569e0eb8abd2..c8bdfa0f99350 100644 --- a/Detectors/TPC/calibration/src/CorrectionMapsLoader.cxx +++ b/Detectors/TPC/calibration/src/CorrectionMapsLoader.cxx @@ -28,8 +28,13 @@ using namespace o2::framework; void CorrectionMapsLoader::extractCCDBInputs(ProcessingContext& pc, float tpcScaler) { pc.inputs().get("tpcCorrPar"); - pc.inputs().get("tpcCorrMap"); - pc.inputs().get("tpcCorrMapRef"); + const auto lumiMode = getLumiScaleMode(); + if (lumiMode != LumiScaleMode::NoCorrection && lumiMode != LumiScaleMode::StaticMapOnly) { + pc.inputs().get("tpcCorrMap"); + } + if (lumiMode != LumiScaleMode::NoCorrection) { + pc.inputs().get("tpcCorrMapRef"); + } const int maxDumRep = 5; int dumRep = 0; o2::ctp::LumiInfo lumiObj; @@ -97,6 +102,10 @@ void CorrectionMapsLoader::requestCCDBInputs(std::vector& inputs, con // for MC corrections addInput(inputs, {"tpcCorrMap", "TPC", "CorrMap", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalCorrMapMC), {}, 1)}); // time-dependent addInput(inputs, {"tpcCorrMapRef", "TPC", "CorrMapRef", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalCorrDerivMapMC), {}, 1)}); // time-dependent + } else if (gloOpts.lumiMode == LumiScaleMode::NoCorrection) { + // no correction maps needed — a dummy map is created at runtime + } else if (gloOpts.lumiMode == LumiScaleMode::StaticMapOnly) { + addInput(inputs, {"tpcCorrMapRef", "TPC", "CorrMapRef", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalCorrMapRef), {}, 0)}); // load once } else { LOG(fatal) << "Correction mode unknown! Choose either 0 (default) or 1 (derivative map) for flag corrmap-lumi-mode."; } diff --git a/Detectors/TPC/calibration/src/CorrectionMapsOptions.cxx b/Detectors/TPC/calibration/src/CorrectionMapsOptions.cxx index 604b7c680385b..5518d680420ca 100644 --- a/Detectors/TPC/calibration/src/CorrectionMapsOptions.cxx +++ b/Detectors/TPC/calibration/src/CorrectionMapsOptions.cxx @@ -21,13 +21,13 @@ CorrectionMapsGloOpts CorrectionMapsOptions::parseGlobalOptions(const o2::framew { CorrectionMapsGloOpts tpcopt; auto lumiTypeVal = opts.get("lumi-type"); - if (lumiTypeVal < -1 || lumiTypeVal > 2) { + if (lumiTypeVal < static_cast(LumiScaleType::Unset) || lumiTypeVal >= static_cast(LumiScaleType::Count)) { LOGP(fatal, "Invalid lumi-type value: {}", lumiTypeVal); } tpcopt.lumiType = static_cast(lumiTypeVal); auto lumiModeVal = opts.get("corrmap-lumi-mode"); - if (lumiModeVal < -1 || lumiModeVal > 2) { + if (lumiModeVal < static_cast(LumiScaleMode::Unset) || lumiModeVal >= static_cast(LumiScaleMode::Count)) { LOGP(fatal, "Invalid corrmap-lumi-mode value: {}", lumiModeVal); } tpcopt.lumiMode = static_cast(lumiModeVal); @@ -45,7 +45,7 @@ void CorrectionMapsOptions::addGlobalOptions(std::vector& optio { // these are options which should be added at the workflow level, since they modify the inputs of the devices addOption(options, ConfigParamSpec{"lumi-type", o2::framework::VariantType::Int, 0, {"1 = use CTP lumi for TPC correction scaling, 2 = use TPC scalers for TPC correction scaling"}}); - addOption(options, ConfigParamSpec{"corrmap-lumi-mode", o2::framework::VariantType::Int, 0, {"scaling mode: (default) 0 = static + scale * full; 1 = full + scale * derivative; 2 = full + scale * derivative (for MC)"}}); + addOption(options, ConfigParamSpec{"corrmap-lumi-mode", o2::framework::VariantType::Int, 0, {"scaling mode: (default) 0 = static + scale * full; 1 = full + scale * derivative; 2 = full + scale * derivative (for MC); 3 = no correction; 4 = static only"}}); addOption(options, ConfigParamSpec{"enable-M-shape-correction", o2::framework::VariantType::Bool, false, {"Enable M-shape distortion correction"}}); addOption(options, ConfigParamSpec{"disable-ctp-lumi-request", o2::framework::VariantType::Bool, false, {"do not request CTP lumi (regardless what is used for corrections)"}}); addOption(options, ConfigParamSpec{"disable-lumi-type-consistency-check", o2::framework::VariantType::Bool, false, {"disable check of selected CTP or IDC scaling source being consistent with the map"}}); diff --git a/Detectors/TPC/calibration/src/TPCCalibrationLinkDef.h b/Detectors/TPC/calibration/src/TPCCalibrationLinkDef.h index 14d3d0a8ffb8e..847ae5ad7d788 100644 --- a/Detectors/TPC/calibration/src/TPCCalibrationLinkDef.h +++ b/Detectors/TPC/calibration/src/TPCCalibrationLinkDef.h @@ -124,6 +124,7 @@ #pragma link C++ class std::vector < o2::tpc::DigitAdd> + ; #pragma link C++ class o2::tpc::PressureTemperatureHelper + ; +#pragma link C++ struct o2::tpc::CMVFileHandle + ; #pragma link C++ class o2::tpc::CMVPerTF + ; #pragma link C++ class o2::tpc::CMVPerTFCompressed + ; diff --git a/Detectors/TPC/workflow/CMakeLists.txt b/Detectors/TPC/workflow/CMakeLists.txt index 37ac398db40ec..f64a223f683d8 100644 --- a/Detectors/TPC/workflow/CMakeLists.txt +++ b/Detectors/TPC/workflow/CMakeLists.txt @@ -309,4 +309,9 @@ o2_add_executable(cmv-aggregate SOURCES src/tpc-aggregate-cmv.cxx PUBLIC_LINK_LIBRARIES O2::TPCWorkflow) +o2_add_executable(cmv-trigger + COMPONENT_NAME tpc + SOURCES test/test_cmv-trigger.cxx + PUBLIC_LINK_LIBRARIES O2::TPCWorkflow) + add_subdirectory(readers) diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/CMVToVectorSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/CMVToVectorSpec.h index add37af5706e5..2f9209ee07da8 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/CMVToVectorSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/CMVToVectorSpec.h @@ -23,7 +23,7 @@ namespace o2::tpc /// create a processor spec /// convert CMV raw values to a vector in a CRU -o2::framework::DataProcessorSpec getCMVToVectorSpec(const std::string inputSpec, std::vector const& crus); +o2::framework::DataProcessorSpec getCMVToVectorSpec(std::string const& inputSpec, std::vector const& crus); } // end namespace o2::tpc diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TPCAggregateCMVSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCAggregateCMVSpec.h index b46f2169f06c9..3383da527cccf 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/TPCAggregateCMVSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TPCAggregateCMVSpec.h @@ -49,9 +49,6 @@ #include "CommonUtils/StringUtils.h" #include "DetectorsCommonDataFormats/FileMetaData.h" -using namespace o2::framework; -using o2::header::gDataOriginTPC; - namespace o2::tpc { @@ -114,7 +111,7 @@ class TPCAggregateCMVDevice : public o2::framework::Task initIntervalTree(); } - void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final + void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) final { o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj); } @@ -136,7 +133,7 @@ class TPCAggregateCMVDevice : public o2::framework::Task } if (mSetDataTakingCont) { - mDataTakingContext = pc.services().get(); + mDataTakingContext = pc.services().get(); mSetDataTakingCont = false; } @@ -147,7 +144,7 @@ class TPCAggregateCMVDevice : public o2::framework::Task const auto currTF = processing_helpers::getCurrentTF(pc); if (mTFFirst == -1) { - for (auto& ref : InputRecordWalker(pc.inputs(), mFirstTFFilter)) { + for (auto& ref : o2::framework::InputRecordWalker(pc.inputs(), mFirstTFFilter)) { mTFFirst = pc.inputs().get(ref); mIntervalFirstTF = mTFFirst; mHasIntervalFirstTF = true; @@ -203,7 +200,7 @@ class TPCAggregateCMVDevice : public o2::framework::Task // Capture orbit info first so setTimestampCCDB can use the measured stride if (!mOrbitInfoSeen[relTF]) { // all CRUs within a batch carry identical timing, so the first one is sufficient - for (auto& ref : InputRecordWalker(pc.inputs(), mOrbitFilter)) { + for (auto& ref : o2::framework::InputRecordWalker(pc.inputs(), mOrbitFilter)) { mOrbitInfo[relTF] = pc.inputs().get(ref); const auto batchFirstOrbit = static_cast(mOrbitInfo[relTF] >> 32); // TimingInfo.firstTForbit is the orbit of the last real TF in the batch (the TF that triggered the FLP to send). @@ -222,8 +219,8 @@ class TPCAggregateCMVDevice : public o2::framework::Task setTimestampCCDB(relTF, mOrbitStep[relTF], pc); } - for (auto& ref : InputRecordWalker(pc.inputs(), mFilter)) { - auto const* hdr = DataRefUtils::getHeader(ref); + for (auto& ref : o2::framework::InputRecordWalker(pc.inputs(), mFilter)) { + auto const* hdr = o2::framework::DataRefUtils::getHeader(ref); const unsigned int cru = hdr->subSpecification; if (!(std::binary_search(mCRUs.begin(), mCRUs.end(), cru))) { LOGP(debug, "Received CMV data from CRU {} which is not part of this aggregate lane", cru); @@ -233,7 +230,7 @@ class TPCAggregateCMVDevice : public o2::framework::Task continue; } - auto cmvVec = pc.inputs().get>(ref); + auto cmvVec = pc.inputs().get>(ref); mRawCMVs[relTF][cru] = std::vector(cmvVec.begin(), cmvVec.end()); mProcessedCRUs[relTF][cru] = true; ++mProcessedCRU[relTF]; @@ -257,7 +254,7 @@ class TPCAggregateCMVDevice : public o2::framework::Task materializeBufferedTFs(true); materializeEOSBuffer(); sendOutput(ec.outputs()); - ec.services().get().readyToQuit(QuitRequest::Me); + ec.services().get().readyToQuit(o2::framework::QuitRequest::Me); } static constexpr header::DataDescription getDataDescriptionCCDBCMV() { return header::DataDescription{"TPC_CMV"}; } @@ -314,18 +311,18 @@ class TPCAggregateCMVDevice : public o2::framework::Task std::unique_ptr mIntervalTree{}; ///< in-memory TTree accumulating one entry per real TF; serialised to CCDB/disk at interval end CMVPerTF mCurrentTF{}; ///< staging object written to the TTree branch for the uncompressed path CMVPerTFCompressed mCurrentCompressedTF{}; ///< staging object written to the TTree branch when any compression flags are set - const std::vector mFilter{ + const std::vector mFilter{ {"cmvagg", - ConcreteDataTypeMatcher{gDataOriginTPC, TPCDistributeCMVSpec::getDataDescriptionCMV(mLaneId)}, - Lifetime::Sporadic}}; - const std::vector mOrbitFilter{ + o2::framework::ConcreteDataTypeMatcher{o2::header::gDataOriginTPC, TPCDistributeCMVSpec::getDataDescriptionCMV(mLaneId)}, + o2::framework::Lifetime::Sporadic}}; + const std::vector mOrbitFilter{ {"cmvorbit", - ConcreteDataMatcher{gDataOriginTPC, TPCDistributeCMVSpec::getDataDescriptionCMVOrbitInfo(mLaneId), header::DataHeader::SubSpecificationType{static_cast(mLaneId)}}, - Lifetime::Sporadic}}; - const std::vector mFirstTFFilter{ + o2::framework::ConcreteDataMatcher{o2::header::gDataOriginTPC, TPCDistributeCMVSpec::getDataDescriptionCMVOrbitInfo(mLaneId), header::DataHeader::SubSpecificationType{static_cast(mLaneId)}}, + o2::framework::Lifetime::Sporadic}}; + const std::vector mFirstTFFilter{ {"firstTF", - ConcreteDataMatcher{gDataOriginTPC, TPCDistributeCMVSpec::getDataDescriptionCMVFirstTF(), header::DataHeader::SubSpecificationType{static_cast(mLaneId)}}, - Lifetime::Sporadic}}; + o2::framework::ConcreteDataMatcher{o2::header::gDataOriginTPC, TPCDistributeCMVSpec::getDataDescriptionCMVFirstTF(), header::DataHeader::SubSpecificationType{static_cast(mLaneId)}}, + o2::framework::Lifetime::Sporadic}}; uint8_t buildCompressionFlags() const { @@ -360,7 +357,7 @@ class TPCAggregateCMVDevice : public o2::framework::Task void collectEOSInputs(o2::framework::ProcessingContext& pc) { if (mEOSFirstOrbit == 0) { - for (auto& ref : InputRecordWalker(pc.inputs(), mOrbitFilter)) { + for (auto& ref : o2::framework::InputRecordWalker(pc.inputs(), mOrbitFilter)) { const auto orbitBC = pc.inputs().get(ref); mEOSFirstOrbit = static_cast(orbitBC >> 32); mEOSFirstBC = static_cast(orbitBC & 0xFFFFu); @@ -368,13 +365,13 @@ class TPCAggregateCMVDevice : public o2::framework::Task } } - for (auto& ref : InputRecordWalker(pc.inputs(), mFilter)) { - auto const* hdr = DataRefUtils::getHeader(ref); + for (auto& ref : o2::framework::InputRecordWalker(pc.inputs(), mFilter)) { + auto const* hdr = o2::framework::DataRefUtils::getHeader(ref); const unsigned int cru = hdr->subSpecification; if (!(std::binary_search(mCRUs.begin(), mCRUs.end(), cru))) { continue; } - auto cmvVec = pc.inputs().get>(ref); + auto cmvVec = pc.inputs().get>(ref); auto& buffer = mEOSRawCMVs[cru]; buffer.insert(buffer.end(), cmvVec.begin(), cmvVec.end()); } @@ -548,7 +545,7 @@ class TPCAggregateCMVDevice : public o2::framework::Task } } - void sendOutput(DataAllocator& output) + void sendOutput(o2::framework::DataAllocator& output) { using timer = std::chrono::high_resolution_clock; @@ -619,8 +616,8 @@ class TPCAggregateCMVDevice : public o2::framework::Task } LOGP(info, "Sending object {} / {} of size {} bytes, valid for {} : {}", ccdbInfoCMV.getPath(), ccdbInfoCMV.getFileName(), image->size(), ccdbInfoCMV.getStartValidityTimestamp(), ccdbInfoCMV.getEndValidityTimestamp()); - output.snapshot(Output{o2::calibration::Utils::gDataOriginCDBPayload, getDataDescriptionCCDBCMV(), 0}, *image); - output.snapshot(Output{o2::calibration::Utils::gDataOriginCDBWrapper, getDataDescriptionCCDBCMV(), 0}, ccdbInfoCMV); + output.snapshot(o2::framework::Output{o2::calibration::Utils::gDataOriginCDBPayload, getDataDescriptionCCDBCMV(), 0}, *image); + output.snapshot(o2::framework::Output{o2::calibration::Utils::gDataOriginCDBWrapper, getDataDescriptionCCDBCMV(), 0}, ccdbInfoCMV); auto stop = timer::now(); std::chrono::duration elapsed = stop - start; @@ -666,25 +663,25 @@ class TPCAggregateCMVDevice : public o2::framework::Task /// Build a DataProcessorSpec for one aggregate lane /// Each lane receives CMV data from one distribute output lane (matched by lane index) and expects the full CRU list — the distribute stage already routes per-CRU data to the correct lane -inline DataProcessorSpec getTPCAggregateCMVSpec(const int lane, - const std::vector& crus, - const unsigned int timeframes, - const bool sendCCDB, - const bool usePreciseTimestamp, - const int nTFsBuffer = 1) +inline o2::framework::DataProcessorSpec getTPCAggregateCMVSpec(const int lane, + const std::vector& crus, + const unsigned int timeframes, + const bool sendCCDB, + const bool usePreciseTimestamp, + const int nTFsBuffer = 1) { - std::vector outputSpecs; + std::vector outputSpecs; if (sendCCDB) { - outputSpecs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBPayload, TPCAggregateCMVDevice::getDataDescriptionCCDBCMV()}, Lifetime::Sporadic); - outputSpecs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBWrapper, TPCAggregateCMVDevice::getDataDescriptionCCDBCMV()}, Lifetime::Sporadic); + outputSpecs.emplace_back(o2::framework::ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBPayload, TPCAggregateCMVDevice::getDataDescriptionCCDBCMV()}, o2::framework::Lifetime::Sporadic); + outputSpecs.emplace_back(o2::framework::ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBWrapper, TPCAggregateCMVDevice::getDataDescriptionCCDBCMV()}, o2::framework::Lifetime::Sporadic); } - std::vector inputSpecs; - inputSpecs.emplace_back(InputSpec{"cmvagg", ConcreteDataTypeMatcher{gDataOriginTPC, TPCDistributeCMVSpec::getDataDescriptionCMV(lane)}, Lifetime::Sporadic}); - inputSpecs.emplace_back(InputSpec{"cmvorbit", gDataOriginTPC, TPCDistributeCMVSpec::getDataDescriptionCMVOrbitInfo(lane), header::DataHeader::SubSpecificationType{static_cast(lane)}, Lifetime::Sporadic}); - inputSpecs.emplace_back(InputSpec{"firstTF", gDataOriginTPC, TPCDistributeCMVSpec::getDataDescriptionCMVFirstTF(), header::DataHeader::SubSpecificationType{static_cast(lane)}, Lifetime::Sporadic}); + std::vector inputSpecs; + inputSpecs.emplace_back(o2::framework::InputSpec{"cmvagg", o2::framework::ConcreteDataTypeMatcher{o2::header::gDataOriginTPC, TPCDistributeCMVSpec::getDataDescriptionCMV(lane)}, o2::framework::Lifetime::Sporadic}); + inputSpecs.emplace_back(o2::framework::InputSpec{"cmvorbit", o2::header::gDataOriginTPC, TPCDistributeCMVSpec::getDataDescriptionCMVOrbitInfo(lane), header::DataHeader::SubSpecificationType{static_cast(lane)}, o2::framework::Lifetime::Sporadic}); + inputSpecs.emplace_back(o2::framework::InputSpec{"firstTF", o2::header::gDataOriginTPC, TPCDistributeCMVSpec::getDataDescriptionCMVFirstTF(), header::DataHeader::SubSpecificationType{static_cast(lane)}, o2::framework::Lifetime::Sporadic}); if (usePreciseTimestamp) { - inputSpecs.emplace_back(InputSpec{"orbitreset", gDataOriginTPC, TPCDistributeCMVSpec::getDataDescriptionCMVOrbitReset(), header::DataHeader::SubSpecificationType{static_cast(lane)}, Lifetime::Sporadic}); + inputSpecs.emplace_back(o2::framework::InputSpec{"orbitreset", o2::header::gDataOriginTPC, TPCDistributeCMVSpec::getDataDescriptionCMVOrbitReset(), header::DataHeader::SubSpecificationType{static_cast(lane)}, o2::framework::Lifetime::Sporadic}); } // Request GRPECS from CCDB so that GRPGeomHelper::getNHBFPerTF() is valid in this (separate) process @@ -696,21 +693,21 @@ inline DataProcessorSpec getTPCAggregateCMVSpec(const int lane, o2::base::GRPGeomRequest::None, // geometry inputSpecs); - DataProcessorSpec spec{ + o2::framework::DataProcessorSpec spec{ fmt::format("tpc-aggregate-cmv-{:02}", lane).data(), inputSpecs, outputSpecs, - AlgorithmSpec{adaptFromTask(lane, crus, timeframes, sendCCDB, usePreciseTimestamp, nTFsBuffer, ccdbRequest)}, - Options{{"output-dir", VariantType::String, "/dev/null", {"CMV output directory, must exist (if not /dev/null)"}}, - {"meta-output-dir", VariantType::String, "/dev/null", {"calibration metadata output directory, must exist (if not /dev/null)"}}, - {"nthreads-compression", VariantType::Int, 1, {"Number of threads used for CMV per timeframe preprocessing and compression"}}, - {"use-sparse", VariantType::Bool, false, {"Sparse encoding (skip zero time bins). Alone: raw uint16 values. With --use-compression-varint: varint exact values. With --use-compression-huffman: Huffman exact values"}}, - {"use-compression-varint", VariantType::Bool, false, {"Delta+zigzag+varint compression (all values). Combined with --use-sparse: sparse positions + varint encoded exact CMV values"}}, - {"use-compression-huffman", VariantType::Bool, false, {"Huffman encoding. Combined with --use-sparse: sparse positions + Huffman-encoded exact CMV values"}}, - {"cmv-zero-threshold", VariantType::Float, 0.f, {"Zero out CMV values whose float magnitude is below this threshold after optional integer rounding and before compression; 0 disables"}}, - {"cmv-round-integers-threshold", VariantType::Int, 0, {"Round values to nearest integer ADC for |v| <= N ADC before compression; 0 disables"}}, - {"cmv-dynamic-precision-mean", VariantType::Float, 1.f, {"Gaussian centre in |CMV| ADC where the strongest fractional bit trimming is applied"}}, - {"cmv-dynamic-precision-sigma", VariantType::Float, 0.f, {"Gaussian width in ADC for smooth CMV fractional bit trimming; 0 disables"}}}}; + o2::framework::AlgorithmSpec{o2::framework::adaptFromTask(lane, crus, timeframes, sendCCDB, usePreciseTimestamp, nTFsBuffer, ccdbRequest)}, + o2::framework::Options{{"output-dir", o2::framework::VariantType::String, "/dev/null", {"CMV output directory, must exist (if not /dev/null)"}}, + {"meta-output-dir", o2::framework::VariantType::String, "/dev/null", {"calibration metadata output directory, must exist (if not /dev/null)"}}, + {"nthreads-compression", o2::framework::VariantType::Int, 1, {"Number of threads used for CMV per timeframe preprocessing and compression"}}, + {"use-sparse", o2::framework::VariantType::Bool, false, {"Sparse encoding (skip zero time bins). Alone: raw uint16 values. With --use-compression-varint: varint exact values. With --use-compression-huffman: Huffman exact values"}}, + {"use-compression-varint", o2::framework::VariantType::Bool, false, {"Delta+zigzag+varint compression (all values). Combined with --use-sparse: sparse positions + varint encoded exact CMV values"}}, + {"use-compression-huffman", o2::framework::VariantType::Bool, false, {"Huffman encoding. Combined with --use-sparse: sparse positions + Huffman-encoded exact CMV values"}}, + {"cmv-zero-threshold", o2::framework::VariantType::Float, 0.f, {"Zero out CMV values whose float magnitude is below this threshold after optional integer rounding and before compression; 0 disables"}}, + {"cmv-round-integers-threshold", o2::framework::VariantType::Int, 0, {"Round values to nearest integer ADC for |v| <= N ADC before compression; 0 disables"}}, + {"cmv-dynamic-precision-mean", o2::framework::VariantType::Float, 1.f, {"Gaussian centre in |CMV| ADC where the strongest fractional bit trimming is applied"}}, + {"cmv-dynamic-precision-sigma", o2::framework::VariantType::Float, 0.f, {"Gaussian width in ADC for smooth CMV fractional bit trimming; 0 disables"}}}}; spec.rank = lane; return spec; } diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TPCDistributeCMVSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCDistributeCMVSpec.h index f3373070ab7bb..af576b2f30a5b 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/TPCDistributeCMVSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TPCDistributeCMVSpec.h @@ -36,10 +36,6 @@ #include "DetectorsBase/GRPGeomHelper.h" #include "CommonDataFormat/Pair.h" -using namespace o2::framework; -using o2::header::gDataOriginTPC; -using namespace o2::tpc; - namespace o2::tpc { @@ -78,8 +74,8 @@ class TPCDistributeCMVSpec : public o2::framework::Task } } - mFilter.emplace_back(InputSpec{"cmvsgroup", ConcreteDataTypeMatcher{gDataOriginTPC, TPCFLPCMVDevice::getDataDescriptionCMVGroup()}, Lifetime::Sporadic}); - mOrbitFilter.emplace_back(InputSpec{"cmvorbit", ConcreteDataTypeMatcher{gDataOriginTPC, TPCFLPCMVDevice::getDataDescriptionCMVOrbitInfo()}, Lifetime::Sporadic}); + mFilter.emplace_back(o2::framework::InputSpec{"cmvsgroup", o2::framework::ConcreteDataTypeMatcher{o2::header::gDataOriginTPC, TPCFLPCMVDevice::getDataDescriptionCMVGroup()}, o2::framework::Lifetime::Sporadic}); + mOrbitFilter.emplace_back(o2::framework::InputSpec{"cmvorbit", o2::framework::ConcreteDataTypeMatcher{o2::header::gDataOriginTPC, TPCFLPCMVDevice::getDataDescriptionCMVOrbitInfo()}, o2::framework::Lifetime::Sporadic}); } void init(o2::framework::InitContext& ic) final @@ -97,13 +93,13 @@ class TPCDistributeCMVSpec : public o2::framework::Task } } - void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final + void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) final { o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj); - if (matcher == ConcreteDataMatcher("CTP", "ORBITRESET", 0)) { + if (matcher == o2::framework::ConcreteDataMatcher("CTP", "ORBITRESET", 0)) { LOGP(debug, "Updating ORBITRESET"); std::fill(mSendCCDBOutputOrbitReset.begin(), mSendCCDBOutputOrbitReset.end(), true); - } else if (matcher == ConcreteDataMatcher("GLO", "GRPECS", 0)) { + } else if (matcher == o2::framework::ConcreteDataMatcher("GLO", "GRPECS", 0)) { // check if received object is valid if (o2::base::GRPGeomHelper::instance().getGRPECS()->getRun() != 0) { LOGP(debug, "Updating GRPECS"); @@ -175,18 +171,18 @@ class TPCDistributeCMVSpec : public o2::framework::Task if (mSendOutputStartInfo[currentBuffer]) { mSendOutputStartInfo[currentBuffer] = false; - pc.outputs().snapshot(Output{gDataOriginTPC, getDataDescriptionCMVFirstTF(), header::DataHeader::SubSpecificationType{currentOutLane}}, mTFStart[currentBuffer]); + pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginTPC, getDataDescriptionCMVFirstTF(), header::DataHeader::SubSpecificationType{currentOutLane}}, mTFStart[currentBuffer]); } if (mSendCCDBOutputOrbitReset[currentOutLane] && mSendCCDBOutputGRPECS[currentOutLane]) { mSendCCDBOutputOrbitReset[currentOutLane] = false; mSendCCDBOutputGRPECS[currentOutLane] = false; - pc.outputs().snapshot(Output{gDataOriginTPC, getDataDescriptionCMVOrbitReset(), header::DataHeader::SubSpecificationType{currentOutLane}}, dataformats::Pair{o2::base::GRPGeomHelper::instance().getOrbitResetTimeMS(), o2::base::GRPGeomHelper::instance().getNHBFPerTF()}); + pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginTPC, getDataDescriptionCMVOrbitReset(), header::DataHeader::SubSpecificationType{currentOutLane}}, dataformats::Pair{o2::base::GRPGeomHelper::instance().getOrbitResetTimeMS(), o2::base::GRPGeomHelper::instance().getNHBFPerTF()}); } forwardOrbitInfo(pc, currentBuffer, relTF, currentOutLane); - for (auto& ref : InputRecordWalker(pc.inputs(), mFilter)) { + for (auto& ref : o2::framework::InputRecordWalker(pc.inputs(), mFilter)) { auto const* tpcCRUHeader = o2::framework::DataRefUtils::getHeader(ref); const unsigned int cru = tpcCRUHeader->subSpecification >> 7; @@ -204,7 +200,7 @@ class TPCDistributeCMVSpec : public o2::framework::Task // to keep track of processed CRUs mProcessedCRUs[currentBuffer][relTF][cru] = true; - sendOutput(pc, currentOutLane, cru, pc.inputs().get>(ref)); + sendOutput(pc, currentOutLane, cru, pc.inputs().get>(ref)); } LOGP(detail, "Number of received CRUs for current TF: {} Needed a total number of processed CRUs of: {} Current TF: {}", mProcessedCRU[currentBuffer][relTF], mCRUs.size(), tf); @@ -223,7 +219,7 @@ class TPCDistributeCMVSpec : public o2::framework::Task } } - void endOfStream(o2::framework::EndOfStreamContext& ec) final { ec.services().get().readyToQuit(QuitRequest::Me); } + void endOfStream(o2::framework::EndOfStreamContext& ec) final { ec.services().get().readyToQuit(o2::framework::QuitRequest::Me); } /// Return data description for aggregated CMVs for a given lane static header::DataDescription getDataDescriptionCMV(const unsigned int lane) @@ -267,8 +263,8 @@ class TPCDistributeCMVSpec : public o2::framework::Task std::array mStartNTFsDataDrop{0}; ///< first relative TF index to check for missing data in each buffer long mProcessedTotalData{0}; ///< call counter used to throttle checkIntervalsForMissingData checks int mCheckEveryNData{1}; ///< check for missing data every N run() calls (0 → default = mTimeFrames/2) - std::vector mFilter{}; ///< filter for looping over CMVGROUP input data from FLPs - std::vector mOrbitFilter{}; ///< filter for CMVORBITINFO input from FLPs + std::vector mFilter{}; ///< filter for looping over CMVGROUP input data from FLPs + std::vector mOrbitFilter{}; ///< filter for CMVORBITINFO input from FLPs std::vector mDataDescrOut{}; ///< per-output-lane CMV data descriptions (CMVAGG0, CMVAGG1, …) std::vector mOrbitDescrOut{}; ///< per-output-lane orbit-info data descriptions (CMVORB0, CMVORB1, …) std::array, 2> mOrbitInfoForwarded{}; ///< tracks whether orbit/BC has been forwarded to the aggregate lane per (buffer, relTF) @@ -280,12 +276,12 @@ class TPCDistributeCMVSpec : public o2::framework::Task void sendOutput(o2::framework::ProcessingContext& pc, const unsigned int currentOutLane, const unsigned int cru, o2::pmr::vector cmvs) { - pc.outputs().adoptContainer(Output{gDataOriginTPC, mDataDescrOut[currentOutLane], header::DataHeader::SubSpecificationType{cru}}, std::move(cmvs)); + pc.outputs().adoptContainer(o2::framework::Output{o2::header::gDataOriginTPC, mDataDescrOut[currentOutLane], header::DataHeader::SubSpecificationType{cru}}, std::move(cmvs)); } void sendOrbitInfo(o2::framework::ProcessingContext& pc, const unsigned int outLane, const uint64_t orbitInfo) { - pc.outputs().snapshot(Output{gDataOriginTPC, mOrbitDescrOut[outLane], header::DataHeader::SubSpecificationType{outLane}}, orbitInfo); + pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginTPC, mOrbitDescrOut[outLane], header::DataHeader::SubSpecificationType{outLane}}, orbitInfo); } void forwardOrbitInfo(o2::framework::ProcessingContext& pc, const bool currentBuffer, const unsigned int relTF, const unsigned int currentOutLane) @@ -294,7 +290,7 @@ class TPCDistributeCMVSpec : public o2::framework::Task return; } - for (auto& ref : InputRecordWalker(pc.inputs(), mOrbitFilter)) { + for (auto& ref : o2::framework::InputRecordWalker(pc.inputs(), mOrbitFilter)) { auto const* hdr = o2::framework::DataRefUtils::getHeader(ref); const unsigned int cru = hdr->subSpecification >> 7; if (!std::binary_search(mCRUs.begin(), mCRUs.end(), cru)) { @@ -313,17 +309,17 @@ class TPCDistributeCMVSpec : public o2::framework::Task if (mSendOutputStartInfo[mBuffer] && (mTFStart[mBuffer] >= 0)) { mSendOutputStartInfo[mBuffer] = false; - pc.outputs().snapshot(Output{gDataOriginTPC, getDataDescriptionCMVFirstTF(), header::DataHeader::SubSpecificationType{currentOutLane}}, mTFStart[mBuffer]); + pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginTPC, getDataDescriptionCMVFirstTF(), header::DataHeader::SubSpecificationType{currentOutLane}}, mTFStart[mBuffer]); } if (mSendCCDBOutputOrbitReset[currentOutLane] && mSendCCDBOutputGRPECS[currentOutLane]) { mSendCCDBOutputOrbitReset[currentOutLane] = false; mSendCCDBOutputGRPECS[currentOutLane] = false; - pc.outputs().snapshot(Output{gDataOriginTPC, getDataDescriptionCMVOrbitReset(), header::DataHeader::SubSpecificationType{currentOutLane}}, dataformats::Pair{o2::base::GRPGeomHelper::instance().getOrbitResetTimeMS(), o2::base::GRPGeomHelper::instance().getNHBFPerTF()}); + pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginTPC, getDataDescriptionCMVOrbitReset(), header::DataHeader::SubSpecificationType{currentOutLane}}, dataformats::Pair{o2::base::GRPGeomHelper::instance().getOrbitResetTimeMS(), o2::base::GRPGeomHelper::instance().getNHBFPerTF()}); } if (!mOrbitInfoForwarded[mBuffer].empty()) { - for (auto& ref : InputRecordWalker(pc.inputs(), mOrbitFilter)) { + for (auto& ref : o2::framework::InputRecordWalker(pc.inputs(), mOrbitFilter)) { auto const* hdr = o2::framework::DataRefUtils::getHeader(ref); const unsigned int cru = hdr->subSpecification >> 7; if (!std::binary_search(mCRUs.begin(), mCRUs.end(), cru)) { @@ -334,13 +330,13 @@ class TPCDistributeCMVSpec : public o2::framework::Task } } - for (auto& ref : InputRecordWalker(pc.inputs(), mFilter)) { + for (auto& ref : o2::framework::InputRecordWalker(pc.inputs(), mFilter)) { auto const* hdr = o2::framework::DataRefUtils::getHeader(ref); const unsigned int cru = hdr->subSpecification >> 7; if (!std::binary_search(mCRUs.begin(), mCRUs.end(), cru)) { continue; } - sendOutput(pc, currentOutLane, cru, pc.inputs().get>(ref)); + sendOutput(pc, currentOutLane, cru, pc.inputs().get>(ref)); } } @@ -398,7 +394,7 @@ class TPCDistributeCMVSpec : public o2::framework::Task for (auto& it : mProcessedCRUs[currentBuffer][iTF]) { if (!it.second) { it.second = true; - sendOutput(pc, outLane, it.first, pmr::vector()); + sendOutput(pc, outLane, it.first, o2::pmr::vector()); } } @@ -417,7 +413,7 @@ class TPCDistributeCMVSpec : public o2::framework::Task mNFactorTFs = 0; // ToDo: Find better fix. Set oldestForChannel to a very large value so the DPL dispatcher does not block waiting for older TF data that will never arrive for (unsigned int ilane = 0; ilane < mOutLanes; ++ilane) { - auto& deviceProxy = pc.services().get(); + auto& deviceProxy = pc.services().get(); auto& state = deviceProxy.getOutputChannelState({static_cast(ilane)}); size_t oldest = std::numeric_limits::max() - 1; state.oldestForChannel = {oldest}; @@ -431,18 +427,18 @@ class TPCDistributeCMVSpec : public o2::framework::Task } }; -DataProcessorSpec getTPCDistributeCMVSpec(const int ilane, const std::vector& crus, const unsigned int timeframes, const unsigned int outlanes, const int firstTF, const bool sendPrecisetimeStamp = false, const int nTFsBuffer = 1) +o2::framework::DataProcessorSpec getTPCDistributeCMVSpec(const int ilane, const std::vector& crus, const unsigned int timeframes, const unsigned int outlanes, const int firstTF, const bool sendPrecisetimeStamp = false, const int nTFsBuffer = 1) { - std::vector inputSpecs; - inputSpecs.emplace_back(InputSpec{"cmvsgroup", ConcreteDataTypeMatcher{gDataOriginTPC, TPCFLPCMVDevice::getDataDescriptionCMVGroup()}, Lifetime::Sporadic}); - inputSpecs.emplace_back(InputSpec{"cmvorbit", ConcreteDataTypeMatcher{gDataOriginTPC, TPCFLPCMVDevice::getDataDescriptionCMVOrbitInfo()}, Lifetime::Sporadic}); + std::vector inputSpecs; + inputSpecs.emplace_back(o2::framework::InputSpec{"cmvsgroup", o2::framework::ConcreteDataTypeMatcher{o2::header::gDataOriginTPC, TPCFLPCMVDevice::getDataDescriptionCMVGroup()}, o2::framework::Lifetime::Sporadic}); + inputSpecs.emplace_back(o2::framework::InputSpec{"cmvorbit", o2::framework::ConcreteDataTypeMatcher{o2::header::gDataOriginTPC, TPCFLPCMVDevice::getDataDescriptionCMVOrbitInfo()}, o2::framework::Lifetime::Sporadic}); - std::vector outputSpecs; + std::vector outputSpecs; outputSpecs.reserve(3 * outlanes); for (unsigned int lane = 0; lane < outlanes; ++lane) { - outputSpecs.emplace_back(ConcreteDataTypeMatcher{gDataOriginTPC, TPCDistributeCMVSpec::getDataDescriptionCMV(lane)}, Lifetime::Sporadic); - outputSpecs.emplace_back(ConcreteDataMatcher{gDataOriginTPC, TPCDistributeCMVSpec::getDataDescriptionCMVOrbitInfo(lane), header::DataHeader::SubSpecificationType{lane}}, Lifetime::Sporadic); - outputSpecs.emplace_back(ConcreteDataMatcher{gDataOriginTPC, TPCDistributeCMVSpec::getDataDescriptionCMVFirstTF(), header::DataHeader::SubSpecificationType{lane}}, Lifetime::Sporadic); + outputSpecs.emplace_back(o2::framework::ConcreteDataTypeMatcher{o2::header::gDataOriginTPC, TPCDistributeCMVSpec::getDataDescriptionCMV(lane)}, o2::framework::Lifetime::Sporadic); + outputSpecs.emplace_back(o2::framework::ConcreteDataMatcher{o2::header::gDataOriginTPC, TPCDistributeCMVSpec::getDataDescriptionCMVOrbitInfo(lane), header::DataHeader::SubSpecificationType{lane}}, o2::framework::Lifetime::Sporadic); + outputSpecs.emplace_back(o2::framework::ConcreteDataMatcher{o2::header::gDataOriginTPC, TPCDistributeCMVSpec::getDataDescriptionCMVFirstTF(), header::DataHeader::SubSpecificationType{lane}}, o2::framework::Lifetime::Sporadic); } // Only lane 0 fetches CCDB orbit-reset/GRPECS objects and broadcasts them to all aggregate lanes, the other distribute lanes do not need them, avoiding redundant CCDB requests @@ -450,7 +446,7 @@ DataProcessorSpec getTPCDistributeCMVSpec(const int ilane, const std::vector(crus, timeframes, nTFsBuffer, outlanes, firstTF, ccdbRequest)}, - Options{{"drop-data-after-nTFs", VariantType::Int, 0, {"Number of TFs after which to drop the data."}}, - {"check-data-every-n", VariantType::Int, 0, {"Number of run function called after which to check for missing data (-1 for no checking, 0 for default checking)."}}, - {"nFactorTFs", VariantType::Int, 1000, {"Number of TFs to skip for sending oldest TF."}}}}; + o2::framework::AlgorithmSpec{o2::framework::adaptFromTask(crus, timeframes, nTFsBuffer, outlanes, firstTF, ccdbRequest)}, + o2::framework::Options{{"drop-data-after-nTFs", o2::framework::VariantType::Int, 0, {"Number of TFs after which to drop the data."}}, + {"check-data-every-n", o2::framework::VariantType::Int, 0, {"Number of run function called after which to check for missing data (-1 for no checking, 0 for default checking)."}}, + {"nFactorTFs", o2::framework::VariantType::Int, 1000, {"Number of TFs to skip for sending oldest TF."}}}}; spec.rank = ilane; return spec; } diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TPCFLPCMVSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCFLPCMVSpec.h index 9931c27c9d3fa..d86356234a1c2 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/TPCFLPCMVSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TPCFLPCMVSpec.h @@ -13,8 +13,8 @@ /// @author Tuba Gündem, tuba.gundem@cern.ch /// @brief TPC device for processing CMVs on FLPs -#ifndef O2_TPCFLPIDCSPEC_H -#define O2_TPCFLPIDCSPEC_H +#ifndef O2_TPCFLPCMVSPEC_H +#define O2_TPCFLPCMVSPEC_H #include #include @@ -28,24 +28,27 @@ #include "Headers/DataHeader.h" #include "TPCWorkflow/ProcessingHelpers.h" #include "TPCBase/CRU.h" +#include "DataFormatsTPC/CMV.h" #include "TFile.h" -using namespace o2::framework; -using o2::header::gDataOriginTPC; -using namespace o2::tpc; - namespace o2::tpc { class TPCFLPCMVDevice : public o2::framework::Task { public: - TPCFLPCMVDevice(const int lane, const std::vector& crus, const int nTFsBuffer) - : mLane{lane}, mCRUs{crus}, mNTFsBuffer{nTFsBuffer} {} + TPCFLPCMVDevice(const int lane, const std::vector& crus, const bool triggerPerFlp, const int nTFsBuffer) + : mLane{lane}, mCRUs{crus}, mTriggerPerFLP{triggerPerFlp}, mNTFsBuffer{nTFsBuffer} {} void init(o2::framework::InitContext& ic) final { mDumpCMVs = ic.options().get("dump-cmvs-flp"); + mEnableTrigger = ic.options().get("trigger"); + mTriggerThresholdCMV = ic.options().get("trigger-threshold-cmv"); + mTriggerThresholdMeanMax = ic.options().get("trigger-threshold-cmvMeanMax"); + mTriggerThresholdMeanMin = ic.options().get("trigger-threshold-cmvMeanMin"); + mTriggerTimebinMin = ic.options().get("trigger-threshold-timebinMin"); + mTriggerTimebinMax = ic.options().get("trigger-threshold-timebinMax"); } void run(o2::framework::ProcessingContext& pc) final @@ -56,7 +59,7 @@ class TPCFLPCMVDevice : public o2::framework::Task // Capture heartbeatOrbit / heartbeatBC from the first TF in the buffer if (mCountTFsForBuffer == 1) { - for (auto& ref : InputRecordWalker(pc.inputs(), mOrbitFilter)) { + for (auto& ref : o2::framework::InputRecordWalker(pc.inputs(), mOrbitFilter)) { auto const* hdr = o2::framework::DataRefUtils::getHeader(ref); const uint32_t cru = hdr->subSpecification >> 7; if (mFirstOrbitBC.find(cru) == mFirstOrbitBC.end()) { @@ -68,11 +71,23 @@ class TPCFLPCMVDevice : public o2::framework::Task } } - for (auto& ref : InputRecordWalker(pc.inputs(), mFilter)) { + bool triggered = false; + for (auto& ref : o2::framework::InputRecordWalker(pc.inputs(), mFilter)) { auto const* tpcCRUHeader = o2::framework::DataRefUtils::getHeader(ref); - const int cru = tpcCRUHeader->subSpecification >> 7; + const uint32_t cru = tpcCRUHeader->subSpecification >> 7; auto vecCMVs = pc.inputs().get>(ref); mCMVs[cru].insert(mCMVs[cru].end(), vecCMVs.begin(), vecCMVs.end()); + + const bool cruTriggered = mEnableTrigger && evaluateTrigger(vecCMVs); + if (!mTriggerPerFLP) { + pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginTPC, getDataDescriptionCMVTrigger(), tpcCRUHeader->subSpecification}, cruTriggered); + } else { + triggered |= cruTriggered; + } + } + if (mTriggerPerFLP) { + const header::DataHeader::SubSpecificationType trigSubSpec{mCRUs.front() << 7}; + pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginTPC, getDataDescriptionCMVTrigger(), trigSubSpec}, triggered); } if (mCountTFsForBuffer >= mNTFsBuffer) { @@ -86,7 +101,7 @@ class TPCFLPCMVDevice : public o2::framework::Task if (mDumpCMVs) { TFile fOut(fmt::format("CMVs_{}_tf_{}.root", mLane, processing_helpers::getCurrentTF(pc)).data(), "RECREATE"); - for (auto& ref : InputRecordWalker(pc.inputs(), mFilter)) { + for (auto& ref : o2::framework::InputRecordWalker(pc.inputs(), mFilter)) { auto const* tpcCRUHeader = o2::framework::DataRefUtils::getHeader(ref); const int cru = tpcCRUHeader->subSpecification >> 7; auto vec = pc.inputs().get>(ref); @@ -103,7 +118,7 @@ class TPCFLPCMVDevice : public o2::framework::Task sendOutput(ec.outputs(), cru); } } - ec.services().get().readyToQuit(QuitRequest::Me); + ec.services().get().readyToQuit(o2::framework::QuitRequest::Me); } static constexpr header::DataDescription getDataDescriptionCMVGroup() { return header::DataDescription{"CMVGROUP"}; } @@ -111,21 +126,65 @@ class TPCFLPCMVDevice : public o2::framework::Task /// Data description for the packed (orbit<<32|bc) scalar forwarded alongside each CRU's CMVGROUP. static constexpr header::DataDescription getDataDescriptionCMVOrbitInfo() { return header::DataDescription{"CMVORBITINFO"}; } + /// Data description for the per-CRU per-TF trigger flag (empty span = not triggered or disabled; {1} = triggered). + static constexpr header::DataDescription getDataDescriptionCMVTrigger() { return header::DataDescription{"CMVTRIGGER"}; } + private: const int mLane{}; ///< lane number of processor const std::vector mCRUs{}; ///< CRUs to process in this instance int mNTFsBuffer{1}; ///< number of TFs to buffer before sending bool mDumpCMVs{}; ///< dump CMVs to file for debugging + bool mTriggerPerFLP{false}; ///< send per-FLP trigger decision aggregated over CRUs int mCountTFsForBuffer{0}; ///< counts TFs to track when to send output std::unordered_map> mCMVs{}; ///< buffered raw 16-bit CMV values per CRU std::unordered_map mFirstOrbitBC{}; ///< first packed orbit/BC per CRU for the current buffer window + bool mEnableTrigger{false}; ///< enable CMV trigger evaluation + float mTriggerThresholdCMV{-10.f}; ///< CMV value threshold: trigger sequence starts when value drops below this + float mTriggerThresholdMeanMax{-40.f}; ///< upper bound on trigger-sequence mean CMV value + float mTriggerThresholdMeanMin{-80.f}; ///< lower bound on trigger-sequence mean CMV value + int mTriggerTimebinMin{4}; ///< minimum trigger-sequence length (timebins) to accept + int mTriggerTimebinMax{-1}; ///< maximum trigger-sequence length (timebins) to accept; -1 disables /// Filter for CMV float vectors (one CMVVECTOR message per CRU per TF) - const std::vector mFilter = {{"cmvs", ConcreteDataTypeMatcher{gDataOriginTPC, "CMVVECTOR"}, Lifetime::Timeframe}}; + const std::vector mFilter = {{"cmvs", o2::framework::ConcreteDataTypeMatcher{o2::header::gDataOriginTPC, "CMVVECTOR"}, o2::framework::Lifetime::Timeframe}}; /// Filter for CMV packet timing info (one CMVORBITS message per CRU per TF, sent by CMVToVectorSpec) - const std::vector mOrbitFilter = {{"cmvorbits", ConcreteDataTypeMatcher{gDataOriginTPC, "CMVORBITS"}, Lifetime::Timeframe}}; + const std::vector mOrbitFilter = {{"cmvorbits", o2::framework::ConcreteDataTypeMatcher{o2::header::gDataOriginTPC, "CMVORBITS"}, o2::framework::Lifetime::Timeframe}}; + + // Scan a CRU's CMV vector for contiguous below-threshold sequences. + // Returns true as soon as one sequence satisfies both the length and mean criteria. + bool evaluateTrigger(const o2::pmr::vector& cmvs) const + { + float seqSum = 0.f; + int seqLen = 0; + + auto checkSequence = [&]() -> bool { + if (seqLen == 0) { + return false; + } + const float mean = seqSum / seqLen; + return (seqLen >= mTriggerTimebinMin) && + (mTriggerTimebinMax < 0 || seqLen <= mTriggerTimebinMax) && + (mean >= mTriggerThresholdMeanMin) && + (mean <= mTriggerThresholdMeanMax); + }; + + for (const auto raw : cmvs) { + const float val = cmv::Data{raw}.getCMVFloat(); + if (val < mTriggerThresholdCMV) { + seqSum += val; + ++seqLen; + } else { + if (checkSequence()) { + return true; + } + seqLen = 0; + seqSum = 0.f; + } + } + return checkSequence(); // trailing sequence that reached end of buffer + } - void sendOutput(DataAllocator& output, const uint32_t cru) + void sendOutput(o2::framework::DataAllocator& output, const uint32_t cru) { const header::DataHeader::SubSpecificationType subSpec{cru << 7}; @@ -134,39 +193,54 @@ class TPCFLPCMVDevice : public o2::framework::Task if (auto it = mFirstOrbitBC.find(cru); it != mFirstOrbitBC.end()) { orbitBC = it->second; } - output.snapshot(Output{gDataOriginTPC, getDataDescriptionCMVOrbitInfo(), subSpec}, orbitBC); + output.snapshot(o2::framework::Output{o2::header::gDataOriginTPC, getDataDescriptionCMVOrbitInfo(), subSpec}, orbitBC); - output.adoptContainer(Output{gDataOriginTPC, getDataDescriptionCMVGroup(), subSpec}, std::move(mCMVs[cru])); + output.adoptContainer(o2::framework::Output{o2::header::gDataOriginTPC, getDataDescriptionCMVGroup(), subSpec}, std::move(mCMVs[cru])); } }; -DataProcessorSpec getTPCFLPCMVSpec(const int ilane, const std::vector& crus, const int nTFsBuffer = 1) +o2::framework::DataProcessorSpec getTPCFLPCMVSpec(const int ilane, const std::vector& crus, const bool triggerPerFlp, const int nTFsBuffer = 1) { - std::vector outputSpecs; - std::vector inputSpecs; - outputSpecs.reserve(crus.size()); - inputSpecs.reserve(crus.size()); + std::vector outputSpecs; + std::vector inputSpecs; + outputSpecs.reserve(crus.size() * 2 + 1); + inputSpecs.reserve(crus.size() * 2); for (const auto& cru : crus) { const header::DataHeader::SubSpecificationType subSpec{cru << 7}; // Inputs from CMVToVectorSpec - inputSpecs.emplace_back(InputSpec{"cmvs", gDataOriginTPC, "CMVVECTOR", subSpec, Lifetime::Timeframe}); - inputSpecs.emplace_back(InputSpec{"cmvorbits", gDataOriginTPC, "CMVORBITS", subSpec, Lifetime::Timeframe}); + inputSpecs.emplace_back(o2::framework::InputSpec{"cmvs", o2::header::gDataOriginTPC, "CMVVECTOR", subSpec, o2::framework::Lifetime::Timeframe}); + inputSpecs.emplace_back(o2::framework::InputSpec{"cmvorbits", o2::header::gDataOriginTPC, "CMVORBITS", subSpec, o2::framework::Lifetime::Timeframe}); // Outputs to TPCDistributeCMVSpec - outputSpecs.emplace_back(ConcreteDataMatcher{gDataOriginTPC, TPCFLPCMVDevice::getDataDescriptionCMVGroup(), subSpec}, Lifetime::Sporadic); - outputSpecs.emplace_back(ConcreteDataMatcher{gDataOriginTPC, TPCFLPCMVDevice::getDataDescriptionCMVOrbitInfo(), subSpec}, Lifetime::Sporadic); + outputSpecs.emplace_back(o2::framework::ConcreteDataMatcher{o2::header::gDataOriginTPC, TPCFLPCMVDevice::getDataDescriptionCMVGroup(), subSpec}, o2::framework::Lifetime::Sporadic); + outputSpecs.emplace_back(o2::framework::ConcreteDataMatcher{o2::header::gDataOriginTPC, TPCFLPCMVDevice::getDataDescriptionCMVOrbitInfo(), subSpec}, o2::framework::Lifetime::Sporadic); + + if (!triggerPerFlp) { + outputSpecs.emplace_back(o2::framework::ConcreteDataMatcher{o2::header::gDataOriginTPC, TPCFLPCMVDevice::getDataDescriptionCMVTrigger(), subSpec}, o2::framework::Lifetime::Timeframe); + } + } + if (triggerPerFlp) { // Single per-FLP trigger output, subspec keyed on the first CRU + const header::DataHeader::SubSpecificationType trigSubSpec{crus.front() << 7}; + outputSpecs.emplace_back(o2::framework::ConcreteDataMatcher{o2::header::gDataOriginTPC, TPCFLPCMVDevice::getDataDescriptionCMVTrigger(), trigSubSpec}, o2::framework::Lifetime::Timeframe); } const auto id = fmt::format("tpc-flp-cmv-{:02}", ilane); - return DataProcessorSpec{ + return o2::framework::DataProcessorSpec{ id.data(), inputSpecs, outputSpecs, - AlgorithmSpec{adaptFromTask(ilane, crus, nTFsBuffer)}, - Options{{"dump-cmvs-flp", VariantType::Bool, false, {"Dump CMVs to file"}}}}; + o2::framework::AlgorithmSpec{o2::framework::adaptFromTask(ilane, crus, triggerPerFlp, nTFsBuffer)}, + o2::framework::Options{ + {"dump-cmvs-flp", o2::framework::VariantType::Bool, false, {"Dump CMVs to file"}}, + {"trigger", o2::framework::VariantType::Bool, false, {"Enable CMV trigger evaluation"}}, + {"trigger-threshold-cmv", o2::framework::VariantType::Float, -10.f, {"CMV threshold: sequence starts when value drops below this (ADC units)"}}, + {"trigger-threshold-cmvMeanMax", o2::framework::VariantType::Float, -40.f, {"Upper bound on trigger-sequence mean CMV value"}}, + {"trigger-threshold-cmvMeanMin", o2::framework::VariantType::Float, -80.f, {"Lower bound on trigger-sequence mean CMV value"}}, + {"trigger-threshold-timebinMin", o2::framework::VariantType::Int, 4, {"Minimum trigger-sequence length in timebins"}}, + {"trigger-threshold-timebinMax", o2::framework::VariantType::Int, -1, {"Maximum trigger-sequence length in timebins (-1 disables upper bound)"}}}}; } } // namespace o2::tpc -#endif \ No newline at end of file +#endif diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TPCFourierTransformAggregatorSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCFourierTransformAggregatorSpec.h index 35f51dd489115..7facee78fb3d6 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/TPCFourierTransformAggregatorSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TPCFourierTransformAggregatorSpec.h @@ -64,6 +64,8 @@ class TPCFourierTransformAggregatorSpec : public o2::framework::Task mLengthIDCScalerSeconds = ic.options().get("tpcScalerLengthS"); mDisableScaler = ic.options().get("disable-scaler"); mEnableFFTCCDB = ic.options().get("enable-fft-CCDB"); + int nthreads = ic.options().get("nthreads"); + TPCFourierTransformAggregatorSpec::IDCFType::setNThreads(nthreads); resizeBuffer(mInputLanes); } @@ -448,7 +450,8 @@ DataProcessorSpec getTPCFourierTransformAggregatorSpec(const unsigned int rangeI {"dump-coefficients-agg", VariantType::Bool, false, {"Dump fourier coefficients to file"}}, {"tpcScalerLengthS", VariantType::Float, 300.f, {"Length of the TPC scalers in seconds"}}, {"disable-scaler", VariantType::Bool, false, {"Disable creation of IDC scaler"}}, - {"enable-fft-CCDB", VariantType::Bool, false, {"Enable writing of FFT coefficients to CCDB"}}}}; + {"enable-fft-CCDB", VariantType::Bool, false, {"Enable writing of FFT coefficients to CCDB"}}, + {"nthreads", VariantType::Int, 1, {"Number of threads which will be used during the calculation of the fourier coefficients."}}}}; } } // namespace o2::tpc diff --git a/Detectors/TPC/workflow/src/CMVToVectorSpec.cxx b/Detectors/TPC/workflow/src/CMVToVectorSpec.cxx index 81ce358d1a809..86cf4ca97aa19 100644 --- a/Detectors/TPC/workflow/src/CMVToVectorSpec.cxx +++ b/Detectors/TPC/workflow/src/CMVToVectorSpec.cxx @@ -76,7 +76,6 @@ class CMVToVectorDevice : public o2::framework::Task { const auto runNumber = processing_helpers::getRunNumber(pc); std::vector filter = {{"check", ConcreteDataTypeMatcher{o2::header::gDataOriginTPC, "RAWDATA"}, Lifetime::Timeframe}}; - const auto& mapper = Mapper::instance(); // open files if necessary if ((mWriteDebug || mWriteDebugOnError) && !mDebugStream) { @@ -95,10 +94,7 @@ class CMVToVectorDevice : public o2::framework::Task mRawOutputFile.open(rawFileName, std::ios::binary); } - uint32_t heartbeatOrbit = 0; - uint16_t heartbeatBC = 0; uint32_t tfCounter = 0; - bool first = true; bool hasErrors = false; for (auto const& ref : InputRecordWalker(pc.inputs(), filter)) { @@ -149,7 +145,7 @@ class CMVToVectorDevice : public o2::framework::Task LOGP(debug, "Processing firstTForbit {:9}, tfCounter {:5}, run {:6}, feeId {:6}, cruID {:3}, link {:2}", dh->firstTForbit, dh->tfCounter, dh->runNumber, feeId, cruID, link); if (std::find(mCRUs.begin(), mCRUs.end(), cruID) == mCRUs.end()) { - LOGP(warning, "CMV CRU {:3} not configured in CRUs, skipping", cruID); + // LOGP(debug, "CMV CRU {:3} not configured in CRUs, skipping", cruID); continue; } @@ -171,7 +167,7 @@ class CMVToVectorDevice : public o2::framework::Task cmvVec.reserve(cmvVec.size() + cmv::NTimeBinsPerPacket); for (uint32_t tb = 0; tb < cmv::NTimeBinsPerPacket; ++tb) { cmvVec.push_back(cmvs.getCMV(tb)); - // LOGP(debug, "Appended CMV {} for timebin {}, CRU {}, orbit {}, bc {}", cmvs.getCMV(tb), tb, cruID, orbit, bc); + // LOGP(debug, "For CRU {}, timebin {}, orbit {}, bc {}, appended CMV {} float: {}", cruID, tb, orbit, bc, cmvs.getCMV(tb), cmvs.getCMVFloat(tb)); } } } catch (const std::exception& e) { @@ -204,7 +200,7 @@ class CMVToVectorDevice : public o2::framework::Task } } - hasErrors |= snapshotCMVs(pc.outputs(), tfCounter); + hasErrors |= snapshotCMVs(pc.outputs()); if (mWriteDebug || (mWriteDebugOnError && hasErrors)) { writeDebugOutput(tfCounter); @@ -274,7 +270,7 @@ class CMVToVectorDevice : public o2::framework::Task std::string mRawOutputFileName; ///< name of the raw output file //____________________________________________________________________________ - bool snapshotCMVs(DataAllocator& output, uint32_t tfCounter) + bool snapshotCMVs(DataAllocator& output) { bool hasErrors = false; @@ -321,12 +317,8 @@ class CMVToVectorDevice : public o2::framework::Task //____________________________________________________________________________ void writeDebugOutput(uint32_t tfCounter) { - const auto& mapper = Mapper::instance(); - mDebugStream->GetFile()->cd(); auto& stream = (*mDebugStream) << "cmvs"; - uint32_t seen = 0; - static uint32_t firstOrbit = std::numeric_limits::max(); for (auto cru : mCRUs) { if (mCMVInfos.find(cru) == mCMVInfos.end()) { @@ -404,7 +396,7 @@ class CMVToVectorDevice : public o2::framework::Task } }; -o2::framework::DataProcessorSpec getCMVToVectorSpec(const std::string inputSpec, std::vector const& crus) +o2::framework::DataProcessorSpec getCMVToVectorSpec(std::string const& inputSpec, std::vector const& crus) { using device = o2::tpc::CMVToVectorDevice; diff --git a/Detectors/TPC/workflow/src/TPCScalerSpec.cxx b/Detectors/TPC/workflow/src/TPCScalerSpec.cxx index 8e2a78d69757b..1df192dd5ec00 100644 --- a/Detectors/TPC/workflow/src/TPCScalerSpec.cxx +++ b/Detectors/TPC/workflow/src/TPCScalerSpec.cxx @@ -183,40 +183,45 @@ class TPCScalerSpec : public Task void buildMap(ProcessingContext& pc) { - // reference map - auto* corrMap = mTPCCorrMapsLoader.getCorrMap(); - - // // new correction map + const auto lumiMode = mTPCCorrMapsLoader.getLumiScaleMode(); o2::gpu::TPCFastTransform finalMap; - finalMap.cloneFromObject(*corrMap, nullptr); - finalMap.setApplyCorrectionOn(); - - const auto* corrMapRef = mTPCCorrMapsLoader.getCorrMapRef(); - const float lumiScale = mTPCCorrMapsLoader.getLumiScale(); std::vector> additionalCorrections; - // if standard scaling is used: map(lumi) = (mean_map - ref_map) * lumiScale + ref_map - if (mTPCCorrMapsLoader.getLumiScaleMode() == LumiScaleMode::Linear) { - const std::vector> step0{{&(corrMapRef->getCorrection()), -1.f}}; - // finalMap = (mean_map - finalMap) - TPCFastSpaceChargeCorrectionHelper::instance()->mergeCorrections(finalMap.getCorrection(), 1, step0, true); - - // finalMap = finalMap * lumiScale + ref_map - const std::vector> step1{{&(corrMapRef->getCorrection()), 1.f}}; - TPCFastSpaceChargeCorrectionHelper::instance()->mergeCorrections(finalMap.getCorrection(), lumiScale, step1, true); - - } else if (mTPCCorrMapsLoader.getLumiScaleMode() == LumiScaleMode::DerivativeMap || mTPCCorrMapsLoader.getLumiScaleMode() == LumiScaleMode::DerivativeMapMC) { - additionalCorrections.emplace_back(&(corrMapRef->getCorrection()), lumiScale); - } + if (lumiMode == LumiScaleMode::NoCorrection) { + std::unique_ptr dummy(TPCFastTransformHelperO2::instance()->create(0)); + finalMap.cloneFromObject(*dummy, nullptr); + finalMap.setApplyCorrectionOff(); + } else { + auto* corrMap = mTPCCorrMapsLoader.getCorrMap(); + const auto* corrMapRef = mTPCCorrMapsLoader.getCorrMapRef(); + finalMap.cloneFromObject(lumiMode == LumiScaleMode::StaticMapOnly && corrMapRef ? *corrMapRef : *corrMap, nullptr); + finalMap.setApplyCorrectionOn(); + + const float lumiScale = mTPCCorrMapsLoader.getLumiScale(); + + // if standard scaling is used: map(lumi) = (mean_map - ref_map) * lumiScale + ref_map + if (lumiMode == LumiScaleMode::Linear) { + const std::vector> step0{{&(corrMapRef->getCorrection()), -1.f}}; + // finalMap = (mean_map - finalMap) + TPCFastSpaceChargeCorrectionHelper::instance()->mergeCorrections(finalMap.getCorrection(), 1, step0, true); + + // finalMap = finalMap * lumiScale + ref_map + const std::vector> step1{{&(corrMapRef->getCorrection()), 1.f}}; + TPCFastSpaceChargeCorrectionHelper::instance()->mergeCorrections(finalMap.getCorrection(), lumiScale, step1, true); + + } else if (lumiMode == LumiScaleMode::DerivativeMap || lumiMode == LumiScaleMode::DerivativeMapMC) { + additionalCorrections.emplace_back(&(corrMapRef->getCorrection()), lumiScale); + } - // if mshape map valid - if (!mTPCCorrMapsLoader.isCorrMapMShapeDummy()) { - LOGP(info, "Adding M-shape correction to the final map with scaling factor {}", mMShapeScalingFac); - additionalCorrections.emplace_back(&(mTPCCorrMapsLoader.getCorrMapMShape()->getCorrection()), 1.f); - } + // if mshape map valid + if (!mTPCCorrMapsLoader.isCorrMapMShapeDummy()) { + LOGP(info, "Adding M-shape correction to the final map with scaling factor {}", mMShapeScalingFac); + additionalCorrections.emplace_back(&(mTPCCorrMapsLoader.getCorrMapMShape()->getCorrection()), 1.f); + } - if (!additionalCorrections.empty()) { - TPCFastSpaceChargeCorrectionHelper::instance()->mergeCorrections(finalMap.getCorrection(), 1, additionalCorrections, true); + if (!additionalCorrections.empty()) { + TPCFastSpaceChargeCorrectionHelper::instance()->mergeCorrections(finalMap.getCorrection(), 1, additionalCorrections, true); + } } Output corrMapOutput{header::gDataOriginTPC, "TPCCORRMAP", 0}; diff --git a/Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx b/Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx index ac3ff15fd3a29..0c0ae72056318 100644 --- a/Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx +++ b/Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx @@ -1825,15 +1825,16 @@ o2::framework::DataProcessorSpec getTPCTimeSeriesSpec(const bool disableWriter, auto dataRequest = std::make_shared(); bool useMC = false; 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(GTrackID::getSourcesMask("TPC"), useMC); + if (src[GTrackID::TPC]) { + dataRequest->requestClusters(GTrackID::getSourcesMask("TPC"), useMC); + } bool tpcOnly = srcTracks == GTrackID::getSourcesMask("TPC"); - if (!tpcOnly) { + if (srcTracks.any() && !tpcOnly) { dataRequest->requestFT0RecPoints(useMC); + dataRequest->requestPrimaryVertices(useMC); } - dataRequest->requestPrimaryVertices(useMC); const bool enableAskMatLUT = matType == o2::base::Propagator::MatCorrType::USEMatCorrLUT; auto ccdbRequest = std::make_shared(!disableWriter, // orbitResetTime diff --git a/Detectors/TPC/workflow/src/tpc-flp-cmv.cxx b/Detectors/TPC/workflow/src/tpc-flp-cmv.cxx index f41fe5b8fbd15..b7734c5d0b24f 100644 --- a/Detectors/TPC/workflow/src/tpc-flp-cmv.cxx +++ b/Detectors/TPC/workflow/src/tpc-flp-cmv.cxx @@ -32,6 +32,7 @@ void customize(std::vector& workflowOptions) {"time-lanes", VariantType::Int, 1, {"Number of parallel processing lanes (timeframes are split per device)"}}, {"crus", VariantType::String, cruDefault.c_str(), {"List of CRUs, comma separated ranges, e.g. 0-3,7,9-15"}}, {"n-TFs-buffer", VariantType::Int, 1, {"Buffer n-TFs before sending output"}}, + {"trigger-per-flp", VariantType::Bool, false, {"Aggregate triggers of CRUs on FLP to a single trigger"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; std::swap(workflowOptions, options); @@ -48,6 +49,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) const auto nLanes = std::min(static_cast(config.options().get("lanes")), nCRUs); const auto time_lanes = static_cast(config.options().get("time-lanes")); const auto crusPerLane = nCRUs / nLanes + ((nCRUs % nLanes) != 0); + const bool triggerPerFLP = config.options().get("trigger-per-flp"); const int nTFsBuffer = config.options().get("n-TFs-buffer"); o2::conf::ConfigurableParam::updateFromFile(config.options().get("configFile")); @@ -65,8 +67,8 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) } const auto last = std::min(tpcCRUs.end(), first + crusPerLane); const std::vector rangeCRUs(first, last); - workflow.emplace_back(timePipeline(getTPCFLPCMVSpec(ilane, rangeCRUs, nTFsBuffer), time_lanes)); + workflow.emplace_back(timePipeline(getTPCFLPCMVSpec(ilane, rangeCRUs, triggerPerFLP, nTFsBuffer), time_lanes)); } return workflow; -} \ No newline at end of file +} diff --git a/Detectors/TPC/workflow/src/tpc-fouriertransform-aggregator.cxx b/Detectors/TPC/workflow/src/tpc-fouriertransform-aggregator.cxx index b0f09e02e627b..2f66a144251f1 100644 --- a/Detectors/TPC/workflow/src/tpc-fouriertransform-aggregator.cxx +++ b/Detectors/TPC/workflow/src/tpc-fouriertransform-aggregator.cxx @@ -26,7 +26,6 @@ void customize(std::vector& workflowOptions) std::vector options{ {"rangeIDC", VariantType::Int, 200, {"Number of 1D-IDCs which will be used for the calculation of the fourier coefficients. TODO ALREADY SET IN ABERAGEGROUP"}}, {"nFourierCoeff", VariantType::Int, 60, {"Number of fourier coefficients (real+imag) which will be stored in the CCDB. The maximum can be 'rangeIDC + 2'."}}, - {"nthreads", VariantType::Int, 1, {"Number of threads which will be used during the calculation of the fourier coefficients."}}, {"inputLanes", VariantType::Int, 2, {"Number of expected input lanes."}}, {"sendOutput", VariantType::Bool, false, {"send fourier coefficients"}}, {"use-naive-fft", VariantType::Bool, false, {"using naive fourier transform (true) or FFTW (false)"}}, @@ -51,8 +50,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) const bool processSACs = config.options().get("process-SACs"); const auto rangeIDC = static_cast(config.options().get("rangeIDC")); const auto nFourierCoeff = std::clamp(static_cast(config.options().get("nFourierCoeff")), static_cast(0), rangeIDC + 2); - const auto nthreadsFourier = static_cast(config.options().get("nthreads")); - TPCFourierTransformAggregatorSpec::IDCFType::setNThreads(nthreadsFourier); TPCFourierTransformAggregatorSpec::IDCFType::setFFT(!fft); const auto inputLanes = config.options().get("inputLanes"); WorkflowSpec workflow{getTPCFourierTransformAggregatorSpec(rangeIDC, nFourierCoeff, sendOutput, processSACs, inputLanes)}; diff --git a/Detectors/TPC/workflow/test/test_cmv-trigger.cxx b/Detectors/TPC/workflow/test/test_cmv-trigger.cxx new file mode 100644 index 0000000000000..c102a5ae531f4 --- /dev/null +++ b/Detectors/TPC/workflow/test/test_cmv-trigger.cxx @@ -0,0 +1,85 @@ +// 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 test_cmv-trigger.cxx +/// @author Tuba Gündem, tuba.gundem@cern.ch +/// @brief Test workflow: reads CMVTRIGGER packets from tpc-flp-cmv and logs results + +#include +#include +#include "Framework/WorkflowSpec.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/Task.h" +#include "Framework/ControlService.h" +#include "Framework/Logger.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/InputRecordWalker.h" +#include "Framework/DataRefUtils.h" +#include "Headers/DataHeader.h" +#include "TPCWorkflow/ProcessingHelpers.h" +#include "TPCWorkflow/TPCFLPCMVSpec.h" + +using namespace o2::framework; + +void customize(std::vector&) {} + +#include "Framework/runDataProcessing.h" + +namespace o2::tpc +{ + +class CMVTriggerDevice : public o2::framework::Task +{ + public: + void run(o2::framework::ProcessingContext& pc) final + { + const auto tf = processing_helpers::getCurrentTF(pc); + + for (auto& ref : o2::framework::InputRecordWalker(pc.inputs(), mFilter)) { + auto const* hdr = o2::framework::DataRefUtils::getHeader(ref); + const uint32_t firstCRU = hdr->subSpecification >> 7; + const bool triggered = pc.inputs().get(ref); + if (triggered) { + LOGP(info, "TF {:6} first CRU {:3}: {}", tf, firstCRU, "triggered"); + } + } + } + + void endOfStream(o2::framework::EndOfStreamContext& ec) final + { + ec.services().get().readyToQuit(o2::framework::QuitRequest::Me); + } + + private: + const std::vector mFilter = { + {"cmvtrigger", o2::framework::ConcreteDataTypeMatcher{o2::header::gDataOriginTPC, o2::tpc::TPCFLPCMVDevice::getDataDescriptionCMVTrigger()}, o2::framework::Lifetime::Timeframe}}; +}; + +o2::framework::DataProcessorSpec getCMVTriggerSpec() +{ + std::vector inputSpecs; + inputSpecs.emplace_back(o2::framework::InputSpec{"cmvtrigger", o2::framework::ConcreteDataTypeMatcher{o2::header::gDataOriginTPC, o2::tpc::TPCFLPCMVDevice::getDataDescriptionCMVTrigger()}, o2::framework::Lifetime::Timeframe}); + + return o2::framework::DataProcessorSpec{ + "tpc-cmv-trigger", + inputSpecs, + {}, + o2::framework::AlgorithmSpec{o2::framework::adaptFromTask()}}; +} + +} // namespace o2::tpc + +WorkflowSpec defineDataProcessing(ConfigContext const& config) +{ + WorkflowSpec workflow; + workflow.emplace_back(o2::tpc::getCMVTriggerSpec()); + return workflow; +} diff --git a/Detectors/TRD/calibration/src/DCSProcessor.cxx b/Detectors/TRD/calibration/src/DCSProcessor.cxx index f110ba844791e..6f719b71e10c3 100644 --- a/Detectors/TRD/calibration/src/DCSProcessor.cxx +++ b/Detectors/TRD/calibration/src/DCSProcessor.cxx @@ -382,7 +382,7 @@ bool DCSProcessor::updateGasDPsCCDB() } std::map md; md["responsible"] = "Ole Schmidt"; - o2::calibration::Utils::prepareCCDBobjectInfo(mTRDDCSGas, mCcdbGasDPsInfo, "TRD/Calib/DCSDPsGas", md, mGasStartTS, mGasStartTS + 3 * o2::ccdb::CcdbObjectInfo::DAY); + o2::calibration::Utils::prepareCCDBobjectInfo(mTRDDCSGas, mCcdbGasDPsInfo, "TRD/Calib/DCSDPsGas", md, mGasStartTS, mCurrentTS + 14 * o2::ccdb::CcdbObjectInfo::DAY); return retVal; } @@ -410,7 +410,7 @@ bool DCSProcessor::updateCurrentsDPsCCDB() } std::map md; md["responsible"] = "Ole Schmidt"; - o2::calibration::Utils::prepareCCDBobjectInfo(mTRDDCSCurrents, mCcdbCurrentsDPsInfo, "TRD/Calib/DCSDPsI", md, mCurrentsStartTS, mCurrentsStartTS + 3 * o2::ccdb::CcdbObjectInfo::DAY); + o2::calibration::Utils::prepareCCDBobjectInfo(mTRDDCSCurrents, mCcdbCurrentsDPsInfo, "TRD/Calib/DCSDPsI", md, mCurrentsStartTS, mCurrentTS + 14 * o2::ccdb::CcdbObjectInfo::DAY); return retVal; } @@ -437,7 +437,7 @@ bool DCSProcessor::updateVoltagesDPsCCDB() } std::map md; md["responsible"] = "Ole Schmidt"; - o2::calibration::Utils::prepareCCDBobjectInfo(mTRDDCSVoltages, mCcdbVoltagesDPsInfo, "TRD/Calib/DCSDPsU", md, mVoltagesStartTS, mVoltagesStartTS + 7 * o2::ccdb::CcdbObjectInfo::DAY); + o2::calibration::Utils::prepareCCDBobjectInfo(mTRDDCSVoltages, mCcdbVoltagesDPsInfo, "TRD/Calib/DCSDPsU", md, mVoltagesStartTS, mCurrentTS + 14 * o2::ccdb::CcdbObjectInfo::DAY); return retVal; } @@ -465,7 +465,7 @@ bool DCSProcessor::updateEnvDPsCCDB() } std::map md; md["responsible"] = "Leonardo Barreto"; - o2::calibration::Utils::prepareCCDBobjectInfo(mTRDDCSEnv, mCcdbEnvDPsInfo, "TRD/Calib/DCSDPsEnv", md, mEnvStartTS, mEnvStartTS + 3 * o2::ccdb::CcdbObjectInfo::DAY); + o2::calibration::Utils::prepareCCDBobjectInfo(mTRDDCSEnv, mCcdbEnvDPsInfo, "TRD/Calib/DCSDPsEnv", md, mEnvStartTS, mCurrentTS + 14 * o2::ccdb::CcdbObjectInfo::DAY); return retVal; } @@ -498,7 +498,7 @@ bool DCSProcessor::updateFedChamberStatusDPsCCDB() // LB: set start timestamp 30000 miliseconds before DPs are received o2::calibration::Utils::prepareCCDBobjectInfo(mTRDDCSFedChamberStatus, mCcdbFedChamberStatusDPsInfo, "TRD/Calib/DCSDPsFedChamberStatus", md, mFedChamberStatusStartTS - 30000, - mFedChamberStatusStartTS + 3 * o2::ccdb::CcdbObjectInfo::DAY); + mCurrentTS + 14 * o2::ccdb::CcdbObjectInfo::DAY); return retVal; } @@ -531,7 +531,7 @@ bool DCSProcessor::updateFedCFGtagDPsCCDB() // LB: set start timestamp 30000 seconds before DPs are received o2::calibration::Utils::prepareCCDBobjectInfo(mTRDDCSFedCFGtag, mCcdbFedCFGtagDPsInfo, "TRD/Calib/DCSDPsFedCFGtag", md, mFedCFGtagStartTS - 30000, - mFedCFGtagStartTS + 3 * o2::ccdb::CcdbObjectInfo::DAY); + mCurrentTS + 14 * o2::ccdb::CcdbObjectInfo::DAY); return retVal; } diff --git a/Detectors/Upgrades/ALICE3/CMakeLists.txt b/Detectors/Upgrades/ALICE3/CMakeLists.txt index 0335e85007c01..334bb13064783 100644 --- a/Detectors/Upgrades/ALICE3/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/CMakeLists.txt @@ -11,6 +11,7 @@ add_subdirectory(Passive) add_subdirectory(TRK) +add_subdirectory(GlobalReconstruction) add_subdirectory(ECal) add_subdirectory(FD3) add_subdirectory(FT3) diff --git a/Detectors/Upgrades/ALICE3/FT3/README.md b/Detectors/Upgrades/ALICE3/FT3/README.md index 34a6782a2b0c2..c11352607db85 100644 --- a/Detectors/Upgrades/ALICE3/FT3/README.md +++ b/Detectors/Upgrades/ALICE3/FT3/README.md @@ -12,18 +12,22 @@ This is top page for the FT3 detector documentation. Configuration of the endcap disks can be done by setting values for the `FT3Base.layoutFT3` configurable, the available options are presented in the following Table: -| Option | Comments | -| ---------------------- | ----------------------------------------------------------------------------------------------------------------- | -| `kSegmented` (default) | Currently, only OT disks have realistic implementation, for ML - simple trapezoids | -| `kTrapezoidal` | Simple trapezoisal disks (in both ML and OT), with `FT3Base.nTrapezoidalSegments=32` | -| `kCylindrical` | Simplest possible disks as TGeoTubes (ML and OT), bad for ACTS (wrong digi due to polar coorinates on disk sides) | +| Option | Comments | +| --------------------------------- | ----------------------------------------------------------------------------------------------------------------- | +| `kSegmentedStave` | Segmentation of ML and OT disks: Modules are placed on staggered staves with user defined constants | +| `kSegmentedStaveOTOnly` (default) | Only OT disks are contain staves with modules, ML layers are segmented with strips of modules on front/back | +| `kSegmented` | Segmentation of ML and OT disk with strips of modules of chips on the front and back of a layer | +| `kTrapezoidal` | Simple trapezoidal disks (in both ML and OT), with `FT3Base.nTrapezoidalSegments=32` | +| `kCylindrical` | Simplest possible disks as TGeoTubes (ML and OT), bad for ACTS (wrong digi due to polar coorinates on disk sides) | + +Furthermore, there are more options in the case of stave segmentation -- for only OT or both. The user can set to cut the staves exactly on the nominal inner radii (true by default), and outer radii (false by default) of the disks. This exists since (planned) placements of sensors & staves often protrude out of the nominal radii to be more able to cover the nominal disk area. In addition, it is possible to draw reference circles in root for the stave segmented layouts for both the inner (red) and outer (blue) radii. This is off by default, yet can be toggled if the user wants to see how tight the tiling is to the nominal radii -- for visualisation purposes only. [ [Link to definitions](./base/include/FT3Base/FT3BaseParam.h) ] -For example, a geometry with the endcaps-only can be obtained by +For example, see the command below to generate a geometry with the endcaps only, all layers with the stave geometry, and including reference circles of nominal radii for visualisation. ```bash o2-sim-serial-run5 -n 1 -g pythia8hi -m FT3 \ - --configKeyValues "FT3Base.layoutFT3=kTrapezoidal" + --configKeyValues "FT3Base.layoutFT3=kSegmented; FT3Base.drawReferenceCircles=true" ``` diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h index 0ed7ca6a8a8d4..b484e13f3546e 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h @@ -16,7 +16,9 @@ #define O2_ALICE_TRK_SPECS #include + #include + // This is a temporary version with the specs for the ALICE3 TRK // This files defines the design specifications of the chips for VD, ML, OT. // Each TGeoShape has the following properties @@ -78,7 +80,7 @@ constexpr double thickness{0 * mu}; // thickness of the copper metal stack - for namespace chip { constexpr double width{25 * mm}; // width of the chip -constexpr double length{32 * mm}; // length of the chip +constexpr double length{29 * mm}; // length of the chip constexpr double pitchX{20 * mu}; // pitch of the row constexpr double pitchZ{20 * mu}; // pitch of the column constexpr double totalThickness{silicon::thickness + metalstack::thickness}; // total thickness of the chip diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h index 63e961db44505..65194ad6edfcb 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h @@ -40,7 +40,8 @@ enum eSrvLayout { struct TRKBaseParam : public o2::conf::ConfigurableParamHelper { std::string configFile = ""; float serviceTubeX0 = 0.02f; // X0 Al2O3 - Bool_t irisOpen = false; + bool irisOpen = false; + bool includeLowServices = false; eVDLayout layoutVD = kIRIS4; // VD detector layout design eMLOTLayout layoutMLOT = kSegmented; // ML and OT detector layout design diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckClusters.C b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckClusters.C index 28dc61aed9c8b..7b9365dbe2011 100644 --- a/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckClusters.C +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckClusters.C @@ -22,7 +22,12 @@ #include #include #include +#include +#include +#include +#include #include +#include #include "DataFormatsTRK/Cluster.h" #include "DataFormatsTRK/ROFRecord.h" @@ -48,11 +53,8 @@ void CheckClusters(const std::string& clusfile = "o2clus_trk.root", { gROOT->SetBatch(batch); - using o2::MCCompLabel; - using ROFRec = o2::trk::ROFRecord; - using MC2ROF = o2::trk::MC2ROFRecord; using HitVec = std::vector; - using MC2HITS_map = std::unordered_map; // maps (trackID << 32) + chipID -> hit index + using MC2HITS_map = std::unordered_map>; // maps (trackID << 32) + chipID -> hit indices // ── Chip response (for hit-segment propagation to charge-collection plane) ── // Fetches the same AlpideSimResponse from CCDB as the digitizer (IT3/Calib/APTSResponse) @@ -130,6 +132,10 @@ void CheckClusters(const std::string& clusfile = "o2clus_trk.root", LOGP(error, "Cannot find o2sim tree in {}", hitfile); return; } + if (hitTree->GetBranch("TRKHit") == nullptr) { + LOGP(error, "Cannot find TRKHit branch in {}", hitfile); + return; + } std::vector mc2hitVec; std::vector hitVecPool; mc2hitVec.resize(hitTree->GetEntries()); @@ -149,8 +155,10 @@ void CheckClusters(const std::string& clusfile = "o2clus_trk.root", std::vector*> rofRecVecPerLayer(nLayers, nullptr); std::vector*> patternsPerLayer(nLayers, nullptr); std::vector*> clusLabArrPerLayer(nLayers, nullptr); + std::vector> patternOffsetsPerLayer(nLayers); + std::vector layerActive(nLayers, false); - bool hasMC = true; + bool hasAnyMC = false; for (int iLayer = 0; iLayer < nLayers; iLayer++) { std::string brClus = std::string("TRKClusterComp_") + std::to_string(iLayer); std::string brROF = std::string("TRKClustersROF_") + std::to_string(iLayer); @@ -161,45 +169,112 @@ void CheckClusters(const std::string& clusfile = "o2clus_trk.root", LOGP(warning, "Branch {} not found, skipping layer {}", brClus, iLayer); continue; } + if (clusTree->GetBranch(brROF.c_str()) == nullptr) { + LOGP(error, "Branch {} not found, skipping layer {}", brROF, iLayer); + continue; + } clusTree->SetBranchAddress(brClus.c_str(), &clusArrPerLayer[iLayer]); clusTree->SetBranchAddress(brROF.c_str(), &rofRecVecPerLayer[iLayer]); if (clusTree->GetBranch(brPatt.c_str()) != nullptr) { clusTree->SetBranchAddress(brPatt.c_str(), &patternsPerLayer[iLayer]); + } else { + LOGP(warning, "Branch {} not found, layer {} cluster positions use bbox origins", brPatt, iLayer); } if (clusTree->GetBranch(brMCTruth.c_str()) != nullptr) { clusTree->SetBranchAddress(brMCTruth.c_str(), &clusLabArrPerLayer[iLayer]); - } else { - hasMC = false; + hasAnyMC = true; } + layerActive[iLayer] = true; } // Read entry and accumulate all layers - clusTree->GetEntry(0); + if (clusTree->GetEntry(0) <= 0) { + LOGP(error, "Cannot read entry 0 from {}", clusfile); + return; + } + + auto hasAnyActiveLayer = false; + for (int iLayer = 0; iLayer < nLayers; iLayer++) { + hasAnyActiveLayer = hasAnyActiveLayer || layerActive[iLayer]; + } + if (!hasAnyActiveLayer) { + LOGP(error, "No usable TRK cluster layers found in {}", clusfile); + return; + } + // Print total clusters per layer for (int iLayer = 0; iLayer < nLayers; iLayer++) { + if (!layerActive[iLayer]) { + continue; + } + if (clusArrPerLayer[iLayer] == nullptr || rofRecVecPerLayer[iLayer] == nullptr) { + LOGP(error, "Layer {} branches were declared but did not load usable data, skipping layer", iLayer); + layerActive[iLayer] = false; + continue; + } LOGP(info, "Layer {}: {} clusters", iLayer, clusArrPerLayer[iLayer]->size()); } + // The pattern stream is variable-length, so index it by cluster entry once. + for (int iLayer = 0; iLayer < nLayers; iLayer++) { + if (!layerActive[iLayer] || patternsPerLayer[iLayer] == nullptr) { + continue; + } + const auto nClusters = clusArrPerLayer[iLayer]->size(); + const auto& patterns = *patternsPerLayer[iLayer]; + auto& offsets = patternOffsetsPerLayer[iLayer]; + offsets.resize(nClusters, std::numeric_limits::max()); + size_t pattPos = 0; + bool validPatterns = true; + for (size_t icl = 0; icl < nClusters; icl++) { + if (pattPos + 2 > patterns.size()) { + validPatterns = false; + break; + } + offsets[icl] = pattPos; + const uint8_t rowSpan = patterns[pattPos]; + const uint8_t colSpan = patterns[pattPos + 1]; + const size_t nBytes = (size_t(rowSpan) * colSpan + 7) / 8; + if (pattPos + 2 + nBytes > patterns.size()) { + validPatterns = false; + break; + } + pattPos += 2 + nBytes; + } + if (!validPatterns || pattPos != patterns.size()) { + LOGP(error, "Malformed pattern stream for layer {}: {} pattern bytes for {} clusters, disabling CoG corrections for this layer", + iLayer, patterns.size(), nClusters); + patternsPerLayer[iLayer] = nullptr; + offsets.clear(); + } + } + // Accumulate max ROF count across all layers unsigned int nROFRec = 0; for (int iLayer = 0; iLayer < nLayers; iLayer++) { + if (!layerActive[iLayer]) { + continue; + } nROFRec = std::max(nROFRec, (unsigned int)rofRecVecPerLayer[iLayer]->size()); } LOGP(info, "Number of ROF records: {}", nROFRec); // ── Load all MC hit events upfront (TRK has no MC2ROF mapping) ────────────── - if (hasMC) { + if (hasAnyMC) { LOGP(info, "Pre-loading {} MC events", hitTree->GetEntries()); for (int im = 0; im < (int)hitTree->GetEntries(); im++) { if (hitVecPool[im] == nullptr) { hitTree->SetBranchAddress("TRKHit", &hitVecPool[im]); - hitTree->GetEntry(im); + if (hitTree->GetEntry(im) <= 0 || hitVecPool[im] == nullptr) { + LOGP(error, "Cannot read TRKHit entry {} from {}", im, hitfile); + return; + } auto& mc2hit = mc2hitVec[im]; const auto* hv = hitVecPool[im]; for (int ih = (int)hv->size(); ih--;) { const auto& hit = (*hv)[ih]; uint64_t key = (uint64_t(hit.GetTrackID()) << 32) + hit.GetDetectorID(); - mc2hit.emplace(key, ih); + mc2hit[key].push_back(ih); } } } @@ -210,40 +285,45 @@ void CheckClusters(const std::string& clusfile = "o2clus_trk.root", // columns: event, MC track label, // local hit x/z (flat frame), global hit x/y/z (midpoint), // global cluster x/y/z, local cluster x/z, - // residuals dx/dz (local, cluster - hit), // ROF frame, cluster size, chipID, layer, disk, subDetID, row, col, pt TNtuple nt("ntc", "TRK cluster ntuple", "event:mcTrackID:hitLocX:hitLocZ:hitGlobX:hitGlobY:hitGlobZ:clusGlobX:clusGlobY:clusGlobZ:clusLocX:clusLocZ:rofFrame:clusSize:chipID:layer:disk:subdet:row:col:pt"); // ── Counters ─────────────────────────────────────────────────────────────── - long nTot{0}, nInvalidLabel{0}, nNoMCHit{0}, nValid{0}; + long nTot{0}, nInvalidLabel{0}, nInvalidEvent{0}, nNoMCHit{0}, nValid{0}; // ── Main loop ────────────────────────────────────────────────────────────── for (unsigned int irof = 0; irof < nROFRec; irof++) { // Process each layer for (int iLayer = 0; iLayer < nLayers; iLayer++) { + if (!layerActive[iLayer]) { + continue; + } if (rofRecVecPerLayer[iLayer]->empty() || irof >= rofRecVecPerLayer[iLayer]->size()) { continue; } const auto& rofRec = (*rofRecVecPerLayer[iLayer])[irof]; const auto& clusArr = *clusArrPerLayer[iLayer]; - const auto& patternsPtr = (patternsPerLayer[iLayer] == nullptr) ? nullptr : patternsPerLayer[iLayer]; const auto& clusLabArr = clusLabArrPerLayer[iLayer]; - - // Create per-layer pattern iterator - auto pattIt = patternsPtr ? patternsPtr->cbegin() : std::vector::const_iterator{}; + const auto* patternsPtr = patternsPerLayer[iLayer]; + const auto& patternOffsets = patternOffsetsPerLayer[iLayer]; for (int icl = 0; icl < rofRec.getNEntries(); icl++) { const int clEntry = rofRec.getFirstEntry() + icl; + if (clEntry < 0 || clEntry >= (int)clusArr.size()) { + LOGP(error, "Layer {} ROF {} points to cluster entry {} outside {} clusters", + iLayer, irof, clEntry, clusArr.size()); + continue; + } const auto& cluster = clusArr[clEntry]; nTot++; // ── Parse pattern → center-of-gravity within bounding box ────────── - // The cluster stores the bounding-box top-left pixel (row, col). - // The pattern stream encodes [rowSpan, colSpan, bitmap...] for each cluster. - // We accumulate pixel row/col offsets to obtain a sub-pixel CoG correction. + // Keep this in sync with Clusterer::getClusterLocalCoordinates(). float cogDr{0.f}, cogDc{0.f}; // mean offsets from bbox origin (pixels) - if (patternsPtr) { + if (patternsPtr != nullptr) { + const auto pattOffset = patternOffsets[clEntry]; + const auto* pattIt = patternsPtr->data() + pattOffset; const uint8_t rowSpan = *pattIt++; const uint8_t colSpan = *pattIt++; const int nBytes = (rowSpan * colSpan + 7) / 8; @@ -267,10 +347,6 @@ void CheckClusters(const std::string& clusfile = "o2clus_trk.root", // ── Cluster local → global (CoG position) ───────────────────────────── // Get local coords of the bounding-box corner pixel, then apply the // fractional CoG displacement using the pixel pitch. - // Formula from detectorToLocalUnchecked: - // VD : xRow = 0.5*(width[lay]-pitchRow) - row*pitchRow → row↑ xRow↓ - // zCol = col*pitchCol + 0.5*(pitchCol-length) → col↑ zCol↑ - // MLOT: same structure with MLOT pitches float clLocX{0.f}, clLocZ{0.f}; o2::trk::SegmentationChip::detectorToLocalUnchecked( cluster.row, cluster.col, clLocX, clLocZ, @@ -281,24 +357,19 @@ void CheckClusters(const std::string& clusfile = "o2clus_trk.root", const float pitchCol = (cluster.subDetID == 0) ? o2::trk::SegmentationChip::PitchColVD : o2::trk::SegmentationChip::PitchColMLOT; - clLocX -= cogDr * pitchRow; // increasing row → decreasing xRow - clLocZ += cogDc * pitchCol; // increasing col → increasing zCol - const float yResponse = (cluster.subDetID == 0) ? yPlaneVD : yPlaneMLOT; - // For VD the L2G matrix is built in the *curved* local frame (quasi-Cartesian, - // origin at the beam axis). Convert flat (clLocX, 0) → curved (xC, yC) first. - // For MLOT (flat sensors) the local frame is already Cartesian: pass directly. - // clLocX is already in the flat frame from detectorToLocalUnchecked + CoG and - // does NOT need any further transformation for the residual comparison. + clLocX -= cogDr * pitchRow; // increasing row -> decreasing xRow + clLocZ += cogDc * pitchCol; // increasing col -> increasing zCol + o2::math_utils::Point3D locC; if (cluster.subDetID == 0) { auto cv = o2::trk::SegmentationChip::flatToCurved(cluster.layer, clLocX, 0.f); locC = {cv.X(), cv.Y(), clLocZ}; } else { - locC = {clLocX, yResponse, clLocZ}; + locC = {clLocX, yPlaneMLOT, clLocZ}; } auto gloC = gman->getMatrixL2G(cluster.chipID)(locC); - if (!hasMC || clusLabArr == nullptr) { + if (!hasAnyMC || clusLabArr == nullptr) { // No MC info: just fill geometry columns, leave residuals as 0 std::array data = { -1.f, -1.f, @@ -321,6 +392,10 @@ void CheckClusters(const std::string& clusfile = "o2clus_trk.root", const auto& lab = labels[0]; const int trID = lab.getTrackID(); const int evID = lab.getEventID(); + if (evID < 0 || evID >= (int)mc2hitVec.size()) { + nInvalidEvent++; + continue; + } // ── Find matching MC hit ──────────────────────────────────────────── const auto& mc2hit = mc2hitVec[evID]; @@ -330,7 +405,53 @@ void CheckClusters(const std::string& clusfile = "o2clus_trk.root", nNoMCHit++; continue; } - const auto& hit = (*hitVecPool[evID])[hitEntry->second]; + auto projectHitToResponsePlane = [&](const o2::trk::Hit& hit, float& hitLocX, float& hitLocZ) { + const auto& gloHend = hit.GetPos(); + const auto& gloHsta = hit.GetPosStart(); + o2::math_utils::Point3D locHsta = gman->getMatrixL2G(cluster.chipID) ^ (gloHsta); // inverse L2G + o2::math_utils::Point3D locHend = gman->getMatrixL2G(cluster.chipID) ^ (gloHend); // inverse L2G + + // Rather than the geometric midpoint, find where the track segment crosses + // the response plane. For VD convert the curved endpoints to the flat frame first. + if (cluster.subDetID == 0) { + auto flatSta = o2::trk::SegmentationChip::curvedToFlat(cluster.layer, locHsta.X(), locHsta.Y()); + auto flatEnd = o2::trk::SegmentationChip::curvedToFlat(cluster.layer, locHend.X(), locHend.Y()); + float x0 = flatSta.X(), y0 = flatSta.Y(), z0 = locHsta.Z(); + float dltx = flatEnd.X() - x0, dlty = flatEnd.Y() - y0, dltz = locHend.Z() - z0; + float r = (std::abs(dlty) > 1e-9f) ? (yPlaneVD - y0) / dlty : 0.5f; + hitLocX = x0 + r * dltx; + hitLocZ = z0 + r * dltz; + } else { + float x0 = locHsta.X(), y0 = locHsta.Y(), z0 = locHsta.Z(); + float dltx = locHend.X() - x0, dlty = locHend.Y() - y0, dltz = locHend.Z() - z0; + float r = (std::abs(dlty) > 1e-9f) ? (yPlaneMLOT - y0) / dlty : 0.5f; + hitLocX = x0 + r * dltx; + hitLocZ = z0 + r * dltz; + } + }; + + const o2::trk::Hit* bestHit = nullptr; + float hitLocX{0.f}, hitLocZ{0.f}; + float bestDist2 = std::numeric_limits::max(); + for (const auto ih : hitEntry->second) { + const auto& candHit = (*hitVecPool[evID])[ih]; + float candLocX{0.f}, candLocZ{0.f}; + projectHitToResponsePlane(candHit, candLocX, candLocZ); + const float dx = clLocX - candLocX; + const float dz = clLocZ - candLocZ; + const float dist2 = dx * dx + dz * dz; + if (dist2 < bestDist2) { + bestDist2 = dist2; + bestHit = &candHit; + hitLocX = candLocX; + hitLocZ = candLocZ; + } + } + if (bestHit == nullptr) { + nNoMCHit++; + continue; + } + const auto& hit = *bestHit; const float pt = TMath::Hypot(hit.GetPx(), hit.GetPy()); // ── Hit global midpoint ──────────────────────────────────────────── @@ -341,32 +462,6 @@ void CheckClusters(const std::string& clusfile = "o2clus_trk.root", 0.5f * (gloHend.Y() + gloHsta.Y()), 0.5f * (gloHend.Z() + gloHsta.Z())); - // ── Hit global → local ───────────────────────────── - o2::math_utils::Point3D locHsta = gman->getMatrixL2G(cluster.chipID) ^ (gloHsta); // inverse L2G - o2::math_utils::Point3D locHend = gman->getMatrixL2G(cluster.chipID) ^ (gloHend); // inverse L2G - - // ── Propagate hit segment to the sensor response surface ─────────────── - // Rather than the geometric midpoint, find where the track segment crosses - // the response plane (y = responseYShift in the flat local frame). - // For VD (curved): convert both endpoints to flat frame first. - // For ML/OT (flat): use local coordinates directly. - float hitLocX{0.f}, hitLocZ{0.f}; - if (cluster.subDetID == 0) { // VD – curved sensor - auto flatSta = o2::trk::SegmentationChip::curvedToFlat(cluster.layer, locHsta.X(), locHsta.Y()); - auto flatEnd = o2::trk::SegmentationChip::curvedToFlat(cluster.layer, locHend.X(), locHend.Y()); - float x0 = flatSta.X(), y0 = flatSta.Y(), z0 = locHsta.Z(); - float dltx = flatEnd.X() - x0, dlty = flatEnd.Y() - y0, dltz = locHend.Z() - z0; - float r = (std::abs(dlty) > 1e-9f) ? (yPlaneVD - y0) / dlty : 0.5f; - hitLocX = x0 + r * dltx; - hitLocZ = z0 + r * dltz; - } else { // ML/OT – flat sensor - float x0 = locHsta.X(), y0 = locHsta.Y(), z0 = locHsta.Z(); - float dltx = locHend.X() - x0, dlty = locHend.Y() - y0, dltz = locHend.Z() - z0; - float r = (std::abs(dlty) > 1e-9f) ? (yPlaneMLOT - y0) / dlty : 0.5f; - hitLocX = x0 + r * dltx; - hitLocZ = z0 + r * dltz; - } - nValid++; std::array data = { (float)evID, (float)trID, @@ -387,6 +482,7 @@ void CheckClusters(const std::string& clusfile = "o2clus_trk.root", LOGP(info, "Total clusters: {}", nTot); LOGP(info, "Valid (hit matched): {}", nValid); LOGP(info, "Invalid/noise MC labels: {}", nInvalidLabel); + LOGP(info, "Invalid MC event IDs: {}", nInvalidEvent); LOGP(info, "MC hit not found: {}", nNoMCHit); // ── Visualisation ────────────────────────────────────────────────────────── auto canvGlobal = new TCanvas("canvGlobal", "Cluster global positions", 1600, 800); @@ -400,25 +496,25 @@ void CheckClusters(const std::string& clusfile = "o2clus_trk.root", auto canvRes = new TCanvas("canvRes", "Residuals (cluster - hit) [cm]", 1600, 1200); canvRes->Divide(2, 3); canvRes->cd(1)->SetLogy(); - nt.Draw("hitLocX-clusLocX>>h_dx_VD(200,-0.02,0.02)", "subdet==0&&event>=0"); + nt.Draw("clusLocX-hitLocX>>h_dx_VD(200,-0.02,0.02)", "subdet==0&&event>=0"); canvRes->cd(2)->SetLogy(); - nt.Draw("hitLocZ-clusLocZ>>h_dz_VD(200,-0.02,0.02)", "subdet==0&&event>=0"); + nt.Draw("clusLocZ-hitLocZ>>h_dz_VD(200,-0.02,0.02)", "subdet==0&&event>=0"); canvRes->cd(3)->SetLogy(); - nt.Draw("hitLocX-clusLocX>>h_dx_MLOT(200,-0.02,0.02)", "subdet==1&&event>=0"); + nt.Draw("clusLocX-hitLocX>>h_dx_MLOT(200,-0.02,0.02)", "subdet==1&&event>=0"); canvRes->cd(4)->SetLogy(); - nt.Draw("hitLocZ-clusLocZ>>h_dz_MLOT(200,-0.02,0.02)", "subdet==1&&event>=0"); + nt.Draw("clusLocZ-hitLocZ>>h_dz_MLOT(200,-0.02,0.02)", "subdet==1&&event>=0"); canvRes->cd(5)->SetLogz(); - nt.Draw("hitLocX-clusLocX:hitLocZ-clusLocZ>>h_dxdz_VD(200,-0.02,0.02,200,-0.02,0.02)", "subdet==0&&event>=0", "colz"); + nt.Draw("clusLocX-hitLocX:clusLocZ-hitLocZ>>h_dxdz_VD(200,-0.02,0.02,200,-0.02,0.02)", "subdet==0&&event>=0", "colz"); canvRes->cd(6); - nt.Draw("hitLocX-clusLocX:hitLocZ-clusLocZ>>h_dxdz_MLOT(200,-0.02,0.02,200,-0.02,0.02)", "subdet==1&&event>=0", "colz"); + nt.Draw("clusLocX-hitLocX:clusLocZ-hitLocZ>>h_dxdz_MLOT(200,-0.02,0.02,200,-0.02,0.02)", "subdet==1&&event>=0", "colz"); canvRes->SaveAs("trk_residuals.png"); auto canvResVsLayer = new TCanvas("canvResVsLayer", "Residuals vs layer", 1600, 600); canvResVsLayer->Divide(2, 1); canvResVsLayer->cd(1); - nt.Draw("hitLocX-clusLocX:layer>>h_dx_vs_lay(20,0,20,200,-0.02,0.02)", "event>=0", "prof"); + nt.Draw("clusLocX-hitLocX:layer>>h_dx_vs_lay(20,0,20,200,-0.02,0.02)", "event>=0", "prof"); canvResVsLayer->cd(2); - nt.Draw("hitLocZ-clusLocZ:layer>>h_dz_vs_lay(20,0,20,200,-0.02,0.02)", "event>=0", "prof"); + nt.Draw("clusLocZ-hitLocZ:layer>>h_dz_vs_lay(20,0,20,200,-0.02,0.02)", "event>=0", "prof"); canvResVsLayer->SaveAs("trk_residuals_vs_layer.png"); fout.cd(); diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt index b8cb6a88f7163..45ce53ba7c3a3 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt @@ -15,28 +15,16 @@ endif() o2_add_library(TRKReconstruction TARGETVARNAME targetName - SOURCES src/TimeFrame.cxx - src/Clusterer.cxx + SOURCES src/Clusterer.cxx $<$:src/ClustererACTS.cxx> - $<$:src/TrackerACTS.cxx> PUBLIC_LINK_LIBRARIES - O2::ITStracking - O2::GPUCommon Microsoft.GSL::GSL - O2::CommonConstants O2::DataFormatsITSMFT O2::DataFormatsTRK O2::SimulationDataFormat - O2::ITSBase - O2::ITSReconstruction - O2::ITSMFTReconstruction - O2::DataFormatsITS - O2::TRKSimulation + O2::TRKBase nlohmann_json::nlohmann_json - ${actsTarget} - PRIVATE_LINK_LIBRARIES - O2::Steer - TBB::tbb) + ${actsTarget}) if(Acts_FOUND) target_compile_definitions(${targetName} PUBLIC O2_WITH_ACTS) diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/Clusterer.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/Clusterer.h index bcd95155f533f..3d30eb5068efe 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/Clusterer.h +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/Clusterer.h @@ -28,6 +28,7 @@ #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" #include "TRKBase/Specs.h" +#include "MathUtils/Cartesian.h" #include #include #include @@ -48,6 +49,7 @@ class Clusterer using Digit = o2::itsmft::Digit; using DigROFRecord = o2::itsmft::ROFRecord; + using DigMC2ROFRecord = o2::itsmft::MC2ROFRecord; using ClusterTruth = o2::dataformats::MCTruthContainer; using ConstDigitTruth = o2::dataformats::ConstMCTruthContainerView; using Label = o2::MCCompLabel; @@ -166,7 +168,12 @@ class Clusterer std::vector& patterns, std::vector& clusterROFs, const ConstDigitTruth* digitLabels = nullptr, - ClusterTruth* clusterLabels = nullptr); + ClusterTruth* clusterLabels = nullptr, + gsl::span digMC2ROFs = {}, + std::vector* clusterMC2ROFs = nullptr); + + static o2::math_utils::Point3D getClusterLocalCoordinates(const Cluster& cluster, const uint8_t* patt, + float yPlaneMLOT = 0.f) noexcept; protected: int mNHugeClus = 0; diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/ClustererACTS.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/ClustererACTS.h index 5d68193e5e375..37a148aa78afb 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/ClustererACTS.h +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/ClustererACTS.h @@ -35,7 +35,9 @@ class ClustererACTS : public Clusterer std::vector& patterns, std::vector& clusterROFs, const ConstDigitTruth* digitLabels = nullptr, - ClusterTruth* clusterLabels = nullptr) override; + ClusterTruth* clusterLabels = nullptr, + gsl::span digMC2ROFs = {}, + std::vector* clusterMC2ROFs = nullptr) override; private: }; diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h deleted file mode 100644 index 005237fe28839..0000000000000 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \file TimeFrame.h -/// \brief TRK TimeFrame class derived from ITS TimeFrame -/// - -#ifndef ALICEO2_TRK_TIMEFRAME_H -#define ALICEO2_TRK_TIMEFRAME_H - -#include "ITStracking/TimeFrame.h" -#include "ITStracking/Constants.h" -#include "ITStracking/Configuration.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include -#include -#include -#include - -#include - -class TTree; - -namespace o2 -{ -namespace trk -{ -class GeometryTGeo; - -/// TRK TimeFrame class that extends ITS TimeFrame functionality -/// This allows for customization of tracking algorithms specific to the TRK detector -template -class TimeFrame : public o2::its::TimeFrame -{ - public: - TimeFrame() = default; - ~TimeFrame() override = default; - - /// Override methods if needed for TRK-specific behavior - /// For now, we inherit all functionality from ITS TimeFrame - - /// Process hits from TTree to initialize ROFs - /// \param hitsTree Tree containing TRK hits - /// \param gman TRK geometry manager instance - /// \param config Configuration parameters for hit reconstruction - int loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, const nlohmann::json& config); - - /// Add primary vertices from MC headers for each ROF - /// \param mcHeaderTree Tree containing MC event headers - /// \param nRofs Number of ROFs (Read-Out Frames) - /// \param nEvents Number of events to process - /// \param inROFpileup Number of events per ROF - /// \param rofLength ROF length in BCs (must match what was used in loadROFsFromHitTree) - void getPrimaryVerticesFromMC(TTree* mcHeaderTree, int nRofs, Long64_t nEvents, int inROFpileup, uint32_t rofLength = 198); -}; - -} // namespace trk -} // namespace o2 - -#endif // ALICEO2_TRK_TIMEFRAME_H diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/Clusterer.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/Clusterer.cxx index e0d689e4db5ed..d60d6900657ba 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/Clusterer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/Clusterer.cxx @@ -14,6 +14,7 @@ #include "TRKReconstruction/Clusterer.h" #include "TRKBase/GeometryTGeo.h" +#include "TRKBase/SegmentationChip.h" #include #include @@ -21,6 +22,51 @@ namespace o2::trk { +//__________________________________________________ +o2::math_utils::Point3D Clusterer::getClusterLocalCoordinates(const Cluster& cluster, const uint8_t* patt, + float yPlaneMLOT) noexcept +{ + const uint8_t rowSpan = *patt++; + const uint8_t colSpan = *patt++; + const int nBytes = (rowSpan * colSpan + 7) / 8; + + float cogDr{0.f}, cogDc{0.f}; + int nPix{0}, pixIdx{0}; + for (int ib = 0; ib < nBytes; ib++) { + const uint8_t byte = *patt++; + for (int bit = 7; bit >= 0 && pixIdx < rowSpan * colSpan; bit--, pixIdx++) { + if (byte & (1 << bit)) { + cogDr += pixIdx / colSpan; + cogDc += pixIdx % colSpan; + nPix++; + } + } + } + if (nPix > 1) { + cogDr /= nPix; + cogDc /= nPix; + } + + float x{0.f}, y{0.f}, z{0.f}; + SegmentationChip::detectorToLocalUnchecked(cluster.row, cluster.col, x, z, + cluster.subDetID, cluster.layer, cluster.disk); + + const float pitchRow = (cluster.subDetID == 0) ? SegmentationChip::PitchRowVD : SegmentationChip::PitchRowMLOT; + const float pitchCol = (cluster.subDetID == 0) ? SegmentationChip::PitchColVD : SegmentationChip::PitchColMLOT; + x -= cogDr * pitchRow; + z += cogDc * pitchCol; + + if (cluster.subDetID == 0) { + auto cv = SegmentationChip::flatToCurved(cluster.layer, x, 0.f); + x = cv.X(); + y = cv.Y(); + } else { + y = yPlaneMLOT; + } + + return {x, y, z}; +} + //__________________________________________________ void Clusterer::process(gsl::span digits, gsl::span digitROFs, @@ -28,7 +74,9 @@ void Clusterer::process(gsl::span digits, std::vector& patterns, std::vector& clusterROFs, const ConstDigitTruth* digitLabels, - ClusterTruth* clusterLabels) + ClusterTruth* clusterLabels, + gsl::span digMC2ROFs, + std::vector* clusterMC2ROFs) { if (!mThread) { mThread = std::make_unique(this); @@ -79,6 +127,13 @@ void Clusterer::process(gsl::span digits, clusterROFs.emplace_back(inROF.getBCData(), inROF.getROFrame(), outFirst, static_cast(clusters.size()) - outFirst); } + + if (clusterMC2ROFs && !digMC2ROFs.empty()) { + clusterMC2ROFs->reserve(clusterMC2ROFs->size() + digMC2ROFs.size()); + for (const auto& in : digMC2ROFs) { + clusterMC2ROFs->emplace_back(in.eventRecordID, in.rofRecordID, in.minROF, in.maxROF); + } + } } //__________________________________________________ diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx index b764fcdd1cd79..30ab503b7e250 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx @@ -162,7 +162,9 @@ void ClustererACTS::process(gsl::span digits, std::vector& patterns, std::vector& clusterROFs, const ConstDigitTruth* digitLabels, - ClusterTruth* clusterLabels) + ClusterTruth* clusterLabels, + gsl::span digMC2ROFs, + std::vector* clusterMC2ROFs) { if (!mThread) { mThread = std::make_unique(this); @@ -385,10 +387,10 @@ void ClustererACTS::process(gsl::span digits, outFirst, static_cast(clusters.size()) - outFirst); } - if (clusterMC2ROFs && !digMC2ROFs.empty()) { - clusterMC2ROFs->reserve(clusterMC2ROFs->size() + digMC2ROFs.size()); - for (const auto& in : digMC2ROFs) { - clusterMC2ROFs->emplace_back(in.eventRecordID, in.rofRecordID, in.minROF, in.maxROF); - } - } + // if (clusterMC2ROFs && !digMC2ROFs.empty()) { + // clusterMC2ROFs->reserve(clusterMC2ROFs->size() + digMC2ROFs.size()); + // for (const auto& in : digMC2ROFs) { + // clusterMC2ROFs->emplace_back(in.eventRecordID, in.rofRecordID, in.minROF, in.maxROF); + // } + // } } diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx deleted file mode 100644 index 957560aea8cae..0000000000000 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx +++ /dev/null @@ -1,225 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \file TimeFrame.cxx -/// \brief TRK TimeFrame implementation -/// - -#include "TRKReconstruction/TimeFrame.h" -#include "TRKSimulation/Hit.h" -#include "TRKBase/GeometryTGeo.h" -#include "Framework/Logger.h" -#include "SimulationDataFormat/MCEventHeader.h" -#include -#include -#include -#include - -using o2::its::clearResizeBoundedVector; - -namespace o2::trk -{ - -template -int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, const nlohmann::json& config) -{ - constexpr std::array startLayer{0, 3}; - const Long64_t nEvents = hitsTree->GetEntries(); - - gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L) | o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); - - std::vector* trkHit = nullptr; - hitsTree->SetBranchAddress("TRKHit", &trkHit); - - const int inROFpileup{config.contains("inROFpileup") ? config["inROFpileup"].get() : 1}; - - // Calculate number of ROFs - const int nRofs = (nEvents + inROFpileup - 1) / inROFpileup; - - // Set up ROF timing for all layers (no staggering in TRK simulation, all layers read out together) - constexpr uint32_t rofLength = 198; // ROF length in BC - o2::its::ROFOverlapTable overlapTable; - for (int iLayer = 0; iLayer < NLayers; ++iLayer) { - overlapTable.defineLayer(iLayer, nRofs, rofLength, 0, 0, 0); - } - overlapTable.init(); - this->setROFOverlapTable(overlapTable); - - // Set up the vertex lookup table timing (pre-allocate, vertices will be filled later) - o2::its::ROFVertexLookupTable vtxLookupTable; - for (int iLayer = 0; iLayer < NLayers; ++iLayer) { - vtxLookupTable.defineLayer(iLayer, nRofs, rofLength, 0, 0, 0); - } - vtxLookupTable.init(); // pre-allocate without vertices - this->setROFVertexLookupTable(vtxLookupTable); - - // Reset and prepare ROF data structures - for (int iLayer{0}; iLayer < NLayers; ++iLayer) { - this->mMinR[iLayer] = std::numeric_limits::max(); - this->mMaxR[iLayer] = std::numeric_limits::lowest(); - this->mROFramesClusters[iLayer].clear(); - this->mROFramesClusters[iLayer].resize(nRofs + 1, 0); - this->mUnsortedClusters[iLayer].clear(); - this->mTrackingFrameInfo[iLayer].clear(); - this->mClusterExternalIndices[iLayer].clear(); - } - - // Pre-count hits to reserve memory efficiently - std::array clusterCountPerLayer{}; - for (Long64_t iEvent = 0; iEvent < nEvents; ++iEvent) { - hitsTree->GetEntry(iEvent); - for (const auto& hit : *trkHit) { - if (gman->getDisk(hit.GetDetectorID()) != -1) { - continue; // skip non-barrel hits - } - int subDetID = gman->getSubDetID(hit.GetDetectorID()); - const int layer = startLayer[subDetID] + gman->getLayer(hit.GetDetectorID()); - if (layer >= NLayers) { - continue; - } - ++clusterCountPerLayer[layer]; - } - } - - // Reserve memory for all layers (mClusterSize is now per-layer) - for (int iLayer{0}; iLayer < NLayers; ++iLayer) { - this->mUnsortedClusters[iLayer].reserve(clusterCountPerLayer[iLayer]); - this->mTrackingFrameInfo[iLayer].reserve(clusterCountPerLayer[iLayer]); - this->mClusterExternalIndices[iLayer].reserve(clusterCountPerLayer[iLayer]); - clearResizeBoundedVector(this->mClusterSize[iLayer], clusterCountPerLayer[iLayer], this->mMemoryPool.get()); - } - - std::array resolution{0.001, 0.001, 0.001, 0.001, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004}; - if (config["geometry"]["pitch"].size() == static_cast(NLayers)) { - for (size_t iLayer{0}; iLayer < config["geometry"]["pitch"].size(); ++iLayer) { - LOGP(info, "Setting resolution for layer {} from config", iLayer); - LOGP(info, "Layer {} pitch {} cm", iLayer, config["geometry"]["pitch"][iLayer].get()); - resolution[iLayer] = config["geometry"]["pitch"][iLayer].get() / std::sqrt(12.f); - } - } - LOGP(info, "Number of active parts in VD: {}", gman->getNumberOfActivePartsVD()); - - // One shared MC label container for all layers - auto* labels = new dataformats::MCTruthContainer(); - - int hitCounter{0}; - int iRof{0}; // Current ROF index - for (Long64_t iEvent = 0; iEvent < nEvents; ++iEvent) { - hitsTree->GetEntry(iEvent); - - for (auto& hit : *trkHit) { - if (gman->getDisk(hit.GetDetectorID()) != -1) { - continue; // skip non-barrel hits for this test - } - int subDetID = gman->getSubDetID(hit.GetDetectorID()); - const int layer = startLayer[subDetID] + gman->getLayer(hit.GetDetectorID()); - - float alpha{0.f}; - o2::math_utils::Point3D gloXYZ; - o2::math_utils::Point3D trkXYZ; - float r{0.f}; - if (layer >= NLayers) { - continue; - } - if (layer >= 3) { - int chipID = hit.GetDetectorID(); - alpha = gman->getSensorRefAlphaMLOT(chipID); - const o2::math_utils::Transform3D& l2g = gman->getMatrixL2G(chipID); - auto locXYZ = l2g ^ (hit.GetPos()); - locXYZ.SetX(locXYZ.X() + gRandom->Gaus(0.0, resolution[layer])); - locXYZ.SetZ(locXYZ.Z() + gRandom->Gaus(0.0, resolution[layer])); - gloXYZ = gman->getMatrixL2G(chipID) * locXYZ; - trkXYZ = gman->getMatrixT2L(chipID - gman->getNumberOfActivePartsVD()) ^ locXYZ; - r = std::hypot(gloXYZ.X(), gloXYZ.Y()); - } else { - const auto& hitPos = hit.GetPos(); - r = std::hypot(hitPos.X(), hitPos.Y()); - alpha = std::atan2(hitPos.Y(), hitPos.X()) + gRandom->Gaus(0.0, resolution[layer] / r); - o2::math_utils::bringTo02Pi(alpha); - gloXYZ.SetX(r * std::cos(alpha)); - gloXYZ.SetY(r * std::sin(alpha)); - gloXYZ.SetZ(hitPos.Z() + gRandom->Gaus(0.0, resolution[layer])); - trkXYZ.SetX(r); - trkXYZ.SetY(0.f); - trkXYZ.SetZ(gloXYZ.Z()); - } - this->mMinR[layer] = std::min(this->mMinR[layer], r); - this->mMaxR[layer] = std::max(this->mMaxR[layer], r); - this->addTrackingFrameInfoToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), trkXYZ.x(), alpha, - std::array{trkXYZ.y(), trkXYZ.z()}, - std::array{resolution[layer] * resolution[layer], 0., resolution[layer] * resolution[layer]}); - /// Rotate to the global frame - const int clusterIdxInLayer = this->mUnsortedClusters[layer].size(); - this->addClusterToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), clusterIdxInLayer); - this->addClusterExternalIndexToLayer(layer, hitCounter); - MCCompLabel label{hit.GetTrackID(), static_cast(iEvent), 0}; - labels->addElement(hitCounter, label); - this->mClusterSize[layer][clusterIdxInLayer] = 1; - hitCounter++; - } - trkHit->clear(); - - // Update ROF structure when we complete an ROF or reach the last event - if ((iEvent + 1) % inROFpileup == 0 || iEvent == nEvents - 1) { - iRof++; - for (unsigned int iLayer{0}; iLayer < this->mUnsortedClusters.size(); ++iLayer) { - this->mROFramesClusters[iLayer][iRof] = this->mUnsortedClusters[iLayer].size(); // effectively calculating an exclusive sum - } - } - } - - // Set the shared labels container for all layers - for (int iLayer = 0; iLayer < NLayers; ++iLayer) { - this->mClusterLabels[iLayer] = labels; - } - - return nRofs; -} - -template -void TimeFrame::getPrimaryVerticesFromMC(TTree* mcHeaderTree, int nRofs, Long64_t nEvents, int inROFpileup, uint32_t rofLength) -{ - auto mcheader = new o2::dataformats::MCEventHeader; - mcHeaderTree->SetBranchAddress("MCEventHeader.", &mcheader); - - this->mPrimaryVertices.clear(); - - int iRof{0}; - for (Long64_t iEvent = 0; iEvent < nEvents; ++iEvent) { - mcHeaderTree->GetEntry(iEvent); - o2::its::Vertex vertex; - vertex.setXYZ(mcheader->GetX(), mcheader->GetY(), mcheader->GetZ()); - vertex.setNContributors(30); - vertex.setChi2(0.f); - - // Set proper BC timestamp for vertex-ROF compatibility - // The vertex timestamp is set to the center of its ROF with half-ROF as error - const uint32_t rofCenter = static_cast(rofLength * iRof + rofLength / 2); - const uint16_t rofHalf = static_cast(rofLength / 2); - vertex.setTimeStamp({rofCenter, rofHalf}); - - LOGP(debug, "ROF {}: Added primary vertex at ({}, {}, {}) with BC timestamp [{}, +/-{}]", - iRof, mcheader->GetX(), mcheader->GetY(), mcheader->GetZ(), rofCenter, rofHalf); - this->addPrimaryVertex(vertex); - if ((iEvent + 1) % inROFpileup == 0 || iEvent == nEvents - 1) { - iRof++; - } - } - this->mMultiplicityCutMask.resetMask(1u); /// all ROFs are valid with MC primary vertices. - - // Update the vertex lookup table with the newly added vertices - this->updateROFVertexLookupTable(); -} - -// Explicit template instantiation for TRK with 11 layers -template class TimeFrame<11>; - -} // namespace o2::trk diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h index ef4d5657a1b4f..e900cfa679ffe 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h @@ -141,6 +141,9 @@ class TRKOTLayer : public TRKSegmentedLayer TGeoVolume* createHalfStave(); void createLayer(TGeoVolume* motherVolume) override; + protected: + static constexpr float sGapBetweenOuterTrackerBarrelHalves = 0.8; // cm, gap between the two halves of the OT barrel + private: static constexpr double sHalfStaveWidth = constants::OT::halfstave::width; static constexpr double sInStaveOverlap = constants::moduleMLOT::gaps::outerEdgeLongSide + constants::moduleMLOT::chip::passiveEdgeReadOut + 0.1; // 1.5mm outer-edge + 1mm deadzone + 1mm (true) overlap diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKServices.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKServices.h index 79033f48cb0b9..dedbbb096b8e8 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKServices.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKServices.h @@ -21,6 +21,7 @@ // Water bundle disk PU 0,44 19 H2O 0,56 36,08 #include + #include namespace o2 @@ -51,6 +52,7 @@ class TRKServices : public FairModule void createMiddleServices(TGeoVolume* motherVolume); void createOuterDisksServices(TGeoVolume* motherVolume); void createOuterBarrelServices(TGeoVolume* motherVolume); + void createServicesAroundBeamPipe(TGeoVolume* motherVolume); void createMLServicesPeacock(TGeoVolume* motherVolume); void createOTServicesPeacock(TGeoVolume* motherVolume); void createVacuumCompositeShape(); @@ -81,4 +83,4 @@ class TRKServices : public FairModule }; } // namespace trk } // namespace o2 -#endif // O2_TRK_SERVICES_H \ No newline at end of file +#endif // O2_TRK_SERVICES_H diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx index 3fa51afe3ba2b..196727b2c140f 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx @@ -98,7 +98,7 @@ void Detector::configMLOT() switch (trkPars.layoutMLOT) { case kCylindrical: { - const std::vector length{128.35f, 128.35f, 128.35f, 128.35f, 128.35f, 256.7f, 256.7f, 256.7f}; + const std::vector length{127.985f, 127.985f, 127.985f, 127.985f, 127.985f, 255.9f, 255.9f, 255.9f}; LOGP(warning, "Loading cylindrical configuration for ALICE3 TRK"); for (int i{0}; i < constants::ML::nLayers + constants::OT::nLayers; ++i) { std::string name = GeometryTGeo::getTRKLayerPattern() + std::to_string(i); @@ -111,7 +111,7 @@ void Detector::configMLOT() // const std::vector tiltAngles{10.f, 16.1f, 19.2f, 0.f, 0.f, 0.f, 0.f, 0.f}; const std::vector nStaves{10, 14, 18, 26, 38, 32, 42, 56}; // const std::vector nStaves{10, 16, 22, 26, 38, 32, 42, 56}; - const std::vector nMods{10, 10, 10, 10, 10, 20, 20, 20}; + const std::vector nMods{11, 11, 11, 11, 11, 22, 22, 22}; const std::vector stagOffsets{0.f, 0.f, 0.f, 1.17f, 0.89f}; @@ -165,18 +165,28 @@ void Detector::configFromFile(std::string fileName) switch (trkPars.layoutMLOT) { case kCylindrical: { + // Expected column mapping in the text file (separated by \t): + // tmpBuff[0] = rInn + // tmpBuff[1] = length + // tmpBuff[2] = thick + // tmpBuff[3] = matBudgetMode (optional, default = Thickness) + // Cylindrical requires at least 3 parameters if (tmpBuff.size() < 3) { LOGP(fatal, "Invalid configuration for cylindrical layer {}: insufficient parameters.", layerCount); } + float rInn = tmpBuff[0]; + float length = tmpBuff[1]; + float thick = tmpBuff[2]; + // Default mode is Thickness - MatBudgetParamMode mode = MatBudgetParamMode::Thickness; + MatBudgetParamMode matBudgetMode = MatBudgetParamMode::Thickness; if (tmpBuff.size() >= 4) { - mode = static_cast(static_cast(tmpBuff[3])); + matBudgetMode = static_cast(static_cast(tmpBuff[3])); } - mLayers.push_back(std::make_unique(layerCount, name, tmpBuff[0], tmpBuff[1], tmpBuff[2], mode)); + mLayers.push_back(std::make_unique(layerCount, name, rInn, length, thick, matBudgetMode)); break; } case kSegmented: { @@ -201,7 +211,7 @@ void Detector::configFromFile(std::string fileName) int nMods = static_cast(tmpBuff[4]); // Default mode is Thickness - MatBudgetParamMode mode = MatBudgetParamMode::Thickness; + MatBudgetParamMode matBudgetMode = MatBudgetParamMode::Thickness; if (layerCount < constants::ML::nLayers) { // ML layers require stagOffset (index 5) @@ -211,17 +221,17 @@ void Detector::configFromFile(std::string fileName) float stagOffset = tmpBuff[5]; if (tmpBuff.size() >= 7) { - mode = static_cast(static_cast(tmpBuff[6])); + matBudgetMode = static_cast(static_cast(tmpBuff[6])); } - mLayers.push_back(std::make_unique(layerCount, name, rInn, stagOffset, tiltAngle, nStaves, nMods, thick, mode)); + mLayers.push_back(std::make_unique(layerCount, name, rInn, stagOffset, tiltAngle, nStaves, nMods, thick, matBudgetMode)); } else { // OT layers do NOT have stagOffset. The optional mode is at index 5. if (tmpBuff.size() >= 6) { - mode = static_cast(static_cast(tmpBuff[5])); + matBudgetMode = static_cast(static_cast(tmpBuff[5])); } - mLayers.push_back(std::make_unique(layerCount, name, rInn, tiltAngle, nStaves, nMods, thick, mode)); + mLayers.push_back(std::make_unique(layerCount, name, rInn, tiltAngle, nStaves, nMods, thick, matBudgetMode)); } break; } diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx index 7a4b7bef34e03..5206985992ecf 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx @@ -388,13 +388,15 @@ TGeoVolume* TRKOTLayer::createHalfStave() { TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); std::string halfStaveName = GeometryTGeo::getTRKHalfStavePattern() + std::to_string(mLayerNumber); - TGeoShape* halfStave = new TGeoBBox(sHalfStaveWidth / 2, mChipThickness / 2, mLength / 2); + float lengthHalfBarrel = mLength / 2; + TGeoShape* halfStave = new TGeoBBox(sHalfStaveWidth / 2, mChipThickness / 2, lengthHalfBarrel / 2); TGeoVolume* halfStaveVol = new TGeoVolume(halfStaveName.c_str(), halfStave, medSi); halfStaveVol->SetLineColor(kYellow); - for (int iModule = 0; iModule < mNumberOfModules; iModule++) { + int nModulesPerHalfBarrel = mNumberOfModules / 2; // assuming mNumberOfModules is always even, which should be the case given the current specifications + for (int iModule = 0; iModule < nModulesPerHalfBarrel; iModule++) { TGeoVolume* moduleVol = createModule(); - double zPos = -0.5 * mNumberOfModules * sModuleLength + (iModule + 0.5) * sModuleLength; + double zPos = -0.5 * nModulesPerHalfBarrel * sModuleLength + (iModule + 0.5) * sModuleLength; TGeoCombiTrans* trans = new TGeoCombiTrans(); trans->SetTranslation(0, 0, zPos); LOGP(debug, "Inserting {} in {} ", moduleVol->GetName(), halfStaveVol->GetName()); @@ -431,35 +433,46 @@ void TRKOTLayer::createLayer(TGeoVolume* motherVolume) TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); // TGeoTube* layer = new TGeoTube(mInnerRadius - 0.333 * sLogicalVolumeThickness, mInnerRadius + 0.667 * sLogicalVolumeThickness, mLength / 2); - TGeoTube* layer = new TGeoTube(rMin, rMax, mLength / 2); + TGeoTube* layer = new TGeoTube(rMin, rMax, (mLength + sGapBetweenOuterTrackerBarrelHalves) / 2); TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); layerVol->SetLineColor(kYellow); // Compute the number of staves - int nStaves = (int)std::ceil(mInnerRadius * 2 * TMath::Pi() / sStaveWidth); - nStaves += nStaves % 2; // Require an even number of staves + int nStavesHalfBarrel = (int)std::ceil(mInnerRadius * 2 * TMath::Pi() / sStaveWidth); + nStavesHalfBarrel += nStavesHalfBarrel % 2; // Require an even number of staves // Nominal average radius used as the placement barycenter for all staves const double avgRadius = 0.5 * (mInnerRadius + mOuterRadius); // Compute the size of the overlap region - double theta = 2. * TMath::Pi() / nStaves; + double theta = 2. * TMath::Pi() / nStavesHalfBarrel; double theta1 = std::atan(sStaveWidth / 2 / mInnerRadius); double st = std::sin(theta); double ct = std::cos(theta); double theta2 = std::atan((mInnerRadius * st - sStaveWidth / 2 * ct) / (mInnerRadius * ct + sStaveWidth / 2 * st)); double overlap = (theta1 - theta2) * mInnerRadius; - LOGP(info, "Creating a layer with {} staves and {} mm overlap", nStaves, overlap * 10); + LOGP(info, "Creating a layer with two half barrels, each with {} staves and {} mm overlap", nStavesHalfBarrel, overlap * 10); + + float lengthHalfBarrel = mLength / 2; + int nStaves = nStavesHalfBarrel * 2; // since we now have two half-barrels (separated by a small gap), we double the number of staves for (int iStave = 0; iStave < nStaves; iStave++) { TGeoVolume* staveVol = createStave(); + int whichHalfBarrel = iStave / nStavesHalfBarrel; // 0 for the first half (negative z), 1 for the second half (positive z) TGeoCombiTrans* trans = new TGeoCombiTrans(); double phi = theta * iStave; double phiDeg = phi * TMath::RadToDeg(); - TGeoRotation* rot = new TGeoRotation("rot", phiDeg + 90 + mTiltAngle, 0, 0); + // TGeoRotation* rot = new TGeoRotation("rot", phiDeg + 90 + mTiltAngle, 0, 0); + TGeoRotation* rot = new TGeoRotation("rot"); + if (whichHalfBarrel == 1) { + rot->RotateY(180.); // degrees, rotate the second half barrel by 180 degrees around Y to achieve the correct staggering orientation + } + rot->RotateZ(phiDeg + 90 + (whichHalfBarrel == 0 ? +1 : -1) * mTiltAngle); // phi in degrees, tilting depends on the half-barrel side trans->SetRotation(rot); // trans->SetTranslation(mInnerRadius * std::cos(phi), mInnerRadius * std::sin(phi), 0); - trans->SetTranslation(avgRadius * std::cos(phi), avgRadius * std::sin(phi), 0); + // trans->SetTranslation(avgRadius * std::cos(phi), avgRadius * std::sin(phi), 0); + double zPos = (whichHalfBarrel == 0 ? -1 : 1) * (0.5 * lengthHalfBarrel + sGapBetweenOuterTrackerBarrelHalves / 2); + trans->SetTranslation(avgRadius * std::cos(phi), avgRadius * std::sin(phi), zPos); LOGP(debug, "Inserting {} in {} ", staveVol->GetName(), layerVol->GetName()); layerVol->AddNode(staveVol, iStave, trans); } diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx index 7cf7dc863607e..53ac0a4b12865 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx @@ -9,22 +9,23 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include #include +#include + +#include +#include +#include +#include +#include #include #include +#include + #include -#include -#include -#include -#include -#include -#include -#include -#include +#include -using std::string; +#include namespace o2 { @@ -108,7 +109,7 @@ void TRKServices::createMaterials() matmgr.Material("ALICE3_TRKSERVICES", 73, "BERYLLIUM", 9.01, 4., 1.848, 35.3, 36.7); // Beryllium - Candidate for IRIS vacuum vessel matmgr.Mixture("ALICE3_TRKSERVICES", 74, "ALUMINIUM5083", aAl5083, zAl5083, dAl5083, 9, wAl5083); // AL5083 - Candidate for IRIS vacuum vessel matmgr.Mixture("ALICE3_TRKSERVICES", 75, "ALUMINIUMBERYLLIUMMETAL", aAlBeMet, zAlBeMet, dAlBeMet, 2, wAlBeMet); // Aluminium-Beryllium metal - Candidate for IRIS vacuum vessel - matmgr.Material("ALICE3_TRKSERVICES", 76, "CARBONFIBERM55J6K", 12.0107, 6, 1.92, 22.4, 999); // Carbon Fiber M55J + matmgr.Material("ALICE3_TRKSERVICES", 76, "CARBONFIBERM55J6K", 12.0107, 6, 1.92, 22.4, 45.4); // Carbon Fiber M55J matmgr.Mixture("ALICE3_PIPE", 77, "VACUUM", aAir, zAir, dAir1, 4, wAir); matmgr.Medium("ALICE3_TRKSERVICES", 1, "CERAMIC", 66, 0, ifield, fieldm, tmaxfd, stemax, deemax, epsil, stmin); // Ceramic for cold plate @@ -140,6 +141,9 @@ void TRKServices::createServices(TGeoVolume* motherVolume) createOuterBarrelServices(vol); } else { LOGP(info, "TRK services: Peacock layout"); + if (trkPars.includeLowServices) { + createServicesAroundBeamPipe(vol); + } createMLServicesPeacock(vol); createOTServicesPeacock(vol); } @@ -187,7 +191,7 @@ void TRKServices::registerVacuum(TGeoVolume* motherVolume) TGeoVolume* vacuumVolume = new TGeoVolume("A3IP_VACUUM", vacuumComposite, kMedVac); // Add the vacuum to the barrel - vacuumVolume->SetLineColor(kAzure + 7); + vacuumVolume->SetLineColor(kAzure + 6); vacuumVolume->SetTransparency(80); motherVolume->AddNode(vacuumVolume, 1, new TGeoTranslation(0, 0, 0)); @@ -521,9 +525,63 @@ void TRKServices::createOuterBarrelServices(TGeoVolume* motherVolume) motherVolume->AddNode(outerBarrelCoolingH2OVolume, 1, nullptr); } +void TRKServices::createServicesAroundBeamPipe(TGeoVolume* motherVolume) +{ + // This method hardcodes the shape for the low services around the beam pipe + auto& matmgr = o2::base::MaterialManager::Instance(); + + TGeoMedium* medCu = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_COPPER"); + + const float tolleranceLowServices = 0.3f; + + // Low services start longitudinally from middle barrel on the C side, while from the middle barrel connection disks on the A side + const float zStartASideFirstBlock = 65.265f + tolleranceLowServices; + const float zStartCSideFirstBlock = 64.5f + tolleranceLowServices; + const float zStartSecondBlock = 150.f; + const float zStartThirdBlock = 365.f; + const float zEndThirdBlock = 400.f; + + // Low services start radially from IRIS out-vacuum services on the A side, while from beam pipe on the C side + const float rInASide = 3.333f + tolleranceLowServices; + const float rInCSide = 5.6f + tolleranceLowServices; + + // Low services end radially at the disks inners radius + const float rOutFirstBlock = 10.f - tolleranceLowServices; + const float rOutSecondBlock = 20.f - tolleranceLowServices; + const float rOutThirdBlock = 15.f - tolleranceLowServices; + + for (auto& orientation : {Orientation::kASide, Orientation::kCSide}) { + std::string orLabel = orientation == Orientation::kASide ? "A" : "C"; + + float zStartLowServices = orientation == Orientation::kASide ? zStartASideFirstBlock : zStartCSideFirstBlock; + float rInLowServices = orientation == Orientation::kASide ? rInASide : rInCSide; + + TGeoTube* lowServicesFirstBlock = new TGeoTube(Form("TRK_LOWSERVICES_FIRSTBLOCKsh_%s", orLabel.c_str()), rInLowServices, rOutFirstBlock, (zStartSecondBlock - zStartLowServices) / 2.); + TGeoVolume* lowServicesFirstBlockVolume = new TGeoVolume(Form("TRK_LOWSERVICES_FIRSTBLOCK_%s", orLabel.c_str()), lowServicesFirstBlock, medCu); + lowServicesFirstBlockVolume->SetLineColor(kGray); + + TGeoTube* lowServicesSecondBlock = new TGeoTube(Form("TRK_LOWSERVICES_SECONDBLOCKsh_%s", orLabel.c_str()), rInLowServices, rOutSecondBlock, (zStartThirdBlock - zStartSecondBlock) / 2.); + TGeoVolume* lowServicesSecondBlockVolume = new TGeoVolume(Form("TRK_LOWSERVICES_SECONDBLOCK_%s", orLabel.c_str()), lowServicesSecondBlock, medCu); + lowServicesSecondBlockVolume->SetLineColor(kGray); + + TGeoTube* lowServicesThirdBlock = new TGeoTube(Form("TRK_LOWSERVICES_THIRDBLOCKsh_%s", orLabel.c_str()), rInLowServices, rOutThirdBlock, (zEndThirdBlock - zStartThirdBlock) / 2.); + TGeoVolume* lowServicesThirdBlockVolume = new TGeoVolume(Form("TRK_LOWSERVICES_THIRDBLOCK_%s", orLabel.c_str()), lowServicesThirdBlock, medCu); + lowServicesThirdBlockVolume->SetLineColor(kGray); + + auto* rot = new TGeoRotation("", 0, 0, 180); + auto* combiTransFirstBlock = new TGeoCombiTrans(0, 0, (int)orientation * (zStartLowServices + (zStartSecondBlock - zStartLowServices) / 2.), rot); + auto* combiTransSecondBlock = new TGeoCombiTrans(0, 0, (int)orientation * (zStartSecondBlock + (zStartThirdBlock - zStartSecondBlock) / 2.), rot); + auto* combiTransThirdBlock = new TGeoCombiTrans(0, 0, (int)orientation * (zStartThirdBlock + (zEndThirdBlock - zStartThirdBlock) / 2.), rot); + + motherVolume->AddNode(lowServicesFirstBlockVolume, 1, combiTransFirstBlock); + motherVolume->AddNode(lowServicesSecondBlockVolume, 1, combiTransSecondBlock); + motherVolume->AddNode(lowServicesThirdBlockVolume, 1, combiTransThirdBlock); + } +} + void TRKServices::createMLServicesPeacock(TGeoVolume* motherVolume) { - // This method hardcoes the yellow shape for the middle services + // This method hardcodes the yellow shape for the middle services auto& matmgr = o2::base::MaterialManager::Instance(); TGeoMedium* medSiO2 = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_SILICONDIOXIDE"); @@ -556,9 +614,10 @@ void TRKServices::createMLServicesPeacock(TGeoVolume* motherVolume) float pePowerAreaD = ITDisknPower * mPowerBundleArea * mPowerBundleComposition[1]; // Carbon Fiber Cylinder support for the middle tracker - float rMinMiddleCarbonSupport = 34.8f; // Arbitrary value - float rMaxMiddleCarbonSupport = 35.f; // 2 mm of carbon fiber - const float zLengthMiddleCarbon = 129.f; + // (from ICD_ALICE3_V3.b.3 drawing: 38.5 cm are allocated for staves and services, + 1 cm for the support; we assume less for the support - to be reconsidered if necessary) + float rMinMiddleCarbonSupport = 39.3f; // cm + float rMaxMiddleCarbonSupport = 39.5f; // cm, assume 2 mm of carbon fiber, ~0.88% X/X0 + const float zLengthMiddleCarbon = 282.f; // cm, to cover the full length of ML barrel and disks, from Corrado's drawing TGeoTube* middleBarrelCarbonSupport = new TGeoTube("TRK_MID_CARBONSUPPORTsh", rMinMiddleCarbonSupport, rMaxMiddleCarbonSupport, zLengthMiddleCarbon / 2.); TGeoVolume* middleBarrelCarbonSupportVolume = new TGeoVolume("TRK_MID_CARBONSUPPORT", middleBarrelCarbonSupport, medCFiber); middleBarrelCarbonSupportVolume->SetLineColor(kGray); @@ -566,27 +625,28 @@ void TRKServices::createMLServicesPeacock(TGeoVolume* motherVolume) motherVolume->AddNode(middleBarrelCarbonSupportVolume, 1, nullptr); // Get geometry information from TRK which is already present - float rMinMiddleServices = 35.f; - float rMinMiddleBarrel = rMinMiddleServices; - const float zLengthCylinderMiddleServices = 40.5f; - const float zLengthMiddleServices = 143.f; + float rMinMiddleServices = 38.0f; // cm, start radius of the ML services = maximum radius allowed for sensors (35 cm), plus some margin for disk paving with modules + const float zMiddleServicesBarrel = 64.5f; // cm, z position of the first barrel ML service disk + const float zMiddleServicesBarrelFwdConnection = 143.f; // cm, z position of barrel to forward connection services + const float zLengthCylinderMiddleServicesBarrel = zMiddleServicesBarrelFwdConnection - zMiddleServicesBarrel; + + const float zStartServicesForMiddleDisks = 77.0f; // cm, starting z position of ML disk services, assumed to be the same as of the first ML disk + const float zLengthCylinderMiddleServicesDisk = zMiddleServicesBarrelFwdConnection - zStartServicesForMiddleDisks; // Middle layer barrel services are only on A side - rMinMiddleServices = 35.f; - LOGP(info, "Building services for Middle Tracker rminMiddleServices"); + LOGP(info, "Building services for barrel Middle Layers"); // Middle barrel connection disks const float rMinMiddleBarrelDisk = 5.68f; - const float rMaxMiddleBarrelDisk = 35.f; - const float zLengthMiddleBarrel = 64.5f; + const float rMaxMiddleBarrelDisk = rMinMiddleServices; auto orientation = Orientation::kASide; float diskCircumference = rMaxMiddleBarrelDisk * 3.14; // Use only half circumference - double zCur = zLengthMiddleBarrel; + double zCur = zMiddleServicesBarrel; double dZ = siO2FiberAreaB / diskCircumference / 2.; TGeoTube* middleBarrelConnDiskSIO2 = new TGeoTube("TRK_MIDBARCONN_DISK_FIBER_SIO2sh", rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, dZ); TGeoVolume* middleBarrelConnDiskSIO2Volume = new TGeoVolume("TRK_MIDBARCONN_DISK_FIBER_SIO2", middleBarrelConnDiskSIO2, medSiO2); - middleBarrelConnDiskSIO2Volume->SetLineColor(kGray); + middleBarrelConnDiskSIO2Volume->SetLineColor(kOrange - 9); auto* rot = new TGeoRotation("", 0, 0, 180); // Why this? auto* combiTransSIO2 = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), rot); @@ -594,7 +654,7 @@ void TRKServices::createMLServicesPeacock(TGeoVolume* motherVolume) dZ = peFiberAreaB / diskCircumference / 2.; TGeoTube* middleBarrelConnDiskPE = new TGeoTube("TRK_MIDBARCONN_DISK_FIBER_PEsh", rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, dZ); TGeoVolume* middleBarrelConnDiskPEVolume = new TGeoVolume("TRK_MIDBARCONN_DISK_FIBER_PE", middleBarrelConnDiskPE, medPE); - middleBarrelConnDiskPEVolume->SetLineColor(kGray); + middleBarrelConnDiskPEVolume->SetLineColor(kOrange - 9); auto* combiTransPE = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), rot); motherVolume->AddNode(middleBarrelConnDiskSIO2Volume, 1, combiTransSIO2); @@ -604,14 +664,14 @@ void TRKServices::createMLServicesPeacock(TGeoVolume* motherVolume) dZ = cuPowerAreaB / diskCircumference / 2.; TGeoTube* middleBarrelConnDiskCu = new TGeoTube("TRK_MIDBARCONN_DISK_POWER_CUsh", rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, dZ); TGeoVolume* middleBarrelConnDiskCuVolume = new TGeoVolume("TRK_MIDBARCONN_DISK_POWER_CU", middleBarrelConnDiskCu, medCu); - middleBarrelConnDiskCuVolume->SetLineColor(kGray); + middleBarrelConnDiskCuVolume->SetLineColor(kOrange - 9); auto* combiTransCu = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), rot); zCur += 2. * dZ; dZ = pePowerAreaB / diskCircumference / 2.; TGeoTube* middleBarrelConnDiskPEPower = new TGeoTube("TRK_MIDBARCONN_DISK_POWER_PEsh", rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, dZ); TGeoVolume* middleBarrelConnDiskPEPowerVolume = new TGeoVolume("TRK_MIDBARCONN_DISK_POWER_PE", middleBarrelConnDiskPEPower, medPE); - middleBarrelConnDiskPEPowerVolume->SetLineColor(kGray); + middleBarrelConnDiskPEPowerVolume->SetLineColor(kOrange - 9); auto* combiTransPEPower = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), rot); motherVolume->AddNode(middleBarrelConnDiskCuVolume, 1, combiTransCu); motherVolume->AddNode(middleBarrelConnDiskPEPowerVolume, 1, combiTransPEPower); @@ -619,7 +679,7 @@ void TRKServices::createMLServicesPeacock(TGeoVolume* motherVolume) for (auto& orientation : {Orientation::kASide, Orientation::kCSide}) { for (int iSide = 0; iSide < 2; iSide++) { // left/right or top/bottom float refAngle = 0; - string orLabel("A"); + std::string orLabel("A"); if (orientation == Orientation::kCSide) { orLabel = "C"; refAngle = 90; @@ -628,31 +688,31 @@ void TRKServices::createMLServicesPeacock(TGeoVolume* motherVolume) // create data fiber volumes double rCur = rMinMiddleServices; double dR = siO2FiberAreaD / (3.14 * rCur); - TGeoTubeSeg* middleDiskFiberSIO2 = new TGeoTubeSeg(Form("TRK_MLD_FIBER_SIO2sh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthCylinderMiddleServices, -45, 45); + TGeoTubeSeg* middleDiskFiberSIO2 = new TGeoTubeSeg(Form("TRK_MLD_FIBER_SIO2sh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthCylinderMiddleServicesDisk / 2, -45, 45); TGeoVolume* middleDiskFiberSIO2Volume = new TGeoVolume(Form("TRK_MLD_FIBER_SIO2_%s%d", orLabel.c_str(), iSide), middleDiskFiberSIO2, medSiO2); - middleDiskFiberSIO2Volume->SetLineColor(kGray); + middleDiskFiberSIO2Volume->SetLineColor(kOrange + 1); rCur += dR; dR = peFiberAreaD / (3.14 * rCur); - TGeoTubeSeg* middleDiskFiberPE = new TGeoTubeSeg(Form("TRK_MLD_FIBER_PEsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthCylinderMiddleServices, -45, 45); + TGeoTubeSeg* middleDiskFiberPE = new TGeoTubeSeg(Form("TRK_MLD_FIBER_PEsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthCylinderMiddleServicesDisk / 2, -45, 45); TGeoVolume* middleDiskFiberPEVolume = new TGeoVolume(Form("TRK_MLD_FIBER_PE_%s%d", orLabel.c_str(), iSide), middleDiskFiberPE, medPE); - middleDiskFiberPEVolume->SetLineColor(kGray); - auto* combiTrans = new TGeoCombiTrans(0, 0, (int)orientation * (zLengthMiddleServices - zLengthCylinderMiddleServices), new TGeoRotation("", refAngle + iSide * 180., 0, 0)); + middleDiskFiberPEVolume->SetLineColor(kOrange + 1); + auto* combiTrans = new TGeoCombiTrans(0, 0, (int)orientation * (zMiddleServicesBarrelFwdConnection - zLengthCylinderMiddleServicesDisk / 2), new TGeoRotation("", refAngle + iSide * 180., 0, 0)); motherVolume->AddNode(middleDiskFiberSIO2Volume, 1, combiTrans); motherVolume->AddNode(middleDiskFiberPEVolume, 1, combiTrans); // Create powerlines rCur += dR; dR = cuPowerAreaD / (3.14 * rCur); - TGeoTubeSeg* middleDiskPowerCu = new TGeoTubeSeg(Form("TRK_MLD_POWER_CUsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthCylinderMiddleServices, -45, 45); + TGeoTubeSeg* middleDiskPowerCu = new TGeoTubeSeg(Form("TRK_MLD_POWER_CUsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthCylinderMiddleServicesDisk / 2, -45, 45); TGeoVolume* middleDiskPowerCuVolume = new TGeoVolume(Form("TRK_MLD_POWER_CU_%s%d", orLabel.c_str(), iSide), middleDiskPowerCu, medCu); - middleDiskPowerCuVolume->SetLineColor(kGray); + middleDiskPowerCuVolume->SetLineColor(kOrange + 1); rCur += dR; dR = pePowerAreaD / (3.14 * rCur); - TGeoTubeSeg* middleDiskPowerPE = new TGeoTubeSeg(Form("TRK_MLD_POWER_PEsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthCylinderMiddleServices, -45, 45); + TGeoTubeSeg* middleDiskPowerPE = new TGeoTubeSeg(Form("TRK_MLD_POWER_PEsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthCylinderMiddleServicesDisk / 2, -45, 45); TGeoVolume* middleDiskPowerPEVolume = new TGeoVolume(Form("TRK_MLD_POWER_PE_%s%d", orLabel.c_str(), iSide), middleDiskPowerPE, medPE); - middleDiskPowerPEVolume->SetLineColor(kGray); + middleDiskPowerPEVolume->SetLineColor(kOrange + 1); motherVolume->AddNode(middleDiskPowerCuVolume, 1, combiTrans); motherVolume->AddNode(middleDiskPowerPEVolume, 1, combiTrans); @@ -662,31 +722,31 @@ void TRKServices::createMLServicesPeacock(TGeoVolume* motherVolume) // create data fiber volumes rCur += dR; dR = siO2FiberAreaB / (3.14 * rCur); - TGeoTubeSeg* middleBarrelFiberSIO2 = new TGeoTubeSeg(Form("TRK_MLB_FIBER_SIO2sh_A%d", iSide), rCur, rCur + dR, zLengthCylinderMiddleServices, -45, 45); + TGeoTubeSeg* middleBarrelFiberSIO2 = new TGeoTubeSeg(Form("TRK_MLB_FIBER_SIO2sh_A%d", iSide), rCur, rCur + dR, zLengthCylinderMiddleServicesBarrel / 2, -45, 45); TGeoVolume* middleBarrelFiberSIO2Volume = new TGeoVolume(Form("TRK_MLB_FIBER_SIO2_A%d", iSide), middleBarrelFiberSIO2, medSiO2); - middleBarrelFiberSIO2Volume->SetLineColor(kGray); + middleBarrelFiberSIO2Volume->SetLineColor(kOrange - 9); rCur += dR; dR = peFiberAreaB / (3.14 * rCur); - TGeoTubeSeg* middleBarrelFiberPE = new TGeoTubeSeg(Form("TRK_MLB_FIBER_PEsh_A%d", iSide), rCur, rCur + dR, zLengthCylinderMiddleServices, -45, 45); + TGeoTubeSeg* middleBarrelFiberPE = new TGeoTubeSeg(Form("TRK_MLB_FIBER_PEsh_A%d", iSide), rCur, rCur + dR, zLengthCylinderMiddleServicesBarrel / 2, -45, 45); TGeoVolume* middleBarrelFiberPEVolume = new TGeoVolume(Form("TRK_MLB_FIBER_PE_A%d", iSide), middleBarrelFiberPE, medPE); - middleBarrelFiberPEVolume->SetLineColor(kGray); - auto* combiTrans = new TGeoCombiTrans(0, 0, (int)orientation * (zLengthMiddleServices - zLengthCylinderMiddleServices), new TGeoRotation(nullptr, refAngle + iSide * 180., 0, 0)); + middleBarrelFiberPEVolume->SetLineColor(kOrange - 9); + auto* combiTrans = new TGeoCombiTrans(0, 0, (int)orientation * (zMiddleServicesBarrelFwdConnection - zLengthCylinderMiddleServicesBarrel / 2), new TGeoRotation(nullptr, refAngle + iSide * 180., 0, 0)); motherVolume->AddNode(middleBarrelFiberSIO2Volume, 1, combiTrans); motherVolume->AddNode(middleBarrelFiberPEVolume, 1, combiTrans); // Create powerlines rCur += dR; dR = cuPowerAreaB / (3.14 * rCur); - TGeoTubeSeg* middleBarrelPowerCu = new TGeoTubeSeg(Form("TRK_MLB_POWER_CUsh_A%d", iSide), rCur, rCur + dR, zLengthCylinderMiddleServices, -45, 45); + TGeoTubeSeg* middleBarrelPowerCu = new TGeoTubeSeg(Form("TRK_MLB_POWER_CUsh_A%d", iSide), rCur, rCur + dR, zLengthCylinderMiddleServicesBarrel / 2, -45, 45); TGeoVolume* middleBarrelPowerCuVolume = new TGeoVolume(Form("TRK_MLB_POWER_CU_A%d", iSide), middleBarrelPowerCu, medCu); - middleBarrelPowerCuVolume->SetLineColor(kGray); + middleBarrelPowerCuVolume->SetLineColor(kOrange - 9); rCur += dR; dR = pePowerAreaB / (3.14 * rCur); - TGeoTubeSeg* middleBarrelPowerPE = new TGeoTubeSeg(Form("TRK_MLB_POWER_PEsh_A%d", iSide), rCur, rCur + dR, zLengthCylinderMiddleServices, -45, 45); + TGeoTubeSeg* middleBarrelPowerPE = new TGeoTubeSeg(Form("TRK_MLB_POWER_PEsh_A%d", iSide), rCur, rCur + dR, zLengthCylinderMiddleServicesBarrel / 2, -45, 45); TGeoVolume* middleBarrelPowerPEVolume = new TGeoVolume(Form("TRK_MLB_POWER_PE_A%d", iSide), middleBarrelPowerPE, medPE); - middleBarrelPowerPEVolume->SetLineColor(kGray); + middleBarrelPowerPEVolume->SetLineColor(kOrange - 9); motherVolume->AddNode(middleBarrelPowerCuVolume, 1, combiTrans); motherVolume->AddNode(middleBarrelPowerPEVolume, 1, combiTrans); @@ -700,10 +760,11 @@ void TRKServices::createMLServicesPeacock(TGeoVolume* motherVolume) // A side: barrel + disk services // C side: only disk services float rMaxMiddleServicesBarFwd = 74.5f; // TODO: add thickness of service barrels + float rMinMiddleBarrel = rMinMiddleServices; // min radius of the service disk diskCircumference = rMaxMiddleServicesBarFwd * 3.14; // Only half of the area is used for (auto& orientation : {Orientation::kASide, Orientation::kCSide}) { float refAngle = 0; - string orLabel("A"); + std::string orLabel("A"); if (orientation == Orientation::kCSide) { refAngle = 90; orLabel = "C"; @@ -711,7 +772,7 @@ void TRKServices::createMLServicesPeacock(TGeoVolume* motherVolume) double totalThickness = 0; for (int iSide = 0; iSide < 2; iSide++) { // Create fibers - double zCur = zLengthMiddleServices; // Change to f + double zCur = zMiddleServicesBarrelFwdConnection; // Change to f double dZ = siO2FiberAreaD / diskCircumference / 2.; totalThickness += 2 * dZ; if (orientation == Orientation::kASide) { @@ -730,8 +791,8 @@ void TRKServices::createMLServicesPeacock(TGeoVolume* motherVolume) } TGeoTubeSeg* middleBarFwdFiberPE = new TGeoTubeSeg(Form("TRK_MIDBARFWD_FIBER_PEsh_%s%d", orLabel.c_str(), iSide), rMinMiddleBarrel, rMaxMiddleServicesBarFwd, dZ, -45, 45); TGeoVolume* middleBarFwdFiberPEVolume = new TGeoVolume(Form("TRK_MIDBARFWD_FIBER_PE_%s%d", orLabel.c_str(), iSide), middleBarFwdFiberPE, medPE); - middleBarFwdFiberSIO2Volume->SetLineColor(kGray); - middleBarFwdFiberPEVolume->SetLineColor(kGray); + middleBarFwdFiberSIO2Volume->SetLineColor(kOrange + 1); + middleBarFwdFiberPEVolume->SetLineColor(kOrange + 1); auto* combiTransPE = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), rot); motherVolume->AddNode(middleBarFwdFiberSIO2Volume, 1, combiTransSIO2); motherVolume->AddNode(middleBarFwdFiberPEVolume, 1, combiTransPE); @@ -755,8 +816,8 @@ void TRKServices::createMLServicesPeacock(TGeoVolume* motherVolume) } TGeoTubeSeg* middleBarFwdPowerPE = new TGeoTubeSeg(Form("TRK_MIDBARFWD_POWER_PEsh_%s%d", orLabel.c_str(), iSide), rMinMiddleBarrel, rMaxMiddleServicesBarFwd, dZ, -45, 45); TGeoVolume* middleBarFwdPowerPEVolume = new TGeoVolume(Form("TRK_MIDBARFWD_POWER_PE_%s%d", orLabel.c_str(), iSide), middleBarFwdPowerPE, medPE); - middleBarFwdPowerCuVolume->SetLineColor(kGray); - middleBarFwdPowerPEVolume->SetLineColor(kGray); + middleBarFwdPowerCuVolume->SetLineColor(kOrange + 1); + middleBarFwdPowerPEVolume->SetLineColor(kOrange + 1); auto* combiTransPEPower = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), rot); motherVolume->AddNode(middleBarFwdPowerCuVolume, 1, combiTransCu); motherVolume->AddNode(middleBarFwdPowerPEVolume, 1, combiTransPEPower); @@ -765,13 +826,13 @@ void TRKServices::createMLServicesPeacock(TGeoVolume* motherVolume) } // Forward part - float zLengthMiddleServicesFwd = 350.f - (143.f + totalThickness); + float zLengthMiddleServicesFwd = 350.f - (zMiddleServicesBarrelFwdConnection + totalThickness); + float rMinMiddleServicesFwd = 74.5f; // 74.5cm for (int iSide = 0; iSide < 2; iSide++) { // Create fibers - float rMinMiddleServicesFwd = 74.5f; // 74.5cm - float translation = (int)orientation * (143.f + totalThickness + zLengthMiddleServicesFwd / 2); + float translation = (int)orientation * (zMiddleServicesBarrelFwdConnection + totalThickness + zLengthMiddleServicesFwd / 2); double rCur = rMinMiddleServicesFwd; double dR = siO2FiberAreaD / (3.14 * rCur); @@ -780,7 +841,7 @@ void TRKServices::createMLServicesPeacock(TGeoVolume* motherVolume) } TGeoTubeSeg* middleFwdFiberSIO2 = new TGeoTubeSeg(Form("TRK_MIDFWD_FIBER_SIO2sh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthMiddleServicesFwd / 2, -45, 45); TGeoVolume* middleFwdFiberSIO2Volume = new TGeoVolume(Form("TRK_MIDFWD_FIBER_SIO2_%s%d", orLabel.c_str(), iSide), middleFwdFiberSIO2, medSiO2); - middleFwdFiberSIO2Volume->SetLineColor(kGray); + middleFwdFiberSIO2Volume->SetLineColor(kOrange + 1); rCur += dR; dR = peFiberAreaD / (3.14 * rCur); @@ -789,7 +850,7 @@ void TRKServices::createMLServicesPeacock(TGeoVolume* motherVolume) } TGeoTubeSeg* middleFwdFiberPE = new TGeoTubeSeg(Form("TRK_MIDFWD_FIBER_PEsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthMiddleServicesFwd / 2, -45, 45); TGeoVolume* middleFwdFiberPEVolume = new TGeoVolume(Form("TRK_MIDFWD_FIBER_PE_%s%d", orLabel.c_str(), iSide), middleFwdFiberPE, medPE); - middleFwdFiberPEVolume->SetLineColor(kGray); + middleFwdFiberPEVolume->SetLineColor(kOrange + 1); auto* rot = new TGeoRotation("", refAngle + iSide * 180., 0, 0.); auto* combiTrans = new TGeoCombiTrans(0, 0, translation, rot); @@ -804,7 +865,7 @@ void TRKServices::createMLServicesPeacock(TGeoVolume* motherVolume) } TGeoTubeSeg* middleFwdPowerCu = new TGeoTubeSeg(Form("TRK_MIDFWD_POWER_CUsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthMiddleServicesFwd / 2, -45, 45); TGeoVolume* middleFwdPowerCuVolume = new TGeoVolume(Form("TRK_MIDFWD_POWER_CU_%s%d", orLabel.c_str(), iSide), middleFwdPowerCu, medCu); - middleFwdPowerCuVolume->SetLineColor(kGray); + middleFwdPowerCuVolume->SetLineColor(kOrange + 1); rCur += dR; dR = pePowerAreaD / (3.14 * rCur); @@ -813,7 +874,7 @@ void TRKServices::createMLServicesPeacock(TGeoVolume* motherVolume) } TGeoTubeSeg* middleFwdPowerPE = new TGeoTubeSeg(Form("TRK_MIDFWD_POWER_PEsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthMiddleServicesFwd / 2, -45, 45); TGeoVolume* middleFwdPowerPEVolume = new TGeoVolume(Form("TRK_MIDFWD_POWER_PE_%s%d", orLabel.c_str(), iSide), middleFwdPowerPE, medPE); - middleFwdPowerPEVolume->SetLineColor(kGray); + middleFwdPowerPEVolume->SetLineColor(kOrange + 1); motherVolume->AddNode(middleFwdPowerCuVolume, 1, combiTrans); motherVolume->AddNode(middleFwdPowerPEVolume, 1, combiTrans); @@ -858,8 +919,20 @@ void TRKServices::createOTServicesPeacock(TGeoVolume* motherVolume) float cuPowerAreaB = OTBarrelnPower * mPowerBundleArea * mPowerBundleComposition[0]; float pePowerAreaB = OTBarrelnPower * mPowerBundleArea * mPowerBundleComposition[1]; - float rMinOuterServices = 68.5f; // 68.5cm - float zLengthOuterServices = 201.f; // 201cm + // geometry of service "disk" for OT barrel + double rMinOTbarrelServices = 45.0; // cm, radius of first OT barrel layer + double rMaxOTbarrelServices = 78.0; // cm, radius of last OT barrel layer + double zOTbarrelServices = 132.0; // cm, approximate position of OT services in z + + // geometry of service "tubes" for OT barrel + float rMinOuterBarrelTubeServices = rMaxOTbarrelServices; // cm, IA, May 11, 2026: temporary radius (?) + float zStartOuterBarrelTubeServices = zOTbarrelServices + 0.8f; // cm, IA, May 11, 2026: start "OT service tubes" close in z to the "OT service disks" + float zLengthOuterBarrelTubeServices = 215.f; // cm, IA, May 11, 2026: temporary length (?) + + // geometry of service "tubes" for OT disks + float rMinOuterDiskServices = 70.5f; // cm + float zStartOuterDiskServices = 149.f; // cm + float zLengthOuterDiskServices = 201.f; // cm // Carbon Fiber Cylinder support for the middle tracker float rMinOuterCarbonSupport = 82.0f; // TODO: get more precise location @@ -872,80 +945,112 @@ void TRKServices::createOTServicesPeacock(TGeoVolume* motherVolume) motherVolume->AddNode(outerBarrelCarbonSupportVolume, 1, nullptr); for (auto& orientation : {Orientation::kASide, Orientation::kCSide}) { - string orLabel = "A"; + std::string orLabel = "A"; float refAngle = 0; if (orientation == Orientation::kCSide) { orLabel = "C"; refAngle = 90; } // TODO: add cables/connections at ends of OT barrels - // Set rMin, rMax and dZ + double zCur = zOTbarrelServices; - double rMin = 45.0; - double rMax = rMinOuterServices; - double zCur = 145.0; - double dZ = siO2FiberAreaB / (4 * 3.14 * rMax); - TGeoTube* outerBarrelFiberSIO2 = new TGeoTube(Form("TRK_OUTERBARREL_FIBER_SIO2sh_%s", orLabel.c_str()), rMin, rMax, dZ); + double dZ = siO2FiberAreaB / (4 * 3.14 * rMaxOTbarrelServices); + TGeoTube* outerBarrelFiberSIO2 = new TGeoTube(Form("TRK_OUTERBARREL_FIBER_SIO2sh_%s", orLabel.c_str()), rMinOTbarrelServices, rMaxOTbarrelServices, dZ); TGeoVolume* outerBarrelFiberSIO2Volume = new TGeoVolume(Form("TRK_OUTERBARREL_FIBER_SIO2_%s", orLabel.c_str()), outerBarrelFiberSIO2, medSiO2); - outerBarrelFiberSIO2Volume->SetLineColor(kGray); + outerBarrelFiberSIO2Volume->SetLineColor(kAzure + 6); auto* combiTrans = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), nullptr); motherVolume->AddNode(outerBarrelFiberSIO2Volume, 1, combiTrans); zCur += 2 * dZ; - dZ = peFiberAreaB / (4 * 3.14 * rMax); - TGeoTube* outerBarrelFiberPE = new TGeoTube(Form("TRK_OUTERBARREL_FIBER_PEsh_%s", orLabel.c_str()), rMin, rMax, dZ); + dZ = peFiberAreaB / (4 * 3.14 * rMaxOTbarrelServices); + TGeoTube* outerBarrelFiberPE = new TGeoTube(Form("TRK_OUTERBARREL_FIBER_PEsh_%s", orLabel.c_str()), rMinOTbarrelServices, rMaxOTbarrelServices, dZ); TGeoVolume* outerBarrelFiberPEVolume = new TGeoVolume(Form("TRK_OUTERBARREL_FIBER_PE_%s", orLabel.c_str()), outerBarrelFiberPE, medPE); - outerBarrelFiberPEVolume->SetLineColor(kGray); + outerBarrelFiberPEVolume->SetLineColor(kAzure + 6); combiTrans = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), nullptr); motherVolume->AddNode(outerBarrelFiberPEVolume, 1, combiTrans); zCur += 2 * dZ; - dZ = cuPowerAreaB / (4 * 3.14 * rMax); - TGeoTube* outerBarrelPowerCu = new TGeoTube(Form("TRK_OUTERBARREL_POWER_CUsh_%s", orLabel.c_str()), rMin, rMax, dZ); + dZ = cuPowerAreaB / (4 * 3.14 * rMaxOTbarrelServices); + TGeoTube* outerBarrelPowerCu = new TGeoTube(Form("TRK_OUTERBARREL_POWER_CUsh_%s", orLabel.c_str()), rMinOTbarrelServices, rMaxOTbarrelServices, dZ); TGeoVolume* outerBarrelPowerCuVolume = new TGeoVolume(Form("TRK_OUTERBARREL_POWER_CU_%s", orLabel.c_str()), outerBarrelPowerCu, medCu); - outerBarrelFiberSIO2Volume->SetLineColor(kGray); + outerBarrelPowerCuVolume->SetLineColor(kAzure + 6); combiTrans = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), nullptr); motherVolume->AddNode(outerBarrelPowerCuVolume, 1, combiTrans); zCur += 2 * dZ; - dZ = pePowerAreaB / (4 * 3.14 * rMax); - TGeoTube* outerBarrelPowerPE = new TGeoTube(Form("TRK_OUTERBARREL_POWER_PEsh_%s", orLabel.c_str()), rMin, rMax, dZ); + dZ = pePowerAreaB / (4 * 3.14 * rMaxOTbarrelServices); + TGeoTube* outerBarrelPowerPE = new TGeoTube(Form("TRK_OUTERBARREL_POWER_PEsh_%s", orLabel.c_str()), rMinOTbarrelServices, rMaxOTbarrelServices, dZ); TGeoVolume* outerBarrelPowerPEVolume = new TGeoVolume(Form("TRK_OUTERBARREL_POWER_PE_%s", orLabel.c_str()), outerBarrelPowerPE, medPE); - outerBarrelPowerPEVolume->SetLineColor(kGray); + outerBarrelPowerPEVolume->SetLineColor(kAzure + 6); combiTrans = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), nullptr); motherVolume->AddNode(outerBarrelPowerPEVolume, 1, combiTrans); for (int iSide = 0; iSide < 2; iSide++) { - // Create fibers - double rCur = rMinOuterServices; - double dR = (siO2FiberAreaD + siO2FiberAreaB) / (3.14 * rCur); - TGeoTubeSeg* outerDisksFiberSIO2 = new TGeoTubeSeg(Form("TRK_OUTERDISKS_FIBER_SIO2sh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthOuterServices / 2, -45, 45); + // #### OT barrel services, implemented as tubes + // Create fibers for service barrel tubes + double rCur = rMinOuterBarrelTubeServices; // set starting radius for barrel service tube + double dR = siO2FiberAreaB / (3.14 * rCur); + TGeoTubeSeg* outerBarrelTubeFiberSIO2 = new TGeoTubeSeg(Form("TRK_OUTERBARREL_TUBE_FIBER_SIO2sh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthOuterBarrelTubeServices / 2, -45, 45); + TGeoVolume* outerBarrelTubeFiberSIO2Volume = new TGeoVolume(Form("TRK_OUTERBARREL_TUBE_FIBER_SIO2_%s%d", orLabel.c_str(), iSide), outerBarrelTubeFiberSIO2, medSiO2); + outerBarrelTubeFiberSIO2Volume->SetLineColor(kAzure + 6); + + rCur += dR; + dR = peFiberAreaB / (3.14 * rCur); + TGeoTubeSeg* outerBarrelTubeFiberPE = new TGeoTubeSeg(Form("TRK_OUTERBARREL_TUBE_FIBER_PEsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthOuterBarrelTubeServices / 2, -45, 45); + TGeoVolume* outerBarrelTubeFiberPEVolume = new TGeoVolume(Form("TRK_OUTERBARREL_TUBE_FIBER_PE_%s%d", orLabel.c_str(), iSide), outerBarrelTubeFiberPE, medPE); + outerBarrelTubeFiberPEVolume->SetLineColor(kAzure + 6); + + float translation = (int)orientation * (zStartOuterBarrelTubeServices + zLengthOuterBarrelTubeServices / 2); + auto* combiTrans = new TGeoCombiTrans(0, 0, translation, new TGeoRotation("", refAngle + iSide * 180., 0, 0)); + motherVolume->AddNode(outerBarrelTubeFiberSIO2Volume, 1, combiTrans); + motherVolume->AddNode(outerBarrelTubeFiberPEVolume, 1, combiTrans); + + // Create power lines for service barrel tubes + rCur += dR; + dR = cuPowerAreaB / (3.14 * rCur); + TGeoTubeSeg* outerBarrelTubePowerCu = new TGeoTubeSeg(Form("TRK_OUTERBARREL_TUBE_POWER_CUsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthOuterBarrelTubeServices / 2, -45, 45); + TGeoVolume* outerBarrelTubePowerCuVolume = new TGeoVolume(Form("TRK_OUTERBARREL_TUBE_POWER_CU_%s%d", orLabel.c_str(), iSide), outerBarrelTubePowerCu, medCu); + outerBarrelTubePowerCuVolume->SetLineColor(kAzure + 6); + + rCur += dR; + dR = pePowerAreaB / (3.14 * rCur); + TGeoTubeSeg* outerBarrelTubePowerPE = new TGeoTubeSeg(Form("TRK_OUTERBARREL_TUBE_POWER_PEsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthOuterBarrelTubeServices / 2, -45, 45); + TGeoVolume* outerBarrelTubePowerPEVolume = new TGeoVolume(Form("TRK_OUTERBARREL_TUBE_POWER_PE_%s%d", orLabel.c_str(), iSide), outerBarrelTubePowerPE, medPE); + outerBarrelTubePowerPEVolume->SetLineColor(kAzure + 6); + motherVolume->AddNode(outerBarrelTubePowerCuVolume, 1, combiTrans); + motherVolume->AddNode(outerBarrelTubePowerPEVolume, 1, combiTrans); + + // #### OT disk services, implemented as tubes + // Create fibers for disks + rCur = rMinOuterDiskServices; // set starting radius for disk service tube + dR = siO2FiberAreaD / (3.14 * rCur); + TGeoTubeSeg* outerDisksFiberSIO2 = new TGeoTubeSeg(Form("TRK_OUTERDISKS_FIBER_SIO2sh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthOuterDiskServices / 2, -45, 45); TGeoVolume* outerDisksFiberSIO2Volume = new TGeoVolume(Form("TRK_OUTERDISKS_FIBER_SIO2_%s%d", orLabel.c_str(), iSide), outerDisksFiberSIO2, medSiO2); - outerDisksFiberSIO2Volume->SetLineColor(kGray); + outerDisksFiberSIO2Volume->SetLineColor(kMagenta); rCur += dR; - dR = (peFiberAreaD + peFiberAreaB) / (3.14 * rCur); - TGeoTubeSeg* outerDisksFiberPE = new TGeoTubeSeg(Form("TRK_OUTERDISKS_FIBER_PEsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthOuterServices / 2, -45, 45); + dR = peFiberAreaD / (3.14 * rCur); + TGeoTubeSeg* outerDisksFiberPE = new TGeoTubeSeg(Form("TRK_OUTERDISKS_FIBER_PEsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthOuterDiskServices / 2, -45, 45); TGeoVolume* outerDisksFiberPEVolume = new TGeoVolume(Form("TRK_OUTERDISKS_FIBER_PE_%s%d", orLabel.c_str(), iSide), outerDisksFiberPE, medPE); - outerDisksFiberPEVolume->SetLineColor(kGray); + outerDisksFiberPEVolume->SetLineColor(kMagenta); - float translation = (int)orientation * (149.f + zLengthOuterServices / 2); // ±149cm - auto* combiTrans = new TGeoCombiTrans(0, 0, translation, new TGeoRotation("", refAngle + iSide * 180., 0, 0)); + translation = (int)orientation * (zStartOuterDiskServices + zLengthOuterDiskServices / 2); + combiTrans = new TGeoCombiTrans(0, 0, translation, new TGeoRotation("", refAngle + iSide * 180., 0, 0)); motherVolume->AddNode(outerDisksFiberSIO2Volume, 1, combiTrans); motherVolume->AddNode(outerDisksFiberPEVolume, 1, combiTrans); - // Create power lines + // Create power lines for disks rCur += dR; - dR = (cuPowerAreaD + cuPowerAreaB) / (3.14 * rCur); - TGeoTubeSeg* outerDisksPowerCu = new TGeoTubeSeg(Form("TRK_OUTERDISKS_POWER_CUsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthOuterServices / 2, -45, 45); + dR = cuPowerAreaD / (3.14 * rCur); + TGeoTubeSeg* outerDisksPowerCu = new TGeoTubeSeg(Form("TRK_OUTERDISKS_POWER_CUsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthOuterDiskServices / 2, -45, 45); TGeoVolume* outerDisksPowerCuVolume = new TGeoVolume(Form("TRK_OUTERDISKS_POWER_CU_%s%d", orLabel.c_str(), iSide), outerDisksPowerCu, medCu); - outerDisksPowerCuVolume->SetLineColor(kGray); + outerDisksPowerCuVolume->SetLineColor(kMagenta + 1); rCur += dR; - dR = (pePowerAreaD + pePowerAreaB) / (3.14 * rCur); - TGeoTubeSeg* outerDisksPowerPE = new TGeoTubeSeg(Form("TRK_OUTERDISKS_POWER_PEsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthOuterServices / 2, -45, 45); + dR = pePowerAreaD / (3.14 * rCur); + TGeoTubeSeg* outerDisksPowerPE = new TGeoTubeSeg(Form("TRK_OUTERDISKS_POWER_PEsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthOuterDiskServices / 2, -45, 45); TGeoVolume* outerDisksPowerPEVolume = new TGeoVolume(Form("TRK_OUTERDISKS_POWER_PE_%s%d", orLabel.c_str(), iSide), outerDisksPowerPE, medPE); - outerDisksPowerPEVolume->SetLineColor(kGray); + outerDisksPowerPEVolume->SetLineColor(kMagenta + 1); motherVolume->AddNode(outerDisksPowerCuVolume, 1, combiTrans); motherVolume->AddNode(outerDisksPowerPEVolume, 1, combiTrans); diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/workflow/CMakeLists.txt index 42402fe6b62dc..e3309d78f47ea 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/CMakeLists.txt @@ -15,8 +15,6 @@ o2_add_library(TRKWorkflow src/DigitWriterSpec.cxx src/ClustererSpec.cxx src/ClusterWriterSpec.cxx - src/TrackerSpec.cxx - src/TrackWriterSpec.cxx src/RecoWorkflow.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::GPUWorkflow @@ -35,5 +33,4 @@ o2_add_executable(reco-workflow COMPONENT_NAME alice3-trk PUBLIC_LINK_LIBRARIES O2::TRKWorkflow O2::TRKSimulation - O2::TRKReconstruction - O2::ITStracking) + O2::TRKReconstruction) diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/README.md b/Detectors/Upgrades/ALICE3/TRK/workflow/README.md index 1cdce15b72726..2afb599319217 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/README.md +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/README.md @@ -1,130 +1,11 @@ # TRK Reconstruction Workflow -This document describes how to run the TRK (ALICE 3 Tracker) reconstruction workflow and provides examples of configuration files. +This workflow handles TRK-local reconstruction devices such as digit reading and clusterization. -## Overview - -The TRK reconstruction workflow performs track reconstruction from simulated hits, producing reconstructed tracks with MC truth labels. The workflow currently supports the track reconstruction from hits using the Cellular Automaton (CA) algorithm. The ouput is stored to a ROOT file for offline analysis (example of QA macro provided in `macros/test/CheckTracksCA.C`). - -## Quick Start - -### Basic Command +## Basic Command ```bash -o2-alice3-trk-reco-workflow --tracking-from-hits-config config_tracker.json -b -``` - -### Command Line Options - -- `--tracking-from-hits-config `: Path to tracking configuration JSON file (required) -- `-b`: Batch mode (no GUI) -- `--disable-root-output`: Skip writing tracks to ROOT file -- `--help`: Show all available options - -## Configuration File - -The tracking configuration is provided via a JSON file that specifies: -1. Input file paths -2. Geometry parameters (magnetic field, detector pitch) -3. Tracking algorithm parameters (can specify multiple iterations) - -### Example Configuration (`config_tracker.json`) - -```json -{ - "inputfiles": { - "hits": "o2sim_HitsTRK.root", - "geometry": "o2sim_geometry.root", - "mcHeader": "o2sim_MCHeader.root", - "kinematics": "o2sim_Kine.root" - }, - "geometry": { - "bz": 5.0, - "pitch": [0.001, 0.001, 0.001, 0.001, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004] - }, - "trackingparams": [{ - "NLayers": 11, - "DeltaROF": 0, - "LayerZ": [25.1, 25.1, 25.1, 64.2, 64.2, 64.2, 64.2, 64.2, 128.5, 128.5, 128.5], - "LayerRadii": [0.5, 1.2, 2.5, 7.05, 9.05, 12.05, 20.05, 30.05, 45.05, 60.5, 80.05], - "LayerxX0": [0.001, 0.001, 0.001, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01], - "LayerResolution": [0.0003, 0.0003, 0.0003, 0.0003, 0.0012, 0.0012, 0.0012, 0.0012, 0.0012, 0.0012, 0.0012], - "SystErrorY2": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], - "SystErrorZ2": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], - "ZBins": 256, - "PhiBins": 128, - "nROFsPerIterations": -1, - "UseDiamond": false, - "Diamond": [0.0, 0.0, 0.0], - "AllowSharingFirstCluster": false, - "ClusterSharing": 0, - "MinTrackLength": 7, - "NSigmaCut": 10, - "PVres": 0.01, - "TrackletMinPt": 0.1, - "TrackletsPerClusterLimit": 2.0, - "CellDeltaTanLambdaSigma": 0.007, - "CellsPerClusterLimit": 2.0, - "MaxChi2ClusterAttachment": 60.0, - "MaxChi2NDF": 30.0, - "ReseedIfShorter": 6, - "MinPt": [0.0, 0.0, 0.0, 0.0], - "StartLayerMask": 4095, - "RepeatRefitOut": false, - "ShiftRefToCluster": true, - "FindShortTracks": false, - "PerPrimaryVertexProcessing": false, - "SaveTimeBenchmarks": false, - "DoUPCIteration": false, - "FataliseUponFailure": true, - "UseTrackFollower": true, - "UseTrackFollowerTop": false, - "UseTrackFollowerBot": false, - "UseTrackFollowerMix": true, - "TrackFollowerNSigmaCutZ": 1.0, - "TrackFollowerNSigmaCutPhi": 1.0, - "createArtefactLabels": false, - "PrintMemory": false, - "DropTFUponFailure": false - }] -} +o2-alice3-trk-reco-workflow -b ``` -Note that the `trackingparams` field can contain multiple sets of parameters for different iterations of the tracking algorithm. The example above shows a single iteration with 11 layers and it is **not** optimized. - -## Complete Workflow Example -### 1. Run Simulation - -First, generate simulation data: - -```bash -o2-sim-serial-run5 -n 200 -g pythia8hi -m TRK --configKeyValues "Diamond.width[0]=0.01;Diamond.width[1]=0.01;Diamond.width[2]=5;TRKBase.layoutML=kTurboStaves;TRKBase.layoutOT=kStaggered;" -``` - -This produces, among other files: -- `o2sim_HitsTRK.root` -- `o2sim_geometry.root` -- `o2sim_MCHeader.root` -- `o2sim_Kine.root` -That will be used by the reconstruction as currently we do not have clusters. - -### 2. Run Reconstruction - -Execute the tracking workflow: - -```bash -o2-alice3-trk-reco-workflow --tracking-from-hits-config config_tracker.json -b -``` - -This produces: -- `o2trac_trk.root`: Reconstructed tracks with MC labels - -### 3. Run Quality Assurance - -Analyze the tracking performance: - -```bash -root -l -.L CheckTracksCA.C+ -CheckTracksCA("o2trac_trk.root", "o2sim_Kine.root", "o2sim_HitsTRK.root", "trk_qa_output.root") -``` +Use `o2-alice3-global-reconstruction-reco-workflow` for ALICE 3 tracking from hits. diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/RecoWorkflow.h b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/RecoWorkflow.h index 7046955a20c2e..863c5deae7241 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/RecoWorkflow.h +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/RecoWorkflow.h @@ -13,8 +13,6 @@ #define O2_TRK_RECOWORKFLOW_H #include "Framework/WorkflowSpec.h" -#include "GPUDataTypesConfig.h" -#include namespace o2::trk { @@ -22,12 +20,9 @@ namespace reco_workflow { o2::framework::WorkflowSpec getWorkflow(bool useMC, - const std::string& hitRecoConfig, bool upstreamDigits = false, bool upstreamClusters = false, - bool disableRootOutput = false, - bool useGPUWF = false, - o2::gpu::gpudatatypes::DeviceType dType = o2::gpu::gpudatatypes::DeviceType::CPU); + bool disableRootOutput = false); } } // namespace o2::trk diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx index d10feb4214f38..02895f42ac094 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx @@ -13,8 +13,6 @@ #include "TRKWorkflow/ClustererSpec.h" #include "TRKWorkflow/ClusterWriterSpec.h" #include "TRKWorkflow/DigitReaderSpec.h" -#include "TRKWorkflow/TrackerSpec.h" -#include "TRKWorkflow/TrackWriterSpec.h" #include "Framework/CCDBParamSpec.h" #include @@ -23,12 +21,9 @@ namespace o2::trk::reco_workflow { framework::WorkflowSpec getWorkflow(bool useMC, - const std::string& hitRecoConfig, bool upstreamDigits, bool upstreamClusters, - bool disableRootOutput, - bool useGPUWF, - o2::gpu::gpudatatypes::DeviceType dtype) + bool disableRootOutput) { framework::WorkflowSpec specs; @@ -43,14 +38,6 @@ framework::WorkflowSpec getWorkflow(bool useMC, specs.emplace_back(o2::trk::getClusterWriterSpec(useMC)); } - if (!hitRecoConfig.empty()) { - LOGP(info, "Using hit reco config from file {}", hitRecoConfig); - specs.emplace_back(o2::trk::getTrackerSpec(useMC, hitRecoConfig, dtype)); - if (!disableRootOutput) { - specs.emplace_back(o2::trk::getTrackWriterSpec(useMC)); - } - } - return specs; } diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx deleted file mode 100644 index c9d793a3ec78f..0000000000000 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx +++ /dev/null @@ -1,439 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include -#include - -#include "DetectorsBase/GeometryManager.h" -#include "ITStracking/TimeFrame.h" -#include "ITStracking/Configuration.h" -#include "Field/MagneticField.h" -#include "Field/MagFieldParam.h" -#include "Framework/ControlService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/CCDBParamSpec.h" -#include "SimulationDataFormat/MCEventHeader.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "TRKBase/GeometryTGeo.h" -#include "TRKBase/SegmentationChip.h" -#include "TRKSimulation/Hit.h" -#include "TRKReconstruction/TimeFrame.h" -#include "TRKWorkflow/TrackerSpec.h" -#include - -#ifdef O2_WITH_ACTS -#include "TRKReconstruction/TrackerACTS.h" -#endif - -#include -#include - -namespace o2 -{ -using namespace framework; -namespace trk -{ - -TrackerDPL::TrackerDPL(std::shared_ptr gr, - bool isMC, - const std::string& hitRecoConfigFileName, - o2::gpu::gpudatatypes::DeviceType dType) -{ - if (!hitRecoConfigFileName.empty()) { - std::ifstream configFile(hitRecoConfigFileName); - mHitRecoConfig = nlohmann::json::parse(configFile); - } - - // mITSTrackingInterface.setTrackingMode(trMode); -} - -void TrackerDPL::init(InitContext& ic) -{ - // mTimer.Stop(); - // mTimer.Reset(); - // o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); - // mChainITS.reset(mRecChain->AddChain()); - // mITSTrackingInterface.setTraitsFromProvider(mChainITS->GetITSVertexerTraits(), - // mChainITS->GetITSTrackerTraits(), - // mChainITS->GetITSTimeframe()); - -#ifdef O2_WITH_ACTS - mUseACTS = ic.options().get("useACTS"); -#endif -} - -void TrackerDPL::stop() -{ - LOGF(info, "CPU Reconstruction total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); -} - -std::vector TrackerDPL::createTrackingParamsFromConfig() -{ - std::vector trackingParams; - - if (!mHitRecoConfig.contains("trackingparams") || !mHitRecoConfig["trackingparams"].is_array()) { - LOGP(fatal, "No trackingparams field found in configuration or it is not an array. Returning empty vector."); - return trackingParams; - } - - for (const auto& paramConfig : mHitRecoConfig["trackingparams"]) { - o2::its::TrackingParameters params; - - // Parse integer parameters - if (paramConfig.contains("NLayers")) { - params.NLayers = paramConfig["NLayers"].get(); - } - if (paramConfig.contains("ZBins")) { - params.ZBins = paramConfig["ZBins"].get(); - } - if (paramConfig.contains("PhiBins")) { - params.PhiBins = paramConfig["PhiBins"].get(); - } - if (paramConfig.contains("ClusterSharing")) { - params.ClusterSharing = paramConfig["ClusterSharing"].get(); - } - if (paramConfig.contains("MinTrackLength")) { - params.MinTrackLength = paramConfig["MinTrackLength"].get(); - } - if (paramConfig.contains("ReseedIfShorter")) { - params.ReseedIfShorter = paramConfig["ReseedIfShorter"].get(); - } - if (paramConfig.contains("StartLayerMask")) { - params.StartLayerMask = paramConfig["StartLayerMask"].get(); - } - - // Parse float parameters - if (paramConfig.contains("NSigmaCut")) { - params.NSigmaCut = paramConfig["NSigmaCut"].get(); - } - if (paramConfig.contains("PVres")) { - params.PVres = paramConfig["PVres"].get(); - } - if (paramConfig.contains("TrackletMinPt")) { - params.TrackletMinPt = paramConfig["TrackletMinPt"].get(); - } - if (paramConfig.contains("CellDeltaTanLambdaSigma")) { - params.CellDeltaTanLambdaSigma = paramConfig["CellDeltaTanLambdaSigma"].get(); - } - if (paramConfig.contains("MaxChi2ClusterAttachment")) { - params.MaxChi2ClusterAttachment = paramConfig["MaxChi2ClusterAttachment"].get(); - } - if (paramConfig.contains("MaxChi2NDF")) { - params.MaxChi2NDF = paramConfig["MaxChi2NDF"].get(); - } - // if (paramConfig.contains("TrackFollowerNSigmaCutZ")) { - // params.TrackFollowerNSigmaCutZ = paramConfig["TrackFollowerNSigmaCutZ"].get(); - // } - // if (paramConfig.contains("TrackFollowerNSigmaCutPhi")) { - // params.TrackFollowerNSigmaCutPhi = paramConfig["TrackFollowerNSigmaCutPhi"].get(); - // } - - // Parse boolean parameters - if (paramConfig.contains("UseDiamond")) { - params.UseDiamond = paramConfig["UseDiamond"].get(); - } - if (paramConfig.contains("AllowSharingFirstCluster")) { - params.AllowSharingFirstCluster = paramConfig["AllowSharingFirstCluster"].get(); - } - if (paramConfig.contains("RepeatRefitOut")) { - params.RepeatRefitOut = paramConfig["RepeatRefitOut"].get(); - } - if (paramConfig.contains("ShiftRefToCluster")) { - params.ShiftRefToCluster = paramConfig["ShiftRefToCluster"].get(); - } - // if (paramConfig.contains("FindShortTracks")) { - // params.FindShortTracks = paramConfig["FindShortTracks"].get(); - // } - if (paramConfig.contains("PerPrimaryVertexProcessing")) { - params.PerPrimaryVertexProcessing = paramConfig["PerPrimaryVertexProcessing"].get(); - } - if (paramConfig.contains("SaveTimeBenchmarks")) { - params.SaveTimeBenchmarks = paramConfig["SaveTimeBenchmarks"].get(); - } - if (paramConfig.contains("DoUPCIteration")) { - params.DoUPCIteration = paramConfig["DoUPCIteration"].get(); - } - if (paramConfig.contains("FataliseUponFailure")) { - params.FataliseUponFailure = paramConfig["FataliseUponFailure"].get(); - } - // if (paramConfig.contains("UseTrackFollower")) { - // params.UseTrackFollower = paramConfig["UseTrackFollower"].get(); - // } - // if (paramConfig.contains("UseTrackFollowerTop")) { - // params.UseTrackFollowerTop = paramConfig["UseTrackFollowerTop"].get(); - // } - // if (paramConfig.contains("UseTrackFollowerBot")) { - // params.UseTrackFollowerBot = paramConfig["UseTrackFollowerBot"].get(); - // } - // if (paramConfig.contains("UseTrackFollowerMix")) { - // params.UseTrackFollowerMix = paramConfig["UseTrackFollowerMix"].get(); - // } - if (paramConfig.contains("createArtefactLabels")) { - params.createArtefactLabels = paramConfig["createArtefactLabels"].get(); - } - if (paramConfig.contains("PrintMemory")) { - params.PrintMemory = paramConfig["PrintMemory"].get(); - } - if (paramConfig.contains("DropTFUponFailure")) { - params.DropTFUponFailure = paramConfig["DropTFUponFailure"].get(); - } - - // Parse vector parameters - if (paramConfig.contains("LayerZ")) { - params.LayerZ = paramConfig["LayerZ"].get>(); - } - if (paramConfig.contains("LayerRadii")) { - params.LayerRadii = paramConfig["LayerRadii"].get>(); - } - if (paramConfig.contains("LayerxX0")) { - params.LayerxX0 = paramConfig["LayerxX0"].get>(); - } - if (paramConfig.contains("LayerResolution")) { - params.LayerResolution = paramConfig["LayerResolution"].get>(); - } - if (paramConfig.contains("SystErrorY2")) { - params.SystErrorY2 = paramConfig["SystErrorY2"].get>(); - } - if (paramConfig.contains("SystErrorZ2")) { - params.SystErrorZ2 = paramConfig["SystErrorZ2"].get>(); - } - if (paramConfig.contains("MinPt")) { - params.MinPt = paramConfig["MinPt"].get>(); - } - - // Parse Diamond array - if (paramConfig.contains("Diamond") && paramConfig["Diamond"].is_array() && paramConfig["Diamond"].size() == 3) { - params.Diamond[0] = paramConfig["Diamond"][0].get(); - params.Diamond[1] = paramConfig["Diamond"][1].get(); - params.Diamond[2] = paramConfig["Diamond"][2].get(); - } - - // Parse size_t parameter - if (paramConfig.contains("MaxMemory")) { - params.MaxMemory = paramConfig["MaxMemory"].get(); - } - - // Parse CorrType enum - if (paramConfig.contains("CorrType")) { - int corrTypeInt = paramConfig["CorrType"].get(); - params.CorrType = static_cast::MatCorrType>(corrTypeInt); - } - - trackingParams.push_back(params); - } - - LOGP(info, "Loaded {} tracking parameter sets from configuration", trackingParams.size()); - return trackingParams; -} - -void TrackerDPL::run(ProcessingContext& pc) -{ - auto cput = mTimer.CpuTime(); - auto realt = mTimer.RealTime(); - mTimer.Start(false); - - if (!mHitRecoConfig.empty()) { - TFile hitsFile(mHitRecoConfig["inputfiles"]["hits"].get().c_str(), "READ"); - TFile mcHeaderFile(mHitRecoConfig["inputfiles"]["mcHeader"].get().c_str(), "READ"); - TTree* hitsTree = hitsFile.Get("o2sim"); - std::vector* trkHit = nullptr; - hitsTree->SetBranchAddress("TRKHit", &trkHit); - - TTree* mcHeaderTree = mcHeaderFile.Get("o2sim"); - auto mcheader = new o2::dataformats::MCEventHeader; - mcHeaderTree->SetBranchAddress("MCEventHeader.", &mcheader); - - o2::base::GeometryManager::loadGeometry(mHitRecoConfig["inputfiles"]["geometry"].get().c_str(), false, true); - auto* gman = o2::trk::GeometryTGeo::Instance(); - - const Long64_t nEvents{hitsTree->GetEntries()}; - LOGP(info, "Starting reconstruction from hits for {} events", nEvents); - - if (mMemoryPool.get() == nullptr) { - mMemoryPool = std::make_shared(); - } - if (mTaskArena.get() == nullptr) { - mTaskArena = std::make_shared(1); /// TODO: make it configurable - } - - o2::trk::TimeFrame<11> timeFrame; - o2::its::TrackerTraits<11> itsTrackerTraits; - o2::its::Tracker<11> itsTracker(&itsTrackerTraits); - timeFrame.setMemoryPool(mMemoryPool); - itsTrackerTraits.setMemoryPool(mMemoryPool); - itsTrackerTraits.setNThreads(mTaskArena->max_concurrency(), mTaskArena); - itsTrackerTraits.adoptTimeFrame(static_cast*>(&timeFrame)); - itsTrackerTraits.setBz(mHitRecoConfig["geometry"]["bz"].get()); - auto field = new field::MagneticField("ALICE3Mag", "ALICE 3 Magnetic Field", mHitRecoConfig["geometry"]["bz"].get() / 5.f, 0.0, o2::field::MagFieldParam::k5kGUniform); - TGeoGlobalMagField::Instance()->SetField(field); - TGeoGlobalMagField::Instance()->Lock(); - itsTracker.adoptTimeFrame(timeFrame); - - const int nRofs = timeFrame.loadROFsFromHitTree(hitsTree, gman, mHitRecoConfig); - const int inROFpileup{mHitRecoConfig.contains("inROFpileup") ? mHitRecoConfig["inROFpileup"].get() : 1}; - - // Add primary vertices from MC headers for each ROF - timeFrame.getPrimaryVerticesFromMC(mcHeaderTree, nRofs, nEvents, inROFpileup); - // Create tracking parameters from config and set them in the time frame - auto trackingParams = createTrackingParamsFromConfig(); - - itsTrackerTraits.updateTrackingParameters(trackingParams); - -#ifdef O2_WITH_ACTS - if (mUseACTS) { - LOG(info) << "Running the tracking with ACTS"; - o2::trk::TrackerACTS<11> actsTracker; - actsTracker.setBz(mHitRecoConfig["geometry"]["bz"].get()); - actsTracker.adoptTimeFrame(timeFrame); - actsTracker.clustersToTracks(); - } -#endif - - const auto trackingLoopStart = std::chrono::steady_clock::now(); - for (size_t iter{0}; iter < trackingParams.size(); ++iter) { - LOGP(info, "{}", trackingParams[iter].asString()); - timeFrame.initialise(iter, trackingParams[iter], 11, false); - itsTrackerTraits.computeLayerTracklets(iter, -1); - LOGP(info, "Number of tracklets in iteration {}: {}", iter, timeFrame.getNumberOfTracklets()); - itsTrackerTraits.computeLayerCells(iter); - LOGP(info, "Number of cells in iteration {}: {}", iter, timeFrame.getNumberOfCells()); - itsTrackerTraits.findCellsNeighbours(iter); - LOGP(info, "Number of cell neighbours in iteration {}: {}", iter, timeFrame.getNumberOfNeighbours()); - itsTrackerTraits.findRoads(iter); - LOGP(info, "Number of tracks in iteration {}: {}", iter, timeFrame.getNumberOfTracks()); - } - const auto trackingLoopElapsedMs = std::chrono::duration_cast(std::chrono::steady_clock::now() - trackingLoopStart).count(); - LOGP(info, "Tracking iterations block took {} ms", trackingLoopElapsedMs); - - itsTracker.computeTracksMClabels(); - - // Collect tracks and labels (flat vectors in the new interface) - const auto& tracks = timeFrame.getTracks(); - const auto& labels = timeFrame.getTracksLabel(); - - // Copy to output vectors (TrackITSExt -> TrackITS slicing for output compatibility) - std::vector allTracks(tracks.begin(), tracks.end()); - std::vector allLabels(labels.begin(), labels.end()); - - int totalTracks = allTracks.size(); - int goodTracks = 0; - int fakeTracks = 0; - - for (const auto& label : allLabels) { - if (label.isFake()) { - fakeTracks++; - } else { - goodTracks++; - } - } - - LOGP(info, "=== Tracking Summary ==="); - LOGP(info, "Total tracks reconstructed: {}", totalTracks); - LOGP(info, "Good tracks: {} ({:.1f}%)", goodTracks, totalTracks > 0 ? 100.0 * goodTracks / totalTracks : 0); - LOGP(info, "Fake tracks: {} ({:.1f}%)", fakeTracks, totalTracks > 0 ? 100.0 * fakeTracks / totalTracks : 0); - - // Stream tracks and labels to DPL output - pc.outputs().snapshot(o2::framework::Output{"TRK", "TRACKS", 0}, allTracks); - pc.outputs().snapshot(o2::framework::Output{"TRK", "TRACKSMCTR", 0}, allLabels); - - LOGP(info, "Tracks and MC labels streamed to output"); - - pc.services().get().endOfStream(); - pc.services().get().readyToQuit(framework::QuitRequest::Me); - } - - mTimer.Stop(); - LOGP(info, "CPU Reconstruction time for this TF {} s (cpu), {} s (wall)", mTimer.CpuTime() - cput, mTimer.RealTime() - realt); -} - -// void TrackerDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) -// { -// // mITSTrackingInterface.finaliseCCDB(matcher, obj); -// } - -void TrackerDPL::endOfStream(EndOfStreamContext& ec) -{ - LOGF(info, "TRK CA-Tracker total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); -} - -DataProcessorSpec getTrackerSpec(bool useMC, const std::string& hitRecoConfig, o2::gpu::gpudatatypes::DeviceType dType) -{ - std::vector inputs; - std::vector outputs; - outputs.emplace_back("TRK", "TRACKS", 0, Lifetime::Timeframe); - auto ggRequest = std::make_shared(false, // orbitResetTime - false, // GRPECS=true - false, // GRPLHCIF - false, // GRPMagField - false, // askMatLUT - o2::base::GRPGeomRequest::None, // geometry, but ignored until it will be put in the CCDB - inputs, - true); - - if (!hitRecoConfig.empty()) { - outputs.emplace_back("TRK", "TRACKSMCTR", 0, Lifetime::Timeframe); - return DataProcessorSpec{ - "trk-hits-tracker", - {}, - outputs, - AlgorithmSpec{adaptFromTask(ggRequest, - useMC, - hitRecoConfig, - dType)}, - Options{ConfigParamSpec{"max-loops", VariantType::Int, 1, {"max number of loops"}} -#ifdef O2_WITH_ACTS - , - {"useACTS", o2::framework::VariantType::Bool, false, {"Use ACTS for tracking"}} -#endif - }}; - } - - inputs.emplace_back("dummy", "TRK", "DUMMY", 0, Lifetime::Timeframe); - - constexpr bool expectClusterInputs = false; - if (expectClusterInputs) { - inputs.pop_back(); - inputs.emplace_back("compClusters", "TRK", "COMPCLUSTERS", 0, Lifetime::Timeframe); - inputs.emplace_back("patterns", "TRK", "PATTERNS", 0, Lifetime::Timeframe); - inputs.emplace_back("ROframes", "TRK", "CLUSTERSROF", 0, Lifetime::Timeframe); - } - - // inputs.emplace_back("itscldict", "TRK", "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec("ITS/Calib/ClusterDictionary")); - // inputs.emplace_back("TRK_almiraparam", "TRK", "ALMIRAPARAM", 0, Lifetime::Condition, ccdbParamSpec("TRK/Config/AlmiraParam")); - - // outputs.emplace_back("TRK", "TRACKCLSID", 0, Lifetime::Timeframe); - // outputs.emplace_back("TRK", "TRKTrackROF", 0, Lifetime::Timeframe); - // outputs.emplace_back("TRK", "VERTICES", 0, Lifetime::Timeframe); - // outputs.emplace_back("TRK", "VERTICESROF", 0, Lifetime::Timeframe); - // outputs.emplace_back("TRK", "IRFRAMES", 0, Lifetime::Timeframe); - - if (useMC) { - // inputs.emplace_back("trkmclabels", "TRK", "CLUSTERSMCTR", 0, Lifetime::Timeframe); - // inputs.emplace_back("TRKMC2ROframes", "TRK", "CLUSTERSMC2ROF", 0, Lifetime::Timeframe); - // outputs.emplace_back("TRK", "VERTICESMCTR", 0, Lifetime::Timeframe); - // outputs.emplace_back("TRK", "VERTICESMCPUR", 0, Lifetime::Timeframe); - // outputs.emplace_back("TRK", "TRACKSMCTR", 0, Lifetime::Timeframe); - // outputs.emplace_back("TRK", "TRKTrackMC2ROF", 0, Lifetime::Timeframe); - } - - return DataProcessorSpec{ - "trk-tracker", - inputs, - outputs, - AlgorithmSpec{adaptFromTask(ggRequest, - useMC, - hitRecoConfig, - dType)}, - Options{}}; -} - -} // namespace trk -} // namespace o2 diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/trk-reco-workflow.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/trk-reco-workflow.cxx index 166e6f65b4b2b..bd1d5acc9b9a7 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/trk-reco-workflow.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/trk-reco-workflow.cxx @@ -9,21 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - #include "TRKWorkflow/RecoWorkflow.h" #include "CommonUtils/ConfigurableParam.h" -#include "ITStracking/TrackingConfigParam.h" -#include "ITStracking/Configuration.h" #include "Framework/CallbacksPolicy.h" #include "Framework/ConfigContext.h" @@ -52,11 +39,7 @@ void customize(std::vector& workflowOptions) {"clusters-from-upstream", VariantType::Bool, false, {"clusters will be provided from upstream, skip clusterizer"}}, {"disable-root-output", VariantType::Bool, false, {"do not write output root files"}}, {"disable-mc", VariantType::Bool, false, {"disable MC propagation even if available"}}, - {"tracking-from-hits-config", VariantType::String, "", {"JSON file with tracking from hits configuration"}}, - {"disable-tracking", VariantType::Bool, false, {"disable tracking step"}}, - {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, - {"use-gpu-workflow", VariantType::Bool, false, {"use GPU workflow (default: false)"}}, - {"gpu-device", VariantType::Int, 1, {"use gpu device: CPU=1,CUDA=2,HIP=3 (default: CPU)"}}}; + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; std::swap(workflowOptions, options); } @@ -67,9 +50,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { // Update the (declared) parameters if changed from the command line auto useMC = !configcontext.options().get("disable-mc"); - auto hitRecoConfig = configcontext.options().get("tracking-from-hits-config"); - auto useGpuWF = configcontext.options().get("use-gpu-workflow"); - auto gpuDevice = static_cast(configcontext.options().get("gpu-device")); auto extDigits = configcontext.options().get("digits-from-upstream"); auto extClusters = configcontext.options().get("clusters-from-upstream"); auto disableRootOutput = configcontext.options().get("disable-root-output"); @@ -78,5 +58,5 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) // write the configuration used for the reco workflow o2::conf::ConfigurableParam::writeINI("o2itsrecoflow_configuration.ini"); - return o2::trk::reco_workflow::getWorkflow(useMC, hitRecoConfig, extDigits, extClusters, disableRootOutput, useGpuWF, gpuDevice); + return o2::trk::reco_workflow::getWorkflow(useMC, extDigits, extClusters, disableRootOutput); } diff --git a/Detectors/Vertexing/include/DetectorsVertexing/PVertexer.h b/Detectors/Vertexing/include/DetectorsVertexing/PVertexer.h index cdf83603258cd..db8bff7d52ebe 100644 --- a/Detectors/Vertexing/include/DetectorsVertexing/PVertexer.h +++ b/Detectors/Vertexing/include/DetectorsVertexing/PVertexer.h @@ -29,6 +29,7 @@ #include "ReconstructionDataFormats/PrimaryVertex.h" #include "DetectorsVertexing/PVertexerHelpers.h" #include "DetectorsVertexing/PVertexerParams.h" +#include "DetectorsBase/Propagator.h" #include "ReconstructionDataFormats/GlobalTrackID.h" #include "DataFormatsCalibration/MeanVertexObject.h" #include "DataFormatsITSMFT/DPLAlpideParam.h" @@ -85,6 +86,7 @@ class PVertexer void setBunchFilling(const o2::BunchFilling& bf); void setBz(float bz) { mBz = bz; } + void setMatCorrType(o2::base::Propagator::MatCorrType type) { mMatCorr = type; } void setValidateWithIR(bool v) { mValidateWithIR = v; } bool getValidateWithIR() const { return mValidateWithIR; } void setTrackSources(GTrackID::mask_t s); @@ -187,6 +189,7 @@ class PVertexer std::vector mTimeZClusters; ///< set of time clusters float mITSROFrameLengthMUS = 0; ///< ITS readout time span in \mus float mBz = 0.; ///< mag.field at beam line + o2::base::Propagator::MatCorrType mMatCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; ///< material correction for propagation float mDBScanDeltaT = 0.; ///< deltaT cut for DBScan check float mDBSMaxZ2InvCorePoint = 0; ///< inverse of max sigZ^2 of the track which can be core point in the DBScan bool mValidateWithIR = false; ///< require vertex validation with InteractionCandidates (if available) diff --git a/Detectors/Vertexing/src/PVertexer.cxx b/Detectors/Vertexing/src/PVertexer.cxx index 10e504bba0772..c4b5dc5cfc14c 100644 --- a/Detectors/Vertexing/src/PVertexer.cxx +++ b/Detectors/Vertexing/src/PVertexer.cxx @@ -1218,7 +1218,7 @@ bool PVertexer::relateTrackToMeanVertex(o2::track::TrackParCov& trc, float vtxEr z = mMeanVertex.getZ(); } mMeanVertex.setMeanXYVertexAtZ(mMeanVertexSeed, z); - if (!o2::base::Propagator::Instance()->propagateToDCA(mMeanVertex, trc, mBz, 2.0f, o2::base::Propagator::MatCorrType::USEMatCorrLUT, &dca, nullptr, 0, mPVParams->dcaTolerance)) { + if (!o2::base::Propagator::Instance()->propagateToDCA(mMeanVertex, trc, mBz, 2.0f, mMatCorr, &dca, nullptr, 0, mPVParams->dcaTolerance)) { return false; } return dca.getY() * dca.getY() / (dca.getSigmaY2() + vtxErr2) < mPVParams->pullIniCut; @@ -1227,7 +1227,7 @@ bool PVertexer::relateTrackToMeanVertex(o2::track::TrackParCov& trc, float vtxEr //______________________________________________ bool PVertexer::relateTrackToVertex(o2::track::TrackParCov& trc, const o2d::VertexBase& vtxSeed) const { - return o2::base::Propagator::Instance()->propagateToDCA(vtxSeed, trc, mBz, 2.0f, o2::base::Propagator::MatCorrType::USEMatCorrLUT); + return o2::base::Propagator::Instance()->propagateToDCA(vtxSeed, trc, mBz, 2.0f, mMatCorr); } //______________________________________________ diff --git a/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx index c65ef2903db59..21fdae4a57760 100644 --- a/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx +++ b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx @@ -21,7 +21,7 @@ #include "Framework/Signpost.h" #include "Framework/DanglingEdgesContext.h" #include "Framework/ConfigContext.h" -#include "Framework/ConfigContext.h" +#include "Framework/ConfigParamsHelper.h" #include #include #include @@ -71,31 +71,45 @@ AlgorithmSpec AnalysisCCDBHelpers::fetchFromCCDB(ConfigContext const& /*ctx*/) { return adaptStateful([](ConfigParamRegistry const& options, DeviceSpec const& spec, InitContext& ic) { auto& dec = ic.services().get(); + // The effective default for each ccdb: option was already resolved at topology + // time by ArrowSupport (consulting task Configurables) and registered on this + // device's options. Here we just read the final value — honouring any further + // runtime override supplied via CLI or JSON config. + std::unordered_map ccdbUrls; + for (auto& input : dec.analysisCCDBInputs) { + for (auto& m : input.metadata) { + if (!m.name.starts_with("ccdb:") || ccdbUrls.count(m.name)) { + continue; + } + std::string url = m.defaultValue.asString(); + if (ConfigParamsHelper::hasOption(spec.options, m.name)) { + url = options.get(m.name.c_str()); + } + LOGP(info, "CCDB path resolved for {}: {}", m.name, url); + ccdbUrls.emplace(m.name, std::move(url)); + } + } std::vector> schemas; - auto schemaMetadata = std::make_shared(); - for (auto& input : dec.analysisCCDBInputs) { + auto schemaMetadata = std::make_shared(); std::vector> fields; schemaMetadata->Append("outputRoute", DataSpecUtils::describe(input)); schemaMetadata->Append("outputBinding", input.binding); - for (auto& m : input.metadata) { - // Save the list of input tables if (m.name.starts_with("input:")) { auto name = m.name.substr(6); schemaMetadata->Append("sourceTable", name); schemaMetadata->Append("sourceMatcher", DataSpecUtils::describe(std::get(DataSpecUtils::fromMetadataString(m.defaultValue.get()).matcher))); continue; } - // Ignore the non ccdb: entries if (!m.name.starts_with("ccdb:")) { continue; } - // Create the schema of the output - auto metadata = std::make_shared(); - metadata->Append("url", m.defaultValue.asString()); + auto fieldMetadata = std::make_shared(); + auto it = ccdbUrls.find(m.name); + fieldMetadata->Append("url", it != ccdbUrls.end() ? it->second : m.defaultValue.asString()); auto columnName = m.name.substr(strlen("ccdb:")); - fields.emplace_back(std::make_shared(columnName, arrow::binary_view(), false, metadata)); + fields.emplace_back(std::make_shared(columnName, arrow::binary_view(), false, fieldMetadata)); } schemas.emplace_back(std::make_shared(fields, schemaMetadata)); } diff --git a/Framework/Core/include/Framework/AnalysisHelpers.h b/Framework/Core/include/Framework/AnalysisHelpers.h index bfc5a02891dad..cfd2f357ba06f 100644 --- a/Framework/Core/include/Framework/AnalysisHelpers.h +++ b/Framework/Core/include/Framework/AnalysisHelpers.h @@ -172,7 +172,7 @@ struct Builder { std::shared_ptr materialize(ProcessingContext& pc); }; -} // namespace o2::framework +} // namespace o2::framework namespace o2::soa { @@ -394,7 +394,7 @@ constexpr auto getIndexMetadata() -> std::vector return {}; } -} // namespace +} // namespace template constexpr auto tableRef2InputSpec() @@ -463,7 +463,7 @@ constexpr auto tableRef2OutputRef() o2::aod::label(), R.version}; } -} // namespace o2::soa +} // namespace o2::soa namespace o2::framework { @@ -672,7 +672,7 @@ struct Spawns : decltype(transformBase()) { std::shared_ptr table = nullptr; std::shared_ptr extension = nullptr; - std::array projectors = [](framework::pack) -> std::array + std::array projectors = [](framework::pack)->std::array { return {{std::move(C::Projector())...}}; } @@ -1077,7 +1077,7 @@ concept is_partition = requires(T t) { requires std::same_as; requires std::same_as>>; }; -} // namespace o2::framework +} // namespace o2::framework namespace o2::soa { @@ -1100,6 +1100,6 @@ auto Attach(T const& table) using output_t = Join, o2::aod::Hash<"JOIN/0"_h>, o2::aod::Hash<"JOIN"_h>, Cs...>>; return output_t{{table.asArrowTable()}, table.offset()}; } -} // namespace o2::soa +} // namespace o2::soa -#endif // o2_framework_AnalysisHelpers_H_DEFINED +#endif // o2_framework_AnalysisHelpers_H_DEFINED diff --git a/Framework/Core/include/Framework/AnalysisTask.h b/Framework/Core/include/Framework/AnalysisTask.h index fbd523c7b0c37..1b25727874749 100644 --- a/Framework/Core/include/Framework/AnalysisTask.h +++ b/Framework/Core/include/Framework/AnalysisTask.h @@ -16,6 +16,7 @@ #include "Framework/AlgorithmSpec.h" #include "Framework/CallbackService.h" #include "Framework/ConfigContext.h" +#include "Framework/ConfigParamsHelper.h" #include "Framework/ControlService.h" #include "Framework/DataProcessorSpec.h" #include "Framework/Expressions.h" @@ -557,6 +558,17 @@ DataProcessorSpec adaptAnalysisTask(ConfigContext const& ctx, Args&&... args) LOG(warn) << "Task " << name_str << " has no inputs"; } + // Auto-register default ccdb: path options from subscribed timestamped-table inputs. + // This allows tasks to accept --ccdb:fXxx overrides without requiring an explicit + // ConfigurableCCDBPath<> member for every column in the subscribed table. + for (auto& input : inputs) { + for (auto& meta : input.metadata) { + if (meta.name.starts_with("ccdb:") && meta.name != "ccdb:") { + ConfigParamsHelper::addOptionIfMissing(options, meta); + } + } + } + homogeneous_apply_refs_sized([&outputs, &hash](auto& element) { return analysis_task_parsers::appendOutput(outputs, element, hash); }, *task.get()); auto requiredServices = CommonServices::defaultServices(); diff --git a/Framework/Core/include/Framework/BinningPolicy.h b/Framework/Core/include/Framework/BinningPolicy.h index 3e41302c920f8..f4ea6885192e4 100644 --- a/Framework/Core/include/Framework/BinningPolicy.h +++ b/Framework/Core/include/Framework/BinningPolicy.h @@ -188,7 +188,7 @@ struct BinningPolicyBase { if constexpr (N == 2) { return getXBinsCount() * getYBinsCount(); } - if constexpr (N == 2) { + if constexpr (N == 3) { return getXBinsCount() * getYBinsCount() * getZBinsCount(); } return -1; diff --git a/Framework/Core/include/Framework/Configurable.h b/Framework/Core/include/Framework/Configurable.h index 0931884da1ff7..3cbd1839b7d89 100644 --- a/Framework/Core/include/Framework/Configurable.h +++ b/Framework/Core/include/Framework/Configurable.h @@ -83,6 +83,26 @@ struct Configurable : IP { template using MutableConfigurable = Configurable>; +/// Convenience wrapper for overriding the CCDB path of a CCDB column declared +/// with DECLARE_SOA_CCDB_COLUMN / DECLARE_SOA_CCDB_COLUMN_FULL. +/// +/// The option name, default value, and help string are all derived automatically +/// from the column type: name = "ccdb:" + Column::mLabel, default = Column::query. +/// +/// Example: +/// struct MyTask { +/// ConfigurableCCDBPath lhcPhasePath; +/// }; +template +struct ConfigurableCCDBPath : Configurable { + ConfigurableCCDBPath() + : Configurable{std::string{"ccdb:"} + Column::mLabel, + std::string{Column::query}, + std::string{"CCDB path for "} + Column::mLabel + " (default: " + Column::query + ")"} + { + } +}; + template concept is_configurable = requires(T t) { requires std::same_as; @@ -93,11 +113,10 @@ concept is_configurable = requires(T t) { using ConfigurableAxis = Configurable, ConfigParamKind::kAxisSpec, ConfigurablePolicyConst, ConfigParamKind::kAxisSpec>>; template -concept is_configurable_axis = is_configurable&& - requires() -{ - T::kind == ConfigParamKind::kAxisSpec; -}; +concept is_configurable_axis = is_configurable && + requires() { + T::kind == ConfigParamKind::kAxisSpec; + }; template struct ProcessConfigurable : Configurable { diff --git a/Framework/Core/include/Framework/DataProcessingContext.h b/Framework/Core/include/Framework/DataProcessingContext.h index 221f7b099dc07..976331ba42c3c 100644 --- a/Framework/Core/include/Framework/DataProcessingContext.h +++ b/Framework/Core/include/Framework/DataProcessingContext.h @@ -13,6 +13,7 @@ #include "Framework/DataRelayer.h" #include "Framework/AlgorithmSpec.h" +#include #include namespace o2::framework @@ -33,7 +34,7 @@ struct DataProcessorContext { DataProcessorContext(DataProcessorContext const&) = delete; DataProcessorContext() = default; - bool allDone = false; + std::atomic allDone = false; /// Latest run number we processed globally for this DataProcessor. int64_t lastRunNumberProcessed = -1; diff --git a/Framework/Core/include/Framework/TableConsumer.h b/Framework/Core/include/Framework/TableConsumer.h index f2a041952470c..1924d0694097b 100644 --- a/Framework/Core/include/Framework/TableConsumer.h +++ b/Framework/Core/include/Framework/TableConsumer.h @@ -12,6 +12,7 @@ #ifndef FRAMEWORK_TABLECONSUMER_H #define FRAMEWORK_TABLECONSUMER_H +#include #include namespace arrow diff --git a/Framework/Core/scripts/dpl-mcp-server/dpl_mcp_server.py b/Framework/Core/scripts/dpl-mcp-server/dpl_mcp_server.py index 3900a646632a1..ed457b8a57d9d 100644 --- a/Framework/Core/scripts/dpl-mcp-server/dpl_mcp_server.py +++ b/Framework/Core/scripts/dpl-mcp-server/dpl_mcp_server.py @@ -14,19 +14,21 @@ Bridges the DPL driver /status WebSocket endpoint to MCP tools so that an AI assistant (e.g. Claude) can inspect and monitor a running DPL workflow. +Supports multiple concurrent workflows. Use the ``connect`` tool to attach +to a running topology by port or PID, then pass the returned workflow name +to every other tool. + Usage ----- - python3 dpl_mcp_server.py --port 8080 - python3 dpl_mcp_server.py --pid 12345 # port derived as 8080 + pid % 30000 - DPL_STATUS_PORT=8080 python3 dpl_mcp_server.py + python3 dpl_mcp_server.py -Wire protocol (client → driver) +Wire protocol (client -> driver) -------------------------------- {"cmd":"list_metrics","device":""} {"cmd":"subscribe","device":"","metrics":["m1","m2"]} {"cmd":"unsubscribe","device":"","metrics":["m1"]} -Wire protocol (driver → client) +Wire protocol (driver -> client) -------------------------------- {"type":"snapshot","devices":[{"name","pid","active","streamingState","deviceState"},...]} {"type":"update","device":,"name":"","metrics":{}} @@ -35,80 +37,121 @@ from __future__ import annotations -import argparse import asyncio import json import os -import sys from typing import Any +from urllib.parse import urlparse import websockets from mcp.server.fastmcp import FastMCP + # --------------------------------------------------------------------------- -# Global connection state (all access from the single asyncio event loop) +# Per-workflow connection state # --------------------------------------------------------------------------- -_port: int = 8080 -_ws: Any = None -_reader_task: asyncio.Task | None = None -_snapshot: dict = {} -_updates: list[dict] = [] -_logs: list[dict] = [] -_metrics_lists: dict[str, list[str]] = {} - - -async def _ensure_connected() -> None: - """Connect (or reconnect) to the driver's /status WebSocket.""" - global _ws, _reader_task +class WorkflowConnection: + """Holds WebSocket connection and buffered state for one DPL workflow.""" + + def __init__(self, *, url: str, name: str, extra_headers: dict[str, str] | None = None): + self.url = url + self.name = name + self.extra_headers = extra_headers or {} + self.ws: Any = None + self.reader_task: asyncio.Task | None = None + self.snapshot: dict = {} + self.updates: list[dict] = [] + self.logs: list[dict] = [] + self.metrics_lists: dict[str, list[str]] = {} + + async def ensure_connected(self) -> None: + """Connect (or reconnect) to the driver's /status WebSocket.""" + if self.ws is not None: + try: + pong = await asyncio.wait_for(self.ws.ping(), timeout=2.0) + await pong + return + except Exception: + old_ws = self.ws + self.ws = None + if self.reader_task is not None and not self.reader_task.done(): + self.reader_task.cancel() + try: + await self.reader_task + except (asyncio.CancelledError, Exception): + pass + self.reader_task = None + try: + await old_ws.close() + except Exception: + pass + + self.ws = await websockets.connect( + self.url, + subprotocols=["dpl"], + additional_headers=self.extra_headers if self.extra_headers else None, + ) + if self.reader_task is None or self.reader_task.done(): + self.reader_task = asyncio.create_task(self._reader()) - # Check liveness of existing connection. - if _ws is not None: + async def _reader(self) -> None: + """Background task: read frames from the driver and buffer them.""" try: - pong = await asyncio.wait_for(_ws.ping(), timeout=2.0) - await pong - return + async for raw in self.ws: + try: + msg = json.loads(raw) + except json.JSONDecodeError: + continue + t = msg.get("type") + if t == "snapshot": + self.snapshot = msg + self.metrics_lists.clear() + elif t == "update": + self.updates.append(msg) + elif t == "log": + self.logs.append(msg) + elif t == "metrics_list": + device = msg.get("device", "") + self.metrics_lists[device] = msg.get("metrics", []) except Exception: - _ws = None - if _reader_task is not None and not _reader_task.done(): - _reader_task.cancel() - _reader_task = None - - url = f"ws://localhost:{_port}/status" - _ws = await websockets.connect(url, subprotocols=["dpl"]) - if _reader_task is None or _reader_task.done(): - _reader_task = asyncio.create_task(_reader()) - - -async def _reader() -> None: - """Background task: read frames from the driver and buffer them.""" - global _ws, _snapshot, _updates, _logs, _metrics_lists - try: - async for raw in _ws: + pass + finally: + self.ws = None + + async def send(self, obj: dict) -> None: + await self.ensure_connected() + await self.ws.send(json.dumps(obj, separators=(",", ":"))) + + async def close(self) -> None: + ws = self.ws + self.ws = None + if self.reader_task is not None and not self.reader_task.done(): + self.reader_task.cancel() try: - msg = json.loads(raw) - except json.JSONDecodeError: - continue - t = msg.get("type") - if t == "snapshot": - _snapshot = msg - # Clear stale metric lists from a previous driver instance. - _metrics_lists.clear() - elif t == "update": - _updates.append(msg) - elif t == "log": - _logs.append(msg) - elif t == "metrics_list": - device = msg.get("device", "") - _metrics_lists[device] = msg.get("metrics", []) - except Exception: - pass - finally: - _ws = None - - -async def _send(obj: dict) -> None: - await _ensure_connected() - await _ws.send(json.dumps(obj, separators=(",", ":"))) + await self.reader_task + except (asyncio.CancelledError, Exception): + pass + self.reader_task = None + if ws is not None: + await ws.close() + + +# --------------------------------------------------------------------------- +# Workflow registry +# --------------------------------------------------------------------------- +_workflows: dict[str, WorkflowConnection] = {} + + +def _get(workflow: str) -> WorkflowConnection: + """Look up a workflow by name, raising a clear error if not found.""" + conn = _workflows.get(workflow) + if conn is None: + available = ", ".join(_workflows.keys()) if _workflows else "(none)" + raise ValueError( + f"No workflow named '{workflow}'. Connected workflows: {available}. " + f"Use the connect tool first." + ) + return conn # --------------------------------------------------------------------------- @@ -118,16 +161,124 @@ async def _send(obj: dict) -> None: @mcp.tool() -async def list_devices() -> str: +async def connect(port: int = 0, pid: int = 0, name: str = "") -> str: + """Connect to a running DPL workflow. + + Provide either ``port`` (the driver's WebSocket port) or ``pid`` (the + driver PID, port derived as 8080 + pid % 30000). An optional ``name`` + gives the workflow a human-friendly label; if omitted the port number is + used. + + Args: + port: TCP port of the DPL driver status WebSocket. + pid: PID of the DPL driver process (alternative to port). + name: Optional human-friendly name for this workflow. + """ + if pid: + port = 8080 + pid % 30000 + if not port: + return "Provide either port or pid." + + wf_name = name or str(port) + if wf_name in _workflows: + old = _workflows[wf_name] + await old.close() + + url = f"ws://localhost:{port}/status" + conn = WorkflowConnection(url=url, name=wf_name) + await conn.ensure_connected() + _workflows[wf_name] = conn + + devices = conn.snapshot.get("devices", []) + return ( + f"Connected to workflow '{wf_name}' on port {port} " + f"({len(devices)} device(s))." + ) + + +@mcp.tool() +async def connect_hyperloop(url: str, name: str = "", token: str = "") -> str: + """Connect to a DPL workflow running on Hyperloop via the remote proxy. + + Accepts a URL like: + https://alimonitor.cern.ch/train-workdir/remote-gui/remote_proxy.html?/ + + and remaps it to the local WebSocket proxy endpoint. + + Args: + url: The remote_proxy.html URL from alimonitor. + name: Optional human-friendly name for this workflow. + token: Hyperloop auth token. Falls back to HYPERLOOP_TOKEN env var. + """ + token = token or os.environ.get("HYPERLOOP_TOKEN", "") + if not token: + return "No token provided and HYPERLOOP_TOKEN environment variable is not set." + + parsed = urlparse(url) + path_suffix = parsed.query # everything after '?' + if not path_suffix: + return f"Cannot parse token/port from URL: {url}" + + ws_url = f"ws://localhost:8888/remote-mcp/o2/{path_suffix}/status" + wf_name = name or path_suffix.split("/")[-1] + + if wf_name in _workflows: + old = _workflows[wf_name] + await old.close() + + headers = {"Authorization": f"Bearer {token}"} + conn = WorkflowConnection(url=ws_url, name=wf_name, extra_headers=headers) + await conn.ensure_connected() + _workflows[wf_name] = conn + + devices = conn.snapshot.get("devices", []) + return ( + f"Connected to Hyperloop workflow '{wf_name}' via {ws_url} " + f"({len(devices)} device(s))." + ) + + +@mcp.tool() +async def disconnect(workflow: str) -> str: + """Disconnect from a DPL workflow and release its resources. + + Args: + workflow: Workflow name as returned by connect. + """ + conn = _get(workflow) + await conn.close() + del _workflows[workflow] + return f"Disconnected from workflow '{workflow}'." + + +@mcp.tool() +async def list_workflows() -> str: + """List all currently connected DPL workflows.""" + if not _workflows: + return "No workflows connected. Use the connect tool first." + lines = [] + for wf_name, conn in _workflows.items(): + n = len(conn.snapshot.get("devices", [])) + status = "connected" if conn.ws is not None else "disconnected" + lines.append(f"{wf_name}: port={conn.port} devices={n} status={status}") + return "\n".join(lines) + + +@mcp.tool() +async def list_devices(workflow: str) -> str: """List all DPL devices with their current status. Returns each device's name, PID, active flag, streaming state, and device state as reported by the driver snapshot. + + Args: + workflow: Workflow name as returned by connect. """ - await _ensure_connected() - if not _snapshot: - return "No snapshot received yet — the driver may still be starting." - devices = _snapshot.get("devices", []) + conn = _get(workflow) + await conn.ensure_connected() + if not conn.snapshot: + return "No snapshot received yet -- the driver may still be starting." + devices = conn.snapshot.get("devices", []) if not devices: return "No devices in snapshot." lines = [] @@ -140,7 +291,7 @@ async def list_devices() -> str: @mcp.tool() -async def list_metrics(device: str) -> str: +async def list_metrics(workflow: str, device: str) -> str: """List the available numeric metrics for a DPL device. Sends a list_metrics command to the driver and waits up to 3 seconds for @@ -148,15 +299,16 @@ async def list_metrics(device: str) -> str: and enum metrics are excluded. Args: + workflow: Workflow name as returned by connect. device: Device name exactly as shown by list_devices. """ - # Remove any stale cached result so we can detect the fresh reply. - _metrics_lists.pop(device, None) - await _send({"cmd": "list_metrics", "device": device}) + conn = _get(workflow) + conn.metrics_lists.pop(device, None) + await conn.send({"cmd": "list_metrics", "device": device}) for _ in range(60): # up to 3 s await asyncio.sleep(0.05) - if device in _metrics_lists: - names = _metrics_lists[device] + if device in conn.metrics_lists: + names = conn.metrics_lists[device] if not names: return f"Device '{device}' has no numeric metrics yet." return f"{len(names)} metric(s): " + ", ".join(names) @@ -164,7 +316,7 @@ async def list_metrics(device: str) -> str: @mcp.tool() -async def subscribe(device: str, metrics: list[str]) -> str: +async def subscribe(workflow: str, device: str, metrics: list[str]) -> str: """Subscribe to one or more metrics for a DPL device. After subscribing, the driver will push update frames for the device @@ -172,60 +324,70 @@ async def subscribe(device: str, metrics: list[str]) -> str: the buffer. Args: + workflow: Workflow name as returned by connect. device: Device name exactly as shown by list_devices. metrics: List of metric names to subscribe to (from list_metrics). """ - await _send({"cmd": "subscribe", "device": device, "metrics": metrics}) + conn = _get(workflow) + await conn.send({"cmd": "subscribe", "device": device, "metrics": metrics}) return f"Subscribed to {len(metrics)} metric(s) for '{device}': {', '.join(metrics)}" @mcp.tool() -async def unsubscribe(device: str, metrics: list[str]) -> str: +async def unsubscribe(workflow: str, device: str, metrics: list[str]) -> str: """Stop receiving updates for specific metrics of a DPL device. Args: + workflow: Workflow name as returned by connect. device: Device name exactly as shown by list_devices. metrics: List of metric names to unsubscribe from. """ - await _send({"cmd": "unsubscribe", "device": device, "metrics": metrics}) + conn = _get(workflow) + await conn.send({"cmd": "unsubscribe", "device": device, "metrics": metrics}) return f"Unsubscribed from {len(metrics)} metric(s) for '{device}'." @mcp.tool() -async def subscribe_logs(device: str) -> str: +async def subscribe_logs(workflow: str, device: str) -> str: """Subscribe to log output for a DPL device. After subscribing, new log lines from the device will be buffered and can be retrieved with get_logs(). Args: + workflow: Workflow name as returned by connect. device: Device name exactly as shown by list_devices. """ - await _send({"cmd": "subscribe_logs", "device": device}) + conn = _get(workflow) + await conn.send({"cmd": "subscribe_logs", "device": device}) return f"Subscribed to logs for '{device}'." @mcp.tool() -async def unsubscribe_logs(device: str) -> str: +async def unsubscribe_logs(workflow: str, device: str) -> str: """Stop receiving log output for a DPL device. Args: + workflow: Workflow name as returned by connect. device: Device name exactly as shown by list_devices. """ - await _send({"cmd": "unsubscribe_logs", "device": device}) + conn = _get(workflow) + await conn.send({"cmd": "unsubscribe_logs", "device": device}) return f"Unsubscribed from logs for '{device}'." @mcp.tool() -async def get_logs(max_lines: int = 100) -> str: +async def get_logs(workflow: str, max_lines: int = 100) -> str: """Drain and return buffered log lines received since the last call. Args: + workflow: Workflow name as returned by connect. max_lines: Maximum number of log lines to return (default 100). """ - await _ensure_connected() - batch = _logs[:max_lines] - del _logs[:max_lines] + conn = _get(workflow) + await conn.ensure_connected() + batch = conn.logs[:max_lines] + del conn.logs[:max_lines] if not batch: return "No buffered log lines." lines = [] @@ -238,17 +400,21 @@ async def get_logs(max_lines: int = 100) -> str: @mcp.tool() -async def start_devices() -> str: +async def start_devices(workflow: str) -> str: """Resume all stopped DPL devices (send SIGCONT). Use this when the workflow was started with -s (all devices paused). + + Args: + workflow: Workflow name as returned by connect. """ - await _send({"cmd": "start_devices"}) + conn = _get(workflow) + await conn.send({"cmd": "start_devices"}) return "Sent SIGCONT to all active devices." @mcp.tool() -async def enable_signpost(device: str, streams: list[str]) -> str: +async def enable_signpost(workflow: str, device: str, streams: list[str]) -> str: """Enable one or more signpost log streams for a DPL device. Signpost streams produce detailed trace output visible in the device logs. @@ -259,27 +425,31 @@ async def enable_signpost(device: str, streams: list[str]) -> str: ch.cern.aliceo2.data_processor_context, ch.cern.aliceo2.stream_context. Args: + workflow: Workflow name as returned by connect. device: Device name as shown by list_devices, or "" for the driver. streams: List of full signpost log names to enable. """ - await _send({"cmd": "enable_signpost", "device": device, "streams": streams}) + conn = _get(workflow) + await conn.send({"cmd": "enable_signpost", "device": device, "streams": streams}) return f"Enabled {len(streams)} signpost stream(s) for '{device or 'driver'}': {', '.join(streams)}" @mcp.tool() -async def disable_signpost(device: str, streams: list[str]) -> str: +async def disable_signpost(workflow: str, device: str, streams: list[str]) -> str: """Disable one or more signpost log streams for a DPL device. Args: + workflow: Workflow name as returned by connect. device: Device name as shown by list_devices, or "" for the driver. streams: List of full signpost log names to disable. """ - await _send({"cmd": "disable_signpost", "device": device, "streams": streams}) + conn = _get(workflow) + await conn.send({"cmd": "disable_signpost", "device": device, "streams": streams}) return f"Disabled {len(streams)} signpost stream(s) for '{device or 'driver'}': {', '.join(streams)}" @mcp.tool() -async def get_updates(max_updates: int = 50) -> str: +async def get_updates(workflow: str, max_updates: int = 50) -> str: """Drain and return buffered metric update frames received since the last call. Each frame contains the latest values of all subscribed metrics that @@ -287,11 +457,13 @@ async def get_updates(max_updates: int = 50) -> str: time-ordered view of metric evolution. Args: + workflow: Workflow name as returned by connect. max_updates: Maximum number of update frames to return (default 50). """ - await _ensure_connected() - batch = _updates[:max_updates] - del _updates[:max_updates] + conn = _get(workflow) + await conn.ensure_connected() + batch = conn.updates[:max_updates] + del conn.updates[:max_updates] if not batch: return "No buffered updates." lines = [] @@ -310,34 +482,6 @@ async def get_updates(max_updates: int = 50) -> str: # Entry point # --------------------------------------------------------------------------- def main() -> None: - global _port - - parser = argparse.ArgumentParser( - description="DPL status MCP server — expose DPL driver metrics via MCP tools" - ) - group = parser.add_mutually_exclusive_group() - group.add_argument( - "--port", - type=int, - default=None, - help="TCP port of the DPL driver status WebSocket (default: 8080 or DPL_STATUS_PORT env var)", - ) - group.add_argument( - "--pid", - type=int, - default=None, - help="PID of the DPL driver process; port is derived as 8080 + pid %% 30000", - ) - args = parser.parse_args() - - if args.pid is not None: - _port = 8080 + args.pid % 30000 - elif args.port is not None: - _port = args.port - elif "DPL_STATUS_PORT" in os.environ: - _port = int(os.environ["DPL_STATUS_PORT"]) - # else leave _port at the default 8080 - mcp.run() diff --git a/Framework/Core/src/AnalysisHelpers.cxx b/Framework/Core/src/AnalysisHelpers.cxx index b7eac692d3859..149664c42caba 100644 --- a/Framework/Core/src/AnalysisHelpers.cxx +++ b/Framework/Core/src/AnalysisHelpers.cxx @@ -201,4 +201,5 @@ std::shared_ptr Builder::materialize(ProcessingContext& pc) result = o2::soa::IndexBuilder::materialize(*builders.get(), std::move(tables), records, outputSchema, exclusive); return result; } + } // namespace o2::framework diff --git a/Framework/Core/src/ArrowSupport.cxx b/Framework/Core/src/ArrowSupport.cxx index 780c836437c2b..eecff4ce87c74 100644 --- a/Framework/Core/src/ArrowSupport.cxx +++ b/Framework/Core/src/ArrowSupport.cxx @@ -34,6 +34,7 @@ #include "Framework/ServiceRegistryHelpers.h" #include "Framework/Signpost.h" #include "Framework/DefaultsHelpers.h" +#include "Framework/ConfigParamsHelper.h" #include "CommonMessageBackendsHelpers.h" #include @@ -637,6 +638,34 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() analysisCCDB->outputs.clear(); analysisCCDB->inputs.clear(); AnalysisSupportHelpers::addMissingOutputsToBuilder(dec.analysisCCDBInputs, dec.requestedAODs, dec.requestedDYNs, *analysisCCDB); + // Register each ccdb: column path as an actual device option on the CCDB + // device so it can be read from ConfigParamRegistry at runtime. + // If any analysis task declared a Configurable with the same + // "ccdb:fXxx" name, prefer its default over the compile-time ::query value. + // First encountered wins; log a warning if two tasks declare conflicting defaults. + for (auto& input : dec.analysisCCDBInputs) { + for (auto& m : input.metadata | std::views::filter(checks::has_params_with_name_starting("ccdb:"))) { + ConfigParamSpec effective = m; // start with compile-time default + bool foundFirst = false; + for (auto& d : workflow | views::exclude_by_name(analysisCCDB->name)) { + for (auto& opt : d.options) { + if (opt.name == m.name) { + if (!foundFirst) { + effective = opt; // first task Configurable wins + foundFirst = true; + } else if (opt.defaultValue.asString() != effective.defaultValue.asString()) { + LOGP(warn, "Task '{}' declares Configurable '{}' = '{}' which conflicts " + "with an earlier value '{}'; earlier value will be used.", + d.name, opt.name, opt.defaultValue.asString(), + effective.defaultValue.asString()); + } + break; + } + } + } + ConfigParamsHelper::addOptionIfMissing(analysisCCDB->options, effective); + } + } // load real AlgorithmSpec before deployment analysisCCDB->algorithm = PluginManager::loadAlgorithmFromPlugin("O2FrameworkCCDBSupport", "AnalysisCCDBFetcherPlugin", ctx); } diff --git a/Framework/Core/src/CommonServices.cxx b/Framework/Core/src/CommonServices.cxx index 0f53f5a6be5a1..2cdd046dedc34 100644 --- a/Framework/Core/src/CommonServices.cxx +++ b/Framework/Core/src/CommonServices.cxx @@ -589,6 +589,46 @@ auto decongestionCallbackOrdered = [](AsyncTask& task, size_t id) -> void { } }; +// Callback for consumeWhenPastOldestPossibleTimeframe. +// Runs in the async queue at the beginning of the next iteration, +// after Retry slots unblocked by an oldestPossibleInput change have +// been consumed and freed. Rescans all slots and forwards the +// (now up-to-date) oldestPossibleOutput downstream. +auto decongestionCallbackPastOldest = [](AsyncTask& task, size_t id) -> void { + auto& ref = task.user().ref; + + auto& decongestion = ref.get(); + auto& timesliceIndex = ref.get(); + auto& relayer = ref.get(); + auto& proxy = ref.get(); + O2_SIGNPOST_ID_GENERATE(cid, async_queue); + + timesliceIndex.rescan(); + timesliceIndex.updateOldestPossibleOutput(decongestion.nextEnumerationTimesliceRewinded); + auto oldestPossibleOutput = relayer.getOldestPossibleOutput(); + + if (oldestPossibleOutput.timeslice.value <= decongestion.lastTimeslice) { + O2_SIGNPOST_EVENT_EMIT(async_queue, cid, "oldest_possible_timeslice", + "consumeWhenPastOldestPossibleTimeframe: not forwarding already sent value %" PRIu64, + (uint64_t)oldestPossibleOutput.timeslice.value); + return; + } + O2_SIGNPOST_EVENT_EMIT(async_queue, cid, "oldest_possible_timeslice", + "consumeWhenPastOldestPossibleTimeframe: forwarding oldest possible timeslice %" PRIu64, + (uint64_t)oldestPossibleOutput.timeslice.value); + DataProcessingHelpers::broadcastOldestPossibleTimeslice(ref, oldestPossibleOutput.timeslice.value); + + for (int fi = 0; fi < proxy.getNumForwardChannels(); fi++) { + auto& info = proxy.getForwardChannelInfo(ChannelIndex{fi}); + auto& state = proxy.getForwardChannelState(ChannelIndex{fi}); + if (info.channelType != ChannelAccountingType::DPL) { + continue; + } + DataProcessingHelpers::sendOldestPossibleTimeframe(ref, info, state, oldestPossibleOutput.timeslice.value); + } + decongestion.lastTimeslice = oldestPossibleOutput.timeslice.value; +}; + // Decongestion service // If we do not have any Timeframe input, it means we must be creating timeslices // in order and that we should propagate the oldest possible timeslice at the end @@ -705,6 +745,22 @@ o2::framework::ServiceSpec timesliceIndex.updateOldestPossibleOutput(decongestion.nextEnumerationTimesliceRewinded); auto oldestPossibleOutput = relayer.getOldestPossibleOutput(); + // When consumeWhenPastOldestPossibleTimeframe is active, we always + // schedule the callback even when oldestPossibleOutput has not changed + // yet. Retry slots held by this policy will be consumed after this + // domainInfoUpdated call (once getReadyToProcess re-checks them), and + // the callback — running in the next iteration — will recompute + // oldestPossibleOutput and forward the updated value downstream. + if (decongestion.consumeWhenPastOldestPossibleTimeframeActive) { + auto& queue = services.get(); + AsyncQueueHelpers::post( + queue, AsyncTask{.timeslice = TimesliceId{oldestPossibleTimeslice}, + .id = decongestion.oldestPossibleTimesliceTask, + .debounce = -1, + .callback = decongestionCallbackPastOldest} + .user({.ref = services, .oldestPossibleOutput = oldestPossibleOutput})); + } + if (oldestPossibleOutput.timeslice.value == decongestion.lastTimeslice) { O2_SIGNPOST_EVENT_EMIT(data_processor_context, cid, "oldest_possible_timeslice", "Synchronous: Not sending already sent value: %" PRIu64, (uint64_t)oldestPossibleOutput.timeslice.value); return; diff --git a/Framework/Core/src/CompletionPolicyHelpers.cxx b/Framework/Core/src/CompletionPolicyHelpers.cxx index cc593ee7a2ed9..e5a91ae58f899 100644 --- a/Framework/Core/src/CompletionPolicyHelpers.cxx +++ b/Framework/Core/src/CompletionPolicyHelpers.cxx @@ -267,6 +267,8 @@ CompletionPolicy CompletionPolicyHelpers::consumeWhenAnyZeroCount(const char* na CompletionPolicy CompletionPolicyHelpers::consumeWhenPastOldestPossibleTimeframe(const char* name, CompletionPolicy::Matcher matcher) { auto callback = [](InputSpan const& inputs, std::vector const&, ServiceRegistryRef& ref) -> CompletionPolicy::CompletionOp { + auto& decongestionService = ref.get(); + decongestionService.consumeWhenPastOldestPossibleTimeframeActive = true; size_t currentTimeslice = -1; for (auto& input : inputs) { if (input.header == nullptr) { diff --git a/Framework/Core/src/ControlWebSocketHandler.cxx b/Framework/Core/src/ControlWebSocketHandler.cxx index 35528a1d6dfec..8d2f85b034364 100644 --- a/Framework/Core/src/ControlWebSocketHandler.cxx +++ b/Framework/Core/src/ControlWebSocketHandler.cxx @@ -14,10 +14,13 @@ #include "StatusWebSocketHandler.h" #include "Framework/DeviceMetricsHelper.h" #include "Framework/ServiceMetricsInfo.h" +#include "Framework/Signpost.h" #include #include "Framework/Logger.h" #include "Framework/DeviceConfigInfo.h" +O2_DECLARE_DYNAMIC_LOG(rate_limiting); + namespace o2::framework { void ControlWebSocketHandler::frame(char const* frame, size_t s) @@ -74,6 +77,10 @@ void ControlWebSocketHandler::endChunk() if (!didProcessMetric) { return; } + O2_SIGNPOST_ID_GENERATE(sid, rate_limiting); + O2_SIGNPOST_START(rate_limiting, sid, "endChunk", + "Processing metrics from device %zu (had new metric: %d)", + mIndex, (int)didHaveNewMetric); size_t timestamp = (uv_hrtime() - mContext.driver->startTime) / 1000000 + mContext.driver->startTimeMsFromEpoch; assert(mContext.metrics); assert(mContext.infos); @@ -91,6 +98,8 @@ void ControlWebSocketHandler::endChunk() for (auto& metricsInfo : *mContext.metrics) { std::fill(metricsInfo.changed.begin(), metricsInfo.changed.end(), false); } + O2_SIGNPOST_END(rate_limiting, sid, "endChunk", + "Done processing metrics from device %zu", mIndex); } void ControlWebSocketHandler::headers(std::map const& headers) diff --git a/Framework/Core/src/DataAllocator.cxx b/Framework/Core/src/DataAllocator.cxx index f0de6a40935b7..d7bfff0dbf19d 100644 --- a/Framework/Core/src/DataAllocator.cxx +++ b/Framework/Core/src/DataAllocator.cxx @@ -263,9 +263,19 @@ void DataAllocator::adopt(const Output& spec, LifetimeHolder& f // Serialization happens in here, so that we can // get rid of the intermediate tree 2 table object, saving memory. auto batch = source.finalize(); + if (!batch) { + // Do not throw here: this callback runs from ~LifetimeHolder which may + // execute during stack unwinding (e.g. if fill() failed). Throwing during + // unwinding calls std::terminate. + LOG(error) << "FragmentToBatch::finalize() returned null RecordBatch, skipping serialization"; + return; + } auto mock = std::make_shared(); int64_t expectedSize = 0; auto mockWriter = arrow::ipc::MakeStreamWriter(mock.get(), batch->schema()); + if (!mockWriter.ok()) { + throw std::runtime_error(fmt::format("Unable to create mock stream writer: {}", mockWriter.status().ToString())); + } arrow::Status outStatus = mockWriter.ValueOrDie()->WriteRecordBatch(*batch); expectedSize = mock->Tell().ValueOrDie(); @@ -275,6 +285,9 @@ void DataAllocator::adopt(const Output& spec, LifetimeHolder& f } auto deferredWriterStream = source.streamer(buffer); + if (!deferredWriterStream) { + throw std::runtime_error("FragmentToBatch streamer returned null OutputStream"); + } auto outBatch = arrow::ipc::MakeStreamWriter(deferredWriterStream, batch->schema()); if (outBatch.ok() == false) { diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index be25133158072..b45a48c28f691 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -1420,14 +1420,24 @@ void DataProcessingDevice::Run() } return missingInfo.empty() ? std::string(" (policy: ") + spec.resourcePolicy.name + ")" : " -" + missingInfo; }; + auto const timeSinceLastScheduled = lastSched ? uv_now(state.loop) - lastSched : 0; if (schedulingStats.numberOfUnscheduledSinceLastScheduled >= schedulingStats.nextWarnAt) { auto const missingStr = buildMissingInfo(); - O2_SIGNPOST_EVENT_EMIT_WARN(scheduling, sid, "Run", - "Not enough resources to schedule computation on stream %d. %zu consecutive skips%s. Missing:%s. Data is not lost and it will be scheduled again.", - streamRef.index, - schedulingStats.numberOfUnscheduledSinceLastScheduled.load(), - schedInfo.c_str(), - missingStr.c_str()); + if (timeSinceLastScheduled >= 50) { + O2_SIGNPOST_EVENT_EMIT_WARN(scheduling, sid, "Run", + "Not enough resources to schedule computation on stream %d. %zu consecutive skips%s. Missing:%s. Data is not lost and it will be scheduled again.", + streamRef.index, + schedulingStats.numberOfUnscheduledSinceLastScheduled.load(), + schedInfo.c_str(), + missingStr.c_str()); + } else { + O2_SIGNPOST_EVENT_EMIT(scheduling, sid, "Run", + "Not enough resources to schedule computation on stream %d. %zu consecutive skips%s. Missing:%s. Data is not lost and it will be scheduled again.", + streamRef.index, + schedulingStats.numberOfUnscheduledSinceLastScheduled.load(), + schedInfo.c_str(), + missingStr.c_str()); + } schedulingStats.nextWarnAt = schedulingStats.nextWarnAt * 2; } else { auto const missingStr = buildMissingInfo(); diff --git a/Framework/Core/src/DataProcessingStates.cxx b/Framework/Core/src/DataProcessingStates.cxx index 64be1829d8c97..8bf80e79b1839 100644 --- a/Framework/Core/src/DataProcessingStates.cxx +++ b/Framework/Core/src/DataProcessingStates.cxx @@ -111,9 +111,9 @@ void DataProcessingStates::updateState(CommandSpec cmd) // Add a static mutex to protect the queue // Get the next available operation in an atomic way. int size = sizeof(CommandHeader) + cmd.size; - if (size > 16384) { - throw runtime_error_f("State size is %d for state %s. States larger than 16384 bytes not supported for now.", - size, stateSpecs[cmd.id].name.c_str()); + if (size > STATES_BUFFER_SIZE / 8) { + throw runtime_error_f("State size is %d (data: %d bytes, header: %zu bytes) for state %s. States larger than %d bytes (1/8 of the states buffer) not supported. State data preview: %.100s", + size, cmd.size, sizeof(CommandHeader), stateSpecs[cmd.id].name.c_str(), STATES_BUFFER_SIZE / 8, cmd.data ? cmd.data : "(null)"); } int idx = nextState.fetch_sub(size, std::memory_order_relaxed); if (idx - size < 0) { diff --git a/Framework/Core/src/DecongestionService.h b/Framework/Core/src/DecongestionService.h index 1a42d3577bc0a..fe7fce8d640db 100644 --- a/Framework/Core/src/DecongestionService.h +++ b/Framework/Core/src/DecongestionService.h @@ -33,6 +33,8 @@ struct DecongestionService { int64_t nextTimeslice = 0; /// Ordered completion policy is active. bool orderedCompletionPolicyActive = false; + /// consumeWhenPastOldestPossibleTimeframe completion policy is active. + bool consumeWhenPastOldestPossibleTimeframeActive = false; // Task to enqueue the oldest possible timeslice propagation // at the end of any processing chain. o2::framework::AsyncTaskId oldestPossibleTimesliceTask = {0}; diff --git a/Framework/Core/src/DevicesManager.cxx b/Framework/Core/src/DevicesManager.cxx index e6fa2c2c61ae6..b427e72ca781d 100644 --- a/Framework/Core/src/DevicesManager.cxx +++ b/Framework/Core/src/DevicesManager.cxx @@ -13,12 +13,19 @@ #include "Framework/RuntimeError.h" #include "Framework/Logger.h" #include "Framework/DeviceController.h" +#include "Framework/Signpost.h" + +O2_DECLARE_DYNAMIC_LOG(devices_manager); namespace o2::framework { void DevicesManager::queueMessage(char const* target, char const* message) { + O2_SIGNPOST_ID_GENERATE(sid, devices_manager); + O2_SIGNPOST_EVENT_EMIT(devices_manager, sid, "queue", + "Queuing message for %{public}s: %{public}s", + target, message); for (int di = 0; di < specs.size(); ++di) { if (specs[di].id == target) { messages.push_back({di, message}); @@ -44,6 +51,10 @@ void DevicesManager::flush() LOGP(info, "Controller for {} now available.", specs[handle.ref.index].id); notifiedAvailable = true; } + O2_SIGNPOST_ID_GENERATE(sid, devices_manager); + O2_SIGNPOST_EVENT_EMIT(devices_manager, sid, "flush", + "Flushing message to %{public}s: %{public}s", + specs[handle.ref.index].id.c_str(), handle.message.c_str()); controller->write(handle.message.c_str(), handle.message.size()); } diff --git a/Framework/Core/src/FragmentToBatch.cxx b/Framework/Core/src/FragmentToBatch.cxx index 1a6c3bca3cc60..ada31be814fc8 100644 --- a/Framework/Core/src/FragmentToBatch.cxx +++ b/Framework/Core/src/FragmentToBatch.cxx @@ -44,7 +44,14 @@ void FragmentToBatch::fill(std::shared_ptr schema, std::shared_pt options->dataset_schema = schema; auto scanner = format->ScanBatchesAsync(options, mFragment); auto batch = (*scanner)(); - mRecordBatch = *batch.result(); + auto result = batch.result(); + if (!result.ok()) { + throw std::runtime_error("FragmentToBatch::fill: scan failed: " + result.status().ToString()); + } + mRecordBatch = *result; + if (!mRecordBatch) { + throw std::runtime_error("FragmentToBatch::fill: scan returned null RecordBatch"); + } // Notice that up to here the buffer was not yet filled. } diff --git a/Framework/Core/src/Plugin.cxx b/Framework/Core/src/Plugin.cxx index 503133442e794..82599310eafe9 100644 --- a/Framework/Core/src/Plugin.cxx +++ b/Framework/Core/src/Plugin.cxx @@ -70,6 +70,10 @@ auto lookForCommandLineAODOptions = [](ConfigParamRegistry& registry, int argc, O2_SIGNPOST_EVENT_EMIT(capabilities, sid, "DiscoverAODOptionsInCommandLineCapability", "AOD options found in arguments. Populating from them."); return true; } + if (arg.starts_with("--aod-origin-")) { + O2_SIGNPOST_EVENT_EMIT(capabilities, sid, "DiscoverAODOptionsInCommandLineCapability", "AOD options found in arguments. Populating from them."); + return true; + } } return false; }; @@ -150,7 +154,7 @@ struct DiscoverAODOptionsInCommandLine : o2::framework::ConfigDiscoveryPlugin { bool injectOption = true; for (size_t i = 0; i < argc; i++) { std::string_view arg = argv[i]; - if (!arg.starts_with("--aod-writer-") && !arg.starts_with("--aod-parent-")) { + if (!arg.starts_with("--aod-writer-") && !arg.starts_with("--aod-parent-") && !arg.starts_with("--aod-origin-")) { continue; } std::string key = arg.data() + 2; diff --git a/Framework/Core/src/WorkflowHelpers.cxx b/Framework/Core/src/WorkflowHelpers.cxx index 5f1c1eaee5544..9b80ef14d7621 100644 --- a/Framework/Core/src/WorkflowHelpers.cxx +++ b/Framework/Core/src/WorkflowHelpers.cxx @@ -627,12 +627,15 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext // Use the new dummy sink when the AOD reader is there O2_SIGNPOST_ID_GENERATE(sid, workflow_helpers); if (tfnsource != workflow.end()) { - O2_SIGNPOST_EVENT_EMIT(workflow_helpers, sid, "injectServiceDevices", "Injecting scheduled dummy sink"); - // if there is a tfnsource, make sure the sink gets TFN/TFF DataSpecUtils::updateInputList(ignored, InputSpec{"tfn", "TFN", "TFNumber", 0, Lifetime::Sporadic}); DataSpecUtils::updateInputList(ignored, InputSpec{"tff", "TFF", "TFFilename", 0, Lifetime::Sporadic}); + } + + if (tfnsource != workflow.end() && !tfnsource->name.starts_with("aod-producer-workflow")) { // any tfnsource except the aod-producer should use scheduled sink + O2_SIGNPOST_EVENT_EMIT(workflow_helpers, sid, "injectServiceDevices", "Injecting scheduled dummy sink"); + // if there is a tfnsource, make sure the sink gets TFN/TFF extraSpecs.push_back(CommonDataProcessors::getScheduledDummySink(ignored)); - } else { + } else { // if there is no tfn source or if that source is aod-producer-workflow, out-of-band channel is used to propagate the number of consumed timeframes O2_SIGNPOST_EVENT_EMIT(workflow_helpers, sid, "injectServiceDevices", "Injecting rate limited dummy sink"); std::string rateLimitingChannelConfigOutput; if (rateLimitingIPCID != -1) { diff --git a/Framework/Core/src/runDataProcessing.cxx b/Framework/Core/src/runDataProcessing.cxx index d012e1656efc4..c58f8e7287b3b 100644 --- a/Framework/Core/src/runDataProcessing.cxx +++ b/Framework/Core/src/runDataProcessing.cxx @@ -2052,6 +2052,7 @@ int runStateMachine(DataProcessorSpecs const& workflow, "--aod-max-io-rate", "--aod-parent-access-level", "--aod-parent-base-path-replacement", + "--aod-origin-level-mapping", "--driver-client-backend", "--fairmq-ipc-prefix", "--readers", diff --git a/Framework/Foundation/include/Framework/Signpost.h b/Framework/Foundation/include/Framework/Signpost.h index 51d1b0433b0de..0bbaaa5c37ed2 100644 --- a/Framework/Foundation/include/Framework/Signpost.h +++ b/Framework/Foundation/include/Framework/Signpost.h @@ -14,6 +14,7 @@ #include "Framework/CompilerBuiltins.h" #include #include +#include #ifdef __APPLE__ #include #endif diff --git a/Framework/GUISupport/src/FrameworkGUIDevicesGraph.cxx b/Framework/GUISupport/src/FrameworkGUIDevicesGraph.cxx index 1c4ddd7e6aabf..eeb9aeb44795e 100644 --- a/Framework/GUISupport/src/FrameworkGUIDevicesGraph.cxx +++ b/Framework/GUISupport/src/FrameworkGUIDevicesGraph.cxx @@ -41,8 +41,37 @@ struct NodeColor { using LogLevel = LogParsingHelpers::LogLevel; -NodeColor decideColorForNode(const DeviceInfo& info) +NodeColor decideColorForNode(const DeviceInfo& info, bool lightMode) { + if (lightMode) { + // Dark-on-bright: rich medium-dark cards on a white canvas, white text on nodes + if (info.active == false) { + return NodeColor{ + .normal = ImVec4(0xb5 / 255.f, 0x26 / 255.f, 0x18 / 255.f, 1), // dark crimson + .hovered = ImVec4(0xc2 / 255.f, 0x2d / 255.f, 0x1d / 255.f, 1)}; + } + switch (info.streamingState) { + case StreamingState::EndOfStreaming: + return NodeColor{ + .normal = ImVec4(0x8c / 255.f, 0x6c / 255.f, 0x00 / 255.f, 1), // dark amber + .hovered = ImVec4(0x9e / 255.f, 0x7a / 255.f, 0x00 / 255.f, 1), + .title = ImVec4(0x6e / 255.f, 0x54 / 255.f, 0x00 / 255.f, 1), + .title_hovered = ImVec4(0x5a / 255.f, 0x44 / 255.f, 0x00 / 255.f, 1)}; + case StreamingState::Idle: + return NodeColor{ + .normal = ImVec4(0x1a / 255.f, 0x80 / 255.f, 0x40 / 255.f, 1), // dark forest green + .hovered = ImVec4(0x22 / 255.f, 0x8b / 255.f, 0x47 / 255.f, 1), + .title = ImVec4(0x11 / 255.f, 0x60 / 255.f, 0x2e / 255.f, 1), + .title_hovered = ImVec4(0x0a / 255.f, 0x4d / 255.f, 0x23 / 255.f, 1)}; + case StreamingState::Streaming: + default: + return NodeColor{ + .normal = ImVec4(0x3a / 255.f, 0x3a / 255.f, 0x3c / 255.f, 1), // macOS tertiary dark + .hovered = ImVec4(0x48 / 255.f, 0x48 / 255.f, 0x4a / 255.f, 1), + .title = ImVec4(0x2c / 255.f, 0x2c / 255.f, 0x2e / 255.f, 1), + .title_hovered = ImVec4(0x1c / 255.f, 0x1c / 255.f, 0x1e / 255.f, 1)}; + } + } if (info.active == false) { return NodeColor{ .normal = PaletteHelpers::RED, @@ -82,7 +111,7 @@ const static ImColor ARROW_BACKGROUND_COLOR = {100, 100, 0}; const static ImColor ARROW_HALFGROUND_COLOR = {170, 170, 70}; const static ImColor ARROW_COLOR = {200, 200, 100}; const static ImColor ARROW_SELECTED_COLOR = {200, 0, 100}; -const static ImU32 GRID_COLOR = ImColor(200, 200, 200, 40); +const static ImU32 GRID_COLOR = ImColor(150, 150, 150, 80); const static ImColor NODE_BORDER_COLOR = {100, 100, 100}; const static ImColor LEGEND_COLOR = {100, 100, 100}; @@ -508,6 +537,8 @@ void showTopologyNodeGraph(WorkspaceGUIState& state, ImGui::SameLine(); ImGui::Checkbox("Show legend", &show_legend); ImGui::SameLine(); + ImGui::Checkbox("Light mode", &state.topologyLightMode); + ImGui::SameLine(); if (ImGui::Button("Center")) { scrolling = ImVec2(0., 0.); } @@ -577,11 +608,10 @@ void showTopologyNodeGraph(WorkspaceGUIState& state, ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(1, 1)); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); -#if defined(ImGuiCol_ChildWindowBg) - ImGui::PushStyleColor(ImGuiCol_ChildWindowBg, (ImU32)ImColor(60, 60, 70, 200)); -#else - ImGui::PushStyleColor(ImGuiCol_WindowBg, (ImU32)ImColor(60, 60, 70, 200)); -#endif + auto canvasBg = state.topologyLightMode ? (ImU32)ImColor(250, 250, 252, 255) : (ImU32)ImColor(44, 44, 46, 255); + auto canvasText = (ImU32)ImColor(235, 235, 245, 255); // nodes are always dark, so text is always light + ImGui::PushStyleColor(ImGuiCol_ChildBg, canvasBg); + ImGui::PushStyleColor(ImGuiCol_Text, canvasText); ImVec2 graphSize = ImGui::GetWindowSize(); if (state.leftPaneVisible) { graphSize.x -= state.leftPaneSize; @@ -604,6 +634,12 @@ void showTopologyNodeGraph(WorkspaceGUIState& state, ImVec2 win_pos = ImGui::GetCursorScreenPos(); ImVec2 canvas_sz = ImGui::GetWindowSize(); + + // Arrow colors — richer amber in light mode to stand out on white canvas + const ImColor arrowBgColor = state.topologyLightMode ? ImColor(140, 80, 0) : ARROW_BACKGROUND_COLOR; + const ImColor arrowHalfColor = state.topologyLightMode ? ImColor(180, 110, 0) : ARROW_HALFGROUND_COLOR; + const ImColor arrowColor = state.topologyLightMode ? ImColor(220, 140, 0) : ARROW_COLOR; + // Display links but only if they are inside the view. for (int link_idx = 0; link_idx < links.Size; link_idx++) { // Do the geometry culling upfront. @@ -627,7 +663,7 @@ void showTopologyNodeGraph(WorkspaceGUIState& state, continue; } draw_list->ChannelsSetCurrent(0); // Background - auto color = ARROW_BACKGROUND_COLOR; + auto color = arrowBgColor; auto thickness = ARROW_BACKGROUND_THICKNESS; bool p1Inside = false; @@ -643,12 +679,12 @@ void showTopologyNodeGraph(WorkspaceGUIState& state, if (p1Inside && p2Inside) { // Whatever the two edges completely within the view, gets brighter color and foreground. draw_list->ChannelsSetCurrent(2); - color = ARROW_COLOR; + color = arrowColor; thickness = ARROW_THICKNESS; } else if (p1Inside || p2Inside) { draw_list->ChannelsSetCurrent(1); // Whenever one of the two ends is within the view, increase the color but keep the background - color = ARROW_HALFGROUND_COLOR; + color = arrowHalfColor; thickness = ARROW_HALFGROUND_THICKNESS; } @@ -756,7 +792,7 @@ void showTopologyNodeGraph(WorkspaceGUIState& state, scrolling = scrolling - ImVec2(ImGui::GetIO().MouseDelta.x / 4.f, ImGui::GetIO().MouseDelta.y / 4.f); } - auto nodeBg = decideColorForNode(info); + auto nodeBg = decideColorForNode(info, state.topologyLightMode); auto hovered = (node_hovered_in_list == node->ID || node_hovered_in_scene == node->ID || (node_hovered_in_list == -1 && node_selected == node->ID)); ImVec4 nodeBgColor = hovered ? nodeBg.hovered : nodeBg.normal; @@ -776,7 +812,7 @@ void showTopologyNodeGraph(WorkspaceGUIState& state, auto pp1 = p1 + offset + slotPos; auto pp2 = p2 + offset + slotPos; auto pp3 = p3 + offset + slotPos; - auto color = ARROW_COLOR; + auto color = arrowColor; if (node_idx == node_selected) { color = ARROW_SELECTED_COLOR; } @@ -881,7 +917,7 @@ void showTopologyNodeGraph(WorkspaceGUIState& state, ImGui::PopItemWidth(); ImGui::EndChild(); - ImGui::PopStyleColor(); + ImGui::PopStyleColor(2); ImGui::PopStyleVar(2); ImGui::EndGroup(); diff --git a/Framework/GUISupport/src/FrameworkGUIState.h b/Framework/GUISupport/src/FrameworkGUIState.h index 8a1cd8dd3bd1e..d36b6c6120fc0 100644 --- a/Framework/GUISupport/src/FrameworkGUIState.h +++ b/Framework/GUISupport/src/FrameworkGUIState.h @@ -34,6 +34,7 @@ struct WorkspaceGUIState { bool leftPaneVisible; bool rightPaneVisible; bool bottomPaneVisible; + bool topologyLightMode; double startTime; }; diff --git a/Framework/GUISupport/src/PaletteHelpers.cxx b/Framework/GUISupport/src/PaletteHelpers.cxx index 1ad04ce7c7f3f..fa19b0dd43319 100644 --- a/Framework/GUISupport/src/PaletteHelpers.cxx +++ b/Framework/GUISupport/src/PaletteHelpers.cxx @@ -13,20 +13,35 @@ namespace o2::framework { -const ImVec4 PaletteHelpers::RED = ImVec4(0.945, 0.094, 0.298, 1); -const ImVec4 PaletteHelpers::GREEN = ImVec4(0x7e / 255., 0xc4 / 255., 0x52 / 255., 1); -const ImVec4 PaletteHelpers::BLUE = ImVec4(0x3d / 255., 0xb7 / 255., 0xe0 / 255., 1); -const ImVec4 PaletteHelpers::YELLOW = ImVec4(0.949, 0.769, 0.239, 1); -const ImVec4 PaletteHelpers::SHADED_RED = ImVec4(0xd5 / 255., 0x72 / 255., 0x73 / 255., 1); -const ImVec4 PaletteHelpers::SHADED_GREEN = ImVec4(0x98 / 255., 0xba / 255., 0x96 / 255., 1); -const ImVec4 PaletteHelpers::SHADED_BLUE = ImVec4(0x7a / 255., 0xab / 255., 0xea / 255., 1); -const ImVec4 PaletteHelpers::SHADED_YELLOW = ImVec4(0xeb / 255., 0xb9 / 255., 0x7a / 255., 1); -const ImVec4 PaletteHelpers::DARK_RED = ImVec4(0xd4 / 255., 0x06 / 255., 0x02 / 255., 255. / 255.); -const ImVec4 PaletteHelpers::DARK_GREEN = ImVec4(153. / 255., 61. / 255., 61. / 255., 255. / 255.); -const ImVec4 PaletteHelpers::DARK_YELLOW = ImVec4(0xf1 / 255., 0x9b / 255., 0x2c / 255., 255. / 255.); -const ImVec4 PaletteHelpers::WHITE = ImVec4(0xce / 255., 0xbe / 255., 0x91 / 255., 1); -const ImVec4 PaletteHelpers::BLACK = ImVec4(0x28 / 255., 0x28 / 255., 0x28 / 255., 1); -const ImVec4 PaletteHelpers::GRAY = ImVec4(60 / 255., 60 / 255., 60 / 255., 1); -const ImVec4 PaletteHelpers::LIGHT_GRAY = ImVec4(75 / 255., 75 / 255., 75 / 255., 1); +// Vivid accent colors — macOS system color palette / Pantone-adjacent +// RED: macOS Red (#FF3B30) / Pantone 485 C adjacent +const ImVec4 PaletteHelpers::RED = ImVec4(0xff / 255., 0x3b / 255., 0x30 / 255., 1); +// GREEN: macOS Green (#34C759) / Pantone 368 C adjacent +const ImVec4 PaletteHelpers::GREEN = ImVec4(0x34 / 255., 0xc7 / 255., 0x59 / 255., 1); +// BLUE: macOS Blue (#007AFF) / Pantone 2728 C adjacent +const ImVec4 PaletteHelpers::BLUE = ImVec4(0x00 / 255., 0x7a / 255., 0xff / 255., 1); +// YELLOW: macOS Yellow (#FFCC00) / Pantone 116 C adjacent +const ImVec4 PaletteHelpers::YELLOW = ImVec4(0xff / 255., 0xcc / 255., 0x00 / 255., 1); +// Muted/shaded variants — desaturated for secondary use +const ImVec4 PaletteHelpers::SHADED_RED = ImVec4(0xff / 255., 0x69 / 255., 0x61 / 255., 1); +const ImVec4 PaletteHelpers::SHADED_GREEN = ImVec4(0x86 / 255., 0xd9 / 255., 0x88 / 255., 1); +const ImVec4 PaletteHelpers::SHADED_BLUE = ImVec4(0x5a / 255., 0xc8 / 255., 0xfa / 255., 1); +const ImVec4 PaletteHelpers::SHADED_YELLOW = ImVec4(0xff / 255., 0xd6 / 255., 0x0a / 255., 1); +// Dark variants — for title bars and hovered states +// DARK_RED: Pantone 485 C (#DA291C) +const ImVec4 PaletteHelpers::DARK_RED = ImVec4(0xda / 255., 0x29 / 255., 0x1c / 255., 1); +// DARK_GREEN: (#1E8449) +const ImVec4 PaletteHelpers::DARK_GREEN = ImVec4(0x1e / 255., 0x84 / 255., 0x49 / 255., 1); +// DARK_YELLOW: macOS Orange (#FF9F0A) / Pantone 137 C adjacent +const ImVec4 PaletteHelpers::DARK_YELLOW = ImVec4(0xff / 255., 0x9f / 255., 0x0a / 255., 1); +// Neutrals — macOS dark mode system backgrounds +// WHITE: used as primary text / highlight color in dark UI +const ImVec4 PaletteHelpers::WHITE = ImVec4(0xf5 / 255., 0xf5 / 255., 0xf7 / 255., 1); +// BLACK: macOS dark background (#1C1C1E) +const ImVec4 PaletteHelpers::BLACK = ImVec4(0x1c / 255., 0x1c / 255., 0x1e / 255., 1); +// GRAY: macOS secondary background (#2C2C2E) +const ImVec4 PaletteHelpers::GRAY = ImVec4(0x2c / 255., 0x2c / 255., 0x2e / 255., 1); +// LIGHT_GRAY: macOS tertiary background (#3A3A3C) +const ImVec4 PaletteHelpers::LIGHT_GRAY = ImVec4(0x3a / 255., 0x3a / 255., 0x3c / 255., 1); } // namespace o2::framework diff --git a/Framework/TestWorkflows/src/o2TestAnalysisCCDB.cxx b/Framework/TestWorkflows/src/o2TestAnalysisCCDB.cxx index f9684762539f7..3cf20d9ff5296 100644 --- a/Framework/TestWorkflows/src/o2TestAnalysisCCDB.cxx +++ b/Framework/TestWorkflows/src/o2TestAnalysisCCDB.cxx @@ -51,8 +51,11 @@ struct DummyTimestampsTable { }; struct SimpleCCDBConsumer { + ConfigurableCCDBPath lhcPhasePath; + void process(o2::aod::TOFCalibrationObjects const& ccdbObjectsForAllTimestamps) { + LOGP(info, "LHCphase CCDB path configurable value: {}", lhcPhasePath.value); LOGP(info, "Looking at all the LHCphases associated to the timestamps"); for (auto& object : ccdbObjectsForAllTimestamps) { std::cout << object.lhcPhase().getStartValidity() << " " << object.lhcPhase().getEndValidity() << std::endl; @@ -60,10 +63,23 @@ struct SimpleCCDBConsumer { } }; +struct AnotherCCDBConsumer { + ConfigurableCCDBPath lhcPhasePath; + + void process(o2::aod::TOFCalibrationObjects const& ccdbObjectsForAllTimestamps) + { + LOGP(info, "AnotherCCDBConsumer LHCphase CCDB path configurable value: {}", lhcPhasePath.value); + for (auto& object : ccdbObjectsForAllTimestamps) { + std::cout << object.lhcPhase().getStartValidity() << " " << object.lhcPhase().getEndValidity() << std::endl; + } + } +}; + WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ adaptAnalysisTask(cfgc), - adaptAnalysisTask(cfgc, TaskName{"simple-ccdb-cunsumer"}), + adaptAnalysisTask(cfgc, TaskName{"simple-ccdb-consumer"}), + adaptAnalysisTask(cfgc, TaskName{"another-ccdb-consumer"}), }; } diff --git a/GPU/Common/GPUCommonMath.h b/GPU/Common/GPUCommonMath.h index 0ff31899dec0c..8f81762d87373 100644 --- a/GPU/Common/GPUCommonMath.h +++ b/GPU/Common/GPUCommonMath.h @@ -108,6 +108,7 @@ class GPUCommonMath GPUd() constexpr static float QuietNaN() { return GPUCA_CHOICE(std::numeric_limits::quiet_NaN(), __builtin_nanf(""), nan(0u)); } #endif GPUd() constexpr static uint32_t Clz(uint32_t val); + GPUd() constexpr static uint32_t Ctz(uint32_t val); GPUd() constexpr static uint32_t Popcount(uint32_t val); GPUd() static void memcpy(void* dst, const void* src, size_t size); @@ -332,6 +333,20 @@ GPUdi() constexpr uint32_t GPUCommonMath::Clz(uint32_t x) #endif } +GPUdi() constexpr uint32_t GPUCommonMath::Ctz(uint32_t x) +{ +#if (defined(__GNUC__) || defined(__clang__) || defined(__CUDACC__) || defined(__HIPCC__)) + return x == 0 ? 32 : GPUCA_CHOICE(__builtin_ctz(x), __ffs(x) - 1, __builtin_ctz(x)); +#else + for (uint32_t i = 0; i < 32; ++i) { + if (x & (1u << i)) { + return i; + } + } + return 32; +#endif +} + GPUdi() constexpr uint32_t GPUCommonMath::Popcount(uint32_t x) { #if (defined(__GNUC__) || defined(__clang__) || defined(__CUDACC__) || defined(__HIPCC__)) && !defined(__OPENCL__) // TODO: remove OPENCL when reported SPIR-V bug is fixed diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceRefit.h b/GPU/GPUTracking/Interface/GPUO2InterfaceRefit.h index 0af10dbe4f388..f85a376b9185a 100644 --- a/GPU/GPUTracking/Interface/GPUO2InterfaceRefit.h +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceRefit.h @@ -16,6 +16,7 @@ #define GPUO2INTERFACEREFIT_H #include "GPUO2ExternalUser.h" +#include #include #include #include diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.h b/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.h index 813444470082e..92f87cee66232 100644 --- a/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.h +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.h @@ -15,6 +15,7 @@ #ifndef GPUO2INTERFACEUTILS_H #define GPUO2INTERFACEUTILS_H +#include #include #include diff --git a/GPU/TPCFastTransformation/CorrectionMapsHelper.cxx b/GPU/TPCFastTransformation/CorrectionMapsHelper.cxx index 7f7deddafe1c8..4bfedc117dec7 100644 --- a/GPU/TPCFastTransformation/CorrectionMapsHelper.cxx +++ b/GPU/TPCFastTransformation/CorrectionMapsHelper.cxx @@ -38,6 +38,10 @@ void CorrectionMapsHelper::setCorrMapMShape(std::unique_ptr&& void CorrectionMapsHelper::updateLumiScale(bool report) { if (!canUseCorrections()) { + if (mLumiScaleMode != LumiScaleMode::NoCorrection) { + LOGP(warning, "Negative meanLumi={} detected, switching to NoCorrection mode for backward compatibility", mMeanLumi); + mLumiScaleMode = LumiScaleMode::NoCorrection; + } mLumiScale = -1.f; } else if ((mLumiScaleMode == LumiScaleMode::DerivativeMap) || (mLumiScaleMode == LumiScaleMode::DerivativeMapMC)) { mLumiScale = mMeanLumiRef ? (mInstLumi - mMeanLumi) / mMeanLumiRef : 0.f; @@ -54,7 +58,40 @@ void CorrectionMapsHelper::updateLumiScale(bool report) //________________________________________________________ void CorrectionMapsHelper::reportScaling() { - LOGP(info, "Map scaling update: LumiScaleType={} instLumi(CTP)={} instLumi(scaling)={} meanLumiRef={}, meanLumi={} -> LumiScale={} lumiScaleMode={}, M-Shape map valid: {}, M-Shape default: {}", - mLumiScaleType == LumiScaleType::NoScaling ? "NoScaling" : (mLumiScaleType == LumiScaleType::CTPLumi ? "LumiCTP" : "TPCScaler"), getInstLumiCTP(), getInstLumi(), getMeanLumiRef(), getMeanLumi(), getLumiScale(), - mLumiScaleMode == LumiScaleMode::Linear ? "Linear" : "Derivative", (mCorrMapMShape != nullptr), isCorrMapMShapeDummy()); + auto lumiTypeName = [](LumiScaleType t) { + switch (t) { + case LumiScaleType::NoScaling: + return "NoScaling"; + case LumiScaleType::CTPLumi: + return "CTPLumi"; + case LumiScaleType::TPCScaler: + return "TPCScaler"; + default: + return "Unknown"; + } + }; + + const bool mshapeValid = (mCorrMapMShape != nullptr) && !isCorrMapMShapeDummy(); + + if (mLumiScaleMode == LumiScaleMode::NoCorrection) { + LOGP(info, "Map scaling update: mode=NoCorrection (corrections disabled, dummy map in use)"); + } else if (mLumiScaleMode == LumiScaleMode::StaticMapOnly) { + LOGP(info, "Map scaling update: mode=StaticMapOnly (static reference map, no lumi scaling), M-Shape correction: {}", mshapeValid ? "applied" : "not applied"); + } else { + auto lumiModeName = [](LumiScaleMode m) { + switch (m) { + case LumiScaleMode::Linear: + return "Linear"; + case LumiScaleMode::DerivativeMap: + return "DerivativeMap"; + case LumiScaleMode::DerivativeMapMC: + return "DerivativeMapMC"; + default: + return "Unknown"; + } + }; + LOGP(info, "Map scaling update: LumiScaleType={} instLumi(CTP)={} instLumi(scaling)={} meanLumiRef={} meanLumi={} -> LumiScale={} lumiScaleMode={}, M-Shape correction: {}", + lumiTypeName(mLumiScaleType), getInstLumiCTP(), getInstLumi(), getMeanLumiRef(), getMeanLumi(), getLumiScale(), + lumiModeName(mLumiScaleMode), mshapeValid ? "applied" : "not applied"); + } } diff --git a/GPU/TPCFastTransformation/CorrectionMapsTypes.h b/GPU/TPCFastTransformation/CorrectionMapsTypes.h index e239b668ab751..092a2927ebe3e 100644 --- a/GPU/TPCFastTransformation/CorrectionMapsTypes.h +++ b/GPU/TPCFastTransformation/CorrectionMapsTypes.h @@ -22,14 +22,18 @@ enum class LumiScaleType : int { Unset = -1, ///< init value NoScaling = 0, ///< no scaling, use map as is CTPLumi = 1, ///< use CTP luminosity for scaling - TPCScaler = 2 ///< use TPC scaler for scaling + TPCScaler = 2, ///< use TPC scaler for scaling + Count ///< sentinel - keep last }; enum class LumiScaleMode : int { - Unset = -1, ///< init value - Linear = 0, ///< map(lumi) = (mean_map - referenceMap) * lumiScale + referenceMap - DerivativeMap = 1, ///< map(lumi) = mean_map + lumiScale * (derivativeMap) where derivativeMap = (mean_map_A - mean_map_B) - DerivativeMapMC = 2 ///< same DerivativeMap, but for MC + Unset = -1, ///< init value + Linear = 0, ///< map(lumi) = (mean_map - referenceMap) * lumiScale + referenceMap + DerivativeMap = 1, ///< map(lumi) = mean_map + lumiScale * (derivativeMap) where derivativeMap = (mean_map_A - mean_map_B) + DerivativeMapMC = 2, ///< same DerivativeMap, but for MC + NoCorrection = 3, ///< no corrections at all + StaticMapOnly = 4, ///< use only static map instead of main map + Count ///< sentinel - keep last }; struct CorrectionMapsGloOpts { diff --git a/prodtests/full-system-test/aggregator-workflow.sh b/prodtests/full-system-test/aggregator-workflow.sh index 0dc30df93669d..18568f2b94388 100755 --- a/prodtests/full-system-test/aggregator-workflow.sh +++ b/prodtests/full-system-test/aggregator-workflow.sh @@ -155,25 +155,35 @@ if workflow_has_parameter CALIB_PROXIES; then if [[ -n ${CALIBDATASPEC_BARREL_SPORADIC:-} ]]; then add_W o2-dpl-raw-proxy "--dataspec \"$CALIBDATASPEC_BARREL_SPORADIC\" $(get_proxy_connection barrel_sp input sporadic)" "" 0 fi - elif [[ $AGGREGATOR_TASKS == TPC_IDCBOTH_SAC ]]; then + elif [[ $AGGREGATOR_TASKS == TPC_IDCBOTH_SAC || $AGGREGATOR_TASKS == TPC_CMV ]]; then if [[ $EPNSYNCMODE != 1 ]]; then - echo "ERROR: TPC IDC / SAC calib workflow enabled without EPNSYNCMODE, please note that there will not be input data for it" 1>&2 + echo "ERROR: TPC IDC / SAC / CMV calib workflow enabled without EPNSYNCMODE, please note that there will not be input data for it" 1>&2 fi CHANNELS_LIST= [[ $EPNSYNCMODE == 0 ]] && FLP_ADDRESS="tcp://localhost:29950" if [[ -n ${CALIBDATASPEC_TPCIDC_A:-} ]] || [[ -n ${CALIBDATASPEC_TPCIDC_C:-} ]] || [[ -n ${CALIBDATASPEC_TPCCMV:-} ]]; then - # define port for FLP - : ${TPC_IDC_FLP_PORT:=29950} + # define port for FLP and channel prefix + TPC_FLP_PORT= + TPC_FLP_CHAN_PREFIX= + if [[ $AGGREGATOR_TASKS == TPC_CMV ]] && [[ -n ${CALIBDATASPEC_TPCCMV:-} ]]; then + TPC_FLP_PORT=29952 + TPC_FLP_CHAN_PREFIX=tpccmv + elif [[ $AGGREGATOR_TASKS == TPC_IDCBOTH_SAC ]] && [[ -n ${CALIBDATASPEC_TPCIDC_A:-} || -n ${CALIBDATASPEC_TPCIDC_C:-} ]]; then + TPC_FLP_PORT=29950 + TPC_FLP_CHAN_PREFIX=tpcidc + fi # expand FLPs; TPC uses from 001 to 145, but 145 is reserved for SAC - if [[ "${GEN_TOPO_DEPLOYMENT_TYPE:-}" == "ALICE_STAGING" ]]; then - FLP_ADDRESS="tcp://alio2-cr1-mvs03-ib:${TPC_IDC_FLP_PORT}" - CHANNELS_LIST+="type=pull,name=tpcidc_flp,transport=zeromq,address=$FLP_ADDRESS,method=connect,rateLogging=10;" - else - for flp in $(seq -f "%03g" 1 144); do - [[ ! $FLP_IDS =~ (^|,)"$flp"(,|$) ]] && continue - [[ $EPNSYNCMODE == 1 ]] && FLP_ADDRESS="tcp://alio2-cr1-flp${flp}-ib:${TPC_IDC_FLP_PORT}" - CHANNELS_LIST+="type=pull,name=tpcidc_flp${flp},transport=zeromq,address=$FLP_ADDRESS,method=connect,rateLogging=10;" - done + if [[ -n $TPC_FLP_PORT ]]; then + if [[ "${GEN_TOPO_DEPLOYMENT_TYPE:-}" == "ALICE_STAGING" ]]; then + FLP_ADDRESS="tcp://alio2-cr1-mvs03-ib:${TPC_FLP_PORT}" + CHANNELS_LIST+="type=pull,name=${TPC_FLP_CHAN_PREFIX}_flp,transport=zeromq,address=$FLP_ADDRESS,method=connect,rateLogging=10;" + else + for flp in $(seq -f "%03g" 1 144); do + [[ ! $FLP_IDS =~ (^|,)"$flp"(,|$) ]] && continue + [[ $EPNSYNCMODE == 1 ]] && FLP_ADDRESS="tcp://alio2-cr1-flp${flp}-ib:${TPC_FLP_PORT}" + CHANNELS_LIST+="type=pull,name=${TPC_FLP_CHAN_PREFIX}_flp${flp},transport=zeromq,address=$FLP_ADDRESS,method=connect,rateLogging=10;" + done + fi fi fi if [[ -n ${CALIBDATASPEC_TPCSAC:-} ]]; then @@ -184,22 +194,25 @@ if workflow_has_parameter CALIB_PROXIES; then fi if [[ -n $CHANNELS_LIST ]]; then DATASPEC_LIST= - if [[ -n ${CALIBDATASPEC_TPCIDC_A:-} ]]; then - add_semicolon_separated DATASPEC_LIST "\"$CALIBDATASPEC_TPCIDC_A\"" - fi - if [[ -n ${CALIBDATASPEC_TPCIDC_C:-} ]]; then - add_semicolon_separated DATASPEC_LIST "\"$CALIBDATASPEC_TPCIDC_C\"" - fi - if [[ -n ${CALIBDATASPEC_TPCCMV:-} ]]; then - add_semicolon_separated DATASPEC_LIST "\"$CALIBDATASPEC_TPCCMV\"" - fi - if [[ -n ${CALIBDATASPEC_TPCSAC:-} ]]; then - add_semicolon_separated DATASPEC_LIST "\"$CALIBDATASPEC_TPCSAC\"" + if [[ $AGGREGATOR_TASKS == TPC_CMV ]]; then + if [[ -n ${CALIBDATASPEC_TPCCMV:-} ]]; then + add_semicolon_separated DATASPEC_LIST "\"$CALIBDATASPEC_TPCCMV\"" + fi + else + if [[ -n ${CALIBDATASPEC_TPCIDC_A:-} ]]; then + add_semicolon_separated DATASPEC_LIST "\"$CALIBDATASPEC_TPCIDC_A\"" + fi + if [[ -n ${CALIBDATASPEC_TPCIDC_C:-} ]]; then + add_semicolon_separated DATASPEC_LIST "\"$CALIBDATASPEC_TPCIDC_C\"" + fi + if [[ -n ${CALIBDATASPEC_TPCSAC:-} ]]; then + add_semicolon_separated DATASPEC_LIST "\"$CALIBDATASPEC_TPCSAC\"" + fi fi if [[ -z ${O2_TPC_IDC_CMV_IO_THREADS:-} ]]; then O2_TPC_IDC_CMV_IO_THREADS=4; - fi - add_W o2-dpl-raw-proxy "--proxy-name tpcidc --io-threads ${O2_TPC_IDC_CMV_IO_THREADS} --dataspec \"$DATASPEC_LIST\" --sporadic-outputs --channel-config \"$CHANNELS_LIST\" ${TIMEFRAME_SHM_LIMIT+--timeframes-shm-limit} $TIMEFRAME_SHM_LIMIT" "" 0 + fi + add_W o2-dpl-raw-proxy "--proxy-name ${TPC_FLP_CHAN_PREFIX} --io-threads ${O2_TPC_IDC_CMV_IO_THREADS} --dataspec \"$DATASPEC_LIST\" --sporadic-outputs --channel-config \"$CHANNELS_LIST\" ${TIMEFRAME_SHM_LIMIT+--timeframes-shm-limit} $TIMEFRAME_SHM_LIMIT" "" 0 fi elif [[ $AGGREGATOR_TASKS == CALO_TF ]]; then if [[ -n ${CALIBDATASPEC_CALO_TF:-} ]]; then @@ -308,7 +321,9 @@ nTFs=$((1000 * 128 / ${NHBPERTF})) nTFs_SAC=$((10000 * 128 / ${NHBPERTF})) nBuffer=$((100 * 128 / ${NHBPERTF})) nBuffer_cmv=$((50 * 128 / ${NHBPERTF})) -lanesCMVaggregate=${O2_TPC_CMV_AGGREGATE_NLANES:-8} +lanesCMVaggregate=${O2_TPC_CMV_AGGREGATE_NLANES:-4} +cmvCompression=${O2_TPC_CMV_COMPRESSION:---use-sparse --cmv-zero-threshold 1.0 --cmv-dynamic-precision-mean 1.0 --cmv-dynamic-precision-sigma 8.0 --use-compression-huffman} +cmvTimeframes=${O2_TPC_CMV_TIMEFRAMES:-4000} IDC_DELTA="--disable-IDCDelta true" # off by default # deltas are on by default; you need to request explicitly to switch them off; if [[ "${DISABLE_IDC_DELTA:-}" == "1" ]]; then IDC_DELTA=""; fi @@ -316,24 +331,25 @@ if [[ "${ENABLE_IDC_DELTA_FILE:-}" == "1" ]]; then IDC_DELTA+=" --dump-IDCDelta- if [[ "${DISABLE_IDC_PAD_MAP_WRITING:-}" == 1 ]]; then TPC_WRITING_PAD_STATUS_MAP=""; else TPC_WRITING_PAD_STATUS_MAP="--enableWritingPadStatusMap true"; fi -if ! workflow_has_parameter CALIB_LOCAL_INTEGRATED_AGGREGATOR && [[ $AGGREGATOR_TASKS == TPC_IDCBOTH_SAC || $AGGREGATOR_TASKS == ALL ]]; then - if [[ $CALIB_TPC_IDC == 1 ]]; then - add_W o2-tpc-idc-distribute "--crus ${crus} --timeframes ${nTFs} --output-lanes ${lanesFactorize} --send-precise-timestamp true --condition-tf-per-query ${nTFs} --n-TFs-buffer ${nBuffer}" - add_W o2-tpc-idc-factorize "--n-TFs-buffer ${nBuffer} --input-lanes ${lanesFactorize} --crus ${crus} --timeframes ${nTFs} --nthreads-grouping ${threadFactorize} --nthreads-IDC-factorization ${threadFactorize} --sendOutputFFT true --enable-CCDB-output true --enablePadStatusMap true ${TPC_WRITING_PAD_STATUS_MAP} --use-precise-timestamp true $IDC_DELTA" "TPCIDCGroupParam.groupPadsSectorEdges=32211" - add_W o2-tpc-idc-ft-aggregator "--rangeIDC 200 --inputLanes ${lanesFactorize} --nFourierCoeff 40 --nthreads 8" - fi - if [[ $CALIB_TPC_CMV == 1 ]]; then - if [[ -z ${O2_TPC_CMV_COMPRESSION:-} ]]; then O2_TPC_CMV_COMPRESSION="--use-sparse --cmv-zero-threshold 1.0 --cmv-dynamic-precision-mean 1.0 --cmv-dynamic-precision-sigma 8.0 --use-compression-huffman"; fi - if [[ -z ${O2_TPC_CMV_TIMEFRAMES:-} ]]; then O2_TPC_CMV_TIMEFRAMES="2000"; fi - add_W o2-tpc-cmv-distribute "--crus ${crus} --lanes 1 --output-lanes ${lanesCMVaggregate} --n-TFs-buffer ${nBuffer_cmv} --timeframes ${O2_TPC_CMV_TIMEFRAMES} --send-precise-timestamp " - add_W o2-tpc-cmv-aggregate "--crus ${crus} --input-lanes ${lanesCMVaggregate} --n-TFs-buffer ${nBuffer_cmv} --nthreads-compression 4 --timeframes ${O2_TPC_CMV_TIMEFRAMES} --use-precise-timestamp ${O2_TPC_CMV_COMPRESSION} --output-dir $CALIB_DIR --meta-output-dir $EPN2EOS_METAFILES_DIR " - fi - if [[ $CALIB_TPC_SAC == 1 ]]; then - add_W o2-tpc-sac-distribute "--timeframes ${nTFs_SAC} --output-lanes 1 " - add_W o2-tpc-sac-factorize "--timeframes ${nTFs_SAC} --nthreads-SAC-factorization 4 --input-lanes 1 --compression 2" - add_W o2-tpc-idc-ft-aggregator "--rangeIDC 200 --nFourierCoeff 40 --process-SACs true --inputLanes 1" +if ! workflow_has_parameter CALIB_LOCAL_INTEGRATED_AGGREGATOR; then + if [[ $AGGREGATOR_TASKS == TPC_IDCBOTH_SAC || $AGGREGATOR_TASKS == ALL ]]; then + if [[ $CALIB_TPC_IDC == 1 ]]; then + add_W o2-tpc-idc-distribute "--crus ${crus} --timeframes ${nTFs} --output-lanes ${lanesFactorize} --send-precise-timestamp true --condition-tf-per-query ${nTFs} --n-TFs-buffer ${nBuffer}" + add_W o2-tpc-idc-factorize "--n-TFs-buffer ${nBuffer} --input-lanes ${lanesFactorize} --crus ${crus} --timeframes ${nTFs} --nthreads-grouping ${threadFactorize} --nthreads-IDC-factorization ${threadFactorize} --sendOutputFFT true --enable-CCDB-output true --enablePadStatusMap true ${TPC_WRITING_PAD_STATUS_MAP} --use-precise-timestamp true $IDC_DELTA" "TPCIDCGroupParam.groupPadsSectorEdges=32211" + add_W o2-tpc-idc-ft-aggregator "--rangeIDC 200 --inputLanes ${lanesFactorize} --nFourierCoeff 40 --nthreads 8" + fi + if [[ $CALIB_TPC_SAC == 1 ]]; then + add_W o2-tpc-sac-distribute "--timeframes ${nTFs_SAC} --output-lanes 1 " + add_W o2-tpc-sac-factorize "--timeframes ${nTFs_SAC} --nthreads-SAC-factorization 4 --input-lanes 1 --compression 2" + add_W o2-tpc-idc-ft-aggregator "--rangeIDC 200 --nFourierCoeff 40 --process-SACs true --inputLanes 1" + fi + elif [[ $AGGREGATOR_TASKS == TPC_CMV || $AGGREGATOR_TASKS == ALL ]]; then + if [[ $CALIB_TPC_CMV == 1 ]]; then + add_W o2-tpc-cmv-distribute "--crus ${crus} --lanes 1 --output-lanes ${lanesCMVaggregate} --n-TFs-buffer ${nBuffer_cmv} --timeframes ${cmvTimeframes} --send-precise-timestamp " + add_W o2-tpc-cmv-aggregate "--crus ${crus} --input-lanes ${lanesCMVaggregate} --n-TFs-buffer ${nBuffer_cmv} --nthreads-compression 8 --timeframes ${cmvTimeframes} --use-precise-timestamp ${cmvCompression} --output-dir $CALIB_DIR --meta-output-dir $EPN2EOS_METAFILES_DIR " + CCDB_POPULATOR_UPLOAD_PATH=none + fi fi - [[ $AGGREGATOR_TASKS == TPC_IDCBOTH_SAC ]] && [[ $CALIB_TPC_IDC == 0 && $CALIB_TPC_SAC == 0 && $CALIB_TPC_CMV == 1 ]] && CCDB_POPULATOR_UPLOAD_PATH="none" fi # Calo cal diff --git a/prodtests/full-system-test/dpl-workflow.sh b/prodtests/full-system-test/dpl-workflow.sh index ca466d311ed30..445cf7a5b5a02 100755 --- a/prodtests/full-system-test/dpl-workflow.sh +++ b/prodtests/full-system-test/dpl-workflow.sh @@ -71,6 +71,50 @@ elif [[ -z ${SYNCRAWMODE:-} ]]; then SYNCRAWMODE=0 fi +# --------------------------------------------------------------------------------------------------------------------- +# build incoming raw inputs specs +define_raw_inputs() +{ + PROXY_INSPEC="dd:FLP/DISTSUBTIMEFRAME/0" + PROXY_IN_N=0 + for i in ${INPUT_DETECTOR_LIST//,/ }; do + if has_detector_flp_processing $i; then + case $i in + TOF) + PROXY_INTYPE="CRAWDATA";; + FT0 | FV0 | FDD) + PROXY_INTYPE="DIGITSBC/0 DIGITSCH/0";; + PHS) + PROXY_INTYPE="CELLS CELLTRIGREC";; + CPV) + PROXY_INTYPE="DIGITS/0 DIGITTRIGREC/0 RAWHWERRORS";; + EMC) + PROXY_INTYPE="CELLS/0 CELLSTRGR/0 DECODERERR";; + CTP) + PROXY_INTYPE="LUMI/0 RAWDATA" + CTP_CONFIG=" --no-lumi " + ;; + *) + echo Input type for detector $i with FLP processing not defined 1>&2 + exit 1;; + esac + else + PROXY_INTYPE=RAWDATA + fi + for j in $PROXY_INTYPE; do + PROXY_INNAME="RAWIN$PROXY_IN_N" + let PROXY_IN_N=$PROXY_IN_N+1 + PROXY_INSPEC+=";$PROXY_INNAME:$i/$j" + done + done + # do we have DPL_RAWTFDUMP_TRIGGER trigger (e.g. TPC/CMVTRIGGER)? If so, add its spec + if has_detector TPC && [[ -n ${DPL_RAWTFDUMP_TRIGGER:-} ]]; then + PROXY_INNAME="RAWIN$PROXY_IN_N" + let PROXY_IN_N=$PROXY_IN_N+1 + PROXY_INSPEC+=";$PROXY_INNAME:${DPL_RAWTFDUMP_TRIGGER}" + fi +} + # --------------------------------------------------------------------------------------------------------------------- # Set some individual workflow arguments depending on configuration GPU_INPUT=zsraw @@ -109,6 +153,7 @@ EVE_OPT=" --jsons-folder $EDJSONS_DIR" : ${ALPIDE_ERR_DUMPS:=} : ${ITSSTAGGERED:=} : ${MFTSTAGGERED:=} +: ${PROXY_INSPEC:=} [[ -z $ALPIDE_ERR_DUMPS ]] && [[ $EPNSYNCMODE == 1 && $RUNTYPE == "PHYSICS" ]] && ALPIDE_ERR_DUMPS=1 || ALPIDE_ERR_DUMPS=0 @@ -483,38 +528,7 @@ if [[ -n $INPUT_DETECTOR_LIST ]]; then add_W o2-raw-tf-reader-workflow "--delay $TFDELAY $TFRAWOPT --loop $TFLOOP $NTIMEFRAMES_CMD --input-data ${TFName} ${INPUT_FILE_COPY_CMD+--copy-cmd} ${INPUT_FILE_COPY_CMD:-} --onlyDet $INPUT_DETECTOR_LIST ${TIMEFRAME_SHM_LIMIT+--timeframes-shm-limit} ${TIMEFRAME_SHM_LIMIT:-}" elif [[ $EXTINPUT == 1 ]]; then PROXY_CHANNEL="name=readout-proxy,type=pull,method=connect,address=ipc://${UDS_PREFIX}${INRAWCHANNAME},transport=shmem,rateLogging=$EPNSYNCMODE" - PROXY_INSPEC="dd:FLP/DISTSUBTIMEFRAME/0" - PROXY_IN_N=0 - for i in ${INPUT_DETECTOR_LIST//,/ }; do - if has_detector_flp_processing $i; then - case $i in - TOF) - PROXY_INTYPE="CRAWDATA";; - FT0 | FV0 | FDD) - PROXY_INTYPE="DIGITSBC/0 DIGITSCH/0";; - PHS) - PROXY_INTYPE="CELLS CELLTRIGREC";; - CPV) - PROXY_INTYPE="DIGITS/0 DIGITTRIGREC/0 RAWHWERRORS";; - EMC) - PROXY_INTYPE="CELLS/0 CELLSTRGR/0 DECODERERR";; - CTP) - PROXY_INTYPE="LUMI/0 RAWDATA" - CTP_CONFIG=" --no-lumi " - ;; - *) - echo Input type for detector $i with FLP processing not defined 1>&2 - exit 1;; - esac - else - PROXY_INTYPE=RAWDATA - fi - for j in $PROXY_INTYPE; do - PROXY_INNAME="RAWIN$PROXY_IN_N" - let PROXY_IN_N=$PROXY_IN_N+1 - PROXY_INSPEC+=";$PROXY_INNAME:$i/$j" - done - done + define_raw_inputs [[ -n ${TIMEFRAME_RATE_LIMIT:-} ]] && [[ $TIMEFRAME_RATE_LIMIT != 0 ]] && PROXY_CHANNEL+=";name=metric-feedback,type=pull,method=connect,address=ipc://${UDS_PREFIX}metric-feedback-${O2JOBID:-$NUMAID},transport=shmem,rateLogging=0" if [[ $EPNSYNCMODE == 1 ]]; then RAWPROXY_CONFIG="--print-input-sizes 1000" @@ -544,8 +558,18 @@ if [[ -z ${WORKFLOW_DETECTORS_USE_GLOBAL_READER_TRACKS} ]] && [[ -z ${WORKFLOW_D fi # --------------------------------------------------------------------------------------------------------------------- -# Raw decoder workflows - disabled in async mode + if [[ $CTFINPUT == 0 && $DIGITINPUT == 0 ]]; then +# Check if raw TF data dump was requested, RAWTF_DUMPRATE must be in % + if [[ ${DPL_RAWTFDUMP:-} == 1 ]]; then + [[ -z ${PROXY_INSPEC} ]] && define_raw_inputs + CONFIG_RAWTFDUMP="--dataspec \"${PROXY_INSPEC}\" --output-dir \"${RAWTF_DIR:-$CTF_DIR}\" --meta-output-dir \"${EPN2EOS_METAFILES_DIR}\" --max-dump-rate ${RAWTF_DUMPRATE:-0.1} " + CONFIG_RAWTFDUMP+=" --min-file-size ${RAWTF_MINSIZE:-$CTF_MINSIZE} --max-tf-per-file ${RAWTF_MAX_PER_FILE:-$CTF_MAX_PER_FILE} --mute-warn-period ${RAWTF_MUTE_PERIOD:-200} --max-warn ${RAWTF_MAX_WARN:-5} " + [[ -n ${DPL_RAWTFDUMP_TRIGGER:-} ]] && CONFIG_RAWTFDUMP+=" --triggerspec \"DMPTRG:${DPL_RAWTFDUMP_TRIGGER}\" " + add_W o2-raw-tf-dump-workflow "$CONFIG_RAWTFDUMP" + fi + +# Raw decoder workflows - disabled in async mode if has_detector TPC && [[ "${TPC_CONVERT_LINKZS_TO_RAW:-}" == "1" ]]; then GPU_INPUT=zsonthefly RAWTODIGITOPTIONS=