From 32c732f65d4acf9418186d5c290471ce26b1533e Mon Sep 17 00:00:00 2001 From: mhkim Date: Thu, 7 Aug 2025 14:25:03 -0500 Subject: [PATCH 01/20] clustering contributions that sit in the same time window --- .../calorimetry/SimCalorimeterHitProcessor.cc | 375 +++++++++--------- .../SimCalorimeterHitProcessorConfig.h | 3 + src/detectors/BEMC/BEMC.cc | 4 + .../SimCalorimeterHitProcessor_factory.h | 1 + 4 files changed, 197 insertions(+), 186 deletions(-) diff --git a/src/algorithms/calorimetry/SimCalorimeterHitProcessor.cc b/src/algorithms/calorimetry/SimCalorimeterHitProcessor.cc index b94a8558fe..4a2175790c 100644 --- a/src/algorithms/calorimetry/SimCalorimeterHitProcessor.cc +++ b/src/algorithms/calorimetry/SimCalorimeterHitProcessor.cc @@ -36,203 +36,206 @@ namespace std { #if defined(podio_VERSION_MAJOR) && defined(podio_VERSION_MINOR) #if podio_VERSION <= PODIO_VERSION(1, 2, 0) -// Hash for podio::ObjectID -template <> struct hash { - size_t operator()(const podio::ObjectID& id) const noexcept { - size_t h1 = std::hash{}(id.collectionID); - size_t h2 = std::hash{}(id.index); - return h1 ^ (h2 << 1); - } -}; - -// Necessary to make MCParticle hashable -template <> struct hash { - size_t operator()(const edm4hep::MCParticle& p) const noexcept { - const auto& id = p.getObjectID(); - return std::hash()(id); - } -}; + // Hash for podio::ObjectID + template <> struct hash { + size_t operator()(const podio::ObjectID& id) const noexcept { + size_t h1 = std::hash{}(id.collectionID); + size_t h2 = std::hash{}(id.index); + return h1 ^ (h2 << 1); + } + }; + + // Necessary to make MCParticle hashable + template <> struct hash { + size_t operator()(const edm4hep::MCParticle& p) const noexcept { + const auto& id = p.getObjectID(); + return std::hash()(id); + } + }; #endif // podio version check #endif // defined(podio_VERSION_MAJOR) && defined(podio_VERSION_MINOR) -// Hash for tuple -// --> not yet supported by any compiler at the moment -template <> struct hash> { - size_t operator()(const std::tuple& key) const noexcept { - const auto& [particle, cellID] = key; - size_t h1 = hash{}(particle); - size_t h2 = hash{}(cellID); - return h1 ^ (h2 << 1); - } -}; + // Hash for tuple + // --> not yet supported by any compiler at the moment + template <> struct hash> { + size_t operator()(const std::tuple& key) const noexcept { + const auto& [particle, cellID, timeID] = key; + size_t h1 = hash{}(particle); + size_t h2 = hash{}(cellID); + size_t h3 = hash{}(timeID); + return ((h1 ^ (h2 << 1)) >> 1) ^ (h3 << 1); + } + }; } // namespace std // unnamed namespace for internal utility namespace { -// Lookup primary MCParticle @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) - break; - primary = primary.getParents(0); - } - return primary; -} -class HitContributionAccumulator { -private: - float m_energy{0}; - float m_avg_time{0}; - float m_min_time{std::numeric_limits::max()}; - edm4hep::Vector3f m_avg_position{0, 0, 0}; - -public: - void add(const float energy, const float time, const edm4hep::Vector3f& pos) { - m_energy += energy; - m_avg_time += energy * time; - m_avg_position = m_avg_position + energy * pos; - m_min_time = (time < m_min_time) ? time : m_min_time; - } - float getEnergy() const { return m_energy; } - float getAvgTime() const { return m_energy > 0 ? m_avg_time / m_energy : 0; } - float getMinTime() const { return m_min_time; } - edm4hep::Vector3f getAvgPosition() const { - return m_energy > 0 ? m_avg_position / m_energy : edm4hep::Vector3f{0, 0, 0}; - } -}; + // Lookup primary MCParticle @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) + break; + primary = primary.getParents(0); + } + return primary; + } + class HitContributionAccumulator { + private: + float m_energy{0}; + float m_avg_time{0}; + float m_min_time{std::numeric_limits::max()}; + edm4hep::Vector3f m_avg_position{0, 0, 0}; + + public: + void add(const float energy, const float time, const edm4hep::Vector3f& pos) { + m_energy += energy; + m_avg_time += energy * time; + m_avg_position = m_avg_position + energy * pos; + m_min_time = (time < m_min_time) ? time : m_min_time; + } + float getEnergy() const { return m_energy; } + float getAvgTime() const { return m_energy > 0 ? m_avg_time / m_energy : 0; } + float getMinTime() const { return m_min_time; } + edm4hep::Vector3f getAvgPosition() const { + return m_energy > 0 ? m_avg_position / m_energy : edm4hep::Vector3f{0, 0, 0}; + } + }; } // namespace namespace eicrecon { -void SimCalorimeterHitProcessor::init() { - - // readout checks - if (m_cfg.readout.empty()) { - error("readoutClass is not provided, it is needed to know the fields in readout ids"); - throw std::runtime_error("readoutClass is not provided"); - } - - // get decoders - try { - m_id_spec = m_geo.detector()->readout(m_cfg.readout).idSpec(); - } catch (...) { - debug("Failed to load ID decoder for {}", m_cfg.readout); - throw std::runtime_error(fmt::format("Failed to load ID decoder for {}", m_cfg.readout)); - } - - // get m_hit_id_mask for adding up hits with the same dimensions that are merged over - { - uint64_t id_inverse_mask = 0; - if (!m_cfg.hitMergeFields.empty()) { - for (auto& field : m_cfg.hitMergeFields) { - id_inverse_mask |= m_id_spec.field(field)->mask(); - } - } - m_hit_id_mask = ~id_inverse_mask; - debug("ID mask in {:s}: {:#064b}", m_cfg.readout, m_hit_id_mask.value()); - } - - // get m_contribution_id_mask for adding up contributions with the same dimensions that are merged over - { - uint64_t id_inverse_mask = 0; - if (!m_cfg.contributionMergeFields.empty()) { - for (auto& field : m_cfg.contributionMergeFields) { - id_inverse_mask |= m_id_spec.field(field)->mask(); - } - m_contribution_id_mask = ~id_inverse_mask; - } - debug("ID mask in {:s}: {:#064b}", m_cfg.readout, m_contribution_id_mask.value()); - } - - // get reference position for attenuating hits and contributions - if (!m_cfg.attenuationReferencePositionName.empty()) { - m_attenuationReferencePosition = - m_geo.detector()->constant(m_cfg.attenuationReferencePositionName) * - edm4eic::unit::mm / dd4hep::mm; - } -} - -// Group contributions by (primary particle, cell ID), apply optional attenuation, and optionally merge into superhits -void SimCalorimeterHitProcessor::process(const SimCalorimeterHitProcessor::Input& input, - const SimCalorimeterHitProcessor::Output& output) const { - - const auto [in_hits] = input; - auto [out_hits, out_hit_contribs] = output; - - // Map for staging output information. We have 2 levels of structure: - // - top level: (MCParticle, Merged Hit CellID) - // - second level: (Merged Contributions) - // Ideally we would want immediately create our output objects and modify the - // contributions when needed. That could reduce the following code to a single loop - // (instead of 2 consecutive loops). However, this is not possible as we may have to merge - // (hence modify) contributions which is not supported for PodIO VectorMembers. Using - // reasonable contribution merging, at least the intermediary structure should be - // quite a bit smaller than the original hit collection. - using HitIndex = std::tuple; - std::unordered_map> - hit_map; - - for (const auto& ih : *in_hits) { - // the cell ID of the new superhit we are making - const uint64_t newhit_cellID = - (ih.getCellID() & m_hit_id_mask.value() & m_contribution_id_mask.value()); - // the cell ID of this particular contribution (we are using contributions to store - // the hits making up this "superhit" with more segmentation) - const uint64_t newcontrib_cellID = (ih.getCellID() & m_hit_id_mask.value()); - // Optional attenuation - const double attFactor = - 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); - auto& hit_accum = hit_map[{primary, newhit_cellID}][newcontrib_cellID]; - const double propagationTime = - m_attenuationReferencePosition - ? std::abs(m_attenuationReferencePosition.value() - ih.getPosition().z) * - m_cfg.inversePropagationSpeed - : 0.; - hit_accum.add(contrib.getEnergy() * attFactor, - contrib.getTime() + propagationTime + m_cfg.fixedTimeDelay, ih.getPosition()); - } - } - - // We now have our data structured as we want it, next we need to visit all hits again - // and create our output structures - for (const auto& [hit_idx, contribs] : hit_map) { - - auto out_hit = out_hits->create(); - - const auto& [particle, cellID] = hit_idx; - HitContributionAccumulator new_hit; - for (const auto& [contrib_idx, contrib] : contribs) { - // Aggregate contributions to for the global hit - new_hit.add(contrib.getEnergy(), contrib.getMinTime(), contrib.getAvgPosition()); - // Now store the contribution itself - auto out_hit_contrib = out_hit_contribs->create(); - out_hit_contrib.setPDG(particle.getPDG()); - out_hit_contrib.setEnergy(contrib.getEnergy()); - out_hit_contrib.setTime(contrib.getMinTime()); - out_hit_contrib.setStepPosition(contrib.getAvgPosition()); - out_hit_contrib.setParticle(particle); - out_hit.addToContributions(out_hit_contrib); - } - out_hit.setCellID(cellID); - out_hit.setEnergy(new_hit.getEnergy()); - out_hit.setPosition(new_hit.getAvgPosition()); - } -} - -double SimCalorimeterHitProcessor::get_attenuation(double zpos) const { - double length = std::abs(m_attenuationReferencePosition.value() - zpos); - double factor = - m_cfg.attenuationParameters[0] * std::exp(-length / m_cfg.attenuationParameters[1]) + - (1 - m_cfg.attenuationParameters[0]) * std::exp(-length / m_cfg.attenuationParameters[2]); - return factor; -} + void SimCalorimeterHitProcessor::init() { + + // readout checks + if (m_cfg.readout.empty()) { + error("readoutClass is not provided, it is needed to know the fields in readout ids"); + throw std::runtime_error("readoutClass is not provided"); + } + + // get decoders + try { + m_id_spec = m_geo.detector()->readout(m_cfg.readout).idSpec(); + } catch (...) { + debug("Failed to load ID decoder for {}", m_cfg.readout); + throw std::runtime_error(fmt::format("Failed to load ID decoder for {}", m_cfg.readout)); + } + + // get m_hit_id_mask for adding up hits with the same dimensions that are merged over + { + uint64_t id_inverse_mask = 0; + if (!m_cfg.hitMergeFields.empty()) { + for (auto& field : m_cfg.hitMergeFields) { + id_inverse_mask |= m_id_spec.field(field)->mask(); + } + } + m_hit_id_mask = ~id_inverse_mask; + debug("ID mask in {:s}: {:#064b}", m_cfg.readout, m_hit_id_mask.value()); + } + + // get m_contribution_id_mask for adding up contributions with the same dimensions that are merged over + { + uint64_t id_inverse_mask = 0; + if (!m_cfg.contributionMergeFields.empty()) { + for (auto& field : m_cfg.contributionMergeFields) { + id_inverse_mask |= m_id_spec.field(field)->mask(); + } + m_contribution_id_mask = ~id_inverse_mask; + } + debug("ID mask in {:s}: {:#064b}", m_cfg.readout, m_contribution_id_mask.value()); + } + + // get reference position for attenuating hits and contributions + if (!m_cfg.attenuationReferencePositionName.empty()) { + m_attenuationReferencePosition = + m_geo.detector()->constant(m_cfg.attenuationReferencePositionName) * + edm4eic::unit::mm / dd4hep::mm; + } + } + + // Group contributions by (primary particle, cell ID), apply optional attenuation, and optionally merge into superhits + void SimCalorimeterHitProcessor::process(const SimCalorimeterHitProcessor::Input& input, + const SimCalorimeterHitProcessor::Output& output) const { + + const auto [in_hits] = input; + auto [out_hits, out_hit_contribs] = output; + + // Map for staging output information. We have 2 levels of structure: + // - top level: (MCParticle, Merged Hit CellID) + // - second level: (Merged Contributions) + // Ideally we would want immediately create our output objects and modify the + // contributions when needed. That could reduce the following code to a single loop + // (instead of 2 consecutive loops). However, this is not possible as we may have to merge + // (hence modify) contributions which is not supported for PodIO VectorMembers. Using + // reasonable contribution merging, at least the intermediary structure should be + // quite a bit smaller than the original hit collection. + using HitIndex = std::tuple; + std::unordered_map> + hit_map; + + for (const auto& ih : *in_hits) { + // the cell ID of the new superhit we are making + const uint64_t newhit_cellID = + (ih.getCellID() & m_hit_id_mask.value() & m_contribution_id_mask.value()); + // the cell ID of this particular contribution (we are using contributions to store + // the hits making up this "superhit" with more segmentation) + const uint64_t newcontrib_cellID = (ih.getCellID() & m_hit_id_mask.value()); + // Optional attenuation + const double attFactor = + 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); + const double propagationTime = + m_attenuationReferencePosition + ? std::abs(m_attenuationReferencePosition.value() - ih.getPosition().z) * + m_cfg.inversePropagationSpeed + : 0.; + const double totalTime = contrib.getTime() + propagationTime + m_cfg.fixedTimeDelay; + const int newhit_timeID = std::floor(totalTime / m_cfg.timeWindow); + auto& hit_accum = hit_map[{primary, newhit_cellID, newhit_timeID}][newcontrib_cellID]; + hit_accum.add(contrib.getEnergy() * attFactor, + totalTime, ih.getPosition()); + } + } + + // We now have our data structured as we want it, next we need to visit all hits again + // and create our output structures + for (const auto& [hit_idx, contribs] : hit_map) { + + auto out_hit = out_hits->create(); + + const auto& [particle, cellID, timeID] = hit_idx; + HitContributionAccumulator new_hit; + for (const auto& [contrib_idx, contrib] : contribs) { + // Aggregate contributions to for the global hit + new_hit.add(contrib.getEnergy(), contrib.getMinTime(), contrib.getAvgPosition()); + // Now store the contribution itself + auto out_hit_contrib = out_hit_contribs->create(); + out_hit_contrib.setPDG(particle.getPDG()); + out_hit_contrib.setEnergy(contrib.getEnergy()); + out_hit_contrib.setTime(contrib.getMinTime()); + out_hit_contrib.setStepPosition(contrib.getAvgPosition()); + out_hit_contrib.setParticle(particle); + out_hit.addToContributions(out_hit_contrib); + } + out_hit.setCellID(cellID); + out_hit.setEnergy(new_hit.getEnergy()); + out_hit.setPosition(new_hit.getAvgPosition()); + } + } + + double SimCalorimeterHitProcessor::get_attenuation(double zpos) const { + double length = std::abs(m_attenuationReferencePosition.value() - zpos); + double factor = + m_cfg.attenuationParameters[0] * std::exp(-length / m_cfg.attenuationParameters[1]) + + (1 - m_cfg.attenuationParameters[0]) * std::exp(-length / m_cfg.attenuationParameters[2]); + return factor; + } } // namespace eicrecon diff --git a/src/algorithms/calorimetry/SimCalorimeterHitProcessorConfig.h b/src/algorithms/calorimetry/SimCalorimeterHitProcessorConfig.h index eb25a30998..b6797df737 100644 --- a/src/algorithms/calorimetry/SimCalorimeterHitProcessorConfig.h +++ b/src/algorithms/calorimetry/SimCalorimeterHitProcessorConfig.h @@ -5,6 +5,7 @@ #include #include +#include namespace eicrecon { @@ -27,6 +28,8 @@ struct SimCalorimeterHitProcessorConfig { double inversePropagationSpeed{}; // detector-related time delay (e.g., scintillation) double fixedTimeDelay{}; + // time window for grouping contributions + double timeWindow{100 * edm4eic::unit::ns}; }; } // namespace eicrecon diff --git a/src/detectors/BEMC/BEMC.cc b/src/detectors/BEMC/BEMC.cc index 24cbee397b..5cb25a3f1d 100644 --- a/src/detectors/BEMC/BEMC.cc +++ b/src/detectors/BEMC/BEMC.cc @@ -45,6 +45,8 @@ void InitPlugin(JApplication* app) { EcalBarrelScFi_inversePropagationSpeed = {(1. / 160) * edm4eic::unit::ns / edm4eic::unit::mm}; decltype(SimCalorimeterHitProcessorConfig::fixedTimeDelay) EcalBarrelScFi_fixedTimeDelay = { 2 * edm4eic::unit::ns}; + decltype(SimCalorimeterHitProcessorConfig::timeWindow) EcalBarrelScFi_timeWindow = { + 100 * edm4eic::unit::ns}; // Make sure digi and reco use the same value decltype(CalorimeterHitDigiConfig::capADC) EcalBarrelScFi_capADC = 16384; //16384, 14bit ADC @@ -64,6 +66,7 @@ void InitPlugin(JApplication* app) { .contributionMergeFields = EcalBarrelScFi_contributionMergeFields, .inversePropagationSpeed = EcalBarrelScFi_inversePropagationSpeed, .fixedTimeDelay = EcalBarrelScFi_fixedTimeDelay, + .timeWindow = EcalBarrelScFi_timeWindow, })); app->Add(new JOmniFactoryGeneratorT( "EcalBarrelScFiNAttenuatedHits", {"EcalBarrelScFiHits"}, @@ -76,6 +79,7 @@ void InitPlugin(JApplication* app) { .contributionMergeFields = EcalBarrelScFi_contributionMergeFields, .inversePropagationSpeed = EcalBarrelScFi_inversePropagationSpeed, .fixedTimeDelay = EcalBarrelScFi_fixedTimeDelay, + .timeWindow = EcalBarrelScFi_timeWindow, })); app->Add(new JOmniFactoryGeneratorT( "EcalBarrelScFiRawHits", {"EventHeader", "EcalBarrelScFiHits"}, diff --git a/src/factories/calorimetry/SimCalorimeterHitProcessor_factory.h b/src/factories/calorimetry/SimCalorimeterHitProcessor_factory.h index 8872d58324..847a56786e 100644 --- a/src/factories/calorimetry/SimCalorimeterHitProcessor_factory.h +++ b/src/factories/calorimetry/SimCalorimeterHitProcessor_factory.h @@ -34,6 +34,7 @@ class SimCalorimeterHitProcessor_factory ParameterRef m_inversePropagationSpeed{this, "inversePropagationSpeed", config().inversePropagationSpeed}; ParameterRef m_fixedTimeDelay{this, "fixedTimeDelay", config().fixedTimeDelay}; + ParameterRef m_timeWindow{this, "timeWindow", config().timeWindow}; Service m_algorithmsInit{this}; From fb4af8e62e24176d75bf89c5e175cb5609b0b1b1 Mon Sep 17 00:00:00 2001 From: mhkim Date: Thu, 7 Aug 2025 18:02:43 -0500 Subject: [PATCH 02/20] apply code formatter --- .../calorimetry/SimCalorimeterHitProcessor.cc | 377 +++++++++--------- 1 file changed, 188 insertions(+), 189 deletions(-) diff --git a/src/algorithms/calorimetry/SimCalorimeterHitProcessor.cc b/src/algorithms/calorimetry/SimCalorimeterHitProcessor.cc index 4a2175790c..ecbbfc220c 100644 --- a/src/algorithms/calorimetry/SimCalorimeterHitProcessor.cc +++ b/src/algorithms/calorimetry/SimCalorimeterHitProcessor.cc @@ -36,206 +36,205 @@ namespace std { #if defined(podio_VERSION_MAJOR) && defined(podio_VERSION_MINOR) #if podio_VERSION <= PODIO_VERSION(1, 2, 0) - // Hash for podio::ObjectID - template <> struct hash { - size_t operator()(const podio::ObjectID& id) const noexcept { - size_t h1 = std::hash{}(id.collectionID); - size_t h2 = std::hash{}(id.index); - return h1 ^ (h2 << 1); - } - }; - - // Necessary to make MCParticle hashable - template <> struct hash { - size_t operator()(const edm4hep::MCParticle& p) const noexcept { - const auto& id = p.getObjectID(); - return std::hash()(id); - } - }; +// Hash for podio::ObjectID +template <> struct hash { + size_t operator()(const podio::ObjectID& id) const noexcept { + size_t h1 = std::hash{}(id.collectionID); + size_t h2 = std::hash{}(id.index); + return h1 ^ (h2 << 1); + } +}; + +// Necessary to make MCParticle hashable +template <> struct hash { + size_t operator()(const edm4hep::MCParticle& p) const noexcept { + const auto& id = p.getObjectID(); + return std::hash()(id); + } +}; #endif // podio version check #endif // defined(podio_VERSION_MAJOR) && defined(podio_VERSION_MINOR) - // Hash for tuple - // --> not yet supported by any compiler at the moment - template <> struct hash> { - size_t operator()(const std::tuple& key) const noexcept { - const auto& [particle, cellID, timeID] = key; - size_t h1 = hash{}(particle); - size_t h2 = hash{}(cellID); - size_t h3 = hash{}(timeID); - return ((h1 ^ (h2 << 1)) >> 1) ^ (h3 << 1); - } - }; +// Hash for tuple +// --> not yet supported by any compiler at the moment +template <> struct hash> { + size_t operator()(const std::tuple& key) const noexcept { + const auto& [particle, cellID, timeID] = key; + size_t h1 = hash{}(particle); + size_t h2 = hash{}(cellID); + size_t h3 = hash{}(timeID); + return ((h1 ^ (h2 << 1)) >> 1) ^ (h3 << 1); + } +}; } // namespace std // unnamed namespace for internal utility namespace { - // Lookup primary MCParticle @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) - break; - primary = primary.getParents(0); - } - return primary; - } - class HitContributionAccumulator { - private: - float m_energy{0}; - float m_avg_time{0}; - float m_min_time{std::numeric_limits::max()}; - edm4hep::Vector3f m_avg_position{0, 0, 0}; - - public: - void add(const float energy, const float time, const edm4hep::Vector3f& pos) { - m_energy += energy; - m_avg_time += energy * time; - m_avg_position = m_avg_position + energy * pos; - m_min_time = (time < m_min_time) ? time : m_min_time; - } - float getEnergy() const { return m_energy; } - float getAvgTime() const { return m_energy > 0 ? m_avg_time / m_energy : 0; } - float getMinTime() const { return m_min_time; } - edm4hep::Vector3f getAvgPosition() const { - return m_energy > 0 ? m_avg_position / m_energy : edm4hep::Vector3f{0, 0, 0}; - } - }; +// Lookup primary MCParticle @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) + break; + primary = primary.getParents(0); + } + return primary; +} +class HitContributionAccumulator { +private: + float m_energy{0}; + float m_avg_time{0}; + float m_min_time{std::numeric_limits::max()}; + edm4hep::Vector3f m_avg_position{0, 0, 0}; + +public: + void add(const float energy, const float time, const edm4hep::Vector3f& pos) { + m_energy += energy; + m_avg_time += energy * time; + m_avg_position = m_avg_position + energy * pos; + m_min_time = (time < m_min_time) ? time : m_min_time; + } + float getEnergy() const { return m_energy; } + float getAvgTime() const { return m_energy > 0 ? m_avg_time / m_energy : 0; } + float getMinTime() const { return m_min_time; } + edm4hep::Vector3f getAvgPosition() const { + return m_energy > 0 ? m_avg_position / m_energy : edm4hep::Vector3f{0, 0, 0}; + } +}; } // namespace namespace eicrecon { - void SimCalorimeterHitProcessor::init() { - - // readout checks - if (m_cfg.readout.empty()) { - error("readoutClass is not provided, it is needed to know the fields in readout ids"); - throw std::runtime_error("readoutClass is not provided"); - } - - // get decoders - try { - m_id_spec = m_geo.detector()->readout(m_cfg.readout).idSpec(); - } catch (...) { - debug("Failed to load ID decoder for {}", m_cfg.readout); - throw std::runtime_error(fmt::format("Failed to load ID decoder for {}", m_cfg.readout)); - } - - // get m_hit_id_mask for adding up hits with the same dimensions that are merged over - { - uint64_t id_inverse_mask = 0; - if (!m_cfg.hitMergeFields.empty()) { - for (auto& field : m_cfg.hitMergeFields) { - id_inverse_mask |= m_id_spec.field(field)->mask(); - } - } - m_hit_id_mask = ~id_inverse_mask; - debug("ID mask in {:s}: {:#064b}", m_cfg.readout, m_hit_id_mask.value()); - } - - // get m_contribution_id_mask for adding up contributions with the same dimensions that are merged over - { - uint64_t id_inverse_mask = 0; - if (!m_cfg.contributionMergeFields.empty()) { - for (auto& field : m_cfg.contributionMergeFields) { - id_inverse_mask |= m_id_spec.field(field)->mask(); - } - m_contribution_id_mask = ~id_inverse_mask; - } - debug("ID mask in {:s}: {:#064b}", m_cfg.readout, m_contribution_id_mask.value()); - } - - // get reference position for attenuating hits and contributions - if (!m_cfg.attenuationReferencePositionName.empty()) { - m_attenuationReferencePosition = - m_geo.detector()->constant(m_cfg.attenuationReferencePositionName) * - edm4eic::unit::mm / dd4hep::mm; - } - } - - // Group contributions by (primary particle, cell ID), apply optional attenuation, and optionally merge into superhits - void SimCalorimeterHitProcessor::process(const SimCalorimeterHitProcessor::Input& input, - const SimCalorimeterHitProcessor::Output& output) const { - - const auto [in_hits] = input; - auto [out_hits, out_hit_contribs] = output; - - // Map for staging output information. We have 2 levels of structure: - // - top level: (MCParticle, Merged Hit CellID) - // - second level: (Merged Contributions) - // Ideally we would want immediately create our output objects and modify the - // contributions when needed. That could reduce the following code to a single loop - // (instead of 2 consecutive loops). However, this is not possible as we may have to merge - // (hence modify) contributions which is not supported for PodIO VectorMembers. Using - // reasonable contribution merging, at least the intermediary structure should be - // quite a bit smaller than the original hit collection. - using HitIndex = std::tuple; - std::unordered_map> - hit_map; - - for (const auto& ih : *in_hits) { - // the cell ID of the new superhit we are making - const uint64_t newhit_cellID = - (ih.getCellID() & m_hit_id_mask.value() & m_contribution_id_mask.value()); - // the cell ID of this particular contribution (we are using contributions to store - // the hits making up this "superhit" with more segmentation) - const uint64_t newcontrib_cellID = (ih.getCellID() & m_hit_id_mask.value()); - // Optional attenuation - const double attFactor = - 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); - const double propagationTime = - m_attenuationReferencePosition - ? std::abs(m_attenuationReferencePosition.value() - ih.getPosition().z) * - m_cfg.inversePropagationSpeed - : 0.; - const double totalTime = contrib.getTime() + propagationTime + m_cfg.fixedTimeDelay; - const int newhit_timeID = std::floor(totalTime / m_cfg.timeWindow); - auto& hit_accum = hit_map[{primary, newhit_cellID, newhit_timeID}][newcontrib_cellID]; - hit_accum.add(contrib.getEnergy() * attFactor, - totalTime, ih.getPosition()); - } - } - - // We now have our data structured as we want it, next we need to visit all hits again - // and create our output structures - for (const auto& [hit_idx, contribs] : hit_map) { - - auto out_hit = out_hits->create(); - - const auto& [particle, cellID, timeID] = hit_idx; - HitContributionAccumulator new_hit; - for (const auto& [contrib_idx, contrib] : contribs) { - // Aggregate contributions to for the global hit - new_hit.add(contrib.getEnergy(), contrib.getMinTime(), contrib.getAvgPosition()); - // Now store the contribution itself - auto out_hit_contrib = out_hit_contribs->create(); - out_hit_contrib.setPDG(particle.getPDG()); - out_hit_contrib.setEnergy(contrib.getEnergy()); - out_hit_contrib.setTime(contrib.getMinTime()); - out_hit_contrib.setStepPosition(contrib.getAvgPosition()); - out_hit_contrib.setParticle(particle); - out_hit.addToContributions(out_hit_contrib); - } - out_hit.setCellID(cellID); - out_hit.setEnergy(new_hit.getEnergy()); - out_hit.setPosition(new_hit.getAvgPosition()); - } - } - - double SimCalorimeterHitProcessor::get_attenuation(double zpos) const { - double length = std::abs(m_attenuationReferencePosition.value() - zpos); - double factor = - m_cfg.attenuationParameters[0] * std::exp(-length / m_cfg.attenuationParameters[1]) + - (1 - m_cfg.attenuationParameters[0]) * std::exp(-length / m_cfg.attenuationParameters[2]); - return factor; - } +void SimCalorimeterHitProcessor::init() { + + // readout checks + if (m_cfg.readout.empty()) { + error("readoutClass is not provided, it is needed to know the fields in readout ids"); + throw std::runtime_error("readoutClass is not provided"); + } + + // get decoders + try { + m_id_spec = m_geo.detector()->readout(m_cfg.readout).idSpec(); + } catch (...) { + debug("Failed to load ID decoder for {}", m_cfg.readout); + throw std::runtime_error(fmt::format("Failed to load ID decoder for {}", m_cfg.readout)); + } + + // get m_hit_id_mask for adding up hits with the same dimensions that are merged over + { + uint64_t id_inverse_mask = 0; + if (!m_cfg.hitMergeFields.empty()) { + for (auto& field : m_cfg.hitMergeFields) { + id_inverse_mask |= m_id_spec.field(field)->mask(); + } + } + m_hit_id_mask = ~id_inverse_mask; + debug("ID mask in {:s}: {:#064b}", m_cfg.readout, m_hit_id_mask.value()); + } + + // get m_contribution_id_mask for adding up contributions with the same dimensions that are merged over + { + uint64_t id_inverse_mask = 0; + if (!m_cfg.contributionMergeFields.empty()) { + for (auto& field : m_cfg.contributionMergeFields) { + id_inverse_mask |= m_id_spec.field(field)->mask(); + } + m_contribution_id_mask = ~id_inverse_mask; + } + debug("ID mask in {:s}: {:#064b}", m_cfg.readout, m_contribution_id_mask.value()); + } + + // get reference position for attenuating hits and contributions + if (!m_cfg.attenuationReferencePositionName.empty()) { + m_attenuationReferencePosition = + m_geo.detector()->constant(m_cfg.attenuationReferencePositionName) * + edm4eic::unit::mm / dd4hep::mm; + } +} + +// Group contributions by (primary particle, cell ID), apply optional attenuation, and optionally merge into superhits +void SimCalorimeterHitProcessor::process(const SimCalorimeterHitProcessor::Input& input, + const SimCalorimeterHitProcessor::Output& output) const { + + const auto [in_hits] = input; + auto [out_hits, out_hit_contribs] = output; + + // Map for staging output information. We have 2 levels of structure: + // - top level: (MCParticle, Merged Hit CellID) + // - second level: (Merged Contributions) + // Ideally we would want immediately create our output objects and modify the + // contributions when needed. That could reduce the following code to a single loop + // (instead of 2 consecutive loops). However, this is not possible as we may have to merge + // (hence modify) contributions which is not supported for PodIO VectorMembers. Using + // reasonable contribution merging, at least the intermediary structure should be + // quite a bit smaller than the original hit collection. + using HitIndex = std::tuple; + std::unordered_map> + hit_map; + + for (const auto& ih : *in_hits) { + // the cell ID of the new superhit we are making + const uint64_t newhit_cellID = + (ih.getCellID() & m_hit_id_mask.value() & m_contribution_id_mask.value()); + // the cell ID of this particular contribution (we are using contributions to store + // the hits making up this "superhit" with more segmentation) + const uint64_t newcontrib_cellID = (ih.getCellID() & m_hit_id_mask.value()); + // Optional attenuation + const double attFactor = + 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); + const double propagationTime = + m_attenuationReferencePosition + ? std::abs(m_attenuationReferencePosition.value() - ih.getPosition().z) * + m_cfg.inversePropagationSpeed + : 0.; + const double totalTime = contrib.getTime() + propagationTime + m_cfg.fixedTimeDelay; + const int newhit_timeID = std::floor(totalTime / m_cfg.timeWindow); + auto& hit_accum = hit_map[{primary, newhit_cellID, newhit_timeID}][newcontrib_cellID]; + hit_accum.add(contrib.getEnergy() * attFactor, totalTime, ih.getPosition()); + } + } + + // We now have our data structured as we want it, next we need to visit all hits again + // and create our output structures + for (const auto& [hit_idx, contribs] : hit_map) { + + auto out_hit = out_hits->create(); + + const auto& [particle, cellID, timeID] = hit_idx; + HitContributionAccumulator new_hit; + for (const auto& [contrib_idx, contrib] : contribs) { + // Aggregate contributions to for the global hit + new_hit.add(contrib.getEnergy(), contrib.getMinTime(), contrib.getAvgPosition()); + // Now store the contribution itself + auto out_hit_contrib = out_hit_contribs->create(); + out_hit_contrib.setPDG(particle.getPDG()); + out_hit_contrib.setEnergy(contrib.getEnergy()); + out_hit_contrib.setTime(contrib.getMinTime()); + out_hit_contrib.setStepPosition(contrib.getAvgPosition()); + out_hit_contrib.setParticle(particle); + out_hit.addToContributions(out_hit_contrib); + } + out_hit.setCellID(cellID); + out_hit.setEnergy(new_hit.getEnergy()); + out_hit.setPosition(new_hit.getAvgPosition()); + } +} + +double SimCalorimeterHitProcessor::get_attenuation(double zpos) const { + double length = std::abs(m_attenuationReferencePosition.value() - zpos); + double factor = + m_cfg.attenuationParameters[0] * std::exp(-length / m_cfg.attenuationParameters[1]) + + (1 - m_cfg.attenuationParameters[0]) * std::exp(-length / m_cfg.attenuationParameters[2]); + return factor; +} } // namespace eicrecon From b9ee625bae4bc334a3d1bdc6f050bf5b304f2c66 Mon Sep 17 00:00:00 2001 From: mhkim Date: Thu, 7 Aug 2025 18:16:14 -0500 Subject: [PATCH 03/20] generate pulse from SimCalorimeterHit using a shared template algorithm --- src/algorithms/digi/PulseGeneration.cc | 216 +++++++++++++++++++ src/algorithms/digi/PulseGeneration.h | 67 ++++++ src/algorithms/digi/PulseGenerationConfig.h | 20 ++ src/detectors/BEMC/BEMC.cc | 28 +++ src/factories/digi/PulseGeneration_factory.h | 59 +++++ 5 files changed, 390 insertions(+) create mode 100644 src/algorithms/digi/PulseGeneration.cc create mode 100644 src/algorithms/digi/PulseGeneration.h create mode 100644 src/algorithms/digi/PulseGenerationConfig.h create mode 100644 src/factories/digi/PulseGeneration_factory.h diff --git a/src/algorithms/digi/PulseGeneration.cc b/src/algorithms/digi/PulseGeneration.cc new file mode 100644 index 0000000000..93316f39fd --- /dev/null +++ b/src/algorithms/digi/PulseGeneration.cc @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (C) 2024-2025 Simon Gardner, Chun Yuen Tsang, Prithwish Tribedy, +// Minho Kim, Sylvester Joosten, Wouter Deconinck, Dmitry Kalinkin +// +// Convert energy deposition into ADC pulses +// ADC pulses are assumed to follow the shape of landau function + +#include "PulseGeneration.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "services/evaluator/EvaluatorSvc.h" + +namespace eicrecon { + +class SignalPulse { + +public: + virtual ~SignalPulse() = default; // Virtual destructor + + virtual double operator()(double time, double charge) = 0; + + virtual double getMaximumTime() const = 0; +}; + +// ---------------------------------------------------------------------------- +// Landau Pulse Shape Functor +// ---------------------------------------------------------------------------- +class LandauPulse : public SignalPulse { +public: + LandauPulse(std::vector params) { + + if ((params.size() != 2) && (params.size() != 3)) { + throw std::runtime_error( + "LandauPulse requires 2 or 3 parameters, gain, sigma_analog, [hit_sigma_offset], got " + + std::to_string(params.size())); + } + + m_gain = params[0]; + m_sigma_analog = params[1]; + if (params.size() == 3) { + m_hit_sigma_offset = params[2]; + } + }; + + double operator()(double time, double charge) override { + return charge * m_gain * + TMath::Landau(time, m_hit_sigma_offset * m_sigma_analog, m_sigma_analog, kTRUE); + } + + double getMaximumTime() const override { return m_hit_sigma_offset * m_sigma_analog; } + +private: + double m_gain = 1.0; + double m_sigma_analog = 1.0; + double m_hit_sigma_offset = 3.5; +}; + +// EvaluatorSvc Pulse +class EvaluatorPulse : public SignalPulse { +public: + EvaluatorPulse(const std::string& expression, const std::vector& params) { + + std::vector keys = {"time", "charge"}; + for (std::size_t i = 0; i < params.size(); i++) { + std::string p = "param" + std::to_string(i); + //Check the expression contains the parameter + if (expression.find(p) == std::string::npos) { + throw std::runtime_error("Parameter " + p + " not found in expression"); + } + keys.push_back(p); + param_map[p] = params[i]; + } + + // Check the expression is contains time and charge + if (expression.find("time") == std::string::npos) { + throw std::runtime_error("Parameter [time] not found in expression"); + } + if (expression.find("charge") == std::string::npos) { + throw std::runtime_error("Parameter [charge] not found in expression"); + } + + auto& serviceSvc = algorithms::ServiceSvc::instance(); + m_evaluator = serviceSvc.service("EvaluatorSvc")->_compile(expression, keys); + }; + + double operator()(double time, double charge) override { + param_map["time"] = time; + param_map["charge"] = charge; + return m_evaluator(param_map); + } + + double getMaximumTime() const override { return 0; } + +private: + std::unordered_map param_map; + std::function&)> m_evaluator; +}; + +class PulseShapeFactory { +public: + static std::unique_ptr createPulseShape(const std::string& type, + const std::vector& params) { + if (type == "LandauPulse") { + return std::make_unique(params); + } + // + // Add more pulse shape variants here as needed + + // If type not found, try and make a function using the ElavulatorSvc + try { + return std::make_unique(type, params); + } catch (...) { + throw std::invalid_argument("Unable to make pulse shape type: " + type); + } + } +}; + +std::tuple +HitAdapter::getPulseSources(const edm4hep::SimTrackerHit& hit) { + return {hit.getTime(), hit.getEDep()}; +} + +std::tuple +HitAdapter::getPulseSources(const edm4hep::SimCalorimeterHit& hit) { + const auto& contribs = hit.getContributions(); + auto earliest_contrib = + std::min_element(contribs.begin(), contribs.end(), + [](const auto& a, const auto& b) { return a.getTime() < b.getTime(); }); + return {earliest_contrib->getTime(), hit.getEnergy()}; +} + +template void PulseGeneration::init() { + m_pulse = + PulseShapeFactory::createPulseShape(m_cfg.pulse_shape_function, m_cfg.pulse_shape_params); + m_min_sampling_time = m_cfg.min_sampling_time; + + if (m_pulse->getMaximumTime() > m_min_sampling_time) { + m_min_sampling_time = m_pulse->getMaximumTime(); + } +} + +template +void PulseGeneration::process( + const typename PulseGenerationAlgorithm::Input& input, + const typename PulseGenerationAlgorithm::Output& output) const { + const auto [simhits] = input; + auto [rawPulses] = output; + + for (const auto& hit : *simhits) { + const auto [time, charge] = HitAdapter::getPulseSources(hit); + // Calculate nearest timestep to the hit time rounded down (assume clocks aligned with time 0) + double signal_time = m_cfg.timestep * std::floor(time / m_cfg.timestep); + + bool passed_threshold = false; + std::uint32_t skip_bins = 0; + float integral = 0; + std::vector pulse; + + for (std::uint32_t i = 0; i < m_cfg.max_time_bins; i++) { + double t = signal_time + i * m_cfg.timestep - time; + auto signal = (*m_pulse)(t, charge); + if (std::abs(signal) < m_cfg.ignore_thres) { + if (!passed_threshold) { + skip_bins = i; + continue; + } + if (t > m_min_sampling_time) { + break; + } + } + passed_threshold = true; + pulse.push_back(signal); + integral += signal; + } + + if (!passed_threshold) { + continue; + } + + auto time_series = rawPulses->create(); + time_series.setCellID(hit.getCellID()); + time_series.setInterval(m_cfg.timestep); + time_series.setTime(signal_time + skip_bins * m_cfg.timestep); + + for (const auto& value : pulse) { + time_series.addToAmplitude(value); + } + +#if EDM4EIC_VERSION_MAJOR > 8 || (EDM4EIC_VERSION_MAJOR == 8 && EDM4EIC_VERSION_MINOR >= 1) + time_series.setIntegral(integral); + time_series.setPosition( + edm4hep::Vector3f(hit.getPosition().x, hit.getPosition().y, hit.getPosition().z)); + time_series.addToTrackerHits(hit); + time_series.addToParticles(hit.getParticle()); +#endif + } + +} // PulseGeneration:process + +template class PulseGeneration; +template class PulseGeneration; + +} // namespace eicrecon diff --git a/src/algorithms/digi/PulseGeneration.h b/src/algorithms/digi/PulseGeneration.h new file mode 100644 index 0000000000..3b98cce1ab --- /dev/null +++ b/src/algorithms/digi/PulseGeneration.h @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (C) 2024-2025 Simon Gardner, Chun Yuen Tsang, Prithwish Tribedy, +// Minho Kim, Sylvester Joosten, Wouter Deconinck, Dmitry Kalinkin +// +// Convert energy deposition into analog pulses + +#pragma once + +#include +#include +#include +#include +#include +#if EDM4EIC_VERSION_MAJOR > 8 || (EDM4EIC_VERSION_MAJOR == 8 && EDM4EIC_VERSION_MINOR >= 1) +#include +#else +#include +#endif +#include +#include +#include + +#include "algorithms/digi/PulseGenerationConfig.h" +#include "algorithms/interfaces/WithPodConfig.h" + +namespace eicrecon { + +#if EDM4EIC_VERSION_MAJOR > 8 || (EDM4EIC_VERSION_MAJOR == 8 && EDM4EIC_VERSION_MINOR >= 1) +using PulseType = edm4eic::SimPulse; +#else +using PulseType = edm4hep::TimeSeries; +#endif + +template struct HitAdapter; + +template <> struct HitAdapter { + static std::tuple getPulseSources(const edm4hep::SimTrackerHit& hit); +}; + +template <> struct HitAdapter { + static std::tuple getPulseSources(const edm4hep::SimCalorimeterHit& hit); +}; + +template +using PulseGenerationAlgorithm = + algorithms::Algorithm, + algorithms::Output>; + +class SignalPulse; + +template +class PulseGeneration : public PulseGenerationAlgorithm, + public WithPodConfig { + +public: + PulseGeneration(std::string_view name) + : PulseGenerationAlgorithm{name, {"RawHits"}, {"OutputPulses"}, {}} {} + void init() final; + void process(const typename PulseGenerationAlgorithm::Input&, + const typename PulseGenerationAlgorithm::Output&) const final; + +private: + std::shared_ptr m_pulse; + float m_min_sampling_time = 0 * edm4eic::unit::ns; +}; + +} // namespace eicrecon diff --git a/src/algorithms/digi/PulseGenerationConfig.h b/src/algorithms/digi/PulseGenerationConfig.h new file mode 100644 index 0000000000..7b716e3cc3 --- /dev/null +++ b/src/algorithms/digi/PulseGenerationConfig.h @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (C) 2025 Simon Gardner + +#pragma once + +#include + +namespace eicrecon { + +struct PulseGenerationConfig { + // Parameters of Silicon signal generation + std::string pulse_shape_function = "LandauPulse"; // Pulse shape function + std::vector pulse_shape_params = {1.0, 0.1}; // Parameters of the pulse shape function + double ignore_thres = 10; // When EDep drops below this value pulse stops + double timestep = 0.2 * edm4eic::unit::ns; // Minimum digitization time step + double min_sampling_time = 0 * edm4eic::unit::ns; // Minimum sampling time + uint32_t max_time_bins = 10000; +}; + +} // namespace eicrecon diff --git a/src/detectors/BEMC/BEMC.cc b/src/detectors/BEMC/BEMC.cc index 5cb25a3f1d..ffac69ff0b 100644 --- a/src/detectors/BEMC/BEMC.cc +++ b/src/detectors/BEMC/BEMC.cc @@ -24,6 +24,7 @@ #include "factories/calorimetry/ImagingClusterReco_factory.h" #include "factories/calorimetry/ImagingTopoCluster_factory.h" #include "factories/calorimetry/SimCalorimeterHitProcessor_factory.h" +#include "factories/digi/PulseGeneration_factory.h" #include "factories/calorimetry/TruthEnergyPositionClusterMerger_factory.h" extern "C" { @@ -48,6 +49,15 @@ void InitPlugin(JApplication* app) { decltype(SimCalorimeterHitProcessorConfig::timeWindow) EcalBarrelScFi_timeWindow = { 100 * edm4eic::unit::ns}; + decltype(PulseGenerationConfig::pulse_shape_function) + EcalBarrelScFi_pulse_shape_function = {"LandauPulse"}; + decltype(PulseGenerationConfig::pulse_shape_params) + EcalBarrelScFi_pulse_shape_params = {1.0, 2 * edm4eic::unit::ns}; + decltype(PulseGenerationConfig::ignore_thres) + EcalBarrelScFi_ignore_thres = {1.0e-5}; + decltype(PulseGenerationConfig::timestep) + EcalBarrelScFi_timestep = {0.2 * edm4eic::unit::ns}; + // Make sure digi and reco use the same value decltype(CalorimeterHitDigiConfig::capADC) EcalBarrelScFi_capADC = 16384; //16384, 14bit ADC decltype(CalorimeterHitDigiConfig::dyRangeADC) EcalBarrelScFi_dyRangeADC = 1500 * dd4hep::MeV; @@ -81,6 +91,24 @@ void InitPlugin(JApplication* app) { .fixedTimeDelay = EcalBarrelScFi_fixedTimeDelay, .timeWindow = EcalBarrelScFi_timeWindow, })); + app->Add(new JOmniFactoryGeneratorT>( + "EcalBarrelScFiPPulses", {"EcalBarrelScFiPAttenuatedHits"}, + {"EcalBarrelScFiPPulses"}, + { + .pulse_shape_function = EcalBarrelScFi_pulse_shape_function, + .pulse_shape_params = EcalBarrelScFi_pulse_shape_params, + .ignore_thres = EcalBarrelScFi_ignore_thres, + .timestep = EcalBarrelScFi_timestep, + })); + app->Add(new JOmniFactoryGeneratorT>( + "EcalBarrelScFiNPulses", {"EcalBarrelScFiNAttenuatedHits"}, + {"EcalBarrelScFiNPulses"}, + { + .pulse_shape_function = EcalBarrelScFi_pulse_shape_function, + .pulse_shape_params = EcalBarrelScFi_pulse_shape_params, + .ignore_thres = EcalBarrelScFi_ignore_thres, + .timestep = EcalBarrelScFi_timestep, + })); app->Add(new JOmniFactoryGeneratorT( "EcalBarrelScFiRawHits", {"EventHeader", "EcalBarrelScFiHits"}, {"EcalBarrelScFiRawHits", "EcalBarrelScFiRawHitAssociations"}, diff --git a/src/factories/digi/PulseGeneration_factory.h b/src/factories/digi/PulseGeneration_factory.h new file mode 100644 index 0000000000..5c2b4bd818 --- /dev/null +++ b/src/factories/digi/PulseGeneration_factory.h @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (C) 2024-2025 Simon Gardner, Chun Yuen Tsang, Prithwish Tribedy +// Minho Kim, Sylvester Joosten, Wouter Deconinck, Dmitry Kalinkin +// + +#pragma once + +#include "algorithms/digi/PulseGeneration.h" +#include "services/algorithms_init/AlgorithmsInit_service.h" +#include "extensions/jana/JOmniFactory.h" + +namespace eicrecon { + +template +class PulseGeneration_factory + : public JOmniFactory, PulseGenerationConfig> { +public: + using AlgoT = eicrecon::PulseGeneration; + using FactoryT = JOmniFactory, PulseGenerationConfig>; + +private: + std::unique_ptr m_algo; + + typename FactoryT::template PodioInput m_in_sim_hits{this}; +#if EDM4EIC_VERSION_MAJOR > 8 || (EDM4EIC_VERSION_MAJOR == 8 && EDM4EIC_VERSION_MINOR >= 1) + typename FactoryT::template PodioOutput m_out_pulses{this}; +#else + typename FactoryT::template PodioOutput m_out_pulses{this}; +#endif + + typename FactoryT::template ParameterRef m_pulse_shape_function{ + this, "pulseShapeFunction", this->config().pulse_shape_function}; + typename FactoryT::template ParameterRef> m_pulse_shape_params{ + this, "pulseShapeParams", this->config().pulse_shape_params}; + typename FactoryT::template ParameterRef m_timestep{this, "timestep", + this->config().timestep}; + typename FactoryT::template ParameterRef m_ignore_thres{this, "ignoreThreshold", + this->config().ignore_thres}; + typename FactoryT::template ParameterRef m_min_sampling_time{ + this, "minSamplingTime", this->config().min_sampling_time}; + typename FactoryT::template ParameterRef m_max_time_bins{this, "maxTimeBins", + this->config().max_time_bins}; + + typename FactoryT::template Service m_algorithmsInit{this}; + +public: + void Configure() { + m_algo = std::make_unique(this->GetPrefix()); + m_algo->level(static_cast(this->logger()->level())); + m_algo->applyConfig(this->config()); + m_algo->init(); + } + + void Process(int32_t /* run_number */, uint64_t /* event_number */) { + m_algo->process({m_in_sim_hits()}, {m_out_pulses().get()}); + } +}; + +} // namespace eicrecon From c3a951475c928ffabeb68cfd1cc53e7af52550cd Mon Sep 17 00:00:00 2001 From: mhkim Date: Thu, 7 Aug 2025 18:28:43 -0500 Subject: [PATCH 04/20] generate pulse from SimCalorimeterHit using a shared template algorithm --- src/services/io/podio/JEventProcessorPODIO.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/services/io/podio/JEventProcessorPODIO.cc b/src/services/io/podio/JEventProcessorPODIO.cc index ce32c57bdc..f7dc602aaa 100644 --- a/src/services/io/podio/JEventProcessorPODIO.cc +++ b/src/services/io/podio/JEventProcessorPODIO.cc @@ -297,6 +297,8 @@ JEventProcessorPODIO::JEventProcessorPODIO() { "EcalBarrelScFiPAttenuatedHits", "EcalBarrelScFiPAttenuatedHitContributions", "EcalBarrelScFiNAttenuatedHits", + "EcalBarrelScFiPPulses", + "EcalBarrelScFiNPulses", "EcalBarrelScFiNAttenuatedHitContributions", "EcalBarrelScFiRawHits", "EcalBarrelScFiRecHits", From 0c01c9851da6639883e0932ed76a65733d5559e6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 7 Aug 2025 23:34:06 +0000 Subject: [PATCH 05/20] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/detectors/BEMC/BEMC.cc | 40 +++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/src/detectors/BEMC/BEMC.cc b/src/detectors/BEMC/BEMC.cc index ffac69ff0b..64e3b328dd 100644 --- a/src/detectors/BEMC/BEMC.cc +++ b/src/detectors/BEMC/BEMC.cc @@ -49,14 +49,12 @@ void InitPlugin(JApplication* app) { decltype(SimCalorimeterHitProcessorConfig::timeWindow) EcalBarrelScFi_timeWindow = { 100 * edm4eic::unit::ns}; - decltype(PulseGenerationConfig::pulse_shape_function) - EcalBarrelScFi_pulse_shape_function = {"LandauPulse"}; - decltype(PulseGenerationConfig::pulse_shape_params) - EcalBarrelScFi_pulse_shape_params = {1.0, 2 * edm4eic::unit::ns}; - decltype(PulseGenerationConfig::ignore_thres) - EcalBarrelScFi_ignore_thres = {1.0e-5}; - decltype(PulseGenerationConfig::timestep) - EcalBarrelScFi_timestep = {0.2 * edm4eic::unit::ns}; + decltype(PulseGenerationConfig::pulse_shape_function) EcalBarrelScFi_pulse_shape_function = { + "LandauPulse"}; + decltype(PulseGenerationConfig::pulse_shape_params) EcalBarrelScFi_pulse_shape_params = { + 1.0, 2 * edm4eic::unit::ns}; + decltype(PulseGenerationConfig::ignore_thres) EcalBarrelScFi_ignore_thres = {1.0e-5}; + decltype(PulseGenerationConfig::timestep) EcalBarrelScFi_timestep = {0.2 * edm4eic::unit::ns}; // Make sure digi and reco use the same value decltype(CalorimeterHitDigiConfig::capADC) EcalBarrelScFi_capADC = 16384; //16384, 14bit ADC @@ -76,7 +74,7 @@ void InitPlugin(JApplication* app) { .contributionMergeFields = EcalBarrelScFi_contributionMergeFields, .inversePropagationSpeed = EcalBarrelScFi_inversePropagationSpeed, .fixedTimeDelay = EcalBarrelScFi_fixedTimeDelay, - .timeWindow = EcalBarrelScFi_timeWindow, + .timeWindow = EcalBarrelScFi_timeWindow, })); app->Add(new JOmniFactoryGeneratorT( "EcalBarrelScFiNAttenuatedHits", {"EcalBarrelScFiHits"}, @@ -89,25 +87,23 @@ void InitPlugin(JApplication* app) { .contributionMergeFields = EcalBarrelScFi_contributionMergeFields, .inversePropagationSpeed = EcalBarrelScFi_inversePropagationSpeed, .fixedTimeDelay = EcalBarrelScFi_fixedTimeDelay, - .timeWindow = EcalBarrelScFi_timeWindow, + .timeWindow = EcalBarrelScFi_timeWindow, })); app->Add(new JOmniFactoryGeneratorT>( - "EcalBarrelScFiPPulses", {"EcalBarrelScFiPAttenuatedHits"}, - {"EcalBarrelScFiPPulses"}, + "EcalBarrelScFiPPulses", {"EcalBarrelScFiPAttenuatedHits"}, {"EcalBarrelScFiPPulses"}, { - .pulse_shape_function = EcalBarrelScFi_pulse_shape_function, - .pulse_shape_params = EcalBarrelScFi_pulse_shape_params, - .ignore_thres = EcalBarrelScFi_ignore_thres, - .timestep = EcalBarrelScFi_timestep, + .pulse_shape_function = EcalBarrelScFi_pulse_shape_function, + .pulse_shape_params = EcalBarrelScFi_pulse_shape_params, + .ignore_thres = EcalBarrelScFi_ignore_thres, + .timestep = EcalBarrelScFi_timestep, })); app->Add(new JOmniFactoryGeneratorT>( - "EcalBarrelScFiNPulses", {"EcalBarrelScFiNAttenuatedHits"}, - {"EcalBarrelScFiNPulses"}, + "EcalBarrelScFiNPulses", {"EcalBarrelScFiNAttenuatedHits"}, {"EcalBarrelScFiNPulses"}, { - .pulse_shape_function = EcalBarrelScFi_pulse_shape_function, - .pulse_shape_params = EcalBarrelScFi_pulse_shape_params, - .ignore_thres = EcalBarrelScFi_ignore_thres, - .timestep = EcalBarrelScFi_timestep, + .pulse_shape_function = EcalBarrelScFi_pulse_shape_function, + .pulse_shape_params = EcalBarrelScFi_pulse_shape_params, + .ignore_thres = EcalBarrelScFi_ignore_thres, + .timestep = EcalBarrelScFi_timestep, })); app->Add(new JOmniFactoryGeneratorT( "EcalBarrelScFiRawHits", {"EventHeader", "EcalBarrelScFiHits"}, From 67876d3e886fed8a6e1148740ac1acfe6656c785 Mon Sep 17 00:00:00 2001 From: mhkim Date: Thu, 14 Aug 2025 18:19:41 -0500 Subject: [PATCH 06/20] Add addAssociations() in the HitAdapter --- src/algorithms/digi/PulseGeneration.cc | 19 +++++++++++++++++-- src/algorithms/digi/PulseGeneration.h | 12 ++++++++++-- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/algorithms/digi/PulseGeneration.cc b/src/algorithms/digi/PulseGeneration.cc index 93316f39fd..5fcfe85823 100644 --- a/src/algorithms/digi/PulseGeneration.cc +++ b/src/algorithms/digi/PulseGeneration.cc @@ -133,6 +133,14 @@ HitAdapter::getPulseSources(const edm4hep::SimTrackerHit return {hit.getTime(), hit.getEDep()}; } +#if EDM4EIC_VERSION_MAJOR > 8 || (EDM4EIC_VERSION_MAJOR == 8 && EDM4EIC_VERSION_MINOR >= 1) +void HitAdapter::addAssociations(MutablePulseType& pulse, + const edm4hep::SimTrackerHit& hit) { + pulse.addToTrackerHits(hit); + pulse.addToParticles(hit.getParticle()); +} +#endif + std::tuple HitAdapter::getPulseSources(const edm4hep::SimCalorimeterHit& hit) { const auto& contribs = hit.getContributions(); @@ -142,6 +150,14 @@ HitAdapter::getPulseSources(const edm4hep::SimCalori return {earliest_contrib->getTime(), hit.getEnergy()}; } +#if EDM4EIC_VERSION_MAJOR > 8 || (EDM4EIC_VERSION_MAJOR == 8 && EDM4EIC_VERSION_MINOR >= 1) +void HitAdapter::addAssociations( + MutablePulseType& pulse, const edm4hep::SimCalorimeterHit& hit) { + pulse.addToCalorimeterHits(hit); + pulse.addToParticles(hit.getContributions(0).getParticle()); +} +#endif + template void PulseGeneration::init() { m_pulse = PulseShapeFactory::createPulseShape(m_cfg.pulse_shape_function, m_cfg.pulse_shape_params); @@ -203,8 +219,7 @@ void PulseGeneration::process( time_series.setIntegral(integral); time_series.setPosition( edm4hep::Vector3f(hit.getPosition().x, hit.getPosition().y, hit.getPosition().z)); - time_series.addToTrackerHits(hit); - time_series.addToParticles(hit.getParticle()); + HitAdapter::addAssociations(time_series, hit); #endif } diff --git a/src/algorithms/digi/PulseGeneration.h b/src/algorithms/digi/PulseGeneration.h index 3b98cce1ab..d0596cde6c 100644 --- a/src/algorithms/digi/PulseGeneration.h +++ b/src/algorithms/digi/PulseGeneration.h @@ -26,19 +26,27 @@ namespace eicrecon { #if EDM4EIC_VERSION_MAJOR > 8 || (EDM4EIC_VERSION_MAJOR == 8 && EDM4EIC_VERSION_MINOR >= 1) -using PulseType = edm4eic::SimPulse; +using PulseType = edm4eic::SimPulse; +using MutablePulseType = edm4eic::MutableSimPulse; #else -using PulseType = edm4hep::TimeSeries; +using PulseType = edm4hep::TimeSeries; +using MutablePulseType = edm4hep::MutableTimeSeries; #endif template struct HitAdapter; template <> struct HitAdapter { static std::tuple getPulseSources(const edm4hep::SimTrackerHit& hit); +#if EDM4EIC_VERSION_MAJOR > 8 || (EDM4EIC_VERSION_MAJOR == 8 && EDM4EIC_VERSION_MINOR >= 1) + static void addAssociations(MutablePulseType& pulse, const edm4hep::SimTrackerHit& hit); +#endif }; template <> struct HitAdapter { static std::tuple getPulseSources(const edm4hep::SimCalorimeterHit& hit); +#if EDM4EIC_VERSION_MAJOR > 8 || (EDM4EIC_VERSION_MAJOR == 8 && EDM4EIC_VERSION_MINOR >= 1) + static void addAssociations(MutablePulseType& pulse, const edm4hep::SimCalorimeterHit& hit); +#endif }; template From 60a56fdb279f3f9dc7744a772858b6f926d436c0 Mon Sep 17 00:00:00 2001 From: mhkim Date: Fri, 15 Aug 2025 12:31:43 -0500 Subject: [PATCH 07/20] fix a typo --- src/algorithms/calorimetry/SimCalorimeterHitProcessor.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/algorithms/calorimetry/SimCalorimeterHitProcessor.cc b/src/algorithms/calorimetry/SimCalorimeterHitProcessor.cc index ecbbfc220c..2a11ab3f18 100644 --- a/src/algorithms/calorimetry/SimCalorimeterHitProcessor.cc +++ b/src/algorithms/calorimetry/SimCalorimeterHitProcessor.cc @@ -185,7 +185,7 @@ void SimCalorimeterHitProcessor::process(const SimCalorimeterHitProcessor::Input (ih.getCellID() & m_hit_id_mask.value() & m_contribution_id_mask.value()); // the cell ID of this particular contribution (we are using contributions to store // the hits making up this "superhit" with more segmentation) - const uint64_t newcontrib_cellID = (ih.getCellID() & m_hit_id_mask.value()); + const uint64_t newcontrib_cellID = (ih.getCellID() & m_contribution_id_mask.value()); // Optional attenuation const double attFactor = m_attenuationReferencePosition ? get_attenuation(ih.getPosition().z) : 1.; From 6268ba5700fd1d495564a1479b4e3e3730a98b6c Mon Sep 17 00:00:00 2001 From: mhkim Date: Fri, 15 Aug 2025 14:59:33 -0500 Subject: [PATCH 08/20] lower the ignore_thres sufficiently --- src/detectors/BEMC/BEMC.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/detectors/BEMC/BEMC.cc b/src/detectors/BEMC/BEMC.cc index 64e3b328dd..0905b76062 100644 --- a/src/detectors/BEMC/BEMC.cc +++ b/src/detectors/BEMC/BEMC.cc @@ -53,7 +53,7 @@ void InitPlugin(JApplication* app) { "LandauPulse"}; decltype(PulseGenerationConfig::pulse_shape_params) EcalBarrelScFi_pulse_shape_params = { 1.0, 2 * edm4eic::unit::ns}; - decltype(PulseGenerationConfig::ignore_thres) EcalBarrelScFi_ignore_thres = {1.0e-5}; + decltype(PulseGenerationConfig::ignore_thres) EcalBarrelScFi_ignore_thres = {1.0e-10}; decltype(PulseGenerationConfig::timestep) EcalBarrelScFi_timestep = {0.2 * edm4eic::unit::ns}; // Make sure digi and reco use the same value From 61417c8b461c3eda68abd95b8edc8476add248ca Mon Sep 17 00:00:00 2001 From: Dmitry Kalinkin Date: Wed, 20 Aug 2025 23:47:39 -0400 Subject: [PATCH 09/20] addAssociations() -> addRelations() Associations are 3rd party objects that relate to two other objects that they connect. Relations are just fields pointing to another objects. This method works with relations and not associations. --- src/algorithms/digi/PulseGeneration.cc | 6 +++--- src/algorithms/digi/PulseGeneration.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/algorithms/digi/PulseGeneration.cc b/src/algorithms/digi/PulseGeneration.cc index 5fcfe85823..774b923fa1 100644 --- a/src/algorithms/digi/PulseGeneration.cc +++ b/src/algorithms/digi/PulseGeneration.cc @@ -134,7 +134,7 @@ HitAdapter::getPulseSources(const edm4hep::SimTrackerHit } #if EDM4EIC_VERSION_MAJOR > 8 || (EDM4EIC_VERSION_MAJOR == 8 && EDM4EIC_VERSION_MINOR >= 1) -void HitAdapter::addAssociations(MutablePulseType& pulse, +void HitAdapter::addRelations(MutablePulseType& pulse, const edm4hep::SimTrackerHit& hit) { pulse.addToTrackerHits(hit); pulse.addToParticles(hit.getParticle()); @@ -151,7 +151,7 @@ HitAdapter::getPulseSources(const edm4hep::SimCalori } #if EDM4EIC_VERSION_MAJOR > 8 || (EDM4EIC_VERSION_MAJOR == 8 && EDM4EIC_VERSION_MINOR >= 1) -void HitAdapter::addAssociations( +void HitAdapter::addRelations( MutablePulseType& pulse, const edm4hep::SimCalorimeterHit& hit) { pulse.addToCalorimeterHits(hit); pulse.addToParticles(hit.getContributions(0).getParticle()); @@ -219,7 +219,7 @@ void PulseGeneration::process( time_series.setIntegral(integral); time_series.setPosition( edm4hep::Vector3f(hit.getPosition().x, hit.getPosition().y, hit.getPosition().z)); - HitAdapter::addAssociations(time_series, hit); + HitAdapter::addRelations(time_series, hit); #endif } diff --git a/src/algorithms/digi/PulseGeneration.h b/src/algorithms/digi/PulseGeneration.h index d0596cde6c..4de7954f88 100644 --- a/src/algorithms/digi/PulseGeneration.h +++ b/src/algorithms/digi/PulseGeneration.h @@ -38,14 +38,14 @@ template struct HitAdapter; template <> struct HitAdapter { static std::tuple getPulseSources(const edm4hep::SimTrackerHit& hit); #if EDM4EIC_VERSION_MAJOR > 8 || (EDM4EIC_VERSION_MAJOR == 8 && EDM4EIC_VERSION_MINOR >= 1) - static void addAssociations(MutablePulseType& pulse, const edm4hep::SimTrackerHit& hit); + static void addRelations(MutablePulseType& pulse, const edm4hep::SimTrackerHit& hit); #endif }; template <> struct HitAdapter { static std::tuple getPulseSources(const edm4hep::SimCalorimeterHit& hit); #if EDM4EIC_VERSION_MAJOR > 8 || (EDM4EIC_VERSION_MAJOR == 8 && EDM4EIC_VERSION_MINOR >= 1) - static void addAssociations(MutablePulseType& pulse, const edm4hep::SimCalorimeterHit& hit); + static void addRelations(MutablePulseType& pulse, const edm4hep::SimCalorimeterHit& hit); #endif }; From de3dd84b5ba4b154f7eeddf127501d15ee67522d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 21 Aug 2025 03:51:10 +0000 Subject: [PATCH 10/20] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/algorithms/digi/PulseGeneration.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/algorithms/digi/PulseGeneration.cc b/src/algorithms/digi/PulseGeneration.cc index 774b923fa1..3c13147017 100644 --- a/src/algorithms/digi/PulseGeneration.cc +++ b/src/algorithms/digi/PulseGeneration.cc @@ -135,7 +135,7 @@ HitAdapter::getPulseSources(const edm4hep::SimTrackerHit #if EDM4EIC_VERSION_MAJOR > 8 || (EDM4EIC_VERSION_MAJOR == 8 && EDM4EIC_VERSION_MINOR >= 1) void HitAdapter::addRelations(MutablePulseType& pulse, - const edm4hep::SimTrackerHit& hit) { + const edm4hep::SimTrackerHit& hit) { pulse.addToTrackerHits(hit); pulse.addToParticles(hit.getParticle()); } @@ -151,8 +151,8 @@ HitAdapter::getPulseSources(const edm4hep::SimCalori } #if EDM4EIC_VERSION_MAJOR > 8 || (EDM4EIC_VERSION_MAJOR == 8 && EDM4EIC_VERSION_MINOR >= 1) -void HitAdapter::addRelations( - MutablePulseType& pulse, const edm4hep::SimCalorimeterHit& hit) { +void HitAdapter::addRelations(MutablePulseType& pulse, + const edm4hep::SimCalorimeterHit& hit) { pulse.addToCalorimeterHits(hit); pulse.addToParticles(hit.getContributions(0).getParticle()); } From 172b9422f7f24c943faf98bc0c541052e7db86a9 Mon Sep 17 00:00:00 2001 From: Wouter Deconinck Date: Wed, 20 Aug 2025 23:04:01 -0500 Subject: [PATCH 11/20] Pulse generation from time-clustered Simcalorimeterhits using a shared template (fix: iwyu) (#2025) This PR applies the include-what-you-use fixes as suggested by https://github.com/eic/EICrecon/actions/runs/17116586226. Please merge this PR into the branch `2001-pulse-generation-from-time-clustered-simcalorimeterhits-using` to resolve failures in PR #2004. Auto-generated by [create-pull-request][1] [1]: https://github.com/peter-evans/create-pull-request Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/algorithms/digi/PulseGeneration.cc | 8 ++++++-- src/algorithms/digi/PulseGeneration.h | 5 +++-- src/detectors/BEMC/BEMC.cc | 7 ++++++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/algorithms/digi/PulseGeneration.cc b/src/algorithms/digi/PulseGeneration.cc index 3c13147017..b2212b5537 100644 --- a/src/algorithms/digi/PulseGeneration.cc +++ b/src/algorithms/digi/PulseGeneration.cc @@ -10,14 +10,18 @@ #include #include #include -#include +#include +#include #include +#include +#include +#include #include #include #include #include -#include #include +#include #include #include diff --git a/src/algorithms/digi/PulseGeneration.h b/src/algorithms/digi/PulseGeneration.h index 4de7954f88..4e5ad0848d 100644 --- a/src/algorithms/digi/PulseGeneration.h +++ b/src/algorithms/digi/PulseGeneration.h @@ -9,16 +9,17 @@ #include #include #include -#include #include +#include #if EDM4EIC_VERSION_MAJOR > 8 || (EDM4EIC_VERSION_MAJOR == 8 && EDM4EIC_VERSION_MINOR >= 1) #include #else #include #endif #include -#include #include +#include +#include #include "algorithms/digi/PulseGenerationConfig.h" #include "algorithms/interfaces/WithPodConfig.h" diff --git a/src/detectors/BEMC/BEMC.cc b/src/detectors/BEMC/BEMC.cc index 2520f6686b..9d0d570e2d 100644 --- a/src/detectors/BEMC/BEMC.cc +++ b/src/detectors/BEMC/BEMC.cc @@ -2,16 +2,21 @@ // Copyright (C) 2022 - 2025 Whitney Armstrong, Sylvester Joosten, Chao Peng, David Lawrence, Thomas Britton, Wouter Deconinck, Maria Zurek, Akshaya Vijay, Nathan Brei, Dmitry Kalinkin, Derek Anderson, Minho Kim #include +#include #include #include #include +#include #include +#include +#include #include #include #include #include "algorithms/calorimetry/CalorimeterHitDigiConfig.h" #include "algorithms/calorimetry/SimCalorimeterHitProcessorConfig.h" +#include "algorithms/digi/PulseGenerationConfig.h" #include "extensions/jana/JOmniFactoryGeneratorT.h" #include "factories/calorimetry/CalorimeterClusterRecoCoG_factory.h" #include "factories/calorimetry/CalorimeterClusterShape_factory.h" @@ -22,8 +27,8 @@ #include "factories/calorimetry/ImagingClusterReco_factory.h" #include "factories/calorimetry/ImagingTopoCluster_factory.h" #include "factories/calorimetry/SimCalorimeterHitProcessor_factory.h" -#include "factories/digi/PulseGeneration_factory.h" #include "factories/calorimetry/TruthEnergyPositionClusterMerger_factory.h" +#include "factories/digi/PulseGeneration_factory.h" extern "C" { void InitPlugin(JApplication* app) { From 05fafa1376d7749ad86cd37fb498e0b0273d7686 Mon Sep 17 00:00:00 2001 From: Dmitry Kalinkin Date: Thu, 21 Aug 2025 23:02:05 -0400 Subject: [PATCH 12/20] disable saving EcalBarrelScFi{P,N}Pulses - file size is too large --- src/services/io/podio/JEventProcessorPODIO.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/services/io/podio/JEventProcessorPODIO.cc b/src/services/io/podio/JEventProcessorPODIO.cc index da4476a575..01d6af2102 100644 --- a/src/services/io/podio/JEventProcessorPODIO.cc +++ b/src/services/io/podio/JEventProcessorPODIO.cc @@ -298,8 +298,6 @@ JEventProcessorPODIO::JEventProcessorPODIO() { "EcalBarrelScFiPAttenuatedHits", "EcalBarrelScFiPAttenuatedHitContributions", "EcalBarrelScFiNAttenuatedHits", - "EcalBarrelScFiPPulses", - "EcalBarrelScFiNPulses", "EcalBarrelScFiNAttenuatedHitContributions", "EcalBarrelScFiRawHits", "EcalBarrelScFiRecHits", From 25572284aa2d5740ea08eb9ee6a0863471982fcc Mon Sep 17 00:00:00 2001 From: Dmitry Kalinkin Date: Fri, 22 Aug 2025 01:15:24 -0400 Subject: [PATCH 13/20] replace SiliconPulseGeneration with PulseGeneration --- src/algorithms/digi/SiliconPulseGeneration.cc | 200 ------------------ src/algorithms/digi/SiliconPulseGeneration.h | 52 ----- .../digi/SiliconPulseGenerationConfig.h | 20 -- src/detectors/BTOF/BTOF.cc | 4 +- src/detectors/LOWQ2/LOWQ2.cc | 4 +- .../digi/SiliconPulseGeneration_factory.h | 52 ----- src/tests/algorithms_test/CMakeLists.txt | 2 +- ...eGeneration.cc => digi_PulseGeneration.cc} | 16 +- 8 files changed, 13 insertions(+), 337 deletions(-) delete mode 100644 src/algorithms/digi/SiliconPulseGeneration.cc delete mode 100644 src/algorithms/digi/SiliconPulseGeneration.h delete mode 100644 src/algorithms/digi/SiliconPulseGenerationConfig.h delete mode 100644 src/factories/digi/SiliconPulseGeneration_factory.h rename src/tests/algorithms_test/{digi_SiliconPulseGeneration.cc => digi_PulseGeneration.cc} (87%) diff --git a/src/algorithms/digi/SiliconPulseGeneration.cc b/src/algorithms/digi/SiliconPulseGeneration.cc deleted file mode 100644 index 5f3fa42573..0000000000 --- a/src/algorithms/digi/SiliconPulseGeneration.cc +++ /dev/null @@ -1,200 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-later -// Copyright (C) 2024-2025 Simon Gardner, Chun Yuen Tsang, Prithwish Tribedy -// -// Convert energy deposition into ADC pulses -// ADC pulses are assumed to follow the shape of landau function - -#include "SiliconPulseGeneration.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "services/evaluator/EvaluatorSvc.h" - -namespace eicrecon { - -class SignalPulse { - -public: - virtual ~SignalPulse() = default; // Virtual destructor - - virtual double operator()(double time, double charge) = 0; - - virtual double getMaximumTime() const = 0; -}; - -// ---------------------------------------------------------------------------- -// Landau Pulse Shape Functor -// ---------------------------------------------------------------------------- -class LandauPulse : public SignalPulse { -public: - LandauPulse(std::vector params) { - - if ((params.size() != 2) && (params.size() != 3)) { - throw std::runtime_error( - "LandauPulse requires 2 or 3 parameters, gain, sigma_analog, [hit_sigma_offset], got " + - std::to_string(params.size())); - } - - m_gain = params[0]; - m_sigma_analog = params[1]; - if (params.size() == 3) { - m_hit_sigma_offset = params[2]; - } - }; - - double operator()(double time, double charge) override { - return charge * m_gain * - TMath::Landau(time, m_hit_sigma_offset * m_sigma_analog, m_sigma_analog, kTRUE); - } - - double getMaximumTime() const override { return m_hit_sigma_offset * m_sigma_analog; } - -private: - double m_gain = 1.0; - double m_sigma_analog = 1.0; - double m_hit_sigma_offset = 3.5; -}; - -// EvaluatorSvc Pulse -class EvaluatorPulse : public SignalPulse { -public: - EvaluatorPulse(const std::string& expression, const std::vector& params) { - - std::vector keys = {"time", "charge"}; - for (std::size_t i = 0; i < params.size(); i++) { - std::string p = "param" + std::to_string(i); - //Check the expression contains the parameter - if (expression.find(p) == std::string::npos) { - throw std::runtime_error("Parameter " + p + " not found in expression"); - } - keys.push_back(p); - param_map[p] = params[i]; - } - - // Check the expression is contains time and charge - if (expression.find("time") == std::string::npos) { - throw std::runtime_error("Parameter [time] not found in expression"); - } - if (expression.find("charge") == std::string::npos) { - throw std::runtime_error("Parameter [charge] not found in expression"); - } - - auto& serviceSvc = algorithms::ServiceSvc::instance(); - m_evaluator = serviceSvc.service("EvaluatorSvc")->_compile(expression, keys); - }; - - double operator()(double time, double charge) override { - param_map["time"] = time; - param_map["charge"] = charge; - return m_evaluator(param_map); - } - - double getMaximumTime() const override { return 0; } - -private: - std::unordered_map param_map; - std::function&)> m_evaluator; -}; - -class PulseShapeFactory { -public: - static std::unique_ptr createPulseShape(const std::string& type, - const std::vector& params) { - if (type == "LandauPulse") { - return std::make_unique(params); - } - // - // Add more pulse shape variants here as needed - - // If type not found, try and make a function using the ElavulatorSvc - try { - return std::make_unique(type, params); - } catch (...) { - throw std::invalid_argument("Unable to make pulse shape type: " + type); - } - } -}; - -void SiliconPulseGeneration::init() { - m_pulse = - PulseShapeFactory::createPulseShape(m_cfg.pulse_shape_function, m_cfg.pulse_shape_params); - m_min_sampling_time = m_cfg.min_sampling_time; - - if (m_pulse->getMaximumTime() > m_min_sampling_time) { - m_min_sampling_time = m_pulse->getMaximumTime(); - } -} - -void SiliconPulseGeneration::process(const SiliconPulseGeneration::Input& input, - const SiliconPulseGeneration::Output& output) const { - const auto [simhits] = input; - auto [rawPulses] = output; - - for (const auto& hit : *simhits) { - - auto cellID = hit.getCellID(); - double time = hit.getTime(); - double charge = hit.getEDep(); - - // Calculate nearest timestep to the hit time rounded down (assume clocks aligned with time 0) - double signal_time = m_cfg.timestep * std::floor(time / m_cfg.timestep); - - bool passed_threshold = false; - std::uint32_t skip_bins = 0; - float integral = 0; - std::vector pulse; - - for (std::uint32_t i = 0; i < m_cfg.max_time_bins; i++) { - double t = signal_time + i * m_cfg.timestep - time; - auto signal = (*m_pulse)(t, charge); - if (std::abs(signal) < m_cfg.ignore_thres) { - if (!passed_threshold) { - skip_bins = i; - continue; - } - if (t > m_min_sampling_time) { - break; - } - } - passed_threshold = true; - pulse.push_back(signal); - integral += signal; - } - - if (!passed_threshold) { - continue; - } - - auto time_series = rawPulses->create(); - time_series.setCellID(cellID); - time_series.setInterval(m_cfg.timestep); - time_series.setTime(signal_time + skip_bins * m_cfg.timestep); - - for (const auto& value : pulse) { - time_series.addToAmplitude(value); - } - -#if EDM4EIC_VERSION_MAJOR > 8 || (EDM4EIC_VERSION_MAJOR == 8 && EDM4EIC_VERSION_MINOR >= 1) - time_series.setIntegral(integral); - time_series.setPosition( - edm4hep::Vector3f(hit.getPosition().x, hit.getPosition().y, hit.getPosition().z)); - time_series.addToTrackerHits(hit); - time_series.addToParticles(hit.getParticle()); -#endif - } - -} // SiliconPulseGeneration:process - -} // namespace eicrecon diff --git a/src/algorithms/digi/SiliconPulseGeneration.h b/src/algorithms/digi/SiliconPulseGeneration.h deleted file mode 100644 index 35b221f6e2..0000000000 --- a/src/algorithms/digi/SiliconPulseGeneration.h +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-later -// Copyright (C) 2024-2025 Simon Gardner, Chun Yuen Tsang, Prithwish Tribedy -// -// Convert energy deposition into analog pulses - -#pragma once - -#include -#include -#include -#include -#if EDM4EIC_VERSION_MAJOR > 8 || (EDM4EIC_VERSION_MAJOR == 8 && EDM4EIC_VERSION_MINOR >= 1) -#include -#else -#include -#endif -#include -#include -#include - -#include "algorithms/digi/SiliconPulseGenerationConfig.h" -#include "algorithms/interfaces/WithPodConfig.h" - -namespace eicrecon { - -#if EDM4EIC_VERSION_MAJOR > 8 || (EDM4EIC_VERSION_MAJOR == 8 && EDM4EIC_VERSION_MINOR >= 1) -using PulseType = edm4eic::SimPulse; -#else -using PulseType = edm4hep::TimeSeries; -#endif - -using SiliconPulseGenerationAlgorithm = - algorithms::Algorithm, - algorithms::Output>; - -class SignalPulse; - -class SiliconPulseGeneration : public SiliconPulseGenerationAlgorithm, - public WithPodConfig { - -public: - SiliconPulseGeneration(std::string_view name) - : SiliconPulseGenerationAlgorithm{name, {"RawHits"}, {"OutputPulses"}, {}} {} - void init() final; - void process(const Input&, const Output&) const final; - -private: - std::shared_ptr m_pulse; - float m_min_sampling_time = 0 * edm4eic::unit::ns; -}; - -} // namespace eicrecon diff --git a/src/algorithms/digi/SiliconPulseGenerationConfig.h b/src/algorithms/digi/SiliconPulseGenerationConfig.h deleted file mode 100644 index 6e4614cf15..0000000000 --- a/src/algorithms/digi/SiliconPulseGenerationConfig.h +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-later -// Copyright (C) 2025 Simon Gardner - -#pragma once - -#include - -namespace eicrecon { - -struct SiliconPulseGenerationConfig { - // Parameters of Silicon signal generation - std::string pulse_shape_function = "LandauPulse"; // Pulse shape function - std::vector pulse_shape_params = {1.0, 0.1}; // Parameters of the pulse shape function - double ignore_thres = 10; // When EDep drops below this value pulse stops - double timestep = 0.2 * edm4eic::unit::ns; // Minimum digitization time step - double min_sampling_time = 0 * edm4eic::unit::ns; // Minimum sampling time - uint32_t max_time_bins = 10000; -}; - -} // namespace eicrecon diff --git a/src/detectors/BTOF/BTOF.cc b/src/detectors/BTOF/BTOF.cc index e883e360c6..773421b2b1 100644 --- a/src/detectors/BTOF/BTOF.cc +++ b/src/detectors/BTOF/BTOF.cc @@ -18,9 +18,9 @@ #include "extensions/jana/JOmniFactoryGeneratorT.h" #include "factories/digi/CFDROCDigitization_factory.h" #include "factories/digi/PulseCombiner_factory.h" +#include "factories/digi/PulseGeneration_factory.h" #include "factories/digi/SiliconChargeSharing_factory.h" #include "factories/digi/SiliconPulseDiscretization_factory.h" -#include "factories/digi/SiliconPulseGeneration_factory.h" #include "factories/digi/SiliconTrackerDigi_factory.h" #include "factories/tracking/TrackerHitReconstruction_factory.h" @@ -72,7 +72,7 @@ void InitPlugin(JApplication* app) { // gain is negative as LGAD voltage is always negative const double gain = -adc_range / Vm / landau_min * sigma_analog; const int offset = 3; - app->Add(new JOmniFactoryGeneratorT( + app->Add(new JOmniFactoryGeneratorT>( "LGADPulseGeneration", {"TOFBarrelSharedHits"}, {"TOFBarrelSmoothPulses"}, { .pulse_shape_function = "LandauPulse", diff --git a/src/detectors/LOWQ2/LOWQ2.cc b/src/detectors/LOWQ2/LOWQ2.cc index 59e9ec8dab..46e3c639ea 100644 --- a/src/detectors/LOWQ2/LOWQ2.cc +++ b/src/detectors/LOWQ2/LOWQ2.cc @@ -25,9 +25,9 @@ #include "algorithms/meta/SubDivideFunctors.h" #include "extensions/jana/JOmniFactoryGeneratorT.h" #include "factories/digi/PulseCombiner_factory.h" +#include "factories/digi/PulseGeneration_factory.h" #include "factories/digi/PulseNoise_factory.h" #include "factories/digi/SiliconChargeSharing_factory.h" -#include "factories/digi/SiliconPulseGeneration_factory.h" #include "factories/digi/SiliconTrackerDigi_factory.h" #include "factories/fardetectors/FarDetectorLinearProjection_factory.h" #include "factories/fardetectors/FarDetectorLinearTracking_factory.h" @@ -62,7 +62,7 @@ void InitPlugin(JApplication* app) { }, app)); // Generate signal pulse from hits - app->Add(new JOmniFactoryGeneratorT( + app->Add(new JOmniFactoryGeneratorT>( "TaggerTrackerPulseGeneration", {"TaggerTrackerSharedHits"}, {"TaggerTrackerHitPulses"}, { .pulse_shape_function = "LandauPulse", diff --git a/src/factories/digi/SiliconPulseGeneration_factory.h b/src/factories/digi/SiliconPulseGeneration_factory.h deleted file mode 100644 index 9a6b946fd5..0000000000 --- a/src/factories/digi/SiliconPulseGeneration_factory.h +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-later -// Copyright (C) 2024-2025 Simon Gardner, Chun Yuen Tsang, Prithwish Tribedy -// - -#pragma once - -#include "algorithms/digi/SiliconPulseGeneration.h" -#include "services/algorithms_init/AlgorithmsInit_service.h" -#include "extensions/jana/JOmniFactory.h" - -namespace eicrecon { - -class SiliconPulseGeneration_factory - : public JOmniFactory { -public: - using AlgoT = eicrecon::SiliconPulseGeneration; - -private: - std::unique_ptr m_algo; - - PodioInput m_in_sim_hits{this}; -#if EDM4EIC_VERSION_MAJOR > 8 || (EDM4EIC_VERSION_MAJOR == 8 && EDM4EIC_VERSION_MINOR >= 1) - PodioOutput m_out_pulses{this}; -#else - PodioOutput m_out_pulses{this}; -#endif - - ParameterRef m_pulse_shape_function{this, "pulseShapeFunction", - config().pulse_shape_function}; - ParameterRef> m_pulse_shape_params{this, "pulseShapeParams", - config().pulse_shape_params}; - ParameterRef m_timestep{this, "timestep", config().timestep}; - ParameterRef m_ignore_thres{this, "ignoreThreshold", config().ignore_thres}; - ParameterRef m_min_sampling_time{this, "minSamplingTime", config().min_sampling_time}; - ParameterRef m_max_time_bins{this, "maxTimeBins", config().max_time_bins}; - - Service m_algorithmsInit{this}; - -public: - void Configure() { - m_algo = std::make_unique(GetPrefix()); - m_algo->level(static_cast(logger()->level())); - m_algo->applyConfig(config()); - m_algo->init(); - } - - void Process(int32_t /* run_number */, uint64_t /* event_number */) { - m_algo->process({m_in_sim_hits()}, {m_out_pulses().get()}); - } -}; - -} // namespace eicrecon diff --git a/src/tests/algorithms_test/CMakeLists.txt b/src/tests/algorithms_test/CMakeLists.txt index 6692ebfcc7..73092c02f9 100644 --- a/src/tests/algorithms_test/CMakeLists.txt +++ b/src/tests/algorithms_test/CMakeLists.txt @@ -12,8 +12,8 @@ add_executable( calorimetry_CalorimeterClusterRecoCoG.cc calorimetry_CalorimeterClusterShape.cc calorimetry_HEXPLIT.cc - digi_SiliconPulseGeneration.cc digi_EICROCDigitization.cc + digi_PulseGeneration.cc pid_MergeTracks.cc pid_MergeParticleID.cc pid_lut_PIDLookup.cc) diff --git a/src/tests/algorithms_test/digi_SiliconPulseGeneration.cc b/src/tests/algorithms_test/digi_PulseGeneration.cc similarity index 87% rename from src/tests/algorithms_test/digi_SiliconPulseGeneration.cc rename to src/tests/algorithms_test/digi_PulseGeneration.cc index cf811c08dc..7ed9ed6ba8 100644 --- a/src/tests/algorithms_test/digi_SiliconPulseGeneration.cc +++ b/src/tests/algorithms_test/digi_PulseGeneration.cc @@ -19,8 +19,8 @@ #include #include -#include "algorithms/digi/SiliconPulseGeneration.h" -#include "algorithms/digi/SiliconPulseGenerationConfig.h" +#include "algorithms/digi/PulseGeneration.h" +#include "algorithms/digi/PulseGenerationConfig.h" #if EDM4EIC_VERSION_MAJOR > 8 || (EDM4EIC_VERSION_MAJOR == 8 && EDM4EIC_VERSION_MINOR >= 1) using PulseType = edm4eic::SimPulse; @@ -28,10 +28,10 @@ using PulseType = edm4eic::SimPulse; using PulseType = edm4hep::TimeSeries; #endif -TEST_CASE("SiliconPulseGeneration generates correct number of pulses", "[SiliconPulseGeneration]") { +TEST_CASE("PulseGeneration generates correct number of pulses", "[PulseGeneration]") { - eicrecon::SiliconPulseGeneration algo("SiliconPulseGeneration"); - eicrecon::SiliconPulseGenerationConfig cfg; + eicrecon::PulseGeneration algo("PulseGeneration"); + eicrecon::PulseGenerationConfig cfg; cfg.pulse_shape_function = "LandauPulse"; // Example pulse shape cfg.pulse_shape_params = {1.0, 1.0}; // Example parameters for the pulse shape cfg.ignore_thres = 1; @@ -67,10 +67,10 @@ TEST_CASE("SiliconPulseGeneration generates correct number of pulses", "[Silicon } TEST_CASE("Test the EvaluatorSvc pulse generation with a square pulse", - "[SiliconPulseGeneration]") { + "[PulseGeneration]") { - eicrecon::SiliconPulseGeneration algo("SiliconPulseGeneration"); - eicrecon::SiliconPulseGenerationConfig cfg; + eicrecon::PulseGeneration algo("PulseGeneration"); + eicrecon::PulseGenerationConfig cfg; // Square wave expression std::string expression = "(time >= param0 && time < param1) ? charge : 0"; From 3c62d7f304f07f185e80c9186895cfd32355cb02 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 22 Aug 2025 05:15:38 +0000 Subject: [PATCH 14/20] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/tests/algorithms_test/digi_PulseGeneration.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tests/algorithms_test/digi_PulseGeneration.cc b/src/tests/algorithms_test/digi_PulseGeneration.cc index 7ed9ed6ba8..94201fbdcf 100644 --- a/src/tests/algorithms_test/digi_PulseGeneration.cc +++ b/src/tests/algorithms_test/digi_PulseGeneration.cc @@ -66,8 +66,7 @@ TEST_CASE("PulseGeneration generates correct number of pulses", "[PulseGeneratio } } -TEST_CASE("Test the EvaluatorSvc pulse generation with a square pulse", - "[PulseGeneration]") { +TEST_CASE("Test the EvaluatorSvc pulse generation with a square pulse", "[PulseGeneration]") { eicrecon::PulseGeneration algo("PulseGeneration"); eicrecon::PulseGenerationConfig cfg; From eac79273a8b85f2e85b3cea0754fa7de5bf14499 Mon Sep 17 00:00:00 2001 From: Wouter Deconinck Date: Fri, 22 Aug 2025 12:31:59 -0500 Subject: [PATCH 15/20] Pulse generation from time-clustered Simcalorimeterhits using a shared template (fix: iwyu) (#2030) This PR applies the include-what-you-use fixes as suggested by https://github.com/eic/EICrecon/actions/runs/17146753827. Please merge this PR into the branch `2001-pulse-generation-from-time-clustered-simcalorimeterhits-using` to resolve failures in PR #2004. Auto-generated by [create-pull-request][1] [1]: https://github.com/peter-evans/create-pull-request Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/detectors/BTOF/BTOF.cc | 4 ++++ src/detectors/LOWQ2/LOWQ2.cc | 1 + 2 files changed, 5 insertions(+) diff --git a/src/detectors/BTOF/BTOF.cc b/src/detectors/BTOF/BTOF.cc index 773421b2b1..38b39354c4 100644 --- a/src/detectors/BTOF/BTOF.cc +++ b/src/detectors/BTOF/BTOF.cc @@ -6,11 +6,15 @@ // Copyright (C) 2024, Dmitry Kalinkin #include +#include #include #include #include #include +#include #include +#include +#include #include #include diff --git a/src/detectors/LOWQ2/LOWQ2.cc b/src/detectors/LOWQ2/LOWQ2.cc index 46e3c639ea..7310799f0d 100644 --- a/src/detectors/LOWQ2/LOWQ2.cc +++ b/src/detectors/LOWQ2/LOWQ2.cc @@ -12,6 +12,7 @@ #include #include #include +#include #include #include // IWYU pragma: keep #include From fcb1c3f2dff1b86f4235f6eaceda0169b75c0724 Mon Sep 17 00:00:00 2001 From: mhkim Date: Fri, 22 Aug 2025 14:27:47 -0500 Subject: [PATCH 16/20] updated the ignore_thres value --- src/detectors/BEMC/BEMC.cc | 2 +- src/services/io/podio/JEventProcessorPODIO.cc | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/detectors/BEMC/BEMC.cc b/src/detectors/BEMC/BEMC.cc index 9d0d570e2d..3ee7b18391 100644 --- a/src/detectors/BEMC/BEMC.cc +++ b/src/detectors/BEMC/BEMC.cc @@ -55,7 +55,7 @@ void InitPlugin(JApplication* app) { "LandauPulse"}; decltype(PulseGenerationConfig::pulse_shape_params) EcalBarrelScFi_pulse_shape_params = { 1.0, 2 * edm4eic::unit::ns}; - decltype(PulseGenerationConfig::ignore_thres) EcalBarrelScFi_ignore_thres = {1.0e-10}; + decltype(PulseGenerationConfig::ignore_thres) EcalBarrelScFi_ignore_thres = {1.0e-6}; decltype(PulseGenerationConfig::timestep) EcalBarrelScFi_timestep = {0.2 * edm4eic::unit::ns}; // Make sure digi and reco use the same value diff --git a/src/services/io/podio/JEventProcessorPODIO.cc b/src/services/io/podio/JEventProcessorPODIO.cc index 01d6af2102..ebe6c721b7 100644 --- a/src/services/io/podio/JEventProcessorPODIO.cc +++ b/src/services/io/podio/JEventProcessorPODIO.cc @@ -299,6 +299,8 @@ JEventProcessorPODIO::JEventProcessorPODIO() { "EcalBarrelScFiPAttenuatedHitContributions", "EcalBarrelScFiNAttenuatedHits", "EcalBarrelScFiNAttenuatedHitContributions", + "EcalBarrelScFiPPulses", + "EcalBarrelScFiNPulses", "EcalBarrelScFiRawHits", "EcalBarrelScFiRecHits", "EcalBarrelScFiClusters", From 71c4ee0d7cf4f020b0e7522b2a277aa4504174d3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 22 Aug 2025 19:28:22 +0000 Subject: [PATCH 17/20] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/services/io/podio/JEventProcessorPODIO.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/io/podio/JEventProcessorPODIO.cc b/src/services/io/podio/JEventProcessorPODIO.cc index ebe6c721b7..7165036d56 100644 --- a/src/services/io/podio/JEventProcessorPODIO.cc +++ b/src/services/io/podio/JEventProcessorPODIO.cc @@ -300,7 +300,7 @@ JEventProcessorPODIO::JEventProcessorPODIO() { "EcalBarrelScFiNAttenuatedHits", "EcalBarrelScFiNAttenuatedHitContributions", "EcalBarrelScFiPPulses", - "EcalBarrelScFiNPulses", + "EcalBarrelScFiNPulses", "EcalBarrelScFiRawHits", "EcalBarrelScFiRecHits", "EcalBarrelScFiClusters", From 027c3d3f65ad6e323c2c6494ef1d9546c782f7fe Mon Sep 17 00:00:00 2001 From: mhkim Date: Sun, 24 Aug 2025 18:05:22 -0500 Subject: [PATCH 18/20] Remove EcalBarrelScFi{P,N}Pulses --- src/services/io/podio/JEventProcessorPODIO.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/services/io/podio/JEventProcessorPODIO.cc b/src/services/io/podio/JEventProcessorPODIO.cc index 7165036d56..01d6af2102 100644 --- a/src/services/io/podio/JEventProcessorPODIO.cc +++ b/src/services/io/podio/JEventProcessorPODIO.cc @@ -299,8 +299,6 @@ JEventProcessorPODIO::JEventProcessorPODIO() { "EcalBarrelScFiPAttenuatedHitContributions", "EcalBarrelScFiNAttenuatedHits", "EcalBarrelScFiNAttenuatedHitContributions", - "EcalBarrelScFiPPulses", - "EcalBarrelScFiNPulses", "EcalBarrelScFiRawHits", "EcalBarrelScFiRecHits", "EcalBarrelScFiClusters", From e9a7938d3545d8773632f139be55b8ea4ede693f Mon Sep 17 00:00:00 2001 From: mhkim Date: Wed, 27 Aug 2025 14:58:06 -0500 Subject: [PATCH 19/20] Optimized ignore_thres and timestep --- src/detectors/BEMC/BEMC.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/detectors/BEMC/BEMC.cc b/src/detectors/BEMC/BEMC.cc index 3ee7b18391..40a3fd9502 100644 --- a/src/detectors/BEMC/BEMC.cc +++ b/src/detectors/BEMC/BEMC.cc @@ -55,8 +55,8 @@ void InitPlugin(JApplication* app) { "LandauPulse"}; decltype(PulseGenerationConfig::pulse_shape_params) EcalBarrelScFi_pulse_shape_params = { 1.0, 2 * edm4eic::unit::ns}; - decltype(PulseGenerationConfig::ignore_thres) EcalBarrelScFi_ignore_thres = {1.0e-6}; - decltype(PulseGenerationConfig::timestep) EcalBarrelScFi_timestep = {0.2 * edm4eic::unit::ns}; + decltype(PulseGenerationConfig::ignore_thres) EcalBarrelScFi_ignore_thres = {1.0e-5}; + decltype(PulseGenerationConfig::timestep) EcalBarrelScFi_timestep = {0.5 * edm4eic::unit::ns}; // Make sure digi and reco use the same value decltype(CalorimeterHitDigiConfig::capADC) EcalBarrelScFi_capADC = 16384; //16384, 14bit ADC From e7792abc035128f9b72737bb29ee2a8279a12afc Mon Sep 17 00:00:00 2001 From: Wouter Deconinck Date: Tue, 9 Sep 2025 17:40:36 -0500 Subject: [PATCH 20/20] Pulse generation from time-clustered Simcalorimeterhits using a shared template (fix: iwyu) (#2073) This PR applies the include-what-you-use fixes as suggested by https://github.com/eic/EICrecon/actions/runs/17595549256. Please merge this PR into the branch `2001-pulse-generation-from-time-clustered-simcalorimeterhits-using` to resolve failures in PR #2004. Auto-generated by [create-pull-request][1] [1]: https://github.com/peter-evans/create-pull-request Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/algorithms/calorimetry/SimCalorimeterHitProcessor.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/algorithms/calorimetry/SimCalorimeterHitProcessor.cc b/src/algorithms/calorimetry/SimCalorimeterHitProcessor.cc index 2a11ab3f18..77bc6b9915 100644 --- a/src/algorithms/calorimetry/SimCalorimeterHitProcessor.cc +++ b/src/algorithms/calorimetry/SimCalorimeterHitProcessor.cc @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include