Coverage for pySDC/projects/parallelSDC/AllenCahn_parallel.py: 100%
92 statements
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-25 20:28 +0000
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-25 20:28 +0000
1import numpy as np
2from mpi4py import MPI
4from pySDC.helpers.stats_helper import get_sorted
6from pySDC.implementations.controller_classes.controller_nonMPI import controller_nonMPI
7from pySDC.implementations.problem_classes.AllenCahn_2D_FD import allencahn_fullyimplicit
8from pySDC.implementations.sweeper_classes.generic_implicit import generic_implicit
9from pySDC.implementations.transfer_classes.TransferMesh_FFT2D import mesh_to_mesh_fft2d
10from pySDC.implementations.hooks.AllenCahn_monitor import AllenCahnMonitor
11from pySDC.implementations.transfer_classes.BaseTransferMPI import base_transfer_MPI
12from pySDC.implementations.sweeper_classes.generic_implicit_MPI import generic_implicit_MPI
14# http://www.personal.psu.edu/qud2/Res/Pre/dz09sisc.pdf
17def run_variant(variant=None):
18 """
19 Routine to run a particular variant
21 Args:
22 variant (str): string describing the variant
24 """
26 # initialize level parameters
27 level_params = dict()
28 level_params['restol'] = 1e-07
29 level_params['dt'] = 1e-03 / 2
30 level_params['nsweeps'] = 1
32 # initialize sweeper parameters
33 sweeper_params = dict()
34 sweeper_params['quad_type'] = 'RADAU-RIGHT'
35 sweeper_params['num_nodes'] = 3
36 sweeper_params['initial_guess'] = 'zero'
38 # This comes as read-in for the problem class
39 problem_params = dict()
41 problem_params['eps'] = 0.04
42 problem_params['newton_maxiter'] = 100
43 problem_params['newton_tol'] = 1e-08
44 problem_params['lin_tol'] = 1e-09
45 problem_params['lin_maxiter'] = 100
46 problem_params['radius'] = 0.25
48 # initialize step parameters
49 step_params = dict()
50 step_params['maxiter'] = 50
52 # initialize controller parameters
53 controller_params = dict()
54 controller_params['logger_level'] = 30
55 controller_params['hook_class'] = AllenCahnMonitor
57 # fill description dictionary for easy step instantiation
58 description = dict()
59 description['problem_class'] = allencahn_fullyimplicit
60 description['level_params'] = level_params # pass level parameters
61 description['step_params'] = step_params # pass step parameters
63 do_print = True
65 # add stuff based on variant
66 if variant == 'sl_serial':
67 maxmeaniters = 5.0
68 sweeper_params['QI'] = ['LU']
69 problem_params['nvars'] = [(128, 128)]
70 description['problem_params'] = problem_params # pass problem parameters
71 description['sweeper_class'] = generic_implicit # pass sweeper
72 description['sweeper_params'] = sweeper_params # pass sweeper parameters
73 elif variant == 'sl_parallel':
74 maxmeaniters = 5.125
75 assert MPI.COMM_WORLD.Get_size() == sweeper_params['num_nodes']
76 sweeper_params['QI'] = ['MIN3']
77 sweeper_params['comm'] = MPI.COMM_WORLD
78 problem_params['nvars'] = [(128, 128)]
79 description['problem_params'] = problem_params # pass problem parameters
80 description['sweeper_class'] = generic_implicit_MPI # pass sweeper
81 description['sweeper_params'] = sweeper_params # pass sweeper parameters
82 do_print = MPI.COMM_WORLD.Get_rank() == 0
83 elif variant == 'ml_serial':
84 maxmeaniters = 3.125
85 sweeper_params['QI'] = ['LU']
86 problem_params['nvars'] = [(128, 128), (64, 64)]
87 description['space_transfer_class'] = mesh_to_mesh_fft2d
88 description['problem_params'] = problem_params # pass problem parameters
89 description['sweeper_class'] = generic_implicit # pass sweeper
90 description['sweeper_params'] = sweeper_params # pass sweeper parameters
91 elif variant == 'ml_parallel':
92 assert MPI.COMM_WORLD.Get_size() == sweeper_params['num_nodes']
93 maxmeaniters = 4.25
94 sweeper_params['QI'] = ['MIN3']
95 sweeper_params['comm'] = MPI.COMM_WORLD
96 problem_params['nvars'] = [(128, 128), (64, 64)]
97 description['problem_params'] = problem_params # pass problem parameters
98 description['sweeper_class'] = generic_implicit_MPI # pass sweeper
99 description['sweeper_params'] = sweeper_params # pass sweeper parameters
100 description['space_transfer_class'] = mesh_to_mesh_fft2d
101 description['base_transfer_class'] = base_transfer_MPI
102 do_print = MPI.COMM_WORLD.Get_rank() == 0
103 else:
104 raise NotImplementedError('Wrong variant specified, got %s' % variant)
106 if do_print:
107 out = 'Working on %s variant...' % variant
108 print(out)
110 # setup parameters "in time"
111 t0 = 0
112 Tend = 0.004
114 # instantiate controller
115 controller = controller_nonMPI(num_procs=1, controller_params=controller_params, description=description)
117 # get initial values on finest level
118 P = controller.MS[0].levels[0].prob
119 uinit = P.u_exact(t0)
121 # call main function to get things done...
122 uend, stats = controller.run(u0=uinit, t0=t0, Tend=Tend)
124 # filter statistics by variant (number of iterations)
125 iter_counts = get_sorted(stats, type='niter', sortby='time')
127 # compute and print statistics
128 niters = np.array([item[1] for item in iter_counts])
130 if do_print:
131 out = ' Mean number of iterations: %4.2f' % np.mean(niters)
132 assert np.mean(niters) <= maxmeaniters, 'ERROR: number of iterations is too high, got %s instead of %s' % (
133 np.mean(niters),
134 maxmeaniters,
135 )
136 print(out)
137 out = ' Range of values for number of iterations: %2i ' % np.ptp(niters)
138 print(out)
139 out = ' Position of max/min number of iterations: %2i -- %2i' % (
140 int(np.argmax(niters)),
141 int(np.argmin(niters)),
142 )
143 print(out)
144 out = ' Std and var for number of iterations: %4.2f -- %4.2f' % (float(np.std(niters)), float(np.var(niters)))
145 print(out)
147 newton_iters = P.work_counters['newton'].niter
148 lin_iters = P.work_counters['linear'].niter
149 print(' Iteration count (nonlinear/linear): %i / %i' % (newton_iters, lin_iters))
150 print(
151 ' Mean Iteration count per call: %4.2f / %4.2f'
152 % (newton_iters / max(P.newton_ncalls, 1), lin_iters / max(P.lin_ncalls, 1))
153 )
155 timing = get_sorted(stats, type='timing_run', sortby='time')
157 print('Time to solution: %6.4f sec.' % timing[0][1])
159 return None
162def main():
163 """
164 Main driver: the serial variants.
166 The parallel variants need a 3-rank job, so they are not run from here. Use
168 mpirun -np 3 python -c "from pySDC.projects.parallelSDC.AllenCahn_parallel import run_variant as r; r('sl_parallel')"
170 or let the test do it -- `tests/test_AllenCahn_parallel.py` runs them through mpi-pytest.
171 """
173 run_variant(variant='sl_serial')
174 print()
175 run_variant(variant='ml_serial')
176 print()
179if __name__ == "__main__":
180 main()