From 4c9f0edc5e4c99465b2e9db8e5975892c4d8efd3 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Mon, 12 May 2025 20:03:02 +0200 Subject: [PATCH 1/8] Add tests for checking the collection ID --- test/k4FWCoreTest/CMakeLists.txt | 1 + .../options/TestCollectionIDAssignment.py | 48 +++++++++++++++++++ .../components/TestCollectionIDAssignment.cpp | 45 +++++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 test/k4FWCoreTest/options/TestCollectionIDAssignment.py create mode 100644 test/k4FWCoreTest/src/components/TestCollectionIDAssignment.cpp diff --git a/test/k4FWCoreTest/CMakeLists.txt b/test/k4FWCoreTest/CMakeLists.txt index d9898b50..911ec8f9 100644 --- a/test/k4FWCoreTest/CMakeLists.txt +++ b/test/k4FWCoreTest/CMakeLists.txt @@ -202,6 +202,7 @@ add_test_with_env(ReadLimitedInputsAllEventsIOSvc options/ExampleIOSvcLimitInput add_test_with_env(ParticleIDMetadataFramework options/ExampleParticleIDMetadata.py) +add_test_with_env(CollectionIDAssignment options/TestCollectionIDAssignment.py) # The following is done to make the tests work without installing the files in # the installation directory. The k4FWCore in the build directory is populated by diff --git a/test/k4FWCoreTest/options/TestCollectionIDAssignment.py b/test/k4FWCoreTest/options/TestCollectionIDAssignment.py new file mode 100644 index 00000000..10b7d063 --- /dev/null +++ b/test/k4FWCoreTest/options/TestCollectionIDAssignment.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# +# 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 a simple test checking that the collection IDs that are assigned for +# the Functional algorithms are the same as we get when adding to a frame. +# Effectively, it checks whether we hash the right thing when we assign a +# collection ID. We need this check because for Functional algorithms the +# insertion into a Frame (where the usual assignment happens) happens just +# before writing. + +from Gaudi.Configuration import VERBOSE +from Configurables import TestCollectionIDAssignment, ExampleFunctionalProducerMultiple + +from k4FWCore import ApplicationMgr +from Configurables import EventDataSvc + +producer = ExampleFunctionalProducerMultiple( + "Producer", OutputCollectionParticles2=["CrazyNamesForCrazyHashes"], OutputLevel=VERBOSE +) + + +collid_checker = TestCollectionIDAssignment( + "CollectionIDChecker", InputCollection=["CrazyNamesForCrazyHashes"], OutputLevel=VERBOSE +) + +ApplicationMgr( + TopAlg=[producer, collid_checker], + EvtSel="NONE", + EvtMax=1, + ExtSvc=[EventDataSvc()], +) diff --git a/test/k4FWCoreTest/src/components/TestCollectionIDAssignment.cpp b/test/k4FWCoreTest/src/components/TestCollectionIDAssignment.cpp new file mode 100644 index 00000000..461321cb --- /dev/null +++ b/test/k4FWCoreTest/src/components/TestCollectionIDAssignment.cpp @@ -0,0 +1,45 @@ +/* + * 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 + +#include + +#include + +#include + +struct TestCollectionIDAssignment final : k4FWCore::Consumer { + TestCollectionIDAssignment(const std::string& name, ISvcLocator* svcLoc) + : Consumer(name, svcLoc, {KeyValues("InputCollection", {"MCParticles"})}) {} + + void operator()(const edm4hep::MCParticleCollection& inputColl) const final { + podio::CollectionIDTable nameHasher; + const auto& inputName = inputLocations("InputCollection")[0]; + const auto expectedHash = nameHasher.add(inputName); + debug() << fmt::format("Expected collection id for '{}': {:0>8x}", inputName, expectedHash) << endmsg; + + if (inputColl.getID() != expectedHash) { + throw std::runtime_error( + fmt::format("Collection ID not as expected! ({:0>8x} vs {:0>8x})", inputColl.getID(), expectedHash)); + } + } +}; + +DECLARE_COMPONENT(TestCollectionIDAssignment) From dfaf86f2f7813d75fedadd013f498a08ead5bbd0 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Mon, 12 May 2025 21:32:49 +0200 Subject: [PATCH 2/8] Assign a collection id when putting collections to the TES --- k4FWCore/include/k4FWCore/FunctionalUtils.h | 22 ++++++++++++++++++--- k4FWCore/include/k4FWCore/Transformer.h | 6 +++--- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/k4FWCore/include/k4FWCore/FunctionalUtils.h b/k4FWCore/include/k4FWCore/FunctionalUtils.h index f8b38c50..26193f25 100644 --- a/k4FWCore/include/k4FWCore/FunctionalUtils.h +++ b/k4FWCore/include/k4FWCore/FunctionalUtils.h @@ -29,6 +29,7 @@ #include #include "podio/CollectionBase.h" +#include #include "k4FWCore/DataWrapper.h" @@ -208,6 +209,21 @@ namespace details { } } + inline auto getCollectionID(const std::string& name) { + podio::CollectionIDTable tbl; + return tbl.add(name); + } + + void putCollectionSetID(std::unique_ptr coll, + const DataObjectWriteHandle>& handle, auto thisClass) { + podio::CollectionIDTable tbl; + coll->setID(getCollectionID(handle.objKey())); + thisClass->verbose() << fmt::format("Assigning collection id {:0>8x} to collection '{}'", coll->getID(), + handle.objKey()) + << endmsg; + Gaudi::Functional::details::put(handle, std::move(coll)); + } + template void putVectorOutputs(std::tuple&& handles, const auto& m_outputs, auto thisClass) { if constexpr (Index < sizeof...(Handles)) { @@ -221,12 +237,12 @@ namespace details { throw GaudiException(thisClass->name(), msg, StatusCode::FAILURE); } for (auto& val : std::get(handles)) { - Gaudi::Functional::details::put(std::get(m_outputs)[i], convertToUniquePtr(std::move(val))); + putCollectionSetID(convertToUniquePtr(std::move(val)), std::get(m_outputs)[i], thisClass); i++; } } else { - Gaudi::Functional::details::put(std::get(m_outputs)[0], - convertToUniquePtr(std::move(std::get(handles)))); + putCollectionSetID(convertToUniquePtr(std::move(std::get(handles))), std::get(m_outputs)[0], + thisClass); } // Recursive call for the next index diff --git a/k4FWCore/include/k4FWCore/Transformer.h b/k4FWCore/include/k4FWCore/Transformer.h index 64056b99..5b47f0ee 100644 --- a/k4FWCore/include/k4FWCore/Transformer.h +++ b/k4FWCore/include/k4FWCore/Transformer.h @@ -116,9 +116,9 @@ namespace details { std::tuple tmp = filter_evtcontext_tt::apply(*this, ctx, this->m_inputs); putVectorOutputs<0, Out>(std::move(tmp), m_outputs, this); } else { - Gaudi::Functional::details::put( - std::get<0>(this->m_outputs)[0], - convertToUniquePtr(std::move(filter_evtcontext_tt::apply(*this, ctx, this->m_inputs)))); + putCollectionSetID( + convertToUniquePtr(std::move(filter_evtcontext_tt::apply(*this, ctx, this->m_inputs))), + std::get<0>(this->m_outputs)[0], this); } return Gaudi::Functional::FilterDecision::PASSED; } catch (GaudiException& e) { From 3867e8c18e0fbbf7628c4d0c28b256c616707b20 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Tue, 20 May 2025 14:38:52 +0200 Subject: [PATCH 3/8] Add the collection id table to the TES Either use the one read from file or place an empty one as soon as required. This makes it possible to check for collisions in id effectively the same way as done in the podio::Frame. --- k4FWCore/components/Reader.cpp | 8 +++++ k4FWCore/include/k4FWCore/FunctionalUtils.h | 35 ++++++++++++++++++--- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/k4FWCore/components/Reader.cpp b/k4FWCore/components/Reader.cpp index c69de4c2..14ba6243 100644 --- a/k4FWCore/components/Reader.cpp +++ b/k4FWCore/components/Reader.cpp @@ -25,6 +25,7 @@ #include "GaudiKernel/StatusCode.h" #include "podio/CollectionBase.h" +#include "podio/CollectionIDTable.h" #include "podio/Frame.h" #include "IIOSvc.h" @@ -136,12 +137,19 @@ class Reader final : public CollectionPusher { auto eds = eventSvc().as(); auto frame = std::move(std::get(val)); + auto idTable = frame.getCollectionIDTableForWrite(); auto tmp = new AnyDataWrapper(std::move(frame)); if (eds->registerObject("/Event" + k4FWCore::frameLocation, tmp).isFailure()) { error() << "Failed to register Frame object" << endmsg; } + // We also store the collection id table to make sure we can detect collisions + auto idTableWrapper = new AnyDataWrapper(std::move(idTable)); + if (eds->registerObject("/Event" + k4FWCore::idTableLocation, idTableWrapper).isFailure()) { + error() << "Failed to register CollectionIDTable object" << endmsg; + } + return std::make_tuple(std::get<0>(val), std::get<1>(val)); } }; diff --git a/k4FWCore/include/k4FWCore/FunctionalUtils.h b/k4FWCore/include/k4FWCore/FunctionalUtils.h index 26193f25..b9c80671 100644 --- a/k4FWCore/include/k4FWCore/FunctionalUtils.h +++ b/k4FWCore/include/k4FWCore/FunctionalUtils.h @@ -44,6 +44,7 @@ namespace k4FWCore { static const std::string frameLocation = "/_Frame"; +static const std::string idTableLocation = "/_CollectionIDTable"; namespace details { @@ -209,15 +210,39 @@ namespace details { } } - inline auto getCollectionID(const std::string& name) { - podio::CollectionIDTable tbl; - return tbl.add(name); + podio::CollectionIDTable& getTESCollectionIDTable(auto thisClass) { + auto eds = thisClass->eventSvc().template as(); + DataObject* p; + auto sc = eds->retrieveObject("/Event" + k4FWCore::idTableLocation, p); + if (!sc.isSuccess()) { + // We are not reading from file so we create one on the fly + thisClass->debug() << "Could not retrieve CollectionIDTable for assigning a collection id" << endmsg; + auto idTableWrapper = new AnyDataWrapper(podio::CollectionIDTable{}); + if (eds->registerObject("/Event" + k4FWCore::idTableLocation, idTableWrapper).isFailure()) { + thisClass->error() << "Failed to place an empty CollectionIDTable into the TES" << endmsg; + } else { + return idTableWrapper->getData(); + } + } else { + auto idTableWrapper = dynamic_cast*>(p); + if (!idTableWrapper) { + thisClass->error() << "Object at /Event" << k4FWCore::idTableLocation + << " is not the expected CollectionIDTable" << endmsg; + } + return idTableWrapper->getData(); + } + + throw std::runtime_error("Could neither retrieve an existing nor place an empty CollectionIDTable in the TES"); } void putCollectionSetID(std::unique_ptr coll, const DataObjectWriteHandle>& handle, auto thisClass) { - podio::CollectionIDTable tbl; - coll->setID(getCollectionID(handle.objKey())); + auto& idTable = getTESCollectionIDTable(thisClass); + if (idTable.present(handle.objKey())) { + thisClass->error() << "Collection with name " << handle.objKey() << " is already stored in the TES" << endmsg; + } + const auto id = idTable.add(handle.objKey()); + coll->setID(id); thisClass->verbose() << fmt::format("Assigning collection id {:0>8x} to collection '{}'", coll->getID(), handle.objKey()) << endmsg; From f0d83bf7caffb59b1166ca1202a8c968b864aff1 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Tue, 20 May 2025 16:37:30 +0200 Subject: [PATCH 4/8] Add service to retrieve collections from objects --- .../components/CollectionFromObjectSvc.cpp | 68 +++++++++++++++++++ k4FWCore/components/CollectionFromObjectSvc.h | 43 ++++++++++++ k4FWCore/include/k4FWCore/FunctionalUtils.h | 8 ++- .../k4FWCore/ICollectionFromObjectSvc.h | 43 ++++++++++++ 4 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 k4FWCore/components/CollectionFromObjectSvc.cpp create mode 100644 k4FWCore/components/CollectionFromObjectSvc.h create mode 100644 k4FWCore/include/k4FWCore/ICollectionFromObjectSvc.h diff --git a/k4FWCore/components/CollectionFromObjectSvc.cpp b/k4FWCore/components/CollectionFromObjectSvc.cpp new file mode 100644 index 00000000..e0875403 --- /dev/null +++ b/k4FWCore/components/CollectionFromObjectSvc.cpp @@ -0,0 +1,68 @@ +/* + * 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 "CollectionFromObjectSvc.h" + +#include "k4FWCore/FunctionalUtils.h" + +#include +#include + +#include +#include + +template <> +struct fmt::formatter : ostream_formatter {}; + +StatusCode CollectionFromObjectSvc::initialize() { + auto sc = Service::initialize(); + if (sc.isFailure()) { + error() << "Unable to initialize base class Serivce." << endmsg; + return sc; + } + m_dataSvc = service("EventDataSvc"); + if (!m_dataSvc) { + error() << "Unable to locate the EventDataSvc" << endmsg; + return StatusCode::FAILURE; + } + + return StatusCode::SUCCESS; +} + +const podio::CollectionBase* CollectionFromObjectSvc::getCollectionFor(const podio::ObjectID id) const { + debug() << "Trying to retrieve collection for object " << id << endmsg; + const auto& idTable = k4FWCore::details::getTESCollectionIDTable(m_dataSvc, this); + auto name = idTable.name(id.collectionID); + if (!name.has_value()) { + error() << "Could not get a collection name for object" << id << endmsg; + return nullptr; + } + + DataObject* p{nullptr}; + if (m_dataSvc->retrieveObject("/Event/" + name.value(), p).isFailure()) { + error() << "Could not get the collection '" << name.value() << "' from the Event store" << endmsg; + return nullptr; + } + const auto collWrapper = dynamic_cast>*>(p); + if (!collWrapper) { + error() << "Could not cast data object to the necessary type for collection " << name.value() << endmsg; + return nullptr; + } + + return collWrapper->getData().get(); +} diff --git a/k4FWCore/components/CollectionFromObjectSvc.h b/k4FWCore/components/CollectionFromObjectSvc.h new file mode 100644 index 00000000..6f6a3a95 --- /dev/null +++ b/k4FWCore/components/CollectionFromObjectSvc.h @@ -0,0 +1,43 @@ +/* + * 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_COLLECTIONFROMOBJECTSVC_H +#define FWCORE_COLLECTIONFROMOBJECTSVC_H + +#include +#include + +#include +#include + +#include "k4FWCore/ICollectionFromObjectSvc.h" + +class CollectionFromObjectSvc : public extends { + using extends::extends; + +public: + StatusCode initialize() override; + +protected: + const podio::CollectionBase* getCollectionFor(const podio::ObjectID id) const override; + +private: + SmartIF m_dataSvc; +}; + +#endif diff --git a/k4FWCore/include/k4FWCore/FunctionalUtils.h b/k4FWCore/include/k4FWCore/FunctionalUtils.h index b9c80671..a45c1670 100644 --- a/k4FWCore/include/k4FWCore/FunctionalUtils.h +++ b/k4FWCore/include/k4FWCore/FunctionalUtils.h @@ -210,8 +210,7 @@ namespace details { } } - podio::CollectionIDTable& getTESCollectionIDTable(auto thisClass) { - auto eds = thisClass->eventSvc().template as(); + podio::CollectionIDTable& getTESCollectionIDTable(IDataProviderSvc* eds, auto thisClass) { DataObject* p; auto sc = eds->retrieveObject("/Event" + k4FWCore::idTableLocation, p); if (!sc.isSuccess()) { @@ -235,6 +234,11 @@ namespace details { throw std::runtime_error("Could neither retrieve an existing nor place an empty CollectionIDTable in the TES"); } + podio::CollectionIDTable& getTESCollectionIDTable(auto thisClass) { + auto eds = thisClass->eventSvc().template as(); + return getTESCollectionIDTable(eds, thisClass); + } + void putCollectionSetID(std::unique_ptr coll, const DataObjectWriteHandle>& handle, auto thisClass) { auto& idTable = getTESCollectionIDTable(thisClass); diff --git a/k4FWCore/include/k4FWCore/ICollectionFromObjectSvc.h b/k4FWCore/include/k4FWCore/ICollectionFromObjectSvc.h new file mode 100644 index 00000000..a8ce882d --- /dev/null +++ b/k4FWCore/include/k4FWCore/ICollectionFromObjectSvc.h @@ -0,0 +1,43 @@ +/* + * 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_ICOLLECTIONFROMOBJECTSVC_H +#define FWCORE_ICOLLECTIONFROMOBJECTSVC_H + +#include + +#include +#include +#include + +#include + +class ICollectionFromObjectSvc : virtual public IInterface { +public: + DeclareInterfaceID(ICollectionFromObjectSvc, 1, 0); + + template + const typename O::collection_type* getCollectionFor(const O& object) const { + return dynamic_cast(getCollectionFor(object.id())); + } + +protected: + virtual const podio::CollectionBase* getCollectionFor(const podio::ObjectID) const = 0; +}; + +#endif From 5d18c981a2327c55d86c9cfa6ae37a0b9bf0b852 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Tue, 20 May 2025 16:38:03 +0200 Subject: [PATCH 5/8] Add tests for collection retrieval from objects --- .../components/CollectionFromObjectSvc.cpp | 4 +- test/k4FWCoreTest/CMakeLists.txt | 2 +- ...y => TestCollectionFromObjectRetrieval.py} | 14 ++-- .../TestCollectionFromObjectRetrieval.cpp | 64 +++++++++++++++++++ 4 files changed, 77 insertions(+), 7 deletions(-) rename test/k4FWCoreTest/options/{TestCollectionIDAssignment.py => TestCollectionFromObjectRetrieval.py} (75%) create mode 100644 test/k4FWCoreTest/src/components/TestCollectionFromObjectRetrieval.cpp diff --git a/k4FWCore/components/CollectionFromObjectSvc.cpp b/k4FWCore/components/CollectionFromObjectSvc.cpp index e0875403..1070bf43 100644 --- a/k4FWCore/components/CollectionFromObjectSvc.cpp +++ b/k4FWCore/components/CollectionFromObjectSvc.cpp @@ -49,7 +49,7 @@ const podio::CollectionBase* CollectionFromObjectSvc::getCollectionFor(const pod const auto& idTable = k4FWCore::details::getTESCollectionIDTable(m_dataSvc, this); auto name = idTable.name(id.collectionID); if (!name.has_value()) { - error() << "Could not get a collection name for object" << id << endmsg; + error() << "Could not get a collection name for object " << id << endmsg; return nullptr; } @@ -66,3 +66,5 @@ const podio::CollectionBase* CollectionFromObjectSvc::getCollectionFor(const pod return collWrapper->getData().get(); } + +DECLARE_COMPONENT(CollectionFromObjectSvc) diff --git a/test/k4FWCoreTest/CMakeLists.txt b/test/k4FWCoreTest/CMakeLists.txt index 911ec8f9..6f13454f 100644 --- a/test/k4FWCoreTest/CMakeLists.txt +++ b/test/k4FWCoreTest/CMakeLists.txt @@ -202,7 +202,7 @@ add_test_with_env(ReadLimitedInputsAllEventsIOSvc options/ExampleIOSvcLimitInput add_test_with_env(ParticleIDMetadataFramework options/ExampleParticleIDMetadata.py) -add_test_with_env(CollectionIDAssignment options/TestCollectionIDAssignment.py) +add_test_with_env(CollectionFromObjectRetrieval options/TestCollectionFromObjectRetrieval.py) # The following is done to make the tests work without installing the files in # the installation directory. The k4FWCore in the build directory is populated by diff --git a/test/k4FWCoreTest/options/TestCollectionIDAssignment.py b/test/k4FWCoreTest/options/TestCollectionFromObjectRetrieval.py similarity index 75% rename from test/k4FWCoreTest/options/TestCollectionIDAssignment.py rename to test/k4FWCoreTest/options/TestCollectionFromObjectRetrieval.py index 10b7d063..afd5fd70 100644 --- a/test/k4FWCoreTest/options/TestCollectionIDAssignment.py +++ b/test/k4FWCoreTest/options/TestCollectionFromObjectRetrieval.py @@ -26,23 +26,27 @@ # before writing. from Gaudi.Configuration import VERBOSE -from Configurables import TestCollectionIDAssignment, ExampleFunctionalProducerMultiple +from Configurables import ( + TestCollectionFromObjectRetrieval, + ExampleFunctionalProducerMultiple, + CollectionFromObjectSvc, +) from k4FWCore import ApplicationMgr from Configurables import EventDataSvc producer = ExampleFunctionalProducerMultiple( - "Producer", OutputCollectionParticles2=["CrazyNamesForCrazyHashes"], OutputLevel=VERBOSE + "Producer", OutputCollectionParticles1=["UnconventionalCollName"], OutputLevel=VERBOSE ) -collid_checker = TestCollectionIDAssignment( - "CollectionIDChecker", InputCollection=["CrazyNamesForCrazyHashes"], OutputLevel=VERBOSE +collid_checker = TestCollectionFromObjectRetrieval( + "CollectionIDChecker", InputCollection=["UnconventionalCollName"], OutputLevel=VERBOSE ) ApplicationMgr( TopAlg=[producer, collid_checker], EvtSel="NONE", EvtMax=1, - ExtSvc=[EventDataSvc()], + ExtSvc=[EventDataSvc(), CollectionFromObjectSvc("CollectionFromObjSvc")], ) diff --git a/test/k4FWCoreTest/src/components/TestCollectionFromObjectRetrieval.cpp b/test/k4FWCoreTest/src/components/TestCollectionFromObjectRetrieval.cpp new file mode 100644 index 00000000..1346901f --- /dev/null +++ b/test/k4FWCoreTest/src/components/TestCollectionFromObjectRetrieval.cpp @@ -0,0 +1,64 @@ +/* + * 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 +#include + +#include + +struct TestCollectionFromObjectRetrieval final : k4FWCore::Consumer { + TestCollectionFromObjectRetrieval(const std::string& name, ISvcLocator* svcLoc) + : Consumer(name, svcLoc, {KeyValues("InputCollection", {"MCParticles"})}) {} + + StatusCode initialize() final { + m_collFromObjSvc = service("CollectionFromObjSvc", false); + if (!m_collFromObjSvc) { + return StatusCode::FAILURE; + } + return StatusCode::SUCCESS; + } + + void operator()(const edm4hep::MCParticleCollection& inputColl) const final { + const auto mc = inputColl[0]; + debug() << "Retrieving collection for object with id " << mc.id() << endmsg; + const auto* collFromObj = m_collFromObjSvc->getCollectionFor(mc); + const auto checkMC = (*collFromObj)[0]; + if (mc != checkMC) { + throw std::runtime_error("Could not get the expected collection from the object"); + } + + auto newMCParticle = edm4hep::MutableMCParticle{}; + const auto* invalidColl = m_collFromObjSvc->getCollectionFor(newMCParticle); + if (invalidColl) { + throw std::runtime_error("Could get a collection for an object that is not in a collection"); + } + + auto newCollection = edm4hep::MCParticleCollection(); + newCollection->push_back(newMCParticle); + const auto* stillInvalidColl = m_collFromObjSvc->getCollectionFor(newMCParticle); + if (stillInvalidColl) { + throw std::runtime_error("Could get a collection for an object that is not in a collection"); + } + } + +private: + SmartIF m_collFromObjSvc; +}; + +DECLARE_COMPONENT(TestCollectionFromObjectRetrieval) From 93b054a06129a6c40d88070709f209a04c1d5d6f Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Tue, 20 May 2025 16:38:57 +0200 Subject: [PATCH 6/8] Remove no longer necessary test algorithm --- .../components/TestCollectionIDAssignment.cpp | 45 ------------------- 1 file changed, 45 deletions(-) delete mode 100644 test/k4FWCoreTest/src/components/TestCollectionIDAssignment.cpp diff --git a/test/k4FWCoreTest/src/components/TestCollectionIDAssignment.cpp b/test/k4FWCoreTest/src/components/TestCollectionIDAssignment.cpp deleted file mode 100644 index 461321cb..00000000 --- a/test/k4FWCoreTest/src/components/TestCollectionIDAssignment.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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 - -#include - -#include - -#include - -struct TestCollectionIDAssignment final : k4FWCore::Consumer { - TestCollectionIDAssignment(const std::string& name, ISvcLocator* svcLoc) - : Consumer(name, svcLoc, {KeyValues("InputCollection", {"MCParticles"})}) {} - - void operator()(const edm4hep::MCParticleCollection& inputColl) const final { - podio::CollectionIDTable nameHasher; - const auto& inputName = inputLocations("InputCollection")[0]; - const auto expectedHash = nameHasher.add(inputName); - debug() << fmt::format("Expected collection id for '{}': {:0>8x}", inputName, expectedHash) << endmsg; - - if (inputColl.getID() != expectedHash) { - throw std::runtime_error( - fmt::format("Collection ID not as expected! ({:0>8x} vs {:0>8x})", inputColl.getID(), expectedHash)); - } - } -}; - -DECLARE_COMPONENT(TestCollectionIDAssignment) From da3f6903cf1ff98d707f73572b00dccc2e61052b Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Tue, 20 May 2025 17:41:45 +0200 Subject: [PATCH 7/8] Make it possible to retrieve collection names via new svc --- k4FWCore/components/CollectionFromObjectSvc.cpp | 11 +++++++++++ k4FWCore/components/CollectionFromObjectSvc.h | 1 + k4FWCore/include/k4FWCore/ICollectionFromObjectSvc.h | 10 +++++++++- .../components/TestCollectionFromObjectRetrieval.cpp | 10 ++++++++++ 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/k4FWCore/components/CollectionFromObjectSvc.cpp b/k4FWCore/components/CollectionFromObjectSvc.cpp index 1070bf43..cd373e79 100644 --- a/k4FWCore/components/CollectionFromObjectSvc.cpp +++ b/k4FWCore/components/CollectionFromObjectSvc.cpp @@ -44,6 +44,17 @@ StatusCode CollectionFromObjectSvc::initialize() { return StatusCode::SUCCESS; } +const std::optional CollectionFromObjectSvc::getCollectionNameFor(const podio::ObjectID id) const { + debug() << "Trying to retrieve collection name for object " << id << endmsg; + const auto& idTable = k4FWCore::details::getTESCollectionIDTable(m_dataSvc, this); + auto name = idTable.name(id.collectionID); + if (!name.has_value()) { + error() << "Could not get a collection name for object " << id << endmsg; + return std::nullopt; + } + return name.value(); +} + const podio::CollectionBase* CollectionFromObjectSvc::getCollectionFor(const podio::ObjectID id) const { debug() << "Trying to retrieve collection for object " << id << endmsg; const auto& idTable = k4FWCore::details::getTESCollectionIDTable(m_dataSvc, this); diff --git a/k4FWCore/components/CollectionFromObjectSvc.h b/k4FWCore/components/CollectionFromObjectSvc.h index 6f6a3a95..46b72dd9 100644 --- a/k4FWCore/components/CollectionFromObjectSvc.h +++ b/k4FWCore/components/CollectionFromObjectSvc.h @@ -35,6 +35,7 @@ class CollectionFromObjectSvc : public extends getCollectionNameFor(const podio::ObjectID id) const override; private: SmartIF m_dataSvc; diff --git a/k4FWCore/include/k4FWCore/ICollectionFromObjectSvc.h b/k4FWCore/include/k4FWCore/ICollectionFromObjectSvc.h index a8ce882d..562faa19 100644 --- a/k4FWCore/include/k4FWCore/ICollectionFromObjectSvc.h +++ b/k4FWCore/include/k4FWCore/ICollectionFromObjectSvc.h @@ -25,7 +25,8 @@ #include #include -#include +#include +#include class ICollectionFromObjectSvc : virtual public IInterface { public: @@ -36,8 +37,15 @@ class ICollectionFromObjectSvc : virtual public IInterface { return dynamic_cast(getCollectionFor(object.id())); } + // TODO: return string_view? Some form of DataHandle? + template + const std::optional getCollectionNameFor(const O& object) const { + return getCollectionNameFor(object.id()); + } + protected: virtual const podio::CollectionBase* getCollectionFor(const podio::ObjectID) const = 0; + virtual const std::optional getCollectionNameFor(const podio::ObjectID) const = 0; }; #endif diff --git a/test/k4FWCoreTest/src/components/TestCollectionFromObjectRetrieval.cpp b/test/k4FWCoreTest/src/components/TestCollectionFromObjectRetrieval.cpp index 1346901f..31238b23 100644 --- a/test/k4FWCoreTest/src/components/TestCollectionFromObjectRetrieval.cpp +++ b/test/k4FWCoreTest/src/components/TestCollectionFromObjectRetrieval.cpp @@ -43,7 +43,17 @@ struct TestCollectionFromObjectRetrieval final : k4FWCore::ConsumergetCollectionNameFor(mc); + if (name.value() != inputLocations(0)[0]) { + throw std::runtime_error("Collection name retrieved via object of collection is not as expected"); + } + auto newMCParticle = edm4hep::MutableMCParticle{}; + const auto invalidName = m_collFromObjSvc->getCollectionNameFor(newMCParticle); + if (invalidName.has_value()) { + throw std::runtime_error("Retrieved a name for an object that is not known to the TES"); + } + const auto* invalidColl = m_collFromObjSvc->getCollectionFor(newMCParticle); if (invalidColl) { throw std::runtime_error("Could get a collection for an object that is not in a collection"); From ea2146fc9f93444867002e0d3601fd9bfdbd4ac8 Mon Sep 17 00:00:00 2001 From: Thomas Madlener Date: Wed, 21 May 2025 20:27:07 +0200 Subject: [PATCH 8/8] Always add the new service to the ApplicationMgr --- python/k4FWCore/ApplicationMgr.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/python/k4FWCore/ApplicationMgr.py b/python/k4FWCore/ApplicationMgr.py index e91460dd..a82472cb 100644 --- a/python/k4FWCore/ApplicationMgr.py +++ b/python/k4FWCore/ApplicationMgr.py @@ -20,7 +20,14 @@ from Gaudi import Configuration from Configurables import ApplicationMgr as AppMgr -from Configurables import Reader, Writer, IOSvc, Gaudi__Sequencer, EventLoopMgr +from Configurables import ( + Reader, + Writer, + IOSvc, + Gaudi__Sequencer, + EventLoopMgr, + CollectionFromObjectSvc, +) from k4FWCore.utils import get_logger logger = get_logger() @@ -118,6 +125,9 @@ def fix_properties(self): if "MetadataSvc" in self._mgr.allConfigurables: self._mgr.ExtSvc.append(self._mgr.allConfigurables["MetadataSvc"]) + # Always attach the CollectionFromObjectSvc + self._mgr.ExtSvc.append(CollectionFromObjectSvc("CollectionFromObjectSvc")) + if "IOSvc" not in self._mgr.allConfigurables: return if not isinstance(self._mgr.allConfigurables["IOSvc"], IOSvc):