Notebooks · pde/poisson_2d
pde/ poisson_2d.viv
pde/poisson_2d.viv · 1 code cellOpen in the workbenchDownload .vivLook inside this program
- Atlas
The program’s map: every function and which calls which.
There’s no recorded run of this program yet.
Shift+Enter in a cell runs it and moves to the next. Running a cell runs every cell above it too, as one program.
[1]lines 24–118
pi = 3.141592653589793 #| Apply -h^2 Laplacian to a padded (n+2) x (n+2) grid; the boundary ring stays zero. def apply_laplacian(u, mask) { lap = 4.0 * u - roll(u, 1.0, 0.0) - roll(u, -1.0, 0.0) - roll(u, 1.0, 1.0) - roll(u, -1.0, 1.0) return lap * mask } #| 1 on interior points, 0 on the boundary ring. def interior_mask(n) { m = zeros(n + 2.0, n + 2.0) for i to n { for j to n { m[i + 1.0, j + 1.0] = 1.0 } } return m } #| The manufactured solution sampled on the padded grid. def exact_solution(n) { h = 1.0 / (n + 1.0) x = reshape(arange(n + 2.0) * h, -1, 1) # a column: x runs down the rows y = arange(n + 2.0) * h # a row: y runs across return sin(pi * x) * sin(2.0 * pi * y) + x * (1.0 - x) * y * (1.0 - y) } #| Its right-hand side, f = -Laplacian(u), worked out by hand. def right_hand_side(n) { h = 1.0 / (n + 1.0) x = reshape(arange(n + 2.0) * h, -1, 1) y = arange(n + 2.0) * h return 5.0 * pi * pi * sin(pi * x) * sin(2.0 * pi * y) + 2.0 * x * (1.0 - x) + 2.0 * y * (1.0 - y) } ITERS = zeros(1) #: ITERS how many CG steps the last solve took (a one-element out-parameter) #| Solve -Laplacian u = f on an n x n interior by conjugate gradients. def solve_poisson(n) { h = 1.0 / (n + 1.0) mask = interior_mask(n) b = (h * h) * right_hand_side(n) * mask u = zeros(n + 2.0, n + 2.0) r = b p = r rr = sum(r * r) target = 1.0e-20 * rr k = 0.0 while rr > target and k < 2000.0 { ap = apply_laplacian(p, mask) alpha = rr / sum(p * ap) u = u + alpha * p r = r - alpha * ap rr_new = sum(r * r) p = r + (rr_new / rr) * p rr = rr_new k = k + 1.0 } ITERS[0] = k return u } #| L2 error of the solve on an n x n grid. def poisson_error(n) { h = 1.0 / (n + 1.0) d = solve_poisson(n) - exact_solution(n) return sqrt(sum(d * d)) * h } e1 = poisson_error(16.0) e2 = poisson_error(33.0) iters = ITERS[0] order = log(e1 / e2) / log(2.0) show e1 show e2 show order show iters # u[i, j] is the solution at x = i h, y = j h: transposed, x runs across and y up figure("The solution of Poisson's equation", "x", "y") origin("lower") plot2d(transpose(solve_poisson(40.0)), "u") test "the solve matches the manufactured solution" { assert_lt(e2, 2.0e-3) } test "the five-point Laplacian is second order" { assert_near(order, 2.0, 0.1) } test "conjugate gradients needs far fewer steps than there are unknowns (33 x 33 = 1089)" { assert_lt(iters, 120.0) } # SPDX-License-Identifier: MIT OR Apache-2.0 # SPDX-FileCopyrightText: 2026 The Viv authors