Coverage for pySDC/implementations/convergence_controller_classes/step_size_limiter.py: 86%
63 statements
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-20 14:51 +0000
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-20 14:51 +0000
1import numpy as np
2from pySDC.core.convergence_controller import ConvergenceController
5class StepSizeLimiter(ConvergenceController):
6 """
7 Class to set limits to adaptive step size computation during run time
9 Please supply dt_min or dt_max in the params to limit in either direction
10 """
12 def setup(self, controller, params, description, **kwargs):
13 """
14 Define parameters here
16 Args:
17 controller (pySDC.Controller): The controller
18 params (dict): The params passed for this specific convergence controller
19 description (dict): The description object used to instantiate the controller
21 Returns:
22 (dict): The updated params dictionary
23 """
24 defaults = {
25 "control_order": +92,
26 "dt_min": 0,
27 "dt_max": np.inf,
28 }
29 return {**defaults, **super().setup(controller, params, description, **kwargs)}
31 def dependencies(self, controller, description, **kwargs):
32 """
33 Load the slope limiter if needed.
35 Args:
36 controller (pySDC.Controller): The controller
37 description (dict): The description object used to instantiate the controller
39 Returns:
40 None
41 """
42 slope_limiter_keys = ['dt_slope_min', 'dt_slope_max', 'dt_rel_min_slope']
43 available_keys = [me for me in slope_limiter_keys if me in self.params.__dict__.keys()]
45 if len(available_keys) > 0:
46 slope_limiter_params = {key: self.params.__dict__[key] for key in available_keys}
47 slope_limiter_params['control_order'] = self.params.control_order - 1
48 controller.add_convergence_controller(
49 StepSizeSlopeLimiter, params=slope_limiter_params, description=description
50 )
52 return None
54 def get_new_step_size(self, controller, S, **kwargs):
55 """
56 Enforce an upper and lower limit to the step size here.
57 Be aware that this is only tested when a new step size has been determined. That means if you set an initial
58 value for the step size outside of the limits, and you don't do any further step size control, that value will
59 go through.
60 Also, the final step is adjusted such that we reach Tend as best as possible, which might give step sizes below
61 the lower limit set here.
63 Args:
64 controller (pySDC.Controller): The controller
65 S (pySDC.Step): The current step
67 Returns:
68 None
69 """
70 for L in S.levels:
71 if L.status.dt_new is not None:
72 if L.status.dt_new < self.params.dt_min:
73 self.log(
74 f"Step size is below minimum, increasing from {L.status.dt_new:.2e} to \
75{self.params.dt_min:.2e}",
76 S,
77 )
78 L.status.dt_new = self.params.dt_min
79 elif L.status.dt_new > self.params.dt_max:
80 self.log(
81 f"Step size exceeds maximum, decreasing from {L.status.dt_new:.2e} to {self.params.dt_max:.2e}",
82 S,
83 )
84 L.status.dt_new = self.params.dt_max
86 return None
89class StepSizeSlopeLimiter(ConvergenceController):
90 """
91 Class to set limits to adaptive step size computation during run time
93 Please supply `dt_slope_min` or `dt_slope_max` in the params to limit in either direction.
94 You can also supply `dt_rel_min_slope` in order to keep the old step size in case the relative change is smaller
95 than this minimum.
96 """
98 def setup(self, controller, params, description, **kwargs):
99 """
100 Define parameters here
102 Args:
103 controller (pySDC.Controller): The controller
104 params (dict): The params passed for this specific convergence controller
105 description (dict): The description object used to instantiate the controller
107 Returns:
108 (dict): The updated params dictionary
109 """
110 defaults = {
111 "control_order": 91,
112 "dt_slope_min": 0,
113 "dt_slope_max": np.inf,
114 "dt_rel_min_slope": 0,
115 }
116 return {**defaults, **super().setup(controller, params, description, **kwargs)}
118 def get_new_step_size(self, controller, S, **kwargs):
119 """
120 Enforce an upper and lower limit to the slope of the step size here.
121 The final step is adjusted such that we reach Tend as best as possible, which might give step sizes below
122 the lower limit set here.
124 Args:
125 controller (pySDC.Controller): The controller
126 S (pySDC.Step): The current step
128 Returns:
129 None
130 """
131 for L in S.levels:
132 if L.status.dt_new is not None:
133 if L.status.dt_new / L.params.dt < self.params.dt_slope_min:
134 dt_new = L.params.dt * self.params.dt_slope_min
135 self.log(
136 f"Step size slope is below minimum, increasing from {L.status.dt_new:.2e} to \
137{dt_new:.2e}",
138 S,
139 )
140 L.status.dt_new = dt_new
141 elif L.status.dt_new / L.params.dt > self.params.dt_slope_max:
142 dt_new = L.params.dt * self.params.dt_slope_max
143 self.log(
144 f"Step size slope exceeds maximum, decreasing from {L.status.dt_new:.2e} to \
145{dt_new:.2e}",
146 S,
147 )
148 L.status.dt_new = dt_new
149 elif abs(L.status.dt_new / L.params.dt - 1) < self.params.dt_rel_min_slope and not S.status.restart:
150 L.status.dt_new = L.params.dt
151 self.log(
152 f"Step size did not change sufficiently to warrant step size change, keeping {L.status.dt_new:.2e}",
153 S,
154 )
156 return None
159class StepSizeRounding(ConvergenceController):
160 """
161 Class to round step size when using adaptive step size selection.
162 """
164 def setup(self, controller, params, description, **kwargs):
165 """
166 Define parameters here
168 Args:
169 controller (pySDC.Controller): The controller
170 params (dict): The params passed for this specific convergence controller
171 description (dict): The description object used to instantiate the controller
173 Returns:
174 (dict): The updated params dictionary
175 """
176 defaults = {
177 "control_order": +93,
178 "digits": 1,
179 "fac": 5,
180 }
181 return {**defaults, **super().setup(controller, params, description, **kwargs)}
183 @staticmethod
184 def _round_step_size(dt, fac, digits):
185 dt_rounded = None
186 exponent = np.log10(dt) // 1
188 dt_norm = dt / 10 ** (exponent - digits)
189 dt_norm_round = (dt_norm // fac) * fac
190 dt_rounded = dt_norm_round * 10 ** (exponent - digits)
191 return dt_rounded
193 def get_new_step_size(self, controller, S, **kwargs):
194 """
195 Enforce an upper and lower limit to the step size here.
196 Be aware that this is only tested when a new step size has been determined. That means if you set an initial
197 value for the step size outside of the limits, and you don't do any further step size control, that value will
198 go through.
199 Also, the final step is adjusted such that we reach Tend as best as possible, which might give step sizes below
200 the lower limit set here.
202 Args:
203 controller (pySDC.Controller): The controller
204 S (pySDC.Step): The current step
206 Returns:
207 None
208 """
209 for L in S.levels:
210 if L.status.dt_new is not None:
211 dt_rounded = self._round_step_size(L.status.dt_new, self.params.fac, self.params.digits)
213 if L.status.dt_new != dt_rounded:
214 self.log(
215 f"Step size rounded from {L.status.dt_new:.6e} to {dt_rounded:.6e}",
216 S,
217 )
218 L.status.dt_new = dt_rounded
220 return None