1
votes

Comment définir une taille de pas fixe avec scipy.integrate?

Je cherche un moyen de définir une taille de pas fixe pour résoudre mon problème de valeur initiale par la méthode Runge-Kutta en Python. En conséquence, comment puis-je dire au scipy.integrate.RK45 de garder une mise à jour constante (pas de taille) pour sa procédure d'intégration?

Merci beaucoup.


4 commentaires

Que voulez-vous réaliser exactement? Voulez-vous que l'algorithme fonctionne comme une méthode à pas fixe ou voulez-vous la solution dans des échantillons également espacés pour le traçage ou d'autres calculs?


Merci pour votre commentaire. Je recherche en fait la première partie, la méthode par étapes fixes.


Comme cela semble être fait à des fins éducatives, il est probablement beaucoup plus facile d'implémenter directement la boucle à pas fixe en utilisant le tableau Dormand-Prince. Vous pouvez simuler cela en utilisant la classe RK45 en réinitialisant continuellement la taille de l'étape avant d'exécuter l'étape suivante, mais il n'y a aucune garantie explicite qu'une seule étape est effectuée.


Je ne sais pas exactement quelle est votre motivation, mais: en tant qu’auteur d’un module d’intégration, j’ai reçu une dizaine de demandes comme la vôtre ces dernières années. Pour chacun d'entre eux, il s'est avéré que la demande était due à un malentendu complet sur l'adaptation de la taille de pas ou à un XY malformé problème , c'est-à-dire que la taille de pas fixe était une solution trop compliquée ou mauvaise à un autre problème. Donc, à moins que vous ne sachiez vraiment ce que vous faites, je vous recommande fortement de vous demander si vous le voulez vraiment (ce qui peut être une bonne question pour Computational Science < / a>).


6 Réponses :


2
votes

En consultant l'implémentation de l'étape , vous constaterez que le mieux que vous puissiez faire est de contrôler la taille initiale du pas (dans les limites fixées par la taille minimale et maximale du pas) en définissant l'attribut h_abs avant d'appeler RK45.step :

In [27]: rk = RK45(lambda t, y: t, 0, [0], 1e6)

In [28]: rk.h_abs = 30

In [29]: rk.step()

In [30]: rk.step_size
Out[30]: 30.0


0 commentaires

4
votes

Il est assez facile de coder le tableau Butcher pour la méthode Dormand-Prince RK45.

for h in [0.2, 0.1, 0.08, 0.05, 0.01][::-1]:
    t = np.arange(0,20,h);
    y = DoPri45integrate(mms_ode,t,mms_x0)
    plt.plot(t, (y[:,0]-np.sin(t))/h**5, 'o', ms=3, label = "h=%.4f"%h);
plt.grid(); plt.legend(); plt.show()  

d'abord dans une fonction pour une seule étape import numpy as np

def mms_ode(t,y): return np.array([ y[1], sin(sin(t))-sin(t)-sin(y[0]) ])
mms_x0 = [0.0, 1.0]

puis dans une boucle standard à pas fixe

def DoPri45integrate(f, t, x0):
    N = len(t)
    x = [x0]
    for k in range(N-1):
        v4, v5 = DoPri45Step(f,t[k],x[k],t[k+1]-t[k])
        x.append(x[k] + (t[k+1]-t[k])*v5)
    return np.array(x)

Puis testez-le pour un exemple de jouet avec la solution exacte connue y(t)=sin(t)

def DoPri45Step(f,t,x,h):

    k1 = f(t,x)
    k2 = f(t + 1./5*h, x + h*(1./5*k1) )
    k3 = f(t + 3./10*h, x + h*(3./40*k1 + 9./40*k2) )
    k4 = f(t + 4./5*h, x + h*(44./45*k1 - 56./15*k2 + 32./9*k3) )
    k5 = f(t + 8./9*h, x + h*(19372./6561*k1 - 25360./2187*k2 + 64448./6561*k3 - 212./729*k4) )
    k6 = f(t + h, x + h*(9017./3168*k1 - 355./33*k2 + 46732./5247*k3 + 49./176*k4 - 5103./18656*k5) )

    v5 = 35./384*k1 + 500./1113*k3 + 125./192*k4 - 2187./6784*k5 + 11./84*k6
    k7 = f(t + h, x + h*v5)
    v4 = 5179./57600*k1 + 7571./16695*k3 + 393./640*k4 - 92097./339200*k5 + 187./2100*k6 + 1./40*k7;

    return v4,v5

et tracez l'erreur mise à l'échelle par h ^ 5 code >

0
1/5   |  1/5
3/10  |  3/40        9/40
4/5   |  44/45       −56/15        32/9
8/9   |  19372/6561  −25360/2187   64448/6561   −212/729
1     |  9017/3168   −355/33       46732/5247   49/176     −5103/18656
1     |  35/384           0        500/1113     125/192    −2187/6784     11/84     
-----------------------------------------------------------------------------------------
      |  35/384           0        500/1113     125/192    −2187/6784     11/84     0
      |  5179/57600       0        7571/16695   393/640    −92097/339200  187/2100  1/40

pour obtenir la confirmation qu'il s'agit bien d'une méthode d'ordre 5, car les graphiques des coefficients d'erreur se rapprochent.

entrez description de l'image ici


6 commentaires

Merci pour votre excellent travail, cela m'a fait gagner beaucoup de temps. Bien que d'une manière ou d'une autre, la définition des tranches de tableau dans DoPri45integrate n'était pas possible et a produit un bogue vraiment désagréable. J'ai modifié le code en un ajout de liste plus robuste.


Je ne sais pas pourquoi vous avez obtenu une erreur, cela fonctionne bien pour moi sur python 2 et python 3.


Je ne suis pas sûr non plus. Peut-être de nouvelles versions numpy ou des dimensions incompatibles. J'ai posté le code sur github: github.com/AlexanderNenninger / DynamicalSystems1-SS2020 / blob / ‌… . J'ai également testé le code en dehors d'un notebook jupyter avec un débogueur et l'attribution de la valeur de tranche semblait être le problème.


Vous savez que ce que vous utilisez n'est pas l'oscillateur Van der Pol standard x '' - mu * (1-x ^ 2) x '+ x = 0 ? Vous implémentez l'équation eps * x '' - x '(1-x') ^ 2 + x = 0 . Dans sa dérivée y = x ' qui devient eps * y' '- y' (1-4y + 3y ^ 2) + y = 0 qui après un certain temps, la remise à l'échelle gagne une certaine ressemblance avec VdP.


Merci pour votre travail de détective, je n'étais pas au courant de cela! Il s'agit d'un devoir et ils sont régulièrement sous / mal spécifiés ou contiennent des erreurs. Pour le moment, je m'en tiendrai à ce qui est écrit et je le mentionnerai dans une remarque.


L'oscillateur VdP provient d'un modèle physique, donc votre formulation peut encore être plus proche de la physique que l'équation normalisée couramment utilisée. Cependant, l'équation n'est plus symétrique, de sorte que le cycle limite ne sera pas symétrique.



1
votes

Scipy.integrate est généralement utilisé avec une méthode d'étape modifiable en contrôlant le TOL (erreur d'une étape) tout en intégrant numériquement. Le TOL est généralement calculé en vérifiant avec une autre méthode numérique. Par exemple, RK45 utilise le 5ème ordre Runge-Kutta pour vérifier le TOL de la 4ème méthode Runge-Kutta d'ordre pour déterminer l'étape d'intégration.

Par conséquent, si vous devez intégrer des ODE avec un pas fixe, désactivez simplement le contrôle TOL en définissant atol, rtol avec une constante assez grande. Par exemple, comme le formulaire:

solve_ivp (votre fonction, t_span = [0, 10], y0 = ..., method = "RK45", max_step = 0.01, atol = 1, rtol = 1)

La vérification TOL est définie pour être si grande que l'étape d'intégration correspond au max_step que vous choisissez.


0 commentaires

0
votes

Si vous êtes intéressé par la taille de l'étape de correction des données, je vous recommande vivement d'utiliser la fonction scipy.integrate.solve_ivp et son argument t_eval .

Cette fonction regroupe tous les solveurs ode scipy.integrate en une seule fonction, vous devez donc choisir la méthode en donnant une valeur à son argument method . Heureusement, la méthode par défaut est le RK45, vous n'avez donc pas à vous en préoccuper.

Ce qui est plus intéressant pour vous, c'est l'argument t_eval , où vous devez donner un tableau plat. La fonction échantillonne la courbe de solution aux valeurs de t_eval et ne renvoie que ces points. Donc, si vous voulez un échantillonnage uniforme par la taille du pas, donnez simplement à l'argument t_eval ce qui suit: numpy.linspace (t0, tf, samplingResolution) , où t0 est le début et tf est la fin de la simulation. Ainsi, vous pouvez avoir un échantillonnage uniforme sans avoir à recourir à une taille de pas fixe qui provoque une instabilité pour certains ODE.


0 commentaires

1
votes

Vous avez dit que vous souhaitiez un comportement de pas de temps fixe, pas seulement un pas de temps d'évolution fixe. Par conséquent, vous devez "pirater" votre chemin si vous ne voulez pas réimplémenter le solveur vous-même. Il vous suffit de définir les tolérances d'intégration atol et rtol sur 1e90, et max_step et first_step sur la valeur dt du pas de temps que vous souhaitez utiliser . De cette façon, l'erreur d'intégration estimée sera toujours très faible, ce qui incitera le solveur à ne pas réduire le pas de temps de manière dynamique.

Cependant, n'utilisez cette astuce qu'avec les algortithmes EXPLICIT (RK23, RK45, DOP853)! Les algorithmes implicites de "résoudre_ivp" (Radau, BDF, peut-être aussi LSODA) ajustent la précision du solveur de Newton non linéaire en fonction de atol et rtol, donc vous pourriez finir par avoir une solution qui n'a aucun sens ...


0 commentaires

0
votes

Je suggère d'écrire votre propre programme à étapes fixes rk4 dans py. Il existe de nombreux exemples Internet pour vous aider. Cela garantit que vous savez précisément comment chaque valeur est calculée. De plus, il n'y aura normalement pas de calculs 0/0 et si c'est le cas, ils seront faciles à tracer et inciteront à un autre regard sur l'ode en cours de résolution.


1 commentaires

Peut-être partager un site Web pour commencer?