/* * * This is modification of Tino Kluge tk spline * calculation is optimized for tridiagonal matrices * * Copyright(C) 2017 * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef CATIMA_SPLINE_H #define CATIMA_SPLINE_H #include #include #include #include #include #include "catima/constants.h" #ifdef GSL_INTERPOLATION #include #endif namespace catima { enum interpolation_t {cspline, linear}; /** * Tridiagonal matrix solver */ template class tridiagonal_matrix { private: std::array a; std::array d; std::array c; public: tridiagonal_matrix() {} // access operator double & operator () (unsigned int i, unsigned int j); // write double operator () (unsigned int i, unsigned int j) const; // read std::array trig_solve(const std::array& b) const; }; template double & tridiagonal_matrix::operator () (unsigned int i, unsigned int j) { int k=j-i; if(k == -1)return c[i]; else if(k==0) return d[i]; else return a[i]; } template double tridiagonal_matrix::operator () (unsigned int i, unsigned int j) const { int k=j-i; if(k==-1)return c[i]; else if(k==0) return d[i]; else if(k==1)return a[i]; else return 0.0; } template std::array tridiagonal_matrix::trig_solve(const std::array& b) const { std::array x; if(d[0] == 0.0){return x;} std::array g; x[0] = b[0]/d[0]; double bet = d[0]; for(std::size_t j=1, max=N;j=0;j--){ x[j] -= g[j+1]*x[j+1]; } return x; } /** * Cubic Spline class, accepting EnergyTable type as x-variable */ template struct cspline_special{ constexpr static int N = T::size(); cspline_special(const T& x, const std::vector& y, bool boundary_second_deriv = true); cspline_special() = default; const T *table; const double *m_x; const double *m_y; std::array m_a,m_b,m_c; double m_b0, m_c0; double operator()(double x)const{return evaluate(x);} double evaluate(double x) const { int idx=std::max( table->index(x), 0); double h=x-m_x[idx]; double interpol; if(xm_x[N-1]) { // extrapolation to the right interpol=(m_b[N-1]*h + m_c[N-1])*h + m_y[N-1]; } else { // interpolation interpol=((m_a[idx]*h + m_b[idx])*h + m_c[idx])*h + m_y[idx]; } return interpol; } double deriv(double x) const { int idx=std::max( table->index(x), 0); double h=x-m_x[idx]; double interpol; if(xm_x[N-1]) { // extrapolation to the right interpol=2.0*m_b[N-1]*h + m_c[N-1]; } else { // interpolation interpol=(3.0*m_a[idx]*h + 2.0*m_b[idx])*h + m_c[idx]; } return interpol; } static_assert (T::size()>2, "N must be > 2"); }; template cspline_special::cspline_special(const T &x, const std::vector& y, bool boundary_second_deriv ):table(&x),m_y(y.data()),m_x(x.values) { static_assert (N>2, "N must be > 2"); tridiagonal_matrix A{}; std::array rhs; for(std::size_t i=1; i