# The finite element method

This is an edited section from my thesis “Solving Poisson’s equation with Dataflow computing”. It might be a bit more formal than most of my other posts.

## Introduction

The finite element method is a method to solve boundary value problems. Boundary value problems pose the problem of finding a function $u:Ω→R$ that satisfies a given differential equation on $Ω$ and given boundary conditions on the boundary $∂Ω$ of the domain. We will use Poisson’s problem on a two-dimensional domain with Dirichlet boundary conditions:

For a given $f∈L_{2}(Ω)$, $u_{∂Ω}:∂Ω→R$, and $Ω$, find a function $u:Ω→R$ such that

- $Δu=f$ on $Ω$
- $u=u_{∂Ω}$ on $∂Ω$

The $Δ$ represents a differential operator called the Laplace operator. For twice-differentiable functions $g:A→R$ on a domain $A⊂R_{d}$, the Laplace operator is defined as

$Δg=i=1∑d ∂x_{i}∂_{2}g $The finite element method is a numerical method. This means that the solution $u$ is approximated by a function $u_{h}≈u$. The function $u_{h}$ is taken to be in some finite-dimensional *approximation space* $V_{h}$. The space $V_{h}$ is spanned by a basis $ψ_{0},ψ_{1},...,ψ_{N−1}:Ω→R$. When the basis functions $ψ_{0}$, …, $ψ_{N−1}$ are fixed, $u_{h}$ can be expressed by a vector $u=(u_{0},u_{1},...u_{N−1})_{⊤}∈R_{N}$:

The elements $u_{0},u_{1},...,u_{N−1}∈R$ are known as *degrees of freedom*.

Conceptually, the finite element method can be separated into several stages:

- A method to convert the boundary value problem to a discrete system of symbolic equations. The most popular method is the Bubnov-Galerkin method, which is often simply called the
*Galerkin method*. - The discretization strategy, which describes how to obtain an actual system of equations for a given symbolic system of equations. This involves picking the basis functions, and evaluating or approximating the integrals in the symbolic system. Also, one or more methods of refinement are usually provided, which can be used to increase the number of basis functions and thus the dimension of $V_{h}$, so that $u_{h}∈V_{h}$ can approximate $u$ better.
- The solver, which solves the system of equations. If the boundary value problem is linear, the system of equations will be linear too, and linear solvers like the conjugate gradient method, the generalized minimum-residual method, or the stabilized biconjugate gradient method are popular choices. Nonlinear systems are harder to solve, and require more advanced methods like the Newton-Rhapson method or the Picard method. Typical nonlinear solvers iteratively linearize the system, and solve the linearized system with a linear solver.

## Galerkin method

The Galerkin method derives a system of symbolic equations for a given boundary value problem. The Galerkin method is based on the weak formulation of the boundary value problem. First, let us define the Sobolev space $H_{1}(Ω)$ of functions $f:Ω→R$ for which the first partial derivatives are square-integrable. The weak formulation of a differential equation can be obtained by multiplying both sides by a test function $v∈V={v∈H_{1}(Ω):v∣_{∂Ω}=0}$, integrating over $Ω$, and demanding equality regardless of the choice of $v∈V$. Applying this to $αΔu+βu=f$ gives

$∀v∈V:∫_{Ω}α(Δu)v+βuvdΩ=∫_{Ω}fvdΩ$One can then apply integration by parts and use $v∣_{∂Ω}=0$ to make the left-hand side symmetric:

$∀v∈V:∫_{Ω}−α(∇u⋅∇v)+βuvdΩ=∫_{Ω}fvdΩ$With this equation, the complete weak formulation becomes:

For a given $f:Ω→R$, $u_{0}:∂Ω→R$, and $Ω$, find a function $u∈H_{1}(Ω)$ such that:

$∀v∈V:∫_{Ω}−α(∇u⋅∇v)+βuvdΩ=∫_{Ω}fvdΩ$ $u=u_{0}on∂Ω$The Galerkin method replaces $V$ by a finite-dimensional approximation $V_{h}$, spanned by basis functions ${ψ_{0},ψ_{1},...,ψ_{N−1}}$, and seeks an approximate solution $u_{h}∈V_{h}$. Ignoring the boundary conditions for now, we get the system

$∀i=0,1,...,N−1:∫_{Ω}−α(∇u_{h}⋅∇ψ_{i})+βu_{h}ψ_{i}dΩ=∫_{Ω}fψ_{i}dΩ$So $u_{h}∈V_{h}$ can be expressed as $u_{h}=∑_{j=0}u_{j}ψ_{j}$. Using this, we get

$∀i=0,1,...,N−1:∫_{Ω}−α(∇(j=0∑N−1 u_{j}ψ_{j})⋅∇ψ_{i})+β(∇(j=0∑N−1 u_{j}ψ_{j})ψ_{i}dΩ=∫_{Ω}fψ_{i}dΩ$Bringing the summation outside the integral yields

$∀i=0,1,...,N−1:j=0∑N−1 (∫_{Ω}−α(∇ψ_{i}⋅∇ψ_{j})+βψ_{i}ψ_{j}dΩ)u_{j}=∫_{Ω}fψ_{i}dΩ$Which can be written as a linear system $Au=b$ of size $N×N$, where the *finite element matrix* $A∈R_{N×N}$ is defined as

and the *right-hand side vector* $b∈R_{N}$ is defined as

Furthermore, we define the *stiffness matrix* $S$ as

and the *mass matrix* $M$ as

We can now express the finite element matrix $A$ in terms of the stiffness matrix $S$ and mass matrix $M$ as

$A=−αS+βM$The Dirichlet boundary conditions can be implemented by a *Dirichlet lift*. Each degree of freedom $u_{i_{0}}$, $u_{i_{1}}$, …, $u_{i_{m−1}}$ which corresponds to a basis function which is nonzero on the boundary $∂Ω$ is prescribed in such a way that $∑_{k=0}u_{i_{k}}ψ_{i_{k}}≈u_{∂Ω}$ on $∂Ω$. As such, the boundary condition is approximately satisfied. To pick $u_{0},u_{1},...,u_{m−1}$ in such a way, one can use $L_{2}$ projection on the boundary, or make sure that $u_{h}$ interpolates $u_{∂Ω}$ in a set of points $x_{0}$, $x_{1}$, … on the boundary.

## Assembly

Suppose that the mass matrix $M$ is to be assembled. One could separately evaluate the integrals $∫_{Ω}ψ_{i}ψ_{j}dΩ$. However, the basis functions are usually defined on sections of the domain called *elements*. The domain can be partitioned into $M$ elements $e_{0},e_{1},...,e_{M−1}$ so that we have

The usual approach is to loop over the elements and evaluate $∫_{e_{k}}ψ_{i}ψ_{j}dΩ$ for the basis functions $ψ_{i},ψ_{j}$ which are nonzero on that element. Typically, the basis functions will have local support, and there will be a small number of nonzero basis functions on each element. For an element on which there are $n$ nonzero basis functions $ψ_{k_{0}},ψ_{k_{1}},...,ψ_{k_{n−1}}$, an *element matrix* $E∈R_{n×n}$ with entries $E_{i,j}=∫ψ_{k_{i}}ψ_{k_{j}}dΩ$ is assembled. After the element matrix is assembled, each entry $E_{i,j}$ is added to $M_{k_{i},k_{j}}$.

Pseudocode for the assembly is as follows. Note that there are many details omitted (for example, how to evaluate the integral in the assembly of the element matrix).

```
let ψ[0], ψ[1], ..., ψ[N − 1] be the basis functions
let e[0], e[1], ..., e[M − 1] be the elements
let glob_mat be an N × N matrix with each element equal to zero
for k = 0 .. M − 1
let n be the number of basis functions with support on e[k]
let s[k, 0], s[k, 1], ..., s[k, n − 1] be the indices of the basis
functions with support on e[k]
let elem_mat be an n × n matrix with each element equal to zero
// assemble element matrix elem_mat
for i = 0 .. n − 1
for j = 0 .. n − 1
elem_mat[i, j] += integral of ψ[s[k, i]] ψ[s[k, j]] over e[k]
end for
end for
// scatter element matrix elem_mat to global matrix glob_mat
for i = 0 .. n − 1
for j = 0 .. n − 1
glob_mat[s[k, i], s[k, j]] += elem_mat[i, j]
end for
end for
end for
```