Numerik

%pylab inline
%pylab is deprecated, use %matplotlib inline and import the required libraries.
Populating the interactive namespace from numpy and matplotlib
# set default values for plotting:
rcParams['axes.titlesize']  = 14
rcParams['axes.labelsize']  = 14
rcParams['xtick.labelsize'] = 14
rcParams['ytick.labelsize'] = 14
rcParams['legend.fontsize'] = 12
rcParams['lines.linewidth'] = 2

Literatur

  • MacCluer: Chapter 12 Divided Differences, p23ff.

  • Papula: Band 2, IV, 6 Numerische Integration einer DGL, p.472ff.

  • Bronson, Costa: Chapters 18 and 19

Beispiel: GDGL 1. Ordnung in 1 Dimension

  • Problem: Gegeben die Funktion \(f\) und der Anfangswert \(y_0\) löse

    \[y'(x) = f(x,y(x)), y(0)= y_0\]

    numerisch.

  • Geometrie: \(f(x,y)\) definiert ein Richtungs-/Steigungsfeld in der \(x\)-\(y\)-Ebene. Finde jene Kurve \(y(x)\), die bei \((x_0,y_0)\) beginnt und Steigungen hat, die gleich den durch \(f\) vorgegebenen sind.

Methode nach Euler

Wähle eine Schrittweite \(h>0\) und ersetze \(y'(x)\) durch den Differenzenquotient \(\frac{y(x + h) - y(x)}{h}\). Daraus ergeben sich die folgenden Rekursionen für \(n=0,1,2,3,\ldots\):

\[\begin{split}\begin{align} y_{n+1} & = y_n + hf(x_n,y_n) \\ x_{n+1} & = x_n + h \end{align}\end{split}\]

Verbesserte Euler-Methode, Heun-Methode

\[\begin{split}\begin{align} k & = f(x_n,y_n) \\ m & = f(x_n + h, y_n + hk) \\ y_{n+1} & = y_n + h\frac{k + m}{2} \\ x_{n+1} & = x_n + h \end{align}\end{split}\]

Runge-Kutta-Methode

Der empfohlene Algorithmus:

\[\begin{split}\begin{align} k_1 & = f(x_n,y_n) \\ k_2 & = f(x_n + h/2, y_n + hk_1/2) \\ k_3 & = f(x_n + h/2, y_n + hk_2/2) \\ k_4 & = f(x_n + h , y_n + hk_3) \\ y_{n+1} & = y_n + \frac{h}{6}(k_1 + 2k_2 + 2k_3 + k_4) \end{align}\end{split}\]

Aufgaben

Aufgabe 1: GDLG 1. Ordnung

Die GDGL 1. Ordnung \(y'(x) = 2xy\) mit Anfangswert \(y(0) = 1\) hat die exakte Lösung \(y(x) = e^{x^2}\). Implementieren Sie die Methoden von Euler und Heun für eine frei wählbare Schrittweite, und vergleichen Sie diese numerischen Lösungen mit der exakten Lösung im Bereich \(x\in[0,1]\).

Lösung:

x = arange(0.00, 1.1, 0.1)
y = arange(0.95, 3.0, 0.1)
X, Y = meshgrid(x, y)

U = ones(X.shape)
V = 2*X*Y

figure(figsize=(6,9))
quiver(X, Y, U, V, headwidth=0.0, headlength=0.01, headaxislength=0.0)
plot(x, exp(x**2), label='exakt') 
xlabel('x')
ylabel('y')
axis('equal')
legend()
grid(True)
_images/GDGL_Numerik_6_0.png
# Euler-Methode

x0 = 0
y0 = 1

N = 5   # Anzahl Schritte
h = 1/N  # Schrittweite

x = zeros(N + 1)
y = zeros(N + 1)
x[0]= x0
y[0]= y0

def my_f(x, y):
    return 2*x*y

for n in arange(N):
    y[n+1] = y[n] + h*my_f(x[n], y[n])
    x[n+1] = x[n] + h
    
y_Euler = y.copy()
# verbesserte Euler-Methode, Heun-Methode

for n in arange(N):
    k      = my_f(x[n],y[n])
    m      = my_f(x[n] + h, y[n] + h*k)
    y[n+1] = y[n] + h*(k + m)/2
    x[n+1] = x[n] + h
    
y_Heun = y.copy()
figure(figsize=(6,9))
quiver(X, Y, U, V, headwidth=0.0, headlength=0.01, headaxislength=0.0)
plot(x, exp(x**2), label='exakt', linewidth= 2) 
plot(x, y_Euler,'-o', label='Euler', linewidth= 2)
plot(x, y_Heun ,'--', label='Heun', linewidth= 2)
xlabel('x')
ylabel('y')
axis('equal');
legend(numpoints=1)
grid(True)
_images/GDGL_Numerik_9_0.png

Aufgabe 2: Räuber-Beute-System

Quellen:

Lotka-Volterra-Gleichung:

\[\begin{split} \begin{align*} \dot{x} & = ax - bxy \\ \dot{y} & = -cy + dxy \end{align*} \end{split}\]
  • \(x\) ist die Anzahl der Beutetiere (beispielsweise Kaninchen)

  • \(y\) ist die Anzahl der Raubtiere (beispielsweise Füchse)

  • \(\dot{x}\) und \(\dot{y}\) sind die Wachstumsraten der beiden Populationen im Laufe der Zeit

  • \(a\), \(b\), \(c\) und \(d\) sind Parameter, die die Wechselwirkung der beiden Arten beschreiben

Die exakten Lösungen sind geschlossene Kurven!

Lösen Sie die DGL mit der Euler-Methode für die Zeiten \(t\in[0,20]\) mit den Parametern \(a = 1,0\), \(b = 0,5\), \(c = 0,75\), \(d = 0,25\) und frei wählbaren Schrittweiten und Anfangszuständen. Stellen Sie das zugehörige Vektorfeld und die Lösung grafisch dar.

Lösung:

a = 1.0
b = 0.5
c = 0.75
d = 0.25

T = 30.0  # Löse von Zeit 0 bis T
N = 1000  # Anzahl Schritte
h = T/N   # Schrittweite

t0 = 0
x0 = 2
y0 = 2

s = zeros((2, N))    # matrix of state vectors
s[:,[0]] = array([[x0], 
                 [y0]])

t = zeros(N)
t[0] = t0

def vf(x,y):
    xd =  a*x - b*x*y
    yd = -c*y + d*x*y
    return array([[xd], 
                  [yd]])

for n in arange(N-1):
    s[:,[n+1]] = s[:,[n]] + h*vf(s[0,n], s[1,n])
    t[n+1]      = t[n] + h
x = linspace(1, 5, 10)
y = linspace(1, 3, 10)
X, Y = meshgrid(x, y)

figure(figsize=(7,4))
quiver(X, Y, a*X - b*X*Y, -c*Y + d*X*Y)
plot(s[0,:], s[1,:])
axis('equal')
xlabel('x (Beute)')
ylabel('y (Räuber)')
grid(True)
_images/GDGL_Numerik_12_0.png
figure(figsize=(7,4))
plot(t, s[0,:], color='green', label='Beute')
plot(t, s[1,:], color='red',   label='Räuber')
xlabel('time')
legend(loc='best')
grid(True)
_images/GDGL_Numerik_13_0.png

Aufgabe 3: Vergleich numerischer Lösungsmethoden

Solververgleich:

  1. Implementieren Sie die Methode von Runge-Kutta für GDGL.

  2. Vergleichen Sie deren Performance mit jenen der Methoden von Euler und Heun am Beispiel der GDGL \(y'(x) = 2xy\) mit Anfangswert \(y(0) = 1\). Erstellen Sie dazu einen Plot, der auf der Abszisse unterschiedliche Anzahlen von verwendeten Schritten \(N\) und auf der Ordinate die zugehörige logarithmische absolute Abweichung \(\log(|y(1) - \hat{y}(1)|)\) des Endwertes der numerischen Berechnung \(\hat{y}(1)\) vom exakten Endwert \(y(1)\) für alle drei Methoden angibt.

  3. Vergleichen Sie zusätzlich die Performance des Solvers odeint.

Lösung:

from scipy.integrate import odeint
x0 = 0
y0 = 1

def my_f(x, y):
    return 2*x*y

def my_f_(y, x):
    return 2*x*y

N_max = 30                       # maximale Anzahl Schritte
N_ = arange(N_max + 1)

y_Euler = zeros(N_max +1)
y_Heun  = zeros(N_max +1)
y_RuKu  = zeros(N_max +1)

y_Euler[0] = y0
y_Heun[0]  = y0
y_RuKu[0]  = y0

for N in arange(1, N_max + 1):   # Anzahl Schritte
    h = 1/N                      # Schrittweite
    x = zeros(N + 1)
    y = zeros(N + 1)
    x[0]= x0
    y[0]= y0

    # Euler:
    for n in arange(N):
        y[n+1] = y[n] + h*my_f(x[n], y[n])
        x[n+1] = x[n] + h
    y_Euler[N] = y[-1]

    # Heun:
    for n in arange(N):
        k      = my_f(x[n],y[n])
        m      = my_f(x[n] + h, y[n] + h*k)
        y[n+1] = y[n] + h*(k + m)/2
        x[n+1] = x[n] + h
    y_Heun[N] = y[-1]
    
    # Runge-Kutta:
    for n in arange(N):
        k1     = my_f(x[n],y[n])
        k2     = my_f(x[n] + h/2, y[n] + h*k1/2)
        k3     = my_f(x[n] + h/2, y[n] + h*k2/2)
        k4     = my_f(x[n] + h  , y[n] + h*k3)
        y[n+1] = y[n] + h/6*(k1 + 2*k2 + 2*k3 + k4)
        x[n+1] = x[n] + h
    y_RuKu[N] = y[-1]
    
# odeint
y = odeint(my_f_, y0, x)
y_odeint = y[-1]
figure(figsize=(7,7))
semilogy(N_, abs(e - y_Euler) ,'o-', label='Euler')
semilogy(N_, abs(e - y_Heun)  ,'o-', label='Heun')
semilogy(N_, abs(e - y_RuKu)  ,'o-', label='RuKu')
semilogy(N_, ones(shape(N_))*abs(e - y_odeint),'-', label='odeint')
xlabel('Anzahl der Schritte')
ylabel('absolute Abweichung von $y(1)$')
legend(numpoints=1)
grid(True)
_images/GDGL_Numerik_17_0.png