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