EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Axis.hpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file Axis.hpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2017-2018 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
9 #pragma once
10 
11 #include "Acts/Utilities/IAxis.hpp"
13 
14 #include <algorithm>
15 #include <cmath>
16 #include <vector>
17 
18 namespace Acts {
19 
20 namespace detail {
21 
22 // This object can be iterated to produce up to two sequences of integer
23 // indices, corresponding to the half-open integer ranges [begin1, end1[ and
24 // [begin2, end2[.
25 //
26 // The goal is to emulate the effect of enumerating a range of neighbor
27 // indices on an axis (which may go out of bounds and wrap around since we
28 // have AxisBoundaryType::Closed), inserting them into an std::vector, and
29 // discarding duplicates, without paying the price of duplicate removal
30 // and dynamic memory allocation in hot magnetic field interpolation code.
31 //
33  public:
34  NeighborHoodIndices() = default;
35 
36  NeighborHoodIndices(size_t begin, size_t end)
37  : m_begin1(begin), m_end1(end), m_begin2(end), m_end2(end) {}
38 
39  NeighborHoodIndices(size_t begin1, size_t end1, size_t begin2, size_t end2)
40  : m_begin1(begin1), m_end1(end1), m_begin2(begin2), m_end2(end2) {}
41 
42  class iterator {
43  public:
44  iterator() = default;
45 
46  // Specialized constructor for end() iterator
47  iterator(size_t current) : m_current(current), m_wrapped(true) {}
48 
49  iterator(size_t begin1, size_t end1, size_t begin2)
50  : m_current(begin1),
51  m_end1(end1),
52  m_begin2(begin2),
53  m_wrapped(begin1 == begin2) {}
54 
55  size_t operator*() const { return m_current; }
56 
58  ++m_current;
59  if (m_current == m_end1) {
61  m_wrapped = true;
62  }
63  return *this;
64  }
65 
66  bool operator==(const iterator& it) const {
67  return (m_current == it.m_current) && (m_wrapped == it.m_wrapped);
68  }
69 
70  bool operator!=(const iterator& it) const { return !(*this == it); }
71 
72  private:
74  bool m_wrapped;
75  };
76 
77  iterator begin() const { return iterator(m_begin1, m_end1, m_begin2); }
78 
79  iterator end() const { return iterator(m_end2); }
80 
81  // Number of indices that will be produced if this sequence is iterated
82  size_t size() const { return (m_end1 - m_begin1) + (m_end2 - m_begin2); }
83 
84  // Collect the sequence of indices into an std::vector
85  std::vector<size_t> collect() const {
86  std::vector<size_t> result;
87  result.reserve(this->size());
88  for (size_t idx : *this) {
89  result.push_back(idx);
90  }
91  return result;
92  }
93 
94  private:
95  size_t m_begin1 = 0, m_end1 = 0, m_begin2 = 0, m_end2 = 0;
96 };
97 
102 template <AxisBoundaryType bdt>
103 class Axis<AxisType::Equidistant, bdt> final : public IAxis {
104  public:
113  Axis(double xmin, double xmax, size_t nBins)
114  : m_min(xmin),
115  m_max(xmax),
116  m_width((xmax - xmin) / nBins),
117  m_bins(nBins) {}
118 
122  bool isEquidistant() const override { return true; }
123 
127  bool isVariable() const override { return false; }
128 
132  AxisBoundaryType getBoundaryType() const override { return bdt; }
133 
141  NeighborHoodIndices neighborHoodIndices(size_t idx, size_t size = 1) const {
142  return neighborHoodIndices(idx, std::make_pair(size, size));
143  }
144 
155  template <AxisBoundaryType T = bdt,
156  std::enable_if_t<T == AxisBoundaryType::Open, int> = 0>
157  NeighborHoodIndices neighborHoodIndices(size_t idx,
158  std::pair<size_t, size_t> sizes = {
159  1, 1}) const {
160  constexpr int min = 0;
161  const int max = getNBins() + 1;
162  const int itmin = std::max(min, int(idx - sizes.first));
163  const int itmax = std::min(max, int(idx + sizes.second));
164  return NeighborHoodIndices(itmin, itmax + 1);
165  }
166 
176  template <AxisBoundaryType T = bdt,
177  std::enable_if_t<T == AxisBoundaryType::Bound, int> = 0>
178  NeighborHoodIndices neighborHoodIndices(size_t idx,
179  std::pair<size_t, size_t> sizes = {
180  1, 1}) const {
181  if (idx <= 0 || idx >= (getNBins() + 1)) {
182  return NeighborHoodIndices();
183  }
184  constexpr int min = 1;
185  const int max = getNBins();
186  const int itmin = std::max(min, int(idx - sizes.first));
187  const int itmax = std::min(max, int(idx + sizes.second));
188  return NeighborHoodIndices(itmin, itmax + 1);
189  }
190 
200  template <AxisBoundaryType T = bdt,
201  std::enable_if_t<T == AxisBoundaryType::Closed, int> = 0>
202  NeighborHoodIndices neighborHoodIndices(size_t idx,
203  std::pair<size_t, size_t> sizes = {
204  1, 1}) const {
205  // Handle invalid indices
206  if (idx <= 0 || idx >= (getNBins() + 1)) {
207  return NeighborHoodIndices();
208  }
209 
210  // Handle corner case where user requests more neighbours than the number
211  // of bins on the axis. We do not want to double-count bins in that case.
212  sizes.first %= getNBins();
213  sizes.second %= getNBins();
214  if (sizes.first + sizes.second + 1 > getNBins()) {
215  sizes.second -= (sizes.first + sizes.second + 1) - getNBins();
216  }
217 
218  // If the entire index range is not covered, we must wrap the range of
219  // targeted neighbor indices into the range of valid bin indices. This may
220  // split the range of neighbor indices in two parts:
221  //
222  // Before wraparound - [ XXXXX]XXX
223  // After wraparound - [ XXXX XXXX ]
224  //
225  const int itmin = idx - sizes.first;
226  const int itmax = idx + sizes.second;
227  const size_t itfirst = wrapBin(itmin);
228  const size_t itlast = wrapBin(itmax);
229  if (itfirst <= itlast) {
230  return NeighborHoodIndices(itfirst, itlast + 1);
231  } else {
232  return NeighborHoodIndices(itfirst, getNBins() + 1, 1, itlast + 1);
233  }
234  }
235 
242  template <AxisBoundaryType T = bdt,
243  std::enable_if_t<T == AxisBoundaryType::Open, int> = 0>
244  size_t wrapBin(int bin) const {
245  return std::max(std::min(bin, static_cast<int>(getNBins()) + 1), 0);
246  }
247 
254  template <AxisBoundaryType T = bdt,
255  std::enable_if_t<T == AxisBoundaryType::Bound, int> = 0>
256  size_t wrapBin(int bin) const {
257  return std::max(std::min(bin, static_cast<int>(getNBins())), 1);
258  }
259 
266  template <AxisBoundaryType T = bdt,
267  std::enable_if_t<T == AxisBoundaryType::Closed, int> = 0>
268  size_t wrapBin(int bin) const {
269  const int w = getNBins();
270  return 1 + (w + ((bin - 1) % w)) % w;
271  // return int(bin<1)*w - int(bin>w)*w + bin;
272  }
273 
284  size_t getBin(double x) const {
285  return wrapBin(std::floor((x - getMin()) / getBinWidth()) + 1);
286  }
287 
291  double getBinWidth(size_t /*bin*/ = 0) const { return m_width; }
292 
303  double getBinLowerBound(size_t bin) const {
304  return getMin() + (bin - 1) * getBinWidth();
305  }
306 
317  double getBinUpperBound(size_t bin) const {
318  return getMin() + bin * getBinWidth();
319  }
320 
328  double getBinCenter(size_t bin) const {
329  return getMin() + (bin - 0.5) * getBinWidth();
330  }
331 
335  double getMax() const override { return m_max; }
336 
340  double getMin() const override { return m_min; }
341 
345  size_t getNBins() const override { return m_bins; }
346 
354  bool isInside(double x) const { return (m_min <= x) && (x < m_max); }
355 
358  std::vector<double> getBinEdges() const override {
359  std::vector<double> binEdges;
360  for (size_t i = 1; i <= m_bins; i++) {
361  binEdges.push_back(getBinLowerBound(i));
362  }
363  binEdges.push_back(getBinUpperBound(m_bins));
364  return binEdges;
365  }
366 
367  private:
369  double m_min;
371  double m_max;
373  double m_width;
375  size_t m_bins;
376 };
377 
382 template <AxisBoundaryType bdt>
383 class Axis<AxisType::Variable, bdt> final : public IAxis {
384  public:
394  Axis(std::vector<double> binEdges) : m_binEdges(std::move(binEdges)) {}
395 
399  bool isEquidistant() const override { return false; }
400 
404  bool isVariable() const override { return true; }
405 
409  AxisBoundaryType getBoundaryType() const override { return bdt; }
410 
418  NeighborHoodIndices neighborHoodIndices(size_t idx, size_t size = 1) const {
419  return neighborHoodIndices(idx, std::make_pair(size, size));
420  }
421 
432  template <AxisBoundaryType T = bdt,
433  std::enable_if_t<T == AxisBoundaryType::Open, int> = 0>
434  NeighborHoodIndices neighborHoodIndices(size_t idx,
435  std::pair<size_t, size_t> sizes = {
436  1, 1}) const {
437  constexpr int min = 0;
438  const int max = getNBins() + 1;
439  const int itmin = std::max(min, int(idx - sizes.first));
440  const int itmax = std::min(max, int(idx + sizes.second));
441  return NeighborHoodIndices(itmin, itmax + 1);
442  }
443 
453  template <AxisBoundaryType T = bdt,
454  std::enable_if_t<T == AxisBoundaryType::Bound, int> = 0>
455  NeighborHoodIndices neighborHoodIndices(size_t idx,
456  std::pair<size_t, size_t> sizes = {
457  1, 1}) const {
458  if (idx <= 0 || idx >= (getNBins() + 1)) {
459  return NeighborHoodIndices();
460  }
461  constexpr int min = 1;
462  const int max = getNBins();
463  const int itmin = std::max(min, int(idx - sizes.first));
464  const int itmax = std::min(max, int(idx + sizes.second));
465  return NeighborHoodIndices(itmin, itmax + 1);
466  }
467 
477  template <AxisBoundaryType T = bdt,
478  std::enable_if_t<T == AxisBoundaryType::Closed, int> = 0>
479  NeighborHoodIndices neighborHoodIndices(size_t idx,
480  std::pair<size_t, size_t> sizes = {
481  1, 1}) const {
482  // Handle invalid indices
483  if (idx <= 0 || idx >= (getNBins() + 1)) {
484  return NeighborHoodIndices();
485  }
486 
487  // Handle corner case where user requests more neighbours than the number
488  // of bins on the axis. We do not want to double-count bins in that case.
489  sizes.first %= getNBins();
490  sizes.second %= getNBins();
491  if (sizes.first + sizes.second + 1 > getNBins()) {
492  sizes.second -= (sizes.first + sizes.second + 1) - getNBins();
493  }
494 
495  // If the entire index range is not covered, we must wrap the range of
496  // targeted neighbor indices into the range of valid bin indices. This may
497  // split the range of neighbor indices in two parts:
498  //
499  // Before wraparound - [ XXXXX]XXX
500  // After wraparound - [ XXXX XXXX ]
501  //
502  const int itmin = idx - sizes.first;
503  const int itmax = idx + sizes.second;
504  const size_t itfirst = wrapBin(itmin);
505  const size_t itlast = wrapBin(itmax);
506  if (itfirst <= itlast) {
507  return NeighborHoodIndices(itfirst, itlast + 1);
508  } else {
509  return NeighborHoodIndices(itfirst, getNBins() + 1, 1, itlast + 1);
510  }
511  }
512 
519  template <AxisBoundaryType T = bdt,
520  std::enable_if_t<T == AxisBoundaryType::Open, int> = 0>
521  size_t wrapBin(int bin) const {
522  return std::max(std::min(bin, static_cast<int>(getNBins()) + 1), 0);
523  }
524 
531  template <AxisBoundaryType T = bdt,
532  std::enable_if_t<T == AxisBoundaryType::Bound, int> = 0>
533  size_t wrapBin(int bin) const {
534  return std::max(std::min(bin, static_cast<int>(getNBins())), 1);
535  }
536 
543  template <AxisBoundaryType T = bdt,
544  std::enable_if_t<T == AxisBoundaryType::Closed, int> = 0>
545  size_t wrapBin(int bin) const {
546  const int w = getNBins();
547  return 1 + (w + ((bin - 1) % w)) % w;
548  // return int(bin<1)*w - int(bin>w)*w + bin;
549  }
550 
561  size_t getBin(double x) const {
562  const auto it =
563  std::upper_bound(std::begin(m_binEdges), std::end(m_binEdges), x);
564  return wrapBin(std::distance(std::begin(m_binEdges), it));
565  }
566 
574  double getBinWidth(size_t bin) const {
575  return m_binEdges.at(bin) - m_binEdges.at(bin - 1);
576  }
577 
588  double getBinLowerBound(size_t bin) const { return m_binEdges.at(bin - 1); }
589 
600  double getBinUpperBound(size_t bin) const { return m_binEdges.at(bin); }
601 
609  double getBinCenter(size_t bin) const {
610  return 0.5 * (getBinLowerBound(bin) + getBinUpperBound(bin));
611  }
612 
616  double getMax() const override { return m_binEdges.back(); }
617 
621  double getMin() const override { return m_binEdges.front(); }
622 
626  size_t getNBins() const override { return m_binEdges.size() - 1; }
627 
635  bool isInside(double x) const {
636  return (m_binEdges.front() <= x) && (x < m_binEdges.back());
637  }
638 
641  std::vector<double> getBinEdges() const override { return m_binEdges; }
642 
643  private:
645  std::vector<double> m_binEdges;
646 };
647 } // namespace detail
648 
649 } // namespace Acts