Skip to content

Commit bcd569a

Browse files
committed
some improvements for AC FFT example + parallel playground
1 parent a8588e6 commit bcd569a

File tree

8 files changed

+296
-47
lines changed

8 files changed

+296
-47
lines changed

pySDC/implementations/problem_classes/AllenCahn_2D_FFT.py

Lines changed: 98 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ class allencahn2d_imex(ptype):
1515
xvalues: grid points in space
1616
dx: mesh width
1717
lap: spectral operator for Laplacian
18-
fft_object: planned FFT for forward transformation
19-
ifft_object: planned IFFT for backward transformation
18+
rfft_object: planned real FFT for forward transformation
19+
irfft_object: planned IFFT for backward transformation
2020
"""
2121

2222
def __init__(self, problem_params, dtype_u=mesh, dtype_f=rhs_imex_mesh):
@@ -57,28 +57,25 @@ def __init__(self, problem_params, dtype_u=mesh, dtype_f=rhs_imex_mesh):
5757
self.xvalues = np.array([i * self.dx - self.params.L / 2.0 for i in range(self.params.nvars[0])])
5858

5959
kx = np.zeros(self.init[0])
60-
ky = np.zeros(self.init[1])
60+
ky = np.zeros(self.init[1] // 2 + 1)
6161
for i in range(0, int(self.init[0] / 2) + 1):
6262
kx[i] = 2 * np.pi / self.params.L * i
63-
for i in range(0, int(self.init[1] / 2) + 1):
63+
for i in range(0, int(self.init[1] // 2) + 1):
6464
ky[i] = 2 * np.pi / self.params.L * i
6565
for i in range(int(self.init[0] / 2) + 1, self.init[0]):
6666
kx[i] = 2 * np.pi / self.params.L * (-self.init[0] + i)
67-
for i in range(int(self.init[1] / 2) + 1, self.init[1]):
68-
ky[i] = 2 * np.pi / self.params.L * (-self.init[1] + i)
6967

70-
self.lap = np.zeros(self.init)
68+
self.lap = np.zeros((self.init[0], self.init[1] // 2 + 1))
7169
for i in range(self.init[0]):
72-
for j in range(self.init[1]):
70+
for j in range(self.init[1] // 2 + 1):
7371
self.lap[i, j] = -kx[i] ** 2 - ky[j] ** 2
7472

75-
# TODO: cleanup and move to real-valued FFT
76-
fft_in = pyfftw.empty_aligned(self.init, dtype='complex128')
77-
fft_out = pyfftw.empty_aligned(self.init, dtype='complex128')
78-
ifft_in = pyfftw.empty_aligned(self.init, dtype='complex128')
79-
ifft_out = pyfftw.empty_aligned(self.init, dtype='complex128')
80-
self.fft_object = pyfftw.FFTW(fft_in, fft_out, direction='FFTW_FORWARD', axes=(0, 1))
81-
self.ifft_object = pyfftw.FFTW(ifft_in, ifft_out, direction='FFTW_BACKWARD', axes=(0, 1))
73+
rfft_in = pyfftw.empty_aligned(self.init, dtype='float64')
74+
fft_out = pyfftw.empty_aligned((self.init[0], self.init[1] // 2 + 1), dtype='complex128')
75+
ifft_in = pyfftw.empty_aligned((self.init[0], self.init[1] // 2 + 1), dtype='complex128')
76+
irfft_out = pyfftw.empty_aligned(self.init, dtype='float64')
77+
self.rfft_object = pyfftw.FFTW(rfft_in, fft_out, direction='FFTW_FORWARD', axes=(0, 1))
78+
self.irfft_object = pyfftw.FFTW(ifft_in, irfft_out, direction='FFTW_BACKWARD', axes=(0, 1))
8279

8380
def eval_f(self, u, t):
8481
"""
@@ -94,8 +91,8 @@ def eval_f(self, u, t):
9491

9592
f = self.dtype_f(self.init)
9693
v = u.values.flatten()
97-
tmp = self.lap * self.fft_object(u.values)
98-
f.impl.values[:] = np.real(self.ifft_object(tmp))
94+
tmp = self.lap * self.rfft_object(u.values)
95+
f.impl.values[:] = np.real(self.irfft_object(tmp))
9996
if self.params.eps > 0:
10097
f.expl.values = 1.0 / self.params.eps ** 2 * v * (1.0 - v ** self.params.nu)
10198
f.expl.values = f.expl.values.reshape(self.params.nvars)
@@ -117,8 +114,8 @@ def solve_system(self, rhs, factor, u0, t):
117114

118115
me = self.dtype_u(self.init)
119116

120-
tmp = self.fft_object(rhs.values) / (1.0 - factor * self.lap)
121-
me.values[:] = np.real(self.ifft_object(tmp))
117+
tmp = self.rfft_object(rhs.values) / (1.0 - factor * self.lap)
118+
me.values[:] = np.real(self.irfft_object(tmp))
122119

123120
return me
124121

@@ -149,3 +146,85 @@ def u_exact(self, t):
149146
raise NotImplementedError('type of initial value not implemented, got %s' % self.params.init_type)
150147

151148
return me
149+
150+
151+
class allencahn2d_imex_stab(allencahn2d_imex):
152+
"""
153+
Example implementing Allen-Cahn equation in 2D using FFTs for solving linear parts, IMEX time-stepping with
154+
stabilized splitting
155+
156+
Attributes:
157+
xvalues: grid points in space
158+
dx: mesh width
159+
lap: spectral operator for Laplacian
160+
rfft_object: planned real FFT for forward transformation
161+
irfft_object: planned IFFT for backward transformation
162+
"""
163+
164+
def __init__(self, problem_params, dtype_u=mesh, dtype_f=rhs_imex_mesh):
165+
"""
166+
Initialization routine
167+
168+
Args:
169+
problem_params (dict): custom parameters for the example
170+
dtype_u: mesh data type (will be passed to parent class)
171+
dtype_f: mesh data type wuth implicit and explicit parts (will be passed to parent class)
172+
"""
173+
super(allencahn2d_imex_stab, self).__init__(problem_params=problem_params, dtype_u=dtype_u, dtype_f=dtype_f)
174+
175+
kx = np.zeros(self.init[0])
176+
ky = np.zeros(self.init[1] // 2 + 1)
177+
for i in range(0, int(self.init[0] / 2) + 1):
178+
kx[i] = 2 * np.pi / self.params.L * i
179+
for i in range(0, int(self.init[1] // 2) + 1):
180+
ky[i] = 2 * np.pi / self.params.L * i
181+
for i in range(int(self.init[0] / 2) + 1, self.init[0]):
182+
kx[i] = 2 * np.pi / self.params.L * (-self.init[0] + i)
183+
184+
self.lap = np.zeros((self.init[0], self.init[1] // 2 + 1))
185+
for i in range(self.init[0]):
186+
for j in range(self.init[1] // 2 + 1):
187+
self.lap[i, j] = -kx[i] ** 2 - ky[j] ** 2 - 2.0 / self.params.eps ** 2
188+
189+
def eval_f(self, u, t):
190+
"""
191+
Routine to evaluate the RHS
192+
193+
Args:
194+
u (dtype_u): current values
195+
t (float): current time
196+
197+
Returns:
198+
dtype_f: the RHS
199+
"""
200+
201+
f = self.dtype_f(self.init)
202+
v = u.values.flatten()
203+
tmp = self.lap * self.rfft_object(u.values)
204+
f.impl.values[:] = np.real(self.irfft_object(tmp))
205+
if self.params.eps > 0:
206+
f.expl.values = 1.0 / self.params.eps ** 2 * v * (1.0 - v ** self.params.nu) + \
207+
2.0 / self.params.eps ** 2 * v
208+
f.expl.values = f.expl.values.reshape(self.params.nvars)
209+
return f
210+
211+
def solve_system(self, rhs, factor, u0, t):
212+
"""
213+
Simple FFT solver for the diffusion part
214+
215+
Args:
216+
rhs (dtype_f): right-hand side for the linear system
217+
factor (float) : abbrev. for the node-to-node stepsize (or any other factor required)
218+
u0 (dtype_u): initial guess for the iterative solver (not used here so far)
219+
t (float): current time (e.g. for time-dependent BCs)
220+
221+
Returns:
222+
dtype_u: solution as mesh
223+
"""
224+
225+
me = self.dtype_u(self.init)
226+
227+
tmp = self.rfft_object(rhs.values) / (1.0 - factor * self.lap)
228+
me.values[:] = np.real(self.irfft_object(tmp))
229+
230+
return me

pySDC/playgrounds/fft/AllenCahn_contracting_circle_FFT.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from pySDC.helpers.stats_helper import filter_stats, sort_stats
99
from pySDC.implementations.collocation_classes.gauss_radau_right import CollGaussRadau_Right
1010
from pySDC.implementations.controller_classes.controller_nonMPI import controller_nonMPI
11-
from pySDC.implementations.problem_classes.AllenCahn_2D_FFT import allencahn2d_imex
11+
from pySDC.implementations.problem_classes.AllenCahn_2D_FFT import allencahn2d_imex, allencahn2d_imex_stab
1212
from pySDC.implementations.sweeper_classes.imex_1st_order import imex_1st_order
1313
from pySDC.implementations.transfer_classes.TransferMesh_FFT2D import mesh_to_mesh_fft2d
1414
from pySDC.projects.TOMS.AllenCahn_monitor import monitor
@@ -32,7 +32,7 @@ def setup_parameters():
3232
level_params = dict()
3333
level_params['restol'] = 1E-08
3434
level_params['dt'] = 1E-03
35-
level_params['nsweeps'] = [2, 1]
35+
level_params['nsweeps'] = [3, 1]
3636

3737
# initialize sweeper parameters
3838
sweeper_params = dict()
@@ -46,13 +46,13 @@ def setup_parameters():
4646
problem_params = dict()
4747
problem_params['nu'] = 2
4848
problem_params['L'] = 1.0
49-
problem_params['nvars'] = [(128, 128), (32, 32)]
50-
problem_params['eps'] = [0.04, 0.04]
49+
problem_params['nvars'] = [(256, 256), (64, 64)]
50+
problem_params['eps'] = [0.04, 0.16]
5151
problem_params['radius'] = 0.25
5252

5353
# initialize step parameters
5454
step_params = dict()
55-
step_params['maxiter'] = 20
55+
step_params['maxiter'] = 50
5656

5757
# initialize controller parameters
5858
controller_params = dict()
@@ -72,13 +72,12 @@ def setup_parameters():
7272
return description, controller_params
7373

7474

75-
def run_SDC_variant(variant=None, inexact=False):
75+
def run_SDC_variant(variant=None):
7676
"""
7777
Routine to run particular SDC variant
7878
7979
Args:
8080
variant (str): string describing the variant
81-
inexact (bool): flag to use inexact nonlinear solve (or nor)
8281
8382
Returns:
8483
timing (float)
@@ -92,6 +91,9 @@ def run_SDC_variant(variant=None, inexact=False):
9291
if variant == 'semi-implicit':
9392
description['problem_class'] = allencahn2d_imex
9493
description['sweeper_class'] = imex_1st_order
94+
elif variant == 'semi-implicit-stab':
95+
description['problem_class'] = allencahn2d_imex_stab
96+
description['sweeper_class'] = imex_1st_order
9597
else:
9698
raise NotImplemented('Wrong variant specified, got %s' % variant)
9799

@@ -100,8 +102,7 @@ def run_SDC_variant(variant=None, inexact=False):
100102
Tend = 0.032
101103

102104
# instantiate controller
103-
controller = controller_nonMPI(num_procs=8, controller_params=controller_params,
104-
description=description)
105+
controller = controller_nonMPI(num_procs=8, controller_params=controller_params, description=description)
105106

106107
# get initial values on finest level
107108
P = controller.MS[0].levels[0].prob
@@ -207,13 +208,13 @@ def show_results(fname, cwd=''):
207208

208209
xcoords = [item0[0] for item0 in computed_radii]
209210
radii = [item0[1] for item0 in computed_radii]
210-
if key[0] + ' ' + key[1] == 'semi-implicit exact':
211+
if key[0] + ' ' + key[1] == 'semi-implicit-stab exact':
211212
ax.plot(xcoords, radii, label=(key[0] + ' ' + key[1]).replace('_v2', ' mod.'))
212213

213214
exact_radii = sort_stats(filter_stats(item, type='exact_radius'), sortby='time')
214215

215-
diff = np.array([abs(item0[1] - item1[1]) for item0, item1 in zip(exact_radii, computed_radii)])
216-
max_pos = int(np.argmax(diff))
216+
# diff = np.array([abs(item0[1] - item1[1]) for item0, item1 in zip(exact_radii, computed_radii)])
217+
# max_pos = int(np.argmax(diff))
217218
# assert max(diff) < 0.07, 'ERROR: computed radius is too far away from exact radius, got %s' % max(diff)
218219
# assert 0.028 < computed_radii[max_pos][0] < 0.03, \
219220
# 'ERROR: largest difference is at wrong time, got %s' % computed_radii[max_pos][0]
@@ -278,9 +279,9 @@ def main(cwd=''):
278279

279280
# Loop over variants, exact and inexact solves
280281
results = {}
281-
for variant in ['semi-implicit']:
282+
for variant in ['semi-implicit-stab']:
282283

283-
results[(variant, 'exact')] = run_SDC_variant(variant=variant, inexact=False)
284+
results[(variant, 'exact')] = run_SDC_variant(variant=variant)
284285

285286
# dump result
286287
fname = 'data/results_SDC_variants_AllenCahn_1E-03'

0 commit comments

Comments
 (0)