Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make sure Functional algorithms check types during conversion #275

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
30 changes: 26 additions & 4 deletions k4FWCore/include/k4FWCore/FunctionalUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#ifndef FWCORE_FUNCTIONALUTILS_H
#define FWCORE_FUNCTIONALUTILS_H

#include <GaudiKernel/GaudiException.h>
#include "Gaudi/Functional/details.h"
#include "GaudiKernel/AnyDataWrapper.h"
#include "GaudiKernel/DataObjID.h"
Expand All @@ -33,6 +34,8 @@

// #include "GaudiKernel/CommonMessaging.h"

#include <fmt/format.h>

#include <memory>
#include <tuple>
#include <type_traits>
Expand Down Expand Up @@ -133,16 +136,35 @@ namespace k4FWCore {
using EDM4hepType = std::remove_cv_t<std::remove_pointer_t<typename TupleType::value_type>>;
auto inputMap = std::vector<const EDM4hepType*>();
for (auto& handle : std::get<Index>(handles)) {
podio::CollectionBase* in = handle.get()->get();
inputMap.push_back(static_cast<const EDM4hepType*>(in));
podio::CollectionBase* in = handle.get()->get();
auto* typedIn = dynamic_cast<const EDM4hepType*>(in);
if (typedIn) {
inputMap.push_back(typedIn);
} else {
throw GaudiException(
thisClass->name(),
fmt::format("Failed to cast collection {} to the required type {}, the type of the collection is {}",
handle.objKey(), typeid(EDM4hepType).name(), in ? in->getTypeName() : "[undetermined]"),
jmcarcell marked this conversation as resolved.
Show resolved Hide resolved
StatusCode::FAILURE);
}
}
std::get<Index>(inputTuple) = std::move(inputMap);
} else {
// Bare EDM4hep type, without pointers or const
using EDM4hepType = std::remove_cv_t<std::remove_pointer_t<TupleType>>;
try {
podio::CollectionBase* in = std::get<Index>(handles)[0].get()->get();
std::get<Index>(inputTuple) = static_cast<EDM4hepType*>(in);
podio::CollectionBase* in = std::get<Index>(handles)[0].get()->get();
auto* typedIn = dynamic_cast<EDM4hepType*>(in);
if (typedIn) {
std::get<Index>(inputTuple) = typedIn;
} else {
throw GaudiException(
thisClass->name(),
fmt::format("Failed to cast collection {} to the required type {}, the type of the collection is {}",
std::get<Index>(handles)[0].objKey(), EDM4hepType::typeName,
in ? in->getTypeName() : "[undetermined]"),
StatusCode::FAILURE);
}
} catch (GaudiException& e) {
// When the type of the collection is different from the one requested, this can happen because
// 1. a mistake was made in the input types of a functional algorithm
Expand Down
4 changes: 3 additions & 1 deletion test/k4FWCoreTest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -207,9 +207,11 @@ add_custom_command(TARGET k4FWCoreTestPlugins POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${PROJECT_SOURCE_DIR}/python/k4FWCore/ ${PROJECT_BINARY_DIR}/k4FWCore/genConfDir/k4FWCore)


add_executable(check_ParticleIDOutputs src/check_ParticleIDOutputs.cpp)
target_link_libraries(check_ParticleIDOutputs PRIVATE podio::podioIO EDM4HEP::edm4hep EDM4HEP::utils fmt::fmt)
add_test(NAME check_ParticleIDOutputs COMMAND check_ParticleIDOutputs example_with_particleids.root)
set_test_env(check_ParticleIDOutputs)
set_tests_properties(check_ParticleIDOutputs PROPERTIES DEPENDS ParticleIDMetadataFramework)
add_test_with_env(TypeMisMatchDemo options/TypeMisMatchDemo.py PROPERTIES PASS_REGULAR_EXPRESSION "Failed to cast collection MCParticles to the required type")

add_test_with_env(TypeMisMatchDemoMultiple options/TypeMisMatchDemoMultiple.py PROPERTIES PASS_REGULAR_EXPRESSION "Failed to cast collection MCParticles1 to the required type")
36 changes: 36 additions & 0 deletions test/k4FWCoreTest/options/TypeMisMatchDemo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/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.
#

from Gaudi.Configuration import INFO, DEBUG
from Configurables import ExampleFunctionalProducer, TypeMisMatchDemo, EventDataSvc

from k4FWCore import ApplicationMgr

producer = ExampleFunctionalProducer("Producer", OutputCollection=["MCParticles"])

mismatch = TypeMisMatchDemo(InputCollection=["MCParticles"], OutputLevel=DEBUG)

ApplicationMgr(
TopAlg=[producer, mismatch],
EvtSel="NONE",
EvtMax=1,
ExtSvc=[EventDataSvc("EventDataSvc")],
OutputLevel=INFO,
)
36 changes: 36 additions & 0 deletions test/k4FWCoreTest/options/TypeMisMatchDemoMultiple.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/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.
#

from Gaudi.Configuration import INFO, DEBUG
from Configurables import ExampleFunctionalProducerMultiple, TypeMisMatchDemoMultiple, EventDataSvc

from k4FWCore import ApplicationMgr

producer = ExampleFunctionalProducerMultiple("Producer")

mismatch = TypeMisMatchDemoMultiple(InputCollections=["Tracks", "MCParticles1"], OutputLevel=DEBUG)

ApplicationMgr(
TopAlg=[producer, mismatch],
EvtSel="NONE",
EvtMax=1,
ExtSvc=[EventDataSvc("EventDataSvc")],
OutputLevel=INFO,
)
14 changes: 12 additions & 2 deletions test/k4FWCoreTest/scripts/CheckOutputFiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,18 @@ def check_metadata(filename, expected_metadata):
podio_reader = podio.root_io.Reader("functional_merged_collections.root")
frames = podio_reader.get("events")
ev = frames[0]
if len(ev.get("NewMCParticles")) != 4:
raise RuntimeError(f"Expected 4 NewMCParticles but got {len(ev.get('NewMCParticles'))}")
new_mcs = ev.get("NewMCParticles")
merged_mc_colls = [ev.get(f"MCParticles{i}") for i in range(1, 4)]
merged_mcs = [mcc[i] for mcc in merged_mc_colls for i in range(len(mcc))]
if len(new_mcs) != len(merged_mcs):
raise RuntimeError(f"Expected {len(merged_mcs)} NewMCParticles but got {len(new_mcs)}")

for new_mc, orig_mc in zip(new_mcs, merged_mcs):
if new_mc.id() != orig_mc.id():
raise RuntimeError(
f"merged mcs do not match, expected [{new_mc.id().collectionID}, {new_mc.id().index}], actual [{orig_mc.id().collectionID}, {orig_mc.id().index}]"
)


check_collections("functional_metadata.root", ["MCParticles"])

Expand Down
64 changes: 64 additions & 0 deletions test/k4FWCoreTest/src/components/TypeMisMatchDemo.cpp
Original file line number Diff line number Diff line change
@@ -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 "edm4hep/MCParticleCollection.h"
#include "edm4hep/TrackCollection.h"

#include "k4FWCore/Transformer.h"

#include <Gaudi/Property.h>
#include <GaudiKernel/ISvcLocator.h>

#include <string>

struct TypeMisMatchDemo final : k4FWCore::Transformer<edm4hep::MCParticleCollection(const edm4hep::TrackCollection&)> {
TypeMisMatchDemo(const std::string& name, ISvcLocator* svcLoc)
: Transformer(name, svcLoc, {KeyValues("InputCollection", {"MCParticles"})},
{KeyValues("OutputCollection", {"MCParticles2"})}) {}

edm4hep::MCParticleCollection operator()(const edm4hep::TrackCollection& inputs) const final {
debug() << inputs.size() << " type = " << inputs.getTypeName() << endmsg;
auto track = inputs[0];
// The next line goes boom
debug() << track.getTrackerHits().size() << endmsg;

return edm4hep::MCParticleCollection{};
}
};

DECLARE_COMPONENT(TypeMisMatchDemo)

struct TypeMisMatchDemoMultiple final
: k4FWCore::Transformer<edm4hep::MCParticleCollection(const std::vector<const edm4hep::TrackCollection*>&)> {
TypeMisMatchDemoMultiple(const std::string& name, ISvcLocator* svcLoc)
: Transformer(name, svcLoc, {KeyValues("InputCollections", {"MCParticles1", "MCParticles2"})},
{KeyValues("OutputCollection", {"OutputMCParticles"})}) {}

edm4hep::MCParticleCollection operator()(const std::vector<const edm4hep::TrackCollection*>& input) const final {
for (const auto& trackColl : input) {
debug() << "collection size: " << trackColl->size() << " type = " << trackColl->getTypeName() << endmsg;
auto track = (*trackColl)[0];
// The next line goes boom
debug() << track.getTrackerHits().size() << endmsg;
}

return edm4hep::MCParticleCollection{};
}
};

DECLARE_COMPONENT(TypeMisMatchDemoMultiple)
Loading