download
raw
6.71 kB
#!/usr/bin/env python
"""
1D wave equation with u=0 at the boundary.
The solver function here offers scalar and vectorized versions.
See wave1D_u0_s.py for documentation. The only difference
is that function solver takes an additional argument "version":
version='scalar' implies explicit loops over mesh point,
while version='vectorized' provides a vectorized version.
"""
import numpy as np
def solver(I, V, f, c, L, dt, C, T, user_action=None,
version='vectorized'):
"""Solve u_tt=c^2*u_xx + f on (0,L)x(0,T]."""
Nt = int(round(T/dt))
t = np.linspace(0, Nt*dt, Nt+1) # Mesh points in time
dx = dt*c/float(C)
Nx = int(round(L/dx))
x = np.linspace(0, L, Nx+1) # Mesh points in space
C2 = C**2 # Help variable in the scheme
if f is None or f == 0:
f = (lambda x, t: 0) if version == 'scalar' else \
lambda x, t: np.zeros(x.shape)
if V is None or V == 0:
V = (lambda x: 0) if version == 'scalar' else \
lambda x: np.zeros(x.shape)
u = np.zeros(Nx+1) # Solution array at new time level
u_1 = np.zeros(Nx+1) # Solution at 1 time level back
u_2 = np.zeros(Nx+1) # Solution at 2 time levels back
import time; t0 = time.clock() # for measuring CPU time
# Load initial condition into u_1
for i in range(0,Nx+1):
u_1[i] = I(x[i])
if user_action is not None:
user_action(u_1, x, t, 0)
# Special formula for first time step
n = 0
for i in range(1, Nx):
u[i] = u_1[i] + dt*V(x[i]) + \
0.5*C2*(u_1[i-1] - 2*u_1[i] + u_1[i+1]) + \
0.5*dt**2*f(x[i], t[n])
u[0] = 0; u[Nx] = 0
if user_action is not None:
user_action(u, x, t, 1)
# Switch variables before next step
u_2[:] = u_1; u_1[:] = u
for n in range(1, Nt):
# Update all inner points at time t[n+1]
if version == 'scalar':
for i in range(1, Nx):
u[i] = - u_2[i] + 2*u_1[i] + \
C2*(u_1[i-1] - 2*u_1[i] + u_1[i+1]) + \
dt**2*f(x[i], t[n])
elif version == 'vectorized': # (1:-1 slice style)
f_a = f(x, t[n]) # Precompute in array
u[1:-1] = - u_2[1:-1] + 2*u_1[1:-1] + \
C2*(u_1[0:-2] - 2*u_1[1:-1] + u_1[2:]) + \
dt**2*f_a[1:-1]
elif version == 'vectorized2': # (1:Nx slice style)
f_a = f(x, t[n]) # Precompute in array
u[1:Nx] = - u_2[1:Nx] + 2*u_1[1:Nx] + \
C2*(u_1[0:Nx-1] - 2*u_1[1:Nx] + u_1[2:Nx+1]) + \
dt**2*f_a[1:Nx]
# Insert boundary conditions
u[0] = 0; u[Nx] = 0
if user_action is not None:
if user_action(u, x, t, n+1):
break
# Switch variables before next step
u_2[:] = u_1; u_1[:] = u
cpu_time = t0 - time.clock()
return u, x, t, cpu_time
def viz(
I, V, f, c, L, dt, C, T, # PDE paramteres
umin, umax, # Interval for u in plots
animate=True, # Simulation with animation?
tool='matplotlib', # 'matplotlib' or 'scitools'
solver_function=solver, # Function with numerical algorithm
version='vectorized', # 'scalar' or 'vectorized'
):
import wave1D_u0
if version == 'vectorized':
# Reuse viz from wave1D_u0, but with the present
# modules' new vectorized solver (which has
# version='vectorized' as default argument;
# wave1D_u0.viz does not feature this argument)
cpu = wave1D_u0.viz(
I, V, f, c, L, dt, C, T, umin, umax,
animate, tool, solver_function=solver)
elif version == 'scalar':
# Call wave1D_u0.viz with a solver with
# scalar code and use wave1D_u0.solver.
cpu = wave1D_u0.viz(
I, V, f, c, L, dt, C, T, umin, umax,
animate, tool,
solver_function=wave1D_u0.solver)
# Method 2: wrap this module's solver with an extra
# argument version='scalar'
#import functools
#scalar_solver = functools.partial(scalar, version='scalar')
return cpu
def test_quadratic():
"""
Check the scalar and vectorized versions work for
a quadratic u(x,t)=x(L-x)(1+t/2) that is exactly reproduced.
"""
# The following function must work for x as array or scalar
u_exact = lambda x, t: x*(L - x)*(1 + 0.5*t)
I = lambda x: u_exact(x, 0)
V = lambda x: 0.5*u_exact(x, 0)
# f is a scalar (zeros_like(x) works for scalar x too)
f = lambda x, t: np.zeros_like(x) + 2*c**2*(1 + 0.5*t)
L = 2.5
c = 1.5
C = 0.75
Nx = 3 # Very coarse mesh for this exact test
dt = C*(L/Nx)/c
T = 18
def assert_no_error(u, x, t, n):
u_e = u_exact(x, t[n])
tol = 1E-13
diff = np.abs(u - u_e).max()
assert diff < tol
solver(I, V, f, c, L, dt, C, T,
user_action=assert_no_error, version='scalar')
solver(I, V, f, c, L, dt, C, T,
user_action=assert_no_error, version='vectorized')
def guitar(C):
"""Triangular wave (pulled guitar string)."""
L = 0.75
x0 = 0.8*L
a = 0.005
freq = 440
wavelength = 2*L
c = freq*wavelength
omega = 2*pi*freq
num_periods = 1
T = 2*pi/omega*num_periods
# Choose dt the same as the stability limit for Nx=50
dt = L/50./c
def I(x):
return a*x/x0 if x < x0 else a/(L-x0)*(L-x)
umin = -1.2*a; umax = -umin
cpu = viz(I, 0, 0, c, L, dt, C, T, umin, umax, animate=True)
def run_efficiency_experiments():
L = 1
x0 = 0.8*L
a = 1
c = 2
T = 8
C = 0.9
umin = -1.2*a; umax = -umin
def I(x):
return a*x/x0 if x < x0 else a/(L-x0)*(L-x)
intervals = []
speedup = []
for Nx in [50, 100, 200, 400, 800]:
dx = float(L)/Nx
dt = C/c*dx
print 'solving scalar Nx=%d' % Nx,
cpu_s = viz(I, 0, 0, c, L, dt, C, T, umin, umax,
animate=False, version='scalar')
print cpu_s
print 'solving vectorized Nx=%d' % Nx,
cpu_v = viz(I, 0, 0, c, L, dt, C, T, umin, umax,
animate=False, version='vectorized')
print cpu_v
intervals.append(Nx)
speedup.append(cpu_s/float(cpu_v))
print 'Nx=%3d: cpu_v/cpu_s: %.3f' % (Nx, 1./speedup[-1])
print 'Nx:', intervals
print 'Speed-up:', speedup
if __name__ == '__main__':
test_quadratic() # verify
import sys
try:
C = float(sys.argv[1])
print 'C=%g' % C
except IndexError:
C = 0.85
guitar(C)
#run_efficiency_experiments()

Xet Storage Details

Size:
6.71 kB
·
Xet hash:
68ead828929d944264dffd83f4c4f4622af6b70eac564f58e446010fbc90f749

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.