Multigrid Benchmarks

In multigrid methods, there are tradeoffs between the levels of coarsening and the number of cycles performed. This docs page provides a simple demonstrations of the relative error of different methods, and the time of execution for these methods. This can help you choose the parameters to be used by your multigrid solves.

This code demonstrates a simple pipeline of creating a mesh, executing a multigrid solve, and plotting statistics about those executions.

using CombinatorialSpaces
using CairoMakie
using GeometryBasics: Point3d
using LinearAlgebra: norm
const Point3D = Point3{Float64}

s = triangulated_grid(1, 1, 1/4, sqrt(3)/2*1/4, Point3D, false)

function plot_residuals(s, title; cycles=1:50, timeit=false)
  i = 0
  function residuals(s, scheme::AbstractSubdivisionScheme, level, sizes)
    series = PrimalGeometricMapSeries(s, scheme, level);
    md = MGData(series, sd -> ∇²(0, sd), 3, scheme)
    sd = finest_mesh(series)
    push!(sizes, nv(sd))
    L = first(md.operators)
    # Observe: putting into range of the Laplacian for solvability.
    b = L*rand(nv(sd))
    u0 = zeros(nv(sd))
    ress = map(cycles) do cyc
      if timeit
        i += 1
        @elapsed u = multigrid_vcycles(u0,b,md,cyc)
      else
        u = multigrid_vcycles(u0,b,md,cyc)
        norm(L*u-b)/norm(b)
      end
    end
  end
  bin_sizes = []
  bin_ress = map(lev -> residuals(s, BinarySubdivision(), lev, bin_sizes), 2:4)
  cub_sizes = []
  cub_ress = map(lev -> residuals(s, CubicSubdivision(), lev, cub_sizes), 2:4)
  f = Figure()
  ax = CairoMakie.Axis(f[1,1];
    title = "Multigrid V-cycles, $title",
    yscale = timeit ? identity : log10,
    ylabel = timeit ? "execution time [s]" : "log₁₀(relative error)",
    xlabel = "# cycles")
  colors = [:blue, :green, :orange]
  for lev in 1:3
    lines!(ax, bin_ress[lev],
      label="binary, $(lev+1) levels, $(bin_sizes[lev]) vertices",
      linestyle=:solid, color=colors[lev])
    lines!(ax, cub_ress[lev],
      label="cubic, $(lev+1) levels, $(cub_sizes[lev]) vertices",
      linestyle=:dash, color=colors[lev])
  end
  f[1,2] = Legend(f,ax,"Scheme")
  f
end
plot_residuals (generic function with 1 method)
plot_residuals(s, "Residuals")
Example block output

We see that the log of the observed relative error is roughly in-line with the resolution of the base mesh used.

plot_residuals(s, "Times", timeit=true)
Example block output

The spikes in these times are due to the fact that we are only executing the cycles a single time, for runtimes' sake. We observe that the timings are linear in the number of cycles. We observe that the slopes are greater for those meshes with more vertices, with the cubic-4-level method being the finest.