1+ # Parts of this code are taken from the PseudoSpectralPython tutorial.
2+ #
3+ # Author: D. Ketcheson
4+ # https://github.com/ketch/PseudoSpectralPython/blob/master/PSPython_01-linear-PDEs.ipynb
5+ #
6+ # The MIT License (MIT)
7+ #
8+ # Copyright (c) 2015 David Ketcheson
9+ #
10+ # Permission is hereby granted, free of charge, to any person obtaining a copy
11+ # of this software and associated documentation files (the "Software"), to deal
12+ # in the Software without restriction, including without limitation the rights
13+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14+ # copies of the Software, and to permit persons to whom the Software is
15+ # furnished to do so, subject to the following conditions:
16+ #
17+ # The above copyright notice and this permission notice shall be included in all
18+ # copies or substantial portions of the Software.
19+ #
20+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26+ # SOFTWARE.
27+
28+ import sys
29+ sys .path .append ('../src' )
30+ import numpy as np
31+ import scipy .sparse as sp
32+ import scipy .linalg as la
33+
34+ from parareal import parareal
35+ from impeuler import impeuler
36+ from intexact import intexact
37+ from trapezoidal import trapezoidal
38+ from special_integrator import special_integrator
39+ from solution_linear import solution_linear
40+
41+ import matplotlib .pyplot as plt
42+ from subprocess import call
43+ from pylab import rcParams
44+
45+ from joblib import Parallel , delayed
46+
47+ # advection speed
48+ uadv = 1.0
49+
50+ # diffusivity parameter
51+ nu = 0.0
52+
53+ # Spatial grid
54+ m = 64 # Number of grid points in space
55+ L = 4.0 # Width of spatial domain
56+
57+ # select coarse integrator:
58+ # 0 = normal backward Euler method
59+ # 1 = artificially constructed method with phase error from backward Euler and exact amplification factor
60+ # 2 = artificially constructed method with exact phase and amplification factor from backward Euler
61+ artificial_coarse = 0
62+
63+ artificial_fine = 0
64+
65+ # Grid points
66+ x = np .linspace (0 , L , m , endpoint = False )
67+ # Grid spacing
68+ dx = x [1 ]- x [0 ]
69+
70+ # Temporal grid
71+ tmax = 16 # Final time
72+ #N = 100 # number grid points in time
73+ #dt = tmax/float(N)
74+ nproc = int (tmax )
75+
76+ Kiter_v = [5 , 10 , 15 ]
77+
78+ xi = np .fft .fftfreq (m )* m * 2 * np .pi / L # Wavenumber "grid"
79+ # (this is the order in which numpy's FFT gives the frequencies)
80+
81+ # define spatial operator matrix
82+ D = - 1j * xi * uadv - nu * xi ** 2
83+
84+ # Initial data
85+ sig = 1.0
86+ u = np .exp (- (x - 0.5 * L )** 2 / sig ** 2 )
87+ u = np .exp (2.0 * np .pi * 1j * x )
88+ uhat0 = np .fft .fft (u )
89+
90+ yend = np .zeros ((3 ,m ), dtype = 'complex' )
91+
92+ ### Because the exact integrator can only deal with scalar problems, we will need to solve
93+ ### to initial value problem for each wave number independently
94+ ### ...we use the JobLib module to speed up the computational
95+ def run_parareal (uhat , D , k ):
96+ sol = solution_linear (np .asarray ([[uhat ]]), np .asarray ([[D ]]))
97+ para = parareal (tstart = 0.0 , tend = tmax , nslices = nproc , fine = intexact , coarse = impeuler , nsteps_fine = 10 , nsteps_coarse = 1 , tolerance = 0.0 , iter_max = k , u0 = sol )
98+ stab_coarse = para .timemesh .slices [0 ].get_coarse_update_matrix (sol )
99+ stab_ex = np .exp (D )
100+
101+ if artificial_coarse == 2 :
102+ stab_tailor = abs (stab_coarse [0 ,0 ])* np .exp (1j * np .angle (stab_ex )) # exact phase speed
103+ elif artificial_coarse == 1 :
104+ stab_tailor = abs (stab_ex )* np .exp (1j * np .angle (stab_coarse [0 ,0 ])) # exact amplification factor
105+
106+ if not artificial_coarse == 0 :
107+ stab_tailor = sp .csc_matrix (np .array ([stab_tailor ], dtype = 'complex' ))
108+ para = parareal (tstart = 0.0 , tend = tmax , nslices = nproc , fine = intexact , coarse = stab_tailor , nsteps_fine = 10 , nsteps_coarse = 1 , tolerance = 0.0 , iter_max = k , u0 = sol )
109+
110+ if artificial_fine == 1 :
111+ assert artificial_coarse == 0 , "Using artifical coarse and fine propagators together is not implemented and probably not working correctly"
112+ stab_fine = abs (stab_ex )* np .exp (1j * np .angle (stab_coarse [0 ,0 ]))
113+ stab_fine = sp .csc_matrix (np .array ([stab_fine ], dtype = 'complex' ))
114+ # Must use nfine=1 in this case
115+ para = parareal (tstart = 0.0 , tend = tmax , nslices = nproc , fine = stab_fine , coarse = impeuler , nsteps_fine = 1 , nsteps_coarse = 1 , tolerance = 0.0 , iter_max = k , u0 = sol )
116+
117+ para .run ()
118+ temp = para .get_last_end_value ()
119+ return temp .y [0 ,0 ]
120+
121+ for k in range (0 ,3 ):
122+ temp = Parallel (n_jobs = 2 , verbose = 5 )(delayed (run_parareal )(uhat0 [n ],D [n ], Kiter_v [k ]) for n in range (m ))
123+ yend [k ,:] = temp
124+
125+ rcParams ['figure.figsize' ] = 2.5 , 2.5
126+ fs = 8
127+ fig1 = plt .figure ()
128+ plt .plot (x , np .fft .ifft (yend [0 ,:]).real , '-s' , color = 'r' , linewidth = 1.5 , label = 'Parareal k=' + str (Kiter_v [0 ]), markevery = (1 ,6 ), mew = 1.0 , markersize = fs / 2 )
129+ plt .plot (x , np .fft .ifft (yend [1 ,:]).real , '-d' , color = 'r' , linewidth = 1.5 , label = 'Parareal k=' + str (Kiter_v [1 ]), markevery = (3 ,6 ), mew = 1.0 , markersize = fs / 2 )
130+ plt .plot (x , np .fft .ifft (yend [2 ,:]).real , '-x' , color = 'r' , linewidth = 1.5 , label = 'Parareal k=' + str (Kiter_v [2 ]), markevery = (5 ,6 ), mew = 1.0 , markersize = fs / 2 )
131+ plt .plot (x , np .fft .ifft (uhat0 ).real , '--' , color = 'k' , linewidth = 1.5 , label = 'Exact' )
132+ plt .xlim ([x [0 ], x [- 1 ]])
133+ plt .ylim ([- .2 , 1.4 ])
134+ plt .xlabel ('x' , fontsize = fs , labelpad = 0.25 )
135+ plt .ylabel ('u' , fontsize = fs , labelpad = 0.5 )
136+ fig1 .gca ().tick_params (axis = 'both' , labelsize = fs )
137+ plt .legend (loc = 'upper left' , fontsize = fs , prop = {'size' :fs - 2 })
138+ #plt.show()
139+ filename = 'parareal-gauss-peak.pdf'
140+ plt .gcf ().savefig (filename , bbox_inches = 'tight' )
141+ call (["pdfcrop" , filename , filename ])
142+
143+ xi = np .fft .fftshift (xi )
144+ xi = xi [m / 2 :m ]/ m
145+ uhat0 = np .fft .fftshift (uhat0 )
146+ yend [0 ,:] = np .around (np .fft .fftshift (yend [0 ,:]), 10 )
147+ yend [1 ,:] = np .around (np .fft .fftshift (yend [1 ,:]), 10 )
148+ yend [2 ,:] = np .around (np .fft .fftshift (yend [2 ,:]), 10 )
149+
150+ fig2 = plt .figure ()
151+ plt .semilogy (xi , np .absolute (uhat0 [m / 2 :m ])/ m , '--' , color = 'k' , linewidth = 1.5 , label = 'Exact' )
152+ plt .semilogy (xi , np .absolute (yend [0 ,m / 2 :m ])/ m , '-s' , color = 'r' , linewidth = 1.5 , label = 'Parareal k=' + str (Kiter_v [0 ]), markevery = (1 ,3 ), mew = 1.0 , markersize = fs / 2 )
153+ plt .semilogy (xi , np .absolute (yend [1 ,m / 2 :m ])/ m , '-d' , color = 'r' , linewidth = 1.5 , label = 'Parareal k=' + str (Kiter_v [1 ]), markevery = (2 ,3 ), mew = 1.0 , markersize = fs / 2 )
154+ plt .semilogy (xi , np .absolute (yend [2 ,m / 2 :m ])/ m , '-x' , color = 'r' , linewidth = 1.5 , label = 'Parareal k=' + str (Kiter_v [2 ]), markevery = (3 ,3 ), mew = 1.0 , markersize = fs / 2 )
155+ plt .xlabel ('Wave number' , fontsize = fs , labelpad = 0.25 )
156+ plt .ylabel (r'abs($\hat{u}$)' )
157+ plt .xticks ([0.0 , 0.1 , 0.3 ], fontsize = fs )
158+ plt .xlim ([0.0 , 0.3 ])
159+ plt .yticks ([1e0 , 1e-3 , 1e-6 , 1e-9 , 1e-12 ], fontsize = fs )
160+ plt .legend (loc = 'upper right' , fontsize = fs , prop = {'size' :fs - 2 })
161+ filename = 'parareal-gauss-peak-spectrum.pdf'
162+ plt .gcf ().savefig (filename , bbox_inches = 'tight' )
163+ call (["pdfcrop" , filename , filename ])
0 commit comments