diff --git a/include/GooseFEM/Iterate.h b/include/GooseFEM/Iterate.h index f2e97ff..8e1254c 100644 --- a/include/GooseFEM/Iterate.h +++ b/include/GooseFEM/Iterate.h @@ -1,66 +1,80 @@ /** Support function for iterations. \file Iterate.h \copyright Copyright 2017. Tom de Geus. All rights reserved. \license This project is released under the GNU Public License (GPLv3). */ #ifndef GOOSEFEM_ITERATE_H #define GOOSEFEM_ITERATE_H #include "config.h" namespace GooseFEM { /** Support function for iterations in end-user programs. */ namespace Iterate { /** Class to perform a residual check based on the last "n" iterations. A typical usage is in dynamic simulations where equilibrium is checked based on a force residual. Fluctuations could however be responsible for this criterion to be triggered too early. By checking several time-steps such case can be avoided. */ class StopList { public: /** Constructor. \param n Number of consecutive iterations to consider. */ StopList(size_t n = 1); /** Reset all residuals to infinity. */ void reset(); /** Reset all residuals to infinity, and change the number of residuals to check. \param n Number of consecutive iterations to consider. */ void reset(size_t n); /** Update list of residuals, return `true` if all residuals are below the tolerance. \param res Current residual. \param tol Tolerance below which all last "n" iterations must lie. */ bool stop(double res, double tol); + /** + Update list of residuals, return `true` if all residuals are below the tolerance, + and all residuals are in a descending order. + + \param res Current residual. + \param tol Tolerance below which all last "n" iterations must lie. + */ + bool stop_sorted(double res, double tol); + + /** + Get the historic residuals. + */ + auto get() const; + private: std::vector m_res; ///< List with residuals. }; } // namespace Iterate } // namespace GooseFEM #include "Iterate.hpp" #endif diff --git a/include/GooseFEM/Iterate.hpp b/include/GooseFEM/Iterate.hpp index 11c9e11..58cdba7 100644 --- a/include/GooseFEM/Iterate.hpp +++ b/include/GooseFEM/Iterate.hpp @@ -1,47 +1,59 @@ /** Implementation of Iterate.h \file Iterate.hpp \copyright Copyright 2017. Tom de Geus. All rights reserved. \license This project is released under the GNU Public License (GPLv3). */ #ifndef GOOSEFEM_ITERATE_HPP #define GOOSEFEM_ITERATE_HPP #include "Iterate.h" namespace GooseFEM { namespace Iterate { inline StopList::StopList(size_t n) { m_res.resize(n); reset(); } inline void StopList::reset() { std::fill(m_res.begin(), m_res.end(), std::numeric_limits::infinity()); } inline void StopList::reset(size_t n) { m_res.resize(n); reset(); } inline bool StopList::stop(double res, double tol) +{ + std::rotate(m_res.begin(), m_res.begin() + 1, m_res.end()); + m_res.back() = res; + return !std::any_of(m_res.cbegin(), m_res.cend(), [=](const auto& i) { return i >= tol; }); +} + +inline bool StopList::stop_sorted(double res, double tol) { std::rotate(m_res.begin(), m_res.begin() + 1, m_res.end()); m_res.back() = res; if (res > tol) { return false; } - return std::is_sorted(m_res.begin(), m_res.end(), std::greater_equal()) && m_res.front() <= tol; + return std::is_sorted(m_res.cbegin(), m_res.cend(), std::greater_equal()) && m_res.front() <= tol; +} + +inline auto StopList::get() const +{ + return m_res; } } // namespace Iterate } // namespace GooseFEM #endif diff --git a/python/Iterate.hpp b/python/Iterate.hpp index c886c22..81a5ce7 100644 --- a/python/Iterate.hpp +++ b/python/Iterate.hpp @@ -1,39 +1,52 @@ /** \file \copyright Copyright 2017. Tom de Geus. All rights reserved. \license This project is released under the GNU Public License (GPLv3). */ #ifndef PYGOOSEFEM_ITERATE_H #define PYGOOSEFEM_ITERATE_H #include #include +#include namespace py = pybind11; void init_Iterate(py::module& mod) { py::class_ cls(mod, "Iterate"); cls.def(py::init(), "Class to perform a residual check based on the last 'n' iterations." "See :cpp:class:`GooseFEM::Iterate::StopList`.", py::arg("n") = 1); cls.def("reset", py::overload_cast<>(&GooseFEM::Iterate::StopList::reset), "Reset." "See :cpp:func:`GooseFEM::Iterate::StopList::reset`."); cls.def("stop", &GooseFEM::Iterate::StopList::stop, "Update list of residuals, return `true` if all residuals are below the tolerance." "See :cpp:func:`GooseFEM::Iterate::StopList::stop`.", py::arg("res"), py::arg("tol")); + cls.def("stop_sorted", + &GooseFEM::Iterate::StopList::stop_sorted, + "Update list of residuals, return `true` if all residuals are sorted and below the tolerance." + "See :cpp:func:`GooseFEM::Iterate::StopList::stop`.", + py::arg("res"), + py::arg("tol")); + + cls.def("get", + &GooseFEM::Iterate::StopList::get, + "Get the list of residuals." + "See :cpp:func:`GooseFEM::Iterate::StopList::get`."); + cls.def("__repr__", [](const GooseFEM::Iterate::StopList&) { return ""; }); } #endif diff --git a/test/basic/Iterate.cpp b/test/basic/Iterate.cpp index db6f797..e31767b 100644 --- a/test/basic/Iterate.cpp +++ b/test/basic/Iterate.cpp @@ -1,38 +1,63 @@ #include #include #include #include #include #define ISCLOSE(a,b) REQUIRE_THAT((a), Catch::WithinAbs((b), 1.e-12)); TEST_CASE("GooseFEM::Iterate", "Iterate.h") { - - SECTION("StopList - sorted input") + SECTION("StopList::stop - sorted input") { GooseFEM::Iterate::StopList stop(5); - // x x x x x v v v v v std::vector res = {5e+0, 5e+1, 5e-1, 5e-2, 5e-3, 5e-4, 4e-4, 3e-4, 2e-4, 1e-4}; std::vector conv = {false, false, false, false, false, false, false, false, false, true}; for (size_t i = 0; i < res.size(); ++i) { REQUIRE(stop.stop(res[i], 1e-3) == conv[i]); } + + std::vector m_res = { 0.05 , 0.005 , 0.0005, 0.0004, 0.0005}; } - SECTION("StopList - unsorted input") + SECTION("StopList::stop - unsorted input") + { + GooseFEM::Iterate::StopList a(5); + GooseFEM::Iterate::StopList b(5); + + std::vector res = {5e+0, 5e+1, 5e-1, 5e-2, 5e-3, 5e-4, 4e-4, 5e-4, 4e-4, 4e-4}; + std::vector conv = {false, false, false, false, false, false, false, false, false, true}; + + for (size_t i = 0; i < res.size(); ++i) { + REQUIRE(a.stop(res[i], 1e-3) == conv[i]); + REQUIRE(b.stop_sorted(res[i], 1e-3) == false); + } + } + + SECTION("StopList::stop_sorted - sorted input") + { + GooseFEM::Iterate::StopList stop(5); + + std::vector res = {5e+0, 5e+1, 5e-1, 5e-2, 5e-3, 5e-4, 4e-4, 3e-4, 2e-4, 1e-4}; + std::vector conv = {false, false, false, false, false, false, false, false, false, true}; + + for (size_t i = 0; i < res.size(); ++i) { + REQUIRE(stop.stop_sorted(res[i], 1e-3) == conv[i]); + } + } + + SECTION("StopList::stop_sorted - unsorted input") { GooseFEM::Iterate::StopList stop(5); - // x x x x x v v v v x v v v v std::vector res = {5e+0, 5e+1, 5e-1, 5e-2, 5e-3, 5e-4, 4e-4, 3e-4, 2e-4, 3e-4, 2e-4, 1e-4, 9e-5, 8e-5}; std::vector conv = {false, false, false, false, false, false, false, false, false, false, false, false, false, true}; for (size_t i = 0; i < res.size(); ++i) { - REQUIRE(stop.stop(res[i], 1e-3) == conv[i]); + REQUIRE(stop.stop_sorted(res[i], 1e-3) == conv[i]); } } }