LDMX Software
ParticleFlow.cxx
2
3#include <vector>
4
5namespace recon {
6
8 // I/O
9 inputEcalCollName_ = ps.getParameter<std::string>("inputEcalCollName");
10 inputHcalCollName_ = ps.getParameter<std::string>("inputHcalCollName");
11 inputTrackCollName_ = ps.getParameter<std::string>("inputTrackCollName");
12 outputCollName_ = ps.getParameter<std::string>("outputCollName");
13 // Algorithm configuration
14 singleParticle_ = ps.getParameter<bool>("singleParticle");
15
16 // Calibration factors, from jason, temperary
17 std::vector<float> em1{250.0, 750.0, 1250.0, 1750.0, 2250.0, 2750.0,
18 3250.0, 3750.0, 4250.0, 4750.0, 5250.0, 5750.};
19 std::vector<float> em2{1.175, 1.02, 0.99, 0.985, 0.975, 0.975,
20 0.96, 0.94, 0.87, 0.8, 0.73, 0.665};
21 std::vector<float> h1{25.0, 75.0, 125.0, 175.0, 225.0,
22 275.0, 325.0, 375.0, 425.0};
23 std::vector<float> h2{8.44, 7.38, 7.76, 8.535, 9.47,
24 10.45, 10.47, 9.71, 8.87};
25 eCorr_ = new TGraph(em1.size(), em1.data(), em2.data());
26 hCorr_ = new TGraph(h1.size(), h1.data(), h2.data());
27}
28
29// produce candidate track info
30void ParticleFlow::fillCandTrack(ldmx::PFCandidate& cand,
31 const ldmx::SimTrackerHit& tk) {
32 // TODO: smear
33 std::vector<float> xyz = tk.getPosition();
34 std::vector<double> pxyz = tk.getMomentum();
35 float ecalZ = 248;
36 float ecalX =
37 xyz[0] + pxyz[0] / pxyz[2] * (ecalZ - xyz[2]); // project onto ecal face
38 float ecalY = xyz[1] + pxyz[1] / pxyz[2] * (ecalZ - xyz[2]);
39 cand.setEcalPositionXYZ(ecalX, ecalY, ecalZ);
40 cand.setTrackPxPyPz(pxyz[0], pxyz[1], pxyz[2]);
41 // also use this object to set truth info
42 cand.setTruthEcalXYZ(ecalX, ecalY, ecalZ);
43 cand.setTruthPxPyPz(pxyz[0], pxyz[1], pxyz[2]);
44 float m2 = pow(tk.getEnergy(), 2) - pow(pxyz[0], 2) - pow(pxyz[1], 2) -
45 pow(pxyz[2], 2);
46 if (m2 < 0) m2 = 0;
47 cand.setTruthMass(sqrt(m2));
48 cand.setTruthEnergy(tk.getEnergy());
49 cand.setTruthPdgId(tk.getPdgID());
50 cand.setPID(cand.getPID() | 1); // OR with 001
51}
52// produce candidate ECal info
53void ParticleFlow::fillCandEMCalo(ldmx::PFCandidate& cand,
54 const ldmx::CaloCluster& em) {
55 float corr = 1.;
56 float e = em.getEnergy();
57 if (e < eCorr_->GetX()[0]) {
58 corr = eCorr_->GetX()[0];
59 } else if (e > eCorr_->GetX()[eCorr_->GetN() - 1]) {
60 corr = eCorr_->GetX()[eCorr_->GetN() - 1];
61 } else {
62 corr = eCorr_->Eval(e);
63 }
64 cand.setEcalEnergy(e * corr);
65 cand.setEcalRawEnergy(e);
66 cand.setEcalClusterXYZ(em.getCentroidX(), em.getCentroidY(),
67 em.getCentroidZ());
68 cand.setEcalClusterEXYZ(em.getRMSX(), em.getRMSY(), em.getRMSZ());
69 cand.setEcalClusterDXDZ(em.getDXDZ());
70 cand.setEcalClusterDYDZ(em.getDYDZ());
71 cand.setEcalClusterEDXDZ(em.getEDXDZ());
72 cand.setEcalClusterEDYDZ(em.getEDYDZ());
73 cand.setPID(cand.getPID() | 2); // OR with 010
74}
75// produce candidate HCal info
76void ParticleFlow::fillCandHadCalo(ldmx::PFCandidate& cand,
77 const ldmx::CaloCluster& had) {
78 float corr = 1.;
79 float e = had.getEnergy();
80 if (e < hCorr_->GetX()[0]) {
81 corr = hCorr_->GetX()[0];
82 } else if (e > hCorr_->GetX()[hCorr_->GetN() - 1]) {
83 corr = hCorr_->GetX()[hCorr_->GetN() - 1];
84 } else {
85 corr = hCorr_->Eval(e);
86 }
87 cand.setHcalEnergy(e * corr);
88 cand.setHcalRawEnergy(e);
89 cand.setHcalClusterXYZ(had.getCentroidX(), had.getCentroidY(),
90 had.getCentroidZ());
91 cand.setHcalClusterEXYZ(had.getRMSX(), had.getRMSY(), had.getRMSZ());
92 cand.setHcalClusterDXDZ(had.getDXDZ());
93 cand.setHcalClusterDYDZ(had.getDYDZ());
94 cand.setHcalClusterEDXDZ(had.getEDXDZ());
95 cand.setHcalClusterEDYDZ(had.getEDYDZ());
96 cand.setPID(cand.getPID() | 4); // OR with 100
97}
98
99// produce track, ecal, and hcal linking
101 if (!event.exists(inputTrackCollName_)) return;
102 if (!event.exists(inputEcalCollName_)) return;
103 if (!event.exists(inputHcalCollName_)) return;
104 // get the track and clustering info
105 const auto ecalClusters =
106 event.getCollection<ldmx::CaloCluster>(inputEcalCollName_);
107 const auto hcalClusters =
108 event.getCollection<ldmx::CaloCluster>(inputHcalCollName_);
109 const auto tracks =
110 event.getCollection<ldmx::SimTrackerHit>(inputTrackCollName_);
111
112 std::vector<ldmx::PFCandidate> pfCands;
113 // multi-particle case
114 if (!singleParticle_) {
115 /*
116 1. Build links maps at the Tk/Ecal and Hcal/Hcal interfaces
117 2. Categorize tracks as: Ecal-matched, (Side) Hcal-matched, unmatched
118 3. Categorize Ecal clusters as: Hcal-matched, unmatched
119 4a. (Upstream?) Categorize tracks with dedx?
120 4b. (Upstream?) Categorize ecal clusters as: EM/Had-like
121 4c. (Upstream?) Categorize hcal clusters as: EM/Had-like
122 5. Build candidates by category, moving from Tk-Ecal-Hcal
123 */
124
125 //
126 // track-calo linking
127 //
128 std::map<int, std::vector<int> > tkCaloMap;
129 std::map<int, std::vector<int> > caloTkMap;
130 std::map<std::pair<int, int>, float> tkEMDist;
131 // std::vector<int> unmatchedTracks;
132 for (int i = 0; i < tracks.size(); i++) {
133 const auto& tk = tracks[i];
134 const std::vector<float> xyz = tk.getPosition();
135 const std::vector<double> pxyz = tk.getMomentum();
136 const float p = sqrt(pow(pxyz[0], 2) + pow(pxyz[1], 2) + pow(pxyz[2], 2));
137 // float bestMatchVal = 9e9;
138 for (int j = 0; j < ecalClusters.size(); j++) {
139 const auto& ecal = ecalClusters[j];
140 // Matching logic
141 const float ecalClusZ = ecal.getCentroidZ();
142 const float tkXAtClus =
143 xyz[0] + pxyz[0] / pxyz[2] * (ecalClusZ - xyz[2]); // extrapolation
144 const float tkYAtClus =
145 xyz[1] + pxyz[1] / pxyz[2] * (ecalClusZ - xyz[2]);
146 float dist = hypot(
147 (tkXAtClus - ecal.getCentroidX()) / std::max(1.0, ecal.getRMSX()),
148 (tkYAtClus - ecal.getCentroidY()) / std::max(1.0, ecal.getRMSY()));
149 tkEMDist[{i, j}] = dist;
150 bool isMatch =
151 (dist < 2) && (ecal.getEnergy() > 0.3 * p &&
152 ecal.getEnergy() < 2 * p); // matching criteria *
153 // if (isMatch && dist < bestMatchVal) bestMatchVal = dist;
154 if (isMatch) {
155 if (tkCaloMap.count(i))
156 tkCaloMap[i].push_back(j);
157 else
158 tkCaloMap[i] = {j};
159 if (caloTkMap.count(j))
160 caloTkMap[j].push_back(i);
161 else
162 caloTkMap[j] = {i};
163 }
164 }
165 }
166
167 // em-hadcalo linking
168 std::map<int, std::vector<int> > emHadCaloMap;
169 std::map<std::pair<int, int>, float> EMHadDist;
170 for (int i = 0; i < ecalClusters.size(); i++) {
171 const auto& ecal = ecalClusters[i];
172 for (int j = 0; j < hcalClusters.size(); j++) {
173 const auto& hcal = hcalClusters[j];
174 // TODO: matching logic
175 const float xAtHClus =
176 ecal.getCentroidX() +
177 ecal.getDXDZ() * (hcal.getCentroidZ() -
178 ecal.getCentroidZ()); // extrapolated position
179 const float yAtHClus =
180 ecal.getCentroidY() +
181 ecal.getDYDZ() * (hcal.getCentroidZ() - ecal.getCentroidZ());
182 float dist = sqrt(
183 pow(xAtHClus - hcal.getCentroidX(), 2) /
184 std::max(1.0, pow(hcal.getRMSX(), 2) + pow(ecal.getRMSX(), 2)) +
185 pow(yAtHClus - hcal.getCentroidY(), 2) /
186 std::max(1.0, pow(hcal.getRMSY(), 2) + pow(ecal.getRMSY(), 2)));
187 EMHadDist[{i, j}] = dist;
188 bool isMatch = (dist < 5); // matching criteria, was 2
189 if (isMatch) {
190 if (emHadCaloMap.count(i))
191 emHadCaloMap[i].push_back(j);
192 else
193 emHadCaloMap[i] = {j};
194 }
195 }
196 }
197
198 // NOT YET IMPLEMENTED...
199 // tk-hadcalo linking (Side HCal)
200 std::map<int, std::vector<int> > tkHadCaloMap;
201 // for(int i=0; i<tracks.size(); i++){
202 // const auto& tk = tracks[i];
203 // for(int j=0; j<hcalClusters.size(); j++){
204 // const auto& hcal = hcalClusters[j];
205 // // TODO: add the matching logic here...
206 // bool isMatch = true;
207 // if(isMatch){
208 // if (tkHadCaloMap.count(i)) tkHadCaloMap[i].push_back(j);
209 // else tkHadCaloMap[i] = {j};
210 // }
211 // }
212 // }
213
214 //
215 // track / ecal cluster arbitration
216 //
217 std::vector<bool> tkIsEMLinked(tracks.size(), false);
218 std::vector<bool> EMIsTkLinked(ecalClusters.size(), false);
219 std::map<int, int> tkEMPairs{};
220 for (int i = 0; i < tracks.size(); i++) {
221 if (tkCaloMap.count(i)) {
222 // pick first (highest-energy) unused matching cluster
223 for (int em_idx : tkCaloMap[i]) {
224 if (!EMIsTkLinked[em_idx]) {
225 EMIsTkLinked[em_idx] = true;
226 tkIsEMLinked[i] = true;
227 tkEMPairs[i] = em_idx;
228 break;
229 }
230 }
231 }
232 }
233
234 // track / hcal cluster arbitration
235 std::vector<bool> EMIsHadLinked(ecalClusters.size(), false);
236 std::vector<bool> HadIsEMLinked(hcalClusters.size(), false);
237 std::map<int, int> EMHadPairs{};
238 for (int i = 0; i < ecalClusters.size(); i++) {
239 if (emHadCaloMap.count(i)) {
240 // pick first (highest-energy) unused matching cluster
241 for (int had_idx : emHadCaloMap[i]) {
242 if (!HadIsEMLinked[had_idx]) {
243 HadIsEMLinked[had_idx] = true;
244 EMIsHadLinked[i] = true;
245 EMHadPairs[i] = had_idx;
246 break;
247 }
248 }
249 }
250 }
251
252 // can consider combining satellite clusters here...
253 // define some "primary cluster" ID criterion
254 // and can add fails to the primaries
255
256 //
257 // Begin building pf candidates from tracks
258 //
259
260 // std::vector<ldmx::PFCandidate> chargedMatch;
261 // std::vector<ldmx::PFCandidate> chargedUnmatch;
262 for (int i = 0; i < tracks.size(); i++) {
264 fillCandTrack(cand, tracks[i]); // append track info to candidate
265
266 if (!tkIsEMLinked[i]) {
267 // chargedUnmatch.push_back(cand);
268 } else { // if track is linked with ECal cluster
269 fillCandEMCalo(cand, ecalClusters[tkEMPairs[i]]);
270 if (EMIsHadLinked[tkEMPairs[i]]) { // if ECal is linked with HCal
271 // cluster
272 fillCandHadCalo(cand, hcalClusters[EMHadPairs[tkEMPairs[i]]]);
273 }
274 // chargedMatch.push_back(cand);
275 }
276 pfCands.push_back(cand);
277 }
278
279 // std::vector<ldmx::PFCandidate> emMatch;
280 // std::vector<ldmx::PFCandidate> emUnmatch;
281 for (int i = 0; i < ecalClusters.size(); i++) {
282 // already linked with ECal in the previous step
283 if (EMIsTkLinked[i]) continue;
284
286 fillCandEMCalo(cand, ecalClusters[i]);
287 if (EMIsHadLinked[tkEMPairs[i]]) {
288 fillCandHadCalo(cand, hcalClusters[EMHadPairs[i]]);
289 // emMatch.push_back(cand);
290 } else {
291 // emUnmatch.push_back(cand);
292 }
293 pfCands.push_back(cand);
294 }
295 std::vector<ldmx::PFCandidate> hadOnly;
296 for (int i = 0; i < hcalClusters.size(); i++) {
297 if (HadIsEMLinked[i]) continue;
299 fillCandHadCalo(cand, hcalClusters[i]);
300 // hadOnly.push_back(cand);
301 pfCands.push_back(cand);
302 }
303
304 // // track / ecal cluster arbitration
305 // std::vector<ldmx::PFCandidate> caloMatchedTks;
306 // std::vector<ldmx::PFCandidate> unmatchedTks;
307 // std::vector<bool> emUsed (ecalClusters.size(), false);
308 // for(int i=0; i<tracks.size(); i++){
309 // ldmx::PFCandidate cand;
310 // fillCandTrack(cand, tracks[i]);
311 // if( tkCaloMap.count(i)==0 ){
312 // unmatchedTks.push_back(cand);
313 // } else {
314 // // look for the first (highest-energy) unused matching cluster
315 // bool linked=false;
316 // for(int em_idx : tkCaloMap[i]){
317 // if(!emUsed[em_idx]){
318 // fillCandEMCalo(cand, tkCaloMap[i][0]);
319 // caloMatchedTks.push_back(cand);
320 // emUsed[ em_idx ] = true;
321 // linked = true;
322 // break;
323 // }
324 // }
325 // if (!linked) unmatchedTks.push_back(cand);
326 // }
327 // }
328
329 // // ecal / hcal cluster arbitration
330 // std::vector<bool> hadUsed (hcalClusters.size(), false);
331 // for(int i=0; i<ecalClusters.size(); i++){
332 // if( emHadCaloMap.count(i)==0 ){
333 // unmatchedTks.push_back(cand);
334 // } else {
335 // // look for the first (highest-energy) unused matching cluster
336 // bool linked=false;
337 // for(int em_idx : tkCaloMap[i]){
338 // if(!emUsed[em_idx]){
339 // fillCandEMCalo(cand, tkCaloMap[i][0]);
340 // caloMatchedTks.push_back(cand);
341 // emUsed[ em_idx ] = true;
342 // linked = true;
343 // break;
344 // }
345 // }
346 // if (!linked) unmatchedTks.push_back(cand);
347 // }
348 // }
349
350 // std::vector<ldmx::PFCandidate> unmatchedEMs;
351 // for(int i=0; i<ecalClusters.size(); i++){
352 // if(emUsed[i]) continue;
353 // ldmx::PFCandidate cand;
354 // fillCandEMCalo(cand, ecalClusters[i]);
355 // }
356
357 // }
358 // if( tkCaloMap[i].size()==1 ){
359 // if(!emUsed[ tkCaloMap[i][0] ]){
360 // fillCandEMCalo(cand, tkCaloMap[i][0]);
361 // caloMatchedTks.push_back(cand);
362 // emUsed[ tkCaloMap[i][0] ] = true;
363 // }
364 // }
365 // } else if( tkCaloMap[i].size()==1 ){
366 // if(!emUsed[ tkCaloMap[i][0] ]){
367 // fillCandEMCalo(cand, tkCaloMap[i][0]);
368 // caloMatchedTks.push_back(cand);
369 // emUsed[ tkCaloMap[i][0] ] = true;
370 // }
371 // }
372 // }
373
374 } else {
375 // Single-particle builder
377 int pid = 0; // initialize pid to add
378 if (tracks.size()) {
379 fillCandTrack(pf, tracks[0]);
380 pid += 1;
381 }
382 if (ecalClusters.size()) {
383 fillCandEMCalo(pf, ecalClusters[0]);
384 pid += 2;
385 }
386 if (hcalClusters.size()) {
387 fillCandHadCalo(pf, hcalClusters[0]);
388 pid += 4;
389 }
390 pf.setPID(pid);
391 pf.setEnergy(pf.getEcalEnergy() + pf.getHcalEnergy());
392 pfCands.push_back(pf);
393 }
394
395 event.add(outputCollName_, pfCands);
396}
397
399 ldmx_log(debug) << "Process ends!";
400 delete eCorr_;
401 delete hCorr_;
402
403 return;
404}
405
406} // namespace recon
407
408DECLARE_PRODUCER_NS(recon, ParticleFlow);
#define DECLARE_PRODUCER_NS(NS, CLASS)
Macro which allows the framework to construct a producer given its name during configuration.
Simple PFlow algorithm.
Implements an event buffer system for storing event data.
Definition Event.h:41
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
Class encapsulating parameters for configuring a processor.
Definition Parameters.h:27
T getParameter(const std::string &name) const
Retrieve the parameter of the given name.
Definition Parameters.h:89
Stores cluster information from the ECal.
Definition CaloCluster.h:26
Represents a reconstructed particle.
Definition PFCandidate.h:19
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].
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.