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 +
76 "' not found! Make sure it's in the directory from "
77 "which just is executed!");
78 }
79
80 float a, b, c;
81
82 while (file >> a >> b >> c) {
83 float p1 = a;
84 float p2 = b;
85 float p3 = c;
86 lut_.insert({p1, p2, p3});
87 }
88
89 ldmx_log(info) << "Loaded LUT with size: " << lut_.size();
90 }
91
92 return;
93}
94
96 // parameters.
97 // one pad cluster collection to use as seed
98 // a vector with the other two
99 // a maximum distance between seed centroid and other pad clusters
100 // allowed to belong to the same track
101 // an output collection name a verbosity controller
102
103 if (verbose_) {
104 ldmx_log(debug)
105 << "TrigScintTrackProducer: produce() starts! Event number: "
106 << event.getEventHeader().getEventNumber();
107 }
108 if (!event.exists(seeding_collection_, pass_name_)) {
109 ldmx_log(info) << "No collection called " << seeding_collection_
110 << "; skipping event";
111 // << "; still, not skipping event";
112 return;
113 }
114
115 if (!event.exists(seeding_collection_, pass_name_)) {
116 ldmx_log(info) << "No collection called " << seeding_collection_
117 << "; skipping event";
118 return;
119 }
120 const auto seeds{event.getCollection<ldmx::TrigScintCluster>(
121 seeding_collection_, pass_name_)};
122 uint num_seeds = seeds.size();
123
124 if (verbose_) {
125 ldmx_log(debug) << "Got track seeding cluster collection "
126 << seeding_collection_ << " with " << num_seeds
127 << " entries ";
128 }
129
130 if (!event.exists(input_collections_.at(0), pass_name_)) {
131 ldmx_log(info) << "No collection called " << input_collections_.at(0)
132 << "; skipping event";
133 // << "; still, not skipping event";
134
135 return;
136 }
137 const auto clusters_pad1{event.getCollection<ldmx::TrigScintCluster>(
138 input_collections_.at(0), pass_name_)};
139
140 if (!event.exists(input_collections_.at(1), pass_name_)) {
141 ldmx_log(info) << "No collection called "
142 << input_collections_.at(1)
143 // << "; still, not skipping event";
144 << "; skipping event";
145 std::vector<ldmx::TrigScintTrack> empty{};
146 event.add(output_collection_, empty);
147 return;
148 }
149
150 const auto clusters_pad2{event.getCollection<ldmx::TrigScintCluster>(
151 input_collections_.at(1), pass_name_)};
152
153 if (verbose_) {
154 ldmx_log(debug) << "Got the other two pad collections:"
155 << input_collections_.at(0) << " with "
156 << clusters_pad1.size() << " entries, and "
157 << input_collections_.at(1) << " with "
158 << clusters_pad2.size() << " entries.";
159 }
160 std::vector<ldmx::TrigScintTrack> cleaned_tracks;
161 std::vector<ldmx::TrigScintTrack> cleaned_tracks_y;
162 std::vector<ldmx::TrigScintTrack> cleaned_tracks_x;
163
164 // loop over the clusters in the seeding pad collection, if there are clusters
165 // in all pads
166 // bool skipDn = false;
167 if (num_seeds && clusters_pad1.size()) {
168 // could check this explicitly here: and then just get out of all checks on
169 // the dn pad immediately
170 // if (! clusters_pad2.size())
171 // skipDn = true ;
172 for (const auto& seed : seeds) {
173 // for each seed, search through the other two pads to match all clusters
174 // with centroids within tolerance to tracks
175 float centroid = seed.getCentroid();
176
177 std::vector<ldmx::TrigScintTrack> track_candidates;
178
179 if (verbose_ > 1) {
180 ldmx_log(debug) << "Got seed with centroid " << centroid;
181 }
182
183 // reset for each seed
184 // bool madeTrack = false;
185
186 if (lut_tracking_) { // if using LUT method
187 for (const auto& cluster1 : clusters_pad1) {
188 for (const auto& cluster2 : clusters_pad2) {
189 float seed_bin = seed.getCentroid();
190 float pad1_bin = cluster1.getCentroid();
191 float pad2_bin = cluster2.getCentroid();
192
193 LUTKey key{seed_bin, pad1_bin,
194 pad2_bin}; // LUTKey defined in header file
195
196 if (lut_.find(key) != lut_.end()) {
197 std::vector<ldmx::TrigScintCluster> three_cluster_vec = {
198 seed, cluster1, cluster2};
199
200 ldmx::TrigScintTrack track = makeTrack(three_cluster_vec);
201 track_candidates.push_back(track);
202 }
203 }
204 }
205
206 }
207
208 else {
209 for (const auto& cluster1 : clusters_pad1) {
210 if (verbose_ > 1) {
211 ldmx_log(debug) << "\tGot pad1 cluster with centroid "
212 << cluster1.getCentroid();
213 }
214 if ((fabs(cluster1.getCentroid() - centroid) < max_delta_ &&
215 centroid < vert_bar_start_idx_) ||
216 (centroid >= vert_bar_start_idx_ &&
217 cluster1.getCentroid() >= vert_bar_start_idx_ &&
218 seed.getCentroidX() == cluster1.getCentroidX())) {
219 // use geometry y overlap scheme to see if this is really a match in
220 // x should be done in a map
221
222 if (centroid >= vert_bar_start_idx_ &&
223 seed.getCentroidY() < cluster1.getCentroidY()) {
224 // impossible combination
225 ldmx_log(warn) << "\tSkipping impossible x cluster combination "
226 "with y flags (tag up) ("
227 << seed.getCentroidY() << " "
228 << cluster1.getCentroidY() << ")";
229 continue;
230 }
231
232 // else: first (possible) match! loop through next pad too
233
234 if (verbose_ > 1) {
235 ldmx_log(debug) << "\t\tIt is close enough!. Check pad2";
236 }
237
238 // try making third pad clusters an optional part of track
239
240 std::vector<ldmx::TrigScintCluster> cluster_vec = {seed, cluster1};
241
242 bool has_match_dn = false;
243
244 for (const auto& cluster2 : clusters_pad2) {
245 if (verbose_ > 1) {
246 ldmx_log(debug) << "\tGot pad2 cluster with centroid "
247 << cluster2.getCentroid();
248 }
249
250 if ((fabs(cluster2.getCentroid() - centroid) < max_delta_ &&
251 centroid < vert_bar_start_idx_) ||
252 (centroid >= vert_bar_start_idx_ &&
253 cluster2.getCentroid() >= vert_bar_start_idx_ &&
254 fabs(seed.getCentroidX() - cluster2.getCentroidX()) <=
255 max_delta_vert_)) {
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_) &&
391 (track.getCentroid() <
392 vert_bar_start_idx_)) // for the horizontal bars
393 || ((fabs(track.getCentroidX() - next_track.getCentroidX()) <
394 2 * max_delta_vert_) &&
395 (track.getCentroidY() == next_track.getCentroidY()) &&
396 (track.getCentroid() >= vert_bar_start_idx_))) {
397 // and for the vertical bars, check if they are in the same quad and
398 // close enough
399 std::vector<ldmx::TrigScintCluster> consts_1 =
400 track.getConstituents();
401 std::vector<ldmx::TrigScintCluster> consts_2 =
402 next_track.getConstituents();
403 if (verbose_ > 1)
404 ldmx_log(debug)
405 << "In track disambiguation loop, got the two tracks, "
406 "with nConstituents "
407 << consts_1.size() << " and " << consts_2.size()
408 << ", respectively. ";
409 // let's do "if either cluster is shared" right now... but could also
410 // have it settable to use a stricter cut: an AND
411 if (((consts_1[1].getCentroid() == consts_2[1].getCentroid() ||
412 ((consts_1.size() > 2) && (consts_2.size() > 2) &&
413 (consts_1[2].getCentroid() == consts_2[2].getCentroid()))) &&
414 (track.getCentroid() < vert_bar_start_idx_)) ||
415 // horizontal bars
416 ((track.getCentroid() >= vert_bar_start_idx_) &&
417 ((consts_1[1].getCentroidX() == consts_2[1].getCentroidX()) ||
418 (consts_1[2].getCentroidX() == consts_2[2].getCentroidX()) ||
419 (consts_1[0].getCentroidX() ==
420 consts_2[0].getCentroidX())))) { // and vertical bars
421
422 if (verbose_ > 1) {
423 ldmx_log(debug) << "Found overlap! Tracks at index " << idx
424 << " and " << idx_comp;
425 ldmx_log(trace) << tracks_.at(idx);
426 ldmx_log(trace) << tracks_.at(idx_comp);
427 }
428
429 if (((fabs((tracks_.at(idx)).getResidualX() -
430 (tracks_.at(idx_comp)).getResidualX())) <
431 0.01) // it should be equal
432 &&
433 (track.getCentroid() >=
434 vert_bar_start_idx_)) { // specific case for the vertical bars
435 continue; // currently we can't do more here
436 } else if (((tracks_.at(idx)).getResidual() <
437 (tracks_.at(idx_comp)).getResidual() &&
438 (track.getCentroid() < vert_bar_start_idx_)) ||
439 ((tracks_.at(idx)).getResidualX() <
440 (tracks_.at(idx_comp)).getResidualX() &&
441 (track.getCentroid() >= vert_bar_start_idx_))) {
442 // next track (lower index) is a worse choice, remove its flag for
443 // keeping
444 keep_indices.at(idx_comp) = 0;
445 } else // prefer next track over current. remove current track's
446 // keep
447 // flag
448 keep_indices.at(idx) = 0;
449 /*}
450 else {
451 tracks_.erase(itNext);
452 // removeIdx.push_back(idx+1);
453 // we might see the same index two times in the loop in this
454 case, if there are three seeds sharing the same clusters
455 downstream.
456 // then the third only gets removed if it's even worse than
457 the second.
458 // one could deal with this with an extra overlap check. not
459 sure we will be in this situation any time soon though.
460 }*/
461 } // over matching/overlapping tracks
462 } // over tracks close enough to share constituents
463 } // over constructed tracks at other indices, to match
464 } // over constructed tracks
465
466 for (uint idx = 0; idx < tracks_.size(); idx++) {
467 if (verbose_ > 1) {
468 ldmx_log(debug) << "keep flag for idx " << idx << " is "
469 << keep_indices.at(idx);
470 }
471 if (keep_indices.at(idx)) { // this hasn't been flagged for removal
472
473 cleaned_tracks.push_back(tracks_.at(idx));
474
475 if (verbose_) {
476 ldmx_log(debug) << "After cleaning, keeping track at index " << idx
477 << ": Centroid = " << (tracks_.at(idx)).getCentroid()
478 << "; CentroidX = "
479 << (tracks_.at(idx)).getCentroidX()
480 << "; CentroidY = "
481 << (tracks_.at(idx)).getCentroidY()
482 << "; track PE = " << (tracks_.at(idx)).getPE()
483 << tracks_.at(idx);
484 }
485 } // if index flagged for keeping
486 } // over all (uniquely seeded) tracks in the event
487
488 if (verbose_) {
489 for (uint idx = 0; idx < tracks_.size(); idx++) {
490 ldmx_log(debug) << "Keeping track at index " << idx << ":"
491 << tracks_.at(idx);
492 }
493 }
494
495 if (verbose_) {
496 ldmx_log(debug) << "Running track x,y matching ";
497 }
498
499 if (cleaned_tracks.size() > 0) {
500 matchXYTracks(cleaned_tracks);
501 std::vector<ldmx::TrigScintTrack> matched_tracks =
502 cleaned_tracks; // don't know why this copying needs to happen but it
503 // does
504 // std::vector<ldmx::TrigScintTrack> matchXYTracks( cleanedTracks
505 //); std::vector<ldmx::TrigScintTrack> matchedTracks =
506 // matchXYTracks( cleanedTracks );
507 for (auto trk : matched_tracks) {
508 /* for (uint idx = 0; idx < tracks_.size(); idx++) {
509 if (verbose_ > 1) {
510 ldmx_log(debug) << "keep flag for idx " << idx << " is "
511 << keepIndices.at(idx);
512 }
513 if (keepIndices.at(idx)) { // this hasn't
514 been flagged for removal
515 //check if channel nb is above that of horizontal bars
516 if (tracks_.at(idx).getCentroid() >= vert_bar_start_idx_)
517 */
518 if (trk.getCentroid() >= vert_bar_start_idx_)
519 cleaned_tracks_x.push_back(trk); // acks_.at(idx));
520 else
521 cleaned_tracks_y.push_back(trk); // acks_.at(idx));
522 // cleanedTracksY.push_back(trk);
523 if (verbose_ > 1) {
524 float centr = trk.getCentroid(); // tracks_.at(idx).getCentroid(); //
525 std::string coll_str = centr >= vert_bar_start_idx_ ? "X" : "Y";
526 coll_str = output_collection_ + coll_str;
527 ldmx_log(debug) << "saving track with centroid " << centr
528 << " to output track collection " << coll_str;
529 }
530 // }
531 }
532 }
533
534 } // if there are clusters in all pads
535 else if (verbose_) {
536 ldmx_log(info)
537 << "Not all pads had clusters; (maybe) skipping tracking attempt";
538 }
539
540 if (verbose_) {
541 ldmx_log(debug) << "Done with tracking step. ";
542 }
543
544 event.add(output_collection_, cleaned_tracks);
545 // event.add(output_collection_, matchedTracks);
546
547 event.add(output_collection_ + "Y", cleaned_tracks_y);
548 event.add(output_collection_ + "X", cleaned_tracks_x);
549
550 tracks_.resize(0);
551
552 return;
553}
554
555ldmx::TrigScintTrack TrigScintTrackProducer::makeTrack(
556 std::vector<ldmx::TrigScintCluster> clusters) {
557 // for now let's keep a straight, unweighted centroid
558 // consider the possibility that at least one cluster has a centroid
559 // identically == 0. then we need to shift them by 1 if we want to do energy
560 // weighted track centroid later. but no need now
562 float centroid = 0;
563 float centroid_x = 0;
564 float centroid_y = 0;
565 float beam_efrac = 0;
566 float pe = 0;
567 for (uint i = 0; i < clusters.size(); i++) {
568 centroid += (clusters.at(i)).getCentroid();
569 centroid_x += (clusters.at(i)).getCentroidX();
570 centroid_y += (clusters.at(i)).getCentroidY();
571 tr.addConstituent(clusters.at(i));
572 beam_efrac += (clusters.at(i)).getBeamEfrac();
573 pe += (clusters.at(i)).getPE();
574 }
575 centroid /= clusters.size();
576 centroid_x /= clusters.size();
577 if (centroid >= vert_bar_start_idx_) {
578 if (verbose_) {
579 ldmx_log(debug)
580 << " -- In makeTrack made vertical bar track with centroid "
581 << centroid << " and y flag sum " << centroid_y;
582 // try commenting this to check if that helps with an out-of-bounds
583 // problem
584 // << " from clusters with y centroids";
585 // for (uint i = 0; i < clusters.size(); i++)
586 // ldmx_log(debug) << "\tpad " << i << ": centroidY "
587 // << (clusters.at(i)).getCentroidY();
588 }
589 // then the sum of centroid y is 0, 2, 4 or 6
590 // we have 4 divisions, so, the center of it should be divNb/8
591 // (or rather, that's where channel nBars/8 begins)
592 // and then a factor 2 for the zig-zag pattern
593 centroid_y = (centroid_y + 1) * 2 * n_bars_y_ / 8.;
594 // TODO: here we could instead just use quadrant indices 0-3 by dividing by
595 // 2 but that would mean that in the raw, x and y track centroidY would mean
596 // different things
597 if (verbose_) ldmx_log(debug) << " -- new centroidY = " << centroid_y;
598 } else
599 centroid_y /= clusters.size();
600
601 beam_efrac /= clusters.size();
602 pe /= clusters.size();
603
604 float residual = 0;
605 for (uint i = 0; i < clusters.size(); i++)
606 residual += ((clusters.at(i)).getCentroid() - centroid) *
607 ((clusters.at(i)).getCentroid() - centroid);
608 residual = sqrt(residual / clusters.size());
609
610 float residual_x = 0; // only for the vertical bars
611 if (centroid >= vert_bar_start_idx_) {
612 for (uint i = 0; i < clusters.size(); i++)
613 residual_x += ((clusters.at(i)).getCentroidX() - centroid_x) *
614 ((clusters.at(i)).getCentroidX() - centroid_x);
615 residual_x = sqrt(residual_x / clusters.size());
616 }
617
618 tr.setResidualX(residual_x);
619 tr.setCentroid(centroid);
620 tr.setCentroidX(centroid_x);
621 tr.setCentroidY(centroid_y);
622 tr.setResidual(residual);
623 tr.setBeamEfrac(beam_efrac);
624 tr.setPE(pe);
625
626 if (verbose_) {
627 ldmx_log(debug) << " -- In makeTrack made track with centroid "
628 << centroid << " and residual " << residual << " and pe "
629 << pe << " from clusters with centroids";
630 for (uint i = 0; i < clusters.size(); i++)
631 ldmx_log(debug) << "\tpad " << i << ": centroid "
632 << (clusters.at(i)).getCentroid();
633 }
634
635 return tr;
636}
637
638// std::vector<ldmx::TrigScintTrack> TrigScintTrackProducer::matchXYTracks(
639void TrigScintTrackProducer::matchXYTracks(
640 std::vector<ldmx::TrigScintTrack>& tracks) {
641 // map quadrant nb to track (can be multiple per quadrant)
642 std::multimap<int, int>
643 y_idx_quad_map; // key = quad, val = track index in collection
644 std::multimap<int, int> x_idx_quad_map;
645
646 std::multimap<int, ldmx::TrigScintTrack> y_quad_map;
647 std::multimap<int, ldmx::TrigScintTrack> x_quad_map;
648 // map track in quadrant back to index in entire track collection
649 // used for updating collection track variables
650 std::map<ldmx::TrigScintTrack, int> y_track_map;
651 std::map<ldmx::TrigScintTrack, int> x_track_map;
652
653 uint trk_idx = -1;
654 for (auto trk : tracks) {
655 trk_idx++;
656 // 1. get the y bar tracks with centroidX = -1
657 if (trk.getCentroidX() == -1) {
658 if (verbose_)
659 ldmx_log(debug) << " -- In matchXYTracks found y track at "
660 << trk.getCentroidY() << "; mapping to quad "
661 << (int)trk.getCentroidY() / (n_bars_y_ / 2)
662 << " with trk index " << trk_idx;
663 // 2. order them... or map them to quadrants. note that there are 2 layers
664 // so 2*n_bars_y_/4 channels per quadrant
665 y_quad_map.insert(
666 std::make_pair((int)(trk.getCentroidY() / (n_bars_y_ / 2)), trk));
667 y_track_map[trk] = trk_idx;
668 y_idx_quad_map.insert(
669 std::make_pair((int)(trk.getCentroidY() / (n_bars_y_ / 2)), trk_idx));
670
671 } else { // 3. get the remaining tracks (from vertical bars) and map them
672 // (back) to (middle of) quadrants
673 x_quad_map.insert(
674 std::make_pair((int)(trk.getCentroidY() / (n_bars_y_ / 2)), trk));
675 x_track_map[trk] = trk_idx;
676 x_idx_quad_map.insert(
677 std::make_pair((int)(trk.getCentroidY() / (n_bars_y_ / 2)), trk_idx));
678 if (verbose_)
679 ldmx_log(debug) << " -- In matchXYTracks found x track at (x,y) = ("
680 << trk.getCentroidX() << ", " << trk.getCentroidY()
681 << "); mapping to quad "
682 << (int)trk.getCentroidY() / (n_bars_y_ / 2)
683 << " with trk index " << trk_idx;
684 }
685 }
686
687 // 4a
688 //
689 // 1) here use the geometry? if we can assume perfect alignment we can take
690 // width and nBars and take nBars/2 as origin
691 // --- now do the matching ---
692
693 // if there is no useful matching to be done: these are the pad width wide
694 // numbers
695 float x0 = 0;
696 // this should be half the pad... could also set
697 // it to full beam spot width
698 float sx0 = fabs(x_start_);
699 float sx0_vert = fabs(bar_length_y_ / 2); // When there are no hits
700 // along the vertical bars
701
702 // y_start_ is half the pad, so this should be half a quadrant
703 float sy0 = fabs(y_start_) / 4.;
704
705 // assume at least one y track. will have to figure out if there is ever a
706 // reason to use an isolated x track in its place.
707 for (auto yitr = y_quad_map.begin(); yitr != y_quad_map.end(); ++yitr) {
708 int n_yin_quad = y_quad_map.count((*yitr).first);
709 int n_xin_quad = x_quad_map.count((*yitr).first);
710 float y{-9999.}, sy{-9999.}, x{-9999.}, x1{-9999.}, x2{-9999.}, sx1{-9999.},
711 sx2{-9999.}, y1{-9999.}, y2{-9999.}, sy1{-9999.}, sy2{-9999.};
712 // quad midpoint:
713 float y0 = (((*yitr).first * 8) * y_conv_factor_) + y_start_ + sy0;
714 float sx = 1. / 2 *
715 x_conv_factor_; // rely on x precision being one single bar
716 // width; always used unless x is undeterminable
717
718 // check all x first
719 // do the easiest first:
720 if (n_xin_quad == 0) { // then there's no hope of setting a better x here
721 // just use the beam spot width... and center of pad
722 x = x0;
723 sx = sx0_vert;
724 if (verbose_)
725 ldmx_log(debug) << "\t\t\t no x info in quad " << (*yitr).first
726 << "; will set x to middle of pad, pad half-width as "
727 "precision: set (x, sx)=("
728 << x << ", " << sx << ")";
729 } // 0 x tracks in quadrant
730 else if (n_xin_quad ==
731 1) { // slightly harder: 1 x track -- might be easy if
732 // it's just one y track; if several, need to
733 // think about overlaps. but in overlap case, just
734 // revert to setting x0 and sx0, when we know
735 auto xitr = x_quad_map.find((*yitr).first);
736 x = ((*xitr).second).getCentroidX() * x_conv_factor_ + x_start_;
737
738 if (verbose_)
739 ldmx_log(debug) << "\t\t\t 1 x in quad " << (*yitr).first
740 << ", getting (x, sx)=(" << x << ", " << sx << ")";
741 } // 1 x track in quadrant
742 else if (n_xin_quad == 2) { // finally if we have two tracks, get x1 and x2
743 // and decide later how to use them
744 // don't think we want to experiment with discerning three overlapping
745 // tracks, so not >= 2
746 // continue; //debugging: skip for now -- didn't help
747 auto xitr1 = x_quad_map.lower_bound((*yitr).first);
748 auto xitr2 = x_quad_map.upper_bound((*yitr).first);
749 xitr2--; // upper_bound points to next element
750
751 if (xitr1 != xitr2) { // should be true already but...
752 x1 = ((*xitr1).second).getCentroidX() * x_conv_factor_ + x_start_;
753 x2 = ((*xitr2).second).getCentroidX() * x_conv_factor_ + x_start_;
754 sx1 = x_conv_factor_ / 2.; // 1 bar width
755 sx2 = sx1;
756 x = (x1 + x2) / 2.;
757 // rely on x precision being one single pad width
758 sx = fabs(x1 - x2) / 2;
759 if (verbose_)
760 ldmx_log(debug) << "\t\t -- 2 x in quad: setting y track x "
761 "coordinate to midpoint";
762 }
763 } // if 2 x tracks in quad
764
765 if (n_xin_quad >= 3) { // no implementaion made so far
766 x = x0;
767 sx = sx0;
768 if (verbose_)
769 ldmx_log(debug)
770 << "\t\t\t currently no x info assigned in ambiguous case of "
771 << n_xin_quad << "vertical bar track candidates in quad "
772 << (*yitr).first
773 << "; will set x to middle of pad, pad half-width as "
774 "precision: set (x, sx)=("
775 << x << ", " << sx << ")";
776 } // 3 x tracks in quadrant
777
778 // ok! over y:
779 // can skip 0 y case by construction
780 if (n_yin_quad == 1) { // we can already now tell what the y coordinate and
781 // its precision is
782 y = ((*yitr).second).getCentroidY() * y_conv_factor_ + y_start_;
783 sy = ((*yitr).second).getResidual() * y_conv_factor_;
784 // if all clusters lined up, assign
785 // precision of 1 bar width
786 if (sy == 0) sy = 1. / 2 * y_conv_factor_;
787
788 if (n_xin_quad <= 1) {
789 // 4. every quadrant which just has one of each --> done ;
790 // b) set the sx, sy of the x track now, using the residuals from the
791 // other b1) special case: no x tracks; then x, sx have been set above
792 if (n_xin_quad == 1) {
793 auto xidx = x_idx_quad_map.find((*yitr).first);
794 tracks.at((*xidx).second).setPosition(x, y);
795 tracks.at((*xidx).second).setSigmaXY(sx, sy);
796 }
797 if (verbose_)
798 ldmx_log(debug) << "\t\t\t in quad " << (*yitr).first
799 << ", set (x, y) = (" << x << ", " << y
800 << ") and (sx, sy) = " << sx << ", " << sy << ")";
801 auto yidx = y_idx_quad_map.find((*yitr).first);
802 tracks.at((*yidx).second).setPosition(x, y);
803 tracks.at((*yidx).second).setSigmaXY(sx, sy);
804 continue;
805 }
806 } // 1 y, 0 or 1 or 3+ x track in quadrant
807
808 if (verbose_)
809 ldmx_log(debug) << "\t\t in quad " << (*yitr).first
810 << ", not single x,y tracks: " << n_xin_quad
811 << " of x and " << n_yin_quad << " of y";
812
813 if (n_yin_quad == 2) { // let's start here and see if we can do >= 2 later
814 // here one could do sth to avoid checking the other y track again in the
815 // outermost loop over y
816 auto yitr1 = y_quad_map.lower_bound((*yitr).first);
817 auto yitr2 = y_quad_map.upper_bound((*yitr).first);
818 yitr2--; // back up once
819 y1 = ((*yitr1).second).getCentroidY() * y_conv_factor_ + y_start_;
820 y2 = ((*yitr2).second).getCentroidY() * y_conv_factor_ + y_start_;
821 sy1 = ((*yitr1).second).getResidual() * y_conv_factor_;
822 sy2 = ((*yitr2).second).getResidual() * y_conv_factor_;
823 if (sy1 == 0) sy1 = 1. / 2 * y_conv_factor_;
824 if (sy2 == 0) sy2 = 1. / 2 * y_conv_factor_;
825 y = (y1 + y2) / 2.;
826 sy = fabs(y1 - y2) / 2;
827 if (verbose_)
828 ldmx_log(debug)
829 << "\t\t -- 2 y in quad: setting x track y coordinate to midpoint";
830 } // 2y in quad
831
832 if ((n_xin_quad == 0 || n_xin_quad >= 3) &&
833 (n_yin_quad == 2)) { // not using the X tracks for now for >=3
834 if (n_xin_quad == 0) {
835 if (verbose_)
836 ldmx_log(debug) << "\t\t -- No x tracks but 2 y tracks in quad: "
837 "unusual behaviour";
838 }
839 auto yidx1 = y_idx_quad_map.lower_bound((*yitr).first);
840 auto yidx2 = y_idx_quad_map.upper_bound((*yitr).first);
841 yidx2--;
842 tracks.at((*yidx1).second).setPosition(x, y1);
843 tracks.at((*yidx1).second).setSigmaXY(sx, sy1);
844 tracks.at((*yidx2).second).setPosition(x, y2);
845 tracks.at((*yidx2).second).setSigmaXY(sx, sy2);
846 continue;
847 }
848
849 if (n_yin_quad == 1 &&
850 n_xin_quad == 2) { // don't think we want to experiment with discerning
851 // three overlapping tracks, so not >= 2
852
853 // first: set the y track coordinates to x = the mid of x tracks, y = y
854 // of y track
855 auto yidx = y_idx_quad_map.find((*yitr).first);
856 tracks.at((*yidx).second).setPosition(x, y);
857 tracks.at((*yidx).second).setSigmaXY(sx, sy);
858
859 int min_overlap_pe = 250;
860 if (((*yitr).second).getPE() < min_overlap_pe) {
861 // can't tell, really, that either of these belong to the y track. so.
862 // let them keep their own x coordinate but set y to quadrant midpoint,
863 // with uncertainty +/- half quadrant width (1/8 of pad height)
864 y = y0;
865 sy = sy0;
866 if (verbose_)
867 ldmx_log(debug) << "\t\t -- Can't tell which x track should be "
868 "matched to single y track. Setting both x track "
869 "coordinates to y quadrant value:";
870 } // if can't assume overlap
871 else if (verbose_)
872 ldmx_log(debug) << "\t\t -- Found large PE count ("
873 << ((*yitr).second).getPE() << " > " << min_overlap_pe
874 << "), suggesting overlap! Setting both x track "
875 "coordinates to y track value:";
876
877 // consider making two x tracks out if this one, and, anyway have to set
878 // their average as the y track x cocordinate
879 // EXPERIMENTAL : apply only to x tracks, which can be disregarded for
880 // electron counting
881 if (verbose_)
882 ldmx_log(debug) << "\t\t -- (x1, x2, y) = (" << x1 << ", " << x2
883 << ", " << y << ") and (sx1, sx2, sy) = " << sx1 << ", "
884 << sx2 << ", " << sy << ")";
885
886 // now set x track coordinates according to overlap check result
887 auto xidx1 = x_idx_quad_map.lower_bound((*yitr).first);
888 auto xidx2 = x_idx_quad_map.upper_bound((*yitr).first);
889 xidx2--; // upper_bound points to (last+1) element
890 tracks.at((*xidx1).second).setPosition(x1, y);
891 tracks.at((*xidx1).second).setSigmaXY(sx1, sy);
892 tracks.at((*xidx2).second).setPosition(x2, y);
893 tracks.at((*xidx2).second).setSigmaXY(sx2, sy);
894
895 } // 1 y, 2 x tracks in the quadrant
896 else if (n_yin_quad == 2 && n_xin_quad == 1) {
897 // 5b) if there are more y than x: could be an overlap
898
899 // first: set the x track coordinates to x = x of x track, y = the mid of
900 // y tracks
901 auto xidx = x_idx_quad_map.find((*yitr).first);
902 tracks.at((*xidx).second).setPosition(x, y);
903 tracks.at((*xidx).second).setSigmaXY(sx, sy);
904
905 auto xitr = x_quad_map.lower_bound((*yitr).first);
906 int min_overlap_pe = 300;
907 if (((*xitr).second).getPE() < min_overlap_pe) {
908 if (verbose_)
909 ldmx_log(debug)
910 << "\t\t just 1 x track with not-unusual PE in the quad -- can't "
911 "match; setting mid-point values for x ";
912 x = x0;
913 sx = sx0;
914 } // if can't assume overlap
915 else {
916 // consider making two x tracks out if this one, and, anyway have to set
917 // their average as the y track x cocordinate
918 // EXPERIMENTAL : apply only to x tracks, which can be disregarded for
919 // electron counting
920 if (verbose_)
921 ldmx_log(debug) << "\t\t -- Found large PE count ("
922 << ((*xitr).second).getPE() << " > " << min_overlap_pe
923 << ") in x track, suggesting overlap! Setting both y "
924 "track coordinates to x track value:";
925 } // if can assume overlap
926 if (verbose_)
927 ldmx_log(debug) << "\t\t -- (x, y1, y2) = (" << x << ", " << y1 << ", "
928 << y2 << ") and (sx, sy1, sy2) = " << sx << ", " << sy1
929 << ", " << sy2 << ")";
930
931 auto yidx1 = y_idx_quad_map.lower_bound((*yitr).first);
932 auto yidx2 = y_idx_quad_map.upper_bound((*yitr).first);
933 yidx2--; // upper_bound points to next element
934 tracks.at((*yidx1).second).setPosition(x, y1);
935 tracks.at((*yidx1).second).setSigmaXY(sx, sy1);
936 tracks.at((*yidx2).second).setPosition(x, y2);
937 tracks.at((*yidx2).second).setSigmaXY(sx, sy2);
938
939 } // 2 y and 1 x track in quad
940 else if (n_yin_quad == 2 && n_xin_quad == 2) {
941 // MIDPONTS ALL OVER!
942 auto xidx1 = x_idx_quad_map.lower_bound((*yitr).first);
943 auto xidx2 = x_idx_quad_map.upper_bound((*yitr).first);
944 xidx2--;
945 auto yidx1 = y_idx_quad_map.lower_bound((*yitr).first);
946 auto yidx2 = y_idx_quad_map.upper_bound((*yitr).first);
947 yidx2--;
948
949 if (y_idx_quad_map.find((*yitr).first) == y_idx_quad_map.end())
950 ldmx_log(error) << "The two y tracks in the same quadrant at "
951 << (*yitr).first
952 << " appear to not be found in the y track map! "
953 "investigate. Note that yidx1.first = "
954 << (*yidx1).first
955 << " and yidx2.first = " << (*yidx2).first;
956 else {
957 tracks.at((*xidx1).second).setPosition(x1, y);
958 tracks.at((*xidx1).second).setSigmaXY(sx1, sy);
959 tracks.at((*xidx2).second).setPosition(x2, y);
960 tracks.at((*xidx2).second).setSigmaXY(sx2, sy);
961
962 tracks.at((*yidx1).second).setPosition(x, y1);
963 tracks.at((*yidx1).second).setSigmaXY(sx, sy1);
964 tracks.at((*yidx2).second).setPosition(x, y2);
965 tracks.at((*yidx2).second).setSigmaXY(sx, sy2);
966
967 if (verbose_)
968 ldmx_log(debug) << "\t\t -- in a 2 x 2 situaiton; midpoint y: " << y
969 << " for both x tracks, midpoint x: " << x
970 << " for both y tracks";
971 }
972 } // if 2 y, 2 x tracks
973
974 if (n_xin_quad > 2) {
975 if (verbose_)
976 ldmx_log(debug) << "\t\t -*-*-*- more than 2 x tracks in the same quad "
977 "-- nothing done about the x,y coordinates in this "
978 "situation -- implement if needed!!";
979 }
980 if (n_yin_quad > 2) {
981 if (verbose_)
982 ldmx_log(debug) << "\t\t -*-*-*- more than 2 y tracks in the same quad "
983 "-- nothing done about the x,y coordinates in this "
984 "situation -- implement if needed!!";
985 }
986
987 } // over y tracks
988
989 y_quad_map.clear();
990 x_quad_map.clear();
991
992 // return tracks;
993}
994
996 ldmx_log(debug) << "Process starts!";
997
998 return;
999}
1000
1002 ldmx_log(debug) << "Process ends!";
1003
1004 return;
1005}
1006
1007} // 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,...