diff --git a/NumLib/TimeStepping/Algorithms/EvolutionaryPIDcontroller.cpp b/NumLib/TimeStepping/Algorithms/EvolutionaryPIDcontroller.cpp index 71b17e2304683c26c90a4d62182d36465a622ab1..54e4ae16e69b69524994a97885af469998f03f9c 100644 --- a/NumLib/TimeStepping/Algorithms/EvolutionaryPIDcontroller.cpp +++ b/NumLib/TimeStepping/Algorithms/EvolutionaryPIDcontroller.cpp @@ -22,6 +22,8 @@ namespace NumLib { bool EvolutionaryPIDcontroller::next(const double solution_error) { + const bool is_previous_step_accepted = _is_accepted; + const double e_n = solution_error; const double zero_threshlod = std::numeric_limits<double>::epsilon(); // step rejected. @@ -32,19 +34,22 @@ bool EvolutionaryPIDcontroller::next(const double solution_error) 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 = + limitStepSize(h_new, _ts_current.dt(), is_previous_step_accepted); h_new = checkSpecificTimeReached(h_new); _ts_current = _ts_prev; _ts_current += h_new; - WARN("This step is rejected due to the relative change from the" - " solution of the previous\n" - "\t time step to the current solution exceeds the given tolerance" - " of %g.\n" - "\t This time step will be repeated with a new time step size of" - " %g\n" - "\t or the simulation will be halted." , _tol, h_new); + WARN( + "This step is rejected due to the relative change from the" + " solution of the previous\n" + "\t time step to the current solution exceeds the given tolerance" + " of %g.\n" + "\t This time step will be repeated with a new time step size of" + " %g\n" + "\t or the simulation will be halted.", + _tol, h_new); return false; } @@ -90,7 +95,7 @@ bool EvolutionaryPIDcontroller::next(const double solution_error) } } - h_new = limitStepSize(h_new, h_n); + h_new = limitStepSize(h_new, h_n, is_previous_step_accepted); h_new = checkSpecificTimeReached(h_new); _dt_vector.push_back(h_new); @@ -104,6 +109,35 @@ bool EvolutionaryPIDcontroller::next(const double solution_error) return true; } +double EvolutionaryPIDcontroller::limitStepSize( + const double h_new, const double h_n, + const bool previous_step_accepted) const +{ + const double h_in_range = std::max(_h_min, std::min(h_new, _h_max)); + double limited_h = + std::max(_rel_h_min * h_n, std::min(h_in_range, _rel_h_max * h_n)); + + if (!previous_step_accepted) + { + // If the last time step was rejected and the new predicted time step + // size is identical to that of the previous rejected step, the new + // step size is then reduced by half. + if (std::fabs(limited_h - _ts_current.dt()) < + std::numeric_limits<double>::min()) + limited_h = std::max(_h_min, 0.5 * limited_h); + + // If the last time step was rejected and the new predicted time step + // size is larger than the step size of the rejected step, the new step + // size takes the half of the size of the rejected step. This could + // happen when a time step is rejected due to a diverged non-linear + // solver. In such case, this algorithm may give a large time step size + // by using the diverged solution. + if (limited_h > _ts_current.dt()) + limited_h = std::max(_h_min, 0.5 * _ts_current.dt()); + } + return limited_h; +} + double EvolutionaryPIDcontroller::checkSpecificTimeReached(const double h_new) { if (_specific_times.empty()) diff --git a/NumLib/TimeStepping/Algorithms/EvolutionaryPIDcontroller.h b/NumLib/TimeStepping/Algorithms/EvolutionaryPIDcontroller.h index bd22e0080f0dfa6a46ea334b12cb4dfc78c2e619..5f0581b96710051a5048ed639e5d18c7e1130acd 100644 --- a/NumLib/TimeStepping/Algorithms/EvolutionaryPIDcontroller.h +++ b/NumLib/TimeStepping/Algorithms/EvolutionaryPIDcontroller.h @@ -81,10 +81,16 @@ public: /// return if current time step is accepted bool accepted() const override { return _is_accepted; } + /// Set the status of the step + /// \param accepted A flag for whether the step is accepted or not + void setAcceptedOrNot(const bool accepted) override + { + _is_accepted = 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 @@ -109,12 +115,8 @@ private: 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 limitStepSize(const double h_new, const double h_n, + const bool previous_step_accepted) const; double checkSpecificTimeReached(const double h_new); }; diff --git a/NumLib/TimeStepping/Algorithms/TimeStepAlgorithm.h b/NumLib/TimeStepping/Algorithms/TimeStepAlgorithm.h index a74cd0f6a9fea415f7eefab749f3c88650a8e49a..659dd07c6145cbeb70208d1d3131599f31e0f6db 100644 --- a/NumLib/TimeStepping/Algorithms/TimeStepAlgorithm.h +++ b/NumLib/TimeStepping/Algorithms/TimeStepAlgorithm.h @@ -72,6 +72,10 @@ public: /// return if current time step is accepted or not virtual bool accepted() const = 0; + /// Set the status of the step. A boolean parameter is needed to indicated + /// whether the step is accepted or not. + virtual void setAcceptedOrNot(const bool /*accepted*/){}; + /// return a history of time step sizes const std::vector<double>& getTimeStepSizeHistory() const { @@ -81,7 +85,6 @@ public: /// 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; diff --git a/ProcessLib/UncoupledProcessesTimeLoop.cpp b/ProcessLib/UncoupledProcessesTimeLoop.cpp index 594525cfbdc00eedf8692fb0f98350b44e0ecad6..9f73bb1e06a319b6beabacf217b73ca904b82c39 100644 --- a/ProcessLib/UncoupledProcessesTimeLoop.cpp +++ b/ProcessLib/UncoupledProcessesTimeLoop.cpp @@ -576,6 +576,9 @@ double UncoupledProcessesTimeLoop::computeTimeStepping( x, norm_type)) : 0.; + if (!ppd.nonlinear_solver_converged) + timestepper->setAcceptedOrNot(false); + if (!timestepper->next(solution_error) && // In case of FixedTimeStepping, which makes timestepper->next(...) // return false when the ending time is reached.