Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2024-09-27 07:03:24

0001 // SPDX-License-Identifier: LGPL-3.0-or-later
0002 // Copyright (C) 2023 Duane Byer
0003 
0004 #ifndef Interpolate_
0005 #define Interpolate_
0006 
0007 #include <array>
0008 #include <cmath>
0009 #include <cstddef>
0010 #include <limits>
0011 #include <stdexcept>
0012 #include <vector>
0013 
0014 /// Linearly interpolate between two endpoints, with \p x between 0 and 1.
0015 template<typename T>
0016 inline T linear(T f1, T f2, T x) {
0017     return (1. - x) * f1 + x * f2;
0018 }
0019 
0020 /// Linear interpolation for non-uniformly spaced points.
0021 template<typename T>
0022 inline T linear(T f1, T f2, T h, T x) {
0023     return ((h - x) / h) * f1 + (x / h) * f2;
0024 }
0025 
0026 /// Cubic interpolation using \p f2 and \p f3 as the endpoints, and \p f1 and
0027 /// \p f4 as the anchor points beyond the endpoints. \p x between 0 and 1.
0028 template<typename T>
0029 inline T cubic(T f0, T f1, T f2, T f3, T x) {
0030     return
0031         2. * (1. - x) * (1. - x) * (0.5 + x) * f1
0032         + 0.5 * (1. - x) * (1. - x) * x * (f2 - f0)
0033         + 2. * x * x * (1.5 - x) * f2
0034         - 0.5 * x * x * (1. - x) * (f3 - f1);
0035 }
0036 
0037 /// Cubic interpolation for non-uniformly spaced points.
0038 template<typename T>
0039 inline T cubic(T f0, T f1, T f2, T f3, T h0, T h1, T h2, T x) {
0040     T h01 = h0 + h1;
0041     T h12 = h1 + h2;
0042     T h = h0 + h1 + h2;
0043     T c0, c1, c2, c3;
0044     if (!std::isfinite(h0) && !std::isfinite(h2)) {
0045         return linear(f1, f2, h1, x);
0046     } else if (!std::isfinite(h0)) {
0047         c0 = 0.;
0048         c1 = (h1 - x) * (h1 * h12 - x * x) / (h1 * h1 * h12);
0049         c2 = x * (h1 * h2 + h1 * x - x * x) / (h2 * h1 * h1);
0050         c3 = -x * x * (h1 - x) / (h1 * h2 * h12);
0051     } else if (!std::isfinite(h2)) {
0052         c0 = -(h1 - x) * (h1 - x) * x / (h0 * h1 * h01);
0053         c1 = (h1 - x) * (h0 * h1 + h1 * x - x * x) / (h0 * h1 * h1);
0054         c2 = x * (h0 * h1 + 2. * h1 * x - x * x) / (h1 * h1 * h01);
0055         c3 = 0.;
0056     } else {
0057         c0 = -(h1 - x) * (h1 - x) * x / (h0 * h1 * h01);
0058         c1 = (h1 - x) * (h0 * h1 * h12 + h1 * h12 * x - h * x * x) / (h0 * h1 * h1 * h12);
0059         c2 = x * (h0 * h1 * h2 + h1 * (h + h2) * x - h * x * x) / (h2 * h1 * h1 * h01);
0060         c3 = -x * x * (h1 - x) / (h1 * h2 * h12);
0061     }
0062     return c0 * f0 + c1 * f1 + c2 * f2 + c3 * f3;
0063 }
0064 
0065 template<std::size_t N>
0066 using CellIndex = std::array<std::size_t, N>;
0067 template<typename T, std::size_t N>
0068 using Point = std::array<T, N>;
0069 template<typename T, std::size_t N>
0070 using Coords = std::array<std::vector<T>, N>;
0071 
0072 template<typename T, std::size_t N>
0073 class Grid;
0074 
0075 enum class Scale {
0076     LINEAR = 0,
0077     LOG    = 1,
0078 };
0079 
0080 /**
0081  * A view into an Grid (or another dataset). The difference between a GridView
0082  * and a Grid is that a Grid owns its data, while a GridView provides a Grid-
0083  * like interface into an already existing set of data.
0084  */
0085 template<typename T, std::size_t N>
0086 class GridView final {
0087     T const* _data;
0088     Coords<T, N> _coords;
0089     std::size_t _count_total;
0090 
0091 public:
0092     /// Construct an GridView out of \p data. The data points are located at the
0093     /// positions given in the \p grid array.
0094     GridView(T const* data, Coords<T, N> coords);
0095     //GridView(T const* data, std::size_t count[N], T lower[N], T upper[N]);
0096     //GridView(T const* data, std::size_t count[N], T lower[N], T upper[N], Scale scale[N]);
0097 
0098     /// Number of data points in dimension \p dim.
0099     std::size_t count(std::size_t dim) const {
0100         return _coords[dim].size();
0101     }
0102     /// Number of data points in each dimension.
0103     CellIndex<N> count() const {
0104         CellIndex<N> result;
0105         for (std::size_t dim = 0; dim < N; ++dim) {
0106             result[dim] = _coords[dim].size();
0107         }
0108         return result;
0109     }
0110     /// Total number of data points.
0111     std::size_t count_total() const {
0112         return _count_total;
0113     }
0114     /// Access to the contigious underlying data.
0115     T const* data() const {
0116         return _data;
0117     }
0118 
0119     /// Access a slice of the data.
0120     GridView<T, N - 1> operator[](std::size_t idx) const;
0121     /// Access an element from the grid.
0122     T const& operator[](CellIndex<N> idx) const;
0123     /// Reduce the dimension of the GridView by accessing a slice.
0124     GridView<T, N - 1> reduce(std::size_t idx) const {
0125         return operator[](idx);
0126     }
0127 
0128     /// Get the grid index associated with a particular point in space.
0129     std::size_t grid_from_space(std::size_t dim, T x) const;
0130     std::size_t grid_from_space(std::size_t dim, T x, T* frac) const;
0131     CellIndex<N> grid_from_space(Point<T, N> x) const;
0132     CellIndex<N> grid_from_space(Point<T, N> x, Point<T, N>* frac) const;
0133 
0134     /// Gets the point in space associated with a grid index.
0135     T space_from_grid(std::size_t dim, std::size_t idx) const;
0136     Point<T, N> space_from_grid(CellIndex<N> idx) const;
0137 };
0138 
0139 /// Base specialization of GridView.
0140 template<typename T>
0141 class GridView<T, 0> {
0142     T const* _data;
0143 
0144 public:
0145     explicit GridView(T const* data) : _data(data) { }
0146     GridView(T const* data, Coords<T, 0> coords) :
0147             _data(data) {
0148         static_cast<void>(coords);
0149     }
0150 
0151     CellIndex<0> count() const {
0152         return CellIndex<0>();
0153     }
0154     std::size_t count_total() const {
0155         return 1;
0156     }
0157     T const* data() const {
0158         return _data;
0159     }
0160 
0161     T const& operator[](CellIndex<0> idx) {
0162         return *_data;
0163     }
0164     operator T const&() const {
0165         return *_data;
0166     }
0167 };
0168 
0169 /**
0170  * A set of N-d data. A Grid is an owned version of a GridView.
0171  *
0172  * \sa GridView
0173  */
0174 template<typename T, std::size_t N>
0175 class Grid final {
0176     std::vector<T> _data;
0177     Coords<T, N> _coords;
0178     std::size_t _count_total;
0179 
0180 public:
0181     Grid() : _data(), _coords(), _count_total(0) { }
0182     Grid(T const* data, Coords<T, N> coords);
0183     operator GridView<T, N>() const {
0184         return GridView<T, N>(_data.data(), _coords);
0185     }
0186 
0187     /// \copydoc GridView::count()
0188     std::size_t count(std::size_t dim) const {
0189         return static_cast<GridView<T, N> >(*this).count(dim);
0190     }
0191     /// \copydoc GridView::count()
0192     CellIndex<N> count() const {
0193         return static_cast<GridView<T, N> >(*this).count();
0194     }
0195     /// \copydoc GridView::count_total()
0196     std::size_t count_total() const {
0197         return _count_total;
0198     }
0199     /// \copydoc GridView::data()
0200     T const* data() const {
0201         return _data.data();
0202     }
0203 
0204     /// \copydoc GridView::operator[]()
0205     GridView<T, N - 1> operator[](std::size_t idx) const {
0206         return static_cast<GridView<T, N> >(*this)[idx];
0207     }
0208     /// \copydoc GridView::operator[]()
0209     T const& operator[](CellIndex<N> idx) const {
0210         return static_cast<GridView<T, N> >(*this)[idx];
0211     }
0212     /// \copydoc GridView::reduce()
0213     GridView<T, N - 1> reduce(std::size_t idx) const {
0214         return operator[](idx);
0215     }
0216 
0217     /// \copydoc GridView::grid_from_space()
0218     std::size_t grid_from_space(std::size_t dim, T x) const {
0219         return static_cast<GridView<T, N> >(*this).grid_from_space(dim, x);
0220     }
0221     std::size_t grid_from_space(std::size_t dim, T x, T* frac) const {
0222         return static_cast<GridView<T, N> >(*this).grid_from_space(dim, x, frac);
0223     }
0224     CellIndex<N> grid_from_space(Point<T, N> x) const {
0225         return static_cast<GridView<T, N> >(*this).grid_from_space(x);
0226     }
0227     CellIndex<N> grid_from_space(Point<T, N> x, Point<T, N>* frac) const {
0228         return static_cast<GridView<T, N> >(*this).grid_from_space(x, frac);
0229     }
0230 
0231     /// \copydoc GridView::space_from_grid()
0232     T space_from_grid(std::size_t dim, std::size_t idx) const {
0233         return static_cast<GridView<T, N> >(*this).space_from_grid(dim, idx);
0234     }
0235     Point<T, N> space_from_grid(CellIndex<N> idx) const {
0236         return static_cast<GridView<T, N> >(*this).space_from_grid(idx);
0237     }
0238 };
0239 
0240 /// Base specialization of Grid.
0241 template<typename T>
0242 class Grid<T, 0> final {
0243     T _data;
0244 
0245 public:
0246     Grid() : _data() { }
0247     Grid(T const* data) : _data(*data) { };
0248     Grid(T const* data, Coords<T, 0> coords) : _data(*data) {
0249         static_cast<void>(coords);
0250     };
0251     operator GridView<T, 0>() const {
0252         return GridView<T, 0>(&_data);
0253     }
0254 
0255     /// \copydoc GridView::count()
0256     CellIndex<0> count() const {
0257         return CellIndex<0>();
0258     }
0259     /// \copydoc GridView::count_total()
0260     std::size_t count_total() const {
0261         return 1;
0262     }
0263     /// \copydoc GridView::data()
0264     T const* data() const {
0265         return &_data;
0266     }
0267 
0268     /// \copydoc GridView::operator[]()
0269     T const& operator[](CellIndex<0> idx) const {
0270         return static_cast<GridView<T, 0> >(*this)[idx];
0271     }
0272 };
0273 
0274 /**
0275  * A function that uses linear interpolation for a regularly spaced rectangular
0276  * grid of N-d data.
0277  */
0278 template<typename T, std::size_t N>
0279 class LinearView final {
0280     static_assert(
0281         std::is_floating_point<T>::value,
0282         "Cannot have a LinearView of a non-floating-point type.");
0283 
0284     GridView<T, N> _grid;
0285 
0286 public:
0287     LinearView(GridView<T, N> grid) : _grid(grid) { }
0288     /// Interpolate from the underlying GridView.
0289     T operator()(Point<T, N> x) const;
0290 };
0291 
0292 /// Base specialization of LinearView.
0293 template<typename T>
0294 class LinearView<T, 0> final {
0295     static_assert(
0296         std::is_floating_point<T>::value,
0297         "Cannot have a LinearView of a non-floating-point type.");
0298 
0299     GridView<T, 0> _grid;
0300 
0301 public:
0302     explicit LinearView(GridView<T, 0> grid) : _grid(grid) { }
0303     T operator()(Point<T, 0> x) const {
0304         static_cast<void>(x);
0305         return static_cast<T>(_grid);
0306     }
0307 };
0308 
0309 /**
0310  * A function that uses cubic interpolation for a regularly spaced rectangular
0311  * grid of N-d data.
0312  */
0313 template<typename T, std::size_t N>
0314 class CubicView final {
0315     static_assert(
0316         std::is_floating_point<T>::value,
0317         "Cannot have a CubicView of a non-floating-point type.");
0318 
0319     GridView<T, N> _grid;
0320 
0321 public:
0322     explicit CubicView(GridView<T, N> grid) : _grid(grid) { }
0323     /// Interpolate from the underlying GridView.
0324     T operator()(Point<T, N> x) const;
0325 };
0326 
0327 /// Base specialization of CubicView.
0328 template<typename T>
0329 class CubicView<T, 0> final {
0330     static_assert(
0331         std::is_floating_point<T>::value,
0332         "Cannot have a CubicView of a non-floating-point type.");
0333 
0334     GridView<T, 0> _grid;
0335 
0336 public:
0337     explicit CubicView(GridView<T, 0> grid) : _grid(grid) { }
0338     T operator()(Point<T, 0> x) const {
0339         static_cast<void>(x);
0340         return static_cast<T>(_grid);
0341     }
0342 };
0343 
0344 /// Loads grids from an array of tuples. The provided data points must be
0345 /// provided either in row-major order or column-major order. For each data
0346 /// point, the first \p N numbers give the coordinates, and the next \p K
0347 /// numbers give the Grid values at those coordinates. \p K different Grid%s
0348 /// are returned.
0349 template<typename T, std::size_t N, std::size_t K = 1>
0350 std::array<Grid<T, N>, K> read_grids(
0351     std::vector<std::array<T, N + K> > const& raw_data);
0352 
0353 /// Not enough points were provided to form a Grid without ragged edges.
0354 struct NotEnoughPointsError : public std::runtime_error {
0355     std::size_t points;
0356     std::size_t expected_points;
0357     NotEnoughPointsError(std::size_t points, std::size_t expected_points);
0358 };
0359 
0360 /// One of the dimensions of a Grid is only one data point thick.
0361 struct SingularDimensionError : public std::runtime_error {
0362     std::size_t dim;
0363     SingularDimensionError(std::size_t dim);
0364 };
0365 
0366 /// The hyper-cube bounds on a Grid invalid, most likely because the hyper-cube
0367 /// has negative volume.
0368 struct InvalidGridPlanesError : public std::runtime_error {
0369     InvalidGridPlanesError();
0370 };
0371 
0372 /// Data points used to construct a Grid are not spaced evenly.
0373 struct InvalidSpacingError : public std::runtime_error {
0374     InvalidSpacingError();
0375 };
0376 
0377 /// Data points used to construct a Grid are provided in an unexpected order.
0378 struct UnexpectedGridPointError : public std::runtime_error {
0379     std::size_t line_number;
0380     UnexpectedGridPointError(std::size_t line_number);
0381 };
0382 
0383 #include "Interpolate.ipp"
0384 
0385 #endif
0386