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