LDMX Software
TrigScintFirmwareTracker.cxx
1
3
4#include <iterator>
5#include <map>
6
7#include "TrigScint/Firmware/clusterproducer.h"
8#include "TrigScint/Firmware/objdef.h"
9#include "TrigScint/Firmware/trackproducer.h"
10
11namespace trigscint {
12
14 min_thr_ = ps.get<double>("clustering_threshold");
15 digis1_collection_ = ps.get<std::string>("digis1_collection");
16 digis2_collection_ = ps.get<std::string>("digis2_collection");
17 digis3_collection_ = ps.get<std::string>("digis3_collection");
18 pass_name_ = ps.get<std::string>("input_pass_name");
19 output_collection_ = ps.get<std::string>("output_collection");
20 verbose_ = ps.get<int>("verbosity");
21 time_tolerance_ = ps.get<double>("time_tolerance");
22 pad_time_ = ps.get<double>("pad_time");
23
24 if (verbose_) {
25 ldmx_log(info) << "In TrigScintFirmwareTracker: configure done!";
26 ldmx_log(info) << "\nClustering threshold: " << min_thr_
27 << "\nExpected pad hit time: " << pad_time_
28 << "\nMax hit time delay: " << time_tolerance_
29 << "\ndigis1 collection: " << digis1_collection_
30 << "\ndigis2 collection: " << digis2_collection_
31 << "\ndigis3 collection: " << digis3_collection_
32 << "\nInput pass name: " << pass_name_
33 << "\nOutput collection: " << output_collection_
34 << "\nVerbosity: " << verbose_;
35 }
36
37 return;
38}
39
41 // This processor takes in TS digis and outputs a track collection. It does so
42 // using clusterproducer_sw and trackproducerHw, which are validated pieces
43 // of HLS code (though clusterproducer_sw has had its instances of pragmas
44 // excluded. I will comment on how clusterproducer and trackproducer work more
45 // thouroughly in them respectively, but generally the clusterproducer makes
46 // only two hit clusters (as ready that was all that was made from the
47 // original sw) and does so by making a digi map and running along channels
48 // numerically and pairing if possible. The trackproducer takes a LOOKUP array
49 // as a LUT and does track pattern mathcing. This depends on alignment through
50 // the A vector below.
51
52 if (verbose_) {
53 ldmx_log(debug)
54 << "TrigScintFirmwareTracker: produce() starts! Event number: "
55 << event.getEventHeader().getEventNumber();
56 }
57
58 // A is the mis-alignment vector
59 ap_int<12> a[3] = {0, 0, 0};
60 ap_int<12> lookup[NCENT][COMBO][2];
61
62 // Initialize the LOOKUP table to zero
63 for (int i = 0; i < NCENT; ++i) {
64 for (int j = 0; j < COMBO; ++j) {
65 for (int k = 0; k < 2; ++k) {
66 lookup[i][j][k] = ap_int<12>(-1);
67 }
68 }
69 }
70
71 // This line fills in the LOOKUP table used for patter matching latter. The
72 // array takes in as its first argument the centroid of a first pad cluster,
73 // then the next two take on which track pattern (of ~9) we are matching to
74 // and the last if we are matching to a cluster with two hits
75 for (int i = 0; i < NCENT; i++) {
76 for (int j = 0; j < COMBO; j++) {
77 lookup[i][j][0] = (i - a[1] + a[0]);
78 lookup[i][j][1] = (i - a[2] + a[0]);
79 if (j / 3 == 0) {
80 lookup[i][j][0] -= 1;
81 } else if (j / 3 == 2) {
82 lookup[i][j][0] += 1;
83 }
84 if (j % 3 == 0) {
85 lookup[i][j][1] -= 1;
86 } else if (j % 3 == 2) {
87 lookup[i][j][1] += 1;
88 }
89 if (not((lookup[i][j][0] >= 0) and (lookup[i][j][1] >= 0) and
90 (lookup[i][j][0] < NCENT) and (lookup[i][j][1] < NCENT))) {
91 lookup[i][j][0] = -1;
92 lookup[i][j][1] = -1;
93 }
94 }
95 }
96 // Here we instantiate arrays necessary to do the rest of it.
97 Hit h_pad1[NHITS];
98 Hit h_pad2[NHITS];
99 Hit h_pad3[NHITS];
100
101 // Pad1 goes with NTRK bc of firmware bandwidth constraints
102 // It is also expected on Pad1 to have 1 cluster per track
103 Cluster pad1[NTRK];
104 Cluster pad2[NCLUS];
105 Cluster pad3[NCLUS];
106 Track out_trk[NTRK];
107
108 for (int j = 0; j < NHITS; j++) {
109 clearHit(h_pad1[j]);
110 clearHit(h_pad2[j]);
111 clearHit(h_pad3[j]);
112 }
113 for (int j = 0; j < NCLUS; j++) {
114 if (j < NTRK) {
115 clearClus(pad1[j]);
116 }
117 clearClus(pad2[j]);
118 clearClus(pad3[j]);
119 }
120 for (int j = 0; j < NTRK; j++) {
121 clearTrack(out_trk[j]);
122 }
123 // I am reading in the three digi collections
124 const auto &digis1{
125 event.getCollection<ldmx::TrigScintHit>(digis1_collection_, pass_name_)};
126 const auto &digis2{
127 event.getCollection<ldmx::TrigScintHit>(digis2_collection_, pass_name_)};
128 const auto &digis3{
129 event.getCollection<ldmx::TrigScintHit>(digis3_collection_, pass_name_)};
130
131 if (verbose_) {
132 ldmx_log(debug) << "Got digi collection " << digis1_collection_ << "_"
133 << pass_name_ << " with " << digis1.size() << " entries ";
134 }
135
136 // The next collection of things fill in the firmware hit objects from reading
137 // in the digi collections the necessary information. The firmware hit objects
138 // only keep bID,mID,Time, and PE count.
139 int occupied[NCHAN];
140 for (int i = 0; i < NCHAN; i++) {
141 occupied[i] = -1;
142 }
143 int count = 0;
144 for (const auto &digi : digis1) {
145 if ((digi.getPE() > min_thr_) and (digi.getBarID() <= NCHAN) and
146 (digi.getBarID() >= 0)) {
147 ap_int<12> b_id = (ap_int<12>)(digi.getBarID());
148 ap_int<12> amp = (ap_int<12>)(digi.getPE());
149 if (occupied[digi.getBarID()] >= 0) {
150 if (h_pad1[occupied[digi.getBarID()]].amp_ < digi.getPE()) {
151 h_pad1[occupied[digi.getBarID()]].b_id_ =
152 (ap_int<12>)(digi.getBarID());
153 h_pad1[occupied[digi.getBarID()]].m_id_ =
154 (ap_int<12>)(digi.getModuleID());
155 h_pad1[occupied[digi.getBarID()]].amp_ = (ap_int<12>)(digi.getPE());
156 h_pad1[occupied[digi.getBarID()]].time_ =
157 (ap_int<12>)(digi.getTime());
158 }
159 } else {
160 h_pad1[count].b_id_ = (ap_int<12>)(digi.getBarID());
161 h_pad1[count].m_id_ = (ap_int<12>)(digi.getModuleID());
162 h_pad1[count].amp_ = (ap_int<12>)(digi.getPE());
163 h_pad1[count].time_ = (ap_int<12>)(digi.getTime());
164 occupied[digi.getBarID()] = count;
165 count++;
166 }
167 }
168 }
169
170 for (int i = 0; i < NCHAN; i++) {
171 occupied[i] = -1;
172 }
173 count = 0;
174 for (const auto &digi : digis2) {
175 if ((digi.getPE() > min_thr_) and (digi.getBarID() <= NCHAN) and
176 (digi.getBarID() >= 0)) {
177 ap_int<12> b_id = (ap_int<12>)(digi.getBarID());
178 ap_int<12> amp = (ap_int<12>)(digi.getPE());
179 if (occupied[digi.getBarID()] >= 0) {
180 if (h_pad2[occupied[digi.getBarID()]].amp_ < digi.getPE()) {
181 h_pad2[occupied[digi.getBarID()]].b_id_ =
182 (ap_int<12>)(digi.getBarID());
183 h_pad2[occupied[digi.getBarID()]].m_id_ =
184 (ap_int<12>)(digi.getModuleID());
185 h_pad2[occupied[digi.getBarID()]].amp_ = (ap_int<12>)(digi.getPE());
186 h_pad2[occupied[digi.getBarID()]].time_ =
187 (ap_int<12>)(digi.getTime());
188 }
189 } else {
190 h_pad2[count].b_id_ = (ap_int<12>)(digi.getBarID());
191 h_pad2[count].m_id_ = (ap_int<12>)(digi.getModuleID());
192 h_pad2[count].amp_ = (ap_int<12>)(digi.getPE());
193 h_pad2[count].time_ = (ap_int<12>)(digi.getTime());
194 occupied[digi.getBarID()] = count;
195 count++;
196 }
197 }
198 }
199 for (int i = 0; i < NCHAN; i++) {
200 occupied[i] = -1;
201 }
202 count = 0;
203 for (const auto &digi : digis3) {
204 if ((digi.getPE() > min_thr_) and (digi.getBarID() <= NCHAN) and
205 (digi.getBarID() >= 0)) {
206 ap_int<12> b_id = (ap_int<12>)(digi.getBarID());
207 ap_int<12> amp = (ap_int<12>)(digi.getPE());
208 if (occupied[digi.getBarID()] >= 0) {
209 if (h_pad3[occupied[digi.getBarID()]].amp_ < digi.getPE()) {
210 h_pad3[occupied[digi.getBarID()]].b_id_ =
211 (ap_int<12>)(digi.getBarID());
212 h_pad3[occupied[digi.getBarID()]].m_id_ =
213 (ap_int<12>)(digi.getModuleID());
214 h_pad3[occupied[digi.getBarID()]].amp_ = (ap_int<12>)(digi.getPE());
215 h_pad3[occupied[digi.getBarID()]].time_ =
216 (ap_int<12>)(digi.getTime());
217 }
218 } else {
219 h_pad3[count].b_id_ = (ap_int<12>)(digi.getBarID());
220 h_pad3[count].m_id_ = (ap_int<12>)(digi.getModuleID());
221 h_pad3[count].amp_ = (ap_int<12>)(digi.getPE());
222 h_pad3[count].time_ = (ap_int<12>)(digi.getTime());
223 occupied[digi.getBarID()] = count;
224 count++;
225 }
226 }
227 }
228 // These next lines here calls clusterproducerSw(HPad1), which is just the
229 // validated firmware module. Since ap_* class is messy, I had to do some
230 // post-call cleanup before looping over the clusters and putting them into
231 // Point i which is feed into track producer
232 int counter_n = 0;
233 std::array<Cluster, NCLUS> point1 = clusterproducerSw(h_pad1);
234 int top_seed = 0;
235 for (int i = 0; i < NCLUS; i++) {
236 if ((point1[i].seed_.amp_ < 450) and (point1[i].seed_.amp_ > 30) and
237 (point1[i].seed_.b_id_ < (NCHAN + 1)) and
238 (point1[i].seed_.b_id_ >= 0) and (point1[i].sec_.amp_ < 450) and
239 (counter_n < NTRK)) {
240 if (point1[i].seed_.b_id_ >= top_seed) {
241 cpyHit(pad1[counter_n].seed_, point1[i].seed_);
242 cpyHit(pad1[counter_n].sec_, point1[i].sec_);
243 calcCent(pad1[counter_n]);
244 counter_n++;
245 top_seed = point1[i].seed_.b_id_;
246 }
247 }
248 }
249 std::array<Cluster, NCLUS> point2 = clusterproducerSw(h_pad2);
250 top_seed = 0;
251 for (int i = 0; i < NCLUS; i++) {
252 if ((point2[i].seed_.amp_ < 450) and (point2[i].seed_.amp_ > 30) and
253 (point2[i].seed_.b_id_ < (NCHAN + 1)) and
254 (point2[i].seed_.b_id_ >= 0) and (point2[i].sec_.amp_ < 450)) {
255 if (point2[i].seed_.b_id_ >= top_seed) {
256 cpyHit(pad2[i].seed_, point2[i].seed_);
257 cpyHit(pad2[i].sec_, point2[i].sec_);
258 calcCent(pad2[i]);
259 top_seed = point2[i].seed_.b_id_;
260 }
261 }
262 }
263 std::array<Cluster, NCLUS> point3 = clusterproducerSw(h_pad3);
264 top_seed = 0;
265 for (int i = 0; i < NCLUS; i++) {
266 if ((point3[i].seed_.amp_ < 450) and (point3[i].seed_.amp_ > 30) and
267 (point3[i].seed_.b_id_ < (NCHAN + 1)) and
268 (point3[i].seed_.b_id_ >= 0) and (point3[i].sec_.amp_ < 450)) {
269 if (point3[i].seed_.b_id_ >= top_seed) {
270 cpyHit(pad3[i].seed_, point3[i].seed_);
271 cpyHit(pad3[i].sec_, point3[i].sec_);
272 calcCent(pad3[i]);
273 top_seed = point3[i].seed_.b_id_;
274 }
275 }
276 }
277 // I have stagged the digis into firmware digi objects and paired them into
278 // firmware cluster objects, so at this point I can insert them and the LUT
279 // into the trackproducerHw to create the track collection I use makeTrack to
280 // revert the firmware track object back into a regular track object for
281 // analysis purposes
282 //
283 // NOTE: Pad1 has NTRK instead of NCLUS clusters for a reason: the firmware
284 // cannot facilitate NCLUS many tracks within its alloted bandwidth , we have
285 // to put a cut on them which is facilitated by a cut on the number of
286 // clusters in Pad1. Do not change this.
287 trackproducerHw(pad1, pad2, pad3, out_trk, lookup);
288 for (int i = 0; i < NTRK; i++) {
289 if (out_trk[i].pad1_.seed_.amp_ > 0. && out_trk[i].pad1_.sec_.amp_ >= 0. &&
290 out_trk[i].pad2_.seed_.amp_ > 0. && out_trk[i].pad2_.sec_.amp_ >= 0. &&
291 out_trk[i].pad3_.seed_.amp_ > 0. && out_trk[i].pad3_.sec_.amp_ >= 0.) {
292 ldmx::TrigScintTrack trk = makeTrack(out_trk[i]);
293 tracks_.push_back(trk);
294 }
295 }
296 event.add(output_collection_, tracks_);
297 tracks_.resize(0);
298
299 return;
300}
301
302ldmx::TrigScintTrack TrigScintFirmwareTracker::makeTrack(Track outTrk) {
303 // This takes a firmware track object and reverts it into an ldmx track
304 // object, unfortunately only retaining that information of the track that is
305 // retained in the firmware track.
307 float pe{0.};
308 pe += static_cast<float>(outTrk.pad1_.seed_.amp_) +
309 static_cast<float>(outTrk.pad1_.sec_.amp_);
310 pe += static_cast<float>(outTrk.pad2_.seed_.amp_) +
311 static_cast<float>(outTrk.pad2_.sec_.amp_);
312 pe += static_cast<float>(outTrk.pad3_.seed_.amp_) +
313 static_cast<float>(outTrk.pad3_.sec_.amp_);
314 tr.setCentroid(calcTCent(outTrk));
315 calcResid(outTrk);
316 tr.setPE(pe);
317 return tr;
318}
319
320} // namespace trigscint
321
#define DECLARE_PRODUCER(CLASS)
Macro which allows the framework to construct a producer given its name during configuration.
Tracker made to emulate and stage real firmware, emulates existing ldmx software but has LUT structur...
Implements an event buffer system for storing event data.
Definition Event.h:42
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
Represents a track of trigger scintillator clusters.
void setPE(float pe)
Set the average cluster pe of the track.
void setCentroid(float centroid)
Set the detector ID centroid of the track.
void produce(framework::Event &event) override
Process the event and put new data products into it.
void configure(framework::config::Parameters &ps) override
Callback for the EventProcessor to configure itself from the given set of parameters.
double min_thr_
add a hit at index idx to a cluster
Definition objdef.h:49
Sign Arbitrary Precision Type.
Definition ap_int.h:28