Coverage for pySDC/projects/TOMS/AllenCahn_contracting_circle.py: 95%

185 statements  

« prev     ^ index     » next       coverage.py v7.16.1, created at 2026-09-25 20:28 +0000

1import os 

2 

3import dill 

4import matplotlib.ticker as ticker 

5import numpy as np 

6 

7import pySDC.helpers.plot_helper as plt_helper 

8from pySDC.helpers.stats_helper import get_sorted 

9 

10from pySDC.implementations.controller_classes.controller_nonMPI import controller_nonMPI 

11from pySDC.implementations.problem_classes.AllenCahn_2D_FD import ( 

12 allencahn_fullyimplicit, 

13 allencahn_semiimplicit, 

14 allencahn_semiimplicit_v2, 

15 allencahn_multiimplicit, 

16 allencahn_multiimplicit_v2, 

17) 

18from pySDC.implementations.sweeper_classes.generic_implicit import generic_implicit 

19from pySDC.implementations.sweeper_classes.imex_1st_order import imex_1st_order 

20from pySDC.implementations.sweeper_classes.multi_implicit import multi_implicit 

21from pySDC.implementations.hooks.AllenCahn_monitor import AllenCahnMonitor 

22 

23# http://www.personal.psu.edu/qud2/Res/Pre/dz09sisc.pdf 

24 

25 

26def setup_parameters(): 

27 """ 

28 Helper routine to fill in all relevant parameters 

29 

30 Note that this file will be used for all versions of SDC, containing more than necessary for each individual run 

31 

32 Returns: 

33 description (dict) 

34 controller_params (dict) 

35 """ 

36 

37 # initialize level parameters 

38 level_params = dict() 

39 level_params['restol'] = 1e-08 

40 level_params['dt'] = 1e-03 

41 level_params['nsweeps'] = [1] 

42 

43 # initialize sweeper parameters 

44 sweeper_params = dict() 

45 sweeper_params['quad_type'] = 'RADAU-RIGHT' 

46 sweeper_params['num_nodes'] = [3] 

47 sweeper_params['Q1'] = ['LU'] 

48 sweeper_params['Q2'] = ['LU'] 

49 sweeper_params['QI'] = ['LU'] 

50 sweeper_params['QE'] = ['EE'] 

51 sweeper_params['initial_guess'] = 'zero' 

52 

53 # This comes as read-in for the problem class 

54 problem_params = dict() 

55 problem_params['nvars'] = [(128, 128)] 

56 problem_params['eps'] = [0.04] 

57 problem_params['newton_maxiter'] = 100 

58 problem_params['newton_tol'] = 1e-09 

59 problem_params['lin_tol'] = 1e-10 

60 problem_params['lin_maxiter'] = 100 

61 problem_params['radius'] = 0.25 

62 

63 # initialize step parameters 

64 step_params = dict() 

65 step_params['maxiter'] = 50 

66 

67 # initialize controller parameters 

68 controller_params = dict() 

69 controller_params['logger_level'] = 30 

70 controller_params['hook_class'] = AllenCahnMonitor 

71 

72 # fill description dictionary for easy step instantiation 

73 description = dict() 

74 description['problem_class'] = None # pass problem class 

75 description['problem_params'] = problem_params # pass problem parameters 

76 description['sweeper_class'] = None # pass sweeper (see part B) 

77 description['sweeper_params'] = sweeper_params # pass sweeper parameters 

78 description['level_params'] = level_params # pass level parameters 

79 description['step_params'] = step_params # pass step parameters 

80 

81 return description, controller_params 

82 

83 

84def run_SDC_variant(variant=None, inexact=False): 

85 """ 

86 Routine to run particular SDC variant 

87 

88 Args: 

89 variant (str): string describing the variant 

90 inexact (bool): flag to use inexact nonlinear solve (or nor) 

91 

92 Returns: 

93 timing (float) 

94 niter (float) 

95 """ 

96 

97 # load (incomplete) default parameters 

98 description, controller_params = setup_parameters() 

99 

100 # add stuff based on variant 

101 if variant == 'fully-implicit': 

102 description['problem_class'] = allencahn_fullyimplicit 

103 description['sweeper_class'] = generic_implicit 

104 if inexact: 

105 description['problem_params']['newton_maxiter'] = 1 

106 elif variant == 'semi-implicit': 

107 description['problem_class'] = allencahn_semiimplicit 

108 description['sweeper_class'] = imex_1st_order 

109 if inexact: 

110 description['problem_params']['lin_maxiter'] = 10 

111 elif variant == 'semi-implicit_v2': 

112 description['problem_class'] = allencahn_semiimplicit_v2 

113 description['sweeper_class'] = imex_1st_order 

114 if inexact: 

115 description['problem_params']['newton_maxiter'] = 1 

116 elif variant == 'multi-implicit': 

117 description['problem_class'] = allencahn_multiimplicit 

118 description['sweeper_class'] = multi_implicit 

119 if inexact: 

120 description['problem_params']['newton_maxiter'] = 1 

121 description['problem_params']['lin_maxiter'] = 10 

122 elif variant == 'multi-implicit_v2': 

123 description['problem_class'] = allencahn_multiimplicit_v2 

124 description['sweeper_class'] = multi_implicit 

125 if inexact: 

126 description['problem_params']['newton_maxiter'] = 1 

127 else: 

128 raise NotImplementedError('Wrong variant specified, got %s' % variant) 

129 

130 if inexact: 

131 out = 'Working on inexact %s variant...' % variant 

132 else: 

133 out = 'Working on exact %s variant...' % variant 

134 print(out) 

135 

136 # setup parameters "in time" 

137 t0 = 0 

138 Tend = 0.032 

139 

140 # instantiate controller 

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

142 

143 # get initial values on finest level 

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

145 uinit = P.u_exact(t0) 

146 

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

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

149 

150 # filter statistics by variant (number of iterations) 

151 iter_counts = get_sorted(stats, type='niter', sortby='time') 

152 

153 # compute and print statistics 

154 niters = np.array([item[1] for item in iter_counts]) 

155 out = ' Mean number of iterations: %4.2f' % np.mean(niters) 

156 print(out) 

157 out = ' Range of values for number of iterations: %2i ' % np.ptp(niters) 

158 print(out) 

159 out = ' Position of max/min number of iterations: %2i -- %2i' % (int(np.argmax(niters)), int(np.argmin(niters))) 

160 print(out) 

161 out = ' Std and var for number of iterations: %4.2f -- %4.2f' % (float(np.std(niters)), float(np.var(niters))) 

162 print(out) 

163 

164 newton_iters = P.work_counters['newton'].niter 

165 lin_iters = P.work_counters['linear'].niter 

166 print(' Iteration count (nonlinear/linear): %i / %i' % (newton_iters, lin_iters)) 

167 print( 

168 ' Mean Iteration count per call: %4.2f / %4.2f' 

169 % (newton_iters / max(P.newton_ncalls, 1), lin_iters / max(P.lin_ncalls, 1)) 

170 ) 

171 

172 timing = get_sorted(stats, type='timing_run', sortby='time') 

173 

174 print('Time to solution: %6.4f sec.' % timing[0][1]) 

175 print() 

176 

177 return stats 

178 

179 

180def show_results(fname, cwd=''): 

181 """ 

182 Plotting routine 

183 

184 Args: 

185 fname (str): file name to read in and name plots 

186 cwd (str): current working directory 

187 """ 

188 

189 file = open(cwd + fname + '.pkl', 'rb') 

190 results = dill.load(file) 

191 file.close() 

192 

193 # plt_helper.mpl.style.use('classic') 

194 plt_helper.setup_mpl() 

195 

196 # set up plot for timings 

197 fig, ax1 = plt_helper.newfig(textwidth=238.96, scale=1.5, ratio=0.4) 

198 

199 timings = {} 

200 niters = {} 

201 for key, item in results.items(): 

202 timings[key] = get_sorted(item, type='timing_run', sortby='time')[0][1] 

203 iter_counts = get_sorted(item, type='niter', sortby='time') 

204 niters[key] = np.mean(np.array([item[1] for item in iter_counts])) 

205 

206 xcoords = list(range(len(timings))) 

207 sorted_timings = sorted([(key, timings[key]) for key in timings], reverse=True, key=lambda tup: tup[1]) 

208 sorted_niters = [(k, niters[k]) for k in [key[0] for key in sorted_timings]] 

209 heights_timings = [item[1] for item in sorted_timings] 

210 heights_niters = [item[1] for item in sorted_niters] 

211 keys = [(item[0][1] + ' ' + item[0][0]).replace('-', '\n').replace('_v2', ' mod.') for item in sorted_timings] 

212 

213 ax1.bar(xcoords, heights_timings, align='edge', width=-0.3, label='timings (left axis)') 

214 ax1.set_ylabel('time (sec)') 

215 

216 ax2 = ax1.twinx() 

217 ax2.bar(xcoords, heights_niters, color='lightcoral', align='edge', width=0.3, label='iterations (right axis)') 

218 ax2.set_ylabel('mean number of iterations') 

219 

220 ax1.set_xticks(xcoords) 

221 ax1.set_xticklabels(keys, rotation=90, ha='center') 

222 

223 # ask matplotlib for the plotted objects and their labels 

224 lines, labels = ax1.get_legend_handles_labels() 

225 lines2, labels2 = ax2.get_legend_handles_labels() 

226 ax2.legend(lines + lines2, labels + labels2, loc=0) 

227 

228 # save plot, beautify 

229 f = fname + '_timings' 

230 plt_helper.savefig(f) 

231 

232 assert os.path.isfile(f + '.pdf'), 'ERROR: plotting did not create PDF file' 

233 # assert os.path.isfile(f + '.pgf'), 'ERROR: plotting did not create PGF file' 

234 assert os.path.isfile(f + '.png'), 'ERROR: plotting did not create PNG file' 

235 

236 # set up plot for radii 

237 fig, ax = plt_helper.newfig(textwidth=238.96, scale=1.0) 

238 

239 exact_radii = [] 

240 for key, item in results.items(): 

241 computed_radii = get_sorted(item, type='computed_radius', sortby='time') 

242 

243 xcoords = [item0[0] for item0 in computed_radii] 

244 radii = [item0[1] for item0 in computed_radii] 

245 if key[0] + ' ' + key[1] == 'fully-implicit exact': 

246 ax.plot(xcoords, radii, label=(key[0] + ' ' + key[1]).replace('_v2', ' mod.')) 

247 

248 exact_radii = get_sorted(item, type='exact_radius', sortby='time') 

249 

250 diff = np.array([abs(item0[1] - item1[1]) for item0, item1 in zip(exact_radii, computed_radii, strict=True)]) 

251 max_pos = int(np.argmax(diff)) 

252 assert max(diff) < 0.07, 'ERROR: computed radius is too far away from exact radius, got %s' % max(diff) 

253 assert 0.028 < computed_radii[max_pos][0] < 0.03, ( 

254 'ERROR: largest difference is at wrong time, got %s' % computed_radii[max_pos][0] 

255 ) 

256 

257 xcoords = [item[0] for item in exact_radii] 

258 radii = [item[1] for item in exact_radii] 

259 ax.plot(xcoords, radii, color='k', linestyle='--', linewidth=1, label='exact') 

260 

261 ax.yaxis.set_major_formatter(ticker.FormatStrFormatter('%1.2f')) 

262 ax.set_ylabel('radius') 

263 ax.set_xlabel('time') 

264 ax.grid() 

265 ax.legend(loc=3) 

266 

267 # save plot, beautify 

268 f = fname + '_radii' 

269 plt_helper.savefig(f) 

270 

271 assert os.path.isfile(f + '.pdf'), 'ERROR: plotting did not create PDF file' 

272 # assert os.path.isfile(f + '.pgf'), 'ERROR: plotting did not create PGF file' 

273 assert os.path.isfile(f + '.png'), 'ERROR: plotting did not create PNG file' 

274 

275 # set up plot for interface width 

276 fig, ax = plt_helper.newfig(textwidth=238.96, scale=1.0) 

277 

278 interface_width = [] 

279 for key, item in results.items(): 

280 interface_width = get_sorted(item, type='interface_width', sortby='time') 

281 xcoords = [item[0] for item in interface_width] 

282 width = [item[1] for item in interface_width] 

283 if key[0] + ' ' + key[1] == 'fully-implicit exact': 

284 ax.plot(xcoords, width, label=key[0] + ' ' + key[1]) 

285 

286 xcoords = [item[0] for item in interface_width] 

287 init_width = [interface_width[0][1]] * len(xcoords) 

288 ax.plot(xcoords, init_width, color='k', linestyle='--', linewidth=1, label='exact') 

289 

290 ax.yaxis.set_major_formatter(ticker.FormatStrFormatter('%1.2f')) 

291 ax.set_ylabel(r'interface width ($\epsilon$)') 

292 ax.set_xlabel('time') 

293 ax.grid() 

294 ax.legend(loc=3) 

295 

296 # save plot, beautify 

297 f = fname + '_interface' 

298 plt_helper.savefig(f) 

299 

300 assert os.path.isfile(f + '.pdf'), 'ERROR: plotting did not create PDF file' 

301 # assert os.path.isfile(f + '.pgf'), 'ERROR: plotting did not create PGF file' 

302 assert os.path.isfile(f + '.png'), 'ERROR: plotting did not create PNG file' 

303 

304 return None 

305 

306 

307def main(cwd=''): 

308 """ 

309 Main driver 

310 

311 Args: 

312 cwd (str): current working directory (need this for testing) 

313 """ 

314 

315 # Loop over variants, exact and inexact solves 

316 results = {} 

317 for variant in ['multi-implicit', 'semi-implicit', 'fully-implicit', 'semi-implicit_v2', 'multi-implicit_v2']: 

318 results[(variant, 'exact')] = run_SDC_variant(variant=variant, inexact=False) 

319 results[(variant, 'inexact')] = run_SDC_variant(variant=variant, inexact=True) 

320 

321 # dump result 

322 fname = 'data/results_SDC_variants_AllenCahn_1E-03' 

323 file = open(cwd + fname + '.pkl', 'wb') 

324 dill.dump(results, file) 

325 file.close() 

326 assert os.path.isfile(cwd + fname + '.pkl'), 'ERROR: dill did not create file' 

327 

328 # visualize 

329 show_results(fname, cwd=cwd) 

330 

331 

332if __name__ == "__main__": 

333 main()