Week 10 — Time-Dependent Problems: Semi-discretisation · θ-Method · Stability · Space–Time Convergence · Cahn–Hilliard
Process Systems Laboratory
Department of Chemical and Biomolecular Engineering, SeoulTech
| Part | Topic | Time |
|---|---|---|
| Quiz | Week 9 (Kirchhoff, SNES reasons, folds, parameters, tolerances) | 10 min |
| A | Theory — method of lines, energy stability, semi-discrete error, the θ-method, amplification factors, space–time error | 50 min |
| B | The heat equation in DOLFINx — one matrix, many right-hand sides; Gaussian benchmark; temporal rates | 40 min |
| C | Stability in practice — explicit Euler, non-smooth data, Crank–Nicolson oscillations, Rannacher start | 30 min |
| D | Cahn–Hilliard (θ-method + Newton) · LLM pitfalls in time loops · HW9 | 30 min |
Lab notebook: W10_lab.ipynb · Homework: W10_HW9.ipynb (due before Week 11)
Weeks 1–7: elliptic, linear. Weeks 8–9: elliptic, nonlinear. Now: parabolic — the process models that evolve in time.
| Process | Model |
|---|---|
| transient conduction, quenching | \rho c_p T_t-\nabla\cdot(k\nabla T)=Q |
| diffusion in a membrane / catalyst pellet | c_t-\nabla\cdot(D\nabla c)=r(c) |
| phase separation (spinodal decomposition) | Cahn–Hilliard: c_t=\nabla\cdot(M\nabla\mu), \mu=f'(c)-\lambda\Delta c |
| start-up flow | \rho\mathbf u_t-\mu\Delta\mathbf u+\nabla p=\mathbf f (Week 14) |
Strategy: discretise space first (FEM, Weeks 1–7) → a system of ODEs → discretise time (θ-method) → at each step an elliptic problem (linear: Weeks 5–6; nonlinear: Weeks 8–9).
u_t-\kappa\Delta u=f\ \text{ in }\Omega\times(0,T],\qquad u=g\ \text{ on }\partial\Omega,\qquad u(\cdot,0)=u_0 .
Weak form: for a.e. t, find u(t)\in H^1, u=g(t) on \partial\Omega, with (u_t,v)+\kappa(\nabla u,\nabla v)=(f(t),v)\qquad\forall v\in H^1_0 .
Semi-discretisation (Galerkin in space, u_h(x,t)=\sum_jU_j(t)\phi_j(x)): M\,\dot U(t)+K\,U(t)=F(t),\qquad M_{ij}=(\phi_j,\phi_i),\ \ K_{ij}=\kappa(\nabla\phi_j,\nabla\phi_i). A stiff ODE system: the generalised eigenvalues K\varphi=\lambda M\varphi range from \lambda_{\min}=O(1) to \lambda_{\max}=O(h^{-2}).
Energy estimate (homogeneous Dirichlet data)
Take v=u: \ \tfrac12\tfrac{d}{dt}\|u\|^2+\kappa\|\nabla u\|^2=(f,u). With Poincaré, \|v\|\le C_P\|\nabla v\|: \|u(t)\|\le e^{-\kappa t/C_P^2}\|u_0\|+\int_0^t e^{-\kappa(t-s)/C_P^2}\|f(s)\|\,ds .
Ritz projection R_h:H^1_0\to V_h: (\nabla R_hu,\nabla v)=(\nabla u,\nabla v) for v\in V_h — the elliptic Galerkin solution, so \|u-R_hu\|\le Ch^{k+1}|u|_{k+1} (Weeks 5–7).
Split u-u_h=\underbrace{(u-R_hu)}_{\rho}+\underbrace{(R_hu-u_h)}_{\vartheta\in V_h}. Subtracting the equations: (\vartheta_t,v)+\kappa(\nabla\vartheta,\nabla v)=-(\rho_t,v)\qquad\forall v\in V_h, and the energy estimate for \vartheta gives \|\vartheta(t)\|\le\|\vartheta(0)\|+\int_0^t\|\rho_t\|\,ds.
Theorem (Wheeler 1973)
\|u(t)-u_h(t)\|\le\|u_h(0)-u_0\|+Ch^{k+1}\Bigl(\|u_0\|_{k+1}+\int_0^t\|u_t\|_{k+1}\,ds\Bigr).
Optimal O(h^{k+1}) in L^2 — provided u_h(0) is O(h^{k+1})-close to u_0 (interpolant or L^2/Ritz projection) and u_t is smooth.
M\frac{U^{n+1}-U^n}{\Delta t}+K\bigl(\theta U^{n+1}+(1-\theta)U^n\bigr)=\theta F^{n+1}+(1-\theta)F^n
| θ | name | order p |
|---|---|---|
| 0 | explicit (forward) Euler | 1 |
| ½ | Crank–Nicolson | 2 |
| 1 | implicit (backward) Euler | 1 |
Each step solves (M+\theta\Delta tK)\,U^{n+1}=\text{rhs}: an elliptic problem with a constant matrix (if \Delta t, θ fixed) — factorise once.
Modal analysis: in an eigenvector K\varphi=\lambda M\varphi (with F=0) one step multiplies by R(z)=\frac{1-(1-\theta)z}{1+\theta z},\qquad z=\lambda\Delta t . 
Stable iff |R(\lambda\Delta t)|\le1 for all \lambda\in[\lambda_{\min},\lambda_{\max}]:
Stability of the θ-method
| R(\infty) | stiff (high-frequency) components | |
|---|---|---|
| backward Euler | 0 | damped at once (L-stable) |
| Crank–Nicolson | −1 | not damped: flip sign every step (§C2) |
| explicit Euler | -\infty | amplified unless \Delta t\lambda_{\max}\le2 |
Measured, P1 on (-2,2)^2, right triangles: \lambda_{\max}h^2=23.8,\ 25.3,\ 25.7 (N=8,16,32); with a lumped (diagonal) mass 8.2. At h=0.125: \Delta t_{\rm crit}=2/\lambda_{\max}=1.2\times10^{-3}, while \lambda_{\min}=1.24 sets the physical time scale ≈ 0.8.
Error of the θ-method (smooth solution)
\|u(t_n)-u_h^n\|\le C\bigl(h^{k+1}+\Delta t^{\,p}\bigr),\qquad p=2\ (\theta=\tfrac12),\quad p=1\ (\text{otherwise}).
t, dt, theta = fem.Constant(msh, 0.0), fem.Constant(msh, dt_value), fem.Constant(msh, 1.0)
u_n = fem.Function(V); u_n.interpolate(u0) # previous step (initial condition)
g = fem.Function(V); g_expr = fem.Expression(u_exact(t), V.element.interpolation_points)
bc = fem.dirichletbc(g, boundary_dofs)
u, v = ufl.TrialFunction(V), ufl.TestFunction(V)
a = fem.form(u * v * ufl.dx + dt * theta * kappa * ufl.inner(ufl.grad(u), ufl.grad(v)) * ufl.dx)
L = fem.form(u_n * v * ufl.dx - dt * (1 - theta) * kappa * ufl.inner(ufl.grad(u_n), ufl.grad(v)) * ufl.dx
+ dt * (theta * f_new + (1 - theta) * f_old) * v * ufl.dx)
A = assemble_matrix(a, bcs=[bc]); A.assemble() # once
ksp = PETSc.KSP().create(comm); ksp.setOperators(A); ksp.setType("preonly"); ksp.getPC().setType("lu") # factorised once
for n in range(1, nsteps + 1): # integer counter: t_n = n * dt
t.value = n * dt_value
g.interpolate(g_expr) # boundary data at t_{n+1}
with b.localForm() as bl: bl.set(0.0)
assemble_vector(b, L); apply_lifting(b, [a], [[bc]])
b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE); set_bc(b, [bc])
ksp.solve(b, uh.x.petsc_vec); uh.x.scatter_forward()
u_n.x.array[:] = uh.x.array # copy values — do not rebind the name| Mathematics | DOLFINx 0.11 |
|---|---|
| t_n, \Delta t, θ | fem.Constant — read at every assembly; change .value, never rebuild forms |
| u^n in the right-hand side | a fem.Function u_n inside L; update with u_n.x.array[:] = uh.x.array |
| M+\theta\Delta tK | a, assembled and factorised once (re-assemble only if \Delta t, θ or coefficients change) |
| g(t_{n+1}) | g.interpolate(fem.Expression(u_exact(t), ...)) after t.value = ... |
| \theta f^{n+1}+(1-\theta)f^n | two Constants t_old, t_new in UFL; \partial_t u for MMS by ufl.diff(u_ex, ufl.variable(t)) |
| \|u(T)-u_h^N\| | assemble_scalar + allreduce with the exact solution at the same t |
Loop over an integer n and set t_n=n\Delta t: while t < T: t += dt accumulates rounding — with \Delta t=0.1 it takes 11 steps to pass T=1.
In \mathbb R^2 with \kappa=1: u(x,y,t)=\frac{1}{1+4at}\exp\Bigl(-\frac{a(x^2+y^2)}{1+4at}\Bigr)\quad\text{solves}\quad u_t=\Delta u,\quad u(\cdot,0)=e^{-a(x^2+y^2)}.

Rates, \Delta t=T/5\to T/160:
| rates | |
|---|---|
| BE, N=64 | 0.96, 0.98, 0.99, 1.00, 1.00 |
| CN, N=64 | 2.00, 1.97, 1.83, 1.10, 0.25 |
| CN, N=128 | 2.01, 2.00, 1.99, 1.95, 1.60 |
Reading a temporal study: a rate that falls as \Delta t\to0 means “another error dominates now” — check by refining h, not by trusting the last digit.
\Delta t_{\rm crit}=2/\lambda_{\max} from the dense eigenvalues (P1, N=32, h=0.125): 1.216\times10^{-3}.

\max|u_h| at t=0.1,\ 0.2,\ 0.5,\ 1:
| data, \Delta t | |
|---|---|
| Gaussian, 0.95\,\Delta t_{\rm crit} | 0.33, 0.20, 0.090, 0.043 |
| Gaussian, 1.05\,\Delta t_{\rm crit} | 0.33, 0.20, ≈ 3, ≈ 5e16 |
| box, 1.05\,\Delta t_{\rm crit} | 11, 1.9e4, 9.9e13, 1.7e30 |
Smooth data contain almost no highest mode — the instability grows from round-off and looks fine for a while (here until t\approx0.4); the exploded values even differ between machines (local 2.8 / 4.6e16, Colab 2.9 / 4.7e16): they are amplified rounding errors. A short test run does not prove stability. Implicit methods remove the limit; explicit ones need \Delta t=O(h^2) — for diffusion almost always the wrong trade.
Indicator of (-\frac12,\frac12)^2 as u_0, u=0 on \partial\Omega, P1, N=32, T=0.1; reference: Rannacher-started CN with 4096 steps.
| errors, \Delta t=T/4\to T/128 | rates | |
|---|---|---|
| BE | 3.1e-2 … 1.05e-3 | 0.95 … 1.00 |
| CN | 4.8e-2, 4.3e-3, 1.7e-4 … 2.6e-6 | 3.47, 4.68, 2.01, 2.00, 2.00 |
| CN + Rannacher (4 BE half steps first) | 6.3e-3 … 5.9e-6 | 2.06, 2.01, 2.00, 2.00, 2.00 |
Positivity. The exact solution is \ge0. Measured \min u_h over all steps: BE -7\times10^{-4} at \Delta t=T/16, -1.6\times10^{-2} at T/128; CN down to -2.7\times10^{-2}. With a consistent mass matrix even backward Euler violates the maximum principle when \Delta t is small compared with h^2 (a known lower bound); mass lumping on acute meshes restores it. Concentrations and temperatures that go negative are a discretisation artefact, not physics.
Phase separation of a binary mixture, concentration c, chemical potential \mu: c_t=\nabla\cdot(M\nabla\mu),\qquad \mu=f'(c)-\lambda\Delta c,\qquad f(c)=100\,c^2(1-c)^2 .
Mixed P1 × P1 for (c,\mu), θ = ½ on the \mu term, NonlinearProblem (Week 9) at every step:
c, mu = ufl.split(u); c0, mu0 = ufl.split(u0)
cv = ufl.variable(c); f = 100 * cv**2 * (1 - cv)**2
mu_mid = (1 - theta) * mu0 + theta * mu
F = (c - c0) * q * dx + dt * inner(grad(mu_mid), grad(q)) * dx \
+ (mu - ufl.diff(f, cv)) * v * dx - lmbda * inner(grad(c), grad(v)) * dx
for n in range(nsteps):
u0.x.array[:] = u.x.array
problem.solve(); assert problem.solver.getConvergedReason() > 064\times64, \lambda=10^{-2}, \Delta t=5\times10^{-6}, 80 steps, c_0=0.63\pm0.01:
Checks with a known answer even without an exact solution: energy decay, mass conservation, -np 1 vs -np 2.
| Kind | What it looks like | Symptom |
|---|---|---|
| Rebinding | u_n = uh at the end of the step |
the form still holds the old u_n object: every step restarts from u_0 |
Time as float |
t = 0.0 inside f or g, then t += dt |
data frozen at t=0 (Week 9’s lam bug, again) |
| Stale boundary data | g.interpolate(...) once, before the loop |
boundary values stay at t=0 — invisible to the Gaussian benchmark (<10^{-3} on \partial\Omega: error 5.2\times10^{-3} with or without the bug); test with an MMS whose boundary data change |
| Float loop | while t < T: t += dt |
one extra step (11 instead of 10 for \Delta t=0.1) |
| Data at the wrong time | f(t_n) or f(t_{n+1}) in Crank–Nicolson | first order instead of second (HW9) |
| Cost | LinearProblem(...) or fem.form(...) inside the loop |
re-assembly and re-factorisation of a constant matrix every step |
| Error at the wrong time | exact solution at t_n compared with u_h^{n+1} | an O(\Delta t) error floor |
Verification habit: print the step count and the final t; check that u_h changes from step to step; temporal and spatial rates; -np 2.
W10_HW9.ipynb (100 pts)
Rules as always: Restart session and run all, -np 2 where stated, the AI-usage log, numbers only from your own cells.
For every rate: prediction first, then measurement, then explanation.
| Concept | Key idea | DOLFINx |
|---|---|---|
| Method of lines | M\dot U+KU=F, stiff: \lambda_{\max}\sim h^{-2} | mass and stiffness forms |
| Semi-discrete error | Ritz projection: O(h^{k+1}) if u_h(0) is good | interpolate u_0 |
| θ-method | R(z)=\frac{1-(1-\theta)z}{1+\theta z}; θ ≥ ½ A-stable; BE L-stable | Constant dt, θ; factorise once |
| Explicit limit | \Delta t\le2/\lambda_{\max}=O(h^2), may hide for smooth data | — |
| Space–time error | O(h^{k+1}+\Delta t^p); data at consistent times | t_old, t_new Constants |
| Non-smooth data | CN oscillates; Rannacher start | a few BE half steps |
| Nonlinear in time | θ-method + Newton per step; energy, mass checks | NonlinearProblem in the loop |
Next week — Stokes: mixed problems, the inf-sup condition, Brezzi’s theorem, Taylor–Hood, MINI; HW10.
