Part C: Using pySDC’s frontend#
Finally, the user-friendliest interface pySDC has to offer: a controller does the whole iteration logic of
Part B for us. We use controller_nonMPI, the default controller. It needs no mpi4py, and
depending on the description it can do SDC, multilevel SDC, multistep SDC and PFASST (more on this in the next
steps).
The description is the same as in Part B. New are the controller parameters, here used to also write the log to a file.
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
from pySDC.implementations.controller_classes.controller_nonMPI import controller_nonMPI
from pySDC.implementations.problem_classes.HeatEquation_ND_FD import heatNd_forced
from pySDC.implementations.sweeper_classes.imex_1st_order import imex_1st_order
# initialize level parameters
level_params = {'restol': 1e-10, 'dt': 0.1}
# initialize sweeper parameters
sweeper_params = {'quad_type': 'RADAU-RIGHT', 'num_nodes': 3}
# initialize problem parameters
problem_params = {
'nu': 0.1, # diffusion coefficient
'freq': 4, # frequency for the test value
'nvars': 1023, # number of degrees of freedom
'bc': 'dirichlet-zero', # boundary conditions
}
# initialize step parameters
step_params = {'maxiter': 20}
# initialize controller parameters
controller_params = {
'log_to_file': True,
'fname': 'data/step_2_C_out.txt',
}
# Fill description dictionary for easy hierarchy creation
description = {
'problem_class': heatNd_forced,
'problem_params': problem_params,
'sweeper_class': imex_1st_order,
'sweeper_params': sweeper_params,
'level_params': level_params,
'step_params': step_params,
}
Path("data").mkdir(parents=True, exist_ok=True)
Instantiating the controller#
When the controller is created, it prints the full setup. Everything marked with --> was set by us, -> marks
what follows from that, and the rest are defaults. This is the place to check what a run actually used. Set the
controller parameter dump_setup to False to switch it off.
# instantiate the controller
controller = controller_nonMPI(num_procs=1, controller_params=controller_params, description=description)
controller - INFO: Welcome to the one and only, really very astonishing and 87.3% bug free
_____ _____ _____
/ ____| __ \ / ____|
_ __ _ _| (___ | | | | |
| '_ \| | | |\___ \| | | | |
| |_) | |_| |____) | |__| | |____
| .__/ \__, |_____/|_____/ \_____|
| | __/ |
|_| |___/
controller - INFO: Setup overview (--> user-defined, -> dependency) -- BEGIN
controller - INFO: ----------------------------------------------------------------------------------------------------
Controller: <class 'pySDC.implementations.controller_classes.controller_nonMPI.controller_nonMPI'>
all_to_done = False
dump_setup = True
--> fname = data/step_2_C_out.txt
hook_class = [<class 'pySDC.core.default_hook.DefaultHooks'>, <class 'pySDC.core.timings.CPUTimings'>]
--> log_to_file = True
logger_level = 20
mssdc_jac = True
predict_type = None
use_iteration_estimator = False
Step: <class 'pySDC.core.step.Step'>
--> maxiter = 20
Number of steps: None
Level: <class 'pySDC.core.level.Level'>
Level 0
--> dt = 0.1
dt_initial = 0.1
nsweeps = 1
residual_type = full_abs
--> restol = 1e-10
--> Problem: <class 'pySDC.implementations.problem_classes.HeatEquation_ND_FD.heatNd_forced'>
--> bc = dirichlet-zero
--> freq = (4,)
liniter = 10000
lintol = 1e-12
--> nu = 0.1
--> nvars = (1023,)
order = 2
sigma = 0.06
solver_type = direct
stencil_type = center
-> Data type u: <class 'pySDC.implementations.datatype_classes.mesh.mesh'>
-> Data type f: <class 'pySDC.implementations.datatype_classes.mesh.imex_mesh'>
--> Sweeper: <class 'pySDC.implementations.sweeper_classes.imex_1st_order.imex_1st_order'>
QE = EE
QI = IE
do_coll_update = False
initial_guess = spread
--> num_nodes = 3
--> quad_type = RADAU-RIGHT
skip_residual_computation = ()
-> Collocation: <class 'pySDC.core.collocation.CollBase'>
Active convergence controllers:
| # | order | convergence controller
----+----+-------+---------------------------------------------------------------------------------------
| 0 | 95 | BasicRestartingNonMPI
-> | 1 | 100 | SpreadStepSizesBlockwiseNonMPI
| 2 | 200 | CheckConvergence
controller - INFO: ----------------------------------------------------------------------------------------------------
controller - INFO: Setup overview (--> user-defined, -> dependency) -- END
Running#
We ask for the time interval \([0.1, 0.3]\), which with dt = 0.1 means two time steps. The initial value comes
from the problem class, which the controller has created inside its step, just like in Part A.
# set time parameters
t0 = 0.1
Tend = 0.3 # note that we are requesting 2 time steps here (dt is 0.1)
# get initial values on finest level
P = controller.MS[0].levels[0].prob
uinit = P.u_exact(t0)
# call main function to get things done...
uend, stats = controller.run(u0=uinit, t0=t0, Tend=Tend)
# compute exact solution and compare
uex = P.u_exact(Tend)
err = abs(uex - uend)
print(f'Error after SDC iterations: {err:8.6e}')
hooks - INFO: Process 0 on time 0.100000 at stage IT_FINE: Level: 0 -- Iteration: 1 -- Sweep: 1 -- residual: 4.11190756e-03
hooks - INFO: Process 0 on time 0.100000 at stage IT_FINE: Level: 0 -- Iteration: 2 -- Sweep: 1 -- residual: 6.68442665e-04
hooks - INFO: Process 0 on time 0.100000 at stage IT_FINE: Level: 0 -- Iteration: 3 -- Sweep: 1 -- residual: 8.80377582e-05
hooks - INFO: Process 0 on time 0.100000 at stage IT_FINE: Level: 0 -- Iteration: 4 -- Sweep: 1 -- residual: 1.21707909e-05
hooks - INFO: Process 0 on time 0.100000 at stage IT_FINE: Level: 0 -- Iteration: 5 -- Sweep: 1 -- residual: 1.38271970e-06
hooks - INFO: Process 0 on time 0.100000 at stage IT_FINE: Level: 0 -- Iteration: 6 -- Sweep: 1 -- residual: 6.36446134e-07
hooks - INFO: Process 0 on time 0.100000 at stage IT_FINE: Level: 0 -- Iteration: 7 -- Sweep: 1 -- residual: 1.68953336e-07
hooks - INFO: Process 0 on time 0.100000 at stage IT_FINE: Level: 0 -- Iteration: 8 -- Sweep: 1 -- residual: 3.52588866e-08
hooks - INFO: Process 0 on time 0.100000 at stage IT_FINE: Level: 0 -- Iteration: 9 -- Sweep: 1 -- residual: 6.07224706e-09
hooks - INFO: Process 0 on time 0.100000 at stage IT_FINE: Level: 0 -- Iteration: 10 -- Sweep: 1 -- residual: 8.27159898e-10
hooks - INFO: Process 0 on time 0.100000 at stage IT_FINE: Level: 0 -- Iteration: 11 -- Sweep: 1 -- residual: 1.19139256e-10
hooks - INFO: Process 0 on time 0.100000 at stage IT_FINE: Level: 0 -- Iteration: 12 -- Sweep: 1 -- residual: 1.44547065e-11
hooks - INFO: Process 0 on time 0.200000 at stage IT_FINE: Level: 0 -- Iteration: 1 -- Sweep: 1 -- residual: 6.69984764e-03
hooks - INFO: Process 0 on time 0.200000 at stage IT_FINE: Level: 0 -- Iteration: 2 -- Sweep: 1 -- residual: 1.05518433e-03
hooks - INFO: Process 0 on time 0.200000 at stage IT_FINE: Level: 0 -- Iteration: 3 -- Sweep: 1 -- residual: 1.40642622e-04
hooks - INFO: Process 0 on time 0.200000 at stage IT_FINE: Level: 0 -- Iteration: 4 -- Sweep: 1 -- residual: 1.85982064e-05
hooks - INFO: Process 0 on time 0.200000 at stage IT_FINE: Level: 0 -- Iteration: 5 -- Sweep: 1 -- residual: 2.79216822e-06
hooks - INFO: Process 0 on time 0.200000 at stage IT_FINE: Level: 0 -- Iteration: 6 -- Sweep: 1 -- residual: 1.12278626e-06
hooks - INFO: Process 0 on time 0.200000 at stage IT_FINE: Level: 0 -- Iteration: 7 -- Sweep: 1 -- residual: 2.85495413e-07
hooks - INFO: Process 0 on time 0.200000 at stage IT_FINE: Level: 0 -- Iteration: 8 -- Sweep: 1 -- residual: 5.78934357e-08
hooks - INFO: Process 0 on time 0.200000 at stage IT_FINE: Level: 0 -- Iteration: 9 -- Sweep: 1 -- residual: 9.68346997e-09
hooks - INFO: Process 0 on time 0.200000 at stage IT_FINE: Level: 0 -- Iteration: 10 -- Sweep: 1 -- residual: 1.26328381e-09
hooks - INFO: Process 0 on time 0.200000 at stage IT_FINE: Level: 0 -- Iteration: 11 -- Sweep: 1 -- residual: 1.83275934e-10
hooks - INFO: Process 0 on time 0.200000 at stage IT_FINE: Level: 0 -- Iteration: 12 -- Sweep: 1 -- residual: 1.91464310e-11
hooks - INFO: Finished run after 8.66e-02s
Error after SDC iterations: 1.166689e-05
That is the whole program a user writes: a description, some pre- and some post-processing. The controller also wrote its log to the file we named; here are its last lines:
print(''.join(Path(controller_params['fname']).read_text().splitlines(True)[-3:]))
2026-09-29 12:50:58,420 - hooks - default_hook - post_sweep - 22 - INFO: Process 0 on time 0.200000 at stage IT_FINE: Level: 0 -- Iteration: 11 -- Sweep: 1 -- residual: 1.83275934e-10
2026-09-29 12:50:58,423 - hooks - default_hook - post_sweep - 22 - INFO: Process 0 on time 0.200000 at stage IT_FINE: Level: 0 -- Iteration: 12 -- Sweep: 1 -- residual: 1.91464310e-11
2026-09-29 12:50:58,425 - hooks - timings - post_run - 287 - INFO: Finished run after 8.66e-02s
Important things to note
The description is all a user needs to steer pySDC; most of the logic and the data structures stay hidden. This part is the prototype for working with pySDC.
Unlike in Part B, we have no direct access to the residuals or the iteration counts yet: they are in
stats, which Step 3 is about.
The check the tests run:
assert err <= 2e-5, f"ERROR: controller doing IMEX SDC iteration did not reduce the error enough, got {err}"