File indexing completed on 2025-01-30 10:04:50
0001
0002
0003
0004 #pragma once
0005
0006 #include <DD4hep/Detector.h>
0007 #include <DD4hep/IDDescriptor.h>
0008 #include <algorithms/algorithm.h>
0009 #include <algorithms/geo.h>
0010 #include <edm4eic/CalorimeterHitCollection.h>
0011 #include <edm4eic/ProtoClusterCollection.h>
0012 #include <edm4hep/Vector2f.h>
0013 #include <edm4hep/utils/vector_utils.h>
0014 #include <fmt/core.h>
0015 #include <array>
0016 #include <cmath>
0017 #include <cstddef>
0018 #include <functional>
0019 #include <gsl/pointers>
0020 #include <set>
0021 #include <string>
0022 #include <string_view>
0023 #include <vector>
0024
0025 #include "CalorimeterIslandClusterConfig.h"
0026 #include "algorithms/interfaces/WithPodConfig.h"
0027
0028 namespace eicrecon {
0029
0030 using CaloHit = edm4eic::CalorimeterHit;
0031
0032 using CalorimeterIslandClusterAlgorithm = algorithms::Algorithm<
0033 algorithms::Input<
0034 edm4eic::CalorimeterHitCollection
0035 >,
0036 algorithms::Output<
0037 edm4eic::ProtoClusterCollection
0038 >
0039 >;
0040
0041 class CalorimeterIslandCluster
0042 : public CalorimeterIslandClusterAlgorithm,
0043 public WithPodConfig<CalorimeterIslandClusterConfig> {
0044
0045 public:
0046 CalorimeterIslandCluster(std::string_view name)
0047 : CalorimeterIslandClusterAlgorithm{name,
0048 {"inputProtoClusterCollection"},
0049 {"outputClusterCollection"},
0050 "Island clustering."} {}
0051
0052 void init() final;
0053 void process(const Input&, const Output&) const final;
0054
0055 private:
0056 const dd4hep::Detector* m_detector{algorithms::GeoSvc::instance().detector()};
0057
0058 public:
0059
0060
0061 std::function<edm4hep::Vector2f(const CaloHit&, const CaloHit&)> hitsDist;
0062
0063 std::function<edm4hep::Vector2f(const CaloHit &h1, const CaloHit &h2)> transverseEnergyProfileMetric;
0064 double u_transverseEnergyProfileScale;
0065 double transverseEnergyProfileScaleUnits;
0066
0067
0068 std::function<bool(const CaloHit &h1, const CaloHit &h2)> is_neighbour;
0069
0070
0071 std::function<bool(const CaloHit &maximum, const CaloHit &other)> is_maximum_neighbourhood;
0072
0073
0074 std::array<double, 2> neighbourDist;
0075
0076
0077 dd4hep::IDDescriptor m_idSpec;
0078
0079 private:
0080
0081
0082 void bfs_group(const edm4eic::CalorimeterHitCollection &hits, std::set<std::size_t> &group, std::size_t idx, std::vector<bool> &visits) const {
0083 visits[idx] = true;
0084
0085
0086 if (hits[idx].getEnergy() < m_cfg.minClusterHitEdep) {
0087 return;
0088 }
0089
0090 group.insert(idx);
0091 size_t prev_size = 0;
0092
0093 while (prev_size != group.size()) {
0094 prev_size = group.size();
0095 for (std::size_t idx1 : group) {
0096
0097 for (std::size_t idx2 = 0; idx2 < hits.size(); ++idx2) {
0098
0099 if (hits[idx2].getEnergy() < m_cfg.minClusterHitEdep) {
0100 continue;
0101 }
0102 if ((!visits[idx2])
0103 && is_neighbour(hits[idx1], hits[idx2])) {
0104 group.insert(idx2);
0105 visits[idx2] = true;
0106 }
0107 }
0108 }
0109 }
0110 }
0111
0112
0113 std::vector<std::size_t> find_maxima(const edm4eic::CalorimeterHitCollection &hits, const std::set<std::size_t> &group, bool global = false) const {
0114 std::vector<std::size_t> maxima;
0115 if (group.empty()) {
0116 return maxima;
0117 }
0118
0119 if (global) {
0120 std::size_t mpos = *group.begin();
0121 for (auto idx : group) {
0122 if (hits[mpos].getEnergy() < hits[idx].getEnergy()) {
0123 mpos = idx;
0124 }
0125 }
0126 if (hits[mpos].getEnergy() >= m_cfg.minClusterCenterEdep) {
0127 maxima.push_back(mpos);
0128 }
0129 return maxima;
0130 }
0131
0132 for (std::size_t idx1 : group) {
0133
0134 if (hits[idx1].getEnergy() < m_cfg.minClusterCenterEdep) {
0135 continue;
0136 }
0137
0138 bool maximum = true;
0139 for (std::size_t idx2 : group) {
0140 if (idx1 == idx2) {
0141 continue;
0142 }
0143
0144 if (is_maximum_neighbourhood(hits[idx1], hits[idx2]) && (hits[idx2].getEnergy() > hits[idx1].getEnergy())) {
0145 maximum = false;
0146 break;
0147 }
0148 }
0149
0150 if (maximum) {
0151 maxima.push_back(idx1);
0152 }
0153 }
0154
0155 return maxima;
0156 }
0157
0158 inline static void vec_normalize(std::vector<double>& vals) {
0159 double total = 0.;
0160 for (auto& val : vals) {
0161 total += val;
0162 }
0163 for (auto& val : vals) {
0164 val /= total;
0165 }
0166 }
0167
0168
0169
0170 void split_group(const edm4eic::CalorimeterHitCollection &hits, std::set<std::size_t>& group, const std::vector<std::size_t>& maxima, edm4eic::ProtoClusterCollection *protoClusters) const {
0171
0172 if (maxima.empty()) {
0173 debug("No maxima found, not building any clusters");
0174 return;
0175 } else if (maxima.size() == 1) {
0176 edm4eic::MutableProtoCluster pcl = protoClusters->create();
0177 for (std::size_t idx : group) {
0178 pcl.addToHits(hits[idx]);
0179 pcl.addToWeights(1.);
0180 }
0181
0182 debug("A single maximum found, added one ProtoCluster");
0183
0184 return;
0185 }
0186
0187
0188
0189 std::vector<double> weights(maxima.size(), 1.);
0190 std::vector<edm4eic::MutableProtoCluster> pcls;
0191 for (size_t k = 0; k < maxima.size(); ++k) {
0192 pcls.push_back(protoClusters->create());
0193 }
0194
0195 for (std::size_t idx : group) {
0196 size_t j = 0;
0197
0198 for (std::size_t cidx : maxima) {
0199 double energy = hits[cidx].getEnergy();
0200 double dist = edm4hep::utils::magnitude(transverseEnergyProfileMetric(hits[cidx], hits[idx]));
0201 weights[j] = std::exp(-dist * transverseEnergyProfileScaleUnits / m_cfg.transverseEnergyProfileScale) * energy;
0202 j += 1;
0203 }
0204
0205
0206 vec_normalize(weights);
0207
0208
0209 for (auto& w : weights) {
0210 if (w < 0.02) {
0211 w = 0;
0212 }
0213 }
0214 vec_normalize(weights);
0215
0216
0217 for (size_t k = 0; k < maxima.size(); ++k) {
0218 double weight = weights[k];
0219 if (weight <= 1e-6) {
0220 continue;
0221 }
0222 pcls[k].addToHits(hits[idx]);
0223 pcls[k].addToWeights(weight);
0224 }
0225 }
0226 debug("Multiple ({}) maxima found, added a ProtoClusters for each maximum", maxima.size());
0227 }
0228 };
0229
0230 }