Coverage for pySDC/projects/Resilience/RBC.py: 19%
187 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
1# script to run a Rayleigh-Benard Convection problem
2from pySDC.implementations.problem_classes.generic_spectral import compute_residual_DAE, get_extrapolated_error_DAE
3from pySDC.implementations.problem_classes.RayleighBenard import RayleighBenard
4from pySDC.implementations.controller_classes.controller_nonMPI import controller_nonMPI
5from pySDC.projects.Resilience.hook import hook_collection, LogData
6from pySDC.projects.Resilience.strategies import merge_descriptions
7from pySDC.implementations.sweeper_classes.imex_1st_order import imex_1st_order
8from pySDC.core.convergence_controller import ConvergenceController
9from pySDC.implementations.convergence_controller_classes.estimate_extrapolation_error import (
10 EstimateExtrapolationErrorNonMPI,
11)
12from pySDC.implementations.convergence_controller_classes.check_convergence import CheckConvergence
14from pySDC.core.errors import ConvergenceError
16import numpy as np
19def u_exact(self, t, u_init=None, t_init=None, recompute=False, _t0=None):
20 import pickle
21 import os
23 path = f'data/stats/RBC-u_init-{t:.8f}.pickle'
24 if os.path.exists(path) and not recompute and t_init is None:
25 with open(path, 'rb') as file:
26 data = pickle.load(file)
27 else:
28 from pySDC.helpers.stats_helper import get_sorted
29 from pySDC.implementations.convergence_controller_classes.adaptivity import Adaptivity
31 convergence_controllers = {
32 Adaptivity: {'e_tol': 1e-8, 'dt_rel_min_slope': 0.25, 'dt_min': 1e-5},
33 }
34 desc = {'convergence_controllers': convergence_controllers}
36 u0 = self._u_exact(0) if u_init is None else u_init
37 t0 = 0 if t_init is None else t_init
39 if t == t0:
40 return u0
41 else:
42 u0 = u0 if _t0 is None else self.u_exact(_t0)
43 _t0 = t0 if _t0 is None else _t0
45 stats, _, _ = run_RBC(Tend=t, u0=u0, t0=_t0, custom_description=desc)
47 u = get_sorted(stats, type='u', recomputed=False)[-1]
48 data = u[1]
50 if t0 == 0:
51 with open(path, 'wb') as file:
52 pickle.dump(data, file)
54 return data
57RayleighBenard._u_exact = RayleighBenard.u_exact
58RayleighBenard.u_exact = u_exact
60PROBLEM_PARAMS = {'Rayleigh': 2e4, 'nx': 256, 'nz': 128}
63class ReachTendExactly(ConvergenceController):
64 """
65 This convergence controller will adapt the step size of (hopefully) the last step such that `Tend` is reached very closely.
66 Please pass the same `Tend` that you pass to the controller to the params for this to work.
67 """
69 def setup(self, controller, params, description, **kwargs):
70 defaults = {
71 "control_order": +94,
72 "Tend": None,
73 'min_step_size': 1e-10,
74 }
75 return {**defaults, **super().setup(controller, params, description, **kwargs)}
77 def get_new_step_size(self, controller, step, **kwargs):
78 L = step.levels[0]
80 if not CheckConvergence.check_convergence(step):
81 return None
83 dt = L.status.dt_new if L.status.dt_new else L.params.dt
84 time_left = self.params.Tend - L.time - L.dt
85 if time_left <= dt + self.params.min_step_size and not step.status.restart and time_left > 0:
86 dt_new = (
87 min([dt + self.params.min_step_size, max([time_left, self.params.min_step_size])])
88 + np.finfo('float').eps * 10
89 )
90 if dt_new != L.status.dt_new:
91 L.status.dt_new = dt_new
92 self.log(
93 f'Reducing step size from {dt:12e} to {L.status.dt_new:.12e} because there is only {time_left:.12e} left.',
94 step,
95 )
98def run_RBC(
99 custom_description=None,
100 num_procs=1,
101 Tend=21.0,
102 hook_class=LogData,
103 fault_stuff=None,
104 custom_controller_params=None,
105 u0=None,
106 t0=20.0,
107 use_MPI=False,
108 **kwargs,
109):
110 """
111 Args:
112 custom_description (dict): Overwrite presets
113 num_procs (int): Number of steps for MSSDC
114 Tend (float): Time to integrate to
115 hook_class (pySDC.Hook): A hook to store data
116 fault_stuff (dict): A dictionary with information on how to add faults
117 custom_controller_params (dict): Overwrite presets
118 u0 (dtype_u): Initial value
119 t0 (float): Starting time
120 use_MPI (bool): Whether or not to use MPI
122 Returns:
123 dict: The stats object
124 controller: The controller
125 bool: If the code crashed
126 """
127 EstimateExtrapolationErrorNonMPI.get_extrapolated_error = get_extrapolated_error_DAE
129 level_params = {}
130 level_params['dt'] = 1e-3
131 level_params['restol'] = -1
133 sweeper_params = {}
134 sweeper_params['quad_type'] = 'RADAU-RIGHT'
135 sweeper_params['num_nodes'] = 3
136 sweeper_params['QI'] = 'LU'
137 sweeper_params['QE'] = 'PIC'
139 from mpi4py import MPI
141 problem_params = {'comm': MPI.COMM_SELF, **PROBLEM_PARAMS}
143 step_params = {}
144 step_params['maxiter'] = 5
146 convergence_controllers = {}
147 convergence_controllers[ReachTendExactly] = {'Tend': Tend}
148 from pySDC.implementations.convergence_controller_classes.step_size_limiter import StepSizeRounding
150 convergence_controllers[StepSizeRounding] = {}
152 controller_params = {}
153 controller_params['logger_level'] = 30
154 controller_params['hook_class'] = hook_collection + (hook_class if type(hook_class) == list else [hook_class])
155 controller_params['mssdc_jac'] = False
157 if custom_controller_params is not None:
158 controller_params = {**controller_params, **custom_controller_params}
160 imex_1st_order.compute_residual = compute_residual_DAE
162 description = {}
163 description['problem_class'] = RayleighBenard
164 description['problem_params'] = problem_params
165 description['sweeper_class'] = imex_1st_order
166 description['sweeper_params'] = sweeper_params
167 description['level_params'] = level_params
168 description['step_params'] = step_params
169 description['convergence_controllers'] = convergence_controllers
171 if custom_description is not None:
172 description = merge_descriptions(description, custom_description)
174 controller_args = {
175 'controller_params': controller_params,
176 'description': description,
177 }
178 if use_MPI:
179 from mpi4py import MPI
180 from pySDC.implementations.controller_classes.controller_MPI import controller_MPI
182 comm = kwargs.get('comm', MPI.COMM_WORLD)
183 controller = controller_MPI(**controller_args, comm=comm)
184 P = controller.S.levels[0].prob
185 else:
186 controller = controller_nonMPI(**controller_args, num_procs=num_procs)
187 P = controller.MS[0].levels[0].prob
189 t0 = 0.0 if t0 is None else t0
190 uinit = P.u_exact(t0) if u0 is None else u0
192 if fault_stuff is not None:
193 from pySDC.projects.Resilience.fault_injection import prepare_controller_for_faults
195 prepare_controller_for_faults(controller, fault_stuff)
197 crash = False
198 try:
199 uend, stats = controller.run(u0=uinit, t0=t0, Tend=Tend)
200 except ConvergenceError as e:
201 print(f'Warning: Premature termination!: {e}')
202 stats = controller.return_stats()
203 crash = True
204 return stats, controller, crash
207def generate_data_for_fault_stats():
208 prob = RayleighBenard(**PROBLEM_PARAMS)
209 _ts = np.linspace(0, 22, 220, dtype=float)
210 for i in range(len(_ts) - 1):
211 prob.u_exact(_ts[i + 1], _t0=_ts[i])
214def plot_order(t, dt, steps, num_nodes, e_tol=1e-9, restol=1e-9, ax=None, recompute=False):
215 from pySDC.implementations.hooks.log_errors import LogGlobalErrorPostRun
216 from pySDC.implementations.hooks.log_work import LogSDCIterations, LogWork
217 from pySDC.implementations.convergence_controller_classes.crash import StopAtNan
218 from pySDC.helpers.stats_helper import get_sorted
219 import pickle
220 import os
222 sweeper_params = {'num_nodes': num_nodes}
223 level_params = {'e_tol': e_tol, 'restol': restol}
224 step_params = {'maxiter': 99}
225 convergence_controllers = {StopAtNan: {}}
227 errors = []
228 dts = []
229 for i in range(steps):
230 dts += [dt / 2**i]
232 path = f'data/stats/RBC-u_order-{t:.8f}-{dts[-1]:.8e}-{num_nodes}-{e_tol:.2e}-{restol:.2e}.pickle'
234 if os.path.exists(path) and not recompute:
235 with open(path, 'rb') as file:
236 stats = pickle.load(file)
237 else:
239 level_params['dt'] = dts[-1]
241 desc = {
242 'sweeper_params': sweeper_params,
243 'level_params': level_params,
244 'step_params': step_params,
245 'convergence_controllers': convergence_controllers,
246 }
248 stats, _, _ = run_RBC(
249 Tend=t + dt,
250 t0=t,
251 custom_description=desc,
252 hook_class=[LogGlobalErrorPostRun, LogSDCIterations, LogWork],
253 )
255 with open(path, 'wb') as file:
256 pickle.dump(stats, file)
258 e = get_sorted(stats, type='e_global_post_run')
259 # k = get_sorted(stats, type='k')
261 if len(e) > 0:
262 errors += [e[-1][1]]
263 else:
264 errors += [np.nan]
266 errors = np.array(errors)
267 dts = np.array(dts)
268 mask = np.isfinite(errors)
269 max_error = np.nanmax(errors)
271 errors = errors[mask]
272 dts = dts[mask]
273 ax.loglog(dts, errors, label=f'{num_nodes} nodes', marker='x')
274 ax.loglog(
275 dts, [max_error * (me / dts[0]) ** (2 * num_nodes - 1) for me in dts], ls='--', label=f'order {2*num_nodes-1}'
276 )
277 ax.set_xlabel(r'$\Delta t$')
278 ax.set_ylabel(r'global error')
279 ax.legend(frameon=False)
282def check_order(t=14, dt=1e-1, steps=6):
283 prob = RayleighBenard(**PROBLEM_PARAMS)
284 _ts = [0, t, t + dt]
285 for i in range(len(_ts) - 1):
286 prob.u_exact(_ts[i + 1], _t0=_ts[i])
288 import matplotlib.pyplot as plt
290 fig, ax = plt.subplots(1, 1)
291 for num_nodes in [1, 2, 3, 4]:
292 plot_order(t=t, dt=dt, steps=steps, num_nodes=num_nodes, ax=ax, restol=1e-9)
293 ax.set_title(f't={t:.2f}, dt={dt:.2f}')
294 plt.show()
297def plot_step_size(t0=0, Tend=30, e_tol=1e-3, recompute=False):
298 import matplotlib.pyplot as plt
299 import pickle
300 import os
301 from pySDC.helpers.stats_helper import get_sorted
303 path = f'data/stats/RBC-u-{t0:.8f}-{Tend:.8f}-{e_tol:.2e}.pickle'
304 if os.path.exists(path) and not recompute:
305 with open(path, 'rb') as file:
306 stats = pickle.load(file)
307 else:
308 from pySDC.implementations.convergence_controller_classes.adaptivity import Adaptivity
310 convergence_controllers = {
311 Adaptivity: {'e_tol': e_tol, 'dt_rel_min_slope': 0.25, 'dt_min': 1e-5},
312 }
313 desc = {'convergence_controllers': convergence_controllers}
315 stats, _, _ = run_RBC(Tend=Tend, t0=t0, custom_description=desc)
317 with open(path, 'wb') as file:
318 pickle.dump(stats, file)
320 fig, ax = plt.subplots(1, 1)
322 dt = get_sorted(stats, type='dt', recomputed=False)
323 ax.plot([me[0] for me in dt], [me[1] for me in dt])
324 ax.set_ylabel(r'$\Delta t$')
325 ax.set_xlabel(r'$t$')
326 ax.set_yscale('log')
327 plt.show()
330if __name__ == '__main__':
331 # plot_step_size(0, 3)
332 generate_data_for_fault_stats()
333 # check_order(t=20, dt=1., steps=7)
334 # stats, _, _ = run_RBC()