Belle II Software  release-06-02-00
ECLNeighbours.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 // ECL GEOMETRY
10 #include <ecl/geometry/ECLNeighbours.h>
11 #include <ecl/geometry/ECLGeometryPar.h>
12 
13 // FRAMEWORK
14 #include <framework/logging/Logger.h>
15 #include <framework/gearbox/Unit.h>
16 
17 // OTHER
18 #include "TMath.h"
19 #include "TVector3.h"
20 
21 using namespace Belle2;
22 using namespace ECL;
23 
24 // Constructor.
25 ECLNeighbours::ECLNeighbours(const std::string& neighbourDef, const double par)
26 {
27  // resize the vector
28  std::vector<short int> fakeneighbours;
29  fakeneighbours.push_back(0); // insert one fake to avoid the "0" entry
30  m_neighbourMap.push_back(fakeneighbours);
31 
32  int parToInt = int(par);
33 
34  // fixed number of neighbours:
35  if (neighbourDef == "N") {
36  B2DEBUG(150, "ECLNeighbours::ECLNeighbours: initialize " << neighbourDef << ", n x n: " << parToInt * 2 + 1 << " x " << parToInt * 2
37  + 1);
38  if ((parToInt >= 0) and (parToInt < 11)) initializeN(parToInt);
39  else B2FATAL("ECLNeighbours::ECLNeighbours: " << parToInt << " is an invalid parameter (must be between 0 and 10)!");
40  } else if (neighbourDef == "NC") {
41  B2DEBUG(150, "ECLNeighbours::ECLNeighbours: initialize " << neighbourDef << ", n x n (minus corners): " << parToInt * 2 + 1 << " x "
42  <<
43  parToInt * 2 + 1);
44  if ((parToInt >= 0) and (parToInt < 11)) initializeNC(parToInt);
45  else B2FATAL("ECLNeighbours::ECLNeighbours: " << parToInt << " is an invalid parameter (must be between 0 and 10)!");
46  } else if (neighbourDef == "NLegacy") {
47  B2DEBUG(150, "ECLNeighbours::ECLNeighbours: initialize " << neighbourDef << ", n x n: " << parToInt * 2 + 1 << " x " << parToInt * 2
48  + 1);
49  if ((parToInt >= 0) and (parToInt < 11)) initializeNLegacy(parToInt);
50  else B2FATAL("ECLNeighbours::ECLNeighbours: " << parToInt << " is an invalid parameter (must be between 0 and 10)!");
51  } else if (neighbourDef == "NCLegacy") {
52  B2DEBUG(150, "ECLNeighbours::ECLNeighbours: initialize " << neighbourDef << ", n x n (minus corners): " << parToInt * 2 + 1 << " x "
53  <<
54  parToInt * 2 + 1);
55  if ((parToInt >= 0) and (parToInt < 11)) initializeNCLegacy(parToInt, 1);
56  else B2FATAL("ECLNeighbours::ECLNeighbours: " << parToInt << " is an invalid parameter (must be between 0 and 10)!");
57  }
58  // or neighbours depend on the distance:
59  else if (neighbourDef == "R") {
60  B2DEBUG(150, "ECLNeighbours::ECLNeighbours: initialize " << neighbourDef << ", radius: " << par << " cm");
61  if ((par > 0.0) and (par < 30.0 * Belle2::Unit::cm)) initializeR(par);
62  else B2FATAL("ECLNeighbours::ECLNeighbours: " << par << " is an invalid parameter (must be between 0 cm and 30 cm)!");
63  }
64  // or neighbours that form a cross, user defined coverage of up to 100% in neighbouring ring:
65  else if (neighbourDef == "F") {
66  double parChecked = par;
67  if (parChecked > 1.0) parChecked = 1.0;
68  else if (parChecked < 0.1) parChecked = 0.1;
69  B2DEBUG(150, "ECLNeighbours::ECLNeighbours: initialize " << neighbourDef << ", fraction: " << parChecked);
70  initializeF(parChecked);
71  }
72  // or wrong type:
73  else {
74  B2FATAL("ECLNeighbours::ECLNeighbours (constructor via std::string): Invalid option: " << neighbourDef <<
75  " (valid: N(n), NC(n), NLegacy(n), NCLegacy(n), R ( with R<30 cm), F (with 0.1<f<1)");
76  }
77 
78 }
79 
81 {
82  ;
83 }
84 
85 void ECLNeighbours::initializeR(double radius)
86 {
87  // resize the vector
88  std::vector<short int> fakeneighbours;
89  fakeneighbours.push_back(0); // insert one fake to avoid the "0" entry
90  m_neighbourMapTemp.push_back(fakeneighbours);
91 
92  // distance calculations take a "long" time, so reduce the number of candidates first:
93  initializeN(6);
94 
95  // ECL geometry
97 
98  for (int i = 0; i < 8736; i++) {
99  // get the central one
100  TVector3 direction = geom->GetCrystalVec(i);
101  TVector3 position = geom->GetCrystalPos(i);
102 
103  // get all nearby crystals
104  std::vector<short int> neighbours = getNeighbours(i + 1);
105  std::vector<short int> neighboursTemp;
106 
107  // ... and calculate the shortest distance between the central one and all possible neighbours (of the reduced set)
108  for (auto const& id : neighbours) {
109  const TVector3& directionNeighbour = geom->GetCrystalVec(id - 1);
110  const double alpha = direction.Angle(directionNeighbour);
111  const double R = position.Mag();
112  const double distance = getDistance(alpha, R);
113 
114  if (distance <= radius) neighboursTemp.push_back(id);
115  }
116 
117  m_neighbourMapTemp.push_back(neighboursTemp);
118  }
119 
120  // all done, reoplace the original map
122 
123 }
124 
125 void ECLNeighbours::initializeF(double frac)
126 {
127  // ECL geometry
129 
130  for (int i = 0; i < 8736; i++) {
131  // this vector will hold all neighbours for the i-th crystal
132  std::vector<short int> neighbours;
133 
134  // phi and theta coordinates of the central crystal
135  geom->Mapping(i);
136  const short tid = geom->GetThetaID();
137  const short pid = geom->GetPhiID();
138 
139  // add the two in the same theta ring
140  const short int phiInc = increasePhiId(pid, tid, 1);
141  const short int phiDec = decreasePhiId(pid, tid, 1);
142  neighbours.push_back(geom->GetCellID(tid , pid) + 1);
143  neighbours.push_back(geom->GetCellID(tid , phiInc) + 1);
144  neighbours.push_back(geom->GetCellID(tid , phiDec) + 1);
145 
146  double fracPos = (pid + 0.5) / m_crystalsPerRing[tid];
147 
148  // find the two closest crystals in the inner theta ring
149  short int tidinner = tid - 1;
150  if (tidinner >= 0) {
151 
152  short int n1 = -1;
153  double dist1 = 999.;
154  short int pid1 = -1;
155  short int n2 = -1;
156  double dist2 = 999.;
157 
158  for (short int inner = 0; inner < m_crystalsPerRing[tidinner]; ++inner) {
159  const double f = (inner + 0.5) / m_crystalsPerRing[tidinner];
160  const double dist = fabs(fracPos - f);
161  if (dist < dist1) {
162  dist2 = dist1;
163  dist1 = dist;
164  n2 = n1;
165  n1 = geom->GetCellID(tidinner, inner);
166  pid1 = inner;
167  } else if (dist < dist2) {
168  dist2 = dist;
169  n2 = geom->GetCellID(tidinner, inner);
170  }
171  }
172 
173  // check coverage
174  double cov = TMath::Min(((double) pid + 1) / m_crystalsPerRing[tid],
175  ((double) pid1 + 1) / m_crystalsPerRing[tidinner]) - TMath::Max(((double) pid) / m_crystalsPerRing[tid],
176  ((double) pid1) / m_crystalsPerRing[tidinner]);
177  cov = cov / (1. / m_crystalsPerRing[tid]);
178 
179  neighbours.push_back(n1 + 1);
180  if (cov < frac - 1e-4) neighbours.push_back(n2 + 1);
181  }
182 
183  // find the two closest crystals in the outer theta ring
184  short int tidouter = tid + 1;
185  if (tidouter <= 68) {
186  short int no1 = -1;
187  double disto1 = 999.;
188  short int pido1 = -1;
189  short int no2 = -1;
190  double disto2 = 999.;
191 
192  for (short int outer = 0; outer < m_crystalsPerRing[tidouter]; ++outer) {
193  const double f = (outer + 0.5) / m_crystalsPerRing[tidouter];
194  const double dist = fabs(fracPos - f);
195  if (dist < disto1) {
196  disto2 = disto1;
197  disto1 = dist;
198  no2 = no1;
199  no1 = geom->GetCellID(tidouter, outer);
200  pido1 = outer;
201  } else if (dist < disto2) {
202  disto2 = dist;
203  no2 = geom->GetCellID(tidouter, outer);
204  }
205  }
206  // check coverage
207  double cov = TMath::Min(((double) pid + 1) / m_crystalsPerRing[tid],
208  ((double) pido1 + 1) / m_crystalsPerRing[tidouter]) - TMath::Max(((double) pid) / m_crystalsPerRing[tid],
209  ((double) pido1) / m_crystalsPerRing[tidouter]);
210  cov = cov / (1. / m_crystalsPerRing[tid]);
211 
212  neighbours.push_back(no1 + 1);
213  if (cov < frac - 1e-4) {
214  neighbours.push_back(no2 + 1);
215  }
216  }
217 
218  // push back the final vector of IDs
219  m_neighbourMap.push_back(neighbours);
220  }
221 
222 }
223 
225 {
226  // This is the "NxN-edges" case (in the barrel)
227  for (int i = 0; i < 8736; i++) {
228 
229  // this vector will hold all neighbours for the i-th crystal
230  std::vector<short int> neighbours;
231  neighbours.push_back(i + 1);
232 
233  std::vector<short int> neighbours_temp;
234 
235  // iterate next, next-to-next, next-to-next-to-next, ...
236  for (int idx = 0; idx < n; idx++) {
237  EclNbr tmpNbr;
238  for (const auto& nbr : neighbours) {
239  const EclNbr& aNbr(tmpNbr.getNbr(nbr - 1)); // uses crystalid, but we store cellids
240  for (std::vector<EclNbr::Identifier>::const_iterator newnbr = aNbr.nearBegin(); newnbr != aNbr.nearEnd(); ++newnbr) {
241  neighbours_temp.push_back(((short) *newnbr) + 1); // store cellid, not crystalid
242  }
243  }
244 
245  // now sort and remove all duplicates from neighbours_temp
246  sort(neighbours_temp.begin(), neighbours_temp.end());
247  neighbours_temp.erase(unique(neighbours_temp.begin(), neighbours_temp.end()), neighbours_temp.end());
248  neighbours.insert(neighbours.end(), neighbours_temp.begin(), neighbours_temp.end());
249  }
250 
251  // push back the final vector of IDs, we have to erease the duplicate first ID
252  sort(neighbours.begin(), neighbours.end());
253  neighbours.erase(unique(neighbours.begin(), neighbours.end()), neighbours.end());
254  m_neighbourMap.push_back(neighbours);
255 
256  }
257 }
258 
260 {
261  // get the normal neighbours
262  initializeN(n);
263  // ECL geometry
265 
266  for (int i = 0; i < 8736; i++) {
267  std::vector<short int> neighbours = m_neighbourMap.at(i + 1);
268  std::vector<short int> neighbours_temp;
269 
270  geom->Mapping(i);
271  const int centerthetaid = geom->GetThetaID();
272 
273  for (const auto& nbr : neighbours) {
274  geom->Mapping(nbr - 1);
275  const int thetaid = geom->GetThetaID();
276 
277  if (abs(thetaid - centerthetaid) == n) {
278  const short int phiInc = increasePhiId(geom->GetPhiID(), geom->GetThetaID(), 1);
279  const short int phiDec = decreasePhiId(geom->GetPhiID(), geom->GetThetaID(), 1);
280  const int cid1 = geom->GetCellID(thetaid , phiInc) + 1;
281  const int cid2 = geom->GetCellID(thetaid , phiDec) + 1;
282 
283  // if that crystal has two neighbours in the same theta-ring, it will not be removed
284  if (!((std::find(neighbours.begin(), neighbours.end(), cid1) != neighbours.end()) and
285  (std::find(neighbours.begin(), neighbours.end(), cid2) != neighbours.end()))) {
286  continue;
287  }
288  }
289  neighbours_temp.push_back(nbr);
290 
291  }
292 
293  m_neighbourMap[i + 1] = neighbours_temp;
294 
295  } // end 8736 loop
296 
297 }
298 
300 {
301  // ECL geometry
303 
304  // This is the "NxN-edges" case (in the barrel)
305  // in the endcaps we project the neighbours to the outer and inner rings.
306  for (int i = 0; i < 8736; i++) {
307 
308  // this vector will hold all neighbours for the i-th crystal
309  std::vector<short int> neighbours;
310 
311  // phi and theta coordinates of the central crystal
312  geom->Mapping(i);
313  const short tid = geom->GetThetaID();
314  const short pid = geom->GetPhiID();
315 
316  // 'left' and 'right' extremal neighbours in the same theta ring
317  const short int phiInc = increasePhiId(pid, tid, n);
318  const short int phiDec = decreasePhiId(pid, tid, n);
319  const double fractionalPhiInc = static_cast < double >(phiInc) / m_crystalsPerRing [ tid ];
320  const double fractionalPhiDec = static_cast < double >(phiDec) / m_crystalsPerRing [ tid ];
321 
322  // loop over all possible theta rings and add neighbours
323  for (int theta = tid - n; theta <= tid + n; theta++) {
324  if ((theta > 68) || (theta < 0)) continue; // valid theta ids are 0..68 (69 in total)
325 
326  short int thisPhiInc = std::lround(fractionalPhiInc * m_crystalsPerRing [ theta ]);
327 
328  short int thisPhiDec = std::lround(fractionalPhiDec * m_crystalsPerRing [ theta ]);
329  if (thisPhiDec == m_crystalsPerRing [ theta ]) thisPhiDec = 0;
330 
331  const std::vector<short int> phiList = getPhiIdsInBetween(thisPhiInc, thisPhiDec, theta);
332 
333  for (unsigned int k = 0; k < phiList.size(); ++k) {
334  neighbours.push_back(geom->GetCellID(theta , phiList.at(k)) + 1);
335  }
336  }
337 
338  // push back the final vector of IDs
339  m_neighbourMap.push_back(neighbours);
340 
341  }
342 }
343 
344 
345 void ECLNeighbours::initializeNCLegacy(const int n, const int corners)
346 {
347  // ECL geometry
349 
350  // This is the "NxN-edges" minus edges case (in the barrel)
351  // in the endcaps we project the neighbours to the outer and inner rings.
352  for (int i = 0; i < 8736; i++) {
353 
354  // this vector will hold all neighbours for the i-th crystal
355  std::vector<short int> neighbours;
356 
357  // phi and theta coordinates of the central crystal
358  geom->Mapping(i);
359  const short tid = geom->GetThetaID();
360  const short pid = geom->GetPhiID();
361 
362  // 'left' and 'right' extremal neighbours in the same theta ring
363  const short int phiInc = increasePhiId(pid, tid, n);
364  const short int phiDec = decreasePhiId(pid, tid, n);
365  const double fractionalPhiInc = static_cast < double >(phiInc) / m_crystalsPerRing [ tid ];
366  const double fractionalPhiDec = static_cast < double >(phiDec) / m_crystalsPerRing [ tid ];
367 
368  // loop over all possible theta rings and add neighbours
369  for (int theta = tid - n; theta <= tid + n; theta++) {
370  if ((theta > 68) || (theta < 0)) continue; // valid theta ids are 0..68 (69 in total)
371 
372  short int thisPhiInc = std::lround(fractionalPhiInc * m_crystalsPerRing [ theta ]);
373 
374  short int thisPhiDec = std::lround(fractionalPhiDec * m_crystalsPerRing [ theta ]);
375  if (thisPhiDec == m_crystalsPerRing [ theta ]) thisPhiDec = 0;
376 
377  std::vector<short int> phiList;
378  if ((theta == tid - n) or (theta == tid + n)) phiList = getPhiIdsInBetweenC(thisPhiInc, thisPhiDec, theta, corners);
379  else if (corners == 2 and ((theta == tid - n + 1)
380  or (theta == tid + n - 1))) phiList = getPhiIdsInBetweenC(thisPhiInc, thisPhiDec, theta, 1);
381  else phiList = getPhiIdsInBetween(thisPhiInc, thisPhiDec, theta);
382 
383  for (unsigned int k = 0; k < phiList.size(); ++k) {
384  neighbours.push_back(geom->GetCellID(theta , phiList.at(k)) + 1);
385  }
386  }
387 
388  // push back the final vector of IDs
389  m_neighbourMap.push_back(neighbours);
390 
391  }
392 }
393 
394 const std::vector<short int>& ECLNeighbours::getNeighbours(const short int cid) const
395 {
396  return m_neighbourMap.at(cid);
397 }
398 
399 // decrease the phi id by "n" integers numbers (valid ids range from 0 to m_crystalsPerRing[thetaid] - 1)
400 short int ECLNeighbours::decreasePhiId(const short int phiid, const short int thetaid, const short int n)
401 {
402  short int previousPhiId = ((phiid - n < 0) ? m_crystalsPerRing[thetaid] + phiid - n : phiid - n); // "underflow"
403  return previousPhiId;
404 }
405 
406 // increase the phi id by "n" integers numbers (valid ids range from 0 to m_crystalsPerRing[thetaid] - 1)
407 short int ECLNeighbours::increasePhiId(const short int phiid, const short int thetaid, const short int n)
408 {
409  short int nextPhiId = (((phiid + n) > (m_crystalsPerRing[thetaid] - 1)) ? phiid + n - m_crystalsPerRing[thetaid] : phiid +
410  n); // "overflow"
411  return nextPhiId;
412 }
413 
414 std::vector<short int> ECLNeighbours::getPhiIdsInBetween(const short int phi0, const short int phi1, const short int theta)
415 {
416  std::vector<short int> phiList;
417  short int loop = phi0;
418 
419  while (1) {
420  phiList.push_back(loop);
421  if (loop == phi1) break;
422  loop = decreasePhiId(loop, theta, 1);
423  }
424 
425  return phiList;
426 }
427 
428 std::vector<short int> ECLNeighbours::getPhiIdsInBetweenC(const short int phi0, const short int phi1, const short int theta,
429  const int corners)
430 {
431  std::vector<short int> phiList;
432  if (phi0 == phi1) return phiList; // could be that there is only one crystal in this theta ring definition
433 
434  short int loop = decreasePhiId(phi0, theta, corners); // start at -n corners
435  if (loop == phi1) return phiList; // there will be no neighbours left in this phi ring after removing the first corners
436 
437  short int stop = increasePhiId(phi1, theta, corners); //stop at +n corners
438 
439  while (1) {
440  phiList.push_back(loop);
441  if (loop == stop) break;
442  loop = decreasePhiId(loop, theta, 1);
443  }
444 
445  return phiList;
446 }
447 
448 double ECLNeighbours::getDistance(const double alpha, const double R)
449 {
450  return 2.0 * R * TMath::Sin(alpha / 2.0);
451 }
452 
The Class for ECL Geometry Parameters.
static ECLGeometryPar * Instance()
Static method to get a reference to the ECLGeometryPar instance.
std::vector< short int > getPhiIdsInBetweenC(const short int phiInc, const short int phiDec, const short int theta, const int corners)
return a list of phi ids between two phi ids minus edges
const std::vector< short int > & getNeighbours(short int cid) const
Return the neighbours for a given cell ID.
const short m_crystalsPerRing[69]
Number of crystals in each theta ring.
Definition: ECLNeighbours.h:47
void initializeNCLegacy(const int nneighbours, const int corners)
initialize the mask neighbour list, remove corners, legacy code.
void initializeF(const double fraction)
initialize the fractional cross neighbour list.
void initializeN(const int nneighbours)
initialize the mask neighbour list.
ECLNeighbours(const std::string &neighbourDef, const double par)
Constructor: Fix number of neighbours ("N") in the seed theta ring, fraction cross ("F"),...
void initializeR(const double radius)
initialize the radius neighbour list.
std::vector< std::vector< short int > > m_neighbourMapTemp
temporary list of list of neighbour cids.
Definition: ECLNeighbours.h:44
short int increasePhiId(const short int phiid, const short int thetaid, const short int n)
return the next phi id.
std::vector< std::vector< short int > > m_neighbourMap
list of list of neighbour cids.
Definition: ECLNeighbours.h:41
std::vector< short int > getPhiIdsInBetween(const short int phiInc, const short int phiDec, const short int theta)
return a list of phi ids between two phi ids
void initializeNC(const int nneighbours)
initialize the mask neighbour list, remove corners.
void initializeNLegacy(const int nneighbours)
initialize the mask neighbour list, legacy code.
double getDistance(const double alpha, const double R)
return the chord length between cells
short int decreasePhiId(const short int phiid, const short int thetaid, const short int n)
return the previous phi id.
EclNbr getNbr(const Identifier aCellId)
get crystals nbr
const std::vector< Identifier >::const_iterator nearEnd() const
get crystals nearEnd
const std::vector< Identifier >::const_iterator nearBegin() const
get crystals nearBegin
static const double cm
Standard units with the value = 1.
Definition: Unit.h:47
Abstract base class for different kinds of events.