Examples#

Tip

First visit? Visual gallery shows what the maintained examples compute, with figures from full-settings runs. Come back here when you need cache names, device behavior, smoke modes and the cost warnings for a specific program.

examples/ holds seven self-contained programs and three notebooks. Each one solves a whole problem — build a mesh, build or load a near-field table, run the volume FMM, report an error — rather than demonstrating a single call, so the shortest path from A first volume potential to your own driver is usually to copy the closest example and change it.

Two things decide what a run costs, and neither is visible in the file name.

Small runs. Five of the seven scripts drop to a small configuration when VOLUMENTIAL_EXAMPLE_SMOKE=1 is set; branched_flow_helmholtz2d.py uses its own --smoke flag instead, and laplace3d.py has no preset at all — only three environment overrides for its quadrature order, level count and multipole order. A small run is a correctness check, never a measurement — see Benchmarks and reproducibility for what a measurement has to record.

Near-field tables. Every script writes its table cache to an SQLite file named in the table below, in the working directory — except branched_flow_helmholtz2d.py, which writes near_field_table.sqlite inside its output directory instead (the one --output-dir names, build/branched-flow-helmholtz2d by default). The first run of a script builds that table and is dominated by the build; later runs load it in milliseconds. Deleting the file, or running from a different directory — for the branched-flow script, passing a different --output-dir — pays the build again. Which build path runs, and how long it takes, is the subject of Near-field table build routing.

At a glance#

Script

Small run

Table cache

Device

laplace2d.py

VOLUMENTIAL_EXAMPLE_SMOKE=1

nft_laplace2d[_smoke].sqlite

PYOPENCL_CTX

laplace2d_adaptive.py

VOLUMENTIAL_EXAMPLE_SMOKE=1

nft_laplace2d[_smoke].sqlite, shared with laplace2d.py

PYOPENCL_CTX

laplace3d.py

no preset, three size overrides

nft_laplace3d.sqlite

PYOPENCL_CTX

poisson3d.py

VOLUMENTIAL_EXAMPLE_SMOKE=1

nft_poisson3d[_smoke].sqlite

PYOPENCL_CTX

helmholtz2d.py

VOLUMENTIAL_EXAMPLE_SMOKE=1

nft_laplace2d_for_helmholtz[_smoke].sqlite

PYOPENCL_CTX if set, else picks its own

helmholtz3d.py

VOLUMENTIAL_EXAMPLE_SMOKE=1

nft_laplace3d_for_helmholtz[_smoke].sqlite

PYOPENCL_CTX if set, else picks its own

branched_flow_helmholtz2d.py

--smoke

under --output-dir

PYOPENCL_CTX if set, else picks its own

“Picks its own” is what the three Helmholtz scripts do when PYOPENCL_CTX is unset: they build an OpenCL context directly, on the first fp64-capable GPU they find, else on an fp64-capable CPU. On a host with a GPU that silently changes the cost class of a run, so set the variable. With it set, they run on the device it selects, and stop if it selects several devices or one without fp64. Device selection lists which script does what, and how to pin the device you meant.

Only laplace2d.py, laplace2d_adaptive.py, helmholtz2d.py and helmholtz3d.py run on pull requests, under VOLUMENTIAL_EXAMPLE_SMOKE=1, in a 30-minute job that holds nothing else. The rest run only in the Examples job of CI Full, at full settings, in a 240-minute budget shared by all seven — so treat “full settings” as tens of minutes each, and expect the 3D ones to sit at the expensive end. See CI and the review bots.

Laplace#

laplace2d.py#

The reference example, and the one A first volume potential walks through line by line. It evaluates the volume potential of a manufactured density over \([-\tfrac12, \tfrac12]^2\) with the Laplace kernel, compares it at the quadrature nodes against the whole-space Gaussian the density was manufactured from (the box leaves out source mass at rounding level), and prints the maximum absolute error — max |reference - computed|, not a relative one, so do not compare it against a relative tolerance from elsewhere. The walkthrough’s program computes the same potential with the same table configuration; the example adds the gallery figures (VOLUMENTIAL_GALLERY_OUTPUT_DIR), and carries a multilevel-table variant and a direct particle-to-particle check, both switched off.

VOLUMENTIAL_EXAMPLE_SMOKE=1 uv run python examples/laplace2d.py   # seconds
uv run python examples/laplace2d.py                               # full settings

laplace2d_adaptive.py#

The same kind of run on an adaptive tree, next to the uniform tree it replaces. The source is manufactured from two Gaussians of different widths, \(u = e^{-400 \lVert \boldsymbol{x} - \boldsymbol{c}_1 \rVert^2} + 0.5\, e^{-6400 \lVert \boldsymbol{x} - \boldsymbol{c}_2 \rVert^2}\) over \([-\tfrac12, \tfrac12]^2\), and the example evaluates its volume potential twice: on a uniform tree of \(16 \times 16\) leaves, and on an adaptive tree allowed at most as many leaves. At full settings both use quadrature order 9, the near-field table of laplace2d.py from the same cache file, and multipole order 20. VOLUMENTIAL_EXAMPLE_SMOKE=1 drops to quadrature order 3, a \(4 \times 4\) uniform tree and multipole order 8, with the smoke table of laplace2d.py; that checks that the run works and resolves nothing.

The adaptive tree starts from the square and is refined in passes. For every leaf, a pass takes the Legendre coefficients of the polynomial that interpolates \(f\) at the leaf’s Gauss nodes — the polynomial the volume FMM integrates on that leaf — and adds up the magnitudes of those of the highest degree in either variable; times the leaf’s area, that is the leaf’s indicator. Every leaf whose indicator is at least half of the largest is refined, MeshGen2D keeps adjacent leaves within one level of each other, and refinement stops before the pass that would give the adaptive tree more leaves than the uniform one. The criterion lives in the example, in resolution_indicator; swap in your own and keep the rest.

For each tree the example prints the number of leaves and nodes, the maximum absolute error over that tree’s quadrature nodes, and the relative \(L^2\) error \(\bigl(\sum_i w_i (u_h - u)_i^2 / \sum_i w_i u_i^2\bigr)^{1/2}\) with the tree’s quadrature weights \(w_i\), against the whole-space solution (outside the box both Gaussians are below \(e^{-40}\)). It then prints the same for uniform trees with two and four times as many leaves per side, \(32 \times 32\) and \(64 \times 64\) at full settings, which shows how far the uniform tree has to be refined to reach the adaptive tree’s accuracy. Visual gallery shows the two trees and their errors from a full-settings run. VOLUMENTIAL_GALLERY_OUTPUT_DIR writes that figure, as it does for laplace2d.py.

VOLUMENTIAL_EXAMPLE_SMOKE=1 uv run python examples/laplace2d_adaptive.py   # seconds
uv run python examples/laplace2d_adaptive.py                               # full settings

laplace3d.py#

The same problem over \([-\tfrac12, \tfrac12]^3\), reporting the maximum absolute error on the evaluation grid and both a maximum absolute and a relative \(L^2\) error at the quadrature nodes.

It has no smoke preset: VOLUMENTIAL_EXAMPLE_SMOKE does not reach it, and the defaults (\(q = 7\), five levels, multipole order 10) are what CI Full runs. What it does have is three size knobs, which is the cheap way to try it:

uv run python examples/laplace3d.py                    # full settings

VOLUMENTIAL_LAPLACE3D_Q_ORDER=3 \
VOLUMENTIAL_LAPLACE3D_N_LEVELS=3 \
VOLUMENTIAL_LAPLACE3D_M_ORDER=8 \
    uv run python examples/laplace3d.py                # much smaller

Either way the first run builds a 3D near-field table, which is the expensive part — CI Full caches nft_laplace3d.sqlite between runs for exactly that reason, and both configurations above share that one cache file. Keep it.

poisson3d.py#

A manufactured 3D Poisson solve over \([-\tfrac12, \tfrac12]^3\) — two shifted Gaussian bumps and the forcing \(f = -\Delta u\) — that also produces pictures: orthogonal slice plots and a point-cloud error plot, written as PNGs into poisson3d_output/ (override with VOLUMENTIAL_POISSON3D_OUTPUT_DIR). Use it when you want to see where the error lives rather than read one number; Visual gallery shows the slice figure of a full-settings run.

The pictures need matplotlib, which nothing in the project declares: both plotting helpers catch the ImportError and skip, so without it the run succeeds and quietly writes no PNGs at all.

VOLUMENTIAL_EXAMPLE_SMOKE=1 uv run --with matplotlib \
    python examples/poisson3d.py

Helmholtz#

helmholtz2d.py#

A q-order convergence study for the 2D Helmholtz volume potential. It is the smallest complete demonstration of the routing described in Near-field table build routing: rather than tabulating the Helmholtz kernel, it reads a Laplace near-field table and adds an online Helmholtz-minus-Laplace smooth correction for the list-1 interactions. What it reports is a manufactured-source PDE residual \((-\Delta - k^2)u - \rho\) on an interior calculus patch, not an error against a closed-form potential.

VOLUMENTIAL_EXAMPLE_SMOKE=1 uv run python examples/helmholtz2d.py

helmholtz3d.py#

The 3D counterpart, with a complex-valued source over \([-0.5, 0.5]^3\) and the same PDE-residual diagnostic. Its run_convergence_study() is what the Helmholtz notebook below imports, so the two stay in step.

VOLUMENTIAL_EXAMPLE_SMOKE=1 uv run python examples/helmholtz3d.py

branched_flow_helmholtz2d.py#

Warning

The default configuration of this script is a publication-scale pilot, not an example run. Give it --smoke unless you are on a machine set aside for it.

Free-space 2D Helmholtz branched flow through a smooth random medium, solved as a constant-background Lippmann-Schwinger equation with GMRES — no artificial outer boundary, no BIE, no PML. It is the largest program in the tree and the only one that exercises the optional fixed right preconditioners, including the Lagrange palette of fixed-wave-number tables. --smoke uses the sumpy far-field backend and needs nothing extra; the default configuration uses fmmlib, so it needs the fmmlib extra installed (uv sync --extra test --extra doc --extra fmmlib) and a pyfmmlib built as DEVELOPMENT.md describes. Output lands under --output-dir, build/branched-flow-helmholtz2d by default.

uv run python examples/branched_flow_helmholtz2d.py --smoke

Notebooks#

The three notebooks below are rendered without being executed, from a copy of the file in examples/: the same prose and code, and the outputs the notebook was committed with — unless the file is larger than 2 MB, in which case the copy drops its outputs rather than shipping them into the page. All three are committed stripped and none is anywhere near that, so what you see below is what the repository holds. Every one of them needs a working OpenCL device, and the two Poisson tutorials run co-refinement studies far past what a documentation build can afford; the Helmholtz one is a smoke-mode wrapper and is cheap, but a docs build is still not where it belongs. So the pages show the prose and the code and no results — the numbers and figures appear only when you run the notebook yourself.

Neither JupyterLab nor matplotlib is a dependency of the library — one is a tool, the other is only used for the pictures — so bring both along for the one command that needs them. --with layers them over the project environment rather than an isolated one, which is what makes the notebook’s kernel the interpreter that has volumential in it:

uv sync --extra test
uv run --with jupyterlab --with matplotlib jupyter lab examples/

Both Poisson notebooks import matplotlib in their first code cell, so without it they fail before doing anything. The 3D one also draws interactive isosurfaces with plotly if it is importable and prints a skip message if it is not; add --with plotly for those.

The two Poisson notebooks are tutorials with a roadmap, staged from a single run through a co-refinement study; the Helmholtz one is a thin wrapper that imports run_convergence_study from examples/helmholtz3d.py and plots its output, so it costs what that script costs in smoke mode. The pages are built from a staged copy, so they carry no “Edit this page” link; the files themselves are poisson2d_pytential_volumential.ipynb, poisson3d_volumential.ipynb and helmholtz3d_volumential.ipynb.

examples/convert_grid is not an example program but a two-line gmsh script used to write a box mesh out as box_grid.msh.