From da951a99c43fd32aa5c30761febae93c18966d44 Mon Sep 17 00:00:00 2001 From: John Chodera Date: Mon, 15 Jun 2020 18:32:55 -0700 Subject: [PATCH 1/2] Auto-detect installed SMIRNOFF force fields --- .../generators/template_generators.py | 48 +++++++++++++++++-- .../tests/test_template_generators.py | 10 +++- 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/openmmforcefields/generators/template_generators.py b/openmmforcefields/generators/template_generators.py index 4253d7a6..55ba380f 100644 --- a/openmmforcefields/generators/template_generators.py +++ b/openmmforcefields/generators/template_generators.py @@ -860,6 +860,10 @@ def _check_for_errors(self, outputtext, other_errors=None, ignore_errors=None): # Open Force Field Initiative SMIRNOFF specific OpenMM ForceField template generation utilities ################################################################################ +class ClassProperty(property): + def __get__(self, cls, owner): + return self.fget.__get__(None, owner)() + class SMIRNOFFTemplateGenerator(SmallMoleculeTemplateGenerator): """ OpenMM ForceField residue template generator for Open Force Field Initiative SMIRNOFF @@ -910,9 +914,6 @@ class SMIRNOFFTemplateGenerator(SmallMoleculeTemplateGenerator): Newly parameterized molecules will be written to the cache, saving time next time! """ - # TODO: Automatically populate this at import by examining plugin directories in order of semantic version - INSTALLED_FORCEFIELDS = ['smirnoff99Frosst-1.1.0', 'openff-1.0.0'] - def __init__(self, molecules=None, cache=None, forcefield=None): """ Create a SMIRNOFFTemplateGenerator with some openforcefield toolkit molecules @@ -1011,6 +1012,41 @@ def __init__(self, molecules=None, cache=None, forcefield=None): # Cache a copy of the OpenMM System generated for each molecule for testing purposes self._system_cache = dict() + @ClassProperty + @classmethod + def INSTALLED_FORCEFIELDS(cls): + """Return a list of the offxml files shipped with the openforcefield package. + + Returns + ------- + file_names : str + The file names of available force fields + + .. todo :: + + Replace this with an API call once this issue is addressed: + https://github.com/openforcefield/openforcefield/issues/477 + + """ + # TODO: Replace this method once there is a public API in the openforcefield toolkit for doing this + # TODO: Impose some sort of ordering by preference? + + from openforcefield.utils import get_data_file_path + from openforcefield.typing.engines.smirnoff.forcefield import _get_installed_offxml_dir_paths + from glob import glob + + file_names = list() + for dir_path in _get_installed_offxml_dir_paths(): + file_pattern = os.path.join(dir_path, '*.offxml') + file_paths = [file_path for file_path in glob(file_pattern)] + for file_path in file_paths: + basename = os.path.basename(file_path) + root, ext = os.path.splitext(basename) + # Only add variants without '_unconstrained' + if '_unconstrained' not in root: + file_names.append(root) + return file_names + def _search_paths(self, filename): """Search registered openforcefield plugin directories @@ -1023,6 +1059,12 @@ def _search_paths(self, filename): ------- fullpath : str Full path to identified file, or None if no file found + + .. todo :: + + Replace this with an API call once this issue is addressed: + https://github.com/openforcefield/openforcefield/issues/477 + """ # TODO: Replace this method once there is a public API in the openforcefield toolkit for doing this diff --git a/openmmforcefields/tests/test_template_generators.py b/openmmforcefields/tests/test_template_generators.py index 1b91685c..e22e69e5 100644 --- a/openmmforcefields/tests/test_template_generators.py +++ b/openmmforcefields/tests/test_template_generators.py @@ -702,6 +702,12 @@ def propagate_dynamics(self, molecule, system): return new_molecule + def test_INSTALLED_FORCEFIELDS(self): + """Test INSTALLED_FORCEFIELDS contains expected force fields""" + assert 'openff-1.1.0' in SMIRNOFFTemplateGenerator.INSTALLED_FORCEFIELDS + assert 'smirnoff99Frosst-1.1.0' in SMIRNOFFTemplateGenerator.INSTALLED_FORCEFIELDS + assert 'openff_unconstrained-1.1.0' not in SMIRNOFFTemplateGenerator.INSTALLED_FORCEFIELDS + def test_energies(self): """Test potential energies match between openforcefield and OpenMM ForceField""" # DEBUG @@ -742,7 +748,7 @@ def test_energies(self): def test_partial_charges_are_none(self): - """Test parameterizing a small molecule with `partial_charges=None` instead + """Test parameterizing a small molecule with `partial_charges=None` instead of zeros (happens frequently in OFFTK>=0.7.0)""" from openforcefield.topology import Molecule molecule = Molecule.from_smiles('C=O') @@ -763,7 +769,7 @@ def test_partial_charges_are_none(self): from simtk.openmm.app import NoCutoff openmm_system = openmm_forcefield.createSystem(molecule.to_topology().to_openmm(), removeCMMotion=False, onbondedMethod=NoCutoff) smirnoff_system = generator.get_openmm_system(molecule) - + def test_version(self): """Test version""" for forcefield in SMIRNOFFTemplateGenerator.INSTALLED_FORCEFIELDS: From de63cbaa342edf3c5e1be7ca95e67ba1e4f1c1bb Mon Sep 17 00:00:00 2001 From: John Chodera Date: Mon, 15 Jun 2020 18:47:28 -0700 Subject: [PATCH 2/2] Update README with changelog --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 20f31132..b31c6876 100644 --- a/README.md +++ b/README.md @@ -157,7 +157,7 @@ Newly parameterized molecules will be written to the cache, saving time next tim ## Using the Open Force Field Initiative SMIRNOFF small molecule force fields The `openmmforcefields` package includes a [residue template generator](http://docs.openmm.org/latest/userguide/application.html#adding-residue-template-generators) for [the OpenMM `ForceField` class](http://docs.openmm.org/latest/api-python/generated/simtk.openmm.app.forcefield.ForceField.html#simtk.openmm.app.forcefield.ForceField) that automatically generates OpenMM residue templates for small molecules lacking parameters using the [Open Force Field Initiative](http://openforcefield.org) [SMIRNOFF](https://open-forcefield-toolkit.readthedocs.io/en/0.6.0/smirnoff.html) small molecule force fields. -This includes the [`openff-1.0.0` ("Parsley")](https://openforcefield.org/news/introducing-openforcefield-1.0/) small molecule force field. +This includes the [`openff-1.0.0` ("Parsley")](https://openforcefield.org/news/introducing-openforcefield-1.0/) small molecule force field, as well as newer versions of this force field. The `SMIRNOFFTemplateGenerator` residue template generator operates in a manner very similar to `GAFFTemplateGenerator`, so we only highlight its differences here. @@ -269,7 +269,9 @@ See the corresponding directories for information on how to use the provided con # Changelog -## +## 0.7.3 Bugfix release: Compatibility with openforcefield toolkit 0.7.0 and auto-detection of installed openforcefield force fields +* [(PR #119)](https://github.com/openmm/openmmforcefields/pull/119) Handle `None` partial charges in openforcefield `Molecule` objects (needed in `openforcefield` toolkit 0.7.0) +* [(PR #120)](https://github.com/openmm/openmmforcefields/pull/120) Auto-detect installed SMIRNOFF force fields ## 0.7.2 Bugfix release: More error checking; OpenMM 7.4.2 minimum version requirement