Coverage for pySDC/projects/Resilience/strategies.py: 72%

867 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-09-09 14:59 +0000

1import numpy as np 

2from matplotlib.colors import TABLEAU_COLORS 

3 

4cmap = TABLEAU_COLORS 

5 

6 

7def merge_descriptions(descA, descB): 

8 """ 

9 Merge two dictionaries that may contain dictionaries, which happens when merging descriptions, for instance. 

10 

11 Keys that occur in both dictionaries will be overwritten by the ones from `descB` and `descA` will be modified, not 

12 copied! 

13 

14 Args: 

15 descA (dict): Dictionary that you want to merge into 

16 descB (dict): Dictionary you want to merge from 

17 

18 Returns: 

19 dict: decsA with updated parameters 

20 """ 

21 for key in descB.keys(): 

22 if type(descB[key]) == dict: 

23 descA[key] = merge_descriptions(descA.get(key, {}), descB[key]) 

24 else: 

25 descA[key] = descB[key] 

26 return descA 

27 

28 

29class Strategy: 

30 ''' 

31 Abstract class for resilience strategies 

32 ''' 

33 

34 def __init__(self, useMPI=False, skip_residual_computation='none', stop_at_nan=True, **kwargs): 

35 ''' 

36 Initialization routine 

37 ''' 

38 self.useMPI = useMPI 

39 self.max_steps = 1e5 

40 

41 # set default values for plotting 

42 self.linestyle = '-' 

43 self.marker = '.' 

44 self.name = '' 

45 self.bar_plot_x_label = '' 

46 self.color = list(cmap.values())[0] 

47 

48 # parameters for computational efficiency 

49 if skip_residual_computation == 'all': 

50 self.skip_residual_computation = ('IT_CHECK', 'IT_DOWN', 'IT_UP', 'IT_FINE', 'IT_COARSE') 

51 elif skip_residual_computation == 'most': 

52 self.skip_residual_computation = ('IT_DOWN', 'IT_UP', 'IT_FINE', 'IT_COARSE') 

53 elif skip_residual_computation == 'none': 

54 self.skip_residual_computation = () 

55 else: 

56 raise NotImplementedError( 

57 f'Don\'t know when to skip residual computation with rule \"{skip_residual_computation}\"' 

58 ) 

59 

60 self.stop_at_nan = stop_at_nan 

61 

62 # setup custom descriptions 

63 self.custom_description = {} 

64 self.custom_description['sweeper_params'] = {'skip_residual_computation': self.skip_residual_computation} 

65 self.custom_description['level_params'] = {} 

66 self.custom_description['problem_params'] = {} 

67 self.custom_description['step_params'] = {} 

68 self.custom_description['convergence_controllers'] = {} 

69 

70 # prepare parameters for masks to identify faults that cannot be fixed by this strategy 

71 self.fixable = [] 

72 self.fixable += [ 

73 { 

74 'key': 'node', 

75 'op': 'gt', 

76 'val': 0, 

77 } 

78 ] 

79 self.fixable += [ 

80 { 

81 'key': 'error', 

82 'op': 'isfinite', 

83 } 

84 ] 

85 

86 # stuff for work-precision diagrams 

87 self.precision_parameter = None 

88 self.precision_parameter_loc = [] 

89 

90 def __str__(self): 

91 return self.name 

92 

93 def get_controller_params(self, **kwargs): 

94 return {'all_to_done': False} 

95 

96 def get_description_for_tolerance(self, problem, param, **kwargs): 

97 return {} 

98 

99 def get_fixable_params(self, **kwargs): 

100 """ 

101 Return a list containing dictionaries which can be passed to `FaultStats.get_mask` as keyword arguments to 

102 obtain a mask of faults that can be fixed 

103 

104 Returns: 

105 list: Dictionary of parameters 

106 """ 

107 return self.fixable 

108 

109 def get_fault_args(self, problem, num_procs): 

110 ''' 

111 Routine to get arguments for the faults that are exempt from randomization 

112 

113 Args: 

114 problem: A function that runs a pySDC problem, see imports for available problems 

115 num_procs (int): Number of processes you intend to run with 

116 

117 Returns: 

118 dict: Arguments for the faults that are exempt from randomization 

119 ''' 

120 args = {} 

121 args['target'] = 0 

122 

123 if problem.__name__ == "run_vdp": 

124 args['time'] = 5.25 

125 elif problem.__name__ == "run_Schroedinger": 

126 args['time'] = 0.3 

127 elif problem.__name__ == "run_quench": 

128 args['time'] = 41.0 

129 elif problem.__name__ == "run_Lorenz": 

130 args['time'] = 0.3 

131 elif problem.__name__ == "run_AC": 

132 args['time'] = 1e-2 

133 

134 return args 

135 

136 def get_random_params(self, problem, num_procs): 

137 ''' 

138 Routine to get parameters for the randomization of faults 

139 

140 Args: 

141 problem: A function that runs a pySDC problem, see imports for available problems 

142 num_procs (int): Number of processes you intend to run with 

143 

144 Returns: 

145 dict: Randomization parameters 

146 ''' 

147 base_params = self.get_base_parameters(problem, num_procs) 

148 

149 rnd_params = {} 

150 rnd_params['iteration'] = base_params['step_params']['maxiter'] 

151 rnd_params['rank'] = num_procs 

152 

153 if problem.__name__ in ['run_Schroedinger', 'run_quench', 'run_AC']: 

154 rnd_params['min_node'] = 1 

155 

156 if problem.__name__ == "run_quench": 

157 rnd_params['iteration'] = 5 

158 elif problem.__name__ == 'run_Lorenz': 

159 rnd_params['iteration'] = 5 

160 return rnd_params 

161 

162 @property 

163 def style(self): 

164 """ 

165 Get the plotting parameters for the strategy. 

166 Supply them to a plotting function using `**` 

167 

168 Returns: 

169 (dict): The plotting parameters as a dictionary 

170 """ 

171 return { 

172 'marker': self.marker, 

173 'label': self.label, 

174 'color': self.color, 

175 'ls': self.linestyle, 

176 } 

177 

178 @property 

179 def label(self): 

180 """ 

181 Get a label for plotting 

182 """ 

183 return self.name 

184 

185 @classmethod 

186 def get_Tend(cls, problem, num_procs=1): 

187 ''' 

188 Get the final time of runs for fault stats based on the problem 

189 

190 Args: 

191 problem (function): A problem to run 

192 num_procs (int): Number of processes 

193 

194 Returns: 

195 float: Tend to put into the run 

196 ''' 

197 if problem.__name__ == "run_vdp": 

198 return 20 # 11.5 

199 elif problem.__name__ == "run_piline": 

200 return 20.0 

201 elif problem.__name__ == "run_Lorenz": 

202 return 1.5 

203 elif problem.__name__ == "run_Schroedinger": 

204 return 1.0 

205 elif problem.__name__ == "run_quench": 

206 return 500.0 

207 elif problem.__name__ == "run_AC": 

208 return 0.025 

209 else: 

210 raise NotImplementedError('I don\'t have a final time for your problem!') 

211 

212 def get_base_parameters(self, problem, num_procs=1): 

213 ''' 

214 Get a base parameters for the problems independent of the strategy. 

215 

216 Args: 

217 problem (function): A problem to run 

218 num_procs (int): Number of processes 

219 

220 Returns: 

221 dict: Custom description 

222 ''' 

223 from pySDC.implementations.convergence_controller_classes.step_size_limiter import StepSizeLimiter 

224 

225 custom_description = {} 

226 custom_description['step_params'] = {} 

227 custom_description['level_params'] = {} 

228 custom_description['problem_params'] = {} 

229 

230 if problem.__name__ == "run_vdp": 

231 custom_description['step_params'] = {'maxiter': 3} 

232 custom_description['problem_params'] = { 

233 'u0': np.array([1.1, 0], dtype=np.float64), 

234 'mu': 1000, 

235 'crash_at_maxiter': False, 

236 'newton_tol': 1e-11, 

237 'stop_at_nan': False, 

238 } 

239 custom_description['level_params'] = {'dt': 1e-4} 

240 

241 elif problem.__name__ == "run_Lorenz": 

242 custom_description['step_params'] = {'maxiter': 5} 

243 custom_description['level_params'] = {'dt': 1e-2} 

244 custom_description['problem_params'] = {'stop_at_nan': False} 

245 elif problem.__name__ == "run_Schroedinger": 

246 custom_description['step_params'] = {'maxiter': 5} 

247 custom_description['level_params'] = {'dt': 1e-2, 'restol': -1} 

248 custom_description['problem_params']['nvars'] = (256, 256) 

249 elif problem.__name__ == "run_quench": 

250 custom_description['level_params'] = {'restol': -1, 'dt': 8.0} 

251 custom_description['step_params'] = {'maxiter': 5} 

252 custom_description['problem_params'] = { 

253 'newton_maxiter': 29, 

254 'newton_tol': 1e-7, 

255 'nvars': 2**6, 

256 'direct_solver': False, 

257 'lintol': 1e-8, 

258 'liniter': 29, 

259 'order': 6, 

260 } 

261 elif problem.__name__ == "run_AC": 

262 eps = 4e-2 

263 custom_description['step_params'] = {'maxiter': 5} 

264 custom_description['problem_params'] = { 

265 'nvars': (128,) * 2, 

266 'init_type': 'circle', 

267 'eps': eps, 

268 'radius': 0.25, 

269 'nu': 2, 

270 } 

271 custom_description['level_params'] = {'restol': -1, 'dt': 0.1 * eps**2} 

272 

273 custom_description['convergence_controllers'] = { 

274 # StepSizeLimiter: {'dt_min': self.get_Tend(problem=problem, num_procs=num_procs) / self.max_steps} 

275 } 

276 

277 if self.stop_at_nan: 

278 from pySDC.implementations.convergence_controller_classes.crash import StopAtNan 

279 

280 custom_description['convergence_controllers'][StopAtNan] = {'thresh': 1e20} 

281 

282 from pySDC.implementations.convergence_controller_classes.crash import StopAtMaxRuntime 

283 

284 max_runtime = { 

285 'run_vdp': 1000, 

286 'run_Lorenz': 60, 

287 'run_Schroedinger': 150, 

288 'run_quench': 150, 

289 'run_AC': 150, 

290 } 

291 

292 custom_description['convergence_controllers'][StopAtMaxRuntime] = { 

293 'max_runtime': max_runtime.get(problem.__name__, 100) 

294 } 

295 return custom_description 

296 

297 def get_custom_description(self, problem, num_procs=1): 

298 ''' 

299 Get a custom description based on the problem 

300 

301 Args: 

302 problem (function): A problem to run 

303 num_procs (int): Number of processes 

304 

305 Returns: 

306 dict: Custom description 

307 ''' 

308 custom_description = self.get_base_parameters(problem, num_procs) 

309 return merge_descriptions(custom_description, self.custom_description) 

310 

311 def get_custom_description_for_faults(self, *args, **kwargs): 

312 ''' 

313 Get a custom description based on the problem to run the fault stuff 

314 

315 Returns: 

316 dict: Custom description 

317 ''' 

318 return self.get_custom_description(*args, **kwargs) 

319 

320 def get_reference_value(self, problem, key, op, num_procs=1): 

321 """ 

322 Get a reference value for a given problem for testing in CI. 

323 

324 Args: 

325 problem: A function that runs a pySDC problem, see imports for available problems 

326 key (str): The name of the variable you want to compare 

327 op (function): The operation you want to apply to the data 

328 num_procs (int): Number of processes 

329 

330 Returns: 

331 The reference value 

332 """ 

333 raise NotImplementedError( 

334 f'The reference value you are looking for is not implemented for {type(self).__name__} strategy!' 

335 ) 

336 

337 

338class InexactBaseStrategy(Strategy): 

339 """ 

340 Base class for inexact strategies. 

341 """ 

342 

343 def __init__( 

344 self, double_adaptivity=False, newton_inexactness=True, linear_inexactness=True, SDC_maxiter=16, **kwargs 

345 ): 

346 kwargs = {**kwargs, 'skip_residual_computation': 'most'} 

347 super().__init__(**kwargs) 

348 self.double_adaptivity = double_adaptivity 

349 self.newton_inexactness = newton_inexactness 

350 self.linear_inexactness = linear_inexactness 

351 self.SDC_maxiter = SDC_maxiter 

352 

353 def get_controller_params(self, **kwargs): 

354 return {'all_to_done': True} 

355 

356 def get_custom_description(self, problem, num_procs=1): 

357 from pySDC.implementations.convergence_controller_classes.inexactness import NewtonInexactness 

358 

359 preconditioner = 'MIN-SR-NS' if problem.__name__ in ['run_Lorenz'] else 'MIN-SR-S' 

360 

361 desc = {} 

362 desc['sweeper_params'] = {'QI': preconditioner} 

363 desc['step_params'] = {'maxiter': self.SDC_maxiter} 

364 desc['problem_params'] = {} 

365 desc['level_params'] = {'restol': 1e-8, 'residual_type': 'last_abs'} 

366 desc['convergence_controllers'] = {} 

367 

368 inexactness_params = { 

369 'min_tol': 1e-12, 

370 'ratio': 1e-2, 

371 'max_tol': 1e-4, 

372 'use_e_tol': False, 

373 'maxiter': 15, 

374 } 

375 

376 if self.newton_inexactness and problem.__name__ not in ['run_Schroedinger', 'run_AC']: 

377 if problem.__name__ == 'run_quench': 

378 inexactness_params['ratio'] = 1e-1 

379 inexactness_params['min_tol'] = 1e-11 

380 inexactness_params['maxiter'] = 5 

381 elif problem.__name__ == "run_vdp": 

382 inexactness_params['ratio'] = 1e-5 

383 inexactness_params['min_tol'] = 1e-15 

384 inexactness_params['maxiter'] = 9 

385 desc['convergence_controllers'][NewtonInexactness] = inexactness_params 

386 

387 if problem.__name__ in ['run_vdp']: 

388 desc['problem_params']['stop_at_nan'] = False 

389 

390 if self.linear_inexactness and problem.__name__ in ['run_quench']: 

391 desc['problem_params']['inexact_linear_ratio'] = 1e-1 

392 if problem.__name__ in ['run_quench']: 

393 desc['problem_params']['direct_solver'] = False 

394 desc['problem_params']['liniter'] = 9 

395 desc['problem_params']['min_lintol'] = 1e-11 

396 

397 from pySDC.implementations.convergence_controller_classes.basic_restarting import BasicRestarting 

398 

399 desc['convergence_controllers'][BasicRestarting.get_implementation(useMPI=self.useMPI)] = { 

400 'max_restarts': 29, 

401 'crash_after_max_restarts': True, 

402 } 

403 return merge_descriptions(super().get_custom_description(problem, num_procs), desc) 

404 

405 

406class BaseStrategy(Strategy): 

407 ''' 

408 Do a fixed iteration count 

409 ''' 

410 

411 def __init__(self, skip_residual_computation='all', **kwargs): 

412 ''' 

413 Initialization routine 

414 ''' 

415 super().__init__(skip_residual_computation=skip_residual_computation, **kwargs) 

416 self.color = list(cmap.values())[0] 

417 self.marker = 'o' 

418 self.name = 'base' 

419 self.bar_plot_x_label = 'base' 

420 self.precision_parameter = 'dt' 

421 self.precision_parameter_loc = ['level_params', 'dt'] 

422 

423 @property 

424 def label(self): 

425 return r'fixed' 

426 

427 def get_custom_description(self, problem, num_procs): 

428 desc = super().get_custom_description(problem, num_procs) 

429 if problem.__name__ == "run_AC": 

430 desc['level_params']['dt'] = 1e-2 * desc['problem_params']['eps'] ** 2 

431 return desc 

432 

433 def get_custom_description_for_faults(self, problem, *args, **kwargs): 

434 desc = self.get_custom_description(problem, *args, **kwargs) 

435 if problem.__name__ == "run_quench": 

436 desc['level_params']['dt'] = 5.0 

437 return desc 

438 

439 def get_reference_value(self, problem, key, op, num_procs=1): 

440 """ 

441 Get a reference value for a given problem for testing in CI. 

442 

443 Args: 

444 problem: A function that runs a pySDC problem, see imports for available problems 

445 key (str): The name of the variable you want to compare 

446 op (function): The operation you want to apply to the data 

447 num_procs (int): Number of processes 

448 

449 Returns: 

450 The reference value 

451 """ 

452 if problem.__name__ == "run_Lorenz": 

453 if key == 'work_newton' and op == sum: 

454 return 2136 

455 elif key == 'e_global_post_run' and op == max: 

456 return 9.256926357892326e-06 

457 

458 super().get_reference_value(problem, key, op, num_procs) 

459 

460 

461class AdaptivityStrategy(Strategy): 

462 ''' 

463 Adaptivity as a resilience strategy 

464 ''' 

465 

466 def __init__(self, **kwargs): 

467 ''' 

468 Initialization routine 

469 ''' 

470 from pySDC.implementations.convergence_controller_classes.adaptivity import Adaptivity 

471 

472 kwargs['skip_residual_computation'] = 'all' 

473 super().__init__(**kwargs) 

474 self.color = list(cmap.values())[1] 

475 self.marker = '*' 

476 self.name = 'adaptivity' 

477 self.bar_plot_x_label = 'adaptivity' 

478 self.precision_parameter = 'e_tol' 

479 self.precision_parameter_loc = ['convergence_controllers', Adaptivity, 'e_tol'] 

480 

481 @property 

482 def label(self): 

483 return r'$\Delta t$-adaptivity' 

484 

485 def get_fixable_params(self, maxiter, **kwargs): 

486 """ 

487 Here faults occurring in the last iteration cannot be fixed. 

488 

489 Args: 

490 maxiter (int): Max. iterations until convergence is declared 

491 

492 Returns: 

493 (list): Contains dictionaries of keyword arguments for `FaultStats.get_mask` 

494 """ 

495 self.fixable += [ 

496 { 

497 'key': 'iteration', 

498 'op': 'lt', 

499 'val': maxiter, 

500 } 

501 ] 

502 return self.fixable 

503 

504 def get_custom_description(self, problem, num_procs): 

505 ''' 

506 Routine to get a custom description that adds adaptivity 

507 

508 Args: 

509 problem: A function that runs a pySDC problem, see imports for available problems 

510 num_procs (int): Number of processes you intend to run with 

511 

512 Returns: 

513 The custom descriptions you can supply to the problem when running it 

514 ''' 

515 from pySDC.implementations.convergence_controller_classes.adaptivity import Adaptivity 

516 from pySDC.implementations.convergence_controller_classes.step_size_limiter import StepSizeLimiter 

517 

518 base_params = super().get_custom_description(problem, num_procs) 

519 custom_description = {} 

520 custom_description['convergence_controllers'] = {} 

521 

522 dt_max = np.inf 

523 dt_slope_max = np.inf 

524 

525 if problem.__name__ == "run_piline": 

526 e_tol = 1e-7 

527 elif problem.__name__ == "run_vdp": 

528 e_tol = 2e-5 

529 elif problem.__name__ == "run_Lorenz": 

530 e_tol = 2e-5 

531 elif problem.__name__ == "run_Schroedinger": 

532 e_tol = 4e-7 

533 elif problem.__name__ == "run_quench": 

534 e_tol = 1e-8 

535 custom_description['problem_params'] = { 

536 'newton_tol': 1e-10, 

537 'lintol': 1e-11, 

538 } 

539 

540 from pySDC.implementations.convergence_controller_classes.basic_restarting import BasicRestarting 

541 

542 custom_description['convergence_controllers'][BasicRestarting.get_implementation(useMPI=self.useMPI)] = { 

543 'max_restarts': 99, 

544 } 

545 elif problem.__name__ == "run_AC": 

546 e_tol = 1e-7 

547 # dt_max = 0.1 * base_params['problem_params']['eps'] ** 2 

548 

549 else: 

550 raise NotImplementedError( 

551 'I don\'t have a tolerance for adaptivity for your problem. Please add one to the\ 

552 strategy' 

553 ) 

554 

555 custom_description['convergence_controllers'][Adaptivity] = { 

556 'e_tol': e_tol, 

557 'dt_slope_max': dt_slope_max, 

558 } 

559 custom_description['convergence_controllers'][StepSizeLimiter] = { 

560 'dt_max': dt_max, 

561 } 

562 return merge_descriptions(base_params, custom_description) 

563 

564 def get_reference_value(self, problem, key, op, num_procs=1): 

565 """ 

566 Get a reference value for a given problem for testing in CI. 

567 

568 Args: 

569 problem: A function that runs a pySDC problem, see imports for available problems 

570 key (str): The name of the variable you want to compare 

571 op (function): The operation you want to apply to the data 

572 num_procs (int): Number of processes 

573 

574 Returns: 

575 The reference value 

576 """ 

577 if problem.__name__ == 'run_Lorenz': 

578 if key == 'work_newton' and op == sum: 

579 return 1369 

580 elif key == 'e_global_post_run' and op == max: 

581 return 9.364841517367495e-06 

582 

583 super().get_reference_value(problem, key, op, num_procs) 

584 

585 def get_custom_description_for_faults(self, problem, num_procs, *args, **kwargs): 

586 from pySDC.implementations.convergence_controller_classes.step_size_limiter import StepSizeLimiter 

587 from pySDC.implementations.convergence_controller_classes.adaptivity import Adaptivity 

588 

589 desc = self.get_custom_description(problem, num_procs, *args, **kwargs) 

590 if problem.__name__ == "run_quench": 

591 desc['level_params']['dt'] = 1.1e1 

592 desc['convergence_controllers'][Adaptivity]['e_tol'] = 1e-6 

593 return desc 

594 

595 

596class AdaptivityRestartFirstStep(AdaptivityStrategy): 

597 def __init__(self, **kwargs): 

598 super().__init__(**kwargs) 

599 self.color = 'teal' 

600 self.name = 'adaptivityRestartFirstStep' 

601 

602 def get_custom_description(self, problem, num_procs): 

603 ''' 

604 Add the other version of basic restarting. 

605 

606 Args: 

607 problem: A function that runs a pySDC problem, see imports for available problems 

608 num_procs (int): Number of processes you intend to run with 

609 

610 Returns: 

611 The custom descriptions you can supply to the problem when running it 

612 ''' 

613 custom_description = super().get_custom_description(problem, num_procs) 

614 from pySDC.implementations.convergence_controller_classes.basic_restarting import BasicRestarting 

615 

616 custom_description['convergence_controllers'][BasicRestarting.get_implementation(useMPI=self.useMPI)] = { 

617 'max_restarts': 15, 

618 'restart_from_first_step': True, 

619 } 

620 return custom_description 

621 

622 @property 

623 def label(self): 

624 return f'{super().label} restart from first step' 

625 

626 

627class AdaptiveHotRodStrategy(Strategy): 

628 ''' 

629 Adaptivity + Hot Rod as a resilience strategy 

630 ''' 

631 

632 def __init__(self, **kwargs): 

633 ''' 

634 Initialization routine 

635 ''' 

636 from pySDC.implementations.convergence_controller_classes.adaptivity import Adaptivity 

637 

638 kwargs['skip_residual_computation'] = 'all' 

639 super().__init__(**kwargs) 

640 self.color = list(cmap.values())[4] 

641 self.marker = '.' 

642 self.name = 'adaptive Hot Rod' 

643 self.bar_plot_x_label = 'adaptive\nHot Rod' 

644 self.precision_parameter = 'e_tol' 

645 self.precision_parameter_loc = ['convergence_controllers', Adaptivity, 'e_tol'] 

646 

647 def get_custom_description(self, problem, num_procs): 

648 ''' 

649 Routine to get a custom description that adds adaptivity and Hot Rod 

650 

651 Args: 

652 problem: A function that runs a pySDC problem, see imports for available problems 

653 num_procs (int): Number of processes you intend to run with 

654 

655 Returns: 

656 The custom description you can supply to the problem when running it 

657 ''' 

658 from pySDC.implementations.convergence_controller_classes.hotrod import HotRod 

659 from pySDC.implementations.convergence_controller_classes.adaptivity import Adaptivity 

660 

661 if problem.__name__ == "run_vdp": 

662 e_tol = 3e-7 

663 dt_min = 1e-3 

664 maxiter = 4 

665 HotRod_tol = 2e-6 

666 elif problem.__name__ == "run_Lorenz": 

667 e_tol = 3e-7 

668 dt_min = 1e-3 

669 maxiter = 4 

670 HotRod_tol = 2e-6 

671 else: 

672 raise NotImplementedError( 

673 'I don\'t have a tolerance for adaptive Hot Rod for your problem. Please add one \ 

674to the strategy' 

675 ) 

676 

677 no_storage = num_procs > 1 

678 

679 custom_description = { 

680 'convergence_controllers': { 

681 HotRod: {'HotRod_tol': HotRod_tol, 'no_storage': no_storage}, 

682 Adaptivity: {'e_tol': e_tol, 'dt_min': dt_min, 'embedded_error_flavor': 'linearized'}, 

683 }, 

684 'step_params': {'maxiter': maxiter}, 

685 } 

686 

687 return merge_descriptions(super().get_custom_description(problem, num_procs), custom_description) 

688 

689 def get_reference_value(self, problem, key, op, num_procs=1): 

690 """ 

691 Get a reference value for a given problem for testing in CI. 

692 

693 Args: 

694 problem: A function that runs a pySDC problem, see imports for available problems 

695 key (str): The name of the variable you want to compare 

696 op (function): The operation you want to apply to the data 

697 num_procs (int): Number of processes 

698 

699 Returns: 

700 The reference value 

701 """ 

702 if problem.__name__ == "run_Lorenz": 

703 if key == 'work_newton' and op == sum: 

704 return 4758 

705 elif key == 'e_global_post_run' and op == max: 

706 return 4.107116318152748e-06 

707 

708 super().get_reference_value(problem, key, op, num_procs) 

709 

710 

711class IterateStrategy(Strategy): 

712 ''' 

713 Iterate for as much as you want 

714 ''' 

715 

716 def __init__(self, **kwargs): 

717 ''' 

718 Initialization routine 

719 ''' 

720 kwargs['skip_residual_computation'] = 'most' 

721 super().__init__(**kwargs) 

722 self.color = list(cmap.values())[2] 

723 self.marker = 'v' 

724 self.name = 'iterate' 

725 self.bar_plot_x_label = 'iterate' 

726 self.precision_parameter = 'restol' 

727 self.precision_parameter_loc = ['level_params', 'restol'] 

728 

729 @property 

730 def label(self): 

731 return r'$k$-adaptivity' 

732 

733 def get_custom_description(self, problem, num_procs): 

734 ''' 

735 Routine to get a custom description that allows for adaptive iteration counts 

736 

737 Args: 

738 problem: A function that runs a pySDC problem, see imports for available problems 

739 num_procs (int): Number of processes you intend to run with 

740 

741 Returns: 

742 The custom description you can supply to the problem when running it 

743 ''' 

744 restol = -1 

745 e_tol = -1 

746 

747 if problem.__name__ == "run_piline": 

748 restol = 2.3e-8 

749 elif problem.__name__ == "run_vdp": 

750 restol = 9e-7 

751 elif problem.__name__ == "run_Lorenz": 

752 restol = 16e-7 

753 elif problem.__name__ == "run_Schroedinger": 

754 restol = 6.5e-7 

755 elif problem.__name__ == "run_quench": 

756 restol = 1e-7 

757 elif problem.__name__ == "run_AC": 

758 restol = 1e-11 

759 else: 

760 raise NotImplementedError( 

761 'I don\'t have a residual tolerance for your problem. Please add one to the \ 

762strategy' 

763 ) 

764 

765 custom_description = { 

766 'step_params': {'maxiter': 99}, 

767 'level_params': {'restol': restol, 'e_tol': e_tol}, 

768 } 

769 

770 if problem.__name__ == "run_quench": 

771 custom_description['level_params']['dt'] = 1.0 

772 

773 return merge_descriptions(super().get_custom_description(problem, num_procs), custom_description) 

774 

775 def get_random_params(self, problem, num_procs): 

776 ''' 

777 Routine to get parameters for the randomization of faults 

778 

779 Args: 

780 problem: A function that runs a pySDC problem, see imports for available problems 

781 num_procs (int): Number of processes you intend to run with 

782 

783 Returns: 

784 dict: Randomization parameters 

785 ''' 

786 

787 rnd_params = super().get_random_params(problem, num_procs) 

788 if problem.__name__ == "run_quench": 

789 rnd_params['iteration'] = 1 

790 return rnd_params 

791 

792 def get_reference_value(self, problem, key, op, num_procs=1): 

793 """ 

794 Get a reference value for a given problem for testing in CI. 

795 

796 Args: 

797 problem: A function that runs a pySDC problem, see imports for available problems 

798 key (str): The name of the variable you want to compare 

799 op (function): The operation you want to apply to the data 

800 num_procs (int): Number of processes 

801 

802 Returns: 

803 The reference value 

804 """ 

805 if problem.__name__ == "run_Lorenz": 

806 if key == 'work_newton' and op == sum: 

807 return 1872 

808 elif key == 'e_global_post_run' and op == max: 

809 return 2.2362043480939064e-05 

810 

811 super().get_reference_value(problem, key, op, num_procs) 

812 

813 

814class kAdaptivityStrategy(IterateStrategy): 

815 def __init__(self, **kwargs): 

816 super().__init__(**kwargs) 

817 self.precision_parameter = 'dt' 

818 self.precision_parameter_loc = ['level_params', 'dt'] 

819 

820 def get_custom_description(self, problem, num_procs, *args, **kwargs): 

821 desc = super().get_custom_description(problem, num_procs, *args, **kwargs) 

822 desc['level_params']['restol'] = 1e-9 

823 if problem.__name__ == "run_quench": 

824 desc['problem_params']['newton_tol'] = 1e-9 

825 desc['problem_params']['lintol'] = 1e-9 

826 desc['level_params']['dt'] = 2.5 

827 elif problem.__name__ == "run_AC": 

828 desc['level_params']['restol'] = 1e-11 

829 desc['level_params']['dt'] = 0.4 * desc['problem_params']['eps'] ** 2 / 8.0 

830 return desc 

831 

832 def get_custom_description_for_faults(self, problem, *args, **kwargs): 

833 desc = self.get_custom_description(problem, *args, **kwargs) 

834 if problem.__name__ == 'run_quench': 

835 desc['level_params']['dt'] = 5.0 

836 elif problem.__name__ == 'run_AC': 

837 desc['level_params']['dt'] = 0.6 * desc['problem_params']['eps'] ** 2 

838 return desc 

839 

840 def get_reference_value(self, problem, key, op, num_procs=1): 

841 """ 

842 Get a reference value for a given problem for testing in CI. 

843 

844 Args: 

845 problem: A function that runs a pySDC problem, see imports for available problems 

846 key (str): The name of the variable you want to compare 

847 op (function): The operation you want to apply to the data 

848 num_procs (int): Number of processes 

849 

850 Returns: 

851 The reference value 

852 """ 

853 if problem.__name__ == "run_Lorenz": 

854 if key == 'work_newton' and op == sum: 

855 return 2392 

856 elif key == 'e_global_post_run' and op == max: 

857 return 4.808610118089973e-06 

858 

859 super().get_reference_value(problem, key, op, num_procs) 

860 

861 

862class HotRodStrategy(Strategy): 

863 ''' 

864 Hot Rod as a resilience strategy 

865 ''' 

866 

867 def __init__(self, **kwargs): 

868 ''' 

869 Initialization routine 

870 ''' 

871 kwargs['skip_residual_computation'] = 'all' 

872 super().__init__(**kwargs) 

873 self.color = list(cmap.values())[3] 

874 self.marker = '^' 

875 self.name = 'Hot Rod' 

876 self.bar_plot_x_label = 'Hot Rod' 

877 self.precision_parameter = 'dt' 

878 self.precision_parameter_loc = ['level_params', 'dt'] 

879 

880 def get_custom_description(self, problem, num_procs): 

881 ''' 

882 Routine to get a custom description that adds Hot Rod 

883 

884 Args: 

885 problem: A function that runs a pySDC problem, see imports for available problems 

886 num_procs (int): Number of processes you intend to run with 

887 

888 Returns: 

889 The custom description you can supply to the problem when running it 

890 ''' 

891 from pySDC.implementations.convergence_controller_classes.hotrod import HotRod 

892 from pySDC.implementations.convergence_controller_classes.basic_restarting import BasicRestartingNonMPI 

893 

894 base_params = super().get_custom_description(problem, num_procs) 

895 if problem.__name__ == "run_vdp": 

896 if num_procs == 4: 

897 HotRod_tol = 1.800804e-04 

898 elif num_procs == 5: 

899 HotRod_tol = 9.329361e-05 

900 else: # 1 process 

901 HotRod_tol = 1.347949e-06 

902 HotRod_tol = 7e-6 if num_procs > 1 else 5e-7 

903 maxiter = 4 

904 elif problem.__name__ == "run_Lorenz": 

905 if num_procs == 5: 

906 HotRod_tol = 9.539348e-06 

907 elif num_procs == 4: 

908 HotRod_tol = 3.201e-6 

909 else: 

910 HotRod_tol = 7.720589e-07 

911 maxiter = 6 

912 elif problem.__name__ == "run_Schroedinger": 

913 if num_procs == 5: 

914 HotRod_tol = 2.497697e-06 

915 elif num_procs == 4: 

916 HotRod_tol = 1.910405e-06 

917 else: 

918 HotRod_tol = 4.476790e-07 

919 maxiter = 6 

920 elif problem.__name__ == "run_quench": 

921 if num_procs == 5: 

922 HotRod_tol = 1.017534e-03 

923 elif num_procs == 4: 

924 HotRod_tol = 1.017534e-03 

925 else: 

926 HotRod_tol = 5.198620e-04 

927 maxiter = 6 

928 elif problem.__name__ == 'run_AC': 

929 HotRod_tol = 9.564437e-06 

930 maxiter = 6 

931 else: 

932 raise NotImplementedError( 

933 'I don\'t have a tolerance for Hot Rod for your problem. Please add one to the\ 

934 strategy' 

935 ) 

936 

937 no_storage = False # num_procs > 1 

938 

939 custom_description = { 

940 'convergence_controllers': { 

941 HotRod: {'HotRod_tol': HotRod_tol, 'no_storage': no_storage}, 

942 BasicRestartingNonMPI: { 

943 'max_restarts': 2, 

944 'crash_after_max_restarts': False, 

945 'restart_from_first_step': True, 

946 }, 

947 }, 

948 'step_params': {'maxiter': maxiter}, 

949 'level_params': {}, 

950 } 

951 if problem.__name__ == "run_AC": 

952 custom_description['level_params']['dt'] = 0.8 * base_params['problem_params']['eps'] ** 2 / 8.0 

953 return merge_descriptions(base_params, custom_description) 

954 

955 def get_custom_description_for_faults(self, problem, *args, **kwargs): 

956 desc = self.get_custom_description(problem, *args, **kwargs) 

957 if problem.__name__ == "run_quench": 

958 desc['level_params']['dt'] = 5.0 

959 return desc 

960 

961 def get_reference_value(self, problem, key, op, num_procs=1): 

962 """ 

963 Get a reference value for a given problem for testing in CI. 

964 

965 Args: 

966 problem: A function that runs a pySDC problem, see imports for available problems 

967 key (str): The name of the variable you want to compare 

968 op (function): The operation you want to apply to the data 

969 num_procs (int): Number of processes 

970 

971 Returns: 

972 The reference value 

973 """ 

974 if problem.__name__ == "run_Lorenz": 

975 if key == 'work_newton' and op == sum: 

976 return 2329 

977 elif key == 'e_global_post_run' and op == max: 

978 return 9.256926357892326e-06 

979 

980 super().get_reference_value(problem, key, op, num_procs) 

981 

982 

983class AdaptivityCollocationStrategy(InexactBaseStrategy): 

984 ''' 

985 Adaptivity based on collocation as a resilience strategy 

986 ''' 

987 

988 def __init__(self, **kwargs): 

989 ''' 

990 Initialization routine 

991 ''' 

992 kwargs = { 

993 'skip_residual_computation': 'most', 

994 **kwargs, 

995 } 

996 

997 from pySDC.implementations.convergence_controller_classes.adaptivity import AdaptivityCollocation 

998 

999 self.restol = None 

1000 super().__init__(**kwargs) 

1001 self.color = list(cmap.values())[1] 

1002 self.marker = '*' 

1003 self.name = 'adaptivity_coll' 

1004 self.bar_plot_x_label = 'adaptivity collocation' 

1005 self.precision_parameter = 'e_tol' 

1006 self.adaptive_coll_params = {} 

1007 self.precision_parameter_loc = ['convergence_controllers', AdaptivityCollocation, 'e_tol'] 

1008 

1009 def get_custom_description(self, problem, num_procs): 

1010 ''' 

1011 Routine to get a custom description that adds adaptivity 

1012 

1013 Args: 

1014 problem: A function that runs a pySDC problem, see imports for available problems 

1015 num_procs (int): Number of processes you intend to run with 

1016 

1017 Returns: 

1018 The custom descriptions you can supply to the problem when running it 

1019 ''' 

1020 from pySDC.implementations.convergence_controller_classes.adaptivity import AdaptivityCollocation 

1021 

1022 custom_description = {} 

1023 

1024 dt_max = np.inf 

1025 dt_min = 1e-5 

1026 

1027 if problem.__name__ == "run_piline": 

1028 e_tol = 1e-7 

1029 dt_min = 1e-2 

1030 elif problem.__name__ == "run_vdp": 

1031 e_tol = 2e-5 

1032 dt_min = 1e-3 

1033 elif problem.__name__ == "run_Lorenz": 

1034 e_tol = 2e-5 

1035 dt_min = 1e-3 

1036 elif problem.__name__ == "run_Schroedinger": 

1037 e_tol = 4e-6 

1038 dt_min = 1e-3 

1039 elif problem.__name__ == "run_quench": 

1040 e_tol = 1e-5 

1041 dt_min = 1e-3 

1042 dt_max = 1e2 

1043 elif problem.__name__ == "run_AC": 

1044 e_tol = 1e-4 

1045 else: 

1046 raise NotImplementedError( 

1047 'I don\'t have a tolerance for adaptivity for your problem. Please add one to the\ 

1048 strategy' 

1049 ) 

1050 

1051 custom_description['convergence_controllers'] = { 

1052 AdaptivityCollocation: { 

1053 'e_tol': e_tol, 

1054 'dt_min': dt_min, 

1055 'dt_max': dt_max, 

1056 'adaptive_coll_params': self.adaptive_coll_params, 

1057 'restol_rel': 1e-2, 

1058 } 

1059 } 

1060 return merge_descriptions(super().get_custom_description(problem, num_procs), custom_description) 

1061 

1062 

1063class AdaptivityCollocationTypeStrategy(AdaptivityCollocationStrategy): 

1064 def __init__(self, **kwargs): 

1065 super().__init__(**kwargs) 

1066 self.color = list(cmap.values())[4] 

1067 self.marker = '.' 

1068 self.adaptive_coll_params = { 

1069 'quad_type': ['RADAU-RIGHT', 'GAUSS'], 

1070 'do_coll_update': [False, True], 

1071 } 

1072 

1073 @property 

1074 def label(self): 

1075 return 'adaptivity type' 

1076 

1077 def get_reference_value(self, problem, key, op, num_procs=1): 

1078 """ 

1079 Get a reference value for a given problem for testing in CI. 

1080 

1081 Args: 

1082 problem: A function that runs a pySDC problem, see imports for available problems 

1083 key (str): The name of the variable you want to compare 

1084 op (function): The operation you want to apply to the data 

1085 num_procs (int): Number of processes 

1086 

1087 Returns: 

1088 The reference value 

1089 """ 

1090 if problem.__name__ == "run_Lorenz": 

1091 if key == 'work_newton' and op == sum: 

1092 return 983 

1093 elif key == 'e_global_post_run' and op == max: 

1094 return 3.944880392126038e-06 

1095 

1096 super().get_reference_value(problem, key, op, num_procs) 

1097 

1098 

1099class AdaptivityCollocationRefinementStrategy(AdaptivityCollocationStrategy): 

1100 def __init__(self, **kwargs): 

1101 super().__init__(**kwargs) 

1102 self.color = list(cmap.values())[5] 

1103 self.marker = '^' 

1104 self.adaptive_coll_params = { 

1105 'num_nodes': [2, 3], 

1106 'quad_type': ['GAUSS', 'RADAU-RIGHT'], 

1107 'do_coll_update': [True, False], 

1108 } 

1109 

1110 @property 

1111 def label(self): 

1112 return 'adaptivity refinement' 

1113 

1114 def get_reference_value(self, problem, key, op, num_procs=1): 

1115 """ 

1116 Get a reference value for a given problem for testing in CI. 

1117 

1118 Args: 

1119 problem: A function that runs a pySDC problem, see imports for available problems 

1120 key (str): The name of the variable you want to compare 

1121 op (function): The operation you want to apply to the data 

1122 num_procs (int): Number of processes 

1123 

1124 Returns: 

1125 The reference value 

1126 """ 

1127 if problem.__name__ == "run_Lorenz": 

1128 if key == 'work_newton' and op == sum: 

1129 return 917 

1130 elif key == 'e_global_post_run' and op == max: 

1131 return 1.0587702028885815e-05 

1132 

1133 super().get_reference_value(problem, key, op, num_procs) 

1134 

1135 

1136class AdaptivityCollocationDerefinementStrategy(AdaptivityCollocationStrategy): 

1137 def __init__(self, **kwargs): 

1138 super().__init__(**kwargs) 

1139 self.color = list(cmap.values())[6] 

1140 self.marker = '^' 

1141 self.adaptive_coll_params = {'num_nodes': [4, 3]} 

1142 

1143 @property 

1144 def label(self): 

1145 return 'adaptivity de-refinement' 

1146 

1147 def get_reference_value(self, problem, key, op, num_procs=1): 

1148 """ 

1149 Get a reference value for a given problem for testing in CI. 

1150 

1151 Args: 

1152 problem: A function that runs a pySDC problem, see imports for available problems 

1153 key (str): The name of the variable you want to compare 

1154 op (function): The operation you want to apply to the data 

1155 num_procs (int): Number of processes 

1156 

1157 Returns: 

1158 The reference value 

1159 """ 

1160 if problem.__name__ == 'run_Lorenz': 

1161 if key == 'work_newton' and op == sum: 

1162 return 1358 

1163 elif key == 'e_global_post_run' and op == max: 

1164 return 0.00010316526647002888 

1165 

1166 super().get_reference_value(problem, key, op, num_procs) 

1167 

1168 

1169class DIRKStrategy(AdaptivityStrategy): 

1170 ''' 

1171 DIRK4(3) 

1172 ''' 

1173 

1174 def __init__(self, **kwargs): 

1175 ''' 

1176 Initialization routine 

1177 ''' 

1178 from pySDC.implementations.convergence_controller_classes.adaptivity import AdaptivityRK 

1179 

1180 super().__init__(**kwargs) 

1181 self.color = list(cmap.values())[7] 

1182 self.marker = '^' 

1183 self.name = 'DIRK' 

1184 self.bar_plot_x_label = 'DIRK4(3)' 

1185 self.precision_parameter = 'e_tol' 

1186 self.precision_parameter_loc = ['convergence_controllers', AdaptivityRK, 'e_tol'] 

1187 self.max_steps = 1e5 

1188 

1189 @property 

1190 def label(self): 

1191 return 'DIRK4(3)' 

1192 

1193 def get_custom_description(self, problem, num_procs): 

1194 ''' 

1195 Routine to get a custom description that adds adaptivity 

1196 

1197 Args: 

1198 problem: A function that runs a pySDC problem, see imports for available problems 

1199 num_procs (int): Number of processes you intend to run with 

1200 

1201 Returns: 

1202 The custom descriptions you can supply to the problem when running it 

1203 ''' 

1204 from pySDC.implementations.convergence_controller_classes.adaptivity import AdaptivityRK, Adaptivity 

1205 from pySDC.implementations.convergence_controller_classes.basic_restarting import BasicRestarting 

1206 from pySDC.implementations.sweeper_classes.Runge_Kutta import DIRK43 

1207 

1208 adaptivity_description = super().get_custom_description(problem, num_procs) 

1209 

1210 e_tol = adaptivity_description['convergence_controllers'][Adaptivity]['e_tol'] 

1211 adaptivity_description['convergence_controllers'].pop(Adaptivity, None) 

1212 adaptivity_description.pop('sweeper_params', None) 

1213 

1214 rk_params = { 

1215 'step_params': {'maxiter': 1}, 

1216 'sweeper_class': DIRK43, 

1217 'convergence_controllers': { 

1218 AdaptivityRK: {'e_tol': e_tol}, 

1219 BasicRestarting.get_implementation(useMPI=self.useMPI): { 

1220 'max_restarts': 49, 

1221 'crash_after_max_restarts': False, 

1222 }, 

1223 }, 

1224 } 

1225 

1226 custom_description = merge_descriptions(adaptivity_description, rk_params) 

1227 

1228 return custom_description 

1229 

1230 def get_reference_value(self, problem, key, op, num_procs=1): 

1231 """ 

1232 Get a reference value for a given problem for testing in CI. 

1233 

1234 Args: 

1235 problem: A function that runs a pySDC problem, see imports for available problems 

1236 key (str): The name of the variable you want to compare 

1237 op (function): The operation you want to apply to the data 

1238 num_procs (int): Number of processes 

1239 

1240 Returns: 

1241 The reference value 

1242 """ 

1243 if problem.__name__ == "run_Lorenz": 

1244 if key == 'work_newton' and op == sum: 

1245 return 1820 

1246 elif key == 'e_global_post_run' and op == max: 

1247 return 0.00013730538358736055 

1248 

1249 super().get_reference_value(problem, key, op, num_procs) 

1250 

1251 def get_random_params(self, problem, num_procs): 

1252 ''' 

1253 Routine to get parameters for the randomization of faults 

1254 

1255 Args: 

1256 problem: A function that runs a pySDC problem, see imports for available problems 

1257 num_procs (int): Number of processes you intend to run with 

1258 

1259 Returns: 

1260 dict: Randomization parameters 

1261 ''' 

1262 rnd_params = super().get_random_params(problem, num_procs) 

1263 rnd_params['iteration'] = 1 

1264 rnd_params['min_node'] = 5 

1265 

1266 return rnd_params 

1267 

1268 

1269class ARKStrategy(AdaptivityStrategy): 

1270 ''' 

1271 ARK5(4) 

1272 ''' 

1273 

1274 def __init__(self, **kwargs): 

1275 ''' 

1276 Initialization routine 

1277 ''' 

1278 from pySDC.implementations.convergence_controller_classes.adaptivity import AdaptivityRK 

1279 

1280 super().__init__(**kwargs) 

1281 self.color = list(cmap.values())[7] 

1282 self.marker = 'P' 

1283 self.name = 'ARK' 

1284 self.bar_plot_x_label = 'ARK5(4)' 

1285 self.precision_parameter = 'e_tol' 

1286 self.precision_parameter_loc = ['convergence_controllers', AdaptivityRK, 'e_tol'] 

1287 self.max_steps = 1e5 

1288 

1289 @property 

1290 def label(self): 

1291 return 'ARK5(4)' 

1292 

1293 def get_custom_description(self, problem, num_procs): 

1294 ''' 

1295 Routine to get a custom description that adds adaptivity 

1296 

1297 Args: 

1298 problem: A function that runs a pySDC problem, see imports for available problems 

1299 num_procs (int): Number of processes you intend to run with 

1300 

1301 Returns: 

1302 The custom descriptions you can supply to the problem when running it 

1303 ''' 

1304 from pySDC.implementations.convergence_controller_classes.adaptivity import AdaptivityRK, Adaptivity 

1305 from pySDC.implementations.convergence_controller_classes.basic_restarting import BasicRestarting 

1306 from pySDC.implementations.sweeper_classes.Runge_Kutta import ARK548L2SA 

1307 

1308 adaptivity_description = super().get_custom_description(problem, num_procs) 

1309 

1310 e_tol = adaptivity_description['convergence_controllers'][Adaptivity]['e_tol'] / 20.0 

1311 adaptivity_description['convergence_controllers'].pop(Adaptivity, None) 

1312 adaptivity_description.pop('sweeper_params', None) 

1313 

1314 rk_params = { 

1315 'step_params': {'maxiter': 1}, 

1316 'sweeper_class': ARK548L2SA, 

1317 'convergence_controllers': { 

1318 AdaptivityRK: {'e_tol': e_tol}, 

1319 BasicRestarting.get_implementation(useMPI=self.useMPI): { 

1320 'max_restarts': 49, 

1321 'crash_after_max_restarts': False, 

1322 }, 

1323 }, 

1324 } 

1325 

1326 custom_description = merge_descriptions(adaptivity_description, rk_params) 

1327 

1328 return custom_description 

1329 

1330 def get_reference_value(self, problem, key, op, num_procs=1): 

1331 """ 

1332 Get a reference value for a given problem for testing in CI. 

1333 

1334 Args: 

1335 problem: A function that runs a pySDC problem, see imports for available problems 

1336 key (str): The name of the variable you want to compare 

1337 op (function): The operation you want to apply to the data 

1338 num_procs (int): Number of processes 

1339 

1340 Returns: 

1341 The reference value 

1342 """ 

1343 if problem.__name__ == "run_Schroedinger": 

1344 if key == 'work_newton' and op == sum: 

1345 return 0 

1346 elif key == 'e_global_post_run' and op == max: 

1347 return 3.1786601531890356e-08 

1348 

1349 super().get_reference_value(problem, key, op, num_procs) 

1350 

1351 

1352class ESDIRKStrategy(AdaptivityStrategy): 

1353 ''' 

1354 ESDIRK5(3) 

1355 ''' 

1356 

1357 def __init__(self, **kwargs): 

1358 ''' 

1359 Initialization routine 

1360 ''' 

1361 from pySDC.implementations.convergence_controller_classes.adaptivity import AdaptivityRK 

1362 

1363 super().__init__(**kwargs) 

1364 self.color = 'violet' 

1365 self.marker = '^' 

1366 self.name = 'ESDIRK' 

1367 self.bar_plot_x_label = 'ESDIRK5(3)' 

1368 self.precision_parameter = 'e_tol' 

1369 self.precision_parameter_loc = ['convergence_controllers', AdaptivityRK, 'e_tol'] 

1370 self.max_steps = 1e5 

1371 

1372 @property 

1373 def label(self): 

1374 return 'ESDIRK5(3)' 

1375 

1376 def get_description_for_tolerance(self, problem, param, **kwargs): 

1377 desc = {} 

1378 if problem.__name__ == 'run_Schroedinger': 

1379 desc['problem_params'] = {'lintol': param} 

1380 return desc 

1381 

1382 def get_custom_description(self, problem, num_procs): 

1383 ''' 

1384 Routine to get a custom description that adds adaptivity 

1385 

1386 Args: 

1387 problem: A function that runs a pySDC problem, see imports for available problems 

1388 num_procs (int): Number of processes you intend to run with 

1389 

1390 Returns: 

1391 The custom descriptions you can supply to the problem when running it 

1392 ''' 

1393 from pySDC.implementations.convergence_controller_classes.adaptivity import AdaptivityRK, Adaptivity 

1394 from pySDC.implementations.convergence_controller_classes.basic_restarting import BasicRestarting 

1395 from pySDC.implementations.sweeper_classes.Runge_Kutta import ESDIRK53 

1396 

1397 adaptivity_description = super().get_custom_description(problem, num_procs) 

1398 

1399 e_tol = adaptivity_description['convergence_controllers'][Adaptivity]['e_tol'] 

1400 adaptivity_description['convergence_controllers'].pop(Adaptivity, None) 

1401 adaptivity_description.pop('sweeper_params', None) 

1402 

1403 mod = 1e1 if problem.__name__ == 'run_quench' else 1.0 

1404 

1405 rk_params = { 

1406 'step_params': {'maxiter': 1}, 

1407 'sweeper_class': ESDIRK53, 

1408 'convergence_controllers': { 

1409 AdaptivityRK: {'e_tol': e_tol * mod}, 

1410 BasicRestarting.get_implementation(useMPI=self.useMPI): { 

1411 'max_restarts': 49, 

1412 'crash_after_max_restarts': False, 

1413 }, 

1414 }, 

1415 } 

1416 

1417 custom_description = merge_descriptions(adaptivity_description, rk_params) 

1418 

1419 return custom_description 

1420 

1421 def get_reference_value(self, problem, key, op, num_procs=1): 

1422 """ 

1423 Get a reference value for a given problem for testing in CI. 

1424 

1425 Args: 

1426 problem: A function that runs a pySDC problem, see imports for available problems 

1427 key (str): The name of the variable you want to compare 

1428 op (function): The operation you want to apply to the data 

1429 num_procs (int): Number of processes 

1430 

1431 Returns: 

1432 The reference value 

1433 """ 

1434 if problem.__name__ == "run_Lorenz": 

1435 if key == 'work_newton' and op == sum: 

1436 return 984 

1437 elif key == 'e_global_post_run' and op == max: 

1438 return 3.148061889390874e-06 

1439 

1440 super().get_reference_value(problem, key, op, num_procs) 

1441 

1442 def get_random_params(self, problem, num_procs): 

1443 ''' 

1444 Routine to get parameters for the randomization of faults 

1445 

1446 Args: 

1447 problem: A function that runs a pySDC problem, see imports for available problems 

1448 num_procs (int): Number of processes you intend to run with 

1449 

1450 Returns: 

1451 dict: Randomization parameters 

1452 ''' 

1453 rnd_params = super().get_random_params(problem, num_procs) 

1454 rnd_params['iteration'] = 1 

1455 rnd_params['min_node'] = 6 

1456 

1457 return rnd_params 

1458 

1459 

1460class ERKStrategy(DIRKStrategy): 

1461 """ 

1462 Explicit embedded RK using Cash-Karp's method 

1463 """ 

1464 

1465 def __init__(self, **kwargs): 

1466 ''' 

1467 Initialization routine 

1468 ''' 

1469 super().__init__(**kwargs) 

1470 self.color = list(cmap.values())[8] 

1471 self.marker = 'x' 

1472 self.name = 'ERK' 

1473 self.bar_plot_x_label = 'ERK5(4)' 

1474 

1475 def get_description_for_tolerance(self, problem, param, **kwargs): 

1476 desc = {} 

1477 if problem.__name__ == 'run_Schroedinger': 

1478 desc['problem_params'] = {'lintol': param} 

1479 

1480 return desc 

1481 

1482 @property 

1483 def label(self): 

1484 return 'CP5(4)' 

1485 

1486 def get_random_params(self, problem, num_procs): 

1487 ''' 

1488 Routine to get parameters for the randomization of faults 

1489 

1490 Args: 

1491 problem: A function that runs a pySDC problem, see imports for available problems 

1492 num_procs (int): Number of processes you intend to run with 

1493 

1494 Returns: 

1495 dict: Randomization parameters 

1496 ''' 

1497 rnd_params = super().get_random_params(problem, num_procs) 

1498 rnd_params['min_node'] = 7 

1499 

1500 return rnd_params 

1501 

1502 def get_custom_description(self, problem, num_procs=1): 

1503 from pySDC.implementations.sweeper_classes.Runge_Kutta import Cash_Karp 

1504 

1505 desc = super().get_custom_description(problem, num_procs) 

1506 desc['sweeper_class'] = Cash_Karp 

1507 

1508 if problem.__name__ == "run_AC": 

1509 desc['level_params']['dt'] = 2e-5 

1510 return desc 

1511 

1512 def get_reference_value(self, problem, key, op, num_procs=1): 

1513 """ 

1514 Get a reference value for a given problem for testing in CI. 

1515 

1516 Args: 

1517 problem: A function that runs a pySDC problem, see imports for available problems 

1518 key (str): The name of the variable you want to compare 

1519 op (function): The operation you want to apply to the data 

1520 num_procs (int): Number of processes 

1521 

1522 Returns: 

1523 The reference value 

1524 """ 

1525 if problem.__name__ == "run_Lorenz": 

1526 if key == 'work_newton' and op == sum: 

1527 return 0 

1528 elif key == 'e_global_post_run' and op == max: 

1529 return 3.5085474063834e-05 

1530 

1531 super().get_reference_value(problem, key, op, num_procs) 

1532 

1533 

1534class DoubleAdaptivityStrategy(AdaptivityStrategy): 

1535 ''' 

1536 Adaptivity based both on embedded estimate and on residual 

1537 ''' 

1538 

1539 def __init__(self, **kwargs): 

1540 ''' 

1541 Initialization routine 

1542 ''' 

1543 from pySDC.implementations.convergence_controller_classes.adaptivity import Adaptivity 

1544 

1545 kwargs['skip_residual_computation'] = 'all' 

1546 super().__init__(**kwargs) 

1547 self.color = list(cmap.values())[7] 

1548 self.marker = '^' 

1549 self.name = 'double_adaptivity' 

1550 self.bar_plot_x_label = 'double adaptivity' 

1551 self.precision_parameter = 'e_tol' 

1552 self.precision_parameter_loc = ['convergence_controllers', Adaptivity, 'e_tol'] 

1553 self.residual_e_tol_ratio = 1.0 

1554 self.residual_e_tol_abs = None 

1555 

1556 @property 

1557 def label(self): 

1558 return 'double adaptivity' 

1559 

1560 def get_custom_description(self, problem, num_procs): 

1561 ''' 

1562 Routine to get a custom description that adds adaptivity 

1563 

1564 Args: 

1565 problem: A function that runs a pySDC problem, see imports for available problems 

1566 num_procs (int): Number of processes you intend to run with 

1567 

1568 Returns: 

1569 The custom descriptions you can supply to the problem when running it 

1570 ''' 

1571 from pySDC.implementations.convergence_controller_classes.adaptivity import AdaptivityResidual, Adaptivity 

1572 from pySDC.implementations.convergence_controller_classes.basic_restarting import BasicRestarting 

1573 

1574 custom_description = super().get_custom_description(problem, num_procs) 

1575 

1576 if self.residual_e_tol_abs: 

1577 e_tol = self.residual_e_tol_abs 

1578 else: 

1579 e_tol = custom_description['convergence_controllers'][Adaptivity]['e_tol'] * self.residual_e_tol_ratio 

1580 custom_description['convergence_controllers'][AdaptivityResidual] = { 

1581 'e_tol': e_tol, 

1582 'allowed_modifications': ['decrease'], 

1583 } 

1584 

1585 custom_description['convergence_controllers'][BasicRestarting.get_implementation(useMPI=self.useMPI)] = { 

1586 'max_restarts': 15 

1587 } 

1588 

1589 return custom_description 

1590 

1591 def get_reference_value(self, problem, key, op, num_procs=1): 

1592 """ 

1593 Get a reference value for a given problem for testing in CI. 

1594 

1595 Args: 

1596 problem: A function that runs a pySDC problem, see imports for available problems 

1597 key (str): The name of the variable you want to compare 

1598 op (function): The operation you want to apply to the data 

1599 num_procs (int): Number of processes 

1600 

1601 Returns: 

1602 The reference value 

1603 """ 

1604 if problem.__name__ == 'run_Lorenz': 

1605 if key == 'work_newton' and op == sum: 

1606 return 1369 

1607 elif key == 'e_global_post_run' and op == max: 

1608 return 9.364841517367495e-06 

1609 

1610 super().get_reference_value(problem, key, op, num_procs) 

1611 

1612 

1613class AdaptivityAvoidRestartsStrategy(AdaptivityStrategy): 

1614 """ 

1615 Adaptivity with the avoid restarts option 

1616 """ 

1617 

1618 @property 

1619 def label(self): 

1620 return 'adaptivity (avoid restarts)' 

1621 

1622 def get_custom_description(self, problem, num_procs): 

1623 ''' 

1624 Routine to get a custom description that adds adaptivity 

1625 

1626 Args: 

1627 problem: A function that runs a pySDC problem, see imports for available problems 

1628 num_procs (int): Number of processes you intend to run with 

1629 

1630 Returns: 

1631 The custom descriptions you can supply to the problem when running it 

1632 ''' 

1633 from pySDC.implementations.convergence_controller_classes.adaptivity import Adaptivity 

1634 from pySDC.implementations.convergence_controller_classes.basic_restarting import BasicRestarting 

1635 

1636 custom_description = super().get_custom_description(problem, num_procs) 

1637 

1638 custom_description['convergence_controllers'][Adaptivity]['avoid_restarts'] = True 

1639 

1640 custom_description['convergence_controllers'][BasicRestarting.get_implementation(useMPI=self.useMPI)] = { 

1641 'max_restarts': 15 

1642 } 

1643 

1644 return custom_description 

1645 

1646 def get_reference_value(self, problem, key, op, num_procs=1): 

1647 """ 

1648 Get a reference value for a given problem for testing in CI. 

1649 

1650 Args: 

1651 problem: A function that runs a pySDC problem, see imports for available problems 

1652 key (str): The name of the variable you want to compare 

1653 op (function): The operation you want to apply to the data 

1654 num_procs (int): Number of processes 

1655 

1656 Returns: 

1657 The reference value 

1658 """ 

1659 if problem.__name__ == "run_Lorenz": 

1660 if key == 'work_newton' and op == sum: 

1661 return 1369 

1662 elif key == 'e_global_post_run' and op == max: 

1663 return 9.364841517367495e-06 

1664 

1665 super().get_reference_value(problem, key, op, num_procs) 

1666 

1667 

1668class AdaptivityInterpolationStrategy(AdaptivityStrategy): 

1669 """ 

1670 Adaptivity with interpolation between restarts 

1671 """ 

1672 

1673 @property 

1674 def label(self): 

1675 return 'adaptivity+interpolation' 

1676 

1677 def get_custom_description(self, problem, num_procs): 

1678 ''' 

1679 Routine to get a custom description that adds adaptivity 

1680 

1681 Args: 

1682 problem: A function that runs a pySDC problem, see imports for available problems 

1683 num_procs (int): Number of processes you intend to run with 

1684 

1685 Returns: 

1686 The custom descriptions you can supply to the problem when running it 

1687 ''' 

1688 from pySDC.implementations.convergence_controller_classes.adaptivity import Adaptivity 

1689 from pySDC.implementations.convergence_controller_classes.interpolate_between_restarts import ( 

1690 InterpolateBetweenRestarts, 

1691 ) 

1692 from pySDC.implementations.convergence_controller_classes.basic_restarting import BasicRestarting 

1693 

1694 custom_description = super().get_custom_description(problem, num_procs) 

1695 

1696 custom_description['convergence_controllers'][Adaptivity]['avoid_restarts'] = False 

1697 custom_description['convergence_controllers'][InterpolateBetweenRestarts] = {} 

1698 

1699 custom_description['convergence_controllers'][BasicRestarting.get_implementation(useMPI=self.useMPI)] = { 

1700 'max_restarts': 15 

1701 } 

1702 

1703 return custom_description 

1704 

1705 def get_reference_value(self, problem, key, op, num_procs=1): 

1706 """ 

1707 Get a reference value for a given problem for testing in CI. 

1708 

1709 Args: 

1710 problem: A function that runs a pySDC problem, see imports for available problems 

1711 key (str): The name of the variable you want to compare 

1712 op (function): The operation you want to apply to the data 

1713 num_procs (int): Number of processes 

1714 

1715 Returns: 

1716 The reference value 

1717 """ 

1718 if problem.__name__ == "run_Lorenz": 

1719 if key == 'work_newton' and op == sum: 

1720 return 6659 

1721 elif key == 'e_global_post_run' and op == max: 

1722 return 2.9780002756552015e-06 

1723 

1724 super().get_reference_value(problem, key, op, num_procs) 

1725 

1726 

1727class AdaptivityExtrapolationWithinQStrategy(InexactBaseStrategy): 

1728 ''' 

1729 Adaptivity based on extrapolation between collocation nodes as a resilience strategy 

1730 ''' 

1731 

1732 def __init__(self, **kwargs): 

1733 ''' 

1734 Initialization routine 

1735 ''' 

1736 from pySDC.implementations.convergence_controller_classes.adaptivity import AdaptivityExtrapolationWithinQ 

1737 

1738 self.restol = None 

1739 super().__init__(**kwargs) 

1740 self.color = list(cmap.values())[8] 

1741 self.marker = '*' 

1742 self.name = 'adaptivity_extraQ' 

1743 self.bar_plot_x_label = 'adaptivity Q' 

1744 self.precision_parameter = 'e_tol' 

1745 self.adaptive_coll_params = {} 

1746 self.precision_parameter_loc = ['convergence_controllers', AdaptivityExtrapolationWithinQ, 'e_tol'] 

1747 

1748 def get_custom_description(self, problem, num_procs): 

1749 ''' 

1750 Routine to get a custom description that adds adaptivity 

1751 

1752 Args: 

1753 problem: A function that runs a pySDC problem, see imports for available problems 

1754 num_procs (int): Number of processes you intend to run with 

1755 

1756 Returns: 

1757 The custom descriptions you can supply to the problem when running it 

1758 ''' 

1759 from pySDC.implementations.convergence_controller_classes.adaptivity import AdaptivityExtrapolationWithinQ 

1760 

1761 custom_description = {} 

1762 

1763 dt_max = np.inf 

1764 dt_min = 1e-5 

1765 

1766 if problem.__name__ == "run_vdp": 

1767 e_tol = 2e-5 

1768 dt_min = 1e-3 

1769 elif problem.__name__ == "run_piline": 

1770 e_tol = 1e-7 

1771 dt_min = 1e-2 

1772 elif problem.__name__ == "run_Lorenz": 

1773 e_tol = 2e-5 

1774 dt_min = 1e-3 

1775 elif problem.__name__ == "run_Schroedinger": 

1776 e_tol = 4e-6 

1777 dt_min = 1e-3 

1778 elif problem.__name__ == "run_quench": 

1779 e_tol = 1e-5 

1780 dt_min = 1e-3 

1781 dt_max = 1e2 

1782 elif problem.__name__ == "run_AC": 

1783 e_tol = 1e-4 

1784 else: 

1785 raise NotImplementedError( 

1786 'I don\'t have a tolerance for adaptivity for your problem. Please add one to the\ 

1787 strategy' 

1788 ) 

1789 

1790 custom_description['convergence_controllers'] = { 

1791 AdaptivityExtrapolationWithinQ: { 

1792 'e_tol': e_tol, 

1793 'dt_min': dt_min, 

1794 'dt_max': dt_max, 

1795 'restol_rel': 1e-2, 

1796 'restart_at_maxiter': True, 

1797 } 

1798 } 

1799 return merge_descriptions(super().get_custom_description(problem, num_procs), custom_description) 

1800 

1801 def get_reference_value(self, problem, key, op, num_procs=1): 

1802 """ 

1803 Get a reference value for a given problem for testing in CI. 

1804 

1805 Args: 

1806 problem: A function that runs a pySDC problem, see imports for available problems 

1807 key (str): The name of the variable you want to compare 

1808 op (function): The operation you want to apply to the data 

1809 num_procs (int): Number of processes 

1810 

1811 Returns: 

1812 The reference value 

1813 """ 

1814 if problem.__name__ == "run_Lorenz": 

1815 if key == 'work_newton' and op == sum: 

1816 return 2198 

1817 elif key == 'e_global_post_run' and op == max: 

1818 return 5.412657451131508e-07 

1819 

1820 super().get_reference_value(problem, key, op, num_procs) 

1821 

1822 

1823class AdaptivityPolynomialError(InexactBaseStrategy): 

1824 ''' 

1825 Adaptivity based on extrapolation between collocation nodes as a resilience strategy 

1826 ''' 

1827 

1828 def __init__(self, interpolate_between_restarts=False, use_restol_rel=True, max_slope=4, **kwargs): 

1829 ''' 

1830 Initialization routine 

1831 ''' 

1832 from pySDC.implementations.convergence_controller_classes.adaptivity import AdaptivityPolynomialError 

1833 

1834 self.restol = None 

1835 super().__init__(**kwargs) 

1836 self.color = list(cmap.values())[9] 

1837 self.marker = '+' 

1838 self.name = 'adaptivity-inter' 

1839 self.bar_plot_x_label = 'adaptivity Q' 

1840 self.precision_parameter = 'e_tol' 

1841 self.adaptive_coll_params = {} 

1842 self.precision_parameter_loc = ['convergence_controllers', AdaptivityPolynomialError, 'e_tol'] 

1843 self.interpolate_between_restarts = interpolate_between_restarts 

1844 self.use_restol_rel = use_restol_rel 

1845 self.max_slope = max_slope 

1846 

1847 def get_custom_description(self, problem, num_procs): 

1848 ''' 

1849 Routine to get a custom description that adds adaptivity 

1850 

1851 Args: 

1852 problem: A function that runs a pySDC problem, see imports for available problems 

1853 num_procs (int): Number of processes you intend to run with 

1854 

1855 Returns: 

1856 The custom descriptions you can supply to the problem when running it 

1857 ''' 

1858 from pySDC.implementations.convergence_controller_classes.adaptivity import AdaptivityPolynomialError 

1859 from pySDC.implementations.convergence_controller_classes.step_size_limiter import StepSizeLimiter 

1860 

1861 base_params = super().get_custom_description(problem, num_procs) 

1862 custom_description = {} 

1863 

1864 dt_max = np.inf 

1865 restol_rel = 1e-4 

1866 restol_min = 1e-12 

1867 dt_min = 0 

1868 level_params = {} 

1869 problem_params = {} 

1870 

1871 if problem.__name__ == "run_vdp": 

1872 e_tol = 6e-4 

1873 level_params['dt'] = 0.1 

1874 restol_rel = 1e-5 

1875 restol_min = 1e-12 

1876 dt_min = 1e-7 

1877 problem_params['newton_tol'] = 1e-14 

1878 elif problem.__name__ == "run_piline": 

1879 e_tol = 1e-7 

1880 elif problem.__name__ == "run_Lorenz": 

1881 e_tol = 7e-4 

1882 elif problem.__name__ == "run_Schroedinger": 

1883 e_tol = 3e-5 

1884 elif problem.__name__ == "run_quench": 

1885 e_tol = 1e-7 

1886 level_params['dt'] = 50.0 

1887 restol_min = 1e-11 

1888 restol_rel = 1e-1 

1889 elif problem.__name__ == "run_AC": 

1890 e_tol = 1.0e-4 

1891 restol_rel = 1e-3 

1892 # dt_max = 0.1 * base_params['problem_params']['eps'] ** 2 

1893 else: 

1894 raise NotImplementedError( 

1895 'I don\'t have a tolerance for adaptivity for your problem. Please add one to the\ 

1896 strategy' 

1897 ) 

1898 

1899 custom_description['convergence_controllers'] = { 

1900 AdaptivityPolynomialError: { 

1901 'e_tol': e_tol, 

1902 'restol_rel': restol_rel if self.use_restol_rel else 1e-11, 

1903 'restol_min': restol_min if self.use_restol_rel else 1e-12, 

1904 'restart_at_maxiter': True, 

1905 'factor_if_not_converged': self.max_slope, 

1906 'interpolate_between_restarts': self.interpolate_between_restarts, 

1907 }, 

1908 StepSizeLimiter: { 

1909 'dt_max': dt_max, 

1910 'dt_slope_max': self.max_slope, 

1911 'dt_min': dt_min, 

1912 }, 

1913 } 

1914 custom_description['level_params'] = level_params 

1915 custom_description['problem_params'] = problem_params 

1916 return merge_descriptions(base_params, custom_description) 

1917 

1918 def get_custom_description_for_faults(self, problem, *args, **kwargs): 

1919 desc = self.get_custom_description(problem, *args, **kwargs) 

1920 if problem.__name__ == "run_quench": 

1921 from pySDC.implementations.convergence_controller_classes.adaptivity import AdaptivityPolynomialError 

1922 

1923 desc['convergence_controllers'][AdaptivityPolynomialError]['e_tol'] = 1e-7 * 11 

1924 desc['level_params']['dt'] = 4.0 

1925 elif problem.__name__ == "run_AC": 

1926 from pySDC.implementations.convergence_controller_classes.adaptivity import AdaptivityPolynomialError 

1927 

1928 desc['convergence_controllers'][AdaptivityPolynomialError]['e_tol'] = 1e-3 

1929 return desc 

1930 

1931 def get_random_params(self, problem, num_procs): 

1932 ''' 

1933 Routine to get parameters for the randomization of faults 

1934 

1935 Args: 

1936 problem: A function that runs a pySDC problem, see imports for available problems 

1937 num_procs (int): Number of processes you intend to run with 

1938 

1939 Returns: 

1940 dict: Randomization parameters 

1941 ''' 

1942 

1943 rnd_params = super().get_random_params(problem, num_procs) 

1944 if problem.__name__ == "run_quench": 

1945 rnd_params['iteration'] = 1 

1946 elif problem.__name__ == 'run_Lorenz': 

1947 rnd_params['iteration'] = 4 

1948 return rnd_params 

1949 

1950 def get_reference_value(self, problem, key, op, num_procs=1): 

1951 """ 

1952 Get a reference value for a given problem for testing in CI. 

1953 

1954 Args: 

1955 problem: A function that runs a pySDC problem, see imports for available problems 

1956 key (str): The name of the variable you want to compare 

1957 op (function): The operation you want to apply to the data 

1958 num_procs (int): Number of processes 

1959 

1960 Returns: 

1961 The reference value 

1962 """ 

1963 if problem.__name__ == "run_Lorenz": 

1964 if key == 'work_newton' and op == sum: 

1965 return 2124 

1966 elif key == 'e_global_post_run' and op == max: 

1967 return 8.484321512014503e-08 

1968 

1969 super().get_reference_value(problem, key, op, num_procs) 

1970 

1971 @property 

1972 def label(self): 

1973 return r'$\Delta t$-$k$-adaptivity'