我一直试图解决一些PDE方程,虽然我已经能够使用有限差分方法成功地做出像扩散或波动方程这样的简单方程式,但是我在高阶PDE方面遇到了困难。
例如,让我们说我希望解决以下等式
U_{xxxx}-x-2x*x_{tx}-2x_{xxtt}-x_{tt}=0
到目前为止,我的经验只是明显的有限差异(主要使用中心差异)。对于像这样的微分方程,这显然是不可能的。
我能找到的大多数资源只能用于二阶导数,而且它们通常不是耦合的。此外,可能有一种比使用有限差分法更有效的方法来解决这样的方程式。
我主要使用的是使用基本波动方程式的教科书中的代码,但它可能会被修剪或完全替换以便更好地使用;我甚至不确定它是否可以完全用于我更复杂的案例。
def solver(I, V, f, c,U_0,U_L, L, dt, C, T, user_action=None):
"""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; dt2 = dt*dt # Help variable in the scheme
# Make sure dx and dt are compatible with x and t
dx = x[1] - x[0]
dt = t[1] - t[0]
if f is None or f == 0 :
f = (lambda x, t: 0)
if V is None or V == 0:
V = (lambda x: 0)
if I is None or I == 0:
I = (lambda x: 0)
if U_0 is not None:
if isinstance(U_0, (float,int)) and U_0 == 0:
U_0 = lambda t: 0
# else: U_0(t) is a function
if U_L is not None:
if isinstance(U_L, (float,int)) and U_L == 0:
U_L = lambda t: 0
u = np.zeros(Nx+1) # Solution array at new time level
u_n = np.zeros(Nx+1) # Solution at 1 time level back
u_nm1 = np.zeros(Nx+1) # Solution at 2 time levels back
Ix = range(0, Nx+1)
It = range(0, Nt+1)
import time; t0 = time.clock() # Measure CPU time
# Load initial condition into u_n
for i in Ix:
u_n[i] = I(x[i])
if user_action is not None:
user_action(u_n, x, t, 0)
# Special formula for the first step
for i in Ix[1:-1]:
u[i] = u_n[i] - dt*V(x[i]) + \
0.5*C2*(u_n[i-1] - 2*u_n[i] + u_n[i+1]) + \
0.5*dt2*f(x[i], t[0])
i = Ix[0]
if U_0 is None:
# Set boundary values du/dn = 0
# x=0: i-1 -> i+1 since u[i-1]=u[i+1]
# x=L: i+1 -> i-1 since u[i+1]=u[i-1])
ip1 = i+1
im1 = ip1 # i-1 -> i+1
u[i] = u_n[i] -dt*V(x[i]) + \
0.5*C2*(u_n[im1] - 2*u_n[i] + u_n[ip1]) + \
0.5*dt2*f(x[i], t[0])
else:
u[0] = U_0(dt)
i = Ix[-1]
if U_L is None:
im1 = i-1
ip1 = im1 # i+1 -> i-1
u[i] = u_n[i] + dt*V(x[i]) + \
0.5*C2*(u_n[im1] - 2*u_n[i] + u_n[ip1]) + \
0.5*dt2*f(x[i], t[0])
else:
u[i] = U_L(dt)
if user_action is not None:
user_action(u, x, t, 1)
# Switch variables before next step
u_nm1, u_n, u = u_n, u, u_nm1
for n in It[1:-1]:
# Update all inner points
for i in Ix[1:-1]:
u[i] = - u_nm1[i] + 2*u_n[i] + \
C2*(u_n[i-1] - 2*u_n[i] + u_n[i+1]) + \
dt2*f(x[i], t[n])
i = Ix[0]
if U_0 is None:
# Set boundary values
# x=0: i-1 -> i+1 since u[i-1]=u[i+1] when du/dn=0
# x=L: i+1 -> i-1 since u[i+1]=u[i-1] when du/dn=0
ip1 = i+1
im1 = ip1
u[i] = - u_nm1[i] + 2*u_n[i] + \
C2*(u_n[im1] - 2*u_n[i] + u_n[ip1]) + \
dt2*f(x[i], t[n])
else:
u[0] = U_0(t[n+1])
i = Ix[-1]
if U_L is None:
im1 = i-1
ip1 = im1
u[i] = - u_nm1[i] + 2*u_n[i] + \
C2*(u_n[im1] - 2*u_n[i] + u_n[ip1]) + \
dt2*f(x[i], t[n])
else:
u[i] = U_L(t[n+1])
if user_action is not None:
if user_action(u, x, t, n+1):
break
# Update data structures for next step
#u_nm1[:] = u_n; u_n[:] = u # safe, but slower
u_nm1, u_n, u = u_n, u, u_nm1
u = u_n
cpu_time = time.clock() - t0
return u, x, t, cpu_time