簡體   English   中英

求解隱式ODE(微分代數方程DAE)

[英]Solve an implicit ODE (differential algebraic equation DAE)

我正在嘗試使用scipy中的odeint解決二階ODE問題。 我遇到的問題是函數隱式耦合到二階項,如簡化片段中所示(請忽略示例的假裝物理):

import numpy as np
from scipy.integrate import odeint

def integral(y,t,F_l,mass):
    dydt = np.zeros_like(y)
    x, v = y
    F_r =  (((1-a)/3)**2 + (2*(1+a)/3)**2) * v # 'a' implicit 
    a  = (F_l - F_r)/mass

    dydt = [v, a]
return dydt


y0 = [0,5]
time = np.linspace(0.,10.,21)
F_lon = 100.
mass = 1000.

dydt = odeint(integral, y0, time, args=(F_lon,mass))

在這種情況下,我意識到可以用代數方式求解隱式變量,但是在我的實際場景中, F_r之間存在很多邏輯,並且a和代數操作的評估失敗。

我相信DAE可以使用MATLAB的ode15i函數來解決,但我試圖盡可能避免這種情況。

我的問題是 - 有沒有辦法在python中解決隱式ODE函數(DAE)(最好是scipy)? 是否有更好的方法來解決上述問題?

作為最后的手段,可以接受從前一個時間步驟傳遞a 我怎么能在每個時間步之后將dydt[1]傳回函數?

相當老,但值得更新,所以它可能對任何人有用,誰偶然發現這個問題。 目前python中可用的包很少,可以解決隱式ODE。 GEKKO( https://github.com/BYU-PRISM/GEKKO )是一個軟件包,專門針對混合整數,非線性優化問題進行動態優化,但也可以用作通用DAE求解器。

上述“假裝物理”問題可以在GEKKO中解決,如下所述。

m= GEKKO()
m.time = np.linspace(0,100,101)
F_l = m.Param(value=1000)
mass = m.Param(value =1000)
m.options.IMODE=4
m.options.NODES=3
F_r = m.Var(value=0)
x = m.Var(value=0)
v = m.Var(value=0,lb=0)
a = m.Var(value=5,lb=0)
m.Equation(x.dt() == v)
m.Equation(v.dt() == a)
m.Equation (F_r ==  (((1-a)/3)**2 + (2*(1+a)/3)**2 * v)) 
m.Equation (a == (1000 - F_l)/mass)
m.solve(disp=False)
plt.plot(x)

在此輸入圖像描述

如果代數操作失敗,你可以找到你的約束的數值解,在每個時間步運行例如fsolve

import sys
from numpy import linspace
from scipy.integrate import odeint
from scipy.optimize import fsolve

y0 = [0, 5]
time = linspace(0., 10., 1000)
F_lon = 10.
mass = 1000.

def F_r(a, v):
    return (((1 - a) / 3) ** 2 + (2 * (1 + a) / 3) ** 2) * v

def constraint(a, v):
    return (F_lon - F_r(a, v)) / mass - a

def integral(y, _):
    v = y[1]
    a, _, ier, mesg = fsolve(constraint, 0, args=[v, ], full_output=True)
    if ier != 1:
        print "I coudn't solve the algebraic constraint, error:\n\n", mesg
        sys.stdout.flush()
    return [v, a]

dydt = odeint(integral, y0, time)

顯然,這會減慢你的時間整合。 始終檢查fsolve找到了一個好的解決方案,並刷新輸出,以便在發生時實現它並停止模擬。

關於如何在前一個時間步“緩存”變量的值,您可以利用默認參數僅在函數定義中計算的事實,

from numpy import linspace
from scipy.integrate import odeint

#you can choose a better guess using fsolve instead of 0
def integral(y, _, F_l, M, cache=[0]):
    v, preva = y[1], cache[0]
    #use value for 'a' from the previous timestep
    F_r = (((1 - preva) / 3) ** 2 + (2 * (1 + preva) / 3) ** 2) * v 
    #calculate the new value
    a = (F_l - F_r) / M
    cache[0] = a
    return [v, a]

y0 = [0, 5]
time = linspace(0., 10., 1000)
F_lon = 100.
mass = 1000.

dydt = odeint(integral, y0, time, args=(F_lon, mass))

請注意,為了使技巧工作, cache參數必須是可變的,這就是我使用列表的原因。 如果您不熟悉默認參數的工作方式,請參閱鏈接。

請注意,這兩個代碼不會產生相同的結果,您應該非常小心地使用前一個時間步的值,以確保數值穩定性和精度。 第二個顯然要快得多。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM