Лабораторная работа №3

ДВ Метод конечных элементов, ММФ, БГУ

преподаватель Лаврова О.А., октябрь 2017

Задача Дирихле для уравнения Пуассона

Постановка задачи

Для заданного точного решения $u(x,y)$ построить задачу Дирихле для уравнения Пуассона в единичном квадрате $[0,1] \times [0,1]$

\begin{equation*} \left\{ \begin{array}{ll} -\Delta u=f, & \quad (x,y) \in \Omega=(0,1)^2\\ u=g, & \quad (x,y) \in \partial \Omega. \end{array} \right. \end{equation*}

Решить построенную задачу средствами библиотеки FEniCS и сравнить визуально численное решение, полученное методом конечных элементов, с точным решением $u(x,y)$.

Например, полагаем $u(x,y) = x^3+y^3$. Тогда задача Дирихле примет вид

\begin{equation*} \left\{ \begin{array}{ll} -\Delta u=-6 x - 6 y, & \quad (x,y) \in \Omega=(0,1)^2\\ u(x,y)=y^3, & \quad (x,y) \in \partial \Omega \cap \{(x,y) : x=0\} \\ u(x,y)=1+y^3, & \quad (x,y) \in \partial \Omega \cap \{(x,y) : x=1\}\\ u(x,y)=x^3, & \quad (x,y) \in \partial \Omega \cap \{(x,y) : y=0\}\\ u(x,y)=x^3+1. & \quad (x,y) \in \partial \Omega \cap \{(x,y) : y=1\}\\ \end{array} \right. \end{equation*}

Реализация

Будет осуществлена поэтапно:

  1. Построение сетки
  2. Определение конечно-элементного пространства
  3. Определение граничных условий
  4. Определение вариационной задачи
  5. Решение системы линейных алгебраических уравнений
  6. Постпроцессинг (визуализация, вычисление ошибки)

Подключаем библиотеки

In [1]:
from fenics import *
import fenics
In [2]:
import matplotlib.pyplot as plt
In [3]:
%matplotlib inline
In [4]:
import numpy as np

Получение справки по функциям библиотеки fenics

In [5]:
help(fenics.plot)
Help on function plot in module dolfin.common.plotting:

plot(object, *args, **kwargs)
    Plot given object.
    
    *Arguments*
        object
            a :py:class:`Mesh <dolfin.cpp.Mesh>`, a :py:class:`MeshFunction
            <dolfin.cpp.MeshFunction>`, a :py:class:`Function
            <dolfin.functions.function.Function>`, a :py:class:`Expression`
            <dolfin.cpp.Expression>, a :py:class:`DirichletBC`
            <dolfin.cpp.DirichletBC>, a :py:class:`FiniteElement
            <ufl.FiniteElement>`, or a :py:class:`MultiMesh
            <dolfin.cpp.MultiMesh>`.
    
    *Examples of usage*
        In the simplest case, to plot only e.g. a mesh, simply use
    
        .. code-block:: python
    
            mesh = UnitSquare(4,4)
            plot(mesh)
    
        Use the ``title`` argument to specify title of the plot
    
        .. code-block:: python
    
            plot(mesh, tite="Finite element mesh")
    
        It is also possible to plot an element
    
        .. code-block:: python
    
            element = FiniteElement("BDM", tetrahedron, 3)
            plot(element)
    
        Vector valued functions can be visualized with an alternative mode
    
        .. code-block:: python
    
            plot(u, mode = "glyphs")
    
        A more advanced example
    
        .. code-block:: python
    
            plot(u,
                 wireframe = True,              # use wireframe rendering
                 interactive = False,           # do not hold plot on screen
                 scalarbar = False,             # hide the color mapping bar
                 hardcopy_prefix = "myplot",    # default plotfile name
                 scale = 2.0,                   # scale the warping/glyphs
                 title = "Fancy plot",          # set your own title
                 backend = "vtk"                # choose plotting backend
                 )

или с помощью pydoc

In [6]:
!pydoc fenics.mesh
Help on package dolfin.mesh in fenics:

NNAAMMEE
    dolfin.mesh - The mesh module of dolfin

PPAACCKKAAGGEE  CCOONNTTEENNTTSS
    ale
    boundarysubdomainfinder
    meshes
    refinement
    svgtools

FFUUNNCCTTIIOONNSS
    ccoommppuuttee__eeddggee__mmaapp(mesh0, mesh1)
        Compute map from edges of mesh0 to vertices of mesh1.
        
        *Arguments*
            mesh0
                a :py:class:`Mesh <dolfin.cpp.Mesh>`.
            mesh1
                a :py:class:`Mesh <dolfin.cpp.Mesh>`.
        
        It is assumed that both meshes have a :py:class:`MeshFunction
        <dolfin.cpp.MeshFunction>` over the vertices named
        "parent_vertex_indices" which contain a mapping from the
        local vertices to a common parent vertex numbering.
    
    ccoommppuuttee__vveerrtteexx__mmaapp(mesh0, mesh1)
        Compute map from vertices of mesh0 to vertices of mesh1.
        
        *Arguments*
            mesh0
                a :py:class:`Mesh <dolfin.cpp.Mesh>`.
            mesh1
                a :py:class:`Mesh <dolfin.cpp.Mesh>`.
        
        It is assumed that both meshes have a :py:class:`MeshFunction
        <dolfin.cpp.MeshFunction>` over the vertices named
        "parent_vertex_indices" which contain a mapping from the
        local vertices to a common parent vertex numbering.
    
    iinniitt__ppaarreenntt__eeddggee__iinnddiicceess(submesh, mesh)
        Initialize data 'parent_edge_indices' for submesh.
    
    rreeffiinnee(mesh, cell_markers=None, redistribute=True)
        Refine given mesh and return the refined mesh.
        
        *Arguments*
            mesh
                the :py:class:`Mesh <dolfin.cpp.Mesh>` to be refined.
            cell_markers
                an optional argument (a boolean :py:class:`MeshFunction
                <dolfin.cpp.MeshFunctionBool>` over cells) may be given
                to specify which cells should be refined. If no cell
                markers are given, then the mesh is refined uniformly.
            redistribute
                an optional argument (boolean) may be given to set whether
                to redistribute the mesh across processes following
                refinement. The is relevant only when the mesh is
                distributed across processes. In serial the argument has
                no impact. Default it True.
        
        *Examples of usage*
        
            .. code-block:: python
        
                mesh = refine(mesh)
        
            To only refine cells with too large error, define a boolean
            MeshFunction over the mesh and mark the cells to be refined
            as True.
        
            .. code-block:: python
        
                cell_markers = CellFunction("bool", mesh)
                cell_markers.set_all(False)
                for cell in cells(mesh):
                    # set cell_markers[cell] = True if the cell's error
                    # indicator is greater than some criterion.
                mesh = refine(mesh, cell_markers)

DDAATTAA
    ____aallll____ = ['compute_vertex_map', 'compute_edge_map', 'init_parent_edge...

FFIILLEE
    /Users/SoloveyDm/anaconda3/envs/fenicsproject/lib/python3.6/site-packages/dolfin/mesh/__init__.py


1 Этап. Построение сетки

In [7]:
mesh = UnitSquareMesh(20, 8)
In [8]:
plot(mesh)
Out[8]:
[<matplotlib.lines.Line2D at 0x111482518>,
 <matplotlib.lines.Line2D at 0x1118208d0>]

2 Этап. Определение конечно-элементного пространства

In [9]:
V1 = FunctionSpace(mesh, 'P', 1) # линейные элементы на треугольниках
In [10]:
V2 = FunctionSpace(mesh, 'P', 2) # квадратичные элементы на треугольниках
In [11]:
V3 = FunctionSpace(mesh, 'P', 3) # кубические элементы на треугольниках

Важно: In FEniCS we DO NOT specify the boundary conditions as part of the function space, so it is sufficient to work with one common space $V$ for both the trial and test functions in the program [From FEniCS Tutorial 2016]

In [12]:
u1 = TrialFunction(V1)
v1 = TestFunction(V1)
In [13]:
u2 = TrialFunction(V2)
v2 = TestFunction(V2)
2.1. Определение точного решения исходной задачи

Точное решение будет использовано на этапе постпроцессинга

In [14]:
u_exact = Expression('pow(x[0],3) + pow(x[1],3)', degree=2)
--- Instant: compiling ---

Для визуализации необходимо описание функции (тип Expression) преобразовать в функцию

In [15]:
u_exact_plt = interpolate(u_exact,V3) 

Важно: if an Expression is used to represent an exact solution which is used to evaluate the accuracy of a computed solution, a higher degree than for the space $V$ must be used for the expression (one or two degree higher) [From FEniCS Tutorial 2016]

3 Этап. Определение граничных условий

3.1. Описание границы
In [16]:
tol = 1E-14
def boundary(x):
    return abs(x[0]) < tol or abs(x[1]) < tol \
    or abs(x[0] - 1) < tol or abs(x[1] - 1) < tol
3.2. Задание функции из условия Дирихле
In [17]:
g_D1 = Expression('pow(x[0],3) + pow(x[1],3)', degree=1)
In [18]:
g_D2 = Expression('pow(x[0],3) + pow(x[1],3)', degree=2)

Важно: To obtain optimal (order of) accuracy in computations, it is usually a good choice to use the same degree as for the space $V$ that is used for the trial and test functions [From FEniCS Tutorial 2016]

3.3. Граничное условие
In [19]:
bc1 = DirichletBC(V1, g_D1, boundary)
In [20]:
bc2 = DirichletBC(V2, g_D2, boundary)

4 Этап. Определение вариационной задачи

Определение функции правой части

In [21]:
f = Expression('-6*x[0]-6*x[1]', degree=1)
--- Instant: compiling ---
In [22]:
a1 = dot(grad(u1), grad(v1))*dx # линейные элементы
L1 = f*v1*dx
In [23]:
a2 = dot(grad(u2), grad(v2))*dx # квадратичные элементы
L2 = f*v2*dx

Важно: The language used to express weak forms is called UFL (Unified Form Language) and is an integral part of FEniCS [From FEniCS Tutorial 2016]

5 Этап. Решение системы линейных алгебраических уравнений

In [24]:
u1 = Function(V1) # переопределение функции u1
solve(a1 == L1, u1, bc1)
In [25]:
u2 = Function(V2) # переопределение функции u2
solve(a2 == L2, u2, bc2)

6 Этап. Постпроцессинг

6.1. Визуализация
In [33]:
plot(u1, interactive = True)
#colorbar()
Out[33]:
<matplotlib.tri.tricontour.TriContourSet at 0x113a30940>
In [35]:
plot(u1-u_exact_plt)
Calling FFC just-in-time (JIT) compiler, this may take some time.
Calling FFC just-in-time (JIT) compiler, this may take some time.
Out[35]:
<matplotlib.tri.tricontour.TriContourSet at 0x113fcfc88>
In [34]:
interactive()

Важно: The call to interactive() is usually placed at the end of a program that creates plot [From FEniCS Tutorial 2016]

Важно: The built-in plotting in FEniCS may not work as expected when running inside a FEniCS Docker container. For Docker users who need plotting, it is recommended to work within a Jupyter/FEniCS notebook (command fenicsproject notebook) [From FEniCS Tutorial 2016]

In [37]:
#from IPython.display import HTML
#HTML(X3DOM().html(u1))
6.2. Вычисление ошибки
1) Вычисление ошибки в норме $L_2$
In [29]:
error_L2_1 = errornorm(u_exact,u1,'L2')
error_L2_2 = errornorm(u_exact,u2,'L2')
In [30]:
print(error_L2_1,error_L2_2)
0.005461746684973916 6.752708685467642e-05

Замечание: Обратите внимание на уменьшение ошибки за счет перехода из пространства $P1$-элементов в пространство $P2$-элементов на одинаковой сетке.

2) Вычисление ошибки в максимальной норме

Данный код реализует вычисление ошибки в узлах сетки (не в степенях свободы конечно-элементого решения)

In [31]:
vertex_values_u_exact = u_exact.compute_vertex_values(mesh)
vertex_values_u1 = u1.compute_vertex_values(mesh)
vertex_values_u2 = u2.compute_vertex_values(mesh)
error_max_1 = np.max(np.abs(vertex_values_u_exact - vertex_values_u1))
error_max_2 = np.max(np.abs(vertex_values_u_exact - vertex_values_u2))
In [32]:
print(error_max_1,error_max_2)
2.10942374679e-15 9.88098491916e-15

Важно: We have here used the maximum and absolute value functions from numpy, because these are much more efficient for large arrays (a factor of 30) than Python's built-in max and abs functions [From FEniCS Tutorial 2016]

In [ ]: