Coverage for pySDC/projects/Resilience/work_precision.py: 0%

310 statements  

« prev     ^ index     » next       coverage.py v7.5.0, created at 2024-04-29 09:02 +0000

1from mpi4py import MPI 

2import numpy as np 

3import matplotlib.pyplot as plt 

4import pickle 

5import logging 

6from time import perf_counter 

7import copy 

8 

9from pySDC.projects.Resilience.strategies import merge_descriptions 

10from pySDC.projects.Resilience.Lorenz import run_Lorenz 

11from pySDC.projects.Resilience.vdp import run_vdp 

12from pySDC.projects.Resilience.Schroedinger import run_Schroedinger 

13from pySDC.projects.Resilience.quench import run_quench 

14from pySDC.projects.Resilience.AC import run_AC 

15 

16from pySDC.helpers.stats_helper import get_sorted, filter_stats 

17from pySDC.helpers.plot_helper import setup_mpl, figsize_by_journal 

18 

19setup_mpl(reset=True) 

20LOGGER_LEVEL = 25 

21LOG_TO_FILE = False 

22 

23logging.getLogger('matplotlib.texmanager').setLevel(90) 

24 

25MAPPINGS = { 

26 'e_global': ('e_global_post_run', max, False), 

27 'e_global_rel': ('e_global_rel_post_run', max, False), 

28 't': ('timing_run', max, False), 

29 # 'e_local_max': ('e_local_post_step', max, False), 

30 'k_SDC': ('k', sum, None), 

31 'k_SDC_no_restart': ('k', sum, False), 

32 'k_Newton': ('work_newton', sum, None), 

33 'k_linear': ('work_linear', sum, None), 

34 'k_Newton_no_restart': ('work_newton', sum, False), 

35 'k_rhs': ('work_rhs', sum, None), 

36 'num_steps': ('dt', len, None), 

37 'restart': ('restart', sum, None), 

38 'dt_mean': ('dt', np.mean, False), 

39 'dt_max': ('dt', max, False), 

40 'dt_min': ('dt', min, False), 

41 'dt_sigma': ('dt', np.std, False), 

42 'e_embedded_max': ('error_embedded_estimate', max, False), 

43 'u0_increment_max': ('u0_increment', max, None), 

44 'u0_increment_mean': ('u0_increment', np.mean, None), 

45 'u0_increment_max_no_restart': ('u0_increment', max, False), 

46 'u0_increment_mean_no_restart': ('u0_increment', np.mean, False), 

47} 

48 

49logger = logging.getLogger('WorkPrecision') 

50logger.setLevel(LOGGER_LEVEL) 

51 

52 

53def get_forbidden_combinations(problem, strategy, **kwargs): 

54 """ 

55 Check if the combination of strategy and problem is forbidden 

56 

57 Args: 

58 problem (function): A problem to run 

59 strategy (Strategy): SDC strategy 

60 """ 

61 if strategy.name == 'ERK': 

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

63 return True 

64 

65 return False 

66 

67 

68def single_run( 

69 problem, 

70 strategy, 

71 data, 

72 custom_description, 

73 num_procs=1, 

74 comm_world=None, 

75 problem_args=None, 

76 hooks=None, 

77 Tend=None, 

78 num_procs_sweeper=1, 

79): 

80 """ 

81 Make a single run of a particular problem with a certain strategy. 

82 

83 Args: 

84 problem (function): A problem to run 

85 strategy (Strategy): SDC strategy 

86 data (dict): Put the results in here 

87 custom_description (dict): Overwrite presets 

88 num_procs (int): Number of processes for the time communicator 

89 comm_world (mpi4py.MPI.Intracomm): Communicator that is available for the entire script 

90 hooks (list): List of additional hooks 

91 num_procs_sweeper (int): Number of processes for the sweeper 

92 

93 Returns: 

94 dict: Stats generated by the run 

95 """ 

96 from pySDC.implementations.hooks.log_errors import LogGlobalErrorPostRun 

97 from pySDC.implementations.hooks.log_work import LogWork 

98 from pySDC.projects.Resilience.hook import LogData 

99 

100 hooks = hooks if hooks else [] 

101 

102 t_last = perf_counter() 

103 

104 num_procs_tot = num_procs * num_procs_sweeper 

105 comm = comm_world.Split(comm_world.rank < num_procs_tot) 

106 if comm_world.rank >= num_procs_tot: 

107 comm.Free() 

108 return None 

109 

110 # make communicators for time and sweepers 

111 comm_time = comm.Split(comm.rank // num_procs) 

112 comm_sweep = comm.Split(comm_time.rank) 

113 

114 strategy_description = strategy.get_custom_description(problem, num_procs) 

115 description = merge_descriptions(strategy_description, custom_description) 

116 if comm_sweep.size > 1: 

117 description['sweeper_params']['comm'] = comm_sweep 

118 

119 controller_params = { 

120 'logger_level': LOGGER_LEVEL, 

121 'log_to_file': LOG_TO_FILE, 

122 'fname': 'out.txt', 

123 **strategy.get_controller_params(), 

124 } 

125 problem_args = {} if problem_args is None else problem_args 

126 

127 stats, controller, crash = problem( 

128 custom_description=description, 

129 Tend=strategy.get_Tend(problem, num_procs) if Tend is None else Tend, 

130 hook_class=[LogData, LogWork, LogGlobalErrorPostRun] + hooks, 

131 custom_controller_params=controller_params, 

132 use_MPI=True, 

133 comm=comm_time, 

134 **problem_args, 

135 ) 

136 

137 t_now = perf_counter() 

138 logger.debug(f'Finished run in {t_now - t_last:.2e} s') 

139 t_last = perf_counter() 

140 

141 # record all the metrics 

142 stats_all = filter_stats(stats, comm=comm_sweep) 

143 comm_sweep.Free() 

144 

145 for key, mapping in MAPPINGS.items(): 

146 if crash: 

147 data[key] += [np.nan] 

148 continue 

149 me = get_sorted(stats_all, comm=comm_time, type=mapping[0], recomputed=mapping[2]) 

150 if len(me) == 0: 

151 data[key] += [np.nan] 

152 else: 

153 data[key] += [mapping[1]([you[1] for you in me])] 

154 

155 t_now = perf_counter() 

156 logger.debug(f'Recorded all data after {t_now - t_last:.2e} s') 

157 t_last = perf_counter() 

158 

159 comm_time.Free() 

160 comm.Free() 

161 return stats 

162 

163 

164def get_parameter(dictionary, where): 

165 """ 

166 Get a parameter at a certain position in a dictionary of dictionaries. 

167 

168 Args: 

169 dictionary (dict): The dictionary 

170 where (list): The list of keys leading to the value you want 

171 

172 Returns: 

173 The value of the dictionary 

174 """ 

175 if len(where) == 1: 

176 return dictionary[where[0]] 

177 else: 

178 return get_parameter(dictionary[where[0]], where[1:]) 

179 

180 

181def set_parameter(dictionary, where, parameter): 

182 """ 

183 Set a parameter at a certain position in a dictionary of dictionaries 

184 

185 Args: 

186 dictionary (dict): The dictionary 

187 where (list): The list of keys leading to the value you want to set 

188 parameter: Whatever you want to set the parameter to 

189 

190 Returns: 

191 None 

192 """ 

193 if len(where) == 1: 

194 dictionary[where[0]] = parameter 

195 else: 

196 set_parameter(dictionary[where[0]], where[1:], parameter) 

197 

198 

199def get_path(problem, strategy, num_procs, handle='', base_path='data/work_precision', num_procs_sweeper=1, mode=''): 

200 """ 

201 Get the path to a certain data. 

202 

203 Args: 

204 problem (function): A problem to run 

205 strategy (Strategy): SDC strategy 

206 num_procs (int): Number of processes for the time communicator 

207 handle (str): The name of the configuration 

208 base_path (str): Some path where all the files are stored 

209 num_procs_sweeper (int): Number of processes for the sweeper 

210 mode (str): The mode this was generated for 

211 

212 Returns: 

213 str: The path to the data you are looking for 

214 """ 

215 return f'{base_path}/{problem.__name__}-{strategy.__class__.__name__}-{handle}{"-wp" if handle else "wp"}-{num_procs}-{num_procs_sweeper}procs-{mode}.pickle' 

216 

217 

218def record_work_precision( 

219 problem, 

220 strategy, 

221 num_procs=1, 

222 custom_description=None, 

223 handle='', 

224 runs=1, 

225 comm_world=None, 

226 problem_args=None, 

227 param_range=None, 

228 Tend=None, 

229 hooks=None, 

230 num_procs_sweeper=1, 

231 mode='', 

232): 

233 """ 

234 Run problem with strategy and record the cost parameters. 

235 

236 Args: 

237 problem (function): A problem to run 

238 strategy (Strategy): SDC strategy 

239 num_procs (int): Number of processes for the time communicator 

240 custom_description (dict): Overwrite presets 

241 handle (str): The name of the configuration 

242 runs (int): Number of runs you want to do 

243 comm_world (mpi4py.MPI.Intracomm): Communicator that is available for the entire script 

244 num_procs_sweeper (int): Number of processes for the sweeper 

245 

246 Returns: 

247 None 

248 """ 

249 if get_forbidden_combinations(problem, strategy): 

250 return None 

251 

252 data = {} 

253 

254 # prepare precision parameters 

255 param = strategy.precision_parameter 

256 description = merge_descriptions( 

257 strategy.get_custom_description(problem, num_procs), 

258 {} if custom_description is None else custom_description, 

259 ) 

260 if param == 'e_tol': 

261 power = 10.0 

262 set_parameter(description, strategy.precision_parameter_loc[:-1] + ['dt_min'], 0) 

263 exponents = [-3, -2, -1, 0, 1, 2, 3][::-1] 

264 if problem.__name__ == 'run_vdp': 

265 exponents = [-4, -3, -2, -1, 0, 1, 2] 

266 elif param == 'dt': 

267 power = 2.0 

268 exponents = [-1, 0, 1, 2, 3][::-1] 

269 elif param == 'restol': 

270 power = 10.0 

271 exponents = [-2, -1, 0, 1, 2, 3] 

272 if problem.__name__ == 'run_vdp': 

273 exponents = [-4, -3, -2, -1, 0, 1] 

274 else: 

275 raise NotImplementedError(f"I don't know how to get default value for parameter \"{param}\"") 

276 

277 where = strategy.precision_parameter_loc 

278 default = get_parameter(description, where) 

279 param_range = [default * power**i for i in exponents] if param_range is None else param_range 

280 

281 if problem.__name__ == 'run_quench': 

282 if param == 'restol': 

283 param_range = [1e-5, 1e-6, 1e-7, 1e-8, 1e-9] 

284 elif param == 'dt': 

285 param_range = [1.25, 2.5, 5.0, 10.0, 20.0][::-1] 

286 

287 # run multiple times with different parameters 

288 for i in range(len(param_range)): 

289 set_parameter(description, where, param_range[i]) 

290 

291 data[param_range[i]] = {key: [] for key in MAPPINGS.keys()} 

292 data[param_range[i]]['param'] = [param_range[i]] 

293 data[param_range[i]][param] = [param_range[i]] 

294 

295 description = merge_descriptions( 

296 descA=description, descB=strategy.get_description_for_tolerance(problem=problem, param=param_range[i]) 

297 ) 

298 for _j in range(runs): 

299 if comm_world.rank == 0: 

300 logger.log( 

301 24, 

302 f'Starting: {problem.__name__}: {strategy} {handle} {num_procs}-{num_procs_sweeper} procs, {param}={param_range[i]:.2e}', 

303 ) 

304 single_run( 

305 problem, 

306 strategy, 

307 data[param_range[i]], 

308 custom_description=description, 

309 comm_world=comm_world, 

310 problem_args=problem_args, 

311 num_procs=num_procs, 

312 hooks=hooks, 

313 Tend=Tend, 

314 num_procs_sweeper=num_procs_sweeper, 

315 ) 

316 

317 comm_world.Barrier() 

318 

319 if comm_world.rank == 0: 

320 if np.isfinite(data[param_range[i]]["k_linear"][-1]): 

321 k_type = "k_linear" 

322 elif np.isfinite(data[param_range[i]]["k_Newton"][-1]): 

323 k_type = 'k_Newton' 

324 else: 

325 k_type = "k_SDC" 

326 logger.log( 

327 25, 

328 f'{problem.__name__}: {strategy} {handle} {num_procs}-{num_procs_sweeper} procs, {param}={param_range[i]:.2e}: e={data[param_range[i]]["e_global"][-1]}, t={data[param_range[i]]["t"][-1]}, {k_type}={data[param_range[i]][k_type][-1]}', 

329 ) 

330 

331 if comm_world.rank == 0: 

332 import socket 

333 import time 

334 

335 data['meta'] = { 

336 'hostname': socket.gethostname(), 

337 'time': time.time, 

338 'runs': runs, 

339 } 

340 path = get_path(problem, strategy, num_procs, handle, num_procs_sweeper=num_procs_sweeper, mode=mode) 

341 with open(path, 'wb') as f: 

342 logger.debug(f'Dumping file \"{path}\"') 

343 pickle.dump(data, f) 

344 return data 

345 

346 

347def load(**kwargs): 

348 """ 

349 Load stored data. Arguments are passed on to `get_path` 

350 

351 Returns: 

352 dict: The data 

353 """ 

354 path = get_path(**kwargs) 

355 with open(path, 'rb') as f: 

356 logger.debug(f'Loading file \"{path}\"') 

357 data = pickle.load(f) 

358 return data 

359 

360 

361def extract_data(data, work_key, precision_key): 

362 """ 

363 Get the work and precision from a data object. 

364 

365 Args: 

366 data (dict): Data from work-precision measurements 

367 work_key (str): Name of variable on x-axis 

368 precision_key (str): Name of variable on y-axis 

369 

370 Returns: 

371 list: Work 

372 list: Precision 

373 """ 

374 keys = [key for key in data.keys() if key not in ['meta']] 

375 work = [np.nanmean(data[key][work_key]) for key in keys] 

376 precision = [np.nanmean(data[key][precision_key]) for key in keys] 

377 return work, precision 

378 

379 

380def get_order(work_key='e_global', precision_key='param', strategy=None, handle=None, **kwargs): 

381 data = load(**kwargs, strategy=strategy, handle=handle) 

382 work, precision = extract_data(data, work_key, precision_key) 

383 

384 order = [np.log(precision[i + 1] / precision[i]) / np.log(work[i + 1] / work[i]) for i in range(len(work) - 1)] 

385 

386 print(f'Order for {strategy} {handle}: {np.mean(order):.2f}') 

387 

388 

389def plot_work_precision( 

390 problem, 

391 strategy, 

392 num_procs, 

393 ax, 

394 work_key='k_SDC', 

395 precision_key='e_global', 

396 handle='', 

397 plotting_params=None, 

398 comm_world=None, 

399 num_procs_sweeper=1, 

400 mode='', 

401): # pragma: no cover 

402 """ 

403 Plot data from running a problem with a strategy. 

404 

405 Args: 

406 problem (function): A problem to run 

407 strategy (Strategy): SDC strategy 

408 num_procs (int): Number of processes for the time communicator 

409 ax (matplotlib.pyplot.axes): Somewhere to plot 

410 work_key (str): The key in the recorded data you want on the x-axis 

411 precision_key (str): The key in the recorded data you want on the y-axis 

412 handle (str): The name of the configuration 

413 plotting_params (dict): Will be passed when plotting 

414 comm_world (mpi4py.MPI.Intracomm): Communicator that is available for the entire script 

415 num_procs_sweeper (int): Number of processes for the sweeper 

416 mode (str): The of the configurations you want to retrieve 

417 

418 Returns: 

419 None 

420 """ 

421 if comm_world.rank > 0 or get_forbidden_combinations(problem, strategy): 

422 return None 

423 

424 data = load( 

425 problem=problem, 

426 strategy=strategy, 

427 num_procs=num_procs, 

428 handle=handle, 

429 num_procs_sweeper=num_procs_sweeper, 

430 mode=mode, 

431 ) 

432 

433 work, precision = extract_data(data, work_key, precision_key) 

434 keys = [key for key in data.keys() if key not in ['meta']] 

435 

436 for key in [work_key, precision_key]: 

437 rel_variance = [np.std(data[me][key]) / max([np.nanmean(data[me][key]), 1.0]) for me in keys] 

438 if not all(me < 1e-1 or not np.isfinite(me) for me in rel_variance): 

439 logger.warning( 

440 f"Variance in \"{key}\" for {get_path(problem, strategy, num_procs, handle, num_procs_sweeper=num_procs_sweeper, mode=mode)} too large! Got {rel_variance}" 

441 ) 

442 

443 style = merge_descriptions( 

444 {**strategy.style, 'label': f'{strategy.style["label"]}{f" {handle}" if handle else ""}'}, 

445 plotting_params if plotting_params else {}, 

446 ) 

447 

448 ax.loglog(work, precision, **style) 

449 

450 # get_order( 

451 # problem=problem, 

452 # strategy=strategy, 

453 # num_procs=num_procs, 

454 # handle=handle, 

455 # work_key=work_key, 

456 # precision_key=precision_key, 

457 # ) 

458 

459 if 't' in [work_key, precision_key]: 

460 meta = data.get('meta', {}) 

461 

462 if meta.get('hostname', None) in ['thomas-work']: 

463 ax.text(0.1, 0.1, "Laptop timings!", transform=ax.transAxes) 

464 if meta.get('runs', None) == 1: 

465 ax.text(0.1, 0.2, "No sampling!", transform=ax.transAxes) 

466 

467 

468def plot_parallel_efficiency_diagonalSDC( 

469 ax, work_key, precision_key, num_procs_sweeper, num_procs=1, **kwargs 

470): # pragma: no cover 

471 serial_data = load( 

472 num_procs=num_procs, 

473 num_procs_sweeper=1, 

474 **kwargs, 

475 ) 

476 parallel_data = load( 

477 num_procs=num_procs, 

478 num_procs_sweeper=num_procs_sweeper, 

479 **kwargs, 

480 ) 

481 serial_work, serial_precision = extract_data(serial_data, work_key, precision_key) 

482 parallel_work, parallel_precision = extract_data(parallel_data, work_key, precision_key) 

483 # assert np.allclose(serial_precision, parallel_precision) 

484 

485 serial_work = np.asarray(serial_work) 

486 parallel_work = np.asarray(parallel_work) 

487 

488 # ax.loglog(serial_work, serial_precision) 

489 # ax.loglog(parallel_work, parallel_precision) 

490 

491 speedup = serial_work / parallel_work 

492 parallel_efficiency = np.median(speedup) / num_procs_sweeper 

493 ax.plot(serial_precision, speedup) 

494 ax.set_xscale('log') 

495 ax.set_ylabel('speedup') 

496 

497 if 't' in [work_key, precision_key]: 

498 meta = parallel_data.get('meta', {}) 

499 

500 if meta.get('hostname', None) in ['thomas-work']: 

501 ax.text(0.1, 0.1, "Laptop timings!", transform=ax.transAxes) 

502 if meta.get('runs', None) == 1: 

503 ax.text(0.1, 0.2, "No sampling!", transform=ax.transAxes) 

504 

505 return np.median(speedup), parallel_efficiency 

506 

507 

508def decorate_panel(ax, problem, work_key, precision_key, num_procs=1, title_only=False): # pragma: no cover 

509 """ 

510 Decorate a plot 

511 

512 Args: 

513 ax (matplotlib.pyplot.axes): Somewhere to plot 

514 problem (function): A problem to run 

515 work_key (str): The key in the recorded data you want on the x-axis 

516 precision_key (str): The key in the recorded data you want on the y-axis 

517 num_procs (int): Number of processes for the time communicator 

518 title_only (bool): Put only the title on top, or do the whole shebang 

519 

520 Returns: 

521 None 

522 """ 

523 labels = { 

524 'k_SDC': 'SDC iterations', 

525 'k_SDC_no_restart': 'SDC iterations (restarts excluded)', 

526 'k_Newton': 'Newton iterations', 

527 'k_Newton_no_restart': 'Newton iterations (restarts excluded)', 

528 'k_rhs': 'right hand side evaluations', 

529 't': 'wall clock time / s', 

530 'e_global': 'global error', 

531 'e_global_rel': 'relative global error', 

532 'e_local_max': 'max. local error', 

533 'restart': 'restarts', 

534 'dt_max': r'$\Delta t_\mathrm{max}$', 

535 'dt_min': r'$\Delta t_\mathrm{min}$', 

536 'dt_sigma': r'$\sigma(\Delta t)$', 

537 'dt_mean': r'$\bar{\Delta t}$', 

538 'param': 'parameter', 

539 'u0_increment_max': r'$\| \Delta u_0 \|_{\infty} $', 

540 'u0_increment_mean': r'$\bar{\Delta u_0}$', 

541 'u0_increment_max_no_restart': r'$\| \Delta u_0 \|_{\infty} $ (restarts excluded)', 

542 'u0_increment_mean_no_restart': r'$\bar{\Delta u_0}$ (restarts excluded)', 

543 'k_linear': 'Linear solver iterations', 

544 'speedup': 'Speedup', 

545 'nprocs': r'$N_\mathrm{procs}$', 

546 '': '', 

547 } 

548 

549 if not title_only: 

550 ax.set_xlabel(labels.get(work_key, 'work')) 

551 ax.set_ylabel(labels.get(precision_key, 'precision')) 

552 # ax.legend(frameon=False) 

553 

554 titles = { 

555 'run_vdp': 'Van der Pol', 

556 'run_Lorenz': 'Lorenz attractor', 

557 'run_Schroedinger': r'Schr\"odinger', 

558 'run_quench': 'Quench', 

559 'run_AC': 'Allen-Cahn', 

560 } 

561 ax.set_title(titles.get(problem.__name__, '')) 

562 

563 

564def execute_configurations( 

565 problem, 

566 configurations, 

567 work_key, 

568 precision_key, 

569 num_procs, 

570 ax, 

571 decorate, 

572 record, 

573 runs, 

574 comm_world, 

575 plotting, 

576 Tend=None, 

577 num_procs_sweeper=1, 

578 mode='', 

579): 

580 """ 

581 Run for multiple configurations. 

582 

583 Args: 

584 problem (function): A problem to run 

585 configurations (dict): The configurations you want to run with 

586 work_key (str): The key in the recorded data you want on the x-axis 

587 precision_key (str): The key in the recorded data you want on the y-axis 

588 num_procs (int): Number of processes for the time communicator 

589 ax (matplotlib.pyplot.axes): Somewhere to plot 

590 decorate (bool): Whether to decorate fully or only put the title 

591 record (bool): Whether to only plot or also record the data first 

592 runs (int): Number of runs you want to do 

593 comm_world (mpi4py.MPI.Intracomm): Communicator that is available for the entire script 

594 plotting (bool): Whether to plot something 

595 num_procs_sweeper (int): Number of processes for the sweeper 

596 mode (str): What you want to look at 

597 

598 Returns: 

599 None 

600 """ 

601 for _, config in configurations.items(): 

602 for strategy in config['strategies']: 

603 shared_args = { 

604 'problem': problem, 

605 'strategy': strategy, 

606 'handle': config.get('handle', ''), 

607 'num_procs': config.get('num_procs', num_procs), 

608 'num_procs_sweeper': config.get('num_procs_sweeper', num_procs_sweeper), 

609 } 

610 if record: 

611 logger.debug('Recording work precision') 

612 record_work_precision( 

613 **shared_args, 

614 custom_description=config.get('custom_description', {}), 

615 runs=runs, 

616 comm_world=comm_world, 

617 problem_args=config.get('problem_args', {}), 

618 param_range=config.get('param_range', None), 

619 hooks=config.get('hooks', None), 

620 Tend=Tend, 

621 mode=mode, 

622 ) 

623 if plotting and comm_world.rank == 0: 

624 logger.debug('Plotting') 

625 plot_work_precision( 

626 **shared_args, 

627 work_key=work_key, 

628 precision_key=precision_key, 

629 ax=ax, 

630 plotting_params=config.get('plotting_params', {}), 

631 comm_world=comm_world, 

632 mode=mode, 

633 ) 

634 

635 if comm_world.rank == 0: 

636 decorate_panel( 

637 ax=ax, 

638 problem=problem, 

639 work_key=work_key, 

640 precision_key=precision_key, 

641 num_procs=num_procs, 

642 title_only=not decorate, 

643 ) 

644 

645 

646def get_configs(mode, problem): 

647 """ 

648 Get configurations for work-precision plots. These are dictionaries containing strategies and handles and so on. 

649 

650 Args: 

651 mode (str): The of the configurations you want to retrieve 

652 problem (function): A problem to run 

653 

654 Returns: 

655 dict: Configurations 

656 """ 

657 configurations = {} 

658 if mode == 'regular': 

659 from pySDC.projects.Resilience.strategies import AdaptivityStrategy, BaseStrategy, IterateStrategy 

660 

661 handle = 'regular' 

662 configurations[0] = { 

663 'handle': handle, 

664 'strategies': [AdaptivityStrategy(useMPI=True), BaseStrategy(useMPI=True), IterateStrategy(useMPI=True)], 

665 } 

666 elif mode == 'step_size_limiting': 

667 from pySDC.implementations.convergence_controller_classes.step_size_limiter import StepSizeLimiter 

668 from pySDC.projects.Resilience.strategies import AdaptivityStrategy, ESDIRKStrategy 

669 

670 limits = [ 

671 25.0, 

672 50.0, 

673 ] 

674 colors = ['teal', 'magenta'] 

675 markers = ['v', 'x'] 

676 markersize = 9 

677 for i in range(len(limits)): 

678 configurations[i] = { 

679 'custom_description': {'convergence_controllers': {StepSizeLimiter: {'dt_max': limits[i]}}}, 

680 'handle': f'steplimiter{limits[i]:.0f}', 

681 'strategies': [AdaptivityStrategy(useMPI=True)], 

682 'plotting_params': { 

683 'color': colors[i], 

684 'marker': markers[i], 

685 'label': rf'$\Delta t \leq { {limits[i]:.0f}} $', 

686 # 'ls': '', 

687 'markersize': markersize, 

688 }, 

689 'num_procs': 1, 

690 } 

691 configurations[99] = { 

692 'custom_description': {}, 

693 'handle': 'no limits', 

694 'plotting_params': { 

695 'label': 'no limiter', 

696 # 'ls': '', 

697 'markersize': markersize, 

698 }, 

699 'strategies': [AdaptivityStrategy(useMPI=True)], 

700 'num_procs': 1, 

701 } 

702 elif mode == 'dynamic_restarts': 

703 """ 

704 Compare Block Gauss-Seidel SDC with restarting the first step in the block or the first step that exceeded the error threshold. 

705 """ 

706 from pySDC.projects.Resilience.strategies import AdaptivityStrategy, AdaptivityRestartFirstStep 

707 

708 desc = {} 

709 desc['sweeper_params'] = {'num_nodes': 3, 'QI': 'IE'} 

710 desc['step_params'] = {'maxiter': 5} 

711 

712 ls = { 

713 1: '-', 

714 2: '--', 

715 3: '-.', 

716 4: ':', 

717 5: ':', 

718 } 

719 

720 configurations[-1] = { 

721 'strategies': [AdaptivityStrategy(useMPI=True)], 

722 'num_procs': 1, 

723 } 

724 

725 for num_procs in [4, 2]: 

726 plotting_params = {'ls': ls[num_procs], 'label': f'adaptivity {num_procs} procs'} 

727 configurations[num_procs] = { 

728 'strategies': [AdaptivityStrategy(useMPI=True), AdaptivityRestartFirstStep(useMPI=True)], 

729 'custom_description': desc, 

730 'num_procs': num_procs, 

731 'plotting_params': plotting_params, 

732 } 

733 

734 elif mode == 'compare_strategies': 

735 """ 

736 Compare the different SDC strategies. 

737 """ 

738 from pySDC.projects.Resilience.strategies import ( 

739 AdaptivityStrategy, 

740 kAdaptivityStrategy, 

741 AdaptivityPolynomialError, 

742 BaseStrategy, 

743 ) 

744 

745 configurations[2] = { 

746 'strategies': [kAdaptivityStrategy(useMPI=True)], 

747 } 

748 configurations[1] = { 

749 'strategies': [AdaptivityPolynomialError(useMPI=True)], 

750 } 

751 configurations[0] = { 

752 'custom_description': { 

753 'step_params': {'maxiter': 5}, 

754 'sweeper_params': {'num_nodes': 3, 'quad_type': 'RADAU-RIGHT'}, 

755 }, 

756 'strategies': [ 

757 AdaptivityStrategy(useMPI=True), 

758 BaseStrategy(useMPI=True), 

759 ], 

760 } 

761 

762 elif mode == 'interpolate_between_restarts': 

763 """ 

764 Compare adaptivity with interpolation between restarts and without 

765 """ 

766 from pySDC.projects.Resilience.strategies import AdaptivityPolynomialError 

767 

768 i = 0 

769 for interpolate_between_restarts, handle, ls in zip( 

770 [True, False], ['Interpolation between restarts', 'regular'], ['--', '-'] 

771 ): 

772 configurations[i] = { 

773 'strategies': [ 

774 AdaptivityPolynomialError(interpolate_between_restarts=interpolate_between_restarts, useMPI=True) 

775 ], 

776 'plotting_params': {'ls': ls}, 

777 'handle': handle, 

778 } 

779 i += 1 

780 elif mode == 'diagonal_SDC': 

781 """ 

782 Run diagonal SDC with different number of nodes and ranks. You can use this to compute a speedup, but it's not strong scaling! 

783 """ 

784 from pySDC.projects.Resilience.strategies import AdaptivityPolynomialError 

785 

786 if problem.__name__ in ['run_Schroedinger']: 

787 from pySDC.implementations.sweeper_classes.imex_1st_order_MPI import imex_1st_order_MPI as parallel_sweeper 

788 else: 

789 from pySDC.implementations.sweeper_classes.generic_implicit_MPI import ( 

790 generic_implicit_MPI as parallel_sweeper, 

791 ) 

792 

793 for parallel in [False, True]: 

794 desc = {'sweeper_class': parallel_sweeper} if parallel else {} 

795 for num_nodes, ls in zip([3, 4, 2], ['-', '--', ':', '-.']): 

796 configurations[num_nodes + (99 if parallel else 0)] = { 

797 'custom_description': {**desc, 'sweeper_params': {'num_nodes': num_nodes}}, 

798 'strategies': [ 

799 AdaptivityPolynomialError(useMPI=True, newton_inexactness=True, linear_inexactness=True) 

800 ], 

801 'num_procs_sweeper': num_nodes if parallel else 1, 

802 'num_procs': 1, 

803 'handle': f'{num_nodes} nodes', 

804 'plotting_params': { 

805 'ls': ls, 

806 'label': f'{num_nodes} procs', 

807 # **{'color': 'grey' if parallel else None}, 

808 }, 

809 } 

810 

811 elif mode == 'parallel_efficiency': 

812 """ 

813 Compare parallel runs of the step size adaptive SDC 

814 """ 

815 from pySDC.projects.Resilience.strategies import AdaptivityStrategy, AdaptivityPolynomialError 

816 

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

818 from pySDC.implementations.sweeper_classes.imex_1st_order_MPI import imex_1st_order_MPI as parallel_sweeper 

819 else: 

820 from pySDC.implementations.sweeper_classes.generic_implicit_MPI import ( 

821 generic_implicit_MPI as parallel_sweeper, 

822 ) 

823 

824 desc = {} 

825 desc['sweeper_params'] = {'num_nodes': 3, 'QI': 'IE', 'QE': 'EE'} 

826 desc['step_params'] = {'maxiter': 5} 

827 

828 ls = { 

829 1: '-', 

830 2: '--', 

831 3: '-.', 

832 4: '--', 

833 5: ':', 

834 12: ':', 

835 } 

836 

837 for num_procs in [4, 1]: 

838 plotting_params = ( 

839 {'ls': ls[num_procs], 'label': fr'$\Delta t$-adaptivity $N$={num_procs}x1'} if num_procs > 1 else {} 

840 ) 

841 configurations[num_procs] = { 

842 'strategies': [AdaptivityStrategy(useMPI=True)], 

843 'custom_description': desc.copy(), 

844 'num_procs': num_procs, 

845 'plotting_params': plotting_params.copy(), 

846 } 

847 configurations[num_procs * 100 + 79] = { 

848 'custom_description': {'sweeper_class': parallel_sweeper}, 

849 'strategies': [ 

850 AdaptivityPolynomialError(useMPI=True, newton_inexactness=True, linear_inexactness=True) 

851 ], 

852 'num_procs_sweeper': 3, 

853 'num_procs': num_procs, 

854 'plotting_params': { 

855 'ls': ls.get(num_procs * 3, '-'), 

856 'label': rf'$\Delta t$-$k$-adaptivity $N$={num_procs}x3', 

857 }, 

858 } 

859 

860 configurations[num_procs * 200 + 79] = { 

861 'strategies': [AdaptivityPolynomialError(useMPI=True, newton_inexactness=True, linear_inexactness=True)], 

862 'num_procs': 1, 

863 } 

864 

865 elif mode[:13] == 'vdp_stiffness': 

866 """ 

867 Run van der Pol with different parameter for the nonlinear term, which controls the stiffness. 

868 """ 

869 from pySDC.projects.Resilience.strategies import AdaptivityStrategy, ERKStrategy, ESDIRKStrategy 

870 

871 mu = float(mode[14:]) 

872 

873 problem_desc = {'problem_params': {'mu': mu}} 

874 

875 desc = {} 

876 desc['sweeper_params'] = {'num_nodes': 3, 'QI': 'IE'} 

877 desc['step_params'] = {'maxiter': 5} 

878 desc['problem_params'] = problem_desc['problem_params'] 

879 

880 ls = { 

881 1: '-', 

882 2: '--', 

883 3: '-.', 

884 4: ':', 

885 5: ':', 

886 } 

887 

888 for num_procs in [5]: 

889 plotting_params = {'ls': ls[num_procs], 'label': f'GSSDC {num_procs} procs'} 

890 configurations[num_procs] = { 

891 'strategies': [AdaptivityStrategy(True)], 

892 'custom_description': desc, 

893 'num_procs': num_procs, 

894 'plotting_params': plotting_params, 

895 'handle': mode, 

896 } 

897 

898 configurations[1] = { 

899 'strategies': [AdaptivityStrategy(True)], 

900 'custom_description': desc, 

901 'num_procs': 1, 

902 'plotting_params': {'ls': ls[1], 'label': 'SDC'}, 

903 'handle': mode, 

904 } 

905 

906 configurations[2] = { 

907 'strategies': [ERKStrategy(useMPI=True)], 

908 'num_procs': 1, 

909 'handle': mode, 

910 'plotting_params': {'label': 'CP5(4)'}, 

911 'custom_description': problem_desc, 

912 } 

913 configurations[4] = { 

914 'strategies': [ESDIRKStrategy(useMPI=True)], 

915 'num_procs': 1, 

916 'handle': mode, 

917 'plotting_params': {'label': 'ESDIRK5(3)'}, 

918 'custom_description': problem_desc, 

919 } 

920 elif mode == 'inexactness': 

921 """ 

922 Compare inexact SDC to exact SDC 

923 """ 

924 from pySDC.projects.Resilience.strategies import ( 

925 AdaptivityPolynomialError, 

926 ) 

927 

928 if problem.__name__ in ['run_Schroedinger']: 

929 from pySDC.implementations.sweeper_classes.imex_1st_order_MPI import imex_1st_order_MPI as parallel_sweeper 

930 else: 

931 from pySDC.implementations.sweeper_classes.generic_implicit_MPI import ( 

932 generic_implicit_MPI as parallel_sweeper, 

933 ) 

934 

935 strategies = [ 

936 AdaptivityPolynomialError, 

937 ] 

938 

939 inexactness = { 

940 'newton_inexactness': True, 

941 'linear_inexactness': True, 

942 } 

943 no_inexactness = { 

944 'newton_inexactness': False, 

945 'linear_inexactness': False, 

946 'SDC_maxiter': 99, 

947 'use_restol_rel': False, 

948 } 

949 

950 configurations[1] = { 

951 'custom_description': {'sweeper_class': parallel_sweeper}, 

952 'strategies': [me(useMPI=True, **no_inexactness) for me in strategies], 

953 'num_procs_sweeper': 3, 

954 'handle': 'exact', 

955 'plotting_params': {'ls': '--'}, 

956 } 

957 configurations[0] = { 

958 'custom_description': {'sweeper_class': parallel_sweeper}, 

959 'strategies': [me(useMPI=True, **inexactness) for me in strategies], 

960 'handle': 'inexact', 

961 'num_procs_sweeper': 3, 

962 } 

963 elif mode == 'compare_adaptivity': 

964 """ 

965 Compare various modes of adaptivity 

966 """ 

967 # TODO: configurations not final! 

968 from pySDC.projects.Resilience.strategies import ( 

969 # AdaptivityCollocationTypeStrategy, 

970 # AdaptivityCollocationRefinementStrategy, 

971 AdaptivityStrategy, 

972 # AdaptivityExtrapolationWithinQStrategy, 

973 ESDIRKStrategy, 

974 ARKStrategy, 

975 AdaptivityPolynomialError, 

976 ) 

977 

978 if problem.__name__ in ['run_Schroedinger']: 

979 from pySDC.implementations.sweeper_classes.imex_1st_order_MPI import imex_1st_order_MPI as parallel_sweeper 

980 else: 

981 from pySDC.implementations.sweeper_classes.generic_implicit_MPI import ( 

982 generic_implicit_MPI as parallel_sweeper, 

983 ) 

984 

985 inexactness_params = { 

986 # 'double_adaptivity': True, 

987 'newton_inexactness': True, 

988 'linear_inexactness': True, 

989 } 

990 

991 strategies = [ 

992 AdaptivityPolynomialError, 

993 # AdaptivityCollocationTypeStrategy, 

994 # AdaptivityExtrapolationWithinQStrategy, 

995 ] 

996 

997 # restol = None 

998 # for strategy in strategies: 

999 # strategy.restol = restol 

1000 

1001 configurations[1] = { 

1002 'custom_description': {'sweeper_class': parallel_sweeper}, 

1003 'strategies': [me(useMPI=True, **inexactness_params) for me in strategies], 

1004 'handle': 'parallel', 

1005 'num_procs_sweeper': 3, 

1006 'plotting_params': {'ls': '-', 'label': '3 procs'}, 

1007 } 

1008 configurations[2] = { 

1009 'strategies': [me(useMPI=True, **inexactness_params) for me in strategies], 

1010 'plotting_params': {'ls': '--'}, 

1011 } 

1012 configurations[4] = { 

1013 'custom_description': {'step_params': {'maxiter': 5}}, 

1014 'strategies': [AdaptivityStrategy(useMPI=True)], 

1015 } 

1016 

1017 desc_RK = {} 

1018 configurations[-1] = { 

1019 'strategies': [ 

1020 ARKStrategy(useMPI=True) if problem.__name__ == 'run_Schroedinger' else ESDIRKStrategy(useMPI=True), 

1021 ], 

1022 'num_procs': 1, 

1023 'custom_description': desc_RK, 

1024 } 

1025 

1026 elif mode == 'preconditioners': 

1027 """ 

1028 Compare different preconditioners 

1029 """ 

1030 from pySDC.projects.Resilience.strategies import ( 

1031 AdaptivityStrategy, 

1032 IterateStrategy, 

1033 BaseStrategy, 

1034 ESDIRKStrategy, 

1035 ERKStrategy, 

1036 AdaptivityPolynomialError, 

1037 ) 

1038 

1039 inexacness = True 

1040 strategies = [ 

1041 AdaptivityPolynomialError( 

1042 useMPI=True, SDC_maxiter=29, newton_inexactness=inexacness, linear_inexactness=inexacness 

1043 ), 

1044 BaseStrategy(useMPI=True), 

1045 ] 

1046 

1047 desc = {} 

1048 desc['sweeper_params'] = { 

1049 'num_nodes': 3, 

1050 } 

1051 # desc['step_params'] = {'maxiter': 5} 

1052 

1053 precons = ['IE', 'LU'] 

1054 ls = ['-.', '--', '-', ':'] 

1055 for i in range(len(precons) + 1): 

1056 if i < len(precons): 

1057 desc['sweeper_params']['QI'] = precons[i] 

1058 handle = precons[i] 

1059 else: 

1060 handle = None 

1061 configurations[i] = { 

1062 'strategies': strategies, 

1063 'custom_description': copy.deepcopy(desc), 

1064 'handle': handle, 

1065 'plotting_params': {'ls': ls[i]}, 

1066 } 

1067 

1068 elif mode == 'RK_comp': 

1069 """ 

1070 Compare parallel adaptive SDC to Runge-Kutta 

1071 """ 

1072 from pySDC.projects.Resilience.strategies import ( 

1073 AdaptivityStrategy, 

1074 ERKStrategy, 

1075 ESDIRKStrategy, 

1076 ARKStrategy, 

1077 AdaptivityPolynomialError, 

1078 ) 

1079 

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

1081 from pySDC.implementations.sweeper_classes.imex_1st_order_MPI import imex_1st_order_MPI as parallel_sweeper 

1082 else: 

1083 from pySDC.implementations.sweeper_classes.generic_implicit_MPI import ( 

1084 generic_implicit_MPI as parallel_sweeper, 

1085 ) 

1086 

1087 desc = {} 

1088 desc['sweeper_params'] = {'num_nodes': 3, 'QI': 'IE', 'QE': "EE"} 

1089 desc['step_params'] = {'maxiter': 5} 

1090 

1091 desc_poly = {} 

1092 desc_poly['sweeper_class'] = parallel_sweeper 

1093 

1094 ls = { 

1095 1: '-', 

1096 2: '--', 

1097 3: '-.', 

1098 4: ':', 

1099 5: ':', 

1100 } 

1101 

1102 configurations[3] = { 

1103 'custom_description': desc_poly, 

1104 'strategies': [AdaptivityPolynomialError(useMPI=True)], 

1105 'num_procs': 1, 

1106 'num_procs_sweeper': 3, 

1107 'plotting_params': {'label': r'$\Delta t$-$k$-adaptivity $N$=1x3'}, 

1108 } 

1109 configurations[-1] = { 

1110 'strategies': [ 

1111 ERKStrategy(useMPI=True), 

1112 ( 

1113 ARKStrategy(useMPI=True) 

1114 if problem.__name__ in ['run_Schroedinger', 'run_AC'] 

1115 else ESDIRKStrategy(useMPI=True) 

1116 ), 

1117 ], 

1118 'num_procs': 1, 

1119 } 

1120 configurations[2] = { 

1121 'strategies': [AdaptivityStrategy(useMPI=True)], 

1122 'custom_description': desc, 

1123 'num_procs': 4, 

1124 'plotting_params': {'label': r'$\Delta t$-adaptivity $N$=4x1'}, 

1125 } 

1126 

1127 elif mode == 'RK_comp_high_order': 

1128 """ 

1129 Compare higher order SDC than we can get with RKM to RKM 

1130 """ 

1131 from pySDC.projects.Resilience.strategies import ( 

1132 AdaptivityStrategy, 

1133 ERKStrategy, 

1134 ESDIRKStrategy, 

1135 ARKStrategy, 

1136 AdaptivityPolynomialError, 

1137 ) 

1138 

1139 if problem.__name__ in ['run_Schroedinger']: 

1140 from pySDC.implementations.sweeper_classes.imex_1st_order_MPI import imex_1st_order_MPI as parallel_sweeper 

1141 else: 

1142 from pySDC.implementations.sweeper_classes.generic_implicit_MPI import ( 

1143 generic_implicit_MPI as parallel_sweeper, 

1144 ) 

1145 

1146 desc = {} 

1147 desc['sweeper_params'] = {'num_nodes': 4, 'QI': 'IE', 'QE': "EE"} 

1148 desc['step_params'] = {'maxiter': 7} 

1149 

1150 desc_poly = {} 

1151 desc_poly['sweeper_class'] = parallel_sweeper 

1152 desc_poly['sweeper_params'] = {'num_nodes': 4} 

1153 

1154 ls = { 

1155 1: '-', 

1156 2: '--', 

1157 3: '-.', 

1158 4: ':', 

1159 5: ':', 

1160 } 

1161 

1162 desc_RK = {} 

1163 if problem.__name__ in ['run_Schroedinger']: 

1164 desc_RK['problem_params'] = {'imex': True} 

1165 

1166 configurations[3] = { 

1167 'custom_description': desc_poly, 

1168 'strategies': [AdaptivityPolynomialError(useMPI=True)], 

1169 'num_procs': 1, 

1170 'num_procs_sweeper': 4, 

1171 } 

1172 configurations[-1] = { 

1173 'strategies': [ 

1174 ERKStrategy(useMPI=True), 

1175 ARKStrategy(useMPI=True) if problem.__name__ in ['run_Schroedinger'] else ESDIRKStrategy(useMPI=True), 

1176 ], 

1177 'num_procs': 1, 

1178 'custom_description': desc_RK, 

1179 } 

1180 

1181 configurations[2] = { 

1182 'strategies': [AdaptivityStrategy(useMPI=True)], 

1183 'custom_description': desc, 

1184 'num_procs': 4, 

1185 } 

1186 elif mode == 'avoid_restarts': 

1187 """ 

1188 Test how well avoiding restarts works. 

1189 """ 

1190 from pySDC.projects.Resilience.strategies import ( 

1191 AdaptivityStrategy, 

1192 AdaptivityAvoidRestartsStrategy, 

1193 AdaptivityPolynomialStrategy, 

1194 ) 

1195 

1196 desc = {'sweeper_params': {'QI': 'IE'}, 'step_params': {'maxiter': 3}} 

1197 param_range = [1e-3, 1e-5] 

1198 configurations[0] = { 

1199 'strategies': [AdaptivityPolynomialStrategy(useMPI=True)], 

1200 'plotting_params': {'ls': '--'}, 

1201 'custom_description': desc, 

1202 'param_range': param_range, 

1203 } 

1204 configurations[1] = { 

1205 'strategies': [AdaptivityAvoidRestartsStrategy(useMPI=True)], 

1206 'plotting_params': {'ls': '-.'}, 

1207 'custom_description': desc, 

1208 'param_range': param_range, 

1209 } 

1210 configurations[2] = { 

1211 'strategies': [AdaptivityStrategy(useMPI=True)], 

1212 'custom_description': desc, 

1213 'param_range': param_range, 

1214 } 

1215 else: 

1216 raise NotImplementedError(f'Don\'t know the mode "{mode}"!') 

1217 

1218 return configurations 

1219 

1220 

1221def get_fig(x=1, y=1, **kwargs): # pragma: no cover 

1222 """ 

1223 Get a figure to plot in. 

1224 

1225 Args: 

1226 x (int): How many panels in horizontal direction you want 

1227 y (int): How many panels in vertical direction you want 

1228 

1229 Returns: 

1230 matplotlib.pyplot.Figure 

1231 """ 

1232 width = 1.0 

1233 ratio = 1.0 if y == 2 else 0.5 

1234 keyword_arguments = { 

1235 'figsize': figsize_by_journal('Springer_Numerical_Algorithms', width, ratio), 

1236 'layout': 'constrained', 

1237 **kwargs, 

1238 } 

1239 return plt.subplots(y, x, **keyword_arguments) 

1240 

1241 

1242def save_fig( 

1243 fig, name, work_key, precision_key, legend=True, format='pdf', base_path='data', squares=True, ncols=None, **kwargs 

1244): # pragma: no cover 

1245 """ 

1246 Save a figure with a legend on the bottom. 

1247 

1248 Args: 

1249 fig (matplotlib.pyplot.Figure): Figure you want to save 

1250 name (str): Name of the plot to put in the path 

1251 work_key (str): The key in the recorded data you want on the x-axis 

1252 precision_key (str): The key in the recorded data you want on the y-axis 

1253 legend (bool): Put a legend or not 

1254 format (str): Format to store the figure with 

1255 base_path (str): Some path where all the files are stored 

1256 squares (bool): Adjust aspect ratio to squares if true 

1257 

1258 Returns: 

1259 None 

1260 """ 

1261 handles = [] 

1262 labels = [] 

1263 for ax in fig.get_axes(): 

1264 h, l = ax.get_legend_handles_labels() 

1265 handles += [h[i] for i in range(len(h)) if l[i] not in labels] 

1266 labels += [me for me in l if me not in labels] 

1267 if squares: 

1268 ax.set_box_aspect(1) 

1269 order = np.argsort([me[0] for me in labels]) 

1270 fig.legend( 

1271 [handles[i] for i in order], 

1272 [labels[i] for i in order], 

1273 loc='outside lower center', 

1274 ncols=ncols if ncols else 3 if len(handles) % 3 == 0 else 4, 

1275 frameon=False, 

1276 fancybox=True, 

1277 handlelength=2.2, 

1278 ) 

1279 

1280 path = f'{base_path}/wp-{name}-{work_key}-{precision_key}.{format}' 

1281 fig.savefig(path, bbox_inches='tight', **kwargs) 

1282 print(f'Stored figure \"{path}\"') 

1283 

1284 

1285def all_problems(mode='compare_strategies', plotting=True, base_path='data', **kwargs): # pragma: no cover 

1286 """ 

1287 Make a plot comparing various strategies for all problems. 

1288 

1289 Args: 

1290 work_key (str): The key in the recorded data you want on the x-axis 

1291 precision_key (str): The key in the recorded data you want on the y-axis 

1292 

1293 Returns: 

1294 None 

1295 """ 

1296 

1297 fig, axs = get_fig(2, 2) 

1298 

1299 shared_params = { 

1300 'work_key': 'k_SDC', 

1301 'precision_key': 'e_global', 

1302 'num_procs': 1, 

1303 'runs': 1, 

1304 'comm_world': MPI.COMM_WORLD, 

1305 'record': False, 

1306 'plotting': plotting, 

1307 **kwargs, 

1308 } 

1309 

1310 problems = [run_vdp, run_quench, run_Schroedinger, run_AC] 

1311 

1312 logger.log(26, f"Doing for all problems {mode}") 

1313 for i in range(len(problems)): 

1314 execute_configurations( 

1315 **shared_params, 

1316 problem=problems[i], 

1317 ax=axs.flatten()[i], 

1318 decorate=True, 

1319 configurations=get_configs(mode, problems[i]), 

1320 mode=mode, 

1321 ) 

1322 

1323 if plotting and shared_params['comm_world'].rank == 0: 

1324 save_fig( 

1325 fig=fig, 

1326 name=mode, 

1327 work_key=shared_params['work_key'], 

1328 precision_key=shared_params['precision_key'], 

1329 legend=True, 

1330 base_path=base_path, 

1331 ncols=3 if mode in ['parallel_efficiency'] else None, 

1332 ) 

1333 

1334 

1335def ODEs(mode='compare_strategies', plotting=True, base_path='data', **kwargs): # pragma: no cover 

1336 """ 

1337 Make a plot comparing various strategies for the two ODEs. 

1338 

1339 Args: 

1340 work_key (str): The key in the recorded data you want on the x-axis 

1341 precision_key (str): The key in the recorded data you want on the y-axis 

1342 

1343 Returns: 

1344 None 

1345 """ 

1346 

1347 fig, axs = get_fig(x=2, y=1) 

1348 

1349 shared_params = { 

1350 'work_key': 'k_SDC', 

1351 'precision_key': 'e_global', 

1352 'num_procs': 1, 

1353 'runs': 1, 

1354 'comm_world': MPI.COMM_WORLD, 

1355 'record': False, 

1356 'plotting': plotting, 

1357 **kwargs, 

1358 } 

1359 

1360 problems = [run_vdp, run_Lorenz] 

1361 

1362 for i in range(len(problems)): 

1363 execute_configurations( 

1364 **shared_params, 

1365 problem=problems[i], 

1366 ax=axs.flatten()[i], 

1367 decorate=i == 0, 

1368 configurations=get_configs(mode, problems[i]), 

1369 ) 

1370 

1371 if plotting and shared_params['comm_world'].rank == 0: 

1372 save_fig( 

1373 fig=fig, 

1374 name=f'ODEs-{mode}', 

1375 work_key=shared_params['work_key'], 

1376 precision_key=shared_params['precision_key'], 

1377 legend=True, 

1378 base_path=base_path, 

1379 ) 

1380 

1381 

1382def single_problem(mode, problem, plotting=True, base_path='data', **kwargs): # pragma: no cover 

1383 """ 

1384 Make a plot for a single problem 

1385 

1386 Args: 

1387 mode (str): What you want to look at 

1388 problem (function): A problem to run 

1389 """ 

1390 fig, ax = get_fig(1, 1, figsize=figsize_by_journal('Springer_Numerical_Algorithms', 1, 0.8)) 

1391 

1392 params = { 

1393 'work_key': 'k_SDC', 

1394 'precision_key': 'e_global', 

1395 'num_procs': 1, 

1396 'runs': 1, 

1397 'comm_world': MPI.COMM_WORLD, 

1398 'record': False, 

1399 'plotting': plotting, 

1400 **kwargs, 

1401 } 

1402 

1403 logger.log(26, f"Doing single problem {mode}") 

1404 execute_configurations( 

1405 **params, problem=problem, ax=ax, decorate=True, configurations=get_configs(mode, problem), mode=mode 

1406 ) 

1407 

1408 if plotting: 

1409 save_fig( 

1410 fig=fig, 

1411 name=f'{problem.__name__}-{mode}', 

1412 work_key=params['work_key'], 

1413 precision_key=params['precision_key'], 

1414 legend=False, 

1415 base_path=base_path, 

1416 ) 

1417 

1418 

1419def vdp_stiffness_plot(base_path='data', format='pdf', **kwargs): # pragma: no cover 

1420 fig, axs = get_fig(2, 2, sharex=True, sharey=True) 

1421 

1422 # mus = [0, 5, 10, 15] 

1423 mus = [0, 10, 20, 40] 

1424 

1425 for i in range(len(mus)): 

1426 params = { 

1427 'runs': 1, 

1428 'problem': run_vdp, 

1429 'record': False, 

1430 'work_key': 't', 

1431 'precision_key': 'e_global', 

1432 'comm_world': MPI.COMM_WORLD, 

1433 **kwargs, 

1434 } 

1435 params['num_procs'] = min(params['comm_world'].size, 5) 

1436 params['plotting'] = params['comm_world'].rank == 0 

1437 

1438 configurations = get_configs(mode=f'vdp_stiffness-{mus[i]}', problem=run_vdp) 

1439 execute_configurations(**params, ax=axs.flatten()[i], decorate=True, configurations=configurations, Tend=100) 

1440 axs.flatten()[i].set_title(rf'$\mu={ {mus[i]}} $') 

1441 

1442 fig.suptitle('Van der Pol') 

1443 if params['comm_world'].rank == 0: 

1444 save_fig( 

1445 fig=fig, 

1446 name='vdp-stiffness', 

1447 work_key=params['work_key'], 

1448 precision_key=params['precision_key'], 

1449 legend=False, 

1450 base_path=base_path, 

1451 format=format, 

1452 ) 

1453 

1454 

1455def aggregate_parallel_efficiency_plot(): # pragma: no cover 

1456 """ 

1457 Make a "weak scaling" plot for diagonal SDC 

1458 """ 

1459 from pySDC.projects.Resilience.strategies import AdaptivityPolynomialError 

1460 

1461 fig, axs = plt.subplots(2, 2) 

1462 

1463 _fig, _ax = plt.subplots(1, 1) 

1464 num_procs = 1 

1465 num_procs_sweeper = 2 

1466 problem = run_quench 

1467 

1468 num_procs_sweeper_list = [2, 3, 4] 

1469 

1470 for problem, ax in zip([run_vdp, run_Lorenz, run_quench], axs.flatten()): 

1471 speedup = [] 

1472 for num_procs_sweeper in num_procs_sweeper_list: 

1473 s, e = plot_parallel_efficiency_diagonalSDC( 

1474 ax=_ax, 

1475 work_key='t', 

1476 precision_key='e_global_rel', 

1477 num_procs=num_procs, 

1478 num_procs_sweeper=num_procs_sweeper, 

1479 problem=problem, 

1480 strategy=AdaptivityPolynomialError(), 

1481 mode='diagonal_SDC', 

1482 handle=f'{num_procs_sweeper} nodes', 

1483 ) 

1484 speedup += [s] 

1485 decorate_panel(ax, problem, work_key='nprocs', precision_key='') 

1486 

1487 ax.plot(num_procs_sweeper_list, speedup, label='speedup') 

1488 ax.plot( 

1489 num_procs_sweeper_list, 

1490 [speedup[i] / num_procs_sweeper_list[i] for i in range(len(speedup))], 

1491 label='parallel efficiency', 

1492 ) 

1493 

1494 fig.tight_layout() 

1495 save_fig(fig, 'parallel_efficiency', 'nprocs', 'speedup') 

1496 

1497 

1498if __name__ == "__main__": 

1499 comm_world = MPI.COMM_WORLD 

1500 

1501 # record = False 

1502 # for mode in [ 

1503 # 'compare_strategies', 

1504 # 'RK_comp', 

1505 # 'parallel_efficiency', 

1506 # ]: 

1507 # params = { 

1508 # 'mode': mode, 

1509 # 'runs': 5, 

1510 # 'plotting': comm_world.rank == 0, 

1511 # } 

1512 # params_single = { 

1513 # **params, 

1514 # 'problem': run_AC, 

1515 # } 

1516 # single_problem(**params_single, work_key='t', precision_key='e_global_rel', record=record) 

1517 

1518 all_params = { 

1519 'record': True, 

1520 'runs': 5, 

1521 'work_key': 't', 

1522 'precision_key': 'e_global_rel', 

1523 'plotting': comm_world.rank == 0, 

1524 } 

1525 

1526 for mode in [ 

1527 'RK_comp', 

1528 'parallel_efficiency', 

1529 'compare_strategies', 

1530 ]: 

1531 all_problems(**all_params, mode=mode) 

1532 comm_world.Barrier() 

1533 

1534 if comm_world.rank == 0: 

1535 plt.show()