LDMX Software
TrigScintTrackProducer.cxx
1#include "TrigScint/TrigScintTrackProducer.h"
2// Ricardo,05-26
3
4#include <cmath>
5#include <fstream>
6#include <iterator> // std::next
7#include <map>
8
9namespace trigscint {
10
12 max_delta_ = ps.get<double>(
13 "delta_max"); // max distance to consider adding in a cluster to track
14 max_delta_vert_ = ps.get<double>(
15 "delta_vert_max"); // max distance between pad 1/2 and 3 along the x axis
16 // to consider make a track using the vertical bars
17 seeding_collection_ = ps.get<std::string>(
18 "seeding_collection"); // probably tagger pad, "TriggerPadTagClusters"
19 input_collections_ = ps.get<std::vector<std::string>>(
20 "further_input_collections"); // {"TriggerPadUpClusters" ,
21 // "TriggerPadDnClusters" }
22 output_collection_ = ps.get<std::string>("output_collection");
23 pass_name_ = ps.get<std::string>("input_pass_name");
24 verbose_ = ps.get<int>("verbosity");
25 vert_bar_start_idx_ = ps.get<int>("vertical_bar_start_index");
26 n_bars_y_ = ps.get<int>("number_horizontal_bars");
27 bar_width_y_ = ps.get<double>("horizontal_bar_width");
28 bar_gap_y_ = ps.get<double>("horizontal_bar_gap");
29 n_bars_x_ = ps.get<int>("number_vertical_bars");
30 bar_width_x_ = ps.get<double>("vertical_bar_width");
31 bar_gap_x_ = ps.get<double>("vertical_bar_gap");
32 skip_last_ = ps.get<bool>("allow_skip_last_collection");
33 bar_length_y_ = ps.get<double>(
34 "horizontal_bar_length"); // bar length of the horizontal bars
35 lut_tracking_ = ps.get<bool>("lut_tracking");
36 std::string lut_file =
37 ps.get<std::string>("lut_file"); // from PatternLUTMaker
38
39 // TO DO: allow any number of input collections
40
41 if (verbose_) {
42 ldmx_log(info) << "In TrigScintTrackProducer: configure done!" << std::endl;
43 ldmx_log(info) << "Got parameters: \nSeeding: " << seeding_collection_
44 << "\nTolerance: " << max_delta_
45 << "\nInput: " << input_collections_.at(0) << " and "
46 << input_collections_.at(1)
47 << "\nInput pass name: " << pass_name_
48 << "\nAllow tracks with no hit in last collection: "
49 << skip_last_
50 << "\nUsing LUT Tracking Method: " << lut_tracking_
51 << "\nIf using LUT Method, LUT from: " << lut_file
52 << "\nVertical bar start index: " << vert_bar_start_idx_
53 << "\nNumber of horizontal bars: " << n_bars_y_
54 << "\nHorizontal bar width: " << bar_width_y_
55 << "\nHorizontal bar gap: " << bar_gap_y_
56 << "\nNumber of vertical bars: " << n_bars_x_
57 << "\nVertical bar width: " << bar_width_x_
58 << "\nVertical bar gap: " << bar_gap_x_
59 << "\nOutput: " << output_collection_
60 << "\nVerbosity: " << verbose_;
61 }
62 // each bar only goes half this distance up (overlap/zig-zag)
63 y_conv_factor_ = (bar_width_y_ + bar_gap_y_) / 2.;
64 // half height of pad
65 y_start_ = -(n_bars_y_ * (bar_width_y_ + bar_gap_y_) - bar_gap_y_) / 2.;
66 // each bar goes entire distance sideways (no overlap)
67 x_conv_factor_ = bar_width_x_ + bar_gap_x_;
68 // half width of pad
69 x_start_ = -(n_bars_x_ * (bar_width_x_ + bar_gap_x_) - bar_gap_x_) / 2.;
70
71 if (lut_tracking_) {
72 std::ifstream file(lut_file);
73 if (!file.good()) {
74 EXCEPTION_RAISE("TrigScintTrackProducer",
75 "LUT file '" + lut_file + "' not found! Make sure it's in the directory from which just is executed!");
76 }
77
78 float a, b, c;
79
80 while (file >> a >> b >> c) {
81 float p1 = a;
82 float p2 = b;
83 float p3 = c;
84 lut_.insert({p1, p2, p3});
85 }
86
87 ldmx_log(info) << "Loaded LUT with size: " << lut_.size();
88 }
89
90 return;
91}
92
94 // parameters.
95 // one pad cluster collection to use as seed
96 // a vector with the other two
97 // a maximum distance between seed centroid and other pad clusters
98 // allowed to belong to the same track
99 // an output collection name a verbosity controller
100
101 if (verbose_) {
102 ldmx_log(debug)
103 << "TrigScintTrackProducer: produce() starts! Event number: "
104 << event.getEventHeader().getEventNumber();
105 }
106 if (!event.exists(seeding_collection_, pass_name_)) {
107 ldmx_log(info) << "No collection called " << seeding_collection_
108 << "; skipping event";
109 // << "; still, not skipping event";
110 return;
111 }
112
113 if (!event.exists(seeding_collection_, pass_name_)) {
114 ldmx_log(info) << "No collection called " << seeding_collection_
115 << "; skipping event";
116 return;
117 }
118 const auto seeds{event.getCollection<ldmx::TrigScintCluster>(
119 seeding_collection_, pass_name_)};
120 uint num_seeds = seeds.size();
121
122 if (verbose_) {
123 ldmx_log(debug) << "Got track seeding cluster collection "
124 << seeding_collection_ << " with " << num_seeds
125 << " entries ";
126 }
127
128 if (!event.exists(input_collections_.at(0), pass_name_)) {
129 ldmx_log(info) << "No collection called " << input_collections_.at(0)
130 << "; skipping event";
131 // << "; still, not skipping event";
132
133 return;
134 }
135 const auto clusters_pad1{event.getCollection<ldmx::TrigScintCluster>(
136 input_collections_.at(0), pass_name_)};
137
138 if (!event.exists(input_collections_.at(1), pass_name_)) {
139 ldmx_log(info) << "No collection called "
140 << input_collections_.at(1)
141 // << "; still, not skipping event";
142 << "; skipping event";
143 std::vector<ldmx::TrigScintTrack> empty{};
144 event.add(output_collection_, empty);
145 return;
146 }
147
148 const auto clusters_pad2{event.getCollection<ldmx::TrigScintCluster>(
149 input_collections_.at(1), pass_name_)};
150
151 if (verbose_) {
152 ldmx_log(debug) << "Got the other two pad collections:"
153 << input_collections_.at(0) << " with "
154 << clusters_pad1.size() << " entries, and "
155 << input_collections_.at(1) << " with "
156 << clusters_pad2.size() << " entries.";
157 }
158 std::vector<ldmx::TrigScintTrack> cleaned_tracks;
159 std::vector<ldmx::TrigScintTrack> cleaned_tracks_y;
160 std::vector<ldmx::TrigScintTrack> cleaned_tracks_x;
161
162 // loop over the clusters in the seeding pad collection, if there are clusters
163 // in all pads
164 // bool skipDn = false;
165 if (num_seeds && clusters_pad1.size()) {
166 // could check this explicitly here: and then just get out of all checks on
167 // the dn pad immediately
168 // if (! clusters_pad2.size())
169 // skipDn = true ;
170 for (const auto& seed : seeds) {
171 // for each seed, search through the other two pads to match all clusters
172 // with centroids within tolerance to tracks
173 float centroid = seed.getCentroid();
174
175 std::vector<ldmx::TrigScintTrack> track_candidates;
176
177 if (verbose_ > 1) {
178 ldmx_log(debug) << "Got seed with centroid " << centroid;
179 }
180
181 // reset for each seed
182 // bool madeTrack = false;
183
184 if (lut_tracking_) { // if using LUT method
185 for (const auto &cluster1 : clusters_pad1) {
186 for (const auto &cluster2 : clusters_pad2) {
187 float seed_bin = seed.getCentroid();
188 float pad1_bin = cluster1.getCentroid();
189 float pad2_bin = cluster2.getCentroid();
190
191 LUTKey key{seed_bin, pad1_bin,
192 pad2_bin}; // LUTKey defined in header file
193
194 if (lut_.find(key) != lut_.end()) {
195 std::vector<ldmx::TrigScintCluster> three_cluster_vec = {
196 seed, cluster1, cluster2};
197
198 ldmx::TrigScintTrack track = makeTrack(three_cluster_vec);
199 track_candidates.push_back(track);
200 }
201 }
202 }
203
204 }
205
206 else {
207 for (const auto &cluster1 : clusters_pad1) {
208 if (verbose_ > 1) {
209 ldmx_log(debug) << "\tGot pad1 cluster with centroid "
210 << cluster1.getCentroid();
211 }
212 if ((fabs(cluster1.getCentroid() - centroid) < max_delta_ &&
213 centroid < vert_bar_start_idx_) ||
214 (centroid >= vert_bar_start_idx_ &&
215 cluster1.getCentroid() >= vert_bar_start_idx_ &&
216 seed.getCentroidX() == cluster1.getCentroidX())) {
217 // use geometry y overlap scheme to see if this is really a match in
218 // x should be done in a map
219
220 if (centroid >= vert_bar_start_idx_ &&
221 seed.getCentroidY() < cluster1.getCentroidY()) {
222 // impossible combination
223 ldmx_log(warn)
224 << "\tSkipping impossible x cluster combination "
225 "with y flags (tag up) ("
226 << seed.getCentroidY() << " " << cluster1.getCentroidY()
227 << ")";
228 continue;
229 }
230
231 // else: first (possible) match! loop through next pad too
232
233 if (verbose_ > 1) {
234 ldmx_log(debug) << "\t\tIt is close enough!. Check pad2";
235 }
236
237 // try making third pad clusters an optional part of track
238
239 std::vector<ldmx::TrigScintCluster> cluster_vec = {seed, cluster1};
240
241 bool has_match_dn = false;
242
243 for (const auto &cluster2 : clusters_pad2) {
244 if (verbose_ > 1) {
245 ldmx_log(debug) << "\tGot pad2 cluster with centroid "
246 << cluster2.getCentroid();
247 }
248
249 if ((fabs(cluster2.getCentroid() - centroid) < max_delta_ &&
250 centroid < vert_bar_start_idx_) ||
251 (centroid >= vert_bar_start_idx_ &&
252 cluster2.getCentroid() >= vert_bar_start_idx_ &&
253 fabs(seed.getCentroidX() - cluster2.getCentroidX()) <=
254 max_delta_vert_)) {
255
256 // use geometry y overlap scheme to see if this is really a
257 // match
258 // in x
259
260 if (centroid >= vert_bar_start_idx_ &&
261 (seed.getCentroidY() < cluster2.getCentroidY() ||
262 cluster1.getCentroidY() >
263 cluster2.getCentroidY())) { // impossible
264 ldmx_log(warn)
265 << "\tSkipping impossible x cluster combination with y "
266 "flags (tag up dn) ("
267 << seed.getCentroidY() << " " << cluster1.getCentroidY()
268 << " " << cluster2.getCentroidY() << ")";
269 continue;
270 }
271
272 // first match! loop through next pad too
273
274 if (verbose_ > 1) {
275 ldmx_log(debug) << "\t\tIt is close enough!. Make a track";
276 }
277
278 // only make this vector now! this ensures against hanging
279 // clusters with indices from earlier in the loop
280 std::vector<ldmx::TrigScintCluster> three_cluster_vec = {
281 seed, cluster1, cluster2};
282
283 /*
284 // here we could break if we didn't want to allow all possible
285 combinations madeTrack=true; break; //we're done with this
286 iteration once there's a track made
287 */
288 // make a track
289 ldmx::TrigScintTrack track = makeTrack(three_cluster_vec);
290 track_candidates.push_back(track);
291 has_match_dn = true;
292 } // if match in pad2
293 } // over clusters in pad2
294 // if there was no match to this in pad 2, make a track with just
295 // these two clusters
296 if (!has_match_dn && skip_last_) {
297 // we allow skipping last pad if needed
298 ldmx::TrigScintTrack track = makeTrack(cluster_vec);
299 track_candidates.push_back(track);
300 }
301
302 } // if possible (x,)y match in pad1
303 /*
304//same here
305if (madeTrack)
306break;
307*/
308
309 } // over clusters in pad1
310 }
311
312 // continue to next seed if 0 track candidates
313 if (track_candidates.size() == 0) continue;
314
315 int keep_idx = 0;
316 float min_residual = 1000; // some large number
317
318 // no need to choose between only one candidate track
319 if (track_candidates.size() > 1) {
320 // now for each seed, pick only the track with the smallest residual.
321
322 if (verbose_) {
323 ldmx_log(debug) << "Got " << track_candidates.size()
324 << " tracks to check.";
325 }
326
327 for (uint idx = 0; idx < track_candidates.size(); idx++) {
328 if ((track_candidates.at(idx)).getResidual() < min_residual) {
329 keep_idx = (int)idx;
330 min_residual =
331 (track_candidates.at(idx)).getResidual(); // update minimum
332
333 if (verbose_ > 1) {
334 ldmx_log(debug)
335 << "Track at index " << idx
336 << " has smallest residual so far: " << min_residual;
337 }
338
339 } // finding min residual
340 } // over track candidates
341 } // if more than 1 to choose from
342
343 // store the track at keepIdx, if there was one we made it this far and
344 // keepIdx is 0 or has been updated to the smallest residual track idx
345 // if (keepIdx >= 0) {
346 tracks_.push_back(track_candidates.at(keep_idx));
347 if (verbose_) {
348 ldmx_log(debug) << "Kept track at index " << keep_idx;
349 ldmx_log(trace) << track_candidates.at(keep_idx);
350 }
351 //}
352 } // over seeds
353
354 // done here if there were no tracks found
355 if (tracks_.size() == 0) {
356 if (verbose_) {
357 ldmx_log(debug) << "No tracks found!";
358 }
359 std::vector<ldmx::TrigScintTrack> empty{};
360 event.add(output_collection_, empty);
361 return;
362 }
363 // now, if there are multiple seeds sharing the same downstream hits, this
364 // should also be remedied with a selection on min residual.
365
366 // The logic of this loop kind of assumes I can remove tracks immediately --
367 // that way I can do pairwise checks between more tracks within a single
368 // loop. But for now I haven't figured out how to erase elements in a fool
369 // proof way. So I iterate over a vector...
370
371 std::vector keep_indices(tracks_.size(), 1);
372 if (verbose_ > 1)
373 ldmx_log(debug) << "vector of indices to keep has size "
374 << keep_indices.size();
375
376 for (uint idx = tracks_.size() - 1; idx > 0; idx--) {
377 // since we start in one end, we only have to check matches in one
378 // direction
379 ldmx::TrigScintTrack track = tracks_.at(idx);
380 for (int idx_comp = idx - 1; idx_comp >= 0; idx_comp--) {
381 if (verbose_ > 1)
382 ldmx_log(debug) << "In track disambiguation loop, idx points at "
383 << idx << " and prev idx points at " << idx_comp;
384
385 ldmx::TrigScintTrack next_track = tracks_.at(idx_comp);
386
387 // no need to start pulling constituents from tracks that are
388 // ridiculously far apart
389 if (((fabs(track.getCentroid() - next_track.getCentroid()) <
390 3 * max_delta_) && (track.getCentroid()
391 < vert_bar_start_idx_)) // for the horizontal bars
392 || ((fabs(track.getCentroidX() - next_track.getCentroidX()) <
393 2 * max_delta_vert_) &&
394 (track.getCentroidY() == next_track.getCentroidY()) &&
395 (track.getCentroid() >= vert_bar_start_idx_))) {
396 // and for the vertical bars, check if they are in the same quad and
397 // close enough
398 std::vector<ldmx::TrigScintCluster> consts_1 =
399 track.getConstituents();
400 std::vector<ldmx::TrigScintCluster> consts_2 =
401 next_track.getConstituents();
402 if (verbose_ > 1)
403 ldmx_log(debug)
404 << "In track disambiguation loop, got the two tracks, "
405 "with nConstituents "
406 << consts_1.size() << " and " << consts_2.size()
407 << ", respectively. ";
408 // let's do "if either cluster is shared" right now... but could also
409 // have it settable to use a stricter cut: an AND
410 if (((consts_1[1].getCentroid() == consts_2[1].getCentroid() ||
411 ((consts_1.size() > 2) && (consts_2.size() > 2) &&
412 (consts_1[2].getCentroid() == consts_2[2].getCentroid()))) &&
413 (track.getCentroid() < vert_bar_start_idx_)) ||
414 // horizontal bars
415 ((track.getCentroid() >= vert_bar_start_idx_) &&
416 ((consts_1[1].getCentroidX() == consts_2[1].getCentroidX()) ||
417 (consts_1[2].getCentroidX() == consts_2[2].getCentroidX()) ||
418 (consts_1[0].getCentroidX() ==
419 consts_2[0].getCentroidX())))) { // and vertical bars
420
421 if (verbose_ > 1) {
422 ldmx_log(debug) << "Found overlap! Tracks at index " << idx
423 << " and " << idx_comp;
424 ldmx_log(trace) << tracks_.at(idx);
425 ldmx_log(trace) << tracks_.at(idx_comp);
426 }
427
428 if (((fabs((tracks_.at(idx)).getResidualX() -
429 (tracks_.at(idx_comp)).getResidualX())) < 0.01) // it should be equal
430 && (track.getCentroid() >= vert_bar_start_idx_)) { // specific case for the vertical bars
431 continue; // currently we can't do more here
432 } else if (((tracks_.at(idx)).getResidual() <
433 (tracks_.at(idx_comp)).getResidual() &&
434 (track.getCentroid() < vert_bar_start_idx_)) ||
435 ((tracks_.at(idx)).getResidualX() <
436 (tracks_.at(idx_comp)).getResidualX() &&
437 (track.getCentroid() >= vert_bar_start_idx_))) {
438 // next track (lower index) is a worse choice, remove its flag for
439 // keeping
440 keep_indices.at(idx_comp) = 0;
441 } else // prefer next track over current. remove current track's
442 // keep
443 // flag
444 keep_indices.at(idx) = 0;
445 /*}
446 else {
447 tracks_.erase(itNext);
448 // removeIdx.push_back(idx+1);
449 // we might see the same index two times in the loop in this
450 case, if there are three seeds sharing the same clusters
451 downstream.
452 // then the third only gets removed if it's even worse than
453 the second.
454 // one could deal with this with an extra overlap check. not
455 sure we will be in this situation any time soon though.
456 }*/
457 } // over matching/overlapping tracks
458 } // over tracks close enough to share constituents
459 } // over constructed tracks at other indices, to match
460 } // over constructed tracks
461
462 for (uint idx = 0; idx < tracks_.size(); idx++) {
463 if (verbose_ > 1) {
464 ldmx_log(debug) << "keep flag for idx " << idx << " is "
465 << keep_indices.at(idx);
466 }
467 if (keep_indices.at(idx)) { // this hasn't been flagged for removal
468
469 cleaned_tracks.push_back(tracks_.at(idx));
470
471 if (verbose_) {
472 ldmx_log(debug) << "After cleaning, keeping track at index " << idx
473 << ": Centroid = " << (tracks_.at(idx)).getCentroid()
474 << "; CentroidX = "
475 << (tracks_.at(idx)).getCentroidX()
476 << "; CentroidY = "
477 << (tracks_.at(idx)).getCentroidY()
478 << "; track PE = " << (tracks_.at(idx)).getPE()
479 << tracks_.at(idx);
480 }
481 } // if index flagged for keeping
482 } // over all (uniquely seeded) tracks in the event
483
484 if (verbose_) {
485 for (uint idx = 0; idx < tracks_.size(); idx++) {
486 ldmx_log(debug) << "Keeping track at index " << idx << ":"
487 << tracks_.at(idx);
488 }
489 }
490
491 if (verbose_) {
492 ldmx_log(debug) << "Running track x,y matching ";
493 }
494
495 if (cleaned_tracks.size() > 0) {
496 matchXYTracks(cleaned_tracks);
497 std::vector<ldmx::TrigScintTrack> matched_tracks =
498 cleaned_tracks; // don't know why this copying needs to happen but it
499 // does
500 // std::vector<ldmx::TrigScintTrack> matchXYTracks( cleanedTracks
501 //); std::vector<ldmx::TrigScintTrack> matchedTracks =
502 // matchXYTracks( cleanedTracks );
503 for (auto trk : matched_tracks) {
504 /* for (uint idx = 0; idx < tracks_.size(); idx++) {
505 if (verbose_ > 1) {
506 ldmx_log(debug) << "keep flag for idx " << idx << " is "
507 << keepIndices.at(idx);
508 }
509 if (keepIndices.at(idx)) { // this hasn't
510 been flagged for removal
511 //check if channel nb is above that of horizontal bars
512 if (tracks_.at(idx).getCentroid() >= vert_bar_start_idx_)
513 */
514 if (trk.getCentroid() >= vert_bar_start_idx_)
515 cleaned_tracks_x.push_back(trk); // acks_.at(idx));
516 else
517 cleaned_tracks_y.push_back(trk); // acks_.at(idx));
518 // cleanedTracksY.push_back(trk);
519 if (verbose_ > 1) {
520 float centr = trk.getCentroid(); // tracks_.at(idx).getCentroid(); //
521 std::string coll_str = centr >= vert_bar_start_idx_ ? "X" : "Y";
522 coll_str = output_collection_ + coll_str;
523 ldmx_log(debug) << "saving track with centroid " << centr
524 << " to output track collection " << coll_str;
525 }
526 // }
527 }
528 }
529
530 } // if there are clusters in all pads
531 else if (verbose_) {
532 ldmx_log(info)
533 << "Not all pads had clusters; (maybe) skipping tracking attempt";
534 }
535
536 if (verbose_) {
537 ldmx_log(debug) << "Done with tracking step. ";
538 }
539
540 event.add(output_collection_, cleaned_tracks);
541 // event.add(output_collection_, matchedTracks);
542
543 event.add(output_collection_ + "Y", cleaned_tracks_y);
544 event.add(output_collection_ + "X", cleaned_tracks_x);
545
546 tracks_.resize(0);
547
548 return;
549}
550
551ldmx::TrigScintTrack TrigScintTrackProducer::makeTrack(
552 std::vector<ldmx::TrigScintCluster> clusters) {
553 // for now let's keep a straight, unweighted centroid
554 // consider the possibility that at least one cluster has a centroid
555 // identically == 0. then we need to shift them by 1 if we want to do energy
556 // weighted track centroid later. but no need now
558 float centroid = 0;
559 float centroid_x = 0;
560 float centroid_y = 0;
561 float beam_efrac = 0;
562 float pe = 0;
563 for (uint i = 0; i < clusters.size(); i++) {
564 centroid += (clusters.at(i)).getCentroid();
565 centroid_x += (clusters.at(i)).getCentroidX();
566 centroid_y += (clusters.at(i)).getCentroidY();
567 tr.addConstituent(clusters.at(i));
568 beam_efrac += (clusters.at(i)).getBeamEfrac();
569 pe += (clusters.at(i)).getPE();
570 }
571 centroid /= clusters.size();
572 centroid_x /= clusters.size();
573 if (centroid >= vert_bar_start_idx_) {
574 if (verbose_) {
575 ldmx_log(debug)
576 << " -- In makeTrack made vertical bar track with centroid "
577 << centroid << " and y flag sum " << centroid_y;
578 // try commenting this to check if that helps with an out-of-bounds
579 // problem
580 // << " from clusters with y centroids";
581 // for (uint i = 0; i < clusters.size(); i++)
582 // ldmx_log(debug) << "\tpad " << i << ": centroidY "
583 // << (clusters.at(i)).getCentroidY();
584 }
585 // then the sum of centroid y is 0, 2, 4 or 6
586 // we have 4 divisions, so, the center of it should be divNb/8
587 // (or rather, that's where channel nBars/8 begins)
588 // and then a factor 2 for the zig-zag pattern
589 centroid_y = (centroid_y + 1) * 2 * n_bars_y_ / 8.;
590 // TODO: here we could instead just use quadrant indices 0-3 by dividing by
591 // 2 but that would mean that in the raw, x and y track centroidY would mean
592 // different things
593 if (verbose_) ldmx_log(debug) << " -- new centroidY = " << centroid_y;
594 } else
595 centroid_y /= clusters.size();
596
597 beam_efrac /= clusters.size();
598 pe /= clusters.size();
599
600 float residual = 0;
601 for (uint i = 0; i < clusters.size(); i++)
602 residual += ((clusters.at(i)).getCentroid() - centroid) *
603 ((clusters.at(i)).getCentroid() - centroid);
604 residual = sqrt(residual / clusters.size());
605
606 float residual_x = 0; // only for the vertical bars
607 if (centroid >= vert_bar_start_idx_) {
608 for (uint i = 0; i < clusters.size(); i++)
609 residual_x += ((clusters.at(i)).getCentroidX() - centroid_x) *
610 ((clusters.at(i)).getCentroidX() - centroid_x);
611 residual_x = sqrt(residual_x / clusters.size());
612 }
613
614 tr.setResidualX(residual_x);
615 tr.setCentroid(centroid);
616 tr.setCentroidX(centroid_x);
617 tr.setCentroidY(centroid_y);
618 tr.setResidual(residual);
619 tr.setBeamEfrac(beam_efrac);
620 tr.setPE(pe);
621
622 if (verbose_) {
623 ldmx_log(debug) << " -- In makeTrack made track with centroid "
624 << centroid << " and residual " << residual << " and pe "
625 << pe << " from clusters with centroids";
626 for (uint i = 0; i < clusters.size(); i++)
627 ldmx_log(debug) << "\tpad " << i << ": centroid "
628 << (clusters.at(i)).getCentroid();
629 }
630
631 return tr;
632}
633
634// std::vector<ldmx::TrigScintTrack> TrigScintTrackProducer::matchXYTracks(
635void TrigScintTrackProducer::matchXYTracks(
636 std::vector<ldmx::TrigScintTrack>& tracks) {
637 // map quadrant nb to track (can be multiple per quadrant)
638 std::multimap<int, int>
639 y_idx_quad_map; // key = quad, val = track index in collection
640 std::multimap<int, int> x_idx_quad_map;
641
642 std::multimap<int, ldmx::TrigScintTrack> y_quad_map;
643 std::multimap<int, ldmx::TrigScintTrack> x_quad_map;
644 // map track in quadrant back to index in entire track collection
645 // used for updating collection track variables
646 std::map<ldmx::TrigScintTrack, int> y_track_map;
647 std::map<ldmx::TrigScintTrack, int> x_track_map;
648
649 uint trk_idx = -1;
650 for (auto trk : tracks) {
651 trk_idx++;
652 // 1. get the y bar tracks with centroidX = -1
653 if (trk.getCentroidX() == -1) {
654 if (verbose_)
655 ldmx_log(debug) << " -- In matchXYTracks found y track at "
656 << trk.getCentroidY() << "; mapping to quad "
657 << (int)trk.getCentroidY() / (n_bars_y_ / 2)
658 << " with trk index " << trk_idx;
659 // 2. order them... or map them to quadrants. note that there are 2 layers
660 // so 2*n_bars_y_/4 channels per quadrant
661 y_quad_map.insert(
662 std::make_pair((int)(trk.getCentroidY() / (n_bars_y_ / 2)), trk));
663 y_track_map[trk] = trk_idx;
664 y_idx_quad_map.insert(
665 std::make_pair((int)(trk.getCentroidY() / (n_bars_y_ / 2)), trk_idx));
666
667 } else { // 3. get the remaining tracks (from vertical bars) and map them
668 // (back) to (middle of) quadrants
669 x_quad_map.insert(
670 std::make_pair((int)(trk.getCentroidY() / (n_bars_y_ / 2)), trk));
671 x_track_map[trk] = trk_idx;
672 x_idx_quad_map.insert(
673 std::make_pair((int)(trk.getCentroidY() / (n_bars_y_ / 2)), trk_idx));
674 if (verbose_)
675 ldmx_log(debug) << " -- In matchXYTracks found x track at (x,y) = ("
676 << trk.getCentroidX() << ", " << trk.getCentroidY()
677 << "); mapping to quad "
678 << (int)trk.getCentroidY() / (n_bars_y_ / 2)
679 << " with trk index " << trk_idx;
680 }
681 }
682
683 // 4a
684 //
685 // 1) here use the geometry? if we can assume perfect alignment we can take
686 // width and nBars and take nBars/2 as origin
687 // --- now do the matching ---
688
689 // if there is no useful matching to be done: these are the pad width wide
690 // numbers
691 float x0 = 0;
692 // this should be half the pad... could also set
693 // it to full beam spot width
694 float sx0 = fabs(x_start_);
695 float sx0_vert = fabs(bar_length_y_ / 2); // When there are no hits
696 // along the vertical bars
697
698 // y_start_ is half the pad, so this should be half a quadrant
699 float sy0 = fabs(y_start_) / 4.;
700
701 // assume at least one y track. will have to figure out if there is ever a
702 // reason to use an isolated x track in its place.
703 for (auto yitr = y_quad_map.begin(); yitr != y_quad_map.end(); ++yitr) {
704 int n_yin_quad = y_quad_map.count((*yitr).first);
705 int n_xin_quad = x_quad_map.count((*yitr).first);
706 float y{-9999.}, sy{-9999.}, x{-9999.}, x1{-9999.}, x2{-9999.}, sx1{-9999.},
707 sx2{-9999.}, y1{-9999.}, y2{-9999.}, sy1{-9999.}, sy2{-9999.};
708 // quad midpoint:
709 float y0 = (((*yitr).first * 8) * y_conv_factor_) + y_start_ + sy0;
710 float sx = 1. / 2 *
711 x_conv_factor_; // rely on x precision being one single bar
712 // width; always used unless x is undeterminable
713
714 // check all x first
715 // do the easiest first:
716 if (n_xin_quad == 0) { // then there's no hope of setting a better x here
717 // just use the beam spot width... and center of pad
718 x = x0;
719 sx = sx0_vert;
720 if (verbose_)
721 ldmx_log(debug) << "\t\t\t no x info in quad " << (*yitr).first
722 << "; will set x to middle of pad, pad half-width as "
723 "precision: set (x, sx)=("
724 << x << ", " << sx << ")";
725 } // 0 x tracks in quadrant
726 else if (n_xin_quad ==
727 1) { // slightly harder: 1 x track -- might be easy if
728 // it's just one y track; if several, need to
729 // think about overlaps. but in overlap case, just
730 // revert to setting x0 and sx0, when we know
731 auto xitr = x_quad_map.find((*yitr).first);
732 x = ((*xitr).second).getCentroidX() * x_conv_factor_ + x_start_;
733
734 if (verbose_)
735 ldmx_log(debug) << "\t\t\t 1 x in quad " << (*yitr).first
736 << ", getting (x, sx)=(" << x << ", " << sx << ")";
737 } // 1 x track in quadrant
738 else if (n_xin_quad == 2) { // finally if we have two tracks, get x1 and x2
739 // and decide later how to use them
740 // don't think we want to experiment with discerning three overlapping
741 // tracks, so not >= 2
742 // continue; //debugging: skip for now -- didn't help
743 auto xitr1 = x_quad_map.lower_bound((*yitr).first);
744 auto xitr2 = x_quad_map.upper_bound((*yitr).first);
745 xitr2--; // upper_bound points to next element
746
747 if (xitr1 != xitr2) { // should be true already but...
748 x1 = ((*xitr1).second).getCentroidX() * x_conv_factor_ + x_start_;
749 x2 = ((*xitr2).second).getCentroidX() * x_conv_factor_ + x_start_;
750 sx1 = x_conv_factor_ / 2.; // 1 bar width
751 sx2 = sx1;
752 x = (x1 + x2) / 2.;
753 // rely on x precision being one single pad width
754 sx = fabs(x1 - x2) /2;
755 if (verbose_)
756 ldmx_log(debug) << "\t\t -- 2 x in quad: setting y track x "
757 "coordinate to midpoint";
758 }
759 } // if 2 x tracks in quad
760
761 if (n_xin_quad >= 3) { // no implementaion made so far
762 x = x0;
763 sx = sx0;
764 if (verbose_)
765 ldmx_log(debug)
766 << "\t\t\t currently no x info assigned in ambiguous case of "
767 << n_xin_quad << "vertical bar track candidates in quad "
768 << (*yitr).first
769 << "; will set x to middle of pad, pad half-width as "
770 "precision: set (x, sx)=("
771 << x << ", " << sx << ")";
772 } // 3 x tracks in quadrant
773
774 // ok! over y:
775 // can skip 0 y case by construction
776 if (n_yin_quad == 1) { // we can already now tell what the y coordinate and
777 // its precision is
778 y = ((*yitr).second).getCentroidY() * y_conv_factor_ + y_start_;
779 sy = ((*yitr).second).getResidual() * y_conv_factor_;
780 // if all clusters lined up, assign
781 // precision of 1 bar width
782 if (sy == 0) sy = 1. / 2 * y_conv_factor_;
783
784 if (n_xin_quad <= 1) {
785 // 4. every quadrant which just has one of each --> done ;
786 // b) set the sx, sy of the x track now, using the residuals from the
787 // other b1) special case: no x tracks; then x, sx have been set above
788 if (n_xin_quad == 1) {
789 auto xidx = x_idx_quad_map.find((*yitr).first);
790 tracks.at((*xidx).second).setPosition(x, y);
791 tracks.at((*xidx).second).setSigmaXY(sx, sy);
792 }
793 if (verbose_)
794 ldmx_log(debug) << "\t\t\t in quad " << (*yitr).first
795 << ", set (x, y) = (" << x << ", " << y
796 << ") and (sx, sy) = " << sx << ", " << sy << ")";
797 auto yidx = y_idx_quad_map.find((*yitr).first);
798 tracks.at((*yidx).second).setPosition(x, y);
799 tracks.at((*yidx).second).setSigmaXY(sx, sy);
800 continue;
801 }
802 } // 1 y, 0 or 1 or 3+ x track in quadrant
803
804 if (verbose_)
805 ldmx_log(debug) << "\t\t in quad " << (*yitr).first
806 << ", not single x,y tracks: " << n_xin_quad
807 << " of x and " << n_yin_quad << " of y";
808
809 if (n_yin_quad == 2) { // let's start here and see if we can do >= 2 later
810 // here one could do sth to avoid checking the other y track again in the
811 // outermost loop over y
812 auto yitr1 = y_quad_map.lower_bound((*yitr).first);
813 auto yitr2 = y_quad_map.upper_bound((*yitr).first);
814 yitr2--; // back up once
815 y1 = ((*yitr1).second).getCentroidY() * y_conv_factor_ + y_start_;
816 y2 = ((*yitr2).second).getCentroidY() * y_conv_factor_ + y_start_;
817 sy1 = ((*yitr1).second).getResidual() * y_conv_factor_;
818 sy2 = ((*yitr2).second).getResidual() * y_conv_factor_;
819 if (sy1 == 0) sy1 = 1. / 2 * y_conv_factor_;
820 if (sy2 == 0) sy2 = 1. / 2 * y_conv_factor_;
821 y = (y1 + y2) / 2.;
822 sy = fabs(y1 - y2) / 2;
823 if (verbose_)
824 ldmx_log(debug)
825 << "\t\t -- 2 y in quad: setting x track y coordinate to midpoint";
826 } // 2y in quad
827
828 if ((n_xin_quad == 0 || n_xin_quad >= 3) &&
829 (n_yin_quad == 2)) { // not using the X tracks for now for >=3
830 if (n_xin_quad == 0) {
831 if (verbose_)
832 ldmx_log(debug) << "\t\t -- No x tracks but 2 y tracks in quad: "
833 "unusual behaviour";
834 }
835 auto yidx1 = y_idx_quad_map.lower_bound((*yitr).first);
836 auto yidx2 = y_idx_quad_map.upper_bound((*yitr).first);
837 yidx2--;
838 tracks.at((*yidx1).second).setPosition(x, y1);
839 tracks.at((*yidx1).second).setSigmaXY(sx, sy1);
840 tracks.at((*yidx2).second).setPosition(x, y2);
841 tracks.at((*yidx2).second).setSigmaXY(sx, sy2);
842 continue;
843 }
844
845 if (n_yin_quad == 1 &&
846 n_xin_quad == 2) { // don't think we want to experiment with discerning
847 // three overlapping tracks, so not >= 2
848
849 // first: set the y track coordinates to x = the mid of x tracks, y = y
850 // of y track
851 auto yidx = y_idx_quad_map.find((*yitr).first);
852 tracks.at((*yidx).second).setPosition(x, y);
853 tracks.at((*yidx).second).setSigmaXY(sx, sy);
854
855 int min_overlap_pe = 250;
856 if (((*yitr).second).getPE() < min_overlap_pe) {
857 // can't tell, really, that either of these belong to the y track. so.
858 // let them keep their own x coordinate but set y to quadrant midpoint,
859 // with uncertainty +/- half quadrant width (1/8 of pad height)
860 y = y0;
861 sy = sy0;
862 if (verbose_)
863 ldmx_log(debug) << "\t\t -- Can't tell which x track should be "
864 "matched to single y track. Setting both x track "
865 "coordinates to y quadrant value:";
866 } // if can't assume overlap
867 else if (verbose_)
868 ldmx_log(debug) << "\t\t -- Found large PE count ("
869 << ((*yitr).second).getPE() << " > " << min_overlap_pe
870 << "), suggesting overlap! Setting both x track "
871 "coordinates to y track value:";
872
873 // consider making two x tracks out if this one, and, anyway have to set
874 // their average as the y track x cocordinate
875 // EXPERIMENTAL : apply only to x tracks, which can be disregarded for
876 // electron counting
877 if (verbose_)
878 ldmx_log(debug) << "\t\t -- (x1, x2, y) = (" << x1 << ", " << x2
879 << ", " << y << ") and (sx1, sx2, sy) = " << sx1 << ", "
880 << sx2 << ", " << sy << ")";
881
882 // now set x track coordinates according to overlap check result
883 auto xidx1 = x_idx_quad_map.lower_bound((*yitr).first);
884 auto xidx2 = x_idx_quad_map.upper_bound((*yitr).first);
885 xidx2--; // upper_bound points to (last+1) element
886 tracks.at((*xidx1).second).setPosition(x1, y);
887 tracks.at((*xidx1).second).setSigmaXY(sx1, sy);
888 tracks.at((*xidx2).second).setPosition(x2, y);
889 tracks.at((*xidx2).second).setSigmaXY(sx2, sy);
890
891 } // 1 y, 2 x tracks in the quadrant
892 else if (n_yin_quad == 2 && n_xin_quad == 1) {
893 // 5b) if there are more y than x: could be an overlap
894
895 // first: set the x track coordinates to x = x of x track, y = the mid of
896 // y tracks
897 auto xidx = x_idx_quad_map.find((*yitr).first);
898 tracks.at((*xidx).second).setPosition(x, y);
899 tracks.at((*xidx).second).setSigmaXY(sx, sy);
900
901 auto xitr = x_quad_map.lower_bound((*yitr).first);
902 int min_overlap_pe = 300;
903 if (((*xitr).second).getPE() < min_overlap_pe) {
904 if (verbose_)
905 ldmx_log(debug)
906 << "\t\t just 1 x track with not-unusual PE in the quad -- can't "
907 "match; setting mid-point values for x ";
908 x = x0;
909 sx = sx0;
910 } // if can't assume overlap
911 else {
912 // consider making two x tracks out if this one, and, anyway have to set
913 // their average as the y track x cocordinate
914 // EXPERIMENTAL : apply only to x tracks, which can be disregarded for
915 // electron counting
916 if (verbose_)
917 ldmx_log(debug) << "\t\t -- Found large PE count ("
918 << ((*xitr).second).getPE() << " > " << min_overlap_pe
919 << ") in x track, suggesting overlap! Setting both y "
920 "track coordinates to x track value:";
921 } // if can assume overlap
922 if (verbose_)
923 ldmx_log(debug) << "\t\t -- (x, y1, y2) = (" << x << ", " << y1 << ", "
924 << y2 << ") and (sx, sy1, sy2) = " << sx << ", " << sy1
925 << ", " << sy2 << ")";
926
927 auto yidx1 = y_idx_quad_map.lower_bound((*yitr).first);
928 auto yidx2 = y_idx_quad_map.upper_bound((*yitr).first);
929 yidx2--; // upper_bound points to next element
930 tracks.at((*yidx1).second).setPosition(x, y1);
931 tracks.at((*yidx1).second).setSigmaXY(sx, sy1);
932 tracks.at((*yidx2).second).setPosition(x, y2);
933 tracks.at((*yidx2).second).setSigmaXY(sx, sy2);
934
935 } // 2 y and 1 x track in quad
936 else if (n_yin_quad == 2 && n_xin_quad == 2) {
937 // MIDPONTS ALL OVER!
938 auto xidx1 = x_idx_quad_map.lower_bound((*yitr).first);
939 auto xidx2 = x_idx_quad_map.upper_bound((*yitr).first);
940 xidx2--;
941 auto yidx1 = y_idx_quad_map.lower_bound((*yitr).first);
942 auto yidx2 = y_idx_quad_map.upper_bound((*yitr).first);
943 yidx2--;
944
945 if (y_idx_quad_map.find((*yitr).first) == y_idx_quad_map.end())
946 ldmx_log(error) << "The two y tracks in the same quadrant at "
947 << (*yitr).first
948 << " appear to not be found in the y track map! "
949 "investigate. Note that yidx1.first = "
950 << (*yidx1).first
951 << " and yidx2.first = " << (*yidx2).first;
952 else {
953 tracks.at((*xidx1).second).setPosition(x1, y);
954 tracks.at((*xidx1).second).setSigmaXY(sx1, sy);
955 tracks.at((*xidx2).second).setPosition(x2, y);
956 tracks.at((*xidx2).second).setSigmaXY(sx2, sy);
957
958 tracks.at((*yidx1).second).setPosition(x, y1);
959 tracks.at((*yidx1).second).setSigmaXY(sx, sy1);
960 tracks.at((*yidx2).second).setPosition(x, y2);
961 tracks.at((*yidx2).second).setSigmaXY(sx, sy2);
962
963 if (verbose_)
964 ldmx_log(debug) << "\t\t -- in a 2 x 2 situaiton; midpoint y: " << y
965 << " for both x tracks, midpoint x: " << x
966 << " for both y tracks";
967 }
968 } // if 2 y, 2 x tracks
969
970 if (n_xin_quad > 2) {
971 if (verbose_)
972 ldmx_log(debug) << "\t\t -*-*-*- more than 2 x tracks in the same quad "
973 "-- nothing done about the x,y coordinates in this "
974 "situation -- implement if needed!!";
975 }
976 if (n_yin_quad > 2) {
977 if (verbose_)
978 ldmx_log(debug) << "\t\t -*-*-*- more than 2 y tracks in the same quad "
979 "-- nothing done about the x,y coordinates in this "
980 "situation -- implement if needed!!";
981 }
982
983 } // over y tracks
984
985 y_quad_map.clear();
986 x_quad_map.clear();
987
988 // return tracks;
989}
990
992 ldmx_log(debug) << "Process starts!";
993
994 return;
995}
996
998 ldmx_log(debug) << "Process ends!";
999
1000 return;
1001}
1002
1003} // namespace trigscint
#define DECLARE_PRODUCER(CLASS)
Macro which allows the framework to construct a producer given its name during configuration.
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:105
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 cluster information from the trigger scintillator pads.
Represents a track of trigger scintillator clusters.
void setCentroidX(float centroid)
Set the x centroid of the track.
void setResidual(float resid)
Set the detector ID residual of the track.
float getCentroidX() const
Get the x centroid of the track.
void setCentroidY(float centroid)
Set the y centroid of the track.
void setPE(float pe)
Set the average cluster pe of the track.
float getCentroid() const
Get the detector ID centroid of the track.
void addConstituent(TrigScintCluster cl)
Add a cluster to the list of track constituents.
void setCentroid(float centroid)
Set the detector ID centroid of the track.
void setBeamEfrac(float e)
Set beam energy fraction of hit.
std::vector< ldmx::TrigScintCluster > getConstituents() const
Get the cluster constituents of the track.
void setResidualX(float resid)
Set the x residual of the track.
float getCentroidY() const
Get the y centroid of the track.
making tracks from trigger scintillator clusters
void configure(framework::config::Parameters &ps) override
Callback for the EventProcessor to configure itself from the given set of parameters.
void onProcessEnd() override
Callback for the EventProcessor to take any necessary action when the processing of events finishes,...
void produce(framework::Event &event) override
Process the event and put new data products into it.
void onProcessStart() override
Callback for the EventProcessor to take any necessary action when the processing of events starts,...