Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/key4hep-build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
build:
strategy:
matrix:
build_type: ["release", "nightly"]
build_type: ["nightly"]
image: ["alma9"]
stack: ["key4hep"]
include:
Expand Down
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ include(cmake/Key4hepConfig.cmake)
include(GNUInstallDirs)

include(CTest)
function(set_test_env _testname)
set_property(TEST ${test_name} APPEND PROPERTY ENVIRONMENT "ROOT_INCLUDE_PATH=$<$<TARGET_EXISTS:podio::podio>:$<TARGET_FILE_DIR:podio::podio>/../include>:$<$<TARGET_EXISTS:EDM4HEP::edm4hep>:$<TARGET_FILE_DIR:EDM4HEP::edm4hep>/../include>:$ENV{ROOT_INCLUDE_PATH}")
set_property(TEST ${test_name} APPEND PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=${PROJECT_BINARY_DIR}:${PROJECT_BINARY_DIR}/${CMAKE_PROJECT_NAME}:${PROJECT_BINARY_DIR}/genConfDir/${PackageName}:$<$<TARGET_EXISTS:ROOT::Core>:$<TARGET_FILE_DIR:ROOT::Core>>:$<$<TARGET_EXISTS:EDM4HEP::edm4hep>:$<TARGET_FILE_DIR:EDM4HEP::edm4hep>>:$<$<TARGET_EXISTS:podio::podio>:$<TARGET_FILE_DIR:podio::podio>>:$ENV{LD_LIBRARY_PATH}")
set_property(TEST ${test_name} APPEND PROPERTY ENVIRONMENT "PYTHONPATH=${CMAKE_BINARY_DIR}:${PROJECT_BINARY_DIR}/genConfDir:$ENV{PYTHONPATH}")
endfunction()

add_subdirectory(RecoMCTruthLinkers)

Expand Down
66 changes: 41 additions & 25 deletions RecoMCTruthLinkers/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,36 +16,52 @@ 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.
]]
set(PackageName RecoMCTruthLinkers)

file(GLOB _plugin_sources components/*.cpp)
gaudi_add_module(k4PerformancePlugins
SOURCES ${_plugin_sources}
LINK Gaudi::GaudiKernel
k4FWCore::k4FWCore
EDM4HEP::edm4hep
)
project(${PackageName})

install(TARGETS k4PerformancePlugins
file(GLOB sources
${PROJECT_SOURCE_DIR}/src/*.cpp
${PROJECT_SOURCE_DIR}/components/*.cpp
)

file(GLOB headers
${PROJECT_SOURCE_DIR}/include/*.h
)

gaudi_add_module(${PackageName}
SOURCES ${sources}
LINK
Gaudi::GaudiKernel
EDM4HEP::edm4hep
k4FWCore::k4FWCore
)

target_include_directories(${PackageName} PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
${MarlinUtil_INCLUDE_DIRS}
${DELPHES_INCLUDE_DIRS}
)

set_target_properties(${PackageName} PROPERTIES PUBLIC_HEADER "${headers}")

file(GLOB scripts
${PROJECT_SOURCE_DIR}/test/*.py
)

file(COPY ${scripts} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/test)

install(TARGETS ${PackageName}
EXPORT ${CMAKE_PROJECT_NAME}Targets
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT bin
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT shlib
PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/@{CMAKE_PROJECT_NAME}"
COMPONENT dev)

include(CTest)

function(set_test_env _testname)
set_property(TEST ${_testname} APPEND PROPERTY ENVIRONMENT
LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}:$<TARGET_FILE_DIR:k4PerformancePlugins>:$<TARGET_FILE_DIR:ROOT::Core>:$<TARGET_FILE_DIR:k4FWCore::k4FWCore>:$<TARGET_FILE_DIR:EDM4HEP::edm4hep>:$<TARGET_FILE_DIR:podio::podio>:$ENV{LD_LIBRARY_PATH}
PYTHONPATH=${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}/genConfDir:$<TARGET_FILE_DIR:k4FWCore::k4FWCore>/../python:$ENV{PYTHONPATH}
PATH=$<TARGET_FILE_DIR:k4FWCore::k4FWCore>/../bin:$ENV{PATH}
K4PROJECTTEMPLATE=${CMAKE_CURRENT_LIST_DIR}/
)
endfunction()
PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${CMAKE_PROJECT_NAME}" COMPONENT dev
)

add_test(NAME runCaloClusterMCParticleLinker
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
COMMAND k4run RecoMCTruthLinkers/options/runCaloClusterMCParticleLinker.py)
set_test_env(runCaloClusterMCParticleLinker)
install(FILES ${scripts} DESTINATION test)

SET(test_name "test_runCaloClusterMCParticleLinker")
ADD_TEST(NAME ${test_name} COMMAND k4run test/runCaloClusterMCParticleLinker.py)
set_test_env(${test_name})

97 changes: 62 additions & 35 deletions RecoMCTruthLinkers/components/CaloClusterMCParticleLinker.cpp
Original file line number Diff line number Diff line change
@@ -1,66 +1,97 @@
/*
* Copyright (c) 2020-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 "edm4hep/ClusterCollection.h"
#include "edm4hep/ClusterMCParticleLinkCollection.h"
#include "edm4hep/MCParticleCollection.h"
#include "podio/UserDataCollection.h"

#include "k4FWCore/Transformer.h"

#include <string>
#include <limits>
#include <cmath>
#include <limits>
#include <string>

/**
* Gaudi functional doing a dummy association between calo clusters and MCParticles based on an angular matching, just here to provide a template code to develop other real applications
* Gaudi functional doing a dummy association between calo clusters and MCParticles based on an angular matching, just
* here to provide a template code to develop other real applications
*/

struct CaloClusterMCParticleLinker final
: k4FWCore::MultiTransformer<std::tuple<edm4hep::ClusterMCParticleLinkCollection, podio::UserDataCollection<float>>(
const edm4hep::MCParticleCollection&, const edm4hep::ClusterCollection&)> {
CaloClusterMCParticleLinker(const std::string& name, ISvcLocator* svcLoc)
: MultiTransformer(name, svcLoc,
{KeyValues("InputMCParticles", {"MCParticles"}),
KeyValues("InputCaloClusters", {"CaloClusters"})},
{KeyValues("OutputCaloClusterMCParticleLink", {"CaloClusterMCParticleLink"}),
KeyValues("OutputErecMinusEgenOverEgen", {"ErecMinusEgenOverEgen"})}) {}
: MultiTransformer(
name, svcLoc,
{KeyValues("InputMCParticles", {"MCParticles"}), KeyValues("InputCaloClusters", {"CaloClusters"})},
{KeyValues("OutputCaloClusterMCParticleLink", {"CaloClusterMCParticleLink"}),
KeyValues("OutputErecMinusEgenOverEgen", {"ErecMinusEgenOverEgen"})}) {}

std::tuple<edm4hep::ClusterMCParticleLinkCollection, podio::UserDataCollection<float>> operator()(const edm4hep::MCParticleCollection& MCParticles, const edm4hep::ClusterCollection& clusters) const override {
std::tuple<edm4hep::ClusterMCParticleLinkCollection, podio::UserDataCollection<float>>
operator()(const edm4hep::MCParticleCollection& MCParticles,
const edm4hep::ClusterCollection& clusters) const override {
// create the collection we will return
edm4hep::ClusterMCParticleLinkCollection clusterMCParticleLinkCollection;
podio::UserDataCollection<float> ErecMinusEgenOverEgeni_datacoll;
for(const auto& particle : MCParticles) {
for (const auto& particle : MCParticles) {
// skip secondary particles
if (particle.getGeneratorStatus() == 0)
continue;
float smallest_angular_distance = std::numeric_limits<float>::max();
float delta_e_over_gen_e = 0;
std::size_t cluster_best_match_index = -1;
// create already the link here to enable efficiency studies, TOCHECK when no match, setFrom wont be called and the link ?gives an empty pointer for the cluster?
// create already the link here to enable efficiency studies, TOCHECK when no match, setFrom wont be called and
// the link ?gives an empty pointer for the cluster?
edm4hep::MutableClusterMCParticleLink clusterMCParticleLink;
clusterMCParticleLink.setTo(particle);
for(const auto& cluster : clusters) {
// derive the cluster/MC particle angular distance with theta = arccos(a.b / |a||b|), assuming the cluster points to the IP
// for a real implementation, we might want to use the real cluster axis and take the magnetic field into account for the matching
float mag_cluster_position = std::sqrt(cluster.getPosition()[0] * cluster.getPosition()[0] + cluster.getPosition()[1] * cluster.getPosition()[1] + cluster.getPosition()[2] * cluster.getPosition()[2]);
float mag_particle_momentum = std::sqrt(particle.getMomentum()[0] * particle.getMomentum()[0] + particle.getMomentum()[1] * particle.getMomentum()[1] + particle.getMomentum()[2] * particle.getMomentum()[2]);
float dot = cluster.getPosition()[0] * particle.getMomentum()[0] + cluster.getPosition()[1] * particle.getMomentum()[1] + cluster.getPosition()[2] * particle.getMomentum()[2];
for (const auto& cluster : clusters) {
// derive the cluster/MC particle angular distance with theta = arccos(a.b / |a||b|), assuming the cluster
// points to the IP for a real implementation, we might want to use the real cluster axis and take the magnetic
// field into account for the matching
float mag_cluster_position = std::sqrt(cluster.getPosition()[0] * cluster.getPosition()[0] +
cluster.getPosition()[1] * cluster.getPosition()[1] +
cluster.getPosition()[2] * cluster.getPosition()[2]);
float mag_particle_momentum = std::sqrt(particle.getMomentum()[0] * particle.getMomentum()[0] +
particle.getMomentum()[1] * particle.getMomentum()[1] +
particle.getMomentum()[2] * particle.getMomentum()[2]);
float dot = cluster.getPosition()[0] * particle.getMomentum()[0] +
cluster.getPosition()[1] * particle.getMomentum()[1] +
cluster.getPosition()[2] * particle.getMomentum()[2];
float cosTheta = dot / (mag_cluster_position * mag_particle_momentum);
// protection for numerical precision
if (cosTheta > 1.0) cosTheta = 1.0;
else if (cosTheta < -1.0) cosTheta = -1.0;

if (cosTheta > 1.0)
cosTheta = 1.0;
else if (cosTheta < -1.0)
cosTheta = -1.0;

// save the information if it is the best match so far
float current_angular_distance = std::acos(cosTheta);
if(current_angular_distance < smallest_angular_distance) {
if (current_angular_distance < smallest_angular_distance) {
delta_e_over_gen_e = (cluster.getEnergy() - particle.getEnergy()) / particle.getEnergy();
smallest_angular_distance = current_angular_distance;
cluster_best_match_index = cluster.getObjectID().index;
}
}
debug() << "Best match angular distance " << smallest_angular_distance << endmsg;
// fill the output entries if the matching is valid
if(smallest_angular_distance < m_cutoff_angle) {
if (smallest_angular_distance < m_cutoff_angle) {
clusterMCParticleLink.setFrom(clusters.at(cluster_best_match_index));
ErecMinusEgenOverEgeni_datacoll.push_back(delta_e_over_gen_e);
clusterMCParticleLinkCollection.push_back(clusterMCParticleLink);
Expand All @@ -69,19 +100,15 @@ struct CaloClusterMCParticleLinker final
return std::make_tuple(std::move(clusterMCParticleLinkCollection), std::move(ErecMinusEgenOverEgeni_datacoll));
}

private:

/// Configurable property to decide whether to produce validation plots
Gaudi::Property<bool> m_produceValidationDistributions{
this, "ProduceValidationDistributions", true, "Decide whether to produce validation distributions or not"
};

/// Criteria on the angle between MCparticle and cluster direction to consider the matching valid
Gaudi::Property<float> m_cutoff_angle{
this, "CutoffAngleRadian", 0.2, "Cut off on the angular distance between cluster and MC particles to be associated [rad]"
};

private:
/// Configurable property to decide whether to produce validation plots
Gaudi::Property<bool> m_produceValidationDistributions{this, "ProduceValidationDistributions", true,
"Decide whether to produce validation distributions or not"};

/// Criteria on the angle between MCparticle and cluster direction to consider the matching valid
Gaudi::Property<float> m_cutoff_angle{
this, "CutoffAngleRadian", 0.2,
"Cut off on the angular distance between cluster and MC particles to be associated [rad]"};
};

DECLARE_COMPONENT(CaloClusterMCParticleLinker)
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
#
# Copyright (c) 2020-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

import os
Expand Down