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-FileCopyrightInfo: 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 | bool applies(Scalar x) const | ||
46 | { | ||
47 | 122694090 | return x_(0) <= x && x <= x_(numSamples_() - 1); | |
48 | } | ||
49 | |||
50 | /*! | ||
51 | * \brief Return the x value of the leftmost sampling point. | ||
52 | */ | ||
53 | Scalar xMin() const | ||
54 | 8 | { return x_(0); } | |
55 | |||
56 | /*! | ||
57 | * \brief Return the x value of the rightmost sampling point. | ||
58 | */ | ||
59 | Scalar xMax() const | ||
60 | 9 | { 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 | 2 | void printCSV(Scalar xi0, Scalar xi1, int k) const | |
80 | { | ||
81 | using std::max; | ||
82 | using std::min; | ||
83 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | Scalar x0 = min(xi0, xi1); |
84 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | Scalar x1 = max(xi0, xi1); |
85 | 4 | 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 | 3964 | if (!applies(x)) { | |
92 |
2/2✓ Branch 0 taken 20 times.
✓ Branch 1 taken 20 times.
|
40 | if (x < 0) { |
93 | 60 | dy_dx = evalDerivative(x_(0)); | |
94 | 60 | y = (x - x_(0))*dy_dx + y_(0); | |
95 | 20 | mono = (dy_dx>0)?1:-1; | |
96 | } | ||
97 | 40 | else if (x > x_(n)) { | |
98 | 60 | dy_dx = evalDerivative(x_(n)); | |
99 | 60 | y = (x - x_(n))*dy_dx + y_(n); | |
100 | 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 | 1962 | mono = monotonic(max<Scalar>(x_(0), x), min<Scalar>(x_(n), x_p1)); | |
112 | } | ||
113 | |||
114 | 8008 | 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 | 23028355 | Scalar eval(Scalar x, bool extrapolate=false) const | |
130 | { | ||
131 |
2/2✓ Branch 0 taken 23026155 times.
✓ Branch 1 taken 2 times.
|
23028355 | assert(extrapolate || applies(x)); |
132 | |||
133 | // handle extrapolation | ||
134 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 23026155 times.
|
23028355 | if (extrapolate) { |
135 | 8 | if (x < xMin()) { | |
136 | 6 | Scalar m = evalDerivative_(xMin(), /*segmentIdx=*/0); | |
137 | 4 | Scalar y0 = y_(0); | |
138 | 4 | return y0 + m*(x - xMin()); | |
139 | } | ||
140 | 4 | else if (x > xMax()) { | |
141 | 8 | Scalar m = evalDerivative_(xMax(), /*segmentIdx=*/numSamples_()-2); | |
142 | 6 | Scalar y0 = y_(numSamples_()-1); | |
143 | 4 | return y0 + m*(x - xMax()); | |
144 | } | ||
145 | } | ||
146 | |||
147 | 46052334 | 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 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1395197 times.
|
1397488 | if (extrapolate) { |
167 | ✗ | if (Dune::FloatCmp::le(x, xMin())) | |
168 | ✗ | return evalDerivative_(xMin(), 0); | |
169 | ✗ | else if (Dune::FloatCmp::ge(x, xMax())) | |
170 | ✗ | return evalDerivative_(xMax(), numSamples_() - 2); | |
171 | } | ||
172 | |||
173 | 2790430 | 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 | 55775 | 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 55775 times.
✓ Branch 1 taken 55775 times.
|
111550 | for (int i = iFirst; i <= iLast; ++i) |
203 | { | ||
204 | 55775 | nSol += intersectSegment_(tmpSol, i, a, b, c, d, x0, x1); | |
205 | |||
206 |
1/2✓ Branch 0 taken 55775 times.
✗ Branch 1 not taken.
|
55775 | 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 55775 times.
|
55775 | if (nSol != 1) |
213 | ✗ | DUNE_THROW(Dune::MathError, | |
214 | "Spline has no intersection"); //<<a<"x^3 + " <<b<"x^2 + "<<c<"x + "<<d<<"!"); | ||
215 | |||
216 | 55775 | 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 | 6870 | if (Dune::FloatCmp::eq<Scalar>(moment_(0), 0) && | |
242 |
3/4✓ Branch 0 taken 981 times.
✓ Branch 1 taken 982 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 981 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 | 7852 | 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 | 14 | 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 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (r != monotonic_(i, x_(i), x_(i + 1))) |
264 | return 0; | ||
265 | |||
266 | // check for the last segment | ||
267 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 5 times.
|
7 | if (x_(iEnd) < x1 && r != monotonic_(iEnd, x_(iEnd), x1)) |
268 | 2 | { 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 | 90648 | void makeFullSystem_(Matrix &M, Vector &d, Scalar m0, Scalar m1) | |
420 | { | ||
421 | 181270 | makeNaturalSystem_(M, d); | |
422 | |||
423 | 181296 | int n = numSamples_() - 1; | |
424 | // first row | ||
425 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 90622 times.
|
90648 | M[0][1] = 1; |
426 | 543818 | d[0] = 6/h_(1) * ( (y_(1) - y_(0))/h_(1) - m0); | |
427 | |||
428 | // last row | ||
429 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 90616 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 90616 times.
|
181284 | M[n][n - 1] = 1; |
430 | |||
431 | // right hand side | ||
432 | 181310 | d[n] = | |
433 | 181282 | 6/h_(n) | |
434 | 90648 | * | |
435 | 362578 | (m1 - (y_(n) - y_(n - 1))/h_(n)); | |
436 | 90648 | } | |
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 | 52 | void makeNaturalSystem_(Matrix &M, Vector &d) | |
445 | { | ||
446 | 90668 | M = 0; | |
447 | 90668 | d = 0; | |
448 | |||
449 | 181292 | 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.
|
90824 | for (int i = 1; i < n; ++i) { |
453 | 354 | 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 | 288 | 6 / (h_(i) + h_(i+1)) | |
457 | 156 | * | |
458 | 912 | ( (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 | 312 | M[i][i] = 2; | |
462 | 312 | M[i][i + 1] = lambda_i; | |
463 | 468 | d[i] = d_i; | |
464 | } | ||
465 | |||
466 | // first row | ||
467 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 90616 times.
|
90668 | M[0][0] = 2; |
468 | |||
469 | // last row | ||
470 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 90616 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 90616 times.
|
181314 | M[n][n] = 2; |
471 | // right hand side | ||
472 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 90616 times.
|
90720 | d[0] = 0.0; |
473 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 90616 times.
|
90750 | d[n] = 0.0; |
474 | 52 | } | |
475 | |||
476 | // evaluate the spline at a given the position and given the | ||
477 | // segment index | ||
478 | 23028351 | Scalar eval_(Scalar x, int i) const | |
479 | { | ||
480 | // See: Stoer2005 "Numerische Mathematik 1", 9th edition, | ||
481 | // Springer, 2005, p. 109 | ||
482 | 46056440 | Scalar h_i1 = h_(i + 1); | |
483 | 46056702 | Scalar x_i = x - x_(i); | |
484 | 46056702 | Scalar x_i1 = x_(i+1) - x; | |
485 | |||
486 | 23028351 | Scalar A_i = | |
487 | 69085053 | (y_(i+1) - y_(i))/h_i1 | |
488 | - | ||
489 | 69085053 | h_i1/6*(moment_(i+1) - moment_(i)); | |
490 | 69085053 | Scalar B_i = y_(i) - moment_(i)* (h_i1*h_i1) / 6; | |
491 | |||
492 | return | ||
493 | 46056702 | moment_(i)* x_i1*x_i1*x_i1 / (6 * h_i1) | |
494 | 23028351 | + | |
495 | 46056702 | moment_(i + 1)* x_i*x_i*x_i / (6 * h_i1) | |
496 | 23028351 | + | |
497 | 23028351 | A_i*x_i | |
498 | + | ||
499 | 23028351 | 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 | 2794660 | Scalar h_i1 = h_(i + 1); | |
509 | 2794984 | Scalar x_i = x - x_(i); | |
510 | 2794984 | Scalar x_i1 = x_(i+1) - x; | |
511 | |||
512 | 1397492 | Scalar A_i = | |
513 | 4192476 | (y_(i+1) - y_(i))/h_i1 | |
514 | - | ||
515 | 4192476 | h_i1/6*(moment_(i+1) - moment_(i)); | |
516 | |||
517 | return | ||
518 | 2794984 | -moment_(i) * x_i1*x_i1 / (2 * h_i1) | |
519 | 1397492 | + | |
520 | 2794984 | 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 | 3944 | x0 = x0 - x_(i); | |
538 | 3944 | x1 = x1 - x_(i); | |
539 | |||
540 | 3944 | Scalar a = a_(i); | |
541 | 3944 | 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 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 1972 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1972 times.
|
3944 | 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 | 55775 | 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 | 55775 | int n = invertCubicPolynomial(sol, | |
586 | 55775 | a_(segIdx) - a, | |
587 | 111550 | b_(segIdx) - b, | |
588 | 55775 | c_(segIdx) - c, | |
589 | 111550 | d_(segIdx) - d); | |
590 | using std::max; | ||
591 | 167325 | x0 = max(x_(segIdx), x0); | |
592 | 167325 | x1 = max(x_(segIdx+1), x1); | |
593 | |||
594 | // filter the intersections outside of the specified interval | ||
595 | 55775 | int k = 0; | |
596 |
2/2✓ Branch 0 taken 128821 times.
✓ Branch 1 taken 55775 times.
|
184596 | for (int j = 0; j < n; ++j) { |
597 | 257642 | sol[j] += x_(segIdx); // add the offset of the interval. For details see Stoer2005 | |
598 |
1/2✓ Branch 0 taken 55775 times.
✗ Branch 1 not taken.
|
55775 | if (x0 <= sol[j] && sol[j] <= x1) { |
599 | 55775 | sol[k] = sol[j]; | |
600 | 55775 | ++k; | |
601 | } | ||
602 | } | ||
603 | 55775 | return k; | |
604 | } | ||
605 | |||
606 | // find the segment index for a given x coordinate | ||
607 | ✗ | int segmentIdx_(Scalar x) const | |
608 | { | ||
609 | // bisection | ||
610 | 24419299 | int iLow = 0; | |
611 | 48838598 | 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.
|
24436146 | while (iLow + 1 < iHigh) { |
614 | 12854 | int i = (iLow + iHigh) / 2; | |
615 | 25708 | if (x_(i) > x) | |
616 | iHigh = i; | ||
617 | else | ||
618 | iLow = i; | ||
619 | } | ||
620 | ✗ | return iLow; | |
621 | } | ||
622 | |||
623 | /*! | ||
624 | * \brief Returns x[i] - x[i - 1] | ||
625 | */ | ||
626 | 397 | Scalar h_(int i) const | |
627 | { | ||
628 | ✗ | assert(x_(i) > x_(i-1)); // the sampling points must be given | |
629 | // in ascending order | ||
630 | 74872254 | return x_(i) - x_(i - 1); | |
631 | } | ||
632 | |||
633 | /*! | ||
634 | * \brief Returns the y coordinate of the i-th sampling point. | ||
635 | */ | ||
636 | Scalar x_(int i) const | ||
637 |
161/260✗ Branch 0 not taken.
✓ Branch 1 taken 2103 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2103 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2103 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 4063 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 4063 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 4063 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 4063 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 4144 times.
✓ Branch 16 taken 6 times.
✓ Branch 17 taken 4138 times.
✓ Branch 18 taken 6 times.
✓ Branch 19 taken 4138 times.
✗ Branch 20 not taken.
✓ Branch 21 taken 90643 times.
✗ Branch 22 not taken.
✓ Branch 23 taken 90643 times.
✓ Branch 24 taken 1191442 times.
✓ Branch 25 taken 19799228 times.
✓ Branch 26 taken 1191442 times.
✓ Branch 27 taken 19799228 times.
✗ Branch 28 not taken.
✓ Branch 29 taken 20990670 times.
✓ Branch 30 taken 155768 times.
✓ Branch 31 taken 144327 times.
✓ Branch 32 taken 155768 times.
✓ Branch 33 taken 144327 times.
✗ Branch 34 not taken.
✓ Branch 35 taken 300095 times.
✓ Branch 36 taken 19499133 times.
✓ Branch 37 taken 144294 times.
✓ Branch 38 taken 19499133 times.
✓ Branch 39 taken 144294 times.
✓ Branch 40 taken 55769 times.
✓ Branch 41 taken 19660704 times.
✓ Branch 42 taken 55769 times.
✓ Branch 43 taken 161571 times.
✓ Branch 44 taken 162 times.
✓ Branch 45 taken 2132658 times.
✓ Branch 46 taken 201450 times.
✓ Branch 47 taken 2132658 times.
✓ Branch 48 taken 201294 times.
✓ Branch 49 taken 2132814 times.
✓ Branch 50 taken 201294 times.
✓ Branch 51 taken 201288 times.
✓ Branch 52 taken 6 times.
✓ Branch 53 taken 201288 times.
✗ Branch 54 not taken.
✓ Branch 55 taken 6 times.
✓ Branch 56 taken 257077 times.
✓ Branch 57 taken 89881 times.
✓ Branch 58 taken 57902 times.
✓ Branch 59 taken 89881 times.
✓ Branch 60 taken 1204185 times.
✓ Branch 61 taken 89901 times.
✓ Branch 62 taken 1204185 times.
✗ Branch 63 not taken.
✓ Branch 64 taken 1202078 times.
✗ Branch 65 not taken.
✓ Branch 66 taken 1191448 times.
✓ Branch 67 taken 55769 times.
✓ Branch 68 taken 3413987 times.
✗ Branch 69 not taken.
✓ Branch 70 taken 3413981 times.
✗ Branch 71 not taken.
✓ Branch 72 taken 216 times.
✓ Branch 73 taken 2222539 times.
✓ Branch 74 taken 216 times.
✗ Branch 75 not taken.
✓ Branch 76 taken 222 times.
✗ Branch 77 not taken.
✓ Branch 78 taken 182 times.
✗ Branch 79 not taken.
✓ Branch 80 taken 182 times.
✗ Branch 81 not taken.
✓ Branch 82 taken 194 times.
✓ Branch 83 taken 1146519 times.
✓ Branch 84 taken 18 times.
✓ Branch 85 taken 1146519 times.
✓ Branch 86 taken 18 times.
✓ Branch 87 taken 12 times.
✓ Branch 88 taken 1146519 times.
✓ Branch 89 taken 12 times.
✗ Branch 90 not taken.
✗ Branch 91 not taken.
✓ Branch 92 taken 12 times.
✗ Branch 93 not taken.
✗ Branch 94 not taken.
✗ Branch 95 not taken.
✗ Branch 96 not taken.
✗ Branch 97 not taken.
✗ Branch 99 not taken.
✗ Branch 100 not taken.
✗ Branch 102 not taken.
✓ Branch 103 taken 397 times.
✗ Branch 104 not taken.
✓ Branch 105 taken 397 times.
✗ Branch 106 not taken.
✓ Branch 107 taken 397 times.
✓ Branch 111 taken 2053 times.
✗ Branch 112 not taken.
✓ Branch 113 taken 2053 times.
✗ Branch 114 not taken.
✗ Branch 115 not taken.
✓ Branch 116 taken 2053 times.
✗ Branch 117 not taken.
✗ Branch 118 not taken.
✗ Branch 119 not taken.
✗ Branch 120 not taken.
✗ Branch 123 not taken.
✗ Branch 124 not taken.
✓ Branch 126 taken 1146 times.
✓ Branch 127 taken 2960 times.
✓ Branch 128 taken 1146 times.
✓ Branch 129 taken 2960 times.
✓ Branch 130 taken 160 times.
✗ Branch 131 not taken.
✓ Branch 132 taken 160 times.
✗ Branch 133 not taken.
✗ Branch 134 not taken.
✓ Branch 135 taken 160 times.
✗ Branch 136 not taken.
✗ Branch 137 not taken.
✗ Branch 138 not taken.
✗ Branch 139 not taken.
✗ Branch 140 not taken.
✗ Branch 141 not taken.
✓ Branch 145 taken 170 times.
✓ Branch 146 taken 150 times.
✓ Branch 147 taken 170 times.
✓ Branch 148 taken 150 times.
✓ Branch 149 taken 2113 times.
✗ Branch 150 not taken.
✓ Branch 151 taken 2113 times.
✗ Branch 152 not taken.
✗ Branch 153 not taken.
✓ Branch 154 taken 2113 times.
✗ Branch 155 not taken.
✗ Branch 156 not taken.
✗ Branch 157 not taken.
✗ Branch 158 not taken.
✗ Branch 159 not taken.
✗ Branch 160 not taken.
✓ Branch 164 taken 1206 times.
✓ Branch 165 taken 3020 times.
✓ Branch 166 taken 1206 times.
✓ Branch 167 taken 3020 times.
✓ Branch 168 taken 18 times.
✗ Branch 169 not taken.
✓ Branch 170 taken 18 times.
✗ Branch 171 not taken.
✗ Branch 172 not taken.
✓ Branch 173 taken 18 times.
✗ Branch 174 not taken.
✗ Branch 175 not taken.
✗ Branch 176 not taken.
✗ Branch 177 not taken.
✗ Branch 178 not taken.
✗ Branch 179 not taken.
✗ Branch 183 not taken.
✓ Branch 184 taken 6 times.
✗ Branch 185 not taken.
✓ Branch 186 taken 6 times.
✗ Branch 187 not taken.
✓ Branch 188 taken 6 times.
✗ Branch 189 not taken.
✓ Branch 190 taken 6 times.
✗ Branch 191 not taken.
✓ Branch 192 taken 6 times.
✗ Branch 193 not taken.
✓ Branch 194 taken 6 times.
✗ Branch 195 not taken.
✓ Branch 196 taken 6 times.
✗ Branch 197 not taken.
✓ Branch 198 taken 6 times.
✗ Branch 199 not taken.
✓ Branch 200 taken 6 times.
✗ Branch 201 not taken.
✓ Branch 202 taken 6 times.
✗ Branch 203 not taken.
✓ Branch 204 taken 6 times.
✗ Branch 205 not taken.
✓ Branch 206 taken 6 times.
✓ Branch 207 taken 1963 times.
✗ Branch 208 not taken.
✓ Branch 209 taken 1963 times.
✗ Branch 210 not taken.
✓ Branch 211 taken 1963 times.
✗ Branch 212 not taken.
✓ Branch 213 taken 1963 times.
✗ Branch 214 not taken.
✓ Branch 215 taken 1963 times.
✗ Branch 216 not taken.
✓ Branch 217 taken 1963 times.
✗ Branch 218 not taken.
✓ Branch 219 taken 1048 times.
✓ Branch 220 taken 2878 times.
✓ Branch 221 taken 1048 times.
✓ Branch 222 taken 2878 times.
✓ Branch 223 taken 1963 times.
✗ Branch 224 not taken.
✓ Branch 225 taken 1963 times.
✗ Branch 226 not taken.
✓ Branch 227 taken 1956 times.
✓ Branch 228 taken 7 times.
✓ Branch 229 taken 1956 times.
✓ Branch 230 taken 7 times.
✓ Branch 231 taken 10 times.
✓ Branch 232 taken 4 times.
✓ Branch 233 taken 10 times.
✓ Branch 234 taken 4 times.
✓ Branch 240 taken 7 times.
✗ Branch 241 not taken.
✓ Branch 242 taken 7 times.
✗ Branch 243 not taken.
✓ Branch 246 taken 1982 times.
✓ Branch 247 taken 20 times.
✓ Branch 248 taken 1982 times.
✓ Branch 249 taken 20 times.
✓ Branch 250 taken 1962 times.
✓ Branch 251 taken 20 times.
✓ Branch 252 taken 1960 times.
✓ Branch 253 taken 2 times.
✓ Branch 254 taken 1960 times.
✓ Branch 255 taken 2 times.
✓ Branch 258 taken 10 times.
✓ Branch 259 taken 10 times.
✓ Branch 260 taken 20 times.
✗ Branch 261 not taken.
✓ Branch 262 taken 20 times.
✗ Branch 263 not taken.
✓ Branch 266 taken 10 times.
✓ Branch 267 taken 10 times.
✓ Branch 268 taken 1962 times.
✗ Branch 269 not taken.
✓ Branch 270 taken 1962 times.
✗ Branch 271 not taken.
✓ Branch 272 taken 131 times.
✗ Branch 273 not taken.
✓ Branch 274 taken 131 times.
✗ Branch 275 not taken.
✗ Branch 276 not taken.
✓ Branch 277 taken 131 times.
✓ Branch 278 taken 1 times.
✓ Branch 279 taken 1 times.
✓ Branch 280 taken 1 times.
✓ Branch 281 taken 1 times.
✓ Branch 284 taken 1 times.
✗ Branch 285 not taken.
✓ Branch 287 taken 141 times.
✓ Branch 288 taken 121 times.
✓ Branch 289 taken 141 times.
✓ Branch 290 taken 121 times.
|
123142215 | { return asImp_().x_(i); } |
638 | |||
639 | /*! | ||
640 | * \brief Returns the y coordinate of the i-th sampling point. | ||
641 | */ | ||
642 | Scalar y_(int i) const | ||
643 |
14/26✗ Branch 0 not taken.
✓ Branch 1 taken 90610 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 90610 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 90610 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 6 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 6 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 6 times.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✓ Branch 29 taken 10 times.
✓ Branch 30 taken 10 times.
✓ Branch 31 taken 10 times.
✓ Branch 32 taken 10 times.
✓ Branch 33 taken 10 times.
✓ Branch 34 taken 10 times.
✓ Branch 35 taken 10 times.
✓ Branch 36 taken 10 times.
|
272392 | { 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 | Scalar moment_(int i) const | ||
650 |
6/8✓ Branch 0 taken 981 times.
✓ Branch 1 taken 982 times.
✓ Branch 2 taken 981 times.
✓ Branch 3 taken 982 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 981 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 981 times.
|
241944514 | { return asImp_().moment_(i); } |
651 | |||
652 | // returns the coefficient in front of the x^0 term. In Stoer2005 this | ||
653 | // is delta. | ||
654 | 57747 | Scalar a_(int i) const | |
655 |
3/6✗ Branch 0 not taken.
✓ Branch 1 taken 57747 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 57747 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 57747 times.
|
173241 | { 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 | Scalar b_(int i) const | ||
660 | 115494 | { return moment_(i)/2; } | |
661 | |||
662 | // returns the coefficient in front of the x^1 term. In Stoer2005 this | ||
663 | // is beta. | ||
664 | 57747 | Scalar c_(int i) const | |
665 |
3/6✗ Branch 0 not taken.
✓ Branch 1 taken 57747 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 57747 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 57747 times.
|
173241 | { 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 | Scalar d_(int i) const | ||
670 | 111550 | { return y_(i); } | |
671 | |||
672 | /*! | ||
673 | * \brief Returns the number of sampling points. | ||
674 | */ | ||
675 | ✗ | int numSamples_() const | |
676 |
22/48✗ Branch 0 not taken.
✓ Branch 1 taken 90610 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 90610 times.
✓ Branch 4 taken 55787 times.
✓ Branch 5 taken 20846499 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 55775 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 2222559 times.
✓ Branch 10 taken 2053 times.
✓ Branch 11 taken 201288 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 1146679 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 160 times.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 25 not taken.
✓ Branch 26 taken 2113 times.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 30 not taken.
✓ Branch 31 taken 18 times.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 36 not taken.
✓ Branch 37 taken 6 times.
✗ Branch 38 not taken.
✓ Branch 39 taken 6 times.
✓ Branch 40 taken 1963 times.
✗ Branch 41 not taken.
✓ Branch 42 taken 1963 times.
✗ Branch 43 not taken.
✓ Branch 44 taken 1962 times.
✓ Branch 45 taken 20 times.
✗ Branch 46 not taken.
✓ Branch 47 taken 131 times.
✗ Branch 48 not taken.
✓ Branch 49 taken 131 times.
✓ Branch 50 taken 1 times.
✗ Branch 51 not taken.
✓ Branch 52 taken 1 times.
✗ Branch 53 not taken.
|
49139977 | { return asImp_().numSamples(); } |
677 | }; | ||
678 | |||
679 | } // end namespace Dumux | ||
680 | |||
681 | #endif | ||
682 |