Praktikum zur Vorlesung Numerik partieller Differentialgleichungen I

Mario Ohlberger, Felix Schindler

Blatt 03, 13.11.2019

  • Aktivieren Sie wie gewohnt ihre Arbeitsumgebung und starten Sie den Jupyter Notebook server, siehe zB Blatt 1, Aufgabe 0).
  • Laden Sie dieses Blatt von der Homepage herunter und speichern Sie es unter ~/NPDGL1/notebooks/blatt_03.ipynb.
  • Importieren Sie numpy und pymor.basic und machen Sie matplotlib für das Notebook nutzbar.
In [ ]:
%matplotlib notebook
import numpy as np
from pymor.basic import *
from matplotlib import pyplot as plt

Nutzen Sie für diesen Zettel das folgende Gitter mit 4 Elementen!

In [ ]:
omega = (0., 1.)
grid = OnedGrid(omega, num_intervals=4)

Aufgabe 1: Die Referenzabbildung [Lemma 2.11, Übungszettel 4]

Ein Merkmal affiner Gitter $\mathcal{T}_h$ mit Referenzelement $\hat{T} \subset \mathbb{R}^d$ ist es, dass zu jedem Gitterelement $T \in \mathcal{T}_h$ eine affine Abbildung, die Referenzabbildung, $$\begin{align*} F_T: \hat{T}&\to T\\ \hat{x}&\mapsto x := F_T(\hat{x}) := A_T\,\hat{x} + b_T, \end{align*}$$ gibt, mit einer positiv definiten Matrix $A_T \in \mathbb{R}^{d \times d}$ und einem Vektor $b_T \in \mathbb{R}^d$, sodass $$ T = F_T(\hat{T}). $$

In einer Raumdimension gilt $\hat{T} := [\hat{a}_0, \hat{a}_1]$ mit $\hat{a}_0 = 0$ und $\hat{a}_1 = 1$.

  1. Schreiben Sie eine Funktion F(grid, T, x_hat), welche die Auswertung von $F_T(\hat{x})$ für alle $\hat{x} \in $x_hat berechnet und testen Sie Ihre Funktion mit dem Code in der nächsten Code-Zelle im Notebook!

    Hinweise:

    • Sie können davon ausgehen, dass Sie als erstes Argument grid ein affines Gitter im pyMOR Sinne übergeben wird, und als zweites Argument T$\in \mathbb{N}$ der Index eines Gitterelementes $T \in \mathcal{T}_h$.

    • Als drittes Argument soll es möglich sein

      • sowohl einzelne Zahlen, also F(grid, T, 0.5), als auch
      • eine Python-Liste von Zahlen, also F(grid, T, [0., 0.5, 1.]), als auch
      • ein numpy-array, also F(grid, T, np.linspace(0., 1., 3)),

      übergeben zu können. Sie können dies mit Hilfe von isinstance innerhalb der Funktion erreichen:

      if not isinstance(x_hat, np.ndarray):
          x_hat = np.array(x_hat)

      Nach diesem Aufruf können Sie davon ausgehen, dass x_hat immer ein antsprechendes numpy-array ist

    • Nutzen Sie die embeddings Funktion des affinen Gitters (siehe Dokumentation oben), um $A_T$ und $b_T$ zu erhalten.

    • Geben Sie ein numpy-array der Größe len(x_hat) zurück.

In [ ]:
 
In [ ]:
T_hat = grid.reference_element(0)
a_hat = T_hat.subentities(0, 1)
print('T_hat = {}'.format(a_hat))
for T in range(grid.size(0)):
    print('  F_{}(T_hat) = {}'.format(T, F(grid, T, a_hat)))
  1. Stellen Sie in Ihrer Funktion außerdem sicher, dass alle Punkte in x_hat im Referenzelement $\hat{T}$ liegen und testen Sie, dass der folgende Aufruf fehlschlägt! Geben Sie eine informative Fehlermeldung, falls die Punkte nicht im Referenzelement liegen.

    Hinweis:

    • Nutzen Sie dazu assert mit zwei Argumenten: assert condition, 'error message if condition is False'

    • Kommentieren Sie nach erfolgreichem Test die nächsten Code-Zellen mit Hilfe von # aus.

In [ ]:
F(grid, 0, -1)
In [ ]:
F(grid, 0, 2)

Eine Aussage von Lemma 2.11 ist dass $F_T(\hat{T}) = T$, es gilt also insbesondere

$$ F_T(\hat{a}_i) = a_i $$

für alle

  • Knotenpunkte $\hat{a}_i$ des Referenzelementes $\hat{T}$ und
  • Knotenpunkte $a_i$ des Elementes $T$,

für $1 \leq i \leq 2$.

  1. Überprüfen Sie diese Aussage für alle Element des Gitters.

    Hinweis:

    • Nutzen Sie die subentities Methode des affinen Gitters (siehe Dokumentation oben), um die globalen Indices der Knotenpunkte eines Elementes zu erhalten.

    • Nutzen Sie diese globalen Indices, um die Koordinaten der Knotenpunkte eines Elementes zu erhalten.

    • Vergleichen Sie die so erhaltenen Koordinaten der Knotenpunkte eines Elementes mit den Ergebnissen der Anwendung der Referenzabbildung auf a_hat.

Eine weitere Aussage von Lemma 2.11 und Übungszettel 4, Aufgabe 2 ist die folgende Abschätzung für den Gradienten der Referenzabbildung:

$$ \|\nabla F_T\| = \|A_T\| \leq \frac{h(T)}{\rho(\hat{T})}\quad,\forall T \in \mathcal{T}_h, $$

wobei $h(T)$ der Durchmesser eines Gitterelementes und $\rho(\hat{T})$ der Durchmesser der Größten in $\hat{T}$ enthaltenen Kugel ist.

  1. Überprüfen Sie diese Aussage für alle Elemente des Gitters.

    Hinweis:

    • Nutzen Sie np.linalg.norm, um die Norm einer Matrix zu berechnen.

    • Den Durchmesser eines Elementes erhalten Sie vom Gitter (siehe Dokumentation oben), $\rho(\hat{T})$ können Sie analytisch herleiten.

Außerdem existiert zu jedem Gitterelement $T \in \mathcal{T}_h$ die inverse Referenzabbildung $$\begin{align*} F^{-1}_T: T &\to \hat{T}\\ x&\mapsto \hat{x} := F^{-1}_T(x). \end{align*}$$

  1. Schreiben Sie eine Funktion F_inv(grid, T, x), welche die Auswertung von $F^{-1}_T(x)$ berechnet und testen Sie Ihre Funktion analog zum Test von der Funktion F oben! Die Ausgabe des Testaufrufs soll folgendermaßen aussehen:

    T_hat = [0 1]
      F_inv_0([0.0, 0.25]) = [0. 1.]
      F_inv_1([0.25, 0.5]) = [0. 1.]
      F_inv_2([0.5, 0.75]) = [0. 1.]
      F_inv_3([0.75, 1.0]) = [0. 1.]

    Hinweise:

    • Es gelten dieselbe Hinweise wie für Teilaufgabe 1.

    • Nutzen Sie np.linalg.solve.

    • Testen Sie Ihre Funktion, indem Sie für jedes Element die Knotenpunkte in a abspeichern und das Resultat von F_inv(grid, T, a) betrachten.

  1. Stellen Sie in Ihrer Funktion außerdem sicher, dass alle Punkte in x im Element $T$ liegen und testen Sie, dass der folgende Aufruf fehlschlägt! Geben Sie eine informative Fehlermeldung, falls die Punkte nicht im Element liegen.

    Hinweis:

    • Es gelten dieselben hinweise wie in Teilaufgabe 2.
In [ ]:
F_inv(grid, 0, -1)
In [ ]:
F_inv(grid, 0, 2)

Eine weitere Aussage von Lemma 2.11 und Übungszettel 4, Aufgabe 2 ist die folgende Abschätzung für den Gradienten der inversen Referenzabbildung:

$$ \|\nabla F_T^{-1}\| = \|A_T^{-1}\| \leq \frac{h(\hat{T})}{\rho(T)}\quad,\forall T \in \mathcal{T}_h, $$

wobei $h(\hat{T})$ der Durchmesser des Referenzelementes und $\rho(T)$ der Durchmesser der Größten in $T$ enthaltenen Kugel ist.

  1. Überprüfen Sie diese Aussage für alle Elemente des Gitters.

    Hinweis:

    • Nutzen Sie np.linalg.inv um die Inverse einer Matrix zu bestimmen und np.linalg.norm, um die Norm einer Matrix zu berechnen.

    • In 1d können Sie $\rho(T) = h(T)$ verwenden, $h(\hat{T})$ können Sie analytisch herleiten.

Aufgabe 2: Die $P^1$-Lagrange Basis [Definition 2.13]

Die Lagrange-Basis erster Ordnung auf dem 1d Referenzelement $\hat{T}$ ist durch die Funktionen $\hat{\varphi}_0, \hat{\varphi}_1: \hat{T} \to \mathbb{R}$ mit $$\begin{align*} \hat{\varphi}_0(\hat{x}) &:= 1 - \hat{x}\\ \hat{\varphi}_1(\hat{x}) &:= \hat{x} \end{align*}$$ für gegeben.

  1. Schreiben Sie eine Funktion lagrangian_p1_basis(T_hat, x_hat), welche die Auswertung von $\hat{\varphi}_0(\hat{x})$ und $\hat{\varphi}_1(\hat{x})$ für alle Punkte $\hat{x} \in $x_hat berechnet und testen Sie ihre Funktion mit Hilfe des Codes in der nächsten Code-Zelle.

    Hinweise:

    • Es gelten dieselben Hinweise wie in Aufgabe 1.1, was die Argumente der Funktion betrifft.

    • Geben Sie ein numpy.array der Größe (2, len(x_hat)) zurück, sodass die erste Zeile die Auswertungen von $\hat{\varphi}_0$, und die zweite Zeile die Auswertungen von $\hat{\varphi}_1$ enthält.

In [ ]:
 
In [ ]:
x_hat = [0, 0.5, 1]
phi = lagrangian_p1_basis(T_hat, x_hat)
print('phi_0({}) = {}'.format(x_hat, phi[0]))
print('phi_1({}) = {}'.format(x_hat, phi[1]))
  1. Stellen Sie in Ihrer Funktion außerdem sicher, dass alle Punkte in x_hat im Referenzelement $\hat{T}$ liegen und testen Sie, dass der folgende Aufruf fehlschlägt! Geben Sie eine informative Fehlermeldung, falls die Punkte nicht im Referenzelement liegen.

    Hinweis:

    • Es gelten dieselben hinweise wie in Teilaufgabe 1.2.
In [ ]:
lagrangian_p1_basis(T_hat, -1)
In [ ]:
lagrangian_p1_basis(T_hat, 2)

Nach Definition ist die $P^1$-Lagrange Basis eine Nodale (knotenbasierte) Basis, es gilt also: $$ \hat{\varphi}_i(\hat{a}_j) = \delta_{ij}\quad\forall 1\leq i, j \leq 2 $$

  1. Überprüfen Sie diese Bedingung programmatisch (d.h. nicht allein durch print) mit Hilfe von np.eye.
  1. Visualisieren Sie $\hat{\varphi}_0$ und $\hat{\varphi}_1$ mit Hilfe von matplotlib zusammen in einem Plot, indem Sie die Funktionen an genügend vielen Punkten auswerten. Geben Sie geeignete Legende und Titel an.