/*
*
* 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