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