diff --git a/k4FWCore/components/IOSvc.cpp b/k4FWCore/components/IOSvc.cpp index 0a92a560..8bba0442 100644 --- a/k4FWCore/components/IOSvc.cpp +++ b/k4FWCore/components/IOSvc.cpp @@ -28,6 +28,7 @@ #include "GaudiKernel/AnyDataWrapper.h" #include "GaudiKernel/IEventProcessor.h" +#include #include #include @@ -63,6 +64,20 @@ StatusCode IOSvc::initialize() { return StatusCode::FAILURE; } + m_metadataSvc = service("MetadataSvc"); + if (!m_metadataSvc) { + error() << "Unable to locate the MetadataSvc" << endmsg; + return StatusCode::FAILURE; + } + if (m_reader) { + auto categories = m_reader->getAvailableCategories(); + if (std::find(categories.begin(), categories.end(), podio::Category::Metadata) != categories.end() && + m_reader->getEntries(podio::Category::Metadata) > 0) { + info() << "Setting metadata frame" << endmsg; + m_metadataSvc->setFrame(m_reader->readEntry(podio::Category::Metadata, 0)); + } + } + m_hiveWhiteBoard = service("EventDataSvc"); return StatusCode::SUCCESS; diff --git a/k4FWCore/components/IOSvc.h b/k4FWCore/components/IOSvc.h index b77025d1..fe23352e 100644 --- a/k4FWCore/components/IOSvc.h +++ b/k4FWCore/components/IOSvc.h @@ -30,6 +30,7 @@ #include "podio/ROOTWriter.h" #include "IIOSvc.h" +#include "k4FWCore/IMetadataSvc.h" #include "k4FWCore/KeepDropSwitch.h" #include @@ -85,6 +86,7 @@ class IOSvc : public extends { SmartIF m_dataSvc; SmartIF m_incidentSvc; SmartIF m_hiveWhiteBoard; + SmartIF m_metadataSvc; void handle(const Incident& incident) override; int m_entries{0}; diff --git a/k4FWCore/components/MetadataSvc.cpp b/k4FWCore/components/MetadataSvc.cpp new file mode 100644 index 00000000..9c0b82f5 --- /dev/null +++ b/k4FWCore/components/MetadataSvc.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014-2024 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 "MetadataSvc.h" + +#include "podio/Frame.h" + +#include +#include +#include + +StatusCode MetadataSvc::initialize() { + StatusCode sc = Service::initialize(); + if (sc.isFailure()) { + error() << "Unable to initialize base class Service." << endmsg; + return sc; + } + m_dataSvc = service("EventDataSvc"); + if (!m_dataSvc) { + error() << "Unable to locate the EventDataSvc" << endmsg; + return StatusCode::FAILURE; + } + + m_frame.reset(new podio::Frame()); + + return StatusCode::SUCCESS; +} + +StatusCode MetadataSvc::finalize() { return Service::finalize(); } + +void MetadataSvc::setFrame(podio::Frame&& fr) { m_frame.reset(new podio::Frame(std::move(fr))); } + +DECLARE_COMPONENT(MetadataSvc) diff --git a/k4FWCore/components/MetadataSvc.h b/k4FWCore/components/MetadataSvc.h new file mode 100644 index 00000000..97fa5e92 --- /dev/null +++ b/k4FWCore/components/MetadataSvc.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2014-2024 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 FWCORE_METADATASVC_H +#define FWCORE_METADATASVC_H + +#include "GaudiKernel/IDataProviderSvc.h" +#include "GaudiKernel/Service.h" + +#include "podio/Frame.h" + +#include "k4FWCore/IMetadataSvc.h" + +class MetadataSvc : public extends { + using extends::extends; + +public: + ~MetadataSvc() override = default; + + StatusCode initialize() override; + StatusCode finalize() override; + +protected: + SmartIF m_dataSvc; + + std::unique_ptr m_frame; + + void setFrame(podio::Frame&& frame) override; +}; + +#endif diff --git a/k4FWCore/components/Writer.cpp b/k4FWCore/components/Writer.cpp index 38ae00e5..bcf8fe37 100644 --- a/k4FWCore/components/Writer.cpp +++ b/k4FWCore/components/Writer.cpp @@ -30,6 +30,7 @@ #include "k4FWCore/DataWrapper.h" #include "k4FWCore/FunctionalUtils.h" +#include "k4FWCore/IMetadataSvc.h" #include #include @@ -53,6 +54,7 @@ class Writer final : public Gaudi::Functional::Consumer iosvc{this, "IOSvc", "IOSvc"}; SmartIF m_hiveWhiteBoard; SmartIF m_dataSvc; + SmartIF m_metadataSvc; mutable bool m_first{true}; StatusCode initialize() override { @@ -74,6 +76,12 @@ class Writer final : public Gaudi::Functional::ConsumergetWriter()->writeFrame(config_metadata_frame, "configuration_metadata"); + if (m_metadataSvc->m_frame) { + iosvc->getWriter()->writeFrame(*std::move(m_metadataSvc->m_frame), podio::Category::Metadata); + } + iosvc->deleteWriter(); + return StatusCode::SUCCESS; } diff --git a/k4FWCore/include/k4FWCore/IMetadataSvc.h b/k4FWCore/include/k4FWCore/IMetadataSvc.h new file mode 100644 index 00000000..ac12dce1 --- /dev/null +++ b/k4FWCore/include/k4FWCore/IMetadataSvc.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014-2024 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 FWCORE_IMETADATASERVICE_H +#define FWCORE_IMETADATASERVICE_H + +#include "GaudiKernel/IInterface.h" + +#include "podio/Frame.h" + +class IMetadataSvc : virtual public IInterface { +public: + DeclareInterfaceID(IMetadataSvc, 1, 0); + + std::unique_ptr m_frame; + + virtual void setFrame(podio::Frame&& fr) = 0; + template void put(const std::string& name, const T& obj) { + if (!m_frame) { + m_frame.reset(new podio::Frame()); + } + m_frame->putParameter(name, obj); + } + template std::optional get(const std::string& name) { return m_frame->getParameter(name); } +}; + +#endif diff --git a/k4FWCore/include/k4FWCore/MetaDataHandle.h b/k4FWCore/include/k4FWCore/MetaDataHandle.h index ced03f49..be05fb9c 100644 --- a/k4FWCore/include/k4FWCore/MetaDataHandle.h +++ b/k4FWCore/include/k4FWCore/MetaDataHandle.h @@ -19,6 +19,9 @@ #ifndef K4FWCORE_METADATAHANDLE_H #define K4FWCORE_METADATAHANDLE_H +#include + +#include "k4FWCore/MetadataUtils.h" #include "k4FWCore/PodioDataSvc.h" template class MetaDataHandle { @@ -95,10 +98,17 @@ template std::optional MetaDataHandle::get_optional() const { //--------------------------------------------------------------------------- template const T MetaDataHandle::get() const { - const auto maybeVal = get_optional(); - if (!maybeVal.has_value()) { - throw GaudiException("MetaDataHandle empty handle access", - "MetaDataHandle " + fullDescriptor() + " not (yet?) available", StatusCode::FAILURE); + std::optional maybeVal; + // DataHandle based algorithms + if (m_podio_data_service) { + maybeVal = get_optional(); + if (!maybeVal.has_value()) { + throw GaudiException("MetaDataHandle empty handle access", + "MetaDataHandle " + fullDescriptor() + " not (yet?) available", StatusCode::FAILURE); + } + // Functional algorithms + } else { + maybeVal = k4FWCore::getParameter(fullDescriptor()); } return maybeVal.value(); } @@ -115,13 +125,20 @@ template void MetaDataHandle::put(T value) { StatusCode::FAILURE); // check whether we are in the proper State // put is only allowed in the initalization - if (m_podio_data_service->targetFSMState() == Gaudi::StateMachine::RUNNING) { - throw GaudiException("MetaDataHandle policy violation", "Put cannot be used during the event loop", - StatusCode::FAILURE); + + std::string full_descriptor = fullDescriptor(); + // DataHandle based algorithms + if (m_podio_data_service) { + if (m_podio_data_service->targetFSMState() == Gaudi::StateMachine::RUNNING) { + throw GaudiException("MetaDataHandle policy violation", "Put cannot be used during the event loop", + StatusCode::FAILURE); + } + podio::Frame& frame = m_podio_data_service->getMetaDataFrame(); + frame.putParameter(full_descriptor, value); + // Functional algorithms + } else { + k4FWCore::putParameter(full_descriptor, value); } - std::string full_descriptor = fullDescriptor(); - podio::Frame& frame = m_podio_data_service->getMetaDataFrame(); - frame.putParameter(full_descriptor, value); } //--------------------------------------------------------------------------- @@ -145,8 +162,12 @@ template void MetaDataHandle::checkPodioDataSvc() { if (cmd.find("genconf") != std::string::npos) return; - if (nullptr == m_podio_data_service) { - std::cout << "ERROR: MetaDataHandles require the PodioDataSvc" << std::endl; + // The proper check would be the following: + // if (!m_podio_data_service && !Gaudi::svcLocator()->service("MetadataSvc")) { + // However, it seems there is always a service called "MetadataSvc" from Gaudi, + // so the check will always pass + if (!m_podio_data_service) { + std::cout << "Warning: MetaDataHandles require the PodioDataSvc (ignore if using IOSvc)" << std::endl; } } diff --git a/k4FWCore/include/k4FWCore/MetadataUtils.h b/k4FWCore/include/k4FWCore/MetadataUtils.h new file mode 100644 index 00000000..2113773e --- /dev/null +++ b/k4FWCore/include/k4FWCore/MetadataUtils.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2014-2024 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 FWCORE_METADATAUTILS_H +#define FWCORE_METADATAUTILS_H + +#include +#include "Gaudi/Algorithm.h" + +#include "k4FWCore/IMetadataSvc.h" + +namespace k4FWCore { + + /// @brief Save a metadata parameter in the metadata frame + /// @param name The name of the parameter + /// @param value The value of the parameter + /// @param alg The algorithm that is saving the parameter, typically "this" + template void putParameter(const std::string& name, const T& value, const Gaudi::Algorithm* alg) { + auto metadataSvc = alg->service("MetadataSvc", false); + if (!metadataSvc) { + alg->error() << "MetadataSvc not found" << endmsg; + return; + } + metadataSvc->put(name, value); + } + /// @brief Save a metadata parameter in the metadata frame. Overload for compatibility + /// with the MetadataHandle, don't use! + template void putParameter(const std::string& name, const T& value) { + auto metadataSvc = Gaudi::svcLocator()->service("MetadataSvc", false); + if (!metadataSvc) { + std::cout << "MetadataSvc not found" << std::endl; + return; + } + return metadataSvc->put(name, value); + } + /// @brief Get a metadata parameter from the metadata frame + /// @param name The name of the parameter + /// @param alg The algorithm that is saving the parameter, typically "this" + /// @return std::optional The value of the parameter, if it exists or std::nullopt + template std::optional getParameter(const std::string& name, const Gaudi::Algorithm* alg) { + auto metadataSvc = alg->service("MetadataSvc", false); + if (!metadataSvc) { + alg->error() << "MetadataSvc not found" << endmsg; + return std::nullopt; + } + return metadataSvc->get(name); + } + /// @brief Get a metadata parameter from the metadata frame. Overload for compatibility + /// with the MetadataHandle, don't use! + template std::optional getParameter(const std::string& name) { + auto metadataSvc = Gaudi::svcLocator()->service("MetadataSvc", false); + if (!metadataSvc) { + return std::nullopt; + } + return metadataSvc->get(name); + } +} // namespace k4FWCore + +#endif // FWCORE_METADATAUTILS_H diff --git a/python/k4FWCore/ApplicationMgr.py b/python/k4FWCore/ApplicationMgr.py index 8c5dc2f3..06f2a499 100644 --- a/python/k4FWCore/ApplicationMgr.py +++ b/python/k4FWCore/ApplicationMgr.py @@ -17,7 +17,7 @@ # limitations under the License. # from Configurables import ApplicationMgr as AppMgr -from Configurables import Reader, Writer, IOSvc, Gaudi__Sequencer +from Configurables import Reader, Writer, IOSvc, MetadataSvc, Gaudi__Sequencer import os from podio.root_io import Reader as PodioReader @@ -39,6 +39,8 @@ def __init__(self, **kwargs): self._mgr = AppMgr(**kwargs) for conf in frozenset(self._mgr.allConfigurables.values()): + if isinstance(conf, MetadataSvc): + self._mgr.ExtSvc.append(conf) if not isinstance(conf, IOSvc): continue props = conf.getPropertiesWithDescription() diff --git a/python/k4FWCore/IOSvc.py b/python/k4FWCore/IOSvc.py index edae89fb..86786991 100644 --- a/python/k4FWCore/IOSvc.py +++ b/python/k4FWCore/IOSvc.py @@ -16,13 +16,14 @@ # See the License for the specific language governing permissions and # limitations under the License. # -from Configurables import IOSvc as IO +from Configurables import IOSvc as IO, MetadataSvc import os class IOSvc: def __init__(self, *args, **kwargs): self._svc = IO(**kwargs) + MetadataSvc("MetadataSvc") def __getattr__(self, attr): return getattr(self._svc, attr) diff --git a/test/k4FWCoreTest/CMakeLists.txt b/test/k4FWCoreTest/CMakeLists.txt index f31e5ede..8f18f36c 100644 --- a/test/k4FWCoreTest/CMakeLists.txt +++ b/test/k4FWCoreTest/CMakeLists.txt @@ -135,9 +135,11 @@ add_test_with_env(FunctionalTransformerRuntimeCollectionsMultiple options/Exampl add_test_with_env(FunctionalTransformerHist options/ExampleFunctionalTransformerHist.py) add_test_with_env(FunctionalCollectionMerger options/ExampleFunctionalCollectionMerger.py) add_test_with_env(FunctionalFilterFile options/ExampleFunctionalFilterFile.py) +add_test_with_env(FunctionalMetadata options/ExampleFunctionalMetadata.py) +add_test_with_env(FunctionalMetadataOldAlgorithm options/ExampleFunctionalMetadataOldAlgorithm.py) add_test(NAME FunctionalCheckFiles COMMAND python3 ${CMAKE_CURRENT_LIST_DIR}/options/CheckOutputFiles.py) -set_tests_properties(FunctionalCheckFiles PROPERTIES DEPENDS "FunctionalFile;FunctionalMTFile;FunctionalMultipleFile;FunctionalOutputCommands;FunctionalProducerAbsolutePath;FunctionalTransformerRuntimeEmpty;FunctionalMix;FunctionalMixIOSvc;FunctionalTransformerHist;FunctionalCollectionMerger;FunctionalFilterFile") +set_tests_properties(FunctionalCheckFiles PROPERTIES DEPENDS "FunctionalFile;FunctionalMTFile;FunctionalMultipleFile;FunctionalOutputCommands;FunctionalProducerAbsolutePath;FunctionalTransformerRuntimeEmpty;FunctionalMix;FunctionalMixIOSvc;FunctionalTransformerHist;FunctionalCollectionMerger;FunctionalFilterFile;FunctionalMetadata;FunctionalMetadataOldAlgorithm") # Do this after checking the files not to overwrite them add_test_with_env(FunctionalFile_toolong options/ExampleFunctionalFile.py -n 999 PROPERTIES DEPENDS FunctionalCheckFiles PASS_REGULAR_EXPRESSION diff --git a/test/k4FWCoreTest/options/CheckOutputFiles.py b/test/k4FWCoreTest/options/CheckOutputFiles.py index 2c6937db..665c2562 100644 --- a/test/k4FWCoreTest/options/CheckOutputFiles.py +++ b/test/k4FWCoreTest/options/CheckOutputFiles.py @@ -29,9 +29,13 @@ def check_collections(filename, names): print(f'Checking file "{filename}" for collections {names}') podio_reader = podio.root_io.Reader(filename) + if "events" not in podio_reader.categories: + raise RuntimeError(f"File {filename} has no events") frames = podio_reader.get("events") if not len(frames) and len(names): print(f"File {filename} is empty but {names} are expected") + # Prevent a possible crash + del podio_reader raise RuntimeError("File is empty but should not be") for frame in frames: available = set(frame.getAvailableCollections()) @@ -154,3 +158,33 @@ def check_events(filename, number): "functional_filter.root", 5, ) + +check_collections("functional_metadata.root", ["MCParticles"]) + +reader = podio.root_io.Reader("functional_metadata.root") +metadata = reader.get("metadata")[0] +for key, value in zip( + [ + "NumberOfParticles", + "ParticleTime", + "PDGValues", + "MetadataString", + "FinalizeMetadataInt", + ], + [3, 1.5, [1, 2, 3, 4], "hello", 10], +): + if metadata.get_parameter(key) != value: + raise RuntimeError( + f"Metadata parameter {key} does not match the expected value, got {metadata.get_parameter(key)} but expected {value}" + ) + +reader = podio.root_io.Reader("functional_metadata_old_algorithm.root") +metadata = reader.get("metadata")[0] +for key, value in zip( + ["SimTrackerHits__CellIDEncoding"], + ["M:3,S-1:3,I:9,J:9,K-1:6"], +): + if metadata.get_parameter(key) != value: + raise RuntimeError( + f"Metadata parameter {key} does not match the expected value, got {metadata.get_parameter(key)} but expected {value}" + ) diff --git a/test/k4FWCoreTest/options/ExampleFunctionalMetadata.py b/test/k4FWCoreTest/options/ExampleFunctionalMetadata.py new file mode 100644 index 00000000..95338819 --- /dev/null +++ b/test/k4FWCoreTest/options/ExampleFunctionalMetadata.py @@ -0,0 +1,42 @@ +# +# Copyright (c) 2014-2024 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 producing metadata + +from Gaudi.Configuration import INFO +from Configurables import ( + ExampleFunctionalMetadataProducer, + ExampleFunctionalMetadataConsumer, +) +from k4FWCore import ApplicationMgr, IOSvc +from Configurables import EventDataSvc + +iosvc = IOSvc() +iosvc.output = "functional_metadata.root" + +producer = ExampleFunctionalMetadataProducer("Producer", OutputCollection=["MCParticles"]) +consumer = ExampleFunctionalMetadataConsumer("Consumer", InputCollection=["MCParticles"]) + +ApplicationMgr( + TopAlg=[producer, consumer], + EvtSel="NONE", + EvtMax=10, + ExtSvc=[EventDataSvc("EventDataSvc")], + OutputLevel=INFO, +) diff --git a/test/k4FWCoreTest/options/ExampleFunctionalMetadataOldAlgorithm.py b/test/k4FWCoreTest/options/ExampleFunctionalMetadataOldAlgorithm.py new file mode 100644 index 00000000..2cbda6a2 --- /dev/null +++ b/test/k4FWCoreTest/options/ExampleFunctionalMetadataOldAlgorithm.py @@ -0,0 +1,42 @@ +# +# Copyright (c) 2014-2024 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 k4FWCoreTest_cellID_writer, k4FWCoreTest_cellID_reader +from k4FWCore import ApplicationMgr, IOSvc + +iosvc = IOSvc() +iosvc.output = "functional_metadata_old_algorithm.root" + +producer = k4FWCoreTest_cellID_writer() +consumer = k4FWCoreTest_cellID_reader() + + +# out = PodioOutput("out") +# out.filename = "output_k4test_exampledata_cellid.root" +# out.outputCommands = ["keep *"] + + +ApplicationMgr( + TopAlg=[producer, consumer], + EvtSel="NONE", + EvtMax=10, + ExtSvc=[], + OutputLevel=INFO, + StopOnSignal=True, +) diff --git a/test/k4FWCoreTest/src/components/ExampleFunctionalMetadataConsumer.cpp b/test/k4FWCoreTest/src/components/ExampleFunctionalMetadataConsumer.cpp new file mode 100644 index 00000000..11bc49e4 --- /dev/null +++ b/test/k4FWCoreTest/src/components/ExampleFunctionalMetadataConsumer.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2014-2024 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 "edm4hep/MCParticleCollection.h" + +#include "k4FWCore/Consumer.h" +#include "k4FWCore/MetadataUtils.h" + +#include +#include + +struct ExampleFunctionalMetadataConsumer final : k4FWCore::Consumer { + ExampleFunctionalMetadataConsumer(const std::string& name, ISvcLocator* svcLoc) + : Consumer(name, svcLoc, KeyValues("InputCollection", {"MCParticles"})) {} + + StatusCode initialize() override { + m_particleNum = k4FWCore::getParameter("NumberOfParticles", this).value_or(0); + if (m_particleNum != 3) { + error() << "NumberOfParticles expected to be 3 but is " << m_particleNum << endmsg; + return StatusCode::FAILURE; + } + + m_particleTime = k4FWCore::getParameter("ParticleTime", this).value_or(0); + if (m_particleTime != 1.5) { + error() << "ParticleTime expected to be 1.5 but is " << m_particleTime << endmsg; + return StatusCode::FAILURE; + } + m_PDGValues = k4FWCore::getParameter>("PDGValues", this).value_or(std::vector{}); + if (m_PDGValues != std::vector{1, 2, 3, 4}) { + error() << "PDGValues expected to be {1, 2, 3, 4} but is {"; + for (const auto& pdg : m_PDGValues) { + error() << pdg << ", "; + } + error() << "}" << endmsg; + return StatusCode::FAILURE; + } + m_metadataString = k4FWCore::getParameter("MetadataString", this).value_or(""); + if (m_metadataString != "hello") { + error() << "MetadataString expected to be 'hello' but is '" << m_metadataString << "'" << endmsg; + return StatusCode::FAILURE; + } + return StatusCode::SUCCESS; + } + + void operator()(const edm4hep::MCParticleCollection& input) const override { + // Check that it's possible to get metadata parameters from the main loop + auto particleNum = k4FWCore::getParameter("NumberOfParticles", this).value_or(-1); + if (input.size() != particleNum) { + error() << "Input MCParticleCollection size is not " << particleNum << endmsg; + return; + } + int i = 0; + for (const auto& particle : input) { + if (particle.getTime() != m_particleTime) { + error() << "ParticleTime expected to be " << m_particleTime << " but is " << particle.getTime() << endmsg; + return; + } + if (particle.getPDG() != m_PDGValues[i]) { + error() << "PDGValues expected to be " << m_PDGValues[i] << " but is " << particle.getPDG() << endmsg; + return; + } + ++i; + } + } + + StatusCode finalize() override { + auto particleNum = k4FWCore::getParameter("NumberOfParticles", this).value_or(-1); + if (particleNum != 3) { + error() << "NumberOfParticles expected to be 3 but is " << particleNum << endmsg; + return StatusCode::FAILURE; + } + + // Putting parameters in the main loop fails + // auto eventMetadataInt = k4FWCore::getParameter("EventMetadataInt", this).value_or(-1); + // if (eventMetadataInt != 5) { + // error() << "EventMetadataInt is not 5" << endmsg; + // return StatusCode::FAILURE; + // } + + auto finalizeMetadataInt = k4FWCore::getParameter("FinalizeMetadataInt", this).value_or(-1); + if (finalizeMetadataInt != 10) { + error() << "FinalizeMetadataInt expected to be 10 but is " << finalizeMetadataInt << endmsg; + return StatusCode::FAILURE; + } + + return StatusCode::SUCCESS; + } + +private: + int m_particleNum; + float m_particleTime; + std::string m_metadataString; + std::vector m_PDGValues; +}; + +DECLARE_COMPONENT(ExampleFunctionalMetadataConsumer) diff --git a/test/k4FWCoreTest/src/components/ExampleFunctionalMetadataProducer.cpp b/test/k4FWCoreTest/src/components/ExampleFunctionalMetadataProducer.cpp new file mode 100644 index 00000000..d368f8fa --- /dev/null +++ b/test/k4FWCoreTest/src/components/ExampleFunctionalMetadataProducer.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2014-2024 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 "edm4hep/MCParticleCollection.h" + +#include "k4FWCore/MetadataUtils.h" +#include "k4FWCore/Producer.h" + +#include +#include + +struct ExampleFunctionalMetadataProducer final : k4FWCore::Producer { + ExampleFunctionalMetadataProducer(const std::string& name, ISvcLocator* svcLoc) + : Producer(name, svcLoc, {}, KeyValues("OutputCollection", {"MCParticles"})) {} + + StatusCode initialize() override { + k4FWCore::putParameter("NumberOfParticles", m_particleNum.value(), this); + k4FWCore::putParameter("ParticleTime", m_particleTime.value(), this); + k4FWCore::putParameter("PDGValues", m_PDGValues.value(), this); + k4FWCore::putParameter("MetadataString", m_metadataString.value(), this); + return StatusCode::SUCCESS; + } + + edm4hep::MCParticleCollection operator()() const override { + // Putting metadata in the main loop doesn't work + // k4FWCore::putParameter("EventMetadataInt", 5, this); + auto coll = edm4hep::MCParticleCollection(); + for (int i = 0; i < m_particleNum.value(); ++i) { + auto particle = coll.create(); + particle.setPDG(m_PDGValues.value()[i]); + particle.setTime(m_particleTime.value()); + } + return coll; + } + + StatusCode finalize() override { + k4FWCore::putParameter("FinalizeMetadataInt", 10, this); + return StatusCode::SUCCESS; + } + +private: + Gaudi::Property m_particleNum{this, "NumberOfParticles", 3, "How many particles will be produced"}; + Gaudi::Property m_particleTime{this, "ParticleTime", 1.5, "Which time will be set for the particles"}; + Gaudi::Property m_metadataString{this, "MetadataString", "hello", "Example of a string"}; + Gaudi::Property> m_PDGValues{ + this, "PDGValues", {1, 2, 3, 4}, "Values of the PDG used for the particles"}; +}; + +DECLARE_COMPONENT(ExampleFunctionalMetadataProducer)