Coverage for pySDC/projects/AllenCahn_Bayreuth/run_simple_forcing_verification.py: 97%
172 statements
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-20 14:51 +0000
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-20 14:51 +0000
1from argparse import ArgumentParser
2import json
3import glob
4import numpy as np
5from mpi4py import MPI
7import pySDC.helpers.plot_helper as plt_helper
8import matplotlib.ticker as ticker
10from pySDC.helpers.stats_helper import get_sorted
12from pySDC.implementations.controller_classes.controller_nonMPI import controller_nonMPI
13from pySDC.implementations.sweeper_classes.imex_1st_order import imex_1st_order
14from pySDC.implementations.problem_classes.AllenCahn_MPIFFT import allencahn_imex, allencahn_imex_timeforcing
15from pySDC.implementations.transfer_classes.TransferMesh_MPIFFT import fft_to_fft
17from pySDC.projects.AllenCahn_Bayreuth.AllenCahn_monitor import monitor
20def run_simulation(name='', spectral=None, nprocs_space=None):
21 """
22 A test program to do PFASST runs for the AC equation with different forcing
24 Args:
25 name (str): name of the run, will be used to distinguish different setups
26 spectral (bool): run in real or spectral space
27 nprocs_space (int): number of processors in space (None if serial)
28 """
30 # set MPI communicator
31 comm = MPI.COMM_WORLD
33 world_rank = comm.Get_rank()
34 world_size = comm.Get_size()
36 # split world communicator to create space-communicators
37 if nprocs_space is not None:
38 color = int(world_rank / nprocs_space)
39 else:
40 color = int(world_rank / 1)
41 space_comm = comm.Split(color=color)
42 space_rank = space_comm.Get_rank()
43 space_size = space_comm.Get_size()
45 assert world_size == space_size, 'This script cannot run parallel-in-time with MPI, only spatial parallelism'
47 # initialize level parameters
48 level_params = dict()
49 level_params['restol'] = 1e-08
50 level_params['dt'] = 1e-03
51 level_params['nsweeps'] = [1]
53 # initialize sweeper parameters
54 sweeper_params = dict()
55 sweeper_params['quad_type'] = 'RADAU-RIGHT'
56 sweeper_params['num_nodes'] = [3]
57 sweeper_params['QI'] = ['LU'] # For the IMEX sweeper, the LU-trick can be activated for the implicit part
58 sweeper_params['initial_guess'] = 'spread'
60 # initialize problem parameters
61 problem_params = dict()
62 problem_params['L'] = 1.0
63 problem_params['nvars'] = [(128, 128), (32, 32)]
64 problem_params['eps'] = [0.04]
65 problem_params['radius'] = 0.25
66 problem_params['comm'] = space_comm
67 problem_params['init_type'] = 'circle'
68 problem_params['spectral'] = spectral
70 if name == 'AC-test-constforce':
71 problem_params['dw'] = [-23.59]
73 # initialize step parameters
74 step_params = dict()
75 step_params['maxiter'] = 50
77 # initialize controller parameters
78 controller_params = dict()
79 controller_params['logger_level'] = 30 if space_rank == 0 else 99 # set level depending on rank
80 controller_params['hook_class'] = monitor
81 controller_params['predict_type'] = 'pfasst_burnin'
83 # fill description dictionary for easy step instantiation
84 description = dict()
85 description['problem_params'] = problem_params # pass problem parameters
86 description['sweeper_class'] = imex_1st_order
87 description['sweeper_params'] = sweeper_params # pass sweeper parameters
88 description['level_params'] = level_params # pass level parameters
89 description['step_params'] = step_params # pass step parameters
90 description['space_transfer_class'] = fft_to_fft
92 if name == 'AC-test-noforce' or name == 'AC-test-constforce':
93 description['problem_class'] = allencahn_imex
94 elif name == 'AC-test-timeforce':
95 description['problem_class'] = allencahn_imex_timeforcing
96 else:
97 raise NotImplementedError(f'{name} is not implemented')
99 # set time parameters
100 t0 = 0.0
101 Tend = 32 * 0.001
103 if space_rank == 0:
104 out = f'---------> Running {name} with spectral={spectral} and {space_size} process(es) in space...'
105 print(out)
107 # instantiate controller
108 controller = controller_nonMPI(num_procs=8, controller_params=controller_params, description=description)
110 # get initial values on finest level
111 P = controller.MS[0].levels[0].prob
112 uinit = P.u_exact(t0)
114 # call main function to get things done...
115 uend, stats = controller.run(u0=uinit, t0=t0, Tend=Tend)
117 if space_rank == 0:
118 # convert filtered statistics to list of computed radii, sorted by time
119 computed_radii = get_sorted(stats, type='computed_radius', sortby='time')
120 exact_radii = get_sorted(stats, type='exact_radius', sortby='time')
121 computed_vol = get_sorted(stats, type='computed_volume', sortby='time')
122 exact_vol = get_sorted(stats, type='exact_volume', sortby='time')
124 # print and store radii and error over time
125 err_test = 0.0
126 results = dict()
127 for cr, er, cv, ev in zip(computed_radii, exact_radii, computed_vol, exact_vol):
128 if name == 'AC-test-noforce':
129 exrad = er[1]
130 exvol = ev[1]
131 else:
132 exrad = computed_radii[0][1]
133 exvol = computed_vol[0][1]
134 if exrad > 0:
135 errr = abs(cr[1] - exrad) / exrad
136 errv = abs(cv[1] - exvol) / exvol
137 else:
138 errr = 1.0
139 errv = 1.0
140 if cr[0] == 0.025:
141 err_test = errr
142 out = f'Computed/exact/error radius for time {cr[0]:6.4f}: ' f'{cr[1]:8.6f} / {exrad:8.6f} / {errr:6.4e}'
143 print(out)
144 results[cr[0]] = (cr[1], exrad, errr, cv[1], exvol, errv)
145 fname = f'./data/{name}_results.json'
146 with open(fname, 'w') as fp:
147 json.dump(results, fp, sort_keys=True, indent=4)
149 print()
151 # convert filtered statistics of iterations count, sorted by time
152 iter_counts = get_sorted(stats, type='niter', sortby='time')
153 niters = np.mean(np.array([item[1] for item in iter_counts]))
154 out = f'Mean number of iterations: {niters:.4f}'
155 print(out)
157 # get setup time
158 timing = get_sorted(stats, type='timing_setup', sortby='time')
159 out = f'Setup time: {timing[0][1]:.4f} sec.'
160 print(out)
162 # get running time
163 timing = get_sorted(stats, type='timing_run', sortby='time')
164 out = f'Time to solution: {timing[0][1]:.4f} sec.'
165 print(out)
167 out = '...Done <---------\n'
168 print(out)
170 # Testing the output
171 if name == 'AC-test-noforce':
172 if spectral:
173 exp_iters = 6.59375
174 exp_err = 7.821e-02
175 else:
176 exp_iters = 7.8125
177 exp_err = 7.85e-02
178 elif name == 'AC-test-constforce':
179 if spectral:
180 exp_iters = 2.875
181 exp_err = 4.678e-04
182 else:
183 exp_iters = 4.3125
184 exp_err = 6.2384e-04
185 elif name == 'AC-test-timeforce':
186 if spectral:
187 exp_iters = 1.65625
188 exp_err = 6.2345e-04
189 else:
190 exp_iters = 2.40625
191 exp_err = 6.2345e-04
192 else:
193 raise NotImplementedError(f'{name} is not implemented')
195 assert niters == exp_iters, f'Got deviating iteration counts of {niters} instead of {exp_iters}'
196 assert err_test < exp_err, f'Got deviating errors of {err_test} instead of {exp_err}'
198 space_comm.Free()
201def visualize_radii():
202 """
203 Routine to plot the radii of the runs vs. the exact radii
204 """
206 plt_helper.setup_mpl()
208 filelist = glob.glob('./data/*_results.json')
210 for file in filelist:
211 # read in file with data
212 with open(file, 'r') as fp:
213 results = json.load(fp)
215 print(f'Working on {file}...')
217 # get times and radii
218 xcoords = list(results)
219 computed_radii = [v[0] for k, v in results.items()]
220 exact_radii = [v[1] for k, v in results.items()]
221 computed_vol = [v[3] for k, v in results.items()]
222 exact_vol = [v[4] for k, v in results.items()]
224 # compute bound for y-axis
225 max_rad = max(max(computed_radii), max(exact_radii))
226 max_vol = max(max(computed_vol), max(exact_vol))
228 # set up plot for radii
229 fig, ax = plt_helper.newfig(textwidth=238.96, scale=1.0)
231 # and plot
232 ax.plot(xcoords, computed_radii, label='Computed radius')
233 ax.plot(xcoords, exact_radii, color='k', linestyle='--', linewidth=1, label='Exact radius')
235 # beautify and save plot
236 ax.set_ylim([-0.01, max_rad * 1.1])
237 ax.yaxis.set_major_formatter(ticker.FormatStrFormatter('%1.2f'))
238 # ax.xaxis.set_major_formatter(ticker.FormatStrFormatter('%1.2f'))
239 ax.xaxis.set_major_locator(ticker.MultipleLocator(4))
240 ax.set_ylabel('radius')
241 ax.set_xlabel('time')
242 ax.grid()
243 ax.legend(loc=3)
244 # ax.set_title(file.split('/')[-1].replace('_results.json', ''))
245 f = file.replace('_results.json', '_radii')
246 plt_helper.savefig(f)
248 # test if all went well
249 assert glob.glob(f'{f}.pdf'), 'ERROR: plotting did not create PDF file'
250 # assert glob.glob(f'{f}.pgf'), 'ERROR: plotting did not create PGF file'
251 assert glob.glob(f'{f}.png'), 'ERROR: plotting did not create PNG file'
253 # set up plot for volumes
254 fig, ax = plt_helper.newfig(textwidth=238.96, scale=1.0)
256 # and plot
257 ax.plot(xcoords, computed_vol, label='Computed volume')
258 ax.plot(xcoords, exact_vol, color='k', linestyle='--', linewidth=1, label='Exact volume')
260 # beautify and save plot
261 ax.set_ylim([-0.01, max_vol * 1.1])
262 ax.yaxis.set_major_formatter(ticker.FormatStrFormatter('%1.2f'))
263 # ax.xaxis.set_major_formatter(ticker.FormatStrFormatter('%1.2f'))
264 ax.xaxis.set_major_locator(ticker.MultipleLocator(4))
265 ax.set_ylabel('radius')
266 ax.set_xlabel('time')
267 ax.grid()
268 ax.legend(loc=3)
269 # ax.set_title(file.split('/')[-1].replace('_results.json', ''))
270 f = file.replace('_results.json', '_volume')
271 plt_helper.savefig(f)
273 # test if all went well
274 assert glob.glob(f'{f}.pdf'), 'ERROR: plotting did not create PDF file'
275 # assert glob.glob(f'{f}.pgf'), 'ERROR: plotting did not create PGF file'
276 assert glob.glob(f'{f}.png'), 'ERROR: plotting did not create PNG file'
279def main(nprocs_space=None):
280 """
281 Little helper routine to run the whole thing
283 Args:
284 nprocs_space (int): number of processors in space (None if serial)
286 """
287 name_list = ['AC-test-noforce', 'AC-test-constforce', 'AC-test-timeforce']
289 for name in name_list:
290 run_simulation(name=name, spectral=False, nprocs_space=nprocs_space)
291 run_simulation(name=name, spectral=True, nprocs_space=nprocs_space)
294if __name__ == "__main__":
295 # Add parser to get number of processors in space (have to do this here to enable automatic testing)
296 parser = ArgumentParser()
297 parser.add_argument("-n", "--nprocs_space", help='Specifies the number of processors in space', type=int)
298 args = parser.parse_args()
300 main(nprocs_space=args.nprocs_space)
301 visualize_radii()