diff --git a/algo/CMakeLists.txt b/algo/CMakeLists.txt index ac9e3f380412904997dfeceaef45b3df00b2257d..7a980fd045abcbce5e13a466da2fb2de62a130d0 100644 --- a/algo/CMakeLists.txt +++ b/algo/CMakeLists.txt @@ -82,12 +82,14 @@ set(SRCS trigger/TimeClusterTrigger.cxx evselector/DigiEventSelector.cxx evselector/DigiEventSelectorConfig.cxx + unpack/CommonUnpacker.cxx unpack/Unpack.cxx unpack/UnpackChain.cxx detectors/sts/ReadoutConfig.cxx detectors/sts/HitfinderChain.cxx detectors/sts/ReadoutConfigLegacy.cxx detectors/sts/Unpack.cxx + detectors/sts/UnpackMS.cxx detectors/much/ReadoutConfig.cxx detectors/much/Unpack.cxx detectors/tof/HitFinder.cxx diff --git a/algo/base/Options.cxx b/algo/base/Options.cxx index 536eb6fefd754479e439931ea14d647d47fe3507..9c97c10df487a9bb3c8761cb1aa0f2120ab8e7c1 100644 --- a/algo/base/Options.cxx +++ b/algo/base/Options.cxx @@ -3,6 +3,8 @@ Authors: Felix Weiglhofer [committer] */ #include "Options.h" +#include "util/StlUtils.h" + #include <boost/program_options.hpp> #include <iostream> @@ -125,3 +127,9 @@ Options::Options(int argc, char** argv) std::exit(EXIT_FAILURE); } } + +bool Options::HasOutput(RecoData recoData) const { return Contains(fOutputTypes, recoData); } + +bool Options::Has(fles::Subsystem detector) const { return Contains(fDetectors, detector); } + +bool Options::Has(Step step) const { return Contains(fRecoSteps, step); } diff --git a/algo/base/Options.h b/algo/base/Options.h index c9959ffe28315032a024063e0f6b8405993ab1eb..f57c4293b87af8be254060445a94879ad98e6501 100644 --- a/algo/base/Options.h +++ b/algo/base/Options.h @@ -43,21 +43,17 @@ namespace cbm::algo bool DumpArchive() const { return fDumpArchive; } const std::vector<Step>& Steps() const { return fRecoSteps; } - bool HasStep(Step step) const { return std::find(fRecoSteps.begin(), fRecoSteps.end(), step) != fRecoSteps.end(); } + const std::vector<RecoData>& OutputTypes() const { return fOutputTypes; } - bool HasOutput(RecoData recoData) const - { - return std::find(fOutputTypes.begin(), fOutputTypes.end(), recoData) != fOutputTypes.end(); - } + bool HasOutput(RecoData recoData) const; bool CompressArchive() const { return fCompressArchive; } const std::vector<fles::Subsystem>& Detectors() const { return fDetectors; } - bool HasDetector(fles::Subsystem detector) const - { - return std::find(fDetectors.begin(), fDetectors.end(), detector) != fDetectors.end(); - } + bool Has(fles::Subsystem detector) const; + + bool Has(Step step) const; private: // members std::string fParamsDir; // TODO: can we make this a std::path? diff --git a/algo/base/RecoParams.h b/algo/base/RecoParams.h index 5c88e0d23145eca2915ad99202b7c3e85be98e18..d51fdbef8f393dc569f8b013cc577ca8560d1d3a 100644 --- a/algo/base/RecoParams.h +++ b/algo/base/RecoParams.h @@ -22,11 +22,6 @@ namespace cbm::algo BlockSort = 0, CUBSegmentedSort = 1, }; - enum class UnpackMode : u8 - { - CPU, - XPU, - }; enum class AllocationMode : u8 { Auto, //< Static on GPU, dynamic on CPU @@ -38,7 +33,6 @@ namespace cbm::algo SortMode digiSortMode; SortMode clusterSortMode; - UnpackMode unpackMode; u8 findClustersMultiKernels; f32 timeCutDigiAbs; @@ -76,7 +70,6 @@ namespace cbm::algo "Digi sort mode (0 = block sort, 1 = cub segmented sort))"), config::Property(&STS::clusterSortMode, "clusterSortMode", "Cluster sort mode"), - config::Property(&STS::unpackMode, "unpackMode", "Unpack mode (0 = legacy, 1 = XPU)"), config::Property(&STS::findClustersMultiKernels, "findClustersMultiKernels", "Split cluster finding into multiple kernels"), @@ -100,18 +93,13 @@ namespace cbm::algo static constexpr auto Properties = std::make_tuple(config::Property(&RecoParams::sts, "sts", "STS reco settings")); }; -}; // namespace cbm::algo +} // namespace cbm::algo CBM_ENUM_DICT(cbm::algo::RecoParams::SortMode, {"BlockSort", RecoParams::SortMode::BlockSort}, {"CUBSegmentedSort", RecoParams::SortMode::CUBSegmentedSort} ); -CBM_ENUM_DICT(cbm::algo::RecoParams::UnpackMode, - {"CPU", RecoParams::UnpackMode::CPU}, - {"XPU", RecoParams::UnpackMode::XPU} -); - CBM_ENUM_DICT(cbm::algo::RecoParams::AllocationMode, {"Auto", RecoParams::AllocationMode::Auto}, {"Static", RecoParams::AllocationMode::Static}, diff --git a/algo/base/util/StlUtils.h b/algo/base/util/StlUtils.h new file mode 100644 index 0000000000000000000000000000000000000000..e84898ccdbf38dd293b1c21c717327f811952897 --- /dev/null +++ b/algo/base/util/StlUtils.h @@ -0,0 +1,24 @@ +/* Copyright (C) 2024 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main + SPDX-License-Identifier: GPL-3.0-only + Authors: Felix Weiglhofer [committer] */ + +#pragma once + +/** + * @file StlUtils.h + * + * @brief This file contains utility functions for STL containers. + */ + +#include <algorithm> + +namespace cbm +{ + + template<class C, class T> + bool Contains(const C& container, const T& value) + { + return std::find(container.begin(), container.end(), value) != container.end(); + } + +} // namespace cbm diff --git a/algo/detectors/sts/Unpack.cxx b/algo/detectors/sts/Unpack.cxx index 7602d592a58879881441cb1da7d2884ffb1a7ee0..d0f6ee3d31d2d69efdec406d1ead8c2dbad1d033 100644 --- a/algo/detectors/sts/Unpack.cxx +++ b/algo/detectors/sts/Unpack.cxx @@ -1,183 +1,48 @@ -/* Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt +/* Copyright (C) 2024 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main SPDX-License-Identifier: GPL-3.0-only - Authors: Pierre-Alain Loizeau, Volker Friese [committer] */ + Authors: Felix Weiglhofer [committer], Dominik Smith */ #include "Unpack.h" -#include "StsXyterMessage.h" +#include "log.hpp" -#include <cassert> -#include <cmath> -#include <utility> -#include <vector> +using namespace cbm::algo::sts; +using fles::Subsystem; -using std::unique_ptr; -using std::vector; - -namespace cbm::algo::sts +Unpack::Unpack(const ReadoutConfigLegacy& readout) : fReadout(readout) { - - // ---- Algorithm execution --------------------------------------------- - Unpack::resultType Unpack::operator()(const uint8_t* msContent, const fles::MicrosliceDescriptor& msDescr, - const uint64_t tTimeslice) const - { - // --- Output data - resultType result = {}; - - // --- Current Timeslice start time in epoch units. Note that it is always a multiple of epochs - // --- and the epoch is a multiple of ns. - TimeSpec time; - const uint64_t epochLengthInNs = fkEpochLength * fkClockCycleNom / fkClockCycleDen; - time.currentTsTime = tTimeslice / epochLengthInNs; - - // --- Current TS_MSB epoch cycle - auto const msTime = msDescr.idx; // Unix time of MS in ns - time.currentCycle = std::ldiv(msTime, fkCycleLength).quot; - - // ---Â Number of messages in microslice - auto msSize = msDescr.size; - if (msSize % sizeof(stsxyter::Message) != 0) { - result.second.fNumErrInvalidMsSize++; - return result; - } - const uint32_t numMessages = msSize / sizeof(stsxyter::Message); - if (numMessages < 2) { - result.second.fNumErrInvalidMsSize++; - return result; - } - - const u32 maxDigis = numMessages - 2; // -2 for the TS_MSB and EPOCH messages - result.first.reserve(maxDigis); - - // --- Interpret MS content as sequence of SMX messages - auto message = reinterpret_cast<const stsxyter::Message*>(msContent); - - // --- The first message in the MS is expected to be of type EPOCH and can be ignored. - if (message[0].GetMessType() != stsxyter::MessType::Epoch) { - result.second.fNumErrInvalidFirstMessage++; - return result; - } - - // --- The second message must be of type ts_msb. - if (message[1].GetMessType() != stsxyter::MessType::TsMsb) { - result.second.fNumErrInvalidFirstMessage++; - return result; - } - ProcessTsmsbMessage(message[1], time); - - // --- Message loop - for (uint32_t messageNr = 2; messageNr < numMessages; messageNr++) { - - // --- Action depending on message type - switch (message[messageNr].GetMessType()) { - - case stsxyter::MessType::Hit: { - ProcessHitMessage(message[messageNr], time, result.first, result.second); - break; - } - case stsxyter::MessType::TsMsb: { - ProcessTsmsbMessage(message[messageNr], time); - break; - } - default: { - result.second.fNumNonHitOrTsbMessage++; - break; - } - - } //? Message type - - } //# Messages - - return result; - } - // -------------------------------------------------------------------------- - - - // ----- Process hit message -------------------------------------------- - inline void Unpack::ProcessHitMessage(const stsxyter::Message& message, const TimeSpec& time, - vector<CbmStsDigi>& digiVec, UnpackMonitorData& monitor) const - { - - // --- Check eLink and get parameters - uint16_t elink = message.GetLinkIndexHitBinning(); - if (elink >= fParams.fElinkParams.size()) { - monitor.fNumErrElinkOutOfRange++; - return; - } - const UnpackElinkPar& elinkPar = fParams.fElinkParams.at(elink); - uint32_t asicNr = elinkPar.fAsicNr; - - // --- Check minimum adc cut - if (message.GetHitAdc() <= elinkPar.fAdcMinCut) { - return; - } - - // --- Check for masked channel - if (!elinkPar.fChanMask.empty() && elinkPar.fChanMask[message.GetHitChannel()] == true) { - return; - } - - // --- Hardware-to-software address - uint32_t channel = 0; - if (asicNr < fParams.fNumAsicsPerModule / 2) { // front side (n side); channels counted upward - channel = message.GetHitChannel() + fParams.fNumChansPerAsic * asicNr; - } - else { // back side (p side); channels counted downward - channel = fParams.fNumChansPerAsic * (asicNr + 1) - message.GetHitChannel() - 1; - } - - // --- Expand time stamp to time within timeslice (in clock cycle) - uint64_t messageTime = message.GetHitTimeBinning() + time.currentEpochTime; - - // --- Convert time stamp from clock cycles to ns. Round to nearest full ns. - messageTime = (messageTime * fkClockCycleNom + fkClockCycleDen / 2) / fkClockCycleDen; - - // --- Correct ASIC-wise offsets - messageTime -= elinkPar.fTimeOffset; - - // --- Apply walk correction if applicable - if (message.GetHitAdc() <= elinkPar.fWalk.size()) { - double walk = elinkPar.fWalk[message.GetHitAdc() - 1]; - messageTime += walk; - } - - // --- Charge - double charge = elinkPar.fAdcOffset + (message.GetHitAdc() - 1) * elinkPar.fAdcGain; - - if (messageTime > CbmStsDigi::kMaxTimestamp) { - monitor.fNumErrTimestampOverflow++; - return; - } - - // --- Create output digi - digiVec.emplace_back(elinkPar.fAddress, channel, messageTime, charge); - } - // -------------------------------------------------------------------------- - - - // ----- Process an epoch (TS_MSB) message ------------------------------ - inline void Unpack::ProcessTsmsbMessage(const stsxyter::Message& message, TimeSpec& time) const - { - // The compression of time is based on the hierarchy epoch cycle - epoch - message time. - // Cycles are counted from the start of Unix time and are multiples of an epoch (ts_msb). - // The epoch number is counted within each cycle. The time in the hit message is expressed - // in units of the readout clock cycle relative to the current epoch. - // The ts_msb message indicates the start of a new epoch. Its basic information is the epoch - // number within the current cycle. A cycle wrap resets the epoch number to zero, so it is - // indicated by the epoch number being smaller than the previous one (epoch messages are - // seemingly not consecutively in the data stream, but only if there are hit messages in between). - auto epoch = message.GetTsMsbValBinning(); - - // --- Cycle wrap - if (epoch < time.currentEpoch) time.currentCycle++; - - // --- Update current epoch counter - time.currentEpoch = epoch; - - // --- Calculate epoch time in clocks cycles relative to timeslice start time - time.currentEpochTime = (time.currentCycle * fkEpochsPerCycle + epoch - time.currentTsTime) * fkEpochLength; - } - // -------------------------------------------------------------------------- - - -} // namespace cbm::algo::sts + uint32_t numChansPerAsicSts = 128; // R/O channels per ASIC for STS + uint32_t numAsicsPerModuleSts = 16; // Number of ASICs per module for STS + + constexpr i64 SystemTimeOffset = -970; // ns? + + auto equipIdsSts = fReadout.GetEquipmentIds(); + for (auto& equip : equipIdsSts) { + std::unique_ptr<sts::UnpackPar> par(new sts::UnpackPar()); + par->fNumChansPerAsic = numChansPerAsicSts; + par->fNumAsicsPerModule = numAsicsPerModuleSts; + const size_t numElinks = fReadout.GetNumElinks(equip); + for (size_t elink = 0; elink < numElinks; elink++) { + sts::UnpackElinkPar elinkPar; + auto mapEntry = fReadout.Map(equip, elink); + elinkPar.fAddress = mapEntry.first; // Module address for this elink + elinkPar.fAsicNr = mapEntry.second; // ASIC number within module + elinkPar.fTimeOffset = SystemTimeOffset; + elinkPar.fAdcMinCut = fReadout.AdcCutMap(equip, elink); + elinkPar.fAdcOffset = 1.; + elinkPar.fAdcGain = 1.; + elinkPar.fWalk = fReadout.WalkMap(elinkPar.fAddress, elinkPar.fAsicNr); + elinkPar.fChanMask = fReadout.MaskMap(equip, elink); + // TODO: Add parameters for time and ADC calibration + par->fElinkParams.push_back(elinkPar); + } + fAlgos[equip].SetParams(std::move(par)); + L_(debug) << "--- Configured equipment " << equip << " with " << numElinks << " elinks"; + } //# equipments +} + +Unpack::Result_t Unpack::operator()(const fles::Timeslice& ts) const +{ + constexpr int SystemVersion = 0x20; + return DoUnpack(Subsystem::STS, ts, SystemVersion); +} diff --git a/algo/detectors/sts/Unpack.h b/algo/detectors/sts/Unpack.h index ec476c55618e91f44daba8333fe3593b38abcbb0..8530069e87d37d61c18f8fc85aacb12e04be6534 100644 --- a/algo/detectors/sts/Unpack.h +++ b/algo/detectors/sts/Unpack.h @@ -1,159 +1,32 @@ -/* Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt +/* Copyright (C) 2024 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main SPDX-License-Identifier: GPL-3.0-only - Authors: Pierre-Alain Loizeau, Volker Friese [committer] */ + Authors: Felix Weiglhofer [committer], Dominik Smith */ -#ifndef CBM_ALGO_UNPACKSTS_H -#define CBM_ALGO_UNPACKSTS_H 1 +#pragma once -#include "CbmStsDigi.h" -#include "Definitions.h" -#include "MicrosliceDescriptor.hpp" -#include "StsXyterMessage.h" -#include "Timeslice.hpp" - -#include <cassert> -#include <cstddef> -#include <cstdint> -#include <memory> -#include <vector> +#include "CommonUnpacker.h" +#include "ReadoutConfigLegacy.h" +#include "UnpackMS.h" namespace cbm::algo::sts { + namespace detail + { + using UnpackBase = CommonUnpacker<CbmStsDigi, UnpackMS, UnpackMonitorData>; + } - /** @struct UnpackStsElinkPar - ** @author Volker Friese <v.friese@gsi.de> - ** @since 25 November 2021 - ** @brief STS Unpacking parameters for one eLink / ASIC - **/ - struct UnpackElinkPar { - int32_t fAddress = 0; ///< CbmStsAddress for the connected module - uint32_t fAsicNr = 0; ///< Number of connected ASIC within the module - uint64_t fTimeOffset = 0.; ///< Time calibration parameter - double fAdcOffset = 0.; ///< Charge calibration parameter - double fAdcGain = 0.; ///< Charge calibration parameter - uint32_t fAdcMinCut = 0.; ///< Minimum Acd cut - std::vector<double> fWalk; ///< Walk correction coefficients - std::vector<bool> fChanMask; ///< Channel masking flags - }; - - - /** @struct UnpackStsPar - ** @author Volker Friese <v.friese@gsi.de> - ** @since 25 November 2021 - ** @brief Parameters required for the STS unpacking (specific to one component) - **/ - struct UnpackPar { - uint32_t fNumChansPerAsic = 0; ///< Number of channels per ASIC - uint32_t fNumAsicsPerModule = 0; ///< Number of ASICS per module - std::vector<UnpackElinkPar> fElinkParams = {}; ///< Parameters for each eLink - }; - - - /** @struct UnpackStsMoni - ** @author Volker Friese <v.friese@gsi.de> - ** @since 2 December 2021 - ** @brief Monitoring data for STS unpacking - **/ - struct UnpackMonitorData { - uint32_t fNumNonHitOrTsbMessage = 0; - uint32_t fNumErrElinkOutOfRange = 0; ///< Elink not contained in parameters - uint32_t fNumErrInvalidFirstMessage = 0; ///< First message is not TS_MSB or second is not EPOCH - uint32_t fNumErrInvalidMsSize = 0; ///< Microslice size is not multiple of message size - uint32_t fNumErrTimestampOverflow = 0; ///< Overflow in 64 bit time stamp - bool HasErrors() - { - uint32_t numErrors = fNumNonHitOrTsbMessage + fNumErrElinkOutOfRange + fNumErrInvalidFirstMessage - + fNumErrInvalidMsSize + fNumErrTimestampOverflow; - return (numErrors > 0 ? true : false); - } - std::string print() - { - std::stringstream ss; - ss << "errors " << fNumNonHitOrTsbMessage << " | " << fNumErrElinkOutOfRange << " | " - << fNumErrInvalidFirstMessage << " | " << fNumErrInvalidMsSize << " | " << fNumErrTimestampOverflow << " | "; - return ss.str(); - } - }; - - /** @class UnpackSts - ** @author Pierre-Alain Loizeau <p.-a.loizeau@gsi.de> - ** @author Volker Friese <v.friese@gsi.de> - ** @since 25 November 2021 - ** @brief Unpack algorithm for STS - **/ - class Unpack { + class Unpack : public detail::UnpackBase { public: - typedef std::pair<std::vector<CbmStsDigi>, UnpackMonitorData> resultType; - - - /** @brief Default constructor **/ - Unpack(){}; - + using Result_t = detail::UnpackBase::Result_t; - /** @brief Destructor **/ - ~Unpack(){}; + Unpack(const ReadoutConfigLegacy& readout); + Result_t operator()(const fles::Timeslice&) const; - /** @brief Algorithm execution - ** @param msContent Microslice payload - ** @param msDescr Microslice descriptor - ** @param tTimeslice Unix start time of timeslice [ns] - ** @return STS digi data - **/ - resultType operator()(const uint8_t* msContent, const fles::MicrosliceDescriptor& msDescr, - const uint64_t tTimeslice) const; - - /** @brief Set the parameter container - ** @param params Pointer to parameter container - **/ - void SetParams(std::unique_ptr<UnpackPar> params) { fParams = *(std::move(params)); } - - private: // types - /** - * @brief Structure to hold the current time information for the current microslice - */ - struct TimeSpec { - u64 currentTsTime = 0; ///< Unix time of timeslice in units of epoch length - u64 currentCycle = 0; ///< Current epoch cycle - u32 currentEpoch = 0; ///< Current epoch number within epoch cycle - u64 currentEpochTime = 0; ///< Current epoch time relative to timeslice in clock cycles - }; - - private: // methods - /** @brief Process a hit message - ** @param message SMX message (32-bit word) - ** @param digiVec Vector to append the created digi to - ** @param monitor Reference to monitor object - **/ - void ProcessHitMessage(const stsxyter::Message& message, const TimeSpec& time, std::vector<CbmStsDigi>& digiVec, - UnpackMonitorData& monitor) const; - - /** @brief Process an epoch message (TS_MSB) - ** @param message SMX message (32-bit word) - **/ - void ProcessTsmsbMessage(const stsxyter::Message& message, TimeSpec& time) const; - - - private: // members - UnpackPar fParams = {}; ///< Parameter container - - /** Number of TS_MSB epochs per cycle **/ - static constexpr uint64_t fkEpochsPerCycle = stsxyter::kuTsMsbNbTsBinsBinning; - - /** Length of TS_MSB epoch in clock cycles **/ - static constexpr uint64_t fkEpochLength = stsxyter::kuHitNbTsBinsBinning; - - /** Clock cycle nominator [ns] and denominator. The clock cycle in ns is nominator / denominator. **/ - static constexpr uint32_t fkClockCycleNom = stsxyter::kulClockCycleNom; - static constexpr uint32_t fkClockCycleDen = stsxyter::kulClockCycleDen; - - /** Epoch cycle length in ns **/ - static constexpr uint64_t fkCycleLength = (fkEpochsPerCycle * fkEpochLength * fkClockCycleNom) / fkClockCycleDen; + private: + ReadoutConfigLegacy fReadout; }; - -} /* namespace cbm::algo::sts */ - -#endif /* CBM_ALGO_UNPACKSTS_H */ +} // namespace cbm::algo::sts diff --git a/algo/detectors/sts/UnpackMS.cxx b/algo/detectors/sts/UnpackMS.cxx new file mode 100644 index 0000000000000000000000000000000000000000..4ffb8dc9221433044f607c6ebe4060ca8edec9da --- /dev/null +++ b/algo/detectors/sts/UnpackMS.cxx @@ -0,0 +1,183 @@ +/* Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Pierre-Alain Loizeau, Volker Friese [committer] */ + +#include "UnpackMS.h" + +#include "StsXyterMessage.h" + +#include <cassert> +#include <cmath> +#include <utility> +#include <vector> + +using std::unique_ptr; +using std::vector; + +namespace cbm::algo::sts +{ + + // ---- Algorithm execution --------------------------------------------- + UnpackMS::Result_t UnpackMS::operator()(const uint8_t* msContent, const fles::MicrosliceDescriptor& msDescr, + const uint64_t tTimeslice) const + { + // --- Output data + Result_t result = {}; + + // --- Current Timeslice start time in epoch units. Note that it is always a multiple of epochs + // --- and the epoch is a multiple of ns. + TimeSpec time; + const uint64_t epochLengthInNs = fkEpochLength * fkClockCycleNom / fkClockCycleDen; + time.currentTsTime = tTimeslice / epochLengthInNs; + + // --- Current TS_MSB epoch cycle + auto const msTime = msDescr.idx; // Unix time of MS in ns + time.currentCycle = std::ldiv(msTime, fkCycleLength).quot; + + // ---Â Number of messages in microslice + auto msSize = msDescr.size; + if (msSize % sizeof(stsxyter::Message) != 0) { + result.second.fNumErrInvalidMsSize++; + return result; + } + const uint32_t numMessages = msSize / sizeof(stsxyter::Message); + if (numMessages < 2) { + result.second.fNumErrInvalidMsSize++; + return result; + } + + const u32 maxDigis = numMessages - 2; // -2 for the TS_MSB and EPOCH messages + result.first.reserve(maxDigis); + + // --- Interpret MS content as sequence of SMX messages + auto message = reinterpret_cast<const stsxyter::Message*>(msContent); + + // --- The first message in the MS is expected to be of type EPOCH and can be ignored. + if (message[0].GetMessType() != stsxyter::MessType::Epoch) { + result.second.fNumErrInvalidFirstMessage++; + return result; + } + + // --- The second message must be of type ts_msb. + if (message[1].GetMessType() != stsxyter::MessType::TsMsb) { + result.second.fNumErrInvalidFirstMessage++; + return result; + } + ProcessTsmsbMessage(message[1], time); + + // --- Message loop + for (uint32_t messageNr = 2; messageNr < numMessages; messageNr++) { + + // --- Action depending on message type + switch (message[messageNr].GetMessType()) { + + case stsxyter::MessType::Hit: { + ProcessHitMessage(message[messageNr], time, result.first, result.second); + break; + } + case stsxyter::MessType::TsMsb: { + ProcessTsmsbMessage(message[messageNr], time); + break; + } + default: { + result.second.fNumNonHitOrTsbMessage++; + break; + } + + } //? Message type + + } //# Messages + + return result; + } + // -------------------------------------------------------------------------- + + + // ----- Process hit message -------------------------------------------- + inline void UnpackMS::ProcessHitMessage(const stsxyter::Message& message, const TimeSpec& time, + vector<CbmStsDigi>& digiVec, UnpackMonitorData& monitor) const + { + + // --- Check eLink and get parameters + uint16_t elink = message.GetLinkIndexHitBinning(); + if (elink >= fParams.fElinkParams.size()) { + monitor.fNumErrElinkOutOfRange++; + return; + } + const UnpackElinkPar& elinkPar = fParams.fElinkParams.at(elink); + uint32_t asicNr = elinkPar.fAsicNr; + + // --- Check minimum adc cut + if (message.GetHitAdc() <= elinkPar.fAdcMinCut) { + return; + } + + // --- Check for masked channel + if (!elinkPar.fChanMask.empty() && elinkPar.fChanMask[message.GetHitChannel()] == true) { + return; + } + + // --- Hardware-to-software address + uint32_t channel = 0; + if (asicNr < fParams.fNumAsicsPerModule / 2) { // front side (n side); channels counted upward + channel = message.GetHitChannel() + fParams.fNumChansPerAsic * asicNr; + } + else { // back side (p side); channels counted downward + channel = fParams.fNumChansPerAsic * (asicNr + 1) - message.GetHitChannel() - 1; + } + + // --- Expand time stamp to time within timeslice (in clock cycle) + uint64_t messageTime = message.GetHitTimeBinning() + time.currentEpochTime; + + // --- Convert time stamp from clock cycles to ns. Round to nearest full ns. + messageTime = (messageTime * fkClockCycleNom + fkClockCycleDen / 2) / fkClockCycleDen; + + // --- Correct ASIC-wise offsets + messageTime -= elinkPar.fTimeOffset; + + // --- Apply walk correction if applicable + if (message.GetHitAdc() <= elinkPar.fWalk.size()) { + double walk = elinkPar.fWalk[message.GetHitAdc() - 1]; + messageTime += walk; + } + + // --- Charge + double charge = elinkPar.fAdcOffset + (message.GetHitAdc() - 1) * elinkPar.fAdcGain; + + if (messageTime > CbmStsDigi::kMaxTimestamp) { + monitor.fNumErrTimestampOverflow++; + return; + } + + // --- Create output digi + digiVec.emplace_back(elinkPar.fAddress, channel, messageTime, charge); + } + // -------------------------------------------------------------------------- + + + // ----- Process an epoch (TS_MSB) message ------------------------------ + inline void UnpackMS::ProcessTsmsbMessage(const stsxyter::Message& message, TimeSpec& time) const + { + // The compression of time is based on the hierarchy epoch cycle - epoch - message time. + // Cycles are counted from the start of Unix time and are multiples of an epoch (ts_msb). + // The epoch number is counted within each cycle. The time in the hit message is expressed + // in units of the readout clock cycle relative to the current epoch. + // The ts_msb message indicates the start of a new epoch. Its basic information is the epoch + // number within the current cycle. A cycle wrap resets the epoch number to zero, so it is + // indicated by the epoch number being smaller than the previous one (epoch messages are + // seemingly not consecutively in the data stream, but only if there are hit messages in between). + auto epoch = message.GetTsMsbValBinning(); + + // --- Cycle wrap + if (epoch < time.currentEpoch) time.currentCycle++; + + // --- Update current epoch counter + time.currentEpoch = epoch; + + // --- Calculate epoch time in clocks cycles relative to timeslice start time + time.currentEpochTime = (time.currentCycle * fkEpochsPerCycle + epoch - time.currentTsTime) * fkEpochLength; + } + // -------------------------------------------------------------------------- + + +} // namespace cbm::algo::sts diff --git a/algo/detectors/sts/UnpackMS.h b/algo/detectors/sts/UnpackMS.h new file mode 100644 index 0000000000000000000000000000000000000000..6ae726b8795539608b1f999ae9eb0e67ddb07984 --- /dev/null +++ b/algo/detectors/sts/UnpackMS.h @@ -0,0 +1,152 @@ +/* Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Pierre-Alain Loizeau, Volker Friese [committer] */ +#pragma once + +#include "CbmStsDigi.h" +#include "Definitions.h" +#include "MicrosliceDescriptor.hpp" +#include "StsXyterMessage.h" + +#include <cassert> +#include <cstddef> +#include <cstdint> +#include <memory> +#include <vector> + +namespace cbm::algo::sts +{ + + /** @struct UnpackStsElinkPar + ** @author Volker Friese <v.friese@gsi.de> + ** @since 25 November 2021 + ** @brief STS Unpacking parameters for one eLink / ASIC + **/ + struct UnpackElinkPar { + int32_t fAddress = 0; ///< CbmStsAddress for the connected module + uint32_t fAsicNr = 0; ///< Number of connected ASIC within the module + uint64_t fTimeOffset = 0.; ///< Time calibration parameter + double fAdcOffset = 0.; ///< Charge calibration parameter + double fAdcGain = 0.; ///< Charge calibration parameter + uint32_t fAdcMinCut = 0.; ///< Minimum Acd cut + std::vector<double> fWalk; ///< Walk correction coefficients + std::vector<bool> fChanMask; ///< Channel masking flags + }; + + + /** @struct UnpackStsPar + ** @author Volker Friese <v.friese@gsi.de> + ** @since 25 November 2021 + ** @brief Parameters required for the STS unpacking (specific to one component) + **/ + struct UnpackPar { + uint32_t fNumChansPerAsic = 0; ///< Number of channels per ASIC + uint32_t fNumAsicsPerModule = 0; ///< Number of ASICS per module + std::vector<UnpackElinkPar> fElinkParams = {}; ///< Parameters for each eLink + }; + + + /** @struct UnpackStsMoni + ** @author Volker Friese <v.friese@gsi.de> + ** @since 2 December 2021 + ** @brief Monitoring data for STS unpacking + **/ + struct UnpackMonitorData { + uint32_t fNumNonHitOrTsbMessage = 0; + uint32_t fNumErrElinkOutOfRange = 0; ///< Elink not contained in parameters + uint32_t fNumErrInvalidFirstMessage = 0; ///< First message is not TS_MSB or second is not EPOCH + uint32_t fNumErrInvalidMsSize = 0; ///< Microslice size is not multiple of message size + uint32_t fNumErrTimestampOverflow = 0; ///< Overflow in 64 bit time stamp + bool HasErrors() + { + uint32_t numErrors = fNumNonHitOrTsbMessage + fNumErrElinkOutOfRange + fNumErrInvalidFirstMessage + + fNumErrInvalidMsSize + fNumErrTimestampOverflow; + return (numErrors > 0 ? true : false); + } + std::string print() + { + std::stringstream ss; + ss << "errors " << fNumNonHitOrTsbMessage << " | " << fNumErrElinkOutOfRange << " | " + << fNumErrInvalidFirstMessage << " | " << fNumErrInvalidMsSize << " | " << fNumErrTimestampOverflow << " | "; + return ss.str(); + } + }; + + /** @class UnpackMS + ** @author Pierre-Alain Loizeau <p.-a.loizeau@gsi.de> + ** @author Volker Friese <v.friese@gsi.de> + ** @since 25 November 2021 + ** @brief Unpack algorithm for STS + **/ + class UnpackMS { + + public: + using Result_t = std::pair<std::vector<CbmStsDigi>, UnpackMonitorData>; + + + /** @brief Default constructor **/ + UnpackMS() = default; + + + /** @brief Destructor **/ + ~UnpackMS() = default; + + + /** @brief Algorithm execution + ** @param msContent Microslice payload + ** @param msDescr Microslice descriptor + ** @param tTimeslice Unix start time of timeslice [ns] + ** @return STS digi data + **/ + Result_t operator()(const uint8_t* msContent, const fles::MicrosliceDescriptor& msDescr, + const uint64_t tTimeslice) const; + + /** @brief Set the parameter container + ** @param params Pointer to parameter container + **/ + void SetParams(std::unique_ptr<UnpackPar> params) { fParams = *(std::move(params)); } + + private: // types + /** + * @brief Structure to hold the current time information for the current microslice + */ + struct TimeSpec { + u64 currentTsTime = 0; ///< Unix time of timeslice in units of epoch length + u64 currentCycle = 0; ///< Current epoch cycle + u32 currentEpoch = 0; ///< Current epoch number within epoch cycle + u64 currentEpochTime = 0; ///< Current epoch time relative to timeslice in clock cycles + }; + + private: // methods + /** @brief Process a hit message + ** @param message SMX message (32-bit word) + ** @param digiVec Vector to append the created digi to + ** @param monitor Reference to monitor object + **/ + void ProcessHitMessage(const stsxyter::Message& message, const TimeSpec& time, std::vector<CbmStsDigi>& digiVec, + UnpackMonitorData& monitor) const; + + /** @brief Process an epoch message (TS_MSB) + ** @param message SMX message (32-bit word) + **/ + void ProcessTsmsbMessage(const stsxyter::Message& message, TimeSpec& time) const; + + + private: // members + UnpackPar fParams = {}; ///< Parameter container + + /** Number of TS_MSB epochs per cycle **/ + static constexpr uint64_t fkEpochsPerCycle = stsxyter::kuTsMsbNbTsBinsBinning; + + /** Length of TS_MSB epoch in clock cycles **/ + static constexpr uint64_t fkEpochLength = stsxyter::kuHitNbTsBinsBinning; + + /** Clock cycle nominator [ns] and denominator. The clock cycle in ns is nominator / denominator. **/ + static constexpr uint32_t fkClockCycleNom = stsxyter::kulClockCycleNom; + static constexpr uint32_t fkClockCycleDen = stsxyter::kulClockCycleDen; + + /** Epoch cycle length in ns **/ + static constexpr uint64_t fkCycleLength = (fkEpochsPerCycle * fkEpochLength * fkClockCycleNom) / fkClockCycleDen; + }; + +} /* namespace cbm::algo::sts */ diff --git a/algo/global/Reco.cxx b/algo/global/Reco.cxx index 14f61de9fd55a774fdb2d94aaa0439bf32e94cad..44d90d27bac8711620a9e016e29eb6eb3db4df6e 100644 --- a/algo/global/Reco.cxx +++ b/algo/global/Reco.cxx @@ -41,6 +41,14 @@ void Reco::Validate(const Options& opts) if (!BuildInfo::WITH_ZSTD && opts.CompressArchive()) { throw FatalError("Archive compression enabled but compiled without Zstd: Remove --archive-compression flag"); } + + if (opts.Has(Step::LocalReco) && !opts.Has(Step::Unpack)) { + throw FatalError("Local reco can't run without unpacking: Add 'Unpack' to the reco steps"); + } + + if (opts.Has(Step::Tracking) && !opts.Has(Step::LocalReco)) { + throw FatalError("Tracking can't run without local reco: Add 'LocalReco' to the reco steps"); + } } void Reco::Init(const Options& opts) @@ -78,6 +86,11 @@ void Reco::Init(const Options& opts) // Unpackers fUnpack.Init(); + if (Opts().Has(fles::Subsystem::STS) && Opts().Has(Step::Unpack)) { + sts::ReadoutConfigLegacy cfg{}; + fStsUnpack = std::make_unique<sts::Unpack>(cfg); + } + // --- Event building fs::path configFile = opts.ParamsDir() / "EventbuildConfig.yaml"; evbuild::Config config(YAML::LoadFile(configFile.string())); @@ -112,42 +125,58 @@ void Reco::Init(const Options& opts) RecoResults Reco::Run(const fles::Timeslice& ts) { - if (!fInitialized) throw std::runtime_error("Chain not initialized"); + if (!fInitialized) { + throw std::runtime_error("Chain not initialized"); + } xpu::t_add_bytes(ts_utils::SizeBytes(ts)); + ProcessingMonitor procMon; + RecoResults results; - xpu::timings tsTimes; { - xpu::scoped_timer t_(fmt::format("TS {}", ts.index()), &tsTimes); + xpu::scoped_timer t_(fmt::format("TS {}", ts.index()), &procMon.time); xpu::t_add_bytes(ts_utils::SizeBytes(ts)); L_(info) << ">>> Processing TS " << ts.index(); xpu::set<cbm::algo::Params>(Params()); - Unpack::resultType unpackResult; - UnpackMonitorData unpackMonitor; - - if (Opts().HasStep(Step::Unpack)) { - switch (Params().sts.unpackMode) { - case RecoParams::UnpackMode::XPU: - // digis = fUnpackXpu.Exec(ts); - throw std::runtime_error("XPU unpacker currently not implemented"); - break; - default: - case RecoParams::UnpackMode::CPU: - unpackResult = fUnpack.Run(ts); - unpackMonitor = unpackResult.second; - QueueUnpackerMetrics(ts, unpackMonitor, unpackResult.first); - break; + DigiData digis; + + if (Opts().Has(Step::Unpack)) { + xpu::scoped_timer timerU("Unpack", &procMon.timeUnpack); + xpu::t_add_bytes(ts_utils::SizeBytes(ts)); + + if (fStsUnpack) { + auto [stsDigis, stsMonitor] = (*fStsUnpack)(ts); + + digis.fSts = std::move(stsDigis); + QueueUnpackerMetricsDet(stsMonitor); } + + auto [digisU, unpackMonitor] = fUnpack.Run(ts); + + digis.fMuch = std::move(digisU.fMuch); + digis.fTof = std::move(digisU.fTof); + digis.fBmon = std::move(digisU.fBmon); + digis.fTrd = std::move(digisU.fTrd); + digis.fTrd2d = std::move(digisU.fTrd2d); + digis.fRich = std::move(digisU.fRich); + digis.fPsd = std::move(digisU.fPsd); + digis.fFsd = std::move(digisU.fFsd); + + L_(info) << "TS contains Digis: STS=" << digis.fSts.size() << " MUCH=" << digis.fMuch.size() + << " TOF=" << digis.fTof.size() << " BMON=" << digis.fBmon.size() << " TRD=" << digis.fTrd.size() + << " TRD2D=" << digis.fTrd2d.size() << " RICH=" << digis.fRich.size() << " PSD=" << digis.fPsd.size() + << " FSD=" << digis.fFsd.size(); + QueueUnpackerMetrics(ts, unpackMonitor, digis); } // --- Digi event building std::vector<DigiEvent> events; evbuild::EventbuildChainMonitorData evbuildMonitor; - if (Opts().HasStep(Step::DigiTrigger)) { - auto [ev, mon] = fEventBuild->Run(unpackResult.first); + if (Opts().Has(Step::DigiTrigger)) { + auto [ev, mon] = fEventBuild->Run(digis); events = std::move(ev); evbuildMonitor = mon; QueueEvbuildMetrics(evbuildMonitor); @@ -156,9 +185,9 @@ RecoResults Reco::Run(const fles::Timeslice& ts) sts::HitfinderMonitor stsHitfinderMonitor; PartitionedSpan<sts::Hit> stsHits; PartitionedVector<sts::Cluster> stsClusters; - if (Opts().HasStep(Step::LocalReco) && Opts().HasDetector(fles::Subsystem::STS)) { + if (Opts().Has(Step::LocalReco) && Opts().Has(fles::Subsystem::STS)) { bool storeClusters = Opts().HasOutput(RecoData::Cluster); - auto stsResults = fStsHitFinder(unpackResult.first.fSts, storeClusters); + auto stsResults = fStsHitFinder(digis.fSts, storeClusters); stsHits = stsResults.hits; stsClusters = std::move(stsResults.clusters); stsHitfinderMonitor = stsResults.monitor; @@ -166,8 +195,8 @@ RecoResults Reco::Run(const fles::Timeslice& ts) } PartitionedVector<tof::Hit> tofHits; - if (Opts().HasStep(Step::LocalReco) && Opts().HasDetector(fles::Subsystem::TOF)) { - auto [caldigis, calmonitor] = fTofCalibrator.Run(unpackResult.first.fTof); + if (Opts().Has(Step::LocalReco) && Opts().Has(fles::Subsystem::TOF)) { + auto [caldigis, calmonitor] = fTofCalibrator.Run(digis.fTof); auto [hits, hitmonitor, digiindices] = fTofHitFinder.Run(caldigis); tofHits = std::move(hits); QueueTofCalibMetrics(calmonitor); @@ -175,7 +204,7 @@ RecoResults Reco::Run(const fles::Timeslice& ts) } // --- Tracking - if (Opts().HasStep(Step::Tracking)) { + if (Opts().Has(Step::Tracking)) { TrackingChain::Input_t input{ .stsHits = stsHits, .tofHits = tofHits, @@ -189,7 +218,7 @@ RecoResults Reco::Run(const fles::Timeslice& ts) QueueTrackingMetrics(trackingOutput.monitorData); } - if (Opts().HasOutput(RecoData::RawDigi)) results.stsDigis = std::move(unpackResult.first.fSts); + if (Opts().HasOutput(RecoData::RawDigi)) results.stsDigis = std::move(digis.fSts); if (Opts().HasOutput(RecoData::DigiEvent)) results.events = std::move(events); if (Opts().HasOutput(RecoData::Cluster)) results.stsClusters = std::move(stsClusters); if (Opts().HasOutput(RecoData::Hit)) { @@ -197,11 +226,8 @@ RecoResults Reco::Run(const fles::Timeslice& ts) results.tofHits = std::move(tofHits); } } - PrintTimings(tsTimes); - - ProcessingMonitor processingMonitor; - processingMonitor.fTime = tsTimes; - QueueProcessingMetrics(processingMonitor); + PrintTimings(procMon.time); + QueueProcessingMetrics(procMon); return results; } @@ -262,21 +288,18 @@ void Reco::QueueUnpackerMetrics(const fles::Timeslice& ts, const UnpackMonitorDa {"tsIdDelta", tsDelta}, {"unpackTimeTotal", monitor.fTime.wall()}, {"unpackThroughput", monitor.fTime.throughput()}, - {"unpackBytesInSts", monitor.fNumBytesInSts}, {"unpackBytesInMuch", monitor.fNumBytesInMuch}, {"unpackBytesInTof", monitor.fNumBytesInTof}, {"unpackBytesInBmon", monitor.fNumBytesInBmon}, {"unpackBytesInTrd", monitor.fNumBytesInTrd}, {"unpackBytesInTrd2d", monitor.fNumBytesInTrd2d}, {"unpackBytesInRich", monitor.fNumBytesInRich}, - {"unpackBytesOutSts", sizeBytes(stsDigis)}, {"unpackBytesOutMuch", sizeBytes(muchDigis)}, {"unpackBytesOutTof", sizeBytes(tofDigis)}, {"unpackBytesOutBmon", sizeBytes(bmonDigis)}, {"unpackBytesOutTrd", sizeBytes(trdDigis)}, {"unpackBytesOutTrd2d", sizeBytes(trd2dDigis)}, {"unpackBytesOutRich", sizeBytes(richDigis)}, - {"unpackExpansionFactorSts", expansionFactor(monitor.fNumBytesInSts, stsDigis)}, {"unpackExpansionFactorMuch", expansionFactor(monitor.fNumBytesInMuch, muchDigis)}, {"unpackExpansionFactorTof", expansionFactor(monitor.fNumBytesInTof, tofDigis)}, {"unpackExpansionFactorBmon", expansionFactor(monitor.fNumBytesInBmon, bmonDigis)}, @@ -290,6 +313,28 @@ void Reco::QueueUnpackerMetrics(const fles::Timeslice& ts, const UnpackMonitorDa }); } +template<class MSMonitor> +void Reco::QueueUnpackerMetricsDet(const UnpackMonitor<MSMonitor>& monitor) +{ + if (!HasMonitor()) { + return; + } + + std::string_view det = ToString(monitor.system); + + auto MkKey = [&](std::string_view key) { return fmt::format("{}{}", det, key); }; + + GetMonitor().QueueMetric("cbmreco", {{"hostname", fles::system::current_hostname()}, {"child", Opts().ChildId()}}, + { + {MkKey("unpackBytesIn"), monitor.sizeBytesIn}, + {MkKey("unpackBytesOut"), monitor.sizeBytesOut}, + {MkKey("unpackExpansionFactor"), monitor.ExpansionFactor()}, + {MkKey("unpackNumMs"), monitor.numMs}, + {MkKey("unpackNumErrInvalidSysVer"), monitor.errInvalidSysVer}, + {MkKey("unpackNumErrInvalidEqId"), monitor.errInvalidEqId}, + }); +} + void Reco::QueueStsRecoMetrics(const sts::HitfinderMonitor& monitor) { if (!HasMonitor()) return; @@ -398,7 +443,7 @@ void Reco::QueueProcessingMetrics(const ProcessingMonitor& mon) GetMonitor().QueueMetric("cbmreco", {{"hostname", fles::system::current_hostname()}, {"child", Opts().ChildId()}}, { - {"processingTimeTotal", mon.fTime.wall()}, - {"processingThroughput", mon.fTime.throughput()}, + {"processingTimeTotal", mon.time.wall()}, + {"processingThroughput", mon.time.throughput()}, }); } diff --git a/algo/global/Reco.h b/algo/global/Reco.h index 905dfa33a899959cc63cb4e053407445cc297d59..8649f997df76961fce165e40f62f118cce1c47bb 100644 --- a/algo/global/Reco.h +++ b/algo/global/Reco.h @@ -11,6 +11,7 @@ #include "ca/TrackingChain.h" #include "global/RecoResults.h" #include "sts/HitfinderChain.h" +#include "sts/Unpack.h" #include "tof/CalibratorChain.h" #include "tof/HitfinderChain.h" @@ -26,7 +27,8 @@ namespace cbm::algo class Options; struct ProcessingMonitor { - xpu::timings fTime; //< total processing time + xpu::timings time; //< total processing time + xpu::timings timeUnpack; //< time spent in unpacking }; class Reco : SubChain { @@ -54,6 +56,7 @@ namespace cbm::algo // STS UnpackChain fUnpack; + std::unique_ptr<sts::Unpack> fStsUnpack; sts::HitfinderChain fStsHitFinder; // TOF @@ -69,6 +72,8 @@ namespace cbm::algo void Validate(const Options& opts); void QueueUnpackerMetrics(const fles::Timeslice&, const UnpackMonitorData&, const DigiData&); + template<class MSMonitor> + void QueueUnpackerMetricsDet(const UnpackMonitor<MSMonitor>&); void QueueStsRecoMetrics(const sts::HitfinderMonitor&); void QueueTofRecoMetrics(const tof::HitfindMonitorData&); void QueueTofCalibMetrics(const tof::CalibrateMonitorData&); diff --git a/algo/test/realdata_test.sh b/algo/test/realdata_test.sh index 290c4f3b5cc5484348139721295143504a8ad502..9b462bb291b06a808dbecd660c3e209232e09368 100755 --- a/algo/test/realdata_test.sh +++ b/algo/test/realdata_test.sh @@ -52,7 +52,8 @@ function ensure_gt_zero { if [ -n "$value" ] && [ "$value" -gt 0 ]; then continue else - echo "$pattern: FAIL ($placeholder is not greater than zero.)" + echo "$pattern: FAIL ($placeholder is not greater than zero: $value)" + echo "Tried to match: $sed_pattern" echo "Output:" echo "$output" exit 1 @@ -70,21 +71,15 @@ check_arg "$tsa_file" "tsa_file" echo "Running '$cbmreco_bin $reco_args'" log="$($cbmreco_bin $reco_args 2>&1)" reco_ok=$? -echo "$log" -echo "==============================" if [ $reco_ok -ne 0 ]; then + echo "$log" + echo "==============================" echo "Error: Reconstruction failed. Exit early." exit 1 fi # Check Digis -ensure_gt_zero "$log" "Timeslice contains %1 STS Digis" -ensure_gt_zero "$log" "Timeslice contains %1 TOF Digis" -ensure_gt_zero "$log" "Timeslice contains %1 BMON Digis" -ensure_gt_zero "$log" "Timeslice contains %1 MUCH Digis" -ensure_gt_zero "$log" "Timeslice contains %1 TRD Digis" -ensure_gt_zero "$log" "Timeslice contains %1 TRD2D Digis" -ensure_gt_zero "$log" "Timeslice contains %1 RICH Digis" +ensure_gt_zero "$log" "TS contains Digis: STS=%1 MUCH=%2 TOF=%3 BMON=%4 TRD=%5 TRD2D=%6 RICH=%7 PSD=0 FSD=0" # Check Events ensure_gt_zero "$log" "Triggers: %1, events %2" diff --git a/algo/unpack/CommonUnpacker.cxx b/algo/unpack/CommonUnpacker.cxx new file mode 100644 index 0000000000000000000000000000000000000000..ab4d188b29bd5563e39fec686c318d1c7940f583 --- /dev/null +++ b/algo/unpack/CommonUnpacker.cxx @@ -0,0 +1,40 @@ +/* Copyright (C) 2024 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main + SPDX-License-Identifier: GPL-3.0-only + Authors: Felix Weiglhofer [committer], Dominik Smith */ +#include "CommonUnpacker.h" + +using namespace cbm::algo; + +detail::MSData::MSData(const fles::Timeslice& ts, fles::Subsystem subsystem, gsl::span<u16> legalEqIds, u8 sys_ver) +{ + monitor.system = subsystem; + + for (uint64_t comp = 0; comp < ts.num_components(); comp++) { + auto this_subsystem = static_cast<fles::Subsystem>(ts.descriptor(comp, 0).sys_id); + + if (this_subsystem != subsystem) { + continue; + } + + const u64 numMsInComp = ts.num_microslices(comp); + const u16 componentId = ts.descriptor(comp, 0).eq_id; + + if (ts.descriptor(comp, 0).sys_ver != sys_ver) { + monitor.errInvalidSysVer++; + continue; + } + + if (std::find(legalEqIds.begin(), legalEqIds.end(), componentId) == legalEqIds.end()) { + monitor.errInvalidEqId++; + continue; + } + + monitor.sizeBytesIn += ts.size_component(comp); + monitor.numMs += numMsInComp; + for (u64 mslice = 0; mslice < numMsInComp; mslice++) { + msEqIds.push_back(componentId); + msDesc.push_back(ts.descriptor(comp, mslice)); + msContent.push_back(ts.content(comp, mslice)); + } + } +} diff --git a/algo/unpack/CommonUnpacker.h b/algo/unpack/CommonUnpacker.h new file mode 100644 index 0000000000000000000000000000000000000000..2db2791b2d7d100b45dd0f87d80ff70281e72c45 --- /dev/null +++ b/algo/unpack/CommonUnpacker.h @@ -0,0 +1,138 @@ +/* Copyright (C) 2024 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main + SPDX-License-Identifier: GPL-3.0-only + Authors: Felix Weiglhofer [committer], Dominik Smith */ +#pragma once + +#include "Definitions.h" +#include "PODVector.h" +#include "Timeslice.hpp" +#include "compat/Algorithm.h" +#include "compat/OpenMP.h" + +#include <cstdint> +#include <gsl/span> +#include <map> +#include <vector> + +#include <xpu/host.h> + +namespace cbm::algo +{ + + namespace detail + { + + struct UnpackMonitorBase { + fles::Subsystem system; // subsystem + size_t numMs = 0; // number of microslices + size_t sizeBytesIn = 0; // total size of microslice contents + size_t sizeBytesOut = 0; // total size of unpacked digis + size_t errInvalidSysVer = 0; + size_t errInvalidEqId = 0; + + double ExpansionFactor() const { return sizeBytesIn > 0 ? static_cast<double>(sizeBytesOut) / sizeBytesIn : 0.0; } + }; + + /** + * @brief Collection of MS data to unpack + * + * @note Helper struct for CommonUnpacker. Moved out of class body to avoid code duplication from different template specializations. + */ + struct MSData { + std::vector<u16> msEqIds; // equipment ids of microslices + std::vector<fles::MicrosliceDescriptor> msDesc; // microslice descriptors + std::vector<const u8*> msContent; // pointer to microslice contents + + UnpackMonitorBase monitor; // monitoring data + + MSData(const fles::Timeslice& ts, fles::Subsystem system, gsl::span<u16> legalEqIds, u8 sys_ver); + ~MSData() = default; + }; + + } // namespace detail + + template<class MSMonitor> + struct UnpackMonitor : detail::UnpackMonitorBase { + std::vector<MSMonitor> msMonitor; // monitoring data per microslice + }; + + template<class Digi, class UnpackAlgo, class MSMonitor> + class CommonUnpacker { + + protected: + using Monitor_t = UnpackMonitor<MSMonitor>; + using Result_t = std::pair<PODVector<Digi>, Monitor_t>; + + std::map<u16, UnpackAlgo> fAlgos; //< Equipment id to unpacker map. Filled by child class! + + Result_t DoUnpack(const fles::Subsystem subsystem, const fles::Timeslice& ts, u8 sys_ver) const + { + xpu::scoped_timer t_(fles::to_string(subsystem)); + auto legalEqIds = GetEqIds(); + + detail::MSData msData{ts, subsystem, gsl::make_span(legalEqIds), sys_ver}; + const size_t numMs = msData.monitor.numMs; + + Result_t out; + auto& digisOut = out.first; + auto& monitorOut = out.second; + + static_cast<detail::UnpackMonitorBase&>(monitorOut) = msData.monitor; + + std::vector<std::vector<Digi>> msDigis(numMs); // unpacked digis per microslice + monitorOut.msMonitor.resize(numMs); // monitoring data per microslice + + xpu::t_add_bytes(msData.monitor.sizeBytesIn); + + xpu::push_timer("Unpack"); + xpu::t_add_bytes(msData.monitor.sizeBytesIn); + CBM_PARALLEL_FOR(schedule(dynamic)) + for (size_t i = 0; i < numMs; i++) { + auto result = fAlgos.at(msData.msEqIds[i])(msData.msContent[i], msData.msDesc[i], ts.start_time()); + msDigis[i] = std::move(result.first); + monitorOut.msMonitor[i] = std::move(result.second); + } + xpu::pop_timer(); + + xpu::push_timer("Resize"); + size_t nDigisTotal = 0; + for (const auto& digis : msDigis) { + nDigisTotal += digis.size(); + } + digisOut.resize(nDigisTotal); + monitorOut.sizeBytesOut = nDigisTotal * sizeof(Digi); + xpu::pop_timer(); + + xpu::push_timer("Merge"); + xpu::t_add_bytes(monitorOut.sizeBytesOut); + CBM_PARALLEL_FOR(schedule(dynamic)) + for (size_t i = 0; i < numMs; i++) { + size_t offset = 0; + for (size_t x = 0; x < i; x++) + offset += msDigis[x].size(); + std::copy(msDigis[i].begin(), msDigis[i].end(), digisOut.begin() + offset); + } + xpu::pop_timer(); + + DoSort(digisOut); + + return out; + } + + private: + void DoSort(gsl::span<Digi> digis) const + { + Sort(digis.begin(), digis.end(), [](const Digi& a, const Digi& b) { return a.GetTime() < b.GetTime(); }); + } + + std::vector<uint16_t> GetEqIds() const + { + std::vector<uint16_t> eqIds; + eqIds.reserve(fAlgos.size()); + for (const auto& [eqId, algo] : fAlgos) { + eqIds.push_back(eqId); + } + return eqIds; + } + }; +} // namespace cbm::algo diff --git a/algo/unpack/Unpack.cxx b/algo/unpack/Unpack.cxx index 660fde3d82ea8f6d568c763e6781518d40f48d64..76912437224e489d1a28e422827494c3500afdb4 100644 --- a/algo/unpack/Unpack.cxx +++ b/algo/unpack/Unpack.cxx @@ -30,11 +30,6 @@ namespace cbm::algo DigiData& digiTs = result.first; UnpackMonitorData& monitor = result.second; - if (DetectorEnabled(Subsystem::STS)) { - monitor.fNumBytesInSts += - ParallelMsLoop(Subsystem::STS, monitor, digiTs.fSts, monitor.fSts, *timeslice, fAlgoSts, 0x20); - } - if (DetectorEnabled(Subsystem::TOF)) { monitor.fNumBytesInTof += ParallelMsLoop(Subsystem::TOF, monitor, digiTs.fTof, monitor.fTof, *timeslice, fAlgoTof, 0x00); @@ -301,33 +296,9 @@ namespace cbm::algo fSubsystems = subIds; // --- Common parameters for all components for STS - uint32_t numChansPerAsicSts = 128; // R/O channels per ASIC for STS - uint32_t numAsicsPerModuleSts = 16; // Number of ASICs per module for STS // Create one algorithm per component for STS and configure it with parameters - auto equipIdsSts = fStsConfig.GetEquipmentIds(); - for (auto& equip : equipIdsSts) { - std::unique_ptr<sts::UnpackPar> par(new sts::UnpackPar()); - par->fNumChansPerAsic = numChansPerAsicSts; - par->fNumAsicsPerModule = numAsicsPerModuleSts; - const size_t numElinks = fStsConfig.GetNumElinks(equip); - for (size_t elink = 0; elink < numElinks; elink++) { - sts::UnpackElinkPar elinkPar; - auto mapEntry = fStsConfig.Map(equip, elink); - elinkPar.fAddress = mapEntry.first; // Module address for this elink - elinkPar.fAsicNr = mapEntry.second; // ASIC number within module - elinkPar.fTimeOffset = fSystemTimeOffset[Subsystem::STS]; - elinkPar.fAdcMinCut = fStsConfig.AdcCutMap(equip, elink); - elinkPar.fAdcOffset = 1.; - elinkPar.fAdcGain = 1.; - if (fApplyWalkCorrection) elinkPar.fWalk = fStsConfig.WalkMap(elinkPar.fAddress, elinkPar.fAsicNr); - elinkPar.fChanMask = fStsConfig.MaskMap(equip, elink); - // TODO: Add parameters for time and ADC calibration - par->fElinkParams.push_back(elinkPar); - } - fAlgoSts[equip].SetParams(std::move(par)); - L_(debug) << "--- Configured equipment " << equip << " with " << numElinks << " elinks"; - } //# equipments + // Create one algorithm per component for MUCH and configure it with parameters auto equipIdsMuch = fMuchConfig.GetEquipmentIds(); @@ -443,8 +414,6 @@ namespace cbm::algo L_(debug) << "--- Configured equipment " << equip << " with " << numAsics << " asics"; } - L_(info) << "--- Configured " << fAlgoSts.size() - << " unpacker algorithms for STS. (Walk correction = " << fApplyWalkCorrection << ")"; // L_(debug) << "Readout map:" << fStsConfig.PrintReadoutMap(); L_(info) << "--- Configured " << fAlgoMuch.size() << " unpacker algorithms for MUCH."; L_(info) << "--- Configured " << fAlgoRich.size() << " unpacker algorithms for RICH."; diff --git a/algo/unpack/Unpack.h b/algo/unpack/Unpack.h index eb9f06cf1918953b50d81c15bdde9099a168e975..99e10884348234bcfb0f74839e82a0cd1513ccae 100644 --- a/algo/unpack/Unpack.h +++ b/algo/unpack/Unpack.h @@ -16,7 +16,6 @@ #include "rich/Unpack.h" #include "sts/Digi.h" #include "sts/ReadoutConfigLegacy.h" -#include "sts/Unpack.h" #include "tof/ReadoutConfig.h" #include "tof/Unpack.h" #include "trd/ReadoutConfig.h" @@ -40,7 +39,6 @@ namespace cbm::algo ** @brief Monitoring data for unpacking **/ struct UnpackMonitorData { - std::vector<sts::UnpackMonitorData> fSts; ///< Monitoring data for STS std::vector<much::UnpackMonitorData> fMuch; ///< Monitoring data for MUCH std::vector<tof::UnpackMonitorData> fTof; ///< Monitoring data for TOF std::vector<bmon::UnpackMonitorData> fBmon; ///< Monitoring data for Bmon @@ -50,7 +48,6 @@ namespace cbm::algo xpu::timings fTime; size_t fNumMs = 0; size_t fNumBytes = 0; - size_t fNumBytesInSts = 0; size_t fNumBytesInMuch = 0; size_t fNumBytesInTof = 0; size_t fNumBytesInBmon = 0; @@ -116,12 +113,6 @@ namespace cbm::algo /** @brief Parameters for RICH unpackers **/ rich::ReadoutConfig fRichConfig{}; - - /** - * @brief Set whether to apply walk correction. Must be set before Init(). (default: true) - **/ - void SetApplyWalkCorrection(bool applyWalkCorrection) { fApplyWalkCorrection = applyWalkCorrection; } - /** @brief Initialize unpackers and fill parameters from config objects * @param subIds: vector of subsystem identifiers to unpack, default: all * @see Init() @@ -166,12 +157,8 @@ namespace cbm::algo private: // members - bool fApplyWalkCorrection = true; ///< Apply walk correction std::vector<Subsystem> fSubsystems = {}; ///< Detector identifiers to unpack - /** @brief STS unpackers **/ - std::map<uint16_t, sts::Unpack> fAlgoSts = {}; - /** @brief MUCH unpackers **/ std::map<uint16_t, much::Unpack> fAlgoMuch = {}; @@ -197,7 +184,7 @@ namespace cbm::algo private: // methods template<typename UnpackAlgo> - std::vector<uint16_t> GetEqIds(const std::map<uint16_t, UnpackAlgo>& algoMap) + std::vector<uint16_t> GetEqIds(const std::map<uint16_t, UnpackAlgo>& algoMap) const { std::vector<uint16_t> eqIds; eqIds.reserve(algoMap.size()); diff --git a/algo/unpack/UnpackChain.cxx b/algo/unpack/UnpackChain.cxx index 68ff5f9bd15fb0a4ba4166d1ac1148b0e9ce61af..8dd57874cbb3b95fe49440fcfb34c8fab4c63f3c 100644 --- a/algo/unpack/UnpackChain.cxx +++ b/algo/unpack/UnpackChain.cxx @@ -10,12 +10,11 @@ using fles::Subsystem; void UnpackChain::Init() { - fUnpack.SetApplyWalkCorrection(true); - if (Opts().HasDetector(Subsystem::TRD)) { + if (Opts().Has(Subsystem::TRD)) { auto yaml = YAML::LoadFile((Opts().ParamsDir() / "TrdReadoutSetup.yaml").string()); fUnpack.fTrdConfig = config::Read<trd::ReadoutConfig>(yaml); } - if (Opts().HasDetector(Subsystem::TRD2D)) { + if (Opts().Has(Subsystem::TRD2D)) { auto yaml = YAML::LoadFile((Opts().ParamsDir() / "Trd2dReadoutSetup.yaml").string()); fUnpack.fTrd2dConfig = config::Read<trd2d::ReadoutConfig>(yaml); } @@ -26,15 +25,5 @@ Unpack::resultType UnpackChain::Run(const fles::Timeslice& timeslice) { auto result = fUnpack(×lice); - auto& digis = result.first; - - if (Opts().HasDetector(Subsystem::STS)) L_(info) << "Timeslice contains " << digis.fSts.size() << " STS Digis"; - if (Opts().HasDetector(Subsystem::TOF)) L_(info) << "Timeslice contains " << digis.fTof.size() << " TOF Digis"; - if (Opts().HasDetector(Subsystem::BMON)) L_(info) << "Timeslice contains " << digis.fBmon.size() << " BMON Digis"; - if (Opts().HasDetector(Subsystem::MUCH)) L_(info) << "Timeslice contains " << digis.fMuch.size() << " MUCH Digis"; - if (Opts().HasDetector(Subsystem::TRD)) L_(info) << "Timeslice contains " << digis.fTrd.size() << " TRD Digis"; - if (Opts().HasDetector(Subsystem::TRD2D)) L_(info) << "Timeslice contains " << digis.fTrd2d.size() << " TRD2D Digis"; - if (Opts().HasDetector(Subsystem::RICH)) L_(info) << "Timeslice contains " << digis.fRich.size() << " RICH Digis"; - return result; }