diff --git a/algo/CMakeLists.txt b/algo/CMakeLists.txt index 09ee075af5715bab37f3bb866c7d4f7bb488f38b..a2a052fa4026f62246d4be605c6a50c2217bf65a 100644 --- a/algo/CMakeLists.txt +++ b/algo/CMakeLists.txt @@ -95,6 +95,12 @@ set(SRCS evselector/DigiEventSelector.cxx evselector/DigiEventSelectorConfig.cxx unpack/CommonUnpacker.cxx + detectors/bmon/Calibrate.cxx + detectors/bmon/Clusterizer.cxx + detectors/bmon/Hitfind.cxx + detectors/bmon/ReadoutConfig.cxx + detectors/bmon/Unpack.cxx + detectors/bmon/UnpackMS.cxx detectors/sts/ChannelMaskSet.cxx detectors/sts/ReadoutConfig.cxx detectors/sts/HitfinderChain.cxx @@ -113,9 +119,6 @@ set(SRCS detectors/tof/UnpackMS.cxx detectors/tof/Hitfind.cxx detectors/tof/TrackingInterface.cxx - detectors/bmon/ReadoutConfig.cxx - detectors/bmon/Unpack.cxx - detectors/bmon/UnpackMS.cxx detectors/trd/Cluster.cxx detectors/trd/Clusterizer.cxx detectors/trd/Cluster2D.cxx @@ -151,6 +154,8 @@ set(SRCS qa/QaData.cxx qa/RecoGeneralQa.cxx qa/QaManager.cxx + qa/hitfind/BmonHitfindQa.cxx + qa/hitfind/BmonHitfindQaParameters.cxx qa/unpack/StsDigiQa.cxx ca/TrackingSetup.cxx ca/TrackingChain.cxx @@ -188,6 +193,7 @@ target_include_directories(Algo ${CMAKE_CURRENT_SOURCE_DIR}/detectors ${CMAKE_CURRENT_SOURCE_DIR}/qa ${CMAKE_CURRENT_SOURCE_DIR}/qa/unpack + ${CMAKE_CURRENT_SOURCE_DIR}/qa/hitfind ${CMAKE_CURRENT_SOURCE_DIR}/kf ${CMAKE_CURRENT_SOURCE_DIR}/kf/core ${CMAKE_CURRENT_SOURCE_DIR}/kf/core/utils diff --git a/algo/ca/qa/CaQa.h b/algo/ca/qa/CaQa.h index 655d6fa50b16a9dac1133223ebafb56abeef08e7..1c42b60562f19c5adf5274c045971fe9bda330bf 100644 --- a/algo/ca/qa/CaQa.h +++ b/algo/ca/qa/CaQa.h @@ -54,7 +54,7 @@ namespace cbm::algo::ca static constexpr HitSetArray_t<EHitSet> kHitSets = {EHitSet::Input, EHitSet::Used}; public: - /// \brief Default destructor + /// \brief Constructor /// \param pManager Pointer to the QA manager /// \param name Name of the QA (directory) Qa(const std::unique_ptr<qa::Manager>& pManager, std::string_view name) : qa::TaskHeader(pManager, name) {} diff --git a/algo/detectors/bmon/Calibrate.cxx b/algo/detectors/bmon/Calibrate.cxx new file mode 100644 index 0000000000000000000000000000000000000000..d5519854902605e3c84e77f584324d3811c88d04 --- /dev/null +++ b/algo/detectors/bmon/Calibrate.cxx @@ -0,0 +1,168 @@ +/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// \file Calibrate.h +/// \brief Calibrator for the BMON digis (implementation) +/// \since 04.02.2025 +/// \author Sergei Zharko <s.zharko@gsi.de> + +#include "Calibrate.h" + +#include "AlgoFairloggerCompat.h" +#include "CbmTofAddress.h" +#include "util/TimingsFormat.h" + +#include <bitset> +#include <chrono> + +using cbm::algo::bmon::Calibrate; +using cbm::algo::bmon::CalibrateSetup; +using fles::Subsystem; + +// --------------------------------------------------------------------------------------------------------------------- +// +Calibrate::Calibrate(CalibrateSetup setup) : fSetup(setup), fSelectionBitsOffset(0) +{ + // Initialize offset arrays for channel deadtime check + int nDiamondsInSetup = fSetup.diamonds.size(); + if (nDiamondsInSetup == 0) { + throw std::runtime_error("No diamonds found in the BMON calibration config"); + } + if (!(fSetup.selectionMask) != (nDiamondsInSetup == 1)) { + throw std::runtime_error("Wrong diamond selection mask: for a single diamond it must be zero, and for multiple" + " diamonds it must be non-zero"); + } + if (nDiamondsInSetup > 1) { + // Define the selection bit offset + while (!((fSetup.selectionMask >> fSelectionBitsOffset) % 2)) { + ++fSelectionBitsOffset; + } + + // Sort the diamonds in the setup by their SM or Side or other distinguishing index + std::sort(fSetup.diamonds.begin(), fSetup.diamonds.end(), [&](const auto& lhs, const auto& rhs) { + return GetDiamondIndex(lhs.refAddress) < GetDiamondIndex(rhs.refAddress); + }); + } + + // Remove the channel information from the reference address + for (auto& diamond : fSetup.diamonds) { + diamond.refAddress &= ~CbmTofAddress::GetChannelIdBitmask(); + } + + // Calculate the channel offsets, needed for the dead time + fChannelOffset = std::vector<size_t>(nDiamondsInSetup + 1, 0); // the last element -- total number of channels + for (int32_t iD = 0; iD < nDiamondsInSetup; ++iD) { + fChannelOffset[iD + 1] = fChannelOffset[iD] + fSetup.diamonds[iD].chanPar.size(); + } + fChannelDeadTime = std::vector<double>(fChannelOffset.back(), std::numeric_limits<double>::quiet_NaN()); + + // **** DEBUG: BEGIN + for (size_t iO = 0; iO < fChannelOffset.size(); ++iO) { + L_(info) << "Channel offset: diamond=" << iO << ", offset=" << fChannelOffset[iO]; + } + L_(info) << "Selection mask: 0b" << std::bitset<32>(fSetup.selectionMask); + L_(info) << "Bits offset: " << static_cast<uint32_t>(fSelectionBitsOffset); + L_(info) << "Size of the channel dead time vector: " << fChannelDeadTime.size(); + // **** DEBUG: END +} + +// --------------------------------------------------------------------------------------------------------------------- +// +Calibrate::resultType Calibrate::operator()(gsl::span<const CbmBmonDigi> digiIn) +{ + xpu::push_timer("BmonCalibrate"); + xpu::t_add_bytes(digiIn.size_bytes()); + + // --- Output data + resultType result = {}; + + auto& calDigiOut = std::get<0>(result); + auto& monitor = std::get<1>(result); + calDigiOut.reserve(digiIn.size()); + + // Reset the channel dead time + std::fill(fChannelDeadTime.begin(), fChannelDeadTime.end(), std::numeric_limits<double>::quiet_NaN()); + + const auto& diamonds = fSetup.diamonds; + for (const auto& digi : digiIn) { + uint32_t address = static_cast<uint32_t>(digi.GetAddress()); + int32_t iChannel = CbmTofAddress::GetChannelId(address); + size_t iDiamond = GetDiamondIndex(address); + if (iDiamond >= diamonds.size() + || (address & ~CbmTofAddress::GetChannelIdBitmask()) != diamonds[iDiamond].refAddress) { + monitor.fDigiCalibUnknownRPC++; + L_(error) << "Unknown BMON digi address: " << CbmTofAddress::ToString(address) << ", iDiamond = " << iDiamond; + continue; + } + + const auto& diamondPar = diamonds[iDiamond]; + const auto& channelPar = diamondPar.chanPar[iChannel]; + + // Check dead time + const size_t iChannelGlb = fChannelOffset[iDiamond] + iChannel; + const double deadTime = fChannelDeadTime[iChannelGlb]; + + if (!std::isnan(deadTime) && digi.GetTime() <= deadTime) { + fChannelDeadTime[iChannelGlb] = digi.GetTime() + diamondPar.channelDeadtime; + monitor.fDigiDeadTimeCount++; + continue; + } + fChannelDeadTime[iChannelGlb] = digi.GetTime() + diamondPar.channelDeadtime; + + // Create calibrated digi + CbmBmonDigi& pCalDigi = calDigiOut.emplace_back(digi); + pCalDigi.SetAddress(address); + + // calibrate Digi Time + pCalDigi.SetTime(pCalDigi.GetTime() - channelPar.vCPTOff); + + // subtract Offset + const double dTot = std::max(pCalDigi.GetCharge() - channelPar.vCPTotOff, 0.001); + + // calibrate Digi charge (corresponds to TOF ToT) + pCalDigi.SetCharge(dTot * channelPar.vCPTotGain); + + // walk correction + const std::vector<double>& walk = channelPar.vCPWalk; + const double dTotBinSize = (diamondPar.TOTMax - diamondPar.TOTMin) / diamondPar.numClWalkBinX; + int32_t iWx = std::max((int32_t)((pCalDigi.GetCharge() - diamondPar.TOTMin) / dTotBinSize), 0); + iWx = std::min(iWx, diamondPar.numClWalkBinX - 1); + + const double dDTot = (pCalDigi.GetCharge() - diamondPar.TOTMin) / dTotBinSize - (double) iWx - 0.5; + double dWT = walk[iWx]; + + // linear interpolation to next bin + if (dDTot > 0) { + if (iWx < diamondPar.numClWalkBinX - 1) { + dWT += dDTot * (walk[iWx + 1] - walk[iWx]); + } + } + else { + if (0 < iWx) { + dWT -= dDTot * (walk[iWx - 1] - walk[iWx]); + } + } + pCalDigi.SetTime(pCalDigi.GetTime() - dWT); // calibrate Digi Time + } + + /// Sort the buffers of hits due to the time offsets applied + // (insert-sort faster than std::sort due to pre-sorting) + for (std::size_t i = 1; i < calDigiOut.size(); ++i) { + CbmTofDigi digi = calDigiOut[i]; + std::size_t j = i; + while (j > 0 && calDigiOut[j - 1].GetTime() > digi.GetTime()) { + calDigiOut[j] = calDigiOut[j - 1]; + --j; + } + calDigiOut[j] = digi; + } + + // Kept for possible unsorted input + // std::sort(calDigiOut.begin(), calDigiOut.end(), + // [](const CbmTofDigi& a, const CbmTofDigi& b) -> bool { return a.GetTime() < b.GetTime(); }); + + monitor.fTime = xpu::pop_timer(); + monitor.fNumDigis = digiIn.size(); + return result; +} diff --git a/algo/detectors/bmon/Calibrate.h b/algo/detectors/bmon/Calibrate.h new file mode 100644 index 0000000000000000000000000000000000000000..644fd203200dbceeac0402248b5cf3a2943be3c3 --- /dev/null +++ b/algo/detectors/bmon/Calibrate.h @@ -0,0 +1,56 @@ +/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// \file Calibrate.h +/// \brief Calibratior for the BMON digis +/// \since 04.02.2025 +/// \author Sergei Zharko <s.zharko@gsi.de> + +#pragma once + +#include "CbmBmonDigi.h" +#include "PartitionedVector.h" +#include "bmon/CalibrateSetup.h" +#include "tof/Calibrate.h" // for the monitor data + +#include <gsl/span> +#include <optional> +#include <sstream> +#include <vector> + +#include <xpu/host.h> + +namespace cbm::algo::bmon +{ + // NOTE: reusing TOF monitor + using CalibrateMonitorData = tof::CalibrateMonitorData; + + /// \class Calibrate + /// \brief Algorithm to calibrate BMon digis + class Calibrate { + public: + using resultType = std::tuple<std::vector<CbmBmonDigi>, CalibrateMonitorData>; + + /// \brief Constructor + /// \param params Calibration parameters + explicit Calibrate(CalibrateSetup params); + + /// \brief Calibrates a portion of digis + /// \param digiIn A portion of digis to calibrate + resultType operator()(gsl::span<const CbmBmonDigi> digiIn); + + private: + /// \brief Returns an index of the diamond by the address + /// \param address A hardware address of the digi + size_t GetDiamondIndex(uint32_t address) const + { + return ((fSetup.selectionMask & address) >> fSelectionBitsOffset); + } + + CalibrateSetup fSetup; ///< Parameters of calibrator + std::vector<size_t> fChannelOffset; ///< Channel offset: offset for the channel index of each diamond + std::vector<double> fChannelDeadTime; ///< Dead time, stored for a channel + uint32_t fSelectionBitsOffset; ///< Number of bits to ther right from the first bit in the selection mask + }; +} // namespace cbm::algo::bmon diff --git a/algo/detectors/bmon/CalibrateSetup.h b/algo/detectors/bmon/CalibrateSetup.h new file mode 100644 index 0000000000000000000000000000000000000000..eeeb58e1784c7ff8896bb5050157171aff162e79 --- /dev/null +++ b/algo/detectors/bmon/CalibrateSetup.h @@ -0,0 +1,64 @@ +/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// \file CalibrateSetup.h +/// \brief Configuration of the calibrator for the BMON digis +/// \since 04.02.2025 +/// \author Sergei Zharko <s.zharko@gsi.de> + +#pragma once + +#include "Definitions.h" +#include "yaml/Property.h" + +#include <array> +#include <map> +#include <string> +#include <vector> + +namespace cbm::algo::bmon +{ + /// \struct CalibrateSetup + /// \brief BMON calibration per channel + struct CalibrateSetup { + // FIXME: remove 'v' from non-vector variable names + struct Channel { + double vCPTOff; + double vCPTotGain; + double vCPTotOff; + std::vector<double> vCPWalk; + + CBM_YAML_PROPERTIES(yaml::Property(&Channel::vCPTOff, "vCPTOff", "CPT offset"), + yaml::Property(&Channel::vCPTotGain, "vCPTotGain", "CP time over threshold gain"), + yaml::Property(&Channel::vCPTotOff, "vCPTotOff", "CP time over threshold offset"), + yaml::Property(&Channel::vCPWalk, "vCPWalk", "CP walk correction", YAML::Block, YAML::Flow)); + }; + + struct Diamond { + uint32_t refAddress; + int32_t numClWalkBinX; + double TOTMax; + double TOTMin; + double channelDeadtime; + std::vector<Channel> chanPar; + + CBM_YAML_PROPERTIES( + yaml::Property(&Diamond::refAddress, "refAddress", "reference HW address to distinguish this BMON"), + yaml::Property(&Diamond::numClWalkBinX, "numClWalkBinX", "number of walk correction bins"), + yaml::Property(&Diamond::TOTMax, "TOTMax", "maximum time over threshold"), + yaml::Property(&Diamond::TOTMin, "TOTMin", "minimum time over threshold"), + yaml::Property(&Diamond::channelDeadtime, "channelDeadtime", "channel dead time"), + yaml::Property(&Diamond::chanPar, "chanPar", "channel parameters")); + }; + + /* Members */ + uint32_t selectionMask; + std::vector<Diamond> diamonds; + + CBM_YAML_PROPERTIES( + yaml::Property(&CalibrateSetup::selectionMask, "selectionMask", "A bit mask to distinguish between different diamonds"), + yaml::Property(&CalibrateSetup::diamonds, "diamonds", "Parameters of each diamond")); + }; + +} // namespace cbm::algo::bmon diff --git a/algo/detectors/bmon/Clusterizer.cxx b/algo/detectors/bmon/Clusterizer.cxx new file mode 100644 index 0000000000000000000000000000000000000000..d3a8e3d4abf18a8a9e1e727606fd08a187afdf88 --- /dev/null +++ b/algo/detectors/bmon/Clusterizer.cxx @@ -0,0 +1,87 @@ +/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// \file Clusterizer.cxx +/// \brief A clusterizer algorithm for BMON (implementation) +/// \since 07.02.2025 +/// \author Sergei Zharko <s.zharko@gsi.de> + + +#include "bmon/Clusterizer.h" + +#include "AlgoFairloggerCompat.h" +#include "CbmBmonDigi.h" +#include "CbmTofAddress.h" + +#include <algorithm> +#include <iomanip> +#include <iostream> +#include <sstream> + +using cbm::algo::bmon::Clusterizer; + +// --------------------------------------------------------------------------------------------------------------------- +// +Clusterizer::Output_t Clusterizer::operator()(const Clusterizer::Input_t& digis) +{ + // Description: + // The input array of digis is traced through until the last element. If the current digi and the next digi are + // close in time and have neighboring channels, a single hit is produced. Otherwise a hit is produced from a + // single digi. The complexity of the algorithm is O(nDigis). Requirements: digis must be sorted in time + Output_t res; + if (digis.empty()) { + return res; + } + + auto& hits = std::get<0>(res); + auto& digiIndices = std::get<1>(res); + hits.reserve(digis.size()); + digiIndices.reserve(digis.size()); + auto itLast = std::prev(digis.end()); // iterator pointing to the last digi + bool bUsedWithPrevious{false}; // A flag: if the current digi was used together with the previous one + for (auto it = digis.begin(), in = it; it != itLast; ++it) { + if (bUsedWithPrevious) { + // skip a digi, if it was already used + bUsedWithPrevious = false; + continue; + } + in = std::next(it); + const auto& digiT = it->first; + const auto& digiN = in->first; + if (digiN.GetTime() - digiT.GetTime() < fParams.fdMaxTimeDist + && abs(digiN.GetChannel() - digiT.GetChannel()) == 1) { + // A hit consisting from two digis is found + hits.emplace_back(fParams.fAddress, digiT, digiN); + digiIndices.emplace_back(it->second); + bUsedWithPrevious = true; + } + else { + // A hit consisting from a single digi + hits.emplace_back(fParams.fAddress, digiT); + digiIndices.emplace_back(it->second); + } + } + if (!bUsedWithPrevious) { + // Create a hit from the last digi + hits.emplace_back(fParams.fAddress, itLast->first); + digiIndices.emplace_back(itLast->second); + } + + return res; +} + +// --------------------------------------------------------------------------------------------------------------------- +// +bool Clusterizer::SelectDigi(const CbmBmonDigi& digi) const +{ + // Dead channel cut + if (fParams.fDeadStrips & (1 << CbmTofAddress::GetChannelId(digi.GetAddress()))) { + return false; + } + + // ??? Other cuts ??? Charge threshold? Also dead strips can be accounted already on the calibrator level, together + // with the cuts + + return true; +} diff --git a/algo/detectors/bmon/Clusterizer.h b/algo/detectors/bmon/Clusterizer.h new file mode 100644 index 0000000000000000000000000000000000000000..86b3c76e3ba74d1595e7ae0282ac1f10e0e22a09 --- /dev/null +++ b/algo/detectors/bmon/Clusterizer.h @@ -0,0 +1,57 @@ +/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// \file Clusterizer.h +/// \brief A clusterizer algorithm for BMON +/// \since 07.02.2025 +/// \author Sergei Zharko <s.zharko@gsi.de> + +#pragma once + +#include "base/PODVector.h" +#include "bmon/ClusterizerPars.h" +#include "bmon/Hit.h" + +#include <cmath> +#include <cstdint> +#include <memory> +#include <utility> +#include <vector> + +class CbmBmonDigi; + +namespace cbm::algo::bmon +{ + /// \class Clusterizer + /// \brief A clusterizer algorithm for a BMON + /// + /// The algorithm is executed on a single hardware module + class Clusterizer { + public: + using Input_t = std::vector<std::pair<CbmBmonDigi, int32_t>>; ///< Input type + using Output_t = std::pair<std::vector<Hit>, PODVector<int32_t>>; ///< Output type + + /// \brief Constructor + /// \param params RPC parameters + explicit Clusterizer(ClusterizerPars params) : fParams(params) {} + + /// \brief Hit building function + Output_t operator()(const Input_t& digisInput); + + /// \brief Applies selection on a digis + /// \return true Digi is selected + /// \return false Digi is cut out + bool SelectDigi(const CbmBmonDigi& digi) const; + + private: + /// \brief Creates a hit from a single digi + Hit CreateHit(const CbmBmonDigi& digi) const; + + /// \brief Creates a hit from two digis + Hit CreateHit(const CbmBmonDigi& digiL, const CbmBmonDigi& digiR) const; + + + ClusterizerPars fParams; ///< parameters container + }; +} // namespace cbm::algo::bmon diff --git a/algo/detectors/bmon/ClusterizerPars.h b/algo/detectors/bmon/ClusterizerPars.h new file mode 100644 index 0000000000000000000000000000000000000000..0e2e65ecf98814f9650e17f9d0c54298441e347d --- /dev/null +++ b/algo/detectors/bmon/ClusterizerPars.h @@ -0,0 +1,25 @@ +/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// \file ClusterizerPars.h +/// \brief BMON clusterizer parameters +/// \since 07.02.2025 +/// \author Sergei Zharko <s.zharko@gsi.de> + +#pragma once + +#include <cstdint> +#include <vector> + +namespace cbm::algo::bmon +{ + /// \struct ClusterizerPars + /// \brief Clusterizer parameters for Diamond + struct ClusterizerPars { + uint32_t fAddress; ///< Address of the diamond (the channel bit field is 0) + uint32_t fDeadStrips; ///< Dead strip bitmask + double fdMaxTimeDist; ///< Maximum time difference between two consecutive digis to form a single hit + double fTimeRes; ///< Time resolution + }; +} // namespace cbm::algo::bmon diff --git a/algo/detectors/bmon/Hit.h b/algo/detectors/bmon/Hit.h new file mode 100644 index 0000000000000000000000000000000000000000..e4e1aef243769e409fc0b8c06466b78548a80461 --- /dev/null +++ b/algo/detectors/bmon/Hit.h @@ -0,0 +1,103 @@ +/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// \file Hitfind.h +/// \brief A BMON hit class +/// \since 07.02.2025 +/// \author Sergei Zharko <s.zharko@gsi.de> + +#pragma once + +#include "CbmBmonDigi.h" + +#include <boost/serialization/access.hpp> + +#include <cstdint> +#include <vector> + +namespace cbm::algo::bmon +{ + /// \class Hit + /// \brief A BMON hit + class Hit { + public: + /// \brief Constructor from a single digi + /// \param address Address of the diamond + /// \param digi A digi + Hit(uint32_t address, const CbmBmonDigi& digi) : fAddress(address), fNofChannels(1) + { + fTime = digi.GetTime(); + fTimeError = 0.; // FIXME: provide a rule to set an error + } + + /// \brief Constructor from two digis + /// \param address Address of the diamond + /// \param digiL First digi + /// \param digiR Second digi + Hit(uint32_t address, const CbmBmonDigi& digiL, const CbmBmonDigi& digiR) : fAddress(address), fNofChannels(2) + { + double weightSum = digiL.GetCharge() + digiR.GetCharge(); + fTime = (digiL.GetTime() * digiL.GetCharge() + digiR.GetTime() * digiR.GetCharge()) / weightSum; + fTimeError = 0.; // FIXME: provide a rule to set an error + } + + /// \brief Constructor + /// \param address Address of diamond (the channel is not stored) + /// \param time Time of the hit [ns] + /// \param timeError Time error of the hit [ns] + /// \param nChannels Number of channels used (either one or two) + Hit(uint32_t address, double time, double timeError, uint8_t nChannels) + : fTime(time) + , fTimeError(timeError) + , fAddress(address) + , fNofChannels(nChannels) + { + } + + /// \brief Gets hardware address + uint32_t GetAddress() const { return fAddress; } + + /// \brief Gets number of channels + uint8_t GetNofChannels() const { return fNofChannels; } + + /// \brief Gets time [ns] + double GetTime() const { return fTime; } + + /// \brief Gets time error [ns] + double GetTimeError() const { return fTimeError; } + + /// \brief Sets address + /// \param address Hardware address + void SetAddress(uint32_t address) { fAddress = address; } + + /// \brief Sets number of channels + /// \param nofChannels Number of channels (digis), used to create a hit + void SetNofChannels(uint8_t nofChannels) { fNofChannels = nofChannels; } + + /// \brief Sets time + /// \param time Hit time [ns] + void SetTime(double time) { fTime = time; } + + /// \brief Sets time error + /// \param timeError Hit time error [ns] + void SetTimeError(double timeError) { fTimeError = timeError; } + + private: + double fTime{0.}; ///< Time [ns] + double fTimeError{0.}; ///< Time error [ns] + uint32_t fAddress{0}; ///< Assigned hit address + uint8_t fNofChannels{0}; ///< Number of channels used + + /// \brief Boost serialization function + friend class boost::serialization::access; + template<class Archive> + void serialize(Archive& ar, unsigned int /*version*/) + { + ar& fTime; + ar& fTimeError; + ar& fAddress; + ar& fNofChannels; + } + }; +} // namespace cbm::algo::bmon diff --git a/algo/detectors/bmon/Hitfind.cxx b/algo/detectors/bmon/Hitfind.cxx new file mode 100644 index 0000000000000000000000000000000000000000..d39a46f6c4bf9faf4bfbe2425557ef85ba02664a --- /dev/null +++ b/algo/detectors/bmon/Hitfind.cxx @@ -0,0 +1,113 @@ +/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// \file Hitfind.cxx +/// \brief A BMON hitfinder steering class (implementation) +/// \since 07.02.2025 +/// \author Sergei Zharko <s.zharko@gsi.de> + + +#include "Hitfind.h" + +#include "AlgoFairloggerCompat.h" +#include "compat/OpenMP.h" +#include "util/TimingsFormat.h" + +#include <chrono> + +using cbm::algo::bmon::ClusterizerPars; +using cbm::algo::bmon::Hitfind; +using cbm::algo::bmon::HitfindSetup; +using fles::Subsystem; + +// --------------------------------------------------------------------------------------------------------------------- +// +Hitfind::Hitfind(HitfindSetup setup, uint32_t nThreads) : fNofThreads(nThreads) +{ + // Create one algorithm per diamond and per thread + size_t nDiamondsInSetup{setup.diamonds.size()}; + if (nDiamondsInSetup == 0) { + throw std::runtime_error("No diamonds found in the BMON calibration config"); + } + if (!(setup.selectionMask) != (nDiamondsInSetup == 1)) { + throw std::runtime_error("Wrong diamond selection mask: for a single diamond it must be zero, and for multiple" + " diamonds it must be non-zero"); + } + + if (nDiamondsInSetup > 1) { + // Define the selection bit offset + while (!((setup.selectionMask >> fSelectionBitsOffset) % 2)) { + ++fSelectionBitsOffset; + } + + // Sort the diamonds in the setup by their SM or Side or other distinguishing index + std::sort(setup.diamonds.begin(), setup.diamonds.end(), [&](const auto& lhs, const auto& rhs) { + return GetDiamondIndex(lhs.refAddress) < GetDiamondIndex(rhs.refAddress); + }); + } + + fSelectionBitmask = setup.selectionMask; + fDiamondAddress = PODVector<uint32_t>(nDiamondsInSetup, 0); + + // Store diamond address + for (size_t iDiamond = 0; iDiamond < nDiamondsInSetup; ++iDiamond) { + fDiamondAddress[iDiamond] = setup.diamonds[iDiamond].refAddress & ~CbmTofAddress::GetChannelIdBitmask(); + } + + // Create and configure clusterizer algorithms per thread and per diamond + fAlgo = std::vector<std::vector<Clusterizer>>(fNofThreads, std::vector<Clusterizer>()); + for (auto& algoPerThread : fAlgo) { + algoPerThread.reserve(nDiamondsInSetup); + for (size_t iDiamond = 0; iDiamond < nDiamondsInSetup; ++iDiamond) { + auto par = std::make_unique<ClusterizerPars>(); + const auto& diamondPar = setup.diamonds[iDiamond]; + par->fAddress = diamondPar.refAddress; + par->fDeadStrips = diamondPar.deadStrips; + par->fdMaxTimeDist = diamondPar.maxTimeDist; + par->fTimeRes = diamondPar.timeRes; + algoPerThread.emplace_back(std::move(*par)); + } + } + L_(info) << "--- Configured hitfinder algorithms for BMON."; +} + +// --------------------------------------------------------------------------------------------------------------------- +// +Hitfind::Output_t Hitfind::operator()(gsl::span<CbmBmonDigi> digisIn, uint32_t iThread) +{ + Output_t res = {}; + auto& resHits = std::get<0>(res); + auto& resMoni = std::get<1>(res); + auto& resDigiIds = std::get<2>(res); + + auto& algoPerThread = fAlgo[iThread]; + + // Distribute digis over diamonds, apply cuts on this level (maybe the Calibrator is a more proper place for it) + size_t nDiamonds = algoPerThread.size(); + auto vDigiStorage = std::vector<Clusterizer::Input_t>(nDiamonds, Clusterizer::Input_t(0)); + + for (int32_t iDigi = 0; iDigi < static_cast<int32_t>(digisIn.size()); ++iDigi) { + const auto& digi = digisIn[iDigi]; + size_t iDiamond = GetDiamondIndex(digi.GetAddress()); + if (algoPerThread[iDiamond].SelectDigi(digi)) { + vDigiStorage[iDiamond].emplace_back(digi, iDigi); + } + } + + // NOTE: I see no sense in storing a full channel address for different BMON hits, + // so for each hit the diamond address will be assigned: address & ~CbmTofAddress::GetChannelIdBitmask() + PODVector<Hit> vHitsFlat; // storage for clusters + PODVector<size_t> vNhitsPerDiamond(fDiamondAddress.size()); // number of hits per diamond + + for (size_t iDiamond = 0; iDiamond < algoPerThread.size(); ++iDiamond) { + auto [hits, digiIds] = algoPerThread[iDiamond](vDigiStorage[iDiamond]); + vNhitsPerDiamond[iDiamond] = hits.size(); + vHitsFlat.insert(vHitsFlat.end(), std::make_move_iterator(hits.begin()), std::make_move_iterator(hits.end())); + resDigiIds.insert(resDigiIds.end(), std::make_move_iterator(digiIds.begin()), + std::make_move_iterator(digiIds.end())); + } + + resHits = PartitionedVector(std::move(vHitsFlat), vNhitsPerDiamond, fDiamondAddress); + return res; +} diff --git a/algo/detectors/bmon/Hitfind.h b/algo/detectors/bmon/Hitfind.h new file mode 100644 index 0000000000000000000000000000000000000000..68d7d3a9b57e80099a6a4ce3854445622cca5384 --- /dev/null +++ b/algo/detectors/bmon/Hitfind.h @@ -0,0 +1,60 @@ +/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// \file Hitfind.h +/// \brief BMON hitfinder steering class +/// \since 07.02.2025 +/// \author Sergei Zharko <s.zharko@gsi.de> + +#pragma once + +#include "CbmBmonDigi.h" +#include "PODVector.h" +#include "PartitionedVector.h" +#include "bmon/Clusterizer.h" +#include "bmon/HitfindSetup.h" +#include "tof/Hitfind.h" // for tof::HitfindMonitorData + +#include <gsl/span> +#include <optional> +#include <sstream> +#include <vector> + +#include <xpu/host.h> + +namespace cbm::algo::bmon +{ + /// \brief TOF hit-finder monitor, re-used for BMON + using HitfindMonitorData = tof::HitfindMonitorData; + + /// \class Hitfind + /// \brief Hit-finder steering class for BMON + class Hitfind { + public: + /// \brief Output format + using Output_t = std::tuple<PartitionedVector<Hit>, HitfindMonitorData, PODVector<int32_t>>; + + /// \brief Constructor + /// \param setup Setup configuration + /// \param nThreads Number of threads (for event-based mode) + explicit Hitfind(HitfindSetup setup, uint32_t nThreads = 1); + + /// \brief Algorithm execution operator + /// \param digiIn A portion of digis in TS/event + /// \param iThread Index of thread + Output_t operator()(gsl::span<CbmBmonDigi> digisIn, uint32_t iThread = 0); + + private: // members + /// \brief Returns an index of the diamond by the address + /// \param address A hardware address of the digi + size_t GetDiamondIndex(uint32_t address) const { return ((fSelectionBitmask & address) >> fSelectionBitsOffset); } + + uint32_t fNofThreads; ///< Number of threads + uint32_t fSelectionBitsOffset; ///< Number of bits to ther right from the first bit in the selection mask + uint32_t fSelectionBitmask; ///< Selection bitmask + + std::vector<std::vector<Clusterizer>> fAlgo; ///< Clusterizer algorithms [thread][diamond] + PODVector<uint32_t> fDiamondAddress; ///< Diamond address + }; +} // namespace cbm::algo::bmon diff --git a/algo/detectors/bmon/HitfindSetup.h b/algo/detectors/bmon/HitfindSetup.h new file mode 100644 index 0000000000000000000000000000000000000000..c958cbc17ca2ffcdc2f5027a42f3e6f8f09b1e22 --- /dev/null +++ b/algo/detectors/bmon/HitfindSetup.h @@ -0,0 +1,43 @@ +/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// \file HitfindSetup.h +/// \brief Parameters of the BMON hitfinder +/// \since 06.02.2025 +/// \author Sergei Zharko <s.zharko@gsi.de> + +#pragma once + +#include "Definitions.h" +#include "yaml/Property.h" + +#include <string> +#include <vector> + +namespace cbm::algo::bmon +{ + /// \struct HitfindSetup + /// \brief Parameters for the BMON hitfinder + struct HitfindSetup { + struct Diamond { + u32 refAddress; + u32 deadStrips; + double maxTimeDist; + double timeRes; + + CBM_YAML_PROPERTIES( + yaml::Property(&Diamond::refAddress, "refAddress", "reference address of the diamond"), + yaml::Property(&Diamond::deadStrips, "deadStrips", "bit mask for dead strips"), + yaml::Property(&Diamond::maxTimeDist, "maxTimeDist", "maximum time distance"), + yaml::Property(&Diamond::timeRes, "timeRes", "time resolution")); + }; + + uint32_t selectionMask; + std::vector<Diamond> diamonds; + + CBM_YAML_PROPERTIES( + yaml::Property(&HitfindSetup::selectionMask, "selectionMask", "A bit mask to distinguish between different diamonds"), + yaml::Property(&HitfindSetup::diamonds, "diamonds", "Parameters of diamonds")); + }; +} // namespace cbm::algo::bmon diff --git a/algo/detectors/bmon/UnpackMS.cxx b/algo/detectors/bmon/UnpackMS.cxx index b4fbf123e176991049b236c672beec4fa2a8e26a..22d56e90d1aee148e917555fa5a3bd1019f3d950 100644 --- a/algo/detectors/bmon/UnpackMS.cxx +++ b/algo/detectors/bmon/UnpackMS.cxx @@ -5,6 +5,7 @@ #include "UnpackMS.h" #include "AlgoFairloggerCompat.h" +#include "CbmTofAddress.h" #include <cassert> #include <cmath> diff --git a/algo/global/ParFiles.cxx b/algo/global/ParFiles.cxx index 8eb07742a6669fc291bbfef84548f8b232de9206..eabec37b4b13cdcbcf8e25e137d37fdfa6b384ba 100644 --- a/algo/global/ParFiles.cxx +++ b/algo/global/ParFiles.cxx @@ -25,7 +25,9 @@ ParFiles::ParFiles(uint32_t runId) switch (setup) { case Setup::mCBM2022: - bmon.readout = "BmonReadout_mcbm2022.yaml"; + bmon.readout = "BmonReadout_mcbm2022.yaml"; + bmon.calibrate = "BmonCalibratePar_mcbm2022.yaml"; + bmon.hitfinder = "BmonHitfinderPar_mcbm2022.yaml"; sts.readout = "StsReadout_mcbm2022.yaml"; sts.chanMask = "StsChannelMaskSet_mcbm2022.yaml"; @@ -46,7 +48,9 @@ ParFiles::ParFiles(uint32_t runId) break; case Setup::mCBM2024_03: - bmon.readout = "BmonReadout_mcbm2024.yaml"; + bmon.readout = "BmonReadout_mcbm2024.yaml"; + bmon.calibrate = "BmonCalibratePar_mcbm2024.yaml"; + bmon.hitfinder = "BmonHitfinderPar_mcbm2024.yaml"; sts.readout = "StsReadout_mcbm2024.yaml"; sts.chanMask = "StsChannelMaskSet_mcbm2024.yaml"; @@ -67,7 +71,9 @@ ParFiles::ParFiles(uint32_t runId) break; case Setup::mCBM2024_05: - bmon.readout = "BmonReadout_mcbm2024.yaml"; + bmon.readout = "BmonReadout_mcbm2024.yaml"; + bmon.calibrate = "mcbm2024_05/BmonCalibratePar.yaml"; + bmon.hitfinder = "mcbm2024_05/BmonHitfinderPar.yaml"; sts.readout = "StsReadout_mcbm2024.yaml"; sts.chanMask = "StsChannelMaskSet_mcbm2024.yaml"; diff --git a/algo/global/ParFiles.h b/algo/global/ParFiles.h index 9c966ac6370f04b830fe0fc17a384800e39d13d1..691e27b39bf8af9b64b89cc209422f786048e1e7 100644 --- a/algo/global/ParFiles.h +++ b/algo/global/ParFiles.h @@ -26,6 +26,8 @@ namespace cbm::algo struct { fs::path readout; + fs::path calibrate; + fs::path hitfinder; } bmon; struct { diff --git a/algo/global/Reco.cxx b/algo/global/Reco.cxx index e9a0a7a9a0e0ff8e1cdb27b051c52f149ebc702b..8460ea28bf34fa71071601370839602433199058 100644 --- a/algo/global/Reco.cxx +++ b/algo/global/Reco.cxx @@ -14,6 +14,8 @@ #include "RecoGeneralQa.h" #include "StsDigiQa.h" #include "TrackingSetup.h" +#include "bmon/Calibrate.h" +#include "bmon/Hitfind.h" #include "bmon/ReadoutConfig.h" #include "bmon/Unpack.h" #include "ca/TrackingChain.h" @@ -22,6 +24,7 @@ #include "evbuild/Config.h" #include "much/Unpack.h" #include "qa/QaManager.h" +#include "qa/hitfind/BmonHitfindQa.h" #include "rich/Unpack.h" #include "sts/ChannelMaskSet.h" #include "sts/HitfinderChain.h" @@ -41,6 +44,10 @@ #include <xpu/host.h> +// DEBUG: BEGIN +#include <set> +// DEBUG: END + using namespace cbm::algo; using fles::Subsystem; @@ -204,6 +211,20 @@ void Reco::Init(const Options& opts) fStsHitFinder->SetParameters(hitFinderPars); } + // BMON Hitfinder + if (Opts().Has(fles::Subsystem::BMON) && Opts().Has(Step::LocalReco)) { + auto calibSetup = yaml::ReadFromFile<bmon::CalibrateSetup>(opts.ParamsDir() / parFiles.bmon.calibrate); + fBmonCalibrator = std::make_unique<bmon::Calibrate>(calibSetup); + + auto hitfindSetup = yaml::ReadFromFile<bmon::HitfindSetup>(opts.ParamsDir() / parFiles.bmon.hitfinder); + fBmonHitFinder = std::make_unique<bmon::Hitfind>(hitfindSetup); + + if (fQaManager != nullptr && Opts().Has(QaStep::RecoBmon)) { + fBmonHitFinderQa = std::make_unique<bmon::HitfindQa>(fQaManager, "BmonHitfindEvent"); + fBmonHitFinderQa->InitParameters(calibSetup, hitfindSetup); + fBmonHitFinderQa->Init(); + } + } // TOF Hitfinder if (Opts().Has(fles::Subsystem::TOF) && Opts().Has(Step::LocalReco)) { @@ -222,7 +243,7 @@ void Reco::Init(const Options& opts) // Tracking if (Opts().Has(Step::Tracking)) { - if (Opts().Has(QaStep::Tracking)) { + if (fQaManager != nullptr && Opts().Has(QaStep::Tracking)) { fTracking = std::make_unique<TrackingChain>(fQaManager, "CaTimeslice"); } else { @@ -372,6 +393,24 @@ RecoResults Reco::Run(const fles::Timeslice& ts) QueueEvbuildMetrics(evbuildMonitor); } + // ***** DEBUG: BEGIN + if constexpr (0) { + int nEvents = events.size(); + for (int iE = 0; iE < nEvents; ++iE) { + const auto& event = events[iE]; + // Calibrate TOF digis: + auto [bmonDigis, bmonCalMonitor] = (*fBmonCalibrator)(event.fBmon); + auto [bmonHits, hitmonitor, digiIndices] = (*fBmonHitFinder)(bmonDigis); + if (fBmonHitFinderQa != nullptr) { + fBmonHitFinderQa->RegisterDigis(&bmonDigis); + fBmonHitFinderQa->RegisterHits(&bmonHits); + fBmonHitFinderQa->RegisterDigiIndices(&digiIndices); + fBmonHitFinderQa->Exec(); + } + } + } + // ***** DEBUG: END + // --- Filter data for output if (Opts().HasOutput(RecoData::DigiTimeslice)) { results.bmonDigis = std::move(digis.fBmon); diff --git a/algo/global/Reco.h b/algo/global/Reco.h index 8e26d35271857feebcc89776c800c5abb4511ca2..6182decdad9d289e7a654aabbb6b5db8650efe29 100644 --- a/algo/global/Reco.h +++ b/algo/global/Reco.h @@ -27,6 +27,9 @@ namespace cbm::algo namespace bmon { class Unpack; + class Calibrate; + class Hitfind; + class HitfindQa; } namespace much @@ -146,6 +149,9 @@ namespace cbm::algo // BMON std::unique_ptr<bmon::Unpack> fBmonUnpack; + std::unique_ptr<bmon::Calibrate> fBmonCalibrator; + std::unique_ptr<bmon::Hitfind> fBmonHitFinder; + std::unique_ptr<bmon::HitfindQa> fBmonHitFinderQa; // MUCH std::unique_ptr<much::Unpack> fMuchUnpack; diff --git a/algo/qa/PadConfig.h b/algo/qa/PadConfig.h index 4d6ac5376c121a618601111ff3aa1d6d07deba09..104b2144263b06834952557d14d3b30d21e6197a 100644 --- a/algo/qa/PadConfig.h +++ b/algo/qa/PadConfig.h @@ -43,6 +43,15 @@ namespace cbm::algo::qa { } + /// \brief Constructor from a single histogram + /// \tparam Hist Histogram class + /// \param hist Histogram object + /// \param opt Draw options for the histogram + template<class Hist> + PadConfig(const Hist* hist, std::string_view opt) + { + RegisterHistogram(hist, opt); + } /// \brief Copy constructor PadConfig(const PadConfig&) = default; @@ -104,11 +113,11 @@ namespace cbm::algo::qa std::string ToString() const; private: - bool fbGridX = false; ///< Grid flag for x-axis - bool fbGridY = false; ///< Grid flag for y-axis - bool fbLogX = false; ///< Log flag for x-axis - bool fbLogY = false; ///< Log flag for y-axis - bool fbLogZ = false; ///< Log flag for z-axis + bool fbGridX{false}; ///< Grid flag for x-axis + bool fbGridY{false}; ///< Grid flag for y-axis + bool fbLogX{false}; ///< Log flag for x-axis + bool fbLogY{false}; ///< Log flag for y-axis + bool fbLogZ{false}; ///< Log flag for z-axis std::vector<std::pair<std::string, std::string>> fvObjectList; ///< List of objects on the pad }; diff --git a/algo/qa/QaTaskHeader.h b/algo/qa/QaTaskHeader.h index ca41383152a066fd98b79293f874428cd53fb350..ceb03e5ad457c9b6ceecb1145c7a5f4c15f262b9 100644 --- a/algo/qa/QaTaskHeader.h +++ b/algo/qa/QaTaskHeader.h @@ -24,7 +24,8 @@ namespace cbm::algo::qa /// \param pManager a QA-manager /// \param name A name of the task (histograms directory) TaskHeader(const std::unique_ptr<Manager>& pManager, std::string_view name) - : fpData(pManager != nullptr ? pManager->GetData() : nullptr) + : fsName(name) + , fpData(pManager != nullptr ? pManager->GetData() : nullptr) { if (fpData != nullptr) { fpData->RegisterNewTask(name); @@ -52,6 +53,9 @@ namespace cbm::algo::qa /// the fpData instance is not defined, and no actions on the task should be performed bool IsActive() const { return static_cast<bool>(fpData != nullptr); } + /// \brief Gets name of the task + const std::string& GetTaskName() { return fsName; } + protected: /// \brief Adds a canvas configuration /// \param canvas A CanvasConfig object @@ -68,6 +72,7 @@ namespace cbm::algo::qa } private: + std::string fsName{}; ///< Name of the task std::shared_ptr<Data> fpData{nullptr}; ///< An instance of the QA data (shared between different tasks) }; } // namespace cbm::algo::qa diff --git a/algo/qa/hitfind/BmonHitfindQa.cxx b/algo/qa/hitfind/BmonHitfindQa.cxx new file mode 100644 index 0000000000000000000000000000000000000000..0bf088900f35ef7f55b6ce59f311bda818dee011 --- /dev/null +++ b/algo/qa/hitfind/BmonHitfindQa.cxx @@ -0,0 +1,112 @@ +/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// \file BmonHitfindQa.cxx +/// \brief A BMON hitfinder QA (implementation) +/// \since 09.02.2025 +/// \author Sergei Zharko <s.zharko@gsi.de> + +#include "qa/hitfind/BmonHitfindQa.h" + +#include "CbmBmonDigi.h" +#include "CbmTofAddress.h" +#include "bmon/Hit.h" +#include "qa/Histogram.h" + +#include <fmt/format.h> + +using cbm::algo::bmon::HitfindQa; + +// --------------------------------------------------------------------------------------------------------------------- +// +void HitfindQa::Init() +try { + using fmt::format; + if (!IsActive()) { + return; + } + + size_t nDiamonds = fParameters.diamonds.size(); + if (nDiamonds < 1) { + throw std::runtime_error("parameters were not initialized. Please, provide the configuration using the function " + "HitfindQa::InitParameters(calSetup, hitSetup)"); + } + + fvphDigiOccupVsChan.resize(nDiamonds, nullptr); + fvphDigiChargeVsChan.resize(nDiamonds, nullptr); + fvphHitNofChan.resize(nDiamonds, nullptr); + fvphHitTimeDiff.resize(nDiamonds, nullptr); + + for (size_t iD = 0; iD < nDiamonds; ++iD) { + const auto& diamondPar = fParameters.diamonds[iD]; + int nCh = diamondPar.nChannels; + auto sDN = format("_diamond_{:#08x}", diamondPar.address); // diamond suffix + + // Histograms initialisation + /* clang-format off */ + fvphDigiOccupVsChan[iD] = MakeObj<qa::H1D>( + format("bmon_digi_occup_channel{}", sDN), + format("BMON-{} digi occupancy vs. channel;channel;counts", iD), + nCh, -0.5, nCh - 0.5); + fvphDigiChargeVsChan[iD] = MakeObj<qa::H2D>( + format("bmon_digi_charge_channel{}", sDN), + format("BMON-{} digi charge vs. channel;channel;charge;counts", iD), + nCh, -0.5, nCh - 0.5, kChrgB, kChrgL, kChrgU); + fvphHitNofChan[iD] = MakeObj<qa::H1D>( + format("bmon_hit_nChannels{}", sDN), + format("BMON-{} hit number of channels;N_{{chan}};counts", iD), + 2, 0.5, 2.5); + fvphHitTimeDiff[iD] = MakeObj<qa::H1D>( + format("bmon_hit_time_diff{}", sDN), + format("BMON-{} digi time difference in a hit formed from two digis;#Delta t_{{digi}} [ns];counts", iD), + kDtimeB, kDtimeL, kDtimeU); + /* clang-format on */ + + // Canvas initialization + auto cName = format("{}/bmon{}", GetTaskName(), sDN); + auto cTitl = format("BMON-{}", iD); + auto canv = qa::CanvasConfig(cName, cTitl, 2, 2); + canv.AddPadConfig(qa::PadConfig(fvphDigiOccupVsChan[iD], "hist")); // (0,0) + canv.AddPadConfig(qa::PadConfig(fvphDigiChargeVsChan[iD], "colz")); // (1,0) + canv.AddPadConfig(qa::PadConfig(fvphHitNofChan[iD], "hist")); // (0,1) + canv.AddPadConfig(qa::PadConfig(fvphHitTimeDiff[iD], "hist")); // (1,1) + AddCanvasConfig(canv); + } +} +catch (const std::exception& err) { + L_(fatal) << "bmon::HitfindQa: initialization failed. Reason: " << err.what(); + throw std::runtime_error("bmon::HitfindQa initialization failure"); +} + +// --------------------------------------------------------------------------------------------------------------------- +// +void HitfindQa::Exec() +{ + if (!IsActive()) { + return; + } + + // Fill digi distributions + for (const auto& digi : *fpDigis) { + size_t iDiamond = fParameters.GetDiamondIndex(digi.GetAddress()); + int32_t chan = digi.GetChannel(); + fvphDigiOccupVsChan[iDiamond]->Fill(chan); + fvphDigiChargeVsChan[iDiamond]->Fill(chan, digi.GetCharge()); + } + + // Fill hit distributions + const auto& hits = fpHits->Data(); + for (size_t iH = 0; iH < hits.size(); ++iH) { + const auto& hit = hits[iH]; + size_t iDiamond = fParameters.GetDiamondIndex(hit.GetAddress()); + int nChannels = hit.GetNofChannels(); + fvphHitNofChan[iDiamond]->Fill(nChannels); + if (nChannels == 2) { + int32_t iDigi = (*fpDigiIndices)[iH]; + const auto& digiF = (*fpDigis)[iDigi]; + const auto& digiS = (*fpDigis)[iDigi + 1]; + fvphHitTimeDiff[iDiamond]->Fill(digiS.GetTime() - digiF.GetTime()); + } + } +} diff --git a/algo/qa/hitfind/BmonHitfindQa.h b/algo/qa/hitfind/BmonHitfindQa.h new file mode 100644 index 0000000000000000000000000000000000000000..020f62fa821a2c8ec298c8aa758684aee023cc21 --- /dev/null +++ b/algo/qa/hitfind/BmonHitfindQa.h @@ -0,0 +1,112 @@ +/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// \file BmonHitfindQa.h +/// \brief A BMON hitfinder QA +/// \since 07.02.2025 +/// \author Sergei Zharko <s.zharko@gsi.de> + +#pragma once + +#include "PODVector.h" +#include "PartitionedVector.h" +#include "qa/QaTaskHeader.h" +#include "qa/hitfind/BmonHitfindQaParameters.h" + +class CbmBmonDigi; + +namespace cbm::algo +{ + namespace qa + { + class H1D; + class H2D; + } // namespace qa + + namespace bmon + { + class Hit; + } +} // namespace cbm::algo + +namespace cbm::algo::bmon +{ + /// \class HitfindQa + /// \brief A QA module for the BMON hit-finder + /// \param pManager Pointer to the QA manager + /// \param name Name of the QA (directory) + class HitfindQa : public qa::TaskHeader { + public: + /// \brief Constructor + /// \param pManager Pointer to the QA manager + /// \param name Name of the QA (directory) + HitfindQa(const std::unique_ptr<qa::Manager>& pManager, std::string_view name) : qa::TaskHeader(pManager, name) {} + + /// \brief Constructor from the configuration object + /// \param config QA configuration object + HitfindQa() = default; + + /// \brief Copy constructor + HitfindQa(const HitfindQa&) = delete; + + /// \brief Move constructor + HitfindQa(HitfindQa&&) = delete; + + /// \brief Destructor + ~HitfindQa() = default; + + /// \brief Copy assignment operator + HitfindQa& operator=(const HitfindQa&) = delete; + + /// \brief Move assignment operator + HitfindQa& operator=(HitfindQa&&) = delete; + + /// \brief Executes the task, fills the histograms + void Exec(); + + /// \brief Initialized the task + void Init(); + + /// \brief Initialisation of the parameters + void InitParameters(const CalibrateSetup& calSetup, const HitfindSetup& hitSetup) + { + fParameters = std::move(HitfindQaParameters(calSetup, hitSetup)); + } + + /// \brief Registers a sample of digis + /// \param pDigis A pointer to a vector of digis + void RegisterDigis(const std::vector<CbmBmonDigi>* pDigis) { fpDigis = pDigis; } + + /// \brief Registers a sample of hits + /// \param pHits A pointer to a vector of hits + void RegisterHits(const PartitionedVector<bmon::Hit>* pHits) { fpHits = pHits; } + + /// \brief Registers a sample of digi indices, used by hits + /// \param pDigiIndices A pointer to a vector of digi indices + void RegisterDigiIndices(const PODVector<int32_t>* pDigiIndices) { fpDigiIndices = pDigiIndices; } + + private: + //* Constants + static constexpr int kChrgB = 150; ///< charge scale: number of bins + static constexpr double kChrgL = 0.; ///< charge scale: lower bound + static constexpr double kChrgU = 150.; ///< charge scale: upper bound + static constexpr int kDtimeB = 40; ///< digi time difference: number of bins + static constexpr double kDtimeL = 0.; ///< digi time difference: lower bound [ns] + static constexpr double kDtimeU = 2.0; ///< digi time difference: upper bound [ns] + + //* Parameters + HitfindQaParameters fParameters; ///< Parameters of the hit finder QA + + //* Data samples + const std::vector<CbmBmonDigi>* fpDigis{nullptr}; ///< Pointer to BMON digi sample + const PartitionedVector<bmon::Hit>* fpHits{nullptr}; ///< Pointer to BMON hit sample + const PODVector<int32_t>* fpDigiIndices{nullptr}; ///< Pointer to BMON digi indices, used by hits + + //* Histograms + std::vector<qa::H1D*> fvphDigiOccupVsChan; ///< Digi occupancy vs. channel [diamond] + std::vector<qa::H2D*> fvphDigiChargeVsChan; ///< Digi charge vs channel [diamond] + std::vector<qa::H1D*> fvphHitNofChan; ///< Hit number of channels [diamond] + std::vector<qa::H1D*> fvphHitTimeDiff; ///< Time difference of two digis in a hit [diamond] + }; +} // namespace cbm::algo::bmon diff --git a/algo/qa/hitfind/BmonHitfindQaParameters.cxx b/algo/qa/hitfind/BmonHitfindQaParameters.cxx new file mode 100644 index 0000000000000000000000000000000000000000..aa7f4aadce0f8e7cad631de165f789177a02c4c9 --- /dev/null +++ b/algo/qa/hitfind/BmonHitfindQaParameters.cxx @@ -0,0 +1,58 @@ +/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// \file BmonHitfindQaParameters.cxx +/// \brief A BMON hitfinder QA parameter configuration (implementation) +/// \since 10.02.2025 +/// \author Sergei Zharko <s.zharko@gsi.de> + +#include "qa/hitfind/BmonHitfindQaParameters.h" + +#include "AlgoFairloggerCompat.h" +#include "CbmTofAddress.h" + +using cbm::algo::bmon::CalibrateSetup; +using cbm::algo::bmon::HitfindQaParameters; +using cbm::algo::bmon::HitfindSetup; + +// --------------------------------------------------------------------------------------------------------------------- +// +HitfindQaParameters::HitfindQaParameters(const CalibrateSetup& calSetup, const HitfindSetup& hitSetup) +{ + + this->selectionMask = calSetup.selectionMask; + if (this->selectionMask != hitSetup.selectionMask) { + throw std::runtime_error("Mismatch of the selection bitmask in the BMON CalibrateSetup and HitfindSetup configs"); + } + + auto nDiamonds = calSetup.diamonds.size(); + if (nDiamonds != hitSetup.diamonds.size()) { + throw std::runtime_error("Mismatch of number of diamonds in the BMON CalibrateSetup and HitfindSetup configs"); + } + + if (nDiamonds > 1) { + while (!((this->selectionMask >> fSelectionBitsOffset) % 2)) { + ++fSelectionBitsOffset; + } + } + + this->diamonds.resize(nDiamonds); + for (const auto& calDiamond : calSetup.diamonds) { + uint32_t address = calDiamond.refAddress & ~CbmTofAddress::GetChannelIdBitmask(); + auto& thisDiamond = this->diamonds[GetDiamondIndex(address)]; + thisDiamond.address = address; + thisDiamond.nChannels = calDiamond.chanPar.size(); + } + + for (const auto& hitDiamond : hitSetup.diamonds) { + int32_t address = hitDiamond.refAddress & ~CbmTofAddress::GetChannelIdBitmask(); + auto& thisDiamond = this->diamonds[GetDiamondIndex(address)]; + if (thisDiamond.address != address) { + throw std::runtime_error("Mismatch between diamond addresses in BMON CalibrateSetup and HitfindSetup configs"); + } + thisDiamond.deadStrips = hitDiamond.deadStrips; + thisDiamond.timeRes = hitDiamond.timeRes; + thisDiamond.maxTimeDist = hitDiamond.maxTimeDist; + } +} diff --git a/algo/qa/hitfind/BmonHitfindQaParameters.h b/algo/qa/hitfind/BmonHitfindQaParameters.h new file mode 100644 index 0000000000000000000000000000000000000000..25f524ba87945024350ed277757e8856f1eb6de6 --- /dev/null +++ b/algo/qa/hitfind/BmonHitfindQaParameters.h @@ -0,0 +1,50 @@ +/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// \file BmonHitfindQaParameters.h +/// \brief A BMON hitfinder QA parameter configuration +/// \since 10.02.2025 +/// \author Sergei Zharko <s.zharko@gsi.de> + +#pragma once + +#include "bmon/CalibrateSetup.h" +#include "bmon/HitfindSetup.h" + +#include <vector> + +namespace cbm::algo::bmon +{ + /// \struct HitfindQaParameters + /// \brief A structure to handle BMON QA parameters + struct HitfindQaParameters { + /// \struct Diamond + /// \brief A diamond representation + struct Diamond { + double timeRes{0.}; ///< Time resolution [ns] + double maxTimeDist{0.}; ///< Max time distance between digis in a hit [ns] + int32_t address{0}; ///< Address of a diamond + int32_t nChannels{0}; ///< Number of channels in a diamond + uint32_t deadStrips{0}; ///< A bit mask of dead strips + }; + + uint32_t selectionMask{0}; ///< A bitmask to distinguish different diamonds + std::vector<Diamond> diamonds{}; + + /// \brief Default constructor + HitfindQaParameters() = default; + + /// \brief Constructor + /// \param calSetup Calibration parameters + /// \param hitSetup Hitfinder parameters + HitfindQaParameters(const CalibrateSetup& calSetup, const HitfindSetup& hitSetup); + + /// \brief Returns an index of the diamond by the address + /// \param address A hardware address of the digi + size_t GetDiamondIndex(uint32_t address) const { return ((selectionMask & address) >> fSelectionBitsOffset); } + + private: + uint32_t fSelectionBitsOffset{0}; ///< Number of bits to the right from the first bit in the selection mask + }; +} // namespace cbm::algo::bmon diff --git a/core/data/bmon/CbmBmonDigi.h b/core/data/bmon/CbmBmonDigi.h index 200e72d0cec551bb7ee3c15e4aac8364fab5aab4..fffed5ca6118e8ff51e2c3bdfbc9d619392094e6 100644 --- a/core/data/bmon/CbmBmonDigi.h +++ b/core/data/bmon/CbmBmonDigi.h @@ -1,12 +1,13 @@ -/* Copyright (C) 2022 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt +/* Copyright (C) 2022-2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt SPDX-License-Identifier: GPL-3.0-only - Authors: Pierre-Alain Loizeau, Volker Friese [committer] */ + Authors: Pierre-Alain Loizeau, Volker Friese [committer], Sergei Zharko */ #ifndef CBMBMONDIGI_H #define CBMBMONDIGI_H 1 #include "CbmDefs.h" +#include "CbmTofAddress.h" #ifndef NO_ROOT #include <Rtypes.h> // for ClassDef @@ -85,6 +86,11 @@ public: **/ double GetTime() const { return fTime; } + /** + ** @brief Gets channel ID + ** @return Channel ID + **/ + int32_t GetChannel() const { return CbmTofAddress::GetChannelId(fAddress); } /** @brief Charge ** @return Charge @@ -111,19 +117,20 @@ public: private: - int32_t fAddress = ToIntegralType<ECbmModuleId>(ECbmModuleId::kBmon); ///< Unique CBM address - double fTime = -1.; ///< Time of signal in BMON [ns] - float fCharge = -1.; ///< Charge - - friend class boost::serialization::access; - - template<class Archive> - void serialize(Archive& ar, const unsigned int /*version*/) - { - ar& fAddress; - ar& fTime; - ar& fCharge; - } + // FIXME: SZh 5.2.2025: change address type int32_t -> uint32_t + int32_t fAddress = ToIntegralType<ECbmModuleId>(ECbmModuleId::kBmon); ///< Unique CBM address + double fTime = -1.; ///< Time of signal in BMON [ns] + float fCharge = -1.; ///< Charge + + friend class boost::serialization::access; + + template<class Archive> + void serialize(Archive& ar, const unsigned int /*version*/) + { + ar& fAddress; + ar& fTime; + ar& fCharge; + } #ifndef NO_ROOT ClassDefNV(CbmBmonDigi, 1); diff --git a/core/data/tof/CbmTofAddress.cxx b/core/data/tof/CbmTofAddress.cxx index 0ed83293f27f2576da1de3ffdb02ab3330afa363..29c1573452a1713f962b5a4347e47577000aca0e 100644 --- a/core/data/tof/CbmTofAddress.cxx +++ b/core/data/tof/CbmTofAddress.cxx @@ -1,6 +1,6 @@ -/* Copyright (C) 2013-2020 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt +/* Copyright (C) 2013-2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt SPDX-License-Identifier: GPL-3.0-only - Authors: Pierre-Alain Loizeau, Florian Uhlig [committer], Norbert Herrmann */ + Authors: Pierre-Alain Loizeau, Florian Uhlig [committer], Norbert Herrmann, Segei Zharko */ /** @file CbmTofAddress.cxx ** @author Pierre-Alain Loizeau <loizeau@physi.uni-heidelberg.de> @@ -11,6 +11,9 @@ #include "CbmTofAddress.h" +#include <iomanip> +#include <sstream> + // It seems C++ standard force the initialization to be in cxx/cpp file (outside of class definition) // When not trivial constant values => To check if it should apply also to simple values maybe? /** Offset in bits for Super Module Id in the address field **/ @@ -40,3 +43,26 @@ const int32_t CbmTofAddress::fgkiStripFullIdMask = (((1 << fgkSystemBits) - 1)) + (((1 << fgkSmIdBits) - 1) << fgkSmIdOffset) + (((1 << fgkSmTypeBits) - 1) << fgkSmTypeOffset) + (((1 << fgkRpcIdBits) - 1) << fgkRpcIdOffset) + (((1 << fgkChannelIdBits) - 1) << fgkChannelIdOffset); + +const int32_t CbmTofAddress::fgkSystemIdBitmask = (1 << CbmAddress::fgkSystemBits) - 1; +const int32_t CbmTofAddress::fgkSmIdBitmask = ((1 << fgkSmIdBits) - 1) << fgkSmIdOffset; +const int32_t CbmTofAddress::fgkSmTypeBitmask = ((1 << fgkSmTypeBits) - 1) << fgkSmTypeOffset; +const int32_t CbmTofAddress::fgkRpcIdBitmask = ((1 << fgkRpcIdBits) - 1) << fgkRpcIdOffset; +const int32_t CbmTofAddress::fgkChannelSideBitmask = ((1 << fgkChannelSideBits) - 1) << fgkChannelSideOffset; +const int32_t CbmTofAddress::fgkChannelIdBitmask = ((1 << fgkChannelIdBits) - 1) << fgkChannelIdOffset; +const int32_t CbmTofAddress::fgkRpcTypeBitmask = ((1 << fgkRpcTypeBits) - 1) << fgkRpcTypeOffset; + +std::string CbmTofAddress::ToString(int32_t address) +{ + using std::setfill; + using std::setw; + std::stringstream msg; + msg << std::hex << "0x" << setw(8) << setfill('0') << address << std::dec << setfill(' '); + msg << ": SmType=" << setw(3) << CbmTofAddress::GetSmType(address); + msg << ", Sm=" << setw(2) << CbmTofAddress::GetSmId(address); + msg << ", Rpc=" << setw(2) << CbmTofAddress::GetRpcId(address); + msg << ", Ch=" << setw(2) << CbmTofAddress::GetChannelId(address); + msg << ", Side=" << setw(1) << CbmTofAddress::GetChannelSide(address); + msg << ", RpcType=" << setw(2) << CbmTofAddress::GetRpcType(address); + return msg.str(); +} diff --git a/core/data/tof/CbmTofAddress.h b/core/data/tof/CbmTofAddress.h index f8954d532e759804afcb121c715e7852627fb345..dfa1a18e89a74bd6037206079cf23e60011c956d 100644 --- a/core/data/tof/CbmTofAddress.h +++ b/core/data/tof/CbmTofAddress.h @@ -1,6 +1,6 @@ -/* Copyright (C) 2013-2020 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt +/* Copyright (C) 2013-2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt SPDX-License-Identifier: GPL-3.0-only - Authors: Pierre-Alain Loizeau, Florian Uhlig [committer], Norbert Herrmann */ + Authors: Pierre-Alain Loizeau, Florian Uhlig [committer], Norbert Herrmann, Sergei Zharko */ /** @file CbmTofAddress.h ** @author Pierre-Alain Loizeau <loizeau@physi.uni-heidelberg.de> @@ -44,6 +44,7 @@ #include "CbmTofDetectorId_v12b.h" // for CbmTofDetectorId_v12b #include <cstdint> +#include <string> class CbmTofAddress : public CbmAddress { public: @@ -53,6 +54,37 @@ public: /** Destructor **/ virtual ~CbmTofAddress() {}; + /** Component bitmasks **/ + /** Gets a bitmask for the System bit field + ** @return a bitmask with the bits set only for the System bit field + **/ + static int32_t GetSystemIdBitmask() { return fgkSystemIdBitmask; } + /** Gets a bitmask for the Super Module ID bit field + ** @return a bitmask with the bits set only for the Super Module ID bit field + **/ + static int32_t GetSmIdBitmask() { return fgkSmIdBitmask; } + /** Gets a bitmask for the Super Module Type bit field + ** @return a bitmask with the bits set only for the Super Module Type bit field + **/ + static int32_t GetSmTypeBitmask() { return fgkSmTypeBitmask; } + /** Gets a bitmask for the RPC ID bit field + ** @return a bitmask with the bits set only for the RPC ID bit field + **/ + static int32_t GetRpcIdBitmask() { return fgkRpcIdBitmask; } + /** Gets a bitmask for the Channel Side bit field + ** @return a bitmask with the bits set only for the Channel Side bit field + **/ + static int32_t GetChannelSideBitmask() { return fgkChannelSideBitmask; } + /** Gets a bitmask for the Channel ID bit field + ** @return a bitmask with the bits set only for the Channel ID bit field + **/ + static int32_t GetChannelIdBitmask() { return fgkChannelIdBitmask; } + /** Gets a bitmask for the RPC Type bit field + ** @return a bitmask with the bits set only for the RPC Type bit field + **/ + static int32_t GetRpcTypeBitmask() { return fgkRpcTypeBitmask; } + + /** Field size accessors **/ /** Number of bits for Super Module Id in the address field ** @return Number of bits @@ -75,6 +107,33 @@ public: **/ static int32_t GetNofChSideBits() { return fgkChannelSideBits; }; + + /** Bit field offsets **/ + /** Gets and offset for the System bit field + ** @return and offset with the bits set only for the System bit field + **/ + static int32_t GetSmIdOffset() { return fgkSmIdOffset; } + /** Gets and offset for the Super Module Type bit field + ** @return and offset with the bits set only for the Super Module Type bit field + **/ + static int32_t GetSmTypeOffset() { return fgkSmTypeOffset; } + /** Gets and offset for the RPC ID bit field + ** @return and offset with the bits set only for the RPC ID bit field + **/ + static int32_t GetRpcIdOffset() { return fgkRpcIdOffset; } + /** Gets and offset for the Channel Side bit field + ** @return and offset with the bits set only for the Channel Side bit field + **/ + static int32_t GetChannelSideOffset() { return fgkChannelSideOffset; } + /** Gets and offset for the Channel ID bit field + ** @return and offset with the bits set only for the Channel ID bit field + **/ + static int32_t GetChannelIdOffset() { return fgkChannelIdOffset; } + /** Gets and offset for the RPC Type bit field + ** @return and offset with the bits set only for the RPC Type bit field + **/ + static int32_t GetRpcTypeOffset() { return fgkRpcTypeOffset; } + /** Maskers **/ /** Get the Super Module Id from the address ** @param address Unique address @@ -91,6 +150,11 @@ public: ** @return systemId **/ static int32_t GetRpcId(uint32_t address) { return ((address >> fgkRpcIdOffset) & ((1 << fgkRpcIdBits) - 1)); }; + /** Get the Rpc Type from the address + ** @param address Unique address + ** @return systemId + **/ + static int32_t GetRpcType(uint32_t address) { return ((address >> fgkRpcTypeOffset) & ((1 << fgkRpcTypeBits) - 1)); }; /** Get the Channel Id from the address ** @param address Unique address ** @return systemId @@ -148,7 +212,9 @@ public: { return (GetModFullId(addressA) == GetModFullId(addressB)) ? true : false; }; - + /** + ** @brief Sets the channel ID to the address + **/ static uint32_t ConvertCbmTofDetectorInfo(CbmTofDetectorInfo infoInput) { // For now assume that the system ID will always be correct @@ -169,8 +235,14 @@ public: return GetUniqueAddress(detId.GetSModule(detIdInput), detId.GetCounter(detIdInput), detId.GetCell(detIdInput), 0, detId.GetSMType(detIdInput)); }; + /** String representation of the address + ** @param address Unique address + ** @return String representation of the address + **/ + static std::string ToString(int32_t address); -private: + private: + // FIXME: SZh 7.2.2025: Make these fields constexpr /** ** To adapt the address sub-fields repartition in size, ** you just need to change number of bits of the two sub-fields changing length. @@ -239,6 +311,16 @@ private: ** For the detector strip Id determination (ignore RPC type and side) **/ static const int32_t fgkiStripFullIdMask; + + + /** Individual bitmasks for each bit field **/ + static const int32_t fgkSystemIdBitmask; //< A bitmask for System ID + static const int32_t fgkSmIdBitmask; //< A bitmask for SM + static const int32_t fgkSmTypeBitmask; //< A bitmask for SM Type + static const int32_t fgkRpcIdBitmask; //< A bitmask for RPC ID + static const int32_t fgkChannelSideBitmask; //< A bitmask for Channel Side + static const int32_t fgkChannelIdBitmask; //< A bitmask for Channel ID + static const int32_t fgkRpcTypeBitmask; //< A bitmask for RPC Type }; #endif // CBMTOFADDRESS_H diff --git a/core/data/tof/CbmTofDigi.h b/core/data/tof/CbmTofDigi.h index 942c6edb189dd38b27476ec982c3849a339a81c4..7448ad1a72d204728b1434704ab183a852700938 100644 --- a/core/data/tof/CbmTofDigi.h +++ b/core/data/tof/CbmTofDigi.h @@ -109,6 +109,7 @@ public: /** ** @brief Inherited from CbmDigi. **/ + // FIXME: SZh 5.2.2025: change address type int32_t -> uint32_t int32_t GetAddress() const { return fuAddress; }; @@ -160,6 +161,8 @@ public: double GetSide() const { return CbmTofAddress::GetChannelSide(GetAddress()); }; /** Modifiers **/ + + // FIXME: SZh 5.2.2025: change address type int32_t -> uint32_t void SetAddress(int32_t address) { fuAddress = address; }; void SetAddress(uint32_t Sm, uint32_t Rpc, uint32_t Channel, uint32_t Side = 0, uint32_t SmType = 0); void SetTime(double time) { fdTime = time; }; @@ -169,19 +172,19 @@ public: private: - double fdTime; ///< Absolute time [ps] - double fdTot; ///< Tot [ps] - uint32_t fuAddress; ///< Unique channel address - - friend class boost::serialization::access; - - template<class Archive> - void serialize(Archive& ar, const unsigned int /*version*/) - { - ar& fuAddress; - ar& fdTime; - ar& fdTot; - } + double fdTime; ///< Absolute time [ns] + double fdTot; ///< Tot [ns?] + uint32_t fuAddress; ///< Unique channel address + + friend class boost::serialization::access; + + template<class Archive> + void serialize(Archive& ar, const unsigned int /*version*/) + { + ar& fuAddress; + ar& fdTime; + ar& fdTot; + } #ifndef NO_ROOT ClassDefNV(CbmTofDigi, 3);