From dd30c1c033e4513b33f895aa98f20c7d6fc235e0 Mon Sep 17 00:00:00 2001
From: Christoph Lehmann <christoph.lehmann@ufz.de>
Date: Mon, 22 Aug 2016 16:04:31 +0200
Subject: [PATCH] [NL] added per component residual check

---
 NumLib/ODESolver/ConvergenceCriterion.cpp     |   3 +
 ...nvergenceCriterionPerComponentResidual.cpp | 121 ++++++++++++++++++
 ...ConvergenceCriterionPerComponentResidual.h |  61 +++++++++
 3 files changed, 185 insertions(+)
 create mode 100644 NumLib/ODESolver/ConvergenceCriterionPerComponentResidual.cpp
 create mode 100644 NumLib/ODESolver/ConvergenceCriterionPerComponentResidual.h

diff --git a/NumLib/ODESolver/ConvergenceCriterion.cpp b/NumLib/ODESolver/ConvergenceCriterion.cpp
index 7e9fba73c6d..e89a854f690 100644
--- a/NumLib/ODESolver/ConvergenceCriterion.cpp
+++ b/NumLib/ODESolver/ConvergenceCriterion.cpp
@@ -14,6 +14,7 @@
 #include "ConvergenceCriterionDeltaX.h"
 #include "ConvergenceCriterionResidual.h"
 #include "ConvergenceCriterionPerComponentDeltaX.h"
+#include "ConvergenceCriterionPerComponentResidual.h"
 
 namespace NumLib
 {
@@ -29,6 +30,8 @@ std::unique_ptr<ConvergenceCriterion> createConvergenceCriterion(
         return createConvergenceCriterionResidual(config);
     } else if (type == "PerComponentDeltaX") {
         return createConvergenceCriterionPerComponentDeltaX(config);
+    } else if (type == "PerComponentResidual") {
+        return createConvergenceCriterionPerComponentResidual(config);
     }
 
     OGS_FATAL("There is no convergence criterion of type `%s'.", type.c_str());
diff --git a/NumLib/ODESolver/ConvergenceCriterionPerComponentResidual.cpp b/NumLib/ODESolver/ConvergenceCriterionPerComponentResidual.cpp
new file mode 100644
index 00000000000..dc94fb9b6e9
--- /dev/null
+++ b/NumLib/ODESolver/ConvergenceCriterionPerComponentResidual.cpp
@@ -0,0 +1,121 @@
+/**
+ * \copyright
+ * Copyright (c) 2012-2016, 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 "ConvergenceCriterionPerComponentResidual.h"
+#include <logog/include/logog.hpp>
+
+#include "BaseLib/ConfigTree.h"
+#include "NumLib/DOF/DOFTableUtil.h"
+#include "NumLib/DOF/LocalToGlobalIndexMap.h"
+
+namespace NumLib
+{
+ConvergenceCriterionPerComponentResidual::
+    ConvergenceCriterionPerComponentResidual(
+        std::vector<double>&& absolute_tolerances,
+        std::vector<double>&& relative_tolerances,
+        MathLib::VecNormType norm_type)
+    : _abstols(std::move(absolute_tolerances)),
+      _reltols(std::move(relative_tolerances)),
+      _norm_type(norm_type),
+      _residual_norms_0(_abstols.size())
+{
+    if (_abstols.size() != _reltols.size())
+        OGS_FATAL(
+            "The number of absolute and relative tolerances given must be the "
+            "same.");
+
+    if (_abstols.empty())
+        OGS_FATAL("The given tolerances vector is empty.");
+}
+
+void ConvergenceCriterionPerComponentResidual::checkResidual(
+    const GlobalVector& residual)
+{
+    if ((!_dof_table) || (!_mesh))
+        OGS_FATAL("D.o.f. table or mesh have not been set.");
+
+    bool satisfied_abs = true;
+    // Make sure that in the first iteration the relative residual tolerance is
+    // not satisfied.
+    bool satisfied_rel = _is_first_iteration ? false : true;
+
+    for (unsigned global_component = 0; global_component < _abstols.size();
+         ++global_component)
+    {
+        // TODO short cut if tol <= 0.0
+        auto norm_res = norm(residual, global_component, _norm_type,
+                             *_dof_table, *_mesh);
+
+        if (_is_first_iteration) {
+            INFO("Convergence criterion: |r0|=%.4e", norm_res);
+            _residual_norms_0[global_component] = norm_res;
+        } else {
+            auto const norm_res0 = _residual_norms_0[global_component];
+            INFO(
+                "Convergence criterion, component %u: |r|=%.4e, |r0|=%.4e, "
+                "|r|/|r0|=%.4e",
+                norm_res, global_component, norm_res0, norm_res / norm_res0);
+        }
+
+        satisfied_abs = satisfied_abs && norm_res < _abstols[global_component];
+        satisfied_rel = satisfied_rel &&
+                        norm_res < _reltols[global_component] *
+                                       _residual_norms_0[global_component];
+    }
+
+    _satisfied = _satisfied && (satisfied_abs || satisfied_rel);
+}
+
+void ConvergenceCriterionPerComponentResidual::setDOFTable(
+    const LocalToGlobalIndexMap& dof_table, MeshLib::Mesh const& mesh)
+{
+    _dof_table = &dof_table;
+    _mesh = &mesh;
+
+    if (_dof_table->getNumberOfComponents() != _abstols.size())
+        OGS_FATAL(
+            "The number of components in the DOF table and the number of "
+            "tolerances given do not match.");
+}
+
+std::unique_ptr<ConvergenceCriterionPerComponentResidual>
+createConvergenceCriterionPerComponentResidual(
+    const BaseLib::ConfigTree& config)
+{
+    config.checkConfigParameter("type", "PerComponentResidual");
+
+    auto abstols =
+        config.getConfigParameterOptional<std::vector<double>>("abstols");
+    auto reltols =
+        config.getConfigParameterOptional<std::vector<double>>("reltols");
+    auto const norm_type_str =
+        config.getConfigParameter<std::string>("norm_type");
+
+    if ((!abstols) && (!reltols))
+        OGS_FATAL(
+            "At least one of absolute or relative tolerance has to be "
+            "specified.");
+    if (!abstols) {
+        abstols = std::vector<double>(reltols->size());
+    } else if (!reltols) {
+        reltols = std::vector<double>(abstols->size());
+    }
+
+    auto const norm_type = MathLib::convertStringToVecNormType(norm_type_str);
+
+    if (norm_type == MathLib::VecNormType::INVALID)
+        OGS_FATAL("Unknown vector norm type `%s'.", norm_type_str.c_str());
+
+    return std::unique_ptr<ConvergenceCriterionPerComponentResidual>(
+        new ConvergenceCriterionPerComponentResidual(
+            std::move(*abstols), std::move(*reltols), norm_type));
+}
+
+}  // NumLib
diff --git a/NumLib/ODESolver/ConvergenceCriterionPerComponentResidual.h b/NumLib/ODESolver/ConvergenceCriterionPerComponentResidual.h
new file mode 100644
index 00000000000..d04e4f6e06f
--- /dev/null
+++ b/NumLib/ODESolver/ConvergenceCriterionPerComponentResidual.h
@@ -0,0 +1,61 @@
+/**
+ * \copyright
+ * Copyright (c) 2012-2016, 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 NUMLIB_CONVERGENCECRITERIONPERCOMPONENTRESIDUAL_H
+#define NUMLIB_CONVERGENCECRITERIONPERCOMPONENTRESIDUAL_H
+
+#include <vector>
+#include "MathLib/LinAlg/LinAlgEnums.h"
+#include "ConvergenceCriterionPerComponent.h"
+
+namespace NumLib
+{
+class LocalToGlobalIndexMap;
+
+class ConvergenceCriterionPerComponentResidual
+    : public ConvergenceCriterionPerComponent
+{
+public:
+    explicit ConvergenceCriterionPerComponentResidual(
+        std::vector<double>&& absolute_tolerances,
+        std::vector<double>&& relative_tolerances,
+        MathLib::VecNormType norm_type);
+
+    bool hasDeltaXCheck() const override { return false; }
+    bool hasResidualCheck() const override { return true; }
+
+    void checkDeltaX(const GlobalVector& /*minus_delta_x*/,
+                     GlobalVector const& /*x*/) override {}
+    void checkResidual(const GlobalVector& residual) override;
+
+    void preFirstIteration() override { _is_first_iteration = true; }
+    void reset() override { _satisfied = true; _is_first_iteration = false; }
+    bool isSatisfied() const override { return _satisfied; }
+
+    void setDOFTable(const LocalToGlobalIndexMap& dof_table,
+                     MeshLib::Mesh const& mesh) override;
+
+private:
+    const std::vector<double> _abstols;
+    const std::vector<double> _reltols;
+    const MathLib::VecNormType _norm_type;
+    bool _satisfied = true;
+    LocalToGlobalIndexMap const* _dof_table = nullptr;
+    MeshLib::Mesh const* _mesh = nullptr;
+    bool _is_first_iteration = true;
+    std::vector<double> _residual_norms_0;
+};
+
+std::unique_ptr<ConvergenceCriterionPerComponentResidual>
+createConvergenceCriterionPerComponentResidual(
+    BaseLib::ConfigTree const& config);
+
+}  // namespace NumLib
+
+#endif  // NUMLIB_CONVERGENCECRITERIONPERCOMPONENTRESIDUAL_H
-- 
GitLab