Belle II Software development
CDCTriggerHoughtrafo.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/houghtracking/CDCTrigger2DFinderModule.h>
10
11#include <framework/logging/Logger.h>
12
13#include <cmath>
14
15#include <root/TMatrix.h>
16
17/* defines */
18#define CDC_SUPER_LAYERS 45
19
20using namespace std;
21using namespace Belle2;
22
23unsigned short
25{
26 if (m_requireSL0 && !layer[0]) return 0;
27 unsigned short lcnt = 0;
28 for (int i = 0; i < CDC_SUPER_LAYERS; ++i) {
29 if (layer[i] == true) ++lcnt;
30 }
31 return lcnt;
32}
33
34bool
36{
37 unsigned short lcnt = 0;
38 // check axial super layers (even layer number),
39 // break at first layer without hit
40 for (int i = 0; i < CDC_SUPER_LAYERS; i += 2) {
41 if (layer[i] == true) ++lcnt;
42 else break;
43 }
44 return (lcnt >= m_minHitsShort);
45}
46
47/*
48* Find the intercept in hough space.
49* In each iteration the Hough plane is divided in quarters.
50* For each quarter containing enough hits, the process is repeated
51* until the quarters correspond to single Hough cells.
52* Return zero on success.
53* x = m
54* y = a
55*/
56int
58 double x1_s, double x2_s,
59 double y1_s, double y2_s,
60 unsigned iterations,
61 unsigned ix_s, unsigned iy_s)
62{
63 string indent = "";
64 for (unsigned i = 0; i < iterations; ++i) indent += " ";
65 B2DEBUG(150, indent << "intercept finder iteration " << iterations
66 << " x1 " << x1_s * 180 / M_PI << " x2 " << x2_s * 180 / M_PI
67 << " y1 " << y1_s << " y2 " << y2_s
68 << " ix " << ix_s << " iy " << iy_s);
69
70 int i, j, iHit;
71 double unitx, unity;
72 double y1, y2;
73 double m, a;
74 cdcPair hp;
75 unsigned short iSL;
76 // list of hit indices crossing the current rectangle
77 vector<unsigned> idx_list;
78
79 // limits of divided rectangles
80 double x1_d, x2_d, y1_d, y2_d;
81
82 // cell indices in full plane
83 unsigned ix, iy;
84
85 unitx = ((x2_s - x1_s) / 2.0);
86 unity = ((y2_s - y1_s) / 2.0);
87
88 // divide x axis in half
89 for (i = 0; i < 2 ; ++i) {
90 // divide y axis in half
91 for (j = 0; j < 2; ++j) {
92 x1_d = x1_s + i * unitx;
93 x2_d = x1_s + (i + 1) * unitx;
94 y1_d = y1_s + j * unity;
95 y2_d = y1_s + (j + 1) * unity;
96
97 ix = 2 * ix_s + i;
98 iy = 2 * iy_s + j;
99
100 // skip extra cells outside of the Hough plane
101 if (x2_d <= -M_PI || x1_d >= M_PI || y2_d <= -maxR + shiftR || y1_d >= maxR + shiftR) {
102 B2DEBUG(150, indent << "skip Hough cell outside of plane limits");
103 continue;
104 }
105
106 // if the cell size in phi is too large, the hit calculation is not reliable
107 // -> continue to next iteration without hit check
108 if (iterations != maxIterations && unitx > M_PI / 2.) {
109 fastInterceptFinder(hits, x1_d, x2_d, y1_d, y2_d, iterations + 1, ix, iy);
110 continue;
111 }
112
113 idx_list.clear();
114 bool layerHit[CDC_SUPER_LAYERS] = {false}; /* For layer filter */
115 for (auto it = hits.begin(); it != hits.end(); ++it) {
116 iHit = it->first;
117 hp = it->second;
118 iSL = hp.first;
119 m = hp.second.X();
120 a = hp.second.Y();
121
122 // calculate Hough curve with slightly enlarged limits to avoid errors due to rounding
123 y1 = m * sin(x1_d - 1e-10) - a * cos(x1_d - 1e-10);
124 y2 = m * sin(x2_d + 1e-10) - a * cos(x2_d + 1e-10);
125 // skip decreasing half of the sine (corresponds to curl back half of the track)
126 if (iterations == maxIterations && y1 > y2) continue;
127 if (!((y1 > y2_d && y2 > y2_d) || (y1 < y1_d && y2 < y1_d))) {
128 if (iterations == maxIterations) {
129 idx_list.push_back(iHit);
130 }
131 layerHit[iSL] = true; /* layer filter */
132 }
133 }
134 unsigned short nSL = countSL(layerHit);
135 B2DEBUG(150, indent << "i " << i << " j " << j
136 << " layerHit " << int(layerHit[0]) << int(layerHit[2])
137 << int(layerHit[4]) << int(layerHit[6]) << int(layerHit[8])
138 << " nSL " << nSL);
139 //std::cout << indent << "i " << i << " j " << j
140 // << " layerHit " << int(layerHit[0]) << int(layerHit[2])
141 // << int(layerHit[4]) << int(layerHit[6]) << int(layerHit[8])
142 // << " nSL " << nSL << std::endl;
143 if (nSL >= m_minHits || shortTrack(layerHit)) {
144 if (iterations != maxIterations) {
145 fastInterceptFinder(hits, x1_d, x2_d, y1_d, y2_d, iterations + 1, ix, iy);
146 } else {
147 ROOT::Math::XYVector v1(x1_d, y1_d);
148 ROOT::Math::XYVector v2(x2_d, y2_d);
149 houghCand.push_back(CDCTriggerHoughCand(idx_list, make_pair(v1, v2),
150 nSL, houghCand.size()));
151 if (m_storePlane > 0) {
152 (*m_houghPlane)[ix - (nCells - m_nCellsPhi) / 2][iy - (nCells - m_nCellsR) / 2] = nSL;
153 }
154 }
155 } else if (m_storePlane > 1) {
156 // to store the full Hough plane, we need to continue the iteration
157 // to minimal cell size everywhere
158 for (unsigned min = m_minHits - 1; min > 0; --min) {
159 if (nSL >= min) {
160 if (iterations != maxIterations) {
161 fastInterceptFinder(hits, x1_d, x2_d, y1_d, y2_d, iterations + 1, ix, iy);
162 } else {
163 (*m_houghPlane)[ix - (nCells - m_nCellsPhi) / 2][iy - (nCells - m_nCellsR) / 2] = nSL;
164 }
165 break;
166 }
167 }
168 }
169 }
170 }
171
172 return 0;
173}
174
175/*
176* Peak finding method: connected regions & center of gravity
177*/
178void
180{
181 vector<vector<CDCTriggerHoughCand>> regions;
182 vector<CDCTriggerHoughCand> cpyCand = houghCand;
183
184 // debug: print candidate list
185 B2DEBUG(50, "houghCand number " << cpyCand.size());
186 for (unsigned icand = 0; icand < houghCand.size(); ++icand) {
187 coord2dPair coord = houghCand[icand].getCoord();
188 B2DEBUG(100, houghCand[icand].getID()
189 << " nSL " << houghCand[icand].getSLcount()
190 << " x1 " << coord.first.X() << " x2 " << coord.second.X()
191 << " y1 " << coord.first.Y() << " y2 " << coord.second.Y());
192 }
193
194 // combine connected cells to regions
195 while (cpyCand.size() > 0) {
196 B2DEBUG(100, "make new region");
197 vector<CDCTriggerHoughCand> region;
198 vector<CDCTriggerHoughCand> rejected;
199 CDCTriggerHoughCand start = cpyCand[0];
200 cpyCand.erase(cpyCand.begin());
201 region.push_back(start);
202 addNeighbors(start, cpyCand, region, rejected, start.getSLcount());
203 regions.push_back(region);
204 for (auto cand = cpyCand.begin(); cand != cpyCand.end();) {
205 if (inList(*cand, region) || inList(*cand, rejected))
206 cpyCand.erase(cand);
207 else
208 ++cand;
209 }
210 }
211
212 // find center of gravity for each region
213 for (unsigned ir = 0; ir < regions.size(); ++ir) {
214 B2DEBUG(50, "region " << ir << " (" << regions[ir].size() << " cells).");
215 // skip regions with size below cut
216 if (regions[ir].size() < m_minCells) {
217 B2DEBUG(50, "Skipping region with " << regions[ir].size() << " cells.");
218 continue;
219 }
220 double xfirst = regions[ir][0].getCoord().first.X();
221 double x = 0;
222 double y = 0;
223 int n = 0;
224 vector<unsigned> mergedList;
225 int xmin = m_nCellsPhi;
226 int xmax = 0;
227 int ymin = m_nCellsR;
228 int ymax = 0;
229 vector<ROOT::Math::XYVector> cellIds = {};
230 for (unsigned ir2 = 0; ir2 < regions[ir].size(); ++ir2) {
231 coord2dPair hc = regions[ir][ir2].getCoord();
232 B2DEBUG(100, " " << regions[ir][ir2].getID()
233 << " nSL " << regions[ir][ir2].getSLcount()
234 << " x1 " << hc.first.X() << " x2 " << hc.second.X()
235 << " y1 " << hc.first.Y() << " y2 " << hc.second.Y());
236 int ix = floor((hc.first.X() + M_PI) / 2. / M_PI * m_nCellsPhi + 0.5);
237 int iy = floor((hc.first.Y() + maxR - shiftR) / 2. / maxR * m_nCellsR + 0.5);
238 x += (hc.first.X() + hc.second.X());
239 if (xfirst - hc.first.X() > M_PI) {
240 x += 4 * M_PI;
241 ix += m_nCellsPhi;
242 } else if (hc.first.X() - xfirst > M_PI) {
243 x -= 4 * M_PI;
244 ix -= m_nCellsPhi;
245 }
246 y += (hc.first.Y() + hc.second.Y());
247 n += 1;
248 vector<unsigned> idList = regions[ir][ir2].getIdList();
249 mergeIdList(mergedList, mergedList, idList);
250 xmin = min(xmin, ix);
251 xmax = max(xmax, ix);
252 ymin = min(ymin, iy);
253 ymax = max(ymax, iy);
254 cellIds.push_back(ROOT::Math::XYVector(ix, iy));
255 }
256 x *= 0.5 / n;
257 if (x > M_PI)
258 x -= 2 * M_PI;
259 else if (x <= -M_PI)
260 x += 2 * M_PI;
261 y *= 0.5 / n;
262 B2DEBUG(50, "x " << x << " y " << y);
263
264 // select 1 hit per super layer
265 vector<unsigned> selectedList = {};
266 vector<unsigned> unselectedList = {};
267 selectHits(mergedList, selectedList, unselectedList);
268
269 // save track
270 const CDCTriggerTrack* track =
271 m_tracks.appendNew(x, 2. * y, 0.);
272 // relations to selected hits
273 for (unsigned i = 0; i < selectedList.size(); ++i) {
274 unsigned its = selectedList[i];
275 track->addRelationTo(m_segmentHits[its]);
276 }
277 // relations to additional hits get a negative weight
278 for (unsigned i = 0; i < unselectedList.size(); ++i) {
279 unsigned its = unselectedList[i];
280 track->addRelationTo(m_segmentHits[its], -1.);
281 }
282 // save detail information about the cluster
283 const CDCTriggerHoughCluster* cluster =
284 m_clusters.appendNew(xmin, xmax, ymin, ymax, cellIds);
285 track->addRelationTo(cluster);
286 }
287}
288
289void
291 const vector<CDCTriggerHoughCand>& candidates,
292 vector<CDCTriggerHoughCand>& merged,
293 vector<CDCTriggerHoughCand>& rejected,
294 unsigned short nSLmax) const
295{
296 for (unsigned icand = 0; icand < candidates.size(); ++icand) {
297 B2DEBUG(120, "compare center " << center.getID()
298 << " to " << candidates[icand].getID());
299 if (inList(candidates[icand], merged) || inList(candidates[icand], rejected)) {
300 B2DEBUG(120, " " << candidates[icand].getID() << " already in list");
301 continue;
302 }
303 bool reject = inList(center, rejected);
304 if (connected(center, candidates[icand])) {
305 if (m_onlyLocalMax && candidates[icand].getSLcount() < nSLmax) {
306 B2DEBUG(100, " lower than highest SLcount, rejected");
307 rejected.push_back(candidates[icand]);
308 } else if (m_onlyLocalMax && !reject && candidates[icand].getSLcount() > nSLmax) {
309 B2DEBUG(100, " new highest SLcount, clearing list");
310 nSLmax = candidates[icand].getSLcount();
311 for (unsigned imerged = 0; imerged < merged.size(); ++imerged) {
312 rejected.push_back(merged[imerged]);
313 }
314 merged.clear();
315 merged.push_back(candidates[icand]);
316 } else if (m_onlyLocalMax && candidates[icand].getSLcount() > center.getSLcount()) {
317 B2DEBUG(100, " connected to rejected cell, skip");
318 continue;
319 } else if (m_onlyLocalMax && reject) {
320 B2DEBUG(100, " connected to rejected cell, rejected");
321 rejected.push_back(candidates[icand]);
322 } else {
323 B2DEBUG(100, " connected");
324 merged.push_back(candidates[icand]);
325 }
326 vector<CDCTriggerHoughCand> cpyCand = candidates;
327 cpyCand.erase(cpyCand.begin() + icand);
328 addNeighbors(candidates[icand], cpyCand, merged, rejected, nSLmax);
329 }
330 }
331}
332
333bool
335 const vector<CDCTriggerHoughCand>& list) const
336{
337 for (unsigned i = 0; i < list.size(); ++i) {
338 if (a == list[i]) return true;
339 }
340 return false;
341}
342
343bool
345 const CDCTriggerHoughCand& b) const
346{
347 double ax1 = a.getCoord().first.X();
348 double ax2 = a.getCoord().second.X();
349 double ay1 = a.getCoord().first.Y();
350 double ay2 = a.getCoord().second.Y();
351 double bx1 = b.getCoord().first.X();
352 double bx2 = b.getCoord().second.X();
353 double by1 = b.getCoord().first.Y();
354 double by2 = b.getCoord().second.Y();
355 // direct neighbors
356 bool direct = ((ax2 == bx1 && ay1 == by1) || // right
357 (ax1 == bx2 && ay1 == by1) || // left
358 (ax1 == bx1 && ay2 == by1) || // above
359 (ax1 == bx1 && ay1 == by2) || // below
360 (ax1 + 2. * M_PI == bx2 && ay1 == by1) ||
361 (ax2 == bx1 + 2. * M_PI && ay1 == by1));
362 // diagonal connections
363 bool diagRise = ((ax2 == bx1 && ay2 == by1) || // right above
364 (ax1 == bx2 && ay1 == by2) || // left below
365 (ax1 + 2. * M_PI == bx2 && ay1 == by2) ||
366 (ax2 == bx1 + 2. * M_PI && ay2 == by1));
367 bool diagFall = ((ax1 == bx2 && ay2 == by1) || // left above
368 (ax2 == bx1 && ay1 == by2) || // right below
369 (ax2 == bx1 + 2. * M_PI && ay1 == by2) ||
370 (ax1 + 2. * M_PI == bx2 && ay2 == by1));
371 if (m_connect == 4) return direct;
372 else if (m_connect == 6) return (direct || diagRise);
373 else if (m_connect == 8) return (direct || diagRise || diagFall);
374 else B2WARNING("Unknown option for connect " << m_connect << ", using default.");
375 return (direct || diagRise);
376}
377
378/*
379* Merge Id lists.
380*/
381void
382CDCTrigger2DFinderModule::mergeIdList(std::vector<unsigned>& merged,
383 std::vector<unsigned>& a,
384 std::vector<unsigned>& b)
385{
386 bool found;
387
388 for (auto it = a.begin(); it != a.end(); ++it) {
389 found = false;
390 for (auto it_in = merged.begin(); it_in != merged.end(); ++it_in) {
391 if (*it_in == *it) {
392 found = true;
393 break;
394 }
395 }
396 if (!found) {
397 merged.push_back(*it);
398 }
399 }
400
401 for (auto it = b.begin(); it != b.end(); ++it) {
402 found = false;
403 for (auto it_in = merged.begin(); it_in != merged.end(); ++it_in) {
404 if (*it_in == *it) {
405 found = true;
406 break;
407 }
408 }
409 if (!found) {
410 merged.push_back(*it);
411 }
412 }
413}
414
415void
417 double x1, double x2,
418 double y1, double y2,
419 const cdcMap& inputMap)
420{
421 double m, a, y1_h, y2_h;
422 for (int iHit = 0; iHit < m_segmentHits.getEntries(); iHit++) {
423 unsigned short iSL = m_segmentHits[iHit]->getISuperLayer();
424 if (iSL % 2) continue;
425 // associate only the hits actually used to form the cluster
426 if (m_suppressClone && inputMap.find(iHit) == inputMap.end()) continue;
427 //TODO: add options: center cell / active priority cell / all priority cells
428 vector<double> phi = {0, 0, 0};
429 phi[0] = m_segmentHits[iHit]->getSegmentID() - TSoffset[iSL];
430 phi[1] = phi[0] + 0.5;
431 phi[2] = phi[0] - 0.5;
432 vector<double> r = {radius[iSL][0], radius[iSL][1], radius[iSL][1]};
433 for (unsigned i = 0; i < 3; ++i) {
434 phi[i] *= 2. * M_PI / (TSoffset[iSL + 1] - TSoffset[iSL]);
435 m = cos(phi[i]) / r[i];
436 a = sin(phi[i]) / r[i];
437 // calculate Hough curve with slightly enlarged limits to avoid errors due to rounding
438 y1_h = m * sin(x1 - 1e-10) - a * cos(x1 - 1e-10);
439 y2_h = m * sin(x2 + 1e-10) - a * cos(x2 + 1e-10);
440 // skip decreasing half of the sine (corresponds to curl back half of the track)
441 if (y1_h > y2_h) continue;
442 if (!((y1_h > y2 && y2_h > y2) || (y1_h < y1 && y2_h < y1))) {
443 list.push_back(iHit);
444 break;
445 }
446 }
447 }
448}
449
450/*
451 * Select one hit per super layer
452 */
453void
454CDCTrigger2DFinderModule::selectHits(std::vector<unsigned>& list,
455 std::vector<unsigned>& selected,
456 std::vector<unsigned>& unselected)
457{
458 std::vector<int> bestPerSL(5, -1);
459 for (unsigned i = 0; i < list.size(); ++i) {
460 unsigned iax = m_segmentHits[list[i]]->getISuperLayer() / 2;
461 bool firstPriority = (m_segmentHits[list[i]]->getPriorityPosition() == 3);
462 if (bestPerSL[iax] < 0) {
463 bestPerSL[iax] = i;
464 } else {
465 unsigned itsBest = list[bestPerSL[iax]];
466 bool firstBest = (m_segmentHits[itsBest]->getPriorityPosition() == 3);
467 // selection rules:
468 // first priority, higher ID
469 if ((firstPriority && !firstBest) ||
470 (firstPriority == firstBest &&
471 m_segmentHits[list[i]]->getSegmentID() > m_segmentHits[itsBest]->getSegmentID())) {
472 bestPerSL[iax] = i;
473 }
474 }
475 }
476
477 for (unsigned i = 0; i < list.size(); ++i) {
478 unsigned iax = m_segmentHits[list[i]]->getISuperLayer() / 2;
479 if (int(i) == bestPerSL[iax]) selected.push_back(list[i]);
480 else unselected.push_back(list[i]);
481 }
482}
483
484/*
485* Alternative peak finding with nested patterns
486*/
487void
489{
490 // fill a matrix of 2 x 2 squares
491 TMatrix plane2(m_nCellsPhi / 2, m_nCellsR / 2);
492 TMatrix planeIcand(m_nCellsPhi, m_nCellsR);
493 for (unsigned icand = 0; icand < houghCand.size(); ++icand) {
494 double x = (houghCand[icand].getCoord().first.X() +
495 houghCand[icand].getCoord().second.X()) / 2.;
496 unsigned ix = floor((x + M_PI) / 2. / M_PI * m_nCellsPhi);
497 double y = (houghCand[icand].getCoord().first.Y() +
498 houghCand[icand].getCoord().second.Y()) / 2.;
499 unsigned iy = floor((y + maxR - shiftR) / 2. / maxR * m_nCellsR);
500 plane2[ix / 2][iy / 2] += 1 << ((ix % 2) + 2 * (iy % 2));
501 planeIcand[ix][iy] = icand;
502 B2DEBUG(100, "candidate " << icand << " at ix " << ix << " iy " << iy);
503 }
504 // look for clusters of 2 x 2 squares in a (rX x rY) region
505 unsigned nX = m_nCellsPhi / 2;
506 unsigned nY = m_nCellsR / 2;
507 unsigned rX = m_clusterSizeX;
508 unsigned rY = m_clusterSizeY;
509 for (unsigned ix = 0; ix < nX; ++ix) {
510 for (unsigned iy = 0; iy < nY; ++iy) {
511 if (!plane2[ix][iy]) continue;
512 // check if we are in a lower left corner
513 unsigned ileft = (ix - 1 + nX) % nX;
514 B2DEBUG(100, "ix " << ix << " iy " << iy);
515 if (connectedLR(plane2[ileft][iy], plane2[ix][iy]) ||
516 (iy > 0 && connectedUD(plane2[ix][iy - 1], plane2[ix][iy])) ||
517 (iy > 0 && connectedDiag(plane2[ileft][iy - 1], plane2[ix][iy]))) {
518 B2DEBUG(100, "skip connected square");
519 continue;
520 }
521 // form cluster
522 vector<unsigned> pattern(rX * rY, 0);
523 pattern[0] = plane2[ix][iy];
524 vector<ROOT::Math::XYVector> cellIds = {ROOT::Math::XYVector(2 * ix, 2 * iy)};
525 for (unsigned ix2 = 0; ix2 < rX; ++ix2) {
526 for (unsigned iy2 = 0; iy2 < rY; ++iy2) {
527 if (iy + iy2 >= nY) continue;
528 unsigned ip = ix2 + rX * iy2;
529 unsigned iright = (ix + ix2 + nX) % nX;
530 ileft = (iright - 1 + nX) % nX;
531 B2DEBUG(100, "ix2 " << ix2 << " ileft " << ileft << " iright " << iright);
532 if ((ix2 > 0 && // check left/right connection
533 pattern[ip - 1] &&
534 connectedLR(plane2[ileft][iy + iy2], plane2[iright][iy + iy2])) ||
535 (iy2 > 0 && // check up/down connection
536 pattern[ip - rX] &&
537 connectedUD(plane2[iright][iy + iy2 - 1], plane2[iright][iy + iy2])) ||
538 (ix2 > 0 && iy2 > 0 && // check diagonal connection
539 pattern[ip - rX - 1] &&
540 connectedDiag(plane2[ileft][iy + iy2 - 1], plane2[iright][iy + iy2]))) {
541 pattern[ip] = plane2[iright][iy + iy2];
542 B2DEBUG(100, "connect cell " << iright << " " << iy + iy2);
543 cellIds.push_back(ROOT::Math::XYVector(2 * (ix + ix2), 2 * (iy + iy2)));
544 }
545 }
546 }
547 B2DEBUG(100, "cluster starting at " << ix << " " << iy);
548 // check if cluster continues beyond defined area
549 bool overflowRight = false;
550 bool overflowTop = false;
551 for (unsigned iy2 = 0; iy2 < rY; ++iy2) {
552 unsigned ip = rX - 1 + rX * iy2;
553 if (!pattern[ip]) continue;
554 unsigned iright = (ix + rX + nX) % nX;
555 ileft = (iright - 1 + nX) % nX;
556 if (connectedLR(plane2[ileft][iy + iy2], plane2[iright][iy + iy2]) ||
557 ((iy + iy2 + 1 < nY) &&
558 connectedDiag(plane2[ileft][iy + iy2], plane2[iright][iy + iy2 + 1]))) {
559 setReturnValue(false);
560 overflowRight = true;
561 }
562 }
563 if (iy + rY < nY) {
564 for (unsigned ix2 = 0; ix2 < rX; ++ix2) {
565 unsigned ip = ix2 + rX * (rY - 1);
566 if (!pattern[ip]) continue;
567 unsigned iright = (ix + ix2 + 1 + nX) % nX;
568 ileft = (iright - 1 + nX) % nX;
569 if (connectedUD(plane2[ileft][iy + rY - 1], plane2[ileft][iy + rY]) ||
570 connectedDiag(plane2[ileft][iy + rY - 1], plane2[iright][iy + rY])) {
571 setReturnValue(false);
572 overflowTop = true;
573 }
574 }
575 }
576 if (overflowRight && !overflowTop) {
577 B2DEBUG(100, "cluster extends right of " << rX << " x " << rY << " area");
578 } else if (overflowTop && !overflowRight) {
579 B2DEBUG(100, "cluster extends above " << rX << " x " << rY << " area");
580 } else if (overflowRight && overflowTop) {
581 B2DEBUG(100, "cluster extends right and above " << rX << " x " << rY << " area");
582 }
583 // find corners of cluster
584 unsigned topRight2 = topRightSquare(pattern);
585 unsigned topRight = topRightCorner(pattern[topRight2]);
586 unsigned bottomLeft = bottomLeftCorner(pattern[0]);
587 B2DEBUG(100, "topRight2 " << topRight2 << " topRight " << topRight << " bottomLeft " << bottomLeft);
588 // average over corners to find cluster center
589 unsigned ixTR = 2 * (topRight2 % m_clusterSizeX) + (topRight % 2);
590 unsigned ixBL = bottomLeft % 2;
591 unsigned iyTR = 2 * (topRight2 / m_clusterSizeX) + (topRight / 2);
592 unsigned iyBL = bottomLeft / 2;
593 B2DEBUG(100, "ixTR " << ixTR << " ixBL " << ixBL << " iyTR " << iyTR << " iyBL " << iyBL);
594 // skip size 1 clusters
595 if (m_minCells > 1 && ixTR == ixBL && iyTR == iyBL) {
596 B2DEBUG(100, "skipping cluster of size 1");
597 continue;
598 }
599 float centerX = 2 * ix + (ixTR + ixBL) / 2.;
600 if (centerX >= m_nCellsPhi) centerX -= m_nCellsPhi;
601 float centerY = 2 * iy + (iyTR + iyBL) / 2.;
602 B2DEBUG(100, "center at cell (" << centerX << ", " << centerY << ")");
603 // convert to coordinates
604 double x = -M_PI + (centerX + 0.5) * 2. * M_PI / m_nCellsPhi;
605 double y = -maxR + shiftR + (centerY + 0.5) * 2. * maxR / m_nCellsR;
606 B2DEBUG(100, "center coordinates (" << x << ", " << y << ")");
607 // get list of related hits
608 vector <unsigned> idList = {};
610 unsigned icandTR = planeIcand[(ixTR + 2 * ix) % m_nCellsPhi][iyTR + 2 * iy];
611 unsigned icandBL = planeIcand[ixBL + 2 * ix][iyBL + 2 * iy];
612 vector<unsigned> candIdListTR = houghCand[icandTR].getIdList();
613 vector<unsigned> candIdListBL = houghCand[icandBL].getIdList();
614 mergeIdList(idList, candIdListTR, candIdListBL);
615 B2DEBUG(100, "merge id lists from candidates " << icandTR << " and " << icandBL);
616 } else {
617 double dx = M_PI / m_nCellsPhi;
618 double dy = maxR / m_nCellsR;
619 double x1 = (round(centerX) == centerX) ? x - dx : x - 2 * dx;
620 double x2 = (round(centerX) == centerX) ? x + dx : x + 2 * dx;
621 double y1 = (round(centerY) == centerY) ? y - dy : y - 2 * dy;
622 double y2 = (round(centerY) == centerY) ? y + dy : y + 2 * dy;
623 findAllCrossingHits(idList, x1, x2, y1, y2, inputMap);
624 }
625 if (idList.size() == 0) {
626 setReturnValue(false);
627 B2DEBUG(100, "id list empty");
628 }
629
630 // select 1 hit per super layer
631 vector<unsigned> selectedList = {};
632 vector<unsigned> unselectedList = {};
633 selectHits(idList, selectedList, unselectedList);
634
635 // save track
636 const CDCTriggerTrack* track =
637 m_tracks.appendNew(x, 2. * y, 0.);
638 // relations to selected hits
639 for (unsigned i = 0; i < selectedList.size(); ++i) {
640 unsigned its = selectedList[i];
641 track->addRelationTo(m_segmentHits[its]);
642 }
643 // relations to additional hits get a negative weight
644 for (unsigned i = 0; i < unselectedList.size(); ++i) {
645 unsigned its = unselectedList[i];
646 track->addRelationTo(m_segmentHits[its], -1.);
647 }
648 // save detail information about the cluster
649 const CDCTriggerHoughCluster* cluster =
650 m_clusters.appendNew(2 * ix, 2 * (ix + m_clusterSizeX) - 1,
651 2 * iy, 2 * (iy + m_clusterSizeY) - 1,
652 cellIds);
653 track->addRelationTo(cluster);
654 }
655 }
656}
657
658/*
659* connection definitions for 2 x 2 squares
660*/
661bool
662CDCTrigger2DFinderModule::connectedLR(unsigned patternL, unsigned patternR)
663{
664 // connected if
665 // . x | x . or . . | . .
666 // . . | . . . x | x .
667 bool connectDirect = (((patternL >> 3) & 1) && ((patternR >> 2) & 1)) ||
668 (((patternL >> 1) & 1) && ((patternR >> 0) & 1));
669 // connected if
670 // . . | x .
671 // . x | . .
672 bool connectRise = ((patternL >> 1) & 1) && ((patternR >> 2) & 1);
673 // connected if
674 // . x | . .
675 // . . | x .
676 bool connectFall = ((patternL >> 3) & 1) && ((patternR >> 0) & 1);
677
678 if (m_connect == 4) return connectDirect;
679 else if (m_connect == 6) return (connectDirect || connectRise);
680 else if (m_connect == 8) return (connectDirect || connectRise || connectFall);
681 else B2WARNING("Unknown option for connect " << m_connect << ", using default.");
682 return (connectDirect || connectRise);
683}
684
685bool
686CDCTrigger2DFinderModule::connectedUD(unsigned patternD, unsigned patternU)
687{
688 // connected if
689 // . . . .
690 // x . . x
691 // --- or ---
692 // x . . x
693 // . . . .
694 bool connectDirect = (((patternU >> 0) & 1) && ((patternD >> 2) & 1)) ||
695 (((patternU >> 1) & 1) && ((patternD >> 3) & 1));
696 // connected if
697 // . .
698 // . x
699 // ---
700 // x .
701 // . .
702 bool connectRise = ((patternU >> 1) & 1) && ((patternD >> 2) & 1);
703 // connected if
704 // . .
705 // x .
706 // ---
707 // . x
708 // . .
709 bool connectFall = ((patternU >> 0) & 1) && ((patternD >> 3) & 1);
710
711 if (m_connect == 4) return connectDirect;
712 else if (m_connect == 6) return (connectDirect || connectRise);
713 else if (m_connect == 8) return (connectDirect || connectRise || connectFall);
714 else B2WARNING("Unknown option for connect " << m_connect << ", using default.");
715 return (connectDirect || connectRise);
716}
717
718bool
719CDCTrigger2DFinderModule::connectedDiag(unsigned patternLD, unsigned patternRU)
720{
721 if (m_connect == 4) return false;
722
723 // connected if
724 // . .
725 // x .
726 // . x
727 // . .
728 return (((patternRU >> 0) & 1) && ((patternLD >> 3) & 1));
729}
730
731unsigned
733{
734 // scan from top right corner until an active square is found
735 for (unsigned index = pattern.size() - 1; index > 0; --index) {
736 if (!pattern[index]) continue;
737 // check for ambiguity
738 unsigned ix = index % m_clusterSizeX;
739 unsigned iy = index / m_clusterSizeX;
740 if (ix < m_clusterSizeX - 1 && iy > 0) {
741 bool unique = true;
742 for (unsigned index2 = index - 1; index2 > 0; --index2) {
743 if (!pattern[index2]) continue;
744 unsigned ix2 = index2 % m_clusterSizeX;
745 unsigned iy2 = index2 / m_clusterSizeX;
746 if (iy2 < iy && ix2 > ix) {
747 unique = false;
748 break;
749 }
750 }
751 if (!unique) {
752 setReturnValue(false);
753 B2DEBUG(100, "topRightSquare not unique");
754 }
755 }
756 return index;
757 }
758 return 0;
759}
760
761unsigned
763{
764 // scan pattern from right to left:
765 // 2 3
766 // 0 1
767 if ((pattern >> 3) & 1) return 3;
768 if ((pattern >> 1) & 1) {
769 if ((pattern >> 2) & 1) {
770 setReturnValue(false);
771 B2DEBUG(100, "topRightCorner not unique");
772 }
773 return 1;
774 }
775 if ((pattern >> 2) & 1) return 2;
776 return 0;
777}
778
779unsigned
781{
782 // scan pattern from left to right:
783 // 2 3
784 // 0 1
785 if (pattern & 1) return 0;
786 if ((pattern >> 2) & 1) {
787 if ((pattern >> 1) & 1) {
788 setReturnValue(false);
789 B2DEBUG(100, "bottomLeftCorner not unique");
790 }
791 return 2;
792 }
793 if ((pattern >> 1) & 1) return 1;
794 return 3;
795}
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.
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
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.
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.
void patternClustering(const cdcMap &inputMap)
Combine Hough candidates to tracks by a fixed pattern algorithm.
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
StoreArray< CDCTriggerTrack > m_tracks
list of found tracks
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.
bool m_requireSL0
switch to check separately for a hit in the innermost super layer
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.
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.
double radius[9][5]
Radius of the CDC layers with priority wires (2 per super layer).
bool m_onlyLocalMax
switch to ignore candidates connected to cells with higher super layer count
unsigned m_clusterSizeY
maximum cluster size for pattern algorithm
void connectedRegions()
Combine Hough candidates to tracks by merging connected cells.
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.
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.