solve_bvp#
- scipy.integrate.solve_bvp(fun, bc, x, y, p=None, S=None, fun_jac=None, bc_jac=None, tol=0.001, max_nodes=1000, verbose=0, bc_tol=None)[Quelle]#
Löst ein Randwertproblem für ein System von ODEs.
Diese Funktion löst numerisch ein System von Differentialgleichungen erster Ordnung, das mit Randbedingungen an zwei Punkten belegt ist.
dy / dx = f(x, y, p) + S * y / (x - a), a <= x <= b bc(y(a), y(b), p) = 0
Hier ist x eine 1D-unabhängige Variable, y(x) eine nD-vektorwertige Funktion und p ein kD-Vektor unbekannter Parameter, die zusammen mit y(x) gefunden werden sollen. Damit das Problem bestimmt ist, muss es n + k Randbedingungen geben, d. h. bc muss eine (n + k)D-Funktion sein.
Der letzte singuläre Term auf der rechten Seite des Systems ist optional. Er wird durch eine n-mal-n-Matrix S definiert, sodass die Lösung S y(a) = 0 erfüllen muss. Diese Bedingung wird während der Iterationen erzwungen und darf daher nicht im Widerspruch zu den Randbedingungen stehen. Siehe [2] für eine Erklärung, wie dieser Term bei der numerischen Lösung von BVPs behandelt wird.
Probleme in einem komplexen Bereich können ebenfalls gelöst werden. In diesem Fall werden y und p als komplex betrachtet und f und bc als komplexwertige Funktionen angenommen, x bleibt jedoch reell. Beachten Sie, dass f und bc komplex differenzierbar sein müssen (Cauchy-Riemann-Gleichungen erfüllen, siehe [4]), andernfalls sollten Sie Ihr Problem für die reellen und imaginären Teile separat umschreiben. Um ein Problem in einem komplexen Bereich zu lösen, übergeben Sie eine anfängliche Schätzung für y mit einem komplexen Datentyp (siehe unten).
- Parameter:
- funcallable
Rechte Seite des Systems. Die Aufrufsignatur lautet
fun(x, y)oderfun(x, y, p), falls Parameter vorhanden sind. Alle Argumente sind ndarray:xmit Form (m,),ymit Form (n, m), was bedeutet, dassy[:, i]zux[i]korrespondiert, undpmit Form (k,). Der Rückgabewert muss ein Array mit Form (n, m) und demselben Layout wieysein.- bcaufrufbar
Funktion, die die Residuen der Randbedingungen auswertet. Die Aufrufsignatur lautet
bc(ya, yb)oderbc(ya, yb, p), falls Parameter vorhanden sind. Alle Argumente sind ndarray:yaundybmit Form (n,) undpmit Form (k,). Der Rückgabewert muss ein Array mit Form (n + k,) sein.- xarray_like, Form (m,)
Anfangsgitter. Muss eine streng aufsteigende Folge reeller Zahlen mit
x[0]=aundx[-1]=bsein.- yarray_like, Form (n, m)
Anfängliche Schätzung für die Funktionswerte an den Gitterknoten, die i-te Spalte entspricht
x[i]. Für Probleme in einem komplexen Bereich übergeben Sie y mit einem komplexen Datentyp (auch wenn die anfängliche Schätzung rein reell ist).- parray_like mit Form (k,) oder None, optional
Anfängliche Schätzung für die unbekannten Parameter. Wenn None (Standard), wird angenommen, dass das Problem von keinen Parametern abhängt.
- Sarray_like mit Form (n, n) oder None
Matrix, die den singulären Term definiert. Wenn None (Standard), wird das Problem ohne den singulären Term gelöst.
- fun_jacaufrufbar oder None, optional
Funktion, die Ableitungen von f nach y und p berechnet. Die Aufrufsignatur lautet
fun_jac(x, y)oderfun_jac(x, y, p), falls Parameter vorhanden sind. Die Rückgabe muss 1 oder 2 Elemente in der folgenden Reihenfolge enthalten.df_dy : array_like, Form (n, n, m), wobei ein Element (i, j, q) gleich d f_i(x_q, y_q, p) / d (y_q)_j ist.
df_dp : array_like, Form (n, k, m), wobei ein Element (i, j, q) gleich d f_i(x_q, y_q, p) / d p_j ist.
Hier nummeriert q die Knoten, an denen x und y definiert sind, während i und j die Vektorkomponenten nummerieren. Wenn das Problem ohne unbekannte Parameter gelöst wird, sollte df_dp nicht zurückgegeben werden.
Wenn fun_jac None ist (Standard), werden die Ableitungen durch Vorwärts-Finite-Differenzen geschätzt.
- bc_jacaufrufbar oder None, optional
Funktion, die Ableitungen von bc nach ya, yb und p berechnet. Die Aufrufsignatur lautet
bc_jac(ya, yb)oderbc_jac(ya, yb, p), falls Parameter vorhanden sind. Die Rückgabe muss 2 oder 3 Elemente in der folgenden Reihenfolge enthalten.dbc_dya : array_like, Form (n, n), wobei ein Element (i, j) gleich d bc_i(ya, yb, p) / d ya_j ist.
dbc_dyb : array_like, Form (n, n), wobei ein Element (i, j) gleich d bc_i(ya, yb, p) / d yb_j ist.
dbc_dp : array_like, Form (n, k), wobei ein Element (i, j) gleich d bc_i(ya, yb, p) / d p_j ist.
Wenn das Problem ohne unbekannte Parameter gelöst wird, sollte dbc_dp nicht zurückgegeben werden.
Wenn bc_jac None ist (Standard), werden die Ableitungen durch Vorwärts-Finite-Differenzen geschätzt.
- tolfloat, optional
Gewünschte Toleranz der Lösung. Wenn wir
r = y' - f(x, y)definieren, wobei y die gefundene Lösung ist, dann versucht der Solver, auf jedem Gitterintervallnorm(r / (1 + abs(f)) < tolzu erreichen, wobeinormin einem quadratischen Mittel (unter Verwendung einer numerischen Quadraturformel) geschätzt wird. Standard ist 1e-3.- max_nodesint, optional
Maximale zulässige Anzahl von Gitterknoten. Wenn überschritten, terminiert der Algorithmus. Standard ist 1000.
- verbose{0, 1, 2}, optional
Level der Ausführlichkeit des Algorithmus
0 (Standard) : arbeitet leise.
1 : zeigt einen Beendigungsbericht an.
2 : Fortschritt während der Iterationen anzeigen.
- bc_tolfloat, optional
Gewünschte absolute Toleranz für die Randbedingungsresiduen: bc Wert sollte komponentenweise
abs(bc) < bc_tolerfüllen. Standardmäßig gleich tol. Bis zu 10 Iterationen sind erlaubt, um diese Toleranz zu erreichen.
- Rückgabe:
- Bunch-Objekt mit den folgenden definierten Feldern
- solPPoly
Gefundene Lösung für y als
scipy.interpolate.PPoly-Instanz, ein C1-kontinuierlicher kubischer Spline.- pndarray oder None, Form (k,)
Gefundene Parameter. None, wenn die Parameter nicht im Problem vorhanden waren.
- xndarray, Form (m,)
Knoten des endgültigen Gitters.
- yndarray, Form (n, m)
Lösungswerte an den Gitterknoten.
- ypndarray, Form (n, m)
Ableitungen der Lösung an den Gitterknoten.
- rms_residualsndarray, Form (m - 1,)
RMS-Werte der relativen Residuen über jedes Gitterintervall (siehe Beschreibung des tol-Parameters).
- niterint
Anzahl der abgeschlossenen Iterationen.
- statusint
Grund für die Beendigung des Algorithmus
0: Der Algorithmus hat die gewünschte Genauigkeit erreicht.
1: Die maximale Anzahl von Gitterknoten wurde überschritten.
2: Eine singuläre Jacobi-Matrix wurde bei der Lösung des Kollokationssystems angetroffen.
- messagestring
Verbale Beschreibung des Abbruchgrundes.
- successbool
True, wenn der Algorithmus die gewünschte Genauigkeit erreicht hat (
status=0).
Hinweise
Diese Funktion implementiert einen Kollokationsalgorithmus 4. Ordnung mit Residualkontrolle, ähnlich wie in [1] beschrieben. Ein Kollokationssystem wird mit einer gedämpften Newton-Methode und einer affininvarianten Kriteriumsfunktion gelöst, wie in [3] beschrieben.
Beachten Sie, dass in [1] die integralen Residuen ohne Normierung durch die Intervalllängen definiert sind. Daher unterscheidet sich ihre Definition um einen Faktor von h**0.5 (h ist die Intervalllänge) von der hier verwendeten Definition.
Hinzugefügt in Version 0.18.0.
Referenzen
[1] (1,2)J. Kierzenka, L. F. Shampine, “A BVP Solver Based on Residual Control and the Maltab PSE”, ACM Trans. Math. Softw., Vol. 27, Number 3, pp. 299-316, 2001.
[2]L.F. Shampine, P. H. Muir and H. Xu, “A User-Friendly Fortran BVP Solver”, J. Numer. Anal., Ind. Appl. Math. (JNAIAM), Vol. 1, Number 2, pp. 201-217, 2006.
[3]U. Ascher, R. Mattheij und R. Russell, “Numerical Solution of Boundary Value Problems for Ordinary Differential Equations”, Philidelphia, PA: Society for Industrial and Applied Mathematics, 1995. DOI:10.1137/1.9781611971231
[4]Cauchy-Riemann-Gleichungen auf Wikipedia.
Beispiele
Im ersten Beispiel lösen wir das Bratu-Problem
y'' + k * exp(y) = 0 y(0) = y(1) = 0
für k = 1.
Wir schreiben die Gleichung als System erster Ordnung um und implementieren die Auswertung ihrer rechten Seite.
y1' = y2 y2' = -exp(y1)
>>> import numpy as np >>> def fun(x, y): ... return np.vstack((y[1], -np.exp(y[0])))
Implementieren Sie die Auswertung der Randbedingungsresiduen.
>>> def bc(ya, yb): ... return np.array([ya[0], yb[0]])
Definieren Sie das anfängliche Gitter mit 5 Knoten.
>>> x = np.linspace(0, 1, 5)
Dieses Problem ist bekannt dafür, zwei Lösungen zu haben. Um beide zu erhalten, verwenden wir zwei verschiedene anfängliche Schätzungen für y. Wir bezeichnen sie mit den Indizes a und b.
>>> y_a = np.zeros((2, x.size)) >>> y_b = np.zeros((2, x.size)) >>> y_b[0] = 3
Nun sind wir bereit, den Solver auszuführen.
>>> from scipy.integrate import solve_bvp >>> res_a = solve_bvp(fun, bc, x, y_a) >>> res_b = solve_bvp(fun, bc, x, y_b)
Lassen Sie uns die beiden gefundenen Lösungen plotten. Wir nutzen die Tatsache, dass die Lösung in Spline-Form vorliegt, um einen glatten Plot zu erzeugen.
>>> x_plot = np.linspace(0, 1, 100) >>> y_plot_a = res_a.sol(x_plot)[0] >>> y_plot_b = res_b.sol(x_plot)[0] >>> import matplotlib.pyplot as plt >>> plt.plot(x_plot, y_plot_a, label='y_a') >>> plt.plot(x_plot, y_plot_b, label='y_b') >>> plt.legend() >>> plt.xlabel("x") >>> plt.ylabel("y") >>> plt.show()
Wir sehen, dass die beiden Lösungen ähnliche Formen haben, sich aber in der Skalierung erheblich unterscheiden.
Im zweiten Beispiel lösen wir ein einfaches Sturm-Liouville-Problem
y'' + k**2 * y = 0 y(0) = y(1) = 0
Es ist bekannt, dass eine nicht-triviale Lösung y = A * sin(k * x) für k = pi * n, wobei n eine ganze Zahl ist, möglich ist. Um die Normierungskonstante A = 1 zu etablieren, fügen wir eine Randbedingung hinzu:
y'(0) = k
Auch hier schreiben wir unsere Gleichung als System erster Ordnung um und implementieren die Auswertung ihrer rechten Seite.
y1' = y2 y2' = -k**2 * y1
>>> def fun(x, y, p): ... k = p[0] ... return np.vstack((y[1], -k**2 * y[0]))
Beachten Sie, dass die Parameter p als Vektor (in unserem Fall mit einem Element) übergeben werden.
Implementieren Sie die Randbedingungen.
>>> def bc(ya, yb, p): ... k = p[0] ... return np.array([ya[0], yb[0], ya[1] - k])
Richten Sie das anfängliche Gitter und die Schätzung für y ein. Wir zielen darauf ab, die Lösung für k = 2 * pi zu finden. Um dies zu erreichen, setzen wir die Werte von y so, dass sie ungefähr sin(2 * pi * x) folgen.
>>> x = np.linspace(0, 1, 5) >>> y = np.zeros((2, x.size)) >>> y[0, 1] = 1 >>> y[0, 3] = -1
Führen Sie den Solver mit 6 als anfänglicher Schätzung für k aus.
>>> sol = solve_bvp(fun, bc, x, y, p=[6])
Wir sehen, dass das gefundene k annähernd korrekt ist.
>>> sol.p[0] 6.28329460046
Und schließlich plotten wir die Lösung, um die erwartete Sinusoide zu sehen.
>>> x_plot = np.linspace(0, 1, 100) >>> y_plot = sol.sol(x_plot)[0] >>> plt.plot(x_plot, y_plot) >>> plt.xlabel("x") >>> plt.ylabel("y") >>> plt.show()