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

834 statements  

« prev     ^ index     » next       coverage.py v7.5.0, created at 2024-04-29 09:02 +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 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([2, 0], dtype=np.float64), 

234 'crash_at_maxiter': False, 

235 'newton_tol': 1e-11, 

236 'stop_at_nan': False, 

237 } 

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

239 

240 elif problem.__name__ == "run_Lorenz": 

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

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

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

244 elif problem.__name__ == "run_Schroedinger": 

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

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

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

248 elif problem.__name__ == "run_quench": 

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

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

251 custom_description['problem_params'] = { 

252 'newton_maxiter': 29, 

253 'newton_tol': 1e-7, 

254 'nvars': 2**6, 

255 'direct_solver': False, 

256 'lintol': 1e-8, 

257 'liniter': 29, 

258 'order': 6, 

259 } 

260 elif problem.__name__ == "run_AC": 

261 eps = 4e-2 

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

263 custom_description['problem_params'] = { 

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

265 'init_type': 'circle', 

266 'eps': eps, 

267 'radius': 0.25, 

268 'nu': 2, 

269 } 

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

271 

272 custom_description['convergence_controllers'] = { 

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

274 } 

275 

276 if self.stop_at_nan: 

277 from pySDC.implementations.convergence_controller_classes.crash import StopAtNan 

278 

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

280 

281 from pySDC.implementations.convergence_controller_classes.crash import StopAtMaxRuntime 

282 

283 max_runtime = { 

284 'run_vdp': 60, 

285 'run_Lorenz': 60, 

286 'run_Schroedinger': 150, 

287 'run_quench': 150, 

288 'run_AC': 150, 

289 } 

290 

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

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

293 } 

294 return custom_description 

295 

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

297 ''' 

298 Get a custom description based on the problem 

299 

300 Args: 

301 problem (function): A problem to run 

302 num_procs (int): Number of processes 

303 

304 Returns: 

305 dict: Custom description 

306 ''' 

307 custom_description = self.get_base_parameters(problem, num_procs) 

308 return merge_descriptions(custom_description, self.custom_description) 

309 

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

311 ''' 

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

313 

314 Returns: 

315 dict: Custom description 

316 ''' 

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

318 

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

320 """ 

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

322 

323 Args: 

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

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

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

327 num_procs (int): Number of processes 

328 

329 Returns: 

330 The reference value 

331 """ 

332 raise NotImplementedError('The reference value you are looking for is not implemented for this strategy!') 

333 

334 

335class InexactBaseStrategy(Strategy): 

336 """ 

337 Base class for inexact strategies. 

338 """ 

339 

340 def __init__( 

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

342 ): 

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

344 super().__init__(**kwargs) 

345 self.double_adaptivity = double_adaptivity 

346 self.newton_inexactness = newton_inexactness 

347 self.linear_inexactness = linear_inexactness 

348 self.SDC_maxiter = SDC_maxiter 

349 

350 def get_controller_params(self, **kwargs): 

351 return {'all_to_done': True} 

352 

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

354 from pySDC.implementations.convergence_controller_classes.inexactness import NewtonInexactness 

355 

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

357 

358 desc = {} 

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

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

361 desc['problem_params'] = {} 

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

363 desc['convergence_controllers'] = {} 

364 

365 inexactness_params = { 

366 'min_tol': 1e-12, 

367 'ratio': 1e-2, 

368 'max_tol': 1e-4, 

369 'use_e_tol': False, 

370 'maxiter': 15, 

371 } 

372 

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

374 if problem.__name__ == 'run_quench': 

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

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

377 inexactness_params['maxiter'] = 5 

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

379 

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

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

382 

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

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

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

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

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

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

389 

390 from pySDC.implementations.convergence_controller_classes.basic_restarting import BasicRestarting 

391 

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

393 'max_restarts': 29, 

394 'crash_after_max_restarts': True, 

395 } 

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

397 

398 

399class BaseStrategy(Strategy): 

400 ''' 

401 Do a fixed iteration count 

402 ''' 

403 

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

405 ''' 

406 Initialization routine 

407 ''' 

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

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

410 self.marker = 'o' 

411 self.name = 'base' 

412 self.bar_plot_x_label = 'base' 

413 self.precision_parameter = 'dt' 

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

415 

416 @property 

417 def label(self): 

418 return r'fixed' 

419 

420 def get_custom_description(self, problem, num_procs): 

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

422 if problem.__name__ == "run_AC": 

423 desc['level_params']['dt'] = 0.8 * desc['problem_params']['eps'] ** 2 / 8.0 

424 return desc 

425 

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

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

428 if problem.__name__ == "run_quench": 

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

430 return desc 

431 

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

433 """ 

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

435 

436 Args: 

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

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

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

440 num_procs (int): Number of processes 

441 

442 Returns: 

443 The reference value 

444 """ 

445 if problem.__name__ == "run_vdp": 

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

447 return 12453 

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

449 return 4.3956128381594795e-06 

450 

451 raise NotImplementedError('The reference value you are looking for is not implemented for this strategy!') 

452 

453 

454class AdaptivityStrategy(Strategy): 

455 ''' 

456 Adaptivity as a resilience strategy 

457 ''' 

458 

459 def __init__(self, **kwargs): 

460 ''' 

461 Initialization routine 

462 ''' 

463 from pySDC.implementations.convergence_controller_classes.adaptivity import Adaptivity 

464 

465 kwargs['skip_residual_computation'] = 'all' 

466 super().__init__(**kwargs) 

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

468 self.marker = '*' 

469 self.name = 'adaptivity' 

470 self.bar_plot_x_label = 'adaptivity' 

471 self.precision_parameter = 'e_tol' 

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

473 

474 @property 

475 def label(self): 

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

477 

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

479 """ 

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

481 

482 Args: 

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

484 

485 Returns: 

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

487 """ 

488 self.fixable += [ 

489 { 

490 'key': 'iteration', 

491 'op': 'lt', 

492 'val': maxiter, 

493 } 

494 ] 

495 return self.fixable 

496 

497 def get_custom_description(self, problem, num_procs): 

498 ''' 

499 Routine to get a custom description that adds adaptivity 

500 

501 Args: 

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

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

504 

505 Returns: 

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

507 ''' 

508 from pySDC.implementations.convergence_controller_classes.adaptivity import Adaptivity 

509 from pySDC.implementations.convergence_controller_classes.step_size_limiter import StepSizeLimiter 

510 

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

512 custom_description = {} 

513 custom_description['convergence_controllers'] = {} 

514 

515 dt_max = np.inf 

516 dt_slope_max = np.inf 

517 

518 if problem.__name__ == "run_piline": 

519 e_tol = 1e-7 

520 elif problem.__name__ == "run_vdp": 

521 e_tol = 2e-5 

522 elif problem.__name__ == "run_Lorenz": 

523 e_tol = 2e-5 

524 elif problem.__name__ == "run_Schroedinger": 

525 e_tol = 4e-7 

526 elif problem.__name__ == "run_quench": 

527 e_tol = 1e-8 

528 custom_description['problem_params'] = { 

529 'newton_tol': 1e-10, 

530 'lintol': 1e-11, 

531 } 

532 

533 from pySDC.implementations.convergence_controller_classes.basic_restarting import BasicRestarting 

534 

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

536 'max_restarts': 99, 

537 } 

538 elif problem.__name__ == "run_AC": 

539 e_tol = 1e-6 

540 dt_max = 0.8 * base_params['problem_params']['eps'] ** 2 

541 

542 else: 

543 raise NotImplementedError( 

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

545 strategy' 

546 ) 

547 

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

549 'e_tol': e_tol, 

550 'dt_slope_max': dt_slope_max, 

551 } 

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

553 'dt_max': dt_max, 

554 } 

555 return merge_descriptions(base_params, custom_description) 

556 

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

558 """ 

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

560 

561 Args: 

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

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

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

565 num_procs (int): Number of processes 

566 

567 Returns: 

568 The reference value 

569 """ 

570 if problem.__name__ == "run_vdp": 

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

572 return 3825 

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

574 return 1.3370376368393444e-05 

575 

576 raise NotImplementedError('The reference value you are looking for is not implemented for this strategy!') 

577 

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

579 from pySDC.implementations.convergence_controller_classes.step_size_limiter import StepSizeLimiter 

580 from pySDC.implementations.convergence_controller_classes.adaptivity import Adaptivity 

581 

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

583 if problem.__name__ == "run_quench": 

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

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

586 return desc 

587 

588 

589class AdaptivityRestartFirstStep(AdaptivityStrategy): 

590 def __init__(self, **kwargs): 

591 super().__init__(**kwargs) 

592 self.color = 'teal' 

593 self.name = 'adaptivityRestartFirstStep' 

594 

595 def get_custom_description(self, problem, num_procs): 

596 ''' 

597 Add the other version of basic restarting. 

598 

599 Args: 

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

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

602 

603 Returns: 

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

605 ''' 

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

607 from pySDC.implementations.convergence_controller_classes.basic_restarting import BasicRestarting 

608 

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

610 'max_restarts': 15, 

611 'restart_from_first_step': True, 

612 } 

613 return custom_description 

614 

615 @property 

616 def label(self): 

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

618 

619 

620class AdaptiveHotRodStrategy(Strategy): 

621 ''' 

622 Adaptivity + Hot Rod as a resilience strategy 

623 ''' 

624 

625 def __init__(self, **kwargs): 

626 ''' 

627 Initialization routine 

628 ''' 

629 from pySDC.implementations.convergence_controller_classes.adaptivity import Adaptivity 

630 

631 kwargs['skip_residual_computation'] = 'all' 

632 super().__init__(**kwargs) 

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

634 self.marker = '.' 

635 self.name = 'adaptive Hot Rod' 

636 self.bar_plot_x_label = 'adaptive\nHot Rod' 

637 self.precision_parameter = 'e_tol' 

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

639 

640 def get_custom_description(self, problem, num_procs): 

641 ''' 

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

643 

644 Args: 

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

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

647 

648 Returns: 

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

650 ''' 

651 from pySDC.implementations.convergence_controller_classes.hotrod import HotRod 

652 from pySDC.implementations.convergence_controller_classes.adaptivity import Adaptivity 

653 

654 if problem.__name__ == "run_vdp": 

655 e_tol = 3e-7 

656 dt_min = 1e-3 

657 maxiter = 4 

658 HotRod_tol = 2e-6 

659 else: 

660 raise NotImplementedError( 

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

662to the strategy' 

663 ) 

664 

665 no_storage = num_procs > 1 

666 

667 custom_description = { 

668 'convergence_controllers': { 

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

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

671 }, 

672 'step_params': {'maxiter': maxiter}, 

673 } 

674 

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

676 

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

678 """ 

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

680 

681 Args: 

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

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

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

685 num_procs (int): Number of processes 

686 

687 Returns: 

688 The reference value 

689 """ 

690 if problem.__name__ == "run_vdp": 

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

692 return 4466 

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

694 return 2.1455229857747504e-06 

695 

696 raise NotImplementedError('The reference value you are looking for is not implemented for this strategy!') 

697 

698 

699class IterateStrategy(Strategy): 

700 ''' 

701 Iterate for as much as you want 

702 ''' 

703 

704 def __init__(self, **kwargs): 

705 ''' 

706 Initialization routine 

707 ''' 

708 kwargs['skip_residual_computation'] = 'most' 

709 super().__init__(**kwargs) 

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

711 self.marker = 'v' 

712 self.name = 'iterate' 

713 self.bar_plot_x_label = 'iterate' 

714 self.precision_parameter = 'restol' 

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

716 

717 @property 

718 def label(self): 

719 return r'$k$-adaptivity' 

720 

721 def get_custom_description(self, problem, num_procs): 

722 ''' 

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

724 

725 Args: 

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

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

728 

729 Returns: 

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

731 ''' 

732 restol = -1 

733 e_tol = -1 

734 

735 if problem.__name__ == "run_piline": 

736 restol = 2.3e-8 

737 elif problem.__name__ == "run_vdp": 

738 restol = 9e-7 

739 elif problem.__name__ == "run_Lorenz": 

740 restol = 16e-7 

741 elif problem.__name__ == "run_Schroedinger": 

742 restol = 6.5e-7 

743 elif problem.__name__ == "run_quench": 

744 restol = 1e-7 

745 elif problem.__name__ == "run_AC": 

746 restol = 1e-11 

747 else: 

748 raise NotImplementedError( 

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

750strategy' 

751 ) 

752 

753 custom_description = { 

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

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

756 } 

757 

758 if problem.__name__ == "run_quench": 

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

760 

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

762 

763 def get_random_params(self, problem, num_procs): 

764 ''' 

765 Routine to get parameters for the randomization of faults 

766 

767 Args: 

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

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

770 

771 Returns: 

772 dict: Randomization parameters 

773 ''' 

774 

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

776 if problem.__name__ == "run_quench": 

777 rnd_params['iteration'] = 1 

778 return rnd_params 

779 

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

781 """ 

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

783 

784 Args: 

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

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

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

788 num_procs (int): Number of processes 

789 

790 Returns: 

791 The reference value 

792 """ 

793 if problem.__name__ == "run_vdp": 

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

795 return 8534 

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

797 return 0.0005961192269257065 

798 

799 raise NotImplementedError('The reference value you are looking for is not implemented for this strategy!') 

800 

801 

802class kAdaptivityStrategy(IterateStrategy): 

803 def __init__(self, **kwargs): 

804 super().__init__(**kwargs) 

805 self.precision_parameter = 'dt' 

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

807 

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

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

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

811 if problem.__name__ == "run_quench": 

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

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

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

815 elif problem.__name__ == "run_AC": 

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

817 desc['level_params']['dt'] = 0.8 * desc['problem_params']['eps'] ** 2 / 8.0 

818 return desc 

819 

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

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

822 if problem.__name__ == 'run_quench': 

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

824 elif problem.__name__ == 'run_AC': 

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

826 return desc 

827 

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

829 """ 

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

831 

832 Args: 

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

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

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

836 num_procs (int): Number of processes 

837 

838 Returns: 

839 The reference value 

840 """ 

841 if problem.__name__ == "run_vdp": 

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

843 return 13241 

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

845 return 1.0505165626284452e-08 

846 

847 raise NotImplementedError('The reference value you are looking for is not implemented for this strategy!') 

848 

849 

850class HotRodStrategy(Strategy): 

851 ''' 

852 Hot Rod as a resilience strategy 

853 ''' 

854 

855 def __init__(self, **kwargs): 

856 ''' 

857 Initialization routine 

858 ''' 

859 kwargs['skip_residual_computation'] = 'all' 

860 super().__init__(**kwargs) 

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

862 self.marker = '^' 

863 self.name = 'Hot Rod' 

864 self.bar_plot_x_label = 'Hot Rod' 

865 self.precision_parameter = 'dt' 

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

867 

868 def get_custom_description(self, problem, num_procs): 

869 ''' 

870 Routine to get a custom description that adds Hot Rod 

871 

872 Args: 

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

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

875 

876 Returns: 

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

878 ''' 

879 from pySDC.implementations.convergence_controller_classes.hotrod import HotRod 

880 from pySDC.implementations.convergence_controller_classes.basic_restarting import BasicRestartingNonMPI 

881 

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

883 if problem.__name__ == "run_vdp": 

884 if num_procs == 4: 

885 HotRod_tol = 1.800804e-04 

886 elif num_procs == 5: 

887 HotRod_tol = 9.329361e-05 

888 else: # 1 process 

889 HotRod_tol = 1.347949e-06 

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

891 maxiter = 4 

892 elif problem.__name__ == "run_Lorenz": 

893 if num_procs == 5: 

894 HotRod_tol = 9.539348e-06 

895 elif num_procs == 4: 

896 HotRod_tol = 3.201e-6 

897 else: 

898 HotRod_tol = 7.720589e-07 

899 maxiter = 6 

900 elif problem.__name__ == "run_Schroedinger": 

901 if num_procs == 5: 

902 HotRod_tol = 2.497697e-06 

903 elif num_procs == 4: 

904 HotRod_tol = 1.910405e-06 

905 else: 

906 HotRod_tol = 4.476790e-07 

907 maxiter = 6 

908 elif problem.__name__ == "run_quench": 

909 if num_procs == 5: 

910 HotRod_tol = 1.017534e-03 

911 elif num_procs == 4: 

912 HotRod_tol = 1.017534e-03 

913 else: 

914 HotRod_tol = 5.198620e-04 

915 maxiter = 6 

916 elif problem.__name__ == 'run_AC': 

917 HotRod_tol = 9.564437e-06 

918 maxiter = 6 

919 else: 

920 raise NotImplementedError( 

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

922 strategy' 

923 ) 

924 

925 no_storage = False # num_procs > 1 

926 

927 custom_description = { 

928 'convergence_controllers': { 

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

930 BasicRestartingNonMPI: { 

931 'max_restarts': 2, 

932 'crash_after_max_restarts': False, 

933 'restart_from_first_step': True, 

934 }, 

935 }, 

936 'step_params': {'maxiter': maxiter}, 

937 'level_params': {}, 

938 } 

939 if problem.__name__ == "run_AC": 

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

941 return merge_descriptions(base_params, custom_description) 

942 

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

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

945 if problem.__name__ == "run_quench": 

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

947 return desc 

948 

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

950 """ 

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

952 

953 Args: 

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

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

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

957 num_procs (int): Number of processes 

958 

959 Returns: 

960 The reference value 

961 """ 

962 if problem.__name__ == "run_vdp": 

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

964 return 15230 

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

966 return 4.3956128381594795e-06 

967 

968 raise NotImplementedError('The reference value you are looking for is not implemented for this strategy!') 

969 

970 

971class AdaptivityCollocationStrategy(InexactBaseStrategy): 

972 ''' 

973 Adaptivity based on collocation as a resilience strategy 

974 ''' 

975 

976 def __init__(self, **kwargs): 

977 ''' 

978 Initialization routine 

979 ''' 

980 kwargs = { 

981 'skip_residual_computation': 'most', 

982 **kwargs, 

983 } 

984 

985 from pySDC.implementations.convergence_controller_classes.adaptivity import AdaptivityCollocation 

986 

987 self.restol = None 

988 super().__init__(**kwargs) 

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

990 self.marker = '*' 

991 self.name = 'adaptivity_coll' 

992 self.bar_plot_x_label = 'adaptivity collocation' 

993 self.precision_parameter = 'e_tol' 

994 self.adaptive_coll_params = {} 

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

996 

997 def get_custom_description(self, problem, num_procs): 

998 ''' 

999 Routine to get a custom description that adds adaptivity 

1000 

1001 Args: 

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

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

1004 

1005 Returns: 

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

1007 ''' 

1008 from pySDC.implementations.convergence_controller_classes.adaptivity import AdaptivityCollocation 

1009 

1010 custom_description = {} 

1011 

1012 dt_max = np.inf 

1013 dt_min = 1e-5 

1014 

1015 if problem.__name__ == "run_piline": 

1016 e_tol = 1e-7 

1017 dt_min = 1e-2 

1018 elif problem.__name__ == "run_vdp": 

1019 e_tol = 2e-5 

1020 dt_min = 1e-3 

1021 elif problem.__name__ == "run_Lorenz": 

1022 e_tol = 2e-5 

1023 dt_min = 1e-3 

1024 elif problem.__name__ == "run_Schroedinger": 

1025 e_tol = 4e-6 

1026 dt_min = 1e-3 

1027 elif problem.__name__ == "run_quench": 

1028 e_tol = 1e-5 

1029 dt_min = 1e-3 

1030 dt_max = 1e2 

1031 elif problem.__name__ == "run_AC": 

1032 e_tol = 1e-4 

1033 else: 

1034 raise NotImplementedError( 

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

1036 strategy' 

1037 ) 

1038 

1039 custom_description['convergence_controllers'] = { 

1040 AdaptivityCollocation: { 

1041 'e_tol': e_tol, 

1042 'dt_min': dt_min, 

1043 'dt_max': dt_max, 

1044 'adaptive_coll_params': self.adaptive_coll_params, 

1045 'restol_rel': 1e-2, 

1046 } 

1047 } 

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

1049 

1050 

1051class AdaptivityCollocationTypeStrategy(AdaptivityCollocationStrategy): 

1052 def __init__(self, **kwargs): 

1053 super().__init__(**kwargs) 

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

1055 self.marker = '.' 

1056 self.adaptive_coll_params = { 

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

1058 'do_coll_update': [False, True], 

1059 } 

1060 

1061 @property 

1062 def label(self): 

1063 return 'adaptivity type' 

1064 

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

1066 """ 

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

1068 

1069 Args: 

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

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

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

1073 num_procs (int): Number of processes 

1074 

1075 Returns: 

1076 The reference value 

1077 """ 

1078 if problem.__name__ == "run_vdp": 

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

1080 return 1954 

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

1082 return 1.1341277847964903e-06 

1083 

1084 raise NotImplementedError('The reference value you are looking for is not implemented for this strategy!') 

1085 

1086 

1087class AdaptivityCollocationRefinementStrategy(AdaptivityCollocationStrategy): 

1088 def __init__(self, **kwargs): 

1089 super().__init__(**kwargs) 

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

1091 self.marker = '^' 

1092 self.adaptive_coll_params = { 

1093 'num_nodes': [2, 3], 

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

1095 'do_coll_update': [True, False], 

1096 } 

1097 

1098 @property 

1099 def label(self): 

1100 return 'adaptivity refinement' 

1101 

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

1103 """ 

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

1105 

1106 Args: 

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

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

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

1110 num_procs (int): Number of processes 

1111 

1112 Returns: 

1113 The reference value 

1114 """ 

1115 if problem.__name__ == "run_vdp": 

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

1117 return 1785 

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

1119 return 1.2448609458259874e-06 

1120 

1121 raise NotImplementedError('The reference value you are looking for is not implemented for this strategy!') 

1122 

1123 

1124class AdaptivityCollocationDerefinementStrategy(AdaptivityCollocationStrategy): 

1125 def __init__(self, **kwargs): 

1126 super().__init__(**kwargs) 

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

1128 self.marker = '^' 

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

1130 

1131 @property 

1132 def label(self): 

1133 return 'adaptivity de-refinement' 

1134 

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

1136 """ 

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

1138 

1139 Args: 

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

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

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

1143 num_procs (int): Number of processes 

1144 

1145 Returns: 

1146 The reference value 

1147 """ 

1148 if problem.__name__ == "run_vdp": 

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

1150 return 2508 

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

1152 return 3.452255036812124e-05 

1153 

1154 raise NotImplementedError('The reference value you are looking for is not implemented for this strategy!') 

1155 

1156 

1157class DIRKStrategy(AdaptivityStrategy): 

1158 ''' 

1159 DIRK4(3) 

1160 ''' 

1161 

1162 def __init__(self, **kwargs): 

1163 ''' 

1164 Initialization routine 

1165 ''' 

1166 from pySDC.implementations.convergence_controller_classes.adaptivity import AdaptivityRK 

1167 

1168 super().__init__(**kwargs) 

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

1170 self.marker = '^' 

1171 self.name = 'DIRK' 

1172 self.bar_plot_x_label = 'DIRK4(3)' 

1173 self.precision_parameter = 'e_tol' 

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

1175 self.max_steps = 1e5 

1176 

1177 @property 

1178 def label(self): 

1179 return 'DIRK4(3)' 

1180 

1181 def get_custom_description(self, problem, num_procs): 

1182 ''' 

1183 Routine to get a custom description that adds adaptivity 

1184 

1185 Args: 

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

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

1188 

1189 Returns: 

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

1191 ''' 

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

1193 from pySDC.implementations.convergence_controller_classes.basic_restarting import BasicRestarting 

1194 from pySDC.implementations.sweeper_classes.Runge_Kutta import DIRK43 

1195 

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

1197 

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

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

1200 adaptivity_description.pop('sweeper_params', None) 

1201 

1202 rk_params = { 

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

1204 'sweeper_class': DIRK43, 

1205 'convergence_controllers': { 

1206 AdaptivityRK: {'e_tol': e_tol}, 

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

1208 'max_restarts': 49, 

1209 'crash_after_max_restarts': False, 

1210 }, 

1211 }, 

1212 } 

1213 

1214 custom_description = merge_descriptions(adaptivity_description, rk_params) 

1215 

1216 return custom_description 

1217 

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

1219 """ 

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

1221 

1222 Args: 

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

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

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

1226 num_procs (int): Number of processes 

1227 

1228 Returns: 

1229 The reference value 

1230 """ 

1231 if problem.__name__ == "run_vdp": 

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

1233 return 2168 

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

1235 return 0.00024166437265116247 

1236 

1237 raise NotImplementedError('The reference value you are looking for is not implemented for this strategy!') 

1238 

1239 def get_random_params(self, problem, num_procs): 

1240 ''' 

1241 Routine to get parameters for the randomization of faults 

1242 

1243 Args: 

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

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

1246 

1247 Returns: 

1248 dict: Randomization parameters 

1249 ''' 

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

1251 rnd_params['iteration'] = 1 

1252 rnd_params['min_node'] = 5 

1253 

1254 return rnd_params 

1255 

1256 

1257class ARKStrategy(AdaptivityStrategy): 

1258 ''' 

1259 ARK5(4) 

1260 ''' 

1261 

1262 def __init__(self, **kwargs): 

1263 ''' 

1264 Initialization routine 

1265 ''' 

1266 from pySDC.implementations.convergence_controller_classes.adaptivity import AdaptivityRK 

1267 

1268 super().__init__(**kwargs) 

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

1270 self.marker = 'P' 

1271 self.name = 'ARK' 

1272 self.bar_plot_x_label = 'ARK5(4)' 

1273 self.precision_parameter = 'e_tol' 

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

1275 self.max_steps = 1e5 

1276 

1277 @property 

1278 def label(self): 

1279 return 'ARK5(4)' 

1280 

1281 def get_custom_description(self, problem, num_procs): 

1282 ''' 

1283 Routine to get a custom description that adds adaptivity 

1284 

1285 Args: 

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

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

1288 

1289 Returns: 

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

1291 ''' 

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

1293 from pySDC.implementations.convergence_controller_classes.basic_restarting import BasicRestarting 

1294 from pySDC.implementations.sweeper_classes.Runge_Kutta import ARK548L2SA 

1295 

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

1297 

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

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

1300 adaptivity_description.pop('sweeper_params', None) 

1301 

1302 rk_params = { 

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

1304 'sweeper_class': ARK548L2SA, 

1305 'convergence_controllers': { 

1306 AdaptivityRK: {'e_tol': e_tol}, 

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

1308 'max_restarts': 49, 

1309 'crash_after_max_restarts': False, 

1310 }, 

1311 }, 

1312 } 

1313 

1314 custom_description = merge_descriptions(adaptivity_description, rk_params) 

1315 

1316 return custom_description 

1317 

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

1319 """ 

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

1321 

1322 Args: 

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

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

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

1326 num_procs (int): Number of processes 

1327 

1328 Returns: 

1329 The reference value 

1330 """ 

1331 if problem.__name__ == "run_Schroedinger": 

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

1333 return 0 

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

1335 return 3.1786601531890356e-08 

1336 

1337 raise NotImplementedError('The reference value you are looking for is not implemented for this strategy!') 

1338 

1339 

1340class ESDIRKStrategy(AdaptivityStrategy): 

1341 ''' 

1342 ESDIRK5(3) 

1343 ''' 

1344 

1345 def __init__(self, **kwargs): 

1346 ''' 

1347 Initialization routine 

1348 ''' 

1349 from pySDC.implementations.convergence_controller_classes.adaptivity import AdaptivityRK 

1350 

1351 super().__init__(**kwargs) 

1352 self.color = 'violet' 

1353 self.marker = '^' 

1354 self.name = 'ESDIRK' 

1355 self.bar_plot_x_label = 'ESDIRK5(3)' 

1356 self.precision_parameter = 'e_tol' 

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

1358 self.max_steps = 1e5 

1359 

1360 @property 

1361 def label(self): 

1362 return 'ESDIRK5(3)' 

1363 

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

1365 desc = {} 

1366 if problem.__name__ == 'run_Schroedinger': 

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

1368 return desc 

1369 

1370 def get_custom_description(self, problem, num_procs): 

1371 ''' 

1372 Routine to get a custom description that adds adaptivity 

1373 

1374 Args: 

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

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

1377 

1378 Returns: 

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

1380 ''' 

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

1382 from pySDC.implementations.convergence_controller_classes.basic_restarting import BasicRestarting 

1383 from pySDC.implementations.sweeper_classes.Runge_Kutta import ESDIRK53 

1384 

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

1386 

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

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

1389 adaptivity_description.pop('sweeper_params', None) 

1390 

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

1392 

1393 rk_params = { 

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

1395 'sweeper_class': ESDIRK53, 

1396 'convergence_controllers': { 

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

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

1399 'max_restarts': 49, 

1400 'crash_after_max_restarts': False, 

1401 }, 

1402 }, 

1403 } 

1404 

1405 custom_description = merge_descriptions(adaptivity_description, rk_params) 

1406 

1407 return custom_description 

1408 

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

1410 """ 

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

1412 

1413 Args: 

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

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

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

1417 num_procs (int): Number of processes 

1418 

1419 Returns: 

1420 The reference value 

1421 """ 

1422 if problem.__name__ == "run_vdp": 

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

1424 return 1562 

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

1426 return 3.6982949243591356e-06 

1427 

1428 raise NotImplementedError('The reference value you are looking for is not implemented for this strategy!') 

1429 

1430 def get_random_params(self, problem, num_procs): 

1431 ''' 

1432 Routine to get parameters for the randomization of faults 

1433 

1434 Args: 

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

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

1437 

1438 Returns: 

1439 dict: Randomization parameters 

1440 ''' 

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

1442 rnd_params['iteration'] = 1 

1443 rnd_params['min_node'] = 6 

1444 

1445 return rnd_params 

1446 

1447 

1448class ERKStrategy(DIRKStrategy): 

1449 """ 

1450 Explicit embedded RK using Cash-Karp's method 

1451 """ 

1452 

1453 def __init__(self, **kwargs): 

1454 ''' 

1455 Initialization routine 

1456 ''' 

1457 super().__init__(**kwargs) 

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

1459 self.marker = 'x' 

1460 self.name = 'ERK' 

1461 self.bar_plot_x_label = 'ERK5(4)' 

1462 

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

1464 desc = {} 

1465 if problem.__name__ == 'run_Schroedinger': 

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

1467 

1468 return desc 

1469 

1470 @property 

1471 def label(self): 

1472 return 'CP5(4)' 

1473 

1474 def get_random_params(self, problem, num_procs): 

1475 ''' 

1476 Routine to get parameters for the randomization of faults 

1477 

1478 Args: 

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

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

1481 

1482 Returns: 

1483 dict: Randomization parameters 

1484 ''' 

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

1486 rnd_params['min_node'] = 7 

1487 

1488 return rnd_params 

1489 

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

1491 from pySDC.implementations.sweeper_classes.Runge_Kutta import Cash_Karp 

1492 

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

1494 desc['sweeper_class'] = Cash_Karp 

1495 

1496 if problem.__name__ == "run_AC": 

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

1498 return desc 

1499 

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

1501 """ 

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

1503 

1504 Args: 

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

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

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

1508 num_procs (int): Number of processes 

1509 

1510 Returns: 

1511 The reference value 

1512 """ 

1513 if problem.__name__ == "run_vdp": 

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

1515 return 0 

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

1517 return 2.0606132165701396e-05 

1518 

1519 raise NotImplementedError('The reference value you are looking for is not implemented for this strategy!') 

1520 

1521 

1522class DoubleAdaptivityStrategy(AdaptivityStrategy): 

1523 ''' 

1524 Adaptivity based both on embedded estimate and on residual 

1525 ''' 

1526 

1527 def __init__(self, **kwargs): 

1528 ''' 

1529 Initialization routine 

1530 ''' 

1531 from pySDC.implementations.convergence_controller_classes.adaptivity import Adaptivity 

1532 

1533 kwargs['skip_residual_computation'] = 'all' 

1534 super().__init__(**kwargs) 

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

1536 self.marker = '^' 

1537 self.name = 'double_adaptivity' 

1538 self.bar_plot_x_label = 'double adaptivity' 

1539 self.precision_parameter = 'e_tol' 

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

1541 self.residual_e_tol_ratio = 1.0 

1542 self.residual_e_tol_abs = None 

1543 

1544 @property 

1545 def label(self): 

1546 return 'double adaptivity' 

1547 

1548 def get_custom_description(self, problem, num_procs): 

1549 ''' 

1550 Routine to get a custom description that adds adaptivity 

1551 

1552 Args: 

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

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

1555 

1556 Returns: 

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

1558 ''' 

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

1560 from pySDC.implementations.convergence_controller_classes.basic_restarting import BasicRestarting 

1561 

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

1563 

1564 if self.residual_e_tol_abs: 

1565 e_tol = self.residual_e_tol_abs 

1566 else: 

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

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

1569 'e_tol': e_tol, 

1570 'allowed_modifications': ['decrease'], 

1571 } 

1572 

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

1574 'max_restarts': 15 

1575 } 

1576 

1577 return custom_description 

1578 

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

1580 """ 

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

1582 

1583 Args: 

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

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

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

1587 num_procs (int): Number of processes 

1588 

1589 Returns: 

1590 The reference value 

1591 """ 

1592 if problem.__name__ == "run_vdp": 

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

1594 return 3825 

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

1596 return 1.3370376368393444e-05 

1597 

1598 raise NotImplementedError('The reference value you are looking for is not implemented for this strategy!') 

1599 

1600 

1601class AdaptivityAvoidRestartsStrategy(AdaptivityStrategy): 

1602 """ 

1603 Adaptivity with the avoid restarts option 

1604 """ 

1605 

1606 @property 

1607 def label(self): 

1608 return 'adaptivity (avoid restarts)' 

1609 

1610 def get_custom_description(self, problem, num_procs): 

1611 ''' 

1612 Routine to get a custom description that adds adaptivity 

1613 

1614 Args: 

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

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

1617 

1618 Returns: 

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

1620 ''' 

1621 from pySDC.implementations.convergence_controller_classes.adaptivity import Adaptivity 

1622 from pySDC.implementations.convergence_controller_classes.basic_restarting import BasicRestarting 

1623 

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

1625 

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

1627 

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

1629 'max_restarts': 15 

1630 } 

1631 

1632 return custom_description 

1633 

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

1635 """ 

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

1637 

1638 Args: 

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

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

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

1642 num_procs (int): Number of processes 

1643 

1644 Returns: 

1645 The reference value 

1646 """ 

1647 if problem.__name__ == "run_vdp": 

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

1649 return 2955 

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

1651 return 5.274015506540053e-07 

1652 

1653 raise NotImplementedError('The reference value you are looking for is not implemented for this strategy!') 

1654 

1655 

1656class AdaptivityInterpolationStrategy(AdaptivityStrategy): 

1657 """ 

1658 Adaptivity with interpolation between restarts 

1659 """ 

1660 

1661 @property 

1662 def label(self): 

1663 return 'adaptivity+interpolation' 

1664 

1665 def get_custom_description(self, problem, num_procs): 

1666 ''' 

1667 Routine to get a custom description that adds adaptivity 

1668 

1669 Args: 

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

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

1672 

1673 Returns: 

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

1675 ''' 

1676 from pySDC.implementations.convergence_controller_classes.adaptivity import Adaptivity 

1677 from pySDC.implementations.convergence_controller_classes.interpolate_between_restarts import ( 

1678 InterpolateBetweenRestarts, 

1679 ) 

1680 from pySDC.implementations.convergence_controller_classes.basic_restarting import BasicRestarting 

1681 

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

1683 

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

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

1686 

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

1688 'max_restarts': 15 

1689 } 

1690 

1691 return custom_description 

1692 

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

1694 """ 

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

1696 

1697 Args: 

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

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

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

1701 num_procs (int): Number of processes 

1702 

1703 Returns: 

1704 The reference value 

1705 """ 

1706 if problem.__name__ == "run_vdp": 

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

1708 return 6659 

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

1710 return 2.9780002756552015e-06 

1711 

1712 raise NotImplementedError('The reference value you are looking for is not implemented for this strategy!') 

1713 

1714 

1715class AdaptivityExtrapolationWithinQStrategy(InexactBaseStrategy): 

1716 ''' 

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

1718 ''' 

1719 

1720 def __init__(self, **kwargs): 

1721 ''' 

1722 Initialization routine 

1723 ''' 

1724 from pySDC.implementations.convergence_controller_classes.adaptivity import AdaptivityExtrapolationWithinQ 

1725 

1726 self.restol = None 

1727 super().__init__(**kwargs) 

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

1729 self.marker = '*' 

1730 self.name = 'adaptivity_extraQ' 

1731 self.bar_plot_x_label = 'adaptivity Q' 

1732 self.precision_parameter = 'e_tol' 

1733 self.adaptive_coll_params = {} 

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

1735 

1736 def get_custom_description(self, problem, num_procs): 

1737 ''' 

1738 Routine to get a custom description that adds adaptivity 

1739 

1740 Args: 

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

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

1743 

1744 Returns: 

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

1746 ''' 

1747 from pySDC.implementations.convergence_controller_classes.adaptivity import AdaptivityExtrapolationWithinQ 

1748 

1749 custom_description = {} 

1750 

1751 dt_max = np.inf 

1752 dt_min = 1e-5 

1753 

1754 if problem.__name__ == "run_vdp": 

1755 e_tol = 2e-5 

1756 dt_min = 1e-3 

1757 elif problem.__name__ == "run_piline": 

1758 e_tol = 1e-7 

1759 dt_min = 1e-2 

1760 elif problem.__name__ == "run_Lorenz": 

1761 e_tol = 2e-5 

1762 dt_min = 1e-3 

1763 elif problem.__name__ == "run_Schroedinger": 

1764 e_tol = 4e-6 

1765 dt_min = 1e-3 

1766 elif problem.__name__ == "run_quench": 

1767 e_tol = 1e-5 

1768 dt_min = 1e-3 

1769 dt_max = 1e2 

1770 elif problem.__name__ == "run_AC": 

1771 e_tol = 1e-4 

1772 else: 

1773 raise NotImplementedError( 

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

1775 strategy' 

1776 ) 

1777 

1778 custom_description['convergence_controllers'] = { 

1779 AdaptivityExtrapolationWithinQ: { 

1780 'e_tol': e_tol, 

1781 'dt_min': dt_min, 

1782 'dt_max': dt_max, 

1783 'restol_rel': 1e-2, 

1784 'restart_at_maxiter': True, 

1785 } 

1786 } 

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

1788 

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

1790 """ 

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

1792 

1793 Args: 

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

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

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

1797 num_procs (int): Number of processes 

1798 

1799 Returns: 

1800 The reference value 

1801 """ 

1802 if problem.__name__ == "run_vdp": 

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

1804 return 2426 

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

1806 return 1.7293825160247245e-06 

1807 

1808 raise NotImplementedError('The reference value you are looking for is not implemented for this strategy!') 

1809 

1810 

1811class AdaptivityPolynomialError(InexactBaseStrategy): 

1812 ''' 

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

1814 ''' 

1815 

1816 def __init__(self, interpolate_between_restarts=False, use_restol_rel=True, **kwargs): 

1817 ''' 

1818 Initialization routine 

1819 ''' 

1820 from pySDC.implementations.convergence_controller_classes.adaptivity import AdaptivityPolynomialError 

1821 

1822 self.restol = None 

1823 super().__init__(**kwargs) 

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

1825 self.marker = '+' 

1826 self.name = 'adaptivity-inter' 

1827 self.bar_plot_x_label = 'adaptivity Q' 

1828 self.precision_parameter = 'e_tol' 

1829 self.adaptive_coll_params = {} 

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

1831 self.interpolate_between_restarts = interpolate_between_restarts 

1832 self.use_restol_rel = use_restol_rel 

1833 

1834 def get_custom_description(self, problem, num_procs): 

1835 ''' 

1836 Routine to get a custom description that adds adaptivity 

1837 

1838 Args: 

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

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

1841 

1842 Returns: 

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

1844 ''' 

1845 from pySDC.implementations.convergence_controller_classes.adaptivity import AdaptivityPolynomialError 

1846 from pySDC.implementations.convergence_controller_classes.step_size_limiter import StepSizeLimiter 

1847 

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

1849 custom_description = {} 

1850 

1851 dt_max = np.inf 

1852 max_slope = 4.0 

1853 restol_rel = 1e-4 

1854 restol_min = 1e-12 

1855 level_params = {} 

1856 

1857 if problem.__name__ == "run_vdp": 

1858 e_tol = 6e-4 

1859 level_params['dt'] = 0.1 

1860 elif problem.__name__ == "run_piline": 

1861 e_tol = 1e-7 

1862 elif problem.__name__ == "run_Lorenz": 

1863 e_tol = 7e-4 

1864 elif problem.__name__ == "run_Schroedinger": 

1865 e_tol = 3e-5 

1866 elif problem.__name__ == "run_quench": 

1867 e_tol = 1e-7 

1868 level_params['dt'] = 50.0 

1869 restol_min = 1e-11 

1870 restol_rel = 1e-1 

1871 elif problem.__name__ == "run_AC": 

1872 e_tol = 1e-4 

1873 dt_max = 0.8 * base_params['problem_params']['eps'] ** 2 

1874 else: 

1875 raise NotImplementedError( 

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

1877 strategy' 

1878 ) 

1879 

1880 custom_description['convergence_controllers'] = { 

1881 AdaptivityPolynomialError: { 

1882 'e_tol': e_tol, 

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

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

1885 'restart_at_maxiter': True, 

1886 'factor_if_not_converged': max_slope, 

1887 'interpolate_between_restarts': self.interpolate_between_restarts, 

1888 }, 

1889 StepSizeLimiter: { 

1890 'dt_max': dt_max, 

1891 'dt_slope_max': max_slope, 

1892 }, 

1893 } 

1894 custom_description['level_params'] = level_params 

1895 return merge_descriptions(base_params, custom_description) 

1896 

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

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

1899 if problem.__name__ == "run_quench": 

1900 from pySDC.implementations.convergence_controller_classes.adaptivity import AdaptivityPolynomialError 

1901 

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

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

1904 elif problem.__name__ == "run_AC": 

1905 from pySDC.implementations.convergence_controller_classes.adaptivity import AdaptivityPolynomialError 

1906 

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

1908 return desc 

1909 

1910 def get_random_params(self, problem, num_procs): 

1911 ''' 

1912 Routine to get parameters for the randomization of faults 

1913 

1914 Args: 

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

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

1917 

1918 Returns: 

1919 dict: Randomization parameters 

1920 ''' 

1921 

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

1923 if problem.__name__ == "run_quench": 

1924 rnd_params['iteration'] = 1 

1925 elif problem.__name__ == 'run_Lorenz': 

1926 rnd_params['iteration'] = 4 

1927 return rnd_params 

1928 

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

1930 """ 

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

1932 

1933 Args: 

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

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

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

1937 num_procs (int): Number of processes 

1938 

1939 Returns: 

1940 The reference value 

1941 """ 

1942 if problem.__name__ == "run_vdp": 

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

1944 return 2819 

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

1946 return 1.8147451097405565e-07 

1947 

1948 raise NotImplementedError('The reference value you are looking for is not implemented for this strategy!') 

1949 

1950 @property 

1951 def label(self): 

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