Belle II Software development
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
23using namespace Belle2;
24using namespace ECL;
25
26// Constructor.
27ECLNeighbours::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
87void 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
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
227void 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
398void 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
447const 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)
453short 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)
460short 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
467std::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
481std::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
501double 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.