From 1b77f2d4b5a2237e58183e8f5cd58236f54ff62d Mon Sep 17 00:00:00 2001 From: Sylvester Joosten Date: Wed, 21 Sep 2022 01:45:45 +0000 Subject: [PATCH] Added first example algorithm, ready for testing --- JugAlgo/JugAlgo/Algorithm.h | 32 +++--- JugAlgo/JugAlgo/detail/DataProxy.h | 97 +++++++++++-------- JugAlgo/src/dummy.cpp | 2 +- JugReco/CMakeLists.txt | 3 +- JugReco/src/components/ClusterRecoCoG.cpp | 48 +++++++++ .../core/include/algorithms/algorithm.h | 22 +++-- .../algorithms/core/include/algorithms/name.h | 16 +++ .../core/include/algorithms/service.h | 9 +- 8 files changed, 160 insertions(+), 69 deletions(-) create mode 100644 JugReco/src/components/ClusterRecoCoG.cpp create mode 100644 external/algorithms/core/include/algorithms/name.h diff --git a/JugAlgo/JugAlgo/Algorithm.h b/JugAlgo/JugAlgo/Algorithm.h index d441455..0f9c949 100644 --- a/JugAlgo/JugAlgo/Algorithm.h +++ b/JugAlgo/JugAlgo/Algorithm.h @@ -13,18 +13,19 @@ namespace Jug::Algo { -namespace detail {} // namespace detail - template class Algorithm : public GaudiAlgorithm { public: - using AlgoType = AlgoImpl; - using InputType = typename AlgoType::InputType; - using OutputType = typename AlgoType::OutputType; + using algo_type = AlgoImpl; + using input_type = typename algo_type::input_type; + using output_type = typename algo_type::output_type; + using Input = typename algo_type::Input; + using Output = typename algo_type::Output; Algorithm(const std::string& name, ISvcLocator* svcLoc) : GaudiAlgorithm(name, svcLoc) - , m_input{this, m_algo.inputNames()} - , m_output{this, m_algo.outputNames()} {} + , m_algo{name} + , m_output{this, m_algo.outputNames()} + , m_input{this, m_algo.inputNames()} {} StatusCode initialize() override { debug() << "Initializing " << name() << endmsg; @@ -33,7 +34,12 @@ template class Algorithm : public GaudiAlgorithm { const algorithms::LogLevel level{ static_cast(msgLevel() > 0 ? msgLevel() - 1 : 0)}; debug() << "Setting the logger level to " << algorithms::logLevelName(level) << endmsg; - m_algo->level(level); + m_algo.level(level); + + // Init our data structures + debug() << "Initializing data structures" << endmsg; + m_input.init(); + m_output.init(); // call configure function that passes properties debug() << "Configuring properties" << endmsg; @@ -56,8 +62,8 @@ template class Algorithm : public GaudiAlgorithm { virtual StatusCode configure() = 0; protected: - template void setAlgoProp(std::string_view name, U&& value) { - m_algo.template setProperty(name, value); + template void setAlgoProp(std::string_view name, T&& value) { + m_algo.template setProperty(name, value); } template T getAlgoProp(std::string name) const { return m_algo.template getProperty(name); @@ -65,9 +71,9 @@ template class Algorithm : public GaudiAlgorithm { bool hasAlgoProp(std::string_view name) const { return m_algo.hasProperty(name); } private: - AlgoType m_algo; - detail::DataProxy m_input; - detail::DataProxy m_output; + algo_type m_algo; + detail::DataProxy m_output; + detail::DataProxy m_input; }; } // namespace Jug::Algo diff --git a/JugAlgo/JugAlgo/detail/DataProxy.h b/JugAlgo/JugAlgo/detail/DataProxy.h index 5dc370b..77b77c3 100644 --- a/JugAlgo/JugAlgo/detail/DataProxy.h +++ b/JugAlgo/JugAlgo/detail/DataProxy.h @@ -12,31 +12,41 @@ namespace Jug::Algo::detail { +enum class DataMode : unsigned { kInput, kOutput }; + // Generate properties for each of the data arguments -template class DataElement { +template class DataElement { + public: - using value_type = std::conditional_t, algorithms::input_type_t, + using value_type = std::conditional_t, algorithms::output_type_t>; using data_type = algorithms::data_type_t; + constexpr static const bool kIsOptional = algorithms::is_optional_v; - DataElement(gsl::not_null owner, std::string_view name) - : m_owner{owner}, m_data_name(m_owner, name, "") {} + template + DataElement(Owner* owner, std::string_view name) + : m_data_name{std::make_unique>(owner, std::string(name), "")} + , m_owner{owner} {} void init() { if (m_handle) { // treat error: already initialized } - if (!m_data_name.empty()) { + if (!m_data_name->empty()) { m_handle = std::make_unique>( - m_data_name, (kIsInput ? Gaudi::DataHandle::Reader : Gaudi::DataHandle::Writer), m_owner); + *m_data_name, + ((kMode == DataMode::kInput) ? Gaudi::DataHandle::Reader : Gaudi::DataHandle::Writer), + m_owner); } else if (!algorithms::is_optional_v) { // treat error: member not optional but no collection name given } } value_type get() const { - if (!m_handle) { - return nullptr; + if constexpr (kIsOptional) { + if (!m_handle) { + return nullptr; + } } - if constexpr (kIsInput) { + if constexpr (kMode == DataMode::kInput) { return m_handle->get(); } else { return m_handle->createAndPut(); @@ -44,29 +54,37 @@ template class DataElement { } private: - GaudiAlgorithm* m_owner; - Gaudi::Property m_data_name; - std::unique_ptr> m_handle; + std::unique_ptr> + m_data_name; // This needs to be a pointer, else things go wrong once we go through + // createElements - probably something about passing the Property through an + // rvalue (or copy) constructor + std::unique_ptr> m_handle; + gsl::not_null m_owner; }; // Specialization for vectors -template class DataElement, kIsInput> { +template class DataElement, kMode> { public: - using value_type = std::conditional_t, algorithms::input_type_t, + using value_type = std::conditional_t, algorithms::output_type_t>; using data_type = algorithms::data_type_t; - DataElement(gsl::not_null owner, std::string_view name) - : m_owner{owner}, m_data_names(m_owner, name, "") {} + template + DataElement(Owner* owner, std::string_view name) + : m_data_names{std::make_unique>>( + owner, std::string(name), {})} + , m_owner{owner} {} void init() { if (!m_handles.empty()) { // treat error: already initialized } - if (!m_data_names.empty()) { - for (const auto& name : m_data_names) { + if (!m_data_names->empty()) { + for (const auto& name : *m_data_names) { if (!name.empty()) { m_handles.emplace_back(std::make_unique>( - name, (kIsInput ? Gaudi::DataHandle::Reader : Gaudi::DataHandle::Writer), m_owner)); + name, + (kMode == DataMode::kInput ? Gaudi::DataHandle::Reader : Gaudi::DataHandle::Writer), + m_owner)); } else { // treat error: empty name } @@ -78,7 +96,7 @@ template class DataElement, std::vector get() const { std::vector ret; for (auto& handle : m_handles) { - if constexpr (kIsInput) { + if constexpr (kMode == DataMode::kInput) { ret.emplace_back(handle->get()); } else { ret.emplace_back(handle->createAndPut()); @@ -88,16 +106,15 @@ template class DataElement, } private: - GaudiAlgorithm* m_owner; - Gaudi::Property> m_data_names; + std::unique_ptr>> m_data_names; std::vector>> m_handles; + gsl::not_null m_owner; }; -template -auto createElements(GaudiAlgorithm* owner, const NamesArray& names, const Tuple&, - std::index_sequence) - -> std::tuple, kIsInput>...> { - return {{owner, std::get(names)}...}; +template +auto createElements(Owner* owner, const NamesArray& names, const Tuple&, std::index_sequence) + -> std::tuple, kMode>...> { + return {DataElement, kMode>(owner, std::get(names))...}; } // Call ::get() on each element of the HandleTuple, and return the result in the format of @@ -111,28 +128,28 @@ ReturnTuple getElements(HandleTuple& handles, std::index_sequence) { template class DataProxy { public: - static constexpr bool kIsInput = algorithms::is_input_v; - using value_type = Data; - using data_type = typename Data::data_type; - constexpr static size_t kSize = Data::kSize; - using names_type = typename Data::DataNames; + static constexpr DataMode kMode = + (algorithms::is_input_v ? DataMode::kInput : DataMode::kOutput); + using value_type = typename Data::value_type; + using data_type = typename Data::data_type; + constexpr static size_t kSize = Data::kSize; + using names_type = typename Data::key_type; using elements_type = - decltype(createElements(std::declval(), names_type(), data_type(), - std::make_index_sequence())); + decltype(createElements(std::declval(), names_type(), data_type(), + std::make_index_sequence())); - DataProxy(gsl::not_null owner, const names_type& names) - : m_owner{owner} - , m_elements{createElements(m_owner, names, data_type(), - std::make_index_sequence())} {} + template + DataProxy(Owner* owner, const names_type& names) + : m_elements{ + createElements(owner, names, data_type(), std::make_index_sequence())} {} void init() { - std::apply([](auto el) { el.init(); }, m_elements); + std::apply([](auto&&... el) { (el.init(), ...); }, m_elements); } value_type get() const { return getElements(m_elements, std::make_index_sequence()); } private: - GaudiAlgorithm* m_owner; elements_type m_elements; }; diff --git a/JugAlgo/src/dummy.cpp b/JugAlgo/src/dummy.cpp index ee09e28..959f352 100644 --- a/JugAlgo/src/dummy.cpp +++ b/JugAlgo/src/dummy.cpp @@ -1,5 +1,5 @@ #include -#include +//#include namespace { constexpr int doNothing() { return 1; } diff --git a/JugReco/CMakeLists.txt b/JugReco/CMakeLists.txt index e430560..748d0b2 100644 --- a/JugReco/CMakeLists.txt +++ b/JugReco/CMakeLists.txt @@ -11,7 +11,8 @@ gaudi_add_module(JugRecoPlugins ${JugRecoPlugins_sources} LINK Gaudi::GaudiAlgLib Gaudi::GaudiKernel - JugBase + JugBase JugAlgo + algocore algocalorimetry ROOT::Core ROOT::RIO ROOT::Tree EDM4HEP::edm4hep EDM4EIC::edm4eic diff --git a/JugReco/src/components/ClusterRecoCoG.cpp b/JugReco/src/components/ClusterRecoCoG.cpp new file mode 100644 index 0000000..2839ca6 --- /dev/null +++ b/JugReco/src/components/ClusterRecoCoG.cpp @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (C) 2022 Sylvester Joosten, Chao, Chao Peng, Whitney Armstrong + +/* + * Reconstruct the cluster with Center of Gravity method + * Logarithmic weighting is used for mimicing energy deposit in transverse direction + * + * Author: Sylvester Joosten, Chao Peng (ANL), 09/20/2022 + */ + +#include +#include + +#include "Gaudi/Property.h" + +namespace Jug::Reco { + +namespace { + using AlgoBase = Jug::Algo::Algorithm; +} + +class ClusterRecoCoG : public AlgoBase { + +public: + ClusterRecoCoG(const std::string& name, ISvcLocator* svcLoc) : AlgoBase(name, svcLoc) {} + + virtual StatusCode configure() { + setAlgoProp("samplingFraction", m_sampFrac.value()); + setAlgoProp("logWeightBase", m_logWeightBase.value()); + setAlgoProp("energyWeight", m_energyWeight.value()); + setAlgoProp("moduleDimZName", m_moduleDimZName.value()); + setAlgoProp("enableEtaBounds", m_enableEtaBounds.value()); + return StatusCode::SUCCESS; + } + +private: + Gaudi::Property m_sampFrac{this, "samplingFraction", 1.0}; + Gaudi::Property m_logWeightBase{this, "logWeightBase", 3.6}; + Gaudi::Property m_energyWeight{this, "energyWeight", "log"}; + Gaudi::Property m_moduleDimZName{this, "moduleDimZName", ""}; + Gaudi::Property m_enableEtaBounds{this, "enableEtaBounds", false}; +}; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +DECLARE_COMPONENT(ClusterRecoCoG) + +} // namespace Jug::Reco + diff --git a/external/algorithms/core/include/algorithms/algorithm.h b/external/algorithms/core/include/algorithms/algorithm.h index 92fec5b..cad074f 100644 --- a/external/algorithms/core/include/algorithms/algorithm.h +++ b/external/algorithms/core/include/algorithms/algorithm.h @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -18,26 +19,31 @@ template struct Input : std::tuple...> { constexpr static const size_t kSize = sizeof...(T); using value_type = std::tuple...>; using data_type = std::tuple; - using index_type = std::array; + using key_type = std::array; }; template struct Output : std::tuple...> { constexpr static const size_t kSize = sizeof...(T); using value_type = std::tuple...>; using data_type = std::tuple; - using index_type = std::array; + using key_type = std::array; }; // TODO: C++20 Concepts version for better error handling template -class Algorithm : public PropertyMixin, public LoggerMixin { +class Algorithm : public PropertyMixin, public LoggerMixin, public NameMixin { public: - using Input = typename InputType::value_type; - using Output = typename OutputType::value_type; - using InputNames = typename InputType::index_type; - using OutputNames = typename OutputType::index_type; + using input_type = InputType; + using output_type = OutputType; + using Input = typename input_type::value_type; + using Output = typename output_type::value_type; + using InputNames = typename input_type::key_type; + using OutputNames = typename output_type::key_type; Algorithm(std::string_view name, const InputNames& input_names, const OutputNames& output_names) - : LoggerMixin(name), m_input_names{input_names}, m_output_names{output_names} {} + : LoggerMixin(name) + , NameMixin(name) + , m_input_names{input_names} + , m_output_names{output_names} {} void init(); void process(const Input& input, const Output& output); diff --git a/external/algorithms/core/include/algorithms/name.h b/external/algorithms/core/include/algorithms/name.h new file mode 100644 index 0000000..d1668dc --- /dev/null +++ b/external/algorithms/core/include/algorithms/name.h @@ -0,0 +1,16 @@ +#pragma once + +namespace algorithms { + +// Simple name Mixin providing consistent name API +class NameMixin { +public: + NameMixin(std::string_view name) : m_name{name} {} + std::string_view name() const { return m_name; } + +private: + const std::string m_name; +}; + +} // namespace algorithms + diff --git a/external/algorithms/core/include/algorithms/service.h b/external/algorithms/core/include/algorithms/service.h index 9c35b28..7fa705c 100644 --- a/external/algorithms/core/include/algorithms/service.h +++ b/external/algorithms/core/include/algorithms/service.h @@ -3,6 +3,7 @@ #include #include +#include #include // Add boilerplate to service class definitions @@ -55,7 +56,7 @@ class ServiceSvc { // CRTP base class 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 Service : public PropertyMixin { +template class Service : public PropertyMixin, public NameMixin { public: static SvcType& instance() { // This is guaranteed to be thread-safe from C++11 onwards. @@ -64,11 +65,7 @@ template class Service : public PropertyMixin { } // constructor for the service base class registers the service, except // for the ServiceSvc which is its own thing (avoid circularity) - Service(std::string_view name) : m_name{name} { ServiceSvc::instance().add(name, this); } - std::string_view name() const { return m_name; } - -private: - const std::string m_name; + Service(std::string_view name) : NameMixin{name} { ServiceSvc::instance().add(name, this); } }; } // namespace algorithms