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