From feac73287f1f486d090c3c9b40bf11a453060a48 Mon Sep 17 00:00:00 2001 From: Wouter Deconinck Date: Sat, 3 Sep 2022 00:44:10 +0000 Subject: [PATCH] Initial commit of new algorithms project --- algorithms/base/include/algorithm.h | 82 +++++++++ algorithms/base/include/property.h | 33 ++++ algorithms/base/include/service.h | 30 +++ .../include/CalorimeterBirksCorr.h | 34 ++++ .../digitization/include/CalorimeterHitDigi.h | 77 ++++++++ algorithms/digitization/src/.gitkeep | 0 algorithms/services/include/logger.h | 171 ++++++++++++++++++ algorithms/services/src/.gitkeep | 0 8 files changed, 427 insertions(+) create mode 100644 algorithms/base/include/algorithm.h create mode 100644 algorithms/base/include/property.h create mode 100644 algorithms/base/include/service.h create mode 100644 algorithms/digitization/include/CalorimeterBirksCorr.h create mode 100644 algorithms/digitization/include/CalorimeterHitDigi.h create mode 100644 algorithms/digitization/src/.gitkeep create mode 100644 algorithms/services/include/logger.h create mode 100644 algorithms/services/src/.gitkeep diff --git a/algorithms/base/include/algorithm.h b/algorithms/base/include/algorithm.h new file mode 100644 index 00000000..6e29dbb1 --- /dev/null +++ b/algorithms/base/include/algorithm.h @@ -0,0 +1,82 @@ +#pragma once + +#include +#include + +namespace algorithms { + + class PropertyBase; + class ServiceBase; + + class StatusCode { + public: + enum Value : uint { + SUCCESS, + FAILURE + }; + StatusCode() = default; + constexpr StatusCode(Value value): m_value(value) { }; + constexpr operator Value() const { return m_value; } + explicit operator bool() const = delete; + constexpr bool operator==(StatusCode a) const { return m_value == a.m_value; } + constexpr bool operator!=(StatusCode a) const { return m_value != a.m_value; } + constexpr bool isFailure() const { return m_value == FAILURE; } + private: + Value m_value; + }; + + class AlgorithmBase { + private: + std::vector> m_properties; + std::vector> m_services; + public: + AlgorithmBase() = default; + void registerProperty(PropertyBase* property, const std::string& name) { + m_properties.push_back(std::make_pair(name, property)); + } + void registerService(ServiceBase* service, const std::string& name) { + m_services.push_back(std::make_pair(name, service)); + } + + }; + + template + class JugAlgorithm : public AlgorithmBase { + private: + std::string m_name; + + static std::function m_debug; + static std::function m_info; + static std::function m_warning; + static std::function m_error; + static std::function m_endmsg; + + static void SetLoggers( + std::function debug, + std::function info, + std::function warning, + std::function error + ) { + m_debug = debug; + m_info = info; + m_warning = warning; + m_error = error; + } + + public: + JugAlgorithm(const std::string& name = "") + : m_name(name) { + } + + virtual Out operator()(const In&) const = 0; + + protected: + static std::ostream& debug() { return m_debug(); }; + static std::ostream& info() { return m_info(); }; + static std::ostream& warning() { return m_warning(); }; + static std::ostream& error() { return m_error(); }; + static std::ostream& endmsg() { return m_endmsg(); }; + + }; + +} // namespace algorithms diff --git a/algorithms/base/include/property.h b/algorithms/base/include/property.h new file mode 100644 index 00000000..0cfdd1c6 --- /dev/null +++ b/algorithms/base/include/property.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +namespace algorithms { + +class PropertyBase { +public: + PropertyBase() = default; +}; + +template class Property : public PropertyBase { +public: + using StorageType = TYPE; + using ValueType = typename std::remove_reference::type; + +private: + std::string m_name; + StorageType m_value; + +public: + template + Property(OWNER* owner, const std::string& name, TYPE&& value) : m_name(name), m_value(std::forward(value)) { + if (owner != nullptr) { + owner->registerProperty(this, m_name); + } + } + + const ValueType& value() const { return this->m_value; } + ValueType& value() { return const_cast((const ValueType&)*this); } +}; + +} // namespace algorithms diff --git a/algorithms/base/include/service.h b/algorithms/base/include/service.h new file mode 100644 index 00000000..54aa875f --- /dev/null +++ b/algorithms/base/include/service.h @@ -0,0 +1,30 @@ +#pragma once + +// Add boilerplate to service class definitions +// - singleton --> no public constructor, no copy constructor, no assigmnent operator +// - constructor should be protected so we can actually inherit from this class if needed +// (mostly needed for the service base class) +#define ALGORITHMS_DEFINE_SERVICE(className) \ + protected: \ + className() {} \ + public: \ + friend class ServiceMixin; \ + className(const className&) = delete; \ + void operator=(const className&) = delete; + +namespace algorithms { + +// Thread-safe lazy-evaluated minimal service system +// CRTP mixin to add the instance method +// This could have been part of DEFINE_SERVICE macro, but I think it is better +// to keep the macro magic to a minimum to maximize transparency +template class ServiceMixin { + public: + static SvcType& instance() { + // This is guaranteed to be thread-safe from C++11 onwards. + static SvcType svc; + return svc; + } +}; + +} diff --git a/algorithms/digitization/include/CalorimeterBirksCorr.h b/algorithms/digitization/include/CalorimeterBirksCorr.h new file mode 100644 index 00000000..8d78efcd --- /dev/null +++ b/algorithms/digitization/include/CalorimeterBirksCorr.h @@ -0,0 +1,34 @@ +// Algorithm base classes +#include "algorithms/base/algorithm.h" +#include "algorithms/base/property.h" +#include "algorithms/base/service.h" +#include "algorithms/base/particleSvc.h" + +// DD4hep +#include "DD4hep/DD4hepUnits.h" + +// EDM4hep +#include "edm4hep/SimCalorimeterHitCollection.h" + +namespace algorithms::digitization { + +/** Generic calorimeter hit digitiziation. + * + * \ingroup digi + * \ingroup calorimetry + */ +class CalorimeterBirksCorr final : public JugAlgorithm { +public: + CalorimeterBirksCorr() = default; + + // Properties + algorithms::Property m_birksConstant{this, "birksConstant", 0.126 * dd4hep::mm / dd4hep::MeV}; + + // Services + algorithms::Service m_pidSvc; + + // Operator + edm4hep::SimCalorimeterHitCollection operator()(const edm4hep::SimCalorimeterHitCollection& input) const; +}; + +} // namespace algorithms::digitization diff --git a/algorithms/digitization/include/CalorimeterHitDigi.h b/algorithms/digitization/include/CalorimeterHitDigi.h new file mode 100644 index 00000000..05b74791 --- /dev/null +++ b/algorithms/digitization/include/CalorimeterHitDigi.h @@ -0,0 +1,77 @@ +#pragma once + +#include + +// Algorithm base classes +#include "algorithms/base/algorithm.h" +#include "algorithms/base/property.h" +#include "algorithms/base/service.h" + +// DD4hep +#include "DD4hep/DD4hepUnits.h" +#include "DDRec/CellIDPositionConverter.h" +#include "DDSegmentation/BitFieldCoder.h" + +// EDM4hep +#include "edm4hep/SimCalorimeterHitCollection.h" +#include "eicd/RawCalorimeterHitCollection.h" + +namespace algorithms::digitization { + +/** Generic calorimeter hit digitization. + * + * \ingroup digi + * \ingroup calorimetry + */ +class CalorimeterHitDigi final : public JugAlgorithm { + +public: + // additional smearing resolutions + algorithms::Property> u_eRes{this, "energyResolutions", {}}; // a/sqrt(E/GeV) + b + c/(E/GeV) + algorithms::Property m_tRes{this, "timeResolution", 0.0 * dd4hep::ns}; + + // digitization settings + algorithms::Property m_capADC{this, "capacityADC", 8096}; + algorithms::Property m_dyRangeADC{this, "dynamicRangeADC", 100 * dd4hep::MeV}; + algorithms::Property m_pedMeanADC{this, "pedestalMean", 400}; + algorithms::Property m_pedSigmaADC{this, "pedestalSigma", 3.2}; + algorithms::Property m_resolutionTDC{this, "resolutionTDC", 0.010 * dd4hep::ns}; + + algorithms::Property m_corrMeanScale{this, "scaleResponse", 1.0}; + // These are better variable names for the "energyResolutions" array which is a bit + // magic @FIXME + // algorithms::Property m_corrSigmaCoeffE{this, "responseCorrectionSigmaCoeffE", 0.0}; + // algorithms::Property m_corrSigmaCoeffSqrtE{this, "responseCorrectionSigmaCoeffSqrtE", 0.0}; + + // signal sums + // @TODO: implement signal sums with timing + // field names to generate id mask, the hits will be grouped by masking the field + algorithms::Property> u_fields{this, "signalSumFields", {}}; + // ref field ids are used for the merged hits, 0 is used if nothing provided + algorithms::Property> u_refs{this, "fieldRefNumbers", {}}; + algorithms::Property m_readout{this, "readoutClass", ""}; + + // Geometry service + algorithms::Service m_geoSvc{this, "geoSvc"}; + + // Random service + algorithms::Service m_normDist{this, "normDist"}; + + // unitless counterparts of inputs FIXME remove + double dyRangeADC{0}, stepTDC{0}, tRes{0}, eRes[3] = {0., 0., 0.}; + uint64_t id_mask{0}, ref_mask{0}; + + CalorimeterHitDigi() = default; + + bool initialize(); + + eicd::RawCalorimeterHitCollection operator()(const edm4hep::SimCalorimeterHitCollection& input) const; + + bool finalize(); + +private: + eicd::RawCalorimeterHitCollection single_hits_digi(const edm4hep::SimCalorimeterHitCollection& simhits) const; + eicd::RawCalorimeterHitCollection signal_sum_digi(const edm4hep::SimCalorimeterHitCollection& simhits) const; +}; + +} // namespace algorithms::digitization diff --git a/algorithms/digitization/src/.gitkeep b/algorithms/digitization/src/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/algorithms/services/include/logger.h b/algorithms/services/include/logger.h new file mode 100644 index 00000000..c1c81ef2 --- /dev/null +++ b/algorithms/services/include/logger.h @@ -0,0 +1,171 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// Simple thread-safe logger with optional overrides by the calling framework +namespace algorithms { + +enum class LogLevel : unsigned { + kJunk = 0, + kDebug = 1, + kInfo = 2, + kWarning = 3, + kError = 4 +}; +constexpr std::array kLogLevelNames{ + "JUNK", "DEBUG", "INFO", "WARNING", "ERROR"}; + +// TODO: integrate proper properties to configure the default level +// Note: the log action is responsible for dealing with concurrent calls +// the default LogAction is a thread-safe example +class LogSvc : public ServiceMixin { + public: + using LogAction = std::function; + void defaultLevel(const LogLevel l) {m_level = l;} + LogLevel defaultLevel() const {return m_level;} + void action(LogAction a) { + m_action = a; + } + void report(const LogLevel l, std::string_view caller, std::string_view msg) const { + m_action(l, caller, msg); + } + + private: + LogLevel m_level = LogLevel::kInfo; + LogAction m_action = [](const LogLevel l, std::string_view caller, std::string_view msg) { + static std::mutex m; + std::lock_guard lock(m); + fmt::print("%s [%s] %s\n", kLogLevelNames[l], caller, msg); + //std::cout << kLogLevelNames[static_cast(l)] << " [" << caller << "] " << msg << std::endl; + }; + ALGORITHMS_DEFINE_SERVICE(LogSvc) +}; + +namespace detail { + // Output buffer that calls our global logger's report() function + class LoggerBuffer : public std::stringbuf { + public: + LoggerBuffer(const LogLevel l, std::string_view caller) : m_mylevel{l}, m_caller{caller}, m_logger{algorithms::LogSvc::instance()} {} + virtual int sync() { + // report should deal with concurrency (the minimal version does) + m_logger.report(m_mylevel, m_caller, this->str()); + return 0; + } + private: + // The output buffer knows the log level of its associated logger + // (eg. is this the debug logger?) + LogLevel m_mylevel; + const std::string m_caller; + const LogSvc& m_logger; + }; + // thread-safe output stream for the logger + class LoggerStream { + public: + LoggerStream(std::string_view caller, const LogLevel level, const LogLevel threshold = LogSvc::instance().defaultLevel()) + : m_buffer{level, caller} + , m_os{&m_buffer} + , m_level{level} + , m_threshold{threshold} {} + LoggerStream() = delete; + LoggerStream(const LoggerStream&) = delete; + + template + LoggerStream& operator<<(Arg&& streamable) { + if (m_level >= m_threshold) { + std::lock_guard lock{m_mutex}; + m_os << std::forward(streamable); + return *this; + } + return *this; + } + // To support input manipulators such as std::endl + // Note: would be better with Concepts + using IOManipType1 = std::ostream&(std::ostream&); // this capturs std::endl; + using IOManipType2 = std::ios_base&(std::ios_base&); + LoggerStream& operator<<(IOManipType1* f) { + if (m_level >= m_threshold) { + std::lock_guard lock{m_mutex}; + f(m_os); + return *this; + } + return *this; + } + LoggerStream& operator<<(IOManipType2* f) { + if (m_level >= m_threshold) { + std::lock_guard lock{m_mutex}; + f(m_os); + return *this; + } + return *this; + } + LogLevel threshold() const {return m_threshold;} + void threshold(const LogLevel th) {m_threshold = th;} + + private: + std::mutex m_mutex; + LoggerBuffer m_buffer; + std::ostream m_os; + const LogLevel m_level; + LogLevel m_threshold; + }; +} + +// Mixin meant to add utility logger functions to algorithms/services/etc +// TODO: add property to configure log level instead of using member function +class LoggerMixin { + public: + LoggerMixin(std::string_view caller, const LogLevel threshold = LogSvc::instance().defaultLevel()) + : m_caller{caller} { + level(threshold); + } + public: + void level(const LogLevel threshold) { + m_level = threshold; + m_error.threshold(m_level); + m_warning.threshold(m_level); + m_info.threshold(m_level); + m_debug.threshold(m_level); + m_junk.threshold(m_level); + } + LogLevel level() const {return m_level;} + + protected: + detail::LoggerStream& error() { + return m_error; + } + detail::LoggerStream& warning() { + return m_warning; + } + detail::LoggerStream& info() { + return m_info; + } + detail::LoggerStream& debug() { + return m_debug; + } + detail::LoggerStream& junk() { + return m_junk; + } + + private: + const std::string m_caller; + LogLevel m_level; + detail::LoggerStream m_error{m_caller, LogLevel::kError}; + detail::LoggerStream m_warning{m_caller, LogLevel::kWarning}; + detail::LoggerStream m_info{m_caller, LogLevel::kInfo}; + detail::LoggerStream m_debug{m_caller, LogLevel::kDebug}; + detail::LoggerStream m_junk{m_caller, LogLevel::kJunk}; +}; + +} + +#define endmsg std::flush diff --git a/algorithms/services/src/.gitkeep b/algorithms/services/src/.gitkeep new file mode 100644 index 00000000..e69de29b -- GitLab