Praktikum zu Vorlesung Modellreduktion parametrisierter Systeme
Mario Ohlberger, Felix Schindler
Aktivieren Sie die virtuelle Umgebung zum Praktikum und starten Sie den Notebook server. Zur Erinnerung:
Terminal starten (super
-Taste/Windows
-Taste, terminal
eingeben, Enter
)
ins Projektverzeichnis wechseln
cd ~/vorlesung_modellreduktion
die virtuelle Umgebung aktivieren
source python_umgebung_praktikum_modellreduktion/bin/activate
Notebook server starten
jupyter-notebook --notebook-dir=notebooks
grid_interpolations
in grid_visualization
um.Erstellen Sie ein neues Python 3
notebook und benennen Sie es in interpolations
um.
Importieren Sie numpy
und pymor.basic
und machen Sie matplotlib
für das Notebook nutzbar.
%matplotlib notebook
import numpy as np
from pymor.basic import *
Schreiben Sie eine Python Funktion visualize_fv(v_h, grid, title)
, die für eine diskrete Funktion $v_h = $ v_h
(gegeben als VectorArray
der Länge 1), Gitter $\mathcal{T}_h = $ grid
und Titel title
, die Funktion $v_h \in V_h^0$ visualisiert, wobei wie in [Blatt 00]
$$V_h^0 := \big\{ v \in L^2(\Omega) \;\big|\; v|_K \in \mathbb{P}_0(K)\quad\forall K \in \mathcal{T}_h\big\}$$
den Raum der stückweise konstanten Funktionen bzgl. des Gitters (Finite Volumen Raum) bezeichnet ($\mathbb{P}_p(\omega)$ ist dabei der Raum der Polynome vom Grad kleiner gleich $p \in \mathbb{N}$ über einem Gebiet $\omega \subset \mathbb{R}^d$).
PatchVisualizer
anzulegen und dessen visualize
Methode zu nuzten.v_h
die richtige Länge und Dimension hat.from pymor.gui.visualizers import PatchVisualizer
def visualize_fv(v_h, grid, title):
fv_visualizer = PatchVisualizer(grid, bounding_box=grid.bounding_box, codim=0)
fv_visualizer.visualize(v_h, d=None, legend=title)
Schreiben Sie eine Python Funktion visualize_grid(grid)
, die ein gegebenes Gitter $\mathcal{T}_h = $ grid
visualisiert.
grid
zu erhalten, einen entsprechenden fv_space
als NumpyVectorSpace
anzulegen, die Funktion $v_h$ als Element von $V_h^0$ mit Hilfe von fv_space
zu erhalten.visualize_fv
Funktion.def visualize_grid(grid):
vertices = grid.centers(grid.dim)
fv_space = NumpyVectorSpace(dim=grid.size(0), id_='FV')
v_h = fv_space.from_data(np.arange(fv_space.dim))
visualize_fv(v_h, grid, 'element indices of a TriaGrid with {} elements'.format(grid.size(0)))
Testen Sie Ihre Funktionen, indem Sie ein Dreicksgitter $\mathcal{T}_h$ mit $1^2$ Intervallen des Gebiets $\Omega = [0, 1]^2$ visualisieren.
visualize_grid
Funktion auf.omega = RectDomain(([0, 0], [1, 1]))
grid = TriaGrid(num_intervals=(1, 1), domain=omega.domain)
visualize_grid(grid)
Zur Visualisierung einer Funktion $f: \Omega \to \mathbb{R}$ auf einem gegebenen Gitter $\mathcal{T}_h$ von $\Omega \subset \mathbb{R}^d$, $d = 1, 2, 3$, benötigen wir eine geeignete Approximation $f_h$. Für stetige Funktionen $f \in C^0$ bietet sich dazu die Lagrange-Interpolation an. Sei dazu
$$S_h^1 := \big\{ v \in C^0(\Omega) \;\big|\; v|_K \in \mathbb{P}_1(K)\quad\forall K \in \mathcal{T}_h\big\} \subset C^0(\Omega)$$der Raum der stetigen und stückweisen linearen Funktionen bezüglich des Gitters.
Sei $\mathcal{T}_h^d$ die Menge aller Knotenpunkte des Gitters (Entitäten mit Kodimension $d$) und $I := |\mathcal{T}_h^d| \in \mathbb{N}$ die Anzahl der Knotenpunkte. Dann ist eine Basis von $S_h^1$ gegeben durch
$$\big\{ \varphi_0, \dots, \varphi_{I - 1} \big\}$$wobei die Hütchenfunktion $\varphi_i$ zu einem Knotenpunkt $\nu_i \in \mathcal{T}_h^d$ eindeutig definiert ist durch
$$\varphi_i(\nu_j) := \delta_{i, j} := \begin{cases}1, & \text{falls }i = j,\\0, &\text{sonst}\end{cases}$$(diese Basis wird Lagrange-Basis genannt).
Damit ist jede Funktion $v_h \in S_h^1$ eindeutig definiert durch die Angabe ihrer Werte in den Knotenpunkten des Gitters, denn aus der Basisdarstellung
$$v_h(x) = \sum_{i = 0}^{I - 1} \underline{v_h}_i \varphi_i(x)$$und der Definition der Basis folgt, dass $\underline{v_h}_i = v_h(\nu_i)$.
Wir können also $S_h^1$ isomorph mit $\mathbb{R}^I$ identifizieren und jede Funktion $v_h \in S_h^1$ mit ihrem DoF Vektor (Degree of Freedom: Freiheitsgrad) $\underline{v_h} \in \mathbb{R}^I$, wobei $\underline{v_h}_i := v_h(\nu_i)$ für alle $0 \leq i_ < I$.
Um für eine beliebige Funktion $f: \Omega \to \mathbb{R}$ eine Approximation in $S_h^1$ zu erhalten, betrachten wir die Lagrange-Interpolation
$$\Pi_{S_h^1}: C^0(\Omega) \rightarrow S_h^1,\quad\quad f \mapsto \Pi_{S_h^1}[f],$$die eindeutig definiert ist durch die Angabe ihrer Werte in den Knotenpunkten,
$$\Pi_{S_h^1}[f](\nu_i) := f(\nu_i) \text{ für } 0 \leq i < I.$$Als Approximation von $f$ erhalten wir also $f_h \in S_h^1$ als $f_h := \Pi_{S_h^1}[f]$ und den dazugehörigen DoF Vektor $\underline{f_h} \in \mathbb{R}^I$ durch
$$\underline{f_h}_i := f(\nu_i), \text{ für alle } 0 \leq 1 < I.$$Bemerkung: Die Lagrange-Interpolation ist eine Projektion, denn $(\Pi_{S_h^1} \circ \Pi_{S_h^1})[v_h] = \Pi_{S_h^1}[v_h]$ für alle $v_h \in S_h^1$.
Schreiben Sie eine Python Funktion interpolate_lagrange_p1(f, grid)
, die für eine gegebene Funktion $f = $ f
und Gitter $\mathcal{T}_h = $ grid
die Lagrange-Interpolierte $f_h := \Pi_{S_h^1}[f]$ als geeignetes VectorArray
zurück gibt.
NumpyVectorSpace
entsprechender Dimension, mit der Kennung CG
(continuous Galerkin) und legen Sie ein entsprechendes Objekt cg_space
an.f
an allen Knotenpunkten des Gitters aus, um $\underline{f_h}$ zu erhalten.cg_space
nutzen.def interpolate_lagrange_p1(f, grid):
cg_space = NumpyVectorSpace(dim=grid.size(grid.dim), id_='CG')
vertices = grid.centers(grid.dim)
f_h = f.evaluate(vertices)
return cg_space.from_data(f_h)
Testen Sie Ihre interpolate_lagrange_p1
Funktion, indem Sie die wie in [Blatt 00, Aufgabe 1] gegebene Funktion $f: \Omega \to \mathbb{R}$, $f(x_0, x_1) := x_0 \cdot x_1$ auf $\Omega = [0, 1]^2$ interpolieren.
ExpressionFunktion
.interpolate_lagrange_p1
mit dieser Funktion und dem Gitter aus Aufgabe 1 auf und geben Sie die Länge und Dimension des resultierenden VectorArray
s, sowie die Anzahl der Knotenpunkte des Gitters aus.f = ExpressionFunction('x[..., 0]*x[..., 1]', dim_domain=2, shape_range=())
f_h = interpolate_lagrange_p1(f, grid)
print(len(f_h))
print(f_h.dim)
print(grid.size(grid.dim))
Schreiben Sie eine Funktion visualize_lagrange_p1(v_h, grid, title)
, die für eine diskrete Funtion $v_h = $ v_h
(gegeben als VectorArray
der Länge 1), Gitter $\mathcal{T}_h = $ grid
und Titel title
, die Funktion $v_h \in S_h^1$ visualisiert.
v_h
die richtige Länge und Dimension hat.cg_visualizer
vom Typ PatchVisualizer, achten Sie auf die korrekte Kodimension!visualize
Methode, geben Sie mit legend=
einen Titel an, setzen Sie bei Bedarf d=None
.def visualize_lagrange_p1(v_h, grid, title):
assert len(v_h) == 1
assert v_h.dim == grid.size(grid.dim)
cg_visualizer = PatchVisualizer(grid, bounding_box=grid.bounding_box, codim=2)
cg_visualizer.visualize(v_h, d=None, legend=title)
Testen Sie Ihre visualize_lagrange_p1
Funktion, indem Sie die Funktion $f$ für verschiede Gitter interpolieren und visualisieren, messen Sie dabei jeweils die Zeit für
Testen Sie mindestens folgende Gitter:
import time
t = time.time()
grid = TriaGrid(num_intervals=(1, 1), domain=omega.domain)
print('creating simplex grid with {} elements took {}s'.format(grid.size(0), time.time() - t))
t = time.time()
f_h = interpolate_lagrange_p1(f, grid)
print('interpolation took {}s'.format(time.time() - t))
t = time.time()
visualize_lagrange_p1(f_h, grid, 'TriaGrid with {} elements'.format(grid.size(0)))
print('visualization took {}s'.format(time.time() - t))
t = time.time()
grid = TriaGrid(num_intervals=(512, 512), domain=omega.domain)
print('creating simplex grid with {} elements took {}s'.format(grid.size(0), time.time() - t))
t = time.time()
f_h = interpolate_lagrange_p1(f, grid)
print('interpolation took {}s'.format(time.time() - t))
t = time.time()
visualize_lagrange_p1(f_h, grid, 'TriaGrid with {} elements'.format(grid.size(0)))
print('visualization took {}s'.format(time.time() - t))
t = time.time()
grid = RectGrid(num_intervals=(2, 2), domain=omega.domain)
print('creating cubic grid with {} elements took {}s'.format(grid.size(0), time.time() - t))
t = time.time()
f_h = interpolate_lagrange_p1(f, grid)
print('interpolation took {}s'.format(time.time() - t))
t = time.time()
visualize_lagrange_p1(f_h, grid, 'RectGrid with {} elements'.format(grid.size(0)))
print('visualization took {}s'.format(time.time() - t))
t = time.time()
grid = RectGrid(num_intervals=(1024, 1024), domain=omega.domain)
print('creating cubic grid with {} elements took {}s'.format(grid.size(0), time.time() - t))
t = time.time()
f_h = interpolate_lagrange_p1(f, grid)
print('interpolation took {}s'.format(time.time() - t))
t = time.time()
visualize_lagrange_p1(f_h, grid, 'RectGrid with {} elements'.format(grid.size(0)))
print('visualization took {}s'.format(time.time() - t))
Für allgemeine (unstetige Funktionen) $f: \Omega \to \mathbb{R}$ bietet sich außerdem die Approximation (und Visualisierung) als Finite Volumen Funktion an, die wir schon zur Visualisierung des Gitters genutzt haben. Sei $\mathcal{T}_h$ ein Gitter mit $I := |\mathcal{T}_h| \in \mathbb{N}$ Elementen (Entitäten der Kodimension 0). Eine Basis des Finite Volumen Raumes $V_h^0$ ist durch
$$\big\{ \chi_{K_0}, \dots, \chi_{K_{I - 1}} \big\}$$gegeben, wobei
$$\chi_{K_i} := \begin{cases}1, &\text{für } x \in K_i\\0, &\text{sonst}\end{cases}$$die Indikatorfunktion bezüglich des Gitterelementes $K_i \in \mathcal{T}_h$ sei, für $0 \leq i < I$. Da jede Funktion $v_h \in V_h^0$ stückweise konstant bezüglich des Gitters $\mathcal{T}_h$ ist (vergleiche die Definition von $V_h^0$ oben), ist Sie eindeutig durch die Angabe eines Wertes $\underline{v_h}_i \in \mathbb{R}$ auf jedem Gitterelement $K_i \in \mathcal{T}_h$ gegeben, was aus der Basisdarstellung
$$v_h(x) := \sum_{i = 0}^{I - 1} \underline{v_h}_i \chi_{K_i}(x)$$und der Definition der Basis und $V_h^0$ folgt.
Um für eine beliebige Funktion $f \in L^2(\Omega)$ eine Approximation in $V_h^0$ zu erhalten, betrachten wir die Finite Volumen-Iterpolation
$$\Pi_{V_h^0}: L^2(\Omega) \rightarrow V_h^0,\quad\quad f \mapsto \Pi_{V_h^0}[f],$$die eindeutig definiert ist durch die Angabe ihrer Werte auf den Gitterelementen als Mittelwert der Funktion $f$,
$$\Pi_{V_h^0}[f]\Big|_{K_i} := |K_i|^{-1} \int_{K_i} f(x) \,\text{d}x \text{ für } 0 \leq i < I.$$Als Approximation von $f$ erhalten wir also $f_h \in V_h^0$ als $f_h := \Pi_{V_h^0}[f]$ und den dazugehörigen DoF Vektor $\underline{f_h} \in \mathbb{R}^I$ durch
$$\underline{f_h}_i := |K_i|^{-1} \int_{K_i} f(x) \,\text{d}x \text{ für } 0 \leq i < I.$$Bemerkung: Die Finite Volumen-Interpolation ist eine Projektion (genauer gesagt eine $L^2$-Projektion), denn $(\Pi_{V_h^0} \circ \Pi_{V_h^0})[v_h] = \Pi_{V_h^0}[v_h]$ für alle $v_h \in V_h^0$.
Schreiben Sie eine Python Funktion interpolate_fv(f, grid)
, die für eine gegebene Funktion $f = $ f
und Gitter $\mathcal{T}_h = $ grid
die Finite Volumen-Interpolierte $f_h := \Pi_{V_h^0}[f]$ als geeignetes VectorArray
zurück gibt.
NumpyVectorSpace
entsprechender Dimension, mit der Kennung FV
und legen Sie ein entsprechendes Objekt fv_space
an.fv_space
nutzen.def interpolate_fv(f, grid):
fv_space = NumpyVectorSpace(dim=grid.size(0), id_='FV')
centers = grid.centers(0)
f_h = f.evaluate(centers)
return fv_space.from_data(f_h)
interpolate_fv
Funktion, indem Sie die Funktionauf $\Omega = [0, 1]^2$ interpolieren.
ExpressionFunktion
.interpolate_fv
mit dieser Funktion und dem groben Dreiecksgitter auf und geben Sie die Länge und Dimension des resultierenden VectorArray
s, sowie die Anzahl der Elements des Gitters aus.grid = TriaGrid(domain=omega.domain, num_intervals=(2, 2))
f = ExpressionFunction('(0.25 <= x[..., 0]) * (x[..., 0] <= 0.5) * (0.25 <= x[..., 1]) * (x[..., 1] <= 0.5) * 1.', dim_domain=2, shape_range=())
f_h = interpolate_fv(f, grid)
print(len(f_h))
print(f_h.dim)
print(grid.size(0))
Testen Sie Ihre interpolate_fv
und visualize_fv
Funktion von oben, indem Sie die Funktion $f$ für verschiede Gitter interpolieren und visualisieren, testen Sie mindestens folgende Gitter:
grid = TriaGrid(num_intervals=(2, 2), domain=omega.domain)
f_h = interpolate_fv(f, grid)
visualize_fv(f_h, grid, 'TriaGrid with {} elements'.format(grid.size(0)))
grid = TriaGrid(num_intervals=(3, 3), domain=omega.domain)
f_h = interpolate_fv(f, grid)
visualize_fv(f_h, grid, 'TriaGrid with {} elements'.format(grid.size(0)))
grid = TriaGrid(num_intervals=(4, 4), domain=omega.domain)
f_h = interpolate_fv(f, grid)
visualize_fv(f_h, grid, 'TriaGrid with {} elements'.format(grid.size(0)))
grid = RectGrid(num_intervals=(2, 2), domain=omega.domain)
f_h = interpolate_fv(f, grid)
visualize_fv(f_h, grid, 'RectGrid with {} elements'.format(grid.size(0)))
grid = RectGrid(num_intervals=(3, 3), domain=omega.domain)
f_h = interpolate_fv(f, grid)
visualize_fv(f_h, grid, 'RectGrid with {} elements'.format(grid.size(0)))
grid = RectGrid(num_intervals=(4, 4), domain=omega.domain)
f_h = interpolate_fv(f, grid)
visualize_fv(f_h, grid, 'RectGrid with {} elements'.format(grid.size(0)))