LDMX Software
VisiblesCutflow.cxx
1#include "DQM/VisiblesCutflow.h"
2
3// LDMX
4#include "DetDescr/EcalID.h"
5#include "DetDescr/HcalID.h"
6#include "DetDescr/SimSpecialID.h"
7#include "Ecal/Event/EcalHit.h"
11#include "SimCore/Event/SimParticle.h"
13#include "Tracking/Event/Track.h"
14
15// C++
16#include <algorithm>
17#include <cmath>
18#include <fstream>
19#include <iostream>
20#include <numeric>
21
22namespace dqm {
23
25 rt_ = std::make_unique<ldmx::ort::ONNXRuntime>(
26 parameters.get<std::string>("bdt_file"));
27 feature_list_name_ = parameters.get<std::string>("feature_list_name");
28 bdt_cut_val_ = parameters.get<double>("disc_cut");
29 ecal_bdt_cut_val_ = parameters.get<double>("ecal_disc_cut");
30 all_cuts_ = parameters.get<bool>("all_cuts");
31
32 beam_energy_mev_ = parameters.get<double>("beam_energy");
33
34 // collection names
35 hcal_rec_collection_ = parameters.get<std::string>("hcal_rec_coll_name");
36 hcal_rec_pass_name_ = parameters.get<std::string>("hcal_rec_pass_name");
37
38 ecal_rec_collection_ = parameters.get<std::string>("ecal_rec_coll_name");
39 ecal_rec_pass_name_ = parameters.get<std::string>("ecal_rec_pass_name");
40
41 recoil_from_tracking_ = parameters.get<bool>("recoil_from_tracking");
42 track_collection_ = parameters.get<std::string>("track_collection");
43 track_pass_name_ = parameters.get<std::string>("track_pass_name");
44
45 sp_collection_ = parameters.get<std::string>("sp_coll_name");
46 sp_pass_name_ = parameters.get<std::string>("sp_pass_name");
47 sim_particles_pass_name_ =
48 parameters.get<std::string>("sim_particles_pass_name");
49
50 ecal_veto_collection_ = parameters.get<std::string>("ecal_veto_coll_name");
51 ecal_veto_pass_ = parameters.get<std::string>("ecal_veto_pass_name");
52}
53
54bool VisiblesCutflow::inList(std::vector<int> parents, int track_id) {
55 return std::find(parents.begin(), parents.end(), track_id) != parents.end();
56}
57
59 std::vector<float> bdt_features;
60
61 double decay_z;
62
63 const auto &particle_map{event.getMap<int, ldmx::SimParticle>(
64 "SimParticles", sim_particles_pass_name_)};
65
66 for (auto const &it : particle_map) {
67 std::vector<int> parents = it.second.getParents();
68 for (int track_id : parents) {
69 if (track_id == 0 && it.second.getPdgID() == -11) {
70 decay_z = it.second.getVertex()[2];
71 }
72 }
73 }
74
75 histograms_.fill("total_events", decay_z);
76
77 // Get target scoring plane hits for recoil electron
78 // Use this to calculate the projected photon line vector
79 // This currently uses truth-level information, but it should be replaced
80 // by reconstructed tracker information, when available
81 std::vector<double> gamma_p(3);
82 double gamma_energy = 90000.;
83 std::vector<double> gamma_x0(3);
84
85 std::vector<double> recoil_p(3);
86 bool found_recoil_e = false;
87
88 if (recoil_from_tracking_) {
89 const auto &recoil_tracks{
90 event.getCollection<ldmx::Track>(track_collection_, track_pass_name_)};
91 for (auto &track : recoil_tracks) {
92 // need to figure out how to best isolate candidate electron track
93 if (track.q() == 1 && track.getNhits() == 5) {
94 gamma_x0 = track.getPosition();
95 gamma_p[0] = -1. * track.getMomentum()[0];
96 gamma_p[1] = -1. * track.getMomentum()[1];
97 gamma_p[2] = 8000. - track.getMomentum()[2];
98 }
99 }
100 } else {
101 if (event.exists(sp_collection_, sp_pass_name_)) {
102 const auto &target_sp_hits = event.getCollection<ldmx::SimTrackerHit>(
103 sp_collection_, sp_pass_name_);
104 bool found_rec = false;
105 for (auto const &it : particle_map) {
106 for (auto const &sphit : target_sp_hits) {
107 if (sphit.getPosition()[2] > 0) {
108 if (it.first == sphit.getTrackID()) {
109 if (it.second.getPdgID() == 622) {
110 std::vector<float> x0_float = sphit.getPosition();
111 std::vector<double> x0_double(x0_float.begin(), x0_float.end());
112 gamma_x0 = x0_double;
113 gamma_p = sphit.getMomentum();
114 gamma_energy = sphit.getEnergy();
115 found_rec = true;
116 }
117 if (it.second.getPdgID() == 11 &&
118 inList(it.second.getParents(), 0)) {
119 if (!found_rec) {
120 std::vector<float> x0_float = sphit.getPosition();
121 std::vector<double> x0_double(x0_float.begin(),
122 x0_float.end());
123 gamma_x0 = x0_double;
124 gamma_p[0] = -1. * sphit.getMomentum()[0];
125 gamma_p[1] = -1. * sphit.getMomentum()[1];
126 gamma_p[2] = beam_energy_mev_ - sphit.getMomentum()[2];
127 gamma_energy = beam_energy_mev_ - sphit.getEnergy();
128 found_rec = true;
129 }
130 recoil_p = sphit.getMomentum();
131 found_recoil_e = true;
132 }
133 }
134 }
135 }
136 }
137 }
138 }
139 histograms_.fill("beam_energy_frac", gamma_energy / 8000.);
140 histograms_.fill("beam_angle",
141 std::acos(gamma_p[2] / std::sqrt(gamma_p[0] * gamma_p[0] +
142 gamma_p[1] * gamma_p[1] +
143 gamma_p[2] * gamma_p[2])));
144
145 double p_mag = 0.;
146 if (found_recoil_e) {
147 p_mag = std::sqrt(recoil_p[0] * recoil_p[0] + recoil_p[1] * recoil_p[1] +
148 recoil_p[2] * recoil_p[2]);
149 }
150
151 if (p_mag < 50. && all_cuts_) {
152 return;
153 }
154 histograms_.fill("pass_acceptance", decay_z);
155
156 // Get EcalRecHits, check that trigger is passed
157 const auto &ecal_rec_hits = event.getCollection<ldmx::EcalHit>(
158 ecal_rec_collection_, ecal_rec_pass_name_);
159 const auto &hcal_rec_hits = event.getCollection<ldmx::HcalHit>(
160 hcal_rec_collection_, hcal_rec_pass_name_);
161
162 double trigger = 0.;
163 double ecal_energy = 0.;
164 double hcal_energy = 0.;
165 bool hcal_containment = true;
166
167 for (const ldmx::EcalHit &hit : ecal_rec_hits) {
168 if (hit.getEnergy() > 0.) {
169 ecal_energy += hit.getEnergy();
170 if (hit.getZPos() <= 541.722) {
171 trigger += hit.getEnergy();
172 }
173 }
174 }
175 for (const ldmx::HcalHit &hit : hcal_rec_hits) {
176 if (hit.getEnergy() > 0.) {
177 ldmx::HcalID det_id(hit.getID());
178 if (det_id.getSection() != 0) {
179 continue;
180 }
181 if (det_id.getLayerID() == 1 && hit.getPE() > 5) {
182 hcal_containment = false;
183 }
184 hcal_energy += 12. * hit.getEnergy();
185 }
186 }
187
188 if (trigger > 3160. && all_cuts_) {
189 return;
190 }
191 histograms_.fill("pass_trigger", decay_z);
192
193 if (ecal_energy > 3160. && all_cuts_) {
194 return;
195 }
196 histograms_.fill("pass_ecal_energy", decay_z);
197
198 if (p_mag > 2400. && all_cuts_) {
199 return;
200 }
201 histograms_.fill("pass_tracker_veto", decay_z);
202
203 const auto &ecal_veto{event.getObject<ldmx::EcalVetoResult>(
204 ecal_veto_collection_, ecal_veto_pass_)};
205 if (ecal_veto.getDisc() < ecal_bdt_cut_val_ && all_cuts_) {
206 return;
207 }
208 histograms_.fill("pass_ecal_bdt", decay_z);
209
210 if (hcal_energy < 4840 && all_cuts_) {
211 return;
212 }
213 histograms_.fill("pass_hcal_energy", decay_z);
214
215 if (!hcal_containment && all_cuts_) {
216 return;
217 }
218 histograms_.fill("pass_hcal_containment", decay_z);
219
220 // initialize all of the features
221 int n_layers_hit = 0;
222 double x_std = 0.;
223 double y_std = 0.;
224 double z_std = 0.;
225 double x_mean = 0.;
226 double y_mean = 0.;
227 double r_mean = 0.;
228 int iso_hits = 0;
229 double iso_energy = 0.;
230 int n_readout_hits = 0;
231 double summed_det = 0.;
232 double r_mean_from_photon_track = 0.;
233
234 double z_mean = 0.; // need this when calculating z_std
235 std::vector<int> layers_hit;
236 for (const ldmx::HcalHit &hit : hcal_rec_hits) {
237 if (hit.getEnergy() > 0.) {
238 ldmx::HcalID det_id(hit.getID());
239 if (det_id.getSection() != 0) { // skip hits that aren't in main Hcal
240 continue;
241 }
242 if (fabs(hit.getXPos()) > 1000 || fabs(hit.getYPos()) > 1000) {
243 continue;
244 }
245 n_readout_hits += 1;
246 double hit_x = hit.getXPos();
247 double hit_y = hit.getYPos();
248 double hit_z = hit.getZPos();
249 double hit_r = sqrt(hit_x * hit_x + hit_y * hit_y);
250
251 summed_det += hit.getEnergy();
252
253 x_mean += hit_x * hit.getEnergy();
254 y_mean += hit_y * hit.getEnergy();
255 z_mean += hit_z * hit.getEnergy();
256 r_mean += hit_r * hit.getEnergy();
257
258 // check if this is a new layer in the collection
259 if (!(std::find(layers_hit.begin(), layers_hit.end(),
260 det_id.getLayerID()) != layers_hit.end())) {
261 layers_hit.push_back(det_id.getLayerID());
262 }
263
264 double proj_x =
265 gamma_x0[0] + (hit_z - gamma_x0[2]) * gamma_p[0] / gamma_p[2];
266 double proj_y =
267 gamma_x0[1] + (hit_z - gamma_x0[2]) * gamma_p[1] / gamma_p[2];
268
269 r_mean_from_photon_track +=
270 hit.getEnergy() * sqrt((hit_x - proj_x) * (hit_x - proj_x) +
271 (hit_y - proj_y) * (hit_y - proj_y));
272
273 // Calculate isolated hits
274 double closest_point = 9999.;
275 for (const ldmx::HcalHit &hit2 : hcal_rec_hits) {
276 if (hit2.getEnergy() > 0.) {
277 ldmx::HcalID det_i_d2(hit2.getID());
278 if (fabs(hit2.getXPos()) > 1000 || fabs(hit2.getYPos()) > 1000) {
279 continue;
280 }
281 if (det_i_d2.getLayerID() == det_id.getLayerID()) {
282 // Determine if a bar is vertical (along y-axis) or horizontal
283 // (along x-axis) Odd layers have horizontal strips Even layers have
284 // vertical strips
285 if (hit.isOrientationX()) {
286 if (fabs(hit2.getYPos() - hit_y) > 0) {
287 if (fabs(hit2.getYPos() - hit_y) < closest_point) {
288 closest_point = abs(hit2.getYPos() - hit_y);
289 }
290 }
291 }
292 if (hit.isOrientationY()) {
293 if (fabs(hit2.getXPos() - hit_x) > 0) {
294 if (fabs(hit2.getXPos() - hit_x) < closest_point) {
295 closest_point = fabs(hit2.getXPos() - hit_x);
296 }
297 }
298 }
299 }
300 }
301 }
302 if (closest_point > 50.) {
303 iso_hits += 1;
304 iso_energy += hit.getEnergy();
305 }
306 }
307 }
308
309 n_layers_hit = layers_hit.size();
310
311 if (summed_det > 0.) {
312 x_mean /= summed_det;
313 y_mean /= summed_det;
314 z_mean /= summed_det;
315 r_mean /= summed_det;
316
317 r_mean_from_photon_track /= summed_det;
318 }
319
320 for (const ldmx::HcalHit &hit : hcal_rec_hits) {
321 if (hit.getEnergy() > 0.) {
322 if (fabs(hit.getXPos()) > 1000 || fabs(hit.getYPos()) > 1000) {
323 continue;
324 }
325 ldmx::HcalID det_id(hit.getID());
326 if (det_id.getSection() == 0) {
327 x_std += hit.getEnergy() * (hit.getXPos() - x_mean) *
328 (hit.getXPos() - x_mean);
329 y_std += hit.getEnergy() * (hit.getYPos() - y_mean) *
330 (hit.getYPos() - y_mean);
331 z_std += hit.getEnergy() * (hit.getZPos() - z_mean) *
332 (hit.getZPos() - z_mean);
333 }
334 }
335 }
336
337 if (summed_det > 0.) {
338 x_std = sqrt(x_std / summed_det);
339 y_std = sqrt(y_std / summed_det);
340 z_std = sqrt(z_std / summed_det);
341 }
342
343 // Fill histograms
344 histograms_.fill("layers_hit", n_layers_hit);
345 histograms_.fill("x_std", x_std);
346 histograms_.fill("y_std", y_std);
347 histograms_.fill("z_std", z_std);
348 histograms_.fill("x_mean", x_mean);
349 histograms_.fill("y_mean", y_mean);
350 histograms_.fill("r_mean", r_mean);
351 histograms_.fill("iso_hits", iso_hits);
352 histograms_.fill("iso_energy", iso_energy);
353 histograms_.fill("n_hits", n_readout_hits);
354 histograms_.fill("total_energy", hcal_energy);
355 histograms_.fill("photon_track", r_mean_from_photon_track);
356
357 bdt_features.push_back(n_layers_hit);
358 bdt_features.push_back(x_std);
359 bdt_features.push_back(y_std);
360 bdt_features.push_back(z_std);
361 bdt_features.push_back(x_mean);
362 bdt_features.push_back(y_mean);
363 bdt_features.push_back(r_mean);
364 bdt_features.push_back(iso_hits);
365 bdt_features.push_back(iso_energy);
366 bdt_features.push_back(n_readout_hits);
367 bdt_features.push_back(hcal_energy);
368 bdt_features.push_back(r_mean_from_photon_track);
369
370 ldmx::ort::FloatArrays inputs({bdt_features});
371 float pred =
372 rt_->run({feature_list_name_}, inputs, {"probabilities"})[0].at(1);
373
374 histograms_.fill("visibles_disc", pred);
375
376 for (int i = 0; i < 10000; i++) {
377 double disc = 0.999 + (double(i) / 10000.) * (1. - 0.999);
378 histograms_.fill("visibles_disc_high_norm",
379 0.999 + ((double(i) + 0.5) / 10000.) * (1. - 0.999));
380 double disc_roc = 0.99 + (double(i) / 10000.) * (1. - 0.99);
381 if (pred >= disc) {
382 histograms_.fill("visibles_disc_high",
383 0.999 + ((double(i) + 0.5) / 10000.) * (1. - 0.999));
384 }
385 if (pred >= disc_roc) {
386 histograms_.fill("roc", disc_roc);
387 }
388 }
389
390 histograms_.fill("ecal_disc_vs_vis_disc", pred, ecal_veto.getDisc());
391
392 if (pred < bdt_cut_val_) {
393 return;
394 }
395 histograms_.fill("pass_visibles_bdt", decay_z);
396
397 return;
398}
399
400} // namespace dqm
401
Class that defines an ECal detector ID with a cell number.
Class used to encapsulate the results obtained from EcalVetoProcessor.
#define DECLARE_ANALYZER(CLASS)
Macro which allows the framework to construct an analyzer given its name during configuration.
Class that stores Stores reconstructed hit information from the HCAL.
Class that defines an HCal sensitive detector.
Class which stores simulated calorimeter hit information.
Class which encapsulates information from a hit in a simulated tracking detector.
void configure(framework::config::Parameters &parameters) 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.
HistogramPool histograms_
helper object for making and filling histograms
Implements an event buffer system for storing event data.
Definition Event.h:42
bool exists(const std::string &name, const std::string &passName, bool unique=true) const
Check for the existence of an object or collection with the given name and pass name in the event.
Definition Event.cxx:92
void fill(const std::string &name, const T &val)
Fill a 1D histogram.
Class encapsulating parameters for configuring a processor.
Definition Parameters.h:29
const T & get(const std::string &name) const
Retrieve the parameter of the given name.
Definition Parameters.h:78
Stores reconstructed hit information from the ECAL.
Definition EcalHit.h:19
Stores reconstructed hit information from the HCAL.
Definition HcalHit.h:24
Implements detector ids for HCal subdetector.
Definition HcalID.h:19
unsigned int getLayerID() const
Get the value of the layer field from the ID.
Definition HcalID.h:96
Class representing a simulated particle.
Definition SimParticle.h:23
Represents a simulated tracker hit in the simulation.
Implementation of a track object.
Definition Track.h:53