From 4ff7f49ed8fc8a2fb9e4efde25d68da7a32370f4 Mon Sep 17 00:00:00 2001
From: Christoph Lehmann <christoph.lehmann@ufz.de>
Date: Tue, 23 Aug 2016 09:53:15 +0200
Subject: [PATCH] [NL] added function for relative tolerance check

---
 NumLib/ODESolver/ConvergenceCriterion.cpp                 | 8 ++++++++
 NumLib/ODESolver/ConvergenceCriterion.h                   | 7 +++++++
 NumLib/ODESolver/ConvergenceCriterionDeltaX.cpp           | 2 +-
 .../ODESolver/ConvergenceCriterionPerComponentDeltaX.cpp  | 3 ++-
 .../ConvergenceCriterionPerComponentResidual.cpp          | 7 ++++---
 NumLib/ODESolver/ConvergenceCriterionResidual.cpp         | 3 ++-
 6 files changed, 24 insertions(+), 6 deletions(-)

diff --git a/NumLib/ODESolver/ConvergenceCriterion.cpp b/NumLib/ODESolver/ConvergenceCriterion.cpp
index e89a854f690..42191dc81cf 100644
--- a/NumLib/ODESolver/ConvergenceCriterion.cpp
+++ b/NumLib/ODESolver/ConvergenceCriterion.cpp
@@ -37,4 +37,12 @@ std::unique_ptr<ConvergenceCriterion> createConvergenceCriterion(
     OGS_FATAL("There is no convergence criterion of type `%s'.", type.c_str());
 }
 
+bool checkRelativeTolerance(const double reltol, const double numerator,
+                            const double denominator)
+{
+    auto const eps = std::numeric_limits<double>::epsilon();
+    return std::abs(numerator) <
+           std::abs(reltol) * (std::abs(denominator) + eps);
+}
+
 }  // NumLib
diff --git a/NumLib/ODESolver/ConvergenceCriterion.h b/NumLib/ODESolver/ConvergenceCriterion.h
index aaf07e8c15f..45d473d8823 100644
--- a/NumLib/ODESolver/ConvergenceCriterion.h
+++ b/NumLib/ODESolver/ConvergenceCriterion.h
@@ -77,6 +77,13 @@ public:
 std::unique_ptr<ConvergenceCriterion> createConvergenceCriterion(
     BaseLib::ConfigTree const& config);
 
+//! Returns if |numerator/denominator| < |reltol|.
+//! This method copes with the case that denominator = 0 by always adding
+//! epsilon to the denominator.
+bool checkRelativeTolerance(double const reltol,
+                            double const numerator,
+                            double const denominator);
+
 } // namespace NumLib
 
 #endif  // NUMLIB_CONVERGENCECRITERION_H
diff --git a/NumLib/ODESolver/ConvergenceCriterionDeltaX.cpp b/NumLib/ODESolver/ConvergenceCriterionDeltaX.cpp
index 5ab4ab6edbd..2c2249c1dbd 100644
--- a/NumLib/ODESolver/ConvergenceCriterionDeltaX.cpp
+++ b/NumLib/ODESolver/ConvergenceCriterionDeltaX.cpp
@@ -45,7 +45,7 @@ void ConvergenceCriterionDeltaX::checkDeltaX(const GlobalVector& minus_delta_x,
         satisfied_abs = error_dx < *_abstol;
     }
     if (_reltol) {
-        satisfied_rel = error_dx < *_reltol * norm_x;
+        satisfied_rel = checkRelativeTolerance(*_reltol, error_dx, norm_x);
     }
 
     _satisfied = _satisfied && (satisfied_abs || satisfied_rel);
diff --git a/NumLib/ODESolver/ConvergenceCriterionPerComponentDeltaX.cpp b/NumLib/ODESolver/ConvergenceCriterionPerComponentDeltaX.cpp
index fe5f73b4528..9615ab813d1 100644
--- a/NumLib/ODESolver/ConvergenceCriterionPerComponentDeltaX.cpp
+++ b/NumLib/ODESolver/ConvergenceCriterionPerComponentDeltaX.cpp
@@ -58,7 +58,8 @@ void ConvergenceCriterionPerComponentDeltaX::checkDeltaX(
 
         satisfied_abs = satisfied_abs && error_dx < _abstols[global_component];
         satisfied_rel =
-            satisfied_rel && error_dx < _reltols[global_component] * norm_x;
+            satisfied_rel && checkRelativeTolerance(_reltols[global_component],
+                                                    error_dx, norm_x);
     }
 
     _satisfied = _satisfied && (satisfied_abs || satisfied_rel);
diff --git a/NumLib/ODESolver/ConvergenceCriterionPerComponentResidual.cpp b/NumLib/ODESolver/ConvergenceCriterionPerComponentResidual.cpp
index dc94fb9b6e9..155857c71fc 100644
--- a/NumLib/ODESolver/ConvergenceCriterionPerComponentResidual.cpp
+++ b/NumLib/ODESolver/ConvergenceCriterionPerComponentResidual.cpp
@@ -65,9 +65,10 @@ void ConvergenceCriterionPerComponentResidual::checkResidual(
         }
 
         satisfied_abs = satisfied_abs && norm_res < _abstols[global_component];
-        satisfied_rel = satisfied_rel &&
-                        norm_res < _reltols[global_component] *
-                                       _residual_norms_0[global_component];
+        satisfied_rel =
+            satisfied_rel &&
+            checkRelativeTolerance(_reltols[global_component], norm_res,
+                                   _residual_norms_0[global_component]);
     }
 
     _satisfied = _satisfied && (satisfied_abs || satisfied_rel);
diff --git a/NumLib/ODESolver/ConvergenceCriterionResidual.cpp b/NumLib/ODESolver/ConvergenceCriterionResidual.cpp
index ff0d719c078..141245a4492 100644
--- a/NumLib/ODESolver/ConvergenceCriterionResidual.cpp
+++ b/NumLib/ODESolver/ConvergenceCriterionResidual.cpp
@@ -48,7 +48,8 @@ void ConvergenceCriterionResidual::checkResidual(const GlobalVector& residual)
         satisfied_abs = norm_res < *_abstol;
     }
     if (_reltol && !_is_first_iteration) {
-        satisfied_rel = norm_res < *_reltol * _residual_norm_0;
+        satisfied_rel =
+            checkRelativeTolerance(*_reltol, norm_res, _residual_norm_0);
     }
 
     _satisfied = _satisfied && (satisfied_abs || satisfied_rel);
-- 
GitLab