forcefill
Turn ligands into a ready-to-use OpenMM force-field XML —
either the non-standard residues (ligands, cofactors, hetero molecules) found in
a PDB, or ligand files on their own. Parameters come from AmberTools
(antechamber + parmchk2) via ParmEd,
from OpenFF Sage or
Espaloma via
openmmforcefields, or — for
CHARMM — by converting the CGenFF stream file
ParamChem gave you.
The output is a plain ffxml file you load alongside the standard force fields:
ff = ForceField("amber14-all.xml", "amber14/tip3p.xml", "extras.xml")
system = ff.createSystem(pdb.topology)
Two ways in, depending on whether you have a structure:
from forcefill import build_forcefield_xml, build_ligand_xml
# A complex: find what amber14 cannot match and parameterize it
build_forcefield_xml("complex.pdb", "extras.xml")
# Just the ligand, no structure anywhere
build_ligand_xml("benzamidinium.sdf", "ben.xml")
What it does
- Identify —
ForceField.getUnmatchedResiduesfinds every residue the base force field (default:amber14-all.xml+amber14/tip3p.xml) has no template for. - Classify — only chemistry a stand-alone GAFF treatment is actually valid for gets parameterized; see What gets skipped, and why.
- Check — before anything expensive: the net charge is read from the ligand file, a supplied file is confirmed to be the same molecule as the residue, and the geometry is checked for the faults that produce NaN energies.
- Parameterize — each unique residue through
antechamber(GAFF2 atom types, AM1-BCC charges →.mol2) andparmchk2(missing parameters →.frcmod), through OpenFF withbackend="smirnoff", through the Espaloma graph network withbackend="espaloma", or from a CGenFF stream file withbackend="charmm". - Assemble — one XML per residue, plus one combined XML.
- Validate — an
openmm.Systemis built frombase force field + new XMLfor every parameterized residue on its own (and for the whole input when nothing was skipped), so a template that does not match its residue fails loudly here instead of at simulation time.minimize=Trueadditionally catches parameters that are unphysical rather than merely absent (see Checks).
Documentation
Full documentation is at forcefill.readthedocs.io:
| Installation | conda-forge, and the two dependencies that are not ordinary ones |
| Quickstart | a complex in, an ffxml out, and simulating with it |
| What gets skipped, and why | the residues forcefill refuses, and what to do instead |
| Cleaning the structure | water, buffer ions and crystallization additives — and the metals it keeps |
| Ligand input | SDF, MOL2 or SMILES; LigandSpec; ligands with no structure at all |
| Four backends | GAFF, OpenFF Sage, Espaloma, CGenFF — and which mix |
| CHARMM and CGenFF | converting a ParamChem stream file, and the three ways doing it by hand goes wrong |
| Bespoke torsions | a BespokeFit OFFXML, and the checks that protect the QC you paid for |
| Checks | what runs before the expensive step, and what runs after |
| Things to get right | explicit hydrogens, net charge, virtual sites, the periodic box |
Relation to openmmforcefields |
which template generators forcefill uses, and why not GAFFTemplateGenerator |
| API reference | every public function, class and constant |
Installation
AmberTools is conda-only, so conda-forge is the recommended route:
conda env create -f environment.yml
conda activate forcefill
pip install -e . --no-deps
Or into an existing environment:
conda install -c conda-forge openmm parmed ambertools
pip install forcefill
Requires Python ≥ 3.10. Two things are not ordinary dependencies: AmberTools is
not a Python package (the antechamber and parmchk2 executables must be on
PATH for the gaff backend), and espaloma is optional because it pulls in
PyTorch. The full list, and the reason the openmmforcefields floor is 0.16, is
in the installation guide.
Quickstart
from forcefill import build_forcefield_xml
result = build_forcefield_xml(
"complex.pdb",
"extras.xml",
net_charges={"LIG": -1}, # essential for sensible AM1-BCC charges
clean_structure=True, # drop water, buffer ions and crystallization additives
)
print(result.parameterized) # ['LIG']
print(result.skipped) # {'ZN': 'monatomic species - ...'}
then simulate with:
from openmm import app
pdb = app.PDBFile("complex.pdb")
ff = app.ForceField("amber14-all.xml", "amber14/tip3p.xml", "extras.xml")
system = ff.createSystem(pdb.topology)
result also reports the per-residue XML files (result.residue_xmls), the
skip reasons (result.skipped), and the directory holding every intermediate
file (result.workdir) for inspection — pass cleanup=True to remove it on
success.
More, including per-ligand settings and the other three backends, in the guide.
Development
conda env create -f environment.yml && conda activate forcefill
pip install -e . --no-deps
pytest -m "not integration and not smirnoff" # fast hermetic tests only
pytest # everything, including real antechamber and OpenFF
Style is enforced by ruff (pip install -e '.[dev]' && pre-commit install).
The docs build is pip-only and separate from the conda environment — see Contributing, which also has the release process.
Changelog
Every release is described in CHANGELOG.md. The public API is
everything exported from forcefill/__init__.py, and it follows semantic
versioning from 1.0.0 on.
Roadmap
- A
forcefillcommand-line interface —build_ligand_xmlis the shape one wants. - Caching, so re-runs into the same workdir skip finished antechamber jobs —
and finished SMIRNOFF/Espaloma templates, which openmmforcefields' own
cache=cannot help with on the code path forcefill uses. - Covalently bound ligands. Still skipped, and deliberately: a stand-alone treatment of a polymer-linked residue is wrong whichever backend produces it.
License
MIT — see LICENSE.
Metadata
Release files for forcefill 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| forcefill-1.0.0.tar.gz | 80.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| forcefill-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 166.3 kB
Release files / forcefill-1.0.0.tar.gz
| Download URL | forcefill-1.0.0.tar.gz |
|---|---|
| Size | 80.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3334efd28c6050d98cd8c9d334e8ac13e1c9d776171d78d9cb43061aab766f36
|
|
BLAKE2b-256 checksum How to use checksums |
2f5ffb5aac97cab18f31010f470f89774c7522789067cccecb5ab2df93c7d647
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.
Transparency logRelease files / forcefill-1.0.0-py3-none-any.whl
| Download URL | forcefill-1.0.0-py3-none-any.whl |
|---|---|
| Size | 85.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d41bba06ae7e3be86a75e506d400721e3d808a5cde46a6289f2ac1aca403f023
|
|
BLAKE2b-256 checksum How to use checksums |
6407cfc456d0c06d8739641fcf8c5c0ffabe63136f085f8a7599d94645725f23
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.
Transparency log