使用 odeint 求解 ODE 的意外解决方案

计算科学 Python 微分方程
2021-12-03 15:56:39

我正在尝试使用 scipy 的 odeint 求解一个由 8 个耦合微分方程组成的系统。我已经编写了我的代码并且运行良好,但是我得到的解决方案与我的预期完全不同。最初,我会在一个循环中编写所有这些方程式,但由于我遇到了问题,所以我将它们全部分开编写。

我需要传递的 Zd 参数是一个与我的时间向量大小相同的指数递减变量。但是,由于 odeint 一次只计算一个样本,所以我需要每次都给它正确的 Zd 样本,所以我使用 t 的副本在函数内部获取它。微分方程每个都有四个项,它们需要在不同的时间打开/关闭,所以我在那里使用阶跃函数。

这个方程组应该代表一种通过相邻系统的能量,因此八个 Z 函数是这些系统中每个系统内的能量随时间的演变。在 t=0 时,能量进入这些系统中的第一个,而其他系统则没有任何能量。Zd 表示输入到系统中的能量,它随时间变化(由于散射而呈指数衰减),并且需要在能量进入/离开每一层时打开/关闭。到我的时间窗口结束时,我预计几乎没有精力或根本没有精力。然而,我得到的 Z 几乎呈指数增长,这没有任何意义。

我一遍又一遍地检查它,但我看不出它有什么问题。等式中的符号和时间是正确的。知道这里有什么问题吗?

这是我的代码和结果图:

def ODE_solver(Z,t,Q,Zd,Z0,dts,ts,cf,t_copy):

    import numpy as np

    # Define functions to solve for:
    [Z1, Z2, Z3, Z4, Z5, Z6, Z7, Z8] = Z
    # Define time intervals:
    [dt1, dt2, dt3, dt4, dt5, dt6, dt7, dt8] = dts    
    # Define cumulative times:
    [t1, t2, t3, t4, t5, t6, t7, t8] = ts    
    # Q factors:
    [Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8] = Q

    # Set some boundary conditions:
    Z9=0; dt9=1/4 # (It doesn't matter which value I have here, since Z9 = 0)
    t0=0; dt0=1/4; 

    # Find index of the time sample that is being used at the moment:
    t_ind=(np.abs(t-t_copy)).argmin()
    Zd=Zd[t_ind]

    # j=1
    dZ1dt= ((1/(4*dt0)) * Z0 * step_fun (t,t0)) + ((1/(4*dt2)) * Z2 * step_fun (t,t0)) 
    - ((1/(4*dt1)) * Z1 * step_fun (t,t1)) - ((1/(4*dt1)) * Z1 * step_fun(t,t0)) 
    + (((2*np.pi*cf)*Q1) * Zd * step_fun(t,t0) * inv_step_fun(t,t1)) 

    # j=2
    dZ2dt= ((1/(4*dt1)) * Z1 * step_fun (t,t1)) + ((1/(4*dt3)) * Z3 * step_fun (t,t1)) 
    - ((1/(4*dt2)) * Z2 * step_fun (t,t2)) - ((1/(4*dt2)) * Z2 * step_fun(t,t1)) 
    + (((2*np.pi*cf)*Q2) * Zd * step_fun(t,t1) * inv_step_fun(t,t2)) 

    # j=3
    dZ3dt= ((1/(4*dt2)) * Z2 * step_fun (t,t2)) + ((1/(4*dt4)) * Z4 * step_fun (t,t2)) 
    - ((1/(4*dt3)) * Z3 * step_fun (t,t3)) - ((1/(4*dt3)) * Z3 * step_fun(t,t2)) 
    + (((2*np.pi*cf)*Q3) * Zd * step_fun(t,t2) * inv_step_fun(t,t3))     

    # j=4
    dZ4dt= ((1/(4*dt3)) * Z3 * step_fun (t,t3)) + ((1/(4*dt5)) * Z5 * step_fun (t,t3)) 
    - ((1/(4*dt4)) * Z4 * step_fun (t,t4)) - ((1/(4*dt4)) * Z4 * step_fun(t,t3)) 
    + (((2*np.pi*cf)*Q4) * Zd * step_fun(t,t3) * inv_step_fun(t,t4))   

    # j=5
    dZ5dt= ((1/(4*dt4)) * Z4 * step_fun (t,t4)) + ((1/(4*dt6)) * Z6 * step_fun (t,t4)) 
    - ((1/(4*dt5)) * Z5 * step_fun (t,t5)) - ((1/(4*dt5)) * Z5 * step_fun(t,t4)) 
    + (((2*np.pi*cf)*Q5) * Zd * step_fun(t,t4) * inv_step_fun(t,t5))  

    # j=6
    dZ6dt= ((1/(4*dt5)) * Z5 * step_fun (t,t5)) + ((1/(4*dt7)) * Z7 * step_fun (t,t5)) 
    - ((1/(4*dt6)) * Z6 * step_fun (t,t6)) - ((1/(4*dt6)) * Z6 * step_fun(t,t5)) 
    + (((2*np.pi*cf)*Q6) * Zd * step_fun(t,t5) * inv_step_fun(t,t6))  

    # j=7
    dZ7dt= ((1/(4*dt6)) * Z6 * step_fun (t,t6)) + ((1/(4*dt8)) * Z8 * step_fun (t,t6)) 
    - ((1/(4*dt7)) * Z7 * step_fun (t,t7)) - ((1/(4*dt7)) * Z7 * step_fun(t,t6)) 
    + (((2*np.pi*cf)*Q7) * Zd * step_fun(t,t6) * inv_step_fun(t,t7))  

    # j=8
    dZ8dt= ((1/(4*dt7)) * Z7 * step_fun (t,t7)) + ((1/(4*dt9)) * Z9 * step_fun (t,t7)) 
    - ((1/(4*dt8)) * Z8 * step_fun (t,t8)) - ((1/(4*dt8)) * Z8 * step_fun(t,t7)) 
    + (((2*np.pi*cf)*Q8) * Zd * step_fun(t,t7) * inv_step_fun(t,t8))  


    return [dZ1dt,dZ2dt,dZ3dt,dZ4dt,dZ5dt,dZ6dt,dZ7dt,dZ8dt]

def step_fun(x,a):

    if x==a:  return 0.5
    elif x<a: return 0
    elif x>a: return 1 


def inv_step_fun(x,a):

    if x==a:  return 0.5
    elif x<a: return 1
    elif x>a: return 0 

####################################################################################################

import copy
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint

# Number of equations:
N=8

# Define t:
t=np.arange(0,50,0.025)
t_copy=copy.deepcopy(t)
Nt=len(t)

# Define other parameters/arguments:
Q=(0.008017307188069223, 0.008117153569161557, 0.008081825341069762, 0.00803759248462624, 0.00803759248462624, 0.008081825341069762, 0.008117153569161557, 0.008017307188069223)
Z0=np.array([22050, 0., 0., 0., 0., 0., 0., 0.])
Zd0=Z0[0]
dts=np.array([20.67120623,  1.43225437,  1.55738981,  1.63132137,  1.63132137, 1.55738981,  1.43225437, 20.67120623])
ts=np.array([20.67120623, 22.10346059, 23.66085041, 25.29217178, 26.92349315, 28.48088296, 29.91313733, 50.58434356])
cf=1.5

# Define Zd: this is a vector of size Nt, but I will need to give the function only the right sample
# at each time t.
Zd=1/(1+np.exp(t))*5e4

# Solve system of differential equations: 
Z=odeint(ODE_solver,Z0,t,args=(Q,Zd,Zd0,dts,ts,cf,t_copy))

# Let's rearrange this results so it is easier to read in the future:
Zs=[]
for n in range(N):
    Zs.append([])
    for vec in Z:
        Zs[n].append(vec[n])
    Zs[n]=np.array(Zs[n])

plt.figure()
for vec in Zs:
    plt.plot(t,vec)

我期待几乎这个确切的结果,但时间逆转(随着时间的推移几乎呈指数下降的函数

1个回答

对于左右“虚拟”隔间在水平上是无限水槽的解释,隔间Z0=Z9=0之间的墙壁在隔间j外部填充开始j+1时被移除,我得到了缩短的实现ts[j]j

def ODE_func(Z,t,Q,Zd,Z0,dts,ts,cf,t_copy):

    # Declare some boundary conditions:
    Z9=0; dt9=1/4; t9=ts[-1]+dt9 # (It doesn't matter which value I have here, since Z9 = 0)
    Z0=0; dt0=1/4; t0=0;
    # Define functions to solve for:
    Z = np.concatenate([[Z0],Z,[Z9]]);
    # Define time intervals:
    dt = np.concatenate([[dt0],dts,[dt9]])
    # Define cumulative times:
    ts = np.concatenate([[t0],ts,[t9]]);


    # Find index of the time sample that is being used at the moment:
    Zd=np.interp(t,t_copy,Zd);

    dZdt = [ (Z[j-1]/dt[j-1]-Z[j]/dt[j])*step_fun(t,ts[j-1])/4 
             + (Z[j+1]/dt[j+1]-Z[j]/dt[j])*step_fun(t,ts[j])/4
             + 2*np.pi*cf*Q[j-1]*Zd*step_fun(t,ts[j-1])*step_fun(ts[j],t) for j in range(1,9)]

    return dZdt

Z[j]/dt[j], j=1,...,8以及长期的“填充高度”图

能级的长期演变

确实在一段时间后水平开始下降。