LDMX Software
PFTrackProducer.cxx
2
3#include <algorithm>
4#include <cmath>
5#include <set>
6
8#include "Tracking/Event/Track.h"
9
10namespace recon {
11
13 truth_tracking_ = ps.get<bool>("truth_tracking");
14 input_track_coll_name_ = ps.get<std::string>("input_track_coll_name");
15 input_pass_name_ = ps.get<std::string>("input_pass_name");
16 output_track_coll_name_ = ps.get<std::string>("output_track_coll_name");
17 do_electron_tracking_ = ps.get<bool>("do_electron_tracking");
18 min_electron_momentum_z_ = ps.get<double>("min_electron_momentum_z");
19 max_electron_track_id_ = ps.get<int>("max_electron_track_id");
20 reco_track_coll_name_ = ps.get<std::string>("reco_track_coll_name");
21 reco_track_pass_name_ = ps.get<std::string>("reco_track_pass_name");
22 min_n_hits_ = ps.get<int>("min_n_hits");
23 max_chi2_ndf_ = ps.get<double>("max_chi2_ndf");
24 min_momentum_ = ps.get<double>("min_momentum");
25 max_shared_frac_ = ps.get<double>("max_shared_frac");
26}
27
28namespace {
29
30double getP(const std::vector<double>& pxyz) {
31 return sqrt(pow(pxyz[0], 2) + pow(pxyz[1], 2) + pow(pxyz[2], 2));
32}
33
34double getP(const ldmx::Track& tk) {
35 auto pxyz = tk.getMomentum(ldmx::AtECAL);
36 return pxyz.empty() ? 0. : getP(pxyz);
37}
38
39// charge of common long lived particles, 0 otherwise
40int chargeFromPdg(int pdg) {
41 int sign = pdg > 0 ? 1 : -1;
42 switch (std::abs(pdg)) {
43 case 11:
44 case 13:
45 case 15:
46 return -sign;
47 case 211:
48 case 321:
49 case 2212:
50 return sign;
51 default:
52 return 0;
53 }
54}
55
56// wrap a scoring plane hit into a track with a single AtECAL state
57ldmx::Track trackFromSPHit(const ldmx::SimTrackerHit& sp_hit) {
59 auto xyz = sp_hit.getPosition();
60 ts.pos_ = {xyz[0], xyz[1], xyz[2]};
61 ts.mom_ = sp_hit.getMomentum();
62 ts.ts_type_ = ldmx::AtECAL;
63 ldmx::Track tk;
64 tk.addTrackState(ts);
65 tk.setTrackID(sp_hit.getTrackID());
66 tk.setPdgID(sp_hit.getPdgID());
67 tk.setCharge(chargeFromPdg(sp_hit.getPdgID()));
68 tk.setTruthProb(1.);
69 return tk;
70}
71
72} // namespace
73
75 if (truth_tracking_)
76 produceTruth(event);
77 else
78 produceReco(event);
79}
80
81void PFTrackProducer::produceTruth(framework::Event& event) {
82 if (!event.exists(input_track_coll_name_, input_pass_name_)) {
83 ldmx_log(fatal) << "Couldn't find input collection "
84 << input_track_coll_name_ << "_" << input_pass_name_;
85 return;
86 }
87 const auto ecal_sp_hits = event.getCollection<ldmx::SimTrackerHit>(
88 input_track_coll_name_, input_pass_name_);
89
90 std::vector<ldmx::Track> pf_tracks;
91 for (const auto& sp_hit : ecal_sp_hits) {
92 if (sp_hit.getPdgID() == 22 || sp_hit.getPdgID() == 2112) continue;
93 if (fabs(240 - sp_hit.getPosition()[2]) > 0.1) continue;
94 if (do_electron_tracking_) { // only select electron SP hits_
95 if (sp_hit.getPdgID() != 11) continue;
96 if (sp_hit.getTrackID() < 2 &&
97 sp_hit.getMomentum()[2] > min_electron_momentum_z_) {
98 // this is almost guaranteed to be a pileup beam electron! keep it
99 pf_tracks.push_back(trackFromSPHit(sp_hit));
100 ldmx_log(debug) << "Added beam electron SP hit: trackID="
101 << sp_hit.getTrackID()
102 << ", pz = " << sp_hit.getMomentum()[2];
103 } else if (sp_hit.getTrackID() <= max_electron_track_id_ &&
104 sp_hit.getMomentum()[2] > 5) {
105 // require more than minimum forward momentum to catch recoil electron
106 // candidates
107 pf_tracks.push_back(trackFromSPHit(sp_hit));
108 ldmx_log(debug) << "Adding SP hit: trackID=" << sp_hit.getTrackID()
109 << ", pdgID= " << sp_hit.getPdgID()
110 << ", pz = " << sp_hit.getMomentum()[2];
111 continue;
112 }
113 } // if electron tracking
114 else {
115 if (sp_hit.getTrackID() != 1 || sp_hit.getMomentum()[2] < 0) continue;
116 pf_tracks.push_back(trackFromSPHit(sp_hit));
117 ldmx_log(debug) << "Adding SP hit: trackID=" << sp_hit.getTrackID()
118 << ", pdgID= " << sp_hit.getPdgID()
119 << ", pz = " << sp_hit.getMomentum()[2];
120 break;
121 }
122 } // over SP hits_
123 std::sort(pf_tracks.begin(), pf_tracks.end(),
124 [](const ldmx::Track& a, const ldmx::Track& b) {
125 return getP(a) > getP(b);
126 });
127 event.add(output_track_coll_name_, pf_tracks);
128}
129
130void PFTrackProducer::produceReco(framework::Event& event) {
131 if (!event.exists(reco_track_coll_name_, reco_track_pass_name_)) {
132 ldmx_log(fatal) << "Couldn't find input collection "
133 << reco_track_coll_name_ << "_" << reco_track_pass_name_;
134 return;
135 }
136 const auto tracks = event.getCollection<ldmx::Track>(reco_track_coll_name_,
137 reco_track_pass_name_);
138
139 // best tracks first: more hits, then lower chi2/ndf
140 std::vector<const ldmx::Track*> ranked;
141 for (const auto& tk : tracks) ranked.push_back(&tk);
142 auto chi2ndf = [](const ldmx::Track* t) {
143 return t->getNdf() > 0 ? t->getChi2() / t->getNdf() : 9e9;
144 };
145 std::stable_sort(ranked.begin(), ranked.end(), [&](auto a, auto b) {
146 if (a->getNhits() != b->getNhits()) return a->getNhits() > b->getNhits();
147 return chi2ndf(a) < chi2ndf(b);
148 });
149
150 std::vector<ldmx::Track> pf_tracks;
151 std::vector<std::set<unsigned int>> used_hits;
152 for (const auto* tk_ptr : ranked) {
153 const auto& tk = *tk_ptr;
154 // need the Ecal face state for linking
155 auto mom = tk.getMomentum(ldmx::AtECAL);
156 if (mom.empty()) {
157 ldmx_log(debug) << "Dropping track without AtECAL state";
158 continue;
159 }
160 if (mom[2] <= 0) continue;
161 if (tk.getNhits() < min_n_hits_) continue;
162 if (max_chi2_ndf_ >= 0 &&
163 (tk.getNdf() <= 0 || tk.getChi2() / tk.getNdf() > max_chi2_ndf_))
164 continue;
165 if (getP(mom) < min_momentum_) continue;
166 // drop duplicates sharing most hits with a better track
167 auto idxs = tk.getMeasurementsIdxs();
168 std::set<unsigned int> hits(idxs.begin(), idxs.end());
169 bool duplicate = false;
170 for (const auto& prev : used_hits) {
171 int n_shared = 0;
172 for (auto h : hits) n_shared += prev.count(h);
173 if (!hits.empty() && n_shared > max_shared_frac_ * hits.size()) {
174 duplicate = true;
175 break;
176 }
177 }
178 if (duplicate) continue;
179 used_hits.push_back(hits);
180 pf_tracks.push_back(tk);
181 ldmx_log(debug) << "Adding track: nhits=" << tk.getNhits()
182 << ", p = " << getP(mom) << ", trackID=" << tk.getTrackID();
183 }
184 std::sort(pf_tracks.begin(), pf_tracks.end(),
185 [](const ldmx::Track& a, const ldmx::Track& b) {
186 return getP(a) > getP(b);
187 });
188 event.add(output_track_coll_name_, pf_tracks);
189}
190} // namespace recon
191
#define DECLARE_PRODUCER(CLASS)
Macro which allows the framework to construct a producer given its name during configuration.
Track selection for PFlow Reco.
Class which encapsulates information from a hit in a simulated tracking detector.
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
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 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].
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.
Implementation of a track object.
Definition Track.h:54
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.
Definition Track.h:237
Selects the tracks passed to ParticleFlow.
virtual void produce(framework::Event &event)
Process the event and put new data products into it.
virtual void configure(framework::config::Parameters &ps)
Callback for the EventProcessor to configure itself from the given set of parameters.