From a3380b2133bce99d3a3d9c7c07220f6a5e71285c Mon Sep 17 00:00:00 2001 From: Juan Miguel Carceller <22276694+jmcarcell@users.noreply.github.com> Date: Mon, 15 Jul 2024 19:43:15 +0200 Subject: [PATCH] Use `std::vector` instead of `std::map` for reading or writing an arbitrary number of collections. (#201) --- CMakeLists.txt | 4 +- k4FWCore/include/k4FWCore/Consumer.h | 32 +++- k4FWCore/include/k4FWCore/FunctionalUtils.h | 64 ++++---- k4FWCore/include/k4FWCore/Transformer.h | 127 ++++++++++++++-- k4FWCore/src/PodioDataSvc.cpp | 3 - ...leFunctionalConsumerRuntimeCollections.cpp | 26 +++- ...onalConsumerRuntimeCollectionsMultiple.cpp | 37 ++--- ...leFunctionalProducerRuntimeCollections.cpp | 33 +++-- ...unctionalTransformerRuntimeCollections.cpp | 20 +-- ...lTransformerRuntimeCollectionsMultiple.cpp | 137 +++++++++--------- ...ampleFunctionalTransformerRuntimeEmpty.cpp | 14 +- 11 files changed, 312 insertions(+), 185 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f0182aae..dec57922 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,9 +39,9 @@ endif() # Set up C++ Standard # ``-DCMAKE_CXX_STANDARD=`` when invoking CMake -set(CMAKE_CXX_STANDARD 17 CACHE STRING "") +set(CMAKE_CXX_STANDARD 20 CACHE STRING "") -if(NOT CMAKE_CXX_STANDARD MATCHES "17|20") +if(NOT CMAKE_CXX_STANDARD MATCHES "20") message(FATAL_ERROR "Unsupported C++ standard: ${CMAKE_CXX_STANDARD}") endif() diff --git a/k4FWCore/include/k4FWCore/Consumer.h b/k4FWCore/include/k4FWCore/Consumer.h index 5a9911bf..0b061ed3 100644 --- a/k4FWCore/include/k4FWCore/Consumer.h +++ b/k4FWCore/include/k4FWCore/Consumer.h @@ -27,6 +27,8 @@ #include "k4FWCore/FunctionalUtils.h" +#include +#include #include #include @@ -41,8 +43,8 @@ namespace k4FWCore { : Gaudi::Functional::details::DataHandleMixin, std::tuple<>, Traits_> { using Gaudi::Functional::details::DataHandleMixin, std::tuple<>, Traits_>::DataHandleMixin; - static_assert(((std::is_base_of_v || isMapToCollLike::value) && ...), - "Consumer input types must be EDM4hep collections or maps to collections"); + static_assert(((std::is_base_of_v || isVectorLike_v)&&...), + "Consumer input types must be EDM4hep collections or vectors of collection pointers"); template using InputHandle_t = Gaudi::Functional::details::InputHandle_t>; @@ -90,6 +92,32 @@ namespace k4FWCore { // ... instead, they must implement the following operator virtual void operator()(const In&...) const = 0; + + /** + * @brief Get the input locations for a given input index + * @param i The index of the input + * @return A range of the input locations + */ + const auto inputLocations(int i) const { + if (i >= sizeof...(In)) { + throw std::out_of_range("Called inputLocations with an index out of range, index: " + std::to_string(i) + + ", number of inputs: " + std::to_string(sizeof...(In))); + } + return m_inputLocations[i] | std::views::transform([](const DataObjID& id) -> const auto& { return id.key(); }); + } + /** + * @brief Get the input locations for a given input name + * @param name The name of the input + * @return A range of the input locations + */ + const auto inputLocations(std::string_view name) const { + auto it = std::ranges::find_if(m_inputLocations, [&name](const auto& prop) { return prop.name() == name; }); + if (it == m_inputLocations.end()) { + throw std::runtime_error("Called inputLocations with an unknown name"); + } + return it->value() | std::views::transform([](const DataObjID& id) -> const auto& { return id.key(); }); + } + static constexpr std::size_t inputLocationsSize() { return sizeof...(In); } }; } // namespace details diff --git a/k4FWCore/include/k4FWCore/FunctionalUtils.h b/k4FWCore/include/k4FWCore/FunctionalUtils.h index 84778e53..58268021 100644 --- a/k4FWCore/include/k4FWCore/FunctionalUtils.h +++ b/k4FWCore/include/k4FWCore/FunctionalUtils.h @@ -31,7 +31,6 @@ // #include "GaudiKernel/CommonMessaging.h" -#include #include #include #include @@ -63,16 +62,21 @@ namespace k4FWCore { return static_cast(*arg); } - // Check if the type is a map like type, where map type is the special map + // Check if the type is a vector like type, where vector is the special // type to have an arbitrary number of collections as input or output: - // std::map where Coll is the collection type - template struct isMapToCollLike : std::false_type {}; + // std::vector where Coll is the collection type for output + // and const std::vector& for input + template struct isVectorLike : std::false_type {}; template requires std::is_base_of_v> - struct isMapToCollLike> : std::true_type {}; + struct isVectorLike> : std::true_type {}; - template inline constexpr bool isMapToCollLike_v = isMapToCollLike::value; + template + requires std::is_base_of_v> + struct isVectorLike> : std::true_type {}; + + template inline constexpr bool isVectorLike_v = isVectorLike::value; // transformType function to transform the types from the ones that the user wants // like edm4hep::MCParticleCollection, to the ones that are actually stored in the @@ -83,7 +87,7 @@ namespace k4FWCore { }; template - requires std::is_base_of_v || isMapToCollLike_v + requires std::is_base_of_v || isVectorLike_v struct transformType { using type = std::shared_ptr; }; @@ -105,9 +109,9 @@ namespace k4FWCore { static auto apply(const Algorithm& algo, const EventContext&, Handles& handles) { auto inputTuple = std::tuple...>(); - // Build the input tuple by picking up either std::map with an arbitrary + // Build the input tuple by picking up either std::vector with an arbitrary // number of collections or single collections - readMapInputs<0, In...>(handles, &algo, inputTuple); + readVectorInputs<0, In...>(handles, &algo, inputTuple); return std::apply( [&](const auto&... input) { return algo(maybeTransformToEDM4hep(input)...); }, inputTuple); @@ -115,17 +119,16 @@ namespace k4FWCore { }; template - void readMapInputs(const std::tuple& handles, auto thisClass, InputTuple& inputTuple) { + void readVectorInputs(const std::tuple& handles, auto thisClass, InputTuple& inputTuple) { if constexpr (Index < sizeof...(Handles)) { - if constexpr (isMapToCollLike_v>>) { - // In case of map types like std::map - // we have to remove the reference to get the actual type + if constexpr (isVectorLike_v>>) { + // Bare EDM4hep type, without pointers using EDM4hepType = - std::remove_reference_t>::mapped_type>; - auto inputMap = std::map(); + std::remove_pointer_t>::value_type>; + auto inputMap = std::vector(); for (auto& handle : std::get(handles)) { auto in = get(handle, thisClass, Gaudi::Hive::currentContext()); - inputMap.emplace(handle.objKey(), *static_cast(in.get())); + inputMap.push_back(static_cast(in.get())); } std::get(inputTuple) = std::move(inputMap); @@ -159,36 +162,23 @@ namespace k4FWCore { } // Recursive call for the next index - readMapInputs(handles, thisClass, inputTuple); + readVectorInputs(handles, thisClass, inputTuple); } } template - void putMapOutputs(std::tuple&& handles, const auto& m_outputs, auto thisClass) { + void putVectorOutputs(std::tuple&& handles, const auto& m_outputs, auto thisClass) { if constexpr (Index < sizeof...(Handles)) { - if constexpr (isMapToCollLike_v>>) { + if constexpr (isVectorLike_v>>) { int i = 0; if (std::get(handles).size() != std::get(m_outputs).size()) { - std::string msg = "Size of the output map " + std::to_string(std::get(handles).size()) + + std::string msg = "Size of the output vector " + std::to_string(std::get(handles).size()) + + " with type " + typeid(std::get(handles)).name() + " does not match the expected size from the steering file " + - std::to_string(std::get(m_outputs).size()) + ". Expected the collections: "; - for (auto& out : std::get(m_outputs)) { - msg += out.objKey() + " "; - } - msg += " but got the collections: "; - for (auto& out : std::get(handles)) { - msg += out.first + " "; - } + std::to_string(std::get(m_outputs).size()); throw GaudiException(thisClass->name(), msg, StatusCode::FAILURE); } - for (auto& [key, val] : std::get(handles)) { - if (key != std::get(m_outputs)[i].objKey()) { - throw GaudiException(thisClass->name(), - "Output key in the map \"" + key + - "\" does not match the expected key from the steering file \"" + - std::get(m_outputs)[i].objKey() + "\"", - StatusCode::FAILURE); - } + for (auto& val : std::get(handles)) { Gaudi::Functional::details::put(std::get(m_outputs)[i], convertToSharedPtr(std::move(val))); i++; } @@ -198,7 +188,7 @@ namespace k4FWCore { } // Recursive call for the next index - putMapOutputs(std::move(handles), m_outputs, thisClass); + putVectorOutputs(std::move(handles), m_outputs, thisClass); } } diff --git a/k4FWCore/include/k4FWCore/Transformer.h b/k4FWCore/include/k4FWCore/Transformer.h index f5030ddd..ec803cc1 100644 --- a/k4FWCore/include/k4FWCore/Transformer.h +++ b/k4FWCore/include/k4FWCore/Transformer.h @@ -27,6 +27,8 @@ // #include "GaudiKernel/CommonMessaging.h" +#include +#include #include #include @@ -41,10 +43,11 @@ namespace k4FWCore { : Gaudi::Functional::details::DataHandleMixin, std::tuple<>, Traits_> { using Gaudi::Functional::details::DataHandleMixin, std::tuple<>, Traits_>::DataHandleMixin; - static_assert(((std::is_base_of_v || isMapToCollLike::value) && ...), - "Transformer and Producer input types must be EDM4hep collections or maps to collections"); - static_assert((std::is_base_of_v || isMapToCollLike::value), - "Transformer and Producer output types must be EDM4hep collections or maps to collections"); + static_assert( + ((std::is_base_of_v || isVectorLike_v)&&...), + "Transformer and Producer input types must be EDM4hep collections or vectors of collection pointers"); + static_assert((std::is_base_of_v || isVectorLike_v), + "Transformer and Producer output types must be EDM4hep collections or vectors of collections"); template using InputHandle_t = Gaudi::Functional::details::InputHandle_t>; @@ -54,7 +57,7 @@ namespace k4FWCore { std::tuple::type>>...> m_inputs; std::tuple::type>>> m_outputs; std::array>, sizeof...(In)> m_inputLocations{}; - std::array>, 1> m_outputLocations{}; + Gaudi::Property> m_outputLocations{}; using base_class = Gaudi::Functional::details::DataHandleMixin, std::tuple<>, Traits_>; @@ -85,10 +88,7 @@ namespace k4FWCore { this, std::get(outputs).first, to_DataObjID(std::get(outputs).second), [this](Gaudi::Details::PropertyBase&) { std::vector::type>> h; - // Is this needed? - // std::sort(this->m_outputLocations[J].value().begin(), this->m_outputLocations[J].value().end(), - // [](const DataObjID& a, const DataObjID& b) { return a.key() < b.key(); }); - for (auto& inpID : this->m_outputLocations[J].value()) { + for (auto& inpID : this->m_outputLocations.value()) { if (inpID.key().empty()) { continue; } @@ -111,9 +111,9 @@ namespace k4FWCore { // derived classes are NOT allowed to implement execute ... StatusCode execute(const EventContext& ctx) const override final { try { - if constexpr (isMapToCollLike::value) { + if constexpr (isVectorLike::value) { std::tuple tmp = filter_evtcontext_tt::apply(*this, ctx, this->m_inputs); - putMapOutputs<0, Out>(std::move(tmp), m_outputs, this); + putVectorOutputs<0, Out>(std::move(tmp), m_outputs, this); } else { Gaudi::Functional::details::put( std::get<0>(this->m_outputs)[0], @@ -126,6 +126,52 @@ namespace k4FWCore { } } + /** + * @brief Get the input locations for a given input index + * @param i The index of the input + * @return A range of the input locations + */ + auto inputLocations(int i) const { + if (i >= sizeof...(In)) { + throw std::out_of_range("Called inputLocations with an index out of range, index: " + std::to_string(i) + + ", number of inputs: " + std::to_string(sizeof...(In))); + } + return m_inputLocations[i] | std::views::transform([](const DataObjID& id) -> const auto& { return id.key(); }); + } + /** + * @brief Get the input locations for a given input name + * @param name The name of the input + * @return A range of the input locations + */ + const auto inputLocations(std::string_view name) const { + auto it = std::ranges::find_if(m_inputLocations, [&name](const auto& prop) { return prop.name() == name; }); + if (it == m_inputLocations.end()) { + throw std::runtime_error("Called inputLocations with an unknown name"); + } + return it->value() | std::views::transform([](const DataObjID& id) -> const auto& { return id.key(); }); + } + + /** + * @brief Get the output locations for a given output index + * @param i The index of the output + * @return A range of the output locations + */ + auto outputLocations() const { + return m_outputLocations | std::views::transform([](const DataObjID& id) -> const auto& { return id.key(); }); + } + /** + * @brief Get the output locations for a given output name + * @param name The name of the output + * @return A range of the output locations + */ + const auto outputLocations(std::string_view name) const { + if (name != m_outputLocations.name()) { + throw std::runtime_error("Called outputLocations with an unknown name"); + } + return m_outputLocations | std::views::transform([](const DataObjID& id) -> const auto& { return id.key(); }); + } + static constexpr std::size_t inputLocationsSize() { return sizeof...(In); } + // ... instead, they must implement the following operator virtual Out operator()(const In&...) const = 0; }; @@ -137,9 +183,9 @@ namespace k4FWCore { : Gaudi::Functional::details::DataHandleMixin, std::tuple<>, Traits_> { using Gaudi::Functional::details::DataHandleMixin, std::tuple<>, Traits_>::DataHandleMixin; - static_assert(((std::is_base_of_v || isMapToCollLike::value) && ...), + static_assert(((std::is_base_of_v || isVectorLike::value) && ...), "Transformer and Producer input types must be EDM4hep collections or maps to collections"); - static_assert(((std::is_base_of_v || isMapToCollLike::value) && ...), + static_assert(((std::is_base_of_v || isVectorLike::value) && ...), "Transformer and Producer output types must be EDM4hep collections or maps to collections"); template @@ -202,7 +248,7 @@ namespace k4FWCore { StatusCode execute(const EventContext& ctx) const override final { try { auto tmp = filter_evtcontext_tt::apply(*this, ctx, this->m_inputs); - putMapOutputs<0, Out...>(std::move(tmp), m_outputs, this); + putVectorOutputs<0, Out...>(std::move(tmp), m_outputs, this); return Gaudi::Functional::FilterDecision::PASSED; } catch (GaudiException& e) { (e.code() ? this->warning() : this->error()) << e.tag() << " : " << e.message() << endmsg; @@ -210,6 +256,59 @@ namespace k4FWCore { } } + /** + * @brief Get the input locations for a given input index + * @param i The index of the input + * @return A range of the input locations + */ + const auto inputLocations(int i) const { + if (i >= sizeof...(In)) { + throw std::out_of_range("Called inputLocations with an index out of range, index: " + std::to_string(i) + + ", number of inputs: " + std::to_string(sizeof...(In))); + } + return m_inputLocations[i] | std::views::transform([](const DataObjID& id) -> const auto& { return id.key(); }); + } + /** + * @brief Get the input locations for a given input name + * @param name The name of the input + * @return A range of the input locations + */ + const auto inputLocations(std::string_view name) const { + auto it = std::ranges::find_if(m_inputLocations, [&name](const auto& prop) { return prop.name() == name; }); + if (it == m_inputLocations.end()) { + throw std::runtime_error("Called inputLocations with an unknown name"); + } + return it->value() | std::views::transform([](const DataObjID& id) -> const auto& { return id.key(); }); + } + + /** + * @brief Get the output locations for a given output index + * @param i The index of the output + * @return A range of the output locations + */ + auto outputLocations(int i) const { + if (i >= sizeof...(Out)) { + throw std::out_of_range("Called outputLocations with an index out of range"); + } + return m_outputLocations[i] | + std::views::transform([](const DataObjID& id) -> const auto& { return id.key(); }); + } + /** + * @brief Get the output locations for a given output name + * @param name The name of the output + * @return A range of the output locations + */ + const auto outputLocations(std::string_view name) const { + auto it = std::ranges::find_if(m_outputLocations.begin(), m_outputLocations.end(), + [&name](const auto& prop) { return prop.name() == name; }); + if (it == m_outputLocations.end()) { + throw std::runtime_error("Called outputLocations with an unknown name"); + } + return it->value() | std::views::transform([](const DataObjID& id) -> const auto& { return id.key(); }); + } + static constexpr std::size_t inputLocationsSize() { return sizeof...(In); } + static constexpr std::size_t outputLocationsSize() { return sizeof...(Out); } + // ... instead, they must implement the following operator virtual std::tuple operator()(const In&...) const = 0; }; diff --git a/k4FWCore/src/PodioDataSvc.cpp b/k4FWCore/src/PodioDataSvc.cpp index d318d531..f80a0371 100644 --- a/k4FWCore/src/PodioDataSvc.cpp +++ b/k4FWCore/src/PodioDataSvc.cpp @@ -18,7 +18,6 @@ */ #include "k4FWCore/PodioDataSvc.h" #include -#include "GaudiKernel/IConversionSvc.h" #include "GaudiKernel/IEventProcessor.h" #include "GaudiKernel/IProperty.h" #include "GaudiKernel/ISvcLocator.h" @@ -26,8 +25,6 @@ #include "podio/CollectionBase.h" -#include "TTree.h" - /// Service initialisation StatusCode PodioDataSvc::initialize() { // Nothing to do: just call base class initialisation diff --git a/test/k4FWCoreTest/src/components/ExampleFunctionalConsumerRuntimeCollections.cpp b/test/k4FWCoreTest/src/components/ExampleFunctionalConsumerRuntimeCollections.cpp index 40e0124f..29bd297b 100644 --- a/test/k4FWCoreTest/src/components/ExampleFunctionalConsumerRuntimeCollections.cpp +++ b/test/k4FWCoreTest/src/components/ExampleFunctionalConsumerRuntimeCollections.cpp @@ -26,20 +26,32 @@ #include struct ExampleFunctionalConsumerRuntimeCollections final - : k4FWCore::Consumer& input)> { - // The pair in KeyValues can be changed from python and it corresponds - // to the name of the input collections + : k4FWCore::Consumer& input)> { + // The pair in KeyValue can be changed from python and it corresponds + // to the name of the output collection ExampleFunctionalConsumerRuntimeCollections(const std::string& name, ISvcLocator* svcLoc) : Consumer(name, svcLoc, KeyValues("InputCollection", {"DefaultValue"})) {} // This is the function that will be called to produce the data - void operator()(const std::map& input) const override { + void operator()(const std::vector& input) const override { if (input.size() != 3) { - throw std::runtime_error("Wrong size of the input map, expected 3, got " + std::to_string(input.size())); + throw std::runtime_error("Wrong size of the input vector, expected 3, got " + std::to_string(input.size())); } - for (auto& [key, val] : input) { + for (int i = 0; i < 3; i++) { + if (inputLocations(0)[i] != "MCParticles" + std::to_string(i)) { + throw std::runtime_error("Wrong name of the input collection, expected MCParticles" + std::to_string(i) + + ", got " + std::string(inputLocations(0)[i])); + } + // Add another redundant check to show that the inputLocations() function + // can be called with a name instead of an index + if (inputLocations("InputCollection")[i] != "MCParticles" + std::to_string(i)) { + throw std::runtime_error("Wrong name of the input collection, expected MCParticles" + std::to_string(i) + + ", got " + std::string(inputLocations(0)[i])); + } + } + for (auto& coll : input) { int i = 0; - for (const auto& particle : val) { + for (const auto&& particle : *coll) { if ((particle.getPDG() != 1 + i + m_offset) || (particle.getGeneratorStatus() != 2 + i + m_offset) || (particle.getSimulatorStatus() != 3 + i + m_offset) || (particle.getCharge() != 4 + i + m_offset) || (particle.getTime() != 5 + i + m_offset) || (particle.getMass() != 6 + i + m_offset)) { diff --git a/test/k4FWCoreTest/src/components/ExampleFunctionalConsumerRuntimeCollectionsMultiple.cpp b/test/k4FWCoreTest/src/components/ExampleFunctionalConsumerRuntimeCollectionsMultiple.cpp index 380cf6ae..b8e77e6d 100644 --- a/test/k4FWCoreTest/src/components/ExampleFunctionalConsumerRuntimeCollectionsMultiple.cpp +++ b/test/k4FWCoreTest/src/components/ExampleFunctionalConsumerRuntimeCollectionsMultiple.cpp @@ -28,9 +28,9 @@ #include struct ExampleFunctionalConsumerRuntimeCollectionsMultiple final - : k4FWCore::Consumer& particleMap, - const std::map& trackMap, - const edm4hep::SimTrackerHitCollection& simTrackerHits)> { + : k4FWCore::Consumer& particleVec, + const std::vector& trackVec, + const edm4hep::SimTrackerHitCollection& simTrackerHits)> { // The pair in KeyValues can be changed from python and it corresponds // to the name of the input collections ExampleFunctionalConsumerRuntimeCollectionsMultiple(const std::string& name, ISvcLocator* svcLoc) @@ -39,18 +39,18 @@ struct ExampleFunctionalConsumerRuntimeCollectionsMultiple final KeyValues("SimTrackerHits", {"MCParticles"})}) {} // This is the function that will be called to produce the data - void operator()(const std::map& particleMap, - const std::map& trackMap, - const edm4hep::SimTrackerHitCollection& simTrackerHits) const override { - info() << "Received " << particleMap.size() << " particle collections and " << trackMap.size() + void operator()(const std::vector& particleVec, + const std::vector& trackVec, + const edm4hep::SimTrackerHitCollection& simTrackerHits) const override { + info() << "Received " << particleVec.size() << " particle collections and " << trackVec.size() << " track collections" << endmsg; - if (particleMap.size() != 5) { - throw std::runtime_error("Wrong size of the particleMap map, expected 5, got " + - std::to_string(particleMap.size())); + if (particleVec.size() != 5) { + throw std::runtime_error("Wrong size of the particle vector, expected 5, got " + + std::to_string(particleVec.size())); } - for (auto& [key, particles] : particleMap) { + for (auto& particles : particleVec) { int i = 0; - for (const auto& particle : particles) { + for (const auto& particle : *particles) { if ((particle.getPDG() != 1 + i + m_offset) || (particle.getGeneratorStatus() != 2 + i + m_offset) || (particle.getSimulatorStatus() != 3 + i + m_offset) || (particle.getCharge() != 4 + i + m_offset) || (particle.getTime() != 5 + i + m_offset) || (particle.getMass() != 6 + i + m_offset)) { @@ -65,14 +65,15 @@ struct ExampleFunctionalConsumerRuntimeCollectionsMultiple final i++; } } - if (trackMap.size() != 3) { - fatal() << "Wrong size of the tracks map, expected 3, got " << trackMap.size() << endmsg; + if (trackVec.size() != 3) { + fatal() << "Wrong size of the tracks vector, expected 3, got " << trackVec.size() << endmsg; } - for (auto& [key, tracks] : trackMap) { - if ((tracks[0].getType() != 1) || (std::abs(tracks[0].getChi2() - 2.1) > 1e-6) || (tracks[0].getNdf() != 3)) { + for (const auto& tracks : trackVec) { + if (((*tracks)[0].getType() != 1) || (std::abs((*tracks)[0].getChi2() - 2.1) > 1e-6) || + ((*tracks)[0].getNdf() != 3)) { std::stringstream error; - error << "Wrong data in tracks collection, expected 1, 2.1, 3, 4.1, 5.1, 6.1 got " << tracks[0].getType() - << ", " << tracks[0].getChi2() << ", " << tracks[0].getNdf(); + error << "Wrong data in tracks collection, expected 1, 2.1, 3, 4.1, 5.1, 6.1 got " << (*tracks)[0].getType() + << ", " << (*tracks)[0].getChi2() << ", " << (*tracks)[0].getNdf(); throw std::runtime_error(error.str()); } } diff --git a/test/k4FWCoreTest/src/components/ExampleFunctionalProducerRuntimeCollections.cpp b/test/k4FWCoreTest/src/components/ExampleFunctionalProducerRuntimeCollections.cpp index ef28e85a..f9874592 100644 --- a/test/k4FWCoreTest/src/components/ExampleFunctionalProducerRuntimeCollections.cpp +++ b/test/k4FWCoreTest/src/components/ExampleFunctionalProducerRuntimeCollections.cpp @@ -17,8 +17,6 @@ * limitations under the License. */ -#include "Gaudi/Property.h" - #include "edm4hep/MCParticleCollection.h" #include "k4FWCore/Producer.h" @@ -26,30 +24,33 @@ #include struct ExampleFunctionalProducerRuntimeCollections final - : k4FWCore::Producer()> { - // The pair in KeyValues can be changed from python and it corresponds + : k4FWCore::Producer()> { + // The pair in KeyValue can be changed from python and it corresponds // to the name of the output collection ExampleFunctionalProducerRuntimeCollections(const std::string& name, ISvcLocator* svcLoc) : Producer(name, svcLoc, {}, {KeyValues("OutputCollections", {"MCParticles"})}) {} // This is the function that will be called to produce the data - std::map operator()() const override { - std::map outputCollections; - for (int i = 0; i < m_numberOfCollections; ++i) { - std::string name = "MCParticles" + std::to_string(i); - auto coll = edm4hep::MCParticleCollection(); + std::vector operator()() const override { + const auto locs = outputLocations(); + std::vector outputCollections; + for (size_t i = 0; i < locs.size(); ++i) { + if (locs[i] != "MCParticles" + std::to_string(i)) { + throw GaudiException("Output collection name does not match the expected one", name(), StatusCode::FAILURE); + } + // Add another redundant check to show that the outputLocations() function + // can be called with a name instead of an index + if (outputLocations("OutputCollections")[i] != "MCParticles" + std::to_string(i)) { + throw GaudiException("Output collection name does not match the expected one", name(), StatusCode::FAILURE); + } + info() << "Creating collection " << i << endmsg; + auto coll = edm4hep::MCParticleCollection(); coll->create(1, 2, 3, 4.f, 5.f, 6.f); coll->create(2, 3, 4, 5.f, 6.f, 7.f); - outputCollections[name] = std::move(coll); + outputCollections.emplace_back(std::move(coll)); } return outputCollections; } - -private: - // We can define any property we want that can be set from python - // and use it inside operator() - Gaudi::Property m_numberOfCollections{this, "NumberOfCollections", 3, - "Example int that can be used in the algorithm"}; }; DECLARE_COMPONENT(ExampleFunctionalProducerRuntimeCollections) diff --git a/test/k4FWCoreTest/src/components/ExampleFunctionalTransformerRuntimeCollections.cpp b/test/k4FWCoreTest/src/components/ExampleFunctionalTransformerRuntimeCollections.cpp index 278125f0..8c26a866 100644 --- a/test/k4FWCoreTest/src/components/ExampleFunctionalTransformerRuntimeCollections.cpp +++ b/test/k4FWCoreTest/src/components/ExampleFunctionalTransformerRuntimeCollections.cpp @@ -32,26 +32,26 @@ * (in this example the same number of input collections but it can be different) */ -using mapType = std::map; - struct ExampleFunctionalTransformerRuntimeCollections final - : k4FWCore::Transformer& input)> { - // The pair in KeyValues can be changed from python and it corresponds + : k4FWCore::Transformer( + const std::vector& input)> { + // The pair in KeyValue can be changed from python and it corresponds // to the name of the output collection ExampleFunctionalTransformerRuntimeCollections(const std::string& name, ISvcLocator* svcLoc) : Transformer(name, svcLoc, {KeyValues("InputCollections", {"MCParticles"})}, {KeyValues("OutputCollections", {"MCParticles"})}) {} // This is the function that will be called to produce the data - mapType operator()(const std::map& input) const override { - std::map outputCollections; + std::vector operator()( + const std::vector& input) const override { + std::vector outputCollections; for (int i = 0; i < input.size(); ++i) { std::string name = "NewMCParticles" + std::to_string(i); - auto& old_coll = input.at("MCParticles" + std::to_string(i)); + auto& old_coll = input.at(i); auto coll = edm4hep::MCParticleCollection(); - coll->push_back(old_coll.at(0).clone()); - coll->push_back(old_coll.at(1).clone()); - outputCollections[name] = std::move(coll); + coll->push_back(old_coll->at(0).clone()); + coll->push_back(old_coll->at(1).clone()); + outputCollections.emplace_back(std::move(coll)); } return outputCollections; } diff --git a/test/k4FWCoreTest/src/components/ExampleFunctionalTransformerRuntimeCollectionsMultiple.cpp b/test/k4FWCoreTest/src/components/ExampleFunctionalTransformerRuntimeCollectionsMultiple.cpp index 11e955ed..480fe55c 100644 --- a/test/k4FWCoreTest/src/components/ExampleFunctionalTransformerRuntimeCollectionsMultiple.cpp +++ b/test/k4FWCoreTest/src/components/ExampleFunctionalTransformerRuntimeCollectionsMultiple.cpp @@ -31,16 +31,15 @@ #include // Which type of collection we are reading -using FloatColl = std::map&>; -using ParticleColl = std::map; -using SimTrackerHitColl = std::map; -using TrackerHitColl = std::map; -using TrackColl = std::map; +using FloatColl = std::vector*>; +using ParticleColl = std::vector; +using SimTrackerHitColl = std::vector; +using TrackerHitColl = std::vector; +using TrackColl = std::vector; -using retType = std::tuple< - std::map>, std::map, - std::map, std::map, - std::map, std::map>; +using retType = std::tuple>, std::vector, + std::vector, std::vector, + std::vector, std::vector>; struct ExampleFunctionalTransformerRuntimeCollectionsMultiple final : k4FWCore::MultiTransformer>(); - auto particleMapOut = std::map(); - auto particle2MapOut = std::map(); - auto simTrackerHitMapOut = std::map(); - auto trackerHitMapOut = std::map(); - auto trackMapOut = std::map(); - - if (floatMap.size() != 3) { - throw std::runtime_error("Wrong size of the floatVector collection map, expected 3, got " + - std::to_string(floatMap.size()) + ""); + retType operator()(const FloatColl& floatVec, const ParticleColl& particlesVec, + const SimTrackerHitColl& simTrackerHitVec, const TrackerHitColl& trackerHitVec, + const TrackColl& trackVec) const override { + auto floatVecOut = std::vector>(); + auto particleVecOut = std::vector(); + auto particle2VecOut = std::vector(); + auto simTrackerHitVecOut = std::vector(); + auto trackerHitVecOut = std::vector(); + auto trackVecOut = std::vector(); + + if (floatVec.size() != 3) { + throw std::runtime_error("Wrong size of the float vector, expected 3, got " + std::to_string(floatVec.size()) + + ""); } - for (const auto& [key, floatVector] : floatMap) { - if (floatVector.size() != 3) { + for (const auto& floatVector : floatVec) { + if (floatVector->size() != 3) { throw std::runtime_error("Wrong size of floatVector collection, expected 3, got " + - std::to_string(floatVector.size()) + ""); + std::to_string(floatVector->size()) + ""); } - if ((floatVector.vec()[0] != 125) || (floatVector.vec()[1] != 25) || (floatVector.vec()[2] != 0)) { + if ((floatVector->vec()[0] != 125) || (floatVector->vec()[1] != 25) || (floatVector->vec()[2] != 0)) { std::stringstream error; - error << "Wrong data in floatVector collection, expected 125, 25, " << 0 << " got " << floatVector.vec()[0] - << ", " << floatVector.vec()[1] << ", " << floatVector.vec()[2]; + error << "Wrong data in floatVector collection, expected 125, 25, " << 0 << " got " << floatVector->vec()[0] + << ", " << floatVector->vec()[1] << ", " << floatVector->vec()[2] << ""; throw std::runtime_error(error.str()); } auto coll = podio::UserDataCollection(); - coll.push_back(floatVector.vec()[0]); - coll.push_back(floatVector.vec()[1]); - coll.push_back(floatVector.vec()[2]); - floatMapOut["New" + key] = std::move(coll); + coll.push_back(floatVector->vec()[0]); + coll.push_back(floatVector->vec()[1]); + coll.push_back(floatVector->vec()[2]); + floatVecOut.emplace_back(std::move(coll)); } - if (particlesMap.size() != 3) { - throw std::runtime_error("Wrong size of the particleMap map, expected 3, got " + - std::to_string(particleMapOut.size()) + ""); + if (particlesVec.size() != 3) { + throw std::runtime_error("Wrong size of the particle vector, expected 3, got " + + std::to_string(particleVecOut.size()) + ""); } - for (auto& [key, particles] : particlesMap) { + for (auto& particles : particlesVec) { auto coll = edm4hep::MCParticleCollection(); int i = 0; - for (const auto& particle : particles) { + for (const auto& particle : *particles) { if ((particle.getPDG() != 1 + i + m_offset) || (particle.getGeneratorStatus() != 2 + i + m_offset) || (particle.getSimulatorStatus() != 3 + i + m_offset) || (particle.getCharge() != 4 + i + m_offset) || (particle.getTime() != 5 + i + m_offset) || (particle.getMass() != 6 + i + m_offset)) { @@ -122,67 +121,67 @@ struct ExampleFunctionalTransformerRuntimeCollectionsMultiple final coll.push_back(particle.clone()); } i++; - particleMapOut["New" + key] = std::move(coll); } + particleVecOut.emplace_back(std::move(coll)); } - if (simTrackerHitMap.size() != 3) { - throw std::runtime_error("Wrong size of the simTrackerHitMap map, expected 3, got " + - std::to_string(simTrackerHitMapOut.size()) + ""); + if (simTrackerHitVec.size() != 3) { + throw std::runtime_error("Wrong size of the simTrackerHit vector, expected 3, got " + + std::to_string(simTrackerHitVecOut.size()) + ""); } - for (auto& [key, simTrackerHits] : simTrackerHitMap) { + for (auto& simTrackerHits : simTrackerHitVec) { auto coll = edm4hep::SimTrackerHitCollection(); - if ((simTrackerHits.at(0).getPosition()[0] != 3) || (simTrackerHits.at(0).getPosition()[1] != 4) || - (simTrackerHits.at(0).getPosition()[2] != 5)) { + if ((simTrackerHits->at(0).getPosition()[0] != 3) || (simTrackerHits->at(0).getPosition()[1] != 4) || + (simTrackerHits->at(0).getPosition()[2] != 5)) { std::stringstream error; error << "Wrong data in simTrackerHits collection, expected 3, 4, 5 got " - << simTrackerHits.at(0).getPosition()[0] << ", " << simTrackerHits.at(0).getPosition()[1] << ", " - << simTrackerHits.at(0).getPosition()[2]; + << simTrackerHits->at(0).getPosition()[0] << ", " << simTrackerHits->at(0).getPosition()[1] << ", " + << simTrackerHits->at(0).getPosition()[2]; throw std::runtime_error(error.str()); } - coll.push_back(simTrackerHits.at(0).clone()); - simTrackerHitMapOut["New" + key] = std::move(coll); + coll.push_back(simTrackerHits->at(0).clone()); + simTrackerHitVecOut.emplace_back(std::move(coll)); } - if (trackerHitMap.size() != 3) { - throw std::runtime_error("Wrong size of the trackerHitMap map, expected 3, got " + - std::to_string(trackerHitMapOut.size()) + ""); + if (trackerHitVec.size() != 3) { + throw std::runtime_error("Wrong size of the trackerHit vector, expected 3, got " + + std::to_string(trackerHitVecOut.size()) + ""); } - for (auto& [key, trackerHits] : trackerHitMap) { + for (auto& trackerHits : trackerHitVec) { auto coll = edm4hep::TrackerHit3DCollection(); - if ((trackerHits.at(0).getPosition()[0] != 3) || (trackerHits.at(0).getPosition()[1] != 4) || - (trackerHits.at(0).getPosition()[2] != 5)) { + if ((trackerHits->at(0).getPosition()[0] != 3) || (trackerHits->at(0).getPosition()[1] != 4) || + (trackerHits->at(0).getPosition()[2] != 5)) { std::stringstream error; - error << "Wrong data in trackerHits collection, expected 3, 4, 5 got " << trackerHits.at(0).getPosition()[0] - << ", " << trackerHits.at(0).getPosition()[1] << ", " << trackerHits.at(0).getPosition()[2]; + error << "Wrong data in trackerHits collection, expected 3, 4, 5 got " << trackerHits->at(0).getPosition()[0] + << ", " << trackerHits->at(0).getPosition()[1] << ", " << trackerHits->at(0).getPosition()[2] << ""; throw std::runtime_error(error.str()); } - coll.push_back(trackerHits.at(0).clone()); - trackerHitMapOut["New" + key] = std::move(coll); + coll.push_back(trackerHits->at(0).clone()); + trackerHitVecOut.emplace_back(std::move(coll)); } - if (trackMap.size() != 3) { - throw std::runtime_error("Wrong size of the trackMap map, expected 3, got " + std::to_string(trackMapOut.size()) + + if (trackVec.size() != 3) { + throw std::runtime_error("Wrong size of the track vector, expected 3, got " + std::to_string(trackVecOut.size()) + ""); } - for (auto& [key, tracks] : trackMap) { + for (auto& tracks : trackVec) { auto coll = edm4hep::TrackCollection(); - if ((tracks.at(0).getType() != 1) || (std::abs(tracks.at(0).getChi2() - 2.1) > 1e-6) || - (tracks.at(0).getNdf() != 3)) { + if ((tracks->at(0).getType() != 1) || (std::abs(tracks->at(0).getChi2() - 2.1) > 1e-6) || + (tracks->at(0).getNdf() != 3)) { std::stringstream error; - error << "Wrong data in tracks collection, expected 1, 2.1, 3, 4.1, 5.1, 6.1 got " << tracks.at(0).getType() - << ", " << tracks.at(0).getChi2() << ", " << tracks.at(0).getNdf(); + error << "Wrong data in tracks collection, expected 1, 2.1, 3, 4.1, 5.1, 6.1 got " << tracks->at(0).getType() + << ", " << tracks->at(0).getChi2() << ", " << tracks->at(0).getNdf(); throw std::runtime_error(error.str()); } - coll->push_back(tracks.at(0).clone()); - trackMapOut["New" + key] = std::move(coll); + coll->push_back(tracks->at(0).clone()); + trackVecOut.emplace_back(std::move(coll)); } - return std::make_tuple(std::move(floatMapOut), std::move(particleMapOut), std::move(particle2MapOut), - std::move(simTrackerHitMapOut), std::move(trackerHitMapOut), std::move(trackMapOut)); + return std::make_tuple(std::move(floatVecOut), std::move(particleVecOut), std::move(particle2VecOut), + std::move(simTrackerHitVecOut), std::move(trackerHitVecOut), std::move(trackVecOut)); } private: diff --git a/test/k4FWCoreTest/src/components/ExampleFunctionalTransformerRuntimeEmpty.cpp b/test/k4FWCoreTest/src/components/ExampleFunctionalTransformerRuntimeEmpty.cpp index ed678a5a..dae730f2 100644 --- a/test/k4FWCoreTest/src/components/ExampleFunctionalTransformerRuntimeEmpty.cpp +++ b/test/k4FWCoreTest/src/components/ExampleFunctionalTransformerRuntimeEmpty.cpp @@ -29,13 +29,12 @@ * * This is an example of a functional transformer that takes an arbitrary number * of input collections and produces an arbitrary number of output collections - * but it's returning an empty map (no output collections) + * but it's returning an empty vector (no output collections) */ -using mapType = std::map; - struct ExampleFunctionalTransformerRuntimeEmpty final - : k4FWCore::Transformer(const mapType& input)> { + : k4FWCore::Transformer( + const std::vector& input)> { // The pair in KeyValues can be changed from python and it corresponds // to the name of the output collections ExampleFunctionalTransformerRuntimeEmpty(const std::string& name, ISvcLocator* svcLoc) @@ -43,9 +42,10 @@ struct ExampleFunctionalTransformerRuntimeEmpty final {KeyValues("OutputCollections", {"MCParticles"})}) {} // This is the function that will be called to produce the data - std::map operator()(const mapType& input) const override { - // We just return an empty map to make sure it works fine - std::map outputCollections; + std::vector operator()( + const std::vector& input) const override { + // Return an empty vector to make sure that also works + std::vector outputCollections; return outputCollections; }