Belle II Software  release-08-01-10
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 
26 using namespace std;
27 using namespace Belle2;
28 
29 
30 unsigned short
31 CDCTriggerHoughETFModule::countSL(bool* layer)
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 
41 bool
42 CDCTriggerHoughETFModule::shortTrack(bool* layer)
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 */
63 int
64 CDCTriggerHoughETFModule::fastInterceptFinder(cdcMap& hits,
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  TVector2 v1(x1_d, y1_d);
150  TVector2 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 */
180 void
181 CDCTriggerHoughETFModule::connectedRegions()
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<TVector2> 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(TVector2(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 
291 void
292 CDCTriggerHoughETFModule::addNeighbors(const CDCTriggerHoughCand& center,
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 
335 bool
336 CDCTriggerHoughETFModule::inList(const CDCTriggerHoughCand& a,
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 
345 bool
346 CDCTriggerHoughETFModule::connected(const CDCTriggerHoughCand& a,
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 */
383 void
384 CDCTriggerHoughETFModule::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 
417 void
418 CDCTriggerHoughETFModule::findAllCrossingHits(std::vector<unsigned>& list,
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  */
455 void
456 CDCTriggerHoughETFModule::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 */
489 bool
490 CDCTriggerHoughETFModule::patternClustering(const cdcMap& inputMap)
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<TVector2> cellIds = {TVector2(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(TVector2(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 = {};
614  if (m_hitRelationsFromCorners) {
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();
643  if (m_useHighPassTimingList) {
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.);
657  if (m_useHighPassTimingList) {
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 */
689 bool
690 CDCTriggerHoughETFModule::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 
713 bool
714 CDCTriggerHoughETFModule::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 
746 bool
747 CDCTriggerHoughETFModule::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 
759 unsigned
760 CDCTriggerHoughETFModule::topRightSquare(vector<unsigned>& pattern)
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 
789 unsigned
790 CDCTriggerHoughETFModule::topRightCorner(unsigned pattern)
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 
807 unsigned
808 CDCTriggerHoughETFModule::bottomLeftCorner(unsigned pattern)
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 }
824 std::vector<int>
825 CDCTriggerHoughETFModule::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 
839 int
840 CDCTriggerHoughETFModule::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 
847 std::vector<int>
848 CDCTriggerHoughETFModule::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 
882 int
883 CDCTriggerHoughETFModule::calcEventTiming()
884 {
885  std::vector<int> ftlists;
886  if (m_useHighPassTimingList) {
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 
902 int
903 CDCTriggerHoughETFModule::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 
916 int
917 CDCTriggerHoughETFModule::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.
Track created by the CDC trigger.
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< unsigned short, TVector2 > cdcPair
Pair of <iSuperLayer, (x, y)>, for hits in conformal space.
std::pair< TVector2, TVector2 > coord2dPair
Hough Tuples.
Abstract base class for different kinds of events.