diff --git a/algo/CMakeLists.txt b/algo/CMakeLists.txt index 58ac4397e40de296b66547e77c29a6497636ba0d..bc9883747a0e9aad120c691a3071046e81d13912 100644 --- a/algo/CMakeLists.txt +++ b/algo/CMakeLists.txt @@ -96,6 +96,11 @@ set(SRCS 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 @@ -114,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 diff --git a/algo/detectors/bmon/Calibrate.cxx b/algo/detectors/bmon/Calibrate.cxx index 53b40e6b13fdec26b63778780af01d1a0d696ff8..1a2a6af79d13a522eaf08ffcf6072ccefdc70852 100644 --- a/algo/detectors/bmon/Calibrate.cxx +++ b/algo/detectors/bmon/Calibrate.cxx @@ -47,7 +47,7 @@ Calibrate::Calibrate(CalibrateSetup setup) : fSetup(setup), fSelectionBitsOffset // Remove the channel information from the reference address for (auto& diamond : fSetup.diamonds) { - diamond.refAddress = CbmTofAddress::GetModFullId(diamond.refAddress); + diamond.refAddress &= ~CbmTofAddress::GetChannelIdBitmask(); } // Calculate the channel offsets, needed for the dead time @@ -90,7 +90,7 @@ Calibrate::resultType Calibrate::operator()(gsl::span<const CbmBmonDigi> digiIn) int32_t iChannel = CbmTofAddress::GetChannelId(address); size_t iDiamond = GetDiamondIndex(address); if (iDiamond >= diamonds.size() - || static_cast<uint32_t>(CbmTofAddress::GetModFullId(address)) != diamonds[iDiamond].refAddress) { + || (address & ~CbmTofAddress::GetChannelIdBitmask()) != diamonds[iDiamond].refAddress) { monitor.fDigiCalibUnknownRPC++; L_(error) << "Unknown BMON digi address: " << CbmTofAddress::ToString(address) << ", iDiamond = " << iDiamond; continue; diff --git a/algo/detectors/bmon/Calibrate.h b/algo/detectors/bmon/Calibrate.h index 72d3914794d5bfa1161e1ee37052e4d658625b25..0e602e6b4cc1b626c768b006c001c541aef5b8db 100644 --- a/algo/detectors/bmon/Calibrate.h +++ b/algo/detectors/bmon/Calibrate.h @@ -41,7 +41,8 @@ namespace cbm::algo::bmon resultType operator()(gsl::span<const CbmBmonDigi> digiIn); private: - /// \brief A digi address + /// \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); @@ -49,7 +50,7 @@ namespace cbm::algo::bmon CalibrateSetup fSetup; ///< Parameters of calibrator std::vector<size_t> fChannelOffset; ///< Channel offset: offset for the channel index of each diamond - std::vector<double> fChannelDeadTime; ///< Channel dead time + 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 index 26803bd010da857a78908b48da369e043566dba7..eeeb58e1784c7ff8896bb5050157171aff162e79 100644 --- a/algo/detectors/bmon/CalibrateSetup.h +++ b/algo/detectors/bmon/CalibrateSetup.h @@ -40,7 +40,6 @@ namespace cbm::algo::bmon int32_t numClWalkBinX; double TOTMax; double TOTMin; - bool swapChannelSides; double channelDeadtime; std::vector<Channel> chanPar; @@ -49,7 +48,6 @@ namespace cbm::algo::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::swapChannelSides, "swapChannelSides", "flag for swapping channel sides"), yaml::Property(&Diamond::channelDeadtime, "channelDeadtime", "channel dead time"), yaml::Property(&Diamond::chanPar, "chanPar", "channel parameters")); }; @@ -59,7 +57,7 @@ namespace cbm::algo::bmon std::vector<Diamond> diamonds; CBM_YAML_PROPERTIES( - yaml::Property(&CalibrateSetup::selectionMask, "selectionMask", "A mask to distinguish between different diamonds"), + yaml::Property(&CalibrateSetup::selectionMask, "selectionMask", "A bit mask to distinguish between different diamonds"), yaml::Property(&CalibrateSetup::diamonds, "diamonds", "Parameters of each diamond")); }; diff --git a/algo/detectors/bmon/Clusterizer.cxx b/algo/detectors/bmon/Clusterizer.cxx new file mode 100644 index 0000000000000000000000000000000000000000..2d48416e35b526462ee7b8ef9bcb2d15086015cf --- /dev/null +++ b/algo/detectors/bmon/Clusterizer.cxx @@ -0,0 +1,86 @@ +/* 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..b1e6794c8e0291231e2fb2e87d51d6ce92f0bb38 --- /dev/null +++ b/algo/detectors/bmon/Hitfind.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 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..c98cc163ff616f0f0e0b8a55158ea224ebf9cbb6 --- /dev/null +++ b/algo/detectors/bmon/HitfindSetup.h @@ -0,0 +1,42 @@ +/* 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> + + +#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/global/Reco.cxx b/algo/global/Reco.cxx index b16a463d0758b0af1d13c8c5a2154028e754df18..5b2698cb3f2f0c2928165baaa30abfcf298040fe 100644 --- a/algo/global/Reco.cxx +++ b/algo/global/Reco.cxx @@ -15,6 +15,7 @@ #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" @@ -214,8 +215,8 @@ void Reco::Init(const Options& opts) 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); + auto hitfindSetup = yaml::ReadFromFile<bmon::HitfindSetup>(opts.ParamsDir() / parFiles.bmon.hitfinder); + fBmonHitFinder = std::make_unique<bmon::Hitfind>(hitfindSetup); } // TOF Hitfinder @@ -386,60 +387,26 @@ RecoResults Reco::Run(const fles::Timeslice& ts) } // ***** DEBUG: BEGIN - if constexpr (1) { - std::set<uint32_t> bmonFoundAddressesUnp; // found addresses among unpacked bmon digis - std::set<uint32_t> bmonFoundAddressesCal; // found addresses among clalibrated bmon digis - + if constexpr (0) { int nEvents = events.size(); + size_t nBmonHitsOneChannel{0}; + size_t nBmonHitsTwoChannels{0}; for (int iE = 0; iE < nEvents; ++iE) { - //L_(info) << "-------------- event #" << iE; const auto& event = events[iE]; - for (const auto& digi : event.fBmon) { - bmonFoundAddressesUnp.insert(digi.GetAddress()); - } - // Calibrate TOF digis: - auto [tofDigis, tofCalMonitor] = (*fTofCalibrator)(event.fTof); - auto [bmonDigis, bmonCalMonitor] = (*fBmonCalibrator)(event.fBmon); - - for (const auto& digi : bmonDigis) { - bmonFoundAddressesCal.insert(digi.GetAddress()); - } - - double bmonMinT = 1e+20; - double bmonMaxT = -1e+20; - for (const auto& digi : bmonDigis) { - //L_(info) << "BMON: " << digi.GetTime(); - bmonMinT = std::min(bmonMinT, digi.GetTime()); - bmonMaxT = std::max(bmonMaxT, digi.GetTime()); - } - double tofMinT = 1e+20; - double tofMaxT = -1e+20; - for (const auto& digi : tofDigis) { - //L_(info) << "TOF: " << digi.GetTime(); - tofMinT = std::min(tofMinT, digi.GetTime()); - tofMaxT = std::max(tofMaxT, digi.GetTime()); - } - double stsMinT = 1e+20; - double stsMaxT = -1e+20; - const auto& stsDigis = event.fSts; - for (const auto& digi : stsDigis) { - //L_(info) << "STS: " << digi.GetTime(); - stsMinT = std::min(stsMinT, digi.GetTime()); - stsMaxT = std::max(stsMaxT, digi.GetTime()); + auto [bmonDigis, bmonCalMonitor] = (*fBmonCalibrator)(event.fBmon); + auto [bmonHits, hitmonitor, digiindices] = (*fBmonHitFinder)(bmonDigis); + for (const auto& hit : bmonHits.Data()) { + if (hit.GetNofChannels() == 1) { + ++nBmonHitsOneChannel; + } + else { + ++nBmonHitsTwoChannels; + } } - //L_(info) << "BMON time: " << bmonMinT << ", " << bmonMaxT << ", " << (bmonMaxT - bmonMinT); - //L_(info) << "TOF time: " << tofMinT << ", " << tofMaxT << ", " << (tofMaxT - tofMinT); - //L_(info) << "STS time: " << stsMinT << ", " << stsMaxT << ", " << (stsMaxT - stsMinT); - } - L_(info) << "!!!! Found addresses in BMON after unpacking: "; - for (auto address : bmonFoundAddressesUnp) { - L_(info) << " - " << CbmTofAddress::ToString(address); - } - L_(info) << "!!!! Found addresses in BMON after calibration: "; - for (auto address : bmonFoundAddressesCal) { - L_(info) << " - " << CbmTofAddress::ToString(address); } + L_(info) << "!!!! BMON hits with two channels: " << nBmonHitsTwoChannels << " / " + << (nBmonHitsTwoChannels + nBmonHitsOneChannel); } // ***** DEBUG: END diff --git a/algo/global/Reco.h b/algo/global/Reco.h index 982a95cfa49cf83dce88485fc3b80118a58b5042..19027c8368b9fe496282f40eca52eaff22f339d6 100644 --- a/algo/global/Reco.h +++ b/algo/global/Reco.h @@ -28,6 +28,7 @@ namespace cbm::algo { class Unpack; class Calibrate; + class Hitfind; } namespace much @@ -148,6 +149,7 @@ namespace cbm::algo // BMON std::unique_ptr<bmon::Unpack> fBmonUnpack; std::unique_ptr<bmon::Calibrate> fBmonCalibrator; + std::unique_ptr<bmon::Hitfind> fBmonHitFinder; // MUCH std::unique_ptr<much::Unpack> fMuchUnpack; diff --git a/core/data/bmon/CbmBmonDigi.h b/core/data/bmon/CbmBmonDigi.h index 7f0584e2fcb7f64727c7d0cf24734bdbd86191c9..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 diff --git a/core/data/tof/CbmTofAddress.cxx b/core/data/tof/CbmTofAddress.cxx index 068504c39956e2424aea4d82707a896fefdd9f28..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> @@ -44,6 +44,14 @@ const int32_t CbmTofAddress::fgkiStripFullIdMask = + (((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; diff --git a/core/data/tof/CbmTofAddress.h b/core/data/tof/CbmTofAddress.h index d174064bf263428eb7fb2f6a8cc87a4b9bf9351f..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> @@ -54,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 @@ -76,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 @@ -154,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 @@ -251,6 +311,16 @@ public: ** 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