From c131e8568dd33fee207f6271328270f77b6247ae Mon Sep 17 00:00:00 2001 From: Sylvester Joosten Date: Thu, 6 Nov 2025 15:44:01 -0600 Subject: [PATCH 1/5] Stop looking for calorimeter hit primaries if we enounter geometries like radiative photons from a primary electrons --- .../calorimetry/SimCalorimeterHitProcessor.cc | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/algorithms/calorimetry/SimCalorimeterHitProcessor.cc b/src/algorithms/calorimetry/SimCalorimeterHitProcessor.cc index ca295a51b7..5a40a55ec1 100644 --- a/src/algorithms/calorimetry/SimCalorimeterHitProcessor.cc +++ b/src/algorithms/calorimetry/SimCalorimeterHitProcessor.cc @@ -70,17 +70,23 @@ template <> struct hash> { // unnamed namespace for internal utility namespace { -// Lookup primary MCParticle @TODO this should be a shared utiliy function in the edm4xxx +// Lookup primary MCParticle +// we stop looking if we find the parent has status 1 but with only 2 daughters +// so we don't merge radiative photons with the primary electron as this prevents us +// from properly linking the clusters back to the event geometry +// @TODO this should be a shared utiliy function in the edm4xxx // libraries edm4hep::MCParticle lookup_primary(const edm4hep::CaloHitContribution& contrib) { const auto contributor = contrib.getParticle(); edm4hep::MCParticle primary = contributor; while (primary.parents_size() > 0) { - if (primary.getGeneratorStatus() != 0) { + auto parent = primary.getParents(0); + if (primary.getGeneratorStatus() != 0 || + (parent.getGeneratorStatus() != 0 && parent.daughters_size() == 2)) { break; } - primary = primary.getParents(0); + primary = parent; } return primary; } From 6eda068bf0d2ef68a3eff4d353115e57cba07b28 Mon Sep 17 00:00:00 2001 From: Sylvester Joosten Date: Fri, 7 Nov 2025 15:23:20 -0600 Subject: [PATCH 2/5] Add extra condition to avoid storing first pair of early showers --- .../calorimetry/SimCalorimeterHitProcessor.cc | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/algorithms/calorimetry/SimCalorimeterHitProcessor.cc b/src/algorithms/calorimetry/SimCalorimeterHitProcessor.cc index 5a40a55ec1..9640efb0d9 100644 --- a/src/algorithms/calorimetry/SimCalorimeterHitProcessor.cc +++ b/src/algorithms/calorimetry/SimCalorimeterHitProcessor.cc @@ -73,20 +73,29 @@ namespace { // Lookup primary MCParticle // we stop looking if we find the parent has status 1 but with only 2 daughters // so we don't merge radiative photons with the primary electron as this prevents us -// from properly linking the clusters back to the event geometry +// from properly linking the clusters back to the event geometry. Note that we also +// enforce for this case that no steps back towards the primary were taken to avoid +// storing the first pair of calorimetric showers that start inside the tracking volume. +// Hence, this algorithm will return: +// - Contribution came from primary: primary +// - Contribution came from immediate daughter of primary and has no childern -> daughter +// - All other cases (i.e. early showers, multi-radiation): primary +// // @TODO this should be a shared utiliy function in the edm4xxx // libraries edm4hep::MCParticle lookup_primary(const edm4hep::CaloHitContribution& contrib) { const auto contributor = contrib.getParticle(); edm4hep::MCParticle primary = contributor; + size_t steps_taken = 0; // The number of steps taken looking for the primary while (primary.parents_size() > 0) { auto parent = primary.getParents(0); if (primary.getGeneratorStatus() != 0 || - (parent.getGeneratorStatus() != 0 && parent.daughters_size() == 2)) { + (parent.getGeneratorStatus() != 0 && parent.daughters_size() == 2 && steps_taken == 0)) { break; } primary = parent; + steps_taken += 1; } return primary; } From 5f19d596a552a6ae3989e2c372468c4c56dd36ab Mon Sep 17 00:00:00 2001 From: Sylvester Joosten Date: Mon, 24 Nov 2025 11:57:45 -0600 Subject: [PATCH 3/5] unified and deduplicated get_primary/lookup_primary --- .../calorimetry/CalorimeterClusterRecoCoG.cc | 21 +--------- .../calorimetry/CalorimeterClusterRecoCoG.h | 1 - .../calorimetry/ImagingClusterReco.cc | 21 +--------- .../calorimetry/ImagingClusterReco.h | 2 - src/algorithms/calorimetry/MCTools.h | 41 +++++++++++++++++++ .../calorimetry/SimCalorimeterHitProcessor.cc | 32 +-------------- 6 files changed, 47 insertions(+), 71 deletions(-) create mode 100644 src/algorithms/calorimetry/MCTools.h diff --git a/src/algorithms/calorimetry/CalorimeterClusterRecoCoG.cc b/src/algorithms/calorimetry/CalorimeterClusterRecoCoG.cc index 5afc5288f7..ba7d7a0ac2 100644 --- a/src/algorithms/calorimetry/CalorimeterClusterRecoCoG.cc +++ b/src/algorithms/calorimetry/CalorimeterClusterRecoCoG.cc @@ -31,6 +31,7 @@ #include "CalorimeterClusterRecoCoG.h" #include "algorithms/calorimetry/CalorimeterClusterRecoCoGConfig.h" +#include "MCTools.h" namespace eicrecon { @@ -219,7 +220,7 @@ void CalorimeterClusterRecoCoG::associate( // -------------------------------------------------------------------- // grab primary responsible for contribution & increment relevant sum // -------------------------------------------------------------------- - edm4hep::MCParticle primary = get_primary(contrib); + edm4hep::MCParticle primary = MCTools::lookup_primary(contrib); mapMCParToContrib[primary] += contrib.getEnergy(); trace("Identified primary: id = {}, pid = {}, total energy = {}, contributed = {}", @@ -251,22 +252,4 @@ void CalorimeterClusterRecoCoG::associate( } } -edm4hep::MCParticle -CalorimeterClusterRecoCoG::get_primary(const edm4hep::CaloHitContribution& contrib) { - // get contributing particle - const auto contributor = contrib.getParticle(); - - // walk back through parents to find primary - // - TODO finalize primary selection. This - // can be improved!! - edm4hep::MCParticle primary = contributor; - while (primary.parents_size() > 0) { - if (primary.getGeneratorStatus() != 0) { - break; - } - primary = primary.getParents(0); - } - return primary; -} - } // namespace eicrecon diff --git a/src/algorithms/calorimetry/CalorimeterClusterRecoCoG.h b/src/algorithms/calorimetry/CalorimeterClusterRecoCoG.h index ebadcdd685..0c8562909c 100644 --- a/src/algorithms/calorimetry/CalorimeterClusterRecoCoG.h +++ b/src/algorithms/calorimetry/CalorimeterClusterRecoCoG.h @@ -81,7 +81,6 @@ class CalorimeterClusterRecoCoG : public CalorimeterClusterRecoCoGAlgorithm, void associate(const edm4eic::Cluster& cl, const edm4eic::MCRecoCalorimeterHitAssociationCollection* mchitassociations, edm4eic::MCRecoClusterParticleAssociationCollection* assocs) const; - static edm4hep::MCParticle get_primary(const edm4hep::CaloHitContribution& contrib); }; } // namespace eicrecon diff --git a/src/algorithms/calorimetry/ImagingClusterReco.cc b/src/algorithms/calorimetry/ImagingClusterReco.cc index 3cd3379c3d..0f296e8f9f 100644 --- a/src/algorithms/calorimetry/ImagingClusterReco.cc +++ b/src/algorithms/calorimetry/ImagingClusterReco.cc @@ -30,6 +30,7 @@ #include "algorithms/calorimetry/ClusterTypes.h" #include "algorithms/calorimetry/ImagingClusterRecoConfig.h" +#include "MCTools.h" namespace eicrecon { @@ -277,7 +278,7 @@ void ImagingClusterReco::associate_mc_particles( // -------------------------------------------------------------------- // grab primary responsible for contribution & increment relevant sum // -------------------------------------------------------------------- - edm4hep::MCParticle primary = get_primary(contrib); + edm4hep::MCParticle primary = MCTools::lookup_primary(contrib); mapMCParToContrib[primary] += contrib.getEnergy(); trace("Identified primary: id = {}, pid = {}, total energy = {}, contributed = {}", @@ -309,22 +310,4 @@ void ImagingClusterReco::associate_mc_particles( } } -edm4hep::MCParticle -ImagingClusterReco::get_primary(const edm4hep::CaloHitContribution& contrib) const { - // get contributing particle - const auto contributor = contrib.getParticle(); - - // walk back through parents to find primary - // - TODO finalize primary selection. This - // can be improved!! - edm4hep::MCParticle primary = contributor; - while (primary.parents_size() > 0) { - if (primary.getGeneratorStatus() != 0) { - break; - } - primary = primary.getParents(0); - } - return primary; -} - } // namespace eicrecon diff --git a/src/algorithms/calorimetry/ImagingClusterReco.h b/src/algorithms/calorimetry/ImagingClusterReco.h index d5d0d349a4..f7d6e3cf42 100644 --- a/src/algorithms/calorimetry/ImagingClusterReco.h +++ b/src/algorithms/calorimetry/ImagingClusterReco.h @@ -76,8 +76,6 @@ class ImagingClusterReco : public ImagingClusterRecoAlgorithm, const edm4eic::Cluster& cl, const edm4eic::MCRecoCalorimeterHitAssociationCollection* mchitassociations, edm4eic::MCRecoClusterParticleAssociationCollection* assocs) const; - - edm4hep::MCParticle get_primary(const edm4hep::CaloHitContribution& contrib) const; }; } // namespace eicrecon diff --git a/src/algorithms/calorimetry/MCTools.h b/src/algorithms/calorimetry/MCTools.h new file mode 100644 index 0000000000..cbd622e637 --- /dev/null +++ b/src/algorithms/calorimetry/MCTools.h @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (C) 2025 Minho Kim, Sylvester Joosten, Derek Anderson, Wouter Deconinck +#pragma once + +// +// @TODO should be migrated to a shared utiliy function in edm4xxx +// + +#include +#include + +namespace eicrecon::MCTools { + +// Lookup primary MCParticle +// we stop looking if we find the parent has status 1 +// so we don't merge e.g. radiative photons with the primary electron as this prevents us +// from properly linking the clusters back to the event geometry. Note that we also +// enforce for this case that no steps back towards the primary were taken to avoid +// storing the first pair of calorimetric showers that start inside the tracking volume. +// Hence, this algorithm will return: +// - Contribution came from primary: primary +// - Contribution came from immediate daughter of primary and has no childern -> daughter +// - All other cases (i.e. early showers, multi-radiation): primary +// libraries +inline edm4hep::MCParticle lookup_primary(const edm4hep::CaloHitContribution& contrib) { + const auto contributor = contrib.getParticle(); + + edm4hep::MCParticle primary = contributor; + size_t steps_taken = 0; // The number of steps taken looking for the primary + while (primary.parents_size() > 0) { + auto parent = primary.getParents(0); + if (primary.getGeneratorStatus() != 0 || + (parent.getGeneratorStatus() != 0 && steps_taken == 0)) { + break; + } + primary = parent; + steps_taken += 1; + } + return primary; +} +} // namespace eicrecon::MCTools diff --git a/src/algorithms/calorimetry/SimCalorimeterHitProcessor.cc b/src/algorithms/calorimetry/SimCalorimeterHitProcessor.cc index 9640efb0d9..a26ff798de 100644 --- a/src/algorithms/calorimetry/SimCalorimeterHitProcessor.cc +++ b/src/algorithms/calorimetry/SimCalorimeterHitProcessor.cc @@ -27,6 +27,7 @@ #include #include "algorithms/calorimetry/SimCalorimeterHitProcessorConfig.h" +#include "MCTools.h" using namespace dd4hep; @@ -70,35 +71,6 @@ template <> struct hash> { // unnamed namespace for internal utility namespace { -// Lookup primary MCParticle -// we stop looking if we find the parent has status 1 but with only 2 daughters -// so we don't merge radiative photons with the primary electron as this prevents us -// from properly linking the clusters back to the event geometry. Note that we also -// enforce for this case that no steps back towards the primary were taken to avoid -// storing the first pair of calorimetric showers that start inside the tracking volume. -// Hence, this algorithm will return: -// - Contribution came from primary: primary -// - Contribution came from immediate daughter of primary and has no childern -> daughter -// - All other cases (i.e. early showers, multi-radiation): primary -// -// @TODO this should be a shared utiliy function in the edm4xxx -// libraries -edm4hep::MCParticle lookup_primary(const edm4hep::CaloHitContribution& contrib) { - const auto contributor = contrib.getParticle(); - - edm4hep::MCParticle primary = contributor; - size_t steps_taken = 0; // The number of steps taken looking for the primary - while (primary.parents_size() > 0) { - auto parent = primary.getParents(0); - if (primary.getGeneratorStatus() != 0 || - (parent.getGeneratorStatus() != 0 && parent.daughters_size() == 2 && steps_taken == 0)) { - break; - } - primary = parent; - steps_taken += 1; - } - return primary; -} class HitContributionAccumulator { private: float m_energy{0}; @@ -206,7 +178,7 @@ void SimCalorimeterHitProcessor::process(const SimCalorimeterHitProcessor::Input m_attenuationReferencePosition ? get_attenuation(ih.getPosition().z) : 1.; // Use primary particle (traced back through parents) to group contributions for (const auto& contrib : ih.getContributions()) { - edm4hep::MCParticle primary = lookup_primary(contrib); + edm4hep::MCParticle primary = MCTools::lookup_primary(contrib); const double propagationTime = m_attenuationReferencePosition ? std::abs(m_attenuationReferencePosition.value() - ih.getPosition().z) * From afe1132df79fc4f7446e815358453117cda9e5e8 Mon Sep 17 00:00:00 2001 From: Sylvester Joosten Date: Mon, 24 Nov 2025 13:51:14 -0600 Subject: [PATCH 4/5] fixed typo in comment --- src/algorithms/calorimetry/MCTools.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/algorithms/calorimetry/MCTools.h b/src/algorithms/calorimetry/MCTools.h index cbd622e637..54deeb1acc 100644 --- a/src/algorithms/calorimetry/MCTools.h +++ b/src/algorithms/calorimetry/MCTools.h @@ -3,7 +3,7 @@ #pragma once // -// @TODO should be migrated to a shared utiliy function in edm4xxx +// @TODO should be migrated to a shared utility function in edm4xxx // #include @@ -19,7 +19,7 @@ namespace eicrecon::MCTools { // storing the first pair of calorimetric showers that start inside the tracking volume. // Hence, this algorithm will return: // - Contribution came from primary: primary -// - Contribution came from immediate daughter of primary and has no childern -> daughter +// - Contribution came from immediate daughter of primary which has no children -> daughter // - All other cases (i.e. early showers, multi-radiation): primary // libraries inline edm4hep::MCParticle lookup_primary(const edm4hep::CaloHitContribution& contrib) { From 1a0ee98c24f82c1026c325dccee15d366d92ca7b Mon Sep 17 00:00:00 2001 From: Sylvester Joosten Date: Mon, 24 Nov 2025 20:33:54 -0600 Subject: [PATCH 5/5] remove spurious word in comment --- src/algorithms/calorimetry/MCTools.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/algorithms/calorimetry/MCTools.h b/src/algorithms/calorimetry/MCTools.h index 54deeb1acc..70c92b49a8 100644 --- a/src/algorithms/calorimetry/MCTools.h +++ b/src/algorithms/calorimetry/MCTools.h @@ -21,7 +21,6 @@ namespace eicrecon::MCTools { // - Contribution came from primary: primary // - Contribution came from immediate daughter of primary which has no children -> daughter // - All other cases (i.e. early showers, multi-radiation): primary -// libraries inline edm4hep::MCParticle lookup_primary(const edm4hep::CaloHitContribution& contrib) { const auto contributor = contrib.getParticle();