From 34b8b965e7d390a9df33ba61315f3120279977fa Mon Sep 17 00:00:00 2001
From: Timo Koch <timo.koch@iws.uni-stuttgart.de>
Date: Fri, 13 Dec 2019 17:22:48 +0100
Subject: [PATCH] [test] Add test for scalar root finding algorithms

---
 dumux/nonlinear/findscalarroot.hh             | 14 +++---
 test/CMakeLists.txt                           |  1 +
 test/nonlinear/CMakeLists.txt                 |  1 +
 test/nonlinear/findscalarroot/CMakeLists.txt  |  2 +
 .../findscalarroot/test_findscalarroot.cc     | 50 +++++++++++++++++++
 5 files changed, 60 insertions(+), 8 deletions(-)
 create mode 100644 test/nonlinear/CMakeLists.txt
 create mode 100644 test/nonlinear/findscalarroot/CMakeLists.txt
 create mode 100644 test/nonlinear/findscalarroot/test_findscalarroot.cc

diff --git a/dumux/nonlinear/findscalarroot.hh b/dumux/nonlinear/findscalarroot.hh
index 2a6d4b1990..7c230d47fe 100644
--- a/dumux/nonlinear/findscalarroot.hh
+++ b/dumux/nonlinear/findscalarroot.hh
@@ -36,14 +36,13 @@
 namespace Dumux {
 
 /*!
- * \file
  * \ingroup Common
  * \brief Newton's root finding algorithm for scalar functions (secant method)
  * \param xOld initial guess
  * \param residual Residual function
  * \param derivative Derivative of the residual
- * \param maxIter Maximum number of iterations
  * \param tol Relative shift tolerance
+ * \param maxIter Maximum number of iterations
  * \todo replace std::result_of by std::invoke_result for C++17
  */
 template<class Scalar, class ResFunc, class DerivFunc,
@@ -68,7 +67,8 @@ Scalar findScalarRootNewton(Scalar xOld, const ResFunc& residual, const DerivFun
         n--;
     }
 
-    if (!std::isfinite(r))
+    using std::isfinite;
+    if (!isfinite(r))
         DUNE_THROW(NumericalProblem, "Residual is not finite: " << r << " after " << maxIter - n << " iterations!");
 
     if (relativeShift > tol)
@@ -78,14 +78,13 @@ Scalar findScalarRootNewton(Scalar xOld, const ResFunc& residual, const DerivFun
 }
 
 /*!
- * \file
  * \ingroup Common
  * \brief Newton's root finding algorithm for scalar functions (secant method)
  * \note The derivative is numerically computed. If the derivative is know use signature with derivative function.
  * \param xOld initial guess
  * \param residual Residual function
- * \param maxIter Maximum number of iterations
  * \param tol Relative shift tolerance
+ * \param maxIter Maximum number of iterations
  */
 template<class Scalar, class ResFunc,
           typename std::enable_if_t<Dune::Std::is_invocable<ResFunc, Scalar>::value>...>
@@ -98,7 +97,6 @@ Scalar findScalarRootNewton(Scalar xOld, const ResFunc& residual,
 }
 
 /*!
- * \file
  * \ingroup Common
  * \brief Brent's root finding algorithm for scalar functions
  * \note Modified from pseudo-code on wikipedia: https://en.wikipedia.org/wiki/Brent%27s_method
@@ -107,8 +105,8 @@ Scalar findScalarRootNewton(Scalar xOld, const ResFunc& residual,
  * \param a Lower bound
  * \param b Upper bound
  * \param residual Residual function
- * \param maxIter Maximum number of iterations
  * \param tol Relative shift tolerance
+ * \param maxIter Maximum number of iterations
  */
 template<class Scalar, class ResFunc,
          typename std::enable_if_t<Dune::Std::is_invocable<ResFunc, Scalar>::value>...>
@@ -123,7 +121,7 @@ Scalar findScalarRootBrent(Scalar a, Scalar b, const ResFunc& residual,
     // check if the root is inside the given interval
     using std::signbit;
     if (!signbit(fa*fb))
-        DUNE_THROW(NumericalProblem, "Brent's algorithm failed: [a,b] does not contain the root!");
+        DUNE_THROW(NumericalProblem, "Brent's algorithm failed: [a,b] does not contain any, or no uniquely defined root!");
 
     // sort bounds
     using std::abs; using std::swap;
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 416332236d..b5c7428a4b 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -4,5 +4,6 @@ add_subdirectory(freeflow)
 add_subdirectory(io)
 add_subdirectory(material)
 add_subdirectory(multidomain)
+add_subdirectory(nonlinear)
 add_subdirectory(porousmediumflow)
 add_subdirectory(discretization)
diff --git a/test/nonlinear/CMakeLists.txt b/test/nonlinear/CMakeLists.txt
new file mode 100644
index 0000000000..3640235bdc
--- /dev/null
+++ b/test/nonlinear/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(findscalarroot)
diff --git a/test/nonlinear/findscalarroot/CMakeLists.txt b/test/nonlinear/findscalarroot/CMakeLists.txt
new file mode 100644
index 0000000000..004545b738
--- /dev/null
+++ b/test/nonlinear/findscalarroot/CMakeLists.txt
@@ -0,0 +1,2 @@
+dumux_add_test(SOURCES test_findscalarroot.cc
+               LABELS unit nonlinear)
diff --git a/test/nonlinear/findscalarroot/test_findscalarroot.cc b/test/nonlinear/findscalarroot/test_findscalarroot.cc
new file mode 100644
index 0000000000..88dcbea992
--- /dev/null
+++ b/test/nonlinear/findscalarroot/test_findscalarroot.cc
@@ -0,0 +1,50 @@
+#include <config.h>
+
+#include <iostream>
+#include <iomanip>
+#include <cmath>
+
+#include <dune/common/exceptions.hh>
+#include <dune/common/float_cmp.hh>
+#include <dumux/nonlinear/findscalarroot.hh>
+
+int main(int argc, char* argv[]) try
+{
+    using namespace Dumux;
+
+    const auto func = [](const double x){ return x*x - 5.0; };
+    const auto deriv = [](const double x){ return 2*x; };
+    const auto absTol = 1e-15;
+    const auto relTol = 1e-15;
+
+    const auto rootNewton = findScalarRootNewton(5.0, func, deriv, absTol);
+    const auto rootNewtonNumDiff = findScalarRootNewton(5.0, func, absTol);
+    const auto rootBrent = findScalarRootBrent(0.0, 5.0, func, relTol);
+    const auto rootExact = std::sqrt(5.0);
+
+    if (Dune::FloatCmp::ne(rootExact, rootNewton, absTol))
+        DUNE_THROW(Dune::Exception, "Wrong root for Newton algorithm (exact derivative)! Relative error: " << std::abs(rootExact-rootNewton)/rootExact);
+    if (Dune::FloatCmp::ne(rootExact, rootNewtonNumDiff, absTol))
+        DUNE_THROW(Dune::Exception, "Wrong root for Newton algorithm (numeric differentiation)! Relative error: " << std::abs(rootExact-rootNewtonNumDiff)/rootExact);
+    if (Dune::FloatCmp::ne(rootExact, rootBrent, absTol))
+        DUNE_THROW(Dune::Exception, "Wrong root for Brent algorithm! Relative error: " << std::abs(rootExact-rootBrent)/rootExact);
+
+    std::cout << "Positive root of f(x) = (x*x - 5) is\n" << std::setprecision(15)
+              << "-- Exact: " << rootExact << "\n"
+              << "-- Newton (exact derivative): " << rootNewton << "\n"
+              << "-- Newton (numeric derivative): " << rootNewtonNumDiff << "\n"
+              << "-- Brent: " << rootBrent << std::endl;
+
+    return 0;
+
+}
+catch (const Dune::Exception& e)
+{
+    std::cout << e << std::endl;
+    return 1;
+}
+catch (...)
+{
+    std::cout << "Unknown exception thrown!" << std::endl;
+    return 1;
+}
-- 
GitLab