Coverage for pySDC/implementations/convergence_controller_classes/hotrod.py: 87%
38 statements
« prev ^ index » next coverage.py v7.6.7, created at 2024-11-16 14:51 +0000
« prev ^ index » next coverage.py v7.6.7, created at 2024-11-16 14:51 +0000
1import numpy as np
3from pySDC.core.convergence_controller import ConvergenceController
6class HotRod(ConvergenceController):
7 """
8 Class that incorporates the Hot Rod detector [1] for soft faults. Based on comparing two estimates of the local
9 error.
11 Default control order is -40.
13 See for the reference:
14 [1]: Lightweight and Accurate Silent Data Corruption Detection in Ordinary Differential Equation Solvers,
15 Guhur et al. 2016, Springer. DOI: https://doi.org/10.1007/978-3-319-43659-3_47
16 """
18 def setup(self, controller, params, description, **kwargs):
19 """
20 Setup default values for crucial parameters.
22 Args:
23 controller (pySDC.Controller): The controller
24 params (dict): The params passed for this specific convergence controller
25 description (dict): The description object used to instantiate the controller
27 Returns:
28 dict: The updated params
29 """
30 default_params = {
31 "HotRod_tol": np.inf,
32 "control_order": -40,
33 "no_storage": False,
34 }
35 return {**default_params, **super().setup(controller, params, description, **kwargs)}
37 def dependencies(self, controller, description, **kwargs):
38 """
39 Load the dependencies of Hot Rod, which are the two error estimators
41 Args:
42 controller (pySDC.Controller): The controller
43 description (dict): The description object used to instantiate the controller
45 Returns:
46 None
47 """
48 from pySDC.implementations.convergence_controller_classes.estimate_embedded_error import EstimateEmbeddedError
50 controller.add_convergence_controller(
51 EstimateEmbeddedError.get_implementation(flavor='linearized', useMPI=self.params.useMPI),
52 description=description,
53 )
54 if not self.params.useMPI:
55 from pySDC.implementations.convergence_controller_classes.estimate_extrapolation_error import (
56 EstimateExtrapolationErrorNonMPI,
57 )
58 from pySDC.implementations.convergence_controller_classes.basic_restarting import BasicRestartingNonMPI
60 controller.add_convergence_controller(
61 EstimateExtrapolationErrorNonMPI, description=description, params={'no_storage': self.params.no_storage}
62 )
63 controller.add_convergence_controller(
64 BasicRestartingNonMPI,
65 description=description,
66 params={'restart_from_first_step': True},
67 )
68 else:
69 raise NotImplementedError("Don't know how to estimate extrapolated error with MPI")
71 def check_parameters(self, controller, params, description, **kwargs):
72 """
73 Check whether parameters are compatible with whatever assumptions went into the step size functions etc.
75 Args:
76 controller (pySDC.Controller): The controller
77 params (dict): The params passed for this specific convergence controller
78 description (dict): The description object used to instantiate the controller
80 Returns:
81 bool: Whether the parameters are compatible
82 str: Error message
83 """
84 if self.params.HotRod_tol == np.inf:
85 controller.logger.warning(
86 "Hot Rod needs a detection threshold, which is now set to infinity, such that a \
87restart is never triggered!"
88 )
90 if description["step_params"].get("restol", -1.0) >= 0:
91 return (
92 False,
93 "Hot Rod needs constant order in time and hence restol in the step parameters has to be \
94smaller than 0!",
95 )
97 if controller.params.mssdc_jac:
98 return (
99 False,
100 "Hot Rod needs the same order on all steps, please activate Gauss-Seidel multistep mode!",
101 )
103 return True, ""
105 def determine_restart(self, controller, S, MS, **kwargs):
106 """
107 Check if the difference between the error estimates exceeds the allowed tolerance
109 Args:
110 controller (pySDC.Controller): The controller
111 S (pySDC.Step): The current step
112 MS (list): List of steps
114 Returns:
115 None
116 """
117 # we determine whether to restart only on the last sweep
118 if S.status.iter < S.params.maxiter:
119 return None
121 for L in S.levels:
122 if None not in [
123 L.status.error_extrapolation_estimate,
124 L.status.error_embedded_estimate,
125 ]:
126 diff = abs(L.status.error_extrapolation_estimate - L.status.error_embedded_estimate)
127 if diff > self.params.HotRod_tol:
128 S.status.restart = True
129 self.log(
130 f"Triggering restart: e_em={L.status.error_embedded_estimate:.2e}, e_ex={L.status.error_extrapolation_estimate:.2e} -> delta={diff:.2e}, tol={self.params.HotRod_tol:.2e}",
131 S,
132 )
133 else:
134 self.debug(
135 f"Not triggering restart: e_em={L.status.error_embedded_estimate:.2e}, e_ex={L.status.error_extrapolation_estimate:.2e} -> delta={diff:.2e}, tol={self.params.HotRod_tol:.2e}",
136 S,
137 )
139 return None
141 def post_iteration_processing(self, controller, S, **kwargs):
142 """
143 Throw away the final sweep to match the error estimates.
145 Args:
146 controller (pySDC.Controller): The controller
147 S (pySDC.Step): The current step
149 Returns:
150 None
151 """
152 if S.status.iter == S.params.maxiter:
153 for L in S.levels:
154 L.u[:] = L.uold[:]
156 return None