diff --git a/.github/workflows/key4hep-build.yaml b/.github/workflows/key4hep-build.yaml index ef68d46..51158a3 100644 --- a/.github/workflows/key4hep-build.yaml +++ b/.github/workflows/key4hep-build.yaml @@ -20,7 +20,7 @@ jobs: build: strategy: matrix: - build_type: ["release", "nightly"] + build_type: ["nightly"] image: ["alma9"] stack: ["key4hep"] include: diff --git a/CMakeLists.txt b/CMakeLists.txt index 5788bf8..9d2eccf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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=$<$:$/../include>:$<$:$/../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}:$<$:$>:$<$:$>:$<$:$>:$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) diff --git a/RecoMCTruthLinkers/CMakeLists.txt b/RecoMCTruthLinkers/CMakeLists.txt index 790901a..76ff16b 100644 --- a/RecoMCTruthLinkers/CMakeLists.txt +++ b/RecoMCTruthLinkers/CMakeLists.txt @@ -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 + $ + $ + ${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}:$:$:$:$:$:$ENV{LD_LIBRARY_PATH} - PYTHONPATH=${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}/genConfDir:$/../python:$ENV{PYTHONPATH} - PATH=$/../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}) diff --git a/RecoMCTruthLinkers/components/CaloClusterMCParticleLinker.cpp b/RecoMCTruthLinkers/components/CaloClusterMCParticleLinker.cpp index 731f729..5f429b1 100644 --- a/RecoMCTruthLinkers/components/CaloClusterMCParticleLinker.cpp +++ b/RecoMCTruthLinkers/components/CaloClusterMCParticleLinker.cpp @@ -1,58 +1,89 @@ +/* + * 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 -#include #include +#include +#include /** - * 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>( 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> operator()(const edm4hep::MCParticleCollection& MCParticles, const edm4hep::ClusterCollection& clusters) const override { + std::tuple> + operator()(const edm4hep::MCParticleCollection& MCParticles, + const edm4hep::ClusterCollection& clusters) const override { // create the collection we will return edm4hep::ClusterMCParticleLinkCollection clusterMCParticleLinkCollection; podio::UserDataCollection 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::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; @@ -60,7 +91,7 @@ struct CaloClusterMCParticleLinker final } 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); @@ -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 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 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 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 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) diff --git a/RecoMCTruthLinkers/options/runCaloClusterMCParticleLinker.py b/RecoMCTruthLinkers/test/runCaloClusterMCParticleLinker.py similarity index 70% rename from RecoMCTruthLinkers/options/runCaloClusterMCParticleLinker.py rename to RecoMCTruthLinkers/test/runCaloClusterMCParticleLinker.py index 1b9bbae..3d0e1a7 100644 --- a/RecoMCTruthLinkers/options/runCaloClusterMCParticleLinker.py +++ b/RecoMCTruthLinkers/test/runCaloClusterMCParticleLinker.py @@ -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