Contributing¶
MDAPY 2.0 grows one feature at a time. This page lists the rules every change follows.
When a feature is done¶
A feature is merged only when it has all of the following:
- the implementation;
- tests that establish its correctness (see below), meeting the coverage thresholds;
- its own page, placed by the documentation layout and following the page template below;
- entries in the Glossary for any new names;
- a line in the release notes.
Documentation layout¶
The tabs follow what the reader is doing, and the directories of docs/ follow the tabs:
| Tab | Directory | Holds |
|---|---|---|
| Getting started | getting-started/ |
installation, the quick tour, the lessons and the cheat sheet in getting-started/learn/ |
| Basics | basics/ |
what every user needs: systems, the box, selecting atoms, files, trajectories, neighbor lists, parallelism |
| Examples | examples/ |
whole calculations from start to end, each a page of its own |
| User guide | user-guide/ |
one directory per area, each page one feature: build/, analysis/, simulation/, mlip/, visualization/, interop/ |
| Performance | performance/ |
timings of MDAPY and of other programs |
| Reference | reference/ |
the Python API in reference/api/, units and conventions, elements, the glossary, the references, file formats |
| About | about/ |
the release notes, reporting a problem, this page |
Each feature has its own page, named after it (eam.md, minimize.md), in
the section of its tab that fits it; closely tied classes, such as an
analysis and its result, share the page. A feature that fits no section goes
to a section Misc of its tab until a section for it exists. A new page is
added to the navigation in mkdocs.yml.
Feature pages¶
Each feature has a hand-written page that tells how to use it, with these sections, in this order:
- Title and one sentence saying what the feature does.
- Minimal example that runs as shown.
tests/test_docs.pyruns every Python block of every page, in order, with shared variables. A block that is only an illustration is preceded by the line<!-- skip-test -->. A page whose examples need an optional package starts with a line such as<!-- needs: ovito -->; its examples are skipped where the package is not installed. Examples import mdapy asimport mdapy as mpand writemdapy.io.read,mp.System; the text names the functions in full, asmdapy.io.read. Where a result can be seen, a short example after it draws it with mdapy's renderer or plots it withmdapy.visualization.plotintoimages/name.png; the tests run it in a folder with an emptyimages. The picture, made by running the page's examples and kept next to the page as a PNG of 256 colors, follows the block with a caption in italics that says what it shows. - Parameters, as a table:
| Name | Type | Default | Unit | Meaning |. - Results: the attributes and every public method, as tables, with the conventions needed to read them.
- Errors: when the documented exceptions are raised.
- For analyses, Definition: what is computed, with formulas, so that a result can be interpreted.
- Please cite: for a feature built on published work, a box
(
!!! quote "Please cite") at the end of the page, linking the papers to cite when the feature is used.
Papers are listed once, on the References
page, each with an anchor such as #zhou2004; pages cite them as links,
for example [Zhou et al. (2004)](../reference/references.md#zhou2004),
and never repeat them. Web pages a feature follows, such as the
specification of a file format or the LAMMPS documentation of a command, are
plain links on the feature's page, in a See also section.
Pages do not describe how a feature is implemented, how it is tested or how fast it is: the tests are the record of correctness, and timings belong on a separate benchmark page. Sections that do not apply are left out.
Docstrings¶
Docstrings use the numpy style and describe inputs and outputs only: meaning, type, shape and unit of each parameter and return value, and the exceptions raised. They contain no formulas and no explanation of the method; that belongs on the feature page. The Python API page is generated from them.
Tests¶
- Correctness comes from sources independent of the code under test:
- analytic results, such as known values for perfect lattices;
- physical invariances: rotation, translation, permutation of atoms, replication of the cell, choice of equivalent cells;
- brute-force reference implementations in plain NumPy, used on small systems;
- finite differences for forces and virials;
- round trips and hand-written examples for file formats.
- Where LAMMPS, OVITO, freud or atomsk implements the same method, the tests compare with it, run live at the reference versions, LAMMPS stable_30Sep2026, OVITO 3.17.1, freud 3.6.1 and atomsk Beta 0.13.1; freud computes in single precision, so its values agree to about 1e-6. They are skipped where these are not installed. Results are never compared against stored snapshots of other software.
- Parallel code is tested for identical results across thread counts.
- Error paths are tested: invalid input raises the documented exception.
- Comparisons state their tolerance and why, and failure messages show the size of the discrepancy.
- Tests run from any working directory and locate their data through fixtures.
Coverage thresholds:
- Total: at least 90 %, enforced by
pytest --cov. - Per file: at least 95 %, checked in CI from the JSON coverage report.
What a complete run needs¶
pip install -e ".[dev]" gives everything a plain pytest needs: the test
tools, matplotlib, ASE and
spglib. A test that needs more is skipped
where it is missing, so a plain run passes on any machine; pytest -rs lists
the skipped tests and why. A complete run needs the following.
pip install -e ".[dev]"
pip install ovito==3.17.1 freud-analysis==3.6.1 phonopy==4.8.6 seekpath==2.2.2 \
dscribe==2.1.2 pymatgen-core networkx torch torchnep
| Package | Tested with it |
|---|---|
| OVITO | structure analyses, colors and pictures, against OVITO |
| freud | analyses and conversions, against freud |
| phonopy | phonons, against phonopy; its dependency symfc for phonons at finite temperature |
| seekpath | band paths from the symmetry |
| DScribe | SOAP descriptors, against DScribe |
| pymatgen | conversions to and from it |
| NetworkX | rings, against a brute-force search |
| torchnep, with PyTorch | the files of training and of predictions |
LAMMPS stable_30Sep2026 is built from source, with its Python module installed from the wheel that the build leaves:
git clone --depth 1 --branch stable_30Sep2026 https://github.com/lammps/lammps.git
cmake -S lammps/cmake -B lammps-build -D CMAKE_BUILD_TYPE=Release \
-D BUILD_SHARED_LIBS=on -D BUILD_MPI=no -D BUILD_OMP=no \
-D PKG_MANYBODY=on -D PKG_MOLECULE=on -D PKG_EXTRA-COMPUTE=on \
-D PKG_EXTRA-FIX=on -D PKG_EXTRA-PAIR=on \
-D PKG_VORONOI=on -D DOWNLOAD_VORO=on -D PKG_PHONON=on \
-D PKG_KOKKOS=on -D Kokkos_ENABLE_SERIAL=on -D Kokkos_ENABLE_THREADS=on \
-D Python_EXECUTABLE="$(which python)"
cmake --build lammps-build -j 4
cmake --build lammps-build --target install-python
pip install lammps-build/lammps-*.whl
| LAMMPS package | Tested with it |
|---|---|
MANYBODY |
the EAM potentials of the tests |
MOLECULE |
molecular files |
EXTRA-COMPUTE |
bond angles, the entropy fingerprint and Ackland-Jones |
EXTRA-FIX, EXTRA-PAIR |
free energies of solids and liquids |
VORONOI |
the Voronoi analysis |
PHONON |
dynamical matrices |
KOKKOS on the CPU |
the shutdown of Kokkos at exit; a build of it for GPUs skips this test |
The rest are programs and conditions of the machine:
| Needs | Tested with it |
|---|---|
Atomsk Beta 0.13.1 on the PATH |
the structure builder, against Atomsk |
FFmpeg on the PATH |
movies |
| a GPU | pictures on the GPU; ray tracing needs a GPU with ray tracing units |
glslc of the shaderc version named in render_vulkan.comp |
the compiled shader of Linux |
dxc of the Windows SDK named in render_d3d12.hlsl |
the compiled shader of Windows |
the test data of torchnep, beside this repository or at MDAPY_TORCHNEP_DATA |
NEP against its reference values |
| Python 3.14 | files compressed with zstd |
| a German or French locale | numbers under a locale with a decimal comma |
Compiled code¶
- C++17. All compiled code goes into the single extension module
mdapy._core; each area registers a submodule fromsrc/cpp/bindings/. - Kernels never create threads or use OpenMP. They call
mdapy::parallel_forormdapy::parallel_reduce. - Kernels that are called from Python run through
mdapy::bindings::run_released, which releases the GIL and supportsprogressandcancel. - Code compiles without warnings with
-Wall -Wextra -Wpedantic -Wshadow; CI builds with warnings as errors and also runs the tests under AddressSanitizer and UndefinedBehaviorSanitizer. tests/cpp/thread_checks.cppchecks the thread pool and the parallel kernels without Python; CI runs it under ThreadSanitizer. New parallel kernels get a check there.
Style¶
ruff check .andruff format --check .pass.- Names private to a class start with two underscores,
self.__table, so that they cannot be reached by accident from outside. Names that other modules of MDAPY use, and methods that subclasses override, start with one underscore: they are internal to MDAPY, not to the class. Private functions and constants of a module start with one underscore. - Immutable objects (
System,Box,Topology) refuse new attributes and return themselves fromcopy.copyandcopy.deepcopy. - Commit messages are short English sentences describing the change.