Contributing#

Volumential is a research code with a small maintainer group, and most changes land as pull requests against main.

Before you start#

  • Set up an environment per Installation and run the traversal sanity check. A contribution measured in an environment that fails that check cannot be evaluated.

  • If the change touches numerics, decide up front which test tier it belongs in (Tests and markers), and if it comes with a cost claim, what that measurement has to record (Benchmarks and reproducibility).

The loop#

uvx ruff@0.13.0 check
uvx basedpyright -p pyproject.toml --level error
uv run pytest -q
sphinx-build -W --keep-going -b html doc/source doc/build/html   # if docs changed

Run all four locally. None of them is a safety net you can lean on, and the difference matters before you assume CI will catch something (CI and the review bots has the full picture):

  • On a pull request targeting main, basedpyright and the default pytest suite run as written above.

  • ruff runs there too, but only as ruff check --select E9,F63,F7,F82 with an unpinned version — the error-level smoke subset, not the ruff.toml rule set. The pinned full check above is a local check; it can fail on baseline diagnostics that pull-request CI never looks at.

  • The documentation build runs in CI Full, which has no pull_request trigger. A documentation change that breaks the -W build is not caught until it is on main.

  • On a stacked pull request — one whose base is another branch — CI does not run at all: its pull_request trigger is restricted to base main. A stacked change gets no lint, type, test or example job until the stack reaches main, so the local loop is the only check it has.

What a change should carry#

  • Tests. A behaviour change without a test that fails before it is not reviewable. For a new kernel, mode or derivative path, also update Validation Matrix in the same pull request — a feature is not “covered” until there is at least one CI-friendly regression test, and for high-order numerical claims either a full_accuracy test or a documented benchmark and provenance path.

  • Documentation. If the change alters something a user would otherwise discover by reading the source, it belongs on a page in this site. The layout table in Development says which section.

  • Lint baseline. If you cleaned a file, delete its entry from the [lint.per-file-ignores] block of ruff.toml in the same commit.

  • No vendored patches. A patch that exists only inside one environment’s site-packages is an incident to remediate, not a fix. Upstream it, put it on a tracked fork branch, or vendor and commit it.

Commit and pull-request style#

Commits use conventional prefixes — feat:, fix:, perf:, refactor:, docs:, ci:, chore:, deps:, bench: — with an optional scope, as in fix(tables): make the batched-to-scalar Duffy fallback loud and recorded. Write the subject as a statement of what the change does, not of what it is about.

Keep a pull request to one reviewable idea. The automated reviewers (CI and the review bots) comment inline, and a branch that mixes four unrelated changes gets four unrelated review threads on the same diff.

Things that are easy to get wrong#

  • Passing the same array as both src_weights and src_func to drive_volume_fmm. The first is the density times the quadrature weights; the second is the bare density.

  • Building a traversal with separate-but-identical source and target arrays. Use targets=None; VOLUMENTIAL_STRICT_SOURCE_TARGET_TREE=1 makes the mistake fail loudly.

  • Quoting a timing from an auto device selection, or from a cold process, without saying so. See Benchmarks and reproducibility.

  • Assuming a cached table is the table you think it is. Check build_routing; see Near-field table build routing.