File indexing completed on 2025-12-16 09:27:57
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022 #include "algorithms/calorimetry/ImagingTopoCluster.h"
0023
0024 #include <DD4hep/Handle.h>
0025 #include <Evaluator/DD4hepUnits.h>
0026 #include <edm4hep/Vector3f.h>
0027 #include <edm4hep/utils/vector_utils.h>
0028 #include <fmt/core.h>
0029 #include <cmath>
0030 #include <cstdlib>
0031 #include <gsl/pointers>
0032 #include <stdexcept>
0033 #include <utility>
0034 #include <variant>
0035 #include <vector>
0036
0037 #include "algorithms/calorimetry/ImagingTopoClusterConfig.h"
0038
0039 namespace eicrecon {
0040 template <typename... L> struct multilambda : L... {
0041 using L::operator()...;
0042 constexpr multilambda(L... lambda) : L(std::move(lambda))... {}
0043 };
0044
0045 void ImagingTopoCluster::init() {
0046
0047 multilambda _toDouble = {
0048 [](const std::string& v) { return dd4hep::_toDouble(v); },
0049 [](const double& v) { return v; },
0050 };
0051
0052
0053
0054 if (m_cfg.minClusterCenterEdep < m_cfg.minClusterHitEdep) {
0055 const std::string msg =
0056 "minClusterCenterEdep must be greater than or equal to minClusterHitEdep";
0057 error(msg);
0058 throw std::runtime_error(msg);
0059 }
0060
0061
0062 sameLayerDistXY[0] = std::visit(_toDouble, m_cfg.sameLayerDistXY[0]) / dd4hep::mm;
0063 sameLayerDistXY[1] = std::visit(_toDouble, m_cfg.sameLayerDistXY[1]) / dd4hep::mm;
0064 diffLayerDistXY[0] = std::visit(_toDouble, m_cfg.diffLayerDistXY[0]) / dd4hep::mm;
0065 diffLayerDistXY[1] = std::visit(_toDouble, m_cfg.diffLayerDistXY[1]) / dd4hep::mm;
0066 sameLayerDistEtaPhi[0] = m_cfg.sameLayerDistEtaPhi[0];
0067 sameLayerDistEtaPhi[1] = m_cfg.sameLayerDistEtaPhi[1] / dd4hep::rad;
0068 diffLayerDistEtaPhi[0] = m_cfg.diffLayerDistEtaPhi[0];
0069 diffLayerDistEtaPhi[1] = m_cfg.diffLayerDistEtaPhi[1] / dd4hep::rad;
0070 sameLayerDistTZ[0] = m_cfg.sameLayerDistTZ[0] / dd4hep::mm;
0071 sameLayerDistTZ[1] = m_cfg.sameLayerDistTZ[1] / dd4hep::mm;
0072 diffLayerDistTZ[0] = m_cfg.diffLayerDistTZ[0] / dd4hep::mm;
0073 diffLayerDistTZ[1] = m_cfg.diffLayerDistTZ[1] / dd4hep::mm;
0074
0075 sectorDist = m_cfg.sectorDist / dd4hep::mm;
0076 minClusterHitEdep = m_cfg.minClusterHitEdep / dd4hep::GeV;
0077 minClusterCenterEdep = m_cfg.minClusterCenterEdep / dd4hep::GeV;
0078 minClusterEdep = m_cfg.minClusterEdep / dd4hep::GeV;
0079
0080
0081 switch (m_cfg.sameLayerMode) {
0082 case ImagingTopoClusterConfig::ELayerMode::xy:
0083 if (m_cfg.sameLayerDistXY.size() != 2) {
0084 const std::string msg = "Expected 2 values (x_dist, y_dist) for sameLayerDistXY";
0085 error(msg);
0086 throw std::runtime_error(msg);
0087 }
0088 info("Same-layer clustering (same sector and same layer): "
0089 "Local [x, y] distance between hits <= [{:.4f} mm, {:.4f} mm].",
0090 sameLayerDistXY[0], sameLayerDistXY[1]);
0091 break;
0092 case ImagingTopoClusterConfig::ELayerMode::etaphi:
0093 if (m_cfg.sameLayerDistEtaPhi.size() != 2) {
0094 const std::string msg = "Expected 2 values (eta_dist, phi_dist) for sameLayerDistEtaPhi";
0095 error(msg);
0096 throw std::runtime_error(msg);
0097 }
0098 info("Same-layer clustering (same sector and same layer): "
0099 "Global [eta, phi] distance between hits <= [{:.4f}, {:.4f} rad].",
0100 sameLayerDistEtaPhi[0], sameLayerDistEtaPhi[1]);
0101 break;
0102 case ImagingTopoClusterConfig::ELayerMode::tz:
0103 if (m_cfg.sameLayerDistTZ.size() != 2) {
0104 const std::string msg = "Expected 2 values (t_dist, z_dist) for sameLayerDistTZ";
0105 error(msg);
0106 throw std::runtime_error(msg);
0107 }
0108 info("Same-layer clustering (same sector and same layer): "
0109 "Global [t, z] distance between hits <= [{:.4f} mm, {:.4f} mm].",
0110 sameLayerDistTZ[0], sameLayerDistTZ[1]);
0111 break;
0112 default:
0113 throw std::runtime_error("Unknown same-layer mode.");
0114 }
0115
0116
0117 switch (m_cfg.diffLayerMode) {
0118 case ImagingTopoClusterConfig::ELayerMode::etaphi:
0119 if (m_cfg.diffLayerDistEtaPhi.size() != 2) {
0120 const std::string msg = "Expected 2 values (eta_dist, phi_dist) for diffLayerDistEtaPhi";
0121 error(msg);
0122 throw std::runtime_error(msg);
0123 }
0124 info("Neighbour layers clustering (same sector and layer id within +- {:d}): "
0125 "Global [eta, phi] distance between hits <= [{:.4f}, {:.4f} rad].",
0126 m_cfg.neighbourLayersRange, diffLayerDistEtaPhi[0], diffLayerDistEtaPhi[1]);
0127 break;
0128 case ImagingTopoClusterConfig::ELayerMode::xy:
0129 if (m_cfg.diffLayerDistXY.size() != 2) {
0130 const std::string msg = "Expected 2 values (x_dist, y_dist) for diffLayerDistXY";
0131 error(msg);
0132 throw std::runtime_error(msg);
0133 }
0134 info("Neighbour layers clustering (same sector and layer id within +- {:d}): "
0135 "Global [x, y] distance between hits <= [{:.4f} mm, {:.4f} mm].",
0136 m_cfg.neighbourLayersRange, diffLayerDistXY[0], diffLayerDistXY[1]);
0137 break;
0138 case ImagingTopoClusterConfig::ELayerMode::tz:
0139 if (m_cfg.diffLayerDistTZ.size() != 2) {
0140 const std::string msg = "Expected 2 values (t_dist, z_dist) for diffLayerDistTZ";
0141 error(msg);
0142 throw std::runtime_error(msg);
0143 }
0144 info("Neighbour layers clustering (same sector and layer id within +- {:d}): "
0145 "Global [t, z] distance between hits <= [{:.4f} mm, {:.4f} mm].",
0146 m_cfg.neighbourLayersRange, diffLayerDistTZ[0], diffLayerDistTZ[1]);
0147 break;
0148 default:
0149 error("Unknown different-layer mode.");
0150 throw std::runtime_error("Unknown different-layer mode.");
0151 }
0152 info("Neighbour sectors clustering (different sector): "
0153 "Global distance between hits <= {:.4f} mm.",
0154 sectorDist);
0155 }
0156
0157 void ImagingTopoCluster::process(const Input& input, const Output& output) const {
0158
0159 const auto [hits] = input;
0160 auto [proto] = output;
0161
0162
0163 auto compare = [&hits](const auto& a, const auto& b) {
0164
0165
0166 if ((*hits)[a].getLayer() == (*hits)[b].getLayer()) {
0167 return (*hits)[a].getObjectID().index < (*hits)[b].getObjectID().index;
0168 }
0169 return (*hits)[a].getLayer() < (*hits)[b].getLayer();
0170 };
0171
0172
0173 std::set<std::size_t, decltype(compare)> indices(compare);
0174
0175 for (std::size_t i = 0; i < hits->size(); ++i) {
0176 indices.insert(i);
0177 }
0178
0179 if (hits->size() != indices.size()) {
0180 error("equivalent hits were dropped: #hits {:d}, #indices {:d}", hits->size(), indices.size());
0181 }
0182
0183
0184 std::vector<std::list<std::size_t>> groups;
0185
0186
0187
0188
0189
0190 for (auto idx = indices.begin(); idx != indices.end();
0191 indices.empty() ? idx = indices.end() : idx) {
0192
0193 trace("hit {:d}: local position = ({}, {}, {}), global position = ({}, {}, {}), energy = {}",
0194 *idx, (*hits)[*idx].getLocal().x, (*hits)[*idx].getLocal().y, (*hits)[*idx].getLocal().z,
0195 (*hits)[*idx].getPosition().x, (*hits)[*idx].getPosition().y,
0196 (*hits)[*idx].getPosition().z, (*hits)[*idx].getEnergy());
0197
0198
0199 if ((*hits)[*idx].getEnergy() < minClusterCenterEdep) {
0200 idx++;
0201 continue;
0202 }
0203
0204
0205 groups.emplace_back(std::list{*idx});
0206 bfs_group(*hits, indices, groups.back(), *idx);
0207
0208
0209 idx = indices.erase(idx);
0210 }
0211 debug("found {} potential clusters (groups of hits)", groups.size());
0212 for (std::size_t i = 0; i < groups.size(); ++i) {
0213 debug("group {}: {} hits", i, groups[i].size());
0214 for (auto idx : groups[i]) {
0215 const auto& hit = (*hits)[idx];
0216 trace(" hit {} -> energy = {:.6f}, layer = {}, sector = {}, local = ({:.2f}, {:.2f}, "
0217 "{:.2f}), global = ({:.2f}, {:.2f}, {:.2f})",
0218 idx, hit.getEnergy(), hit.getLayer(), hit.getSector(), hit.getLocal().x,
0219 hit.getLocal().y, hit.getLocal().z, hit.getPosition().x, hit.getPosition().y,
0220 hit.getPosition().z);
0221 }
0222 }
0223
0224
0225 for (const auto& group : groups) {
0226 if (group.size() < m_cfg.minClusterNhits) {
0227 continue;
0228 }
0229 double energy = 0.;
0230 for (std::size_t idx : group) {
0231 energy += (*hits)[idx].getEnergy();
0232 }
0233 if (energy < minClusterEdep) {
0234 continue;
0235 }
0236 auto pcl = proto->create();
0237 for (std::size_t idx : group) {
0238 pcl.addToHits((*hits)[idx]);
0239 pcl.addToWeights(1);
0240 }
0241 }
0242 }
0243
0244
0245 bool ImagingTopoCluster::is_neighbour(const edm4eic::CalorimeterHit& h1,
0246 const edm4eic::CalorimeterHit& h2) const {
0247
0248 if (h1.getSector() != h2.getSector()) {
0249 return std::hypot((h1.getPosition().x - h2.getPosition().x),
0250 (h1.getPosition().y - h2.getPosition().y),
0251 (h1.getPosition().z - h2.getPosition().z)) <= sectorDist;
0252 }
0253
0254
0255 int ldiff = std::abs(h1.getLayer() - h2.getLayer());
0256
0257 if (ldiff == 0) {
0258 switch (m_cfg.sameLayerMode) {
0259 case ImagingTopoClusterConfig::ELayerMode::xy:
0260 return (std::abs(h1.getLocal().x - h2.getLocal().x) <= sameLayerDistXY[0]) &&
0261 (std::abs(h1.getLocal().y - h2.getLocal().y) <= sameLayerDistXY[1]);
0262
0263 case ImagingTopoClusterConfig::ELayerMode::etaphi:
0264 return (std::abs(edm4hep::utils::eta(h1.getPosition()) -
0265 edm4hep::utils::eta(h2.getPosition())) <= sameLayerDistEtaPhi[0]) &&
0266 (std::abs(edm4hep::utils::angleAzimuthal(h1.getPosition()) -
0267 edm4hep::utils::angleAzimuthal(h2.getPosition())) <= sameLayerDistEtaPhi[1]);
0268
0269 case ImagingTopoClusterConfig::ELayerMode::tz: {
0270
0271 auto phi = 0.5 * (edm4hep::utils::angleAzimuthal(h1.getPosition()) +
0272 edm4hep::utils::angleAzimuthal(h2.getPosition()));
0273 auto h1_t = (h1.getPosition().x * sin(phi)) - (h1.getPosition().y * cos(phi));
0274 auto h2_t = (h2.getPosition().x * sin(phi)) - (h2.getPosition().y * cos(phi));
0275 auto h1_z = h1.getPosition().z;
0276 auto h2_z = h2.getPosition().z;
0277
0278 return (std::abs(h1_t - h2_t) <= sameLayerDistTZ[0]) &&
0279 (std::abs(h1_z - h2_z) <= sameLayerDistTZ[1]);
0280 }
0281
0282 default:
0283 error("Unknown layer mode for same-layer clustering.");
0284 return false;
0285 }
0286 } else if (ldiff <= m_cfg.neighbourLayersRange) {
0287 switch (m_cfg.diffLayerMode) {
0288 case eicrecon::ImagingTopoClusterConfig::ELayerMode::etaphi:
0289 return (std::abs(edm4hep::utils::eta(h1.getPosition()) -
0290 edm4hep::utils::eta(h2.getPosition())) <= diffLayerDistEtaPhi[0]) &&
0291 (std::abs(edm4hep::utils::angleAzimuthal(h1.getPosition()) -
0292 edm4hep::utils::angleAzimuthal(h2.getPosition())) <= diffLayerDistEtaPhi[1]);
0293
0294 case eicrecon::ImagingTopoClusterConfig::ELayerMode::xy:
0295
0296 return (std::abs(h1.getPosition().x - h2.getPosition().x) <= diffLayerDistXY[0]) &&
0297 (std::abs(h1.getPosition().y - h2.getPosition().y) <= diffLayerDistXY[1]);
0298
0299 case eicrecon::ImagingTopoClusterConfig::ELayerMode::tz: {
0300 auto phi = 0.5 * (edm4hep::utils::angleAzimuthal(h1.getPosition()) +
0301 edm4hep::utils::angleAzimuthal(h2.getPosition()));
0302 auto h1_t = (h1.getPosition().x * sin(phi)) - (h1.getPosition().y * cos(phi));
0303 auto h2_t = (h2.getPosition().x * sin(phi)) - (h2.getPosition().y * cos(phi));
0304 auto h1_z = h1.getPosition().z;
0305 auto h2_z = h2.getPosition().z;
0306
0307 return (std::abs(h1_t - h2_t) <= diffLayerDistTZ[0]) &&
0308 (std::abs(h1_z - h2_z) <= diffLayerDistTZ[1]);
0309 }
0310
0311 default:
0312 error("Unknown layer mode for different-layer clustering.");
0313 return false;
0314 }
0315 }
0316
0317
0318 return false;
0319 }
0320
0321 }