Line | Branch | Exec | Source |
---|---|---|---|
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-FileCopyrightText: Copyright © DuMux Project contributors, see AUTHORS.md in root folder | ||
5 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
6 | // | ||
7 | /*! | ||
8 | * \file | ||
9 | * \ingroup Core | ||
10 | * \brief The common code for all 3rd order polynomial splines. | ||
11 | */ | ||
12 | #ifndef DUMUX_SPLINE_COMMON__HH | ||
13 | #define DUMUX_SPLINE_COMMON__HH | ||
14 | |||
15 | #include <algorithm> | ||
16 | #include <iostream> | ||
17 | #include <cassert> | ||
18 | |||
19 | #include <dune/common/exceptions.hh> | ||
20 | #include <dune/common/float_cmp.hh> | ||
21 | |||
22 | #include "math.hh" | ||
23 | |||
24 | namespace Dumux { | ||
25 | |||
26 | /*! | ||
27 | * \ingroup Core | ||
28 | * \brief The common code for all 3rd order polynomial splines. | ||
29 | */ | ||
30 | template<class ScalarT, class ImplementationT> | ||
31 | class SplineCommon_ | ||
32 | { | ||
33 | using Scalar = ScalarT; | ||
34 | using Implementation = ImplementationT; | ||
35 | |||
36 | Implementation &asImp_() | ||
37 | { return *static_cast<Implementation*>(this); } | ||
38 | const Implementation &asImp_() const | ||
39 | { return *static_cast<const Implementation*>(this); } | ||
40 | |||
41 | public: | ||
42 | /*! | ||
43 | * \brief Return true if the given x is in range [x1, xn]. | ||
44 | */ | ||
45 | 24497081 | bool applies(Scalar x) const | |
46 | { | ||
47 |
10/18✓ Branch 0 taken 20934053 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 222398 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2185921 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1148632 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 18 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1963 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 1963 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 1982 times.
✓ Branch 15 taken 20 times.
✓ Branch 16 taken 131 times.
✗ Branch 17 not taken.
|
24497081 | return x_(0) <= x && x <= x_(numSamples_() - 1); |
48 | } | ||
49 | |||
50 | /*! | ||
51 | * \brief Return the x value of the leftmost sampling point. | ||
52 | */ | ||
53 | 2 | Scalar xMin() const | |
54 | ✗ | { return x_(0); } | |
55 | |||
56 | /*! | ||
57 | * \brief Return the x value of the rightmost sampling point. | ||
58 | */ | ||
59 | 1 | Scalar xMax() const | |
60 | 3 | { return x_(numSamples_() - 1); } | |
61 | |||
62 | /*! | ||
63 | * \brief Prints k tuples of the format (x, y, dx/dy, isMonotonic) | ||
64 | * to stdout. | ||
65 | * | ||
66 | * If the spline does not apply for parts of [x0, x1] it is | ||
67 | * extrapolated using a straight line. The result can be inspected | ||
68 | * using the following commands: | ||
69 | * | ||
70 | ----------- snip ----------- | ||
71 | ./yourProgramm > spline.csv | ||
72 | gnuplot | ||
73 | |||
74 | gnuplot> plot "spline.csv" using 1:2 w l ti "Curve", \ | ||
75 | "spline.csv" using 1:3 w l ti "Derivative", \ | ||
76 | "spline.csv" using 1:4 w p ti "Monotonic" | ||
77 | ----------- snap ----------- | ||
78 | */ | ||
79 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | void printCSV(Scalar xi0, Scalar xi1, int k) const |
80 | { | ||
81 | using std::max; | ||
82 | using std::min; | ||
83 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | Scalar x0 = min(xi0, xi1); |
84 | 2 | Scalar x1 = max(xi0, xi1); | |
85 | 2 | const int n = numSamples_() - 1; | |
86 |
2/2✓ Branch 0 taken 2002 times.
✓ Branch 1 taken 2 times.
|
2004 | for (int i = 0; i <= k; ++i) { |
87 | 2002 | double x = i*(x1 - x0)/k + x0; | |
88 | double y; | ||
89 | double dy_dx; | ||
90 | 2002 | double mono = 1; | |
91 | 4004 | if (!applies(x)) { | |
92 |
2/2✓ Branch 0 taken 20 times.
✓ Branch 1 taken 20 times.
|
40 | if (x < 0) { |
93 | 20 | dy_dx = evalDerivative(x_(0)); | |
94 | 20 | y = (x - x_(0))*dy_dx + y_(0); | |
95 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 10 times.
|
20 | mono = (dy_dx>0)?1:-1; |
96 | } | ||
97 |
1/2✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
|
20 | else if (x > x_(n)) { |
98 | 20 | dy_dx = evalDerivative(x_(n)); | |
99 | 20 | y = (x - x_(n))*dy_dx + y_(n); | |
100 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 10 times.
|
20 | mono = (dy_dx>0)?1:-1; |
101 | } | ||
102 | else { | ||
103 | ✗ | std::cerr << "ooops: " << x << "\n"; | |
104 | ✗ | exit(1); | |
105 | } | ||
106 | } | ||
107 | else { | ||
108 | 1962 | y = eval(x); | |
109 | 1962 | dy_dx = evalDerivative(x); | |
110 | 1962 | double x_p1 = x + (x1 - x0)/k; | |
111 |
2/2✓ Branch 0 taken 1960 times.
✓ Branch 1 taken 2 times.
|
3924 | mono = monotonic(max<Scalar>(x_(0), x), min<Scalar>(x_(n), x_p1)); |
112 | } | ||
113 | |||
114 | 2002 | std::cout << x << " " << y << " " << dy_dx << " " << mono << "\n"; | |
115 | } | ||
116 | 2 | } | |
117 | |||
118 | /*! | ||
119 | * \brief Evaluate the spline at a given position. | ||
120 | * | ||
121 | * \param x The value on the abscissa where the spline ought to be | ||
122 | * evaluated | ||
123 | * \param extrapolate If this parameter is set to true, the spline | ||
124 | * will be extended beyond its range by | ||
125 | * straight lines, if false calling extrapolate | ||
126 | * for \f$ x \not [x_{min}, x_{max}]\f$ will | ||
127 | * cause a failed assertion. | ||
128 | */ | ||
129 | 23060042 | Scalar eval(Scalar x, bool extrapolate=false) const | |
130 | { | ||
131 |
2/2✓ Branch 0 taken 23057842 times.
✓ Branch 1 taken 2 times.
|
23060042 | assert(extrapolate || applies(x)); |
132 | |||
133 | // handle extrapolation | ||
134 | if (extrapolate) { | ||
135 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
4 | if (x < xMin()) { |
136 | 2 | Scalar m = evalDerivative_(xMin(), /*segmentIdx=*/0); | |
137 | 2 | Scalar y0 = y_(0); | |
138 | 2 | return y0 + m*(x - xMin()); | |
139 | } | ||
140 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
2 | else if (x > xMax()) { |
141 | 4 | Scalar m = evalDerivative_(xMax(), /*segmentIdx=*/numSamples_()-2); | |
142 | 2 | Scalar y0 = y_(numSamples_()-1); | |
143 | 2 | return y0 + m*(x - xMax()); | |
144 | } | ||
145 | } | ||
146 | |||
147 | 23060038 | return eval_(x, segmentIdx_(x)); | |
148 | } | ||
149 | |||
150 | /*! | ||
151 | * \brief Evaluate the spline's derivative at a given position. | ||
152 | * | ||
153 | * \param x The value on the abscissa where the spline's | ||
154 | * derivative ought to be evaluated | ||
155 | * | ||
156 | * \param extrapolate If this parameter is set to true, the spline | ||
157 | * will be extended beyond its range by | ||
158 | * straight lines, if false calling extrapolate | ||
159 | * for \f$ x \not [x_{min}, x_{max}]\f$ will | ||
160 | * cause a failed assertion. | ||
161 | |||
162 | */ | ||
163 | 1397488 | Scalar evalDerivative(Scalar x, bool extrapolate=false) const | |
164 | { | ||
165 |
1/2✓ Branch 0 taken 1395197 times.
✗ Branch 1 not taken.
|
1397488 | assert(extrapolate || applies(x)); |
166 | if (extrapolate) { | ||
167 | ✗ | if (Dune::FloatCmp::le(x, xMin())) | |
168 | ✗ | return evalDerivative_(xMin(), 0); | |
169 | 1397488 | else if (Dune::FloatCmp::ge(x, xMax())) | |
170 | ✗ | return evalDerivative_(xMax(), numSamples_() - 2); | |
171 | } | ||
172 | |||
173 | 1397488 | return evalDerivative_(x, segmentIdx_(x)); | |
174 | } | ||
175 | |||
176 | /*! | ||
177 | * \brief Find the intersections of the spline with a cubic | ||
178 | * polynomial in the whole interval, throws | ||
179 | * Dune::MathError exception if there is more or less than | ||
180 | * one solution. | ||
181 | */ | ||
182 | Scalar intersect(Scalar a, Scalar b, Scalar c, Scalar d) const | ||
183 | { | ||
184 | return intersectInterval(xMin(), xMax(), a, b, c, d); | ||
185 | } | ||
186 | |||
187 | /*! | ||
188 | * \brief Find the intersections of the spline with a cubic | ||
189 | * polynomial in a sub-interval of the spline, throws | ||
190 | * Dune::MathError exception if there is more or less than | ||
191 | * one solution. | ||
192 | */ | ||
193 | 19057 | Scalar intersectInterval(Scalar x0, Scalar x1, | |
194 | Scalar a, Scalar b, Scalar c, Scalar d) const | ||
195 | { | ||
196 | ✗ | assert(applies(x0) && applies(x1)); | |
197 | |||
198 | Scalar tmpSol[3]; | ||
199 | int nSol = 0; | ||
200 | int iFirst = segmentIdx_(x0); | ||
201 | int iLast = segmentIdx_(x1); | ||
202 |
2/2✓ Branch 0 taken 19057 times.
✓ Branch 1 taken 19057 times.
|
38114 | for (int i = iFirst; i <= iLast; ++i) |
203 | { | ||
204 | 19057 | nSol += intersectSegment_(tmpSol, i, a, b, c, d, x0, x1); | |
205 | |||
206 |
1/2✓ Branch 0 taken 19057 times.
✗ Branch 1 not taken.
|
19057 | if (nSol > 1) { |
207 | ✗ | DUNE_THROW(Dune::MathError, | |
208 | "Spline has more than one intersection"); //<<a<<"x^3 + "<<b<"x^2 + "<<c<"x + "<<d); | ||
209 | } | ||
210 | } | ||
211 | |||
212 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 19057 times.
|
19057 | if (nSol != 1) |
213 | ✗ | DUNE_THROW(Dune::MathError, | |
214 | "Spline has no intersection"); //<<a<"x^3 + " <<b<"x^2 + "<<c<"x + "<<d<<"!"); | ||
215 | |||
216 | 19057 | return tmpSol[0]; | |
217 | } | ||
218 | |||
219 | /*! | ||
220 | * \brief Returns 1 if the spline is monotonically increasing, -1 | ||
221 | * if the spline is monotonously decreasing and 0 if the | ||
222 | * spline is not monotonous in the interval (x0, x1). | ||
223 | * | ||
224 | * In the corner case where the whole spline is flat, it returns | ||
225 | * 2. | ||
226 | */ | ||
227 | 1963 | int monotonic(Scalar x0, Scalar x1) const | |
228 | { | ||
229 | ✗ | assert(applies(x0)); | |
230 | ✗ | assert(applies(x1)); | |
231 |
2/4✓ Branch 0 taken 1963 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1963 times.
|
3926 | assert(Dune::FloatCmp::ne<Scalar>(x0, x1)); |
232 | |||
233 | // make sure that x0 is smaller than x1 | ||
234 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1963 times.
|
1963 | if (x0 > x1) |
235 | { | ||
236 | using std::swap; | ||
237 | ✗ | swap(x0, x1); | |
238 | } | ||
239 | |||
240 | // corner case where the whole spline is a constant | ||
241 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 981 times.
|
981 | if (Dune::FloatCmp::eq<Scalar>(moment_(0), 0) && |
242 |
2/2✓ Branch 0 taken 981 times.
✓ Branch 1 taken 982 times.
|
1963 | Dune::FloatCmp::eq<Scalar>(moment_(1), 0) && |
243 | ✗ | Dune::FloatCmp::eq<Scalar>(y_(0), y_(1))) | |
244 | { | ||
245 | // actually the is monotonically increasing as well as | ||
246 | // monotonously decreasing | ||
247 | return 2; | ||
248 | } | ||
249 | |||
250 | |||
251 | 1963 | int i = segmentIdx_(x0); | |
252 |
1/2✓ Branch 0 taken 1963 times.
✗ Branch 1 not taken.
|
1963 | if (x_(i) <= x0 && x1 <= x_(i+1)) { |
253 | // the interval to check is completely included in a | ||
254 | // single segment | ||
255 | 1956 | return monotonic_(i, x0, x1); | |
256 | } | ||
257 | |||
258 | // make sure that the segments which are completely in the | ||
259 | // interval [x0, x1] all exhibit the same monotonicity. | ||
260 | 7 | int iEnd = segmentIdx_(x1); | |
261 | 7 | int r = monotonic_(i, x0, x_(1)); | |
262 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 7 times.
|
9 | for (; i < iEnd - 1; ++i) |
263 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (r != monotonic_(i, x_(i), x_(i + 1))) |
264 | return 0; | ||
265 | |||
266 | // check for the last segment | ||
267 |
3/4✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 5 times.
|
14 | if (x_(iEnd) < x1 && r != monotonic_(iEnd, x_(iEnd), x1)) |
268 | { return 0; } | ||
269 | |||
270 | return r; | ||
271 | } | ||
272 | |||
273 | /*! | ||
274 | * \brief Same as monotonic(x0, x1), but with the entire range of the | ||
275 | * spline as interval. | ||
276 | */ | ||
277 | int monotonic() const | ||
278 | { return monotonic(xMin(), xMax()); } | ||
279 | |||
280 | protected: | ||
281 | // this is an internal class, so everything is protected! | ||
282 | SplineCommon_() = default; | ||
283 | |||
284 | /*! | ||
285 | * \brief Set the sampling point vectors. | ||
286 | * | ||
287 | * This takes care that the order of the x-values is ascending, | ||
288 | * although the input must be ordered already! | ||
289 | */ | ||
290 | template <class DestVector, class SourceVector> | ||
291 | void assignSamplingPoints_(DestVector &destX, | ||
292 | DestVector &destY, | ||
293 | const SourceVector &srcX, | ||
294 | const SourceVector &srcY, | ||
295 | int numSamples) | ||
296 | { | ||
297 | assert(numSamples >= 2); | ||
298 | |||
299 | // copy sample points, make sure that the first x value is | ||
300 | // smaller than the last one | ||
301 | bool reverse = (srcX[0] > srcX[numSamples - 1]); | ||
302 | |||
303 | for (int i = 0; i < numSamples; ++i) { | ||
304 | int idx = i; | ||
305 | if (reverse) | ||
306 | idx = numSamples - i - 1; | ||
307 | destX[i] = srcX[idx]; | ||
308 | destY[i] = srcY[idx]; | ||
309 | } | ||
310 | } | ||
311 | |||
312 | /*! | ||
313 | * \brief Set the sampling point vectors. | ||
314 | * | ||
315 | * This takes care that the order of the x-values is ascending, | ||
316 | * although the input must be ordered already! | ||
317 | */ | ||
318 | template <class DestVector, class ListIterator> | ||
319 | void assignFromArrayList_(DestVector &destX, | ||
320 | DestVector &destY, | ||
321 | const ListIterator &srcBegin, | ||
322 | const ListIterator &srcEnd, | ||
323 | int numSamples) | ||
324 | { | ||
325 | assert(numSamples >= 2); | ||
326 | |||
327 | // find out whether the x values are in reverse order | ||
328 | ListIterator it = srcBegin; | ||
329 | ++it; | ||
330 | bool reverse = ((*srcBegin)[0] > (*it)[0]); | ||
331 | --it; | ||
332 | |||
333 | // loop over all sampling points | ||
334 | for (int i = 0; it != srcEnd; ++i, ++it) { | ||
335 | int idx = i; | ||
336 | if (reverse) | ||
337 | idx = numSamples - i - 1; | ||
338 | destX[idx] = (*it)[0]; | ||
339 | destY[idx] = (*it)[1]; | ||
340 | } | ||
341 | } | ||
342 | |||
343 | /*! | ||
344 | * \brief Set the sampling points. | ||
345 | * | ||
346 | * Here we assume that the elements of the source vector have an | ||
347 | * [] operator where v[0] is the x value and v[1] is the y value | ||
348 | * if the sampling point. | ||
349 | */ | ||
350 | template <class DestVector, class ListIterator> | ||
351 | void assignFromTupleList_(DestVector &destX, | ||
352 | DestVector &destY, | ||
353 | ListIterator srcBegin, | ||
354 | ListIterator srcEnd, | ||
355 | int numSamples) | ||
356 | { | ||
357 | assert(numSamples >= 2); | ||
358 | |||
359 | // copy sample points, make sure that the first x value is | ||
360 | // smaller than the last one | ||
361 | |||
362 | // find out whether the x values are in reverse order | ||
363 | ListIterator it = srcBegin; | ||
364 | ++it; | ||
365 | bool reverse = (std::get<0>(*srcBegin) > std::get<0>(*it)); | ||
366 | --it; | ||
367 | |||
368 | // loop over all sampling points | ||
369 | for (int i = 0; it != srcEnd; ++i, ++it) { | ||
370 | int idx = i; | ||
371 | if (reverse) | ||
372 | idx = numSamples - i - 1; | ||
373 | destX[idx] = std::get<0>(*it); | ||
374 | destY[idx] = std::get<1>(*it); | ||
375 | } | ||
376 | } | ||
377 | |||
378 | |||
379 | /*! | ||
380 | * \brief Make the linear system of equations Mx = d which results | ||
381 | * in the moments of the periodic spline. | ||
382 | * | ||
383 | * When solving Mx = d, it should be noted that x[0] is trash and | ||
384 | * needs to be set to x[n-1] | ||
385 | */ | ||
386 | template <class Vector, class Matrix> | ||
387 | void makePeriodicSystem_(Matrix &M, Vector &d) | ||
388 | { | ||
389 | // a periodic spline only makes sense if the first and the | ||
390 | // last sampling point have the same y value | ||
391 | assert(y_(0) == y_(numSamples_() - 1)); | ||
392 | |||
393 | makeNaturalSystem(M, d); | ||
394 | |||
395 | int n = numSamples_() - 1; | ||
396 | |||
397 | Scalar lambda_n = h_(1)/(h_(n) + h_(1)); | ||
398 | Scalar lambda_1 = M[1][2]; | ||
399 | Scalar mu_1 = 1 - lambda_1; | ||
400 | Scalar mu_n = 1 - lambda_n; | ||
401 | |||
402 | Scalar d_n = | ||
403 | 6 / (h_(n) + h_(1)) | ||
404 | * | ||
405 | ( (y_(1) - y_(n))/h_(1) - (y_(n) - y_(n-1))/h_(n)); | ||
406 | |||
407 | // insert lambda_n and mu_1 | ||
408 | M[1][n] = mu_1; | ||
409 | M[n][1] = lambda_n; | ||
410 | M[n][n-1] = mu_n; | ||
411 | d[n] = d_n; | ||
412 | } | ||
413 | |||
414 | /*! | ||
415 | * \brief Make the linear system of equations Mx = d which results | ||
416 | * in the moments of the full spline. | ||
417 | */ | ||
418 | template <class Vector, class Matrix> | ||
419 | 90666 | void makeFullSystem_(Matrix &M, Vector &d, Scalar m0, Scalar m1) | |
420 | { | ||
421 | 90666 | makeNaturalSystem_(M, d); | |
422 | |||
423 | 90666 | int n = numSamples_() - 1; | |
424 | // first row | ||
425 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
|
90666 | M[0][1] = 1; |
426 | 90666 | d[0] = 6/h_(1) * ( (y_(1) - y_(0))/h_(1) - m0); | |
427 | |||
428 | // last row | ||
429 | 90666 | M[n][n - 1] = 1; | |
430 | |||
431 | // right hand side | ||
432 | 90666 | d[n] = | |
433 | 90666 | 6/h_(n) | |
434 | 90666 | * | |
435 | 90666 | (m1 - (y_(n) - y_(n - 1))/h_(n)); | |
436 | 90666 | } | |
437 | |||
438 | /*! | ||
439 | * \brief Make the linear system of equations Mx = d which results | ||
440 | * in the moments of the natural spline. | ||
441 | * Stoer2005: Numerische Mathematik 1, p. 111 \cite stoer2005 | ||
442 | */ | ||
443 | template <class Vector, class Matrix> | ||
444 | 90686 | void makeNaturalSystem_(Matrix &M, Vector &d) | |
445 | { | ||
446 |
2/2✓ Branch 0 taken 181268 times.
✓ Branch 1 taken 90634 times.
|
362588 | M = 0; |
447 | 90686 | d = 0; | |
448 | |||
449 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 90634 times.
|
90664 | const int n = numSamples_() - 1; |
450 | |||
451 | // second to next to last rows | ||
452 |
2/2✓ Branch 0 taken 78 times.
✓ Branch 1 taken 26 times.
|
208 | for (int i = 1; i < n; ++i) { |
453 | 156 | const Scalar lambda_i = h_(i + 1) / (h_(i) + h_(i+1)); | |
454 | 156 | const Scalar mu_i = 1 - lambda_i; | |
455 | 156 | const Scalar d_i = | |
456 | 156 | 6 / (h_(i) + h_(i+1)) | |
457 | 156 | * | |
458 | 156 | ( (y_(i+1) - y_(i))/h_(i+1) - (y_(i) - y_(i-1))/h_(i)); | |
459 | |||
460 | 156 | M[i][i-1] = mu_i; | |
461 | 156 | M[i][i] = 2; | |
462 | 156 | M[i][i + 1] = lambda_i; | |
463 | 156 | d[i] = d_i; | |
464 | } | ||
465 | |||
466 | // first row | ||
467 | 90686 | M[0][0] = 2; | |
468 | |||
469 | // last row | ||
470 | 90686 | M[n][n] = 2; | |
471 | // right hand side | ||
472 | 90686 | d[0] = 0.0; | |
473 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 90634 times.
|
90686 | d[n] = 0.0; |
474 | 52 | } | |
475 | |||
476 | // evaluate the spline at a given the position and given the | ||
477 | // segment index | ||
478 | 23060038 | Scalar eval_(Scalar x, int i) const | |
479 | { | ||
480 | // See: Stoer2005 "Numerische Mathematik 1", 9th edition, | ||
481 | // Springer, 2005, p. 109 | ||
482 | 23060038 | Scalar h_i1 = h_(i + 1); | |
483 | 23060038 | Scalar x_i = x - x_(i); | |
484 | 23060038 | Scalar x_i1 = x_(i+1) - x; | |
485 | |||
486 | 23060038 | Scalar A_i = | |
487 | 23060038 | (y_(i+1) - y_(i))/h_i1 | |
488 | - | ||
489 | 23060038 | h_i1/6*(moment_(i+1) - moment_(i)); | |
490 | 23060038 | Scalar B_i = y_(i) - moment_(i)* (h_i1*h_i1) / 6; | |
491 | |||
492 | return | ||
493 | 23060038 | moment_(i)* x_i1*x_i1*x_i1 / (6 * h_i1) | |
494 | 23060038 | + | |
495 | 23060038 | moment_(i + 1)* x_i*x_i*x_i / (6 * h_i1) | |
496 | 23060038 | + | |
497 | 23060038 | A_i*x_i | |
498 | + | ||
499 | 23060038 | B_i; | |
500 | } | ||
501 | |||
502 | // evaluate the derivative of a spline given the actual position | ||
503 | // and the segment index | ||
504 | 1397492 | Scalar evalDerivative_(Scalar x, int i) const | |
505 | { | ||
506 | // See: Stoer2005 "Numerische Mathematik 1", 9th edition, | ||
507 | // Springer, 2005, p. 109 | ||
508 | 1397492 | Scalar h_i1 = h_(i + 1); | |
509 | 1397492 | Scalar x_i = x - x_(i); | |
510 | 1397492 | Scalar x_i1 = x_(i+1) - x; | |
511 | |||
512 | 1397492 | Scalar A_i = | |
513 | 1397492 | (y_(i+1) - y_(i))/h_i1 | |
514 | - | ||
515 | 1397492 | h_i1/6*(moment_(i+1) - moment_(i)); | |
516 | |||
517 | return | ||
518 | 1397492 | -moment_(i) * x_i1*x_i1 / (2 * h_i1) | |
519 | 1397492 | + | |
520 | 1397492 | moment_(i + 1) * x_i*x_i / (2 * h_i1) | |
521 | + | ||
522 | 1397492 | A_i; | |
523 | } | ||
524 | |||
525 | |||
526 | // returns the monotonicality of an interval of a spline segment | ||
527 | // | ||
528 | // The return value have the following meaning: | ||
529 | // | ||
530 | // 1: spline is monotonously increasing in the specified interval | ||
531 | // 0: spline is not monotonic in the specified interval | ||
532 | // -1: spline is monotonously decreasing in the specified interval | ||
533 | 1972 | int monotonic_(int i, Scalar x0, Scalar x1) const | |
534 | { | ||
535 | // shift the interval so that it is consistent with the | ||
536 | // definitions by Stoer2005 | ||
537 | 1972 | x0 = x0 - x_(i); | |
538 | 1972 | x1 = x1 - x_(i); | |
539 | |||
540 | 1972 | Scalar a = a_(i); | |
541 | 1972 | Scalar b = b_(i); | |
542 | 3944 | Scalar c = c_(i); | |
543 | |||
544 | 1972 | Scalar disc = 4*b*b - 12*a*c; | |
545 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1972 times.
|
1972 | if (disc < 0) { |
546 | // discriminant is smaller than 0, i.e. the segment does | ||
547 | // not exhibit any extrema. | ||
548 | ✗ | return (x0*(x0*3*a + 2*b) + c > 0) ? 1 : -1; | |
549 | } | ||
550 | using std::sqrt; | ||
551 | 1972 | disc = sqrt(disc); | |
552 | 1972 | Scalar xE1 = (-2*b + disc)/(6*a); | |
553 | 1972 | Scalar xE2 = (-2*b - disc)/(6*a); | |
554 | |||
555 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1972 times.
|
1972 | if (Dune::FloatCmp::eq<Scalar>(disc, 0)) { |
556 | // saddle point -> no extrema | ||
557 | ✗ | if (Dune::FloatCmp::eq<Scalar>(xE1, x0)) | |
558 | // make sure that we're not picking the saddle point | ||
559 | // to determine whether we're monotonically increasing | ||
560 | // or decreasing | ||
561 | ✗ | x0 = x1; | |
562 | ✗ | return sign(x0*(x0*3*a + 2*b) + c); | |
563 | } | ||
564 |
6/6✓ Branch 0 taken 1042 times.
✓ Branch 1 taken 930 times.
✓ Branch 2 taken 1036 times.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 631 times.
✓ Branch 5 taken 1335 times.
|
1972 | if ((x0 < xE1 && xE1 < x1) || |
565 |
2/2✓ Branch 0 taken 625 times.
✓ Branch 1 taken 6 times.
|
631 | (x0 < xE2 && xE2 < x1)) |
566 | { | ||
567 | // there is an extremum in the range (x0, x1) | ||
568 | return 0; | ||
569 | } | ||
570 | // no extremum in range (x0, x1) | ||
571 | 1960 | x0 = (x0 + x1)/2; // pick point in the middle of the interval | |
572 | // to avoid extrema on the boundaries | ||
573 | 1960 | return sign(x0*(x0*3*a + 2*b) + c); | |
574 | } | ||
575 | |||
576 | /*! | ||
577 | * \brief Find all the intersections of a segment of the spline | ||
578 | * with a cubic polynomial within a specified interval. | ||
579 | */ | ||
580 | 19057 | int intersectSegment_(Scalar *sol, | |
581 | int segIdx, | ||
582 | Scalar a, Scalar b, Scalar c, Scalar d, | ||
583 | Scalar x0 = -1e100, Scalar x1 = 1e100) const | ||
584 | { | ||
585 | 76228 | int n = invertCubicPolynomial(sol, | |
586 | 19057 | a_(segIdx) - a, | |
587 | 19057 | b_(segIdx) - b, | |
588 | 19057 | c_(segIdx) - c, | |
589 | 19057 | d_(segIdx) - d); | |
590 | using std::max; | ||
591 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 19057 times.
|
19057 | x0 = max(x_(segIdx), x0); |
592 | 38114 | x1 = max(x_(segIdx+1), x1); | |
593 | |||
594 | // filter the intersections outside of the specified interval | ||
595 | 19057 | int k = 0; | |
596 |
2/2✓ Branch 0 taken 38135 times.
✓ Branch 1 taken 19057 times.
|
57192 | for (int j = 0; j < n; ++j) { |
597 | 38135 | sol[j] += x_(segIdx); // add the offset of the interval. For details see Stoer2005 | |
598 |
3/4✓ Branch 0 taken 19057 times.
✓ Branch 1 taken 19078 times.
✓ Branch 2 taken 19057 times.
✗ Branch 3 not taken.
|
38135 | if (x0 <= sol[j] && sol[j] <= x1) { |
599 | 19057 | sol[k] = sol[j]; | |
600 | 19057 | ++k; | |
601 | } | ||
602 | } | ||
603 | 19057 | return k; | |
604 | } | ||
605 | |||
606 | // find the segment index for a given x coordinate | ||
607 | 2404 | int segmentIdx_(Scalar x) const | |
608 | { | ||
609 | // bisection | ||
610 | 5603 | int iLow = 0; | |
611 | 8631 | int iHigh = numSamples_() - 1; | |
612 | |||
613 |
12/12✓ Branch 0 taken 4106 times.
✓ Branch 1 taken 2053 times.
✓ Branch 2 taken 320 times.
✓ Branch 3 taken 160 times.
✓ Branch 4 taken 4226 times.
✓ Branch 5 taken 2113 times.
✓ Branch 6 taken 3926 times.
✓ Branch 7 taken 1963 times.
✓ Branch 8 taken 14 times.
✓ Branch 9 taken 7 times.
✓ Branch 10 taken 262 times.
✓ Branch 11 taken 131 times.
|
21685 | while (iLow + 1 < iHigh) { |
614 | 12854 | int i = (iLow + iHigh) / 2; | |
615 |
12/12✓ Branch 0 taken 1146 times.
✓ Branch 1 taken 2960 times.
✓ Branch 2 taken 170 times.
✓ Branch 3 taken 150 times.
✓ Branch 4 taken 1206 times.
✓ Branch 5 taken 3020 times.
✓ Branch 6 taken 1048 times.
✓ Branch 7 taken 2878 times.
✓ Branch 8 taken 10 times.
✓ Branch 9 taken 4 times.
✓ Branch 10 taken 141 times.
✓ Branch 11 taken 121 times.
|
12854 | if (x_(i) > x) |
616 | iHigh = i; | ||
617 | else | ||
618 | 10148 | iLow = i; | |
619 | } | ||
620 | return iLow; | ||
621 | } | ||
622 | |||
623 | /*! | ||
624 | * \brief Returns x[i] - x[i - 1] | ||
625 | */ | ||
626 | 24685164 | Scalar h_(int i) const | |
627 | { | ||
628 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 99335 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 24577214 times.
|
24685164 | assert(x_(i) > x_(i-1)); // the sampling points must be given |
629 | // in ascending order | ||
630 | 24685164 | return x_(i) - x_(i - 1); | |
631 | } | ||
632 | |||
633 | /*! | ||
634 | * \brief Returns the y coordinate of the i-th sampling point. | ||
635 | */ | ||
636 | 98176732 | Scalar x_(int i) const | |
637 |
67/105✗ Branch 0 not taken.
✓ Branch 1 taken 10291 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1191454 times.
✓ Branch 5 taken 19723398 times.
✓ Branch 6 taken 1210493 times.
✓ Branch 7 taken 88611 times.
✓ Branch 8 taken 19742449 times.
✓ Branch 9 taken 19210 times.
✓ Branch 10 taken 19084 times.
✓ Branch 12 taken 156 times.
✓ Branch 13 taken 19057 times.
✓ Branch 15 taken 220345 times.
✓ Branch 16 taken 36 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 399291 times.
✓ Branch 20 taken 2053 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 2185761 times.
✓ Branch 23 taken 2053 times.
✓ Branch 24 taken 2 times.
✗ Branch 25 not taken.
✗ Branch 27 not taken.
✓ Branch 28 taken 330 times.
✓ Branch 30 taken 1146 times.
✓ Branch 31 taken 2960 times.
✓ Branch 32 taken 1146679 times.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✓ Branch 35 taken 160 times.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✓ Branch 42 taken 170 times.
✓ Branch 43 taken 150 times.
✓ Branch 44 taken 2113 times.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✓ Branch 47 taken 2113 times.
✗ Branch 48 not taken.
✗ Branch 49 not taken.
✗ Branch 51 not taken.
✗ Branch 52 not taken.
✓ Branch 54 taken 1206 times.
✓ Branch 55 taken 3020 times.
✓ Branch 56 taken 18 times.
✗ Branch 57 not taken.
✗ Branch 58 not taken.
✓ Branch 59 taken 18 times.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✗ Branch 63 not taken.
✗ Branch 64 not taken.
✗ Branch 66 not taken.
✓ Branch 67 taken 6 times.
✓ Branch 69 taken 1963 times.
✗ Branch 70 not taken.
✓ Branch 71 taken 1963 times.
✗ Branch 72 not taken.
✓ Branch 73 taken 1963 times.
✗ Branch 74 not taken.
✗ Branch 75 not taken.
✓ Branch 76 taken 1963 times.
✓ Branch 77 taken 1048 times.
✓ Branch 78 taken 2878 times.
✓ Branch 79 taken 1963 times.
✗ Branch 80 not taken.
✓ Branch 81 taken 1956 times.
✓ Branch 82 taken 7 times.
✓ Branch 83 taken 10 times.
✓ Branch 84 taken 4 times.
✓ Branch 87 taken 7 times.
✗ Branch 88 not taken.
✓ Branch 90 taken 1982 times.
✓ Branch 91 taken 20 times.
✓ Branch 92 taken 1962 times.
✓ Branch 93 taken 20 times.
✓ Branch 94 taken 1960 times.
✓ Branch 95 taken 2 times.
✓ Branch 97 taken 10 times.
✓ Branch 98 taken 10 times.
✓ Branch 99 taken 20 times.
✗ Branch 100 not taken.
✓ Branch 102 taken 10 times.
✓ Branch 103 taken 10 times.
✓ Branch 104 taken 1962 times.
✗ Branch 105 not taken.
✓ Branch 106 taken 131 times.
✗ Branch 107 not taken.
✗ Branch 108 not taken.
✓ Branch 109 taken 131 times.
✓ Branch 110 taken 1 times.
✓ Branch 111 taken 1 times.
✓ Branch 113 taken 1 times.
✗ Branch 114 not taken.
✓ Branch 116 taken 141 times.
✓ Branch 117 taken 121 times.
✓ Branch 4 taken 22060565 times.
✓ Branch 11 taken 19057 times.
✓ Branch 14 taken 2337759 times.
✓ Branch 19 taken 2185761 times.
✗ Branch 26 not taken.
✓ Branch 29 taken 1146519 times.
✗ Branch 38 not taken.
|
49230571 | { return asImp_().x_(i); } |
638 | |||
639 | /*! | ||
640 | * \brief Returns the y coordinate of the i-th sampling point. | ||
641 | */ | ||
642 | 24674541 | Scalar y_(int i) const | |
643 |
4/6✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 10 times.
✓ Branch 8 taken 10 times.
✓ Branch 9 taken 10 times.
✓ Branch 10 taken 10 times.
|
90767 | { return asImp_().y_(i); } |
644 | |||
645 | /*! | ||
646 | * \brief Returns the moment (i.e. second derivative) of the | ||
647 | * spline at the i-th sampling point. | ||
648 | */ | ||
649 | 24500015 | Scalar moment_(int i) const | |
650 |
3/4✓ Branch 0 taken 981 times.
✓ Branch 1 taken 982 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 981 times.
|
24488629 | { return asImp_().moment_(i); } |
651 | |||
652 | // returns the coefficient in front of the x^0 term. In Stoer2005 this | ||
653 | // is delta. | ||
654 | 21029 | Scalar a_(int i) const | |
655 | 21029 | { return (moment_(i+1) - moment_(i))/(6*h_(i+1)); } | |
656 | |||
657 | // returns the coefficient in front of the x^2 term In Stoer2005 this | ||
658 | // is gamma. | ||
659 | 21029 | Scalar b_(int i) const | |
660 | 21029 | { return moment_(i)/2; } | |
661 | |||
662 | // returns the coefficient in front of the x^1 term. In Stoer2005 this | ||
663 | // is beta. | ||
664 | 21029 | Scalar c_(int i) const | |
665 | 21029 | { return (y_(i+1) - y_(i))/h_(i + 1) - h_(i+1)/6*(2*moment_(i) + moment_(i+1)); } | |
666 | |||
667 | // returns the coefficient in front of the x^0 term. In Stoer2005 this | ||
668 | // is alpha. | ||
669 | 19057 | Scalar d_(int i) const | |
670 | 19057 | { return y_(i); } | |
671 | |||
672 | /*! | ||
673 | * \brief Returns the number of sampling points. | ||
674 | */ | ||
675 | 605 | int numSamples_() const | |
676 |
3/8✗ Branch 1 not taken.
✓ Branch 2 taken 160 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 131 times.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
|
314 | { return asImp_().numSamples(); } |
677 | }; | ||
678 | |||
679 | } // end namespace Dumux | ||
680 | |||
681 | #endif | ||
682 |