LDMX Software
dqm::EcalClusterAnalyzer Class Reference

Public Member Functions

 EcalClusterAnalyzer (const std::string &name, framework::Process &process)
 
void configure (framework::config::Parameters &ps) override
 Callback for the EventProcessor to configure itself from the given set of parameters.
 
void analyze (const framework::Event &event) override
 Process the event and make histograms or summaries.
 
- Public Member Functions inherited from framework::Analyzer
 Analyzer (const std::string &name, Process &process)
 Class constructor.
 
virtual void process (Event &event) final
 Processing an event for an Analyzer is calling analyze.
 
virtual void beforeNewRun (ldmx::RunHeader &run_header) final
 Don't allow Analyzers to add parameters to the run header.
 
- Public Member Functions inherited from framework::EventProcessor
 DECLARE_FACTORY (EventProcessor, EventProcessor *, const std::string &, Process &)
 declare that we have a factory for this class
 
 EventProcessor (const std::string &name, Process &process)
 Class constructor.
 
virtual ~EventProcessor ()=default
 Class destructor.
 
virtual void onNewRun (const ldmx::RunHeader &run_header)
 Callback for the EventProcessor to take any necessary action when the run being processed changes.
 
virtual void onFileOpen (EventFile &event_file)
 Callback for the EventProcessor to take any necessary action when a new event input ROOT file is opened.
 
virtual void onFileClose (EventFile &event_file)
 Callback for the EventProcessor to take any necessary action when a event input ROOT file is closed.
 
virtual void onProcessStart ()
 Callback for the EventProcessor to take any necessary action when the processing of events starts, such as creating histograms.
 
virtual void onProcessEnd ()
 Callback for the EventProcessor to take any necessary action when the processing of events finishes, such as calculating job-summary quantities.
 
template<class T >
const T & getCondition (const std::string &condition_name)
 Access a conditions object for the current event.
 
TDirectory * getHistoDirectory ()
 Access/create a directory in the histogram file for this event processor to create histograms and analysis tuples.
 
void setStorageHint (framework::StorageControl::Hint hint)
 Mark the current event as having the given storage control hint from this module_.
 
void setStorageHint (framework::StorageControl::Hint hint, const std::string &purposeString)
 Mark the current event as having the given storage control hint from this module and the given purpose string.
 
int getLogFrequency () const
 Get the current logging frequency from the process.
 
int getRunNumber () const
 Get the run number from the process.
 
std::string getName () const
 Get the processor name.
 
void createHistograms (const std::vector< framework::config::Parameters > &histos)
 Internal function which is used to create histograms passed from the python configuration @parma histos vector of Parameters that configure histograms to create.
 

Private Attributes

bool use_simulated_electron_number_
 Use the number of simulated electrons instead of the number of determined by the TS track counting.
 
int nbr_of_electrons_
 What is the number of electrons in the event?
 
std::string ecal_sim_hit_coll_
 
std::string ecal_sim_hit_pass_
 
std::string rec_hit_coll_name_
 
std::string rec_hit_pass_name_
 
std::string cluster_coll_name_
 
std::string cluster_pass_name_
 
std::string ecal_sp_hits_passname_
 
bool inverse_skim_
 
int n_ecal_clusters_min_
 

Additional Inherited Members

- Protected Member Functions inherited from framework::EventProcessor
void abortEvent ()
 Abort the event immediately.
 
- Protected Attributes inherited from framework::EventProcessor
HistogramPool histograms_
 helper object for making and filling histograms
 
NtupleManagerntuple_ {NtupleManager::getInstance()}
 Manager for any ntuples.
 
logging::logger the_log_
 The logger for this EventProcessor.
 

Detailed Description

Definition at line 30 of file EcalClusterAnalyzer.h.

Constructor & Destructor Documentation

◆ EcalClusterAnalyzer()

dqm::EcalClusterAnalyzer::EcalClusterAnalyzer ( const std::string & name,
framework::Process & process )
inline

Definition at line 32 of file EcalClusterAnalyzer.h.

33 : Analyzer(name, process) {}
virtual void process(Event &event) final
Processing an event for an Analyzer is calling analyze.
Analyzer(const std::string &name, Process &process)
Class constructor.

Member Function Documentation

◆ analyze()

void dqm::EcalClusterAnalyzer::analyze ( const framework::Event & event)
overridevirtual

Process the event and make histograms or summaries.

Parameters
eventThe Event to analyze

Implements framework::Analyzer.

Definition at line 25 of file EcalClusterAnalyzer.cxx.

25 {
26 const auto& ecal_rec_hits{event.getCollection<ldmx::EcalHit>(
27 rec_hit_coll_name_, rec_hit_pass_name_)};
28 const auto& ecal_sim_hits{event.getCollection<ldmx::SimCalorimeterHit>(
29 ecal_sim_hit_coll_, ecal_sim_hit_pass_)};
30 const auto& ecal_clusters{event.getCollection<ldmx::EcalCluster>(
31 cluster_coll_name_, cluster_pass_name_)};
32
33 // Determine the number of recoil electrons in the event
34 // By default from the TS track counting
35 int nbr_of_electrons{event.getElectronCount()};
36 // If configured to use the simulated electron number, use that instead
38 nbr_of_electrons = nbr_of_electrons_;
39 }
40
41 std::map<int, int> layer_cluster_count;
42 for (const auto& cluster : ecal_clusters) {
43 auto layer = cluster.getLayer();
44 layer_cluster_count[layer]++;
45 }
46
47 int total_clusters = 0;
48 for (const auto& [layer, count] : layer_cluster_count) {
49 total_clusters += count;
50 }
51
52 int n_ecal_clusters = 0;
53 if (layer_cluster_count.size() != 0) {
54 n_ecal_clusters = static_cast<int>(std::round(
55 static_cast<double>(total_clusters) / layer_cluster_count.size()));
56 }
57
58 ldmx_log(info) << "Avg number of clusters per layer: " << n_ecal_clusters;
59 // Fill histograms with the number of clusters
60 histograms_.fill("number_of_clusters", total_clusters);
61 histograms_.fill("number_of_clusters_per_layer", n_ecal_clusters);
62 histograms_.fill("number_of_clusters_first_layer", layer_cluster_count[0]);
63
64 // Fill simplied 3-bin histogram to check the prediction
65 if (n_ecal_clusters == nbr_of_electrons) {
66 // correct
67 histograms_.fill("correctly_predicted_events", 1);
68 } else if (n_ecal_clusters < nbr_of_electrons) {
69 // undercounting
70 histograms_.fill("correctly_predicted_events", 0);
71 } else if (n_ecal_clusters > nbr_of_electrons) {
72 // overcounting
73 histograms_.fill("correctly_predicted_events", 2);
74 }
75
76 std::unordered_map<int, std::pair<int, std::vector<double>>> hit_info;
77 hit_info.reserve(ecal_rec_hits.size());
78
79 // Determine the truth information for the recoil electron
80 std::vector<std::vector<float>> sp_electron_positions;
81 const auto& ecal_sp_hits{event.getCollection<ldmx::SimTrackerHit>(
82 "EcalScoringPlaneHits", ecal_sp_hits_passname_)};
83
84 std::vector<ldmx::SimTrackerHit> sorted_sp_hits = ecal_sp_hits;
85 std::sort(sorted_sp_hits.begin(), sorted_sp_hits.end(),
86 [](const ldmx::SimTrackerHit& a, const ldmx::SimTrackerHit& b) {
87 return a.getTrackID() < b.getTrackID();
88 });
89
90 ldmx_log(trace) << "Number of ECal Scoring Plane Hits: "
91 << sorted_sp_hits.size();
92
93 // Collect positions of all recoil electrons on the SP
94 // relying on the track ID to identify them
95 unsigned int n_filled = 0;
96 for (const ldmx::SimTrackerHit& sp_hit : sorted_sp_hits) {
97 if (sp_hit.getPdgID() != 11) continue;
98 if (sp_hit.getMomentum()[2] <= 0) continue;
99 ldmx::SimSpecialID hit_id(sp_hit.getID());
100 // Ecal scoring plane is plane 31
101 if (hit_id.plane() != 31) continue;
102 if (n_filled < nbr_of_electrons) {
103 ldmx_log(trace) << "\tSP Hit to be added with Track ID : "
104 << sp_hit.getTrackID() << ", SP Hit Position ("
105 << sp_hit.getPosition()[0] << ", "
106 << sp_hit.getPosition()[1] << ", "
107 << sp_hit.getPosition()[2] << ") mm";
108 sp_electron_positions.push_back(sp_hit.getPosition());
109 n_filled++;
110 }
111 }
112
113 ldmx_log(info) << "Number of ECal CLUE clusters: " << n_ecal_clusters
114 << ", TS counted electrons: " << nbr_of_electrons
115 << ", SP electrons: " << sp_electron_positions.size();
116
117 double sp_ele_dist{9999.};
118 if (nbr_of_electrons == 2 && sp_electron_positions.size() > 1) {
119 // Measures sp_ele_distance between two electrons in the ECal scoring plane
120 // TODO: generalize for n electrons
121 std::vector<float> pos1;
122 std::vector<float> pos2;
123 pos1 = sp_electron_positions[0];
124 pos2 = sp_electron_positions[1];
125 sp_ele_dist = std::sqrt((pos1[0] - pos2[0]) * (pos1[0] - pos2[0]) +
126 (pos1[1] - pos2[1]) * (pos1[1] - pos2[1]));
127
128 } // end block about the scoring plane hits
129
130 ldmx_log(trace) << "Distance between the two e- in the ECal scoring plane: "
131 << sp_ele_dist << " mm";
132
133 // Loop over the rechits and find the matching simhits
134 ldmx_log(trace) << "Loop over the rechits and find the matching simhits";
135 for (const auto& hit : ecal_rec_hits) {
136 auto it = std::find_if(
137 ecal_sim_hits.begin(), ecal_sim_hits.end(),
138 [&hit](const auto& sim_hit) { return sim_hit.getID() == hit.getID(); });
139 if (it != ecal_sim_hits.end()) {
140 // if found a simhit matching this rechit
141 int ancestor = 0;
142 int prev_ancestor = 0;
143 bool tagged = false;
144 int tag = 0;
145 std::vector<double> edep;
146 edep.resize(nbr_of_electrons + 1);
147 for (int i = 0; i < it->getNumberOfContribs(); i++) {
148 // for each contrib in this simhit
149 const auto& contrib = it->getContrib(i);
150 // get origin electron ID
151 ancestor = contrib.origin_id_;
152 // store energy from this contrib at index = origin electron ID
153 if (ancestor <= nbr_of_electrons) edep[ancestor] += contrib.edep_;
154 if (!tagged && i != 0 && prev_ancestor != ancestor) {
155 // if origin electron ID does not match previous origin electron ID
156 // this hit has contributions from several electrons, ie mixed case
157 tag = 0;
158 tagged = true;
159 }
160 prev_ancestor = ancestor;
161 }
162 if (!tagged) {
163 // if not tagged, hit was from a single electron
164 tag = prev_ancestor;
165 }
166 histograms_.fill("ancestors", tag);
167 hit_info.insert({hit.getID(), std::make_pair(tag, edep)});
168 } // end if simhit found
169 } // end loop on the rechits
170
171 // Loop over the clusters
172 int clustered_hits = 0;
173 ldmx_log(trace) << "Loop over the clusters, N = " << n_ecal_clusters;
174 for (const auto& cl : ecal_clusters) {
175 auto layer = cl.getLayer();
176 ldmx_log(trace) << "Cluster in layer " << layer
177 << ", energy: " << cl.getEnergy()
178 << ", number of hits: " << cl.getHitIDs().size();
179 auto cluster_centroid_x = cl.getCentroidX();
180 auto cluster_centroid_y = cl.getCentroidY();
181 auto cluster_rms_x = cl.getRMSX();
182 auto cluster_rms_y = cl.getRMSY();
183
184 // Find the closest sp_electron_positions to the cluster centroid
185 double min_distance = 9999.;
186 double sp_clue_x_residuals = 9999.;
187 double sp_clue_y_residuals = 9999.;
188 for (const auto& sp_pos : sp_electron_positions) {
189 double distance = std::sqrt(
190 (sp_pos[0] - cluster_centroid_x) * (sp_pos[0] - cluster_centroid_x) +
191 (sp_pos[1] - cluster_centroid_y) * (sp_pos[1] - cluster_centroid_y));
192 if (distance < min_distance) {
193 min_distance = distance;
194 sp_clue_x_residuals = sp_pos[0] - cluster_centroid_x;
195 sp_clue_y_residuals = sp_pos[1] - cluster_centroid_y;
196 }
197 } // end loop on the scoring plane electron positions
198 // Fill histogram with the distance to the closest scoring plane electron
199 ldmx_log(trace) << "\tCluster centroid: (" << cluster_centroid_x << " +/- "
200 << cluster_rms_x << ", " << cluster_centroid_y << " +/- "
201 << cluster_rms_y
202 << " mm; min distance to SP electron: " << min_distance
203 << " mm";
204 if (layer == 0) {
205 histograms_.fill("sp_clue_distance", min_distance);
206 histograms_.fill("sp_clue_x_residual", sp_clue_x_residuals);
207 histograms_.fill("sp_clue_y_residual", sp_clue_y_residuals);
208 }
209 histograms_.fill("sp_clue_distance_vs_layer", layer, min_distance);
210
211 // for each cluster
212 // total number of hits coming from electron, index = electron ID
213 std::vector<double> n_hits_from_electron;
214 n_hits_from_electron.resize(nbr_of_electrons + 1);
215 // total number of energy coming from electron, index = electron ID
216 std::vector<double> energy_from_electron;
217 energy_from_electron.resize(nbr_of_electrons + 1);
218 double energy_sum = 0.;
219 double n_sum = 0.;
220
221 const auto& hit_ids = cl.getHitIDs();
222 for (const auto& id : hit_ids) {
223 // for each hit in cluster, find previously stored info
224 auto it = hit_info.find(id);
225 if (it != hit_info.end()) {
226 auto t = it->second;
227 // origin electron ID (or 0 for mixed)
228 auto id_electron = t.first;
229 // energy vector
230 auto energies = t.second;
231 // increment number of hits coming from this electron
232 n_hits_from_electron[id_electron]++;
233 n_sum++;
234
235 double hit_energy_sum = 0.;
236 for (int i = 1; i < nbr_of_electrons + 1; i++) {
237 // loop through energy vector
238 if (energies[i] > 0.) {
239 energy_sum += energies[i];
240 // add energy from electron i in this hit to total energy from
241 // electron i in cluster
242 energy_from_electron[i] += energies[i];
243 }
244 }
245 // if mixed hit, add the total energy of this hit to mixed hit energy
246 // counter
247 if (id_electron == 0) energy_from_electron[0] += hit_energy_sum;
248 energy_sum += hit_energy_sum;
249
250 clustered_hits++;
251 } // end if hit info found
252 } // end loop on the hit IDs in the cluster
253
254 if (energy_sum > 0) {
255 // get largest energy contribution
256 double max_energy_contribution = *max_element(
257 energy_from_electron.begin(), energy_from_electron.end());
258 // energy purity = largest contribution / all energy
259 histograms_.fill("energy_percentage",
260 100. * (max_energy_contribution / energy_sum));
261 if (energy_from_electron[0] > 0.)
262 histograms_.fill("mixed_hit_energy",
263 100. * (energy_from_electron[0] / energy_sum));
264
265 histograms_.fill("total_energy_vs_hits", energy_sum,
266 cl.getHitIDs().size());
267 histograms_.fill("total_energy_vs_purity", energy_sum,
268 100. * (max_energy_contribution / energy_sum));
269
270 if (nbr_of_electrons == 2) {
271 histograms_.fill("sp_ele_distance_vs_purity", sp_ele_dist,
272 100. * (max_energy_contribution / energy_sum));
273 }
274 }
275 if (n_sum > 0) {
276 double n_max = *max_element(n_hits_from_electron.begin(),
277 n_hits_from_electron.end());
278 histograms_.fill("same_ancestor", 100. * (n_max / n_sum));
279 }
280 } // end loop on the clusters
281
282 histograms_.fill("clusterless_hits", (ecal_rec_hits.size() - clustered_hits));
283 histograms_.fill("total_rechits_in_event", ecal_rec_hits.size());
285 "clusterless_hits_percentage",
286 100. * (ecal_rec_hits.size() - clustered_hits) / ecal_rec_hits.size());
287
288 if (inverse_skim_) {
289 // inverse operation: drop events with enough clusters
290 if (n_ecal_clusters > n_ecal_clusters_min_) {
292 } else {
294 }
295 } else {
296 // normal operation: keep events with enough clusters
297 if (n_ecal_clusters > n_ecal_clusters_min_) {
299 } else {
301 }
302 }
303}
int nbr_of_electrons_
What is the number of electrons in the event?
bool use_simulated_electron_number_
Use the number of simulated electrons instead of the number of determined by the TS track counting.
HistogramPool histograms_
helper object for making and filling histograms
void setStorageHint(framework::StorageControl::Hint hint)
Mark the current event as having the given storage control hint from this module_.
void fill(const std::string &name, const T &val)
Fill a 1D histogram.
Stores cluster information from the ECal.
Definition EcalCluster.h:20
Stores reconstructed hit information from the ECAL.
Definition EcalHit.h:19
Stores simulated calorimeter hit information.
Implements detector ids for special simulation-derived hits like scoring planes.
Represents a simulated tracker hit in the simulation.
constexpr StorageControl::Hint HINT_SHOULD_DROP
storage control hint alias for backwards compatibility
constexpr StorageControl::Hint HINT_SHOULD_KEEP
storage control hint alias for backwards compatibility

References framework::HistogramPool::fill(), framework::HINT_SHOULD_DROP, framework::HINT_SHOULD_KEEP, framework::EventProcessor::histograms_, nbr_of_electrons_, ldmx::SimSpecialID::plane(), framework::EventProcessor::setStorageHint(), and use_simulated_electron_number_.

◆ configure()

void dqm::EcalClusterAnalyzer::configure ( framework::config::Parameters & parameters)
overridevirtual

Callback for the EventProcessor to configure itself from the given set of parameters.

The parameters a processor has access to are the member variables of the python class in the sequence that has className equal to the EventProcessor class name.

For an example, look at MyProcessor.

Parameters
parametersParameters for configuration.

Reimplemented from framework::EventProcessor.

Definition at line 5 of file EcalClusterAnalyzer.cxx.

5 {
7 ps.get<bool>("use_simulated_electron_number");
8 nbr_of_electrons_ = ps.get<int>("nbr_of_electrons");
9
10 ecal_sim_hit_coll_ = ps.get<std::string>("ecal_sim_hit_coll");
11 ecal_sim_hit_pass_ = ps.get<std::string>("ecal_sim_hit_pass");
12
13 rec_hit_coll_name_ = ps.get<std::string>("rec_hit_coll_name");
14 rec_hit_pass_name_ = ps.get<std::string>("rec_hit_pass_name");
15
16 cluster_coll_name_ = ps.get<std::string>("cluster_coll_name");
17 cluster_pass_name_ = ps.get<std::string>("cluster_pass_name");
18
19 ecal_sp_hits_passname_ = ps.get<std::string>("ecal_sp_hits_passname");
20 inverse_skim_ = ps.get<bool>("inverse_skim");
21 n_ecal_clusters_min_ = ps.get<int>("n_ecal_clusters_min");
22 return;
23}

References framework::config::Parameters::get(), nbr_of_electrons_, and use_simulated_electron_number_.

Member Data Documentation

◆ cluster_coll_name_

std::string dqm::EcalClusterAnalyzer::cluster_coll_name_
private

Definition at line 59 of file EcalClusterAnalyzer.h.

◆ cluster_pass_name_

std::string dqm::EcalClusterAnalyzer::cluster_pass_name_
private

Definition at line 62 of file EcalClusterAnalyzer.h.

◆ ecal_sim_hit_coll_

std::string dqm::EcalClusterAnalyzer::ecal_sim_hit_coll_
private

Definition at line 47 of file EcalClusterAnalyzer.h.

◆ ecal_sim_hit_pass_

std::string dqm::EcalClusterAnalyzer::ecal_sim_hit_pass_
private

Definition at line 50 of file EcalClusterAnalyzer.h.

◆ ecal_sp_hits_passname_

std::string dqm::EcalClusterAnalyzer::ecal_sp_hits_passname_
private

Definition at line 64 of file EcalClusterAnalyzer.h.

◆ inverse_skim_

bool dqm::EcalClusterAnalyzer::inverse_skim_
private

Definition at line 67 of file EcalClusterAnalyzer.h.

◆ n_ecal_clusters_min_

int dqm::EcalClusterAnalyzer::n_ecal_clusters_min_
private

Definition at line 70 of file EcalClusterAnalyzer.h.

◆ nbr_of_electrons_

int dqm::EcalClusterAnalyzer::nbr_of_electrons_
private

What is the number of electrons in the event?

Definition at line 44 of file EcalClusterAnalyzer.h.

Referenced by analyze(), and configure().

◆ rec_hit_coll_name_

std::string dqm::EcalClusterAnalyzer::rec_hit_coll_name_
private

Definition at line 53 of file EcalClusterAnalyzer.h.

◆ rec_hit_pass_name_

std::string dqm::EcalClusterAnalyzer::rec_hit_pass_name_
private

Definition at line 56 of file EcalClusterAnalyzer.h.

◆ use_simulated_electron_number_

bool dqm::EcalClusterAnalyzer::use_simulated_electron_number_
private

Use the number of simulated electrons instead of the number of determined by the TS track counting.

Definition at line 41 of file EcalClusterAnalyzer.h.

Referenced by analyze(), and configure().


The documentation for this class was generated from the following files: