3.2-git
DUNE for Multi-{Phase, Component, Scale, Physics, ...} flow and transport in porous media
splinecommon_.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 * See the file COPYING for full copying permissions. *
5 * *
6 * This program is free software: you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation, either version 3 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
18 *****************************************************************************/
24#ifndef DUMUX_SPLINE_COMMON__HH
25#define DUMUX_SPLINE_COMMON__HH
26
27#include <algorithm>
28#include <iostream>
29#include <cassert>
30
31#include <dune/common/exceptions.hh>
32#include <dune/common/float_cmp.hh>
33
34#include "math.hh"
35#include "valgrind.hh"
36
37namespace Dumux {
38
43template<class ScalarT, class ImplementationT>
45{
46 using Scalar = ScalarT;
47 using Implementation = ImplementationT;
48
49 Implementation &asImp_()
50 { return *static_cast<Implementation*>(this); }
51 const Implementation &asImp_() const
52 { return *static_cast<const Implementation*>(this); }
53
54public:
58 bool applies(Scalar x) const
59 {
60 return x_(0) <= x && x <= x_(numSamples_() - 1);
61 }
62
66 Scalar xMin() const
67 { return x_(0); }
68
72 Scalar xMax() const
73 { return x_(numSamples_() - 1); }
74
92 void printCSV(Scalar xi0, Scalar xi1, int k) const
93 {
94 using std::max;
95 using std::min;
96 Scalar x0 = min(xi0, xi1);
97 Scalar x1 = max(xi0, xi1);
98 const int n = numSamples_() - 1;
99 for (int i = 0; i <= k; ++i) {
100 double x = i*(x1 - x0)/k + x0;
101 double x_p1 = x + (x1 - x0)/k;
102 double y;
103 double dy_dx;
104 double mono = 1;
105 if (!applies(x)) {
106 if (x < 0) {
107 dy_dx = evalDerivative(x_(0));
108 y = (x - x_(0))*dy_dx + y_(0);
109 mono = (dy_dx>0)?1:-1;
110 }
111 else if (x > x_(n)) {
112 dy_dx = evalDerivative(x_(n));
113 y = (x - x_(n))*dy_dx + y_(n);
114 mono = (dy_dx>0)?1:-1;
115 }
116 else {
117 std::cerr << "ooops: " << x << "\n";
118 exit(1);
119 }
120 }
121 else {
122 y = eval(x);
123 dy_dx = evalDerivative(x);
124 mono = monotonic(max<Scalar>(x_(0), x), min<Scalar>(x_(n), x_p1));
125 }
126
127 std::cout << x << " " << y << " " << dy_dx << " " << mono << "\n";
128 }
129 }
130
142 Scalar eval(Scalar x, bool extrapolate=false) const
143 {
144 assert(extrapolate || applies(x));
145
146 // handle extrapolation
147 if (extrapolate) {
148 if (x < xMin()) {
149 Scalar m = evalDerivative(xMin(), /*segmentIdx=*/0);
150 Scalar y0 = y_(0);
151 return y0 + m*(x - xMin());
152 }
153 else if (x > xMax()) {
154 Scalar m = evalDerivative(xMax(), /*segmentIdx=*/numSamples_()-1);
155 Scalar y0 = y_(numSamples_()-1);
156 return y0 + m*(x - xMax());
157 }
158 }
159
160 return eval_(x, segmentIdx_(x));
161 }
162
176 Scalar evalDerivative(Scalar x, bool extrapolate=false) const
177 {
178 assert(extrapolate || applies(x));
179 if (extrapolate) {
180 if (Dune::FloatCmp::le(x, xMin()))
181 return evalDerivative_(xMin(), 0);
182 else if (Dune::FloatCmp::ge(x, xMax()))
183 return evalDerivative_(xMax(), numSamples_() - 1);
184 }
185
186 return evalDerivative_(x, segmentIdx_(x));
187 }
188
195 Scalar intersect(Scalar a, Scalar b, Scalar c, Scalar d) const
196 {
197 return intersectIntervall(xMin(), xMax(), a, b, c, d);
198 }
199
206 Scalar intersectInterval(Scalar x0, Scalar x1,
207 Scalar a, Scalar b, Scalar c, Scalar d) const
208 {
209 assert(applies(x0) && applies(x1));
210
211 Scalar tmpSol[3];
212 int nSol = 0;
213 int iFirst = segmentIdx_(x0);
214 int iLast = segmentIdx_(x1);
215 for (int i = iFirst; i <= iLast; ++i)
216 {
217 nSol += intersectSegment_(tmpSol, i, a, b, c, d, x0, x1);
218
219 if (nSol > 1) {
220 DUNE_THROW(Dune::MathError,
221 "Spline has more than one intersection"); //<<a<<"x^3 + "<<b<"x^2 + "<<c<"x + "<<d);
222 }
223 }
224
225 if (nSol != 1)
226 DUNE_THROW(Dune::MathError,
227 "Spline has no intersection"); //<<a<"x^3 + " <<b<"x^2 + "<<c<"x + "<<d<<"!");
228
229 return tmpSol[0];
230 }
231
240 int monotonic(Scalar x0, Scalar x1) const
241 {
242 assert(applies(x0));
243 assert(applies(x1));
244 assert(Dune::FloatCmp::ne<Scalar>(x0, x1));
245
246 // make sure that x0 is smaller than x1
247 if (x0 > x1)
248 {
249 using std::swap;
250 swap(x0, x1);
251 }
252
253 // corner case where the whole spline is a constant
254 if (Dune::FloatCmp::eq<Scalar>(moment_(0), 0) &&
255 Dune::FloatCmp::eq<Scalar>(moment_(1), 0) &&
256 Dune::FloatCmp::eq<Scalar>(y_(0), y_(1)))
257 {
258 // actually the is monotonically increasing as well as
259 // monotonously decreasing
260 return 2;
261 }
262
263
264 int i = segmentIdx_(x0);
265 if (x_(i) <= x0 && x1 <= x_(i+1)) {
266 // the interval to check is completely included in a
267 // single segment
268 return monotonic_(i, x0, x1);
269 }
270
271 // make sure that the segments which are completly in the
272 // interval [x0, x1] all exhibit the same monotonicity.
273 int iEnd = segmentIdx_(x1);
274 int r = monotonic_(i, x0, x_(1));
275 for (; i < iEnd - 1; ++i)
276 if (r != monotonic_(i, x_(i), x_(i + 1)))
277 return 0;
278
279 // check for the last segment
280 if (x_(iEnd) < x1 && r != monotonic_(iEnd, x_(iEnd), x1))
281 { return 0; }
282
283 return r;
284 }
285
290 int monotonic() const
291 { return monotonic(xMin(), xMax()); }
292
293protected:
294 // this is an internal class, so everything is protected!
296 { Valgrind::SetUndefined(asImp_()); }
297
304 template <class DestVector, class SourceVector>
305 void assignSamplingPoints_(DestVector &destX,
306 DestVector &destY,
307 const SourceVector &srcX,
308 const SourceVector &srcY,
309 int numSamples)
310 {
311 assert(numSamples >= 2);
312
313 // copy sample points, make sure that the first x value is
314 // smaller than the last one
315 for (int i = 0; i < numSamples; ++i) {
316 int idx = i;
317 if (srcX[0] > srcX[numSamples - 1])
318 idx = numSamples - i - 1;
319 destX[i] = srcX[idx];
320 destY[i] = srcY[idx];
321 }
322 }
323
324 template <class DestVector, class ListIterator>
325 void assignFromArrayList_(DestVector &destX,
326 DestVector &destY,
327 const ListIterator &srcBegin,
328 const ListIterator &srcEnd,
329 int numSamples)
330 {
331 assert(numSamples >= 2);
332
333 // find out wether the x values are in reverse order
334 ListIterator it = srcBegin;
335 ++it;
336 bool reverse = false;
337 if ((*srcBegin)[0] > (*it)[0])
338 reverse = true;
339 --it;
340
341 // loop over all sampling points
342 for (int i = 0; it != srcEnd; ++i, ++it) {
343 int idx = i;
344 if (reverse)
345 idx = numSamples - i - 1;
346 destX[i] = (*it)[0];
347 destY[i] = (*it)[1];
348 }
349 }
350
358 template <class DestVector, class ListIterator>
359 void assignFromTupleList_(DestVector &destX,
360 DestVector &destY,
361 ListIterator srcBegin,
362 ListIterator srcEnd,
363 int numSamples)
364 {
365 assert(numSamples >= 2);
366
367 // copy sample points, make sure that the first x value is
368 // smaller than the last one
369
370 // find out wether the x values are in reverse order
371 ListIterator it = srcBegin;
372 ++it;
373 bool reverse = false;
374 if (std::get<0>(*srcBegin) > std::get<0>(*it))
375 reverse = true;
376 --it;
377
378 // loop over all sampling points
379 for (int i = 0; it != srcEnd; ++i, ++it) {
380 int idx = i;
381 if (reverse)
382 idx = numSamples - i - 1;
383 destX[i] = std::get<0>(*it);
384 destY[i] = std::get<1>(*it);
385 }
386 }
387
388
396 template <class Vector, class Matrix>
397 void makePeriodicSystem_(Matrix &M, Vector &d)
398 {
399 // a periodic spline only makes sense if the first and the
400 // last sampling point have the same y value
401 assert(y_(0) == y_(numSamples_() - 1));
402
403 makeNaturalSystem(M, d);
404
405 int n = numSamples_() - 1;
406
407 Scalar lambda_n = h_(1)/(h_(n) + h_(1));
408 Scalar lambda_1 = M[1][2];
409 Scalar mu_1 = 1 - lambda_1;
410 Scalar mu_n = 1 - lambda_n;
411
412 Scalar d_n =
413 6 / (h_(n) + h_(1))
414 *
415 ( (y_(1) - y_(n))/h_(1) - (y_(n) - y_(n-1))/h_(n));
416
417 // insert lambda_n and mu_1
418 M[1][n] = mu_1;
419 M[n][1] = lambda_n;
420 M[n][n-1] = mu_n;
421 d[n] = d_n;
422 }
423
428 template <class Vector, class Matrix>
429 void makeFullSystem_(Matrix &M, Vector &d, Scalar m0, Scalar m1)
430 {
431 makeNaturalSystem_(M, d);
432
433 int n = numSamples_() - 1;
434 // first row
435 M[0][1] = 1;
436 d[0] = 6/h_(1) * ( (y_(1) - y_(0))/h_(1) - m0);
437
438 // last row
439 M[n][n - 1] = 1;
440
441 // right hand side
442 d[n] =
443 6/h_(n)
444 *
445 (m1 - (y_(n) - y_(n - 1))/h_(n));
446 }
447
453 template <class Vector, class Matrix>
454 void makeNaturalSystem_(Matrix &M, Vector &d)
455 {
456 M = 0;
457 d = 0;
458
459 const int n = numSamples_() - 1;
460
461 // second to next to last rows
462 for (int i = 1; i < n; ++i) {
463 const Scalar lambda_i = h_(i + 1) / (h_(i) + h_(i+1));
464 const Scalar mu_i = 1 - lambda_i;
465 const Scalar d_i =
466 6 / (h_(i) + h_(i+1))
467 *
468 ( (y_(i+1) - y_(i))/h_(i+1) - (y_(i) - y_(i-1))/h_(i));
469
470 M[i][i-1] = mu_i;
471 M[i][i] = 2;
472 M[i][i + 1] = lambda_i;
473 d[i] = d_i;
474 }
475
476 // first row
477 M[0][0] = 2;
478
479 // last row
480 M[n][n] = 2;
481 // right hand side
482 d[0] = 0.0;
483 d[n] = 0.0;
484 }
485
486 // evaluate the spline at a given the position and given the
487 // segment index
488 Scalar eval_(Scalar x, int i) const
489 {
490 // See: J. Stoer: "Numerische Mathematik 1", 9th edition,
491 // Springer, 2005, p. 109
492 Scalar h_i1 = h_(i + 1);
493 Scalar x_i = x - x_(i);
494 Scalar x_i1 = x_(i+1) - x;
495
496 Scalar A_i =
497 (y_(i+1) - y_(i))/h_i1
498 -
499 h_i1/6*(moment_(i+1) - moment_(i));
500 Scalar B_i = y_(i) - moment_(i)* (h_i1*h_i1) / 6;
501
502 return
503 moment_(i)* x_i1*x_i1*x_i1 / (6 * h_i1)
504 +
505 moment_(i + 1)* x_i*x_i*x_i / (6 * h_i1)
506 +
507 A_i*x_i
508 +
509 B_i;
510 }
511
512 // evaluate the derivative of a spline given the actual position
513 // and the segment index
514 Scalar evalDerivative_(Scalar x, int i) const
515 {
516 // See: J. Stoer: "Numerische Mathematik 1", 9th edition,
517 // Springer, 2005, p. 109
518 Scalar h_i1 = h_(i + 1);
519 Scalar x_i = x - x_(i);
520 Scalar x_i1 = x_(i+1) - x;
521
522 Scalar A_i =
523 (y_(i+1) - y_(i))/h_i1
524 -
525 h_i1/6*(moment_(i+1) - moment_(i));
526
527 return
528 -moment_(i) * x_i1*x_i1 / (2 * h_i1)
529 +
530 moment_(i + 1) * x_i*x_i / (2 * h_i1)
531 +
532 A_i;
533 }
534
535
536 // returns the monotonicality of an interval of a spline segment
537 //
538 // The return value have the following meaning:
539 //
540 // 1: spline is monotonously increasing in the specified interval
541 // 0: spline is not monotonic in the specified interval
542 // -1: spline is monotonously decreasing in the specified interval
543 int monotonic_(int i, Scalar x0, Scalar x1) const
544 {
545 // shift the interval so that it is consistent with the
546 // definitions by Stoer
547 x0 = x0 - x_(i);
548 x1 = x1 - x_(i);
549
550 Scalar a = a_(i);
551 Scalar b = b_(i);
552 Scalar c = c_(i);
553
554 Scalar disc = 4*b*b - 12*a*c;
555 if (disc < 0) {
556 // discriminant is smaller than 0, i.e. the segment does
557 // not exhibit any extrema.
558 return (x0*(x0*3*a + 2*b) + c > 0) ? 1 : -1;
559 }
560 using std::sqrt;
561 disc = sqrt(disc);
562 Scalar xE1 = (-2*b + disc)/(6*a);
563 Scalar xE2 = (-2*b - disc)/(6*a);
564
565 if (Dune::FloatCmp::eq<Scalar>(disc, 0)) {
566 // saddle point -> no extrema
567 if (Dune::FloatCmp::eq<Scalar>(xE1, x0))
568 // make sure that we're not picking the saddle point
569 // to determine whether we're monotonically increasing
570 // or decreasing
571 x0 = x1;
572 return sign(x0*(x0*3*a + 2*b) + c);
573 }
574 if ((x0 < xE1 && xE1 < x1) ||
575 (x0 < xE2 && xE2 < x1))
576 {
577 // there is an extremum in the range (x0, x1)
578 return 0;
579 }
580 // no extremum in range (x0, x1)
581 x0 = (x0 + x1)/2; // pick point in the middle of the interval
582 // to avoid extrema on the boundaries
583 return sign(x0*(x0*3*a + 2*b) + c);
584 }
585
590 int intersectSegment_(Scalar *sol,
591 int segIdx,
592 Scalar a, Scalar b, Scalar c, Scalar d,
593 Scalar x0 = -1e100, Scalar x1 = 1e100) const
594 {
595 int n = invertCubicPolynomial(sol,
596 a_(segIdx) - a,
597 b_(segIdx) - b,
598 c_(segIdx) - c,
599 d_(segIdx) - d);
600 using std::max;
601 x0 = max(x_(segIdx), x0);
602 x1 = max(x_(segIdx+1), x1);
603
604 // filter the intersections outside of the specified intervall
605 int k = 0;
606 for (int j = 0; j < n; ++j) {
607 sol[j] += x_(segIdx); // add the offset of the intervall. For details see Stoer
608 if (x0 <= sol[j] && sol[j] <= x1) {
609 sol[k] = sol[j];
610 ++k;
611 }
612 }
613 return k;
614 }
615
616 // find the segment index for a given x coordinate
617 int segmentIdx_(Scalar x) const
618 {
619 // bisection
620 int iLow = 0;
621 int iHigh = numSamples_() - 1;
622
623 while (iLow + 1 < iHigh) {
624 int i = (iLow + iHigh) / 2;
625 if (x_(i) > x)
626 iHigh = i;
627 else
628 iLow = i;
629 }
630 return iLow;
631 }
632
636 Scalar h_(int i) const
637 {
638 assert(x_(i) > x_(i-1)); // the sampling points must be given
639 // in ascending order
640 return x_(i) - x_(i - 1);
641 }
642
646 Scalar x_(int i) const
647 { return asImp_().x_(i); }
648
652 Scalar y_(int i) const
653 { return asImp_().y_(i); }
654
659 Scalar moment_(int i) const
660 { return asImp_().moment_(i); }
661
662 // returns the coefficient in front of the x^0 term. In Stoer this
663 // is delta.
664 Scalar a_(int i) const
665 { return (moment_(i+1) - moment_(i))/(6*h_(i+1)); }
666
667 // returns the coefficient in front of the x^2 term In Stoer this
668 // is gamma.
669 Scalar b_(int i) const
670 { return moment_(i)/2; }
671
672 // returns the coefficient in front of the x^1 term. In Stoer this
673 // is beta.
674 Scalar c_(int i) const
675 { return (y_(i+1) - y_(i))/h_(i + 1) - h_(i+1)/6*(2*moment_(i) + moment_(i+1)); }
676
677 // returns the coefficient in front of the x^0 term. In Stoer this
678 // is alpha.
679 Scalar d_(int i) const
680 { return y_(i); }
681
685 int numSamples_() const
686 { return asImp_().numSamples(); }
687};
688
689} // end namespace Dumux
690
691#endif
Define some often used mathematical functions.
Some templates to wrap the valgrind macros.
int invertCubicPolynomial(SolContainer *sol, Scalar a, Scalar b, Scalar c, Scalar d)
Invert a cubic polynomial analytically.
Definition: math.hh:242
void SetUndefined(const T &value)
Make the memory on which an object resides undefined.
Definition: valgrind.hh:102
constexpr int sign(const ValueType &value) noexcept
Sign or signum function.
Definition: math.hh:618
Definition: adapt.hh:29
The common code for all 3rd order polynomial splines.
Definition: splinecommon_.hh:45
Scalar evalDerivative_(Scalar x, int i) const
Definition: splinecommon_.hh:514
Scalar moment_(int i) const
Returns the moment (i.e. second derivative) of the spline at the i-th sampling point.
Definition: splinecommon_.hh:659
Scalar evalDerivative(Scalar x, bool extrapolate=false) const
Evaluate the spline's derivative at a given position.
Definition: splinecommon_.hh:176
Scalar y_(int i) const
Returns the y coordinate of the i-th sampling point.
Definition: splinecommon_.hh:652
Scalar intersectInterval(Scalar x0, Scalar x1, Scalar a, Scalar b, Scalar c, Scalar d) const
Find the intersections of the spline with a cubic polynomial in a sub-intervall of the spline,...
Definition: splinecommon_.hh:206
int intersectSegment_(Scalar *sol, int segIdx, Scalar a, Scalar b, Scalar c, Scalar d, Scalar x0=-1e100, Scalar x1=1e100) const
Find all the intersections of a segment of the spline with a cubic polynomial within a specified inte...
Definition: splinecommon_.hh:590
Scalar eval(Scalar x, bool extrapolate=false) const
Evaluate the spline at a given position.
Definition: splinecommon_.hh:142
int segmentIdx_(Scalar x) const
Definition: splinecommon_.hh:617
void makeFullSystem_(Matrix &M, Vector &d, Scalar m0, Scalar m1)
Make the linear system of equations Mx = d which results in the moments of the full spline.
Definition: splinecommon_.hh:429
void assignFromArrayList_(DestVector &destX, DestVector &destY, const ListIterator &srcBegin, const ListIterator &srcEnd, int numSamples)
Definition: splinecommon_.hh:325
void assignSamplingPoints_(DestVector &destX, DestVector &destY, const SourceVector &srcX, const SourceVector &srcY, int numSamples)
Set the sampling point vectors.
Definition: splinecommon_.hh:305
Scalar a_(int i) const
Definition: splinecommon_.hh:664
int monotonic_(int i, Scalar x0, Scalar x1) const
Definition: splinecommon_.hh:543
void assignFromTupleList_(DestVector &destX, DestVector &destY, ListIterator srcBegin, ListIterator srcEnd, int numSamples)
Set the sampling points.
Definition: splinecommon_.hh:359
Scalar xMax() const
Return the x value of the rightmost sampling point.
Definition: splinecommon_.hh:72
int numSamples_() const
Returns the number of sampling points.
Definition: splinecommon_.hh:685
Scalar xMin() const
Return the x value of the leftmost sampling point.
Definition: splinecommon_.hh:66
Scalar c_(int i) const
Definition: splinecommon_.hh:674
Scalar b_(int i) const
Definition: splinecommon_.hh:669
int monotonic() const
Same as monotonic(x0, x1), but with the entire range of the spline as interval.
Definition: splinecommon_.hh:290
int monotonic(Scalar x0, Scalar x1) const
Returns 1 if the spline is monotonically increasing, -1 if the spline is mononously decreasing and 0 ...
Definition: splinecommon_.hh:240
void makeNaturalSystem_(Matrix &M, Vector &d)
Make the linear system of equations Mx = d which results in the moments of the natural spline....
Definition: splinecommon_.hh:454
void makePeriodicSystem_(Matrix &M, Vector &d)
Make the linear system of equations Mx = d which results in the moments of the periodic spline.
Definition: splinecommon_.hh:397
bool applies(Scalar x) const
Return true if the given x is in range [x1, xn].
Definition: splinecommon_.hh:58
Scalar intersect(Scalar a, Scalar b, Scalar c, Scalar d) const
Find the intersections of the spline with a cubic polynomial in the whole intervall,...
Definition: splinecommon_.hh:195
Scalar h_(int i) const
Returns x[i] - x[i - 1].
Definition: splinecommon_.hh:636
void printCSV(Scalar xi0, Scalar xi1, int k) const
Prints k tuples of the format (x, y, dx/dy, isMonotonic) to stdout.
Definition: splinecommon_.hh:92
Scalar x_(int i) const
Returns the y coordinate of the i-th sampling point.
Definition: splinecommon_.hh:646
Scalar eval_(Scalar x, int i) const
Definition: splinecommon_.hh:488
SplineCommon_()
Definition: splinecommon_.hh:295
Scalar d_(int i) const
Definition: splinecommon_.hh:679