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