Getting started#
A Poisson problem, end to end, in fifteen lines. Six steps take a continuous problem to a solved grid function, and every tutorial afterwards is one of them in detail.
The problem#
with the manufactured solution \(u_{\text{exact}}(x, y) = \sin(\pi x)\sin(\pi y)\), so \(g = 2\pi^2 u_{\text{exact}}\). The known solution makes the error computable.
The code#
using Bramble
uexact(x) = sinpi(x[1]) * sinpi(x[2])
g(x) = 2π^2 * uexact(x)
Ω = domain(interval(0.0, 1.0) × interval(0.0, 1.0)) # geometry plus boundary labels
Ωₕ = mesh(Ω, (33, 33), (true, true)) # 33 × 33 points, uniformly spaced
Wₕ = gridspace(Ωₕ) # one unknown per point
gₕ = element(Wₕ)
Rₕ!(gₕ, g) # the source, sampled at the points
a = form(Wₕ, Wₕ, (u, v) -> inner₊(∇ₕ(u), ∇ₕ(v))) # the discrete Laplacian
l = form(Wₕ, v -> innerₕ(gₕ, v)) # the load
bcs = dirichlet_constraints(Ω, :boundary => uexact)
A, F = assemble(a, l; dirichlet = bcs)
uₕ = element(Wₕ)
uₕ .= A \ F
normₕ(uₕ .- Rₕ(Wₕ, uexact))
0.0004017888396870854
Four parts in ten thousand.
The same problem on a random 17 × 17 grid, coloured by the solution. The grid in the code is uniform, which keeps the example short, but a uniform grid can flatter a scheme: the linear Poisson example checks convergence on a random non-uniform grid for that reason. Halving the spacing should cut the error by four, since the scheme is second order:
function poisson_error(n)
Ωₕ = mesh(Ω, (n, n), (true, true))
Wₕ = gridspace(Ωₕ)
gₕ = element(Wₕ)
Rₕ!(gₕ, g)
A, F = assemble(
form(Wₕ, Wₕ, (u, v) -> inner₊(∇ₕ(u), ∇ₕ(v))),
form(Wₕ, v -> innerₕ(gₕ, v));
dirichlet = dirichlet_constraints(Ω, :boundary => uexact))
uₕ = element(Wₕ)
uₕ .= A \ F
return normₕ(uₕ .- Rₕ(Wₕ, uexact))
end
e₁, e₂ = poisson_error(33), poisson_error(65)
e₁, e₂, log2(e₁ / e₂)
(0.0004017888396870854, 0.00010041090485689163, 2.0005215336974485)
Forms and assembly#
a and l are expressions in the trial and test functions. form stores the expression and assemble walks the mesh, so the same a can be refilled every step of a time loop, handed to a nonlinear solver, or differentiated.
Coding with Claude#
The repository includes a Claude Code plugin with the API by task, the rules that keep Bramble code correct (exported versus public names, seeding random meshes, literal integer coefficients in forms) and worked examples whose results are checked. Install it from Claude Code with /plugin marketplace add gpena/Bramble.jl, then /plugin install bramble@bramble.
Reading order#
Read the tutorials in the order of the sidebar, top to bottom. The worked examples then run the whole chain on real problems.
Geometry tutorial: Describe where a problem lives: a set, its named boundary pieces and the domain that bundles both.
Mesh tutorial: Turn a domain into a mesh, make its points non-uniform, and read the spacings and cell measures.
Grid spaces and discrete functions: Put a function on a mesh, measure it, and stack copies into a composite space.
Difference, jump and average operators: Apply differences, jumps and averages, see what each does at the grid edge, and get the matrix.
Linear and bilinear forms: Write, assemble and solve a linear or bilinear form, with Dirichlet conditions, for a Poisson problem.
Coupled systems: Write a form over several unknowns, constrain one block, and read values across different meshes.
Automatic differentiation: Differentiate a discrete quantity with respect to a parameter, and choose between the two backends.