Repository: openmm/openmmforcefields

Issue:
Openforcefields versions
This table will be updated as a part of the openforcefields release process, per https://github.com/openforcefield/openforcefields/wiki/Release-Process

Close this issue once https://github.com/openforcefield/openforcefield/issues/477 is implemented
| Filename | DOI | FF line | Release Date | Change in functional form? |
| -------- | --- | -------- | --- | --- |
| `openff-2.0.0.offxml` |  | Sage | Late 2020 or Early 2021 (Anticipated) | Likely |
| `openff_unconstrained-2.0.0.offxml` |  | Sage | Late 2020 or Early 2021 (Anticipated) | Likely |
| `openff-1.2.0.offxml` | [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.3872244.svg)](https://doi.org/10.5281/zenodo.3872244) | Parsley | May 29, 2020 | No |
| `openff_unconstrained-1.2.0.offxml` | [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.3872244.svg)](https://doi.org/10.5281/zenodo.3872244) | Parsley | May 29, 2020 | No |
| `openff-1.1.1.offxml` | [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.3751818.svg)](https://doi.org/10.5281/zenodo.3751818) | Parsley | Apr 14, 2020 | No |
| `openff_unconstrained-1.1.1.offxml` | [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.3751818.svg)](https://doi.org/10.5281/zenodo.3751818) | Parsley | Apr 14, 2020 | No |
| `openff-1.1.0.offxml` | [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.3695094.svg)](https://doi.org/10.5281/zenodo.3695094) | Parsley | Mar 3, 2020 | No |
| `openff_unconstrained-1.1.0.offxml` | [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.3695094.svg)](https://doi.org/10.5281/zenodo.3695094) | Parsley | Mar 3, 2020 | No |
| `openff-1.0.1.offxml` | [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.3751812.svg)](https://doi.org/10.5281/zenodo.3751812) | Parsley | Apr 14, 2020 | No |
| `openff_unconstrained-1.0.1.offxml` | [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.3751812.svg)](https://doi.org/10.5281/zenodo.3751812) | Parsley | Apr 14, 2020 | No |
| `openff-1.0.0.offxml` | [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.3483227.svg)](https://doi.org/10.5281/zenodo.3483227) | Parsley | Oct 12, 2019 | No |
| `openff_unconstrained-1.0.0.offxml` | [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.3483227.svg)](https://doi.org/10.5281/zenodo.3483227) | Parsley | Oct 12, 2019 | No |
| `openff-1.0.0-RC2.offxml` | [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.3473554.svg)](https://doi.org/10.5281/zenodo.3473554) | None | Oct 4, 2019 | No |
| `openff_unconstrained-1.0.0-RC2.offxml` | [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.3473554.svg)](https://doi.org/10.5281/zenodo.3473554) | None | Oct 4, 2019 | No |
| `openff-1.0.0-RC1.offxml` | None | None | Oct 4, 2019 | N/A |
| `openff_unconstrained-1.0.0-RC1.offxml` | None | None | Oct 4, 2019 | N/A |


Discussion:

Patch:
diff --git a/README.md b/README.md
index 20f3113..b31c687 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
 
diff --git a/openmmforcefields/generators/template_generators.py b/openmmforcefields/generators/template_generators.py
index 4253d7a..55ba380 100644
--- a/openmmforcefields/generators/template_generators.py
+++ b/openmmforcefields/generators/template_generators.py
@@ -860,6 +860,10 @@ class GAFFTemplateGenerator(SmallMoleculeTemplateGenerator):
 # 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 @@ class SMIRNOFFTemplateGenerator(SmallMoleculeTemplateGenerator):
         # 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 @@ class SMIRNOFFTemplateGenerator(SmallMoleculeTemplateGenerator):
         -------
         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
 


Test patch:
diff --git a/openmmforcefields/tests/test_template_generators.py b/openmmforcefields/tests/test_template_generators.py
index 1b91685..e22e69e 100644
--- a/openmmforcefields/tests/test_template_generators.py
+++ b/openmmforcefields/tests/test_template_generators.py
@@ -702,6 +702,12 @@ class TestSMIRNOFFTemplateGenerator(TestGAFFTemplateGenerator):
 
         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 @@ class TestSMIRNOFFTemplateGenerator(TestGAFFTemplateGenerator):
 
 
     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 @@ class TestSMIRNOFFTemplateGenerator(TestGAFFTemplateGenerator):
             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: