odeint returns wrong results for an ODE including descrete function

妖精的绣舞 提交于 2019-12-25 01:44:57

问题


I'm trying to model the ODE:

I implemented:

import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
m = 1
k = 1
M = 0.1
b = 1
Fmax = 1
def dXdt(X,t):
    return [X[1], - b * X[1] / m - k * X[0] / m - M * np.sign(X[1]) / m + Fmax / m ]

X0 = [1, 2]
ts = np.linspace(0, 10, 200)
Xs = odeint(dXdt, X0, ts)
plt.plot(ts, Xs[:, 0])

resulting:

which contradicts what I get from Modelica (OpenModelica):

model model1
//constants
  parameter Real m = 1;
  parameter Real k = 1;
  parameter Real b = 1;
  parameter Real M = 0.1;
  parameter Real Fmax = 1;
//variables
  Real x, v, a;
initial equation
  x = 1;
  v = 2;
equation
  v = der(x);
  a = der(v);
  m * a + b * v + k * x + M * sign(v) = Fmax;
end model1;

I would appreciate if you could help me know where is my mistake and how I can solve it.


回答1:


Your system has 3 smooth sub-systems or phases according to the sign of x'. As long as the integration stays inside these smooth phases, the step size controller works as expected. At the moment that the phase changes however, the step size controller sees huge changes and oscillations in the quantities that it uses to adapt the step size, requiring to regulate the step size down.

Next comes that the method in odeint, lsode, is implicit, and that the assumption for the implicit method is that the right side of the equation is again sufficiently differentiable, at least once. Failing that the implicit solver can go anywhere, which you observe in the spike.


One solution is to eliminate the discontinuities by continuing each phase beyond their boundaries and use the event mechanism of the ODE solver to find the points where the boundary is crossed. To that end introduce a sign/phase selector parameter S and solve the system

m*x''+b*x'+k*x+M*s = F

using the function e(t)=x'(t) as event function.

# Define differential equation
m,b,k,M,F = 1., 1., 1., 0.1, 1.
def fun(t, x, S):
    dx = [x[1], (F-b*x[1]-k*x[0]-M*S)/m]
    return np.asarray(dx)

# Define event function and make it a terminal event
def event(t, x):
    return x[1]
event.terminal = True

t = 0
x = [1.,2.]; 
S = np.sign(u[1]);
tend = 10

As we need to change the phase selector at the event location, the modus of the event has to be terminal. Then loop through the phase segments and combine them to a global solution like in the answer of chthonicdaemon to the question "How to use if statement in a differential equation (SciPy)?".

To get a definitive behavior make sure that at each event the phase boundary is crossed at each event (if the acceleration is non-zero (and it is almost always non-zero)).

ts = []
xs = []
eps=1e-8
for _ in range(50):
    sol = solve_ivp(lambda t,u:fun(t,u,S), (t, tend), x, events=event, atol=1e-12, rtol=1e-8, max_step=0.01);
    ts.append(sol.t)
    xs.append(sol.y)
    if sol.status == 1: # Event was hit
        # New start time for integration
        t = sol.t[-1]
        # Reset initial state
        x = sol.y[:, -1].copy()
        # ensure the crossing of the phase boundary
        dx = fun(t,x,S)
        dt = -(eps*S+x[1])/dx[1]; # should be minimal
        if dt > 0: t += dt; x += dt*dx;
        # new phase parameter
        S = np.sign(x[1]);
        # stop the iteration if it stalls 
        if t-sol.t[0] <5e-12: break
    else:
        break

# We have to stitch together the separate simulation results for plotting
t=np.concatenate(ts);
x=np.concatenate(xs, axis=1);

Then plot the solutions, for instance like below. The integration stalls at t=4.7880468 with x=0.9453532. Around this point on x'=0 the acceleration is x''=-0.0453532 for slightly positive x' and x''=0.15464678 for slightly negative x' and x''=0.05464678 on x'=0. There is no equilibrium position and no way to proceed forward in time. Due to the forcing to pass through the boundary, in the numerical computation the dynamic can proceed in time, however the smaller the size of eps, the amplitude of that oscillation, the smaller the wave length and thus the step size. The last condition in the integration loop finishes (for much smaller eps) the integration if the oscillation wave length becomes too small.



来源:https://stackoverflow.com/questions/54767096/odeint-returns-wrong-results-for-an-ode-including-descrete-function

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!