StrainRelief calculates the ligand strain of docked poses and has a suite of different force fields with which to do this. This includes our own MACE neural network potential trained on SPICE2 and also Meta's FAIRChem models, such as e-SEN and UMA. Optimisations are run in parallel using the neural-optimiser package.
- 📄 The publication can be found here.
- 📊 All relevant datasets here.
- 💬 RAG chatbot for questions about the paper and references.
- 💻 Chatbot source code.
- 📉 neural-optimiser package.
This update focuses on accelerating the StrainRelief backend and increasig usability through simpler configurations and workflows:
- Molecular optimisation are now paralellised using the
neural-optimiserpackage providing a 3x speed-up. - New simplified hyra configuration. An example of the full configuration is given here.
Note: this introduces changes that are not backwards-compatible with previous versions.
Pre-requisities: Python 3.11, PyTorch and PyTorch Geometric compatible with your envirnment
# For example
uv pip install torch==2.8.0 -f https://data.pyg.org/whl/torch-2.8.0+cu128.html
uv pip install torch-geometric==2.7.0 torch-cluster -f https://data.pyg.org/whl/torch-2.8.0+cu128.htmlpip install strain-relief
uv sync --extra dev --editableor create a virtual environment and install the package and its dependencies in editable mode:
uv venv
source .venv/bin/activate
# [install torch and torch-geometric as above]
uv pip install -e ".[dev]"
uv pip install --force-reinstall e3nn==0.5 fairchem-core
uv run pre-commit installNote: mace-torch==0.3.x requires e3nn==0.4.4 (only for training, not inference). fairchem-core requires e3nn>=0.5. So until mace-torch==0.4 is released we will have to do this finicky way of installing (GitHub issue).
The protocol used in StrainRelief is designed to be simple, fast and model agnostic - all that is needed to apply a new force field is to write a neural-optimiser Calculator class (such as an ASE calculator wrapper). Additionally, the package is already compatible with all MACE and Meta FAIRChem models.
The protocol consists of 5 steps:
- Minimise the docked pose with a loose convergence criteria to give a local minimum.
- Generate 20 conformers from the docked ligand pose.
- Minimise the generated conformers (and the original docked pose) with a stricter convergence criteria.
- Evaluate the energy of all conformers and choose the lowest energy as an approximation of the global minimum.
- Calculate
E(ligand strain) = E(local minimum) - E(global minimum)and apply threshold.
N.B. energies returned are in kcal/mol.
StrainRelief runs are configured using hydra configs.
from strain_relief import compute_strain
computed = compute_strain(poses: list[RDKit.Mol], config: DictConfig)
for i, r in computed.iterrows():
print(f"Pose {r['id']} has a strain of {r['ligand_strain']:.2f} kcal/mol")
For a complete set of examples see the tutorial notebook.
strain-relief \
experiment=mmff94 \
io.input.parquet_path=data/example_ligboundconf_input.parquet \
io.output.parquet_path=data/example_ligboundconf_output.parquet \
conformers.numConfs=1 \More examples are given here, including the command used for the calculations in the StrainRelief paper.
Add a new Calculator class from neural-optimiser to the strain_relief/calculators/ directory. This can be as simple as implementing a wrapper around an existing ASE calculator:
from neural_optimiser.calculators.base import Calculator
class YourCalculator(Calculator):
def __init__(self, **kwargs):
self.calculator = YourASECalculator(**kwargs)
def _calculate(self, batch: Data | Batch) -> tuple[torch.Tensor, torch.Tensor]:
"""Return (energies, forces) from the calculator."""
ase_atoms = batch.to_ase()
ase_atoms.calc = self.calculator
return ase_atoms.get_potential_energy(), ase_atoms.get_gradients()Note: MACECalculator and FAIRChemCalculator from neural-optimiser use a from_atomic_data helper method. This converts ConformerBatch objects to AtomicData model inputs in a batched process; a workflow bottleneck not handled by either the MACE or FAIRChem internal ASE calculators. I would recommend implementing something similar for high throughput workflows.
Add a new config to hydra_config/calculator/your_calculator.yaml:
_target_: strain_relief.calculators.your_calculator.YourCalculator
model_paths: null
device: ${device}
any_other_kwargs: null
Common kwargs
threshold(set by default to 16.1 kcal/mol - calibrated using LigBoundConf 2.0)conformers.numConfsglobal_optimiser.steps/local_optimiser.stepsglobal_optimiser.fmax/local_optimiser.fmaxio.input.include_chargedhydra.verboseseed
Logging is set to the INFO level by default which logs only aggregate information. hydra.verbose=true can be used to activate DEBUG level logging which includes information for every molecule and conformer.
uv run pytest tests/- runs all tests (unit and integration)uv run pytest tests/ -m "not integration"- runs all unit tests
Note: Unit tests will run on a GPU if available.
If you use StrainRelief or adapt the StrainRelief code for any purpose, please cite:
@article{wallace2025strain,
title={Strain Problems Got You in a Twist? Try StrainRelief: A Quantum-Accurate Tool for Ligand Strain Calculations},
author={Wallace, Ewan RS and Frey, Nathan C and Rackers, Joshua A},
journal={Journal of Chemical Information and Modeling},
year={2025},
publisher={ACS Publications},
url={https://pubs.acs.org/doi/10.1021/acs.jcim.5c00586}
}For any questions, please reach out to Ewan Wallace: ewan.wallace@roche.com

