web-dev-qa-db-fra.com

Résolution ODE numérique en Python

Comment résoudre numériquement un ODE en Python?

Considérer

equation to solve

\ddot{u}(\phi) = -u + \sqrt{u}

avec les conditions suivantes

u(0) = 1.49907

et

\dot{u}(0) = 0

avec la contrainte

0 <= \phi <= 7\pi.

Enfin, je veux produire un tracé paramétrique où les coordonnées x et y sont générées en fonction de u.

Le problème est que j'ai besoin d'exécuter odeint deux fois car il s'agit d'une équation différentielle de second ordre. J'ai essayé de le relancer après la première fois mais il revient avec une erreur jacobienne. Il doit y avoir un moyen de l'exécuter deux fois en même temps.

Voici l'erreur:

odepack.error: La fonction et son jacobien doivent être des fonctions appelables

que le code ci-dessous génère. La ligne en question est le sol = odeint.

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


def f(u, t):
    return -u + np.sqrt(u)


times = linspace(0.0001, 7 * np.pi, 1000)
y0 = 1.49907
yprime0 = 0
yvals = odeint(f, yprime0, times)

sol = odeint(yvals, y0, times)

x = 1 / sol * np.cos(times)
y = 1 / sol * np.sin(times)

plot(x,y)

plt.show()

Modifier

J'essaie de construire l'intrigue à la page 9

Mécanique classique Taylor

Voici l'intrigue avec Mathematica

mathematica plot

In[27]:= sol = 
 NDSolve[{y''[t] == -y[t] + Sqrt[y[t]], y[0] == 1/.66707928, 
   y'[0] == 0}, y, {t, 0, 10*\[Pi]}];

In[28]:= ysol = y[t] /. sol[[1]];

In[30]:= ParametricPlot[{1/ysol*Cos[t], 1/ysol*Sin[t]}, {t, 0, 
  7 \[Pi]}, PlotRange -> {{-2, 2}, {-2.5, 2.5}}]
13
dustin
import scipy.integrate as integrate
import matplotlib.pyplot as plt
import numpy as np

pi = np.pi
sqrt = np.sqrt
cos = np.cos
sin = np.sin

def deriv_z(z, phi):
    u, udot = z
    return [udot, -u + sqrt(u)]

phi = np.linspace(0, 7.0*pi, 2000)
zinit = [1.49907, 0]
z = integrate.odeint(deriv_z, zinit, phi)
u, udot = z.T
# plt.plot(phi, u)
fig, ax = plt.subplots()
ax.plot(1/u*cos(phi), 1/u*sin(phi))
ax.set_aspect('equal')
plt.grid(True)
plt.show()

enter image description here

26
unutbu

Le code de votre autre question est vraiment proche de ce que vous voulez. Deux changements sont nécessaires:

  • Vous étiez en train de résoudre un autre ODE (car vous avez modifié deux signes à l'intérieur de la fonction deriv)
  • Le composant y de votre tracé souhaité provient des valeurs de la solution, pas des valeurs de la dérivée première de la solution, vous devez donc remplacer u[:,0] (valeurs de fonction) pour u[:, 1] (dérivés).

Voici le résultat final:

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

def deriv(u, t):
    return np.array([u[1], -u[0] + np.sqrt(u[0])])

time = np.arange(0.01, 7 * np.pi, 0.0001)
uinit = np.array([1.49907, 0])
u = odeint(deriv, uinit, time)

x = 1 / u[:, 0] * np.cos(time)
y = 1 / u[:, 0] * np.sin(time)

plt.plot(x, y)
plt.show()

Cependant, je vous suggère d'utiliser le code de la réponse d'unutbu car il est auto-documenté (u, udot = z) et utilise np.linspace au lieu de np.arange. Ensuite, exécutez ceci pour obtenir le chiffre souhaité:

x = 1 / u * np.cos(phi)
y = 1 / u * np.sin(phi)
plt.plot(x, y)
plt.show()
4
jorgeca

Vous pouvez utiliser scipy.integrate.ode. Pour résoudre dy/dt = f (t, y), avec la condition initiale y (t0) = y0, à l'instant = t1 avec Runge-Kutta de 4e ordre, vous pouvez faire quelque chose comme ceci:

from scipy.integrate import ode
solver = ode(f).set_integrator('dopri5')
solver.set_initial_value(y0, t0)
dt = 0.1
while t < t1:
    y = solver.integrate(t+dt)
    t += dt

Edit: Vous devez obtenir votre dérivé au premier ordre pour utiliser l'intégration numérique. Vous pouvez y parvenir en définissant par exemple z1 = u et z2 = du/dt, après quoi vous avez dz1/dt = z2 et dz2/dt = d ^ 2u/dt ^ 2. Remplacez-les dans votre équation d'origine et parcourez simplement le vecteur dZ/dt, qui est de premier ordre.

Edit 2: Voici un exemple de code pour le tout:

import numpy as np
import matplotlib.pyplot as plt

from numpy import sqrt, pi, sin, cos
from scipy.integrate import ode

# use z = [z1, z2] = [u, u']
# and then f = z' = [u', u''] = [z2, -z1+sqrt(z1)]
def f(phi, z):
    return [z[1], -z[0]+sqrt(z[0])]


# initialize the 4th order Runge-Kutta solver
solver = ode(f).set_integrator('dopri5')

# initial value
z0 = [1.49907, 0.]
solver.set_initial_value(z0)

values = 1000
phi = np.linspace(0.0001, 7.*pi, values)
u = np.zeros(values)

for ii in range(values):
    u[ii] = solver.integrate(phi[ii])[0] #z[0]=u

x = 1. / u * cos(phi)
y = 1. / u * sin(phi)

plt.figure()
plt.plot(x,y)
plt.grid()
plt.show()
3
HenriV

scipy.integrate () fait l'intégration ODE. C'est bien ce que vous cherchez?

2
Bitwise