File indexing completed on 2026-08-11 08:33:33
0001
0002
0003
0004
0005
0006
0007
0008 #include <algorithms/logger.h>
0009 #include <edm4eic/ClusterCollection.h>
0010 #include <edm4eic/MCRecoClusterParticleAssociationCollection.h>
0011 #include <edm4eic/MCRecoParticleAssociationCollection.h>
0012 #include <edm4eic/ReconstructedParticleCollection.h>
0013 #include <edm4hep/MCParticleCollection.h>
0014 #include <edm4hep/Vector3f.h>
0015 #include <edm4hep/utils/vector_utils.h>
0016 #include <podio/ObjectID.h>
0017 #include <podio/detail/Link.h>
0018 #include <podio/detail/LinkCollectionImpl.h>
0019 #include <cmath>
0020 #include <iterator>
0021 #include <map>
0022 #include <memory>
0023 #include <tuple>
0024 #include <utility>
0025
0026 #include "MatchClusters.h"
0027
0028 namespace eicrecon {
0029
0030 void MatchClusters::process(const MatchClusters::Input& input,
0031 const MatchClusters::Output& output) const {
0032
0033 const auto [mcparticles, inparts, inpartsassoc, clusters, clustersassoc] = input;
0034 auto [outparts, outlinks, outpartsassoc] = output;
0035
0036 debug("Processing cluster info for new event");
0037
0038 debug("Step 0/2: Getting indexed list of clusters...");
0039
0040
0041 auto clusterMap = indexedClusters(clusters, clustersassoc);
0042
0043
0044
0045 debug("Step 1/2: Matching clusters to charged particles...");
0046
0047 for (const auto inpart : *inparts) {
0048 debug(" --> Processing charged particle {}, PDG {}, energy {}", inpart.getObjectID().index,
0049 inpart.getPDG(), inpart.getEnergy());
0050
0051 auto outpart = inpart.clone();
0052 outparts->push_back(outpart);
0053
0054
0055 int bestMcID = -1;
0056 double bestWeight = -1.;
0057
0058 for (const auto& assoc : *inpartsassoc) {
0059 if (assoc.getRec().getObjectID() == inpart.getObjectID()) {
0060 const double w = assoc.getWeight();
0061 if (w > bestWeight) {
0062 bestWeight = w;
0063 bestMcID = assoc.getSim().getObjectID().index;
0064 }
0065 }
0066 }
0067
0068 trace(" --> Found particle with best mcID {} weight {}", bestMcID, bestWeight);
0069
0070 if (bestMcID < 0) {
0071 debug(" --> cannot match track without associated mcID");
0072 } else if (clusterMap.contains(bestMcID)) {
0073 const auto& clus = clusterMap[bestMcID];
0074 debug(" --> found matching cluster with energy: {}", clus.getEnergy());
0075 debug(" --> adding cluster to reconstructed particle");
0076 outpart.addToClusters(clus);
0077 clusterMap.erase(bestMcID);
0078 }
0079
0080
0081 for (const auto& assoc : *inpartsassoc) {
0082 if (assoc.getRec().getObjectID() == inpart.getObjectID()) {
0083 auto link = outlinks->create();
0084 link.setWeight(assoc.getWeight());
0085 link.setFrom(outpart);
0086 link.setTo(assoc.getSim());
0087 auto outassoc = outpartsassoc->create();
0088 outassoc.setWeight(assoc.getWeight());
0089 outassoc.setRec(outpart);
0090 outassoc.setSim(assoc.getSim());
0091 }
0092 }
0093 }
0094
0095
0096
0097 debug("Step 2/2: Creating neutrals for remaining clusters...");
0098 for (const auto& [mcID, clus] : clusterMap) {
0099 debug(" --> Processing unmatched cluster with energy: {}", clus.getEnergy());
0100
0101
0102 const auto mc = (*mcparticles)[mcID];
0103 const double mass = 0.;
0104 const int32_t pdg = 0;
0105 if (level() <= algorithms::LogLevel::kDebug) {
0106 if (mc.getCharge() != 0.0F) {
0107 debug(" --> associated mcparticle is not a neutral (PDG: {}), "
0108 "setting the reconstructed particle ID to 0 (unidentified)",
0109 mc.getPDG());
0110 }
0111 debug(" --> found matching associated mcparticle with PDG: {}, energy: {}", pdg,
0112 mc.getEnergy());
0113 }
0114
0115
0116 const auto outpart = reconstruct_neutral(&clus, mass, pdg);
0117 debug(" --> Reconstructed neutral particle with PDG: {}, energy: {}", outpart.getPDG(),
0118 outpart.getEnergy());
0119
0120 outparts->push_back(outpart);
0121
0122
0123 auto link = outlinks->create();
0124 link.setWeight(1.0);
0125 link.setFrom(outpart);
0126 link.setTo((*mcparticles)[mcID]);
0127 auto assoc = outpartsassoc->create();
0128 assoc.setWeight(1.0);
0129 assoc.setRec(outpart);
0130 assoc.setSim((*mcparticles)[mcID]);
0131 }
0132 }
0133
0134
0135
0136
0137 std::map<int, edm4eic::Cluster> MatchClusters::indexedClusters(
0138 const edm4eic::ClusterCollection* clusters,
0139 const edm4eic::MCRecoClusterParticleAssociationCollection* associations) const {
0140
0141
0142 std::map<int, std::pair<edm4eic::Cluster, float>> bestForMc;
0143
0144
0145 for (const auto cluster : *clusters) {
0146
0147 int bestMcID = -1;
0148 float bestWeight = -1.F;
0149
0150
0151 for (const auto assoc : *associations) {
0152 if (assoc.getRec() == cluster) {
0153 const int candMcID = assoc.getSim().getObjectID().index;
0154 const float w = assoc.getWeight();
0155 if (w > bestWeight) {
0156 bestWeight = w;
0157 bestMcID = candMcID;
0158 }
0159 }
0160 }
0161
0162 trace(" --> Found cluster with best mcID {} weight {} and energy {}", bestMcID, bestWeight,
0163 cluster.getEnergy());
0164
0165 if (bestMcID < 0) {
0166 trace(" --> WARNING: no valid MC truth link found, skipping cluster...");
0167 continue;
0168 }
0169
0170
0171 auto it = bestForMc.find(bestMcID);
0172 if (it == bestForMc.end()) {
0173 bestForMc.emplace(bestMcID, std::make_pair(cluster, bestWeight));
0174 } else {
0175 const float existingWeight = it->second.second;
0176 if (bestWeight > existingWeight ||
0177 (bestWeight == existingWeight && cluster.getEnergy() > it->second.first.getEnergy())) {
0178 it->second = std::make_pair(cluster, bestWeight);
0179 }
0180 }
0181 }
0182
0183
0184 std::map<int, edm4eic::Cluster> matched;
0185 for (const auto& kv : bestForMc) {
0186 matched.emplace(kv.first, kv.second.first);
0187 }
0188
0189 return matched;
0190 }
0191
0192
0193
0194 edm4eic::MutableReconstructedParticle
0195 MatchClusters::reconstruct_neutral(const edm4eic::Cluster* cluster, const double mass,
0196 const int32_t pdg) {
0197
0198 const float energy = cluster->getEnergy();
0199 const float p = energy < mass ? 0 : std::sqrt(energy * energy - mass * mass);
0200 const auto position = cluster->getPosition();
0201 const auto momentum = p * (position / edm4hep::utils::magnitude(position));
0202
0203 edm4eic::MutableReconstructedParticle part;
0204 part.setMomentum(momentum);
0205 part.setPDG(pdg);
0206 part.setCharge(0);
0207 part.setEnergy(energy);
0208 part.setMass(mass);
0209 part.addToClusters(*cluster);
0210 return part;
0211 }
0212
0213 }