Praktikum zur Vorlesung Numerik partieller Differentialgleichungen I
Mario Ohlberger, Felix Schindler
~/NPDGL1/notebooks/blatt_04.ipynb
.numpy
und pymor.basic
und machen Sie matplotlib
für das Notebook nutzbar.%autosave 0
%matplotlib notebook
import numpy as np
from pymor.basic import *
from matplotlib import pyplot as plt
Machen Sie Teile von Blatt 2 wiederverwedbar, indem Sie
interpolations.py
erstellen und darininterpolate_lagrange_p1(grid, f)
erstellen, welche die Interpolation im Sinne von Blatt 2, Aufgabe 1 berechnet und zurück gibt, undvisualizations.py
erstellen und darinvisualize_lagrange_p1(grid, f_h, name, title)
erstellen, welche eine beliebige Menge von diskreten Funktionen im Sinne von Blatt 2, Aufgabe 2 visualisiert.cat interpolations.py
cat visualizations.py
Hinweis: Nennen Sie das Gebiet omega
, das Gitter coarse_grid
, die Funktion f
und ihre Interpolierte f_H
.
omega = (0., 2.*np.pi)
coarse_grid = OnedGrid(omega, num_intervals=4)
from interpolations import interpolate_lagrange_p1
from visualizations import visualize_lagrange_p1
f = ExpressionFunction('sin(x)')
f_H = interpolate_lagrange_p1(coarse_grid, f)
visualize_lagrange_p1(coarse_grid, f_H, 'f_H', 'Interpolation on coarse grid')
Machen Sie Teile von Blatt 3 wiederverwedbar, indem Sie
grid_tools.py
erstellen und darinF(grid, T, x_hat)
erstellen, welche die Auswertung der Referenzabbildung im Sinne von Blatt 3, Aufgabe 1 berechnet und zurück gibt, undF_inv(grid, T, x)
erstellen, welche die Auswertung der inversen Referenzabbildung im Sinne von Blatt 3, Aufgabe 1 berechnet und zurück gibt undshape_functions.py
erstellen und darinlagrangian_p1_basis(T_hat, x_hat)
erstellen, welche die Auswertung der $P^1$-Basis im Sinne von Blatt 3, Aufgabe 2 berechnet und zurück gibt.cat grid_tools.py
cat shape_functions.py
Schreiben Sie eine Funktion L2_norm_lagrange_p1(grid, f_h)
, welche $\|f_h\|_{L^2(\Omega)}$ berechnet, wobei f_h
der Vektor der Freiheitsgrade zum Gitter grid
sei und testen Sie Ihre Funktion mit unten stehendem Code.
Leiten Sie dazu zuerst eine entsprechende Integrationsformel her:
Hinweis:
quadrature
von einem Referenzelement.integration_elements
vom Gitterfrom shape_functions import lagrangian_p1_basis
def L2_norm_lagrange_p1(grid, f_h):
assert len(f_h) == grid.size(1)
l2_norm_sqrd = 0.
T_hat = grid.reference_element(0)
points, weights = T_hat.quadrature(order=2)
for T in range(grid.size(0)):
ie = grid.integration_elements(0)[T]
for x_q, w_q in zip(points, weights):
phi_x_q = lagrangian_p1_basis(T_hat, x_q)
global_vertex_ids = grid.subentities(0, 1)[T]
f_h_x_q = phi_x_q.ravel().dot(f_h[global_vertex_ids])
l2_norm_sqrd += ie * w_q * f_h_x_q**2
return np.sqrt(l2_norm_sqrd)
unit_interval = OnedGrid()
one = np.ones(unit_interval.size(1))
assert L2_norm_lagrange_p1(unit_interval, one) == 1
Mit der Definition der Lagrange-Basis von Blatt 5 ergibt sich aus der Basisdarstellung von $f_h$, dass
$$\begin{align*} f_h|_T = \sum_{I=1}^{\dim S_h^1}(\underline{f_h})_I\; \varphi_I|_T = \sum_{i = 1}^{d + 1} (\underline{f_h})_I\; \hat{\varphi}_i \circ F_T&&\text{mit } I = \sigma_T(i). \end{align*}$$Damit ergibt sich für die $L^2$-Norm
$$\begin{align*} \|f_h\|_{L^2}^2 &= \int_\Omega f_h(x)^2 \text{d}x\\ &= \sum_{T \in \tau_h} \int_T f_h(x)^2 \text{d}x\\ &= \sum_{T \in \tau_h} \int_T \Big[\sum_{i = 1}^{d + 1} (\underline{f_h})_I\; \big(\hat{\varphi}_i \circ F_T\big)(x)\Big]^2 \text{d}x\\ &= \sum_{T \in \tau_h} \int_{\hat{T}} |\det\nabla F|\; \Big[\sum_{i = 1}^{d + 1} (\underline{f_h})_I\; \big(\hat{\varphi}_i \circ F_T \circ F_T^{-1}\big)(\hat{x})\Big]^2 \text{d}\hat{x}\\ &= \sum_{T \in \tau_h} \int_{\hat{T}} |\det\nabla F|\; \big(\sum_{i = 1}^{d + 1}(\underline{f_h})_I\; \hat{\varphi}_i(\hat{x})\big)^2\text{d}\hat{x}\\ &\approx \sum_{T \in \tau_h} \sum_{q = 1}^Q \omega_q\, |\det\nabla F|\; \big(\sum_{i = 1}^{d + 1}(\underline{f_h})_I \;\hat{\varphi}_i(\hat{x}_q)\big)^2 \end{align*}$$Schreiben Sie eine Funktion prolong_lagrange_p1(coarse_grid, coarse_DoFs, fine_grid)
, welche eine diskrete Funtion (gegeben durch den Vektor an Freiheitsgeraden coarse_DoFs
bezüglich des Gitters coarse_grid
) auf einem feinerem Gitter fehlerfrei interpoliert.
interpolate_lagrange_p1
Funktion eine Interpolation bezüglich des feinen Gitters.from grid_tools import F, F_inv
def prolong_lagrange_p1(coarse_grid, coarse_DoFs, fine_grid):
assert len(coarse_DoFs) == coarse_grid.size(1)
fine_vertices = fine_grid.centers(1)
f_h = np.zeros(len(fine_vertices))
for i, x in enumerate(fine_vertices):
found_element = False
for T in range(coarse_grid.size(0)):
global_vertex_ids = coarse_grid.subentities(0, 1)[T]
a = coarse_grid.centers(1)[global_vertex_ids]
if a[0] <= x <= a[1]:
found_element = True
break # this is the correct element
assert found_element, 'Point {} is not contained in grid!'.format(x)
x_hat = F_inv(coarse_grid, T, x)
phi_x = lagrangian_p1_basis(coarse_grid.reference_element(0), x_hat)
f_x = phi_x.ravel().dot(coarse_DoFs[global_vertex_ids])
f_h[i] = f_x
return f_h
fine_grid = OnedGrid(omega, num_intervals=8)
f_h = prolong_lagrange_p1(coarse_grid, f_H, fine_grid)
visualize_lagrange_p1([coarse_grid, fine_grid], [f_H, f_h], ['f_H', 'f_h'])
Bestimmen Sie den Interpolationsfehler, indem Sie zuerst
und dann für Gitter der Feinheit 4, 8, 16, 32 jeweils
Visualisieren Sie die Differenzen in einem Plot.
I = 4
num_refs = 4
reference_grid = OnedGrid(omega, num_intervals=I * 2**(num_refs + 2))
f_ref = interpolate_lagrange_p1(reference_grid, f)
differences = []
labels = []
grid_sizes = []
grid_widths = []
for _ in range(num_refs):
print(f'prolonging from grid of size {I}...')
grid = OnedGrid(omega, num_intervals=I)
grid_sizes.append(grid.size(0))
grid_widths.append(np.max(grid.diameters(0)))
f_h = interpolate_lagrange_p1(grid, f)
f_h = prolong_lagrange_p1(grid, f_h, reference_grid)
differences.append(f_ref - f_h)
labels.append(f'f - f_h ({I} elements)')
I *= 2
visualize_lagrange_p1([reference_grid] * num_refs, differences, labels, title='interpolation error')
Wir wissen nach Satz 2.34, dass $$ \|f - f_h\|_{L^2(\Omega)} \leq C h^k $$ für eine geeignete Konstante $C > 0$ (die nicht vom Gitter abhängt). Bestimmen Sie die Konvergenzordnung $k$ experimentell, indem Sie den EOC (experimental order of convergence) berechnen, der folgendermaßen definiert ist: $$ EOC^{(\nu)} := \frac{\ln\Big(\frac{\big\|f - f_h^{(\nu)}\big\|_{L^2(\Omega)}}{\big\|f - f_h^{(\nu - 1)}\big\|_{L^2(\Omega)}}\Big)}{\ln\Big(\frac{h^{(\nu)}}{h^{(\nu - 1)}}\Big)} $$ Dabei bezeichnet $\nu = 0, 1, 2, \dots$ das Level der Verfeinerung und $f_h^{(\nu)}$ die Interpolierte auf das jeweilige Gitter mit Gitterweite $h^{(\nu)}$.
Visualisieren Sie die Fehler geeigneter Weise.
errors = []
for diff in differences:
l2_norm_sqrd = 0.
T_hat = reference_grid.reference_element(0)
points, weights = T_hat.quadrature(order=9)
#print(f'points = {points}')
#print(f'weights = {weights}')
for T in range(reference_grid.size(0)):
ie = reference_grid.integration_elements(0)[T]
for x_q, w_q in zip(points, weights):
# evaluate difference in x_q
phi_x_q = lagrangian_p1_basis(T_hat, x_q)
global_vertex_ids = reference_grid.subentities(0, 1)[T]
diff_x_q = phi_x_q.ravel().dot(diff[global_vertex_ids])
l2_norm_sqrd += ie * w_q * diff_x_q**2
errors.append(np.sqrt(l2_norm_sqrd))
plt.figure()
plt.loglog(grid_sizes, errors, label='L^2 error')
plt.xlabel('grid sizes')
plt.ylabel('error')
plt.legend()
for i in range(len(errors) - 1):
e_coarse = errors[i]
e_fine = errors[i + 1]
h_coarse = grid_widths[i]
h_fine = grid_widths[i + 1]
print(np.log(e_fine / e_coarse) / np.log(h_fine / h_coarse))