Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-07-27 08:32:53

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 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 https://mozilla.org/MPL/2.0/.
0008 
0009 #pragma once
0010 
0011 #include "Acts/Utilities/IAxis.hpp"
0012 #include "Acts/Utilities/detail/MultiAxisHelper.hpp"
0013 
0014 #include <algorithm>
0015 #include <iostream>
0016 #include <memory>
0017 #include <stdexcept>
0018 
0019 #include <boost/container/small_vector.hpp>
0020 
0021 namespace Acts {
0022 
0023 template <std::size_t _DIM>
0024 class IMultiAxisXD;
0025 /// Type alias for a multi-axis of dimension 1
0026 using IMultiAxis1D = IMultiAxisXD<1>;
0027 /// Type alias for a multi-axis of dimension 2
0028 using IMultiAxis2D = IMultiAxisXD<2>;
0029 /// Type alias for a multi-axis of dimension 3
0030 using IMultiAxis3D = IMultiAxisXD<3>;
0031 
0032 /// @brief Common base class for all MultiAxis instances. This allows generic
0033 /// handling such as for inspection.
0034 ///
0035 /// A multi-axis describes the binning of a multi-dimensional grid as the
0036 /// product of several one-dimensional @c IAxis objects. The number of axes
0037 /// (i.e. the dimension of the grid) is only known at runtime through this
0038 /// interface; the dimension-aware variant is exposed by the derived
0039 /// @c IMultiAxisXD template.
0040 ///
0041 /// This base class exposes a type-erased, dynamically sized API (the @c *Any
0042 /// methods, using small vectors) so that grids of differing dimension can be
0043 /// handled through a common pointer. Bins are addressed either via a
0044 /// multi-index (one local bin index per axis) or via a single flattened global
0045 /// bin index. As for @c IAxis, local bin indices start at @c 1, with index
0046 /// @c 0 and <tt>nBins + 1</tt> denoting the underflow and overflow bins of an
0047 /// axis; flattened global indices include these under-/overflow bins.
0048 class IMultiAxis {
0049  private:
0050   /// Small vector type used to hold per-axis values without heap allocation
0051   /// for the common low-dimensional cases.
0052   template <typename T>
0053   using SmallVector = boost::container::small_vector<T, 3>;
0054 
0055  public:
0056   /// Flattened global bin index across all axes
0057   using GlobalBin = std::size_t;
0058   /// Dynamically sized multi-index holding one local bin index per axis
0059   using AnyLocalBins = SmallVector<std::size_t>;
0060   /// Dynamically sized point holding one coordinate per axis
0061   using AnyPoint = SmallVector<double>;
0062   /// Dynamically sized vector of (non-owning) pointers to the contained axes
0063   using AnyAxesVector = SmallVector<const IAxis*>;
0064 
0065   /// Factory method to create a multi-axis of dimension 1
0066   /// @param axis1 the single axis of the grid
0067   /// @return unique pointer to the created multi-axis
0068   static std::unique_ptr<IMultiAxis1D> create(const IAxis& axis1);
0069 
0070   /// Factory method to create a multi-axis of dimension 2
0071   /// @param axis1 the first axis of the grid
0072   /// @param axis2 the second axis of the grid
0073   /// @return unique pointer to the created multi-axis
0074   static std::unique_ptr<IMultiAxis2D> create(const IAxis& axis1,
0075                                               const IAxis& axis2);
0076 
0077   /// Factory method to create a multi-axis of dimension 3
0078   /// @param axis1 the first axis of the grid
0079   /// @param axis2 the second axis of the grid
0080   /// @param axis3 the third axis of the grid
0081   /// @return unique pointer to the created multi-axis
0082   static std::unique_ptr<IMultiAxis3D> create(const IAxis& axis1,
0083                                               const IAxis& axis2,
0084                                               const IAxis& axis3);
0085 
0086   virtual ~IMultiAxis() = default;
0087 
0088   /// Get the number of axes spanning the grid
0089   /// @return number of axes (i.e. the dimension of the grid)
0090   virtual std::size_t getNAxes() const = 0;
0091 
0092   /// Get the axis at the given dimension
0093   /// @param i index of the axis
0094   /// @return const reference to the requested axis
0095   virtual const IAxis& getAxis(std::size_t i) const = 0;
0096 
0097   /// Get the number of bins along each axis
0098   /// @return per-axis number of bins (excluding under-/overflow bins)
0099   virtual AnyLocalBins getNBinsAny() const {
0100     AnyLocalBins result;
0101     result.reserve(getNAxes());
0102     for (const IAxis& axis : *this) {
0103       result.push_back(axis.getNBins());
0104     }
0105     return result;
0106   }
0107 
0108   /// Get the total number of bins in the grid
0109   /// @param includeOverflowBins if @c true the under-/overflow bins of every axis are
0110   /// included in the count, otherwise only the regular bins are counted
0111   /// @return product of the per-axis bin counts
0112   virtual std::size_t getNTotalBins(bool includeOverflowBins = false) const {
0113     std::size_t result = 1;
0114     for (const IAxis& axis : *this) {
0115       result *= axis.getNBins() + (includeOverflowBins ? 2 : 0);
0116     }
0117     return result;
0118   }
0119 
0120   /// Get (non-owning) pointers to all contained axes
0121   /// @return vector of pointers to the axes, in axis order
0122   virtual AnyAxesVector getAnyAxesVector() const {
0123     AnyAxesVector result;
0124     std::ranges::transform(*this, std::back_inserter(result),
0125                            [](const IAxis& axis) { return &axis; });
0126     return result;
0127   }
0128 
0129   /// Get the lower boundary of the grid range along each axis
0130   /// @return point holding the minimum of each axis
0131   virtual AnyPoint getMinPointAny() const {
0132     AnyPoint result;
0133     result.reserve(getNAxes());
0134     for (const IAxis& axis : *this) {
0135       result.push_back(axis.getMin());
0136     }
0137     return result;
0138   }
0139 
0140   /// Get the upper boundary of the grid range along each axis
0141   /// @return point holding the maximum of each axis
0142   virtual AnyPoint getMaxPointAny() const {
0143     AnyPoint result;
0144     result.reserve(getNAxes());
0145     for (const IAxis& axis : *this) {
0146       result.push_back(axis.getMax());
0147     }
0148     return result;
0149   }
0150 
0151   /// Check whether a point lies inside the grid limits
0152   /// @param point coordinates to check, one per axis
0153   /// @return @c true if the point is within range along every axis
0154   /// @throws std::invalid_argument if the number of coordinates does not match
0155   ///         the number of axes
0156   virtual bool isInsideAny(const AnyPoint& point) const {
0157     if (point.size() != getNAxes()) {
0158       throw std::invalid_argument("Invalid number of coordinates");
0159     }
0160     for (std::size_t i = 0; i < point.size(); ++i) {
0161       const IAxis& axis = getAxis(i);
0162       if (!axis.isInside(point[i])) {
0163         return false;
0164       }
0165     }
0166     return true;
0167   }
0168 
0169   /// Get the lower-left corner of the bin given by a multi-index
0170   /// @param indices local bin indices along each axis
0171   /// @return point holding the lower bin boundary of each axis
0172   virtual AnyPoint getLowerLeftBinEdgeAny(const AnyLocalBins& indices) const {
0173     AnyPoint result;
0174     result.reserve(getNAxes());
0175     for (std::size_t i = 0; i < indices.size(); ++i) {
0176       const IAxis& axis = getAxis(i);
0177       result.push_back(axis.getBinLowerBound(indices[i]));
0178     }
0179     return result;
0180   }
0181 
0182   /// Get the upper-right corner of the bin given by a multi-index
0183   /// @param indices local bin indices along each axis
0184   /// @return point holding the upper bin boundary of each axis
0185   virtual AnyPoint getUpperRightBinEdgeAny(const AnyLocalBins& indices) const {
0186     AnyPoint result;
0187     result.reserve(getNAxes());
0188     for (std::size_t i = 0; i < indices.size(); ++i) {
0189       const IAxis& axis = getAxis(i);
0190       result.push_back(axis.getBinUpperBound(indices[i]));
0191     }
0192     return result;
0193   }
0194 
0195   /// Get the center of the bin given by a multi-index
0196   /// @param indices local bin indices along each axis
0197   /// @return point holding the bin center coordinate of each axis
0198   virtual AnyPoint getBinCenterAny(const AnyLocalBins& indices) const {
0199     AnyPoint result;
0200     result.reserve(getNAxes());
0201     for (std::size_t i = 0; i < indices.size(); ++i) {
0202       const IAxis& axis = getAxis(i);
0203       result.push_back(axis.getBinCenter(indices[i]));
0204     }
0205     return result;
0206   }
0207 
0208   /// Random-access iterator over the contained axes. Dereferencing yields a
0209   /// const reference to the @c IAxis at the current dimension.
0210   class iterator {
0211    public:
0212     /// The type of the values the iterator points to
0213     using value_type = const IAxis;
0214     /// The type used to represent the distance between two iterators
0215     using difference_type = std::ptrdiff_t;
0216     /// The pointer type of the values the iterator points to
0217     using pointer = const IAxis*;
0218     /// The reference type of the values the iterator points to
0219     using reference = const IAxis&;
0220 
0221     /// The iterator category (random-access)
0222     using iterator_category = std::random_access_iterator_tag;
0223     /// The iterator concept (random-access)
0224     using iterator_concept = std::random_access_iterator_tag;
0225 
0226     constexpr iterator() noexcept = default;
0227     /// Construct an iterator pointing at the given axis dimension
0228     /// @param multiAxis the multi-axis to iterate over
0229     /// @param index the axis dimension to point at
0230     constexpr iterator(const IMultiAxis& multiAxis, std::size_t index) noexcept
0231         : m_multiAxis(&multiAxis), m_index(index) {}
0232 
0233     /// Dereference the iterator
0234     /// @return a const reference to the axis at the current dimension
0235     constexpr reference operator*() const {
0236       return m_multiAxis->getAxis(m_index);
0237     }
0238     /// Pre-increment the iterator
0239     /// @return a reference to the incremented iterator
0240     constexpr iterator& operator++() noexcept {
0241       ++m_index;
0242       return *this;
0243     }
0244     /// Post-increment the iterator
0245     /// @return a copy of the iterator before incrementing
0246     constexpr iterator operator++(int) noexcept {
0247       auto tmp = *this;
0248       ++(*this);
0249       return tmp;
0250     }
0251     /// Pre-decrement the iterator
0252     /// @return a reference to the decremented iterator
0253     constexpr iterator& operator--() noexcept {
0254       --m_index;
0255       return *this;
0256     }
0257     /// Post-decrement the iterator
0258     /// @return a copy of the iterator before decrementing
0259     constexpr iterator operator--(int) noexcept {
0260       auto tmp = *this;
0261       --(*this);
0262       return tmp;
0263     }
0264     /// Advance the iterator by @p n positions
0265     /// @param n the number of positions to advance
0266     /// @return a reference to the advanced iterator
0267     constexpr iterator& operator+=(difference_type n) noexcept {
0268       m_index += n;
0269       return *this;
0270     }
0271     /// Move the iterator back by @p n positions
0272     /// @param n the number of positions to move back
0273     /// @return a reference to the moved iterator
0274     constexpr iterator& operator-=(difference_type n) noexcept {
0275       m_index -= n;
0276       return *this;
0277     }
0278 
0279    private:
0280     const IMultiAxis* m_multiAxis{};
0281     std::size_t m_index{};
0282 
0283     friend constexpr iterator operator+(iterator it,
0284                                         difference_type n) noexcept {
0285       return it += n;
0286     }
0287 
0288     friend constexpr iterator operator+(difference_type n,
0289                                         iterator it) noexcept {
0290       return it += n;
0291     }
0292 
0293     friend constexpr iterator operator-(iterator it,
0294                                         difference_type n) noexcept {
0295       return it -= n;
0296     }
0297 
0298     friend constexpr difference_type operator-(const iterator& lhs,
0299                                                const iterator& rhs) noexcept {
0300       return lhs.m_index - rhs.m_index;
0301     }
0302 
0303     friend constexpr auto operator<=>(const iterator& a,
0304                                       const iterator& b) noexcept {
0305       return a.m_index <=> b.m_index;
0306     }
0307 
0308     friend constexpr bool operator==(const iterator& a,
0309                                      const iterator& b) noexcept {
0310       return a.m_index == b.m_index;
0311     }
0312   };
0313 
0314   /// @return iterator to the first axis
0315   iterator begin() const { return iterator(*this, 0); }
0316 
0317   /// @return iterator past the last axis
0318   iterator end() const { return iterator(*this, getNAxes()); }
0319 
0320  protected:
0321   /// Print the contained axes to the given stream
0322   /// @param os output stream
0323   virtual void toStream(std::ostream& os) const {
0324     for (std::size_t i = 0; i < getNAxes(); ++i) {
0325       os << getAxis(i);
0326       if (i < getNAxes() - 1) {
0327         os << ", ";
0328       }
0329     }
0330   }
0331 
0332  private:
0333   /// Check if two multi-axes are equal
0334   /// @param lhs first multi-axis
0335   /// @param rhs second multi-axis
0336   /// @return @c true if both have the same number of axes and all axes compare
0337   ///         equal
0338   friend bool operator==(const IMultiAxis& lhs, const IMultiAxis& rhs) {
0339     if (lhs.getNAxes() != rhs.getNAxes()) {
0340       return false;
0341     }
0342     return std::ranges::equal(lhs, rhs);
0343   }
0344 
0345   /// Output stream operator
0346   /// @param os output stream
0347   /// @param multiAxis the multi-axis to be printed
0348   /// @return the output stream
0349   friend std::ostream& operator<<(std::ostream& os,
0350                                   const IMultiAxis& multiAxis) {
0351     multiAxis.toStream(os);
0352     return os;
0353   }
0354 };
0355 
0356 /// @brief Common base class for all multi-axes of a fixed, compile-time
0357 /// dimension.
0358 ///
0359 /// On top of the dynamically sized @c IMultiAxis API this adds a statically
0360 /// sized API (using @c std::array of fixed length @c DIM) and the grid index
0361 /// conversions between points, multi-indices and flattened global indices.
0362 /// The actual axis storage is provided by the concrete @c MultiAxis derived
0363 /// class.
0364 ///
0365 /// @tparam _DIM number of axes (dimension of the grid)
0366 template <std::size_t _DIM>
0367 class IMultiAxisXD : public IMultiAxis {
0368  public:
0369   /// Dimension of the grid (number of axes)
0370   static constexpr std::size_t DIM = _DIM;
0371 
0372   static_assert(DIM > 0, "MultiAxis dimension must be greater than zero");
0373 
0374   /// Statically sized multi-index holding one local bin index per axis
0375   using LocalBins = std::array<std::size_t, DIM>;
0376   /// Statically sized point holding one coordinate per axis
0377   using Point = std::array<double, DIM>;
0378   /// Statically sized array of (non-owning) pointers to the contained axes
0379   using AnyAxesArray = std::array<const IAxis*, DIM>;
0380   /// Tuple of const references to the contained axes
0381   using AnyAxesTuple = decltype(std::apply(
0382       [](auto&&... xs) { return std::tie(*xs...); }, AnyAxesArray{}));
0383 
0384   /// Get the number of axes spanning the grid
0385   /// @return the compile-time dimension @c DIM
0386   std::size_t getNAxes() const override { return DIM; }
0387 
0388   /// Get (non-owning) pointers to all contained axes
0389   /// @return fixed-size array of pointers to the axes, in axis order
0390   virtual AnyAxesArray getAnyAxesArray() const {
0391     AnyAxesArray result{};
0392     std::ranges::transform(*this, result.begin(),
0393                            [](const IAxis& axis) { return &axis; });
0394     return result;
0395   }
0396 
0397   /// Get const references to all contained axes as a tuple
0398   /// @return tuple of references to the axes, in axis order
0399   virtual AnyAxesTuple getAnyAxesTuple() const {
0400     return std::apply([](auto&&... xs) { return std::tie(*xs...); },
0401                       getAnyAxesArray());
0402   }
0403 
0404   /// Get the number of bins along each axis
0405   /// @return per-axis number of bins (excluding under-/overflow bins)
0406   virtual LocalBins getNBins() const {
0407     LocalBins result{};
0408     for (std::size_t i = 0; i < DIM; ++i) {
0409       result[i] = getAxis(i).getNBins();
0410     }
0411     return result;
0412   }
0413 
0414   /// Get the lower boundary of the grid range along each axis
0415   /// @return point holding the minimum of each axis
0416   virtual Point getMinPoint() const {
0417     Point result{};
0418     for (std::size_t i = 0; i < DIM; ++i) {
0419       result[i] = getAxis(i).getMin();
0420     }
0421     return result;
0422   }
0423 
0424   /// Get the upper boundary of the grid range along each axis
0425   /// @return point holding the maximum of each axis
0426   virtual Point getMaxPoint() const {
0427     Point result{};
0428     for (std::size_t i = 0; i < DIM; ++i) {
0429       result[i] = getAxis(i).getMax();
0430     }
0431     return result;
0432   }
0433 
0434   /// Check whether a point lies inside the grid limits
0435   /// @param point coordinates to check, one per axis
0436   /// @return @c true if the point is within range along every axis
0437   virtual bool isInside(const Point& point) const {
0438     for (std::size_t i = 0; i < DIM; ++i) {
0439       if (!getAxis(i).isInside(point[i])) {
0440         return false;
0441       }
0442     }
0443     return true;
0444   }
0445 
0446   /// Get the lower-left corner of the bin given by a multi-index
0447   /// @param localBins local bin indices along each axis
0448   /// @return point holding the lower bin boundary of each axis
0449   virtual Point getLowerLeftBinEdge(const LocalBins& localBins) const {
0450     Point result{};
0451     for (std::size_t i = 0; i < DIM; ++i) {
0452       result[i] = getAxis(i).getBinLowerBound(localBins[i]);
0453     }
0454     return result;
0455   }
0456 
0457   /// Get the upper-right corner of the bin given by a multi-index
0458   /// @param localBins local bin indices along each axis
0459   /// @return point holding the upper bin boundary of each axis
0460   virtual Point getUpperRightBinEdge(const LocalBins& localBins) const {
0461     Point result{};
0462     for (std::size_t i = 0; i < DIM; ++i) {
0463       result[i] = getAxis(i).getBinUpperBound(localBins[i]);
0464     }
0465     return result;
0466   }
0467 
0468   /// Get the center of the bin given by a multi-index
0469   /// @param localBins local bin indices along each axis
0470   /// @return point holding the bin center coordinate of each axis
0471   virtual Point getBinCenter(const LocalBins& localBins) const {
0472     Point result{};
0473     for (std::size_t i = 0; i < DIM; ++i) {
0474       result[i] = getAxis(i).getBinCenter(localBins[i]);
0475     }
0476     return result;
0477   }
0478 
0479   /// Get the bin width along each axis for a given multi-index
0480   /// @param localBins local bin indices along each axis
0481   /// @return point holding the bin width along each axis
0482   virtual Point getBinWidth(const LocalBins& localBins) const {
0483     Point result{};
0484     for (std::size_t i = 0; i < DIM; ++i) {
0485       result[i] = getAxis(i).getBinWidth(localBins[i]);
0486     }
0487     return result;
0488   }
0489 
0490   /// Determine the flattened global bin index for a given point
0491   /// @param point coordinates to look up, one per axis
0492   /// @return global bin index of the bin containing the point
0493   virtual GlobalBin getGlobalBinFromPoint(const Point& point) const {
0494     return getGlobalBinFromLocalBins(getLocalBinsFromPoint(point));
0495   }
0496 
0497   /// Determine the flattened global bin index from a multi-index
0498   /// @param localBins local bin indices along each axis (under-/overflow bins
0499   ///        are allowed)
0500   /// @return global bin index of the bin
0501   virtual GlobalBin getGlobalBinFromLocalBins(
0502       const LocalBins& localBins) const {
0503     return detail::MultiAxisHelper::getGlobalBinFromLocalBins(
0504         localBins, getAnyAxesTuple());
0505   }
0506 
0507   /// Determine the multi-index of local bin indices for a given point
0508   /// @param point coordinates to look up, one per axis
0509   /// @return local bin indices along each axis (may be under-/overflow bins)
0510   virtual LocalBins getLocalBinsFromPoint(const Point& point) const {
0511     return detail::MultiAxisHelper::getLocalBinsFromPoint(point,
0512                                                           getAnyAxesTuple());
0513   }
0514 
0515   /// Determine the multi-index of local bin indices from a flattened global
0516   /// bin index
0517   /// @param globalBin global bin index
0518   /// @return local bin indices along each axis (may be under-/overflow bins)
0519   virtual LocalBins getLocalBinsFromGlobalBin(GlobalBin globalBin) const {
0520     return detail::MultiAxisHelper::getLocalBinsFromGlobalBin(
0521         globalBin, getAnyAxesTuple());
0522   }
0523 
0524   /// Get the global bin indices of the bins in the neighborhood of a bin
0525   /// @param localBins local bin indices of the bin of interest
0526   /// @param size number of adjacent bins to include along each axis (symmetric)
0527   /// @return sorted collection of global bin indices in the neighborhood
0528   virtual detail::FlatNeighborHoodIndices<DIM> getNeighborHoodIndices(
0529       const LocalBins& localBins, std::size_t size = 1u) const = 0;
0530 
0531   /// Get the global bin indices of the bins in the neighborhood of a bin
0532   /// @param localBins local bin indices of the bin of interest
0533   /// @param size of neighborhood determining how many
0534   /// adjacent bins along each axis are considered
0535   /// @return sorted collection of global bin indices in the neighborhood
0536   virtual detail::FlatNeighborHoodIndices<DIM> getNeighborHoodIndices(
0537       const LocalBins& localBins, const std::pair<int, int>& size) const = 0;
0538 
0539   /// Get the global bin indices of the bins in the neighborhood of a bin, with
0540   /// a separate neighborhood size per axis
0541   /// @param localBins local bin indices of the bin of interest
0542   /// @param sizePerAxis per-axis lower/upper number of adjacent bins to include
0543   /// @return sorted collection of global bin indices in the neighborhood
0544   virtual detail::FlatNeighborHoodIndices<DIM> getNeighborHoodIndices(
0545       const LocalBins& localBins,
0546       const std::array<std::pair<int, int>, DIM>& sizePerAxis) const = 0;
0547 
0548   /// Get the global bin indices of the grid points closest to the given bin
0549   /// @param localBins local bin indices of the bin of interest
0550   /// @return sorted collection of global bin indices whose lower-left corners
0551   ///         are the closest grid points to every point in the given bin
0552   virtual detail::FlatNeighborHoodIndices<DIM> getClosestPointsIndices(
0553       const LocalBins& localBins) const = 0;
0554 
0555   /// Get the global bin indices of the grid points closest to the given point
0556   /// @param point coordinates to look up, one per axis
0557   /// @return sorted collection of global bin indices of the closest grid points
0558   virtual detail::FlatNeighborHoodIndices<DIM> getClosestPointsIndices(
0559       const Point& point) const {
0560     return getClosestPointsIndices(getLocalBinsFromPoint(point));
0561   }
0562 };
0563 
0564 }  // namespace Acts