version 3.10-dev
monotonecubicspline.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_MONOTONE_CUBIC_SPLINE_HH
13#define DUMUX_COMMON_MONOTONE_CUBIC_SPLINE_HH
14
15#include <cmath>
16#include <cassert>
17#include <iterator>
18#include <vector>
19#include <algorithm>
20
21#include <dune/common/float_cmp.hh>
22
23// Hermite basis functions
25
26// for inversion
28
29namespace Dumux {
30
37template<class Scalar = double>
39{
41public:
46
53 MonotoneCubicSpline(const std::vector<Scalar>& x, const std::vector<Scalar>& y)
54 {
55 updatePoints(x, y);
56 }
57
63 void updatePoints(const std::vector<Scalar>& x, const std::vector<Scalar>& y)
64 {
65 // check some requirements
66 assert (x.size() == y.size());
67 assert (x.size() >=2);
68 assert (std::is_sorted(x.begin(), x.end()) || std::is_sorted(x.rbegin(), x.rend()));
69
70 // save a copy of the control points
71 x_ = x;
72 y_ = y;
73
74 // the number of control points
75 numPoints_ = x.size();
76
77 // whether we are increasing
78 increasingX_ = x_.back() > x_.front();
79 increasingY_ = y_.back() > y_.front();
80
81 // the slope at every control point
82 m_.resize(numPoints_);
83
84 // compute slopes (see Fritsch & Butland (1984), Eq. (5))
85 Scalar deltaX = (x[1]-x[0]);
86 Scalar secant = m_.front() = (y[1]-y[0])/deltaX;
87 Scalar prevDeltaX = deltaX;
88 Scalar prevSecant = secant;
89 for (int i = 1; i < numPoints_-1; ++i, prevSecant = secant, prevDeltaX = deltaX)
90 {
91 deltaX = (x[i+1]-x[i]);
92 secant = (y[i+1]-y[i])/deltaX;
93 const auto alpha = (prevDeltaX + 2*deltaX)/(3*(prevDeltaX + deltaX));
94 m_[i] = prevSecant*secant > 0.0 ? prevSecant*secant/(alpha*secant + (1.0-alpha)*prevSecant) : 0.0;
95 }
96 m_.back() = secant;
97 }
98
104 Scalar eval(const Scalar x) const
105 {
106 if ((x <= x_.front() && increasingX_) || (x >= x_.front() && !increasingX_))
107 return y_.front() + m_.front()*(x - x_.front());
108 else if ((x > x_.back() && increasingX_) || (x < x_.back() && !increasingX_))
109 return y_.back() + m_.back()*(x - x_.back());
110
111 return eval_(x);
112 }
113
119 Scalar evalDerivative(const Scalar x) const
120 {
121 if ((x <= x_.front() && increasingX_) || (x >= x_.front() && !increasingX_))
122 return m_.front();
123 else if ((x > x_.back() && increasingX_) || (x < x_.back() && !increasingX_))
124 return m_.back();
125
126 return evalDerivative_(x);
127 }
128
135 Scalar evalInverse(const Scalar y) const
136 {
137 if ((y <= y_.front() && increasingY_) || (y >= y_.front() && !increasingY_))
138 return x_.front() + (y - y_.front())/m_.front();
139 else if ((y > y_.back() && increasingY_) || (y < y_.back() && !increasingY_))
140 return x_.back() + (y - y_.back())/m_.back();
141
142 return evalInverse_(y);
143 }
144
145private:
146 Scalar eval_(const Scalar x) const
147 {
148 // interpolate parametrization parameter t in [0,1]
149 const auto lookUpIndex = lookUpIndex_(x_, x, increasingX_);
150 const auto h = (x_[lookUpIndex] - x_[lookUpIndex-1]);
151 const auto t = (x - x_[lookUpIndex-1])/h;
152 return y_[lookUpIndex-1]*Basis::h00(t) + h*m_[lookUpIndex-1]*Basis::h10(t)
153 + y_[lookUpIndex]*Basis::h01(t) + h*m_[lookUpIndex]*Basis::h11(t);
154 }
155
156 Scalar evalDerivative_(const Scalar x) const
157 {
158 // interpolate parametrization parameter t in [0,1]
159 const auto lookUpIndex = lookUpIndex_(x_, x, increasingX_);
160 const auto h = (x_[lookUpIndex] - x_[lookUpIndex-1]);
161 const auto t = (x - x_[lookUpIndex-1])/h;
162 const auto dtdx = 1.0/h;
163 return y_[lookUpIndex-1]*Basis::dh00(t)*dtdx + m_[lookUpIndex-1]*Basis::dh10(t)
164 + y_[lookUpIndex]*Basis::dh01(t)*dtdx + m_[lookUpIndex]*Basis::dh11(t);
165 }
166
167 Scalar evalInverse_(const Scalar y) const
168 {
169 const auto lookUpIndex = lookUpIndex_(y_, y, increasingY_);
170 auto localPolynomial = [&](const auto x) {
171 // interpolate parametrization parameter t in [0,1]
172 const auto h = (x_[lookUpIndex] - x_[lookUpIndex-1]);
173 const auto t = (x - x_[lookUpIndex-1])/h;
174 return y - (y_[lookUpIndex-1]*Basis::h00(t) + h*m_[lookUpIndex-1]*Basis::h10(t)
175 + y_[lookUpIndex]*Basis::h01(t) + h*m_[lookUpIndex]*Basis::h11(t));
176 };
177
178 // use an epsilon for the bracket
179 const auto eps = (x_[lookUpIndex]-x_[lookUpIndex-1])*1e-5;
180 return findScalarRootBrent(x_[lookUpIndex-1]-eps, x_[lookUpIndex]+eps, localPolynomial);
181 }
182
183 auto lookUpIndex_(const std::vector<Scalar>& vec, const Scalar v, bool increasing) const
184 {
185 return increasing ? lookUpIndexIncreasing_(vec, v) : lookUpIndexDecreasing_(vec, v);
186 }
187
188 auto lookUpIndexIncreasing_(const std::vector<Scalar>& vec, const Scalar v) const
189 {
190 const auto lookUpIndex = std::distance(vec.begin(), std::lower_bound(vec.begin(), vec.end(), v));
191 assert(lookUpIndex != 0 && lookUpIndex < vec.size());
192 return lookUpIndex;
193 }
194
195 auto lookUpIndexDecreasing_(const std::vector<Scalar>& vec, const Scalar v) const
196 {
197 const auto lookUpIndex = vec.size() - std::distance(vec.rbegin(), std::upper_bound(vec.rbegin(), vec.rend(), v));
198 assert(lookUpIndex != 0 && lookUpIndex < vec.size());
199 return lookUpIndex;
200 }
201
202 std::vector<Scalar> x_;
203 std::vector<Scalar> y_;
204 std::vector<Scalar> m_;
205 std::size_t numPoints_;
206 bool increasingX_;
207 bool increasingY_;
208};
209
210} // end namespace Dumux
211
212#endif
A monotone cubic spline.
Definition: monotonecubicspline.hh:39
MonotoneCubicSpline()=default
Default constructor.
void updatePoints(const std::vector< Scalar > &x, const std::vector< Scalar > &y)
Create a monotone cubic spline from the control points (x[i], y[i])
Definition: monotonecubicspline.hh:63
Scalar evalInverse(const Scalar y) const
Evaluate the inverse function.
Definition: monotonecubicspline.hh:135
Scalar eval(const Scalar x) const
Evaluate the y value at a given x value.
Definition: monotonecubicspline.hh:104
Scalar evalDerivative(const Scalar x) const
Evaluate the first derivative dy/dx at a given x value.
Definition: monotonecubicspline.hh:119
MonotoneCubicSpline(const std::vector< Scalar > &x, const std::vector< Scalar > &y)
Construct a monotone cubic spline from the control points (x[i], y[i])
Definition: monotonecubicspline.hh:53
The cubic hermite spline basis.
Root finding algorithms for scalar functions.
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
Scalar findScalarRootBrent(Scalar a, Scalar b, const ResFunc &residual, const Scalar tol=1e-13, const int maxIter=200)
Brent's root finding algorithm for scalar functions.
Definition: findscalarroot.hh:99
Definition: adapt.hh:17
The cubic spline hermite basis.
Definition: cubicsplinehermitebasis.hh:24
static constexpr Scalar h01(const Scalar t)
Definition: cubicsplinehermitebasis.hh:31
static constexpr Scalar dh01(const Scalar t)
Definition: cubicsplinehermitebasis.hh:43
static constexpr Scalar dh11(const Scalar t)
Definition: cubicsplinehermitebasis.hh:46
static constexpr Scalar h11(const Scalar t)
Definition: cubicsplinehermitebasis.hh:34
static constexpr Scalar h00(const Scalar t)
Definition: cubicsplinehermitebasis.hh:25
static constexpr Scalar dh10(const Scalar t)
Definition: cubicsplinehermitebasis.hh:40
static constexpr Scalar h10(const Scalar t)
Definition: cubicsplinehermitebasis.hh:28
static constexpr Scalar dh00(const Scalar t)
Definition: cubicsplinehermitebasis.hh:37