diff --git a/src/algorithms/CMakeLists.txt b/src/algorithms/CMakeLists.txt index 3cfcf8d530..5a81cc9be1 100644 --- a/src/algorithms/CMakeLists.txt +++ b/src/algorithms/CMakeLists.txt @@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.16) add_subdirectory(interfaces) add_subdirectory(calorimetry) add_subdirectory(tracking) +add_subdirectory(particle) add_subdirectory(pid) add_subdirectory(pid_lut) add_subdirectory(digi) diff --git a/src/algorithms/particle/CMakeLists.txt b/src/algorithms/particle/CMakeLists.txt new file mode 100644 index 0000000000..9075ac6dac --- /dev/null +++ b/src/algorithms/particle/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.16) + +set(PLUGIN_NAME "algorithms_particle") + +# Function creates ${PLUGIN_NAME}_plugin and ${PLUGIN_NAME}_library targets +# Setting default includes, libraries and installation paths +plugin_add(${PLUGIN_NAME} WITH_SHARED_LIBRARY WITHOUT_PLUGIN) + +# The macro grabs sources as *.cc *.cpp *.c and headers as *.h *.hh *.hpp Then +# correctly sets sources for ${_name}_plugin and ${_name}_library targets Adds +# headers to the correct installation directory +plugin_glob_all(${PLUGIN_NAME}) + +# Find dependencies +plugin_add_algorithms(${PLUGIN_NAME}) +plugin_add_dd4hep(${PLUGIN_NAME}) +plugin_add_event_model(${PLUGIN_NAME}) +plugin_add_eigen3(${PLUGIN_NAME}) diff --git a/src/algorithms/particle/ChargedCandidateMaker.cc b/src/algorithms/particle/ChargedCandidateMaker.cc new file mode 100644 index 0000000000..3d539b1395 --- /dev/null +++ b/src/algorithms/particle/ChargedCandidateMaker.cc @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (C) 2025 Derek Anderson + +#include +#include +#include +#include + +#include "ChargedCandidateMaker.h" + +namespace eicrecon { + +// ---------------------------------------------------------------------------- +//! Process inputs +// ---------------------------------------------------------------------------- +/*! Construct a candidate charged particle via the + * following algorithm. + * 1. Build map of tracks onto vectors of their + * matched clusters + * 2. For each track, create a Reconstructed + * Particle with track and cluster relations + * filled + */ +void ChargedCandidateMaker::process(const ChargedCandidateMaker::Input& input, + const ChargedCandidateMaker::Output& output) const { + + // grab inputs/outputs + const auto [in_match] = input; + auto [out_particle] = output; + + // exit if no matches in collection + if (in_match->size() == 0) { + debug("No track-cluster matches in collection"); + return; + } + + MapToVecClust mapTrkToClust; + for (const auto& match : *in_match) { + mapTrkToClust[match.getTrack()].push_back(match.getCluster()); + } + + for (const auto& [track, clusters] : mapTrkToClust) { + edm4eic::MutableReconstructedParticle particle = out_particle->create(); + particle.addToTracks(track); + for (const edm4eic::Cluster& cluster : clusters) { + particle.addToClusters(cluster); + } + } +} // end 'process(Input&, Output&)' +} // namespace eicrecon diff --git a/src/algorithms/particle/ChargedCandidateMaker.h b/src/algorithms/particle/ChargedCandidateMaker.h new file mode 100644 index 0000000000..26cdaaa33b --- /dev/null +++ b/src/algorithms/particle/ChargedCandidateMaker.h @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (C) 2025 Derek Anderson + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "algorithms/interfaces/WithPodConfig.h" + +namespace eicrecon { + +// -------------------------------------------------------------------------- +//! Algorithm input/output +// -------------------------------------------------------------------------- +using ChargedCandidateMakerAlgorithm = + algorithms::Algorithm, + algorithms::Output>; + +// ========================================================================== +//! Candidate Charged Particle Maker +// ========================================================================== +/*! An algorithm which takes a collection of track-cluster matches + * and converts them into charged-particle candidates, one for + * each track. + */ +class ChargedCandidateMaker : public ChargedCandidateMakerAlgorithm, + public WithPodConfig { + +public: + // ------------------------------------------------------------------------ + //! Comparator struct for tracks + // ------------------------------------------------------------------------ + /*! Organizes tracks by their ObjectIDs in decreasing collection + * ID first, and by decreasing index second. + */ + struct CompareTrack { + bool operator()(const edm4eic::Track& lhs, const edm4eic::Track& rhs) const { + if (lhs.getObjectID().collectionID == rhs.getObjectID().collectionID) { + return (lhs.getObjectID().index < rhs.getObjectID().index); + } else { + return (lhs.getObjectID().collectionID < rhs.getObjectID().collectionID); + } + } + }; + + ///! Alias for a map from a track to matched clusters + using MapToVecClust = std::map, CompareTrack>; + + ///! Algorithm constructor + ChargedCandidateMaker(std::string_view name) + : ChargedCandidateMakerAlgorithm{name, + {"inputTrackClusterMatches"}, + {"outputChargedCandidateParticles"}, + "Forms candidate charged particles."} {} + + // public method + void process(const Input&, const Output&) const final; + +}; // end ChargedCandidateMaker + +} // namespace eicrecon diff --git a/src/factories/CMakeLists.txt b/src/factories/CMakeLists.txt index 950ffe52bd..b562ad7bef 100644 --- a/src/factories/CMakeLists.txt +++ b/src/factories/CMakeLists.txt @@ -2,6 +2,7 @@ add_subdirectory(calorimetry) add_subdirectory(digi) add_subdirectory(fardetectors) add_subdirectory(meta) +add_subdirectory(particle) add_subdirectory(pid) add_subdirectory(pid_lut) add_subdirectory(reco) diff --git a/src/factories/particle/CMakeLists.txt b/src/factories/particle/CMakeLists.txt new file mode 100644 index 0000000000..591839f6df --- /dev/null +++ b/src/factories/particle/CMakeLists.txt @@ -0,0 +1,2 @@ +set(PLUGIN_NAME "factories_particle") +plugin_headers_only(${PLUGIN_NAME}) diff --git a/src/factories/particle/ChargedCandidateMaker_factory.h b/src/factories/particle/ChargedCandidateMaker_factory.h new file mode 100644 index 0000000000..8dfa8c6d47 --- /dev/null +++ b/src/factories/particle/ChargedCandidateMaker_factory.h @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (C) 2025 Derek Anderson + +#pragma once + +#include + +#include "extensions/jana/JOmniFactory.h" +#include "algorithms/particle/ChargedCandidateMaker.h" + +namespace eicrecon { + +class ChargedCandidateMaker_factory : public JOmniFactory { + +public: + ///! alias for algorithm name + using AlgoT = eicrecon::ChargedCandidateMaker; + +private: + // pointer to algorithm + std::unique_ptr m_algo; + + // input collection + PodioInput m_track_cluster_match_input{this}; + + // output collection + PodioOutput m_charged_candidate_output{this}; + +public: + ///! Configures algorithm + void Configure() { + m_algo = std::make_unique(GetPrefix()); + m_algo->level(static_cast(logger()->level())); + m_algo->applyConfig(config()); + m_algo->init(); + } + + ///! Primary algorithm call + void Process(int32_t /*run_number*/, uint64_t /*event_number*/) { + m_algo->process({m_track_cluster_match_input()}, {m_charged_candidate_output().get()}); + } +}; // end ChargedCandidateMaker_factory + +} // namespace eicrecon diff --git a/src/global/CMakeLists.txt b/src/global/CMakeLists.txt index 4d12e01fc8..bbff676663 100644 --- a/src/global/CMakeLists.txt +++ b/src/global/CMakeLists.txt @@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.16) add_subdirectory(tracking) add_subdirectory(reco) +add_subdirectory(particle) add_subdirectory(pid) add_subdirectory(pid_lut) add_subdirectory(beam) diff --git a/src/global/particle/CMakeLists.txt b/src/global/particle/CMakeLists.txt new file mode 100644 index 0000000000..fe74cf15d4 --- /dev/null +++ b/src/global/particle/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.16) + +# Automatically set plugin name the same as the directory name Don't forget +# string(REPLACE " " "_" PLUGIN_NAME ${PLUGIN_NAME}) if this dir has spaces in +# its name +get_filename_component(PLUGIN_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) + +# Function creates ${PLUGIN_NAME}_plugin and ${PLUGIN_NAME}_library targets +# Setting default includes, libraries and installation paths +plugin_add(${PLUGIN_NAME} PLUGIN_USE_CC_ONLY) + +# The macro grabs sources as *.cc *.cpp *.c and headers as *.h *.hh *.hpp Then +# correctly sets sources for ${_name}_plugin and ${_name}_library targets Adds +# headers to the correct installation directory +plugin_glob_all(${PLUGIN_NAME}) + +# Find dependencies +plugin_add_dd4hep(${PLUGIN_NAME}) +plugin_add_event_model(${PLUGIN_NAME}) + +# Add include directories (works same as target_include_directories) +# plugin_include_directories(${PLUGIN_NAME} SYSTEM PUBLIC ...) + +# Add libraries (works same as target_include_directories) +plugin_link_libraries(${PLUGIN_NAME} algorithms_particle_library) diff --git a/src/global/particle/particle.cc b/src/global/particle/particle.cc new file mode 100644 index 0000000000..47f392a439 --- /dev/null +++ b/src/global/particle/particle.cc @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (C) 2025 Derek Anderson + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "extensions/jana/JOmniFactoryGeneratorT.h" +#include "factories/meta/CollectionCollector_factory.h" +#include "factories/particle/ChargedCandidateMaker_factory.h" + +extern "C" { + +void InitPlugin(JApplication* app) { + + using namespace eicrecon; + + InitJANAPlugin(app); + + // ==================================================================== + // PFAlpha: baseline PF implementation + // ==================================================================== + + // -------------------------------------------------------------------- + // PFA (1b) arbitration: form charged candidates + // -------------------------------------------------------------------- + + // backward ----------------------------------------------------------- + + app->Add( + new JOmniFactoryGeneratorT>( + "EndcapNTrackClusterMatches", + {"EcalEndcapNTrackClusterMatches", "HcalEndcapNTrackClusterMatches"}, + {"EndcapNTrackClusterMatches"}, app)); + + app->Add(new JOmniFactoryGeneratorT( + "EndcapNChargedCandidateParticlesAlpha", {"EndcapNTrackClusterMatches"}, + {"EndcapNChargedCandidateParticlesAlpha"}, {}, app)); + + // central ------------------------------------------------------------ + + app->Add( + new JOmniFactoryGeneratorT>( + "BarrelTrackClusterMatches", + {"EcalBarrelTrackClusterMatches", "HcalBarrelTrackClusterMatches"}, + {"BarrelTrackClusterMatches"}, app)); + + app->Add(new JOmniFactoryGeneratorT( + "BarrelChargedCandidateParticlesAlpha", {"BarrelTrackClusterMatches"}, + {"BarrelChargedCandidateParticlesAlpha"}, {}, app)); + + // forward ------------------------------------------------------------ + + app->Add( + new JOmniFactoryGeneratorT>( + "EndcapPTrackClusterMatches", + {"EcalEndcapPTrackClusterMatches", "LFHCALTrackClusterMatches"}, + {"EndcapPTrackClusterMatches"}, app)); + + app->Add(new JOmniFactoryGeneratorT( + "EndcapPChargedCandidateParticlesAlpha", {"EndcapPTrackClusterMatches"}, + {"EndcapPChargedCandidateParticlesAlpha"}, {}, app)); + + app->Add( + new JOmniFactoryGeneratorT>( + "EndcapPInsertTrackClusterMatches", + {"EcalEndcapPTrackClusterMatches", "HcalEndcapPInsertTrackClusterMatches"}, + {"EndcapPInsertTrackClusterMatches"}, app)); + + app->Add(new JOmniFactoryGeneratorT( + "EndcapPInsertChargedCandidateParticlesAlpha", {"EndcapPInsertTrackClusterMatches"}, + {"EndcapPInsertChargedCandidateParticlesAlpha"}, {}, app)); +} +} // extern "C" diff --git a/src/services/io/podio/JEventProcessorPODIO.cc b/src/services/io/podio/JEventProcessorPODIO.cc index 3e36aeb0f9..7a197d63a3 100644 --- a/src/services/io/podio/JEventProcessorPODIO.cc +++ b/src/services/io/podio/JEventProcessorPODIO.cc @@ -394,6 +394,11 @@ JEventProcessorPODIO::JEventProcessorPODIO() { "EcalEndcapNTrackClusterMatches", "HcalEndcapNTrackClusterMatches", + // particle flow + "EndcapNChargedCandidateParticlesAlpha", + "BarrelChargedCandidateParticlesAlpha", + "EndcapPChargedCandidateParticlesAlpha", + "EndcapPInsertChargedCandidateParticlesAlpha", }; std::vector output_exclude_collections; // need to get as vector, then convert to set std::string output_include_collections = "DEPRECATED"; diff --git a/src/utilities/eicrecon/eicrecon.cc b/src/utilities/eicrecon/eicrecon.cc index 8e389203b0..8e78f722ca 100644 --- a/src/utilities/eicrecon/eicrecon.cc +++ b/src/utilities/eicrecon/eicrecon.cc @@ -25,6 +25,7 @@ std::vector EICRECON_DEFAULT_PLUGINS = { "beam", "reco", "tracking", + "particle", "pid", "global_pid_lut", "EEMC",