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

1import numpy as np 

2from pySDC.core.convergence_controller import ConvergenceController 

3 

4 

5class StepSizeLimiter(ConvergenceController): 

6 """ 

7 Class to set limits to adaptive step size computation during run time 

8 

9 Please supply dt_min or dt_max in the params to limit in either direction 

10 """ 

11 

12 def setup(self, controller, params, description, **kwargs): 

13 """ 

14 Define parameters here 

15 

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 

20 

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)} 

30 

31 def dependencies(self, controller, description, **kwargs): 

32 """ 

33 Load the slope limiter if needed. 

34 

35 Args: 

36 controller (pySDC.Controller): The controller 

37 description (dict): The description object used to instantiate the controller 

38 

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()] 

44 

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 ) 

51 

52 return None 

53 

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. 

62 

63 Args: 

64 controller (pySDC.Controller): The controller 

65 S (pySDC.Step): The current step 

66 

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 

85 

86 return None 

87 

88 

89class StepSizeSlopeLimiter(ConvergenceController): 

90 """ 

91 Class to set limits to adaptive step size computation during run time 

92 

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 """ 

97 

98 def setup(self, controller, params, description, **kwargs): 

99 """ 

100 Define parameters here 

101 

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 

106 

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)} 

117 

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. 

123 

124 Args: 

125 controller (pySDC.Controller): The controller 

126 S (pySDC.Step): The current step 

127 

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 ) 

155 

156 return None 

157 

158 

159class StepSizeRounding(ConvergenceController): 

160 """ 

161 Class to round step size when using adaptive step size selection. 

162 """ 

163 

164 def setup(self, controller, params, description, **kwargs): 

165 """ 

166 Define parameters here 

167 

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 

172 

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)} 

182 

183 @staticmethod 

184 def _round_step_size(dt, fac, digits): 

185 dt_rounded = None 

186 exponent = np.log10(dt) // 1 

187 

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 

192 

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. 

201 

202 Args: 

203 controller (pySDC.Controller): The controller 

204 S (pySDC.Step): The current step 

205 

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) 

212 

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 

219 

220 return None