11.3.1. Richardson Verfahren#

Die Theorie folgt direkt dem iFEM Jupyter-Notebook, wobei wir für das Beispiel Dirichlet Randbedingungen implementieren.

Fixpunkt Iteration#

Sei \(C\) eine reguläre Matrix und \(\alpha\in\mathbb{R}^+\) ein Dämpfungsfaktor. Dann ist das lineare Gleichungssystem äquivalent zur Fixpunktgleichung

(11.1)#\[x = x + \alpha\, C^{-1}\,(b-A x).\]

Daraus ergibt sich die Fixpunkt Iteration, auch einfache Iteration oder Richardson Iteration genannt:

\[x_{k+1} = x_k + \alpha\, C^{-1}\,(b-A x_k)\]

wobei der Startwert \(x_0\) gegeben ist.

Sei \(x^*\) eine Lösung von (11.1) und

\[e_k = x_k - x^*\]

der Iterationsfehler. Dann gilt für den neuen Fehler

\[\begin{split}\begin{split} e_{k+1} & = x_{k+1}-x^* = x_k + \alpha\, C^{-1} (b-A\,x_k) - x^*\\ & = (x_k-x^*) + \alpha\, C^{-1} (A\,x_k-A\,x^*)\\ & = (\mathbb{1} - \alpha\,C^{-1} A)\, (x_k-x^*)\\ & = (\mathbb{1} - \alpha\,C^{-1} A)\, e_k.\\ \end{split}\end{split}\]

Der neue Fehler ergibt sich durch die Iterationsmatrix oder Fehlerübergangsmatrix

\[M = (\mathbb{1} - \alpha\,C^{-1} A)\]

als

\[e_{k+1} = M\, e_k.\]

Sei \(\|\cdot\|\) eine beliebige Vektornorm und

\[|\!|\!|M|\!|\!|:=\sup_{x\in\mathbb{R}^n}\frac{\|M x\|}{\|x\|}\]

die zugehörige Matrixnorm (Operatornorm vgl. (4.2)). Damit gilt für den neuen Fehler (vgl. (4.3))

\[\|e_{k+1}\| \le |\!|\!|M|\!|\!|\, \|e_k\|.\]

Hinreichend für die Konvergenz ist damit die Existenz einer Matrixnorm mit

\[|\!|\!|M|\!|\!|<1.\]

Vorkonditionierer#

Die Matrix \(C\) wird Vorkonditionierer (Preconditioner) genannt. Sie soll zwei Eigenschaften erfüllen:

  • Die Matrix-Vektormultiplikation mit \(C^{-1}\cdot r\) soll billig sein.

  • Die Matrix \(C\) soll eine gute Approximation zu \(A\) sein.

Zur Wahl der Matrix \(C\)

  • Wählt man zum Beispiel \(C = \mathbb{1}\) oder etwas besser \(C = \mathop{diag} A\), dann ist die Operation \(C^{-1}\cdot r\) sehr billig. Die Approximation \(C \approx A\) kann aber schlecht sein.

  • Wählt man hingegen \(C = A\) und \(\alpha = 1\), dann ist die zweite Eigenschaft best-möglich erfüllt:

    \[M = \mathbb{1} - \alpha C^{-1} A = \mathbb{1} - A^{-1}A = 0.\]

    Es gilt daher \(\|M\| = 0\).

    Die Invertierung von \(C\) ist jetzt aber genau das ursprüngliche Problem.

Ziel ist es Vorkonditionierer mit beiden Eigenschaften zu finden.

Richardson Iteration#

Im Richardson Verfahren wird die Matrix \(C=\mathbb{1}\) gesetzt. Damit folgt für die Fehlerübergangsmatrix

\[M = \mathbb{1} - \alpha A.\]

Für die Überprüfung der Konvergenz gibt es zwei Möglichkeiten:

  • den Konvergenzradius zu überprüfen

    \[\rho(\mathbb{1} - \alpha A) = \max_{\lambda\in\sigma(\mathbb{1} - \alpha A)} |\lambda| < 1\]
  • eine geeignete Norm \(\|\cdot\|\) finden, so dass die Matrixnorm

    \[|\!|\!|\mathbb{1} - \alpha A|\!|\!|:=\sup_{x\in\mathbb{R}^n}\frac{\|(\mathbb{1} - \alpha A) x\|}{\|x\|} < 1\]

    erfüllt ist.

Optimieren des Dämpfungsfaktors#

Sei \(A\) symmetrisch und positiv definit. Damit sind die Eigenwerte \(\sigma(A) = \{\lambda_i\in\mathbb{R}\}\) mit \(0<\lambda_1 \le \lambda_2 \ldots \le \lambda_n\).

Die Eigenwerte der Fehlerübergangsmatrix \(M = \mathbb{1} - \alpha\,A\) sind in dem Fall gegeben durch \(\{1-\alpha\, \lambda_i\}\). Wenn wir daher

\[0 < \alpha < \frac{2}{\lambda_n}\]

wählen, folgt \(\rho(M) < 1\) und damit eine konvergente Itertation.

Der Spektralradius von \(M\) ist gegeben durch

\[\rho(M) = \max_i \{|1-\alpha\,\lambda_i|\} = \max \{1-\alpha \lambda_1, -(1-\alpha\,\lambda_n)\}.\]

Das Maximum ist minimiert, wenn wir \(\alpha\) optimal wählen, daher

\[1-\alpha \lambda_1 = -(1-\alpha\,\lambda_n).\]

Somit sei

\[\alpha_{opt} = \frac{2}{\lambda_1+\lambda_n},\]

was zur optimalen Konvergenzrate

\[\rho_{opt} = \frac{\lambda_n-\lambda_1}{\lambda_n+\lambda_1} \approx 1 - \frac{\lambda_1}{\lambda_n} = 1 - \frac{1}{\kappa(A)}\]

führt, wobei mit \(\kappa(A)\) die Konditionszahl \(\kappa(A) = \lambda_{\max}(A) / \lambda_{\min}(A)\) bezeichnet sei.

Nach \(N\) Iterationen ist der Fehler um den Faktor

\[\left(1-\frac{1}{\kappa}\right)^N\]

reduziert. Um die Fehlerreduktion \(\epsilon\) zu erreichen, sind

\[N = \frac{\log \epsilon}{\log(1-1/\kappa)} \approx \kappa |\log \epsilon|\]

Iterationen notwendig.

Anwendung auf Modellproblem#

Wir wenden das Verfahren auf das Modellproblem

\[\begin{split}\begin{split} -\Delta u + 10\, u & = 1\quad x\in\Omega = [0,1]^2\\ u & = 0\quad x \in \partial\Omega\end{split}\end{split}\]

an.

from netgen.geom2d import unit_square
from ngsolve import *
from ngsolve.webgui import Draw
import matplotlib.pyplot as plt
from myst_nb import glue

Diskretierung der schwachen Gleichung mit FEM 1. Ordnung:

mesh = Mesh(unit_square.GenerateMesh(maxh=0.1))
V = H1(mesh, order=1, dirichlet='bottom|right|top|left')
u = V.TrialFunction()
v = V.TestFunction()
a = BilinearForm(grad(u)*grad(v)*dx+10*u*v*dx).Assemble()
f = LinearForm(1*v*dx).Assemble()
gfu = GridFunction(V)

Wir nutzen das CSR Modul von scipy um das reduzierte Problem für die freien Freiheitsgrade zu lösen.

import scipy.sparse as sp
import numpy as np
from numpy.linalg import norm

Für die reduzierte Systemmatrix folgt

rows,cols,vals = a.mat.COO()
ind = np.arange(V.ndof)[np.array(V.FreeDofs())]

# Reduktion auf die freien Freiheitsgrade der Systemmatrix
A = sp.csr_matrix((vals,(rows,cols)))
A = A[np.ix_(ind,ind)]
plt.spy(A)
plt.show()

# Reduktion auf die freien Freiheitsgrade der rechten Seite
fd = np.array(f.vec)[ind]
../_images/b78ed049f43249a95c80098d59111bea9226b485a305fbfce10419cdb34ad85e.png

Eine Approximation der Dämpfung wird mit Hilfe von ein paar Potenziterationen bestimmt:

hv = np.random.rand(ind.shape[0])
hv /= norm(hv)
for k in range(20):
    hv2 = A@hv
    rho = norm(hv2)
    print(rho)
    hv = 1/rho*hv2
1.93892201500378
4.035376374773378
4.391698016287348
4.537850601281457
4.632750753289792
4.708700926551877
4.775311720432426
4.835756943570336
4.891018048080095
4.941275873037939
4.9864812644106795
5.02661571053674
5.061795316336084
5.092286001475633
5.118472911718596
5.140813353082259
5.159791060375444
5.175880311802537
5.189522081405155
5.201110965264451

Richardson Verfahren anwenden

alpha = 1 / rho
sol = np.zeros(V.ndof)
err0 = norm(fd)
its = 0
errs = []
reltol = 1e-8
maxiter = 10000
while True:
    r = fd - A * sol[ind]
    err = norm(r)
    errs.append(err)
    #print ("iteration", its, "res=", err)
    sol[ind] += alpha * r
    if err < reltol * err0 or its > maxiter: break
    its = its+1
print ("needed", its, "iterations")
needed 361 iterations

Für die Abschätzung der Anzahl notwendiger Iterationen erhalten wir:

import scipy.sparse.linalg as spl
np.linalg.cond(A.todense())*np.abs(np.log(1e-8))
np.float64(382.75172899276646)

Achtung, diese Berechnung funktioniert nur für wenig Freiheitsgrade.

fig, ax = plt.subplots()
ax.loglog(errs)
ax.grid()
ax.set_title('Richardson Verfahren: '+str(its)+' iterationen')
ax.set_xlabel('Iteration')
ax.set_ylabel('Residuum')
#plt.savefig("FEM_RichardsonVerfahren_fig.png")
glue("FEM_RichardsonVerfahren_fig", fig, display=False)
../_images/5be5d91979e327e94427f01ca59cdcb3ef7dbcc77938f529fa03c81f1aa97cb7.png

Schreiben wir die Lösung wieder zurück in die GridFunction von ngsolve können wir die Lösung visualisieren:

gfu.vec[:] = sol
Draw(gfu,mesh,'u');