Loading [MathJax]/jax/output/HTML-CSS/jax.js

Praktikum zu Vorlesung Modellreduktion parametrisierter Systeme

Mario Ohlberger, Felix Schindler

Blatt 01, 10.04.2019

Aufgabe 0: jupyter Notebook Server starten

  1. 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
      
  1. Benennen Sie das notebook von letzter Woche von grid_interpolations in grid_visualization um.
  1. Erstellen Sie ein neues Python 3 notebook und benennen Sie es in interpolations um.

  2. Importieren Sie numpy und pymor.basic und machen Sie matplotlib für das Notebook nutzbar.

In [1]:
%matplotlib notebook
import numpy as np
from pymor.basic import *

Aufgabe 1: Wiederholung

  1. Schreiben Sie eine Python Funktion visualize_fv(v_h, grid, title), die für eine diskrete Funktion vh= v_h (gegeben als VectorArray der Länge 1), Gitter Th= grid und Titel title, die Funktion vhV0h visualisiert, wobei wie in [Blatt 00]

    V0h:={vL2(Ω)|v|KP0(K)KTh}

    den Raum der stückweise konstanten Funktionen bzgl. des Gitters (Finite Volumen Raum) bezeichnet (Pp(ω) ist dabei der Raum der Polynome vom Grad kleiner gleich pN über einem Gebiet ωRd).

    • Kopieren Sie dazu den Programmcode aus [Blatt 00], um einen entsprechenden PatchVisualizer anzulegen und dessen visualize Methode zu nuzten.
    • Stellen Sie sicher, dass v_h die richtige Länge und Dimension hat.
In [2]:
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)
  1. Schreiben Sie eine Python Funktion visualize_grid(grid), die ein gegebenes Gitter Th= grid visualisiert.

    • Kopieren Sie dazu den Programmcode aus [Blatt 00], um den DoF Vektor der Funktion vhV0h,vh|K:=index of K in the grid,KTh aus grid zu erhalten, einen entsprechenden fv_space als NumpyVectorSpace anzulegen, die Funktion vh als Element von V0h mit Hilfe von fv_space zu erhalten.
    • Nutzen Sie die visualize_fv Funktion.
In [3]:
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)))
  1. Testen Sie Ihre Funktionen, indem Sie ein Dreicksgitter Th mit 12 Intervallen des Gebiets Ω=[0,1]2 visualisieren.

    • Kopieren Sie den Programmcode aus [Blatt 00], um ein Gebiet und ein Dreiecksgitter anzulegen.
    • Rufen Sie die visualize_grid Funktion auf.
In [4]:
omega = RectDomain(([0, 0], [1, 1]))
grid = TriaGrid(num_intervals=(1, 1), domain=omega.domain)
visualize_grid(grid)

Aufgabe 2: Lagrange Interpolation

Zur Visualisierung einer Funktion f:ΩR auf einem gegebenen Gitter Th von ΩRd, d=1,2,3, benötigen wir eine geeignete Approximation fh. Für stetige Funktionen fC0 bietet sich dazu die Lagrange-Interpolation an. Sei dazu

S1h:={vC0(Ω)|v|KP1(K)KTh}C0(Ω)

der Raum der stetigen und stückweisen linearen Funktionen bezüglich des Gitters.

Sei Tdh die Menge aller Knotenpunkte des Gitters (Entitäten mit Kodimension d) und I:=|Tdh|N die Anzahl der Knotenpunkte. Dann ist eine Basis von S1h gegeben durch

{φ0,,φI1}

wobei die Hütchenfunktion φi zu einem Knotenpunkt νiTdh eindeutig definiert ist durch

φi(νj):=δi,j:={1,falls i=j,0,sonst

(diese Basis wird Lagrange-Basis genannt).

Damit ist jede Funktion vhS1h eindeutig definiert durch die Angabe ihrer Werte in den Knotenpunkten des Gitters, denn aus der Basisdarstellung

vh(x)=I1i=0vh_iφi(x)

und der Definition der Basis folgt, dass vh_i=vh(νi).

Wir können also S1h isomorph mit RI identifizieren und jede Funktion vhS1h mit ihrem DoF Vektor (Degree of Freedom: Freiheitsgrad) vh_RI, wobei vh_i:=vh(νi) für alle 0i<I.

Um für eine beliebige Funktion f:ΩR eine Approximation in S1h zu erhalten, betrachten wir die Lagrange-Interpolation

ΠS1h:C0(Ω)S1h,fΠS1h[f],

die eindeutig definiert ist durch die Angabe ihrer Werte in den Knotenpunkten,

ΠS1h[f](νi):=f(νi) für 0i<I.

Als Approximation von f erhalten wir also fhS1h als fh:=ΠS1h[f] und den dazugehörigen DoF Vektor fh_RI durch

fh_i:=f(νi), für alle 01<I.

Bemerkung: Die Lagrange-Interpolation ist eine Projektion, denn (ΠS1hΠS1h)[vh]=ΠS1h[vh] für alle vhS1h.

  1. Schreiben Sie eine Python Funktion interpolate_lagrange_p1(f, grid), die für eine gegebene Funktion f= f und Gitter Th= grid die Lagrange-Interpolierte fh:=ΠS1h[f] als geeignetes VectorArray zurück gibt.

    • Definieren Sie S1h als NumpyVectorSpace entsprechender Dimension, mit der Kennung CG (continuous Galerkin) und legen Sie ein entsprechendes Objekt cg_space an.
    • Werten Sie die Funktion f an allen Knotenpunkten des Gitters aus, um fh_ zu erhalten.
    • Erstellen Sie fhS1h, indem Sie die entsprechende Funktion des cg_space nutzen.
In [5]:
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)
  1. Testen Sie Ihre interpolate_lagrange_p1 Funktion, indem Sie die wie in [Blatt 00, Aufgabe 1] gegebene Funktion f:ΩR, f(x0,x1):=x0x1 auf Ω=[0,1]2 interpolieren.

    • Erstellen Sie f als ExpressionFunktion.
    • Rufen Sie interpolate_lagrange_p1 mit dieser Funktion und dem Gitter aus Aufgabe 1 auf und geben Sie die Länge und Dimension des resultierenden VectorArrays, sowie die Anzahl der Knotenpunkte des Gitters aus.
In [6]:
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))
1
5
5
  1. Schreiben Sie eine Funktion visualize_lagrange_p1(v_h, grid, title), die für eine diskrete Funtion vh= v_h (gegeben als VectorArray der Länge 1), Gitter Th= grid und Titel title, die Funktion vhS1h visualisiert.

    • Stellen Sie sicher, dass v_h die richtige Länge und Dimension hat.
    • Erstellen Sie einen cg_visualizer vom Typ PatchVisualizer, achten Sie auf die korrekte Kodimension!
    • Nutzen Sie die visualize Methode, geben Sie mit legend= einen Titel an, setzen Sie bei Bedarf d=None.
In [7]:
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)
  1. 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

    • Anlegen des Gitters
    • Interpolation
    • Visualisierung

    Testen Sie mindestens folgende Gitter:

    • ein grobes Dreiecksgitter mit 12 Intervallen
    • ein feines Dreiecksgitter mit 5122 Intervallen
    • ein grobes Vierecksgitter mit 22 Intervallen
    • ein feines Vierecksgitter (10242 Intervallen)
In [8]:
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))
creating simplex grid with 4 elements took 0.0012879371643066406s
interpolation took 0.0045588016510009766s
visualization took 0.10411477088928223s
In [9]:
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))
creating simplex grid with 1048576 elements took 0.11692428588867188s
interpolation took 3.60695481300354s
visualization took 10.50976037979126s
In [10]:
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))
creating cubic grid with 4 elements took 0.00307464599609375s
interpolation took 0.002695322036743164s
visualization took 0.10469818115234375s
In [11]:
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))
creating cubic grid with 1048576 elements took 0.15709519386291504s
interpolation took 4.54743766784668s
visualization took 19.82135844230652s

Aufgabe 3: Finite Volumen Interpoaltion

Für allgemeine (unstetige Funktionen) f:ΩR bietet sich außerdem die Approximation (und Visualisierung) als Finite Volumen Funktion an, die wir schon zur Visualisierung des Gitters genutzt haben. Sei Th ein Gitter mit I:=|Th|N Elementen (Entitäten der Kodimension 0). Eine Basis des Finite Volumen Raumes V0h ist durch

{χK0,,χKI1}

gegeben, wobei

χKi:={1,für xKi0,sonst

die Indikatorfunktion bezüglich des Gitterelementes KiTh sei, für 0i<I. Da jede Funktion vhV0h stückweise konstant bezüglich des Gitters Th ist (vergleiche die Definition von V0h oben), ist Sie eindeutig durch die Angabe eines Wertes vh_iR auf jedem Gitterelement KiTh gegeben, was aus der Basisdarstellung

vh(x):=I1i=0vh_iχKi(x)

und der Definition der Basis und V0h folgt.

Um für eine beliebige Funktion fL2(Ω) eine Approximation in V0h zu erhalten, betrachten wir die Finite Volumen-Iterpolation

ΠV0h:L2(Ω)V0h,fΠV0h[f],

die eindeutig definiert ist durch die Angabe ihrer Werte auf den Gitterelementen als Mittelwert der Funktion f,

ΠV0h[f]|Ki:=|Ki|1Kif(x)dx für 0i<I.

Als Approximation von f erhalten wir also fhV0h als fh:=ΠV0h[f] und den dazugehörigen DoF Vektor fh_RI durch

fh_i:=|Ki|1Kif(x)dx für 0i<I.

Bemerkung: Die Finite Volumen-Interpolation ist eine Projektion (genauer gesagt eine L2-Projektion), denn (ΠV0hΠV0h)[vh]=ΠV0h[vh] für alle vhV0h.

  1. Schreiben Sie eine Python Funktion interpolate_fv(f, grid), die für eine gegebene Funktion f= f und Gitter Th= grid die Finite Volumen-Interpolierte fh:=ΠV0h[f] als geeignetes VectorArray zurück gibt.

    • Definieren Sie V0h als NumpyVectorSpace entsprechender Dimension, mit der Kennung FV und legen Sie ein entsprechendes Objekt fv_space an.
    • Approximieren Sie den Mittelwert der Funktion auf einem Gitterelement KTh durch die Auswertung der Funktion im Baryzentrum von K, um fh_ zu erhalten.
    • Erstellen Sie fhV0h, indem Sie die entsprechende Funktion des fv_space nutzen.
In [12]:
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)
  1. Testen Sie Ihre interpolate_fv Funktion, indem Sie die Funktion
f:ΩR,xf(x):={1,für x[14,12]20,sonst

auf Ω=[0,1]2 interpolieren.

  • Erstellen Sie f als ExpressionFunktion.
  • Rufen Sie interpolate_fv mit dieser Funktion und dem groben Dreiecksgitter auf und geben Sie die Länge und Dimension des resultierenden VectorArrays, sowie die Anzahl der Elements des Gitters aus.
In [13]:
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))
1
16
16
  1. 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:

    • ein grobes Dreiecksgitter mit 22 Intervallen
    • ein grobes Dreiecksgitter mit 32 Intervallen
    • ein feineres Dreiecksgitter mit 42 Intervallen
    • ein grobes Vierecksgitter mit 22 Intervallen
    • ein grobes Vierecksgitter mit 32 Intervallen
    • ein feines Vierecksgitter (42 Intervallen)
In [14]:
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)))
In [15]:
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)))
In [16]:
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)))
In [17]:
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)))
In [18]:
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)))
In [19]:
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)))