贡献指南

TensorMesh 规模不大,一个有的放矢的补丁就足以带来切实改变。本页介绍实际的工作流程:搭建开发环境、运行测试、构建文档,以及提交 PR。

如果您有想法但不确定它该归在何处,可在 GitHub DiscussionsIdeas & RFCs 类别)中发帖,或在 Discord 上留言。对于已确认的缺陷和具体的功能请求,issue 跟踪器才是合适的入口——参见 GitHub Issues

环境搭建

环境要求:

  • Python ≥ 3.10

  • PyTorch ≥ 2.0(若要运行 GPU 路径,则需带 CUDA)

其余所有依赖都可在下面的步骤中从 PyPI 安装——包括 torch-sla>= 0.2.1),这是提供全部稀疏求解器后端的硬性依赖。无需 C++ 工具链或手动构建步骤。

克隆并以可编辑模式安装:

git clone https://github.com/camlab-ethz/TensorMesh.git
cd TensorMesh
pip install -e ".[test]"

可选附加项:cupy / cudss(GPU 稀疏直接求解器后端)、gpu(同时安装两者),以及 example(部分绘图脚本所需的 Plotly)。

运行随附的检查脚本对安装进行冒烟测试,它会在 CPU 上(以及在可用时在 GPU 上)求解一个极小的泊松方程问题,并报告哪些稀疏求解器后端已就绪:

python -m tensormesh.verify_install

如果 CPU 和 CUDA 两种求解相对于解析参考解都打印出合理的误差,那就说明一切正常。

运行测试

测试位于 tests/ 目录下,其结构与包的布局相对应(tests/element/tests/sparse/tests/ode/ 等)。它们使用原生 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

在编写新测试之前,有几条值得了解的约定:

  • 新测试中默认使用 ``float64`` 和 ``cpu``;当您所测试的代码路径涉及设备/数据类型的处理时,请添加由 @pytest.mark.skipif(not torch.cuda.is_available()) 保护的 GPU 变体测试。

  • 避免在装配器的 ``forward()`` 方法内部使用 NumPy。 装配器在 torch.vmap 下运行,而它与 NumPy 数组存在不会报错的隐性不兼容——请始终使用 torch。

  • 测试的命名应反映其所测试的内容,而非文件名。在不同测试文件中重复的函数名虽然能正常运行,却会使 pytest -k 过滤变得混乱。

构建文档

文档使用 Sphinx 配合 pydata_sphinx_theme 主题。在本地构建:

cd docs
make html

渲染输出会生成到 docs/_build/html/;打开 docs/_build/html/index.html 即可查看。目前即使是干净的构建也会产生少量已存在的警告——example_gallery/wave.rst 中一个过时的 ExplicitRungeKutta 交叉引用,外加几处 NumPy 风格的文档字符串格式警告——因此实际的规则很简单:不要新增警告,而修复已有的警告则始终受欢迎。

本站点通过 Sphinx 的 gettext 工作流实现中英双语。权威的英文源是 .rst 文件;中文字符串位于 docs/source/locale/zh_CN/ 下的 .po 文件中。在编辑英文文本后,重新生成 .po 文件:

make gettext                    # extract strings
sphinx-intl update -p _build/gettext -l zh_CN   # merge into .po

构建中文站点,或同时构建两个站点:

make zh         # Chinese build only
make html-all   # both EN and ZH

如果您的 PR 仅改动英文文本,可以不去动 .po 文件——维护者会批量处理国际化更新。

代码风格

目前 CI 中没有强制执行的自动格式化工具或代码检查工具,因此实际的规则是与您正在编辑的文件保持一致。具体而言,这意味着:

  • 采用 4 个空格缩进,遵循 PEP 8 的精神,行长约为 88 至 100 个字符。

  • NumPy 风格的文档字符串,依次为 Summary → Parameters → Returns → Notes → Examples(按此顺序)。鼓励但不强制使用类型注解;如果您添加注解,请优先使用 torch.Tensor 而非泛型的 Any

  • 对于与 nn.Module 兼容的张量集合,请使用已有的 ``BufferDict`` / ``BufferList`` 容器(tensormesh.nn)。在跨越 nn.Module 边界时,不要自行使用普通的张量 dict

  • 请让 forward() 方法保持为纯张量代码——不要基于张量值进行 Python 控制流操作,不要使用 NumPy,除非结果用于打印/日志,否则不要调用 .item()

提交拉取请求

具体操作流程:

  1. 从 ``main`` 派生并创建分支。 分支命名不拘形式;feat/<short> / fix/<short> / docs/<short> 与提交信息的风格相匹配。

  2. 编写测试。 改动数值代码却没有相应测试的 PR 不太可能被合并。CPU + float64 是最低要求;如果所改动的代码会根据设备分支,请添加一个 GPU 变体测试。

  3. 在本地运行相关测试并构建一次文档;两者都应没有问题。

  4. 提交 PR,用 Fixes #NN 关联对应 issue(部分修复则用 Refs #NN),并在正文中说明为什么这样做——而不仅仅是做了什么,因为后者已由 diff 体现。

  5. 提交信息风格沿用项目其余部分的约定:<type>(<scope>): <imperative summary>,例如 fix(ode): propagate u0.dtype/device through step()。使用中的类型有:featfixdocstestrefactorperfci

评审者会关注的要点:

  • 覆盖您所改动路径的测试,并在适用时包含修复前的失败情形。

  • 如果您改动了文档字符串或 .rst 文件,不应引入新的 Sphinx 警告。

  • 不应出现意外的向后不兼容——如果某个公共符号被重命名或移除,PR 描述中应明确指出这一点。

  • 数值方面的改动(新增单元、新增数值积分、新增求解器)应至少包含一项对照解析参考解的检验。

这是一个小规模的维护团队;响应时间为尽力而为。如果某个 PR 沉寂了一两周以上,欢迎在 Discord#dev 频道礼貌地提醒一下。

签署声明与开发者原创证书(DCO)

进入 main 的每一次提交都必须依据 开发者原创证书(DCO)进行签署声明——这与 Linux 内核、PyTorch、vLLM 以及大多数采用 Apache 许可的项目所使用的轻量级贡献政策相同。完整文本位于代码仓库根目录的 DCO 文件中;其要点是,您声明这些代码是您有权贡献的(或来自一个许可正当的来源),并且您是在本项目的 Apache 2.0 许可下提交它的。

操作非常简单——向 git commit 传入 -s,Git 就会自动为您追加该行:

git commit -s -m "fix(ode): propagate dtype through step()"

从而生成如下形式的尾注(trailer):

Signed-off-by: Your Name <your@email>

请使用您的真实法定姓名和一个您能掌控的电子邮箱。如果某个拉取请求的提交缺少有效的 Signed-off-by 尾注,它将不会被合并。

如果您在已推送的提交上忘记了签署声明:

# 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

维护者无法代您签署——签署声明是贡献者自身的证明。

项目结构

为新贡献者提供的一份各部分位置的大致地图:

路径

内容

tensormesh/mesh/

Mesh、生成器(gen_rectanglegen_cube 等)、输入/输出。

tensormesh/element/

单元类型(LineTriangle 等)、基函数、数值积分、Gmsh/VTK 重排序。

tensormesh/assemble/

ElementAssembler / NodeAssembler / FacetAssembler 基类,以及内置的 Laplace / Mass / 线弹性装配器。

tensormesh/sparse/

SparseMatrix(继承自 torch_sla.SparseTensor)、spsolve 分发、nonlinear_solve

tensormesh/operator/

Condenser(通过静态凝聚施加狄利克雷边界条件)。

tensormesh/ode/

时间积分方案——显式/隐式线性欧拉法、中点法、龙格-库塔基类。

tensormesh/functional/

面向有限元方法的张量工具(Voigt 记法、梯度)。

tensormesh/dataset/

批量网格生成,以及用于机器学习训练数据的 …MultiFrequency 源项采样器。

tensormesh/nn/

BufferDict / BufferList

tests/

测试套件,其结构与包的布局相对应。

examples/

端到端的完整示例。

docs/

Sphinx 源文件、构建配置和翻译文件。

拿不准时,请用 grep 搜索附近的某个符号——本包规模不大,rg <SymbolName> tensormesh/ 通常就能把您引到正确的位置附近。