File indexing completed on 2024-09-27 07:02:57
0001
0002
0003
0004 #include <edm4eic/CalorimeterHit.h>
0005 #include <edm4hep/Vector3f.h>
0006 #include <edm4hep/utils/vector_utils.h>
0007 #include <fmt/core.h>
0008 #include <podio/ObjectID.h>
0009 #include <podio/RelationRange.h>
0010 #include <stdint.h>
0011 #include <cmath>
0012 #include <cstddef>
0013 #include <gsl/pointers>
0014
0015
0016 #include "TrackClusterMergeSplitter.h"
0017 #include "algorithms/calorimetry/TrackClusterMergeSplitterConfig.h"
0018
0019
0020
0021 namespace eicrecon {
0022
0023
0024
0025
0026 void TrackClusterMergeSplitter::init(const dd4hep::Detector* detector) {
0027
0028
0029 m_idCalo = detector -> constant<int>(m_cfg.idCalo);
0030 debug("Collecting projections to detector with system id {}", m_idCalo);
0031
0032 }
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062 void TrackClusterMergeSplitter::process(
0063 const TrackClusterMergeSplitter::Input& input,
0064 const TrackClusterMergeSplitter::Output& output
0065 ) const {
0066
0067
0068 const auto [in_protoclusters, in_projections] = input;
0069 auto [out_protoclusters] = output;
0070
0071
0072 if (in_protoclusters->size() == 0) {
0073 debug("No proto-clusters in input collection.");
0074 return;
0075 }
0076
0077
0078
0079
0080 VecProj vecProject;
0081 get_projections(in_projections, vecProject);
0082
0083
0084
0085
0086 MapToVecProj mapProjToSplit;
0087 if (vecProject.size() == 0) {
0088 debug("No projections to match clusters to.");
0089 return;
0090 } else {
0091 match_clusters_to_tracks(in_protoclusters, vecProject, mapProjToSplit);
0092 }
0093
0094
0095
0096
0097 SetClust setUsedClust;
0098 MapToVecClust mapClustToMerge;
0099 for (auto& [clustSeed, vecMatchProj] : mapProjToSplit) {
0100
0101
0102
0103 auto projSeed = vecMatchProj.front();
0104
0105
0106 if (setUsedClust.count(clustSeed)) {
0107 continue;
0108 }
0109
0110
0111 mapClustToMerge[clustSeed].push_back( clustSeed );
0112 setUsedClust.insert( clustSeed );
0113
0114
0115 const float eClustSeed = get_cluster_energy(clustSeed);
0116 const float eProjSeed = m_cfg.avgEP * edm4hep::utils::magnitude(projSeed.momentum);
0117
0118
0119
0120
0121 const float sigSeed = (eClustSeed - eProjSeed) / m_cfg.sigEP;
0122 trace("Seed energy = {}, expected energy = {}, significance = {}", eClustSeed, eProjSeed, sigSeed);
0123
0124
0125
0126
0127
0128 if (sigSeed > m_cfg.minSigCut) {
0129 continue;
0130 }
0131
0132
0133 const auto posSeed = get_cluster_position(clustSeed);
0134 const float etaSeed = edm4hep::utils::eta(posSeed);
0135 const float phiSeed = edm4hep::utils::angleAzimuthal(posSeed);
0136
0137
0138 float eClustSum = eClustSeed;
0139 float sigSum = sigSeed;
0140 for (auto in_cluster : *in_protoclusters) {
0141
0142
0143 if (setUsedClust.count(in_cluster)) {
0144 continue;
0145 }
0146
0147
0148 const auto posClust = get_cluster_position(in_cluster);
0149 const float etaClust = edm4hep::utils::eta(posClust);
0150 const float phiClust = edm4hep::utils::angleAzimuthal(posClust);
0151
0152
0153 const float drToSeed = std::hypot(
0154 etaSeed - etaClust,
0155 std::remainder(phiSeed - phiClust, 2. * M_PI)
0156 );
0157
0158
0159
0160
0161 if (drToSeed > m_cfg.drAdd) {
0162 continue;
0163 } else {
0164 mapClustToMerge[clustSeed].push_back( in_cluster );
0165 setUsedClust.insert( in_cluster );
0166 }
0167
0168
0169
0170
0171 if (mapProjToSplit.count(in_cluster)) {
0172 vecMatchProj.insert(
0173 vecMatchProj.end(),
0174 mapProjToSplit[in_cluster].begin(),
0175 mapProjToSplit[in_cluster].end()
0176 );
0177 }
0178
0179
0180 eClustSum += get_cluster_energy(in_cluster);
0181 sigSum = (eClustSum - eProjSeed) / m_cfg.sigEP;
0182 trace(
0183 "{} clusters to merge: current sum = {}, current significance = {}, {} track(s) pointing to merged cluster",
0184 mapClustToMerge[clustSeed].size(),
0185 eClustSum,
0186 sigSum,
0187 vecMatchProj.size()
0188 );
0189 }
0190 }
0191
0192
0193
0194
0195
0196 for (auto& [clustSeed, vecClustToMerge] : mapClustToMerge) {
0197 merge_and_split_clusters(
0198 vecClustToMerge,
0199 mapProjToSplit[clustSeed],
0200 out_protoclusters
0201 );
0202 }
0203
0204
0205
0206
0207 for (auto in_cluster : *in_protoclusters) {
0208
0209
0210 if (setUsedClust.count(in_cluster)) {
0211 continue;
0212 }
0213
0214
0215 edm4eic::MutableProtoCluster out_cluster = in_cluster.clone();
0216 out_protoclusters->push_back(out_cluster);
0217 trace("Copied input cluster {} onto output cluster {}",
0218 in_cluster.getObjectID().index,
0219 out_cluster.getObjectID().index
0220 );
0221
0222 }
0223
0224 }
0225
0226
0227
0228
0229
0230
0231 void TrackClusterMergeSplitter::get_projections(
0232 const edm4eic::TrackSegmentCollection* projections,
0233 VecProj& relevant_projects
0234 ) const {
0235
0236
0237 if (projections->size() == 0) {
0238 debug("No projections in input collection.");
0239 return;
0240 }
0241
0242
0243 for (auto project : *projections) {
0244 for (auto point : project.getPoints()) {
0245 if (
0246 (point.system == m_idCalo) &&
0247 (point.surface == 1)
0248 ) {
0249 relevant_projects.push_back(point);
0250 }
0251 }
0252 }
0253 debug("Collected relevant projections: {} to process", relevant_projects.size());
0254
0255 }
0256
0257
0258
0259
0260
0261
0262
0263
0264 void TrackClusterMergeSplitter::match_clusters_to_tracks(
0265 const edm4eic::ProtoClusterCollection* clusters,
0266 const VecProj& projections,
0267 MapToVecProj& matches
0268 ) const {
0269
0270
0271
0272 for (uint32_t iProject = 0; iProject < projections.size(); ++iProject) {
0273
0274
0275 auto project = projections[iProject];
0276
0277
0278 const float etaProj = edm4hep::utils::eta(project.position);
0279 const float phiProj = edm4hep::utils::angleAzimuthal(project.position);
0280
0281
0282 edm4eic::ProtoCluster match;
0283
0284
0285 bool foundMatch = false;
0286 float dMatch = m_cfg.drAdd;
0287 for (auto cluster : *clusters) {
0288
0289
0290 const auto posClust = get_cluster_position(cluster);
0291 const float etaClust = edm4hep::utils::eta(posClust);
0292 const float phiClust = edm4hep::utils::angleAzimuthal(posClust);
0293
0294
0295 const float dist = std::hypot(
0296 etaProj - etaClust,
0297 std::remainder(phiProj - phiClust, 2. * M_PI)
0298 );
0299
0300
0301 if (dist <= dMatch) {
0302 foundMatch = true;
0303 dMatch = dist;
0304 match = cluster;
0305 }
0306 }
0307
0308
0309 if (foundMatch) {
0310 matches[match].push_back(project);
0311 trace("Matched cluster to track projection: eta-phi distance = {}", dMatch);
0312 }
0313 }
0314 debug("Finished matching clusters to track projections: {} matches", matches.size());
0315
0316 }
0317
0318
0319
0320
0321
0322
0323
0324
0325
0326
0327 void TrackClusterMergeSplitter::merge_and_split_clusters(
0328 const VecClust& to_merge,
0329 const VecProj& to_split,
0330 edm4eic::ProtoClusterCollection* out_protoclusters
0331 ) const {
0332
0333
0334 if (to_split.size() == 1) {
0335 edm4eic::MutableProtoCluster new_clust = out_protoclusters->create();
0336 for (const auto& old_clust : to_merge) {
0337 for (const auto& hit : old_clust.getHits()) {
0338 new_clust.addToHits( hit );
0339 new_clust.addToWeights( 1. );
0340 }
0341 trace("Merged input cluster {} into output cluster {}", old_clust.getObjectID().index, new_clust.getObjectID().index);
0342 }
0343 return;
0344 }
0345
0346
0347 std::vector<edm4eic::MutableProtoCluster> new_clusters;
0348 for (const auto& proj : to_split) {
0349 new_clusters.push_back( out_protoclusters->create() );
0350 }
0351 trace("Splitting merged cluster across {} tracks", to_split.size());
0352
0353
0354 std::vector<float> weights( to_split.size(), 1. );
0355 for (const auto& old_clust : to_merge) {
0356 for (const auto& hit : old_clust.getHits()) {
0357
0358
0359 for (std::size_t iProj = 0; const auto& proj : to_split) {
0360
0361
0362 const float etaProj = edm4hep::utils::eta(proj.position);
0363 const float phiProj = edm4hep::utils::angleAzimuthal(proj.position);
0364
0365
0366 const float etaHit = edm4hep::utils::eta(hit.getPosition());
0367 const float phiHit = edm4hep::utils::angleAzimuthal(hit.getPosition());
0368
0369
0370 const float mom = edm4hep::utils::magnitude(proj.momentum);
0371 const float dist = std::hypot(
0372 etaHit - etaProj,
0373 std::remainder(phiHit - phiProj, 2. * M_PI)
0374 );
0375
0376
0377 weights[iProj] = std::exp(-1. * dist / m_cfg.transverseEnergyProfileScale) * mom;
0378 ++iProj;
0379 }
0380
0381
0382 float wTotal = 0.;
0383 for (const float weight : weights) {
0384 wTotal += weight;
0385 }
0386 for (float& weight : weights) {
0387 weight /= wTotal;
0388 }
0389
0390
0391 for (std::size_t iProj = 0; auto& new_clust : new_clusters) {
0392 new_clust.addToHits( hit );
0393 new_clust.addToWeights( weights[iProj] );
0394 }
0395
0396 }
0397 }
0398
0399 }
0400
0401
0402
0403
0404
0405
0406 float TrackClusterMergeSplitter::get_cluster_energy(const edm4eic::ProtoCluster& clust) const {
0407
0408 float eClust = 0.;
0409 for (auto hit : clust.getHits()) {
0410 eClust += hit.getEnergy();
0411 }
0412 return eClust / m_cfg.sampFrac;
0413
0414 }
0415
0416
0417
0418
0419
0420
0421 edm4hep::Vector3f TrackClusterMergeSplitter::get_cluster_position(const edm4eic::ProtoCluster& clust) const {
0422
0423
0424 const float eClust = get_cluster_energy(clust) * m_cfg.sampFrac;
0425
0426
0427 float wTotal = 0.;
0428 edm4hep::Vector3f position(0., 0., 0.);
0429 for (auto hit : clust.getHits()) {
0430
0431
0432 float weight = hit.getEnergy() / eClust;
0433 wTotal += weight;
0434
0435
0436 position = position + (hit.getPosition() * weight);
0437 }
0438
0439 float norm = 1.;
0440 if (wTotal == 0.) {
0441 warning("Total weight of 0 in position calculation!");
0442 } else {
0443 norm = wTotal;
0444 }
0445 return position / norm;
0446
0447 }
0448
0449 }