|
| 1 | +/** |
| 2 | + * @file EcalClusterAnalyzer.cxx |
| 3 | + * @brief Analysis of cluster performance |
| 4 | + * @author Ella Viirola, Lund University |
| 5 | + */ |
| 6 | + |
| 7 | +#include "DQM/EcalClusterAnalyzer.h" |
| 8 | + |
| 9 | +#include <algorithm> |
| 10 | +#include <fstream> |
| 11 | +#include <iostream> |
| 12 | + |
| 13 | +#include "DetDescr/SimSpecialID.h" |
| 14 | +#include "Ecal/Event/EcalCluster.h" |
| 15 | +#include "Ecal/Event/EcalHit.h" |
| 16 | +#include "SimCore/Event/SimCalorimeterHit.h" |
| 17 | +#include "SimCore/Event/SimTrackerHit.h" |
| 18 | + |
| 19 | +namespace dqm { |
| 20 | + |
| 21 | +void EcalClusterAnalyzer::configure(framework::config::Parameters& ps) { |
| 22 | + nbr_of_electrons_ = ps.getParameter<int>("nbr_of_electrons"); |
| 23 | + |
| 24 | + ecal_sim_hit_coll_ = ps.getParameter<std::string>("ecal_sim_hit_coll"); |
| 25 | + ecal_sim_hit_pass_ = ps.getParameter<std::string>("ecal_sim_hit_pass"); |
| 26 | + |
| 27 | + rec_hit_coll_name_ = ps.getParameter<std::string>("rec_hit_coll_name"); |
| 28 | + rec_hit_pass_name_ = ps.getParameter<std::string>("rec_hit_pass_name"); |
| 29 | + |
| 30 | + cluster_coll_name_ = ps.getParameter<std::string>("cluster_coll_name"); |
| 31 | + cluster_pass_name_ = ps.getParameter<std::string>("cluster_pass_name"); |
| 32 | + return; |
| 33 | +} |
| 34 | + |
| 35 | +void EcalClusterAnalyzer::analyze(const framework::Event& event) { |
| 36 | + const auto& ecal_rec_hits{event.getCollection<ldmx::EcalHit>( |
| 37 | + rec_hit_coll_name_, rec_hit_pass_name_)}; |
| 38 | + const auto& ecal_sim_hits{event.getCollection<ldmx::SimCalorimeterHit>( |
| 39 | + ecal_sim_hit_coll_, ecal_sim_hit_pass_)}; |
| 40 | + const auto& ecal_clusters{event.getCollection<ldmx::EcalCluster>( |
| 41 | + cluster_coll_name_, cluster_pass_name_)}; |
| 42 | + |
| 43 | + if (ecal_clusters.size() == nbr_of_electrons_) |
| 44 | + histograms_.fill("correctly_predicted_events", 1); // correct |
| 45 | + else if (ecal_clusters.size() < nbr_of_electrons_) |
| 46 | + histograms_.fill("correctly_predicted_events", 0); // undercounting |
| 47 | + else if (ecal_clusters.size() > nbr_of_electrons_) |
| 48 | + histograms_.fill("correctly_predicted_events", 2); // overcounting |
| 49 | + |
| 50 | + std::unordered_map<int, std::pair<int, std::vector<double>>> hitInfo; |
| 51 | + hitInfo.reserve(ecal_rec_hits.size()); |
| 52 | + |
| 53 | + double dist; |
| 54 | + if (nbr_of_electrons_ == 2) { |
| 55 | + // Measures distance between two electrons in the ECal scoring plane |
| 56 | + // TODO: generalize for n electrons |
| 57 | + std::vector<float> pos1; |
| 58 | + std::vector<float> pos2; |
| 59 | + bool p1 = false; |
| 60 | + bool p2 = false; |
| 61 | + |
| 62 | + const auto& ecal_sp_hits{ |
| 63 | + event.getCollection<ldmx::SimTrackerHit>("EcalScoringPlaneHits")}; |
| 64 | + for (const ldmx::SimTrackerHit& spHit : ecal_sp_hits) { |
| 65 | + if (spHit.getTrackID() == 1) { |
| 66 | + pos1 = spHit.getPosition(); |
| 67 | + p1 = true; |
| 68 | + } else if (spHit.getTrackID() == 2) { |
| 69 | + pos2 = spHit.getPosition(); |
| 70 | + p2 = true; |
| 71 | + } |
| 72 | + } |
| 73 | + if (p1 && p2) |
| 74 | + dist = std::sqrt(std::pow((pos1[0] - pos2[0]), 2) + |
| 75 | + std::pow((pos1[1] - pos2[1]), 2)); |
| 76 | + } |
| 77 | + |
| 78 | + for (const auto& hit : ecal_rec_hits) { |
| 79 | + auto it = std::find_if( |
| 80 | + ecal_sim_hits.begin(), ecal_sim_hits.end(), |
| 81 | + [&hit](const auto& simHit) { return simHit.getID() == hit.getID(); }); |
| 82 | + if (it != ecal_sim_hits.end()) { |
| 83 | + // if found a simhit matching this rechit |
| 84 | + int ancestor = 0; |
| 85 | + int prevAncestor = 0; |
| 86 | + bool tagged = false; |
| 87 | + int tag = 0; |
| 88 | + std::vector<double> edep; |
| 89 | + edep.resize(nbr_of_electrons_ + 1); |
| 90 | + for (int i = 0; i < it->getNumberOfContribs(); i++) { |
| 91 | + // for each contrib in this simhit |
| 92 | + const auto& c = it->getContrib(i); |
| 93 | + // get origin electron ID |
| 94 | + ancestor = c.originID; |
| 95 | + // store energy from this contrib at index = origin electron ID |
| 96 | + if (ancestor <= nbr_of_electrons_) edep[ancestor] += c.edep; |
| 97 | + if (!tagged && i != 0 && prevAncestor != ancestor) { |
| 98 | + // if origin electron ID does not match previous origin electron ID |
| 99 | + // this hit has contributions from several electrons, ie mixed case |
| 100 | + tag = 0; |
| 101 | + tagged = true; |
| 102 | + } |
| 103 | + prevAncestor = ancestor; |
| 104 | + } |
| 105 | + if (!tagged) { |
| 106 | + // if not tagged, hit was from a single electron |
| 107 | + tag = prevAncestor; |
| 108 | + } |
| 109 | + histograms_.fill("ancestors", tag); |
| 110 | + hitInfo.insert({hit.getID(), std::make_pair(tag, edep)}); |
| 111 | + } |
| 112 | + } |
| 113 | + |
| 114 | + int clusteredHits = 0; |
| 115 | + |
| 116 | + for (const auto& cl : ecal_clusters) { |
| 117 | + // for each cluster |
| 118 | + // total number of hits coming from electron, index = electron ID |
| 119 | + std::vector<double> n; |
| 120 | + n.resize(nbr_of_electrons_ + 1); |
| 121 | + // total number of energy coming from electron, index = electron ID |
| 122 | + std::vector<double> e; |
| 123 | + e.resize(nbr_of_electrons_ + 1); |
| 124 | + double eSum = 0.; |
| 125 | + double nSum = 0.; |
| 126 | + |
| 127 | + const auto& hitIDs = cl.getHitIDs(); |
| 128 | + for (const auto& id : hitIDs) { |
| 129 | + // for each hit in cluster, find previously stored info |
| 130 | + auto it = hitInfo.find(id); |
| 131 | + if (it != hitInfo.end()) { |
| 132 | + auto t = it->second; |
| 133 | + auto eId = t.first; // origin electron ID (or 0 for mixed) |
| 134 | + auto energies = t.second; // energy vector |
| 135 | + n[eId]++; // increment number of hits coming from this electron |
| 136 | + nSum++; |
| 137 | + |
| 138 | + double hitESum = 0.; |
| 139 | + for (int i = 1; i < nbr_of_electrons_ + 1; i++) { |
| 140 | + // loop through energy vector |
| 141 | + if (energies[i] > 0.) { |
| 142 | + hitESum += energies[i]; |
| 143 | + // add energy from electron i in this hit to total energy from |
| 144 | + // electron i in cluster |
| 145 | + e[i] += energies[i]; |
| 146 | + } |
| 147 | + } |
| 148 | + // if mixed hit, add the total energy of this hit to mixed hit energy |
| 149 | + // counter |
| 150 | + if (eId == 0) e[0] += hitESum; |
| 151 | + eSum += hitESum; |
| 152 | + |
| 153 | + clusteredHits++; |
| 154 | + } |
| 155 | + } |
| 156 | + |
| 157 | + if (eSum > 0) { |
| 158 | + // get largest energy contribution |
| 159 | + double eMax = *max_element(e.begin(), e.end()); |
| 160 | + // energy purity = largest contribution / all energy |
| 161 | + histograms_.fill("energy_percentage", 100. * (eMax / eSum)); |
| 162 | + if (e[0] > 0.) histograms_.fill("mixed_hit_energy", 100. * (e[0] / eSum)); |
| 163 | + |
| 164 | + histograms_.fill("total_energy_vs_hits", eSum, cl.getHitIDs().size()); |
| 165 | + histograms_.fill("total_energy_vs_purity", eSum, 100. * (eMax / eSum)); |
| 166 | + |
| 167 | + if (nbr_of_electrons_ == 2) |
| 168 | + histograms_.fill("distance_energy_purity", dist, 100. * (eMax / eSum)); |
| 169 | + } |
| 170 | + if (nSum > 0) { |
| 171 | + double nMax = *max_element(n.begin(), n.end()); |
| 172 | + histograms_.fill("same_ancestor", 100. * (nMax / nSum)); |
| 173 | + } |
| 174 | + } |
| 175 | + |
| 176 | + histograms_.fill("clusterless_hits", (ecal_rec_hits.size() - clusteredHits)); |
| 177 | + histograms_.fill("total_rechits_in_event", ecal_rec_hits.size()); |
| 178 | + histograms_.fill( |
| 179 | + "clusterless_hits_percentage", |
| 180 | + 100. * (ecal_rec_hits.size() - clusteredHits) / ecal_rec_hits.size()); |
| 181 | +} |
| 182 | + |
| 183 | +} // namespace dqm |
| 184 | + |
| 185 | +DECLARE_ANALYZER(dqm::EcalClusterAnalyzer) |
0 commit comments