问题
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