Skip to content

Commit 3e17778

Browse files
author
sveinlin
committed
update advec dir
1 parent 31651ba commit 3e17778

2 files changed

Lines changed: 436 additions & 0 deletions

File tree

src/advec/advec1D.py

Lines changed: 367 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,367 @@
1+
import numpy as np
2+
import matplotlib.pyplot as plt
3+
4+
def solver_FECS(I, U0, v, L, dt, C, T, user_action=None):
5+
Nt = int(round(T/float(dt)))
6+
t = np.linspace(0, Nt*dt, Nt+1) # Mesh points in time
7+
dx = v*dt/C
8+
Nx = int(round(L/dx))
9+
x = np.linspace(0, L, Nx+1) # Mesh points in space
10+
# Make sure dx and dt are compatible with x and t
11+
dx = x[1] - x[0]
12+
dt = t[1] - t[0]
13+
C = v*dt/dx
14+
15+
u = np.zeros(Nx+1)
16+
u_n = np.zeros(Nx+1)
17+
18+
# Set initial condition u(x,0) = I(x)
19+
for i in range(0, Nx+1):
20+
u_n[i] = I(x[i])
21+
22+
if user_action is not None:
23+
user_action(u_n, x, t, 0)
24+
25+
for n in range(0, Nt):
26+
# Compute u at inner mesh points
27+
for i in range(1, Nx):
28+
u[i] = u_n[i] - 0.5*C*(u_n[i+1] - u_n[i-1])
29+
30+
# Insert boundary condition
31+
u[0] = U0
32+
33+
if user_action is not None:
34+
user_action(u, x, t, n+1)
35+
36+
# Switch variables before next step
37+
u_n, u = u, u_n
38+
39+
40+
def solver(I, U0, v, L, dt, C, T, user_action=None,
41+
scheme='FE', periodic_bc=True):
42+
Nt = int(round(T/float(dt)))
43+
t = np.linspace(0, Nt*dt, Nt+1) # Mesh points in time
44+
dx = v*dt/C
45+
Nx = int(round(L/dx))
46+
x = np.linspace(0, L, Nx+1) # Mesh points in space
47+
# Make sure dx and dt are compatible with x and t
48+
dx = x[1] - x[0]
49+
dt = t[1] - t[0]
50+
C = v*dt/dx
51+
print 'dt=%g, dx=%g, Nx=%d, C=%g' % (dt, dx, Nx, C)
52+
53+
u = np.zeros(Nx+1)
54+
u_n = np.zeros(Nx+1)
55+
u_nm1 = np.zeros(Nx+1)
56+
integral = np.zeros(Nt+1)
57+
58+
# Set initial condition u(x,0) = I(x)
59+
for i in range(0, Nx+1):
60+
u_n[i] = I(x[i])
61+
62+
# Insert boundary condition
63+
u[0] = U0
64+
65+
# Compute the integral under the curve
66+
integral[0] = dx*(0.5*u_n[0] + 0.5*u_n[Nx] + np.sum(u_n[1:-1]))
67+
68+
if user_action is not None:
69+
user_action(u_n, x, t, 0)
70+
71+
for n in range(0, Nt):
72+
if scheme == 'FE':
73+
if periodic_bc:
74+
i = 0
75+
u[i] = u_n[i] - 0.5*C*(u_n[i+1] - u_n[Nx])
76+
u[Nx] = u[0]
77+
#u[i] = u_n[i] - 0.5*C*(u_n[1] - u_n[Nx])
78+
for i in range(1, Nx):
79+
u[i] = u_n[i] - 0.5*C*(u_n[i+1] - u_n[i-1])
80+
elif scheme == 'LF':
81+
if n == 0:
82+
# Use upwind for first step
83+
if periodic_bc:
84+
i = 0
85+
#u[i] = u_n[i] - C*(u_n[i] - u_n[Nx-1])
86+
u_n[i] = u_n[Nx]
87+
for i in range(1, Nx+1):
88+
u[i] = u_n[i] - C*(u_n[i] - u_n[i-1])
89+
else:
90+
if periodic_bc:
91+
i = 0
92+
# Must have this,
93+
u[i] = u_nm1[i] - C*(u_n[i+1] - u_n[Nx-1])
94+
# not this:
95+
#u_n[i] = u_n[Nx]
96+
for i in range(1, Nx):
97+
u[i] = u_nm1[i] - C*(u_n[i+1] - u_n[i-1])
98+
if periodic_bc:
99+
u[Nx] = u[0]
100+
elif scheme == 'UP':
101+
if periodic_bc:
102+
u_n[0] = u_n[Nx]
103+
for i in range(1, Nx+1):
104+
u[i] = u_n[i] - C*(u_n[i] - u_n[i-1])
105+
elif scheme == 'LW':
106+
if periodic_bc:
107+
i = 0
108+
# Must have this,
109+
u[i] = u_n[i] - 0.5*C*(u_n[i+1] - u_n[Nx-1]) + \
110+
0.5*C*(u_n[i+1] - 2*u_n[i] + u_n[Nx-1])
111+
# not this:
112+
#u_n[i] = u_n[Nx]
113+
for i in range(1, Nx):
114+
u[i] = u_n[i] - 0.5*C*(u_n[i+1] - u_n[i-1]) + \
115+
0.5*C*(u_n[i+1] - 2*u_n[i] + u_n[i-1])
116+
if periodic_bc:
117+
u[Nx] = u[0]
118+
else:
119+
raise ValueError('scheme="%s" not implemented' % scheme)
120+
121+
if not periodic_bc:
122+
# Insert boundary condition
123+
u[0] = U0
124+
125+
# Compute the integral under the curve
126+
integral[n+1] = dx*(0.5*u[0] + 0.5*u[Nx] + np.sum(u[1:-1]))
127+
128+
if user_action is not None:
129+
user_action(u, x, t, n+1)
130+
131+
# Switch variables before next step
132+
u_nm1, u_n, u = u_n, u, u_nm1
133+
print 'I:', integral[n+1]
134+
return integral
135+
136+
def run_FECS(case):
137+
"""Special function for the FECS case."""
138+
if case == 'gaussian':
139+
def I(x):
140+
return np.exp(-0.5*((x-L/10)/sigma)**2)
141+
elif case == 'cosinehat':
142+
def I(x):
143+
return np.cos(np.pi*5/L*(x - L/10)) if x < L/5 else 0
144+
145+
L = 1.0
146+
sigma = 0.02
147+
legends = []
148+
149+
def plot(u, x, t, n):
150+
"""Animate and plot every m steps in the same figure."""
151+
plt.figure(1)
152+
if n == 0:
153+
lines = plot(x, u)
154+
else:
155+
lines[0].set_ydata(u)
156+
plt.draw()
157+
#plt.savefig()
158+
plt.figure(2)
159+
m = 40
160+
if n % m != 0:
161+
return
162+
print 't=%g, n=%d, u in [%g, %g] w/%d points' % \
163+
(t[n], n, u.min(), u.max(), x.size)
164+
if np.abs(u).max() > 3: # Instability?
165+
return
166+
plt.plot(x, u)
167+
legends.append('t=%g' % t[n])
168+
if n > 0:
169+
plt.hold('on')
170+
171+
plt.ion()
172+
U0 = 0
173+
dt = 0.001
174+
C = 1
175+
T = 1
176+
solver(I=I, U0=U0, v=1.0, L=L, dt=dt, C=C, T=T,
177+
user_action=plot)
178+
plt.legend(legends, loc='lower left')
179+
plt.savefig('tmp.png'); plt.savefig('tmp.pdf')
180+
plt.axis([0, L, -0.75, 1.1])
181+
plt.show()
182+
183+
def run(scheme='UP', case='gaussian', C=1, dt=0.01):
184+
"""General admin routine for explicit and implicit solvers."""
185+
186+
if case == 'gaussian':
187+
def I(x):
188+
return np.exp(-0.5*((x-L/10)/sigma)**2)
189+
elif case == 'cosinehat':
190+
def I(x):
191+
return np.cos(np.pi*5/L*(x - L/10)) \
192+
if 0 < x < L/5 else 0
193+
194+
L = 1.0
195+
sigma = 0.02
196+
global lines # needs to be saved between calls to plot
197+
198+
def plot(u, x, t, n):
199+
"""Plot t=0 and t=0.6 in the same figure."""
200+
plt.figure(1)
201+
global lines
202+
if n == 0:
203+
lines = plt.plot(x, u)
204+
plt.axis([x[0], x[-1], -0.5, 1.5])
205+
plt.xlabel('x'); plt.ylabel('u')
206+
plt.axes().set_aspect(0.15)
207+
plt.savefig('tmp_%04d.png' % n)
208+
plt.savefig('tmp_%04d.pdf' % n)
209+
else:
210+
lines[0].set_ydata(u)
211+
plt.axis([x[0], x[-1], -0.5, 1.5])
212+
plt.title('C=%g, dt=%g, dx=%g' %
213+
(C, t[1]-t[0], x[1]-x[0]))
214+
plt.legend(['t=%.3f' % t[n]])
215+
plt.xlabel('x'); plt.ylabel('u')
216+
plt.draw()
217+
plt.savefig('tmp_%04d.png' % n)
218+
plt.figure(2)
219+
eps = 1E-14
220+
if abs(t[n] - 0.6) > eps and abs(t[n] - 0) > eps:
221+
return
222+
print 't=%g, n=%d, u in [%g, %g] w/%d points' % \
223+
(t[n], n, u.min(), u.max(), x.size)
224+
if np.abs(u).max() > 3: # Instability?
225+
return
226+
plt.plot(x, u)
227+
plt.hold('on')
228+
plt.draw()
229+
if n > 0:
230+
y = [I(x_-v*t[n]) for x_ in x]
231+
plt.plot(x, y, 'k--')
232+
if abs(t[n] - 0.6) < eps:
233+
filename = ('tmp_%s_dt%s_C%s' % \
234+
(scheme, t[1]-t[0], C)).replace('.', '')
235+
np.savez(filename, x=x, u=u, u_e=y)
236+
237+
plt.ion()
238+
U0 = 0
239+
T = 0.7
240+
v = 1
241+
# Define video formats and libraries
242+
codecs = dict(flv='flv', mp4='libx264', webm='libvpx',
243+
ogg='libtheora')
244+
# Remove video files
245+
import glob, os
246+
for name in glob.glob('tmp_*.png'):
247+
os.remove(name)
248+
for ext in codecs:
249+
name = 'movie.%s' % ext
250+
if os.path.isfile(name):
251+
os.remove(name)
252+
253+
if scheme == 'CN':
254+
integral = solver_theta(
255+
I, v, L, dt, C, T, user_action=plot, FE=False)
256+
elif scheme == 'BE':
257+
integral = solver_theta(
258+
I, v, L, dt, C, T, theta=1, user_action=plot)
259+
else:
260+
integral = solver(
261+
I=I, U0=U0, v=v, L=L, dt=dt, C=C, T=T,
262+
scheme=scheme, user_action=plot)
263+
# Finish figure(2)
264+
plt.figure(2)
265+
plt.axis([0, L, -0.5, 1.1])
266+
plt.xlabel('$x$'); plt.ylabel('$u$')
267+
plt.axes().set_aspect(0.5) # no effect
268+
plt.savefig('tmp1.png'); plt.savefig('tmp1.pdf')
269+
plt.show()
270+
# Make videos from figure(1) animation files
271+
for codec in codecs:
272+
cmd = 'ffmpeg -i tmp_%%04d.png -r 25 -vcodec %s movie.%s' % \
273+
(codecs[codec], codec)
274+
os.system(cmd)
275+
print 'Integral of u:', integral.max(), integral.min()
276+
277+
def solver_theta(I, v, L, dt, C, T, theta=0.5, user_action=None, FE=False):
278+
"""
279+
Full solver for the model problem using the theta-rule
280+
difference approximation in time (no restriction on F,
281+
i.e., the time step when theta >= 0.5).
282+
Vectorized implementation and sparse (tridiagonal)
283+
coefficient matrix.
284+
"""
285+
import time; t0 = time.clock() # for measuring the CPU time
286+
Nt = int(round(T/float(dt)))
287+
t = np.linspace(0, Nt*dt, Nt+1) # Mesh points in time
288+
dx = v*dt/C
289+
Nx = int(round(L/dx))
290+
x = np.linspace(0, L, Nx+1) # Mesh points in space
291+
# Make sure dx and dt are compatible with x and t
292+
dx = x[1] - x[0]
293+
dt = t[1] - t[0]
294+
C = v*dt/dx
295+
print 'dt=%g, dx=%g, Nx=%d, C=%g' % (dt, dx, Nx, C)
296+
297+
u = np.zeros(Nx+1)
298+
u_n = np.zeros(Nx+1)
299+
u_nm1 = np.zeros(Nx+1)
300+
integral = np.zeros(Nt+1)
301+
302+
# Set initial condition u(x,0) = I(x)
303+
for i in range(0, Nx+1):
304+
u_n[i] = I(x[i])
305+
306+
# Compute the integral under the curve
307+
integral[0] = dx*(0.5*u_n[0] + 0.5*u_n[Nx] + np.sum(u_n[1:-1]))
308+
309+
if user_action is not None:
310+
user_action(u_n, x, t, 0)
311+
312+
# Representation of sparse matrix and right-hand side
313+
diagonal = np.zeros(Nx+1)
314+
lower = np.zeros(Nx)
315+
upper = np.zeros(Nx)
316+
b = np.zeros(Nx+1)
317+
318+
# Precompute sparse matrix (scipy format)
319+
diagonal[:] = 1
320+
lower[:] = -0.5*theta*C
321+
upper[:] = 0.5*theta*C
322+
if FE:
323+
diagonal[:] += 4./6
324+
lower[:] += 1./6
325+
upper[:] += 1./6
326+
# Insert boundary conditions
327+
upper[0] = 0
328+
lower[-1] = 0
329+
330+
diags = [0, -1, 1]
331+
import scipy.sparse
332+
import scipy.sparse.linalg
333+
A = scipy.sparse.diags(
334+
diagonals=[diagonal, lower, upper],
335+
offsets=[0, -1, 1], shape=(Nx+1, Nx+1),
336+
format='csr')
337+
#print A.todense()
338+
339+
# Time loop
340+
for n in range(0, Nt):
341+
b[1:-1] = u_n[1:-1] + 0.5*(1-theta)*C*(u_n[:-2] - u_n[2:])
342+
if FE:
343+
b[1:-1] += 1./6*u_n[:-2] + 1./6*u_n[:-2] + 4./6*u_n[1:-1]
344+
b[0] = u_n[Nx]; b[-1] = u_n[0] # boundary conditions
345+
b[0] = 0; b[-1] = 0 # boundary conditions
346+
u[:] = scipy.sparse.linalg.spsolve(A, b)
347+
348+
if user_action is not None:
349+
user_action(u, x, t, n+1)
350+
351+
# Compute the integral under the curve
352+
integral[n+1] = dx*(0.5*u[0] + 0.5*u[Nx] + np.sum(u[1:-1]))
353+
354+
# Update u_n before next step
355+
u_n, u = u, u_n
356+
357+
t1 = time.clock()
358+
return integral
359+
360+
361+
if __name__ == '__main__':
362+
#run(scheme='LF', case='gaussian', C=1)
363+
#run(scheme='UP', case='gaussian', C=0.8, dt=0.01)
364+
#run(scheme='LF', case='gaussian', C=0.8, dt=0.001)
365+
#run(scheme='LF', case='cosinehat', C=0.8, dt=0.01)
366+
#run(scheme='CN', case='gaussian', C=1, dt=0.01)
367+
run(scheme='LW', case='gaussian', C=1, dt=0.01)

0 commit comments

Comments
 (0)