OGS
EvolutionaryPIDcontroller.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
2// SPDX-License-Identifier: BSD-3-Clause
3
5
6#include <functional>
7#include <limits>
8#include <vector>
9
10#include "BaseLib/Algorithm.h"
11#include "BaseLib/Logging.h"
12
13namespace NumLib
14{
15double EvolutionaryPIDcontroller::next(double const solution_error,
16 int const /*number_iterations*/,
17 NumLib::TimeStep& /*timestep_previous*/,
18 NumLib::TimeStep& timestep_current)
19{
20 const bool is_previous_step_accepted = timestep_current.isAccepted();
21
22 const double e_n = solution_error;
23 const double zero_threshold = std::numeric_limits<double>::epsilon();
24 // step rejected.
25 if (e_n > _tol)
26 {
27 timestep_current.setAccepted(false);
28
29 double h_new = (e_n > zero_threshold)
30 ? timestep_current.dt() * _tol / e_n
31 : 0.5 * timestep_current.dt();
32
33 h_new =
34 limitStepSize(h_new, is_previous_step_accepted, timestep_current);
35
36 WARN(
37 "This step is rejected due to the relative change from the"
38 " solution of the previous\n"
39 "\t time step to the current solution exceeds the given tolerance"
40 " of {:g}.\n"
41 "\t This time step will be repeated with a new time step size of"
42 " {:g}\n"
43 "\t or the simulation will be halted.",
44 _tol, h_new);
45
46 return h_new;
47 }
48
49 // step accepted.
50 timestep_current.setAccepted(true);
51
52 if (timestep_current.timeStepNumber() == 0)
53 {
54 _e_n_minus1 = e_n;
55
56 return _h0;
57 }
58 const double h_n = timestep_current.dt();
59 double h_new = h_n;
60
61 if (e_n > zero_threshold)
62 {
63 if (_e_n_minus1 > zero_threshold)
64 {
65 if (_e_n_minus2 > zero_threshold)
66 {
67 h_new =
68 std::pow(_e_n_minus1 / e_n, _kP) *
69 std::pow(_tol / e_n, _kI) *
70 std::pow(_e_n_minus1 * _e_n_minus1 / (e_n * _e_n_minus2),
71 _kD) *
72 h_n;
73 }
74 else
75 {
76 h_new = std::pow(_e_n_minus1 / e_n, _kP) *
77 std::pow(_tol / e_n, _kI) * h_n;
78 }
79 }
80 else
81 {
82 h_new = std::pow(_tol / e_n, _kI) * h_n;
83 }
84 }
85
86 h_new = limitStepSize(h_new, is_previous_step_accepted, timestep_current);
87
89 _e_n_minus1 = e_n;
90
91 return h_new;
92}
93
95 const double h_new, const bool previous_step_accepted,
96 NumLib::TimeStep const& timestep_current) const
97{
98 const double h_n = timestep_current.dt();
99 // Forced the computed time step size in the given range
100 // (see the formulas in the documentation of the class)
101 const double h_in_range = std::max(_h_min, std::min(h_new, _h_max));
102 // Limit the step size change ratio.
103 double limited_h =
104 std::max(_rel_h_min * h_n, std::min(h_in_range, _rel_h_max * h_n));
105
106 if (!previous_step_accepted)
107 {
108 // If the last time step was rejected and the new predicted time step
109 // size is identical to that of the previous rejected step, the new
110 // step size is then reduced by half.
111 if (std::abs(limited_h - timestep_current.dt()) <
112 std::numeric_limits<double>::min())
113 {
114 limited_h = std::max(_h_min, 0.5 * limited_h);
115 }
116
117 // If the last time step was rejected and the new predicted time step
118 // size is larger than the step size of the rejected step, the new step
119 // size takes the half of the size of the rejected step. This could
120 // happen when a time step is rejected due to a diverged non-linear
121 // solver. In such case, this algorithm may give a large time step size
122 // by using the diverged solution.
123 if (limited_h > timestep_current.dt())
124 {
125 limited_h = std::max(_h_min, 0.5 * timestep_current.dt());
126 }
127 }
128
129 if (_fixed_times_for_output.empty())
130 {
131 return limited_h;
132 }
133
134 // find first fixed output time larger than the current time, i.e.,
135 // current time < fixed output time
136 auto fixed_output_time_it = std::find_if(
138 [&timestep_current](auto const fixed_output_time)
139 { return timestep_current.current() < Time{fixed_output_time}; });
140
141 if (fixed_output_time_it != _fixed_times_for_output.end())
142 {
143 // check if the fixed output time is in the interval
144 // (current time, current time + limited_h)
145 if (Time{*fixed_output_time_it} <
146 timestep_current.current() + limited_h)
147 {
148 // check if the potential adjusted time step is larger than zero
149 if (std::abs(*fixed_output_time_it - timestep_current.current()()) >
150 std::numeric_limits<double>::epsilon() *
151 timestep_current.current()())
152 {
153 return *fixed_output_time_it - timestep_current.current()();
154 }
155 }
156 }
157 return limited_h;
158}
159
161 NumLib::TimeStep const& timestep_previous,
162 NumLib::TimeStep const& timestep_current) const
163{
164 return NumLib::canReduceTimestepSize(timestep_previous, timestep_current,
165 _h_min);
166}
167} // namespace NumLib
void WARN(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:34
const double _h_max
maximum step size.
std::vector< double > const _fixed_times_for_output
const double _h_min
minimum step size.
virtual bool canReduceTimestepSize(NumLib::TimeStep const &timestep_previous, NumLib::TimeStep const &timestep_current) const override
Query the timestepper if further time step size reduction is possible.
double limitStepSize(const double h_new, const bool previous_step_accepted, NumLib::TimeStep const &timestep_current) const
const double _h0
initial time step size.
double next(double solution_error, int number_iterations, NumLib::TimeStep &timestep_previous, NumLib::TimeStep &timestep_current) override
Time step object.
Definition TimeStep.h:24
void setAccepted(bool const accepted)
Definition TimeStep.h:77
Time current() const
return current time step
Definition TimeStep.h:71
bool isAccepted() const
Definition TimeStep.h:78
double dt() const
time step size from _previous
Definition TimeStep.h:73
std::size_t timeStepNumber() const
the time step number
Definition TimeStep.h:75
bool canReduceTimestepSize(TimeStep const &timestep_previous, TimeStep const &timestep_current, double const min_dt)