Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:27:55

0001 // This file is part of the Acts project.
0002 //
0003 // Copyright (C) 2017-2018 CERN for the benefit of the Acts project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
0008 
0009 #pragma once
0010 
0011 #include "Acts/Utilities/IAxis.hpp"
0012 #include "Acts/Utilities/detail/AxisFwd.hpp"
0013 
0014 #include <algorithm>
0015 #include <cmath>
0016 #include <vector>
0017 
0018 namespace Acts::detail {
0019 
0020 // This object can be iterated to produce up to two sequences of integer
0021 // indices, corresponding to the half-open integer ranges [begin1, end1[ and
0022 // [begin2, end2[.
0023 //
0024 // The goal is to emulate the effect of enumerating a range of neighbor
0025 // indices on an axis (which may go out of bounds and wrap around since we
0026 // have AxisBoundaryType::Closed), inserting them into an std::vector, and
0027 // discarding duplicates, without paying the price of duplicate removal
0028 // and dynamic memory allocation in hot magnetic field interpolation code.
0029 //
0030 class NeighborHoodIndices {
0031  public:
0032   NeighborHoodIndices() = default;
0033 
0034   NeighborHoodIndices(std::size_t begin, std::size_t end)
0035       : m_begin1(begin), m_end1(end), m_begin2(end), m_end2(end) {}
0036 
0037   NeighborHoodIndices(std::size_t begin1, std::size_t end1, std::size_t begin2,
0038                       std::size_t end2)
0039       : m_begin1(begin1), m_end1(end1), m_begin2(begin2), m_end2(end2) {}
0040 
0041   class iterator {
0042    public:
0043     iterator() = default;
0044 
0045     // Specialized constructor for end() iterator
0046     iterator(std::size_t current) : m_current(current), m_wrapped(true) {}
0047 
0048     iterator(std::size_t begin1, std::size_t end1, std::size_t begin2)
0049         : m_current(begin1),
0050           m_end1(end1),
0051           m_begin2(begin2),
0052           m_wrapped(begin1 == begin2) {}
0053 
0054     std::size_t operator*() const { return m_current; }
0055 
0056     iterator& operator++() {
0057       ++m_current;
0058       if (m_current == m_end1) {
0059         m_current = m_begin2;
0060         m_wrapped = true;
0061       }
0062       return *this;
0063     }
0064 
0065     bool operator==(const iterator& it) const {
0066       return (m_current == it.m_current) && (m_wrapped == it.m_wrapped);
0067     }
0068 
0069     bool operator!=(const iterator& it) const { return !(*this == it); }
0070 
0071    private:
0072     std::size_t m_current = 0, m_end1 = 0, m_begin2 = 0;
0073     bool m_wrapped = false;
0074   };
0075 
0076   iterator begin() const { return iterator(m_begin1, m_end1, m_begin2); }
0077 
0078   iterator end() const { return iterator(m_end2); }
0079 
0080   // Number of indices that will be produced if this sequence is iterated
0081   std::size_t size() const { return (m_end1 - m_begin1) + (m_end2 - m_begin2); }
0082 
0083   // Collect the sequence of indices into an std::vector
0084   std::vector<std::size_t> collect() const {
0085     std::vector<std::size_t> result;
0086     result.reserve(this->size());
0087     for (std::size_t idx : *this) {
0088       result.push_back(idx);
0089     }
0090     return result;
0091   }
0092 
0093  private:
0094   std::size_t m_begin1 = 0, m_end1 = 0, m_begin2 = 0, m_end2 = 0;
0095 };
0096 
0097 /// @brief calculate bin indices for an equidistant binning
0098 ///
0099 /// This class provides some basic functionality for calculating bin indices
0100 /// for a given equidistant binning.
0101 template <AxisBoundaryType bdt>
0102 class Axis<AxisType::Equidistant, bdt> final : public IAxis {
0103  public:
0104   /// @brief default constructor
0105   ///
0106   /// @param [in] xmin lower boundary of axis range
0107   /// @param [in] xmax upper boundary of axis range
0108   /// @param [in] nBins number of bins to divide the axis range into
0109   ///
0110   /// Divide the range \f$[\text{xmin},\text{xmax})\f$ into \f$\text{nBins}\f$
0111   /// equidistant bins.
0112   Axis(ActsScalar xmin, ActsScalar xmax, std::size_t nBins)
0113       : m_min(xmin),
0114         m_max(xmax),
0115         m_width((xmax - xmin) / nBins),
0116         m_bins(nBins) {}
0117 
0118   /// @brief returns whether the axis is equidistant
0119   ///
0120   /// @return bool is equidistant
0121   bool isEquidistant() const override { return true; }
0122 
0123   /// @brief returns whether the axis is variable
0124   ///
0125   /// @return bool is variable
0126   bool isVariable() const override { return false; }
0127 
0128   /// @brief returns the boundary type set in the template param
0129   ///
0130   /// @return @c AxisBoundaryType of this axis
0131   AxisBoundaryType getBoundaryType() const override { return bdt; }
0132 
0133   /// @brief Get #size bins which neighbor the one given
0134   ///
0135   /// Generic overload with symmetric size
0136   ///
0137   /// @param [in] idx requested bin index
0138   /// @param [in] sizes how many neighboring bins (up/down)
0139   /// @return Set of neighboring bin indices (global)
0140   NeighborHoodIndices neighborHoodIndices(std::size_t idx,
0141                                           std::size_t size = 1) const {
0142     return neighborHoodIndices(idx, std::make_pair(-size, size));
0143   }
0144 
0145   /// @brief Get #size bins which neighbor the one given
0146   ///
0147   /// This is the version for Open
0148   ///
0149   /// @param [in] idx requested bin index
0150   /// @param [in] sizes how many neighboring bins (up/down)
0151   /// @return Set of neighboring bin indices (global)
0152   /// @note Open varies given bin and allows 0 and NBins+1 (underflow,
0153   /// overflow)
0154   ///       as neighbors
0155   template <AxisBoundaryType T = bdt,
0156             std::enable_if_t<T == AxisBoundaryType::Open, int> = 0>
0157   NeighborHoodIndices neighborHoodIndices(std::size_t idx,
0158                                           std::pair<int, int> sizes = {
0159                                               -1, 1}) const {
0160     constexpr int min = 0;
0161     const int max = getNBins() + 1;
0162     const int itmin = std::clamp(static_cast<int>(idx + sizes.first), min, max);
0163     const int itmax =
0164         std::clamp(static_cast<int>(idx + sizes.second), min, max);
0165     return NeighborHoodIndices(itmin, itmax + 1);
0166   }
0167 
0168   /// @brief Get #size bins which neighbor the one given
0169   ///
0170   /// This is the version for Bound
0171   ///
0172   /// @param [in] idx requested bin index
0173   /// @param [in] sizes how many neighboring bins (up/down)
0174   /// @return Set of neighboring bin indices (global)
0175   /// @note Bound varies given bin and allows 1 and NBins (regular bins)
0176   ///       as neighbors
0177   template <AxisBoundaryType T = bdt,
0178             std::enable_if_t<T == AxisBoundaryType::Bound, int> = 0>
0179   NeighborHoodIndices neighborHoodIndices(std::size_t idx,
0180                                           std::pair<int, int> sizes = {
0181                                               -1, 1}) const {
0182     if (idx <= 0 || idx >= (getNBins() + 1)) {
0183       return NeighborHoodIndices();
0184     }
0185     constexpr int min = 1;
0186     const int max = getNBins();
0187     const int itmin = std::clamp(static_cast<int>(idx) + sizes.first, min, max);
0188     const int itmax =
0189         std::clamp(static_cast<int>(idx) + sizes.second, min, max);
0190     return NeighborHoodIndices(itmin, itmax + 1);
0191   }
0192 
0193   /// @brief Get #size bins which neighbor the one given
0194   ///
0195   /// This is the version for Closed (i.e. Wrapping)
0196   ///
0197   /// @param [in] idx requested bin index
0198   /// @param [in] sizes how many neighboring bins (up/down)
0199   /// @return Set of neighboring bin indices (global)
0200   /// @note Closed varies given bin and allows bins on the opposite
0201   ///       side of the axis as neighbors. (excludes underflow / overflow)
0202   template <AxisBoundaryType T = bdt,
0203             std::enable_if_t<T == AxisBoundaryType::Closed, int> = 0>
0204   NeighborHoodIndices neighborHoodIndices(std::size_t idx,
0205                                           std::pair<int, int> sizes = {
0206                                               -1, 1}) const {
0207     // Handle invalid indices
0208     if (idx <= 0 || idx >= (getNBins() + 1)) {
0209       return NeighborHoodIndices();
0210     }
0211 
0212     // Handle corner case where user requests more neighbours than the number
0213     // of bins on the axis. All bins are returned in this case.
0214 
0215     const int max = getNBins();
0216     sizes.first = std::clamp(sizes.first, -max, max);
0217     sizes.second = std::clamp(sizes.second, -max, max);
0218     if (std::abs(sizes.first - sizes.second) >= max) {
0219       sizes.first = 1 - idx;
0220       sizes.second = max - idx;
0221     }
0222 
0223     // If the entire index range is not covered, we must wrap the range of
0224     // targeted neighbor indices into the range of valid bin indices. This may
0225     // split the range of neighbor indices in two parts:
0226     //
0227     // Before wraparound - [        XXXXX]XXX
0228     // After wraparound  - [ XXXX   XXXX ]
0229     //
0230     const int itmin = idx + sizes.first;
0231     const int itmax = idx + sizes.second;
0232     const std::size_t itfirst = wrapBin(itmin);
0233     const std::size_t itlast = wrapBin(itmax);
0234     if (itfirst <= itlast) {
0235       return NeighborHoodIndices(itfirst, itlast + 1);
0236     } else {
0237       return NeighborHoodIndices(itfirst, max + 1, 1, itlast + 1);
0238     }
0239   }
0240 
0241   /// @brief Converts bin index into a valid one for this axis.
0242   ///
0243   /// @note Open: bin index is clamped to [0, nBins+1]
0244   ///
0245   /// @param [in] bin The bin to wrap
0246   /// @return valid bin index
0247   template <AxisBoundaryType T = bdt,
0248             std::enable_if_t<T == AxisBoundaryType::Open, int> = 0>
0249   std::size_t wrapBin(int bin) const {
0250     return std::max(std::min(bin, static_cast<int>(getNBins()) + 1), 0);
0251   }
0252 
0253   /// @brief Converts bin index into a valid one for this axis.
0254   ///
0255   /// @note Bound: bin index is clamped to [1, nBins]
0256   ///
0257   /// @param [in] bin The bin to wrap
0258   /// @return valid bin index
0259   template <AxisBoundaryType T = bdt,
0260             std::enable_if_t<T == AxisBoundaryType::Bound, int> = 0>
0261   std::size_t wrapBin(int bin) const {
0262     return std::max(std::min(bin, static_cast<int>(getNBins())), 1);
0263   }
0264 
0265   /// @brief Converts bin index into a valid one for this axis.
0266   ///
0267   /// @note Closed: bin index wraps around to other side
0268   ///
0269   /// @param [in] bin The bin to wrap
0270   /// @return valid bin index
0271   template <AxisBoundaryType T = bdt,
0272             std::enable_if_t<T == AxisBoundaryType::Closed, int> = 0>
0273   std::size_t wrapBin(int bin) const {
0274     const int w = getNBins();
0275     return 1 + (w + ((bin - 1) % w)) % w;
0276     // return int(bin<1)*w - int(bin>w)*w + bin;
0277   }
0278 
0279   /// @brief get corresponding bin index for given coordinate
0280   ///
0281   /// @param  [in] x input coordinate
0282   /// @return index of bin containing the given value
0283   ///
0284   /// @note Bin intervals are defined with closed lower bounds and open upper
0285   ///       bounds, that is \f$l <= x < u\f$ if the value @c x lies within a
0286   ///       bin with lower bound @c l and upper bound @c u.
0287   /// @note Bin indices start at @c 1. The underflow bin has the index @c 0
0288   ///       while the index <tt>nBins + 1</tt> indicates the overflow bin .
0289   std::size_t getBin(ActsScalar x) const {
0290     return wrapBin(
0291         static_cast<int>(std::floor((x - getMin()) / getBinWidth()) + 1));
0292   }
0293 
0294   /// @brief get bin width
0295   ///
0296   /// @return constant width for all bins
0297   ActsScalar getBinWidth(std::size_t /*bin*/ = 0) const { return m_width; }
0298 
0299   /// @brief get lower bound of bin
0300   ///
0301   /// @param  [in] bin index of bin
0302   /// @return lower bin boundary
0303   ///
0304   /// @pre @c bin must be a valid bin index (excluding the underflow bin),
0305   ///      i.e. \f$1 \le \text{bin} \le \text{nBins} + 1\f$
0306   ///
0307   /// @note Bin intervals have a closed lower bound, i.e. the lower boundary
0308   ///       belongs to the bin with the given bin index.
0309   ActsScalar getBinLowerBound(std::size_t bin) const {
0310     return getMin() + (bin - 1) * getBinWidth();
0311   }
0312 
0313   /// @brief get upper bound of bin
0314   ///
0315   /// @param  [in] bin index of bin
0316   /// @return upper bin boundary
0317   ///
0318   /// @pre @c bin must be a valid bin index (excluding the overflow bin),
0319   ///      i.e. \f$0 \le \text{bin} \le \text{nBins}\f$
0320   ///
0321   /// @note Bin intervals have an open upper bound, i.e. the upper boundary
0322   ///       does @b not belong to the bin with the given bin index.
0323   ActsScalar getBinUpperBound(std::size_t bin) const {
0324     return getMin() + bin * getBinWidth();
0325   }
0326 
0327   /// @brief get bin center
0328   ///
0329   /// @param  [in] bin index of bin
0330   /// @return bin center position
0331   ///
0332   /// @pre @c bin must be a valid bin index (excluding under-/overflow bins),
0333   ///      i.e. \f$1 \le \text{bin} \le \text{nBins}\f$
0334   ActsScalar getBinCenter(std::size_t bin) const {
0335     return getMin() + (bin - 0.5) * getBinWidth();
0336   }
0337 
0338   /// @brief get maximum of binning range
0339   ///
0340   /// @return maximum of binning range
0341   ActsScalar getMax() const override { return m_max; }
0342 
0343   /// @brief get minimum of binning range
0344   ///
0345   /// @return minimum of binning range
0346   ActsScalar getMin() const override { return m_min; }
0347 
0348   /// @brief get total number of bins
0349   ///
0350   /// @return total number of bins (excluding under-/overflow bins)
0351   std::size_t getNBins() const override { return m_bins; }
0352 
0353   /// @brief check whether value is inside axis limits
0354   ///
0355   /// @return @c true if \f$\text{xmin} \le x < \text{xmax}\f$, otherwise
0356   ///         @c false
0357   ///
0358   /// @post If @c true is returned, the bin containing the given value is a
0359   ///       valid bin, i.e. it is neither the underflow nor the overflow bin.
0360   bool isInside(ActsScalar x) const { return (m_min <= x) && (x < m_max); }
0361 
0362   /// @brief Return a vector of bin edges
0363   /// @return Vector which contains the bin edges
0364   std::vector<ActsScalar> getBinEdges() const override {
0365     std::vector<ActsScalar> binEdges;
0366     for (std::size_t i = 1; i <= m_bins; i++) {
0367       binEdges.push_back(getBinLowerBound(i));
0368     }
0369     binEdges.push_back(getBinUpperBound(m_bins));
0370     return binEdges;
0371   }
0372 
0373  private:
0374   /// minimum of binning range
0375   ActsScalar m_min;
0376   /// maximum of binning range
0377   ActsScalar m_max;
0378   /// constant bin width
0379   ActsScalar m_width;
0380   /// number of bins (excluding under-/overflow bins)
0381   std::size_t m_bins;
0382 };
0383 
0384 /// @brief calculate bin indices for a variable binning
0385 ///
0386 /// This class provides some basic functionality for calculating bin indices
0387 /// for a given binning with variable bin sizes.
0388 template <AxisBoundaryType bdt>
0389 class Axis<AxisType::Variable, bdt> final : public IAxis {
0390  public:
0391   /// @brief default constructor
0392   ///
0393   /// @param [in] binEdges vector of bin edges
0394   /// @pre @c binEdges must be strictly sorted in ascending order.
0395   /// @pre @c binEdges must contain at least two entries.
0396   ///
0397   /// Create a binning structure with @c nBins variable-sized bins from the
0398   /// given bin boundaries. @c nBins is given by the number of bin edges
0399   /// reduced by one.
0400   Axis(std::vector<ActsScalar> binEdges) : m_binEdges(std::move(binEdges)) {}
0401 
0402   /// @brief returns whether the axis is equidistante
0403   ///
0404   /// @return bool is equidistant
0405   bool isEquidistant() const override { return false; }
0406 
0407   /// @brief returns whether the axis is variable
0408   ///
0409   /// @return bool is variable
0410   bool isVariable() const override { return true; }
0411 
0412   /// @brief returns the boundary type set in the template param
0413   ///
0414   /// @return @c AxisBoundaryType of this axis
0415   AxisBoundaryType getBoundaryType() const override { return bdt; }
0416 
0417   /// @brief Get #size bins which neighbor the one given
0418   ///
0419   /// Generic overload with symmetric size
0420   ///
0421   /// @param [in] idx requested bin index
0422   /// @param [in] size how many neighboring bins
0423   /// @return Set of neighboring bin indices (global)
0424   NeighborHoodIndices neighborHoodIndices(std::size_t idx,
0425                                           std::size_t size = 1) const {
0426     return neighborHoodIndices(idx, std::make_pair(-size, size));
0427   }
0428 
0429   /// @brief Get #size bins which neighbor the one given
0430   ///
0431   /// This is the version for Open
0432   ///
0433   /// @param [in] idx requested bin index
0434   /// @param [in] sizes how many neighboring bins (up/down)
0435   /// @return Set of neighboring bin indices (global)
0436   /// @note Open varies given bin and allows 0 and NBins+1 (underflow,
0437   /// overflow)
0438   ///       as neighbors
0439   template <AxisBoundaryType T = bdt,
0440             std::enable_if_t<T == AxisBoundaryType::Open, int> = 0>
0441   NeighborHoodIndices neighborHoodIndices(std::size_t idx,
0442                                           std::pair<int, int> sizes = {
0443                                               -1, 1}) const {
0444     constexpr int min = 0;
0445     const int max = getNBins() + 1;
0446     const int itmin = std::max(min, static_cast<int>(idx) + sizes.first);
0447     const int itmax = std::min(max, static_cast<int>(idx) + sizes.second);
0448     return NeighborHoodIndices(itmin, itmax + 1);
0449   }
0450 
0451   /// @brief Get #size bins which neighbor the one given
0452   ///
0453   /// This is the version for Bound
0454   ///
0455   /// @param [in] idx requested bin index
0456   /// @param [in] sizes how many neighboring bins (up/down)
0457   /// @return Set of neighboring bin indices (global)
0458   /// @note Bound varies given bin and allows 1 and NBins (regular bins)
0459   ///       as neighbors
0460   template <AxisBoundaryType T = bdt,
0461             std::enable_if_t<T == AxisBoundaryType::Bound, int> = 0>
0462   NeighborHoodIndices neighborHoodIndices(std::size_t idx,
0463                                           std::pair<int, int> sizes = {
0464                                               -1, 1}) const {
0465     if (idx <= 0 || idx >= (getNBins() + 1)) {
0466       return NeighborHoodIndices();
0467     }
0468     constexpr int min = 1;
0469     const int max = getNBins();
0470     const int itmin = std::max(min, static_cast<int>(idx) + sizes.first);
0471     const int itmax = std::min(max, static_cast<int>(idx) + sizes.second);
0472     return NeighborHoodIndices(itmin, itmax + 1);
0473   }
0474 
0475   /// @brief Get #size bins which neighbor the one given
0476   ///
0477   /// This is the version for Closed
0478   ///
0479   /// @param [in] idx requested bin index
0480   /// @param [in] sizes how many neighboring bins (up/down)
0481   /// @return Set of neighboring bin indices (global)
0482   /// @note Closed varies given bin and allows bins on the opposite
0483   ///       side of the axis as neighbors. (excludes underflow / overflow)
0484   template <AxisBoundaryType T = bdt,
0485             std::enable_if_t<T == AxisBoundaryType::Closed, int> = 0>
0486   NeighborHoodIndices neighborHoodIndices(std::size_t idx,
0487                                           std::pair<int, int> sizes = {
0488                                               -1, 1}) const {
0489     // Handle invalid indices
0490     if (idx <= 0 || idx >= (getNBins() + 1)) {
0491       return NeighborHoodIndices();
0492     }
0493 
0494     // Handle corner case where user requests more neighbours than the number
0495     // of bins on the axis. All bins are returned in this case
0496 
0497     const int max = getNBins();
0498     sizes.first = std::clamp(sizes.first, -max, max);
0499     sizes.second = std::clamp(sizes.second, -max, max);
0500     if (std::abs(sizes.first - sizes.second) >= max) {
0501       sizes.first = 1 - idx;
0502       sizes.second = max - idx;
0503     }
0504 
0505     // If the entire index range is not covered, we must wrap the range of
0506     // targeted neighbor indices into the range of valid bin indices. This may
0507     // split the range of neighbor indices in two parts:
0508     //
0509     // Before wraparound - [        XXXXX]XXX
0510     // After wraparound  - [ XXXX   XXXX ]
0511     //
0512     const int itmin = idx + sizes.first;
0513     const int itmax = idx + sizes.second;
0514     const std::size_t itfirst = wrapBin(itmin);
0515     const std::size_t itlast = wrapBin(itmax);
0516     if (itfirst <= itlast) {
0517       return NeighborHoodIndices(itfirst, itlast + 1);
0518     } else {
0519       return NeighborHoodIndices(itfirst, max + 1, 1, itlast + 1);
0520     }
0521   }
0522 
0523   /// @brief Converts bin index into a valid one for this axis.
0524   ///
0525   /// @note Open: bin index is clamped to [0, nBins+1]
0526   ///
0527   /// @param [in] bin The bin to wrap
0528   /// @return valid bin index
0529   template <AxisBoundaryType T = bdt,
0530             std::enable_if_t<T == AxisBoundaryType::Open, int> = 0>
0531   std::size_t wrapBin(int bin) const {
0532     return std::max(std::min(bin, static_cast<int>(getNBins()) + 1), 0);
0533   }
0534 
0535   /// @brief Converts bin index into a valid one for this axis.
0536   ///
0537   /// @note Bound: bin index is clamped to [1, nBins]
0538   ///
0539   /// @param [in] bin The bin to wrap
0540   /// @return valid bin index
0541   template <AxisBoundaryType T = bdt,
0542             std::enable_if_t<T == AxisBoundaryType::Bound, int> = 0>
0543   std::size_t wrapBin(int bin) const {
0544     return std::max(std::min(bin, static_cast<int>(getNBins())), 1);
0545   }
0546 
0547   /// @brief Converts bin index into a valid one for this axis.
0548   ///
0549   /// @note Closed: bin index wraps around to other side
0550   ///
0551   /// @param [in] bin The bin to wrap
0552   /// @return valid bin index
0553   template <AxisBoundaryType T = bdt,
0554             std::enable_if_t<T == AxisBoundaryType::Closed, int> = 0>
0555   std::size_t wrapBin(int bin) const {
0556     const int w = getNBins();
0557     return 1 + (w + ((bin - 1) % w)) % w;
0558     // return int(bin<1)*w - int(bin>w)*w + bin;
0559   }
0560 
0561   /// @brief get corresponding bin index for given coordinate
0562   ///
0563   /// @param  [in] x input coordinate
0564   /// @return index of bin containing the given value
0565   ///
0566   /// @note Bin intervals are defined with closed lower bounds and open upper
0567   ///       bounds, that is \f$l <= x < u\f$ if the value @c x lies within a
0568   ///       bin with lower bound @c l and upper bound @c u.
0569   /// @note Bin indices start at @c 1. The underflow bin has the index @c 0
0570   ///       while the index <tt>nBins + 1</tt> indicates the overflow bin .
0571   std::size_t getBin(ActsScalar x) const {
0572     const auto it =
0573         std::upper_bound(std::begin(m_binEdges), std::end(m_binEdges), x);
0574     return wrapBin(std::distance(std::begin(m_binEdges), it));
0575   }
0576 
0577   /// @brief get bin width
0578   ///
0579   /// @param  [in] bin index of bin
0580   /// @return width of given bin
0581   ///
0582   /// @pre @c bin must be a valid bin index (excluding under-/overflow bins),
0583   ///      i.e. \f$1 \le \text{bin} \le \text{nBins}\f$
0584   ActsScalar getBinWidth(std::size_t bin) const {
0585     return m_binEdges.at(bin) - m_binEdges.at(bin - 1);
0586   }
0587 
0588   /// @brief get lower bound of bin
0589   ///
0590   /// @param  [in] bin index of bin
0591   /// @return lower bin boundary
0592   ///
0593   /// @pre @c bin must be a valid bin index (excluding the underflow bin),
0594   ///      i.e. \f$1 \le \text{bin} \le \text{nBins} + 1\f$
0595   ///
0596   /// @note Bin intervals have a closed lower bound, i.e. the lower boundary
0597   ///       belongs to the bin with the given bin index.
0598   ActsScalar getBinLowerBound(std::size_t bin) const {
0599     return m_binEdges.at(bin - 1);
0600   }
0601 
0602   /// @brief get upper bound of bin
0603   ///
0604   /// @param  [in] bin index of bin
0605   /// @return upper bin boundary
0606   ///
0607   /// @pre @c bin must be a valid bin index (excluding the overflow bin),
0608   ///      i.e. \f$0 \le \text{bin} \le \text{nBins}\f$
0609   ///
0610   /// @note Bin intervals have an open upper bound, i.e. the upper boundary
0611   ///       does @b not belong to the bin with the given bin index.
0612   ActsScalar getBinUpperBound(std::size_t bin) const {
0613     return m_binEdges.at(bin);
0614   }
0615 
0616   /// @brief get bin center
0617   ///
0618   /// @param  [in] bin index of bin
0619   /// @return bin center position
0620   ///
0621   /// @pre @c bin must be a valid bin index (excluding under-/overflow bins),
0622   ///      i.e. \f$1 \le \text{bin} \le \text{nBins}\f$
0623   ActsScalar getBinCenter(std::size_t bin) const {
0624     return 0.5 * (getBinLowerBound(bin) + getBinUpperBound(bin));
0625   }
0626 
0627   /// @brief get maximum of binning range
0628   ///
0629   /// @return maximum of binning range
0630   ActsScalar getMax() const override { return m_binEdges.back(); }
0631 
0632   /// @brief get minimum of binning range
0633   ///
0634   /// @return minimum of binning range
0635   ActsScalar getMin() const override { return m_binEdges.front(); }
0636 
0637   /// @brief get total number of bins
0638   ///
0639   /// @return total number of bins (excluding under-/overflow bins)
0640   std::size_t getNBins() const override { return m_binEdges.size() - 1; }
0641 
0642   /// @brief check whether value is inside axis limits
0643   ///
0644   /// @return @c true if \f$\text{xmin} \le x < \text{xmax}\f$, otherwise
0645   ///         @c false
0646   ///
0647   /// @post If @c true is returned, the bin containing the given value is a
0648   ///       valid bin, i.e. it is neither the underflow nor the overflow bin.
0649   bool isInside(ActsScalar x) const {
0650     return (m_binEdges.front() <= x) && (x < m_binEdges.back());
0651   }
0652 
0653   /// @brief Return a vector of bin edges
0654   /// @return Vector which contains the bin edges
0655   std::vector<ActsScalar> getBinEdges() const override { return m_binEdges; }
0656 
0657  private:
0658   /// vector of bin edges (sorted in ascending order)
0659   std::vector<ActsScalar> m_binEdges;
0660 };
0661 }  // namespace Acts::detail