File indexing completed on 2025-07-03 07:55:55
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 <fmt/core.h>
0017 #include <podio/ObjectID.h>
0018 #include <cmath>
0019 #include <gsl/pointers>
0020 #include <map>
0021
0022 #include "MatchClusters.h"
0023
0024 namespace eicrecon {
0025
0026 void MatchClusters::process(const MatchClusters::Input& input,
0027 const MatchClusters::Output& output) const {
0028
0029 const auto [mcparticles, inparts, inpartsassoc, clusters, clustersassoc] = input;
0030 auto [outparts, outpartsassoc] = output;
0031
0032 debug("Processing cluster info for new event");
0033
0034 debug("Step 0/2: Getting indexed list of clusters...");
0035
0036
0037 auto clusterMap = indexedClusters(clusters, clustersassoc);
0038
0039
0040
0041 debug("Step 1/2: Matching clusters to charged particles...");
0042
0043 for (const auto inpart : *inparts) {
0044 debug(" --> Processing charged particle {}, PDG {}, energy {}", inpart.getObjectID().index,
0045 inpart.getPDG(), inpart.getEnergy());
0046
0047 auto outpart = inpart.clone();
0048 outparts->push_back(outpart);
0049
0050 int mcID = -1;
0051
0052
0053 for (const auto& assoc : *inpartsassoc) {
0054 if (assoc.getRec().getObjectID() == inpart.getObjectID()) {
0055 mcID = assoc.getSim().getObjectID().index;
0056 break;
0057 }
0058 }
0059
0060 trace(" --> Found particle with mcID {}", mcID);
0061
0062 if (mcID < 0) {
0063 debug(" --> cannot match track without associated mcID");
0064 continue;
0065 }
0066
0067 if (clusterMap.contains(mcID)) {
0068 const auto& clus = clusterMap[mcID];
0069 debug(" --> found matching cluster with energy: {}", clus.getEnergy());
0070 debug(" --> adding cluster to reconstructed particle");
0071 outpart.addToClusters(clus);
0072 clusterMap.erase(mcID);
0073 }
0074
0075
0076 auto assoc = outpartsassoc->create();
0077 assoc.setRecID(outpart.getObjectID().index);
0078 assoc.setSimID(mcID);
0079 assoc.setWeight(1.0);
0080 assoc.setRec(outpart);
0081 assoc.setSim((*mcparticles)[mcID]);
0082 }
0083
0084
0085
0086 debug("Step 2/2: Creating neutrals for remaining clusters...");
0087 for (const auto& [mcID, clus] : clusterMap) {
0088 debug(" --> Processing unmatched cluster with energy: {}", clus.getEnergy());
0089
0090
0091 const auto mc = (*mcparticles)[mcID];
0092 const double mass = 0.;
0093 const int32_t pdg = 0;
0094 if (level() <= algorithms::LogLevel::kDebug) {
0095 if (mc.getCharge() != 0.0F) {
0096 debug(" --> associated mcparticle is not a neutral (PDG: {}), "
0097 "setting the reconstructed particle ID to 0 (unidentified)",
0098 mc.getPDG());
0099 }
0100 debug(" --> found matching associated mcparticle with PDG: {}, energy: {}", pdg,
0101 mc.getEnergy());
0102 }
0103
0104
0105 const auto outpart = reconstruct_neutral(&clus, mass, pdg);
0106 debug(" --> Reconstructed neutral particle with PDG: {}, energy: {}", outpart.getPDG(),
0107 outpart.getEnergy());
0108
0109 outparts->push_back(outpart);
0110
0111
0112 auto assoc = outpartsassoc->create();
0113 assoc.setRecID(outpart.getObjectID().index);
0114 assoc.setSimID(mcID);
0115 assoc.setWeight(1.0);
0116 assoc.setRec(outpart);
0117 assoc.setSim((*mcparticles)[mcID]);
0118 }
0119 }
0120
0121
0122
0123 std::map<int, edm4eic::Cluster> MatchClusters::indexedClusters(
0124 const edm4eic::ClusterCollection* clusters,
0125 const edm4eic::MCRecoClusterParticleAssociationCollection* associations) const {
0126
0127 std::map<int, edm4eic::Cluster> matched = {};
0128
0129
0130 for (const auto cluster : *clusters) {
0131
0132 int mcID = -1;
0133
0134
0135 for (const auto assoc : *associations) {
0136 if (assoc.getRec() == cluster) {
0137 mcID = assoc.getSim().getObjectID().index;
0138 break;
0139 }
0140 }
0141
0142 trace(" --> Found cluster with mcID {} and energy {}", mcID, cluster.getEnergy());
0143
0144 if (mcID < 0) {
0145 trace(" --> WARNING: no valid MC truth link found, skipping cluster...");
0146 continue;
0147 }
0148
0149 const bool duplicate = matched.contains(mcID);
0150 if (duplicate) {
0151 trace(" --> WARNING: this is a duplicate mcID, keeping the higher energy cluster");
0152
0153 if (cluster.getEnergy() < matched[mcID].getEnergy()) {
0154 continue;
0155 }
0156 }
0157 matched[mcID] = cluster;
0158 }
0159 return matched;
0160 }
0161
0162
0163
0164 edm4eic::MutableReconstructedParticle
0165 MatchClusters::reconstruct_neutral(const edm4eic::Cluster* cluster, const double mass,
0166 const int32_t pdg) {
0167
0168 const float energy = cluster->getEnergy();
0169 const float p = energy < mass ? 0 : std::sqrt(energy * energy - mass * mass);
0170 const auto position = cluster->getPosition();
0171 const auto momentum = p * (position / edm4hep::utils::magnitude(position));
0172
0173 edm4eic::MutableReconstructedParticle part;
0174 part.setMomentum(momentum);
0175 part.setPDG(pdg);
0176 part.setCharge(0);
0177 part.setEnergy(energy);
0178 part.setMass(mass);
0179 part.addToClusters(*cluster);
0180 return part;
0181 }
0182
0183 }