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 | // SPDX-FileCopyrightInfo: Copyright John D. Cook | ||
7 | // SPDX-License-Identifier: BSD-2-Clause | ||
8 | // Modified after the original version at | ||
9 | // https://www.codeproject.com/Articles/31550/Fast-Numerical-Integration licensed under | ||
10 | // BSD-2-Clause. All Changes are licensed under GPL-3.0-or-later. | ||
11 | // | ||
12 | /***************************************************************************** | ||
13 | * This version is modified after the original version by John D. Cook * | ||
14 | * see https://www.codeproject.com/ * | ||
15 | * Articles/31550/Fast-Numerical-Integration * | ||
16 | * which is licensed under BSD-2-clause, which reads as follows: * | ||
17 | * Copyright John D. Cook * | ||
18 | * * | ||
19 | * Redistribution and use in source and binary forms, with or without * | ||
20 | * modification, are permitted provided that the following * | ||
21 | * conditions are met: * | ||
22 | * 1. Redistributions of source code must retain the above * | ||
23 | * copyright notice, this list of conditions * | ||
24 | and the following disclaimer. * | ||
25 | * 2. Redistributions in binary form must reproduce the above * | ||
26 | * copyright notice, this list of conditions * | ||
27 | * and the following disclaimer * | ||
28 | * in the documentation and/or other materials * | ||
29 | * provided with the distribution. * | ||
30 | * * | ||
31 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * | ||
32 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * | ||
33 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * | ||
34 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * | ||
35 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * | ||
36 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * | ||
37 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * | ||
38 | * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * | ||
39 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * | ||
40 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * | ||
41 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * | ||
42 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * | ||
43 | * * | ||
44 | *****************************************************************************/ | ||
45 | /*! | ||
46 | * \file | ||
47 | * \ingroup Core | ||
48 | * \brief A double exponential integrator | ||
49 | */ | ||
50 | #ifndef DUMUX_COMMON_DOUBLEEXP_INTEGRATOR_HH | ||
51 | #define DUMUX_COMMON_DOUBLEEXP_INTEGRATOR_HH | ||
52 | |||
53 | #include <cmath> | ||
54 | #include <limits> | ||
55 | #include <type_traits> | ||
56 | |||
57 | #include <dumux/common/doubleexpintegrationconstants.hh> | ||
58 | |||
59 | namespace Dumux { | ||
60 | |||
61 | /*! | ||
62 | * \brief Numerical integration in one dimension using the double exponential method of M. Mori. | ||
63 | */ | ||
64 | template<class Scalar> | ||
65 | class DoubleExponentialIntegrator | ||
66 | { | ||
67 | public: | ||
68 | /*! | ||
69 | * \brief Integrate an analytic function over a finite interval | ||
70 | * \param f the integrand (invocable with a single scalar) | ||
71 | * \param a lower limit of integration | ||
72 | * \param b upper limit of integration | ||
73 | * \param targetAbsoluteError desired bound on error | ||
74 | * \param numFunctionEvaluations number of function evaluations used | ||
75 | * \param errorEstimate estimate for error in integration | ||
76 | * \return The value of the integral | ||
77 | */ | ||
78 | template<class Function, | ||
79 | typename std::enable_if_t<std::is_invocable_r_v<Scalar, Function, Scalar>>...> | ||
80 | ✗ | static Scalar integrate(const Function& f, | |
81 | const Scalar a, | ||
82 | const Scalar b, | ||
83 | const Scalar targetAbsoluteError, | ||
84 | int& numFunctionEvaluations, | ||
85 | Scalar& errorEstimate) | ||
86 | { | ||
87 | // Apply the linear change of variables x = ct + d | ||
88 | // $$\int_a^b f(x) dx = c \int_{-1}^1 f( ct + d ) dt$$ | ||
89 | // c = (b-a)/2, d = (a+b)/2 | ||
90 | |||
91 | 2093485 | const Scalar c = 0.5*(b - a); | |
92 | 2093485 | const Scalar d = 0.5*(a + b); | |
93 | 2093485 | return integrateCore_(f, c, d, targetAbsoluteError, numFunctionEvaluations, errorEstimate, | |
94 | ✗ | doubleExponentialIntegrationAbcissas, doubleExponentialIntegrationWeights); | |
95 | } | ||
96 | |||
97 | /*! | ||
98 | * \brief Integrate an analytic function over a finite interval. | ||
99 | * \note This version overloaded to not require arguments passed in for | ||
100 | * function evaluation counts or error estimates. | ||
101 | * \param f the integrand (invocable with a single scalar) | ||
102 | * \param a lower integral bound | ||
103 | * \param b upper integral bound | ||
104 | * \param targetAbsoluteError desired absolute error in the result | ||
105 | * \return The value of the integral. | ||
106 | */ | ||
107 | template<class Function, | ||
108 | typename std::enable_if_t<std::is_invocable_r_v<Scalar, Function, Scalar>>...> | ||
109 | 2123726 | static Scalar integrate(const Function& f, | |
110 | const Scalar a, | ||
111 | const Scalar b, | ||
112 | const Scalar targetAbsoluteError) | ||
113 | { | ||
114 | int numFunctionEvaluations; | ||
115 | Scalar errorEstimate; | ||
116 | 2123726 | return integrate(f, a, b, targetAbsoluteError, numFunctionEvaluations, errorEstimate); | |
117 | } | ||
118 | |||
119 | private: | ||
120 | // Integrate f(cx + d) with the given integration constants | ||
121 | template<class Function> | ||
122 | 2093485 | static Scalar integrateCore_(const Function& f, | |
123 | const Scalar c, // slope of change of variables | ||
124 | const Scalar d, // intercept of change of variables | ||
125 | Scalar targetAbsoluteError, | ||
126 | int& numFunctionEvaluations, | ||
127 | Scalar& errorEstimate, | ||
128 | const double* abcissas, | ||
129 | const double* weights) | ||
130 | { | ||
131 | 2093485 | targetAbsoluteError /= c; | |
132 | |||
133 | // Offsets to where each level's integration constants start. | ||
134 | // The last element is not a beginning but an end. | ||
135 | static const int offsets[] = {1, 4, 7, 13, 25, 49, 97, 193}; | ||
136 | static const int numLevels = sizeof(offsets)/sizeof(int) - 1; | ||
137 | |||
138 | 2093485 | Scalar newContribution = 0.0; | |
139 | 2093485 | Scalar integral = 0.0; | |
140 | 2093485 | Scalar h = 1.0; | |
141 | 2093485 | errorEstimate = std::numeric_limits<Scalar>::max(); | |
142 | 2093485 | Scalar previousDelta, currentDelta = std::numeric_limits<Scalar>::max(); | |
143 | |||
144 | 2093485 | integral = f(c*abcissas[0] + d) * weights[0]; | |
145 | int i; | ||
146 |
2/2✓ Branch 0 taken 6189732 times.
✓ Branch 1 taken 2063244 times.
|
8373940 | for (i = offsets[0]; i != offsets[1]; ++i) |
147 | 6280455 | integral += weights[i]*(f(c*abcissas[i] + d) + f(-c*abcissas[i] + d)); | |
148 | |||
149 |
2/2✓ Branch 0 taken 12199089 times.
✓ Branch 1 taken 2015783 times.
|
14414638 | for (int level = 1; level != numLevels; ++level) |
150 | { | ||
151 | 12372895 | h *= 0.5; | |
152 | 12372895 | newContribution = 0.0; | |
153 |
2/2✓ Branch 0 taken 381689136 times.
✓ Branch 1 taken 12199089 times.
|
399272284 | for (i = offsets[level]; i != offsets[level+1]; ++i) |
154 | 386899389 | newContribution += weights[i]*(f(c*abcissas[i] + d) + f(-c*abcissas[i] + d)); | |
155 | 12372895 | newContribution *= h; | |
156 | |||
157 | // difference in consecutive integral estimates | ||
158 | 12372895 | previousDelta = currentDelta; | |
159 | using std::abs; | ||
160 |
2/2✓ Branch 0 taken 2063244 times.
✓ Branch 1 taken 10135845 times.
|
12372895 | currentDelta = abs(0.5*integral - newContribution); |
161 | 12372895 | integral = 0.5*integral + newContribution; | |
162 | |||
163 | // Once convergence kicks in, error is approximately squared at each step. | ||
164 | // Determine whether we're in the convergent region by looking at the trend in the error. | ||
165 |
2/2✓ Branch 0 taken 2063244 times.
✓ Branch 1 taken 10135845 times.
|
12372895 | if (level == 1) |
166 | ✗ | continue; // previousDelta meaningless, so cannot check convergence. | |
167 | |||
168 | // Exact comparison with zero is harmless here. Could possibly be replaced with | ||
169 | // a small positive upper limit on the size of currentDelta, but determining | ||
170 | // that upper limit would be difficult. At worse, the loop is executed more | ||
171 | // times than necessary. But no infinite loop can result since there is | ||
172 | // an upper bound on the loop variable. | ||
173 |
2/2✓ Branch 0 taken 10133526 times.
✓ Branch 1 taken 2319 times.
|
10279410 | if (currentDelta == 0.0) |
174 | break; | ||
175 | |||
176 | using std::log; | ||
177 | 10277090 | const Scalar rate = log( currentDelta )/log( previousDelta ); // previousDelta != 0 or would have been kicked out previously | |
178 | |||
179 |
4/4✓ Branch 0 taken 1158 times.
✓ Branch 1 taken 10132368 times.
✓ Branch 2 taken 1130 times.
✓ Branch 3 taken 28 times.
|
10277090 | if (rate > 1.9 && rate < 2.1) |
180 | { | ||
181 | // If convergence theory applied perfectly, r would be 2 in the convergence region. | ||
182 | // r close to 2 is good enough. We expect the difference between this integral estimate | ||
183 | // and the next one to be roughly delta^2. | ||
184 | 2701 | errorEstimate = currentDelta*currentDelta; | |
185 | } | ||
186 | else | ||
187 | { | ||
188 | // Not in the convergence region. Assume only that error is decreasing. | ||
189 | 10274389 | errorEstimate = currentDelta; | |
190 | } | ||
191 | |||
192 |
2/2✓ Branch 0 taken 10088384 times.
✓ Branch 1 taken 45142 times.
|
10277090 | if (errorEstimate < 0.1*targetAbsoluteError) |
193 | break; | ||
194 | } | ||
195 | |||
196 | 2093485 | numFunctionEvaluations = 2*i - 1; | |
197 | 2093485 | errorEstimate *= c; | |
198 | 2093485 | return c*integral; | |
199 | } | ||
200 | }; | ||
201 | |||
202 | } // end namespace Dumux | ||
203 | |||
204 | #endif | ||
205 |