Skip to content

Commit

Permalink
Added first example algorithm, ready for testing
Browse files Browse the repository at this point in the history
  • Loading branch information
sly2j committed Sep 21, 2022
1 parent 8b0de86 commit 1b77f2d
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 69 deletions.
32 changes: 19 additions & 13 deletions JugAlgo/JugAlgo/Algorithm.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,19 @@

namespace Jug::Algo {

namespace detail {} // namespace detail

template <class AlgoImpl> 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;
Expand All @@ -33,7 +34,12 @@ template <class AlgoImpl> class Algorithm : public GaudiAlgorithm {
const algorithms::LogLevel level{
static_cast<algorithms::LogLevel>(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;
Expand All @@ -56,18 +62,18 @@ template <class AlgoImpl> class Algorithm : public GaudiAlgorithm {
virtual StatusCode configure() = 0;

protected:
template <typename T, typename U> void setAlgoProp(std::string_view name, U&& value) {
m_algo.template setProperty<T>(name, value);
template <typename T> void setAlgoProp(std::string_view name, T&& value) {
m_algo.template setProperty(name, value);
}
template <typename T> T getAlgoProp(std::string name) const {
return m_algo.template getProperty<T>(name);
}
bool hasAlgoProp(std::string_view name) const { return m_algo.hasProperty(name); }

private:
AlgoType m_algo;
detail::DataProxy<InputType> m_input;
detail::DataProxy<OutputType> m_output;
algo_type m_algo;
detail::DataProxy<output_type> m_output;
detail::DataProxy<input_type> m_input;
};

} // namespace Jug::Algo
Expand Down
97 changes: 57 additions & 40 deletions JugAlgo/JugAlgo/detail/DataProxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,61 +12,79 @@

namespace Jug::Algo::detail {

enum class DataMode : unsigned { kInput, kOutput };

// Generate properties for each of the data arguments
template <class T, bool kIsInput> class DataElement {
template <class T, DataMode kMode> class DataElement {

public:
using value_type = std::conditional_t<algorithms::is_input_v<T>, algorithms::input_type_t<T>,
using value_type = std::conditional_t<kMode == DataMode::kInput, algorithms::input_type_t<T>,
algorithms::output_type_t<T>>;
using data_type = algorithms::data_type_t<T>;
constexpr static const bool kIsOptional = algorithms::is_optional_v<T>;

DataElement(gsl::not_null<GaudiAlgorithm*> owner, std::string_view name)
: m_owner{owner}, m_data_name(m_owner, name, "") {}
template <class Owner>
DataElement(Owner* owner, std::string_view name)
: m_data_name{std::make_unique<Gaudi::Property<std::string>>(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<DataHandle<data_type>>(
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<T>) {
// 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();
}
}

private:
GaudiAlgorithm* m_owner;
Gaudi::Property<std::string> m_data_name;
std::unique_ptr<DataHandle<T>> m_handle;
std::unique_ptr<Gaudi::Property<std::string>>
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<DataHandle<data_type>> m_handle;
gsl::not_null<Gaudi::Algorithm*> m_owner;
};

// Specialization for vectors
template <class T, class A, bool kIsInput> class DataElement<std::vector<T, A>, kIsInput> {
template <class T, class A, DataMode kMode> class DataElement<std::vector<T, A>, kMode> {
public:
using value_type = std::conditional_t<algorithms::is_input_v<T>, algorithms::input_type_t<T>,
using value_type = std::conditional_t<kMode == DataMode::kInput, algorithms::input_type_t<T>,
algorithms::output_type_t<T>>;
using data_type = algorithms::data_type_t<T>;

DataElement(gsl::not_null<GaudiAlgorithm*> owner, std::string_view name)
: m_owner{owner}, m_data_names(m_owner, name, "") {}
template <class Owner>
DataElement(Owner* owner, std::string_view name)
: m_data_names{std::make_unique<Gaudi::Property<std::vector<std::string>>>(
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<DataHandle<data_type>>(
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
}
Expand All @@ -78,7 +96,7 @@ template <class T, class A, bool kIsInput> class DataElement<std::vector<T, A>,
std::vector<value_type> get() const {
std::vector<value_type> 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());
Expand All @@ -88,16 +106,15 @@ template <class T, class A, bool kIsInput> class DataElement<std::vector<T, A>,
}

private:
GaudiAlgorithm* m_owner;
Gaudi::Property<std::vector<std::string>> m_data_names;
std::unique_ptr<Gaudi::Property<std::vector<std::string>>> m_data_names;
std::vector<std::unique_ptr<DataHandle<T>>> m_handles;
gsl::not_null<Gaudi::Algorithm*> m_owner;
};

template <bool kIsInput, class NamesArray, class Tuple, size_t... I>
auto createElements(GaudiAlgorithm* owner, const NamesArray& names, const Tuple&,
std::index_sequence<I...>)
-> std::tuple<DataElement<std::tuple_element_t<I, Tuple>, kIsInput>...> {
return {{owner, std::get<I>(names)}...};
template <DataMode kMode, class Owner, class NamesArray, class Tuple, size_t... I>
auto createElements(Owner* owner, const NamesArray& names, const Tuple&, std::index_sequence<I...>)
-> std::tuple<DataElement<std::tuple_element_t<I, Tuple>, kMode>...> {
return {DataElement<std::tuple_element_t<I, Tuple>, kMode>(owner, std::get<I>(names))...};
}

// Call ::get() on each element of the HandleTuple, and return the result in the format of
Expand All @@ -111,28 +128,28 @@ ReturnTuple getElements(HandleTuple& handles, std::index_sequence<I...>) {

template <class Data> class DataProxy {
public:
static constexpr bool kIsInput = algorithms::is_input_v<Data>;
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<Data> ? 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<kIsInput>(std::declval<GaudiAlgorithm*>(), names_type(), data_type(),
std::make_index_sequence<kSize>()));
decltype(createElements<kMode>(std::declval<GaudiAlgorithm*>(), names_type(), data_type(),
std::make_index_sequence<kSize>()));

DataProxy(gsl::not_null<GaudiAlgorithm*> owner, const names_type& names)
: m_owner{owner}
, m_elements{createElements<kIsInput>(m_owner, names, data_type(),
std::make_index_sequence<kSize>())} {}
template <class Owner>
DataProxy(Owner* owner, const names_type& names)
: m_elements{
createElements<kMode>(owner, names, data_type(), std::make_index_sequence<kSize>())} {}
void init() {
std::apply([](auto el) { el.init(); }, m_elements);
std::apply([](auto&&... el) { (el.init(), ...); }, m_elements);
}
value_type get() const {
return getElements<value_type>(m_elements, std::make_index_sequence<kSize>());
}

private:
GaudiAlgorithm* m_owner;
elements_type m_elements;
};

Expand Down
2 changes: 1 addition & 1 deletion JugAlgo/src/dummy.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#include <JugAlgo/IAlgoServiceSvc.h>
#include <JugAlgo/Algorithm.h>
//#include <JugAlgo/Algorithm.h>

namespace {
constexpr int doNothing() { return 1; }
Expand Down
3 changes: 2 additions & 1 deletion JugReco/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
48 changes: 48 additions & 0 deletions JugReco/src/components/ClusterRecoCoG.cpp
Original file line number Diff line number Diff line change
@@ -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 <JugAlgo/Algorithm.h>
#include <algorithms/calorimetry/ClusterRecoCoG.h>

#include "Gaudi/Property.h"

namespace Jug::Reco {

namespace {
using AlgoBase = Jug::Algo::Algorithm<algorithms::calorimetry::ClusterRecoCoG>;
}

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<double> m_sampFrac{this, "samplingFraction", 1.0};
Gaudi::Property<double> m_logWeightBase{this, "logWeightBase", 3.6};
Gaudi::Property<std::string> m_energyWeight{this, "energyWeight", "log"};
Gaudi::Property<std::string> m_moduleDimZName{this, "moduleDimZName", ""};
Gaudi::Property<bool> m_enableEtaBounds{this, "enableEtaBounds", false};
};

// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DECLARE_COMPONENT(ClusterRecoCoG)

} // namespace Jug::Reco

22 changes: 14 additions & 8 deletions external/algorithms/core/include/algorithms/algorithm.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <vector>

#include <algorithms/logger.h>
#include <algorithms/name.h>
#include <algorithms/property.h>
#include <algorithms/type_traits.h>

Expand All @@ -18,26 +19,31 @@ template <class... T> struct Input : std::tuple<input_type_t<T>...> {
constexpr static const size_t kSize = sizeof...(T);
using value_type = std::tuple<input_type_t<T>...>;
using data_type = std::tuple<T...>;
using index_type = std::array<const std::string, kSize>;
using key_type = std::array<const std::string, kSize>;
};
template <class... T> struct Output : std::tuple<output_type_t<T>...> {
constexpr static const size_t kSize = sizeof...(T);
using value_type = std::tuple<output_type_t<T>...>;
using data_type = std::tuple<T...>;
using index_type = std::array<const std::string, kSize>;
using key_type = std::array<const std::string, kSize>;
};

// TODO: C++20 Concepts version for better error handling
template <class InputType, class OutputType>
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);
Expand Down
16 changes: 16 additions & 0 deletions external/algorithms/core/include/algorithms/name.h
Original file line number Diff line number Diff line change
@@ -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

9 changes: 3 additions & 6 deletions external/algorithms/core/include/algorithms/service.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <map>
#include <string>

#include <algorithms/name.h>
#include <algorithms/property.h>

// Add boilerplate to service class definitions
Expand Down Expand Up @@ -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 SvcType> class Service : public PropertyMixin {
template <class SvcType> class Service : public PropertyMixin, public NameMixin {
public:
static SvcType& instance() {
// This is guaranteed to be thread-safe from C++11 onwards.
Expand All @@ -64,11 +65,7 @@ template <class SvcType> 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
Expand Down

0 comments on commit 1b77f2d

Please sign in to comment.