version 3.10-dev
cubicspline.hh
Go to the documentation of this file.
1// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2// vi: set et ts=4 sw=4 sts=4:
3//
4// SPDX-FileCopyrightInfo: Copyright © DuMux Project contributors, see AUTHORS.md in root folder
5// SPDX-License-Identifier: GPL-3.0-or-later
6//
12#ifndef DUMUX_COMMON_CUBIC_SPLINE_HH
13#define DUMUX_COMMON_CUBIC_SPLINE_HH
14
15#include <iterator>
16#include <vector>
17#include <algorithm>
18
19#include <dune/common/fvector.hh>
20#include <dune/common/fmatrix.hh>
21#include <dune/istl/btdmatrix.hh>
22#include <dune/istl/bvector.hh>
23
24namespace Dumux {
25
31template<class Scalar = double>
33{
34public:
38 CubicSpline() = default;
39
45 CubicSpline(const std::vector<Scalar>& x, const std::vector<Scalar> y)
46 {
47 // check some requirements
48 assert (x.size() == y.size());
49 assert (x.size() >=2);
50 assert (std::is_sorted(x.begin(), x.end()));
51
52 updatePoints(x, y);
53 }
54
61 void updatePoints(const std::vector<Scalar>& x, const std::vector<Scalar>& y)
62 {
63 // save a copy of the control points
64 x_ = x;
65
66 // the number of control points
67 numPoints_ = x.size();
68
69 const auto numSegments = numPoints_-1;
70 coeff_.resize(numSegments*4+2); // 4 coefficients for each segment + last y-value and derivative
71
72 // construct a block-tridiagonal matrix system solving for the derivatives
73 Dune::BTDMatrix<Dune::FieldMatrix<double, 1, 1>> matrix(numPoints_);
74 Dune::BlockVector<Dune::FieldVector<double, 1>> rhs(numPoints_);
75 Dune::BlockVector<Dune::FieldVector<double, 1>> d(numPoints_);
76
77 // assemble matrix and rhs row-wise
78 matrix[0][0] = 2.0;
79 matrix[0][1] = 1.0;
80 rhs[0] = 3.0*(y[1] - y[0]);
81 for (int i = 1; i < numPoints_-1; ++i)
82 {
83 matrix[i][i-1] = 1.0;
84 matrix[i][i] = 4.0;
85 matrix[i][i+1] = 1.0;
86 rhs[i] = 3.0*(y[i+1] - y[i-1]);
87 }
88 matrix[numPoints_-1][numPoints_-1] = 2.0;
89 matrix[numPoints_-1][numPoints_-2] = 1.0;
90 rhs[numPoints_-1] = 3.0*(y[numPoints_-1] - y[numPoints_-2]);
91
92 // solve for derivatives
93 matrix.solve(d, rhs);
94
95 // compute coefficients
96 std::size_t offset = 0;
97 for (int i = 0; i < numSegments; ++i, offset += 4)
98 {
99 coeff_[offset+0] = y[i];
100 coeff_[offset+1] = d[i];
101 coeff_[offset+2] = 3.0*(y[i+1]-y[i]) - 2.0*d[i] - d[i+1];
102 coeff_[offset+3] = 2.0*(y[i]-y[i+1]) + d[i] + d[i+1];
103 }
104 coeff_[offset+0] = y[numPoints_-1];
105 coeff_[offset+1] = d[numPoints_-1];
106 }
107
113 Scalar eval(const Scalar x) const
114 {
115 if (x <= x_[0])
116 return coeff_[0] + evalDerivative(x_[0])*(x - x_[0]);
117 else if (x > x_[numPoints_-1])
118 return coeff_[(numPoints_-1)*4] + evalDerivative(x_[numPoints_-1])*(x - x_[numPoints_-1]);
119 else
120 {
121 const auto lookUpIndex = std::distance(x_.begin(), std::lower_bound(x_.begin(), x_.end(), x));
122 assert(lookUpIndex != 0);
123
124 // get coefficients
125 const auto* coeff = coeff_.data() + (lookUpIndex-1)*4;
126
127 // interpolate parametrization parameter t in [0,1]
128 const auto t = (x - x_[lookUpIndex-1])/(x_[lookUpIndex] - x_[lookUpIndex-1]);
129 return coeff[0] + t*(coeff[1] + t*coeff[2] + t*t*coeff[3]);
130 }
131 }
132
138 Scalar evalDerivative(const Scalar x) const
139 {
140 if (x <= x_[0])
141 return coeff_[1]/(x_[1] - x_[0]);
142 else if (x > x_[numPoints_-1])
143 return coeff_[(numPoints_-1)*4 + 1]/(x_[numPoints_-1] - x_[numPoints_-2]);
144 else
145 {
146 const auto lookUpIndex = std::distance(x_.begin(), std::lower_bound(x_.begin(), x_.end(), x));
147 assert(lookUpIndex != 0);
148
149 // get coefficients
150 const auto* coeff = coeff_.data() + (lookUpIndex-1)*4;
151
152 // interpolate parametrization parameter t in [0,1]
153 const auto t = (x - x_[lookUpIndex-1])/(x_[lookUpIndex] - x_[lookUpIndex-1]);
154 const auto dtdx = 1.0/(x_[lookUpIndex] - x_[lookUpIndex-1]);
155 return dtdx*(coeff[1] + t*(2.0*coeff[2] + t*3.0*coeff[3]));
156 }
157 }
158
159private:
160 std::vector<Scalar> x_;
161 std::size_t numPoints_;
162 std::vector<Scalar> coeff_;
163};
164
165} // end namespace Dumux
166
167#endif
A simple implementation of a natural cubic spline.
Definition: cubicspline.hh:33
CubicSpline(const std::vector< Scalar > &x, const std::vector< Scalar > y)
Construct a natural cubic spline from the control points (x[i], y[i])
Definition: cubicspline.hh:45
CubicSpline()=default
Default constructor.
Scalar eval(const Scalar x) const
Evaluate the y value at a given x value.
Definition: cubicspline.hh:113
Scalar evalDerivative(const Scalar x) const
Evaluate the first derivative dy/dx at a given x value.
Definition: cubicspline.hh:138
void updatePoints(const std::vector< Scalar > &x, const std::vector< Scalar > &y)
Create a natural cubic spline from the control points (x[i], y[i])
Definition: cubicspline.hh:61
static ctype distance(const Dune::FieldVector< ctype, dimWorld > &a, const Dune::FieldVector< ctype, dimWorld > &b)
Compute the shortest distance between two points.
Definition: distance.hh:282
Definition: adapt.hh:17