diff --git a/Documentation/ProjectFile/prj/time_loop/i_time_loop.md b/Documentation/ProjectFile/prj/time_loop/i_time_loop.md index 576add64edccf5d980a0ff86143dc43a7109a788..aa3cece571f10e2567a06028b73a3626306799d2 100644 --- a/Documentation/ProjectFile/prj/time_loop/i_time_loop.md +++ b/Documentation/ProjectFile/prj/time_loop/i_time_loop.md @@ -1 +1 @@ -\ogs_missing_documentation +Time discretization loop. diff --git a/Documentation/ProjectFile/prj/time_loop/processes/process/t_time_stepping.md b/Documentation/ProjectFile/prj/time_loop/processes/process/t_time_stepping.md new file mode 100644 index 0000000000000000000000000000000000000000..2c33d8289fdb551170cc9460b7246af6acdeecf4 --- /dev/null +++ b/Documentation/ProjectFile/prj/time_loop/processes/process/t_time_stepping.md @@ -0,0 +1 @@ +Define a time stepping method for the modelling of a process. diff --git a/Documentation/ProjectFile/prj/time_loop/time_stepping/EvolutionaryPIDcontroller/c_EvolutionaryPIDcontroller.md b/Documentation/ProjectFile/prj/time_loop/time_stepping/EvolutionaryPIDcontroller/c_EvolutionaryPIDcontroller.md new file mode 100644 index 0000000000000000000000000000000000000000..730f3fe09a1c9d2661ad05c7eefea7edf7e2d72d --- /dev/null +++ b/Documentation/ProjectFile/prj/time_loop/time_stepping/EvolutionaryPIDcontroller/c_EvolutionaryPIDcontroller.md @@ -0,0 +1 @@ +\copydoc NumLib::EvolutionaryPIDcontroller diff --git a/Documentation/ProjectFile/prj/time_loop/time_stepping/EvolutionaryPIDcontroller/t_dt_guess.md b/Documentation/ProjectFile/prj/time_loop/time_stepping/EvolutionaryPIDcontroller/t_dt_guess.md new file mode 100644 index 0000000000000000000000000000000000000000..96fc64a7031a9a2b48ad4b8860842cdf3b216418 --- /dev/null +++ b/Documentation/ProjectFile/prj/time_loop/time_stepping/EvolutionaryPIDcontroller/t_dt_guess.md @@ -0,0 +1 @@ +The initial guess of time step size. diff --git a/Documentation/ProjectFile/prj/time_loop/time_stepping/EvolutionaryPIDcontroller/t_dt_max.md b/Documentation/ProjectFile/prj/time_loop/time_stepping/EvolutionaryPIDcontroller/t_dt_max.md new file mode 100644 index 0000000000000000000000000000000000000000..da4f14e93324fe19019153f66e3cf7926175e4f3 --- /dev/null +++ b/Documentation/ProjectFile/prj/time_loop/time_stepping/EvolutionaryPIDcontroller/t_dt_max.md @@ -0,0 +1 @@ +The maximum restriction of time step size. diff --git a/Documentation/ProjectFile/prj/time_loop/time_stepping/EvolutionaryPIDcontroller/t_dt_min.md b/Documentation/ProjectFile/prj/time_loop/time_stepping/EvolutionaryPIDcontroller/t_dt_min.md new file mode 100644 index 0000000000000000000000000000000000000000..c975b5939b85682055c0d50049ff03b2a74ade5a --- /dev/null +++ b/Documentation/ProjectFile/prj/time_loop/time_stepping/EvolutionaryPIDcontroller/t_dt_min.md @@ -0,0 +1 @@ +The minimum restriction of time step size. diff --git a/Documentation/ProjectFile/prj/time_loop/time_stepping/EvolutionaryPIDcontroller/t_rel_dt_max.md b/Documentation/ProjectFile/prj/time_loop/time_stepping/EvolutionaryPIDcontroller/t_rel_dt_max.md new file mode 100644 index 0000000000000000000000000000000000000000..b83f827b1b9a03f6a3fe474d03e904c3202ac72a --- /dev/null +++ b/Documentation/ProjectFile/prj/time_loop/time_stepping/EvolutionaryPIDcontroller/t_rel_dt_max.md @@ -0,0 +1 @@ +The maximum restriction of time step size ratio against the previous time step. diff --git a/Documentation/ProjectFile/prj/time_loop/time_stepping/EvolutionaryPIDcontroller/t_rel_dt_min.md b/Documentation/ProjectFile/prj/time_loop/time_stepping/EvolutionaryPIDcontroller/t_rel_dt_min.md new file mode 100644 index 0000000000000000000000000000000000000000..5d846abd2b589b661dfa5d19d20fd3b3ce40767b --- /dev/null +++ b/Documentation/ProjectFile/prj/time_loop/time_stepping/EvolutionaryPIDcontroller/t_rel_dt_min.md @@ -0,0 +1 @@ +The minimum restriction of time step size ratio against the previous time step. diff --git a/Documentation/ProjectFile/prj/time_loop/time_stepping/EvolutionaryPIDcontroller/t_specific_times.md b/Documentation/ProjectFile/prj/time_loop/time_stepping/EvolutionaryPIDcontroller/t_specific_times.md new file mode 100644 index 0000000000000000000000000000000000000000..92fff5fadbdc54f4f17ebfe9d85beb72be5ee700 --- /dev/null +++ b/Documentation/ProjectFile/prj/time_loop/time_stepping/EvolutionaryPIDcontroller/t_specific_times.md @@ -0,0 +1,2 @@ +The specified times that must be reached in the time stepping. + diff --git a/Documentation/ProjectFile/prj/time_loop/time_stepping/EvolutionaryPIDcontroller/t_t_end.md b/Documentation/ProjectFile/prj/time_loop/time_stepping/EvolutionaryPIDcontroller/t_t_end.md new file mode 100644 index 0000000000000000000000000000000000000000..ce96f332203a067242130456016be3ff2455f3d4 --- /dev/null +++ b/Documentation/ProjectFile/prj/time_loop/time_stepping/EvolutionaryPIDcontroller/t_t_end.md @@ -0,0 +1 @@ +The end time. diff --git a/Documentation/ProjectFile/prj/time_loop/time_stepping/EvolutionaryPIDcontroller/t_t_initial.md b/Documentation/ProjectFile/prj/time_loop/time_stepping/EvolutionaryPIDcontroller/t_t_initial.md new file mode 100644 index 0000000000000000000000000000000000000000..7ca53cb25af157b6f0b483ed4128adac11267fc8 --- /dev/null +++ b/Documentation/ProjectFile/prj/time_loop/time_stepping/EvolutionaryPIDcontroller/t_t_initial.md @@ -0,0 +1 @@ +The begin time. diff --git a/Documentation/ProjectFile/prj/time_loop/time_stepping/EvolutionaryPIDcontroller/t_tol.md b/Documentation/ProjectFile/prj/time_loop/time_stepping/EvolutionaryPIDcontroller/t_tol.md new file mode 100644 index 0000000000000000000000000000000000000000..804855455c24028e0b0f842e004cd0b71583e33d --- /dev/null +++ b/Documentation/ProjectFile/prj/time_loop/time_stepping/EvolutionaryPIDcontroller/t_tol.md @@ -0,0 +1 @@ +The tolerance of the relative error that controls the time stepping. diff --git a/Documentation/ProjectFile/prj/time_loop/time_stepping/FixedTimeStepping/c_FixedTimeStepping.md b/Documentation/ProjectFile/prj/time_loop/time_stepping/FixedTimeStepping/c_FixedTimeStepping.md index 576add64edccf5d980a0ff86143dc43a7109a788..6c73dd28077ea87e2a77268299c7d816776654b0 100644 --- a/Documentation/ProjectFile/prj/time_loop/time_stepping/FixedTimeStepping/c_FixedTimeStepping.md +++ b/Documentation/ProjectFile/prj/time_loop/time_stepping/FixedTimeStepping/c_FixedTimeStepping.md @@ -1 +1 @@ -\ogs_missing_documentation +Defines a fixed time step method. diff --git a/Documentation/ProjectFile/prj/time_loop/time_stepping/FixedTimeStepping/t_t_end.md b/Documentation/ProjectFile/prj/time_loop/time_stepping/FixedTimeStepping/t_t_end.md index 576add64edccf5d980a0ff86143dc43a7109a788..ce96f332203a067242130456016be3ff2455f3d4 100644 --- a/Documentation/ProjectFile/prj/time_loop/time_stepping/FixedTimeStepping/t_t_end.md +++ b/Documentation/ProjectFile/prj/time_loop/time_stepping/FixedTimeStepping/t_t_end.md @@ -1 +1 @@ -\ogs_missing_documentation +The end time. diff --git a/Documentation/ProjectFile/prj/time_loop/time_stepping/FixedTimeStepping/t_t_initial.md b/Documentation/ProjectFile/prj/time_loop/time_stepping/FixedTimeStepping/t_t_initial.md index 576add64edccf5d980a0ff86143dc43a7109a788..7ca53cb25af157b6f0b483ed4128adac11267fc8 100644 --- a/Documentation/ProjectFile/prj/time_loop/time_stepping/FixedTimeStepping/t_t_initial.md +++ b/Documentation/ProjectFile/prj/time_loop/time_stepping/FixedTimeStepping/t_t_initial.md @@ -1 +1 @@ -\ogs_missing_documentation +The begin time. diff --git a/Documentation/ProjectFile/prj/time_loop/time_stepping/SingleStep/c_SingleStep.md b/Documentation/ProjectFile/prj/time_loop/time_stepping/SingleStep/c_SingleStep.md index 576add64edccf5d980a0ff86143dc43a7109a788..116071e10a23f299e7fa086986f3eac260d1c677 100644 --- a/Documentation/ProjectFile/prj/time_loop/time_stepping/SingleStep/c_SingleStep.md +++ b/Documentation/ProjectFile/prj/time_loop/time_stepping/SingleStep/c_SingleStep.md @@ -1 +1 @@ -\ogs_missing_documentation +Defines a SingleStep time stepper. diff --git a/Documentation/ProjectFile/prj/time_loop/time_stepping/i_time_stepping.md b/Documentation/ProjectFile/prj/time_loop/time_stepping/i_time_stepping.md index 576add64edccf5d980a0ff86143dc43a7109a788..3e5fafed0952b5cc42ed90619f990818ab02883a 100644 --- a/Documentation/ProjectFile/prj/time_loop/time_stepping/i_time_stepping.md +++ b/Documentation/ProjectFile/prj/time_loop/time_stepping/i_time_stepping.md @@ -1 +1 @@ -\ogs_missing_documentation +Defines a time stepper. diff --git a/Documentation/ProjectFile/prj/time_loop/time_stepping/t_type.md b/Documentation/ProjectFile/prj/time_loop/time_stepping/t_type.md index 576add64edccf5d980a0ff86143dc43a7109a788..2cac2c7c0deff1b4f42f6fa915daca19e8c51d31 100644 --- a/Documentation/ProjectFile/prj/time_loop/time_stepping/t_type.md +++ b/Documentation/ProjectFile/prj/time_loop/time_stepping/t_type.md @@ -1 +1 @@ -\ogs_missing_documentation +The type of a time stepping method. diff --git a/Documentation/bibliography.bib b/Documentation/bibliography.bib index c101c7cb1f04389646fda183d0e4026aae003ad9..696f7b6de36ad5615b4aff1f1c1349a901dc0166 100644 --- a/Documentation/bibliography.bib +++ b/Documentation/bibliography.bib @@ -110,3 +110,13 @@ keywords = "Mont Terri ", abstract = "Abstract The constitutive modeling of shales is an important topic in the geomechanics community as it is often encountered in advanced applications such as nuclear waste storage, \{CO2\} sequestration, and unconventional oil and gas exploitation. The goal of this work is to describe, within a unique plastic-damage framework, the full mechanical behavior of shales such as pre-peak hardening plasticity, non-linearity in the onset of inelastic strains, dilatancy, post-peak softening, and degradation of the elastic parameters. The model validation against three sets of triaxial experimental results on shale demonstrates its capability to reproduce the main mechanical characteristics. " } + +@article{ahmed2015adaptive, + title={Adaptive time step control for higher order variational time discretizations applied to convection--diffusion--reaction equations}, + author={Ahmed, Naveed and John, Volker}, + journal={Computer Methods in Applied Mechanics and Engineering}, + volume={285}, + pages={83--101}, + year={2015}, + publisher={Elsevier} +} diff --git a/NumLib/ODESolver/ConvergenceCriterion.h b/NumLib/ODESolver/ConvergenceCriterion.h index 154b23b4de4c4d7514a4507d1ad2670d45495528..59babdd8450e4a88623e1a2c5d183b7372f56b4d 100644 --- a/NumLib/ODESolver/ConvergenceCriterion.h +++ b/NumLib/ODESolver/ConvergenceCriterion.h @@ -10,6 +10,7 @@ #pragma once #include <memory> +#include "MathLib/LinAlg/LinAlg.h" // For MathLib::VecNormType #include "NumLib/NumericsConfig.h" namespace BaseLib { @@ -27,6 +28,11 @@ namespace NumLib class ConvergenceCriterion { public: + ConvergenceCriterion(const MathLib::VecNormType norm_type) + : _norm_type(norm_type) + { + } + //! Tells if the change of the solution between iterations is checked. //! //! \remark @@ -76,11 +82,14 @@ public: //! Tell if the convergence criterion is satisfied. virtual bool isSatisfied() const { return _satisfied; }; + MathLib::VecNormType getVectorNormType () const { return _norm_type; } + virtual ~ConvergenceCriterion() = default; protected: bool _satisfied = true; bool _is_first_iteration = true; + const MathLib::VecNormType _norm_type; }; //! Creates a convergence criterion from the given configuration. diff --git a/NumLib/ODESolver/ConvergenceCriterionDeltaX.cpp b/NumLib/ODESolver/ConvergenceCriterionDeltaX.cpp index 88de56434264079d10a16d5fb528cb703bf0a6ee..1bbece408622b75a3d9f16963e343b707dfc350c 100644 --- a/NumLib/ODESolver/ConvergenceCriterionDeltaX.cpp +++ b/NumLib/ODESolver/ConvergenceCriterionDeltaX.cpp @@ -18,10 +18,9 @@ namespace NumLib ConvergenceCriterionDeltaX::ConvergenceCriterionDeltaX( boost::optional<double>&& absolute_tolerance, boost::optional<double>&& relative_tolerance, - MathLib::VecNormType norm_type) - : _abstol(std::move(absolute_tolerance)), - _reltol(std::move(relative_tolerance)), - _norm_type(norm_type) + const MathLib::VecNormType norm_type) + : ConvergenceCriterion(norm_type), _abstol(std::move(absolute_tolerance)), + _reltol(std::move(relative_tolerance)) { if ((!_abstol) && (!_reltol)) OGS_FATAL( diff --git a/NumLib/ODESolver/ConvergenceCriterionDeltaX.h b/NumLib/ODESolver/ConvergenceCriterionDeltaX.h index 75ede7ec830da063c8cb22d4b466c8e17e12a87f..71715ca1950c2e25469e5ddace7d37d9a6ca1efd 100644 --- a/NumLib/ODESolver/ConvergenceCriterionDeltaX.h +++ b/NumLib/ODESolver/ConvergenceCriterionDeltaX.h @@ -27,7 +27,7 @@ public: ConvergenceCriterionDeltaX( boost::optional<double>&& absolute_tolerance, boost::optional<double>&& relative_tolerance, - MathLib::VecNormType norm_type); + const MathLib::VecNormType norm_type); bool hasDeltaXCheck() const override { return true; } bool hasResidualCheck() const override { return false; } @@ -40,7 +40,6 @@ public: private: const boost::optional<double> _abstol; const boost::optional<double> _reltol; - const MathLib::VecNormType _norm_type; }; std::unique_ptr<ConvergenceCriterionDeltaX> createConvergenceCriterionDeltaX( diff --git a/NumLib/ODESolver/ConvergenceCriterionPerComponent.h b/NumLib/ODESolver/ConvergenceCriterionPerComponent.h index 0ff42fdebf1c5c6f302110ce0df9158242e64f90..8da246ad60d1856e59dec0f516a5061665b79194 100644 --- a/NumLib/ODESolver/ConvergenceCriterionPerComponent.h +++ b/NumLib/ODESolver/ConvergenceCriterionPerComponent.h @@ -11,6 +11,8 @@ #include "ConvergenceCriterion.h" +#include "MathLib/LinAlg/LinAlg.h" // For MathLib::VecNormType + namespace MeshLib { class Mesh; @@ -26,6 +28,9 @@ class LocalToGlobalIndexMap; class ConvergenceCriterionPerComponent : public ConvergenceCriterion { public: + ConvergenceCriterionPerComponent(const MathLib::VecNormType norm_type) + :ConvergenceCriterion(norm_type) {} + //! Sets the d.o.f. table used to extract data for a specific component. virtual void setDOFTable(NumLib::LocalToGlobalIndexMap const& dof_table, MeshLib::Mesh const& mesh) = 0; diff --git a/NumLib/ODESolver/ConvergenceCriterionPerComponentDeltaX.cpp b/NumLib/ODESolver/ConvergenceCriterionPerComponentDeltaX.cpp index b9faaeed41ed0f4ae6e37633a7185bee5e30b930..f5ef433565e803e7d0f9a1ee6141cdbe1ee3f51e 100644 --- a/NumLib/ODESolver/ConvergenceCriterionPerComponentDeltaX.cpp +++ b/NumLib/ODESolver/ConvergenceCriterionPerComponentDeltaX.cpp @@ -19,10 +19,10 @@ namespace NumLib ConvergenceCriterionPerComponentDeltaX::ConvergenceCriterionPerComponentDeltaX( 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) + const MathLib::VecNormType norm_type) + : ConvergenceCriterionPerComponent(norm_type), + _abstols(std::move(absolute_tolerances)), + _reltols(std::move(relative_tolerances)) { if (_abstols.size() != _reltols.size()) OGS_FATAL( diff --git a/NumLib/ODESolver/ConvergenceCriterionPerComponentDeltaX.h b/NumLib/ODESolver/ConvergenceCriterionPerComponentDeltaX.h index a35ea708527e8c83dc54775637b05e33692b2180..3622a32e286d9b979749181d94a014e4b2eebdd6 100644 --- a/NumLib/ODESolver/ConvergenceCriterionPerComponentDeltaX.h +++ b/NumLib/ODESolver/ConvergenceCriterionPerComponentDeltaX.h @@ -29,7 +29,7 @@ public: ConvergenceCriterionPerComponentDeltaX( std::vector<double>&& absolute_tolerances, std::vector<double>&& relative_tolerances, - MathLib::VecNormType norm_type); + const MathLib::VecNormType norm_type); bool hasDeltaXCheck() const override { return true; } bool hasResidualCheck() const override { return false; } @@ -46,7 +46,6 @@ public: private: const std::vector<double> _abstols; const std::vector<double> _reltols; - const MathLib::VecNormType _norm_type; LocalToGlobalIndexMap const* _dof_table = nullptr; MeshLib::Mesh const* _mesh = nullptr; }; diff --git a/NumLib/ODESolver/ConvergenceCriterionPerComponentResidual.cpp b/NumLib/ODESolver/ConvergenceCriterionPerComponentResidual.cpp index 29600a819952605a9669b730bef056398eaa357e..acd515f7ccab2dc91f921428ac29f554d13ff568 100644 --- a/NumLib/ODESolver/ConvergenceCriterionPerComponentResidual.cpp +++ b/NumLib/ODESolver/ConvergenceCriterionPerComponentResidual.cpp @@ -20,10 +20,10 @@ ConvergenceCriterionPerComponentResidual:: ConvergenceCriterionPerComponentResidual( std::vector<double>&& absolute_tolerances, std::vector<double>&& relative_tolerances, - MathLib::VecNormType norm_type) - : _abstols(std::move(absolute_tolerances)), + const MathLib::VecNormType norm_type) + : ConvergenceCriterionPerComponent(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()) diff --git a/NumLib/ODESolver/ConvergenceCriterionPerComponentResidual.h b/NumLib/ODESolver/ConvergenceCriterionPerComponentResidual.h index f54c1a6a932e04919a0d4f4dcc8f5d5f7dd755a2..743a6fc5da5b63e9428de7d94cd63d312e721257 100644 --- a/NumLib/ODESolver/ConvergenceCriterionPerComponentResidual.h +++ b/NumLib/ODESolver/ConvergenceCriterionPerComponentResidual.h @@ -30,7 +30,7 @@ public: ConvergenceCriterionPerComponentResidual( std::vector<double>&& absolute_tolerances, std::vector<double>&& relative_tolerances, - MathLib::VecNormType norm_type); + const MathLib::VecNormType norm_type); bool hasDeltaXCheck() const override { return true; } bool hasResidualCheck() const override { return true; } @@ -47,7 +47,6 @@ public: private: const std::vector<double> _abstols; const std::vector<double> _reltols; - const MathLib::VecNormType _norm_type; LocalToGlobalIndexMap const* _dof_table = nullptr; MeshLib::Mesh const* _mesh = nullptr; std::vector<double> _residual_norms_0; diff --git a/NumLib/ODESolver/ConvergenceCriterionResidual.cpp b/NumLib/ODESolver/ConvergenceCriterionResidual.cpp index dc437fc66fe3ecf6dbe14e6e31dfd42f1332973f..e160ac6a4bf7a4385d711f0056fca15d41ddf2bb 100644 --- a/NumLib/ODESolver/ConvergenceCriterionResidual.cpp +++ b/NumLib/ODESolver/ConvergenceCriterionResidual.cpp @@ -18,10 +18,9 @@ namespace NumLib ConvergenceCriterionResidual::ConvergenceCriterionResidual( boost::optional<double>&& absolute_tolerance, boost::optional<double>&& relative_tolerance, - MathLib::VecNormType norm_type) - : _abstol(std::move(absolute_tolerance)), - _reltol(std::move(relative_tolerance)), - _norm_type(norm_type) + const MathLib::VecNormType norm_type) + : ConvergenceCriterion(norm_type), _abstol(std::move(absolute_tolerance)), + _reltol(std::move(relative_tolerance)) { if ((!_abstol) && (!_reltol)) OGS_FATAL( @@ -29,8 +28,8 @@ ConvergenceCriterionResidual::ConvergenceCriterionResidual( "specified."); } -void ConvergenceCriterionResidual::checkDeltaX(const GlobalVector& minus_delta_x, - GlobalVector const& x) +void ConvergenceCriterionResidual::checkDeltaX( + const GlobalVector& minus_delta_x, GlobalVector const& x) { auto error_dx = MathLib::LinAlg::norm(minus_delta_x, _norm_type); auto norm_x = MathLib::LinAlg::norm(x, _norm_type); @@ -43,21 +42,38 @@ void ConvergenceCriterionResidual::checkResidual(const GlobalVector& residual) { auto norm_res = MathLib::LinAlg::norm(residual, _norm_type); - if (_is_first_iteration) { + if (_is_first_iteration) + { INFO("Convergence criterion: |r0|=%.4e", norm_res); _residual_norm_0 = norm_res; - } else { - INFO("Convergence criterion: |r|=%.4e |r0|=%.4e |r|/|r0|=%.4e", - norm_res, _residual_norm_0, norm_res / _residual_norm_0); + } + else + { + _residual_norm_0 = + (_residual_norm_0 < std::numeric_limits<double>::epsilon()) + ? norm_res + : _residual_norm_0; + if (_residual_norm_0 < std::numeric_limits<double>::epsilon()) + { + INFO("Convergence criterion: |r|=%.4e |r0|=%.4e", + norm_res, _residual_norm_0); + } + else + { + INFO("Convergence criterion: |r|=%.4e |r0|=%.4e |r|/|r0|=%.4e", + norm_res, _residual_norm_0, norm_res / _residual_norm_0); + } } bool satisfied_abs = false; bool satisfied_rel = false; - if (_abstol) { + if (_abstol) + { satisfied_abs = norm_res < *_abstol; } - if (_reltol && !_is_first_iteration) { + if (_reltol && !_is_first_iteration) + { satisfied_rel = checkRelativeTolerance(*_reltol, norm_res, _residual_norm_0); } diff --git a/NumLib/ODESolver/ConvergenceCriterionResidual.h b/NumLib/ODESolver/ConvergenceCriterionResidual.h index b598cfbc83a3920dcbcdf4df61ba4473b5d57b41..b63af27e9b9af5010a7e7f297341b567b4db28d0 100644 --- a/NumLib/ODESolver/ConvergenceCriterionResidual.h +++ b/NumLib/ODESolver/ConvergenceCriterionResidual.h @@ -27,7 +27,7 @@ public: ConvergenceCriterionResidual( boost::optional<double>&& absolute_tolerance, boost::optional<double>&& relative_tolerance, - MathLib::VecNormType norm_type); + const MathLib::VecNormType norm_type); bool hasDeltaXCheck() const override { return true; } bool hasResidualCheck() const override { return true; } @@ -41,7 +41,6 @@ public: private: const boost::optional<double> _abstol; const boost::optional<double> _reltol; - const MathLib::VecNormType _norm_type; double _residual_norm_0; }; diff --git a/NumLib/ODESolver/TimeDiscretization.cpp b/NumLib/ODESolver/TimeDiscretization.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e7cf70c71188bf60c952ccd3af9c1d07cf47ae3b --- /dev/null +++ b/NumLib/ODESolver/TimeDiscretization.cpp @@ -0,0 +1,138 @@ +/** + * \copyright + * Copyright (c) 2012-2017, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + * \file TimeDiscretization.cpp + * Created on March 31, 2017, 10:30 AM + */ + +#include "TimeDiscretization.h" + +#include "MathLib/LinAlg/MatrixVectorTraits.h" + +namespace NumLib +{ +double TimeDiscretization::computeRelativeChangeFromPreviousTimestep( + GlobalVector const& x, + GlobalVector const& x_old, + MathLib::VecNormType norm_type) +{ + if (norm_type == MathLib::VecNormType::INVALID) + { + OGS_FATAL("An invalid norm type has been passed"); + } + + if (!_dx) + _dx = MathLib::MatrixVectorTraits<GlobalVector>::newInstance(x); + + auto& dx = *_dx; + MathLib::LinAlg::copy(x, dx); // copy x to dx. + + // dx = x - x_old --> x - dx --> dx + MathLib::LinAlg::axpy(dx, -1.0, x_old); + const double norm_dx = MathLib::LinAlg::norm(dx, norm_type); + + const double norm_x = MathLib::LinAlg::norm(x, norm_type); + if (norm_x > std::numeric_limits<double>::epsilon()) + return norm_dx / norm_x; + + // Both of norm_x and norm_dx are close to zero + if (norm_dx < std::numeric_limits<double>::epsilon()) + return 1.0; + + // Only norm_x is close to zero + return norm_dx / std::numeric_limits<double>::epsilon(); +} + +double BackwardEuler::getRelativeChangeFromPreviousTimestep( + GlobalVector const& x, MathLib::VecNormType norm_type) +{ + return computeRelativeChangeFromPreviousTimestep(x, _x_old, norm_type); +} + +double ForwardEuler::getRelativeChangeFromPreviousTimestep( + GlobalVector const& x, MathLib::VecNormType norm_type) +{ + return computeRelativeChangeFromPreviousTimestep(x, _x_old, norm_type); +} + +double CrankNicolson::getRelativeChangeFromPreviousTimestep( + GlobalVector const& x, MathLib::VecNormType norm_type) +{ + return computeRelativeChangeFromPreviousTimestep(x, _x_old, norm_type); +} + +double BackwardDifferentiationFormula::getRelativeChangeFromPreviousTimestep( + GlobalVector const& x, MathLib::VecNormType norm_type) +{ + return computeRelativeChangeFromPreviousTimestep( + x, *_xs_old[_offset], norm_type); +} + +void BackwardDifferentiationFormula::pushState(const double, + GlobalVector const& x, + InternalMatrixStorage const&) +{ + namespace LinAlg = MathLib::LinAlg; + // TODO use boost circular buffer? + + // until _xs_old is filled, lower-order BDF formulas are used. + if (_xs_old.size() < _num_steps) + { + _xs_old.push_back(&NumLib::GlobalVectorProvider::provider.getVector(x)); + } + else + { + LinAlg::copy(x, *_xs_old[_offset]); + _offset = + (_offset + 1) % _num_steps; // treat _xs_old as a circular buffer + } +} + +namespace detail +{ +//! Coefficients used in the backward differentiation formulas. +const double BDF_Coeffs[6][7] = { + // leftmost column: weight of the solution at the new timestep + // signs of columns > 1 are flipped compared to standard BDF tableaus + {1.0, 1.0}, + {1.5, 2.0, -0.5}, + {11.0 / 6.0, 3.0, -1.5, 1.0 / 3.0}, + {25.0 / 12.0, 4.0, -3.0, 4.0 / 3.0, -0.25}, + {137.0 / 60.0, 5.0, -5.0, 10.0 / 3.0, -1.25, 1.0 / 5.0}, + {147.0 / 60.0, 6.0, -7.5, 20.0 / 3.0, -3.75, 6.0 / 5.0, -1.0 / 6.0} + // coefficient of (for BDF(6), the oldest state, x_n, is always rightmost) + // x_+6, x_+5, x_+4, x_+3, x_+2, x_+1, x_n +}; +} + +double BackwardDifferentiationFormula::getNewXWeight() const +{ + auto const k = eff_num_steps(); + return detail::BDF_Coeffs[k - 1][0] / _delta_t; +} + +void BackwardDifferentiationFormula::getWeightedOldX(GlobalVector& y) const +{ + namespace LinAlg = MathLib::LinAlg; + + auto const k = eff_num_steps(); + auto const* const BDFk = detail::BDF_Coeffs[k - 1]; + + // compute linear combination \sum_{i=0}^{k-1} BDFk_{k-i} \cdot x_{n+i} + LinAlg::copy(*_xs_old[_offset], y); // _xs_old[offset] = x_n + LinAlg::scale(y, BDFk[k]); + + for (unsigned i = 1; i < k; ++i) + { + auto const off = (_offset + i) % k; + LinAlg::axpy(y, BDFk[k - i], *_xs_old[off]); + } + + LinAlg::scale(y, 1.0 / _delta_t); +} + +} // end of namespace NumLib diff --git a/NumLib/ODESolver/TimeDiscretization.h b/NumLib/ODESolver/TimeDiscretization.h index 0db170d920e23a839d14025ff862284e4f07e7d6..ede512492ec2320005e834958b5e2d148584305a 100644 --- a/NumLib/ODESolver/TimeDiscretization.h +++ b/NumLib/ODESolver/TimeDiscretization.h @@ -114,9 +114,20 @@ public: class TimeDiscretization { public: + TimeDiscretization() = default; + //! Sets the initial condition. virtual void setInitialState(const double t0, GlobalVector const& x0) = 0; + /*! Get the relative change of solutions between two successive time steps + * by \f$ e_n = \|u^{n+1}-u^{n}\|/\|u^{n+1}\| \f$. + * + * \param x The solution at the current timestep. + * \param norm_type The type of global vector norm. + */ + virtual double getRelativeChangeFromPreviousTimestep( + GlobalVector const& x, MathLib::VecNormType norm_type) = 0; + /*! Indicate that the current timestep is done and that you will proceed to * the next one. * @@ -200,6 +211,25 @@ public: */ virtual bool needsPreload() const { return false; } //! @} + +protected: + std::unique_ptr<GlobalVector> _dx; ///< Used to store \f$ u_{n+1}-u_{n}\f$. + + /** + * Compute and return the relative change of solutions between two successive + * time steps by \f$ e_n = \|u^{n+1}-u^{n}\|/\|u^{n+1}\| \f$. + * + * @param x The current solution + * @param x_old The previous solution + * @param norm_type The norm type of global vector + * @return \f$ e_n = \|u^{n+1}-u^{n}\|/\|u^{n+1}\| \f$. + * + * @warning the value of x_old is changed to x - x_old after this computation. + */ + double computeRelativeChangeFromPreviousTimestep( + GlobalVector const& x, + GlobalVector const& x_old, + MathLib::VecNormType norm_type); }; //! Backward Euler scheme. @@ -222,6 +252,9 @@ public: MathLib::LinAlg::copy(x0, _x_old); } + double getRelativeChangeFromPreviousTimestep( + GlobalVector const& x, MathLib::VecNormType norm_type) override; + void pushState(const double /*t*/, GlobalVector const& x, InternalMatrixStorage const&) override { @@ -256,7 +289,7 @@ class ForwardEuler final : public TimeDiscretization { public: ForwardEuler() - : _x_old(NumLib::GlobalVectorProvider::provider.getVector()) + : _x_old(NumLib::GlobalVectorProvider::provider.getVector()) { } @@ -272,6 +305,9 @@ public: MathLib::LinAlg::copy(x0, _x_old); } + double getRelativeChangeFromPreviousTimestep( + GlobalVector const& x, MathLib::VecNormType norm_type) override; + void pushState(const double /*t*/, GlobalVector const& x, InternalMatrixStorage const&) override { @@ -346,6 +382,9 @@ public: MathLib::LinAlg::copy(x0, _x_old); } + double getRelativeChangeFromPreviousTimestep( + GlobalVector const& x, MathLib::VecNormType norm_type) override; + void pushState(const double, GlobalVector const& x, InternalMatrixStorage const& strg) override { @@ -382,23 +421,6 @@ private: GlobalVector& _x_old; //!< the solution from the preceding timestep }; -namespace detail -{ -//! Coefficients used in the backward differentiation formulas. -const double BDF_Coeffs[6][7] = { - // leftmost column: weight of the solution at the new timestep - // signs of columns > 1 are flipped compared to standard BDF tableaus - {1.0, 1.0}, - {1.5, 2.0, -0.5}, - {11.0 / 6.0, 3.0, -1.5, 1.0 / 3.0}, - {25.0 / 12.0, 4.0, -3.0, 4.0 / 3.0, -0.25}, - {137.0 / 60.0, 5.0, -5.0, 10.0 / 3.0, -1.25, 1.0 / 5.0}, - {147.0 / 60.0, 6.0, -7.5, 20.0 / 3.0, -3.75, 6.0 / 5.0, -1.0 / 6.0} - // coefficient of (for BDF(6), the oldest state, x_n, is always rightmost) - // x_+6, x_+5, x_+4, x_+3, x_+2, x_+1, x_n -}; -} - //! Backward differentiation formula. class BackwardDifferentiationFormula final : public TimeDiscretization { @@ -416,7 +438,7 @@ public: * the first timesteps. */ explicit BackwardDifferentiationFormula(const unsigned num_steps) - : _num_steps(num_steps) + : _num_steps(num_steps) { assert(1 <= num_steps && num_steps <= 6); _xs_old.reserve(num_steps); @@ -435,25 +457,11 @@ public: &NumLib::GlobalVectorProvider::provider.getVector(x0)); } + double getRelativeChangeFromPreviousTimestep( + GlobalVector const& x, MathLib::VecNormType norm_type) override; + void pushState(const double, GlobalVector const& x, - InternalMatrixStorage const&) override - { - namespace LinAlg = MathLib::LinAlg; - // TODO use boost cirular buffer? - - // until _xs_old is filled, lower-order BDF formulas are used. - if (_xs_old.size() < _num_steps) - { - _xs_old.push_back( - &NumLib::GlobalVectorProvider::provider.getVector(x)); - } - else - { - LinAlg::copy(x, *_xs_old[_offset]); - _offset = (_offset + 1) % - _num_steps; // treat _xs_old as a circular buffer - } - } + InternalMatrixStorage const&) override; void nextTimestep(const double t, const double delta_t) override { @@ -462,31 +470,10 @@ public: } double getCurrentTime() const override { return _t; } - double getNewXWeight() const override - { - auto const k = eff_num_steps(); - return detail::BDF_Coeffs[k - 1][0] / _delta_t; - } - - void getWeightedOldX(GlobalVector& y) const override - { - namespace LinAlg = MathLib::LinAlg; - - auto const k = eff_num_steps(); - auto const* const BDFk = detail::BDF_Coeffs[k - 1]; - // compute linear combination \sum_{i=0}^{k-1} BDFk_{k-i} \cdot x_{n+i} - LinAlg::copy(*_xs_old[_offset], y); // _xs_old[offset] = x_n - LinAlg::scale(y, BDFk[k]); + double getNewXWeight() const override; - for (unsigned i = 1; i < k; ++i) - { - auto const off = (_offset + i) % k; - LinAlg::axpy(y, BDFk[k - i], *_xs_old[off]); - } - - LinAlg::scale(y, 1.0 / _delta_t); - } + void getWeightedOldX(GlobalVector& y) const override; private: std::size_t eff_num_steps() const { return _xs_old.size(); } diff --git a/NumLib/TimeStepping/Algorithms/CreateEvolutionaryPIDcontroller.cpp b/NumLib/TimeStepping/Algorithms/CreateEvolutionaryPIDcontroller.cpp new file mode 100644 index 0000000000000000000000000000000000000000..84b63208c16ec97563017fcaeec4d48e414b0abc --- /dev/null +++ b/NumLib/TimeStepping/Algorithms/CreateEvolutionaryPIDcontroller.cpp @@ -0,0 +1,66 @@ +/** + * \copyright + * Copyright (c) 2012-2017, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + * \file CreateEvolutionaryPIDcontroller.cpp + * Created on June 26, 2017, 4:43 PM + */ + +#include "CreateEvolutionaryPIDcontroller.h" + +#include "BaseLib/ConfigTree.h" +#include "BaseLib/makeVectorUnique.h" + +#include "EvolutionaryPIDcontroller.h" +#include "TimeStepAlgorithm.h" + +namespace NumLib +{ +class TimeStepAlgorithm; +std::unique_ptr<TimeStepAlgorithm> createEvolutionaryPIDcontroller( + BaseLib::ConfigTree const& config) +{ + //! \ogs_file_param{prj__time_loop__time_stepping__type} + config.checkConfigParameter("type", "EvolutionaryPIDcontroller"); + + //! \ogs_file_param{prj__time_loop__time_stepping__EvolutionaryPIDcontroller__t_initial} + auto const t0 = config.getConfigParameter<double>("t_initial"); + //! \ogs_file_param{prj__time_loop__time_stepping__EvolutionaryPIDcontroller__t_end} + auto const t_end = config.getConfigParameter<double>("t_end"); + //! \ogs_file_param{prj__time_loop__time_stepping__EvolutionaryPIDcontroller__dt_guess} + auto const h0 = config.getConfigParameter<double>("dt_guess"); + + //! \ogs_file_param{prj__time_loop__time_stepping__EvolutionaryPIDcontroller__dt_min} + auto const h_min = config.getConfigParameter<double>("dt_min"); + //! \ogs_file_param{prj__time_loop__time_stepping__EvolutionaryPIDcontroller__dt_max} + auto const h_max = config.getConfigParameter<double>("dt_max"); + //! \ogs_file_param{prj__time_loop__time_stepping__EvolutionaryPIDcontroller__rel_dt_min} + auto const rel_h_min = config.getConfigParameter<double>("rel_dt_min"); + //! \ogs_file_param{prj__time_loop__time_stepping__EvolutionaryPIDcontroller__rel_dt_max} + auto const rel_h_max = config.getConfigParameter<double>("rel_dt_max"); + + auto specific_times = + //! \ogs_file_param{prj__time_loop__time_stepping__EvolutionaryPIDcontroller__specific_times} + config.getConfigParameter<std::vector<double>>( + "specific_times", std::vector<double>{}); + if (!specific_times.empty()) + { + // Sort in descending order. + std::sort(specific_times.begin(), + specific_times.end(), + std::greater<double>()); + // Remove possible duplicated elements. + BaseLib::makeVectorUnique(specific_times); + } + + //! \ogs_file_param{prj__time_loop__time_stepping__EvolutionaryPIDcontroller__tol} + auto const tol = config.getConfigParameter<double>("tol"); + + return std::make_unique<EvolutionaryPIDcontroller>( + t0, t_end, h0, h_min, h_max, rel_h_min, rel_h_max, + std::move(specific_times), tol); +} +} // end of namespace NumLib diff --git a/NumLib/TimeStepping/Algorithms/CreateEvolutionaryPIDcontroller.h b/NumLib/TimeStepping/Algorithms/CreateEvolutionaryPIDcontroller.h new file mode 100644 index 0000000000000000000000000000000000000000..84c35364026d0440df1925d00daaac17012c5ca2 --- /dev/null +++ b/NumLib/TimeStepping/Algorithms/CreateEvolutionaryPIDcontroller.h @@ -0,0 +1,29 @@ +/** + * \copyright + * Copyright (c) 2012-2017, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + * \file CreateEvolutionaryPIDcontroller.h + * Created on June 26, 2017, 4:43 PM + */ + +#pragma once + +#include <memory> + +namespace BaseLib +{ +class ConfigTree; +} + +namespace NumLib +{ +class TimeStepAlgorithm; + +/// Create an EvolutionaryPIDcontroller time stepper from the given +/// configuration +std::unique_ptr<TimeStepAlgorithm> createEvolutionaryPIDcontroller( + BaseLib::ConfigTree const& config); +} // end of namespace NumLib diff --git a/NumLib/TimeStepping/Algorithms/CreateFixedTimeStepping.cpp b/NumLib/TimeStepping/Algorithms/CreateFixedTimeStepping.cpp new file mode 100644 index 0000000000000000000000000000000000000000..edc2085f479ec4d408b3cce8f888cfa0d6dcd58e --- /dev/null +++ b/NumLib/TimeStepping/Algorithms/CreateFixedTimeStepping.cpp @@ -0,0 +1,82 @@ +/** + * \copyright + * Copyright (c) 2012-2017, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + * \file CreateFixedTimeStepping.cpp + * Created on June 26, 2017, 5:03 PM + */ + +#include "CreateFixedTimeStepping.h" +#include <string> + +#include "BaseLib/ConfigTree.h" +#include "BaseLib/Error.h" + +#include "FixedTimeStepping.h" +#include "TimeStepAlgorithm.h" + +namespace NumLib +{ +class TimeStepAlgorithm; +std::unique_ptr<TimeStepAlgorithm> createFixedTimeStepping( + BaseLib::ConfigTree const& config) +{ + //! \ogs_file_param{prj__time_loop__time_stepping__type} + config.checkConfigParameter("type", "FixedTimeStepping"); + + //! \ogs_file_param{prj__time_loop__time_stepping__FixedTimeStepping__t_initial} + auto const t_initial = config.getConfigParameter<double>("t_initial"); + //! \ogs_file_param{prj__time_loop__time_stepping__FixedTimeStepping__t_end} + auto const t_end = config.getConfigParameter<double>("t_end"); + //! \ogs_file_param{prj__time_loop__time_stepping__FixedTimeStepping__timesteps} + auto const delta_ts = config.getConfigSubtree("timesteps"); + + std::vector<double> timesteps; + double t_curr = t_initial; + double delta_t = 0.0; + + // TODO: consider adding call "listNonEmpty" to config tree + //! \ogs_file_param{prj__time_loop__time_stepping__FixedTimeStepping__timesteps__pair} + auto const range = delta_ts.getConfigSubtreeList("pair"); + if (range.begin() == range.end()) + { + OGS_FATAL("no timesteps have been given"); + } + for (auto const pair : range) + { + //! \ogs_file_param{prj__time_loop__time_stepping__FixedTimeStepping__timesteps__pair__repeat} + auto const repeat = pair.getConfigParameter<std::size_t>("repeat"); + //! \ogs_file_param{prj__time_loop__time_stepping__FixedTimeStepping__timesteps__pair__delta_t} + delta_t = pair.getConfigParameter<double>("delta_t"); + + if (repeat == 0) + { + OGS_FATAL("<repeat> is zero."); + } + if (delta_t <= 0.0) + { + OGS_FATAL("timestep <delta_t> is <= 0.0."); + } + + if (t_curr <= t_end) + { + timesteps.resize(timesteps.size() + repeat, delta_t); + + t_curr += repeat * delta_t; + } + } + + // append last delta_t until t_end is reached + if (t_curr <= t_end) + { + auto const repeat = + static_cast<std::size_t>(std::ceil((t_end - t_curr) / delta_t)); + timesteps.resize(timesteps.size() + repeat, delta_t); + } + + return std::make_unique<FixedTimeStepping>(t_initial, t_end, timesteps); +} +} // end of namespace NumLib diff --git a/NumLib/TimeStepping/Algorithms/CreateFixedTimeStepping.h b/NumLib/TimeStepping/Algorithms/CreateFixedTimeStepping.h new file mode 100644 index 0000000000000000000000000000000000000000..ff7e066a04390579652187a4f41ab99db3dd9955 --- /dev/null +++ b/NumLib/TimeStepping/Algorithms/CreateFixedTimeStepping.h @@ -0,0 +1,29 @@ +/** + * \copyright + * Copyright (c) 2012-2017, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + * \file CreateFixedTimeStepping.h + * Created on June 26, 2017, 5:02 PM + */ + +#pragma once + +#include <memory> + +namespace BaseLib +{ +class ConfigTree; +} + +namespace NumLib +{ +class TimeStepAlgorithm; + +/// Create a FixedTimeStepping time stepper from the given +/// configuration +std::unique_ptr<TimeStepAlgorithm> createFixedTimeStepping( + BaseLib::ConfigTree const& config); +} // end of namespace NumLib diff --git a/NumLib/TimeStepping/Algorithms/EvolutionaryPIDcontroller.cpp b/NumLib/TimeStepping/Algorithms/EvolutionaryPIDcontroller.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f3317e6e1e11a564b3904e3312621f64a18f67f8 --- /dev/null +++ b/NumLib/TimeStepping/Algorithms/EvolutionaryPIDcontroller.cpp @@ -0,0 +1,112 @@ +/** + * \copyright + * Copyright (c) 2012-2017, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + * \file EvolutionaryPIDcontroller.cpp + * Created on March 31, 2017, 4:13 PM + */ + +#include <algorithm> +#include <functional> +#include <limits> +#include <vector> + +#include "EvolutionaryPIDcontroller.h" + +namespace NumLib +{ +bool EvolutionaryPIDcontroller::next(const double solution_error) +{ + const double e_n = solution_error; + const double zero_threshlod = std::numeric_limits<double>::epsilon(); + // step rejected. + if (e_n > _tol) // e_n < TOL + { + _is_accepted = false; + + double h_new = (e_n > zero_threshlod) + ? _ts_current.dt() * _tol / e_n + : 0.5 * _ts_current.dt(); + + h_new = limitStepSize(h_new, _ts_current.dt()); + h_new = checkSpecificTimeReached(h_new); + + _ts_current = _ts_prev; + _ts_current += h_new; + return false; + } + + // step accepted. + _is_accepted = true; + + if (_ts_current.steps() == 0) + { + _ts_prev = _ts_current; + _ts_current += _h0; + _e_n_minus1 = e_n; + + _dt_vector.push_back(_h0); + } + else + { + const double h_n = _ts_current.dt(); + double h_new = h_n; + + if (e_n > zero_threshlod) + { + if (_e_n_minus1 > zero_threshlod) + { + if (_e_n_minus2 > zero_threshlod) + { + h_new = std::pow(_e_n_minus1 / e_n, _kP) * + std::pow(_tol / e_n, _kI) * + std::pow( + _e_n_minus1 * _e_n_minus1 / (e_n * _e_n_minus2), + _kD) * h_n; + } + else + { + h_new = std::pow(_e_n_minus1 / e_n, _kP) * + std::pow(_tol / e_n, _kI) * h_n; + } + } + else + { + h_new = std::pow(_tol / e_n, _kI) * h_n; + } + } + + h_new = limitStepSize(h_new, h_n); + h_new = checkSpecificTimeReached(h_new); + _dt_vector.push_back(h_new); + + _ts_prev = _ts_current; + _ts_current += h_new; + + _e_n_minus2 = _e_n_minus1; + _e_n_minus1 = e_n; + } + + return true; +} + +double EvolutionaryPIDcontroller::checkSpecificTimeReached(const double h_new) +{ + if (_specific_times.empty()) + return h_new; + + const double specific_time = _specific_times.back(); + const double zero_threshold = std::numeric_limits<double>::epsilon(); + if ((specific_time > _ts_current.current()) && + (_ts_current.current() + h_new - specific_time > zero_threshold)) + { + _specific_times.pop_back(); + return specific_time - _ts_current.current(); + } + + return h_new; +} +} // end of namespace NumLib diff --git a/NumLib/TimeStepping/Algorithms/EvolutionaryPIDcontroller.h b/NumLib/TimeStepping/Algorithms/EvolutionaryPIDcontroller.h new file mode 100644 index 0000000000000000000000000000000000000000..5e5226667f64bc5624bbbd2968be39d5247cd14a --- /dev/null +++ b/NumLib/TimeStepping/Algorithms/EvolutionaryPIDcontroller.h @@ -0,0 +1,127 @@ +/** + * \copyright + * Copyright (c) 2012-2017, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + * \file EvolutionaryPIDcontroller.h + * Created on March 31, 2017, 4:13 PM + */ + +#pragma once + +#include <vector> +#include <memory> + +#include "TimeStepAlgorithm.h" + +namespace BaseLib +{ +class ConfigTree; +} + +namespace NumLib +{ +/** + * This class gives an adaptive algorithm whose time step control is + * evolutionary PID controller. With an definition of relative solution change + * \f$e_n=\frac{\|u^{n+1}-u^n\|}{\|u^{n+1}\|}\f$, the algorithm gives a time + * step size estimation as + * \f[ + * h_{n+1} = \left(\frac{e_{n-1}}{e_n}\right)^{k_P} + * \left(\frac{TOL}{e_n}\right)^{k_I} + * \left(\frac{e^2_{n-1}}{e_n e_{n-2}}\right)^{k_D} + * \f] + * where \f$k_P=0.075\f$, \f$k_I=0.175\f$, \f$k_D=0.01\f$ are empirical PID + * parameters. + * + * In the computation, \f$ e_n\f$ is calculated firstly. If \f$e_n>TOL\f$, the + * current time step is rejected and repeated with a new time step size of + * \f$h=\frac{TOL}{e_n} h_n\f$. + * + * Limits of the time step size are given as + * \f[ + * h_{\mbox{min}} \leq h_{n+1} \leq h_{\mbox{max}}, + * l \leq \frac{h_{n+1}}{h_n} \leq L + * \f] + * + * Similar algorithm can be found in \cite ahmed2015adaptive . + */ +class EvolutionaryPIDcontroller final : public TimeStepAlgorithm +{ +public: + EvolutionaryPIDcontroller(const double t0, const double t_end, + const double h0, const double h_min, + const double h_max, const double rel_h_min, + const double rel_h_max, + const std::vector<double>&& specific_times, + const double tol) + : TimeStepAlgorithm(t0, t_end), + _h0(h0), + _h_min(h_min), + _h_max(h_max), + _rel_h_min(rel_h_min), + _rel_h_max(rel_h_max), + _specific_times(std::move(specific_times)), + _tol(tol), + _e_n_minus1(0.), + _e_n_minus2(0.), + _is_accepted(true) + { + } + + /** + * move to the next time step + * @param solution_error \f$e_n\f$, solution error between two successive + time steps. + * @return true if the next step exists + */ + bool next(const double solution_error) override; + + /// return if current time step is accepted + bool accepted() const override { return _is_accepted; } + /// Get a flag to indicate that this algorithm need to compute + /// solution error. + bool isSolutionErrorComputationNeeded() override { return true; } + +private: + const double _kP = 0.075; ///< Parameter. \see EvolutionaryPIDcontroller + const double _kI = 0.175; ///< Parameter. \see EvolutionaryPIDcontroller + const double _kD = 0.01; ///< Parameter. \see EvolutionaryPIDcontroller + + const double _h0; ///< initial time step size. + const double _h_min; ///< minimum step size. + const double _h_max; ///< maximum step size. + + /// \f$l\f$ in \f$ h_{\mbox{min}} \leq h_{n+1} \leq h_{\mbox{max}},\f$ + const double _rel_h_min; + /// \f$L\f$ in \f$ h_{\mbox{min}} \leq h_{n+1} \leq h_{\mbox{max}},\f$ + const double _rel_h_max; + + // Given times that steps have to reach. + std::vector<double> _specific_times; + + const double _tol; + + double _e_n_minus1; ///< \f$e_{n-1}\f$. + double _e_n_minus2; ///< \f$e_{n-2}\f$. + + bool _is_accepted; + + double limitStepSize(const double h_new, const double h_n) const + { + const double h_in_range = std::max(_h_min, std::min(h_new, _h_max)); + return std::max(_rel_h_min * h_n, + std::min(h_in_range, _rel_h_max * h_n)); + } + + double checkSpecificTimeReached(const double h_new); +}; + +/// Create an EvolutionaryPIDcontroller time stepper from the given +/// configuration +std::unique_ptr<TimeStepAlgorithm> createEvolutionaryPIDcontroller( + BaseLib::ConfigTree const& config); + +} // end of namespace NumLib diff --git a/NumLib/TimeStepping/Algorithms/FixedTimeStepping.cpp b/NumLib/TimeStepping/Algorithms/FixedTimeStepping.cpp index 7ee39bc34fcf0d0ec5c275984514edf94f58a404..63fb6da19973272e151e3460eb632286906ee2fc 100644 --- a/NumLib/TimeStepping/Algorithms/FixedTimeStepping.cpp +++ b/NumLib/TimeStepping/Algorithms/FixedTimeStepping.cpp @@ -12,92 +12,30 @@ #include "FixedTimeStepping.h" #include <algorithm> -#include <cmath> #include <numeric> #include <limits> #include <cassert> -#include <logog/include/logog.hpp> - -#include "BaseLib/ConfigTree.h" -#include "BaseLib/Error.h" - namespace NumLib { - -FixedTimeStepping::FixedTimeStepping(double t0, double tn, const std::vector<double> &vec_all_dt) -: _t_initial(t0), _t_end(computeEnd(t0, tn, vec_all_dt)), _dt_vector(vec_all_dt), _ts_prev(t0), _ts_current(t0) -{} - -FixedTimeStepping::FixedTimeStepping(double t0, double tn, double dt) -: _t_initial(t0), _t_end(tn), _dt_vector(static_cast<std::size_t>(std::ceil((tn-t0)/dt)), dt), _ts_prev(t0), _ts_current(t0) -{} - -std::unique_ptr<ITimeStepAlgorithm> -FixedTimeStepping::newInstance(BaseLib::ConfigTree const& config) +FixedTimeStepping::FixedTimeStepping(double t0, + double tn, + const std::vector<double>& vec_all_dt) + : TimeStepAlgorithm(t0, computeEnd(t0, tn, vec_all_dt), vec_all_dt) { - //! \ogs_file_param{prj__time_loop__time_stepping__type} - config.checkConfigParameter("type", "FixedTimeStepping"); - - //! \ogs_file_param{prj__time_loop__time_stepping__FixedTimeStepping__t_initial} - auto const t_initial = config.getConfigParameter<double>("t_initial"); - //! \ogs_file_param{prj__time_loop__time_stepping__FixedTimeStepping__t_end} - auto const t_end = config.getConfigParameter<double>("t_end"); - //! \ogs_file_param{prj__time_loop__time_stepping__FixedTimeStepping__timesteps} - auto const delta_ts = config.getConfigSubtree("timesteps"); - - std::vector<double> timesteps; - double t_curr = t_initial; - double delta_t = 0.0; - - // TODO: consider adding call "listNonEmpty" to config tree - //! \ogs_file_param{prj__time_loop__time_stepping__FixedTimeStepping__timesteps__pair} - auto const range = delta_ts.getConfigSubtreeList("pair"); - if (range.begin() == range.end()) { - OGS_FATAL("no timesteps have been given"); - } - for (auto const pair : range) - { - //! \ogs_file_param{prj__time_loop__time_stepping__FixedTimeStepping__timesteps__pair__repeat} - auto const repeat = pair.getConfigParameter<std::size_t>("repeat"); - //! \ogs_file_param{prj__time_loop__time_stepping__FixedTimeStepping__timesteps__pair__delta_t} - delta_t = pair.getConfigParameter<double>("delta_t"); - - if (repeat == 0) { - OGS_FATAL("<repeat> is zero."); - } - if (delta_t <= 0.0) { - OGS_FATAL("timestep <delta_t> is <= 0.0."); - } - - if (t_curr <= t_end) { - timesteps.resize(timesteps.size() + repeat, delta_t); - - t_curr += repeat * delta_t; - } - } - - // append last delta_t until t_end is reached - if (t_curr <= t_end) - { - auto const repeat = - static_cast<std::size_t>(std::ceil((t_end - t_curr) / delta_t)); - timesteps.resize(timesteps.size() + repeat, delta_t); - } - - return std::make_unique<FixedTimeStepping>(t_initial, t_end, timesteps); } -const TimeStep FixedTimeStepping::getTimeStep() const +FixedTimeStepping::FixedTimeStepping(double t0, double tn, double dt) + : TimeStepAlgorithm(t0, tn, dt) { - return _ts_current; } -bool FixedTimeStepping::next() +bool FixedTimeStepping::next(const double /*solution_error*/) { // check if last time step - if (_ts_current.steps() == _dt_vector.size() - || std::abs(_ts_current.current()-_t_end) < std::numeric_limits<double>::epsilon()) + if (_ts_current.steps() == _dt_vector.size() || + std::abs(_ts_current.current() - _t_end) < + std::numeric_limits<double>::epsilon()) return false; // confirm current time and move to the next if accepted @@ -107,17 +45,20 @@ bool FixedTimeStepping::next() // prepare the next time step info _ts_current = _ts_prev; double dt = _dt_vector[_ts_prev.steps()]; - if (_ts_prev.current() + dt > _t_end) // upper bound by t_end + if (_ts_prev.current() + dt > _t_end) // upper bound by t_end dt = _t_end - _ts_prev.current(); _ts_current += dt; return true; } -double FixedTimeStepping::computeEnd(double t_initial, double t_end, const std::vector<double> &dt_vector) +double FixedTimeStepping::computeEnd(double t_initial, + double t_end, + const std::vector<double>& dt_vector) { - double t_sum = t_initial + std::accumulate(dt_vector.begin(), dt_vector.end(), 0.); + double t_sum = + t_initial + std::accumulate(dt_vector.begin(), dt_vector.end(), 0.); return std::min(t_end, t_sum); } -} //NumLib +} // NumLib diff --git a/NumLib/TimeStepping/Algorithms/FixedTimeStepping.h b/NumLib/TimeStepping/Algorithms/FixedTimeStepping.h index af57f4f9ddb65137060d2ece618019513101ea81..8cdffd4250737599ca62bd4635841099594bf8fb 100644 --- a/NumLib/TimeStepping/Algorithms/FixedTimeStepping.h +++ b/NumLib/TimeStepping/Algorithms/FixedTimeStepping.h @@ -14,20 +14,16 @@ #include <memory> #include <vector> -#include "ITimeStepAlgorithm.h" - -namespace BaseLib { class ConfigTree; } +#include "TimeStepAlgorithm.h" namespace NumLib { - /** * \brief Fixed time stepping algorithm * * This algorithm returns time step size defined by a user priori. */ -class FixedTimeStepping final - : public ITimeStepAlgorithm +class FixedTimeStepping final : public TimeStepAlgorithm { public: /** @@ -58,43 +54,18 @@ public: * @param t_end finish time * @param vec_all_dt a vector of all time steps */ - FixedTimeStepping(double t_initial, double t_end, const std::vector<double> &vec_all_dt); - - /// Create timestepper from the given configuration - static std::unique_ptr<ITimeStepAlgorithm> newInstance(BaseLib::ConfigTree const& config); - - /// return the beginning of time steps - double begin() const override { return _t_initial; } - - /// return the end of time steps - double end() const override { return _t_end; } - - /// return current time step - const TimeStep getTimeStep() const override; + FixedTimeStepping(double t_initial, double t_end, + const std::vector<double>& vec_all_dt); /// move to the next time step - bool next() override; + bool next(const double solution_error) override; /// return if current time step is accepted bool accepted() const override { return true; } - - /// return a history of time step sizes - const std::vector<double>& getTimeStepSizeHistory() const override { return _dt_vector; } - private: /// determine true end time - static double computeEnd(double t_initial, double t_end, const std::vector<double> &dt_vector); - - /// initial time - const double _t_initial; - /// end time - const double _t_end; - /// a vector of time step sizes - const std::vector<double> _dt_vector; - /// previous time step information - TimeStep _ts_prev; - /// current time step information - TimeStep _ts_current; + static double computeEnd(double t_initial, double t_end, + const std::vector<double>& dt_vector); }; -} //NumLib +} // NumLib diff --git a/NumLib/TimeStepping/Algorithms/ITimeStepAlgorithm.h b/NumLib/TimeStepping/Algorithms/ITimeStepAlgorithm.h deleted file mode 100644 index 2dda10b913d87b0b37b5d4df2923b3128405b9af..0000000000000000000000000000000000000000 --- a/NumLib/TimeStepping/Algorithms/ITimeStepAlgorithm.h +++ /dev/null @@ -1,50 +0,0 @@ -/** - * \author Norihiro Watanabe - * \date 2012-08-03 - * - * \copyright - * Copyright (c) 2012-2017, OpenGeoSys Community (http://www.opengeosys.org) - * Distributed under a Modified BSD License. - * See accompanying file LICENSE.txt or - * http://www.opengeosys.org/project/license - */ - -#pragma once - -#include <vector> - -#include "NumLib/TimeStepping/TimeStep.h" - -namespace NumLib -{ - -/** - * \brief Interface of time stepping algorithms - * - */ -class ITimeStepAlgorithm -{ -public: - /// return the beginning of time steps - virtual double begin() const = 0; - - /// return the end of time steps - virtual double end() const = 0; - - /// move to the next time step - /// \return true if the next step exists - virtual bool next() = 0; - - /// return current time step - virtual const TimeStep getTimeStep() const = 0; - - /// return if current time step is accepted or not - virtual bool accepted() const = 0; - - /// return a history of time step sizes - virtual const std::vector<double>& getTimeStepSizeHistory() const = 0; - - virtual ~ITimeStepAlgorithm() = default; -}; - -} //NumLib diff --git a/NumLib/TimeStepping/Algorithms/IterationNumberBasedAdaptiveTimeStepping.cpp b/NumLib/TimeStepping/Algorithms/IterationNumberBasedAdaptiveTimeStepping.cpp index d6e27d960e357f54a1d690ce9fe0ea669dcfb62b..db913264a371c83e2bf344ffe0b4045b7303cd26 100644 --- a/NumLib/TimeStepping/Algorithms/IterationNumberBasedAdaptiveTimeStepping.cpp +++ b/NumLib/TimeStepping/Algorithms/IterationNumberBasedAdaptiveTimeStepping.cpp @@ -22,57 +22,54 @@ namespace NumLib IterationNumberBasedAdaptiveTimeStepping:: IterationNumberBasedAdaptiveTimeStepping( double t0, double tn, double min_ts, double max_ts, double initial_ts, - std::vector<std::size_t> iter_times_vector, - std::vector<double> multiplier_vector) - : _t_initial(t0), - _t_end(tn), - _iter_times_vector(std::move(iter_times_vector)), - _multiplier_vector(std::move(multiplier_vector)), + std::vector<std::size_t>& iter_times_vector, + std::vector<double>& multiplier_vector) + : TimeStepAlgorithm(t0, tn), + _iter_times_vector(iter_times_vector), + _multiplier_vector(multiplier_vector), _min_ts(min_ts), _max_ts(max_ts), _initial_ts(initial_ts), _max_iter(_iter_times_vector.empty() ? 0 : _iter_times_vector.back()), _iter_times(0), - _ts_pre(t0), - _ts_current(t0), _n_rejected_steps(0) { assert(iter_times_vector.size() == multiplier_vector.size()); } -bool IterationNumberBasedAdaptiveTimeStepping::next() +bool IterationNumberBasedAdaptiveTimeStepping::next( + const double /*solution_error*/) { // check current time step - if (std::abs(_ts_current.current()-_t_end) < std::numeric_limits<double>::epsilon()) + if (std::abs(_ts_current.current() - _t_end) < + std::numeric_limits<double>::epsilon()) return false; // confirm current time and move to the next if accepted - if (accepted()) { - _ts_pre = _ts_current; + if (accepted()) + { + _ts_prev = _ts_current; _dt_vector.push_back(_ts_current.dt()); - } else { + } + else + { ++_n_rejected_steps; } // prepare the next time step info - _ts_current = _ts_pre; + _ts_current = _ts_prev; _ts_current += getNextTimeStepSize(); return true; } -const TimeStep IterationNumberBasedAdaptiveTimeStepping::getTimeStep() const -{ - return _ts_current; -} - double IterationNumberBasedAdaptiveTimeStepping::getNextTimeStepSize() const { double dt = 0.0; // if this is the first time step // then we use initial guess provided by a user - if ( _ts_pre.steps() == 0 ) + if (_ts_prev.steps() == 0) { dt = _initial_ts; } @@ -83,29 +80,29 @@ double IterationNumberBasedAdaptiveTimeStepping::getNextTimeStepSize() const if (!_multiplier_vector.empty()) tmp_multiplier = _multiplier_vector[0]; // finding the right multiplier - for (std::size_t i=0; i<_iter_times_vector.size(); i++ ) - if ( this->_iter_times > _iter_times_vector[i] ) + for (std::size_t i = 0; i < _iter_times_vector.size(); i++) + if (this->_iter_times > _iter_times_vector[i]) tmp_multiplier = _multiplier_vector[i]; // multiply the the multiplier - dt = _ts_pre.dt() * tmp_multiplier; + dt = _ts_prev.dt() * tmp_multiplier; } // check whether out of the boundary - if ( dt < _min_ts ) + if (dt < _min_ts) dt = _min_ts; - else if ( dt > _max_ts ) + else if (dt > _max_ts) dt = _max_ts; - double t_next = dt + _ts_pre.current(); + double t_next = dt + _ts_prev.current(); if (t_next > end()) - dt = end() - _ts_pre.current(); + dt = end() - _ts_prev.current(); return dt; } bool IterationNumberBasedAdaptiveTimeStepping::accepted() const { - return ( this->_iter_times <= this->_max_iter ); + return (_iter_times <= _max_iter); } -} // NumLib +} // NumLib diff --git a/NumLib/TimeStepping/Algorithms/IterationNumberBasedAdaptiveTimeStepping.h b/NumLib/TimeStepping/Algorithms/IterationNumberBasedAdaptiveTimeStepping.h index 38058e29d314b10b0cacf9fa6a4ccabddbc7b936..0cdf734d5caf27de8dd378dd8ac8c5f2bcefade6 100644 --- a/NumLib/TimeStepping/Algorithms/IterationNumberBasedAdaptiveTimeStepping.h +++ b/NumLib/TimeStepping/Algorithms/IterationNumberBasedAdaptiveTimeStepping.h @@ -13,24 +13,26 @@ #include <vector> -#include "ITimeStepAlgorithm.h" +#include "TimeStepAlgorithm.h" namespace NumLib { - /** * \brief Iteration number based adaptive time stepping * * This algorithm estimates a time step size depending on the number of * iterations (e.g. of iterative linear solvers, nonlinear methods, partitioned - * coupling) needed in the last time step (see Hoffmann (2010) for Newton-Raphson case). + * coupling) needed in the last time step (see Hoffmann (2010) for + * Newton-Raphson case). * The new time step \f$\Delta t_{n+1}\f$ size is calculated as * \f[ * \Delta t_{n+1} = \alpha \Delta t_n * \f] - * with the previous time step size \f$\Delta t_{n}\f$ and a multiplier coefficient + * with the previous time step size \f$\Delta t_{n}\f$ and a multiplier + * coefficient * \f$\alpha\f$ depending on the iteration number. - * Note that a time step size is always bounded by the minimum and maximum allowed + * Note that a time step size is always bounded by the minimum and maximum + * allowed * value. * \f[ * \Delta t_{\min} \le \Delta t \le \Delta t_{\max} @@ -39,24 +41,30 @@ namespace NumLib * For example, users can setup the following time stepping strategy based on * the iteration number of the Newton-Raphson method in the last time step. * <table border="1"> - * <tr><th>Num. of Newton steps</th><th>0-2</th><th>3-6</th><th>7-8</th><th>9<</th></tr> - * <tr><th>Time step size multiplier</th><th>1.6</th><th>1.</th><th>0.5</th><th>0.25 (repeat time step)</th></tr> - * <tr><th>Upper and lower bound</th><th colspan="4"> \f$ 1. \le \Delta t \le 10.\f$ </th></tr> + * <tr><th>Num. of Newton + * steps</th><th>0-2</th><th>3-6</th><th>7-8</th><th>9<</th></tr> + * <tr><th>Time step size + * multiplier</th><th>1.6</th><th>1.</th><th>0.5</th><th>0.25 (repeat time + * step)</th></tr> + * <tr><th>Upper and lower bound</th><th colspan="4"> \f$ 1. \le \Delta t \le + * 10.\f$ </th></tr> * </table> - * A time step size is increased for the small iteration number, and decreased for the - * large iteration number. If the iteration number exceeds a user-defined threshold (e.g. 9), + * A time step size is increased for the small iteration number, and decreased + * for the + * large iteration number. If the iteration number exceeds a user-defined + * threshold (e.g. 9), * a time step is repeated with a smaller time step size. * * Reference * - Hoffmann J (2010) Reactive Transport and Mineral Dissolution/Precipitation * in Porous Media:Efficient Solution Algorithms, Benchmark Computations and - * Existence of Global Solutions. PhD thesis. pp82. Friedrich-Alexander-Universität Erlangen-Nürnberg. + * Existence of Global Solutions. PhD thesis. pp82. + * Friedrich-Alexander-Universität Erlangen-Nürnberg. * */ -class IterationNumberBasedAdaptiveTimeStepping : public ITimeStepAlgorithm +class IterationNumberBasedAdaptiveTimeStepping final : public TimeStepAlgorithm { public: - /** * Constructor * @@ -71,55 +79,41 @@ public: * If an iteration number is larger than \f$i_n\f$, current time step is * repeated with the new time step size. * @param multiplier_vector a vector of multiplier coefficients - * (\f$a_1\f$, \f$a_2\f$, ..., \f$a_n\f$) corresponding to the intervals given by iter_times_vector. + * (\f$a_1\f$, \f$a_2\f$, ..., \f$a_n\f$) corresponding to the intervals + * given by iter_times_vector. * A time step size is calculated by \f$\Delta t_{n+1} = a * \Delta t_{n}\f$ */ - IterationNumberBasedAdaptiveTimeStepping(double t_initial, - double t_end, - double min_ts, - double max_ts, - double initial_ts, - std::vector<std::size_t> - iter_times_vector, - std::vector<double> - multiplier_vector); + IterationNumberBasedAdaptiveTimeStepping( + double t_initial, + double t_end, + double min_ts, + double max_ts, + double initial_ts, + std::vector<std::size_t>& iter_times_vector, + std::vector<double>& multiplier_vector); ~IterationNumberBasedAdaptiveTimeStepping() override = default; - /// return the beginning of time steps - double begin() const override { return _t_initial; } - /// return the end of time steps - double end() const override { return _t_end; } - /// return current time step - const TimeStep getTimeStep() const override; - /// move to the next time step - bool next() override; + bool next(const double solution_error) override; /// return if the current step is accepted bool accepted() const override; - /// return a history of time step sizes - const std::vector<double>& getTimeStepSizeHistory() const override - { - return this->_dt_vector; - } - /// set the number of iterations - void setNIterations(std::size_t n_itr) {this->_iter_times = n_itr;} - + void setIterationNumber(std::size_t n_itr) { this->_iter_times = n_itr; } /// return the number of repeated steps - std::size_t getNumberOfRepeatedSteps() const {return this->_n_rejected_steps;} + std::size_t getNumberOfRepeatedSteps() const + { + return this->_n_rejected_steps; + } private: /// calculate the next time step size double getNextTimeStepSize() const; - /// initial time - const double _t_initial; - /// end time - const double _t_end; - /// this vector stores the number of iterations to which the respective multiplier coefficient will be applied + /// this vector stores the number of iterations to which the respective + /// multiplier coefficient will be applied const std::vector<std::size_t> _iter_times_vector; /// this vector stores the multiplier coefficients const std::vector<double> _multiplier_vector; @@ -133,14 +127,8 @@ private: const std::size_t _max_iter; /// the number of nonlinear iterations std::size_t _iter_times; - /// previous time step - TimeStep _ts_pre; - /// current time step - TimeStep _ts_current; - /// history of time step sizes - std::vector<double> _dt_vector; /// the number of rejected steps std::size_t _n_rejected_steps; }; -} // NumLib +} // NumLib diff --git a/NumLib/TimeStepping/Algorithms/TimeStepAlgorithm.h b/NumLib/TimeStepping/Algorithms/TimeStepAlgorithm.h new file mode 100644 index 0000000000000000000000000000000000000000..1c459e5b7972963f64f992988d2e963cd53a32c1 --- /dev/null +++ b/NumLib/TimeStepping/Algorithms/TimeStepAlgorithm.h @@ -0,0 +1,99 @@ +/** + * \author Norihiro Watanabe + * \date 2012-08-03 + * + * \copyright + * Copyright (c) 2012-2017, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + */ + +#pragma once + +#include <vector> +#include <cmath> + +#include "NumLib/TimeStepping/TimeStep.h" + +namespace NumLib +{ +/** + * \brief Interface of time stepping algorithms + * + */ +class TimeStepAlgorithm +{ +public: + TimeStepAlgorithm(const double t0, const double t_end) + : _t_initial(t0), _t_end(t_end), _ts_prev(t0), _ts_current(t0) + { + } + + TimeStepAlgorithm(const double t0, const double t_end, const double dt) + : _t_initial(t0), + _t_end(t_end), + _ts_prev(t0), + _ts_current(t0), + _dt_vector(static_cast<std::size_t>(std::ceil((t_end - t0) / dt)), dt) + { + } + + TimeStepAlgorithm(const double t0, const double t_end, + const std::vector<double>& all_step_sizes) + : _t_initial(t0), + _t_end(t_end), + _ts_prev(t0), + _ts_current(t0), + _dt_vector(all_step_sizes) + { + } + + virtual ~TimeStepAlgorithm() = default; + + /// return the beginning of time steps + double begin() const { return _t_initial; } + /// return the end of time steps + double end() const { return _t_end; } + /// return current time step + const TimeStep getTimeStep() const { return _ts_current; } + /// reset the current step size from the previous time + void resetCurrentTimeStep(const double dt) + { + _ts_current = _ts_prev; + _ts_current += dt; + } + + /// move to the next time step + /// \param solution_error Solution error between two successive time steps. + /// \return true if the next step exists + virtual bool next(const double solution_error) = 0; + + /// return if current time step is accepted or not + virtual bool accepted() const = 0; + + /// return a history of time step sizes + const std::vector<double>& getTimeStepSizeHistory() const + { + return _dt_vector; + } + + /// Get a flag to indicate whether this algorithm needs to compute + /// solution error. The default return value is false. + virtual bool isSolutionErrorComputationNeeded() { return false; } +protected: + /// initial time + const double _t_initial; + /// end time + const double _t_end; + + /// previous time step information + TimeStep _ts_prev; + /// current time step information + TimeStep _ts_current; + + /// a vector of time step sizes + std::vector<double> _dt_vector; +}; + +} // NumLib diff --git a/NumLib/TimeStepping/CreateTimeStepper.cpp b/NumLib/TimeStepping/CreateTimeStepper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3326b397756a75fb8836eb8cf5136e1875562d02 --- /dev/null +++ b/NumLib/TimeStepping/CreateTimeStepper.cpp @@ -0,0 +1,61 @@ +/** + * \copyright + * Copyright (c) 2012-2017, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + * \file CreateTimeStepper.cpp + * Created on May 2, 2017, 12:18 PM + */ + +#include "CreateTimeStepper.h" + +#include <memory> +#include <string> + +#include "BaseLib/ConfigTree.h" +#include "BaseLib/Error.h" + +#include "NumLib/TimeStepping/Algorithms/CreateFixedTimeStepping.h" +#include "NumLib/TimeStepping/Algorithms/FixedTimeStepping.h" +#include "NumLib/TimeStepping/Algorithms/CreateEvolutionaryPIDcontroller.h" + +namespace NumLib +{ +std::unique_ptr<TimeStepAlgorithm> createTimeStepper( + BaseLib::ConfigTree const& config) +{ + //! \ogs_file_param{prj__time_loop__time_stepping__type} + auto const type = config.peekConfigParameter<std::string>("type"); + + std::unique_ptr<NumLib::TimeStepAlgorithm> timestepper; + + if (type == "SingleStep") + { + //! \ogs_file_param_special{prj__time_loop__time_stepping__SingleStep} + config.ignoreConfigParameter("type"); + timestepper = + std::make_unique<NumLib::FixedTimeStepping>(0.0, 1.0, 1.0); + } + else if (type == "FixedTimeStepping") + { + timestepper = NumLib::createFixedTimeStepping(config); + } + else if (type == "EvolutionaryPIDcontroller") + { + timestepper = NumLib::createEvolutionaryPIDcontroller(config); + } + else + { + OGS_FATAL( + "Unknown time stepping type: `%s'. " + "The available types are \n\tSingleStep, \n\tFixedTimeStepping" + "\n\tEvolutionaryPIDcontroller\n", + type.data()); + } + + return timestepper; +} + +} // end of namespace NumLib \ No newline at end of file diff --git a/NumLib/TimeStepping/CreateTimeStepper.h b/NumLib/TimeStepping/CreateTimeStepper.h new file mode 100644 index 0000000000000000000000000000000000000000..25434a686b83cac992c5e122e2ec23e2922050bc --- /dev/null +++ b/NumLib/TimeStepping/CreateTimeStepper.h @@ -0,0 +1,26 @@ +/** + * \copyright + * Copyright (c) 2012-2017, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + * \file CreateTimeStepper.h + * Created on May 2, 2017, 12:18 PM + */ + +#pragma once + +#include <memory> + +namespace BaseLib +{ +class ConfigTree; +} + +namespace NumLib +{ +class TimeStepAlgorithm; +std::unique_ptr<TimeStepAlgorithm> createTimeStepper( + BaseLib::ConfigTree const& config); +}; diff --git a/NumLib/TimeStepping/TimeStep.h b/NumLib/TimeStepping/TimeStep.h index 1a593a7035b26c1923639d1552c76132ab4e9e1e..a85ebb4827fecf13d5e4c2feed6b5c0d171ad5b9 100644 --- a/NumLib/TimeStepping/TimeStep.h +++ b/NumLib/TimeStepping/TimeStep.h @@ -15,12 +15,13 @@ namespace NumLib { - /** * \brief Time step object * - * TimeStep class contains previous time(\f$t_{n}\f$), current time (\f$t_{n+1}\f$), - * time step length (\f$\Delta t_{n+1}\f$), and the number of time steps (\f$n+1\f$). + * TimeStep class contains previous time(\f$t_{n}\f$), current time + * (\f$t_{n+1}\f$), + * time step length (\f$\Delta t_{n+1}\f$), and the number of time steps + * (\f$n+1\f$). */ class TimeStep { @@ -30,7 +31,12 @@ public: * @param current_time current time */ explicit TimeStep(double current_time) - : _previous(current_time), _current(current_time), _dt(_current-_previous), _steps(0) {} + : _previous(current_time), + _current(current_time), + _dt(_current - _previous), + _steps(0) + { + } /** * Initialize a time step @@ -39,11 +45,21 @@ public: * @param n the number of time steps */ TimeStep(double previous_time, double current_time, std::size_t n) - : _previous(previous_time), _current(current_time), _dt(_current-_previous), _steps(n) {} + : _previous(previous_time), + _current(current_time), + _dt(_current - _previous), + _steps(n) + { + } /// copy a time step - TimeStep(const TimeStep &src) - : _previous(src._previous), _current(src._current), _dt(_current-_previous), _steps(src._steps) {} + TimeStep(const TimeStep& src) + : _previous(src._previous), + _current(src._current), + _dt(_current - _previous), + _steps(src._steps) + { + } /// copy a time step TimeStep& operator=(const TimeStep& src) = default; @@ -57,7 +73,7 @@ public: } /// increment time step - TimeStep &operator+=(const double dt) + TimeStep& operator+=(const double dt) { _previous = _current; _current += dt; @@ -67,35 +83,33 @@ public: } /// compare current time - bool operator<(const TimeStep &t) const {return (_current < t._current);} - + bool operator<(const TimeStep& t) const { return (_current < t._current); } /// compare current time - bool operator<(const double &t) const {return (_current < t);} - + bool operator<(const double& t) const { return (_current < t); } /// compare current time - bool operator<=(const TimeStep &t) const {return (_current <= t._current);} + bool operator<=(const TimeStep& t) const + { + return (_current <= t._current); + } /// compare current time - bool operator<=(const double &t) const {return (_current <= t);} - + bool operator<=(const double& t) const { return (_current <= t); } /// compare current time - bool operator==(const TimeStep &t) const {return (_current == t._current);} + bool operator==(const TimeStep& t) const + { + return (_current == t._current); + } /// compare current time - bool operator==(const double &t) const {return (_current == t);} - + bool operator==(const double& t) const { return (_current == t); } /// return previous time step - double previous() const {return _previous;} - + double previous() const { return _previous; } /// return current time step - double current() const {return _current;} - + double current() const { return _current; } /// time step size from _previous - double dt() const {return _dt;} - + double dt() const { return _dt; } /// the number of time _steps - std::size_t steps() const {return _steps;} - + std::size_t steps() const { return _steps; } private: /// previous time step double _previous; @@ -107,4 +121,4 @@ private: std::size_t _steps; }; -} //NumLib +} // NumLib diff --git a/ProcessLib/AbstractJacobianAssembler.h b/ProcessLib/AbstractJacobianAssembler.h index 4505fc8c6f95e6ab94892d853cbf14c35e0c262b..d081da64d4f45b66c986fd9feb01dfc19a2fcebe 100644 --- a/ProcessLib/AbstractJacobianAssembler.h +++ b/ProcessLib/AbstractJacobianAssembler.h @@ -32,7 +32,7 @@ public: //! Assembles the Jacobian, the matrices \f$M\f$ and \f$K\f$, and the vector //! \f$b\f$ with coupling. - virtual void assembleWithJacobianAndCouping( + virtual void assembleWithJacobianAndCoupling( LocalAssemblerInterface& /*local_assembler*/, double const /*t*/, std::vector<double> const& /*local_x*/, std::vector<double> const& /*local_xdot*/, const double /*dxdot_dx*/, diff --git a/ProcessLib/LiquidFlow/Tests.cmake b/ProcessLib/LiquidFlow/Tests.cmake index cb3d9dfc9df5fb0070f63b2cddb2070723144eda..d2933143d52615305e4acdd59e0ac982addc226f 100644 --- a/ProcessLib/LiquidFlow/Tests.cmake +++ b/ProcessLib/LiquidFlow/Tests.cmake @@ -86,12 +86,29 @@ AddTest( WRAPPER time TESTER vtkdiff REQUIREMENTS NOT OGS_USE_MPI - ABSTOL 1e-16 RELTOL 1e-10 + ABSTOL 1.1 RELTOL 1e-7 DIFF_DATA - mesh2D.vtu gravity_driven_pcs_1_ts_5_t_300.000000.vtu pressure_ref pressure - mesh2D.vtu gravity_driven_pcs_1_ts_5_t_300.000000.vtu temperature_ref temperature - mesh2D.vtu gravity_driven_pcs_1_ts_5_t_300.000000.vtu v_x_ref v_x - mesh2D.vtu gravity_driven_pcs_1_ts_5_t_300.000000.vtu v_y_ref v_y + mesh2D.vtu gravity_driven_pcs_1_ts_10_t_300.000000.vtu OGS5_PRESSURE1 pressure + mesh2D.vtu gravity_driven_pcs_1_ts_10_t_300.000000.vtu OGS5_TEMPERATURE1 temperature + mesh2D.vtu gravity_driven_pcs_1_ts_10_t_300.000000.vtu v_x_ref v_x + mesh2D.vtu gravity_driven_pcs_1_ts_10_t_300.000000.vtu v_y_ref v_y +) + +# Coupling +AddTest( + NAME Adaptive_dt_StaggeredTH_ThermalDensityDrivenFlow2D + PATH StaggeredCoupledProcesses/TH/ThermalDensityDrivenFlow2D + EXECUTABLE ogs + EXECUTABLE_ARGS thermal_gravity_driven_flow2d_adaptive_dt.prj + WRAPPER time + TESTER vtkdiff + REQUIREMENTS NOT OGS_USE_MPI + ABSTOL 1.1 RELTOL 1e-6 + DIFF_DATA + mesh2D.vtu gravity_driven_adaptive_dt_pcs_1_ts_11_t_300.000000.vtu OGS5_PRESSURE1 pressure + mesh2D.vtu gravity_driven_adaptive_dt_pcs_1_ts_11_t_300.000000.vtu OGS5_TEMPERATURE1 temperature + mesh2D.vtu gravity_driven_adaptive_dt_pcs_1_ts_11_t_300.000000.vtu v_x_ref v_x + mesh2D.vtu gravity_driven_adaptive_dt_pcs_1_ts_11_t_300.000000.vtu v_y_ref v_y ) # PETSc/MPI @@ -181,10 +198,28 @@ AddTest( WRAPPER_ARGS -np 1 TESTER vtkdiff REQUIREMENTS OGS_USE_MPI - ABSTOL 1e-10 RELTOL 1e-10 + ABSTOL 1.1 RELTOL 1e-7 + DIFF_DATA + mesh2D.vtu gravity_driven_pcs_1_ts_10_t_300_000000_0.vtu OGS5_PRESSURE1 pressure + mesh2D.vtu gravity_driven_pcs_1_ts_10_t_300_000000_0.vtu OGS5_TEMPERATURE1 temperature +# To be activated when the output of velocity is correct under PETSc version. +# mesh2D.vtu gravity_driven_pcs_1_ts_10_t_300_000000_0.vtu v_x_ref v_x +# mesh2D.vtu gravity_driven_pcs_1_ts_10_t_300_000000_0.vtu v_y_ref v_y + +) + +AddTest( + NAME Adaptive_dt_StaggeredTH_ThermalDensityDrivenFlow2D + PATH StaggeredCoupledProcesses/TH/ThermalDensityDrivenFlow2D + EXECUTABLE_ARGS thermal_gravity_driven_flow2d_adaptive_dt.prj + WRAPPER mpirun + WRAPPER_ARGS -np 1 + TESTER vtkdiff + REQUIREMENTS OGS_USE_MPI + ABSTOL 1.1 RELTOL 1e-7 DIFF_DATA - mesh2D.vtu gravity_driven_pcs_1_ts_5_t_300_000000_0.vtu pressure_ref pressure - mesh2D.vtu gravity_driven_pcs_1_ts_5_t_300_000000_0.vtu temperature_ref temperature + mesh2D.vtu gravity_driven_adaptive_dt_pcs_1_ts_11_t_300_000000_0.vtu OGS5_PRESSURE1 pressure + mesh2D.vtu gravity_driven_adaptive_dt_pcs_1_ts_11_t_300_000000_0.vtu OGS5_TEMPERATURE1 temperature # To be activated when the output of velocity is correct under PETSc version. # mesh2D.vtu gravity_driven_pcs_1_ts_5_t_300_000000_0.vtu v_x_ref v_x # mesh2D.vtu gravity_driven_pcs_1_ts_5_t_300_000000_0.vtu v_y_ref v_y diff --git a/ProcessLib/LocalAssemblerInterface.cpp b/ProcessLib/LocalAssemblerInterface.cpp index c626e7cb71cd2b915e6f5fad759817bc2318929a..0732fd810248f3a57ea15a6d24cd4e8c71352049 100644 --- a/ProcessLib/LocalAssemblerInterface.cpp +++ b/ProcessLib/LocalAssemblerInterface.cpp @@ -13,7 +13,6 @@ namespace ProcessLib { - void LocalAssemblerInterface::assembleWithCoupledTerm( double const /*t*/, std::vector<double> const& /*local_x*/, std::vector<double>& /*local_M_data*/, @@ -22,8 +21,8 @@ void LocalAssemblerInterface::assembleWithCoupledTerm( LocalCouplingTerm const& /*coupling_term*/) { OGS_FATAL( - "The assembleWithCoupledTerm() function is not implemented in the local " - "assembler."); + "The assembleWithCoupledTerm() function is not implemented in the " + "local assembler."); } void LocalAssemblerInterface::assembleWithJacobian( @@ -39,7 +38,7 @@ void LocalAssemblerInterface::assembleWithJacobian( "assembler."); } -void LocalAssemblerInterface::assembleWithJacobianAndCouping( +void LocalAssemblerInterface::assembleWithJacobianAndCoupling( double const /*t*/, std::vector<double> const& /*local_x*/, std::vector<double> const& /*local_xdot*/, const double /*dxdot_dx*/, const double /*dx_dx*/, std::vector<double>& /*local_M_data*/, @@ -49,15 +48,14 @@ void LocalAssemblerInterface::assembleWithJacobianAndCouping( LocalCouplingTerm const& /*coupling_term*/) { OGS_FATAL( - "The assembleWithJacobianAndCouping() function is not implemented in" + "The assembleWithJacobianAndCoupling() function is not implemented in" " the local assembler."); } void LocalAssemblerInterface::computeSecondaryVariable( - std::size_t const mesh_item_id, - NumLib::LocalToGlobalIndexMap const& dof_table, - double const t, GlobalVector const& x, - StaggeredCouplingTerm const& coupled_term) + std::size_t const mesh_item_id, + NumLib::LocalToGlobalIndexMap const& dof_table, double const t, + GlobalVector const& x, StaggeredCouplingTerm const& coupled_term) { auto const indices = NumLib::getIndices(mesh_item_id, dof_table); auto const local_x = x.get(indices); @@ -68,11 +66,14 @@ void LocalAssemblerInterface::computeSecondaryVariable( } else { - auto const local_coupled_xs - = getCurrentLocalSolutionsOfCoupledProcesses( - coupled_term.coupled_xs, indices); - computeSecondaryVariableWithCoupledProcessConcrete(t, local_x, - local_coupled_xs); + auto const local_coupled_xs = + getCurrentLocalSolutionsOfCoupledProcesses(coupled_term.coupled_xs, + indices); + if (!local_coupled_xs.empty()) + computeSecondaryVariableWithCoupledProcessConcrete( + t, local_x, local_coupled_xs); + else + computeSecondaryVariableConcrete(t, local_x); } } diff --git a/ProcessLib/LocalAssemblerInterface.h b/ProcessLib/LocalAssemblerInterface.h index 040e6123f39f15b892bde0074900094143fdd00f..c9ad2950358f366fa3cf994720d5bd4c1b7c2d49 100644 --- a/ProcessLib/LocalAssemblerInterface.h +++ b/ProcessLib/LocalAssemblerInterface.h @@ -56,7 +56,7 @@ public: std::vector<double>& local_b_data, std::vector<double>& local_Jac_data); - virtual void assembleWithJacobianAndCouping(double const t, + virtual void assembleWithJacobianAndCoupling(double const t, std::vector<double> const& local_x, std::vector<double> const& local_xdot, const double dxdot_dx, const double dx_dx, diff --git a/ProcessLib/UncoupledProcessesTimeLoop.cpp b/ProcessLib/UncoupledProcessesTimeLoop.cpp index f31970e6894ce354fe4bdacf8ee519e8e72746aa..e8b122ca6a79706d77a373a1f1d0657cff094ff9 100644 --- a/ProcessLib/UncoupledProcessesTimeLoop.cpp +++ b/ProcessLib/UncoupledProcessesTimeLoop.cpp @@ -8,42 +8,16 @@ */ #include "UncoupledProcessesTimeLoop.h" + #include "BaseLib/uniqueInsert.h" #include "BaseLib/RunTime.h" #include "NumLib/ODESolver/TimeDiscretizationBuilder.h" #include "NumLib/ODESolver/TimeDiscretizedODESystem.h" #include "NumLib/ODESolver/ConvergenceCriterionPerComponent.h" -#include "NumLib/TimeStepping/Algorithms/FixedTimeStepping.h" +#include "NumLib/TimeStepping/CreateTimeStepper.h" #include "MathLib/LinAlg/LinAlg.h" -std::unique_ptr<NumLib::ITimeStepAlgorithm> createTimeStepper( - BaseLib::ConfigTree const& config) -{ - //! \ogs_file_param{prj__time_loop__time_stepping__type} - auto const type = config.peekConfigParameter<std::string>("type"); - - std::unique_ptr<NumLib::ITimeStepAlgorithm> timestepper; - - if (type == "SingleStep") - { - //! \ogs_file_param_special{prj__time_loop__time_stepping__SingleStep} - config.ignoreConfigParameter("type"); - timestepper = - std::make_unique<NumLib::FixedTimeStepping>(0.0, 1.0, 1.0); - } - else if (type == "FixedTimeStepping") - { - timestepper = NumLib::FixedTimeStepping::newInstance(config); - } - else - { - OGS_FATAL("Unknown timestepper type: `%s'.", type.c_str()); - } - - return timestepper; -} - std::unique_ptr<ProcessLib::Output> createOutput( BaseLib::ConfigTree const& config, std::string const& output_directory) { @@ -129,6 +103,7 @@ struct SingleProcessData { template <NumLib::NonlinearSolverTag NLTag> SingleProcessData( + std::unique_ptr<NumLib::TimeStepAlgorithm>&& timestepper_, NumLib::NonlinearSolver<NLTag>& nonlinear_solver, std::unique_ptr<NumLib::ConvergenceCriterion>&& conv_crit_, std::unique_ptr<NumLib::TimeDiscretization>&& time_disc_, @@ -139,6 +114,13 @@ struct SingleProcessData SingleProcessData(SingleProcessData&& spd); + std::unique_ptr<NumLib::TimeStepAlgorithm> timestepper; + + //! Flag of skiping time stepping. It is used in the modelling of + //! coupled processes. If the stepping of any process reaches a steady state + //! or the ending time, the flag is set to true. + bool skip_time_stepping = false; + //! Tag containing the missing type information necessary to cast the //! other members of this struct to their concrety types. NumLib::NonlinearSolverTag const nonlinear_solver_tag; @@ -159,13 +141,15 @@ struct SingleProcessData template <NumLib::NonlinearSolverTag NLTag> SingleProcessData::SingleProcessData( + std::unique_ptr<NumLib::TimeStepAlgorithm>&& timestepper_, NumLib::NonlinearSolver<NLTag>& nonlinear_solver, std::unique_ptr<NumLib::ConvergenceCriterion>&& conv_crit_, std::unique_ptr<NumLib::TimeDiscretization>&& time_disc_, Process& process_, std::unordered_map<std::type_index, Process const&>&& coupled_processes_, ProcessOutput&& process_output_) - : nonlinear_solver_tag(NLTag), + : timestepper(std::move(timestepper_)), + nonlinear_solver_tag(NLTag), nonlinear_solver(nonlinear_solver), conv_crit(std::move(conv_crit_)), time_disc(std::move(time_disc_)), @@ -176,7 +160,8 @@ SingleProcessData::SingleProcessData( } SingleProcessData::SingleProcessData(SingleProcessData&& spd) - : nonlinear_solver_tag(spd.nonlinear_solver_tag), + : timestepper(std::move(spd.timestepper)), + nonlinear_solver_tag(spd.nonlinear_solver_tag), nonlinear_solver(spd.nonlinear_solver), conv_crit(std::move(spd.conv_crit)), time_disc(std::move(spd.time_disc)), @@ -243,6 +228,7 @@ void setTimeDiscretizedODESystem(SingleProcessData& spd) } std::unique_ptr<SingleProcessData> makeSingleProcessData( + std::unique_ptr<NumLib::TimeStepAlgorithm>&& timestepper, NumLib::NonlinearSolverBase& nonlinear_solver, Process& process, std::unique_ptr<NumLib::TimeDiscretization>&& time_disc, @@ -257,18 +243,18 @@ std::unique_ptr<SingleProcessData> makeSingleProcessData( &nonlinear_solver)) { return std::make_unique<SingleProcessData>( - *nonlinear_solver_picard, std::move(conv_crit), - std::move(time_disc), process, std::move(coupled_processes), - std::move(process_output)); + std::move(timestepper), *nonlinear_solver_picard, + std::move(conv_crit), std::move(time_disc), process, + std::move(coupled_processes), std::move(process_output)); } if (auto* nonlinear_solver_newton = dynamic_cast<NumLib::NonlinearSolver<Tag::Newton>*>( &nonlinear_solver)) { return std::make_unique<SingleProcessData>( - *nonlinear_solver_newton, std::move(conv_crit), - std::move(time_disc), process, std::move(coupled_processes), - std::move(process_output)); + std::move(timestepper), *nonlinear_solver_newton, + std::move(conv_crit), std::move(time_disc), process, + std::move(coupled_processes), std::move(process_output)); } OGS_FATAL("Encountered unknown nonlinear solver type. Aborting"); @@ -302,6 +288,10 @@ std::vector<std::unique_ptr<SingleProcessData>> createPerProcessData( //! \ogs_file_param{prj__time_loop__processes__process__time_discretization} pcs_config.getConfigSubtree("time_discretization")); + auto timestepper = NumLib::createTimeStepper( + //! \ogs_file_param{prj__time_loop__processes__process__time_stepping} + pcs_config.getConfigSubtree("time_stepping")); + auto conv_crit = NumLib::createConvergenceCriterion( //! \ogs_file_param{prj__time_loop__processes__process__convergence_criterion} pcs_config.getConfigSubtree("convergence_criterion")); @@ -336,8 +326,9 @@ std::vector<std::unique_ptr<SingleProcessData>> createPerProcessData( ProcessOutput process_output{pcs_config.getConfigSubtree("output")}; per_process_data.emplace_back(makeSingleProcessData( - nl_slv, pcs, std::move(time_disc), std::move(conv_crit), - std::move(coupled_processes), std::move(process_output))); + std::move(timestepper), nl_slv, pcs, std::move(time_disc), + std::move(conv_crit), std::move(coupled_processes), + std::move(process_output))); } if (per_process_data.size() != processes.size()) @@ -370,10 +361,6 @@ std::unique_ptr<UncoupledProcessesTimeLoop> createUncoupledProcessesTimeLoop( coupling_config->getConfigSubtree("convergence_criterion")); } - auto timestepper = - //! \ogs_file_param{prj__time_loop__time_stepping} - createTimeStepper(config.getConfigSubtree("time_stepping")); - auto output = //! \ogs_file_param{prj__time_loop__output} createOutput(config.getConfigSubtree("output"), output_directory); @@ -382,14 +369,27 @@ std::unique_ptr<UncoupledProcessesTimeLoop> createUncoupledProcessesTimeLoop( //! \ogs_file_param{prj__time_loop__processes} config.getConfigSubtree("processes"), processes, nonlinear_solvers); + const auto minmax_iter = std::minmax_element( + per_process_data.begin(), + per_process_data.end(), + [](std::unique_ptr<SingleProcessData> const& a, + std::unique_ptr<SingleProcessData> const& b) { + return (a->timestepper->end() < b->timestepper->end()); + }); + const double start_time = + per_process_data[minmax_iter.first - per_process_data.begin()] + ->timestepper->begin(); + const double end_time = + per_process_data[minmax_iter.second - per_process_data.begin()] + ->timestepper->end(); + return std::make_unique<UncoupledProcessesTimeLoop>( - std::move(timestepper), std::move(output), std::move(per_process_data), - max_coupling_iterations, std::move(coupling_conv_crit)); + std::move(output), std::move(per_process_data), max_coupling_iterations, + std::move(coupling_conv_crit), start_time, end_time); } std::vector<GlobalVector*> setInitialConditions( double const t0, - double const delta_t0, std::vector<std::unique_ptr<SingleProcessData>> const& per_process_data) { std::vector<GlobalVector*> process_solutions; @@ -419,10 +419,8 @@ std::vector<GlobalVector*> setInitialConditions( auto& nonlinear_solver = spd->nonlinear_solver; auto& mat_strg = *spd->mat_strg; auto& conv_crit = *spd->conv_crit; - time_disc.nextTimestep(t0, delta_t0); setEquationSystem(nonlinear_solver, ode_sys, conv_crit, nl_tag); - nonlinear_solver.assemble( x0, ProcessLib::createVoidStaggeredCouplingTerm()); time_disc.pushState( @@ -468,21 +466,19 @@ bool solveOneTimeStepOneProcess(GlobalVector& x, std::size_t const timestep, bool nonlinear_solver_succeeded = nonlinear_solver.solve(x, coupling_term, post_iteration_callback); - auto& mat_strg = *process_data.mat_strg; - time_disc.pushState(t, x, mat_strg); - return nonlinear_solver_succeeded; } UncoupledProcessesTimeLoop::UncoupledProcessesTimeLoop( - std::unique_ptr<NumLib::ITimeStepAlgorithm>&& timestepper, std::unique_ptr<Output>&& output, std::vector<std::unique_ptr<SingleProcessData>>&& per_process_data, const unsigned global_coupling_max_iterations, - std::unique_ptr<NumLib::ConvergenceCriterion>&& global_coupling_conv_crit) - : _timestepper{std::move(timestepper)}, - _output(std::move(output)), + std::unique_ptr<NumLib::ConvergenceCriterion>&& global_coupling_conv_crit, + const double start_time, const double end_time) + : _output(std::move(output)), _per_process_data(std::move(per_process_data)), + _start_time(start_time), + _end_time(end_time), _global_coupling_max_iterations(global_coupling_max_iterations), _global_coupling_conv_crit(std::move(global_coupling_conv_crit)) { @@ -491,7 +487,7 @@ UncoupledProcessesTimeLoop::UncoupledProcessesTimeLoop( bool UncoupledProcessesTimeLoop::setCoupledSolutions() { // Do nothing if process are not coupled - if (!_global_coupling_conv_crit && _global_coupling_max_iterations == 1) + if ((!_global_coupling_conv_crit) || _global_coupling_max_iterations == 1) return false; unsigned pcs_idx = 0; @@ -542,6 +538,129 @@ bool UncoupledProcessesTimeLoop::setCoupledSolutions() return true; } +double UncoupledProcessesTimeLoop::computeTimeStepping( + const double prev_dt, double& t, std::size_t& accepted_steps, + std::size_t& rejected_steps) +{ + bool all_process_steps_accepted = true; + // Get minimum time step size among step sizes of all processes. + double dt = std::numeric_limits<double>::max(); + for (std::size_t i = 0; i < _per_process_data.size(); i++) + { + auto& ppd = *_per_process_data[i]; + const auto& timestepper = ppd.timestepper; + if (t > timestepper->end()) + { + // skip the process that already reaches the ending time. + ppd.skip_time_stepping = true; + continue; + } + + auto& time_disc = ppd.time_disc; + auto const& x = *_process_solutions[i]; + + auto const& conv_crit = ppd.conv_crit; + const MathLib::VecNormType norm_type = + (conv_crit) ? conv_crit->getVectorNormType() + : MathLib::VecNormType::NORM2; + + const double solution_error = + (timestepper->isSolutionErrorComputationNeeded()) + ? ((t == timestepper->begin()) + ? 0. // Always accepts the zeroth step + : time_disc->getRelativeChangeFromPreviousTimestep( + x, norm_type)) + : 0.; + if (!timestepper->next(solution_error) && + // In case of FixedTimeStepping, which makes timestepper->next(...) + // return false when the ending time is reached. + t + std::numeric_limits<double>::epsilon() < timestepper->end()) + { + // Not all processes have accepted steps. + all_process_steps_accepted = false; + } + + if (timestepper->getTimeStep().dt() > + std::numeric_limits<double>::min() || + std::abs(t - timestepper->end()) < + std::numeric_limits<double>::epsilon()) + { + if (timestepper->getTimeStep().dt() < dt) + { + dt = timestepper->getTimeStep().dt(); + } + } + else + { + // dt being close to 0 only happens when + // t_n + dt > t_s, and dt is forced to be zero. Where t_n the time + // of previous time step, and t_s is the specified time taken from + // input or the end time. Under this condition, the time stepping + // is skipped. + ppd.skip_time_stepping = true; + } + } + + bool is_initial_step = false; + // Reset the time step with the minimum step size, dt + // Update the solution of the previous time step in time_disc. + for (std::size_t i = 0; i < _per_process_data.size(); i++) + { + const auto& ppd = *_per_process_data[i]; + auto& timestepper = ppd.timestepper; + timestepper->resetCurrentTimeStep(dt); + + if (ppd.skip_time_stepping) + continue; + + if (t == timestepper->begin()) + { + is_initial_step = true; + continue; + } + + if (all_process_steps_accepted) + { + auto& time_disc = ppd.time_disc; + auto& mat_strg = *ppd.mat_strg; + auto const& x = *_process_solutions[i]; + time_disc->pushState(t, x, mat_strg); + } + else + { + if (t < _end_time) + { + WARN( + "Time step %d is rejected. " + "The computation is back to the previous time.", + accepted_steps + rejected_steps); + } + } + } + + if (!is_initial_step) + { + if (all_process_steps_accepted) + accepted_steps++; + else + { + if (t < _end_time) + { + t -= prev_dt; + rejected_steps++; + } + } + } + + // Adjust step size if t < _end_time, while t+dt exceeds the end time + if (t < _end_time && t + dt > _end_time) + { + dt = _end_time - t; + } + + return dt; +} + /* * TODO: * Now we have a structure inside the time loop which is very similar to the @@ -574,12 +693,8 @@ bool UncoupledProcessesTimeLoop::loop() } } - auto const t0 = _timestepper->getTimeStep().current(); // time of the IC - auto const delta_t0 = - _timestepper->getTimeStep().dt(); // initial time increment - // init solution storage - _process_solutions = setInitialConditions(t0, delta_t0, _per_process_data); + _process_solutions = setInitialConditions(_start_time, _per_process_data); // output initial conditions { @@ -589,46 +704,80 @@ bool UncoupledProcessesTimeLoop::loop() auto& pcs = spd->process; auto const& x0 = *_process_solutions[pcs_idx]; - pcs.preTimestep(x0, t0, _timestepper->getTimeStep().dt()); - _output->doOutput(pcs, spd->process_output, 0, t0, x0); + pcs.preTimestep(x0, _start_time, + spd->timestepper->getTimeStep().dt()); + _output->doOutput(pcs, spd->process_output, 0, _start_time, x0); ++pcs_idx; } } const bool is_staggered_coupling = setCoupledSolutions(); - double t = t0; - std::size_t timestep = 1; // the first timestep really is number one + double t = _start_time; + std::size_t accepted_steps = 0; + std::size_t rejected_steps = 0; bool nonlinear_solver_succeeded = true; - while (_timestepper->next()) + double dt = computeTimeStepping(0.0, t, accepted_steps, rejected_steps); + + while (t < _end_time) { BaseLib::RunTime time_timestep; time_timestep.start(); - auto const ts = _timestepper->getTimeStep(); - auto const delta_t = ts.dt(); - t = ts.current(); - timestep = ts.steps(); + t += dt; + const double prev_dt = dt; - INFO("=== timestep #%u (t=%gs, dt=%gs) ==============================", - timestep, t, delta_t); + const std::size_t timesteps = accepted_steps + rejected_steps + 1; + // TODO, input option for time unit. + INFO("=== Time stepping at step #%u and time %g with step size %g", + timesteps, t, dt); if (is_staggered_coupling) nonlinear_solver_succeeded = - solveCoupledEquationSystemsByStaggeredScheme(t, delta_t, - timestep); + solveCoupledEquationSystemsByStaggeredScheme(t, dt, timesteps); else nonlinear_solver_succeeded = - solveUncoupledEquationSystems(t, delta_t, timestep); + solveUncoupledEquationSystems(t, dt, timesteps); - INFO("[time] Time step #%u took %g s.", timestep, + INFO("[time] Time step #%u took %g s.", timesteps, time_timestep.elapsed()); if (!nonlinear_solver_succeeded) + { + WARN( + "Time step %d is rejected due to " + "the divergence of the non-linear solver.\n" + "\tThe time stepping steps back to the previous time\n" + "\tand starts again with the half of the current step size.", + timesteps); + t -= prev_dt; + dt *= 0.5; + rejected_steps++; + continue; + } + + dt = computeTimeStepping(prev_dt, t, accepted_steps, rejected_steps); + + if (t + dt > _end_time || + t + std::numeric_limits<double>::epsilon() > _end_time) + break; + + if (dt < std::numeric_limits<double>::epsilon()) + { + WARN( + "Time step size of %g is too small.\n" + "Time stepping stops at step %u and at time of %g.", + dt, timesteps, t); break; + } } + INFO( + "The whole computation of the time stepping took %u steps, in which\n" + "\t the accepted steps are %u, and the rejected steps are %u.\n", + accepted_steps + rejected_steps, accepted_steps, rejected_steps) + // output last time step if (nonlinear_solver_succeeded) { @@ -637,7 +786,8 @@ bool UncoupledProcessesTimeLoop::loop() { auto& pcs = spd->process; auto const& x = *_process_solutions[pcs_idx]; - _output->doOutputLastTimestep(pcs, spd->process_output, timestep, t, + _output->doOutputLastTimestep(pcs, spd->process_output, + accepted_steps + rejected_steps, t, x); ++pcs_idx; @@ -654,11 +804,18 @@ bool UncoupledProcessesTimeLoop::solveUncoupledEquationSystems( unsigned pcs_idx = 0; for (auto& spd : _per_process_data) { - auto& pcs = spd->process; + if (spd->skip_time_stepping) + { + INFO("Process %u is skipped in the time stepping.", pcs_idx); + ++pcs_idx; + continue; + } + BaseLib::RunTime time_timestep_process; time_timestep_process.start(); auto& x = *_process_solutions[pcs_idx]; + auto& pcs = spd->process; pcs.preTimestep(x, t, dt); const auto void_staggered_coupling_term = @@ -708,7 +865,13 @@ bool UncoupledProcessesTimeLoop::solveCoupledEquationSystemsByStaggeredScheme( unsigned pcs_idx = 0; for (auto& spd : _per_process_data) { - auto& pcs = spd->process; + if (spd->skip_time_stepping) + { + INFO("Process %u is skipped in the time stepping.", pcs_idx); + ++pcs_idx; + continue; + } + BaseLib::RunTime time_timestep_process; time_timestep_process.start(); @@ -719,7 +882,7 @@ bool UncoupledProcessesTimeLoop::solveCoupledEquationSystemsByStaggeredScheme( // belongs to process. For some problems, both of the current // solution and the solution of the previous time step are // required for the coupling computation. - pcs.preTimestep(x, t, dt); + spd->process.preTimestep(x, t, dt); // Set the flag of the first iteration be true. _global_coupling_conv_crit->preFirstIteration(); @@ -750,8 +913,8 @@ bool UncoupledProcessesTimeLoop::solveCoupledEquationSystemsByStaggeredScheme( timestep_id, t, pcs_idx); // save unsuccessful solution - _output->doOutputAlways(pcs, spd->process_output, timestep_id, - t, x); + _output->doOutputAlways(spd->process, spd->process_output, + timestep_id, t, x); break; } @@ -791,6 +954,12 @@ bool UncoupledProcessesTimeLoop::solveCoupledEquationSystemsByStaggeredScheme( unsigned pcs_idx = 0; for (auto& spd : _per_process_data) { + if (spd->skip_time_stepping) + { + ++pcs_idx; + continue; + } + auto& pcs = spd->process; auto& x = *_process_solutions[pcs_idx]; pcs.postTimestep(x); diff --git a/ProcessLib/UncoupledProcessesTimeLoop.h b/ProcessLib/UncoupledProcessesTimeLoop.h index 497e1276a9238786284f560d9a33c42aab076800..df0c9e058b3fc1ba66587f458fbe733ee990053c 100644 --- a/ProcessLib/UncoupledProcessesTimeLoop.h +++ b/ProcessLib/UncoupledProcessesTimeLoop.h @@ -17,7 +17,7 @@ #include <logog/include/logog.hpp> #include "NumLib/ODESolver/NonlinearSolver.h" -#include "NumLib/TimeStepping/Algorithms/ITimeStepAlgorithm.h" +#include "NumLib/TimeStepping/Algorithms/TimeStepAlgorithm.h" #include "Output.h" #include "Process.h" @@ -38,12 +38,12 @@ class UncoupledProcessesTimeLoop { public: explicit UncoupledProcessesTimeLoop( - std::unique_ptr<NumLib::ITimeStepAlgorithm>&& timestepper, std::unique_ptr<Output>&& output, std::vector<std::unique_ptr<SingleProcessData>>&& per_process_data, const unsigned global_coupling_max_iterations, std::unique_ptr<NumLib::ConvergenceCriterion>&& - global_coupling_conv_crit); + global_coupling_conv_crit, + const double start_time, const double end_time); bool loop(); @@ -63,10 +63,12 @@ public: private: std::vector<GlobalVector*> _process_solutions; - std::unique_ptr<NumLib::ITimeStepAlgorithm> _timestepper; std::unique_ptr<Output> _output; std::vector<std::unique_ptr<SingleProcessData>> _per_process_data; + const double _start_time; + const double _end_time; + /// Maximum iterations of the global coupling. const unsigned _global_coupling_max_iterations; /// Convergence criteria of the global coupling iterations. @@ -110,6 +112,21 @@ private: */ bool solveCoupledEquationSystemsByStaggeredScheme( const double t, const double dt, const std::size_t timestep_id); + + /** + * Find the minimum time step size among the predicted step sizes of + * processes and step it as common time step size. + * + * @param prev_dt Previous time step size. + * @param t Current time. + * @param accepted_steps Accepted time steps that are counted in this + * function. + * @param rejected_steps Rejected time steps that are counted in this + * function. + */ + double computeTimeStepping(const double prev_dt, double& t, + std::size_t& accepted_steps, + std::size_t& rejected_steps); }; //! Builds an UncoupledProcessesTimeLoop from the given configuration. diff --git a/ProcessLib/VectorMatrixAssembler.cpp b/ProcessLib/VectorMatrixAssembler.cpp index 0420af82b719d319bcb3ab2e163b44d9836d59df..04b9e6ba71bccdd05b26d795828d6e436e9905ff 100644 --- a/ProcessLib/VectorMatrixAssembler.cpp +++ b/ProcessLib/VectorMatrixAssembler.cpp @@ -79,13 +79,22 @@ void VectorMatrixAssembler::assemble( getPreviousLocalSolutionsOfCoupledProcesses(coupling_term, indices); auto local_coupled_xs = getCurrentLocalSolutionsOfCoupledProcesses( coupling_term.coupled_xs, indices); - ProcessLib::LocalCouplingTerm local_coupling_term( - coupling_term.dt, coupling_term.coupled_processes, - std::move(local_coupled_xs0), std::move(local_coupled_xs)); - local_assembler.assembleWithCoupledTerm(t, local_x, _local_M_data, - _local_K_data, _local_b_data, - local_coupling_term); + if (local_coupled_xs0.empty() || local_coupled_xs.empty()) + { + local_assembler.assemble(t, local_x, _local_M_data, _local_K_data, + _local_b_data); + } + else + { + ProcessLib::LocalCouplingTerm local_coupling_term( + coupling_term.dt, coupling_term.coupled_processes, + std::move(local_coupled_xs0), std::move(local_coupled_xs)); + + local_assembler.assembleWithCoupledTerm( + t, local_x, _local_M_data, _local_K_data, _local_b_data, + local_coupling_term); + } } auto const num_r_c = indices.size(); @@ -137,14 +146,23 @@ void VectorMatrixAssembler::assembleWithJacobian( getPreviousLocalSolutionsOfCoupledProcesses(coupling_term, indices); auto local_coupled_xs = getCurrentLocalSolutionsOfCoupledProcesses( coupling_term.coupled_xs, indices); - ProcessLib::LocalCouplingTerm local_coupling_term( - coupling_term.dt, coupling_term.coupled_processes, - std::move(local_coupled_xs0), std::move(local_coupled_xs)); - - _jacobian_assembler->assembleWithJacobianAndCouping( - local_assembler, t, local_x, local_xdot, dxdot_dx, dx_dx, - _local_M_data, _local_K_data, _local_b_data, _local_Jac_data, - local_coupling_term); + if (local_coupled_xs0.empty() || local_coupled_xs.empty()) + { + _jacobian_assembler->assembleWithJacobian( + local_assembler, t, local_x, local_xdot, dxdot_dx, dx_dx, + _local_M_data, _local_K_data, _local_b_data, _local_Jac_data); + } + else + { + ProcessLib::LocalCouplingTerm local_coupling_term( + coupling_term.dt, coupling_term.coupled_processes, + std::move(local_coupled_xs0), std::move(local_coupled_xs)); + + _jacobian_assembler->assembleWithJacobianAndCoupling( + local_assembler, t, local_x, local_xdot, dxdot_dx, dx_dx, + _local_M_data, _local_K_data, _local_b_data, _local_Jac_data, + local_coupling_term); + } } auto const num_r_c = indices.size(); diff --git a/Tests/Data b/Tests/Data index 277b6617f11ab54c72f3e6c27560674722869b5e..ca1db9c602b485f66cead0e82924181c26b86539 160000 --- a/Tests/Data +++ b/Tests/Data @@ -1 +1 @@ -Subproject commit 277b6617f11ab54c72f3e6c27560674722869b5e +Subproject commit ca1db9c602b485f66cead0e82924181c26b86539 diff --git a/Tests/NumLib/TestTimeSteppingEvolutionaryPIDcontroller.cpp b/Tests/NumLib/TestTimeSteppingEvolutionaryPIDcontroller.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1e509148fcf6b0f8545a63f5141ba51edc79e4e8 --- /dev/null +++ b/Tests/NumLib/TestTimeSteppingEvolutionaryPIDcontroller.cpp @@ -0,0 +1,110 @@ +/** + * \copyright + * Copyright (c) 2012-2017, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + * \file TestTimeSteppingEvolutionaryPIDcontroller.cpp + * Created on April 5, 2017, 12:09 PM + */ + +#include <gtest/gtest.h> + +#include <vector> +#include <memory> + +#include <logog/include/logog.hpp> + +#include "BaseLib/ConfigTree.h" + +#include "NumLib/TimeStepping/TimeStep.h" +#include "NumLib/TimeStepping/Algorithms/EvolutionaryPIDcontroller.h" + +#include "Tests/TestTools.h" + +std::unique_ptr<NumLib::TimeStepAlgorithm> createTestTimeStepper( + const char xml[]) +{ + auto const ptree = readXml(xml); + BaseLib::ConfigTree conf(ptree, "", BaseLib::ConfigTree::onerror, + BaseLib::ConfigTree::onwarning); + auto const& sub_config = conf.getConfigSubtree("time_stepping"); + return NumLib::createEvolutionaryPIDcontroller(sub_config); +} + +TEST(NumLibTimeStepping, testEvolutionaryPIDcontroller) +{ + const char xml[] = + "<time_stepping>" + " <type>EvolutionaryPIDcontroller</type>" + " <t_initial> 0.0 </t_initial>" + " <t_end> 10 </t_end>" + " <dt_guess> 0.01 </dt_guess>" + " <dt_min> 0.001 </dt_min>" + " <dt_max> 1 </dt_max>" + " <rel_dt_min> 0.01 </rel_dt_min>" + " <rel_dt_max> 5 </rel_dt_max>" + " <tol> 1.e-3 </tol>" + "</time_stepping>"; + auto const PIDStepper = createTestTimeStepper(xml); + + double solution_error = 0.; + // 1st step + ASSERT_TRUE(PIDStepper->next(solution_error)); + NumLib::TimeStep ts = PIDStepper->getTimeStep(); + double h_new = 0.01; + double t_previous = 0.; + ASSERT_EQ(1u, ts.steps()); + ASSERT_EQ(t_previous, ts.previous()); + ASSERT_EQ(t_previous + h_new, ts.current()); + ASSERT_EQ(h_new, ts.dt()); + ASSERT_TRUE(PIDStepper->accepted()); + t_previous += h_new; + + // e_n_minus1 is filled. + solution_error = 1.0e-4; + PIDStepper->next(solution_error); + ts = PIDStepper->getTimeStep(); + h_new = ts.dt(); + ASSERT_EQ(2u, ts.steps()); + const double tol = 1.e-16; + ASSERT_NEAR(t_previous, ts.previous(), tol); + ASSERT_NEAR(t_previous + h_new, ts.current(), tol); + ASSERT_TRUE(PIDStepper->accepted()); + t_previous += h_new; + + // e_n_minus2 is filled. + solution_error = 0.5e-3; + PIDStepper->next(solution_error); + ts = PIDStepper->getTimeStep(); + h_new = ts.dt(); + ASSERT_EQ(3u, ts.steps()); + ASSERT_NEAR(t_previous, ts.previous(), tol); + ASSERT_NEAR(t_previous + h_new, ts.current(), tol); + ASSERT_TRUE(PIDStepper->accepted()); + + // error > TOL=1.3-3, step rejected and new step size estimated. + solution_error = 0.01; + PIDStepper->next(solution_error); + ts = PIDStepper->getTimeStep(); + h_new = ts.dt(); + // No change in ts.steps + ASSERT_EQ(3u, ts.steps()); + // No change in ts.previous(), which is the same as that of the previous + // step. + ASSERT_NEAR(t_previous, ts.previous(), tol); + ASSERT_NEAR(t_previous + h_new, ts.current(), tol); + ASSERT_FALSE(PIDStepper->accepted()); + t_previous += h_new; + + // With e_n, e_n_minus1, e_n_minus2 + solution_error = 0.4e-3; + PIDStepper->next(solution_error); + ts = PIDStepper->getTimeStep(); + h_new = ts.dt(); + ASSERT_EQ(4u, ts.steps()); + ASSERT_NEAR(t_previous, ts.previous(), tol); + ASSERT_NEAR(t_previous + h_new, ts.current(), tol); + ASSERT_TRUE(PIDStepper->accepted()); +} diff --git a/Tests/NumLib/TestTimeSteppingIterationNumber.cpp b/Tests/NumLib/TestTimeSteppingIterationNumber.cpp index baf12ca923216bb2022bfc3c8720c6cf22bf1363..00c82b9813715a989d08b59050b1e14c26e07c8f 100644 --- a/Tests/NumLib/TestTimeSteppingIterationNumber.cpp +++ b/Tests/NumLib/TestTimeSteppingIterationNumber.cpp @@ -29,7 +29,9 @@ TEST(NumLib, TimeSteppingIterationNumberBased1) std::vector<double> multiplier_vector = {2.0, 1.0, 0.5, 0.25}; NumLib::IterationNumberBasedAdaptiveTimeStepping alg(1, 31, 1, 10, 1, iter_times_vector, multiplier_vector); - ASSERT_TRUE(alg.next()); // t=2, dt=1 + const double solution_error = 0.; + + ASSERT_TRUE(alg.next(solution_error)); // t=2, dt=1 NumLib::TimeStep ts = alg.getTimeStep(); ASSERT_EQ(1u, ts.steps()); ASSERT_EQ(1., ts.previous()); @@ -37,11 +39,11 @@ TEST(NumLib, TimeSteppingIterationNumberBased1) ASSERT_EQ(1., ts.dt()); ASSERT_TRUE(alg.accepted()); - ASSERT_TRUE(alg.next()); // t=4, dt=2 + ASSERT_TRUE(alg.next(solution_error)); // t=4, dt=2 // dt*=2 - alg.setNIterations(3); - ASSERT_TRUE(alg.next()); // t=8, dt=4 + alg.setIterationNumber(3); + ASSERT_TRUE(alg.next(solution_error)); // t=8, dt=4 ts = alg.getTimeStep(); ASSERT_EQ(3u, ts.steps()); ASSERT_EQ(4., ts.previous()); @@ -50,8 +52,8 @@ TEST(NumLib, TimeSteppingIterationNumberBased1) ASSERT_TRUE(alg.accepted()); // dt*=1 - alg.setNIterations(5); - ASSERT_TRUE(alg.next()); // t=12, dt=4 + alg.setIterationNumber(5); + ASSERT_TRUE(alg.next(solution_error)); // t=12, dt=4 ts = alg.getTimeStep(); ASSERT_EQ(4u, ts.steps()); ASSERT_EQ(8., ts.previous()); @@ -60,8 +62,8 @@ TEST(NumLib, TimeSteppingIterationNumberBased1) ASSERT_TRUE(alg.accepted()); // dt*=0.5 - alg.setNIterations(7); - ASSERT_TRUE(alg.next()); // t=14, dt=2 + alg.setIterationNumber(7); + ASSERT_TRUE(alg.next(solution_error)); // t=14, dt=2 ts = alg.getTimeStep(); ASSERT_EQ(5u, ts.steps()); ASSERT_EQ(12., ts.previous()); @@ -70,8 +72,8 @@ TEST(NumLib, TimeSteppingIterationNumberBased1) ASSERT_TRUE(alg.accepted()); // dt*=0.25 but dt_min = 1 - alg.setNIterations(8); // exceed max - ASSERT_TRUE(alg.next()); // t=13, dt=1 + alg.setIterationNumber(8); // exceed max + ASSERT_TRUE(alg.next(solution_error)); // t=13, dt=1 ts = alg.getTimeStep(); ASSERT_EQ(5u, ts.steps()); ASSERT_EQ(12., ts.previous()); @@ -80,8 +82,8 @@ TEST(NumLib, TimeSteppingIterationNumberBased1) ASSERT_FALSE(alg.accepted()); // restart, dt*=1 - alg.setNIterations(4); - ASSERT_TRUE(alg.next()); // t=14, dt=1 + alg.setIterationNumber(4); + ASSERT_TRUE(alg.next(solution_error)); // t=14, dt=1 ts = alg.getTimeStep(); ASSERT_EQ(6u, ts.steps()); ASSERT_EQ(13., ts.previous()); @@ -114,7 +116,7 @@ TEST(NumLib, TimeSteppingIterationNumberBased2) { std::size_t n = (i<_nr_iterations.size()) ? _nr_iterations[i++] : 0; //INFO("-> NR-iterations=%d", n); - obj.setNIterations(n); + obj.setIterationNumber(n); } }; diff --git a/Tests/NumLib/TimeSteppingTestingTools.h b/Tests/NumLib/TimeSteppingTestingTools.h index 0922d246883df3aa5556ba540dd5a3fa77b5a535..eaeca4df6100f4c60c50f0b6f338e2ae19872537 100644 --- a/Tests/NumLib/TimeSteppingTestingTools.h +++ b/Tests/NumLib/TimeSteppingTestingTools.h @@ -31,7 +31,7 @@ std::vector<double> timeStepping(T_TIME_STEPPING &algorithm, T* obj=nullptr) std::vector<double> vec_t; vec_t.push_back(algorithm.begin()); - while (algorithm.next()) { + while (algorithm.next(0.0)) { NumLib::TimeStep t = algorithm.getTimeStep(); //INFO("t: n=%d,t=%g,dt=%g", t.steps(), t.current(), t.dt()); if (obj)