From PDE to FEniCS: Solving Heat Conduction with Finite Elements
A step-by-step walkthrough of how the 2-D steady-state heat equation is transformed from its strong form into a finite element discretisation and solved in FEniCS — the core of my M.E. thesis.
The starting point of my M.E. thesis was a deceptively simple question: can you solve a PDE for heat conduction in a material with spatially varying conductivity through a web browser? The answer turned out to be yes — but getting there required understanding the finite element method from the ground up.
The Strong Form
We want to find the temperature field in a domain that satisfies:
with boundary conditions:
The conductivity can vary spatially — this is what makes the problem interesting and practically relevant (composite materials, non-uniform heating).
Deriving the Weak Form
The finite element method does not solve the PDE directly in this strong form. Instead, we multiply both sides by a test function (which vanishes on ) and integrate over the domain:
Applying the divergence theorem (Green's first identity):
Splitting the boundary integral and substituting the Neumann and Robin conditions:
This is the weak form: find such that for all :
where:
Implementing in FEniCS
The elegance of FEniCS is that the weak form maps almost directly to Python code. Here is a simplified version:
from fenics import *
mesh = UnitSquareMesh(32, 32)
V = FunctionSpace(mesh, 'P', 1)
# Boundary conditions
bc = DirichletBC(V, Constant(0.0), left_boundary)
# Spatially varying conductivity
k = Expression('1 + x[0]*x[0]', degree=2)
T = TrialFunction(V)
v = TestFunction(V)
a = k * dot(grad(T), grad(v)) * dx
L = Constant(0.0) * v * dx
T_sol = Function(V)
solve(a == L, T_sol, bc)
The dx integration measure automatically handles Gaussian quadrature over the mesh elements. The ds measure handles boundary integrals for the Neumann and Robin terms.
Key Insights from the Implementation
Mesh refinement matters. At first I used a coarse 8×8 mesh — the solution looked smooth but was wrong near the boundaries. Refining to 32×32 and checking the error against an analytic solution revealed the convergence rate matched the theoretical for linear elements.
Weak BCs vs strong BCs. Neumann and Robin conditions enter naturally through the boundary integral in the weak form — you do not apply them explicitly. Only Dirichlet conditions are imposed strongly (the DirichletBC object modifies the stiffness matrix rows).
Function spaces define accuracy. Using 'P', 1 (linear Lagrange elements) is sufficient for smooth conductivity fields. For sharp gradients (e.g., a crack or interface), 'P', 2 (quadratic) or adaptive refinement would be needed.
The Web Application Layer
The solver is wrapped in a Django view that accepts material parameters and boundary condition values via a JSON POST request, runs the FEniCS solve, and returns the temperature field data as a Matplotlib contour plot encoded in base64. The front-end displays it inline — no page reload required.
This architecture demonstrates a principle I think is underappreciated: numerical solvers can be first-class web services. The same approach could serve interactive CFD tools for students or remote engineering consultations.
What Comes Next
The natural extension — which I am now pursuing — is to couple this solver to a fluid flow solver, creating a conjugate heat transfer system where the temperature field in a solid depends on the convective heat flux from an adjacent fluid domain. That requires solving the Navier–Stokes equations, which is the focus of the next set of notes.