conda install -c conda-forge fenics
conda install -c conda-forge mshr
conda create -n fenics_env python=3.6
source activate fenics_env
conda install -c conda-forge fenics
conda install notebook
conda install -c conda-forge matplotlib
conda install -c conda-forge mshr
jupyter notebook
Finite elements methods starts from variational form of PDE. Consider Poisson equation with Dirichlet boundary conditions $$ \begin{align} -\Delta u &= f(x), \quad x \in \Omega\\ u(x) &= u_D(x), \quad x \in \partial \Omega \end{align} $$
To get its variational form one needs:
Therefore, variational form of Poisson equation with Dirichlet boundary conditions is $$ \int_{\Omega} \nabla u \cdot \nabla v dx = \int_{\Omega} f \cdot v dx $$ Now let's see how these form is converted to FEniCS code...
In [1]:
%matplotlib notebook
from __future__ import print_function
import fenics
import matplotlib.pyplot as plt
In [2]:
mesh = fenics.UnitSquareMesh(8, 8)
_ = fenics.plot(mesh)
In [3]:
import dolfin
import mshr
import math
domain_vertices = [dolfin.Point(0.0, 0.0),
dolfin.Point(10.0, 0.0),
dolfin.Point(10.0, 2.0),
dolfin.Point(8.0, 2.0),
dolfin.Point(7.5, 1.0),
dolfin.Point(2.5, 1.0),
dolfin.Point(2.0, 4.0),
dolfin.Point(0.0, 4.0),
dolfin.Point(0.0, 0.0)]
p = mshr.Polygon(domain_vertices);
fenics.plot(mshr.generate_mesh(p, 20))
Out[3]:
In [5]:
R = 1.1
r = 0.9
x, y, z = 1, -1, 1
# Create geometry
s1 = mshr.Sphere(dolfin.Point(0, 0, 0), 1)
s2 = mshr.Sphere(dolfin.Point(x, y, z), r)
b1 = mshr.Box(dolfin.Point(-2, -2, -0.03), dolfin.Point(2, 2, 0.03))
geometry = s1 - b1 - s2
death_star_mesh = mshr.generate_mesh(geometry, 10)
# Plot mesh
fenics.plot(death_star_mesh, color="grey")
plt.xlabel("x")
plt.ylabel("y")
Out[5]:
In [6]:
V = fenics.FunctionSpace(mesh, 'P', 1)
In [7]:
u_D = fenics.Expression('1 + x[0]*x[0] + 2*x[1]*x[1]', degree=2)
def boundary(x, on_boundary):
return on_boundary
bc = fenics.DirichletBC(V, u_D, boundary)
Expression
object that is directly translated into efficient C++ codeExpression
some constant appeares, they should be clearly specifiedf = Expression("exp(-kappa*pow(pi, 2)*t)*sin(pi*k*x[0])", degree=2, kappa=1.0, t=0, k=4)
boundary
checks whether requested point x
lies on the Dirichlet boundary or not. Argument on_boundary
is True
if requested point lies on the physical boundary of the mesh and False
otherwise
In [8]:
u = fenics.TrialFunction(V)
v = fenics.TestFunction(V)
f = fenics.Constant(-6.0) # Or f = Expression(’-6’, degree=0)
# Left-hand side
a = fenics.dot(fenics.grad(u), fenics.grad(v))*fenics.dx
# Right-hand side
L = f*v*fenics.dx
dot
is used for vectors and inner
is used for matrices
In [9]:
u = fenics.Function(V)
fenics.solve(a == L, u, bc)
u
from TrialFunction
to solution Function
==
, unknown solution and bondary conditions
In [10]:
# Plot solution and mesh
fenics.plot(u)
Out[10]:
In [11]:
error_L2 = fenics.errornorm(u_D, u, 'L2')
print("Error in L2 norm = {}".format(error_L2))
error_H1 = fenics.errornorm(u_D, u, 'H1')
print("Error in H1 norm = {}".format(error_H1))
Maximum of the difference between boundary function and solution on mesh vertices: $$ \max_{x_i \in M} |u(x_i) - u_D(x_i)| $$
In [12]:
vertex_values_u_D = u_D.compute_vertex_values(mesh)
vertex_values_u = u.compute_vertex_values(mesh)
import numpy as np
error_max = np.max(np.abs(vertex_values_u_D - vertex_values_u))
print('Error max =', error_max)
In [13]:
plt.contourf(vertex_values_u.reshape(9, 9).T)
plt.colorbar()
Out[13]: