version 3.8
findscalarroot.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// SPDX-FileCopyrightInfo: Copyright © DuMux Project contributors, see AUTHORS.md in root folder
5// SPDX-License-Identifier: GPL-3.0-or-later
6//
12#ifndef DUMUX_COMMON_SCALAR_ROOT_FINDING_HH
13#define DUMUX_COMMON_SCALAR_ROOT_FINDING_HH
14
15#include <cmath>
16#include <limits>
17#include <type_traits>
18
22
23namespace Dumux {
24
34template<class Scalar, class ResFunc, class DerivFunc,
35 typename std::enable_if_t<std::is_invocable_r_v<Scalar, ResFunc, Scalar> &&
36 std::is_invocable_r_v<Scalar, DerivFunc, Scalar>>...>
37Scalar findScalarRootNewton(Scalar xOld, const ResFunc& residual, const DerivFunc& derivative,
38 const Scalar tol = 1e-13, const int maxIter = 200)
39{
40 Scalar xNew = xOld;
41 Scalar r = residual(xNew);
42
43 int n = maxIter;
44 Scalar relativeShift = std::numeric_limits<Scalar>::max();
45 while (relativeShift > tol && n > 0)
46 {
47 xNew = xOld - r/derivative(xOld);
48 r = residual(xNew);
49
50 using std::abs; using std::max;
51 relativeShift = abs(xOld-xNew)/max(abs(xOld), abs(xNew));
52 xOld = xNew;
53 n--;
54 }
55
56 using std::isfinite;
57 if (!isfinite(r))
58 DUNE_THROW(NumericalProblem, "Residual is not finite: " << r << " after " << maxIter - n << " iterations!");
59
60 if (relativeShift > tol)
61 DUNE_THROW(NumericalProblem, "Scalar newton solver did not converge after " << maxIter << " iterations!");
62
63 return xNew;
64}
65
75template<class Scalar, class ResFunc,
76 typename std::enable_if_t<std::is_invocable_r_v<Scalar, ResFunc, Scalar>>...>
77Scalar findScalarRootNewton(Scalar xOld, const ResFunc& residual,
78 const Scalar tol = 1e-13, const int maxIter = 200)
79{
80 const auto eps = NumericDifferentiation::epsilon(xOld);
81 auto derivative = [&](const auto x){ return (residual(x + eps)-residual(x))/eps; };
82 return findScalarRootNewton(xOld, residual, derivative, tol, maxIter);
83}
84
97template<class Scalar, class ResFunc,
98 typename std::enable_if_t<std::is_invocable_r_v<Scalar, ResFunc, Scalar>>...>
99Scalar findScalarRootBrent(Scalar a, Scalar b, const ResFunc& residual,
100 const Scalar tol = 1e-13, const int maxIter = 200)
101{
102 // precompute the residuals (minimize function evaluations)
103 Scalar fa = residual(a);
104 Scalar fb = residual(b);
105 Scalar fs = 0.0;
106
107 // check if the root is inside the given interval
108 using std::signbit;
109 if (!signbit(fa*fb))
110 DUNE_THROW(NumericalProblem, "Brent's algorithm failed: [a,b] does not contain any, or no uniquely defined root!");
111
112 // sort bounds
113 using std::abs; using std::swap;
114 if (abs(fa) < abs(fb))
115 {
116 swap(a, b);
117 swap(fa, fb);
118 }
119
120 Scalar c = a;
121 Scalar fc = fa;
122 Scalar d = 0.0;
123 Scalar s = 0.0;
124 bool flag = true;
125
126 for (int i = 0; i < maxIter; ++i)
127 {
128 // stopping criterion
129 using std::max;
130 if (abs(b-a) < tol*max(abs(a), abs(b)))
131 return b;
132
133 // inverse quadratic interpolation
134 if (fa != fc && fb != fc)
135 {
136 const auto fab = fa-fb;
137 const auto fac = fa-fc;
138 const auto fbc = fb-fc;
139 s = a*fb*fc/(fab*fac) - b*fa*fc/(fab*fbc) + c*fa*fb/(fac*fbc);
140 }
141
142 // secant method
143 else
144 {
145 s = b - fb*(b-a)/(fb-fa);
146 }
147
148 // bisection method
149 if ( (s < (3*a + b)*0.25 || s > b)
150 || (flag && abs(s-b) >= abs(b-c)*0.5)
151 || (!flag && abs(s-b) >= abs(c-d)*0.5)
152 || (flag && abs(b-c) < tol*max(abs(b), abs(c)))
153 || (!flag && abs(c-d) < tol*max(abs(c), abs(d))) )
154 {
155 s = (a+b)*0.5;
156 flag = true;
157 }
158 else
159 flag = false;
160
161 // compute residual at new guess
162 fs = residual(s);
163 d = c;
164 c = b;
165 fc = fb;
166
167 // check on which side of the root s falls
168 if (fa*fs < 0.0)
169 {
170 b = s;
171 fb = fs;
172 }
173 else
174 {
175 a = s;
176 fa = fs;
177 }
178
179 // sort if necessary
180 if (abs(fa) < abs(fb))
181 {
182 swap(a, b);
183 swap(fa, fb);
184 }
185 }
186
187 DUNE_THROW(NumericalProblem, "Brent's algorithm didn't converge after " << maxIter << " iterations!");
188}
189
190} // end namespace Dumux
191
192#endif
static Scalar epsilon(const Scalar value, const Scalar baseEps=1e-10)
Computes the epsilon used for numeric differentiation.
Definition: numericdifferentiation.hh:35
Exception thrown if a fixable numerical problem occurs.
Definition: exceptions.hh:27
Some exceptions thrown in DuMux
Scalar findScalarRootNewton(Scalar xOld, const ResFunc &residual, const DerivFunc &derivative, const Scalar tol=1e-13, const int maxIter=200)
Newton's root finding algorithm for scalar functions (secant method)
Definition: findscalarroot.hh:37
Scalar findScalarRootBrent(Scalar a, Scalar b, const ResFunc &residual, const Scalar tol=1e-13, const int maxIter=200)
Brent's root finding algorithm for scalar functions.
Definition: findscalarroot.hh:99
Definition: adapt.hh:17
A class for numeric differentiation.
The infrastructure to retrieve run-time parameters from Dune::ParameterTrees.