Contributing¶
TensorMesh is small enough that one well-aimed patch can move the needle. This page covers the practical loop: get a dev environment, run the tests, build the docs, and open a PR.
If you have an idea but aren’t sure where it fits, post on GitHub Discussions (Ideas & RFCs category) or drop a line on Discord. For confirmed bugs and specific feature requests, the issue tracker is the right entry point — see GitHub Issues.
Getting set up¶
Requirements:
Python ≥ 3.10
PyTorch ≥ 2.0 (with CUDA if you want to run the GPU paths)
Everything else installs from PyPI in the step below — including
torch-sla (>= 0.2.1), the hard dependency that provides every
sparse-solver backend. No C++ toolchain or manual build step is
required.
Clone and install in editable mode:
git clone https://github.com/camlab-ethz/TensorMesh.git
cd TensorMesh
pip install -e ".[test]"
Optional extras: cupy / cudss (GPU sparse-direct solver
backends), gpu (both at once), and example (Plotly for some
figure scripts).
Smoke-test the install by running the bundled checker, which solves a tiny Poisson problem on CPU (and on GPU if available) and reports which sparse-solver backends are wired up:
python -m tensormesh.verify_install
If both the CPU and the CUDA solve print sensible errors against the analytical reference, you’re good.
Running the tests¶
Tests live under tests/, mirroring the package layout
(tests/element/, tests/sparse/, tests/ode/, …). They use
plain pytest:
pytest tests/ # full suite
pytest tests/sparse/test_spsolve.py -v # one file
pytest tests/element/test_basis.py::test_triangle_basis -v # one test
pytest tests/ --cov=tensormesh # coverage report
A few conventions worth knowing before writing new tests:
Default to ``float64`` and ``cpu`` in new tests; add a GPU variant guarded by
@pytest.mark.skipif(not torch.cuda.is_available())when the code path you’re exercising has device/dtype handling.Avoid NumPy inside assembler ``forward()`` methods. Assemblers run under
torch.vmap, which is silently incompatible with NumPy arrays — stay in torch.Name tests after what they exercise, not the file. Duplicate function names across test files run fine but make
pytest -kfiltering noisy.
Building the docs¶
The docs use Sphinx with the pydata_sphinx_theme. Build locally:
cd docs
make html
Rendered output goes to docs/_build/html/; open
docs/_build/html/index.html to view. A clean build currently emits
a handful of pre-existing warnings — a stale ExplicitRungeKutta
cross-reference in example_gallery/wave.rst plus a few NumPy-style
docstring-formatting warnings — so the working rule is simply: don’t
add new ones, and fixing an existing one is always welcome.
The site is bilingual EN/ZH via Sphinx’s gettext workflow. The
canonical English source is the .rst files; Chinese strings live
in .po files under docs/source/locale/zh_CN/. To regenerate the
.po after editing English text:
make gettext # extract strings
sphinx-intl update -p _build/gettext -l zh_CN # merge into .po
To build the Chinese site or both sites together:
make zh # Chinese build only
make html-all # both EN and ZH
If your PR only touches English text, you can leave the .po files
alone — a maintainer will batch the i18n update.
Code style¶
There is no automated formatter / linter enforced in CI today, so the working rule is match the file you’re editing. In practice that means:
4-space indent, PEP 8 in spirit, line length around 88-100 characters.
NumPy-style docstrings with Summary → Parameters → Returns → Notes → Examples (in that order). Type hints are encouraged but not required; if you add them, prefer
torch.Tensorover the genericAny.Use the existing ``BufferDict`` / ``BufferList`` containers (
tensormesh.nn) fornn.Module-compatible tensor collections. Don’t roll a plaindictof tensors when crossingnn.Moduleboundaries.Keep
forward()methods pure tensor code — no Python control flow on tensor values, no NumPy, no.item()calls unless the result is for a print/log.
Submitting a pull request¶
The mechanical flow:
Fork & branch from ``main``. Branch naming is informal;
feat/<short>/fix/<short>/docs/<short>matches the commit-message style.Write tests. A PR that touches numerical code without an associated test is unlikely to be merged. CPU +
float64is the minimum; add a GPU variant if the touched code branches on device.Run the relevant tests and a docs build locally; both should be clean.
Open the PR, link the issue with
Fixes #NN(orRefs #NNfor partial fixes), and fill in the body explaining the why — not just the what, since the diff already covers that.Commit message style follows the rest of the project:
<type>(<scope>): <imperative summary>, e.g.fix(ode): propagate u0.dtype/device through step(). Types in use:feat,fix,docs,test,refactor,perf,ci.
What reviewers will look for:
Tests that exercise the path you changed, including the failure mode before your fix if applicable.
No new Sphinx warnings if you touched docstrings or
.rst.No accidental backwards-incompatibility — if a public symbol is renamed or removed, the PR description should call it out explicitly.
Numerical changes (new element, new quadrature, new solver) should include at least one analytical-reference check.
This is a small maintainer team; response times are best-effort. If a
PR has been silent for more than a week or two, a polite ping on
Discord #dev is welcome.
Sign-off and the Developer Certificate of Origin¶
Every commit that lands on main must be signed off under the
Developer Certificate of Origin
(DCO) — the same lightweight contribution policy used by the Linux
kernel, PyTorch, vLLM, and most Apache-licensed projects. The full
text lives in the DCO file at the repository root; the gist is
that you certify the code is yours to contribute (or that it came
from a properly-licensed source) and that you are submitting it under
the project’s Apache 2.0 license.
The mechanics are trivial — pass -s to git commit and Git
appends the line for you:
git commit -s -m "fix(ode): propagate dtype through step()"
producing a trailer of the form:
Signed-off-by: Your Name <your@email>
Use your real legal name and an email you control. A pull request
whose commits lack a valid Signed-off-by trailer will not be
merged.
If you forget the sign-off on a commit you have already pushed:
# for the latest commit only
git commit --amend -s --no-edit
git push --force-with-lease
# for an older commit, or the whole branch
git rebase --signoff main
git push --force-with-lease
A maintainer cannot sign on your behalf — sign-off is the contributor’s own certification.
Project layout¶
A rough map of where things live, for new contributors:
Path |
Contents |
|---|---|
|
|
|
Element types ( |
|
|
|
|
|
|
|
Time-integration schemes — explicit/implicit linear Euler, midpoint, RK base classes. |
|
Tensor utilities for FEM (Voigt notation, gradients). |
|
Batched mesh generation + |
|
|
|
Test suite, mirroring the package layout. |
|
End-to-end worked examples. |
|
Sphinx sources, build configuration, and translation files. |
When in doubt, grep for a nearby symbol — the package is small enough
that rg <SymbolName> tensormesh/ will usually point you at the
right neighbourhood.