diff --git a/algo/base/Prelude.h b/algo/base/Prelude.h new file mode 100644 index 0000000000000000000000000000000000000000..e8d614071183796143de962be9db7778c95955ca --- /dev/null +++ b/algo/base/Prelude.h @@ -0,0 +1,41 @@ +/* Copyright (C) 2023 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main + SPDX-License-Identifier: GPL-3.0-only + Authors: Felix Weiglhofer [committer] */ +#ifndef CBM_ALGO_BASE_PRELUDE_H +#define CBM_ALGO_BASE_PRELUDE_H + +#include <cstdint> +#include <gsl/span> + +namespace cbm::algo +{ + + // typealias for Rust-like fixed size integer types + using i8 = std::int8_t; + using u8 = std::uint8_t; + using i16 = std::int16_t; + using u16 = std::uint16_t; + using i32 = std::int32_t; + using u32 = std::uint32_t; + using i64 = std::int64_t; + using u64 = std::uint64_t; + using f32 = float; + using f64 = double; + +#ifdef CBM_ALGO_REAL64 + using real = f64; +#else + using real = f32; +#endif + +#if !XPU_IS_CUDA && !XPU_IS_HIP // FIXME why doesn't this work with CUDA? + template<typename T> + using span = gsl::span<T>; + + template<typename T> + using cspan = gsl::span<const T>; +#endif + +} // namespace cbm::algo + +#endif // CBM_ALGO_BASE_PRELUDE_H diff --git a/algo/base/RecoParams.h b/algo/base/RecoParams.h new file mode 100644 index 0000000000000000000000000000000000000000..ed9a7e79f27f0108727865e931f3c6aa49d22d50 --- /dev/null +++ b/algo/base/RecoParams.h @@ -0,0 +1,101 @@ +/* Copyright (C) 2023 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main + SPDX-License-Identifier: GPL-3.0-only + Authors: Felix Weiglhofer [committer] */ +#ifndef CBM_ALGO_BASE_RECOPARAMS_H +#define CBM_ALGO_BASE_RECOPARAMS_H + +#include <xpu/defines.h> + +#include "Prelude.h" +#include "config/Property.h" + +namespace cbm::algo +{ + + /** + * @brief RecoParams contains all parameters to configure reconstruction + */ + struct RecoParams { + enum SortMode : u8 + { + SortBlockSort = 0, + SortCUBSegmentedSort = 1, + }; + enum UnpackMode : u8 + { + CPU, + XPU, + }; + + struct STS { + SortMode digiSortMode; + SortMode clusterSortMode; + + UnpackMode unpackMode; + u8 findClustersMultiKernels; + + f32 timeCutDigiAbs; + f32 timeCutDigiSig; + f32 timeCutClusterAbs; + f32 timeCutClusterSig; + + struct MemoryLimits { + u64 maxDigisPerTS; + u64 maxDigisPerMS; + u64 maxDigisPerModule; + f64 clustersPerDigiTS; + f64 clustersPerDigiModule; + f64 hitsPerClusterTS; + f64 hitsPerClusterModule; + + XPU_D u64 maxClustersPerTS() const { return maxDigisPerTS * clustersPerDigiTS; } + XPU_D u64 maxClustersPerModule() const { return maxDigisPerModule * clustersPerDigiModule; } + XPU_D u64 maxHitsPerClusterTS() const { return maxClustersPerTS() * hitsPerClusterTS; } + XPU_D u64 maxHitsPerClusterModule() const { return maxClustersPerModule() * hitsPerClusterModule; } + + static constexpr auto Properties = std::make_tuple( + config::Property(&MemoryLimits::maxDigisPerTS, "maxDigisPerTS", "Maximal number of digis per time slice"), + config::Property(&MemoryLimits::maxDigisPerMS, "maxDigisPerMS", "Maximal number of digis per micro slice"), + config::Property(&MemoryLimits::maxDigisPerModule, "maxDigisPerModule", "Maximal number of digis per module"), + config::Property(&MemoryLimits::clustersPerDigiTS, "clustersPerDigiTS", + "Number of clusters per digi in a time slice"), + config::Property(&MemoryLimits::clustersPerDigiModule, "clustersPerDigiModule", + "Number of clusters per digi in a module"), + config::Property(&MemoryLimits::hitsPerClusterTS, "hitsPerClusterTS", + "Number of hits per cluster in a time slice"), + config::Property(&MemoryLimits::hitsPerClusterModule, "hitsPerClusterModule", + "Number of hits per cluster in a module")); + } memoryLimits; + + static constexpr auto Properties = std::make_tuple( + config::Property(&STS::digiSortMode, "digiSortMode", + "Digi sort mode (0 = block sort, 1 = cub segmented sort))"), + config::Property(&STS::clusterSortMode, "clusterSortMode", "Cluster sort mode"), + + config::Property(&STS::unpackMode, "unpackMode", "Unpack mode (0 = legacy, 1 = XPU)"), + config::Property(&STS::findClustersMultiKernels, "findClustersMultiKernels", + "Split cluster finding into multiple kernels"), + + config::Property(&STS::timeCutDigiAbs, "timeCutDigiAbs", + "Time delta for neighboring digis to be considered for the same cluster. [ns]"), + config::Property( + &STS::timeCutDigiSig, "timeCutDigiSig", + "Used if timeCutDigiAbs is negative. Time delta must be < 'value * sqrt2 * timeResolution'. [ns]"), + config::Property(&STS::timeCutClusterAbs, "timeCutClusterAbs", + "Maximal time difference between two clusters in a hit [ns]." + " Setting to a positive value will override timeCutClustersSig."), + config::Property( + &STS::timeCutClusterSig, "timeCutClusterSig", + "Time cut for clusters." + " Two clusters are considered it their time difference is below 'value * sqrt(terr1**2 + terr2*+2)'"), + config::Property(&STS::memoryLimits, "memoryLimits", "Memory limits for STS reco")); + }; + + STS sts; + + static constexpr auto Properties = std::make_tuple(config::Property(&RecoParams::sts, "sts", "STS reco settings")); + }; + +}; // namespace cbm::algo + +#endif // CBM_ALGO_BASE_RECOPARAMS_H diff --git a/algo/base/config/BaseTypes.h b/algo/base/config/BaseTypes.h new file mode 100644 index 0000000000000000000000000000000000000000..b69620abcde029914f5515a190dcc3887c9b4c5c --- /dev/null +++ b/algo/base/config/BaseTypes.h @@ -0,0 +1,88 @@ +/* Copyright (C) 2023 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main + SPDX-License-Identifier: GPL-3.0-only + Authors: Felix Weiglhofer [committer] */ +#ifndef CBM_ALGO_BASE_CONFIG_BASETYPES_H +#define CBM_ALGO_BASE_CONFIG_BASETYPES_H + +#include <string> +#include <tuple> +#include <vector> + +#include "Prelude.h" + +namespace cbm::algo::config +{ + + using BaseTypes = std::tuple<bool, u8, i8, u16, i16, u32, i32, u64, i64, f32, f64, std::string>; + + template<typename T, typename Tuple> + struct has_type; + + template<typename T, typename... Us> + struct has_type<T, std::tuple<Us...>> : std::disjunction<std::is_same<T, Us>...> { + }; + + template<typename T> + constexpr bool IsBaseType = has_type<T, BaseTypes>::value; + + template<typename T> + constexpr bool IsEnum = std::is_enum_v<T>; + + template<typename T, typename = std::enable_if_t<IsBaseType<T>>> + constexpr std::string_view BaseTypeToStr() + { + if constexpr (std::is_same_v<bool, T>) { return "bool"; } + else if constexpr (std::is_same_v<u8, T>) { + return "u8"; + } + else if constexpr (std::is_same_v<i8, T>) { + return "i8"; + } + else if constexpr (std::is_same_v<u16, T>) { + return "u16"; + } + else if constexpr (std::is_same_v<i16, T>) { + return "i16"; + } + else if constexpr (std::is_same_v<u32, T>) { + return "u32"; + } + else if constexpr (std::is_same_v<i32, T>) { + return "i32"; + } + else if constexpr (std::is_same_v<float, T>) { + return "float"; + } + else if constexpr (std::is_same_v<std::string, T>) { + return "string"; + } + else { + return "unknown"; + } + } + + template<typename> + struct is_std_vector : std::false_type { + }; + + template<typename T, typename A> + struct is_std_vector<std::vector<T, A>> : std::true_type { + }; + + template<typename T> + constexpr bool IsVector = is_std_vector<T>::value; + + template<typename> + struct is_std_array : std::false_type { + }; + + template<typename T, std::size_t N> + struct is_std_array<std::array<T, N>> : std::true_type { + }; + + template<typename T> + constexpr bool IsArray = is_std_array<T>::value; + +} // namespace cbm::algo::config + +#endif // CBM_ALGO_BASE_CONFIG_BASETYPES_H diff --git a/algo/base/config/Property.h b/algo/base/config/Property.h new file mode 100644 index 0000000000000000000000000000000000000000..baddbd9755e22dff031fd8120c43d72f2c49374b --- /dev/null +++ b/algo/base/config/Property.h @@ -0,0 +1,66 @@ +/* Copyright (C) 2023 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main + SPDX-License-Identifier: GPL-3.0-only + Authors: Felix Weiglhofer [committer] */ +#ifndef CBM_ALGO_BASE_CONFIG_PROPERTY_H +#define CBM_ALGO_BASE_CONFIG_PROPERTY_H + +#include <optional> +#include <string_view> +#include <tuple> + +#include <yaml-cpp/emittermanip.h> + +#include "Prelude.h" + +namespace cbm::algo::config +{ + + template<typename Class, typename T> + class Property { + + private: + T Class::*fMember; + std::string_view fKey; + std::string_view fDescription; + std::optional<YAML::EMITTER_MANIP> fFormat; + std::optional<YAML::EMITTER_MANIP> fFormatEntries; + + public: + using ClassType = Class; + using ValueType = T; + + Property() = delete; + + constexpr Property(T Class::*member, std::string_view key, std::string_view description = "", + std::optional<YAML::EMITTER_MANIP> fmt = {}, std::optional<YAML::EMITTER_MANIP> fmtEntries = {}) + : fMember(member) + , fKey(key) + , fDescription(description) + , fFormat(fmt) + , fFormatEntries(fmtEntries) + { + } + + Property(const Property&) = delete; + Property& operator=(const Property&) = delete; + + Property(Property&&) = default; + Property& operator=(Property&&) = default; + + std::string_view Key() const { return fKey; } + std::string_view Description() const { return fDescription; } + std::optional<YAML::EMITTER_MANIP> Format() const { return fFormat; } + std::optional<YAML::EMITTER_MANIP> FormatEntries() const { return fFormatEntries; } + + T& Get(Class& object) const { return object.*fMember; } + const T& Get(const Class& object) const { return object.*fMember; } + + void Set(Class& object, const T& value) const { object.*fMember = value; } + }; + + template<typename Class, typename T> + Property(T Class::*member, std::string_view key, std::string_view description) -> Property<Class, T>; + +} // namespace cbm::algo::config + +#endif // CBM_ALGO_BASE_CONFIG_PROPERTY_H diff --git a/algo/base/config/Yaml.h b/algo/base/config/Yaml.h new file mode 100644 index 0000000000000000000000000000000000000000..0109c5b2344f0732a97c3b6d8a441545fabe5c0e --- /dev/null +++ b/algo/base/config/Yaml.h @@ -0,0 +1,149 @@ +/* Copyright (C) 2023 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main + SPDX-License-Identifier: GPL-3.0-only + Authors: Felix Weiglhofer [committer] */ +#ifndef CBM_ALGO_BASE_CONFIG_DESERIALIZE_H +#define CBM_ALGO_BASE_CONFIG_DESERIALIZE_H + +#include <fairlogger/Logger.h> + +#include <sstream> +#include <string_view> + +#include <yaml-cpp/yaml.h> + +#include "BaseTypes.h" +#include "Prelude.h" +#include "Property.h" + +namespace CORE_INTERFACE_NS::config +{ + + template<typename T, T... Values, typename Func> + constexpr void ForEach(std::integer_sequence<T, Values...>, Func&& func) + { + (func(std::integral_constant<T, Values> {}), ...); + } + + template<typename T> + T Read(const YAML::Node& node) + { + using Type = std::remove_cv_t<std::remove_reference_t<T>>; + + // TODO: error handling + if constexpr (IsBaseType<Type>) { return node.as<Type>(); } + else if constexpr (IsEnum<Type>) { + return static_cast<Type>(node.as<std::underlying_type_t<Type>>()); + } + else if constexpr (IsVector<Type>) { + Type vector; + for (const auto& element : node) { + vector.push_back(Read<typename Type::value_type>(element)); + } + return vector; + } + else if constexpr (IsArray<Type>) { + Type array {}; + auto vector = Read<std::vector<typename Type::value_type>>(node); + if (vector.size() != array.size()) { + throw std::runtime_error(fmt::format("Array size mismatch: expected {}, got {}", array.size(), vector.size())); + } + std::copy(vector.begin(), vector.end(), array.begin()); + return array; + } + else { + Type object; + constexpr auto nProperties = std::tuple_size<decltype(Type::Properties)>::value; + ForEach(std::make_integer_sequence<std::size_t, nProperties> {}, [&](auto index) { + auto& property = std::get<index>(Type::Properties); + using ValueType = std::remove_cv_t<std::remove_reference_t<decltype(property.Get(object))>>; + ValueType& value = property.Get(object); + value = Read<ValueType>(node[std::string {property.Key()}]); + }); + return object; + } + } + + template<typename T> + std::string MakeDocString(int indent = 0) + { + using Type = std::remove_cv_t<std::remove_reference_t<T>>; + + std::stringstream docString; + + if constexpr (IsBaseType<Type>) { docString << BaseTypeToStr<Type>(); } + else if constexpr (IsVector<Type> || IsArray<Type>) { + using ChildType = typename Type::value_type; + if constexpr (IsBaseType<ChildType>) { + docString << std::string(indent, ' ') << "list of " << BaseTypeToStr<ChildType>() << std::endl; + } + else { + docString << std::string(indent, ' ') << "list of" << std::endl; + docString << MakeDocString<ChildType>(indent + 2); + } + } + else { + constexpr auto nProperties = std::tuple_size<decltype(Type::Properties)>::value; + ForEach(std::make_integer_sequence<std::size_t, nProperties> {}, [&](auto index) { + using ChildType = std::remove_cv_t< + std::remove_reference_t<decltype(std::get<index>(Type::Properties).Get(std::declval<Type>()))>>; + auto& property = std::get<index>(Type::Properties); + if constexpr (IsBaseType<ChildType>) { + docString << std::string(indent, ' ') << property.Key() << ": " << property.Description() << " [" + << BaseTypeToStr<ChildType>() << "]" << std::endl; + } + else { + docString << std::string(indent, ' ') << property.Key() << ": " << property.Description() << std::endl; + docString << MakeDocString<ChildType>(indent + 2); + } + }); + } + + return docString.str(); + } + + class Dump { + + public: + template<typename T> + std::string operator()(const T& object) + { + YAML::Emitter ss; + ss << YAML::BeginDoc; + DoDump(object, ss); + ss << YAML::EndDoc; + return ss.c_str(); + } + + private: + template<typename T> + void DoDump(const T& object, YAML::Emitter& ss, std::optional<YAML::EMITTER_MANIP> formatEntries = {}) + { + if constexpr (IsBaseType<T>) { ss << object; } + else if constexpr (IsVector<T> || IsArray<T>) { + ss << YAML::BeginSeq; + for (const auto& element : object) { + if (formatEntries.has_value()) { ss << formatEntries.value(); } + DoDump(element, ss); + } + ss << YAML::EndSeq; + } + else { + constexpr auto nProperties = std::tuple_size<decltype(T::Properties)>::value; + ss << YAML::BeginMap; + ForEach(std::make_integer_sequence<std::size_t, nProperties> {}, [&](auto index) { + auto& property = std::get<index>(T::Properties); + auto& value = property.Get(object); + auto format = property.Format(); + ss << YAML::Key << std::string {property.Key()}; + if (format.has_value()) { ss << format.value(); } + ss << YAML::Value; + DoDump(value, ss, property.FormatEntries()); + }); + ss << YAML::EndMap; + } + } + }; + +} // namespace CORE_INTERFACE_NS::config + +#endif diff --git a/algo/params/RecoParams.yaml b/algo/params/RecoParams.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b69e5db5ea563ddfef1646a202181e773d9aecbd --- /dev/null +++ b/algo/params/RecoParams.yaml @@ -0,0 +1,24 @@ +--- +sts: + # 0: block_sort, 1: cub + digiSortMode: 0 + clusterSortMode: 0 + # 0: cpu, 1: xpu + unpackMode: 0 + findClustersMultiKernels: 1 + timeCutDigiAbs: -1 + timeCutDigiSig: 3 + timeCutClusterAbs: -1 + timeCutClusterSig: 4 + + memoryLimits: + maxDigisPerTS: 80000000 + # TODO: Find sensible value! + maxDigisPerMS: 100000 + # TODO: Find sensible value + maxDigisPerModule: 4000000 + clustersPerDigiTS: 1.0 + clustersPerDigiModule: 1.0 + hitsPerClusterTS: 1.0 + hitsPerClusterModule: 1.0 +...