download
raw
9.9 kB
#!/usr/bin/env python
"""
Functions for solving a 1D diffusion equations of simplest types
(constant coefficient, no source term):
u_t = a*u_xx on (0,L)
with boundary conditions u=0 on x=0,L, for t in (0,T].
Initial condition: u(x,0)=I(x).
The following naming convention of variables are used.
===== ==========================================================
Name Description
===== ==========================================================
Nx The total number of mesh cells; mesh points are numbered
from 0 to Nx.
F The dimensionless number a*dt/dx**2, which implicitly
specifies the time step.
T The stop time for the simulation.
I Initial condition (Python function of x).
a Variable coefficient (constant).
L Length of the domain ([0,L]).
x Mesh points in space.
t Mesh points in time.
n Index counter in time.
u Unknown at current/new time level.
u_1 u at the previous time level.
dx Constant mesh spacing in x.
dt Constant mesh spacing in t.
===== ==========================================================
user_action is a function of (u, x, t, n), u[i] is the solution at
spatial mesh point x[i] at time t[n], where the calling code
can add visualization, error computations, data analysis,
store solutions, etc.
"""
from scipy.sparse import spdiags
from scipy.sparse.linalg import spsolve, use_solver
def solver_FE_simple(I, a, L, Nx, F, T):
"""
Simplest expression of the computational algorithm
using the Forward Euler method and explicit Python loops.
For this method F <= 0.5 for stability.
"""
x = linspace(0, L, Nx+1) # mesh points in space
dx = x[1] - x[0]
dt = F*dx**2/a
Nt = int(round(T/float(dt)))
t = linspace(0, T, Nt+1) # mesh points in time
u = zeros(Nx+1)
u_1 = zeros(Nx+1)
# Set initial condition u(x,0) = I(x)
for i in range(0, Nx+1):
u_1[i] = I(x[i])
for n in range(0, Nt):
# Compute u at inner mesh points
for i in range(1, Nx):
u[i] = u_1[i] + F*(u_1[i-1] - 2*u_1[i] + u_1[i+1])
# Insert boundary conditions
u[0] = 0; u[Nx] = 0
# Switch variables before next step
u_1, u = u, u_1
return u
def solver_FE(I, a, L, Nx, F, T,
user_action=None, version='scalar'):
"""
Vectorized implementation of solver_FE_simple.
"""
import time
t0 = time.clock()
x = linspace(0, L, Nx+1) # mesh points in space
dx = x[1] - x[0]
dt = F*dx**2/a
Nt = int(round(T/float(dt)))
t = linspace(0, T, Nt+1) # mesh points in time
u = zeros(Nx+1) # solution array
u_1 = zeros(Nx+1) # solution at t-dt
u_2 = zeros(Nx+1) # solution at t-2*dt
# Set initial condition
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)
for n in range(0, Nt):
# Update all inner points
if version == 'scalar':
for i in range(1, Nx):
u[i] = u_1[i] + F*(u_1[i-1] - 2*u_1[i] + u_1[i+1])
elif version == 'vectorized':
u[1:Nx] = u_1[1:Nx] + \
F*(u_1[0:Nx-1] - 2*u_1[1:Nx] + u_1[2:Nx+1])
else:
raise ValueError('version=%s' % version)
# Insert boundary conditions
u[0] = 0; u[Nx] = 0
if user_action is not None:
user_action(u, x, t, n+1)
# Switch variables before next step
#u_1[:] = u # slow
u_1, u = u, u_1
t1 = time.clock()
return u, x, t, t1-t0
def solver_BE_simple(I, a, L, Nx, F, T):
"""
Simplest expression of the computational algorithm
for the Backward Euler method, using explicit Python loops
and a dense matrix format for the coefficient matrix.
"""
x = linspace(0, L, Nx+1) # mesh points in space
dx = x[1] - x[0]
dt = F*dx**2/a
Nt = int(round(T/float(dt)))
t = linspace(0, T, Nt+1) # mesh points in time
u = zeros(Nx+1)
u_1 = zeros(Nx+1)
# Data structures for the linear system
A = zeros((Nx+1, Nx+1))
b = zeros(Nx+1)
for i in range(1, Nx):
A[i,i-1] = -F
A[i,i+1] = -F
A[i,i] = 1 + 2*F
A[0,0] = A[Nx,Nx] = 1
# Set initial condition u(x,0) = I(x)
for i in range(0, Nx+1):
u_1[i] = I(x[i])
for n in range(0, Nt):
# Compute b and solve linear system
for i in range(1, Nx):
b[i] = -u_1[i]
b[0] = b[Nx] = 0
u[:] = linalg.solve(A, b)
# Switch variables before next step
u_1, u = u, u_1
return u
def solver_BE(I, a, L, Nx, F, T, user_action=None):
"""
Vectorized implementation of solver_BE_simple using also
a sparse (tridiagonal) matrix for efficiency.
"""
import time
t0 = time.clock()
x = linspace(0, L, Nx+1) # mesh points in space
dx = x[1] - x[0]
dt = F*dx**2/a
Nt = int(round(T/float(dt)))
t = linspace(0, T, Nt+1) # mesh points in time
u = zeros(Nx+1) # solution array at t[n+1]
u_1 = zeros(Nx+1) # solution at t[n]
# Representation of sparse matrix and right-hand side
diagonal = zeros(Nx+1)
lower = zeros(Nx+1)
upper = zeros(Nx+1)
b = zeros(Nx+1)
# "Active" values: diagonal[:], upper[1:], lower[:-1]
# Precompute sparse matrix
diagonal[:] = 1 + 2*F
lower[:] = -F #1
upper[:] = -F #1
# Insert boundary conditions
diagonal[0] = 1
diagonal[Nx] = 1
# Remove unused/inactive values
upper[0:2] = 0
lower[-2:] = 0
diags = [0, -1, 1]
A = spdiags([diagonal, lower, upper], diags, Nx+1, Nx+1)
print A.todense()
# Set initial condition
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)
for n in range(0, Nt):
b = u_1
b[0] = b[-1] = 0.0 # boundary conditions
u[:] = spsolve(A, b)
if user_action is not None:
user_action(u, x, t, n+1)
# Switch variables before next step
u_1, u = u, u_1
t1 = time.clock()
return u, x, t, t1-t0
def solver_theta(I, a, L, Nx, F, T, theta=0.5, u_L=0, u_R=0,
user_action=None):
"""
Full solver for the model problem using the theta-rule
difference approximation in time (no restriction on F,
i.e., the time step when theta >= 0.5).
Vectorized implementation and sparse (tridiagonal)
coefficient matrix.
"""
import time
t0 = time.clock()
x = linspace(0, L, Nx+1) # mesh points in space
dx = x[1] - x[0]
dt = F*dx**2/a
Nt = int(round(T/float(dt)))
t = linspace(0, T, Nt+1) # mesh points in time
u = zeros(Nx+1) # solution array at t[n+1]
u_1 = zeros(Nx+1) # solution at t[n]
# Representation of sparse matrix and right-hand side
diagonal = zeros(Nx+1)
lower = zeros(Nx+1)
upper = zeros(Nx+1)
b = zeros(Nx+1)
# "Active" values: diagonal[:], upper[1:], lower[:-1]
# Precompute sparse matrix (scipy format)
diagonal[:] = 1 + 2*Fl
lower[:] = -Fl #1
upper[:] = -Fl #1
# Insert boundary conditions
diagonal[0] = 1
diagonal[Nx] = 1
# Remove unused/inactive values
upper[0:2] = 0
lower[-2:] = 0
diags = [0, -1, 1]
A = spdiags([diagonal, lower, upper], diags, Nx+1, Nx+1)
#print A.todense()
# Set initial condition
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)
# Time loop
for n in range(0, Nt):
b[1:-1] = u_1[1:-1] + Fr*(u_1[:-2] - 2*u_1[1:-1] + u_1[2:])
b[0] = u_L; b[-1] = u_R # boundary conditions
u[:] = spsolve(A, b)
if user_action is not None:
user_action(u, x, t, n+1)
# Switch variables before next step
u_1, u = u, u_1
t1 = time.clock()
return u, x, t, t1-t0
def viz(I, a, L, Nx, F, T, umin, umax,
scheme='FE', animate=True):
def plot_u(u, x, t, n):
plot(x, u, 'r-', axis=[0, L, umin, umax], title='t=%f' % t[n])
if t[n] == 0:
time.sleep(2)
else:
time.sleep(0.2)
user_action = plot_u if animate else lambda u,x,t,n: None
u, x, t, cpu = eval('solver_'+scheme)\
(I, a, L, Nx, F, T,
user_action=user_action)
return u, cpu
def plug(scheme='FE', F=0.5, Nx=50):
L = 1.
a = 1
T = 0.1
def I(x):
"""Plug profile as initial condition."""
if abs(x-L/2.0) > 0.1:
return 0
else:
return 1
u, cpu = viz(I, a, L, Nx, F, T,
umin=-0.1, umax=1.1,
scheme=scheme, animate=True)
print 'CPU time:', cpu
"""
if not allclose(solutions[0], solutions[-1],
atol=1.0E-10, rtol=1.0E-12):
print 'error in computations'
else:
print 'correct solution'
"""
def expsin(scheme='FE', F=0.5, m=3):
L = 10.0
a = 1
T = 1.2
def exact(x, t):
return exp(-m**2*pi**2*a/L**2*t)*sin(m*pi/L*x)
def I(x):
return exact(x, 0)
Nx = 80
viz(I, a, L, Nx, F, T, -1, 1, scheme=scheme, animate=True)
# Convergence study
def action(u, x, t, n):
e = abs(u - exact(x, t[n])).max()
errors.append(e)
errors = []
Nx_values = [10, 20, 40, 80, 160]
for Nx in Nx_values:
eval('solver_'+scheme)(I, a, L, Nx, F, T, user_action=action)
dt = F*(L/Nx)**2/a
print dt, errors[-1]
if __name__ == '__main__':
import sys, time
from scitools.std import *
if len(sys.argv) < 2:
print """Usage %s function arg1 arg2 arg3 ...""" % sys.argv[0]
sys.exit(0)
cmd = '%s(%s)' % (sys.argv[1], ', '.join(sys.argv[2:]))
print cmd
eval(cmd)

Xet Storage Details

Size:
9.9 kB
·
Xet hash:
38b08d968ad723f823949e41da90aa2c841be926d6ff6828b72f6fd756ea859c

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