Differenzieren, Integrieren und Lösung von Differenzialgleichungen mit SciPy

SciPy stellt Funktionen für das numerische Differenzieren und Integrieren bereit.

Außerdem können Sie mit diesem Modul Differenzialgleichungen und Differenzialgleichungssysteme numerisch lösen.

Differenzieren

Mit den SciPy-Funktionen derivative() können mathematische Funktionen numerisch differenziert werden.

Das Untermodul misc wird demnächst aus dem Modul scipy entfernt. Damit steht auch die Funktion derivative() nicht mehr zur Verfügung. Stattdessen empfiehlt die Dokumentation von SciPy das Modul numdifftools zu verwenden.

#ableitung.py
import numpy as np
import matplotlib.pyplot as plt
from numdifftools import Derivative
#Funktionsdefinition
def f(x):
    #y=5*x
    y=0.25*x**2
    #y=np.sin(x)
    return y

def ableitung(x,n=1):
    df = Derivative(f,n=n)
    return df(x)
    
n=1 #Ordnung der Ableitung
x=np.linspace(0,10,100)
fig, ax=plt.subplots(figsize=(5,5))
ax.plot(x,f(x),'b-',label='Funktion')
ax.plot(x,ableitung(x,n),'r--',label='Ableitung')
ax.legend(loc='best')
ax.set(xlabel='x',ylabel='y',title='Ableitung einer Funktion')
fig.savefig('ableitung.svg')
plt.show()
    

Ausgabe

Ableitung

Integrieren

Mit den SciPy-Funktionen quad(), dblquad() und tplquad() können Sie Einfachintegrale, Zweifachintegrale und Dreifachintegrale numerisch berechnen.

Einfachintegral

Das Programm berechnet die Stammfunktion einer stetigen mathematischen Funktionen.

\[\frac{1}{4} \int x^{2}dx=\frac{1}{12} x^{3} \]

#integral.py
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import quad
#Funktionsdefinition
def f(x):
    #y=5*x
    y=x**2/4
    #y=np.sin(x)
    return y

@np.vectorize
def integral(x):
    return quad(f,0,x)[0]

x=np.linspace(0,10,100)
fig, ax=plt.subplots(figsize=(5,5))
ax.plot(x,f(x),'b-',label='Funktion')
ax.plot(x,integral(x),'r--',label='Integral')
ax.legend(loc='best')
ax.set(xlabel='x',ylabel='y',title='Ableitung einer Funktion')
fig.savefig('integral.svg')
plt.show()

Ausgabe

Ableitung

Zweifachintegral

\[\int^{2}_{1} \int^{3y}_{y} \left( x+y\right) dxdy=14 \]

 
#zweifach_dblquad.py
from scipy.integrate import dblquad
#Funktionsdefinition
def f(x,y):
    return x+y
#untere Grenze, variabel
def y1(y):
    return y
#obere Grenze, variabel
def y2(y):
    return 3*y
#Hauptprogramm
V= dblquad(f,1,2,y1,y2)[0]
print("V =",V)

Ausgabe

V = 14.0

Dreifachintegral

\[\int^{1}_{0} \int^{1-x}_{0} \int^{2-x}_{0} xyzdzdydx=\frac{13}{240} \]

#deifachintegral.py
from scipy.integrate import tplquad
#Funktionsdefinition
def f(z,y,x):
    return x*y*z
#obere Grenze, variabel
def y1(x):
    return 1-x
#obere Grenze, variabel
def y2(x,y):
    return 2-x
#Hauptprogramm
I = tplquad(f,0,1,0,y1,0,y2)[0]
print("I =",I)

Ausgabe

I = 0.054166666666666675

Differenzialgleichungen mit SciPy lösen

Differenzialgleichungen werden mit der Funktion
z=solve_ivp(dgl,intervall,anfangswerte,args=(...),dense_output=True)
numerisch gelöst. Der Lösungsvektor wird in dem Objekt z gespeichert. Die einzelnen Lösungen werden mit  x, y = z.sol(t) in das Tupel x,y gespeichert. Differenzialgleichungen n-ter Ordnung müssen in ein DGL-System 1. Ordnung umgewandelt werden.

Sprungantwort eines fremderregten Gleichstrommotors

Das Beispiel zeigt, wie die DGL für die Sprungantwort eins R-L-C-Gliedes mit der SciPy-Funktion solve_ivp(...) numerisch gelöst wird. Das R-L-C-Glied kann als Ersatzschaltbild eines fremderregten Gleichstrommotors interpretiert werden.

ersatzschaltbild gleichstrommotor

Aus dem Ersatzschaltbild des Gleichstrommotors kann folgendes DGL-System abgelesen werden:

\[\frac{du_{c}}{dt} =\frac{i}{C} \]

\[\frac{di}{dt} =\left( U_{0}-RC\frac{du}{dt} -u_{c}\right) \frac{1}{L} \]

Die dynamische Kapazität C = Cdyn wird aus dem Trägheitsmoment J, dem Bemessungsstrom In und dem Bemessungsmoment Mn des Gleichstrommotors berechnet:

\[C=J\left( \frac{I_{n}}{M_{n}} \right)^{2}\]

Sie verzögert die Hochlaufzeit.

Die elektrische Zeitkonstante
\[T_{el}=\frac{L}{R} \] beeinflusst die Verzögerung des Stromflusses während des Einschaltvorgangs.

Aus dem Ankerwiderstand R und der dynamischen Kapazität C wird die mechanische Zeitkonstante
\[T_{mech}=R\cdot C\] berechnet.

#Lineare DGL 2. Ordnung
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import solve_ivp
U0 = 100  #Eingangsspannung in V
R = 1.5   #Ankerwiderstand in Ohm
L = 0.025 #Ankerinduktivität in H
Mn=150    #Bemessungsmoment in Nm
In=50     #Bemessungsstrom in A
J=0.2     #Trägheitsmoment in kgm^2
tmax=0.5  
#DGL-System
def dgl(t,anfangswerte,R,L,C):
    uc,i = anfangswerte
    duc_dt = i/C
    di_dt = (U0 - R*C*duc_dt-uc)/L
    return [duc_dt, di_dt]
#Berechnungen und Anfangswerte
C = J*(In/Mn)**2 #dynamische Kapazität
a0 = [0,0]       #Anfangswerte fuer Kondensatorspannung und Strom
ti=[0,tmax]      #Integrationsintervall
t = np.linspace(0,tmax,500)
#Lösung des DGL-Systems 
z=solve_ivp(dgl,ti,a0,args=(R,L,C),dense_output=True) #1
uc,ic = z.sol(t)
#Grafikbereich
fig,axes=plt.subplots(2,1,figsize=(8,8))
#Kondensatorspannung
axes[0].set_title("Sprungantwort eines fremderregten Gleichstrommotors")
axes[0].plot(t, uc,"b",lw=2)
axes[0].set_ylabel('Ausgangsspannung in V')
axes[0].grid(True)
#Stromverlauf
axes[1].plot(t, ic,"r",lw=2)
axes[1].grid(True)
axes[1].set_ylabel('Ankerstrom in A')
axes[1].set_xlabel('Zeit in Sekunden')
print("dynamische Kapazität:",C,"F")
fig.tight_layout()
plt.show()

Ausgabe

dynamische Kapazität: 0.022222222222222223 F

sprunganwort gleichstrommotor

Analyse

Die Lösung der DGL erfolgt in Zeile #1 mit der SciPy-Methode solve_ivp(dgl,ti,a0,args=(R,L,C),dense_output=True). Als erster Parameter wird die Funktionsdefinition der DGL übergeben. Der Parameter ti legt das Integrationsintervall fest. Der dritte Parameter a0 bestimmt die Anfangswerte. Mit args=(R,L,C) können zusätzliche Parameter übergeben werden. Der letzte Parameter dense_output=True ermöglicht, dass die Lösung der DGL in dem Objekt z gespeichert werden kann.

Simulation eines gekoppleten Fadenpendels

Die Massen m von zwei Fadenpendeln werden mit einer Schraubenfeder verbunden. Die Schwingungen sind von den Massen, der Federkonstanten c und den Fadenlängen l abhängig.

gekoppeltes fadenpendel

Das gekoppelte Fadenpendel kann durch folgendes Differenzialgleichungssystem beschrieben werden:

\[\frac{d\varphi_{1} }{dt} =\omega_{1}\]

\[\frac{d\omega_{1} }{dt} =-\frac{g}{l} \varphi_{1} -\frac{c}{m} \left( \varphi_{1} -\varphi_{2} \right) \]

\[\frac{d\varphi_{2} }{dt} =\omega_{2}\]

\[\frac{d\omega_{2} }{dt} =-\frac{g}{l} \varphi_{2} +\frac{c}{m} \left( \varphi_{1} -\varphi_{2} \right) \]

Das DGL-System kann direkt in eine Python-Funktion implementiert werden. Gelöst wird das DGL-System wieder mit der SciPy-Methode solve_ivp(...).

#gekoppeltes_fadenpendel.py
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import solve_ivp
#Pendeldaten
g = 9.81    #Erdbeschleunigung in m/s^2
l= 0.2      #Pendellaengen in m
m= 0.2      #Pendelmassen in kg
c=1         #Federkonstante N/m
phi1, phi2 = -20, 0  #1
tmax=10
#DGL-System, der Unterstrich bedeutet 1. Ableitung nach der Zeit
def dgl(t,ya,l, m, c):
    phi1,w1,phi2,w2 = ya #1
    phi1_dt=w1
    w1_dt=-g/l*phi1-c/m*(phi1-phi2)
    phi2_dt=w2
    w2_dt=-g/l*phi2+c/m*(phi1-phi2)
    return [phi1_dt, w1_dt, phi2_dt, w2_dt] #2
#Lösung des DGL-Systems
omega1 = omega2 = 0
ya =[np.radians(phi1),omega1,np.radians(phi2),omega2] #Anfangswerte
t = np.linspace(0,tmax,500)
z=solve_ivp(dgl,[0,tmax],ya,args=(l,m,c),dense_output=True)#3
phi1,w1,phi2,w2 =z.sol(t) #4
auslenkung1 = np.degrees(phi1) #5
auslenkung2 = np.degrees(phi2) #6
#Grafik-Bereich
fig,ax=plt.subplots(2,1,figsize=(6,8))
#Auslenkung l1
ax[0].plot(t,auslenkung1,"r",lw=2)
ax[0].set(ylabel=r"$\varphi_{1}$",title="Pendel 1")
#Auslenkung l2
ax[1].plot(t,auslenkung2,'b',lw=2)
ax[1].set(xlabel="Zeit",ylabel=r"$\varphi_{2}$",title="Pendel 2")
ax[0].grid(True);ax[1].grid(True)
fig.tight_layout()
plt.show()

Ausgabe

auslenkung

Analyse

Zeile #1 legt die Anfangswerte für die Auslenkwinkel fest.

Die Python-Funktion gibt in Zeile #2 das DGL-System als Liste zurück.

In Zeile #3 wird das DGL-System gelöst.

In Zeile #4 wird die Lösung getrennt. Die einzelnen Teillösungen werden in ein Tupel gespeichert.

In den Zeilen #5 und #6 werden die Auslenkwinkel von Radiant in Grad umgerechnet.

Dieses Beispiel wird in meinem Buch "Mathematische Algorithmen mit Python" mit dem Euler-Verfahren gelöst.

Simulation einer Epidemie

Die Ausbreitung einer Epidemie lässt sich mit einem Differenzialgleichungssystem aus drei Differenzialgleichungen beschreiben. Eine Population N wird in drei Gruppen aufgeteilt: Gesunde S, Infizierte I und wieder Genesene R.

\[\frac{dS}{dt} =-b\frac{S\cdot I}{N} \]

\[\frac{dI}{dt} =b\frac{S\cdot I}{N} -g\cdot I\]

\[\frac{dR}{dt} =g\cdot I\]

Dabei steht g für die Genesungsrate und b für die Infektionsrate. Dieses DGL-System ist nicht-linear, es kann nur numerisch gelöst werden.

#Simulation einer Epidemie
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import solve_ivp
tmax=120
#Anfangswerte
S0=997     #nicht immune Gesunde
I0=3       #Infizierte
R0=0       #Genesene
N=S0+I0+R0 #Population
#Parameter
b=0.4  #Infektionsrate
g=0.04 #Genesungsrate
#DGL-System
def dgl(t,ya):
    S,I,R=ya
    dS_dt=-b*S*I/N    #nicht immune Gesunde
    dI_dt=b*S*I/N-g*I #Infizierte
    dR_dt=g*I         #Genesene
    return [dS_dt,dI_dt,dR_dt]
#Anfangswerte
y0 = [S0,I0,R0]
t = np.linspace(0, tmax, 500)
z=solve_ivp(dgl,[0,tmax],y0,dense_output=True) 
S, I, R = z.sol(t) #1
fig, ax = plt.subplots(figsize=(8,6))
ax.plot(t, S,'b-', lw=2, label="Gesunde")
ax.plot(t, I,'r--',lw=2, label="Infizierte")
ax.plot(t, R,'g:', lw=2, label="Genesene")
ax.legend(loc="best")
ax.set_xlabel("Zeit")
ax.set_ylabel("Individuen")
ax.grid(True)
plt.show()        

Ausgabe

epidemie simulation

Analyse

In Zeile #1 werden die Teillösungen des Lösungsvektors z in ein Tupel gespeichert. Das Tupel enthält die Werte für die Gesunden (S:susceptible, ansteckbare), die Infizierten (I: infected, infizierte) und die wieder Genesenen (R: removed, recovered) als eindimensionales Array.

Besuchen Sie die Seite von SciPy.

StartseiteNumPyMatplotlibSymPyVPython