LDMX Software
ParticleFlowDQM.cxx
1#include "DQM/ParticleFlowDQM.h"
2
3#include <algorithm>
4#include <cmath>
5#include <map>
6#include <vector>
7
9#include "SimCore/Event/SimParticle.h"
11
12namespace dqm {
13
14namespace {
15
16// truth species, bins of the per species axes in dqm.py; charged up to kP
17enum Category { kE, kMu, kPi, kK, kP, kGamma, kNHad, kOther };
18
19// where a neutral hadron interacts, from its sim end point
20enum Region { kRegionEcal, kRegionSideHcal, kRegionBackHcal, kNoInteraction };
21
22// PF types as in CMS, bins of the PF type axes in dqm.py
23enum PFType { kPFE, kPFMu, kPFH, kPFGamma, kPFH0, kPFOther, kPFNotFound };
24
25int category(int pdg) {
26 switch (std::abs(pdg)) {
27 case 11:
28 return kE;
29 case 13:
30 return kMu;
31 case 211:
32 return kPi;
33 case 321:
34 return kK;
35 case 2212:
36 return kP;
37 case 22:
38 return kGamma;
39 case 2112:
40 case 130:
41 case 310:
42 case 3122:
43 return kNHad;
44 default:
45 return kOther;
46 }
47}
48
49// PF type of a candidate from its pdg
50int pfType(int pdg) {
51 switch (std::abs(pdg)) {
52 case 11:
53 return kPFE;
54 case 13:
55 return kPFMu;
56 case 211:
57 return kPFH;
58 case 22:
59 return kPFGamma;
60 case 130:
61 return kPFH0;
62 default:
63 return kPFOther;
64 }
65}
66
67// PF type a truth species should end up as
68int truthPFType(int pdg) {
69 switch (category(pdg)) {
70 case kE:
71 return kPFE;
72 case kMu:
73 return kPFMu;
74 case kPi:
75 case kK:
76 case kP:
77 return kPFH;
78 case kGamma:
79 return kPFGamma;
80 case kNHad:
81 return kPFH0;
82 default:
83 return kPFOther;
84 }
85}
86
87// neutral hadron rows of the neutral matrix: n by interaction region
88// (0 to 3, see Region), K0, other neutral hadrons
89int neutralRow(int pdg, int region) {
90 switch (std::abs(pdg)) {
91 case 2112:
92 return region;
93 case 130:
94 case 310:
95 return 4;
96 default:
97 return 5;
98 }
99}
100
101// charged hadron mass hypothesis column: pi, K, p
102int massColumn(float mass) {
103 if (mass > 700) return 2;
104 if (mass > 300) return 1;
105 return 0;
106}
107
108// a particle crossing the Ecal front face
109struct TruthParticle {
110 int track_id_;
111 int pdg_;
112 double visible_e_; // total for e and gamma, kinetic otherwise
113 double total_e_;
114 double x_, y_, z_, px_, py_, pz_;
115 bool matched_{false};
116};
117
118// truth position projected along the momentum to z
119std::pair<double, double> project(const TruthParticle& t, double z) {
120 if (t.pz_ <= 0) return {t.x_, t.y_};
121 return {t.x_ + t.px_ / t.pz_ * (z - t.z_), t.y_ + t.py_ / t.pz_ * (z - t.z_)};
122}
123
124// candidate direction from the target, unit vector
125std::vector<double> direction(const ldmx::PFCandidate& c) {
126 auto xyz = (c.getPID() & 2) ? c.getEcalClusterXYZ() : c.getHcalClusterXYZ();
127 double n = std::hypot(xyz[0], xyz[1], xyz[2]);
128 if (n <= 0) return {0, 0, 1};
129 return {xyz[0] / n, xyz[1] / n, xyz[2] / n};
130}
131
132} // namespace
133
134// reconstructable truth: angle to the beam axis and per species momentum or
135// kinetic energy thresholds; e and gamma need only the angle
136bool ParticleFlowDQM::reconstructable(int pdg, double px, double py, double pz,
137 double ke) const {
138 double p = std::hypot(px, py, pz);
139 if (p <= 0 || std::acos(pz / p) > reco_max_theta_ * M_PI / 180.) return false;
140 switch (category(pdg)) {
141 case kPi:
142 return p > reco_min_p_pion_;
143 case kK:
144 case kP:
145 return p > reco_min_p_heavy_;
146 case kNHad:
147 return ke > reco_min_ke_neutral_;
148 default:
149 return true;
150 }
151}
152
153// interaction region from the sim end point: Ecal box, side Hcal around
154// it, back Hcal behind it, or no interaction (left the detector)
155int ParticleFlowDQM::interactionRegion(const std::vector<double>& end) const {
156 double x = std::abs(end[0]), y = std::abs(end[1]), z = end[2];
157 if (x > hcal_max_xy_ || y > hcal_max_xy_ || z > hcal_max_z_ || z < 0)
158 return kNoInteraction;
159 if (z < ecal_back_z_) {
160 return (x < ecal_half_xy_ && y < ecal_half_xy_) ? kRegionEcal
161 : kRegionSideHcal;
162 }
163 return kRegionBackHcal;
164}
165
167 pf_cand_coll_name_ = ps.get<std::string>("pf_cand_coll_name");
168 pf_cand_pass_name_ = ps.get<std::string>("pf_cand_pass_name");
169 ecal_sp_coll_name_ = ps.get<std::string>("ecal_sp_coll_name");
170 ecal_sp_pass_name_ = ps.get<std::string>("ecal_sp_pass_name");
171 sim_particles_coll_name_ = ps.get<std::string>("sim_particles_coll_name");
172 sim_particles_pass_name_ = ps.get<std::string>("sim_particles_pass_name");
173 truth_min_energy_ = ps.get<double>("truth_min_energy");
174 neutral_match_dist_ = ps.get<double>("neutral_match_dist");
175 neutral_match_max_ratio_ = ps.get<double>("neutral_match_max_ratio");
176 neutral_hcal_match_angle_ = ps.get<double>("neutral_hcal_match_angle");
177 reco_max_theta_ = ps.get<double>("reco_max_theta");
178 reco_min_p_pion_ = ps.get<double>("reco_min_p_pion");
179 reco_min_p_heavy_ = ps.get<double>("reco_min_p_heavy");
180 reco_min_ke_neutral_ = ps.get<double>("reco_min_ke_neutral");
181 reco_min_ke_pi0_ = ps.get<double>("reco_min_ke_pi0");
182 ecal_back_z_ = ps.get<double>("ecal_back_z");
183 ecal_half_xy_ = ps.get<double>("ecal_half_xy");
184 hcal_max_z_ = ps.get<double>("hcal_max_z");
185 hcal_max_xy_ = ps.get<double>("hcal_max_xy");
186}
187
189 if (!event.exists(pf_cand_coll_name_, pf_cand_pass_name_)) {
190 ldmx_log(debug) << "No collection " << pf_cand_coll_name_;
191 return;
192 }
193 auto cands = event.getCollection<ldmx::PFCandidate>(pf_cand_coll_name_,
194 pf_cand_pass_name_);
195 histograms_.fill("n_cands", cands.size());
196
197 int n_charged = 0, n_neutral = 0;
198 double charged_p = 0, neutral_e = 0, pf_e = 0;
199 for (auto& cand : cands) {
200 int pid = cand.getPID();
201 histograms_.fill("pid", pid);
202 histograms_.fill("reco_type", pfType(cand.getPdgId()));
203 if (pfType(cand.getPdgId()) == kPFH) {
204 histograms_.fill("charged_hadron_mass", massColumn(cand.getMass()));
205 }
206 pf_e += cand.getEnergy();
207 double ecal_e = cand.getEcalEnergy();
208 double hcal_e = cand.getHcalEnergy();
209
210 if (pid & 1) {
211 n_charged++;
212 auto p = cand.getTrackPxPyPz();
213 double trk_p = std::hypot(p[0], p[1], p[2]);
214 charged_p += trk_p;
215 histograms_.fill("charged_track_p", trk_p);
216 histograms_.fill("charged_is_ecal_linked", (pid & 2) ? 1 : 0);
217 if (cand.getDeDxNhits() > 0) {
218 histograms_.fill("charged_dedx_mass2_vs_p", trk_p,
219 cand.getDeDxMassSquared());
220 }
221 if ((pid & 2) && trk_p > 0) {
222 histograms_.fill("charged_ecal_e_over_p", ecal_e / trk_p);
223 histograms_.fill("charged_ecal_raw_e_over_p",
224 cand.getEcalRawEnergy() / trk_p);
225 // track at Ecal face vs linked cluster centroid
226 auto trk_pos = cand.getEcalPositionXYZ();
227 auto cl_pos = cand.getEcalClusterXYZ();
228 histograms_.fill("charged_cluster_dx", cl_pos[0] - trk_pos[0]);
229 histograms_.fill("charged_cluster_dy", cl_pos[1] - trk_pos[1]);
230 }
231 if (cand.getTruthPdgId() != 0) {
232 histograms_.fill("charged_truth_pdg", cand.getTruthPdgId());
233 auto tp = cand.getTruthPxPyPz();
234 double true_p = std::hypot(tp[0], tp[1], tp[2]);
235 if (true_p > 0) {
236 histograms_.fill("charged_dp_over_p", (trk_p - true_p) / true_p);
237 histograms_.fill("charged_p_vs_truth_p", true_p, trk_p);
238 }
239 auto t_pos = cand.getTruthEcalXYZ();
240 auto r_pos = cand.getEcalPositionXYZ();
241 histograms_.fill("charged_dx_truth", r_pos[0] - t_pos[0]);
242 histograms_.fill("charged_dy_truth", r_pos[1] - t_pos[1]);
243 int t_cat = category(cand.getTruthPdgId());
244 if ((pid & 2) && trk_p > 0) {
245 histograms_.fill("charged_e_over_p_vs_truth", t_cat, ecal_e / trk_p);
246 }
247 if (cand.getDeDxNhits() > 0) {
248 histograms_.fill("charged_dedx_mass2_vs_truth", t_cat,
249 cand.getDeDxMassSquared());
250 }
251 }
252 } else {
253 n_neutral++;
254 neutral_e += cand.getEnergy();
255 if (pid == 2) histograms_.fill("neutral_ecal_only_e", ecal_e);
256 if (pid == 6) {
257 histograms_.fill("neutral_ecal_hcal_ecal_e", ecal_e);
258 histograms_.fill("neutral_ecal_hcal_hcal_e", hcal_e);
259 }
260 if (pid == 4) histograms_.fill("neutral_hcal_only_e", hcal_e);
261 }
262 }
263 histograms_.fill("n_charged", n_charged);
264 histograms_.fill("n_neutral", n_neutral);
265 histograms_.fill("sum_charged_p", charged_p);
266 histograms_.fill("sum_neutral_e", neutral_e);
267 histograms_.fill("sum_pf_e", pf_e);
268
269 // reco diphoton mass, directions from the target
270 std::vector<int> photons;
271 for (int i = 0; i < static_cast<int>(cands.size()); i++) {
272 if (cands[i].getPdgId() == 22) photons.push_back(i);
273 }
274 for (size_t a = 0; a < photons.size(); a++) {
275 for (size_t b = a + 1; b < photons.size(); b++) {
276 const auto& c1 = cands[photons[a]];
277 const auto& c2 = cands[photons[b]];
278 auto d1 = direction(c1);
279 auto d2 = direction(c2);
280 double cos12 = d1[0] * d2[0] + d1[1] * d2[1] + d1[2] * d2[2];
281 double m2 = 2 * c1.getEnergy() * c2.getEnergy() * (1 - cos12);
282 histograms_.fill("mgg", std::sqrt(std::max(0., m2)));
283 }
284 }
285
286 analyzeTruth(event, cands);
287}
288
289void ParticleFlowDQM::analyzeTruth(const framework::Event& event,
290 std::vector<ldmx::PFCandidate>& cands) {
291 if (!event.exists(ecal_sp_coll_name_, ecal_sp_pass_name_)) return;
292 const auto& sp_hits = event.getCollection<ldmx::SimTrackerHit>(
293 ecal_sp_coll_name_, ecal_sp_pass_name_);
294
295 // particles entering the Ecal front face, highest energy per track
296 std::map<int, TruthParticle> by_id;
297 for (const auto& sp : sp_hits) {
298 auto pos = sp.getPosition();
299 auto mom = sp.getMomentum();
300 if (std::abs(240 - pos[2]) > 0.1 || mom[2] <= 0) continue;
301 double e = sp.getEnergy();
302 int apdg = std::abs(sp.getPdgID());
303 double vis = (apdg == 11 || apdg == 22)
304 ? e
305 : e - std::sqrt(std::max(0., e * e - mom[0] * mom[0] -
306 mom[1] * mom[1] -
307 mom[2] * mom[2]));
308 if (vis < truth_min_energy_) continue;
309 auto it = by_id.find(sp.getTrackID());
310 if (it != by_id.end() && it->second.visible_e_ >= vis) continue;
311 by_id[sp.getTrackID()] = {sp.getTrackID(), sp.getPdgID(), vis, e,
312 pos[0], pos[1], pos[2], mom[0],
313 mom[1], mom[2]};
314 }
315 std::vector<TruthParticle> truth;
316 double truth_e = 0;
317 // same convention as PF: total for charged, visible for neutral
318 for (auto& [id, t] : by_id) {
319 truth.push_back(t);
320 int cat = category(t.pdg_);
321 truth_e += (cat <= kP) ? t.total_e_ : t.visible_e_;
322 }
323 histograms_.fill("sum_truth_e", truth_e);
324 double pf_e = 0;
325 for (const auto& c : cands) pf_e += c.getEnergy();
326 if (truth_e > 0) histograms_.fill("sum_pf_e_over_truth", pf_e / truth_e);
327
328 // charged candidates match by track ID
329 std::vector<int> cand_truth(cands.size(), -1);
330 for (size_t i = 0; i < cands.size(); i++) {
331 if (!(cands[i].getPID() & 1)) continue;
332 for (size_t j = 0; j < truth.size(); j++) {
333 if (!truth[j].matched_ &&
334 truth[j].track_id_ == cands[i].getTruthTrackId()) {
335 truth[j].matched_ = true;
336 cand_truth[i] = j;
337 break;
338 }
339 }
340 }
341
342 // neutral candidates match by position, highest energy first
343 std::vector<size_t> neutral;
344 for (size_t i = 0; i < cands.size(); i++) {
345 if (!(cands[i].getPID() & 1)) neutral.push_back(i);
346 }
347 std::sort(neutral.begin(), neutral.end(), [&cands](size_t a, size_t b) {
348 return cands[a].getEnergy() > cands[b].getEnergy();
349 });
350 for (size_t i : neutral) {
351 bool has_ecal = cands[i].getPID() & 2;
352 auto xyz =
353 has_ecal ? cands[i].getEcalClusterXYZ() : cands[i].getHcalClusterXYZ();
354 // Ecal: distance at the cluster [mm]; Hcal only: angle from the target
355 // [rad], hadron showers are wide and the side Hcal is not along z
356 auto cand_dir = direction(cands[i]);
357 int best = -1;
358 double best_d = has_ecal ? neutral_match_dist_
359 : neutral_hcal_match_angle_ * M_PI / 180.;
360 for (size_t j = 0; j < truth.size(); j++) {
361 if (truth[j].matched_) continue;
362 // fragments must not count as found particles
363 double ratio = cands[i].getEnergy() / truth[j].visible_e_;
364 if (ratio < 1. / neutral_match_max_ratio_ ||
365 ratio > neutral_match_max_ratio_)
366 continue;
367 double d;
368 if (has_ecal) {
369 auto [tx, ty] = project(truth[j], xyz[2]);
370 d = std::hypot(tx - xyz[0], ty - xyz[1]);
371 } else {
372 const auto& t = truth[j];
373 double pt = std::hypot(t.px_, t.py_, t.pz_);
374 double c =
375 (t.px_ * cand_dir[0] + t.py_ * cand_dir[1] + t.pz_ * cand_dir[2]) /
376 pt;
377 d = std::acos(std::clamp(c, -1., 1.));
378 }
379 if (d < best_d) {
380 best_d = d;
381 best = j;
382 }
383 }
384 if (best >= 0) {
385 truth[best].matched_ = true;
386 cand_truth[i] = best;
387 }
388 }
389
390 // unmatched candidates and energy response
391 std::vector<int> truth_cand(truth.size(), -1);
392 for (size_t i = 0; i < cands.size(); i++) {
393 int r_type = pfType(cands[i].getPdgId());
394 if (cand_truth[i] < 0) {
395 // last truth bin: candidates without a truth particle
396 histograms_.fill("pf_confusion", kPFNotFound, r_type);
397 if (r_type == kPFH) {
398 histograms_.fill("charged_hadron_confusion", 3,
399 massColumn(cands[i].getMass()));
400 }
401 if (!(cands[i].getPID() & 1)) {
402 histograms_.fill("unmatched_neutral_e", cands[i].getEnergy());
403 }
404 continue;
405 }
406 truth_cand[cand_truth[i]] = i;
407 const auto& t = truth[cand_truth[i]];
408 // charged energy includes the mass, calorimeters see the visible part
409 double ref = (cands[i].getPID() & 1) ? t.total_e_ : t.visible_e_;
410 if (ref > 0) {
411 histograms_.fill("response_vs_type", r_type, cands[i].getEnergy() / ref);
412 }
413 }
414
415 // sim particles give where neutral hadrons interact, and pi0 parents
416 bool has_particles =
417 event.exists(sim_particles_coll_name_, sim_particles_pass_name_);
418 std::map<int, ldmx::SimParticle> particles;
419 if (has_particles) {
420 particles = event.getMap<int, ldmx::SimParticle>(sim_particles_coll_name_,
421 sim_particles_pass_name_);
422 }
423
424 // unmatched truth with any candidate at its position: seen, but with the
425 // wrong energy or merged into another particle ("other"), not lost
426 std::vector<bool> seen(truth.size(), false);
427 for (size_t j = 0; j < truth.size(); j++) {
428 if (truth_cand[j] >= 0) continue;
429 for (const auto& cand : cands) {
430 int pid = cand.getPID();
431 if (pid & 4 && !(pid & 2)) {
432 auto d = direction(cand);
433 const auto& t = truth[j];
434 double pt = std::hypot(t.px_, t.py_, t.pz_);
435 double cs = (t.px_ * d[0] + t.py_ * d[1] + t.pz_ * d[2]) / pt;
436 if (cs > std::cos(neutral_hcal_match_angle_ * M_PI / 180.))
437 seen[j] = true;
438 } else {
439 auto xyz =
440 (pid & 2) ? cand.getEcalClusterXYZ() : cand.getEcalPositionXYZ();
441 auto [tx, ty] = project(truth[j], xyz[2]);
442 if (std::hypot(tx - xyz[0], ty - xyz[1]) < neutral_match_dist_)
443 seen[j] = true;
444 }
445 if (seen[j]) break;
446 }
447 }
448
449 // confusion per truth particle; last two reco bins: other (seen, wrong
450 // energy or merged) and not found (nothing at its position)
451 for (size_t j = 0; j < truth.size(); j++) {
452 const auto& t = truth[j];
453 int c = truth_cand[j];
454 int r_type = c >= 0 ? pfType(cands[c].getPdgId())
455 : (seen[j] ? kPFOther : kPFNotFound);
456 bool reco = reconstructable(t.pdg_, t.px_, t.py_, t.pz_, t.visible_e_);
457 histograms_.fill("pf_confusion", truthPFType(t.pdg_), r_type);
458 if (reco) {
459 histograms_.fill("pf_confusion_reconstructable", truthPFType(t.pdg_),
460 r_type);
461 }
462 int sp = category(t.pdg_);
463 if (sp == kPi || sp == kK || sp == kP) {
464 // columns: h with pi, K, p hypothesis, other, not found
465 int col = r_type == kPFH ? massColumn(cands[c].getMass())
466 : r_type == kPFNotFound ? 4
467 : 3;
468 histograms_.fill("charged_hadron_confusion", sp - kPi, col);
469 if (reco) {
470 histograms_.fill("charged_hadron_confusion_reconstructable", sp - kPi,
471 col);
472 }
473 }
474 if (sp == kNHad) {
475 // columns: h0, gamma, both gammas of a pi0, other, not found
476 int col = r_type == kPFNotFound ? 4 : 3;
477 if (r_type == kPFH0) col = 0;
478 if (r_type == kPFGamma) col = 1;
479 int region = kNoInteraction;
480 if (auto it = particles.find(t.track_id_); it != particles.end()) {
481 region = interactionRegion(it->second.getEndPoint());
482 }
483 int row = neutralRow(t.pdg_, region);
484 histograms_.fill("neutral_confusion", row, col);
485 if (reco) {
486 histograms_.fill("neutral_confusion_reconstructable", row, col);
487 }
488 }
489 }
490 for (const auto& t : truth) {
491 histograms_.fill("truth_type", category(t.pdg_));
492 if (t.matched_) histograms_.fill("truth_type_matched", category(t.pdg_));
493 if (t.pdg_ == 22) {
494 histograms_.fill("truth_gamma_e", t.visible_e_);
495 if (t.matched_) histograms_.fill("truth_gamma_e_matched", t.visible_e_);
496 }
497 }
498
499 // diphoton mass of photon pairs from the same pi0
500 if (!has_particles) return;
501 auto pi0_parent = [&particles](int track_id) {
502 auto it = particles.find(track_id);
503 if (it == particles.end() || it->second.getParents().empty()) return -1;
504 int parent = it->second.getParents()[0];
505 auto pit = particles.find(parent);
506 if (pit == particles.end() || pit->second.getPdgID() != 111) return -1;
507 return parent;
508 };
509 for (size_t a = 0; a < cands.size(); a++) {
510 if (cands[a].getPdgId() != 22 || cand_truth[a] < 0) continue;
511 int pa = pi0_parent(truth[cand_truth[a]].track_id_);
512 if (pa < 0) continue;
513 for (size_t b = a + 1; b < cands.size(); b++) {
514 if (cands[b].getPdgId() != 22 || cand_truth[b] < 0) continue;
515 if (pi0_parent(truth[cand_truth[b]].track_id_) != pa) continue;
516 auto d1 = direction(cands[a]);
517 auto d2 = direction(cands[b]);
518 double cos12 = d1[0] * d2[0] + d1[1] * d2[1] + d1[2] * d2[2];
519 double m2 = 2 * cands[a].getEnergy() * cands[b].getEnergy() * (1 - cos12);
520 histograms_.fill("mgg_pi0_matched", std::sqrt(std::max(0., m2)));
521 }
522 }
523 // truth pi0 photons entering the Ecal, for the pair efficiency
524 std::map<int, std::vector<int>> pi0_photons;
525 for (size_t j = 0; j < truth.size(); j++) {
526 if (truth[j].pdg_ != 22) continue;
527 int pa = pi0_parent(truth[j].track_id_);
528 if (pa >= 0) pi0_photons[pa].push_back(j);
529 }
530 for (const auto& [pa, photons] : pi0_photons) {
531 histograms_.fill("truth_pi0_photons_in_ecal", photons.size());
532 if (photons.size() < 2) continue;
533 // pi0 row of the neutral matrix, both photons at the Ecal face
534 int n_gamma = 0, n_h0 = 0, n_seen = 0;
535 for (int j : photons) {
536 int c = truth_cand[j];
537 n_seen += c >= 0 || seen[j];
538 if (c < 0) continue;
539 int r_type = pfType(cands[c].getPdgId());
540 n_gamma += r_type == kPFGamma;
541 n_h0 += r_type == kPFH0;
542 }
543 int col = n_seen > 0 ? 3 : 4;
544 if (n_gamma >= 2)
545 col = 2;
546 else if (n_h0 > 0)
547 col = 0;
548 else if (n_gamma == 1)
549 col = 1;
550 histograms_.fill("neutral_confusion", 6, col);
551 const auto& pi0 = particles.at(pa);
552 double pi0_ke = pi0.getEnergy() - pi0.getMass();
553 double pi0_theta = std::acos(
554 pi0.getMomentum()[2] /
555 std::max(1e-9, std::hypot(pi0.getMomentum()[0], pi0.getMomentum()[1],
556 pi0.getMomentum()[2])));
557 if (pi0_ke > reco_min_ke_pi0_ &&
558 pi0_theta < reco_max_theta_ * M_PI / 180.) {
559 histograms_.fill("neutral_confusion_reconstructable", 6, col);
560 }
561 }
562}
563
564} // namespace dqm
565
#define DECLARE_ANALYZER(CLASS)
Macro which allows the framework to construct an analyzer given its name during configuration.
Class that represents a reconstructed particle candidate.
Class which encapsulates information from a hit in a simulated tracking detector.
Monitor PF candidates, their ID, and the match to truth.
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.
HistogramPool histograms_
helper object for making and filling histograms
Implements an event buffer system for storing event data.
Definition Event.h:40
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:107
void fill(const std::string &name, const T &val)
Fill a 1D histogram.
Class encapsulating parameters for configuring a processor.
Definition Parameters.h:26
const T & get(const std::string &name) const
Retrieve the parameter of the given name.
Definition Parameters.h:75
Represents a reconstructed particle.
Definition PFCandidate.h:21
Class representing a simulated particle.
Definition SimParticle.h:25
Represents a simulated tracker hit in the simulation.