Coverage for pySDC/tutorial/step_2/C_using_pySDCs_frontend.py: 100%

35 statements  

« prev     ^ index     » next       coverage.py v7.17.0, created at 2026-11-02 12:30 +0000

1# --- 

2# jupyter: 

3# jupytext: 

4# formats: py:percent 

5# kernelspec: 

6# display_name: Python 3 

7# name: python3 

8# --- 

9 

10# %% [markdown] 

11# # Part C: Using pySDC's frontend 

12# 

13# Finally, the user-friendliest interface pySDC has to offer: a **controller** does the whole iteration logic of 

14# [Part B](B_my_first_sweeper) for us. We use `controller_nonMPI`, the default controller. It needs no `mpi4py`, and 

15# depending on the description it can do SDC, multilevel SDC, multistep SDC and PFASST (more on this in the next 

16# steps). 

17# 

18# The description is the same as in Part B. New are the **controller parameters**, here used to also write the 

19# log to a file. 

20 

21# %% 

22from pathlib import Path 

23 

24import matplotlib.pyplot as plt 

25import numpy as np 

26 

27from pySDC.implementations.controller_classes.controller_nonMPI import controller_nonMPI 

28from pySDC.implementations.problem_classes.HeatEquation_ND_FD import heatNd_forced 

29from pySDC.implementations.sweeper_classes.imex_1st_order import imex_1st_order 

30 

31# initialize level parameters 

32level_params = {'restol': 1e-10, 'dt': 0.1} 

33 

34# initialize sweeper parameters 

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

36 

37# initialize problem parameters 

38problem_params = { 

39 'nu': 0.1, # diffusion coefficient 

40 'freq': 4, # frequency for the test value 

41 'nvars': 1023, # number of degrees of freedom 

42 'bc': 'dirichlet-zero', # boundary conditions 

43} 

44 

45# initialize step parameters 

46step_params = {'maxiter': 20} 

47 

48# initialize controller parameters 

49controller_params = { 

50 'log_to_file': True, 

51 'fname': 'data/step_2_C_out.txt', 

52} 

53 

54# Fill description dictionary for easy hierarchy creation 

55description = { 

56 'problem_class': heatNd_forced, 

57 'problem_params': problem_params, 

58 'sweeper_class': imex_1st_order, 

59 'sweeper_params': sweeper_params, 

60 'level_params': level_params, 

61 'step_params': step_params, 

62} 

63 

64Path("data").mkdir(parents=True, exist_ok=True) 

65 

66# %% [markdown] 

67# ## Instantiating the controller 

68# 

69# When the controller is created, it prints the full setup. Everything marked with `-->` was set by us, `->` marks 

70# what follows from that, and the rest are defaults. This is the place to check what a run actually used. Set the 

71# controller parameter `dump_setup` to `False` to switch it off. 

72 

73# %% tags=["scroll-output"] 

74# instantiate the controller 

75controller = controller_nonMPI(num_procs=1, controller_params=controller_params, description=description) 

76 

77# %% [markdown] 

78# ## Running 

79# 

80# We ask for the time interval $[0.1, 0.3]$, which with `dt = 0.1` means two time steps. The initial value comes 

81# from the problem class, which the controller has created inside its step, just like in Part A. 

82 

83# %% 

84# set time parameters 

85t0 = 0.1 

86Tend = 0.3 # note that we are requesting 2 time steps here (dt is 0.1) 

87 

88# get initial values on finest level 

89P = controller.MS[0].levels[0].prob 

90uinit = P.u_exact(t0) 

91 

92# call main function to get things done... 

93uend, stats = controller.run(u0=uinit, t0=t0, Tend=Tend) 

94 

95# compute exact solution and compare 

96uex = P.u_exact(Tend) 

97err = abs(uex - uend) 

98print(f'Error after SDC iterations: {err:8.6e}') 

99 

100# %% [markdown] 

101# That is the whole program a user writes: a description, some pre- and some post-processing. The controller also 

102# wrote its log to the file we named; here are its last lines: 

103 

104# %% 

105print(''.join(Path(controller_params['fname']).read_text().splitlines(True)[-3:])) 

106 

107# %% tags=["hide-input"] 

108x = np.array([(i + 1) * P.dx for i in range(P.nvars[0])]) 

109fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 3)) 

110ax1.plot(x, uinit, label=f'$t = {t0}$') 

111ax1.plot(x, uend, label=f'$t = {Tend}$, SDC') 

112ax1.plot(x[::40], uex[::40], 'k.', label=f'$t = {Tend}$, exact') 

113ax1.set_xlabel('$x$') 

114ax1.legend(frameon=False, fontsize=8) 

115ax2.plot(x, np.abs(uend - uex)) 

116ax2.set_xlabel('$x$') 

117ax2.set_title('error at $t = 0.3$', fontsize=10) 

118fig.tight_layout() 

119 

120# %% [markdown] 

121# :::{admonition} Important things to note 

122# - The description is all a user needs to steer pySDC; most of the logic and the data structures stay hidden. 

123# This part is the prototype for working with pySDC. 

124# - Unlike in Part B, we have no direct access to the residuals or the iteration counts yet: they are in `stats`, 

125# which [Step 3](../step_3) is about. 

126# ::: 

127# 

128# The check the tests run: 

129 

130# %% 

131assert err <= 2e-5, f"ERROR: controller doing IMEX SDC iteration did not reduce the error enough, got {err}"