Python 为什么不是';t这个中心的四阶精确有限差分格式产生解偏微分方程的四阶收敛性
我用有限差分格式解耗散方程。初始条件为半正弦波,两侧为Dirchlet边界条件。我在域的每一侧插入一个额外的点来强制Dirchlet边界条件,同时保持四阶精度,然后使用前向euler对其进行时间演化 当我从二阶精度模具切换到四阶精度模具/12时 当我绘制与误差估计相比的曲线时,我看不到收敛速度的改善 我编写并注释了一段代码,显示了我的问题。当我使用5点策略时,我的收敛速度是相同的: 为什么会这样?为什么四阶精确模具不帮助收敛速度?我仔细地梳理了这一点,我认为我的理解中肯定存在一些问题Python 为什么不是';t这个中心的四阶精确有限差分格式产生解偏微分方程的四阶收敛性,python,differential-equations,calculus,convergence,Python,Differential Equations,Calculus,Convergence,我用有限差分格式解耗散方程。初始条件为半正弦波,两侧为Dirchlet边界条件。我在域的每一侧插入一个额外的点来强制Dirchlet边界条件,同时保持四阶精度,然后使用前向euler对其进行时间演化 当我从二阶精度模具切换到四阶精度模具/12时 当我绘制与误差估计相比的曲线时,我看不到收敛速度的改善 我编写并注释了一段代码,显示了我的问题。当我使用5点策略时,我的收敛速度是相同的: 为什么会这样?为什么四阶精确模具不帮助收敛速度?我仔细地梳理了这一点,我认为我的理解中肯定存在一些问题 # L
# Let's evolve the diffusion equation in time with Dirchlet BCs
# Load modules
import numpy as np
import matplotlib.pyplot as plt
# Domain size
XF = 1
# Viscosity
nu = 0.01
# Spatial Differentiation function, approximates d^u/dx^2
def diffusive_dudt(un, nu, dx, strategy='5c'):
undiff = np.zeros(un.size, dtype=np.float128)
# O(h^2)
if strategy == '3c':
undiff[2:-2] = nu * (un[3:-1] - 2 * un[2:-2] + un[1:-3]) / dx**2
# O(h^4)
elif strategy == '5c':
undiff[2:-2] = nu * (-1 * un[4:] + 16 * un[3:-1] - 30 * un[2:-2] + 16 * un[1:-3] - un[:-4]) / (12 * dx**2 )
else: raise(IOError("Invalid diffusive strategy")) ; quit()
return undiff
def geturec(x, nu=.05, evolution_time=1, u0=None, n_save_t=50, ubl=0., ubr=0., diffstrategy='5c', dt=None, returndt=False):
dx = x[1] - x[0]
# Prescribde cfl=0.1 and ftcs=0.2
if dt is not None: pass
else: dt = min(.1 * dx / 1., .2 / nu * dx ** 2)
if returndt: return dt
nt = int(evolution_time / dt)
divider = int(nt / float(n_save_t))
if divider ==0: raise(IOError("not enough time steps to save %i times"%n_save_t))
# The default initial condition is a half sine wave.
u_initial = ubl + np.sin(x * np.pi)
if u0 is not None: u_initial = u0
u = u_initial
u[0] = ubl
u[-1] = ubr
# insert ghost cells; extra cells on the left and right
# for the edge cases of the finite difference scheme
x = np.insert(x, 0, x[0]-dx)
x = np.insert(x, -1, x[-1]+dx)
u = np.insert(u, 0, ubl)
u = np.insert(u, -1, ubr)
# u_record holds all the snapshots. They are evenly spaced in time,
# except the final and initial states
u_record = np.zeros((x.size, int(nt / divider + 2)))
# Evolve through time
ii = 1
u_record[:, 0] = u
for _ in range(nt):
un = u.copy()
dudt = diffusive_dudt(un, nu, dx, strategy=diffstrategy)
# forward euler time step
u = un + dt * dudt
# Save every xth time step
if _ % divider == 0:
#print "C # ---> ", u * dt / dx
u_record[:, ii] = u.copy()
ii += 1
u_record[:, -1] = u
return u_record[1:-1, :]
# define L-1 Norm
def ul1(u, dx): return np.sum(np.abs(u)) / u.size
# Now let's sweep through dxs to find convergence rate
# Define dxs to sweep
xrang = np.linspace(350, 400, 4)
# this function accepts a differentiation key name and returns a list of dx and L-1 points
def errf(strat):
# Lists to record dx and L-1 points
ypoints = []
dxs= []
# Establish truth value with a more-resolved grid
x = np.linspace(0, XF, 800) ; dx = x[1] - x[0]
# Record truth L-1 and dt associated with finest "truth" grid
trueu = geturec(nu=nu, x=x, diffstrategy=strat, evolution_time=2, n_save_t=20, ubl=0, ubr=0)
truedt = geturec(nu=nu, x=x, diffstrategy=strat, evolution_time=2, n_save_t=20, ubl=0, ubr=0, returndt=True)
trueqoi = ul1(trueu[:, -1], dx)
# Sweep dxs
for nx in xrang:
x = np.linspace(0, XF, nx) ; dx = x[1] - x[0]
dxs.append(dx)
# Run solver, hold dt fixed
u = geturec(nu=nu, x=x, diffstrategy='5c', evolution_time=2, n_save_t=20, ubl=0, ubr=0, dt=truedt)
# record |L-1(dx) - L-1(truth)|
qoi = ul1(u[:, -1], dx)
ypoints.append(np.abs(trueqoi - qoi))
return dxs, ypoints
# Plot results. The fourth order method should have a slope of 4 on the log-log plot.
from scipy.optimize import minimize as mini
strategy = '5c'
dxs, ypoints = errf(strategy)
def fit2(a): return 1000 * np.sum((a * np.array(dxs) ** 2 - ypoints) ** 2)
def fit4(a): return 1000 * np.sum((np.exp(a) * np.array(dxs) ** 4 - ypoints) ** 2)
a = mini(fit2, 500).x
b = mini(fit4, 11).x
plt.plot(dxs, a * np.array(dxs)**2, c='k', label=r"$\nu^2$", ls='--')
plt.plot(dxs, np.exp(b) * np.array(dxs)**4, c='k', label=r"$\nu^4$")
plt.plot(dxs, ypoints, label=r"Convergence", marker='x')
plt.yscale('log')
plt.xscale('log')
plt.xlabel(r"$\Delta X$")
plt.ylabel("$L-L_{true}$")
plt.title(r"$\nu=%f, strategy=%s$"%(nu, strategy))
plt.legend()
plt.savefig('/Users/kilojoules/Downloads/%s.pdf'%strategy, bbox_inches='tight')
该方案的误差为
O(dt,dx²)
resp<代码>O(dt,dx⁴)。当您保持dt=O(dx^2)
时,两种情况下的组合误差都是O(dx²)。您可以尝试缩放dt=O(dx⁴)
然而,在第二种情况下,欧拉或任何一阶方法的截断和浮点误差的平衡在L*dt=1e-8
左右达到,其中L
是右侧的Lipschitz常数,因此对于更复杂的右侧更高。即使在最好的情况下,超过dx=0.01
也是徒劳的。使用时间方向上的高阶方法应该有帮助。该方案的误差为O(dt,dx²)
resp.O(dt,dx²)⁴)
。当您保持dt=O(dx^2)
时,两种情况下的组合误差都是O(dx²)。您可以尝试缩放dt=O(dx⁴)
然而,在第二种情况下,欧拉或任何一阶方法的截断和浮点误差的平衡在L*dt=1e-8
左右达到,其中L
是右侧的Lipschitz常数,因此对于更复杂的右侧更高。即使在最好的情况下,超过dx=0.01
也是徒劳的。使用g时间方向上的高阶方法应该会有所帮助。您使用了错误的误差度量。如果您逐点比较字段,您将得到您所追求的收敛速度。您使用了错误的误差度量。如果您逐点比较字段,您将得到您所追求的收敛速度。为什么要改变差分近似的阶数对收敛速度有任何影响?这不是阶数所指的吗?O(dx^2)意味着误差将与dx的平方成比例,对吗?如果是这样,误差与dx在对数轴上的斜率将为2,我希望是O(dx^4)斜率为4。阶数指的是空间近似阶数。这并不能说明收敛速度。@Talonmes你能解释为什么吗?我用四阶runge-kutta时间微分方案尝试了这一点,得到了相同的结果。我不明白这为什么不起作用。两件事。保持状态解应该是是抛物线的,所以使用高阶差分项对误差的表示性不应有显著的变化,并且,正如您定义的那样,误差和收敛性可能会受到时间积分格式的截断影响大得多。为什么改变差分近似的阶数对收敛性有任何影响ate?这不是顺序所指的吗?O(dx^2)意味着误差将与dx的平方成比例,对吗?如果是这样,误差与dx在对数轴上的斜率为2,我希望是O(dx^4)斜率为4。阶数指的是空间近似阶数。这并不能说明收敛速度。@Talonmes你能解释为什么吗?我用四阶runge-kutta时间微分方案尝试了这一点,得到了相同的结果。我不明白这为什么不起作用。两件事。保持状态解应该是是抛物线型的,因此使用高阶差分项不会对误差的代表性产生重大变化,并且误差和收敛性,正如您所定义的,可能更受时间积分模式的截断的影响。What isdt=O(dx^2)
?我用龙格-库塔四阶精确格式尝试了相同的设置,得到了相同的结果。通过设置dt=min(.1*dx/1.,.2/nu*dx**2)
可以得到dt=O(dx²)
。对于第二个问题,必须检查空间离散化是否为O(dx>⁴)代码>也在边界处,因为那里的错误会影响整体错误。您能否提供dt=O(dx²)的来源
point?我认为CFL和FTCS数字是为了保证稳定性,与订单无关。我已将时间步长固定为与代码中最小网格相关的时间步长。如果我没有这样做,我明白为什么它应该以O(dx^2)
进行缩放。dt=O(dx^2)如何
?我用龙格-库塔四阶精确格式尝试了相同的设置,得到了相同的结果。通过设置dt=min(.1*dx/1.,.2/nu*dx**2)
可以得到dt=O(dx²)
。对于第二个问题,必须检查空间离散化是否为O(dx>⁴)代码>也在边界处,因为那里的错误会影响整体错误。您能否提供dt=O(dx²)的来源
point?我认为CFL和FTCS编号是为了保证稳定性,与订单无关。我已将时间步长固定为与代码中最小网格相关的时间步长。如果我没有这样做,我明白为什么它应该以O(dx^2)
进行缩放。