Research Notes
FEMFEniCSHeat TransferPDE

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.

April 10, 2025·4 min read

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 T(x)T(\mathbf{x}) in a domain Ω\Omega that satisfies:

(k(x)T)=0in Ω\nabla \cdot (k(\mathbf{x})\,\nabla T) = 0 \quad \text{in } \Omega

with boundary conditions:

T=TDon ΓD(Dirichlet)T = T_D \quad \text{on } \Gamma_D \qquad (\text{Dirichlet}) kTn=qnon ΓN(Neumann)k\,\frac{\partial T}{\partial n} = q_n \quad \text{on } \Gamma_N \qquad (\text{Neumann}) kTn=h(TT)on ΓR(Robin)k\,\frac{\partial T}{\partial n} = h(T_\infty - T) \quad \text{on } \Gamma_R \qquad (\text{Robin})

The conductivity k(x)k(\mathbf{x}) 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 vV0v \in V_0 (which vanishes on ΓD\Gamma_D) and integrate over the domain:

Ω(kT)vdΩ=0\int_\Omega \nabla \cdot (k\,\nabla T)\,v\,d\Omega = 0

Applying the divergence theorem (Green's first identity):

ΩkTvdΩ=ΩkTnvdΓ\int_\Omega k\,\nabla T \cdot \nabla v\,d\Omega = \int_{\partial\Omega} k\,\frac{\partial T}{\partial n}\,v\,d\Gamma

Splitting the boundary integral and substituting the Neumann and Robin conditions:

ΩkTvdΩ=ΓNqnvdΓ+ΓRh(TT)vdΓ\int_\Omega k\,\nabla T \cdot \nabla v\,d\Omega = \int_{\Gamma_N} q_n\,v\,d\Gamma + \int_{\Gamma_R} h(T_\infty - T)\,v\,d\Gamma

This is the weak form: find TVT \in V such that for all vV0v \in V_0:

a(T,v)=L(v)a(T, v) = L(v)

where:

a(T,v)=ΩkTvdΩ+ΓRhTvdΓa(T,v) = \int_\Omega k\,\nabla T \cdot \nabla v\,d\Omega + \int_{\Gamma_R} h\,T\,v\,d\Gamma L(v)=ΓNqnvdΓ+ΓRhTvdΓL(v) = \int_{\Gamma_N} q_n\,v\,d\Gamma + \int_{\Gamma_R} h\,T_\infty\,v\,d\Gamma

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 L2L^2 error against an analytic solution revealed the convergence rate matched the theoretical O(h2)\mathcal{O}(h^2) 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.