From d78bdaf6e878010b1a4773241718725eabdadecc Mon Sep 17 00:00:00 2001
From: Norihiro Watanabe <norihiro.watanabe@ufz.de>
Date: Fri, 7 Mar 2014 14:39:11 +0100
Subject: [PATCH] add NewtonRaphson

---
 MathLib/Nonlinear/NewtonRaphson-impl.h | 141 +++++++++++++++++++++++++
 MathLib/Nonlinear/NewtonRaphson.h      | 123 +++++++++++++++++++++
 2 files changed, 264 insertions(+)
 create mode 100644 MathLib/Nonlinear/NewtonRaphson-impl.h
 create mode 100644 MathLib/Nonlinear/NewtonRaphson.h

diff --git a/MathLib/Nonlinear/NewtonRaphson-impl.h b/MathLib/Nonlinear/NewtonRaphson-impl.h
new file mode 100644
index 00000000000..4a466b3c657
--- /dev/null
+++ b/MathLib/Nonlinear/NewtonRaphson-impl.h
@@ -0,0 +1,141 @@
+/**
+ * \author Norihiro Watanabe
+ * \date   2012-06-25
+ *
+ * \copyright
+ * Copyright (c) 2013, OpenGeoSys Community (http://www.opengeosys.org)
+ *            Distributed under a Modified BSD License.
+ *              See accompanying file LICENSE.txt or
+ *              http://www.opengeosys.org/project/license
+ *
+ */
+
+#include <limits>
+
+#include "logog/include/logog.hpp"
+
+namespace MathLib
+{
+
+namespace Nonlinear
+{
+
+NewtonRaphson::NewtonRaphson()
+: _normType(VecNormType::INFINITY_N),
+  _r_abs_tol(std::numeric_limits<double>::max()), _r_rel_tol(1e-6), _dx_rel_tol(.0),
+  _max_itr(25), _printErrors(true), _n_iterations(0), _r_abs_error(.0), _r_rel_error(.0), _dx_rel_error(.0)
+{
+}
+
+template<class F_RESIDUAL, class F_DX, class T_VALUE>
+bool NewtonRaphson::solve(F_RESIDUAL &f_residual, F_DX &f_dx, const T_VALUE &x0, T_VALUE &x_new)
+{
+    T_VALUE r(x0), dx(x0);
+    r = .0;
+    dx = .0;
+
+    const bool checkAbsResidualError = (_r_abs_tol<std::numeric_limits<double>::max());
+    const bool checkRelResidualError = (_r_rel_tol<std::numeric_limits<double>::max());
+    const bool checkRelDxError = (_dx_rel_tol>.0);
+    const bool needXNorm =  (checkRelResidualError || checkRelDxError);
+
+    INFO("------------------------------------------------------------------");
+    INFO("*** NEWTON-RAPHSON nonlinear solver");
+    INFO("-> iteration started");
+
+    double x_norm = -1.;
+    double dx_norm = -1.;
+    double r_norm = -1.;
+    bool converged = false;
+
+    // evaluate initial residual
+    x_new = x0;
+    f_residual(x_new, r);
+    // check convergence
+    r_norm = norm(r, _normType);
+    dx_norm = norm(dx, _normType);
+    if (needXNorm)
+        x_norm = norm(x_new, _normType);
+    converged = ((r_norm < _r_abs_tol && r_norm < _r_rel_tol*x_norm)
+                || (checkRelDxError && dx_norm < _dx_rel_tol*x_norm));
+    if (_printErrors)
+        INFO("-> %d: ||r||=%1.3e, ||dx||=%1.3e, ||x||=%1.3e, ||dx||/||x||=%1.3e", 0, r_norm, dx_norm, x_norm, x_norm==0 ? dx_norm : dx_norm/x_norm);
+
+    std::size_t itr_cnt = 0;
+    if (!converged) {
+        for (itr_cnt=1; itr_cnt<_max_itr; itr_cnt++) {
+            // solve dx=-J^-1*r
+            f_dx(x_new, r, dx);
+            x_new += dx;
+            // evaluate residual
+            f_residual(x_new, r);
+#ifdef NDEBUG
+            printout(std::cout, itr_cnt, x_new, r, dx);
+#endif
+            // check convergence
+            r_norm = norm(r, _normType);
+            dx_norm = norm(dx, _normType);
+            if (needXNorm)
+                x_norm = norm(x_new, _normType);
+            converged = ((r_norm < _r_abs_tol && r_norm < _r_rel_tol*x_norm)
+                        || (checkRelDxError && dx_norm < _dx_rel_tol*x_norm));
+            if (_printErrors)
+                INFO("-> %d: ||r||=%1.3e, ||dx||=%1.3e, ||x||=%1.3e, ||dx||/||x||=%1.3e", itr_cnt, r_norm, dx_norm, x_norm, x_norm==0 ? dx_norm : dx_norm/x_norm);
+
+            if (converged)
+                break;
+        }
+    }
+
+    INFO("-> iteration finished");
+    if (_max_itr==1) {
+        INFO("status    : iteration not required");
+    } else {
+        INFO("status    : %s", (converged ? "CONVERGED" : "***DIVERGED***"));
+    }
+    INFO("iteration : %d/%d", itr_cnt, _max_itr);
+    if (checkAbsResidualError)
+        INFO("abs. res. : %1.3e (tolerance=%1.3e)", r_norm, _r_abs_tol);
+    if (checkRelResidualError)
+        INFO("rel. res. : %1.3e (tolerance=%1.3e)", x_norm==0?r_norm:r_norm/x_norm, _r_rel_tol);
+    if (checkRelDxError)
+        INFO("dx        : %1.3e (tolerance=%1.3e)", x_norm==0?dx_norm:dx_norm/x_norm, _dx_rel_tol);
+    INFO("norm type : %s", convertVecNormTypeToString(_normType).c_str());
+    INFO("------------------------------------------------------------------");
+
+    this->_n_iterations = itr_cnt;
+    this->_r_abs_error = r_norm;
+    this->_r_rel_error = (x_norm==0 ? r_norm : r_norm / x_norm);
+    this->_dx_rel_error = (x_norm==0 ? dx_norm : dx_norm / x_norm);
+
+    return converged;
+}
+
+
+#ifdef NDEBUG
+template<class T_VALUE>
+inline void NewtonRaphson::printout(std::ostream& os, std::size_t i, T_VALUE& x_new, T_VALUE& r, T_VALUE& dx)
+{
+    os << "-> " << i <<": x=(";
+    for (std::size_t i=0; i<x_new.size(); i++)
+        os << x_new[i] << " ";
+    os << "), r=(";
+    for (std::size_t i=0; i<dx.size(); i++)
+        os << r[i] << " ";
+    os << "), dx=(";
+    for (std::size_t i=0; i<dx.size(); i++)
+        os << dx[i] << " ";
+    os << ")\n";
+}
+
+// in case of double
+template<>
+inline void NewtonRaphson::printout(std::ostream& os, std::size_t i, double& x_new, double& r, double& dx)
+{
+    os << "-> " << i <<": x=" << x_new << ", r=" << r << ", dx=" << dx << "\n";
+}
+#endif
+
+}
+
+} //end
diff --git a/MathLib/Nonlinear/NewtonRaphson.h b/MathLib/Nonlinear/NewtonRaphson.h
new file mode 100644
index 00000000000..feb44f3c4c8
--- /dev/null
+++ b/MathLib/Nonlinear/NewtonRaphson.h
@@ -0,0 +1,123 @@
+/**
+ * \author Norihiro Watanabe
+ * \date   2012-06-25
+ *
+ * \copyright
+ * Copyright (c) 2013, OpenGeoSys Community (http://www.opengeosys.org)
+ *            Distributed under a Modified BSD License.
+ *              See accompanying file LICENSE.txt or
+ *              http://www.opengeosys.org/project/license
+ *
+ */
+
+#ifndef MATHLIB_NONLINEAR_NEWTONRAPHSON_H_
+#define MATHLIB_NONLINEAR_NEWTONRAPHSON_H_
+
+#include "LinAlg/VectorNorms.h"
+
+namespace MathLib
+{
+
+namespace Nonlinear
+{
+
+/**
+ * \brief Newton-Raphson method
+ */
+class NewtonRaphson
+{
+public:
+    /// Default constructor with norm type INFINITY_N, relative tolerance 1e-6
+    /// for residual, and the maximum number of iterations 25
+    NewtonRaphson();
+
+    /// set a vector norm type
+    void setNormType(VecNormType normType) {_normType = normType;}
+
+    /// set the maximum number of iterations
+    void setMaxIterations(std::size_t max_itr) {_max_itr = max_itr;}
+
+    /// set the absolute residual tolerance used by the stopping criteria
+    void setAbsResidualTolerance(double abs_tol) {_r_abs_tol = abs_tol;}
+
+    /// set the relative residual tolerance used by the stopping criteria
+    void setRelResidualTolerance(double rel_tol) {_r_rel_tol = rel_tol;}
+
+    /// set the relative solution increment tolerance used by the stopping criteria
+    void setRelDxTolerance(double rel_tol) {_dx_rel_tol = rel_tol;}
+
+    /// print errors during iterations
+    void printErrors(bool flag) {_printErrors = flag;}
+
+    /**
+     * solve a nonlinear problem
+     *
+     * \tparam F_RESIDUAL       Function object returning a residual of an equation.
+     * The object should have an operator ()(\f$x_k\f$, \f$r_k}\f$).
+     * \f$x_k\f$ is current solution. \f$r_k\f$ is calculated residual and an output
+     * of this function.
+     * \tparam F_DX             Function object returning a solution increment.
+     * The object should have an operator ()(\f$x_k\f$, \f$r_k, \f$\Delta x_{k+1}}\f$).
+     * \f$\Delta x_{k+1}}\f$ is a calculated solution increment and an output of
+     * this function.
+     * \tparam T_VALUE          Data type of \f$x_k\f$
+     * Both scalar and vector types are available as far as the following conditions
+     * are satisfied
+     * - T_VALUE has a copy constructor (for non-primitive data types)
+     * - MathLib::norm(T_VALUE) exists
+     * \param f_residual        Residual function object \f$r(x)\f$
+     * \param f_dx              Solution increment function object \f$dx(x)=J^{-1}r\f$
+     * \param x0                Initial guess
+     * \param x_new             Solution
+     * \return true if converged
+     */
+    template<class F_RESIDUAL, class F_DX, class T_VALUE>
+    bool solve(F_RESIDUAL &f_residual, F_DX &f_dx, const T_VALUE &x0, T_VALUE &x_new);
+
+    /// return the number of iterations
+    std::size_t getNIterations() const {return _n_iterations; }
+
+    /// return absolute error in the last iteration
+    double getAbsResidualError() const {return _r_abs_error; }
+
+    /// return relative error in the last iteration
+    double getRelResidualError() const {return _r_rel_error; }
+
+    /// return relative error in the last iteration
+    double getRelDxError() const {return _dx_rel_error; }
+
+private:
+#ifdef NDEBUG
+    /// print out for debugging
+    template<class T_VALUE>
+    inline void printout(std::ostream& os, std::size_t i, T_VALUE& x_new, T_VALUE& r, T_VALUE& dx);
+#endif
+
+    /// vector norm type used to evaluate errors
+    VecNormType _normType;
+    /// absolute tolerance for residual
+    double _r_abs_tol;
+    /// relative tolerance for residual
+    double _r_rel_tol;
+    /// relative tolerance for dx
+    double _dx_rel_tol;
+    /// the maximum allowed iteration number
+    std::size_t _max_itr;
+    /// print iteration errors or not
+    bool _printErrors;
+    /// the number of iterations in the last calculation
+    std::size_t _n_iterations;
+    /// absolute residual error in the last calculation
+    double _r_abs_error;
+    /// relative residual error in the last calculation
+    double _r_rel_error;
+    /// relative dx error in the last calculation
+    double _dx_rel_error;
+};
+
+} // Nonlinear
+} // MathLib
+
+#include "NewtonRaphson-impl.h"
+
+#endif // MATHLIB_NONLINEAR_NEWTONRAPHSON_H_
-- 
GitLab