Belle II Software development
CDCTriggerHoughtrafoForETF.cc
1/**************************************************************************
2 * basf2 (Belle II Analysis Software Framework) *
3 * Author: The Belle II Collaboration *
4 * *
5 * See git log for contributors and copyright holders. *
6 * This file is licensed under LGPL-3.0, see LICENSE.md. *
7 **************************************************************************/
8
9#include <trg/cdc/modules/houghETF/CDCTriggerHoughETFModule.h>
10
11#include <framework/logging/Logger.h>
12
13#include <cmath>
14
15#include <root/TMatrix.h>
16
17#include <algorithm>
18
19#include <array>
20
21
22/* defines */
23#define CDC_SUPER_LAYERS 9
24#define CDC_ETF_SECTOR 64
25
26using namespace std;
27using namespace Belle2;
28
29
30unsigned short
32{
33 if (m_requireSL0 && !layer[0]) return 0;
34 unsigned short lcnt = 0;
35 for (int i = 0; i < CDC_SUPER_LAYERS; ++i) {
36 if (layer[i] == true) ++lcnt;
37 }
38 return lcnt;
39}
40
41bool
43{
44 unsigned short lcnt = 0;
45 // check axial super layers (even layer number),
46 // break at first layer without hit
47 for (int i = 0; i < CDC_SUPER_LAYERS; i += 2) {
48 if (layer[i] == true) ++lcnt;
49 else break;
50 }
51 return (lcnt >= m_minHitsShort);
52}
53
54/*
55* Find the intercept in hough space.
56* In each iteration the Hough plane is divided in quarters.
57* For each quarter containing enough hits, the process is repeated
58* until the quarters correspond to single Hough cells.
59* Return zero on success.
60* x = m
61* y = a
62*/
63int
65 double x1_s, double x2_s,
66 double y1_s, double y2_s,
67 unsigned iterations,
68 unsigned ix_s, unsigned iy_s)
69{
70 string indent = "";
71 for (unsigned i = 0; i < iterations; ++i) indent += " ";
72 B2DEBUG(150, indent << "intercept finder iteration " << iterations
73 << " x1 " << x1_s * 180 / M_PI << " x2 " << x2_s * 180 / M_PI
74 << " y1 " << y1_s << " y2 " << y2_s
75 << " ix " << ix_s << " iy " << iy_s);
76
77 int i, j, iHit;
78 double unitx, unity;
79 double y1, y2;
80 double m, a;
81 cdcPair hp;
82 unsigned short iSL;
83 // list of hit indices crossing the current rectangle
84 vector<unsigned> idx_list;
85
86 // limits of divided rectangles
87 double x1_d, x2_d, y1_d, y2_d;
88
89 // cell indices in full plane
90 unsigned ix, iy;
91
92 unitx = ((x2_s - x1_s) / 2.0);
93 unity = ((y2_s - y1_s) / 2.0);
94
95 // divide x axis in half
96 for (i = 0; i < 2 ; ++i) {
97 // divide y axis in half
98 for (j = 0; j < 2; ++j) {
99 x1_d = x1_s + i * unitx;
100 x2_d = x1_s + (i + 1) * unitx;
101 y1_d = y1_s + j * unity;
102 y2_d = y1_s + (j + 1) * unity;
103
104 ix = 2 * ix_s + i;
105 iy = 2 * iy_s + j;
106
107 // skip extra cells outside of the Hough plane
108 if (x2_d <= -M_PI || x1_d >= M_PI || y2_d <= -maxR + shiftR || y1_d >= maxR + shiftR) {
109 B2DEBUG(150, indent << "skip Hough cell outside of plane limits");
110 continue;
111 }
112
113 // if the cell size in phi is too large, the hit calculation is not reliable
114 // -> continue to next iteration without hit check
115 if (iterations != maxIterations && unitx > M_PI / 2.) {
116 fastInterceptFinder(hits, x1_d, x2_d, y1_d, y2_d, iterations + 1, ix, iy);
117 continue;
118 }
119
120 idx_list.clear();
121 bool layerHit[CDC_SUPER_LAYERS] = {false}; /* For layer filter */
122 for (auto it = hits.begin(); it != hits.end(); ++it) {
123 iHit = it->first;
124 hp = it->second;
125 iSL = hp.first;
126 m = hp.second.X();
127 a = hp.second.Y();
128 // calculate Hough curve with slightly enlarged limits to avoid errors due to rounding
129 y1 = m * sin(x1_d - 1e-10) - a * cos(x1_d - 1e-10);
130 y2 = m * sin(x2_d + 1e-10) - a * cos(x2_d + 1e-10);
131 // skip decreasing half of the sine (corresponds to curl back half of the track)
132 if (iterations == maxIterations && y1 > y2) continue;
133 if (!((y1 > y2_d && y2 > y2_d) || (y1 < y1_d && y2 < y1_d))) {
134 if (iterations == maxIterations) {
135 idx_list.push_back(iHit);
136 }
137 layerHit[iSL] = true; /* layer filter */
138 }
139 }
140 unsigned short nSL = countSL(layerHit);
141 B2DEBUG(150, indent << "i " << i << " j " << j
142 << " layerHit " << int(layerHit[0]) << int(layerHit[2])
143 << int(layerHit[4]) << int(layerHit[6]) << int(layerHit[8])
144 << " nSL " << nSL);
145 if (nSL >= m_minHits || shortTrack(layerHit)) {
146 if (iterations != maxIterations) {
147 fastInterceptFinder(hits, x1_d, x2_d, y1_d, y2_d, iterations + 1, ix, iy);
148 } else {
149 ROOT::Math::XYVector v1(x1_d, y1_d);
150 ROOT::Math::XYVector v2(x2_d, y2_d);
151 houghCand.push_back(CDCTriggerHoughCand(idx_list, make_pair(v1, v2),
152 nSL, houghCand.size()));
153 if (m_storePlane > 0) {
154 (*m_houghPlane)[ix - (nCells - m_nCellsPhi) / 2][iy - (nCells - m_nCellsR) / 2] = nSL;
155 }
156 }
157 } else if (m_storePlane > 1) {
158 // to store the full Hough plane, we need to continue the iteration
159 // to minimal cell size everywhere
160 for (unsigned min = m_minHits - 1; min > 0; --min) {
161 if (nSL >= min) {
162 if (iterations != maxIterations) {
163 fastInterceptFinder(hits, x1_d, x2_d, y1_d, y2_d, iterations + 1, ix, iy);
164 } else {
165 (*m_houghPlane)[ix - (nCells - m_nCellsPhi) / 2][iy - (nCells - m_nCellsR) / 2] = nSL;
166 }
167 break;
168 }
169 }
170 }
171 }
172 }
173
174 return 0;
175}
176
177/*
178* Peak finding method: connected regions & center of gravity
179*/
180void
182{
183 vector<vector<CDCTriggerHoughCand>> regions;
184 vector<CDCTriggerHoughCand> cpyCand = houghCand;
185
186 // debug: print candidate list
187 B2DEBUG(50, "houghCand number " << cpyCand.size());
188 for (unsigned icand = 0; icand < houghCand.size(); ++icand) {
189 coord2dPair coord = houghCand[icand].getCoord();
190 B2DEBUG(100, houghCand[icand].getID()
191 << " nSL " << houghCand[icand].getSLcount()
192 << " x1 " << coord.first.X() << " x2 " << coord.second.X()
193 << " y1 " << coord.first.Y() << " y2 " << coord.second.Y());
194 }
195
196 // combine connected cells to regions
197 while (cpyCand.size() > 0) {
198 B2DEBUG(100, "make new region");
199 vector<CDCTriggerHoughCand> region;
200 vector<CDCTriggerHoughCand> rejected;
201 CDCTriggerHoughCand start = cpyCand[0];
202 cpyCand.erase(cpyCand.begin());
203 region.push_back(start);
204 addNeighbors(start, cpyCand, region, rejected, start.getSLcount());
205 regions.push_back(region);
206 for (auto cand = cpyCand.begin(); cand != cpyCand.end();) {
207 if (inList(*cand, region) || inList(*cand, rejected))
208 cpyCand.erase(cand);
209 else
210 ++cand;
211 }
212 }
213
214 // find center of gravity for each region
215 for (unsigned ir = 0; ir < regions.size(); ++ir) {
216 B2DEBUG(50, "region " << ir << " (" << regions[ir].size() << " cells).");
217 // skip regions with size below cut
218 if (regions[ir].size() < m_minCells) {
219 B2DEBUG(50, "Skipping region with " << regions[ir].size() << " cells.");
220 continue;
221 }
222 double xfirst = regions[ir][0].getCoord().first.X();
223 double x = 0;
224 double y = 0;
225 int n = 0;
226 vector<unsigned> mergedList;
227 int xmin = m_nCellsPhi;
228 int xmax = 0;
229 int ymin = m_nCellsR;
230 int ymax = 0;
231 vector<ROOT::Math::XYVector> cellIds = {};
232 for (unsigned ir2 = 0; ir2 < regions[ir].size(); ++ir2) {
233 coord2dPair hc = regions[ir][ir2].getCoord();
234 B2DEBUG(100, " " << regions[ir][ir2].getID()
235 << " nSL " << regions[ir][ir2].getSLcount()
236 << " x1 " << hc.first.X() << " x2 " << hc.second.X()
237 << " y1 " << hc.first.Y() << " y2 " << hc.second.Y());
238 int ix = floor((hc.first.X() + M_PI) / 2. / M_PI * m_nCellsPhi + 0.5);
239 int iy = floor((hc.first.Y() + maxR - shiftR) / 2. / maxR * m_nCellsR + 0.5);
240 x += (hc.first.X() + hc.second.X());
241 if (xfirst - hc.first.X() > M_PI) {
242 x += 4 * M_PI;
243 ix += m_nCellsPhi;
244 } else if (hc.first.X() - xfirst > M_PI) {
245 x -= 4 * M_PI;
246 ix -= m_nCellsPhi;
247 }
248 y += (hc.first.Y() + hc.second.Y());
249 n += 1;
250 vector<unsigned> idList = regions[ir][ir2].getIdList();
251 mergeIdList(mergedList, mergedList, idList);
252 xmin = min(xmin, ix);
253 xmax = max(xmax, ix);
254 ymin = min(ymin, iy);
255 ymax = max(ymax, iy);
256 cellIds.push_back(ROOT::Math::XYVector(ix, iy));
257 }
258 x *= 0.5 / n;
259 if (x > M_PI)
260 x -= 2 * M_PI;
261 else if (x <= -M_PI)
262 x += 2 * M_PI;
263 y *= 0.5 / n;
264 B2DEBUG(50, "x " << x << " y " << y);
265
266 // select 1 hit per super layer
267 vector<unsigned> selectedList = {};
268 vector<unsigned> unselectedList = {};
269 selectHits(mergedList, selectedList, unselectedList);
270
271 // save track
272 const CDCTriggerTrack* track =
273 m_tracks.appendNew(x, 2. * y, 0.);
274 // relations to selected hits
275 for (unsigned i = 0; i < selectedList.size(); ++i) {
276 unsigned its = selectedList[i];
277 if (m_storeTracks) track->addRelationTo(m_segmentHits[its]);
278 }
279 // relations to additional hits get a negative weight
280 for (unsigned i = 0; i < unselectedList.size(); ++i) {
281 unsigned its = unselectedList[i];
282 if (m_storeTracks) track->addRelationTo(m_segmentHits[its], -1.);
283 }
284 // save detail information about the cluster
285 const CDCTriggerHoughCluster* cluster =
286 m_clusters.appendNew(xmin, xmax, ymin, ymax, cellIds);
287 if (m_storeTracks) track->addRelationTo(cluster);
288 }
289}
290
291void
293 const vector<CDCTriggerHoughCand>& candidates,
294 vector<CDCTriggerHoughCand>& merged,
295 vector<CDCTriggerHoughCand>& rejected,
296 unsigned short nSLmax) const
297{
298 for (unsigned icand = 0; icand < candidates.size(); ++icand) {
299 B2DEBUG(120, "compare center " << center.getID()
300 << " to " << candidates[icand].getID());
301 if (inList(candidates[icand], merged) || inList(candidates[icand], rejected)) {
302 B2DEBUG(120, " " << candidates[icand].getID() << " already in list");
303 continue;
304 }
305 bool reject = inList(center, rejected);
306 if (connected(center, candidates[icand])) {
307 if (m_onlyLocalMax && candidates[icand].getSLcount() < nSLmax) {
308 B2DEBUG(100, " lower than highest SLcount, rejected");
309 rejected.push_back(candidates[icand]);
310 } else if (m_onlyLocalMax && !reject && candidates[icand].getSLcount() > nSLmax) {
311 B2DEBUG(100, " new highest SLcount, clearing list");
312 nSLmax = candidates[icand].getSLcount();
313 for (unsigned imerged = 0; imerged < merged.size(); ++imerged) {
314 rejected.push_back(merged[imerged]);
315 }
316 merged.clear();
317 merged.push_back(candidates[icand]);
318 } else if (m_onlyLocalMax && candidates[icand].getSLcount() > center.getSLcount()) {
319 B2DEBUG(100, " connected to rejected cell, skip");
320 continue;
321 } else if (m_onlyLocalMax && reject) {
322 B2DEBUG(100, " connected to rejected cell, rejected");
323 rejected.push_back(candidates[icand]);
324 } else {
325 B2DEBUG(100, " connected");
326 merged.push_back(candidates[icand]);
327 }
328 vector<CDCTriggerHoughCand> cpyCand = candidates;
329 cpyCand.erase(cpyCand.begin() + icand);
330 addNeighbors(candidates[icand], cpyCand, merged, rejected, nSLmax);
331 }
332 }
333}
334
335bool
337 const vector<CDCTriggerHoughCand>& list) const
338{
339 for (unsigned i = 0; i < list.size(); ++i) {
340 if (a == list[i]) return true;
341 }
342 return false;
343}
344
345bool
347 const CDCTriggerHoughCand& b) const
348{
349 double ax1 = a.getCoord().first.X();
350 double ax2 = a.getCoord().second.X();
351 double ay1 = a.getCoord().first.Y();
352 double ay2 = a.getCoord().second.Y();
353 double bx1 = b.getCoord().first.X();
354 double bx2 = b.getCoord().second.X();
355 double by1 = b.getCoord().first.Y();
356 double by2 = b.getCoord().second.Y();
357 // direct neighbors
358 bool direct = ((ax2 == bx1 && ay1 == by1) || // right
359 (ax1 == bx2 && ay1 == by1) || // left
360 (ax1 == bx1 && ay2 == by1) || // above
361 (ax1 == bx1 && ay1 == by2) || // below
362 (ax1 + 2. * M_PI == bx2 && ay1 == by1) ||
363 (ax2 == bx1 + 2. * M_PI && ay1 == by1));
364 // diagonal connections
365 bool diagRise = ((ax2 == bx1 && ay2 == by1) || // right above
366 (ax1 == bx2 && ay1 == by2) || // left below
367 (ax1 + 2. * M_PI == bx2 && ay1 == by2) ||
368 (ax2 == bx1 + 2. * M_PI && ay2 == by1));
369 bool diagFall = ((ax1 == bx2 && ay2 == by1) || // left above
370 (ax2 == bx1 && ay1 == by2) || // right below
371 (ax2 == bx1 + 2. * M_PI && ay1 == by2) ||
372 (ax1 + 2. * M_PI == bx2 && ay2 == by1));
373 if (m_connect == 4) return direct;
374 else if (m_connect == 6) return (direct || diagRise);
375 else if (m_connect == 8) return (direct || diagRise || diagFall);
376 else B2WARNING("Unknown option for connect " << m_connect << ", using default.");
377 return (direct || diagRise);
378}
379
380/*
381* Merge Id lists.
382*/
383void
384CDCTriggerHoughETFModule::mergeIdList(std::vector<unsigned>& merged,
385 std::vector<unsigned>& a,
386 std::vector<unsigned>& b)
387{
388 bool found;
389
390 for (auto it = a.begin(); it != a.end(); ++it) {
391 found = false;
392 for (auto it_in = merged.begin(); it_in != merged.end(); ++it_in) {
393 if (*it_in == *it) {
394 found = true;
395 break;
396 }
397 }
398 if (!found) {
399 merged.push_back(*it);
400 }
401 }
402
403 for (auto it = b.begin(); it != b.end(); ++it) {
404 found = false;
405 for (auto it_in = merged.begin(); it_in != merged.end(); ++it_in) {
406 if (*it_in == *it) {
407 found = true;
408 break;
409 }
410 }
411 if (!found) {
412 merged.push_back(*it);
413 }
414 }
415}
416
417void
419 double x1, double x2,
420 double y1, double y2,
421 const cdcMap& inputMap)
422{
423 double m, a, y1_h, y2_h;
424 for (int iHit = 0; iHit < m_segmentHits.getEntries(); iHit++) {
425 unsigned short iSL = m_segmentHits[iHit]->getISuperLayer();
426 if (iSL % 2) continue;
427 // associate only the hits actually used to form the cluster
428 if (m_suppressClone && inputMap.find(iHit) == inputMap.end()) continue;
429 //TODO: add options: center cell / active priority cell / all priority cells
430 vector<double> phi = {0, 0, 0};
431 phi[0] = m_segmentHits[iHit]->getSegmentID() - TSoffset[iSL];
432 phi[1] = phi[0] + 0.5;
433 phi[2] = phi[0] - 0.5;
434 vector<double> r = {radius[iSL][0], radius[iSL][1], radius[iSL][1]};
435 for (unsigned i = 0; i < 3; ++i) {
436 phi[i] *= 2. * M_PI / (TSoffset[iSL + 1] - TSoffset[iSL]);
437 m = cos(phi[i]) / r[i];
438 a = sin(phi[i]) / r[i];
439 // calculate Hough curve with slightly enlarged limits to avoid errors due to rounding
440 y1_h = m * sin(x1 - 1e-10) - a * cos(x1 - 1e-10);
441 y2_h = m * sin(x2 + 1e-10) - a * cos(x2 + 1e-10);
442 // skip decreasing half of the sine (corresponds to curl back half of the track)
443 if (y1_h > y2_h) continue;
444 if (!((y1_h > y2 && y2_h > y2) || (y1_h < y1 && y2_h < y1))) {
445 list.push_back(iHit);
446 break;
447 }
448 }
449 }
450}
451
452/*
453 * Select one hit per super layer
454 */
455void
456CDCTriggerHoughETFModule::selectHits(std::vector<unsigned>& list,
457 std::vector<unsigned>& selected,
458 std::vector<unsigned>& unselected)
459{
460 std::vector<int> bestPerSL(5, -1);
461 for (unsigned i = 0; i < list.size(); ++i) {
462 unsigned iax = m_segmentHits[list[i]]->getISuperLayer() / 2;
463 bool firstPriority = (m_segmentHits[list[i]]->getPriorityPosition() == 3);
464 if (bestPerSL[iax] < 0) {
465 bestPerSL[iax] = i;
466 } else {
467 unsigned itsBest = list[bestPerSL[iax]];
468 bool firstBest = (m_segmentHits[itsBest]->getPriorityPosition() == 3);
469 // selection rules:
470 // first priority, higher ID
471 if ((firstPriority && !firstBest) ||
472 (firstPriority == firstBest &&
473 m_segmentHits[list[i]]->getSegmentID() > m_segmentHits[itsBest]->getSegmentID())) {
474 bestPerSL[iax] = i;
475 }
476 }
477 }
478
479 for (unsigned i = 0; i < list.size(); ++i) {
480 unsigned iax = m_segmentHits[list[i]]->getISuperLayer() / 2;
481 if (int(i) == bestPerSL[iax]) selected.push_back(list[i]);
482 else unselected.push_back(list[i]);
483 }
484}
485
486/*
487* Alternative peak finding with nested patterns
488*/
489bool
491{
492 bool foundTrack = false;
493 std::vector<CDCTriggerSegmentHit*> associatedTSHits;
494
495 // fill a matrix of 2 x 2 squares
496 TMatrix plane2(m_nCellsPhi / 2, m_nCellsR / 2);
497 TMatrix planeIcand(m_nCellsPhi, m_nCellsR);
498 for (unsigned icand = 0; icand < houghCand.size(); ++icand) {
499 double x = (houghCand[icand].getCoord().first.X() +
500 houghCand[icand].getCoord().second.X()) / 2.;
501 unsigned ix = floor((x + M_PI) / 2. / M_PI * m_nCellsPhi);
502 double y = (houghCand[icand].getCoord().first.Y() +
503 houghCand[icand].getCoord().second.Y()) / 2.;
504 unsigned iy = floor((y + maxR - shiftR) / 2. / maxR * m_nCellsR);
505 plane2[ix / 2][iy / 2] += 1 << ((ix % 2) + 2 * (iy % 2));
506 planeIcand[ix][iy] = icand;
507 B2DEBUG(100, "candidate " << icand << " at ix " << ix << " iy " << iy);
508 }
509 // look for clusters of 2 x 2 squares in a (rX x rY) region
510 unsigned nX = m_nCellsPhi / 2;
511 unsigned nY = m_nCellsR / 2;
512 unsigned rX = m_clusterSizeX;
513 unsigned rY = m_clusterSizeY;
514 for (unsigned ix = 0; ix < nX; ++ix) {
515 for (unsigned iy = 0; iy < nY; ++iy) {
516 if (!plane2[ix][iy]) continue;
517 // check if we are in a lower left corner
518 unsigned ileft = (ix - 1 + nX) % nX;
519 B2DEBUG(100, "ix " << ix << " iy " << iy);
520 if (connectedLR(plane2[ileft][iy], plane2[ix][iy]) ||
521 (iy > 0 && connectedUD(plane2[ix][iy - 1], plane2[ix][iy])) ||
522 (iy > 0 && connectedDiag(plane2[ileft][iy - 1], plane2[ix][iy]))) {
523 B2DEBUG(100, "skip connected square");
524 continue;
525 }
526 // form cluster
527 vector<unsigned> pattern(rX * rY, 0);
528 pattern[0] = plane2[ix][iy];
529 vector<ROOT::Math::XYVector> cellIds = {ROOT::Math::XYVector(2 * ix, 2 * iy)};
530 for (unsigned ix2 = 0; ix2 < rX; ++ix2) {
531 for (unsigned iy2 = 0; iy2 < rY; ++iy2) {
532 if (iy + iy2 >= nY) continue;
533 unsigned ip = ix2 + rX * iy2;
534 unsigned iright = (ix + ix2 + nX) % nX;
535 ileft = (iright - 1 + nX) % nX;
536 B2DEBUG(100, "ix2 " << ix2 << " ileft " << ileft << " iright " << iright);
537 if ((ix2 > 0 && // check left/right connection
538 pattern[ip - 1] &&
539 connectedLR(plane2[ileft][iy + iy2], plane2[iright][iy + iy2])) ||
540 (iy2 > 0 && // check up/down connection
541 pattern[ip - rX] &&
542 connectedUD(plane2[iright][iy + iy2 - 1], plane2[iright][iy + iy2])) ||
543 (ix2 > 0 && iy2 > 0 && // check diagonal connection
544 pattern[ip - rX - 1] &&
545 connectedDiag(plane2[ileft][iy + iy2 - 1], plane2[iright][iy + iy2]))) {
546 pattern[ip] = plane2[iright][iy + iy2];
547 B2DEBUG(100, "connect cell " << iright << " " << iy + iy2);
548 cellIds.push_back(ROOT::Math::XYVector(2 * (ix + ix2), 2 * (iy + iy2)));
549 }
550 }
551 }
552 B2DEBUG(100, "cluster starting at " << ix << " " << iy);
553 // check if cluster continues beyond defined area
554 bool overflowRight = false;
555 bool overflowTop = false;
556 for (unsigned iy2 = 0; iy2 < rY; ++iy2) {
557 unsigned ip = rX - 1 + rX * iy2;
558 if (!pattern[ip]) continue;
559 unsigned iright = (ix + rX + nX) % nX;
560 ileft = (iright - 1 + nX) % nX;
561 if (connectedLR(plane2[ileft][iy + iy2], plane2[iright][iy + iy2]) ||
562 ((iy + iy2 + 1 < nY) &&
563 connectedDiag(plane2[ileft][iy + iy2], plane2[iright][iy + iy2 + 1]))) {
564 setReturnValue(false);
565 overflowRight = true;
566 }
567 }
568 if (iy + rY < nY) {
569 for (unsigned ix2 = 0; ix2 < rX; ++ix2) {
570 unsigned ip = ix2 + rX * (rY - 1);
571 if (!pattern[ip]) continue;
572 unsigned iright = (ix + ix2 + 1 + nX) % nX;
573 ileft = (iright - 1 + nX) % nX;
574 if (connectedUD(plane2[ileft][iy + rY - 1], plane2[ileft][iy + rY]) ||
575 connectedDiag(plane2[ileft][iy + rY - 1], plane2[iright][iy + rY])) {
576 setReturnValue(false);
577 overflowTop = true;
578 }
579 }
580 }
581 if (overflowRight && !overflowTop) {
582 B2DEBUG(100, "cluster extends right of " << rX << " x " << rY << " area");
583 } else if (overflowTop && !overflowRight) {
584 B2DEBUG(100, "cluster extends above " << rX << " x " << rY << " area");
585 } else if (overflowRight && overflowTop) {
586 B2DEBUG(100, "cluster extends right and above " << rX << " x " << rY << " area");
587 }
588 // find corners of cluster
589 unsigned topRight2 = topRightSquare(pattern);
590 unsigned topRight = topRightCorner(pattern[topRight2]);
591 unsigned bottomLeft = bottomLeftCorner(pattern[0]);
592 B2DEBUG(100, "topRight2 " << topRight2 << " topRight " << topRight << " bottomLeft " << bottomLeft);
593 // average over corners to find cluster center
594 unsigned ixTR = 2 * (topRight2 % m_clusterSizeX) + (topRight % 2);
595 unsigned ixBL = bottomLeft % 2;
596 unsigned iyTR = 2 * (topRight2 / m_clusterSizeX) + (topRight / 2);
597 unsigned iyBL = bottomLeft / 2;
598 B2DEBUG(100, "ixTR " << ixTR << " ixBL " << ixBL << " iyTR " << iyTR << " iyBL " << iyBL);
599 // skip size 1 clusters
600 if (m_minCells > 1 && ixTR == ixBL && iyTR == iyBL) {
601 B2DEBUG(100, "skipping cluster of size 1");
602 continue;
603 }
604 float centerX = 2 * ix + (ixTR + ixBL) / 2.;
605 if (centerX >= m_nCellsPhi) centerX -= m_nCellsPhi;
606 float centerY = 2 * iy + (iyTR + iyBL) / 2.;
607 B2DEBUG(100, "center at cell (" << centerX << ", " << centerY << ")");
608 // convert to coordinates
609 double x = -M_PI + (centerX + 0.5) * 2. * M_PI / m_nCellsPhi;
610 double y = -maxR + shiftR + (centerY + 0.5) * 2. * maxR / m_nCellsR;
611 B2DEBUG(100, "center coordinates (" << x << ", " << y << ")");
612 // get list of related hits
613 vector <unsigned> idList = {};
615 unsigned icandTR = planeIcand[(ixTR + 2 * ix) % m_nCellsPhi][iyTR + 2 * iy];
616 unsigned icandBL = planeIcand[ixBL + 2 * ix][iyBL + 2 * iy];
617 vector<unsigned> candIdListTR = houghCand[icandTR].getIdList();
618 vector<unsigned> candIdListBL = houghCand[icandBL].getIdList();
619 mergeIdList(idList, candIdListTR, candIdListBL);
620 B2DEBUG(100, "merge id lists from candidates " << icandTR << " and " << icandBL);
621 } else {
622 double dx = M_PI / m_nCellsPhi;
623 double dy = maxR / m_nCellsR;
624 double x1 = (round(centerX) == centerX) ? x - dx : x - 2 * dx;
625 double x2 = (round(centerX) == centerX) ? x + dx : x + 2 * dx;
626 double y1 = (round(centerY) == centerY) ? y - dy : y - 2 * dy;
627 double y2 = (round(centerY) == centerY) ? y + dy : y + 2 * dy;
628 findAllCrossingHits(idList, x1, x2, y1, y2, inputMap);
629 }
630 if (idList.size() == 0) {
631 setReturnValue(false);
632 B2DEBUG(100, "id list empty");
633 }
634
635 foundTrack = true;
636
637 // select 1 hit per super layer
638 vector<unsigned> selectedList = {};
639 vector<unsigned> unselectedList = {};
640 selectHits(idList, selectedList, unselectedList);
641
642 associatedTSHits.clear();
644 for (unsigned i = 0; i < selectedList.size(); ++i)
645 associatedTSHits.push_back(m_segmentHits[selectedList[i]]);
646 associatedTSHitsList.push_back(associatedTSHits);
647 } else {
648 for (unsigned i = 0; i < idList.size(); ++i)
649 associatedTSHits.push_back(m_segmentHits[idList[i]]);
650 associatedTSHitsList.push_back(associatedTSHits);
651 }
652
653 // save track
654 if (m_storeTracks) {
655 const CDCTriggerTrack* track =
656 m_tracks.appendNew(x, 2. * y, 0.);
658 // relations to selected hits
659 for (unsigned i = 0; i < selectedList.size(); ++i) {
660 unsigned its = selectedList[i];
661 track->addRelationTo(m_segmentHits[its]);
662 }
663 // relations to additional hits get a negative weight
664 for (unsigned i = 0; i < unselectedList.size(); ++i) {
665 unsigned its = unselectedList[i];
666 track->addRelationTo(m_segmentHits[its], -1.);
667 }
668 } else {
669 for (unsigned i = 0; i < idList.size(); ++i) {
670 unsigned its = idList[i];
671 track->addRelationTo(m_segmentHits[its], -1.);
672 }
673 }
674 // save detail information about the cluster
675 const CDCTriggerHoughCluster* cluster =
676 m_clusters.appendNew(2 * ix, 2 * (ix + m_clusterSizeX) - 1,
677 2 * iy, 2 * (iy + m_clusterSizeY) - 1,
678 cellIds);
679 track->addRelationTo(cluster);
680 }
681 }
682 }
683 return foundTrack;
684}
685
686/*
687* connection definitions for 2 x 2 squares
688*/
689bool
690CDCTriggerHoughETFModule::connectedLR(unsigned patternL, unsigned patternR)
691{
692 // connected if
693 // . x | x . or . . | . .
694 // . . | . . . x | x .
695 bool connectDirect = (((patternL >> 3) & 1) && ((patternR >> 2) & 1)) ||
696 (((patternL >> 1) & 1) && ((patternR >> 0) & 1));
697 // connected if
698 // . . | x .
699 // . x | . .
700 bool connectRise = ((patternL >> 1) & 1) && ((patternR >> 2) & 1);
701 // connected if
702 // . x | . .
703 // . . | x .
704 bool connectFall = ((patternL >> 3) & 1) && ((patternR >> 0) & 1);
705
706 if (m_connect == 4) return connectDirect;
707 else if (m_connect == 6) return (connectDirect || connectRise);
708 else if (m_connect == 8) return (connectDirect || connectRise || connectFall);
709 else B2WARNING("Unknown option for connect " << m_connect << ", using default.");
710 return (connectDirect || connectRise);
711}
712
713bool
714CDCTriggerHoughETFModule::connectedUD(unsigned patternD, unsigned patternU)
715{
716 // connected if
717 // . . . .
718 // x . . x
719 // --- or ---
720 // x . . x
721 // . . . .
722 bool connectDirect = (((patternU >> 0) & 1) && ((patternD >> 2) & 1)) ||
723 (((patternU >> 1) & 1) && ((patternD >> 3) & 1));
724 // connected if
725 // . .
726 // . x
727 // ---
728 // x .
729 // . .
730 bool connectRise = ((patternU >> 1) & 1) && ((patternD >> 2) & 1);
731 // connected if
732 // . .
733 // x .
734 // ---
735 // . x
736 // . .
737 bool connectFall = ((patternU >> 0) & 1) && ((patternD >> 3) & 1);
738
739 if (m_connect == 4) return connectDirect;
740 else if (m_connect == 6) return (connectDirect || connectRise);
741 else if (m_connect == 8) return (connectDirect || connectRise || connectFall);
742 else B2WARNING("Unknown option for connect " << m_connect << ", using default.");
743 return (connectDirect || connectRise);
744}
745
746bool
747CDCTriggerHoughETFModule::connectedDiag(unsigned patternLD, unsigned patternRU)
748{
749 if (m_connect == 4) return false;
750
751 // connected if
752 // . .
753 // x .
754 // . x
755 // . .
756 return (((patternRU >> 0) & 1) && ((patternLD >> 3) & 1));
757}
758
759unsigned
761{
762 // scan from top right corner until an active square is found
763 for (unsigned index = pattern.size() - 1; index > 0; --index) {
764 if (!pattern[index]) continue;
765 // check for ambiguity
766 unsigned ix = index % m_clusterSizeX;
767 unsigned iy = index / m_clusterSizeX;
768 if (ix < m_clusterSizeX - 1 && iy > 0) {
769 bool unique = true;
770 for (unsigned index2 = index - 1; index2 > 0; --index2) {
771 if (!pattern[index2]) continue;
772 unsigned ix2 = index2 % m_clusterSizeX;
773 unsigned iy2 = index2 / m_clusterSizeX;
774 if (iy2 < iy && ix2 > ix) {
775 unique = false;
776 break;
777 }
778 }
779 if (!unique) {
780 setReturnValue(false);
781 B2DEBUG(100, "topRightSquare not unique");
782 }
783 }
784 return index;
785 }
786 return 0;
787}
788
789unsigned
791{
792 // scan pattern from right to left:
793 // 2 3
794 // 0 1
795 if ((pattern >> 3) & 1) return 3;
796 if ((pattern >> 1) & 1) {
797 if ((pattern >> 2) & 1) {
798 setReturnValue(false);
799 B2DEBUG(100, "topRightCorner not unique");
800 }
801 return 1;
802 }
803 if ((pattern >> 2) & 1) return 2;
804 return 0;
805}
806
807unsigned
809{
810 // scan pattern from left to right:
811 // 2 3
812 // 0 1
813 if (pattern & 1) return 0;
814 if ((pattern >> 2) & 1) {
815 if ((pattern >> 1) & 1) {
816 setReturnValue(false);
817 B2DEBUG(100, "bottomLeftCorner not unique");
818 }
819 return 2;
820 }
821 if ((pattern >> 1) & 1) return 1;
822 return 3;
823}
824std::vector<int>
825CDCTriggerHoughETFModule::highPassTimingList()
826{
827 std::vector<int> ftlists;
828
829 for (long unsigned int iTrack = 0; iTrack < associatedTSHitsList.size(); iTrack++) {
830 for (long unsigned int iHit = 0; iHit < associatedTSHitsList[iTrack].size(); iHit++) {
831 short ft = (!m_usePriorityTiming) ? associatedTSHitsList[iTrack][iHit]->fastestTime()
832 : associatedTSHitsList[iTrack][iHit]->priorityTime();
833 ftlists.push_back(ft * 2);
834 }
835 }
836 return ftlists;
837}
838
839int
840CDCTriggerHoughETFModule::getSector(int id, int sl)
841{
842 unsigned int localid = id - TSoffset[sl];
843 unsigned int base = NTS[sl] / NSEC[sl];
844 return localid / base + NSecOffset[sl];
845}
846
847std::vector<int>
848CDCTriggerHoughETFModule::sectorTimingList()
849{
850 std::vector<int> ftlists;
851 std::array<std::vector<std::pair<int, int>>, CDC_ETF_SECTOR> arr_sector;
852
853 // distribute into each sector
854 for (long unsigned int iTrack = 0; iTrack < associatedTSHitsList.size(); iTrack++) {
855 for (long unsigned int iHit = 0; iHit < associatedTSHitsList[iTrack].size(); iHit++) {
856 int sl = associatedTSHitsList[iTrack][iHit]->getISuperLayer();
857 int id = associatedTSHitsList[iTrack][iHit]->getSegmentID();
858 int pp = associatedTSHitsList[iTrack][iHit]->getPriorityPosition();
859 int ft = associatedTSHitsList[iTrack][iHit]->fastestTime() * 2; // 2ns -> 1ns
860
861 arr_sector [getSector(id, sl)].push_back(make_pair(ft, pp));
862 }
863 }
864
865 //fastest && 1st pp in each sector
866 for (int sec = 0; sec < CDC_ETF_SECTOR; sec++) {
867 if (!arr_sector[sec].size()) continue;
868 int minft = 1e9;
869 int pp = 0;
870 for (auto& ift : arr_sector[sec]) {
871 if ((minft > ift.first) && (pp <= ift.second)) {
872 minft = ift.first;
873 pp = ift.second;
874 }
875 }
876 ftlists.push_back(minft);
877 }
878
879 return ftlists;
880}
881
882int
883CDCTriggerHoughETFModule::calcEventTiming()
884{
885 std::vector<int> ftlists;
887 ftlists = highPassTimingList();
888 } else {
889 ftlists = sectorTimingList();
890 }
891
892 if (m_t0CalcMethod == 0) {
893 std::sort(ftlists.begin(), ftlists.end());
894 return ftlists[m_arrivalOrder];
895 } else if (m_t0CalcMethod == 1) {
896 return median(ftlists);
897 } else {
898 return medianInTimeWindow(ftlists);
899 }
900}
901
902int
903CDCTriggerHoughETFModule::median(std::vector<int> v)
904{
905 int size = v.size();
906 std::vector<int> _v;
907 copy(v.begin(), v.end(), back_inserter(_v));
908 std::sort(_v.begin(), _v.end());
909 if (size % 2) {
910 return _v[(size - 1) / 2];
911 } else {
912 return (_v[(size / 2) - 1] + _v[size / 2]) / 2;
913 }
914}
915
916int
917CDCTriggerHoughETFModule::medianInTimeWindow(std::vector<int> v)
918{
919 int med = median(v);
920 int tbegin = med - abs(m_timeWindowBegin);
921 int tend = med + abs(m_timeWindowEnd);
922
923 std::vector<int> _inWindow;
924
925 for (auto& t : v) if (t > tbegin && t < tend) _inWindow.push_back(t);
926
927 if (_inWindow.size()) {
928 return median(_inWindow);
929 } else {
930 return med;
931 }
932}
unsigned getID() const
Get candidate number.
unsigned short getSLcount() const
Get super layer count.
Cluster created by the Hough finder of the CDC trigger.
int NSecOffset[9]
Number of sector offset of each super layer.
std::vector< CDCTriggerHoughCand > houghCand
Hough Candidates.
bool connectedLR(unsigned patternL, unsigned patternR)
Check for left/right connection of patterns in 2 x 2 squares.
unsigned topRightCorner(unsigned pattern)
Find the top right corner within 2 x 2 square.
int fastInterceptFinder(cdcMap &hits, double x1_s, double x2_s, double y1_s, double y2_s, unsigned iterations, unsigned ix_s, unsigned iy_s)
Fast intercept finder Divide Hough plane recursively to find cells with enough crossing lines.
bool m_useHighPassTimingList
Use associated fastest timings track-by-track.
unsigned m_minCells
minimum number of cells in a cluster to form a track
bool connectedUD(unsigned patternD, unsigned patternU)
Check for up/down connection of patterns in 2 x 2 squares.
unsigned maxIterations
number of iterations for the fast peak finder, smallest n such that 2^(n+1) > max(nCellsPhi,...
unsigned m_minHitsShort
short tracks require hits in the first minHitsShort super layers to form a candidate
unsigned m_nCellsR
number of Hough cells in 1/r
StoreArray< CDCTriggerSegmentHit > m_segmentHits
list of track segment hits
unsigned m_nCellsPhi
number of Hough cells in phi
short m_timeWindowEnd
End time of time window relative to median.
unsigned short countSL(bool *superLayers)
count the number of super layers with hits
double maxR
Hough plane limit in 1/r [1/cm].
bool m_hitRelationsFromCorners
switch for creating relations to hits in the pattern clustering algorithm.
std::vector< std::vector< CDCTriggerSegmentHit * > > associatedTSHitsList
list of fastest timing of TS associated with Track
unsigned m_storePlane
switch to save the Hough plane in DataStore (0: don't save, 1: save only peaks, 2: save full plane)
bool inList(const CDCTriggerHoughCand &a, const std::vector< CDCTriggerHoughCand > &list) const
Check if candidate is in list.
unsigned m_connect
number of neighbors to check for connection (4: direct, 6: direct + upper right and lower left corner...
double shiftR
Hough plane shift in 1/r [1/cm].
void findAllCrossingHits(std::vector< unsigned > &list, double x1, double x2, double y1, double y2, const cdcMap &inputMap)
Find all hits in inputMap whose Hough curve crosses the rectangle with corners (x1,...
unsigned nCells
number of cells for the fast peak finder: 2^(maxIterations + 1).
void selectHits(std::vector< unsigned > &list, std::vector< unsigned > &selected, std::vector< unsigned > &unselected)
Select one hit per super layer.
bool m_suppressClone
switch to send only the first found track and suppress the subsequent clones
unsigned m_t0CalcMethod
Switch method to determine the event timing.
double radius[9][2]
Radius of the CDC layers with priority wires (2 per super layer).
StoreArray< CDCTriggerTrack > m_tracks
list of found tracks
unsigned m_arrivalOrder
arrival order of fastest timing used as t0 (effective when t0CalcMEthod == 0)
unsigned m_clusterSizeX
maximum cluster size for pattern algorithm
void mergeIdList(std::vector< unsigned > &merged, std::vector< unsigned > &a, std::vector< unsigned > &b)
Merge lists a and b and put the result in merged.
bool connected(const CDCTriggerHoughCand &a, const CDCTriggerHoughCand &b) const
Check if candidates are connected.
unsigned bottomLeftCorner(unsigned pattern)
Find the bottom left corner within 2 x 2 square.
const int NSEC[9]
Number of sector in each super layer.
bool m_requireSL0
switch to check separately for a hit in the innermost super layer
short m_timeWindowBegin
Start time of time window relative to median.
unsigned m_minHits
minimum number of hits from different super layers in a Hough cell to form a candidate
StoreArray< CDCTriggerHoughCluster > m_clusters
list of clusters in the Hough map
void addNeighbors(const CDCTriggerHoughCand &center, const std::vector< CDCTriggerHoughCand > &candidates, std::vector< CDCTriggerHoughCand > &merged, std::vector< CDCTriggerHoughCand > &rejected, unsigned short nSLmax) const
Recursive function to add combine connected cells.
const int NTS[9]
Number of track segments in each super layers.
bool shortTrack(bool *superLayers)
check the short track condition (= hits in the inner super layers rather than any super layers)
bool connectedDiag(unsigned patternLD, unsigned patternRU)
Check for diagonal connected of patterns in 2 x 2 squares.
unsigned topRightSquare(std::vector< unsigned > &pattern)
Find the top right square within a cluster of 2 x 2 squares In case of ambiguity, top is favored over...
unsigned TSoffset[10]
Number of track segments up to super layer.
bool m_storeTracks
Switch to save the 2D Hough track reconstructed in this module.
bool patternClustering(const cdcMap &inputMap)
Combine Hough candidates to tracks by a fixed pattern algorithm.
bool m_onlyLocalMax
switch to ignore candidates connected to cells with higher super layer count
bool m_usePriorityTiming
Switch to use priority timing instead of fastest timing.
unsigned m_clusterSizeY
maximum cluster size for pattern algorithm
void connectedRegions()
Combine Hough candidates to tracks by merging connected cells.
Track created by the CDC trigger.
void setReturnValue(int value)
Sets the return value for this module as integer.
Definition: Module.cc:220
T * appendNew()
Construct a new T object at the end of the array.
Definition: StoreArray.h:246
int getEntries() const
Get the number of objects in the array.
Definition: StoreArray.h:216
int getID(const std::vector< double > &breaks, double t)
get id of the time point t
Definition: calibTools.h:60
std::map< int, cdcPair > cdcMap
Map of <counter, cdcPair>, for hits with indices.
std::pair< ROOT::Math::XYVector, ROOT::Math::XYVector > coord2dPair
Hough Tuples.
std::pair< unsigned short, ROOT::Math::XYVector > cdcPair
Pair of <iSuperLayer, (x, y)>, for hits in conformal space.
Abstract base class for different kinds of events.
STL namespace.