22. Boundary Conditions#
We discuss more general boundary conditions for the Poisson equation. The whole boundary is split into three non-overlapping parts: \(\partial \Omega = \Gamma_D \cup \Gamma_N \cup \Gamma_R\)
Dirichlet boundary conditions
\[ u(x) = u_D(x) \qquad \forall \, x \in \Gamma_D \]Dirichlet boundary conditions are used, for example, to specify the temperature at the boundary.
Neumann boundary conditions
\[ \frac{\partial u}{\partial n}(x) = g(x) \qquad \forall \, x \in \Gamma_N \]Neumann boundary conditions model the given heat flux at the boundary, for example \(g = 0\) in case of thermal insulation.
Robin boundary conditions
\[ \alpha u(x) + \frac{\partial u}{\partial n}(x) = \alpha u_0(x) \qquad \forall \, x \in \Gamma_R \]Robin boundary condition model the heat transmission at the boundary, the flux \(\partial_n u \sim (u_0-u)\), where \(u_0-u\) is the temperature difference between the domain and the environment.
In the derivation of the weak form we had
22.1. Natural boundary conditions#
Neumann and Robin boundary conditions are the simple ones, they can be included naturally into the variational form.
Assume we don’t have a Dirichlet boundary, i.e. \(\partial \Omega = \Gamma_N \cup \Gamma_R\). We replace \(\frac{\partial u}{\partial n}\) by the boundary condition:
We see that \(\int g v\) and \(\int \alpha u_0 v\) are linear terms in the test-function, and thus belong to the linear-form \(f\):
Thus, we have to adapt the bilinear-form and linear-form:
from ngsolve import *
from ngsolve.webgui import Draw
from netgen.occ import unit_square
mesh = Mesh(unit_square.GenerateMesh(maxh=0.1))
fes = H1(mesh, order=3)
gfu = GridFunction(fes)
a = BilinearForm(fes)
f = LinearForm (fes)
u = fes.TrialFunction()
v = fes.TestFunction()
alpha = 10
u0 = 293
g = 1
a += grad(u)*grad(v)*dx
a += alpha*u*v*ds("bottom")
f += g * v*ds("right")
f += alpha*u0*v*ds("bottom")
# assemble and solve
a.Assemble()
f.Assemble()
gfu.vec.data = a.mat.Inverse() * f.vec
Draw (gfu, mesh, "temperature")
Draw (grad(gfu), mesh, "gradient");
22.2. Essential boundary conditions#
Essential boundary conditions are less natural: We have to set the solution field to the given Dirichlet values, and restrict the test-functions to 0 on the Dirichlet boundary:
We split the solution vector into two parts: The given coefficients on the Dirichlet boundary, and all other including internal coefficients and coefficients on the natural boundaries:
(f like free). Accordingly, the matrix and the right hand side are split as
The test functions are reduced to the free nodes. Thus, the equations to solve are
The given \(u_D\) is moved to the right hand side, and thus
It is convenient to assemble the complete matrix, invert a part of the matrix, end extend by zero:
The case of homogeneous Dirichlet boundary condtions \(u_D = 0\) is simple: We just say
In NGSolve, the finite element space maintains a BitArray marking the free degrees of freedom. It is used to invert the sub-matrix:
# fes = H1(mesh, dirichlet="left|bottom")
# u.vec.data = a.mat.Inverse(fes.FreeDofs()) * f.vec
Non-homogeneous Dirichlet b.c. are reduced to homogeneous once as follows: Choose some function \(\widetilde u\) such that
and set \(u = \widetilde u + w\). So we have to find a correction \(w\) with \(w = 0\) on \(\Gamma_D\) and
In matrix notation, the correction \(w\) is
from netgen.occ import unit_square
mesh = Mesh(unit_square.GenerateMesh(maxh=0.1))
fes = H1(mesh, order=3, dirichlet="bottom|left|right")
gfu = GridFunction(fes)
a = BilinearForm(fes)
f = LinearForm (fes)
u = fes.TrialFunction()
v = fes.TestFunction()
uD = sin(2*pi*x)
g = 1
a += grad(u)*grad(v)*dx
# assemble and solve
a.Assemble()
f.Assemble();
Now we set the Dirichlet boundary values and don’t worry about the rest. The Set - function of a GridFunction does some kind of interpolation.
gfu.Set (uD, definedon=mesh.Boundaries("bottom"))
Draw(gfu);
Now we compute the correction:
r = f.vec - a.mat * gfu.vec
gfu.vec.data += a.mat.Inverse(fes.FreeDofs()) * r
Draw(gfu);