12#include "Framework/Exception/Exception.h"
19 input_ecal_coll_name_ = ps.
get<std::string>(
"input_ecal_coll_name");
20 input_hcal_coll_name_ = ps.
get<std::string>(
"input_hcal_coll_name");
21 input_track_coll_name_ = ps.
get<std::string>(
"input_track_coll_name");
22 output_coll_name_ = ps.
get<std::string>(
"output_coll_name");
23 input_ecal_passname_ = ps.
get<std::string>(
"input_ecal_passname");
24 input_hcal_passname_ = ps.
get<std::string>(
"input_hcal_passname");
25 input_tracks_passname_ = ps.
get<std::string>(
"input_tracks_passname");
26 truth_sp_coll_name_ = ps.
get<std::string>(
"truth_sp_coll_name");
27 truth_sp_passname_ = ps.
get<std::string>(
"truth_sp_passname");
30 single_particle_ = ps.
get<
bool>(
"single_particle");
31 use_existing_ecal_clusters_ = ps.
get<
bool>(
"use_existing_ecal_clusters");
32 input_mass_coll_name_ = ps.
get<std::string>(
"input_mass_coll_name");
33 input_mass_passname_ = ps.
get<std::string>(
"input_mass_passname");
34 link_max_dist_ = ps.
get<
double>(
"link_max_dist");
35 electron_ep_min_ = ps.
get<
double>(
"electron_ep_min");
36 link_min_rms_ = ps.
get<
double>(
"link_min_rms");
37 excess_ep_ = ps.
get<
double>(
"excess_ep");
38 proton_min_mass2_ = ps.
get<
double>(
"proton_min_mass2");
39 kaon_min_mass2_ = ps.
get<
double>(
"kaon_min_mass2");
40 proton_max_p_ = ps.
get<
double>(
"proton_max_p");
41 kaon_max_p_ = ps.
get<
double>(
"kaon_max_p");
42 recovery_radius_ = ps.
get<
double>(
"recovery_radius");
43 hcal_merge_angle_ = ps.
get<
double>(
"hcal_merge_angle");
44 hcal_track_angle_ = ps.
get<
double>(
"hcal_track_angle");
45 hadron_ecal_radius_ = ps.
get<
double>(
"hadron_ecal_radius");
46 hadron_absorb_ep_ = ps.
get<
double>(
"hadron_absorb_ep");
47 min_neutral_e_ = ps.
get<
double>(
"min_neutral_e");
48 ecal_front_z_ = ps.
get<
double>(
"ecal_front_z");
49 electron_max_depth_ = ps.
get<
double>(
"electron_max_depth");
50 electron_depth_slope_ = ps.
get<
double>(
"electron_depth_slope");
51 recovery_stop_ep_ = ps.
get<
double>(
"recovery_stop_ep");
52 ecal_link_radius_ = ps.
get<
double>(
"ecal_link_radius");
53 ecal_link_ratio_ = ps.
get<
double>(
"ecal_link_ratio");
54 ecal_link_min_layers_ = ps.
get<
int>(
"ecal_link_min_layers");
55 photon_keep_min_e_ = ps.
get<
double>(
"photon_keep_min_e");
56 photon_keep_min_dist_ = ps.
get<
double>(
"photon_keep_min_dist");
57 photon_keep_max_zstart_ = ps.
get<
double>(
"photon_keep_max_zstart");
58 dedx_min_hits_ = ps.
get<
int>(
"dedx_min_hits");
61 ecal_corr_energies_ = ps.
get<std::vector<double>>(
"ecal_corr_energies", {});
62 ecal_corr_factors_ = ps.
get<std::vector<double>>(
"ecal_corr_factors", {});
63 hcal_corr_energies_ = ps.
get<std::vector<double>>(
"hcal_corr_energies", {});
64 hcal_corr_factors_ = ps.
get<std::vector<double>>(
"hcal_corr_factors", {});
65 for (
const auto& [x, y] :
66 {std::pair{&ecal_corr_energies_, &ecal_corr_factors_},
67 std::pair{&hcal_corr_energies_, &hcal_corr_factors_}}) {
68 if (x->size() != y->size() || !std::is_sorted(x->begin(), x->end())) {
69 EXCEPTION_RAISE(
"InvalidArg",
70 "Correction energies must be sorted and match factors");
76double ParticleFlow::correction(
const std::vector<double>& energies,
77 const std::vector<double>& factors,
79 if (energies.empty())
return 1.;
80 if (energy <= energies.front())
return factors.front();
81 if (energy >= energies.back())
return factors.back();
82 auto hi = std::upper_bound(energies.begin(), energies.end(), energy);
83 size_t i = hi - energies.begin();
84 double t = (energy - energies[i - 1]) / (energies[i] - energies[i - 1]);
85 return factors[i - 1] + t * (factors[i] - factors[i - 1]);
89void ParticleFlow::fillCandTrack(
91 const std::vector<ldmx::SimTrackerHit>& sp_hits) {
92 auto ts = tk.getTrackState(ldmx::AtECAL);
93 if (!ts || ts->pos_.size() < 3 || ts->mom_.size() < 3)
return;
94 const std::vector<double>& xyz = ts->pos_;
95 const std::vector<double>& pxyz = ts->mom_;
98 xyz[0] + pxyz[0] / pxyz[2] * (ecal_z - xyz[2]);
99 float ecal_y = xyz[1] + pxyz[1] / pxyz[2] * (ecal_z - xyz[2]);
100 cand.setEcalPositionXYZ(ecal_x, ecal_y, ecal_z);
101 cand.setTrackPxPyPz(pxyz[0], pxyz[1], pxyz[2]);
102 cand.setPID(cand.getPID() | 1);
106 double best_dp = 9e9;
107 for (
const auto& sp : sp_hits) {
108 if (sp.getTrackID() != tk.getTrackID())
continue;
109 if (fabs(240 - sp.getPosition()[2]) > 0.1)
continue;
110 auto sp_p = sp.getMomentum();
112 std::hypot(sp_p[0] - pxyz[0], sp_p[1] - pxyz[1], sp_p[2] - pxyz[2]);
121 cand.setTruthEcalXYZ(t_xyz[0] + t_pxyz[0] / t_pxyz[2] * (ecal_z - t_xyz[2]),
122 t_xyz[1] + t_pxyz[1] / t_pxyz[2] * (ecal_z - t_xyz[2]),
124 cand.setTruthPxPyPz(t_pxyz[0], t_pxyz[1], t_pxyz[2]);
125 float m2 = pow(best->
getEnergy(), 2) - pow(t_pxyz[0], 2) - pow(t_pxyz[1], 2) -
128 cand.setTruthMass(sqrt(m2));
130 cand.setTruthPdgId(best->
getPdgID());
136 float energy = em.getEnergy();
137 float corr = correction(ecal_corr_energies_, ecal_corr_factors_, energy);
138 cand.setEcalEnergy(energy * corr);
139 cand.setEcalRawEnergy(energy);
143 cand.setEcalClusterDXDZ(em.
getDXDZ());
144 cand.setEcalClusterDYDZ(em.
getDYDZ());
145 cand.setEcalClusterEDXDZ(em.
getEDXDZ());
146 cand.setEcalClusterEDYDZ(em.
getEDYDZ());
147 cand.setEcalDepth(ecalDepth(em));
148 cand.clearEcalHits();
149 cand.addEcalHits(em.getHitIDs(), em.getHitE());
150 cand.setPID(cand.getPID() | 2);
155 float energy = had.getEnergy();
156 float corr = correction(hcal_corr_energies_, hcal_corr_factors_, energy);
157 cand.setHcalEnergy(energy * corr);
158 cand.setHcalRawEnergy(energy);
159 cand.clearHcalHits();
160 cand.addHcalHits(had.getHitIDs(), had.getHitE());
164 cand.setHcalClusterDXDZ(had.
getDXDZ());
165 cand.setHcalClusterDYDZ(had.
getDYDZ());
166 cand.setHcalClusterEDXDZ(had.
getEDXDZ());
167 cand.setHcalClusterEDYDZ(had.
getEDYDZ());
168 cand.setPID(cand.getPID() | 4);
172double ParticleFlow::trackClusterDist(
const std::vector<double>& xyz,
173 const std::vector<double>& pxyz,
176 double x = xyz[0] + pxyz[0] / pxyz[2] * (z - xyz[2]);
177 double y = xyz[1] + pxyz[1] / pxyz[2] * (z - xyz[2]);
188std::vector<ldmx::CaloCluster> ParticleFlow::linkEcalClusters(
189 const std::vector<ldmx::CaloCluster>& clusters,
190 const std::vector<ldmx::Track>& tracks)
const {
191 int n = clusters.size();
192 std::vector<int> nlay(n), parent(n, -1);
193 std::vector<double> zstart(n);
194 std::vector<bool> tk_linked(n,
false);
195 for (
int i = 0; i < n; i++) {
196 std::set<int> layers;
198 for (
float z : clusters[i].getHitZ()) {
199 layers.insert(std::lround(z));
200 zmin = std::min<double>(zmin, z);
202 nlay[i] = layers.size();
203 zstart[i] = layers.empty() ? clusters[i].getCentroidZ() : zmin;
204 for (
const auto& tk : tracks) {
205 auto ts = tk.getTrackState(ldmx::AtECAL);
206 if (!ts || ts->mom_.size() < 3 || ts->mom_[2] <= 0)
continue;
207 if (trackClusterDist(ts->pos_, ts->mom_, clusters[i]) < link_max_dist_) {
213 for (
int s = 0; s < n; s++) {
214 if (tk_linked[s])
continue;
215 double es = clusters[s].getEnergy();
217 double best_d = ecal_link_radius_;
218 double nearest = 9e9;
219 for (
int p = 0; p < n; p++) {
220 if (p == s || clusters[p].getEnergy() <= es)
continue;
222 std::hypot(clusters[p].getCentroidX() - clusters[s].getCentroidX(),
223 clusters[p].getCentroidY() - clusters[s].getCentroidY());
224 nearest = std::min(nearest, d);
225 bool satellite = nlay[s] < ecal_link_min_layers_ ||
226 es < ecal_link_ratio_ * clusters[p].getEnergy();
227 if (nlay[p] >= ecal_link_min_layers_ && satellite && d < best_d) {
233 bool photon_like = es >= photon_keep_min_e_ &&
234 nearest > photon_keep_min_dist_ &&
235 zstart[s] < photon_keep_max_zstart_;
236 if (best >= 0 && !photon_like) parent[s] = best;
240 auto root = [&parent](
int i) {
241 while (parent[i] >= 0) i = parent[i];
244 std::map<int, std::vector<int>> groups;
245 for (
int i = 0; i < n; i++) groups[root(i)].push_back(i);
246 std::vector<ldmx::CaloCluster> merged;
247 for (
const auto& [r, members] : groups) {
248 merged.push_back(mergeClusters(clusters, r, members));
250 std::sort(merged.begin(), merged.end(), [](
const auto& a,
const auto& b) {
251 return a.getEnergy() > b.getEnergy();
259 const std::vector<ldmx::CaloCluster>& clusters,
int root,
260 const std::vector<int>& members) {
261 if (members.size() == 1)
return clusters[root];
263 double e = 0, x = 0, y = 0, z = 0;
264 std::vector<unsigned int> ids;
265 std::vector<float> hx, hy, hz, he;
266 for (
int m : members) {
267 const auto& c = clusters[m];
269 x += c.getEnergy() * c.getCentroidX();
270 y += c.getEnergy() * c.getCentroidY();
271 z += c.getEnergy() * c.getCentroidZ();
272 ids.insert(ids.end(), c.getHitIDs().begin(), c.getHitIDs().end());
273 hx.insert(hx.end(), c.getHitX().begin(), c.getHitX().end());
274 hy.insert(hy.end(), c.getHitY().begin(), c.getHitY().end());
275 hz.insert(hz.end(), c.getHitZ().begin(), c.getHitZ().end());
276 he.insert(he.end(), c.getHitE().begin(), c.getHitE().end());
291std::vector<ldmx::CaloCluster> ParticleFlow::linkHcalClusters(
292 const std::vector<ldmx::CaloCluster>& clusters)
const {
293 int n = clusters.size();
294 std::vector<int> order(n);
295 for (
int i = 0; i < n; i++) order[i] = i;
296 std::sort(order.begin(), order.end(), [&clusters](
int a,
int b) {
297 return clusters[a].getEnergy() > clusters[b].getEnergy();
299 auto dir = [&clusters](
int i) {
300 const auto& c = clusters[i];
301 double r = std::hypot(c.getCentroidX(), c.getCentroidY(), c.getCentroidZ());
302 return std::array<double, 3>{c.getCentroidX() / r, c.getCentroidY() / r,
303 c.getCentroidZ() / r};
305 double cos_max = std::cos(hcal_merge_angle_ * M_PI / 180.);
306 std::vector<bool> used(n,
false);
307 std::vector<ldmx::CaloCluster> merged;
308 for (
int s : order) {
309 if (used[s])
continue;
311 std::vector<int> members;
312 for (
int o : order) {
313 if (used[o])
continue;
315 if (ds[0] * d[0] + ds[1] * d[1] + ds[2] * d[2] >= cos_max) {
317 members.push_back(o);
320 merged.push_back(mergeClusters(clusters, s, members));
327int ParticleFlow::linkTrackHcal(
const ldmx::Track& tk,
328 const std::vector<ldmx::CaloCluster>& clusters,
329 const std::vector<bool>& used)
const {
330 auto ts = tk.getTrackState(ldmx::AtECAL);
331 if (hcal_track_angle_ <= 0 || !ts || ts->mom_.size() < 3)
return -1;
332 const auto& pos = ts->pos_;
333 const auto& mom = ts->mom_;
334 double pm = std::hypot(mom[0], mom[1], mom[2]);
336 double best_cos = std::cos(hcal_track_angle_ * M_PI / 180.);
337 for (
int j = 0; j < static_cast<int>(clusters.size()); j++) {
338 if (used[j])
continue;
339 double dx = clusters[j].getCentroidX() - pos[0];
340 double dy = clusters[j].getCentroidY() - pos[1];
341 double dz = clusters[j].getCentroidZ() - pos[2];
342 double c = (dx * mom[0] + dy * mom[1] + dz * mom[2]) /
343 (pm * std::hypot(dx, dy, dz));
356void ParticleFlow::absorbHadronShower(
358 const std::vector<ldmx::CaloCluster>& ecal, std::vector<bool>& ecal_used,
359 const std::vector<ldmx::CaloCluster>& hcal,
360 std::vector<bool>& hcal_used)
const {
361 auto ts = tk.getTrackState(ldmx::AtECAL);
362 if (hadron_absorb_ep_ <= 0 || !ts || ts->mom_.size() < 3 || ts->mom_[2] <= 0)
364 const auto& pos = ts->pos_;
365 const auto& mom = ts->mom_;
366 double pm = std::hypot(mom[0], mom[1], mom[2]);
367 double budget = hadron_absorb_ep_ * pm;
368 double calo = cand.getEcalEnergy() + cand.getHcalEnergy();
371 std::vector<std::tuple<double, bool, int>> near;
372 for (
int j = 0; j < static_cast<int>(ecal.size()); j++) {
373 if (ecal_used[j])
continue;
374 double z = ecal[j].getCentroidZ();
375 double d = std::hypot(
376 pos[0] + mom[0] / mom[2] * (z - pos[2]) - ecal[j].getCentroidX(),
377 pos[1] + mom[1] / mom[2] * (z - pos[2]) - ecal[j].getCentroidY());
378 if (d < hadron_ecal_radius_) near.emplace_back(d,
false, j);
380 double cos_max = std::cos(hcal_track_angle_ * M_PI / 180.);
381 for (
int j = 0; j < static_cast<int>(hcal.size()); j++) {
382 if (hcal_used[j])
continue;
383 double dx = hcal[j].getCentroidX() - pos[0];
384 double dy = hcal[j].getCentroidY() - pos[1];
385 double dz = hcal[j].getCentroidZ() - pos[2];
386 double cs = (dx * mom[0] + dy * mom[1] + dz * mom[2]) /
387 (pm * std::hypot(dx, dy, dz));
390 double ang = std::acos(std::min(1., cs)) * 180. / M_PI;
391 near.emplace_back(hadron_ecal_radius_ + ang,
true, j);
394 std::sort(near.begin(), near.end());
395 for (
const auto& [d, is_hcal, j] : near) {
396 const auto& cl = is_hcal ? hcal[j] : ecal[j];
397 double raw = cl.getEnergy();
400 ? correction(hcal_corr_energies_, hcal_corr_factors_, raw)
401 : correction(ecal_corr_energies_, ecal_corr_factors_, raw));
402 if (calo + e > budget)
continue;
405 cand.setHcalEnergy(cand.getHcalEnergy() + e);
406 cand.setHcalRawEnergy(cand.getHcalRawEnergy() + raw);
407 cand.addHcalHits(cl.getHitIDs(), cl.getHitE());
408 cand.setPID(cand.getPID() | 4);
411 cand.setEcalEnergy(cand.getEcalEnergy() + e);
412 cand.setEcalRawEnergy(cand.getEcalRawEnergy() + raw);
413 cand.addEcalHits(cl.getHitIDs(), cl.getHitE());
423 auto ts = tk.getTrackState(ldmx::AtECAL);
424 if (!ts || ts->mom_.size() < 3 || ts->mom_[2] <= 0)
return 0.;
425 const auto& pos = ts->pos_;
426 const auto& mom = ts->mom_;
427 const auto& hx = cl.getHitX();
428 const auto& hy = cl.getHitY();
429 const auto& hz = cl.getHitZ();
430 const auto& he = cl.getHitE();
431 double e = 0, er2 = 0;
432 for (
size_t k = 0; k < he.size() && k < hz.size(); k++) {
433 double x = pos[0] + mom[0] / mom[2] * (hz[k] - pos[2]);
434 double y = pos[1] + mom[1] / mom[2] * (hz[k] - pos[2]);
436 er2 += he[k] * (std::pow(hx[k] - x, 2) + std::pow(hy[k] - y, 2));
438 return e > 0 ? std::sqrt(er2 / e) : 0.;
443 const auto& hz = cl.getHitZ();
444 const auto& he = cl.getHitE();
445 double e = 0, ez = 0;
446 for (
size_t k = 0; k < hz.size() && k < he.size(); k++) {
450 return (e > 0 ? ez / e : cl.
getCentroidZ()) - ecal_front_z_;
456 const std::vector<ldmx::CaloCluster>& clusters,
457 std::vector<bool>& used)
const {
458 auto ts = tk.getTrackState(ldmx::AtECAL);
459 if (recovery_radius_ <= 0 || !ts || ts->mom_.size() < 3 || ts->mom_[2] <= 0)
461 const auto& xyz = ts->pos_;
462 const auto& pxyz = ts->mom_;
463 double trk_p = std::hypot(pxyz[0], pxyz[1], pxyz[2]);
464 if (cand.getEcalEnergy() >= electron_ep_min_ * trk_p)
return;
465 std::vector<std::pair<double, int>> near;
466 for (
int j = 0; j < static_cast<int>(clusters.size()); j++) {
467 if (used[j])
continue;
468 double z = clusters[j].getCentroidZ();
469 double d = std::hypot(
470 xyz[0] + pxyz[0] / pxyz[2] * (z - xyz[2]) - clusters[j].getCentroidX(),
471 xyz[1] + pxyz[1] / pxyz[2] * (z - xyz[2]) - clusters[j].getCentroidY());
472 if (d < recovery_radius_) near.emplace_back(d, j);
474 std::sort(near.begin(), near.end());
475 for (
const auto& [d, j] : near) {
476 if (cand.getEcalEnergy() >= recovery_stop_ep_ * trk_p)
break;
477 double e = clusters[j].getEnergy();
478 double corr = correction(ecal_corr_energies_, ecal_corr_factors_, e);
479 cand.setEcalEnergy(cand.getEcalEnergy() + e * corr);
480 cand.setEcalRawEnergy(cand.getEcalRawEnergy() + e);
481 cand.addEcalHits(clusters[j].getHitIDs(), clusters[j].getHitE());
490 auto p = cand.getTrackPxPyPz();
491 double trk_p = std::hypot(p[0], p[1], p[2]);
492 double ep = trk_p > 0 ? cand.getEcalEnergy() / trk_p : 0;
493 int q = cand.getCharge() >= 0 ? 1 : -1;
495 bool has_dedx = cand.getDeDxNhits() >= dedx_min_hits_;
496 double m2 = cand.getDeDxMassSquared();
497 bool is_proton = has_dedx && trk_p < proton_max_p_ && m2 > proton_min_mass2_;
498 bool is_kaon = has_dedx && trk_p < kaon_max_p_ && m2 > kaon_min_mass2_ &&
499 m2 <= proton_min_mass2_;
501 cand.setPdgId(211 * q);
502 cand.setMass(938.27);
503 }
else if (is_kaon) {
504 cand.setPdgId(211 * q);
505 cand.setMass(493.68);
506 }
else if ((cand.getPID() & 2) && ep > electron_ep_min_ &&
507 cand.getEcalDepth() <
508 electron_max_depth_ +
509 electron_depth_slope_ *
510 std::log2(std::max(trk_p, 100.) / 1000.)) {
513 cand.setPdgId(-11 * q);
516 cand.setPdgId(211 * q);
517 cand.setMass(139.57);
523 if (!event.
exists(input_track_coll_name_, input_tracks_passname_)) {
524 ldmx_log(error) <<
"Unable to find (one) collection named "
525 << input_track_coll_name_ <<
"_" << input_tracks_passname_;
528 if (!event.
exists(input_ecal_coll_name_, input_ecal_passname_)) {
529 ldmx_log(error) <<
"Unable to find (one) collection named "
530 << input_ecal_coll_name_ <<
"_" << input_ecal_passname_;
533 if (!event.
exists(input_hcal_coll_name_, input_hcal_passname_)) {
534 ldmx_log(error) <<
"Unable to find (one) collection named "
535 << input_hcal_coll_name_ <<
"_" << input_hcal_passname_;
540 input_hcal_coll_name_, input_hcal_passname_);
541 if (hcal_merge_angle_ > 0) hcal_clusters = linkHcalClusters(hcal_clusters);
542 const auto tracks =
event.getCollection<
ldmx::Track>(input_track_coll_name_,
543 input_tracks_passname_);
545 std::vector<ldmx::SimTrackerHit> sp_hits;
546 if (event.
exists(truth_sp_coll_name_, truth_sp_passname_))
550 std::map<int, ldmx::TrackDeDxMassEstimate> mass_estimates;
551 if (!input_mass_coll_name_.empty() &&
552 event.
exists(input_mass_coll_name_, input_mass_passname_)) {
554 input_mass_coll_name_, input_mass_passname_)) {
555 mass_estimates[est.getTrackIndex()] = est;
560 use_existing_ecal_clusters_
561 ? getEcalClusters(event, input_ecal_coll_name_, input_ecal_passname_)
563 input_ecal_passname_);
564 if (ecal_link_radius_ > 0)
565 ecal_clusters = linkEcalClusters(ecal_clusters, tracks);
567 std::vector<ldmx::PFCandidate> pf_cands;
569 if (!single_particle_) {
583 std::vector<bool> tk_usable(tracks.size(),
false);
585 std::map<int, std::vector<std::pair<float, int>>> tk_calo_map;
586 for (
int i = 0; i < tracks.size(); i++) {
587 const auto& tk = tracks[i];
588 const std::vector<double> xyz = tk.
getPosition(ldmx::AtECAL);
589 const std::vector<double> pxyz = tk.
getMomentum(ldmx::AtECAL);
591 if (pxyz.empty() || pxyz[2] <= 0) {
592 ldmx_log(warn) <<
"Skipping track " << i <<
" without AtECAL state";
596 for (
int j = 0; j < ecal_clusters.size(); j++) {
597 float dist = trackClusterDist(xyz, pxyz, ecal_clusters[j]);
599 if (dist < link_max_dist_) tk_calo_map[i].emplace_back(dist, j);
601 std::sort(tk_calo_map[i].begin(), tk_calo_map[i].end());
605 std::map<int, std::vector<int>> em_had_calo_map;
606 for (
int i = 0; i < ecal_clusters.size(); i++) {
607 const auto& ecal = ecal_clusters[i];
608 for (
int j = 0; j < hcal_clusters.size(); j++) {
609 const auto& hcal = hcal_clusters[j];
610 const float x_at_h_clus =
611 ecal.getCentroidX() +
612 ecal.getDXDZ() * (hcal.getCentroidZ() -
613 ecal.getCentroidZ());
614 const float y_at_h_clus =
615 ecal.getCentroidY() +
616 ecal.getDYDZ() * (hcal.getCentroidZ() - ecal.getCentroidZ());
618 pow(x_at_h_clus - hcal.getCentroidX(), 2) /
619 std::max(1.0, pow(hcal.getRMSX(), 2) + pow(ecal.getRMSX(), 2)) +
620 pow(y_at_h_clus - hcal.getCentroidY(), 2) /
621 std::max(1.0, pow(hcal.getRMSY(), 2) + pow(ecal.getRMSY(), 2)));
622 bool is_match = (dist < 5);
623 if (is_match) em_had_calo_map[i].push_back(j);
630 std::vector<bool> tk_is_em_linked(tracks.size(),
false);
631 std::vector<bool> em_is_tk_linked(ecal_clusters.size(),
false);
632 std::map<int, int> tk_em_pairs{};
633 for (
int i = 0; i < tracks.size(); i++) {
634 if (tk_calo_map.count(i)) {
636 for (
const auto& [dist, em_idx] : tk_calo_map[i]) {
637 if (!em_is_tk_linked[em_idx]) {
638 em_is_tk_linked[em_idx] =
true;
639 tk_is_em_linked[i] =
true;
640 tk_em_pairs[i] = em_idx;
648 std::vector<bool> em_is_had_linked(ecal_clusters.size(),
false);
649 std::vector<bool> had_is_em_linked(hcal_clusters.size(),
false);
650 std::map<int, int> em_had_pairs{};
651 for (
int i = 0; i < ecal_clusters.size(); i++) {
652 if (em_had_calo_map.count(i)) {
654 for (
int had_idx : em_had_calo_map[i]) {
655 if (!had_is_em_linked[had_idx]) {
656 had_is_em_linked[had_idx] =
true;
657 em_is_had_linked[i] =
true;
658 em_had_pairs[i] = had_idx;
672 for (
int i = 0; i < tracks.size(); i++) {
673 if (!tk_usable[i])
continue;
675 fillCandTrack(cand, tracks[i],
677 cand.setTrackIndex(i);
678 if (tk_is_em_linked[i]) {
679 int em_idx = tk_em_pairs[i];
680 fillCandEMCalo(cand, ecal_clusters[em_idx]);
681 cand.setEcalWidth(ecalWidth(ecal_clusters[em_idx], tracks[i]));
682 cand.setEcalIndex(em_idx);
683 if (em_is_had_linked[em_idx]) {
684 fillCandHadCalo(cand, hcal_clusters[em_had_pairs[em_idx]]);
685 cand.setHcalIndex(em_had_pairs[em_idx]);
687 recoverEcal(cand, tracks[i], ecal_clusters, em_is_tk_linked);
691 if (cand.getHcalIndex() < 0) {
692 int h = linkTrackHcal(tracks[i], hcal_clusters, had_is_em_linked);
694 fillCandHadCalo(cand, hcal_clusters[h]);
695 cand.setHcalIndex(h);
696 had_is_em_linked[h] =
true;
699 if (
auto it = mass_estimates.find(i); it != mass_estimates.end()) {
700 cand.setDeDxMassSquared(it->second.getMassSquared());
701 cand.setDeDxIh(it->second.getIh());
702 cand.setDeDxNhits(it->second.getNhits());
704 cand.setCharge(
static_cast<int>(tracks[i].getCharge()));
705 identifyCharged(cand);
706 if (std::abs(cand.getPdgId()) == 211) {
707 absorbHadronShower(cand, tracks[i], ecal_clusters, em_is_tk_linked,
708 hcal_clusters, had_is_em_linked);
711 auto p = cand.getTrackPxPyPz();
712 double trk_p = std::hypot(p[0], p[1], p[2]);
713 cand.setEnergy(std::hypot(trk_p, cand.getMass()));
714 pf_cands.push_back(cand);
717 double ecal_e = cand.getEcalEnergy();
718 if (tk_is_em_linked[i] && trk_p > 0 && ecal_e > excess_ep_ * trk_p) {
720 fillCandEMCalo(excess, ecal_clusters[tk_em_pairs[i]]);
721 excess.setEcalIndex(tk_em_pairs[i]);
724 excess.clearEcalHits();
725 excess.setEnergy(ecal_e - trk_p);
726 pf_cands.push_back(excess);
731 for (
int i = 0; i < ecal_clusters.size(); i++) {
732 if (em_is_tk_linked[i])
continue;
734 fillCandEMCalo(cand, ecal_clusters[i]);
735 cand.setEcalIndex(i);
736 if (em_is_had_linked[i]) {
737 fillCandHadCalo(cand, hcal_clusters[em_had_pairs[i]]);
738 cand.setHcalIndex(em_had_pairs[i]);
742 cand.setPdgId(em_is_had_linked[i] ? 130 : 22);
743 cand.setEnergy(cand.getEcalEnergy() + cand.getHcalEnergy());
744 if (cand.getEnergy() >= min_neutral_e_) pf_cands.push_back(cand);
748 for (
int i = 0; i < hcal_clusters.size(); i++) {
749 if (had_is_em_linked[i])
continue;
751 fillCandHadCalo(cand, hcal_clusters[i]);
752 cand.setHcalIndex(i);
754 cand.setEnergy(cand.getHcalEnergy());
755 if (cand.getEnergy() >= min_neutral_e_) pf_cands.push_back(cand);
762 if (tracks.size() && !tracks[0].getMomentum(ldmx::AtECAL).empty()) {
763 fillCandTrack(pf, tracks[0], sp_hits);
767 if (ecal_clusters.size()) {
768 fillCandEMCalo(pf, ecal_clusters[0]);
772 if (hcal_clusters.size()) {
773 fillCandHadCalo(pf, hcal_clusters[0]);
778 pf.setEnergy(pf.getEcalEnergy() + pf.getHcalEnergy());
779 pf_cands.push_back(pf);
782 event.add(output_coll_name_, pf_cands);
785const std::vector<ldmx::CaloCluster> ParticleFlow::getEcalClusters(
787 std::string inputClusterPassName) {
789 inputClusterCollName, inputClusterPassName);
790 std::string new_name = inputClusterCollName +
"Cast";
791 std::vector<ldmx::CaloCluster> new_clusters;
792 for (
auto cl : tmp_clusters) {
793 new_clusters.emplace_back(cl);
795 event.add(new_name, new_clusters);
796 const auto calo_clusters =
798 return calo_clusters;
Class that stores cluster information from the ECal.
#define DECLARE_PRODUCER(CLASS)
Macro which allows the framework to construct a producer given its name during configuration.
Class that represents the estimated mass of a particle using tracker dE/dx information.
Implements an event buffer system for storing event data.
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.
const std::vector< ContentType > & getCollection(const std::string &collectionName, const std::string &passName) const
Get a collection (std::vector) of objects from the event bus.
Class encapsulating parameters for configuring a processor.
const T & get(const std::string &name) const
Retrieve the parameter of the given name.
Stores cluster information from the ECal.
double getRMSX() const
rms in x
void setNHits(int nHits)
Sets total number of hits in the cluster.
double getDYDZ() const
Delta in y-z plane.
double getCentroidZ() const
centroid z-location
double getCentroidX() const
centroid x-location
void setCentroidXYZ(double centroid_x, double centroid_y, double centroid_z)
Sets the three coordinates of the cluster centroid.
double getRMSZ() const
rms in z
double getRMSY() const
rms in y
void setEnergy(double energy)
Sets total energy for the cluster.
void setIDs(std::vector< unsigned int > &hitIDs)
Sets a sorted vector for the IDs of the hits_ that make up the cluster.
double getCentroidY() const
centroid y-location
double getEDXDZ() const
Delta unc on unc in x-z plane.
double getDXDZ() const
Delta in x-z plane.
double getEDYDZ() const
Delta unc on unc in y-z plane.
Stores cluster information from the ECal.
Represents a reconstructed particle.
Represents a simulated tracker hit in the simulation.
int getPdgID() const
Get the Sim particle track ID of the hit.
std::vector< float > getPosition() const
Get the XYZ position of the hit [mm].
float getEnergy() const
Get the energy.
std::vector< double > getMomentum() const
Get the XYZ momentum of the particle at the position at which the hit took place [MeV].
int getTrackID() const
Get the Sim particle track ID of the hit.
Represents the estimated mass of a particle using tracker dE/dx information.
Implementation of a track object.
std::vector< double > getMomentum(TrackStateType tstype) const
Returns the momentum (px, py, pz) in MeV in the LDMX global frame for the requested TrackState type.
std::vector< double > getPosition(TrackStateType tstype) const
Returns the position (x, y, z) in mm in the LDMX global frame for the requested TrackState type.
virtual void produce(framework::Event &event)
Process the event and put new data products into it.
virtual void onProcessEnd()
Callback for the EventProcessor to take any necessary action when the processing of events finishes,...
virtual void configure(framework::config::Parameters &ps)
Callback for the EventProcessor to configure itself from the given set of parameters.