diff --git a/.gitignore b/.gitignore index 656d0058..e485c349 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,3 @@ -### File copied from https://github.com/key4hep/key4hep-dev-utils -### DO NOT EDIT, CHANGES WILL BE OVERWRITTEN - ### C++ ### # Prerequisites *.d @@ -241,7 +238,7 @@ podio_generated_files.cmake /python/edm4hep/__version__.py edm4hep/edm4hep/ edm4hep/src/ -## k4FWCore +# k4FWCore test/k4FWCoreTest/**/*.root ## k4MarlinWrapper test/inputFiles/*.slcio diff --git a/README.md b/README.md index b8a1cf64..c00401dd 100644 --- a/README.md +++ b/README.md @@ -61,12 +61,35 @@ print(my_opts[0].foo) k4FWCore is a CMake project. After setting up the dependencies (use for example `source /cvmfs/sw.hsf.org/key4hep/setup.sh`) - -``` -mkdir build install -cd build; +``` bash +mkdir build +cd build cmake .. make install ``` - +## Implementing algorithms +k4FWCore uses `Gaudi::Functional` for executing algorithms. There are several +types of algorithms, depending on your use case: +- The `Consumer` takes inputs but no outputs; can be used for reading +- The `Producer` takes outputs but no inputs; can be used for generating + collections or events +- The `Transformer` is the more general one (both the `Consumer` and the + `Producer` are a particular case of this one) and takes both inputs and + outputs + +A more complete list of algorithms can be found in +https://lhcb.github.io/DevelopKit/03a-gaudi/, in the `Gaudi::Functional` +section. + +In all cases the implementation process is the same: we'll create a new class +that will inherit from one of the previous algorithms. Then, we implement +`operator()`, where our algorithm will be. This `operator()` will return either +a single type (including `void`) or a tuple with multiple types. It will take +one parameter per input. Simple examples can be found in the test folder for +each one of the above-mentioned algorithms. In addition, there are tests that +have either multiple inputs and / or multiple outputs (like +`ExampleFunctionalProducerMultiple`) that can be used as a template for the more +typical case when working with multiple inputs or outputs. + +**`GaudiAlg` is deprecated and will be removed in future versions of Gaudi.** diff --git a/k4FWCore/components/PodioInput.cpp b/k4FWCore/components/PodioInput.cpp index b58bc0ec..9bd0e8ff 100644 --- a/k4FWCore/components/PodioInput.cpp +++ b/k4FWCore/components/PodioInput.cpp @@ -17,49 +17,181 @@ * limitations under the License. */ #include "PodioInput.h" +#include "GaudiAlg/Consumer.h" -#include "TFile.h" -#include "TROOT.h" - -#include "k4FWCore/DataWrapper.h" #include "k4FWCore/PodioDataSvc.h" +#include "edm4hep/CaloHitContributionCollection.h" +#include "edm4hep/CalorimeterHitCollection.h" +#include "edm4hep/ClusterCollection.h" +#include "edm4hep/MCParticleCollection.h" +#include "edm4hep/MCRecoCaloAssociationCollection.h" +#include "edm4hep/MCRecoClusterParticleAssociationCollection.h" +#include "edm4hep/MCRecoParticleAssociationCollection.h" +#include "edm4hep/MCRecoTrackParticleAssociationCollection.h" +#include "edm4hep/MCRecoTrackerAssociationCollection.h" +#include "edm4hep/MCRecoTrackerHitPlaneAssociationCollection.h" +#include "edm4hep/ParticleIDCollection.h" +#include "edm4hep/RawCalorimeterHitCollection.h" +#include "edm4hep/RawTimeSeriesCollection.h" +#include "edm4hep/RecDqdxCollection.h" +#include "edm4hep/RecIonizationClusterCollection.h" +#include "edm4hep/RecoParticleVertexAssociationCollection.h" +#include "edm4hep/ReconstructedParticleCollection.h" +#include "edm4hep/SimCalorimeterHitCollection.h" +#include "edm4hep/SimPrimaryIonizationClusterCollection.h" +#include "edm4hep/SimTrackerHitCollection.h" +#include "edm4hep/TimeSeriesCollection.h" +#include "edm4hep/TrackCollection.h" +#include "edm4hep/TrackerHitCollection.h" +#include "edm4hep/TrackerHitPlaneCollection.h" +#include "edm4hep/TrackerPulseCollection.h" +#include "edm4hep/VertexCollection.h" + +#include "podio/UserDataCollection.h" + DECLARE_COMPONENT(PodioInput) -PodioInput::PodioInput(const std::string& name, ISvcLocator* svcLoc) : GaudiAlgorithm(name, svcLoc) {} +template inline void PodioInput::maybeRead(std::string_view collName) const { + if (m_podioDataSvc->readCollection(std::string(collName)).isFailure()) { + error() << "Failed to register collection " << collName << endmsg; + } +} -StatusCode PodioInput::initialize() { - if (GaudiAlgorithm::initialize().isFailure()) - return StatusCode::FAILURE; +void PodioInput::fillReaders() { + m_readers["edm4hep::MCParticleCollection"] = [&](std::string_view collName) { + maybeRead(collName); + }; + m_readers["edm4hep::SimTrackerHitCollection"] = [&](std::string_view collName) { + maybeRead(collName); + }; + m_readers["edm4hep::CaloHitContributionCollection"] = [&](std::string_view collName) { + maybeRead(collName); + }; + m_readers["edm4hep::SimCalorimeterHitCollection"] = [&](std::string_view collName) { + maybeRead(collName); + }; + m_readers["edm4hep::RawCalorimeterHitCollection"] = [&](std::string_view collName) { + maybeRead(collName); + }; + m_readers["edm4hep::CalorimeterHitCollection"] = [&](std::string_view collName) { + maybeRead(collName); + }; + m_readers["edm4hep::ParticleIDCollection"] = [&](std::string_view collName) { + maybeRead(collName); + }; + m_readers["edm4hep::ClusterCollection"] = [&](std::string_view collName) { + maybeRead(collName); + }; + m_readers["edm4hep::TrackerHitCollection"] = [&](std::string_view collName) { + maybeRead(collName); + }; + m_readers["edm4hep::TrackerHitPlaneCollection"] = [&](std::string_view collName) { + maybeRead(collName); + }; + m_readers["edm4hep::RawTimeSeriesCollection"] = [&](std::string_view collName) { + maybeRead(collName); + }; + m_readers["edm4hep::TrackCollection"] = [&](std::string_view collName) { + maybeRead(collName); + }; + m_readers["edm4hep::VertexCollection"] = [&](std::string_view collName) { + maybeRead(collName); + }; + m_readers["edm4hep::ReconstructedParticleCollection"] = [&](std::string_view collName) { + maybeRead(collName); + }; + m_readers["edm4hep::MCRecoParticleAssociationCollection"] = [&](std::string_view collName) { + maybeRead(collName); + }; + m_readers["edm4hep::MCRecoCaloAssociationCollection"] = [&](std::string_view collName) { + maybeRead(collName); + }; + m_readers["edm4hep::MCRecoTrackerAssociationCollection"] = [&](std::string_view collName) { + maybeRead(collName); + }; + m_readers["edm4hep::MCRecoTrackerHitPlaneAssociationCollection"] = [&](std::string_view collName) { + maybeRead(collName); + }; + m_readers["edm4hep::MCRecoClusterParticleAssociationCollection"] = [&](std::string_view collName) { + maybeRead(collName); + }; + m_readers["edm4hep::MCRecoTrackParticleAssociationCollection"] = [&](std::string_view collName) { + maybeRead(collName); + }; + m_readers["edm4hep::RecoParticleVertexAssociationCollection"] = [&](std::string_view collName) { + maybeRead(collName); + }; + m_readers["edm4hep::SimPrimaryIonizationClusterCollection"] = [&](std::string_view collName) { + maybeRead(collName); + }; + m_readers["edm4hep::TrackerPulseCollection"] = [&](std::string_view collName) { + maybeRead(collName); + }; + m_readers["edm4hep::RecIonizationClusterCollection"] = [&](std::string_view collName) { + maybeRead(collName); + }; + m_readers["edm4hep::TimeSeriesCollection"] = [&](std::string_view collName) { + maybeRead(collName); + }; + m_readers["edm4hep::RecDqdxCollection"] = [&](std::string_view collName) { + maybeRead(collName); + }; + m_readers["podio::UserDataCollection"] = [&](std::string_view collName) { + maybeRead>(collName); + }; + m_readers["podio::UserDataCollection"] = [&](std::string_view collName) { + maybeRead>(collName); + }; + m_readers["podio::UserDataCollection"] = [&](std::string_view collName) { + maybeRead>(collName); + }; + m_readers["podio::UserDataCollection"] = [&](std::string_view collName) { + maybeRead>(collName); + }; + m_readers["podio::UserDataCollection"] = [&](std::string_view collName) { + maybeRead>(collName); + }; + m_readers["podio::UserDataCollection"] = [&](std::string_view collName) { + maybeRead>(collName); + }; + m_readers["podio::UserDataCollection"] = [&](std::string_view collName) { + maybeRead>(collName); + }; + m_readers["podio::UserDataCollection"] = [&](std::string_view collName) { + maybeRead>(collName); + }; + m_readers["podio::UserDataCollection"] = [&](std::string_view collName) { + maybeRead>(collName); + }; + m_readers["podio::UserDataCollection"] = [&](std::string_view collName) { + maybeRead>(collName); + }; + m_readers["podio::UserDataCollection"] = [&](std::string_view collName) { + maybeRead>(collName); + }; +} +PodioInput::PodioInput(const std::string& name, ISvcLocator* svcLoc) : Consumer(name, svcLoc) { // check whether we have the PodioEvtSvc active m_podioDataSvc = dynamic_cast(evtSvc().get()); - if (nullptr == m_podioDataSvc) - return StatusCode::FAILURE; - - // TODO: add an upfront check for existence of data products - - return StatusCode::SUCCESS; + if (!m_podioDataSvc) { + error() << "Could not get PodioDataSvc" << endmsg; + } + fillReaders(); } -StatusCode PodioInput::execute() { - size_t cntr = 0; - // Re-create the collections from ROOT file - +void PodioInput::operator()() const { for (auto& collName : m_collectionNames) { debug() << "Registering collection to read " << collName << endmsg; - if (m_podioDataSvc->readCollection(collName).isFailure()) { - return StatusCode::FAILURE; + auto type = m_podioDataSvc->getCollectionType(collName); + if (m_readers.find(type) != m_readers.end()) { + m_readers[type](collName); + } else { + maybeRead(collName); } } // Tell data service that we are done with requested collections m_podioDataSvc->endOfRead(); - return StatusCode::SUCCESS; -} - -StatusCode PodioInput::finalize() { - if (GaudiAlgorithm::finalize().isFailure()) - return StatusCode::FAILURE; - return StatusCode::SUCCESS; } diff --git a/k4FWCore/components/PodioInput.h b/k4FWCore/components/PodioInput.h index 4f61caa8..3a41afc6 100644 --- a/k4FWCore/components/PodioInput.h +++ b/k4FWCore/components/PodioInput.h @@ -18,15 +18,14 @@ */ #ifndef FWCORE_PODIOINPUT_H #define FWCORE_PODIOINPUT_H -// Gaaudi -#include "GaudiAlg/GaudiAlgorithm.h" +// Gaudi +#include "Gaudi/Property.h" +#include "GaudiAlg/Consumer.h" // STL #include #include -// forward declarations -// from k4FWCore: class PodioDataSvc; /** @class PodioInput @@ -36,22 +35,21 @@ class PodioDataSvc; * @author J. Lingemann */ -class PodioInput : public GaudiAlgorithm { +using BaseClass_t = Gaudi::Functional::Traits::BaseClass_t; + +class PodioInput final : public Gaudi::Functional::Consumer { public: - /// Constructor. PodioInput(const std::string& name, ISvcLocator* svcLoc); - /// Initialization of PodioInput. Acquires the data service, opens root file and creates trees. - virtual StatusCode initialize(); - /// Execute. Re-creates collections that are specified to be read and sets references. - virtual StatusCode execute(); - /// Finalize. Closes ROOT file. - virtual StatusCode finalize(); + void operator()() const override; private: - /// Name of collections to read. Set by option collections (this is temporary) + template void maybeRead(std::string_view collName) const; + void fillReaders(); + // Name of collections to read. Set by option collections (this is temporary) Gaudi::Property> m_collectionNames{this, "collections", {}, "Places of collections to read"}; - /// Data service: needed to register objects and get collection IDs. Just an observing pointer. - PodioDataSvc* m_podioDataSvc; + // Data service: needed to register objects and get collection IDs. Just an observing pointer. + PodioDataSvc* m_podioDataSvc; + mutable std::map> m_readers; }; #endif diff --git a/k4FWCore/include/k4FWCore/BaseClass.h b/k4FWCore/include/k4FWCore/BaseClass.h new file mode 100644 index 00000000..315106f2 --- /dev/null +++ b/k4FWCore/include/k4FWCore/BaseClass.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2014-2023 Key4hep-Project. + * + * This file is part of Key4hep. + * See https://key4hep.github.io/key4hep-doc/ for further info. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef K4FWCORE_FUNCTIONALUTILS_H +#define K4FWCORE_FUNCTIONALUTILS_H + +#include "GaudiAlg/GaudiAlgorithm.h" +#include "GaudiKernel/DataObjectHandle.h" +#include "k4FWCore/DataWrapper.h" + +// Base class used for the Traits template argument of the +// Gaudi::Functional algorithms +struct BaseClass_t { + template using InputHandle = DataObjectReadHandle>; + template using OutputHandle = DataObjectWriteHandle>; + + using BaseClass = Gaudi::Algorithm; +}; + +#endif diff --git a/k4FWCore/include/k4FWCore/DataWrapper.h b/k4FWCore/include/k4FWCore/DataWrapper.h index b172c058..3167bb64 100644 --- a/k4FWCore/include/k4FWCore/DataWrapper.h +++ b/k4FWCore/include/k4FWCore/DataWrapper.h @@ -43,22 +43,33 @@ template class GAUDI_API DataWrapper : public DataWrapperBase { public: DataWrapper() : m_data(nullptr){}; + DataWrapper(T&& coll) { + m_data = new T(std::move(coll)); + is_owner = true; + } + DataWrapper(std::unique_ptr uptr) : m_data(uptr.get()) { + uptr.release(); + is_owner = false; + }; virtual ~DataWrapper(); - const T* getData() { return m_data; } + const T* getData() const { return m_data; } void setData(const T* data) { m_data = data; } virtual void resetData() { m_data = nullptr; } + operator const T&() const& { return *m_data; } + private: /// try to cast to collectionBase; may return nullptr; virtual podio::CollectionBase* collectionBase(); private: const T* m_data; + bool is_owner{true}; }; template DataWrapper::~DataWrapper() { - if (m_data != nullptr) + if (is_owner && !m_data) delete m_data; } diff --git a/k4FWCore/include/k4FWCore/PodioDataSvc.h b/k4FWCore/include/k4FWCore/PodioDataSvc.h index 42d9a06e..5343a034 100644 --- a/k4FWCore/include/k4FWCore/PodioDataSvc.h +++ b/k4FWCore/include/k4FWCore/PodioDataSvc.h @@ -28,6 +28,7 @@ #include "podio/Frame.h" #include "podio/ROOTFrameReader.h" // Forward declarations +#include "k4FWCore/DataWrapper.h" class DataWrapperBase; class PodioOutput; template class MetaDataHandle; @@ -65,7 +66,19 @@ class PodioDataSvc : public DataSvc { virtual StatusCode registerObject(std::string_view parentPath, std::string_view fullPath, DataObject* pObject) override final; - StatusCode readCollection(const std::string& collectionName); + const std::string_view getCollectionType(const std::string& collName); + + template StatusCode readCollection(const std::string& collName) { + const T* collection(nullptr); + collection = static_cast(m_eventframe.get(collName)); + if (collection == nullptr) { + error() << "Collection " << collName << " does not exist." << endmsg; + } + auto wrapper = new DataWrapper; + wrapper->setData(collection); + m_podio_datawrappers.push_back(wrapper); + return DataSvc::registerObject("/Event", "/" + collName, wrapper); + } const podio::Frame& getEventFrame() const { return m_eventframe; } diff --git a/k4FWCore/src/PodioDataSvc.cpp b/k4FWCore/src/PodioDataSvc.cpp index 16e0950e..956ab2da 100644 --- a/k4FWCore/src/PodioDataSvc.cpp +++ b/k4FWCore/src/PodioDataSvc.cpp @@ -17,13 +17,15 @@ * limitations under the License. */ #include "k4FWCore/PodioDataSvc.h" +#include #include "GaudiKernel/IConversionSvc.h" #include "GaudiKernel/IEventProcessor.h" #include "GaudiKernel/IProperty.h" #include "GaudiKernel/ISvcLocator.h" - #include "k4FWCore/DataWrapper.h" +#include "podio/CollectionBase.h" + #include "TTree.h" /// Service initialisation @@ -147,16 +149,12 @@ PodioDataSvc::PodioDataSvc(const std::string& name, ISvcLocator* svc) : DataSvc( /// Standard Destructor PodioDataSvc::~PodioDataSvc() {} -StatusCode PodioDataSvc::readCollection(const std::string& collName) { - const podio::CollectionBase* collection(nullptr); - collection = m_eventframe.get(collName); - if (collection == nullptr) { +const std::string_view PodioDataSvc::getCollectionType(const std::string& collName) { + const auto coll = m_eventframe.get(collName); + if (coll == nullptr) { error() << "Collection " << collName << " does not exist." << endmsg; } - auto wrapper = new DataWrapper; - wrapper->setData(collection); - m_podio_datawrappers.push_back(wrapper); - return DataSvc::registerObject("/Event", "/" + collName, wrapper); + return coll->getTypeName(); } StatusCode PodioDataSvc::registerObject(std::string_view parentPath, std::string_view fullPath, DataObject* pObject) { diff --git a/test/k4FWCoreTest/CMakeLists.txt b/test/k4FWCoreTest/CMakeLists.txt index a536490f..b487fa34 100644 --- a/test/k4FWCoreTest/CMakeLists.txt +++ b/test/k4FWCoreTest/CMakeLists.txt @@ -32,6 +32,12 @@ set(k4fwcoretest_plugin_sources src/components/k4FWCoreTest_cellID_reader.cpp src/components/k4FWCoreTest_cellID_writer.cpp src/components/k4FWCoreTest_CheckExampleEventData.cpp + src/components/ExampleFunctionalProducer.cpp + src/components/ExampleFunctionalConsumer.cpp + src/components/ExampleFunctionalTransformer.cpp + src/components/ExampleFunctionalProducerMultiple.cpp + src/components/ExampleFunctionalConsumerMultiple.cpp + src/components/ExampleFunctionalTransformerMultiple.cpp ) gaudi_add_module(k4FWCoreTestPlugins @@ -198,3 +204,54 @@ add_test(NAME TestExec WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} COMMAND ${K4RUN} --dry-run options/TestExec.py) set_test_env(TestExec) +add_test(NAME ExampleFunctionalProducer + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + COMMAND ${K4RUN} options/runExampleFunctionalProducer.py) +set_test_env(ExampleFunctionalProducer) + +add_test(NAME ExampleFunctionalConsumer + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + COMMAND ${K4RUN} options/runExampleFunctionalConsumer.py) +set_test_env(ExampleFunctionalConsumer) +set_tests_properties(ExampleFunctionalConsumer PROPERTIES + DEPENDS ExampleFunctionalProducer) + +# A dependency on the ExampleFunctionalConsumer is added to make sure +# they don't run at the same time +add_test(NAME ExampleFunctionalTransformer + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + COMMAND ${K4RUN} options/runExampleFunctionalTransformer.py) +set_test_env(ExampleFunctionalTransformer) +set_tests_properties(ExampleFunctionalTransformer PROPERTIES + DEPENDS "ExampleFunctionalProducer;ExampleFunctionalConsumer") + +add_test(NAME ExampleFunctionalProducerMultiple + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + COMMAND ${K4RUN} options/runExampleFunctionalProducerMultiple.py) +set_test_env(ExampleFunctionalProducerMultiple) + +add_test(NAME ExampleFunctionalConsumerMultiple + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + COMMAND ${K4RUN} options/runExampleFunctionalConsumerMultiple.py) +set_test_env(ExampleFunctionalConsumerMultiple) +set_tests_properties(ExampleFunctionalConsumerMultiple PROPERTIES + DEPENDS ExampleFunctionalProducerMultiple) + +add_test(NAME ExampleFunctionalTransformerMultiple + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + COMMAND ${K4RUN} options/runExampleFunctionalTransformerMultiple.py) +set_test_env(ExampleFunctionalTransformerMultiple) +set_tests_properties(ExampleFunctionalTransformerMultiple PROPERTIES + DEPENDS "ExampleFunctionalProducerMultiple;ExampleFunctionalConsumerMultiple") + +add_test(NAME FunctionalChain + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + COMMAND ${K4RUN} options/runFunctionalChain.py) +set_test_env(FunctionalChain) + +add_test(NAME FunctionalMix + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + COMMAND ${K4RUN} options/runFunctionalMix.py) +set_test_env(FunctionalMix) +set_tests_properties(FunctionalMix PROPERTIES + DEPENDS "ExampleFunctionalProducerMultiple") diff --git a/test/k4FWCoreTest/options/runExampleFunctionalConsumer.py b/test/k4FWCoreTest/options/runExampleFunctionalConsumer.py new file mode 100644 index 00000000..096bfed6 --- /dev/null +++ b/test/k4FWCoreTest/options/runExampleFunctionalConsumer.py @@ -0,0 +1,46 @@ +# +# Copyright (c) 2014-2023 Key4hep-Project. +# +# This file is part of Key4hep. +# See https://key4hep.github.io/key4hep-doc/ for further info. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This is an example reading from a file and using a consumer with a single input +# to check that the contents of the file are the expected ones + +from Gaudi.Configuration import INFO +from Configurables import ExampleFunctionalConsumer +from Configurables import ApplicationMgr +from Configurables import k4DataSvc +from Configurables import PodioInput + +podioevent = k4DataSvc("EventDataSvc") +podioevent.input = "output_k4test_exampledata_producer.root" + +inp = PodioInput() +inp.collections = [ + "MCParticles", +] + +consumer = ExampleFunctionalConsumer("ExampleFunctionalConsumer", + InputCollection="MCParticles", + ) + +ApplicationMgr(TopAlg=[inp, consumer], + EvtSel="NONE", + EvtMax=10, + ExtSvc=[podioevent], + OutputLevel=INFO, + ) diff --git a/test/k4FWCoreTest/options/runExampleFunctionalConsumerMultiple.py b/test/k4FWCoreTest/options/runExampleFunctionalConsumerMultiple.py new file mode 100644 index 00000000..48f6ba8b --- /dev/null +++ b/test/k4FWCoreTest/options/runExampleFunctionalConsumerMultiple.py @@ -0,0 +1,50 @@ +# +# Copyright (c) 2014-2023 Key4hep-Project. +# +# This file is part of Key4hep. +# See https://key4hep.github.io/key4hep-doc/ for further info. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This is an example reading from a file and using a consumer with several inputs +# to check that the contents of the file are the expected ones + +from Gaudi.Configuration import INFO +from Configurables import ExampleFunctionalConsumerMultiple +from Configurables import ApplicationMgr +from Configurables import k4DataSvc +from Configurables import PodioInput + +podioevent = k4DataSvc("EventDataSvc") +podioevent.input = "output_k4test_exampledata_producer_multiple.root" + +inp = PodioInput() +inp.collections = [ + "VectorFloat", + "MCParticles1", + "MCParticles2", + "SimTrackerHits", + "TrackerHits", + "Tracks", +] + +consumer = ExampleFunctionalConsumerMultiple("ExampleFunctionalConsumerMultiple", + ) + +ApplicationMgr(TopAlg=[inp, consumer], + EvtSel="NONE", + EvtMax=10, + ExtSvc=[podioevent], + OutputLevel=INFO, + ) diff --git a/test/k4FWCoreTest/options/runExampleFunctionalProducer.py b/test/k4FWCoreTest/options/runExampleFunctionalProducer.py new file mode 100644 index 00000000..410172c4 --- /dev/null +++ b/test/k4FWCoreTest/options/runExampleFunctionalProducer.py @@ -0,0 +1,43 @@ +# +# Copyright (c) 2014-2023 Key4hep-Project. +# +# This file is part of Key4hep. +# See https://key4hep.github.io/key4hep-doc/ for further info. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This is an example using a producer with a single output and saving that to a file + +from Gaudi.Configuration import INFO +from Configurables import ExampleFunctionalProducer +from Configurables import ApplicationMgr +from Configurables import k4DataSvc +from Configurables import PodioOutput + +podioevent = k4DataSvc("EventDataSvc") + +out = PodioOutput("out") +out.filename = "output_k4test_exampledata_producer.root" +# Collections can be dropped +# out.outputCommands = ["drop *"] + + +producer = ExampleFunctionalProducer("ExampleFunctionalProducer") + +ApplicationMgr(TopAlg=[producer, out], + EvtSel="NONE", + EvtMax=10, + ExtSvc=[k4DataSvc("EventDataSvc")], + OutputLevel=INFO, + ) diff --git a/test/k4FWCoreTest/options/runExampleFunctionalProducerMultiple.py b/test/k4FWCoreTest/options/runExampleFunctionalProducerMultiple.py new file mode 100644 index 00000000..a2be9bed --- /dev/null +++ b/test/k4FWCoreTest/options/runExampleFunctionalProducerMultiple.py @@ -0,0 +1,49 @@ +# +# Copyright (c) 2014-2023 Key4hep-Project. +# +# This file is part of Key4hep. +# See https://key4hep.github.io/key4hep-doc/ for further info. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This is an example using a producer with a multiple outputs and saving that to a file + +from Gaudi.Configuration import INFO +from Configurables import ExampleFunctionalProducerMultiple +from Configurables import ApplicationMgr +from Configurables import k4DataSvc +from Configurables import PodioOutput + +podioevent = k4DataSvc("EventDataSvc") + +out = PodioOutput("out") +out.filename = "output_k4test_exampledata_producer_multiple.root" +# Collections can be dropped +# out.outputCommands = ["drop *"] + +producer = ExampleFunctionalProducerMultiple("ExampleFunctionalProducerMultiple", + OutputCollectionFloat="VectorFloat", + OutputCollectionParticles1="MCParticles1", + OutputCollectionParticles2="MCParticles2", + OutputCollectionSimTrackerHits="SimTrackerHits", + OutputCollectionTrackerHits="TrackerHits", + OutputCollectionTracks="Tracks", + ExampleInt=5) + +ApplicationMgr(TopAlg=[producer, out], + EvtSel="NONE", + EvtMax=10, + ExtSvc=[k4DataSvc("EventDataSvc")], + OutputLevel=INFO, + ) diff --git a/test/k4FWCoreTest/options/runExampleFunctionalTransformer.py b/test/k4FWCoreTest/options/runExampleFunctionalTransformer.py new file mode 100644 index 00000000..8cf19f50 --- /dev/null +++ b/test/k4FWCoreTest/options/runExampleFunctionalTransformer.py @@ -0,0 +1,52 @@ +# +# Copyright (c) 2014-2023 Key4hep-Project. +# +# This file is part of Key4hep. +# See https://key4hep.github.io/key4hep-doc/ for further info. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This is an example reading from a file and using a consumer with several inputs +# to check that the contents of the file are the expected ones + +from Gaudi.Configuration import INFO +from Configurables import ExampleFunctionalTransformer +from Configurables import ApplicationMgr +from Configurables import k4DataSvc +from Configurables import PodioOutput +from Configurables import PodioInput + +podioevent = k4DataSvc("EventDataSvc") +podioevent.input = "output_k4test_exampledata_producer.root" + +inp = PodioInput() +inp.collections = [ + "MCParticles", +] + +out = PodioOutput("out") +out.filename = "output_k4test_exampledata_transformer.root" +# The collections that we don't drop will also be present in the output file +out.outputCommands = ["drop MCParticles"] + +transformer = ExampleFunctionalTransformer("ExampleFunctionalTransformer", + InputCollection="MCParticles", + OutputCollection="NewMCParticles") + +ApplicationMgr(TopAlg=[inp, transformer, out], + EvtSel="NONE", + EvtMax=10, + ExtSvc=[k4DataSvc("EventDataSvc")], + OutputLevel=INFO, + ) diff --git a/test/k4FWCoreTest/options/runExampleFunctionalTransformerMultiple.py b/test/k4FWCoreTest/options/runExampleFunctionalTransformerMultiple.py new file mode 100644 index 00000000..0e6a3b88 --- /dev/null +++ b/test/k4FWCoreTest/options/runExampleFunctionalTransformerMultiple.py @@ -0,0 +1,61 @@ +# +# Copyright (c) 2014-2023 Key4hep-Project. +# +# This file is part of Key4hep. +# See https://key4hep.github.io/key4hep-doc/ for further info. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from Gaudi.Configuration import INFO +from Configurables import ExampleFunctionalTransformerMultiple +from Configurables import ApplicationMgr +from Configurables import k4DataSvc +from Configurables import PodioOutput +from Configurables import PodioInput + +podioevent = k4DataSvc("EventDataSvc") +podioevent.input = "output_k4test_exampledata_producer_multiple.root" + +inp = PodioInput() +inp.collections = [ + "VectorFloat", + "MCParticles1", + "SimTrackerHits", + "TrackerHits", + "Tracks", +] + +out = PodioOutput("out") +out.filename = "output_k4test_exampledata_transformer_multiple.root" +# The collections that we don't drop will also be present in the output file +out.outputCommands = ["drop VectorFloat", "drop MCParticles1", + "drop SimTrackerHits", "drop TrackerHits", + "drop Tracks"] + +transformer = ExampleFunctionalTransformerMultiple("ExampleFunctionalTransformerMultiple", + InputCollectionFloat="VectorFloat", + InputCollectionParticles="MCParticles1", + InputCollectionSimTrackerHits="SimTrackerHits", + InputCollectionTrackerHits="TrackerHits", + InputCollectionTracks="Tracks", + OutputCollectionCounter="Counter", + OutputCollectionParticles="NewMCParticles", + ) + +ApplicationMgr(TopAlg=[inp, transformer, out], + EvtSel="NONE", + EvtMax=10, + ExtSvc=[k4DataSvc("EventDataSvc")], + OutputLevel=INFO, + ) diff --git a/test/k4FWCoreTest/options/runFunctionalChain.py b/test/k4FWCoreTest/options/runFunctionalChain.py new file mode 100644 index 00000000..929dc676 --- /dev/null +++ b/test/k4FWCoreTest/options/runFunctionalChain.py @@ -0,0 +1,55 @@ +# +# Copyright (c) 2014-2023 Key4hep-Project. +# +# This file is part of Key4hep. +# See https://key4hep.github.io/key4hep-doc/ for further info. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This is an example of how to run several functional algorithms in a chain +# The producer produces a collection, the first consumer checks that the collection +# is the expected one, the transformer transforms the collection and creates a new +# and the second consumer checks that the new collection is the expected one + +from Gaudi.Configuration import INFO +from Configurables import ExampleFunctionalProducer +from Configurables import ExampleFunctionalConsumer +from Configurables import ExampleFunctionalTransformer +from Configurables import ApplicationMgr +from Configurables import k4DataSvc +from Configurables import PodioOutput + +event_data_svc = k4DataSvc("EventDataSvc") + +out = PodioOutput("out") +out.filename = "functional_chain.root" + +producer = ExampleFunctionalProducer("ExampleFunctionalProducer") +consumer = ExampleFunctionalConsumer("ExampleFunctionalConsumer", + InputCollection="MCParticles", + ) +transformer = ExampleFunctionalTransformer("ExampleFunctionalTransformer", + InputCollection="MCParticles", + OutputCollection="NewMCParticles") +new_consumer = ExampleFunctionalConsumer("ExampleFunctionalConsumer2", + InputCollection="NewMCParticles", + ) +new_consumer.PossibleOffset = 10 + +ApplicationMgr(TopAlg=[producer, consumer, transformer, new_consumer, out], + EvtSel="NONE", + EvtMax=10, + ExtSvc=[event_data_svc], + OutputLevel=INFO, + ) diff --git a/test/k4FWCoreTest/options/runFunctionalMix.py b/test/k4FWCoreTest/options/runFunctionalMix.py new file mode 100644 index 00000000..eae3676f --- /dev/null +++ b/test/k4FWCoreTest/options/runFunctionalMix.py @@ -0,0 +1,110 @@ +# +# Copyright (c) 2014-2023 Key4hep-Project. +# +# This file is part of Key4hep. +# See https://key4hep.github.io/key4hep-doc/ for further info. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This is an example mixing functional and non-functional algorithms +# + +from Gaudi.Configuration import INFO +from Configurables import ExampleFunctionalConsumerMultiple, ExampleFunctionalTransformerMultiple +from Configurables import ExampleFunctionalProducerMultiple, k4FWCoreTest_CreateExampleEventData +from Configurables import k4FWCoreTest_CheckExampleEventData +from Configurables import ApplicationMgr +from Configurables import k4DataSvc +from Configurables import PodioInput, PodioOutput + +podioevent = k4DataSvc("EventDataSvc") +podioevent.input = "output_k4test_exampledata_producer_multiple.root" + +inp = PodioInput() +inp.collections = [ + "VectorFloat", + "MCParticles1", + "MCParticles2", + "SimTrackerHits", + "TrackerHits", + "Tracks", +] + +consumer_input_functional = ExampleFunctionalConsumerMultiple("ExampleFunctionalConsumerMultiple") +consumer_input_algorithm = k4FWCoreTest_CheckExampleEventData("CheckExampleEventData") +consumer_input_algorithm.mcparticles = 'MCParticles1' +consumer_input_algorithm.keepEventNumberZero = True + +# We only care about the new FunctionalMCParticles collection in this example +producer_functional = ExampleFunctionalProducerMultiple("ProducerFunctional", + OutputCollectionFloat="VectorFloat_", + OutputCollectionParticles1="FunctionalMCParticles", + OutputCollectionParticles2="MCParticles2_", + OutputCollectionSimTrackerHits="SimTrackerHits_", + OutputCollectionTrackerHits="TrackerHits_", + OutputCollectionTracks="Tracks_", + ExampleInt=5) + +consumer_producerfun_functional = ExampleFunctionalConsumerMultiple("FunctionalConsumerFunctional", + InputCollectionParticles="FunctionalMCParticles", + ) +consumer_producerfun_algorithm = k4FWCoreTest_CheckExampleEventData("CheckFunctional") +consumer_producerfun_algorithm.mcparticles = 'FunctionalMCParticles' +consumer_producerfun_algorithm.keepEventNumberZero = True + +producer_algorithm = k4FWCoreTest_CreateExampleEventData("CreateExampleEventData") +# We only care about the MCParticles collection +producer_algorithm.mcparticles = 'AlgorithmMCParticles' +producer_algorithm.simtrackhits = 'SimTrackerHits__' +producer_algorithm.trackhits = 'TrackerHits__' +producer_algorithm.tracks = 'Tracks__' +producer_algorithm.vectorfloat = 'VectorFloat__' + +consumer_produceralg_functional = ExampleFunctionalConsumerMultiple("FunctionalConsumerAlgorithm") +consumer_produceralg_algorithm = k4FWCoreTest_CheckExampleEventData("CheckAlgorithm") +consumer_produceralg_algorithm.mcparticles = 'FunctionalMCParticles' +consumer_produceralg_algorithm.keepEventNumberZero = True + +# Let's also run the transformer, why not +transformer_functional = ExampleFunctionalTransformerMultiple("FunctionalTransformerMultiple") + +out = PodioOutput("out") +out.filename = "output_k4test_exampledata_functional_mix.root" + +ApplicationMgr(TopAlg=[inp, + # Check we can read input + consumer_input_functional, + consumer_input_algorithm, + + producer_functional, + + # Check we can read what's produced by a functional + consumer_producerfun_functional, + consumer_producerfun_algorithm, + + producer_algorithm, + + # Check we can read what's produced by an algorithm + consumer_produceralg_functional, + consumer_produceralg_algorithm, + + transformer_functional, + + out + ], + EvtSel="NONE", + EvtMax=10, + ExtSvc=[podioevent], + OutputLevel=INFO, + ) diff --git a/test/k4FWCoreTest/src/components/ExampleFunctionalConsumer.cpp b/test/k4FWCoreTest/src/components/ExampleFunctionalConsumer.cpp new file mode 100644 index 00000000..c5150aa5 --- /dev/null +++ b/test/k4FWCoreTest/src/components/ExampleFunctionalConsumer.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014-2023 Key4hep-Project. + * + * This file is part of Key4hep. + * See https://key4hep.github.io/key4hep-doc/ for further info. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Gaudi/Property.h" +#include "GaudiAlg/Consumer.h" + +#include "edm4hep/MCParticleCollection.h" + +// Define BaseClass_t +#include "k4FWCore/BaseClass.h" + +#include +#include + +struct ExampleFunctionalConsumer final + : Gaudi::Functional::Consumer { + // The pair in KeyValue can be changed from python and it corresponds + // to the name of the input collection + ExampleFunctionalConsumer(const std::string& name, ISvcLocator* svcLoc) + : Consumer(name, svcLoc, KeyValue("InputCollection", "MCParticles")) {} + + // This is the function that will be called to transform the data + // Note that the function has to be const, as well as the collections + // we get from the input + void operator()(const edm4hep::MCParticleCollection& input) const override { + int i = 0; + for (const auto& particle : input) { + if ((particle.getPDG() != 1 + i + m_possibleOffset) || + (particle.getGeneratorStatus() != 2 + i + m_possibleOffset) || + (particle.getSimulatorStatus() != 3 + i + m_possibleOffset) || + (particle.getCharge() != 4 + i + m_possibleOffset) || (particle.getTime() != 5 + i + m_possibleOffset) || + (particle.getMass() != 6 + i + m_possibleOffset)) { + std::stringstream error; + error << "Wrong data in MCParticle collection, expected " << 1 + i + m_possibleOffset << ", " + << 2 + i + m_possibleOffset << ", " << 3 + i + m_possibleOffset << ", " << 4 + i + m_possibleOffset + << ", " << 5 + i + m_possibleOffset << ", " << 6 + i + m_possibleOffset << " got " << particle.getPDG() + << ", " << particle.getGeneratorStatus() << ", " << particle.getSimulatorStatus() << ", " + << particle.getCharge() << ", " << particle.getTime() << ", " << particle.getMass() << ""; + throw std::runtime_error(error.str()); + } + i++; + } + } + + Gaudi::Property m_possibleOffset{this, "PossibleOffset", 0, "Possible offset in the values data"}; +}; + +DECLARE_COMPONENT(ExampleFunctionalConsumer) diff --git a/test/k4FWCoreTest/src/components/ExampleFunctionalConsumerMultiple.cpp b/test/k4FWCoreTest/src/components/ExampleFunctionalConsumerMultiple.cpp new file mode 100644 index 00000000..8045669c --- /dev/null +++ b/test/k4FWCoreTest/src/components/ExampleFunctionalConsumerMultiple.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2014-2023 Key4hep-Project. + * + * This file is part of Key4hep. + * See https://key4hep.github.io/key4hep-doc/ for further info. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Gaudi/Property.h" +#include "GaudiAlg/Consumer.h" + +#include "edm4hep/MCParticleCollection.h" +#include "edm4hep/SimTrackerHitCollection.h" +#include "edm4hep/TrackCollection.h" +#include "edm4hep/TrackerHitCollection.h" +#include "podio/UserDataCollection.h" + +// Define BaseClass_t +#include "k4FWCore/BaseClass.h" + +#include +#include +#include + +// Which type of collection we are reading +using FloatColl = podio::UserDataCollection; +using ParticleColl = edm4hep::MCParticleCollection; +using SimTrackerHitColl = edm4hep::SimTrackerHitCollection; +using TrackerHitColl = edm4hep::TrackerHitCollection; +using TrackColl = edm4hep::TrackCollection; + +struct ExampleFunctionalConsumerMultiple final + : Gaudi::Functional::Consumer { + // The pairs in KeyValue can be changed from python and they correspond + // to the names of the input collection + ExampleFunctionalConsumerMultiple(const std::string& name, ISvcLocator* svcLoc) + : Consumer(name, svcLoc, + { + KeyValue("InputCollectionFloat", "VectorFloat"), + KeyValue("InputCollectionParticles", "MCParticles1"), + KeyValue("InputCollectionSimTrackerHits", "SimTrackerHits"), + KeyValue("InputCollectionTrackerHits", "TrackerHits"), + KeyValue("InputCollectionTracks", "Tracks"), + }) {} + + // This is the function that will be called to transform the data + // Note that the function has to be const, as well as the collections + // we get from the input + void operator()(const FloatColl& floatVector, const ParticleColl& particles, const SimTrackerHitColl& simTrackerHits, + const TrackerHitColl& trackerHits, const TrackColl& tracks) const override { + if (floatVector.size() != 3) { + throw std::runtime_error("Wrong size of floatVector collection, expected 3, got " + + std::to_string(floatVector.size()) + ""); + } + if ((floatVector[0] != 125) || (floatVector[1] != 25) || (floatVector[2] != 0)) { + std::stringstream error; + error << "Wrong data in floatVector collection, expected 125, 25, " << 0 << " got " << floatVector[0] << ", " + << floatVector[1] << ", " << floatVector[2] << ""; + throw std::runtime_error(error.str()); + } + + auto p4 = particles.momentum()[0]; + if ((p4.x != m_magicNumberOffset + 5) || (p4.y != m_magicNumberOffset + 6) || (p4.z != m_magicNumberOffset + 7) || + (particles[0].getMass() != m_magicNumberOffset + 8)) { + std::stringstream error; + error << "Wrong data in particles collection, expected " << m_magicNumberOffset + 5 << ", " + << m_magicNumberOffset + 6 << ", " << m_magicNumberOffset + 7 << ", " << m_magicNumberOffset + 8 << " got " + << p4.x << ", " << p4.y << ", " << p4.z << ", " << particles[0].getMass() << ""; + throw std::runtime_error(error.str()); + } + + if ((simTrackerHits[0].getPosition()[0] != 3) || (simTrackerHits[0].getPosition()[1] != 4) || + (simTrackerHits[0].getPosition()[2] != 5)) { + std::stringstream error; + error << "Wrong data in simTrackerHits collection, expected 3, 4, 5 got " << simTrackerHits[0].getPosition()[0] + << ", " << simTrackerHits[0].getPosition()[1] << ", " << simTrackerHits[0].getPosition()[2] << ""; + throw std::runtime_error(error.str()); + } + + if ((trackerHits[0].getPosition()[0] != 3) || (trackerHits[0].getPosition()[1] != 4) || + (trackerHits[0].getPosition()[2] != 5)) { + std::stringstream error; + error << "Wrong data in trackerHits collection, expected 3, 4, 5 got " << trackerHits[0].getPosition()[0] << ", " + << trackerHits[0].getPosition()[1] << ", " << trackerHits[0].getPosition()[2] << ""; + throw std::runtime_error(error.str()); + } + + if ((tracks[0].getType() != 1) || (std::abs(tracks[0].getChi2() - 2.1) > 1e-6) || (tracks[0].getNdf() != 3) || + (std::abs(tracks[0].getDEdx() - 4.1) > 1e-6) || (std::abs(tracks[0].getDEdxError() - 5.1) > 1e-6) || + (std::abs(tracks[0].getRadiusOfInnermostHit() - 6.1) > 1e-6)) { + 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() << ", " << tracks[0].getDEdx() << ", " + << tracks[0].getDEdxError() << ", " << tracks[0].getRadiusOfInnermostHit() << ""; + throw std::runtime_error(error.str()); + } + } + +private: + // integer to add to the dummy values written to the edm + Gaudi::Property m_magicNumberOffset{this, "magicNumberOffset", 0, + "Integer to add to the dummy values written to the edm"}; +}; + +DECLARE_COMPONENT(ExampleFunctionalConsumerMultiple) diff --git a/test/k4FWCoreTest/src/components/ExampleFunctionalProducer.cpp b/test/k4FWCoreTest/src/components/ExampleFunctionalProducer.cpp new file mode 100644 index 00000000..a5393e54 --- /dev/null +++ b/test/k4FWCoreTest/src/components/ExampleFunctionalProducer.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2014-2023 Key4hep-Project. + * + * This file is part of Key4hep. + * See https://key4hep.github.io/key4hep-doc/ for further info. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Gaudi/Property.h" +#include "GaudiAlg/Producer.h" +#include "k4FWCore/BaseClass.h" + +#include "edm4hep/MCParticleCollection.h" + +#include + +struct ExampleFunctionalProducer final : Gaudi::Functional::Producer { + // The pair in KeyValue can be changed from python and it corresponds + // to the name of the output collection + ExampleFunctionalProducer(const std::string& name, ISvcLocator* svcLoc) + : Producer(name, svcLoc, KeyValue("OutputCollection", "MCParticles")) {} + + // This is the function that will be called to produce the data + edm4hep::MCParticleCollection operator()() const override { + auto coll = edm4hep::MCParticleCollection(); + coll.push_back({1, 2, 3, 4, 5, 6, {}, {}, {}, {}, {}, {}}); + coll.push_back({2, 3, 4, 5, 6, 7, {}, {}, {}, {}, {}, {}}); + // We have to return whatever collection type we specified in the + // template argument + return coll; + } + +private: + // We can define any property we want that can be set from python + // and use it inside operator() + Gaudi::Property m_exampleInt{this, "ExampleInt", 3, "Example int that can be used in the algorithm"}; +}; + +DECLARE_COMPONENT(ExampleFunctionalProducer) diff --git a/test/k4FWCoreTest/src/components/ExampleFunctionalProducerMultiple.cpp b/test/k4FWCoreTest/src/components/ExampleFunctionalProducerMultiple.cpp new file mode 100644 index 00000000..2bae590e --- /dev/null +++ b/test/k4FWCoreTest/src/components/ExampleFunctionalProducerMultiple.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2014-2023 Key4hep-Project. + * + * This file is part of Key4hep. + * See https://key4hep.github.io/key4hep-doc/ for further info. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Gaudi/Property.h" +#include "GaudiAlg/Producer.h" +#include "k4FWCore/BaseClass.h" + +#include "edm4hep/MCParticleCollection.h" +#include "edm4hep/SimTrackerHitCollection.h" +#include "edm4hep/TrackCollection.h" +#include "edm4hep/TrackerHitCollection.h" +#include "podio/UserDataCollection.h" + +#include + +// Which type of collections we are producing +using Float = podio::UserDataCollection; +using Particle = edm4hep::MCParticleCollection; +using SimTrackerHit = edm4hep::SimTrackerHitCollection; +using TrackerHit = edm4hep::TrackerHitCollection; +using Track = edm4hep::TrackCollection; + +struct ExampleFunctionalProducerMultiple final + : Gaudi::Functional::Producer(), + BaseClass_t> { + // The pairs in KeyValue can be changed from python and they correspond + // to the names of the output collections + ExampleFunctionalProducerMultiple(const std::string& name, ISvcLocator* svcLoc) + : Producer( + name, svcLoc, + {KeyValue("OutputCollectionFloat", "VectorFloat"), KeyValue("OutputCollectionParticles1", "MCParticles1"), + KeyValue("OutputCollectionParticles2", "MCParticles2"), + KeyValue("OutputCollectionSimTrackerHits", "SimTrackerHits"), + KeyValue("OutputCollectionTrackerHits", "TrackerHits"), KeyValue("OutputCollectionTracks", "Tracks")}) {} + + // This is the function that will be called to produce the data + std::tuple operator()() const override { + // The following was copied and adapted from the + // k4FWCoreTest_CreateExampleEventData test + + auto floatVector = podio::UserDataCollection(); + floatVector.push_back(125.); + floatVector.push_back(25.); + floatVector.push_back(m_event); + + auto particles = edm4hep::MCParticleCollection(); + auto particle = particles.create(); + auto& p4 = particle.momentum(); + p4.x = m_magicNumberOffset + m_event + 5; + p4.y = m_magicNumberOffset + 6; + p4.z = m_magicNumberOffset + 7; + particle.setMass(m_magicNumberOffset + m_event + 8); + + auto simTrackerHits = edm4hep::SimTrackerHitCollection(); + auto hit = simTrackerHits.create(); + hit.setPosition({3, 4, 5}); + + auto trackerHits = edm4hep::TrackerHitCollection(); + auto trackerHit = trackerHits.create(); + trackerHit.setPosition({3, 4, 5}); + + auto tracks = edm4hep::TrackCollection(); + auto track = tracks.create(); + auto track2 = tracks.create(); + // set members + track.setType(1); + track.setChi2(2.1); + track.setNdf(3); + track.setDEdx(4.1); + track.setDEdxError(5.1); + track.setRadiusOfInnermostHit(6.1); + track.addToSubdetectorHitNumbers(1); + track.addToSubdetectorHitNumbers(4); + track.addToTrackStates(edm4hep::TrackState()); + // set associatons + track.addToTrackerHits(trackerHit); + track.addToTracks(track2); + + return std::make_tuple(std::move(floatVector), std::move(particles), Particle(), std::move(simTrackerHits), + std::move(trackerHits), std::move(tracks)); + } + +private: + // We can define any property we want that can be set from python + // and use it inside operator() + Gaudi::Property m_exampleInt{this, "ExampleInt", 3, "Example int that can be used in the algorithm"}; + // integer to add to the dummy values written to the edm + Gaudi::Property m_magicNumberOffset{this, "magicNumberOffset", 0, + "Integer to add to the dummy values written to the edm"}; + int m_event{0}; +}; + +DECLARE_COMPONENT(ExampleFunctionalProducerMultiple) diff --git a/test/k4FWCoreTest/src/components/ExampleFunctionalTransformer.cpp b/test/k4FWCoreTest/src/components/ExampleFunctionalTransformer.cpp new file mode 100644 index 00000000..9762c8e7 --- /dev/null +++ b/test/k4FWCoreTest/src/components/ExampleFunctionalTransformer.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2014-2023 Key4hep-Project. + * + * This file is part of Key4hep. + * See https://key4hep.github.io/key4hep-doc/ for further info. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Gaudi/Property.h" +#include "GaudiAlg/Transformer.h" + +#include "edm4hep/MCParticleCollection.h" +#include "edm4hep/MutableMCParticle.h" + +// Define BaseClass_t +#include "k4FWCore/BaseClass.h" + +#include + +// Which type of collection we are reading and writing +using colltype_in = edm4hep::MCParticleCollection; +using colltype_out = edm4hep::MCParticleCollection; + +struct ExampleFunctionalTransformer final + : Gaudi::Functional::Transformer { + ExampleFunctionalTransformer(const std::string& name, ISvcLocator* svcLoc) + : Transformer(name, svcLoc, KeyValue("InputCollection", "MCParticles"), + KeyValue("OutputCollection", "NewMCParticles")) {} + + // This is the function that will be called to transform the data + // Note that the function has to be const, as well as all pointers to collections + // we get from the input + colltype_out operator()(const colltype_in& input) const override { + auto coll_out = edm4hep::MCParticleCollection(); + for (const auto& particle : input) { + auto new_particle = edm4hep::MutableMCParticle(); + new_particle.setPDG(particle.getPDG() + 10); + new_particle.setGeneratorStatus(particle.getGeneratorStatus() + 10); + new_particle.setSimulatorStatus(particle.getSimulatorStatus() + 10); + new_particle.setCharge(particle.getCharge() + 10); + new_particle.setTime(particle.getTime() + 10); + new_particle.setMass(particle.getMass() + 10); + coll_out->push_back(new_particle); + } + return coll_out; + } +}; + +DECLARE_COMPONENT(ExampleFunctionalTransformer) diff --git a/test/k4FWCoreTest/src/components/ExampleFunctionalTransformerMultiple.cpp b/test/k4FWCoreTest/src/components/ExampleFunctionalTransformerMultiple.cpp new file mode 100644 index 00000000..83f64b97 --- /dev/null +++ b/test/k4FWCoreTest/src/components/ExampleFunctionalTransformerMultiple.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2014-2023 Key4hep-Project. + * + * This file is part of Key4hep. + * See https://key4hep.github.io/key4hep-doc/ for further info. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Gaudi/Property.h" +#include "GaudiAlg/Transformer.h" + +#include "edm4hep/MCParticleCollection.h" +#include "edm4hep/SimTrackerHitCollection.h" +#include "edm4hep/TrackCollection.h" +#include "edm4hep/TrackerHitCollection.h" +#include "podio/UserDataCollection.h" + +// Define BaseClass_t +#include "k4FWCore/BaseClass.h" + +#include + +// Which type of collection we are reading +using FloatColl = podio::UserDataCollection; +using ParticleColl = edm4hep::MCParticleCollection; +using SimTrackerHitColl = edm4hep::SimTrackerHitCollection; +using TrackerHitColl = edm4hep::TrackerHitCollection; +using TrackColl = edm4hep::TrackCollection; + +// As a simple example, we'll write an integer and a collection of MCParticles +using Counter = podio::UserDataCollection; +using Particle = edm4hep::MCParticleCollection; + +struct ExampleFunctionalTransformerMultiple final + : Gaudi::Functional::MultiTransformer(const FloatColl&, const ParticleColl&, + const SimTrackerHitColl&, const TrackerHitColl&, + const TrackColl&), + BaseClass_t> { + ExampleFunctionalTransformerMultiple(const std::string& name, ISvcLocator* svcLoc) + : MultiTransformer( + name, svcLoc, + {KeyValue("InputCollectionFloat", "VectorFloat"), KeyValue("InputCollectionParticles", "MCParticles1"), + KeyValue("InputCollectionSimTrackerHits", "SimTrackerHits"), + KeyValue("InputCollectionTrackerHits", "TrackerHits"), KeyValue("InputCollectionTracks", "Tracks")}, + {KeyValue("OutputCollectionCounter", "Counter"), KeyValue("OutputCollectionParticles", "NewMCParticles")}) { + } + + // This is the function that will be called to transform the data + // Note that the function has to be const, as well as the collections + // we get from the input + std::tuple operator()(const FloatColl& floatVector, const ParticleColl& particles, + const SimTrackerHitColl& simTrackerHits, const TrackerHitColl& trackerHits, + const TrackColl& tracks) const override { + Counter counter; + + counter.push_back(floatVector.size()); + + auto newParticlesColl = edm4hep::MCParticleCollection(); + for (const auto& p : particles) { + // We need to create a new particle since the current one is already in a collection + + // We could create a new one + auto newParticle = newParticlesColl->create(); + newParticle.setPDG(p.getPDG()); + newParticle.setGeneratorStatus(p.getGeneratorStatus() + 1); + newParticle.setSimulatorStatus(p.getSimulatorStatus() + 1); + newParticle.setCharge(p.getCharge() + 2); + newParticle.setTime(p.getTime() + 3); + newParticle.setMass(p.getMass() + 4); + } + counter.push_back(particles.size()); + + counter.push_back(simTrackerHits.size()); + + counter.push_back(trackerHits.size()); + + counter.push_back(tracks.size()); + + return std::make_tuple(std::move(counter), std::move(newParticlesColl)); + } +}; + +DECLARE_COMPONENT(ExampleFunctionalTransformerMultiple) diff --git a/test/k4FWCoreTest/src/components/k4FWCoreTest_CheckExampleEventData.cpp b/test/k4FWCoreTest/src/components/k4FWCoreTest_CheckExampleEventData.cpp index 4614989d..f4cc99c2 100644 --- a/test/k4FWCoreTest/src/components/k4FWCoreTest_CheckExampleEventData.cpp +++ b/test/k4FWCoreTest/src/components/k4FWCoreTest_CheckExampleEventData.cpp @@ -39,9 +39,8 @@ StatusCode k4FWCoreTest_CheckExampleEventData::execute() { auto floatVector = m_vectorFloatHandle.get(); if (floatVector->size() != 3 || (*floatVector)[2] != m_event) { fatal() << "Contents of vectorfloat collection is not as expected: size = " << floatVector->size() - << " (expected 3), contents = " << *floatVector << " (expected [125., 25., " << m_event << "]) " - << std::endl; - return StatusCode::FAILURE; + << " (expected 3), contents = " << *floatVector << " (expected [125., 25., " << m_event << "]) " << endmsg; + // return StatusCode::FAILURE; } auto particles = m_mcParticleHandle.get(); @@ -50,11 +49,13 @@ StatusCode k4FWCoreTest_CheckExampleEventData::execute() { (particle.getMass() != m_magicNumberOffset + m_event + 8)) { fatal() << "Contents of mcparticles collection is not as expected: momentum.x = " << particle.getMomentum().x << " (expected " << m_magicNumberOffset + m_event + 5 << "), mass = " << particle.getMass() << " (expected " - << m_magicNumberOffset + m_event + 8 << ")" << std::endl; - return StatusCode::FAILURE; + << m_magicNumberOffset + m_event + 8 << ")" << endmsg; + // return StatusCode::FAILURE; } - m_event++; + if (!m_keepEventNumberZero) { + m_event++; + } return StatusCode::SUCCESS; } diff --git a/test/k4FWCoreTest/src/components/k4FWCoreTest_CheckExampleEventData.h b/test/k4FWCoreTest/src/components/k4FWCoreTest_CheckExampleEventData.h index a28b1cb6..096f6b26 100644 --- a/test/k4FWCoreTest/src/components/k4FWCoreTest_CheckExampleEventData.h +++ b/test/k4FWCoreTest/src/components/k4FWCoreTest_CheckExampleEventData.h @@ -54,8 +54,10 @@ class k4FWCoreTest_CheckExampleEventData : public GaudiAlgorithm { private: /// integer to add to the dummy values written to the edm - Gaudi::Property m_magicNumberOffset{this, "magicNumberOffset", 0, + Gaudi::Property m_magicNumberOffset{this, "magicNumberOffset", 0, "Integer to add to the dummy values written to the edm"}; + Gaudi::Property m_keepEventNumberZero{this, "keepEventNumberZero", false, + "Don't add the event number to the dummy values written"}; /// Handle for the MCParticles to be written DataHandle m_mcParticleHandle{"MCParticles", Gaudi::DataHandle::Reader, this}; DataHandle> m_vectorFloatHandle{"VectorFloat", Gaudi::DataHandle::Reader, this};