OpenMM Backend¶
The OpenMMEngine is Q2MM's most versatile backend, supporting both
Harmonic and MM3 functional forms. It runs in-process via the
OpenMM Python API, avoiding subprocess overhead.
Installation¶
OpenMM is available via conda-forge:
Or with pip:
For GPU (CUDA) support, install the CUDA plugin package:
This provides CUDA plugin binaries that JIT-compile kernels via NVRTC, supporting all NVIDIA architectures including Blackwell (RTX 5090). Works on Linux, WSL2, and native Windows — requires an NVIDIA GPU and a compatible driver (≥ 535).
WSL2 recommended for GPU benchmarks
For GPU benchmarks and the full CUDA stack (JAX CUDA + JAX-MD + OpenMM CUDA), WSL2 is the recommended environment on Windows. Native Windows supports OpenMM CUDA but not JAX CUDA or JAX-MD.
Avoid OpenCL for GPU acceleration
OpenCL on modern NVIDIA GPUs (e.g. RTX 5090) gives very poor GPU utilisation (~14%). Always prefer CUDA over OpenCL when an NVIDIA GPU is available. The auto-detection order (CUDA > OpenCL > CPU) ensures CUDA is selected first when both plugins are installed.
Verify installation
Platform Detection¶
OpenMMEngine auto-detects the fastest available compute platform:
| Priority | Platform | Notes |
|---|---|---|
| 1 | CUDA | Requires NVIDIA GPU + CUDA toolkit |
| 2 | OpenCL | AMD/Intel GPUs |
| 3 | CPU | Multi-threaded, available everywhere |
| 4 | Reference | Single-threaded, for debugging only |
Override with the platform_name constructor parameter if needed.
Supported Energy Terms¶
| Term | Harmonic Mode | MM3 Mode |
|---|---|---|
| Bonds | ✅ Harmonic | ✅ Cubic/quartic |
| Angles | ✅ Harmonic | ✅ Sextic |
| Torsions | ✅ | ✅ |
| Improper torsions | ❌ | ❌ |
| vdW (LJ 12-6) | ✅ | — |
| vdW (Buckingham exp-6) | — | ✅ |
| Electrostatics | ❌ | ❌ |
| 1-4 scaling | ✅ AMBER (ε/2) | None (MM3) |
Configuration¶
from q2mm.backends.mm import OpenMMEngine
engine = OpenMMEngine(
platform_name=None, # auto-detect (CUDA > OpenCL > CPU > Reference)
precision=None, # "single", "mixed", or "double" (GPU only; default: "mixed")
)
| Parameter | Type | Default | Description |
|---|---|---|---|
platform_name |
str \| None |
None |
Force a specific OpenMM platform |
precision |
str \| None |
None |
GPU precision mode; ignored on CPU |
Runtime Parameter Updates¶
OpenMMEngine supports in-place parameter updates via update_forcefield(),
which mutates force parameters in the existing OpenMM Context without
rebuilding the system. This makes iterative optimization fast.
Capabilities¶
| Method | Supported | Notes |
|---|---|---|
energy() |
✅ | Returns kcal/mol |
minimize() |
✅ | OpenMM L-BFGS minimizer |
hessian() |
✅ | Numerical (finite-difference) |
frequencies() |
✅ | From numerical Hessian |
energy_and_param_grad() |
✅ | Exact for bond/angle params; torsion/vdW gradients returned as zero |
supports_runtime_params() |
✅ | — |
supports_analytical_gradients() |
✅ | Bond/angle only |
Serialization¶
Systems can be saved to and loaded from OpenMM XML:
Limitations¶
- Numerical Hessians —
hessian()uses finite differences, not analytical second derivatives. Accurate but slower than JAX's analytical Hessian. - Partial analytical gradients —
energy_and_param_grad()provides exact gradients for bond and angle parameters via OpenMM global-parameter derivatives. Torsion and vdW parameter gradients are returned as zero. - No improper torsions — not yet implemented.
- No electrostatics — charge optimization is not supported.
Example¶
from q2mm.backends.mm import OpenMMEngine
from q2mm.models.forcefield import ForceField
from q2mm.models.molecule import Q2MMMolecule
engine = OpenMMEngine()
# Load molecule and force field
mol = Q2MMMolecule.from_xyz("molecule.xyz")
ff = ForceField.from_amber_frcmod("params.frcmod", mol)
# Single-point energy
e = engine.energy(mol, ff)
print(f"Energy: {e:.4f} kcal/mol")
# Frequencies
freqs = engine.frequencies(mol, ff)
print(f"Frequencies: {freqs}")