使用 scipy.integrate.solve_ivp 时出错:索引错误:索引 0 超出轴 0 的范围,大小为 0

计算科学 Python 数字 scipy 数值限制
2021-12-08 22:46:17

我最近正在研究基于 Python 中的粘滑现象的代码。这是https://www.sciencedirect.com/science/article/pii/S0888327020301205中的粘滑振荡器(第 3.4 章)我的目标是实现如图 9 所示的情节。问题是经过一些计算后,我不断收到以下错误:

Traceback (most recent call last):
  File "stick_slip.py", line 89, in <module>
    cur_y = list(sol.y_events[-1][0])
IndexError: index 0 is out of bounds for axis 0 with size 0

而且我不知道问题是什么,所以如果你能帮助我解决这个错误并从我的代码中消除它或者告诉我应该在这里更正什么以获得预期的结果,我将非常感激。

代码如下。所以我从包含库开始,然后定义参数、初始条件等。

#libraries, etc.
import scipy as sp
import numpy as np
import math
import matplotlib.pyplot as plt
from scipy.integrate import odeint 
from scipy.integrate import solve_ivp
#define variables and parameters
eta=2.088
v0=1
fs=8
fk=5.0
epsilon = 1e-12

period = (2.0*np.pi)/eta
transient_periods = 40
periods_to_save = 100
points_per_period = 100
tau_transient=[*np.linspace(0, transient_periods*period, transient_periods*points_per_period+1)]
tau_to_save = [*np.linspace(transient_periods*period, (transient_periods+periods_to_save)*period, points_per_period*periods_to_save+1)]
y=[0,0,0]
# t_span = (0,period,100000)
#tau_2=(period,2000,1000)

然后我将粘滞现象设置为全局变量。

is_stick = False

接下来,我根据文章中的条件定义了函数。那么当棒存在时方程的面是什么,如果不存在怎么办

def f(tau, y):
    dy3 = eta
    if is_stick:
        dy1=v0
        dy2=0
    elif np.abs(y[2] - v0) == 0:
        fff = fk*np.sign(y[0]-np.cos(y[3])) 
        dy1 = y[2]
        dy2 = -y[0]+np.cos(y[3])+fff
    else:
        fff = fk*np.sign(v0-y[2])
        dy1 = y[2]
        dy2 = -y[0]+np.cos(y[3])+fff
    
    return [dy1, dy2, dy3]     

然后我使用 scipy.integrate.solve_ivp 来制作一些事件,以便在事件发生时停止集成一个函数,然后转移到另一个函数等等。

#from slip to stick, detect slip and move to stick   
def sliptostick(tau, y): return y[2]-v0 
sliptostick.terminal=True 
sliptostick.direction=0
   

# detect a stick and move to slip 
def sticktoslip(tau, y): return abs(y[0] - np.cos(y[3])) - fs
sticktoslip.terminal=True
sticktoslip.direction=1
   

这里我设置了一些必要的值。

cur_tau = 0.0
cur_y = y[:]
tau_max = tau_transient[-1]

tau_list = []
y_list = []

然后是更新 y 的棒和值的部分。

def update_stick(y):
    global is_stick
    if np.abs(v0-y[2]) == 0.0 and abs(y[0]-np.cos(y[3]))-fs < 0:
        is_stick = True
    else:
        is_stick = False

update_stick(cur_y)

print(cur_tau)
print(tau_max)
#input()

现在它是带有while循环的代码部分,所以在这里,程序正在集成stick或slip函数(取决于发生的事件)。数据正在保存到适当的变量中。

while tau_transient:
    
    if(is_stick):
        sol = solve_ivp(f,(cur_tau, tau_max),y,events=sticktoslip, t_eval = tau_transient)
    else:
        print("cur_tau: " + str(cur_tau))
        print("cur_y: " + str(cur_y))
        print("tau_max: " + str(tau_max))
        print("tau_trans_len:" + str(len(tau_transient)))
        #input()
        sol = solve_ivp(f,(cur_tau, tau_max),y,events=sliptostick,t_eval = tau_transient)

    tau_list.extend([*sol.t])
    y_list.extend([[*q] for q in list((sol.y).T)])
    cur_y = list(sol.y_events[-1][0])
    cur_tau = sol.t_events[-1][0]  
    update_stick(cur_y)
    tau_transient = [local_t for local_t in tau_transient if local_t > cur_tau]

在这里,我绘制了结果。


plt.plot(tau_list, [q[0] for q in y_list],'k-',label='Y')
plt.plot(tau_list, [q[2] for q in y_list],'r-',label='dY/dt')
plt.xlabel('Time')
plt.legend()
plt.grid()
plt.show()

plt.plot([q[0] for q in y_list],[q[2] for q in y_list],'dodgerblue',markersize=1)
plt.xlabel('y1')
plt.ylabel('y2')
plt.grid()
plt.show()

1个回答

我相信这可以满足您的需求:

import math
import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt

#--------------------------------------
# Problem parameters
#--------------------------------------
# --- coefs ---------------------------
eta = 2.088
v0 = 1.
fs = 8.
fk = 5.
# --- time ----------------------------
period = (2.*np.pi) / eta
n_periods = 400
t_final = n_periods*period
max_timestep = 1e-3
# --- initial values ------------------
t = 0.
x = np.zeros(3)

#--------------------------------------
# ODE functions
#--------------------------------------
def f(x):
    """small f function."""
    if not math.isclose(x[1], v0):
        return fk * np.sign(v0 - x[1])

    value = x[0] - np.cos(x[2])

    if np.abs(value) >= fs:
        return fk * np.sign(value)

    return value


def fun(t, x):
    """ODE function dx/dt (t) = fun(t, x(t))."""
    return np.array([x[1], -x[0] + np.cos(x[2]) + f(x), eta])

#--------------------------------------
# Event detection
#--------------------------------------
def stickevent(t, x): 
    """detect sticking event."""
    return x[1]-v0

stickevent.terminal = True 


def slipevent(t, x): 
    """detect slip event."""
    return np.abs(x[0] - np.cos(x[2])) - fs

slipevent.terminal = True

def stick_phase(x):
    """check if we are sticking."""
    if math.isclose(x[1], v0):
        return True
    return False

#--------------------------------------
# Main loop
#--------------------------------------
t_grid = []
x_vals = []

max_iter = 1000
iter_count = 0

while t < t_final and iter_count < max_iter:
    
    if stick_phase(x): event = slipevent
    else: event = stickevent
    print(f"It: {iter_count+1} ### Sticking: {stick_phase(x)} ### Time: {t} of {t_final}", end='\r')
    
    sol = solve_ivp(fun, (t, t_final), x, events=event, max_step=max_timestep)

    t_grid.extend(sol.t)
    x_vals.extend((np.squeeze(y) for y in np.hsplit(sol.y, sol.y.shape[1])))
    
    t = t_grid[-1]
    x = x_vals[-1]

    iter_count += 1


#--------------------------------------
# Plotting
#--------------------------------------
x1 = [x[0] for x in x_vals]
x2 = [x[1] for x in x_vals]

plt.plot(x1, x2, color='k')
plt.xlabel('x1')
plt.ylabel('x2')
plt.savefig('stick-slip.jpg')

它产生:

阴谋