因此,我尝试计算来自耦合质量弹簧系统的质量该系统是线性的:,因此分析 -th 时间导数是,这可以轻松估计误差.


















在我不一定引以为豪的“努力”方法中,我测试了以数字方式计算这些导数的不同方法:
- 使用样条拟合,然后可以区分
gradient在求解器的输出时间向量上使用有限差分(来自 Python 包 Numpy)
- 再次使用有限差分,但使用求解器的连续扩展在更精细的时间网格上插值的解
- 再次使用有限差分,但使用样条插值器在更精细的时间网格上插值的解
Runge-Kutta 方法的连续扩展/密集输出的快速方面:许多 RK 方法都配备了密集输出能力,它使用内部阶段值来计算离散解的高阶插值,具有阶插值误差低于或等于方法的阶数。我看看能不能找到参考。
这是代码:
import numpy as np
import matplotlib.pyplot as plt
import numpy.linalg
import scipy.integrate
# System of two coupled masses linked by springs
k1=20;k2=300;m1=1;m2=5
A = np.array([[0,1,0,0],
[-(k1+k2)/m1, 0, k2/m1, 0],
[0,0,0,1],
[k2/m2,0,-k2/m2,0]])
def f(t,x):
return A.dot(x)
def jacfun(t,x):
return A
def deriv_sol(t,x,order):
""" Compute the n-th time derivative of the solution"""
temp = np.copy(x)
for i in range(order):
temp = A.dot(temp)
return temp
x0 = np.array([1, 0, 0, 0]) # initial condition
tf = 0.25 # physical time simulated
tol = 1e-8
# compute numerical solution with adaptive time stepping
sol = scipy.integrate.solve_ivp(fun=f, y0=x0, t_span=(0,tf), method='RK45',
atol=tol, rtol=tol, jac=jacfun,
dense_output=True)
x1,v1,x2,v2 = sol.y
#%% various ways to ompute the high-order time derivatives
t_test = sol.t
sol_interp = sol.sol(t_test) # high-order continuous extension of the solve_ivp method
## 1 - true solution using the fact that the system is linear
true_result = [deriv_sol(t_test, sol_interp, order=i)[0,:] for i in range(4)]
## 2 - using a spline interpolator
import scipy.interpolate as interp
tck = interp.splrep(sol.t, x1) # get the spline fit
spline_result = [interp.splev(t_test, tck, der=i) for i in range(4)]
## 3 - Finite difference on the solver time grid
grad_result = [sol_interp[0,:]]
for ider in range(1,4):
grad_result.append( np.gradient(grad_result[-1], t_test))
## 3.5 - Finite difference of the solution interpolated on a finer grid (using continuous extension)
t_test_fine = np.linspace(0, tf, int(1e4))
sol_interp_fine= sol.sol(t_test_fine) # high-order continuous extension of the solve_ivp method
grad_result_fine = [sol_interp_fine[0,:]]
for ider in range(1,4):
grad_result_fine.append( np.gradient(grad_result_fine[-1], t_test_fine))
# reinterp on the initial grid so that we can compare with the other solutions
for ider in range(4):
tck = interp.splrep(t_test_fine, grad_result_fine[ider]) # get the spline fit
grad_result_fine[ider] = interp.splev(t_test, tck)
## 3.5.5 - Finite difference of the solution interpolated on a finer grid (using splines)
tck = interp.splrep(sol.t, x1) # get the spline fit
sol_interp_fine_spline = interp.splev(t_test_fine, tck)
grad_result_fine_spline = [ sol_interp_fine[0,:] ]
for ider in range(1,4):
grad_result_fine_spline.append( np.gradient(grad_result_fine_spline[-1], t_test_fine))
# reinterp on the initial grid so that we can compare with the other solutions
for ider in range(4):
tck = interp.splrep(t_test_fine, grad_result_fine_spline[ider]) # get the spline fit
grad_result_fine_spline[ider] = interp.splev(t_test, tck)
## plot the values
various_solutions = ((spline_result, 'spline', '-'),
(grad_result, 'grad coarse', '-'),
(grad_result_fine, 'grad fine (cont ext)', '-'),
(grad_result_fine_spline, 'grad fine (spline)', '--'))
selected_derivatives = range(1,4)
fig, ax = plt.subplots(len(selected_derivatives),1,sharex=True, dpi=300)
for iax, ider in enumerate(selected_derivatives):
cax = ax[iax]
for val,name, linestyle in various_solutions:
cax.plot(t_test, val[ider], label=name, linestyle=linestyle)
cax.plot(t_test, true_result[ider], label='analytical')
cax.set_ylim(1.1*np.min(true_result[ider]), 1.1*np.max(true_result[ider]))
if ider==1:
cax.legend()
cax.grid()
cax.set_ylabel(r'$\frac{{ d^{{{}}} x_1 }}{{ dt^{{{}}} }}$'.format(ider, ider), rotation='horizontal')
fig.suptitle(r'Successive time derivatives of $x_1$')
plt.tight_layout()
## plot the error
fig, ax = plt.subplots(len(selected_derivatives),1,sharex=True, dpi=300)
for iax, ider in enumerate(selected_derivatives):
cax = ax[iax]
for val,name,linestyle in various_solutions:
cax.semilogy(t_test, np.abs((val[ider]-true_result[ider])/true_result[ider]),
label=name, linestyle=linestyle)
if ider==1:
cax.legend()
cax.grid()
cax.set_ylabel(r'$\frac{{ d^{{{}}} x_1 }}{{ dt^{{{}}} }}$'.format(ider, ider), rotation='horizontal')
cax.yaxis.get_major_locator().numticks = 4
fig.suptitle('Relative errors wrt to analytical values')
plt.tight_layout()
以下是在积分容差设置为的情况下计算解决方案时产生的图:





这里是更宽松的集成容差():





这表明,使用这些技术,如果要计算合理的高阶时间导数,则需要具有良好的时间分辨率。