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 Define some often used mathematical functions | ||
11 | */ | ||
12 | #ifndef DUMUX_MATH_HH | ||
13 | #define DUMUX_MATH_HH | ||
14 | |||
15 | #include <algorithm> | ||
16 | #include <numeric> | ||
17 | #include <cmath> | ||
18 | #include <utility> | ||
19 | |||
20 | #include <dune/common/typetraits.hh> | ||
21 | #include <dune/common/fvector.hh> | ||
22 | #include <dune/common/fmatrix.hh> | ||
23 | #include <dune/common/dynmatrix.hh> | ||
24 | #include <dune/common/float_cmp.hh> | ||
25 | |||
26 | namespace Dumux { | ||
27 | |||
28 | /*! | ||
29 | * \ingroup Core | ||
30 | * \brief Calculate the (weighted) arithmetic mean of two scalar values. | ||
31 | * | ||
32 | * \param x The first input value | ||
33 | * \param y The second input value | ||
34 | * \param wx The first weight | ||
35 | * \param wy The second weight | ||
36 | */ | ||
37 | template <class Scalar> | ||
38 | constexpr Scalar arithmeticMean(Scalar x, Scalar y, Scalar wx = 1.0, Scalar wy = 1.0) noexcept | ||
39 | { | ||
40 | static_assert(Dune::IsNumber<Scalar>::value, "The arguments x, y, wx, and wy have to be numbers!"); | ||
41 | |||
42 |
4/4✓ Branch 0 taken 125500978 times.
✓ Branch 1 taken 608176 times.
✓ Branch 2 taken 110665516 times.
✓ Branch 3 taken 566378 times.
|
236785052 | if (x*y <= 0) |
43 | return 0; | ||
44 | 236166494 | return (x * wx + y * wy)/(wx + wy); | |
45 | } | ||
46 | |||
47 | /*! | ||
48 | * \ingroup Core | ||
49 | * \brief Calculate the (weighted) harmonic mean of two scalar values. | ||
50 | * | ||
51 | * \param x The first input value | ||
52 | * \param y The second input value | ||
53 | * \param wx The first weight | ||
54 | * \param wy The second weight | ||
55 | */ | ||
56 | template <class Scalar> | ||
57 | constexpr Scalar harmonicMean(Scalar x, Scalar y, Scalar wx = 1.0, Scalar wy = 1.0) noexcept | ||
58 | { | ||
59 | static_assert(Dune::IsNumber<Scalar>::value, "The arguments x, y, wx, and wy have to be numbers!"); | ||
60 | |||
61 |
14/18✓ Branch 0 taken 1032633713 times.
✓ Branch 1 taken 1010558 times.
✓ Branch 2 taken 678253082 times.
✓ Branch 3 taken 1036604 times.
✓ Branch 4 taken 183573648 times.
✓ Branch 5 taken 2020608 times.
✓ Branch 6 taken 274976364 times.
✓ Branch 7 taken 10000 times.
✓ Branch 8 taken 22986628 times.
✓ Branch 9 taken 10000 times.
✓ Branch 10 taken 11335942 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 4286880 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 2373056 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 2362080 times.
✗ Branch 17 not taken.
|
2216869163 | if (x*y <= 0) |
62 | return 0; | ||
63 | 2212781393 | return (wx + wy) * x * y / (wy * x + wx * y); | |
64 | } | ||
65 | |||
66 | /*! | ||
67 | * \ingroup Core | ||
68 | * \brief Calculate the geometric mean of two scalar values. | ||
69 | * | ||
70 | * \param x The first input value | ||
71 | * \param y The second input value | ||
72 | * \note as std::sqrt is not constexpr this function is not constexpr | ||
73 | */ | ||
74 | template <class Scalar> | ||
75 | Scalar geometricMean(Scalar x, Scalar y) noexcept | ||
76 | { | ||
77 | static_assert(Dune::IsNumber<Scalar>::value, "The arguments x and y have to be numbers!"); | ||
78 | |||
79 | if (x*y <= 0) | ||
80 | return 0; | ||
81 | using std::sqrt; | ||
82 | return sqrt(x*y)*sign(x); | ||
83 | } | ||
84 | |||
85 | /*! | ||
86 | * \ingroup Core | ||
87 | * \brief Calculate the harmonic mean of a fixed-size matrix. | ||
88 | * | ||
89 | * This is done by calculating the harmonic mean for each entry | ||
90 | * individually. The result is stored the first argument. | ||
91 | * | ||
92 | * \param K The matrix for storing the result | ||
93 | * \param Ki The first input matrix | ||
94 | * \param Kj The second input matrix | ||
95 | */ | ||
96 | template <class Scalar, int m, int n> | ||
97 | void harmonicMeanMatrix(Dune::FieldMatrix<Scalar, m, n> &K, | ||
98 | const Dune::FieldMatrix<Scalar, m, n> &Ki, | ||
99 | const Dune::FieldMatrix<Scalar, m, n> &Kj) | ||
100 | { | ||
101 | for (int rowIdx=0; rowIdx < m; rowIdx++){ | ||
102 | for (int colIdx=0; colIdx< n; colIdx++){ | ||
103 | if (Dune::FloatCmp::ne<Scalar>(Ki[rowIdx][colIdx], Kj[rowIdx][colIdx])) { | ||
104 | K[rowIdx][colIdx] = | ||
105 | harmonicMean(Ki[rowIdx][colIdx], | ||
106 | Kj[rowIdx][colIdx]); | ||
107 | } | ||
108 | else | ||
109 | K[rowIdx][colIdx] = Ki[rowIdx][colIdx]; | ||
110 | } | ||
111 | } | ||
112 | } | ||
113 | |||
114 | /*! | ||
115 | * \ingroup Core | ||
116 | * \brief A smoothed minimum function (using cubic interpolation) | ||
117 | * \note The returned value is guaranteed to be smaller or equal to the minimum of the two input values. | ||
118 | * \note The smoothing kicks in if the difference between the two input values is smaller than the smoothing parameter. | ||
119 | * \param a The first input value | ||
120 | * \param b The second input value | ||
121 | * \param k The smoothing parameter (k > 0) | ||
122 | */ | ||
123 | template<class Scalar> | ||
124 | Scalar smoothMin(const Scalar a, const Scalar b, const Scalar k) | ||
125 | { | ||
126 | using std::max; using std::min; using std::abs; | ||
127 |
8/8✓ Branch 0 taken 1681 times.
✓ Branch 1 taken 150 times.
✓ Branch 2 taken 1681 times.
✓ Branch 3 taken 150 times.
✓ Branch 4 taken 199 times.
✓ Branch 5 taken 18 times.
✓ Branch 6 taken 199 times.
✓ Branch 7 taken 18 times.
|
4098 | const auto h = max(k-abs(a-b), 0.0 )/k; |
128 |
4/4✓ Branch 0 taken 1110 times.
✓ Branch 1 taken 721 times.
✓ Branch 2 taken 164 times.
✓ Branch 3 taken 53 times.
|
3324 | return min(a, b) - h*h*h*k*(1.0/6.0); |
129 | } | ||
130 | |||
131 | /*! | ||
132 | * \ingroup Core | ||
133 | * \brief A smoothed maximum function (using cubic interpolation) | ||
134 | * | ||
135 | * \param a The first input value | ||
136 | * \param b The second input value | ||
137 | * \param k The smoothing parameter (k > 0) | ||
138 | */ | ||
139 | template<class Scalar> | ||
140 | Scalar smoothMax(const Scalar a, const Scalar b, const Scalar k) | ||
141 | 4 | { return -smoothMin(-a, -b, k); } | |
142 | |||
143 | /*! | ||
144 | * \ingroup Core | ||
145 | * \brief Invert a linear polynomial analytically | ||
146 | * | ||
147 | * The polynomial is defined as | ||
148 | * \f[ p(x) = a\; x + b \f] | ||
149 | * | ||
150 | * This method Returns the number of solutions which are in the real | ||
151 | * numbers, i.e. 1 except if the slope of the line is 0. | ||
152 | * | ||
153 | * \param sol Container into which the solutions are written | ||
154 | * \param a The coefficiont for the linear term | ||
155 | * \param b The coefficiont for the constant term | ||
156 | */ | ||
157 | template <class Scalar, class SolContainer> | ||
158 | int invertLinearPolynomial(SolContainer &sol, | ||
159 | Scalar a, | ||
160 | Scalar b) | ||
161 | { | ||
162 | 600000 | if (a == 0.0) | |
163 | return 0; | ||
164 | |||
165 | 600000 | sol[0] = -b/a; | |
166 | return 1; | ||
167 | } | ||
168 | |||
169 | /*! | ||
170 | * \ingroup Core | ||
171 | * \brief Invert a quadratic polynomial analytically | ||
172 | * | ||
173 | * The polynomial is defined as | ||
174 | * \f[ p(x) = a\; x^2 + + b\;x + c \f] | ||
175 | * | ||
176 | * This method returns the number of solutions which are in the real | ||
177 | * numbers. The "sol" argument contains the real roots of the parabola | ||
178 | * in order with the smallest root first. | ||
179 | * | ||
180 | * \param sol Container into which the solutions are written | ||
181 | * \param a The coefficiont for the quadratic term | ||
182 | * \param b The coefficiont for the linear term | ||
183 | * \param c The coefficiont for the constant term | ||
184 | */ | ||
185 | template <class Scalar, class SolContainer> | ||
186 | 1200000 | int invertQuadraticPolynomial(SolContainer &sol, | |
187 | Scalar a, | ||
188 | Scalar b, | ||
189 | Scalar c) | ||
190 | { | ||
191 | // check for a line | ||
192 |
2/2✓ Branch 0 taken 600000 times.
✓ Branch 1 taken 600000 times.
|
1200000 | if (a == 0.0) |
193 |
1/2✓ Branch 0 taken 600000 times.
✗ Branch 1 not taken.
|
600000 | return invertLinearPolynomial(sol, b, c); |
194 | |||
195 | // discriminant | ||
196 | 600000 | Scalar Delta = b*b - 4*a*c; | |
197 |
1/2✓ Branch 0 taken 600000 times.
✗ Branch 1 not taken.
|
600000 | if (Delta < 0) |
198 | return 0; // no real roots | ||
199 | |||
200 | using std::sqrt; | ||
201 | 600000 | Delta = sqrt(Delta); | |
202 | 600000 | sol[0] = (- b + Delta)/(2*a); | |
203 | 600000 | sol[1] = (- b - Delta)/(2*a); | |
204 | |||
205 | // sort the result | ||
206 |
2/2✓ Branch 0 taken 400000 times.
✓ Branch 1 taken 200000 times.
|
600000 | if (sol[0] > sol[1]) |
207 | { | ||
208 | using std::swap; | ||
209 | 400000 | swap(sol[0], sol[1]); | |
210 | } | ||
211 | return 2; // two real roots | ||
212 | } | ||
213 | |||
214 | //! \cond false | ||
215 | template <class Scalar, class SolContainer> | ||
216 | 3801593 | void invertCubicPolynomialPostProcess_(SolContainer &sol, | |
217 | int numSol, | ||
218 | Scalar a, Scalar b, Scalar c, Scalar d, | ||
219 | std::size_t numIterations = 1) | ||
220 | { | ||
221 | 28525529 | const auto eval = [&](auto x){ return d + x*(c + x*(b + x*a)); }; | |
222 | 20213642 | const auto evalDeriv = [&](auto x){ return c + x*(2*b + x*3*a); }; | |
223 | |||
224 | // do numIterations Newton iterations on the analytic solution | ||
225 | // and update result if the precision is increased | ||
226 |
2/2✓ Branch 0 taken 8311887 times.
✓ Branch 1 taken 3801593 times.
|
12113480 | for (int i = 0; i < numSol; ++i) |
227 | { | ||
228 | 8311887 | Scalar x = sol[i]; | |
229 | 16623774 | Scalar fCurrent = eval(x); | |
230 | 8311887 | const Scalar fOld = fCurrent; | |
231 |
2/2✓ Branch 0 taken 16412049 times.
✓ Branch 1 taken 8311887 times.
|
24723936 | for (int j = 0; j < numIterations; ++j) |
232 | { | ||
233 | 32824098 | const Scalar fPrime = evalDeriv(x); | |
234 |
1/2✓ Branch 0 taken 16412049 times.
✗ Branch 1 not taken.
|
16412049 | if (fPrime == 0.0) |
235 | break; | ||
236 | 16412049 | x -= fCurrent/fPrime; | |
237 | 16412049 | fCurrent = eval(x); | |
238 | } | ||
239 | |||
240 | using std::abs; | ||
241 |
6/6✓ Branch 0 taken 5208773 times.
✓ Branch 1 taken 3103114 times.
✓ Branch 2 taken 5208773 times.
✓ Branch 3 taken 3103114 times.
✓ Branch 4 taken 5208773 times.
✓ Branch 5 taken 3103114 times.
|
24935661 | if (abs(fCurrent) < abs(fOld)) |
242 | 5208773 | sol[i] = x; | |
243 | } | ||
244 | 3801593 | } | |
245 | //! \endcond | ||
246 | |||
247 | /*! | ||
248 | * \ingroup Core | ||
249 | * \brief Invert a cubic polynomial analytically | ||
250 | * | ||
251 | * The polynomial is defined as | ||
252 | * \f[ p(x) = a\; x^3 + + b\;x^3 + c\;x + d \f] | ||
253 | * | ||
254 | * This method returns the number of solutions which are in the real | ||
255 | * numbers. The "sol" argument contains the real roots of the cubic | ||
256 | * polynomial in order with the smallest root first. | ||
257 | * | ||
258 | * \note The closer the roots are to each other the less | ||
259 | * precise the inversion becomes. Increase number of post-processing iterations for improved results. | ||
260 | * | ||
261 | * \param sol Container into which the solutions are written | ||
262 | * \param a The coefficient for the cubic term | ||
263 | * \param b The coefficient for the quadratic term | ||
264 | * \param c The coefficient for the linear term | ||
265 | * \param d The coefficient for the constant term | ||
266 | * \param numPostProcessIterations The number of iterations to increase precision of the analytical result | ||
267 | */ | ||
268 | template <class Scalar, class SolContainer> | ||
269 | 3801593 | int invertCubicPolynomial(SolContainer *sol, | |
270 | Scalar a, Scalar b, Scalar c, Scalar d, | ||
271 | std::size_t numPostProcessIterations = 1) | ||
272 | { | ||
273 | // reduces to a quadratic polynomial | ||
274 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3801593 times.
|
3801593 | if (a == 0) |
275 | 1200000 | return invertQuadraticPolynomial(sol, b, c, d); | |
276 | |||
277 | // normalize the polynomial | ||
278 | 3801593 | b /= a; | |
279 | 3801593 | c /= a; | |
280 | 3801593 | d /= a; | |
281 | 3801593 | a = 1; | |
282 | |||
283 | // get rid of the quadratic term by substituting x = t - b/3 | ||
284 | 3801593 | Scalar p = c - b*b/3; | |
285 | 3801593 | Scalar q = d + (2*b*b*b - 9*b*c)/27; | |
286 | |||
287 | // now we are at the form t^3 + p*t + q = 0. First we handle some | ||
288 | // special cases to avoid divisions by zero later... | ||
289 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 3801593 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
3801593 | if (p == 0.0 && q == 0.0) { |
290 | // t^3 = 0, i.e. triple root at t = 0 | ||
291 | ✗ | sol[0] = sol[1] = sol[2] = 0.0 - b/3; | |
292 | ✗ | return 3; | |
293 | } | ||
294 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 3801593 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
3801593 | else if (p == 0.0 && q != 0.0) { |
295 | // t^3 + q = 0, | ||
296 | // | ||
297 | // i. e. single real root at t=curt(q) | ||
298 | using std::cbrt; | ||
299 | ✗ | Scalar t = cbrt(q); | |
300 | ✗ | sol[0] = t - b/3; | |
301 | |||
302 | ✗ | return 1; | |
303 | } | ||
304 |
2/4✓ Branch 0 taken 3801593 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3801593 times.
|
3801593 | else if (p != 0.0 && q == 0.0) { |
305 | // t^3 + p*t = 0 = t*(t^2 + p), | ||
306 | // | ||
307 | // i. e. roots at t = 0, t^2 + p = 0 | ||
308 | ✗ | if (p > 0) { | |
309 | ✗ | sol[0] = 0.0 - b/3; | |
310 | ✗ | return 1; // only a single real root at t=0 | |
311 | } | ||
312 | |||
313 | // two additional real roots at t = sqrt(-p) and t = -sqrt(-p) | ||
314 | using std::sqrt; | ||
315 | ✗ | sol[0] = -sqrt(-p) - b/3; | |
316 | ✗ | sol[1] = 0.0 - b/3; | |
317 | ✗ | sol[2] = sqrt(-p) - b/3; | |
318 | |||
319 | ✗ | return 3; | |
320 | } | ||
321 | |||
322 | // At this point | ||
323 | // | ||
324 | // t^3 + p*t + q = 0 | ||
325 | // | ||
326 | // with p != 0 and q != 0 holds. Introducing the variables u and v | ||
327 | // with the properties | ||
328 | // | ||
329 | // u + v = t and 3*u*v + p = 0 | ||
330 | // | ||
331 | // leads to | ||
332 | // | ||
333 | // u^3 + v^3 + q = 0 . | ||
334 | // | ||
335 | // multiplying both sides with u^3 and taking advantage of the | ||
336 | // fact that u*v = -p/3 leads to | ||
337 | // | ||
338 | // u^6 + q*u^3 - p^3/27 = 0 | ||
339 | // | ||
340 | // Now, substituting u^3 = w yields | ||
341 | // | ||
342 | // w^2 + q*w - p^3/27 = 0 | ||
343 | // | ||
344 | // This is a quadratic equation with the solutions | ||
345 | // | ||
346 | // w = -q/2 + sqrt(q^2/4 + p^3/27) and | ||
347 | // w = -q/2 - sqrt(q^2/4 + p^3/27) | ||
348 | // | ||
349 | // Since w is equivalent to u^3 it is sufficient to only look at | ||
350 | // one of the two cases. Then, there are still 2 cases: positive | ||
351 | // and negative discriminant. | ||
352 | 3801593 | Scalar wDisc = q*q/4 + p*p*p/27; | |
353 |
2/2✓ Branch 0 taken 1546446 times.
✓ Branch 1 taken 2255147 times.
|
3801593 | if (wDisc >= 0) { // the positive discriminant case: |
354 | // calculate the cube root of - q/2 + sqrt(q^2/4 + p^3/27) | ||
355 | using std::cbrt; | ||
356 | using std::sqrt; | ||
357 | |||
358 | // Choose the root that is safe against the loss of precision | ||
359 | // that can cause wDisc to be equal to q*q/4 despite p != 0. | ||
360 | // We do not want u to be zero in that case. Mathematically, | ||
361 | // we are happy with either root. | ||
362 | 1546446 | const Scalar u = [&]{ | |
363 |
2/2✓ Branch 0 taken 1504327 times.
✓ Branch 1 taken 42119 times.
|
1546446 | return q > 0 ? cbrt(-0.5*q - sqrt(wDisc)) : cbrt(-0.5*q + sqrt(wDisc)); |
364 | 1546446 | }(); | |
365 | // at this point, u != 0 since p^3 = 0 is necessary in order | ||
366 | // for u = 0 to hold, so | ||
367 | 1546446 | sol[0] = u - p/(3*u) - b/3; | |
368 | // does not produce a division by zero. the remaining two | ||
369 | // roots of u are rotated by +- 2/3*pi in the complex plane | ||
370 | // and thus not considered here | ||
371 | 1546446 | invertCubicPolynomialPostProcess_(sol, 1, a, b, c, d, numPostProcessIterations); | |
372 | 1546446 | return 1; | |
373 | } | ||
374 | else { // the negative discriminant case: | ||
375 | 2255147 | Scalar uCubedRe = - q/2; | |
376 | using std::sqrt; | ||
377 | 2255147 | Scalar uCubedIm = sqrt(-wDisc); | |
378 | // calculate the cube root of - q/2 + sqrt(q^2/4 + p^3/27) | ||
379 | using std::cbrt; | ||
380 | 2255147 | Scalar uAbs = cbrt(sqrt(uCubedRe*uCubedRe + uCubedIm*uCubedIm)); | |
381 | using std::atan2; | ||
382 | 2255147 | Scalar phi = atan2(uCubedIm, uCubedRe)/3; | |
383 | |||
384 | // calculate the length and the angle of the primitive root | ||
385 | |||
386 | // with the definitions from above it follows that | ||
387 | // | ||
388 | // x = u - p/(3*u) - b/3 | ||
389 | // | ||
390 | // where x and u are complex numbers. Rewritten in polar form | ||
391 | // this is equivalent to | ||
392 | // | ||
393 | // x = |u|*e^(i*phi) - p*e^(-i*phi)/(3*|u|) - b/3 . | ||
394 | // | ||
395 | // Factoring out the e^ terms and subtracting the additional | ||
396 | // terms, yields | ||
397 | // | ||
398 | // x = (e^(i*phi) + e^(-i*phi))*(|u| - p/(3*|u|)) - y - b/3 | ||
399 | // | ||
400 | // with | ||
401 | // | ||
402 | // y = - |u|*e^(-i*phi) + p*e^(i*phi)/(3*|u|) . | ||
403 | // | ||
404 | // The crucial observation is the fact that y is the conjugate | ||
405 | // of - x + b/3. This means that after taking advantage of the | ||
406 | // relation | ||
407 | // | ||
408 | // e^(i*phi) + e^(-i*phi) = 2*cos(phi) | ||
409 | // | ||
410 | // the equation | ||
411 | // | ||
412 | // x = 2*cos(phi)*(|u| - p / (3*|u|)) - conj(x) - 2*b/3 | ||
413 | // | ||
414 | // holds. Since |u|, p, b and cos(phi) are real numbers, it | ||
415 | // follows that Im(x) = - Im(x) and thus Im(x) = 0. This | ||
416 | // implies | ||
417 | // | ||
418 | // Re(x) = x = cos(phi)*(|u| - p / (3*|u|)) - b/3 . | ||
419 | // | ||
420 | // Considering the fact that u is a cubic root, we have three | ||
421 | // values for phi which differ by 2/3*pi. This allows to | ||
422 | // calculate the three real roots of the polynomial: | ||
423 |
2/2✓ Branch 0 taken 6765441 times.
✓ Branch 1 taken 2255147 times.
|
9020588 | for (int i = 0; i < 3; ++i) { |
424 | using std::cos; | ||
425 | 6765441 | sol[i] = cos(phi)*(uAbs - p/(3*uAbs)) - b/3; | |
426 | 6765441 | phi += 2*M_PI/3; | |
427 | } | ||
428 | |||
429 | // post process the obtained solution to increase numerical | ||
430 | // precision | ||
431 | 2255147 | invertCubicPolynomialPostProcess_(sol, 3, a, b, c, d, numPostProcessIterations); | |
432 | |||
433 | // sort the result | ||
434 | using std::sort; | ||
435 | 4510294 | sort(sol, sol + 3); | |
436 | |||
437 | 2255147 | return 3; | |
438 | } | ||
439 | |||
440 | // NOT REACHABLE! | ||
441 | return 0; | ||
442 | } | ||
443 | |||
444 | /*! | ||
445 | * \ingroup Core | ||
446 | * \brief Comparison of two position vectors | ||
447 | * | ||
448 | * Compares an current position vector with a reference vector, and returns true | ||
449 | * if the position vector is larger. | ||
450 | * "Larger" in this case means that all the entries of each spatial dimension are | ||
451 | * larger compared to the reference vector. | ||
452 | * | ||
453 | * \param pos Vector holding the current Position that is to be checked | ||
454 | * \param smallerVec Reference vector, holding the minimum values for comparison. | ||
455 | */ | ||
456 | template <class Scalar, int dim> | ||
457 | bool isLarger(const Dune::FieldVector<Scalar, dim> &pos, | ||
458 | const Dune::FieldVector<Scalar, dim> &smallerVec) | ||
459 | { | ||
460 | for (int i=0; i < dim; i++) | ||
461 | { | ||
462 | if (pos[i]<= smallerVec[i]) | ||
463 | { | ||
464 | return false; | ||
465 | } | ||
466 | } | ||
467 | return true; | ||
468 | } | ||
469 | |||
470 | /*! | ||
471 | * \ingroup Core | ||
472 | * \brief Comparison of two position vectors | ||
473 | * | ||
474 | * Compares an current position vector with a reference vector, and returns true | ||
475 | * if the position vector is smaller. | ||
476 | * "Smaller" in this case means that all the entries of each spatial dimension are | ||
477 | * smaller in comparison with the reference vector. | ||
478 | * | ||
479 | * \param pos Vector holding the current Position that is to be checked | ||
480 | * \param largerVec Reference vector, holding the maximum values for comparison. | ||
481 | */ | ||
482 | template <class Scalar, int dim> | ||
483 | bool isSmaller(const Dune::FieldVector<Scalar, dim> &pos, | ||
484 | const Dune::FieldVector<Scalar, dim> &largerVec) | ||
485 | { | ||
486 | for (int i=0; i < dim; i++) | ||
487 | { | ||
488 | if (pos[i]>= largerVec[i]) | ||
489 | { | ||
490 | return false; | ||
491 | } | ||
492 | } | ||
493 | return true; | ||
494 | } | ||
495 | |||
496 | /*! | ||
497 | * \ingroup Core | ||
498 | * \brief Comparison of three position vectors | ||
499 | * | ||
500 | * Compares an current position vector with two reference vector, and returns true | ||
501 | * if the position vector lies in between them. | ||
502 | * "Between" in this case means that all the entries of each spatial dimension are | ||
503 | * smaller in comparison with the larger reference vector as well as larger compared | ||
504 | * to the smaller reference. | ||
505 | * This is comfortable to cheack weather the current position is located inside or | ||
506 | * outside of a lense with different properties. | ||
507 | * | ||
508 | * \param pos Vector holding the current Position that is to be checked | ||
509 | * \param smallerVec Reference vector, holding the minimum values for comparison. | ||
510 | * \param largerVec Reference vector, holding the maximum values for comparison. | ||
511 | */ | ||
512 | template <class Scalar, int dim> | ||
513 | bool isBetween(const Dune::FieldVector<Scalar, dim> &pos, | ||
514 | const Dune::FieldVector<Scalar, dim> &smallerVec, | ||
515 | const Dune::FieldVector<Scalar, dim> &largerVec) | ||
516 | { | ||
517 | if (isLarger(pos, smallerVec) && isSmaller(pos, largerVec)) | ||
518 | { | ||
519 | return true; | ||
520 | } | ||
521 | else | ||
522 | return false; | ||
523 | } | ||
524 | |||
525 | //! forward declaration of the linear interpolation policy (default) | ||
526 | namespace InterpolationPolicy { struct Linear; } | ||
527 | |||
528 | /*! | ||
529 | * \ingroup Core | ||
530 | * \brief a generic function to interpolate given a set of parameters and an interpolation point | ||
531 | * \param params the parameters used for interpolation (depends on the policy used) | ||
532 | * \param ip the interpolation point | ||
533 | */ | ||
534 | template <class Policy = InterpolationPolicy::Linear, class Scalar, class ... Parameter> | ||
535 | Scalar interpolate(Scalar ip, Parameter&& ... params) | ||
536 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
51291200 | { return Policy::interpolate(ip, std::forward<Parameter>(params) ...); } |
537 | |||
538 | /*! | ||
539 | * \ingroup Core | ||
540 | * \brief Interpolation policies | ||
541 | */ | ||
542 | namespace InterpolationPolicy { | ||
543 | |||
544 | /*! | ||
545 | * \ingroup Core | ||
546 | * \brief interpolate linearly between two given values | ||
547 | */ | ||
548 | struct Linear | ||
549 | { | ||
550 | /*! | ||
551 | * \brief interpolate linearly between two given values | ||
552 | * \param ip the interpolation point in [0,1] | ||
553 | * \param params array with the lower and upper bound | ||
554 | */ | ||
555 | template<class Scalar> | ||
556 | static constexpr Scalar interpolate(Scalar ip, const std::array<Scalar, 2>& params) | ||
557 | { | ||
558 | 48851177 | return params[0]*(1.0 - ip) + params[1]*ip; | |
559 | } | ||
560 | }; | ||
561 | |||
562 | /*! | ||
563 | * \ingroup Core | ||
564 | * \brief interpolate linearly in a piecewise linear function (tabularized function) | ||
565 | */ | ||
566 | struct LinearTable | ||
567 | { | ||
568 | /*! | ||
569 | * \brief interpolate linearly in a piecewise linear function (tabularized function) | ||
570 | * \param ip the interpolation point | ||
571 | * \param range positions of values | ||
572 | * \param values values to interpolate from | ||
573 | * \note if the interpolation point is out of bounds this will return the bounds | ||
574 | */ | ||
575 | template<class Scalar, class RandomAccessContainer0, class RandomAccessContainer1> | ||
576 | 51292377 | static constexpr Scalar interpolate(Scalar ip, const RandomAccessContainer0& range, const RandomAccessContainer1& values) | |
577 | { | ||
578 | // check bounds | ||
579 |
6/6✓ Branch 0 taken 2440317 times.
✓ Branch 1 taken 48852060 times.
✓ Branch 2 taken 2440317 times.
✓ Branch 3 taken 48852060 times.
✓ Branch 4 taken 2440317 times.
✓ Branch 5 taken 48852060 times.
|
153877131 | if (ip > range[range.size()-1]) return values[values.size()-1]; |
580 |
2/2✓ Branch 0 taken 882 times.
✓ Branch 1 taken 48851178 times.
|
48852060 | if (ip < range[0]) return values[0]; |
581 | |||
582 | // if we are within bounds find the index of the lower bound | ||
583 |
4/4✓ Branch 0 taken 1 times.
✓ Branch 1 taken 48851177 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 48851177 times.
|
97702356 | const auto lookUpIndex = std::distance(range.begin(), std::lower_bound(range.begin(), range.end(), ip)); |
584 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 48851177 times.
|
48851178 | if (lookUpIndex == 0) |
585 | 1 | return values[0]; | |
586 | |||
587 | 97702354 | const auto ipLinear = (ip - range[lookUpIndex-1])/(range[lookUpIndex] - range[lookUpIndex-1]); | |
588 | 195404708 | return Dumux::interpolate<Linear>(ipLinear, std::array<Scalar, 2>{{values[lookUpIndex-1], values[lookUpIndex]}}); | |
589 | } | ||
590 | |||
591 | template<class Scalar, class RandomAccessContainer> | ||
592 | static constexpr Scalar interpolate(Scalar ip, const std::pair<RandomAccessContainer, RandomAccessContainer>& table) | ||
593 | { | ||
594 | 2352 | const auto& [range, values] = table; | |
595 | 1176 | return interpolate(ip, range, values); | |
596 | } | ||
597 | }; | ||
598 | |||
599 | } // end namespace InterpolationPolicy | ||
600 | |||
601 | /*! | ||
602 | * \ingroup Core | ||
603 | * \brief Generates linearly spaced vectors | ||
604 | * | ||
605 | * \param begin The first value in the vector | ||
606 | * \param end The last value in the vector | ||
607 | * \param samples The size of the vector | ||
608 | * \param endPoint if the range is including the interval's end point or not | ||
609 | */ | ||
610 | template <class Scalar> | ||
611 | 50 | std::vector<Scalar> linspace(const Scalar begin, const Scalar end, | |
612 | std::size_t samples, | ||
613 | bool endPoint = true) | ||
614 | { | ||
615 | using std::max; | ||
616 |
1/2✓ Branch 0 taken 50 times.
✗ Branch 1 not taken.
|
50 | samples = max(std::size_t{2}, samples); // only makes sense for 2 or more samples |
617 |
2/2✓ Branch 0 taken 49 times.
✓ Branch 1 taken 1 times.
|
50 | const Scalar divisor = endPoint ? samples-1 : samples; |
618 | 50 | const Scalar delta = (end-begin)/divisor; | |
619 | 100 | std::vector<Scalar> vec(samples); | |
620 |
2/2✓ Branch 0 taken 8048755 times.
✓ Branch 1 taken 50 times.
|
8048805 | for (std::size_t i = 0; i < samples; ++i) |
621 | 16097510 | vec[i] = begin + i*delta; | |
622 | 50 | return vec; | |
623 | } | ||
624 | |||
625 | |||
626 | /*! | ||
627 | * \ingroup Core | ||
628 | * \brief Evaluates the Antoine equation used to calculate the vapour | ||
629 | * pressure of various liquids. | ||
630 | * | ||
631 | * See http://en.wikipedia.org/wiki/Antoine_equation | ||
632 | * | ||
633 | * \param temperature The temperature [K] of the fluid | ||
634 | * \param A The first coefficient for the Antoine equation | ||
635 | * \param B The first coefficient for the Antoine equation | ||
636 | * \param C The first coefficient for the Antoine equation | ||
637 | */ | ||
638 | template <class Scalar> | ||
639 | Scalar antoine(Scalar temperature, | ||
640 | Scalar A, | ||
641 | Scalar B, | ||
642 | Scalar C) | ||
643 | { | ||
644 | const Scalar ln10 = 2.3025850929940459; | ||
645 | using std::exp; | ||
646 | return exp(ln10*(A - B/(C + temperature))); | ||
647 | } | ||
648 | |||
649 | /*! | ||
650 | * \ingroup Core | ||
651 | * \brief Sign or signum function. | ||
652 | * | ||
653 | * Returns 1 for a positive argument. | ||
654 | * Returns -1 for a negative argument. | ||
655 | * Returns 0 if the argument is zero. | ||
656 | */ | ||
657 | template<class ValueType> | ||
658 | ✗ | constexpr int sign(const ValueType& value) noexcept | |
659 | { | ||
660 |
13/13✓ Branch 0 taken 26 times.
✓ Branch 1 taken 158776510 times.
✓ Branch 2 taken 101242356 times.
✓ Branch 3 taken 43613912 times.
✓ Branch 4 taken 124074537 times.
✓ Branch 5 taken 69538 times.
✓ Branch 6 taken 146894738 times.
✓ Branch 7 taken 252987 times.
✓ Branch 8 taken 56753133 times.
✓ Branch 9 taken 1152762 times.
✓ Branch 10 taken 1835 times.
✓ Branch 11 taken 1003572 times.
✓ Branch 12 taken 15 times.
|
1169128681 | return (ValueType(0) < value) - (value < ValueType(0)); |
661 | } | ||
662 | |||
663 | /*! | ||
664 | * \ingroup Core | ||
665 | * \brief Cross product of two vectors in three-dimensional Euclidean space | ||
666 | * | ||
667 | * \param vec1 The first vector | ||
668 | * \param vec2 The second vector | ||
669 | */ | ||
670 | template <class Scalar> | ||
671 | 39345780 | Dune::FieldVector<Scalar, 3> crossProduct(const Dune::FieldVector<Scalar, 3> &vec1, | |
672 | const Dune::FieldVector<Scalar, 3> &vec2) | ||
673 | { | ||
674 | 157383120 | return {vec1[1]*vec2[2]-vec1[2]*vec2[1], | |
675 | 118037340 | vec1[2]*vec2[0]-vec1[0]*vec2[2], | |
676 | 236074680 | vec1[0]*vec2[1]-vec1[1]*vec2[0]}; | |
677 | } | ||
678 | |||
679 | /*! | ||
680 | * \ingroup Core | ||
681 | * \brief Cross product of two vectors in two-dimensional Euclidean space retuning scalar | ||
682 | * | ||
683 | * \param vec1 The first vector | ||
684 | * \param vec2 The second vector | ||
685 | */ | ||
686 | template <class Scalar> | ||
687 | Scalar crossProduct(const Dune::FieldVector<Scalar, 2> &vec1, | ||
688 | const Dune::FieldVector<Scalar, 2> &vec2) | ||
689 |
20/20✓ Branch 0 taken 13628205 times.
✓ Branch 1 taken 21035116 times.
✓ Branch 2 taken 13628205 times.
✓ Branch 3 taken 21035116 times.
✓ Branch 4 taken 13628205 times.
✓ Branch 5 taken 21035116 times.
✓ Branch 6 taken 13628205 times.
✓ Branch 7 taken 21035116 times.
✓ Branch 8 taken 13628205 times.
✓ Branch 9 taken 21035116 times.
✓ Branch 10 taken 200 times.
✓ Branch 11 taken 200 times.
✓ Branch 12 taken 200 times.
✓ Branch 13 taken 200 times.
✓ Branch 14 taken 200 times.
✓ Branch 15 taken 200 times.
✓ Branch 16 taken 200 times.
✓ Branch 17 taken 200 times.
✓ Branch 18 taken 200 times.
✓ Branch 19 taken 200 times.
|
361779670 | { return vec1[0]*vec2[1]-vec1[1]*vec2[0]; } |
690 | |||
691 | /*! | ||
692 | * \ingroup Core | ||
693 | * \brief Triple product of three vectors in three-dimensional Euclidean space retuning scalar | ||
694 | * | ||
695 | * \param vec1 The first vector | ||
696 | * \param vec2 The second vector | ||
697 | * \param vec3 The third vector | ||
698 | */ | ||
699 | template <class Scalar> | ||
700 | Scalar tripleProduct(const Dune::FieldVector<Scalar, 3> &vec1, | ||
701 | const Dune::FieldVector<Scalar, 3> &vec2, | ||
702 | const Dune::FieldVector<Scalar, 3> &vec3) | ||
703 | 10272607 | { return crossProduct<Scalar>(vec1, vec2)*vec3; } | |
704 | |||
705 | /*! | ||
706 | * \ingroup Core | ||
707 | * \brief Transpose a FieldMatrix | ||
708 | * | ||
709 | * \param M The matrix to be transposed | ||
710 | */ | ||
711 | template <class Scalar, int m, int n> | ||
712 | Dune::FieldMatrix<Scalar, n, m> getTransposed(const Dune::FieldMatrix<Scalar, m, n>& M) | ||
713 | { | ||
714 | 2185722 | Dune::FieldMatrix<Scalar, n, m> T; | |
715 |
6/6✓ Branch 0 taken 1753604 times.
✓ Branch 1 taken 876802 times.
✓ Branch 2 taken 38758 times.
✓ Branch 3 taken 12920 times.
✓ Branch 4 taken 2592000 times.
✓ Branch 5 taken 1296000 times.
|
6570084 | for (std::size_t i = 0; i < m; ++i) |
716 |
6/6✓ Branch 0 taken 3507208 times.
✓ Branch 1 taken 1753604 times.
✓ Branch 2 taken 116270 times.
✓ Branch 3 taken 38758 times.
✓ Branch 4 taken 5184000 times.
✓ Branch 5 taken 2592000 times.
|
13191840 | for (std::size_t j = 0; j < n; ++j) |
717 | 44037390 | T[j][i] = M[i][j]; | |
718 | |||
719 | return T; | ||
720 | } | ||
721 | |||
722 | /*! | ||
723 | * \ingroup Core | ||
724 | * \brief Transpose a DynamicMatrix | ||
725 | * | ||
726 | * \param M The matrix to be transposed | ||
727 | */ | ||
728 | template <class Scalar> | ||
729 | Dune::DynamicMatrix<Scalar> getTransposed(const Dune::DynamicMatrix<Scalar>& M) | ||
730 | { | ||
731 | std::size_t rows_T = M.M(); | ||
732 | std::size_t cols_T = M.N(); | ||
733 | |||
734 | Dune::DynamicMatrix<Scalar> M_T(rows_T, cols_T, 0.0); | ||
735 | |||
736 | for (std::size_t i = 0; i < rows_T; ++i) | ||
737 | for (std::size_t j = 0; j < cols_T; ++j) | ||
738 | M_T[i][j] = M[j][i]; | ||
739 | |||
740 | return M_T; | ||
741 | } | ||
742 | |||
743 | /*! | ||
744 | * \ingroup Core | ||
745 | * \brief Multiply two dynamic matrices | ||
746 | * | ||
747 | * \param M1 The first dynamic matrix | ||
748 | * \param M2 The second dynamic matrix (to be multiplied to M1 from the right side) | ||
749 | */ | ||
750 | template <class Scalar> | ||
751 | 52461048 | Dune::DynamicMatrix<Scalar> multiplyMatrices(const Dune::DynamicMatrix<Scalar> &M1, | |
752 | const Dune::DynamicMatrix<Scalar> &M2) | ||
753 | { | ||
754 | using size_type = typename Dune::DynamicMatrix<Scalar>::size_type; | ||
755 | 52461048 | const size_type rows = M1.N(); | |
756 | 52461048 | const size_type cols = M2.M(); | |
757 | |||
758 | DUNE_ASSERT_BOUNDS(M1.M() == M2.N()); | ||
759 | |||
760 | 52461048 | Dune::DynamicMatrix<Scalar> result(rows, cols, 0.0); | |
761 |
2/2✓ Branch 1 taken 207548397 times.
✓ Branch 2 taken 52461048 times.
|
260009445 | for (size_type i = 0; i < rows; i++) |
762 |
2/2✓ Branch 0 taken 822834623 times.
✓ Branch 1 taken 207548397 times.
|
1030383020 | for (size_type j = 0; j < cols; j++) |
763 |
2/2✓ Branch 1 taken 3395029373 times.
✓ Branch 2 taken 822834623 times.
|
4217863996 | for (size_type k = 0; k < M1.M(); k++) |
764 | 23765205611 | result[i][j] += M1[i][k]*M2[k][j]; | |
765 | |||
766 | 52461048 | return result; | |
767 | } | ||
768 | |||
769 | /*! | ||
770 | * \ingroup Core | ||
771 | * \brief Multiply two field matrices | ||
772 | * | ||
773 | * \param M1 The first field matrix | ||
774 | * \param M2 The second field matrix (to be multiplied to M1 from the right side) | ||
775 | */ | ||
776 | template <class Scalar, int rows1, int cols1, int cols2> | ||
777 | 706929 | Dune::FieldMatrix<Scalar, rows1, cols2> multiplyMatrices(const Dune::FieldMatrix<Scalar, rows1, cols1> &M1, | |
778 | const Dune::FieldMatrix<Scalar, cols1, cols2> &M2) | ||
779 | { | ||
780 | using size_type = typename Dune::FieldMatrix<Scalar, rows1, cols2>::size_type; | ||
781 | |||
782 | 706929 | Dune::FieldMatrix<Scalar, rows1, cols2> result(0.0); | |
783 |
2/2✓ Branch 0 taken 2827716 times.
✓ Branch 1 taken 706929 times.
|
3534645 | for (size_type i = 0; i < rows1; i++) |
784 |
2/2✓ Branch 0 taken 11310864 times.
✓ Branch 1 taken 2827716 times.
|
14138580 | for (size_type j = 0; j < cols2; j++) |
785 |
2/2✓ Branch 0 taken 45243456 times.
✓ Branch 1 taken 11310864 times.
|
56554320 | for (size_type k = 0; k < cols1; k++) |
786 | 316704192 | result[i][j] += M1[i][k]*M2[k][j]; | |
787 | |||
788 | 706929 | return result; | |
789 | } | ||
790 | |||
791 | |||
792 | /*! | ||
793 | * \ingroup Core | ||
794 | * \brief Trace of a dense matrix | ||
795 | * | ||
796 | * \param M The dense matrix | ||
797 | */ | ||
798 | template <class MatrixType> | ||
799 | typename Dune::DenseMatrix<MatrixType>::field_type | ||
800 | trace(const Dune::DenseMatrix<MatrixType>& M) | ||
801 | { | ||
802 | 1 | const auto rows = M.N(); | |
803 | DUNE_ASSERT_BOUNDS(rows == M.M()); // rows == cols | ||
804 | |||
805 | using MatType = Dune::DenseMatrix<MatrixType>; | ||
806 | 1 | typename MatType::field_type trace = 0.0; | |
807 | |||
808 |
4/4✓ Branch 0 taken 1204579 times.
✓ Branch 1 taken 3585729 times.
✓ Branch 2 taken 651 times.
✓ Branch 3 taken 1297 times.
|
4792255 | for (typename MatType::size_type i = 0; i < rows; ++i) |
809 | 10761090 | trace += M[i][i]; | |
810 | |||
811 | return trace; | ||
812 | } | ||
813 | |||
814 | /*! | ||
815 | * \ingroup Core | ||
816 | * \brief Returns the result of the projection of | ||
817 | * a vector v with a Matrix M. | ||
818 | * | ||
819 | * Note: We use DenseVector and DenseMatrix here so that | ||
820 | * it can be used with the statically and dynamically | ||
821 | * allocated Dune Vectors/Matrices. Size mismatch | ||
822 | * assertions are done in the respective Dune classes. | ||
823 | * | ||
824 | * \param M The matrix | ||
825 | * \param v The vector | ||
826 | */ | ||
827 | template <class MAT, class V> | ||
828 | typename Dune::DenseVector<V>::derived_type | ||
829 | 85257460 | mv(const Dune::DenseMatrix<MAT>& M, | |
830 | const Dune::DenseVector<V>& v) | ||
831 | { | ||
832 |
2/4✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
|
93716269 | typename Dune::DenseVector<V>::derived_type res(v); |
833 | 93716262 | M.mv(v, res); | |
834 | 85257460 | return res; | |
835 | } | ||
836 | |||
837 | /*! | ||
838 | * \ingroup Core | ||
839 | * \brief Returns the result of a vector v multiplied by a scalar m. | ||
840 | * | ||
841 | * Note: We use DenseVector and DenseMatrix here so that | ||
842 | * it can be used with the statically and dynamically | ||
843 | * allocated Dune Vectors/Matrices. Size mismatch | ||
844 | * assertions are done in the respective Dune classes. | ||
845 | * | ||
846 | * \note We need the enable_if to make sure that only Scalars | ||
847 | * fit here. Matrix types are forced to use the above | ||
848 | * mv(DenseMatrix, DenseVector) instead. | ||
849 | * | ||
850 | * \param m The scale factor | ||
851 | * \param v The vector | ||
852 | */ | ||
853 | |||
854 | template <class FieldScalar, class V> | ||
855 | typename std::enable_if_t<Dune::IsNumber<FieldScalar>::value, | ||
856 | typename Dune::DenseVector<V>::derived_type> | ||
857 | 1764 | mv(const FieldScalar m, const Dune::DenseVector<V>& v) | |
858 | { | ||
859 | 1766 | typename Dune::DenseVector<V>::derived_type res(v); | |
860 | 1764 | res *= m; | |
861 | 1764 | return res; | |
862 | } | ||
863 | |||
864 | /*! | ||
865 | * \ingroup Core | ||
866 | * \brief Evaluates the scalar product of a vector v2, projected by | ||
867 | * a matrix M, with a vector v1. | ||
868 | * | ||
869 | * Note: We use DenseVector and DenseMatrix here so that | ||
870 | * it can be used with the statically and dynamically | ||
871 | * allocated Dune Vectors/Matrices. Size mismatch | ||
872 | * assertions are done in the respective Dune classes. | ||
873 | * | ||
874 | * \param v1 The first vector | ||
875 | * \param M The matrix | ||
876 | * \param v2 The second vector | ||
877 | */ | ||
878 | template <class V1, class MAT, class V2> | ||
879 | typename Dune::DenseMatrix<MAT>::value_type | ||
880 | 6 | vtmv(const Dune::DenseVector<V1>& v1, | |
881 | const Dune::DenseMatrix<MAT>& M, | ||
882 | const Dune::DenseVector<V2>& v2) | ||
883 | { | ||
884 |
5/10✓ Branch 1 taken 3988534 times.
✓ Branch 2 taken 83478 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 1 times.
|
104267742 | return v1*mv(M, v2); |
885 | } | ||
886 | |||
887 | /*! | ||
888 | * \ingroup Core | ||
889 | * \brief Evaluates the scalar product of a vector v2, scaled by | ||
890 | * a scalar m, with a vector v1. | ||
891 | * | ||
892 | * Note: We use DenseVector and DenseMatrix here so that | ||
893 | * it can be used with the statically and dynamically | ||
894 | * allocated Dune Vectors/Matrices. Size mismatch | ||
895 | * assertions are done in the respective Dune classes. | ||
896 | * | ||
897 | * \note We need the enable_if to make sure that only Scalars | ||
898 | * fit here. Matrix types are forced to use the above | ||
899 | * vtmv(DenseVector, DenseMatrix, DenseVector) instead. | ||
900 | * | ||
901 | * \param v1 The first vector | ||
902 | * \param m The scale factor | ||
903 | * \param v2 The second vector | ||
904 | */ | ||
905 | |||
906 | template <class V1, class FieldScalar, class V2> | ||
907 | typename std::enable_if_t<Dune::IsNumber<FieldScalar>::value, FieldScalar> | ||
908 | vtmv(const Dune::DenseVector<V1>& v1, | ||
909 | const FieldScalar m, | ||
910 | const Dune::DenseVector<V2>& v2) | ||
911 | { | ||
912 |
8/12✓ Branch 0 taken 466732665 times.
✓ Branch 1 taken 7888094 times.
✓ Branch 2 taken 154976125 times.
✓ Branch 3 taken 1679216 times.
✓ Branch 4 taken 875901 times.
✓ Branch 5 taken 1083504 times.
✓ Branch 6 taken 770556 times.
✓ Branch 7 taken 49896 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
|
2759015103 | return m*(v1*v2); |
913 | } | ||
914 | |||
915 | /*! | ||
916 | * \ingroup Core | ||
917 | * \brief Returns the [intercept, slope] of the regression line | ||
918 | * fitted to a set of (x, y) data points. | ||
919 | * | ||
920 | * Note: We use least-square regression method to find | ||
921 | * the regression line. | ||
922 | * | ||
923 | * \param x x-values of the data set | ||
924 | * \param y y-values of the data set | ||
925 | */ | ||
926 | template <class Scalar> | ||
927 | 3 | std::array<Scalar, 2> linearRegression(const std::vector<Scalar>& x, | |
928 | const std::vector<Scalar>& y) | ||
929 | { | ||
930 |
3/6✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
|
9 | if (x.size() != y.size()) |
931 | ✗ | DUNE_THROW(Dune::InvalidStateException, "x and y array must have the same length."); | |
932 | |||
933 | 3 | const Scalar averageX = std::accumulate(x.begin(), x.end(), 0.0)/x.size(); | |
934 | 9 | const Scalar averageY = std::accumulate(y.begin(), y.end(), 0.0)/y.size(); | |
935 | |||
936 | // calculate temporary variables necessary for slope computation | ||
937 | 12 | const Scalar numerator = std::inner_product( | |
938 | x.begin(), x.end(), y.begin(), 0.0, std::plus<Scalar>(), | ||
939 | 208 | [&](auto xx, auto yy) { return (xx - averageX) * (yy - averageY); } | |
940 | ); | ||
941 | 3 | const Scalar denominator = std::inner_product( | |
942 | x.begin(), x.end(), x.begin(), 0.0, std::plus<Scalar>(), | ||
943 | 208 | [&](auto xx, auto yy) { return (xx - averageX) * (yy - averageX); } | |
944 | ); | ||
945 | |||
946 | // compute slope and intercept of the regression line | ||
947 | 3 | const Scalar slope = numerator / denominator; | |
948 | 3 | const Scalar intercept = averageY - slope * averageX; | |
949 | |||
950 | 3 | return {intercept, slope}; | |
951 | } | ||
952 | |||
953 | } // end namespace Dumux | ||
954 | |||
955 | #endif | ||
956 |