Analysis Software
Documentation for sPHENIX simulation 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:
73  size_t m_current = 0, m_end1 = 0, m_begin2 = 0;
74  bool m_wrapped = false;
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:
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<int, int> sizes = {
159  -1, 1}) const {
160  constexpr int min = 0;
161  const int max = getNBins() + 1;
162  const int itmin = std::clamp(static_cast<int>(idx + sizes.first), min, max);
163  const int itmax =
164  std::clamp(static_cast<int>(idx + sizes.second), min, max);
165  return NeighborHoodIndices(itmin, itmax + 1);
166  }
167 
177  template <AxisBoundaryType T = bdt,
178  std::enable_if_t<T == AxisBoundaryType::Bound, int> = 0>
179  NeighborHoodIndices neighborHoodIndices(size_t idx,
180  std::pair<int, int> sizes = {
181  -1, 1}) const {
182  if (idx <= 0 || idx >= (getNBins() + 1)) {
183  return NeighborHoodIndices();
184  }
185  constexpr int min = 1;
186  const int max = getNBins();
187  const int itmin = std::clamp(static_cast<int>(idx) + sizes.first, min, max);
188  const int itmax =
189  std::clamp(static_cast<int>(idx) + sizes.second, min, max);
190  return NeighborHoodIndices(itmin, itmax + 1);
191  }
192 
202  template <AxisBoundaryType T = bdt,
203  std::enable_if_t<T == AxisBoundaryType::Closed, int> = 0>
204  NeighborHoodIndices neighborHoodIndices(size_t idx,
205  std::pair<int, int> sizes = {
206  -1, 1}) const {
207  // Handle invalid indices
208  if (idx <= 0 || idx >= (getNBins() + 1)) {
209  return NeighborHoodIndices();
210  }
211 
212  // Handle corner case where user requests more neighbours than the number
213  // of bins on the axis. All bins are returned in this case.
214 
215  const int max = getNBins();
216  sizes.first = std::clamp(sizes.first, -max, max);
217  sizes.second = std::clamp(sizes.second, -max, max);
218  if (std::abs(sizes.first - sizes.second) >= max) {
219  sizes.first = 1 - idx;
220  sizes.second = max - idx;
221  }
222 
223  // If the entire index range is not covered, we must wrap the range of
224  // targeted neighbor indices into the range of valid bin indices. This may
225  // split the range of neighbor indices in two parts:
226  //
227  // Before wraparound - [ XXXXX]XXX
228  // After wraparound - [ XXXX XXXX ]
229  //
230  const int itmin = idx + sizes.first;
231  const int itmax = idx + sizes.second;
232  const size_t itfirst = wrapBin(itmin);
233  const size_t itlast = wrapBin(itmax);
234  if (itfirst <= itlast) {
235  return NeighborHoodIndices(itfirst, itlast + 1);
236  } else {
237  return NeighborHoodIndices(itfirst, max + 1, 1, itlast + 1);
238  }
239  }
240 
247  template <AxisBoundaryType T = bdt,
248  std::enable_if_t<T == AxisBoundaryType::Open, int> = 0>
249  size_t wrapBin(int bin) const {
250  return std::max(std::min(bin, static_cast<int>(getNBins()) + 1), 0);
251  }
252 
259  template <AxisBoundaryType T = bdt,
260  std::enable_if_t<T == AxisBoundaryType::Bound, int> = 0>
261  size_t wrapBin(int bin) const {
262  return std::max(std::min(bin, static_cast<int>(getNBins())), 1);
263  }
264 
271  template <AxisBoundaryType T = bdt,
272  std::enable_if_t<T == AxisBoundaryType::Closed, int> = 0>
273  size_t wrapBin(int bin) const {
274  const int w = getNBins();
275  return 1 + (w + ((bin - 1) % w)) % w;
276  // return int(bin<1)*w - int(bin>w)*w + bin;
277  }
278 
289  size_t getBin(ActsScalar x) const {
290  return wrapBin(
291  static_cast<int>(std::floor((x - getMin()) / getBinWidth()) + 1));
292  }
293 
297  ActsScalar getBinWidth(size_t /*bin*/ = 0) const { return m_width; }
298 
309  ActsScalar getBinLowerBound(size_t bin) const {
310  return getMin() + (bin - 1) * getBinWidth();
311  }
312 
323  ActsScalar getBinUpperBound(size_t bin) const {
324  return getMin() + bin * getBinWidth();
325  }
326 
334  ActsScalar getBinCenter(size_t bin) const {
335  return getMin() + (bin - 0.5) * getBinWidth();
336  }
337 
341  ActsScalar getMax() const override { return m_max; }
342 
346  ActsScalar getMin() const override { return m_min; }
347 
351  size_t getNBins() const override { return m_bins; }
352 
360  bool isInside(ActsScalar x) const { return (m_min <= x) && (x < m_max); }
361 
364  std::vector<ActsScalar> getBinEdges() const override {
365  std::vector<ActsScalar> binEdges;
366  for (size_t i = 1; i <= m_bins; i++) {
367  binEdges.push_back(getBinLowerBound(i));
368  }
369  binEdges.push_back(getBinUpperBound(m_bins));
370  return binEdges;
371  }
372 
373  private:
381  size_t m_bins;
382 };
383 
388 template <AxisBoundaryType bdt>
389 class Axis<AxisType::Variable, bdt> final : public IAxis {
390  public:
400  Axis(std::vector<ActsScalar> binEdges) : m_binEdges(std::move(binEdges)) {}
401 
405  bool isEquidistant() const override { return false; }
406 
410  bool isVariable() const override { return true; }
411 
415  AxisBoundaryType getBoundaryType() const override { return bdt; }
416 
424  NeighborHoodIndices neighborHoodIndices(size_t idx, size_t size = 1) const {
425  return neighborHoodIndices(idx, std::make_pair(-size, size));
426  }
427 
438  template <AxisBoundaryType T = bdt,
439  std::enable_if_t<T == AxisBoundaryType::Open, int> = 0>
440  NeighborHoodIndices neighborHoodIndices(size_t idx,
441  std::pair<int, int> sizes = {
442  -1, 1}) const {
443  constexpr int min = 0;
444  const int max = getNBins() + 1;
445  const int itmin = std::max(min, static_cast<int>(idx) + sizes.first);
446  const int itmax = std::min(max, static_cast<int>(idx) + sizes.second);
447  return NeighborHoodIndices(itmin, itmax + 1);
448  }
449 
459  template <AxisBoundaryType T = bdt,
460  std::enable_if_t<T == AxisBoundaryType::Bound, int> = 0>
461  NeighborHoodIndices neighborHoodIndices(size_t idx,
462  std::pair<int, int> sizes = {
463  -1, 1}) const {
464  if (idx <= 0 || idx >= (getNBins() + 1)) {
465  return NeighborHoodIndices();
466  }
467  constexpr int min = 1;
468  const int max = getNBins();
469  const int itmin = std::max(min, static_cast<int>(idx) + sizes.first);
470  const int itmax = std::min(max, static_cast<int>(idx) + sizes.second);
471  return NeighborHoodIndices(itmin, itmax + 1);
472  }
473 
483  template <AxisBoundaryType T = bdt,
484  std::enable_if_t<T == AxisBoundaryType::Closed, int> = 0>
485  NeighborHoodIndices neighborHoodIndices(size_t idx,
486  std::pair<int, int> sizes = {
487  -1, 1}) const {
488  // Handle invalid indices
489  if (idx <= 0 || idx >= (getNBins() + 1)) {
490  return NeighborHoodIndices();
491  }
492 
493  // Handle corner case where user requests more neighbours than the number
494  // of bins on the axis. All bins are returned in this case
495 
496  const int max = getNBins();
497  sizes.first = std::clamp(sizes.first, -max, max);
498  sizes.second = std::clamp(sizes.second, -max, max);
499  if (std::abs(sizes.first - sizes.second) >= max) {
500  sizes.first = 1 - idx;
501  sizes.second = max - idx;
502  }
503 
504  // If the entire index range is not covered, we must wrap the range of
505  // targeted neighbor indices into the range of valid bin indices. This may
506  // split the range of neighbor indices in two parts:
507  //
508  // Before wraparound - [ XXXXX]XXX
509  // After wraparound - [ XXXX XXXX ]
510  //
511  const int itmin = idx + sizes.first;
512  const int itmax = idx + sizes.second;
513  const size_t itfirst = wrapBin(itmin);
514  const size_t itlast = wrapBin(itmax);
515  if (itfirst <= itlast) {
516  return NeighborHoodIndices(itfirst, itlast + 1);
517  } else {
518  return NeighborHoodIndices(itfirst, max + 1, 1, itlast + 1);
519  }
520  }
521 
528  template <AxisBoundaryType T = bdt,
529  std::enable_if_t<T == AxisBoundaryType::Open, int> = 0>
530  size_t wrapBin(int bin) const {
531  return std::max(std::min(bin, static_cast<int>(getNBins()) + 1), 0);
532  }
533 
540  template <AxisBoundaryType T = bdt,
541  std::enable_if_t<T == AxisBoundaryType::Bound, int> = 0>
542  size_t wrapBin(int bin) const {
543  return std::max(std::min(bin, static_cast<int>(getNBins())), 1);
544  }
545 
552  template <AxisBoundaryType T = bdt,
553  std::enable_if_t<T == AxisBoundaryType::Closed, int> = 0>
554  size_t wrapBin(int bin) const {
555  const int w = getNBins();
556  return 1 + (w + ((bin - 1) % w)) % w;
557  // return int(bin<1)*w - int(bin>w)*w + bin;
558  }
559 
570  size_t getBin(ActsScalar x) const {
571  const auto it =
572  std::upper_bound(std::begin(m_binEdges), std::end(m_binEdges), x);
573  return wrapBin(std::distance(std::begin(m_binEdges), it));
574  }
575 
583  ActsScalar getBinWidth(size_t bin) const {
584  return m_binEdges.at(bin) - m_binEdges.at(bin - 1);
585  }
586 
597  ActsScalar getBinLowerBound(size_t bin) const {
598  return m_binEdges.at(bin - 1);
599  }
600 
611  ActsScalar getBinUpperBound(size_t bin) const { return m_binEdges.at(bin); }
612 
620  ActsScalar getBinCenter(size_t bin) const {
621  return 0.5 * (getBinLowerBound(bin) + getBinUpperBound(bin));
622  }
623 
627  ActsScalar getMax() const override { return m_binEdges.back(); }
628 
632  ActsScalar getMin() const override { return m_binEdges.front(); }
633 
637  size_t getNBins() const override { return m_binEdges.size() - 1; }
638 
646  bool isInside(ActsScalar x) const {
647  return (m_binEdges.front() <= x) && (x < m_binEdges.back());
648  }
649 
652  std::vector<ActsScalar> getBinEdges() const override { return m_binEdges; }
653 
654  private:
656  std::vector<ActsScalar> m_binEdges;
657 };
658 } // namespace detail
659 
660 } // namespace Acts