File indexing completed on 2024-11-15 08:59:47
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021 #pragma once
0022
0023 #include <algorithm>
0024 #include <numeric>
0025
0026 #include <algorithms/algorithm.h>
0027 #include <DD4hep/BitFieldCoder.h>
0028 #include <DDRec/CellIDPositionConverter.h>
0029 #include <DDRec/Surface.h>
0030 #include <DDRec/SurfaceManager.h>
0031
0032 #include <spdlog/spdlog.h>
0033
0034
0035 #include <edm4eic/CalorimeterHitCollection.h>
0036 #include <edm4eic/ProtoClusterCollection.h>
0037 #include <edm4hep/utils/vector_utils.h>
0038
0039 #include "algorithms/interfaces/WithPodConfig.h"
0040 #include "ImagingTopoClusterConfig.h"
0041
0042 namespace eicrecon {
0043
0044 using ImagingTopoClusterAlgorithm = algorithms::Algorithm<
0045 algorithms::Input<
0046 edm4eic::CalorimeterHitCollection
0047 >,
0048 algorithms::Output<
0049 edm4eic::ProtoClusterCollection
0050 >
0051 >;
0052
0053 class ImagingTopoCluster
0054 : public ImagingTopoClusterAlgorithm,
0055 public WithPodConfig<ImagingTopoClusterConfig> {
0056
0057 public:
0058 ImagingTopoCluster(std::string_view name)
0059 : ImagingTopoClusterAlgorithm{name,
0060 {"inputHitCollection"},
0061 {"outputProtoClusterCollection"},
0062 "Topological cell clustering algorithm for imaging calorimetry."} {}
0063
0064 private:
0065
0066
0067 std::array<double,2> localDistXY{0, 0};
0068 std::array<double,2> layerDistEtaPhi{0, 0};
0069 std::array<double,2> layerDistXY{0, 0};
0070 double sectorDist{0};
0071 double minClusterHitEdep{0};
0072 double minClusterCenterEdep{0};
0073 double minClusterEdep{0};
0074
0075 public:
0076 void init() {
0077
0078
0079 if (m_cfg.localDistXY.size() != 2) {
0080 error( "Expected 2 values (x_dist, y_dist) for localDistXY");
0081 return;
0082 }
0083 if (m_cfg.layerDistEtaPhi.size() != 2) {
0084 error( "Expected 2 values (eta_dist, phi_dist) for layerDistEtaPhi" );
0085 return;
0086 }
0087 if (m_cfg.minClusterCenterEdep < m_cfg.minClusterHitEdep) {
0088 error( "minClusterCenterEdep must be greater than or equal to minClusterHitEdep" );
0089 return;
0090 }
0091
0092
0093 localDistXY[0] = m_cfg.localDistXY[0] / dd4hep::mm;
0094 localDistXY[1] = m_cfg.localDistXY[1] / dd4hep::mm;
0095 layerDistXY[0] = m_cfg.layerDistXY[0] / dd4hep::mm;
0096 layerDistXY[1] = m_cfg.layerDistXY[1] / dd4hep::mm;
0097 layerDistEtaPhi[0] = m_cfg.layerDistEtaPhi[0];
0098 layerDistEtaPhi[1] = m_cfg.layerDistEtaPhi[1] / dd4hep::rad;
0099 sectorDist = m_cfg.sectorDist / dd4hep::mm;
0100 minClusterHitEdep = m_cfg.minClusterHitEdep / dd4hep::GeV;
0101 minClusterCenterEdep = m_cfg.minClusterCenterEdep / dd4hep::GeV;
0102 minClusterEdep = m_cfg.minClusterEdep / dd4hep::GeV;
0103
0104
0105 info("Local clustering (same sector and same layer): "
0106 "Local [x, y] distance between hits <= [{:.4f} mm, {:.4f} mm].",
0107 localDistXY[0], localDistXY[1]
0108 );
0109 switch (m_cfg.layerMode) {
0110 case ImagingTopoClusterConfig::ELayerMode::etaphi:
0111 info("Neighbour layers clustering (same sector and layer id within +- {:d}: "
0112 "Global [eta, phi] distance between hits <= [{:.4f}, {:.4f} rad].",
0113 m_cfg.neighbourLayersRange, layerDistEtaPhi[0], layerDistEtaPhi[1]
0114 );
0115 break;
0116 case ImagingTopoClusterConfig::ELayerMode::xy:
0117 info("Neighbour layers clustering (same sector and layer id within +- {:d}: "
0118 "Local [x, y] distance between hits <= [{:.4f} mm, {:.4f} mm].",
0119 m_cfg.neighbourLayersRange, layerDistXY[0], layerDistXY[1]
0120 );
0121 break;
0122 default:
0123 error("Unknown layer mode.");
0124 }
0125 info("Neighbour sectors clustering (different sector): "
0126 "Global distance between hits <= {:.4f} mm.",
0127 sectorDist
0128 );
0129 }
0130
0131 void process(const Input& input, const Output& output) const final {
0132
0133 const auto [hits] = input;
0134 auto [proto] = output;
0135
0136
0137 auto compare = [&hits](const auto& a, const auto& b) {
0138
0139
0140 if ((*hits)[a].getLayer() == (*hits)[b].getLayer()) {
0141 return (*hits)[a].getObjectID().index < (*hits)[b].getObjectID().index;
0142 }
0143 return (*hits)[a].getLayer() < (*hits)[b].getLayer();
0144 };
0145
0146
0147 std::set<std::size_t, decltype(compare)> indices(compare);
0148
0149 for (std::size_t i = 0; i < hits->size(); ++i) {
0150 indices.insert(i);
0151 }
0152
0153 if (hits->size() != indices.size()) {
0154 error("equivalent hits were dropped: #hits {:d}, #indices {:d}", hits->size(), indices.size());
0155 }
0156
0157
0158 std::vector<std::list<std::size_t>> groups;
0159
0160
0161
0162
0163
0164 for (auto idx = indices.begin(); idx != indices.end();
0165 indices.empty() ? idx = indices.end() : idx) {
0166
0167 debug("hit {:d}: local position = ({}, {}, {}), global position = ({}, {}, {}), energy = {}", *idx,
0168 (*hits)[*idx].getLocal().x, (*hits)[*idx].getLocal().y, (*hits)[*idx].getPosition().z,
0169 (*hits)[*idx].getPosition().x, (*hits)[*idx].getPosition().y, (*hits)[*idx].getPosition().z,
0170 (*hits)[*idx].getEnergy()
0171 );
0172
0173
0174 if ((*hits)[*idx].getEnergy() < minClusterCenterEdep) {
0175 idx++;
0176 continue;
0177 }
0178
0179
0180 groups.emplace_back(std::list{*idx});
0181 bfs_group(*hits, indices, groups.back(), *idx);
0182
0183
0184 idx = indices.erase(idx);
0185 }
0186 debug("found {} potential clusters (groups of hits)", groups.size());
0187 for (size_t i = 0; i < groups.size(); ++i) {
0188 debug("group {}: {} hits", i, groups[i].size());
0189 }
0190
0191
0192 for (const auto &group : groups) {
0193 if (group.size() < m_cfg.minClusterNhits) {
0194 continue;
0195 }
0196 double energy = 0.;
0197 for (std::size_t idx : group) {
0198 energy += (*hits)[idx].getEnergy();
0199 }
0200 if (energy < minClusterEdep) {
0201 continue;
0202 }
0203 auto pcl = proto->create();
0204 for (std::size_t idx : group) {
0205 pcl.addToHits((*hits)[idx]);
0206 pcl.addToWeights(1);
0207 }
0208 }
0209 }
0210
0211 private:
0212
0213
0214 bool is_neighbour(const edm4eic::CalorimeterHit& h1, const edm4eic::CalorimeterHit& h2) const {
0215
0216 if (h1.getSector() != h2.getSector()) {
0217 return std::hypot((h1.getPosition().x - h2.getPosition().x),
0218 (h1.getPosition().y - h2.getPosition().y),
0219 (h1.getPosition().z - h2.getPosition().z)) <= sectorDist;
0220 }
0221
0222
0223 int ldiff = std::abs(h1.getLayer() - h2.getLayer());
0224
0225 if (ldiff == 0) {
0226 return (std::abs(h1.getLocal().x - h2.getLocal().x) <= localDistXY[0]) &&
0227 (std::abs(h1.getLocal().y - h2.getLocal().y) <= localDistXY[1]);
0228 } else if (ldiff <= m_cfg.neighbourLayersRange) {
0229 switch(m_cfg.layerMode){
0230 case eicrecon::ImagingTopoClusterConfig::ELayerMode::etaphi:
0231 return (std::abs(edm4hep::utils::eta(h1.getPosition()) - edm4hep::utils::eta(h2.getPosition())) <= layerDistEtaPhi[0]) &&
0232 (std::abs(edm4hep::utils::angleAzimuthal(h1.getPosition()) - edm4hep::utils::angleAzimuthal(h2.getPosition())) <=
0233 layerDistEtaPhi[1]);
0234 case eicrecon::ImagingTopoClusterConfig::ELayerMode::xy:
0235 return (std::abs(h1.getPosition().x - h2.getPosition().x) <= layerDistXY[0]) &&
0236 (std::abs(h1.getPosition().y - h2.getPosition().y) <= layerDistXY[1]);
0237 }
0238 }
0239
0240 return false;
0241 }
0242
0243
0244
0245 template<typename Compare>
0246 void bfs_group(const edm4eic::CalorimeterHitCollection &hits, std::set<std::size_t,Compare>& indices, std::list<std::size_t> &group, const std::size_t idx) const {
0247
0248
0249 for (auto idx1 = group.begin(); idx1 != group.end(); ++idx1) {
0250
0251 for (auto idx2 = indices.begin(); idx2 != indices.end();
0252 indices.empty() ? idx2 = indices.end() : idx2) {
0253
0254
0255
0256 if (*idx2 == *idx1 || *idx2 == idx) {
0257 idx2++;
0258 continue;
0259 }
0260
0261
0262
0263
0264
0265
0266
0267 if (hits[*idx2].getEnergy() < m_cfg.minClusterHitEdep) {
0268 idx2 = indices.erase(idx2);
0269 continue;
0270 }
0271
0272 if (is_neighbour(hits[*idx1], hits[*idx2])) {
0273 group.push_back(*idx2);
0274 idx2 = indices.erase(idx2);
0275 } else {
0276 idx2++;
0277 }
0278 }
0279 }
0280 }
0281
0282 };
0283
0284 }