Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "desire paths" to API #1192

Merged
merged 17 commits into from
Feb 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
contain the root `toctree` directive.

.. _openff-toolkit-index:

Open Force Field Toolkit
========================

Expand Down
4 changes: 3 additions & 1 deletion docs/releasehistory.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ print(value_roundtrip)
[`create_openmm_system`](openff.toolkit.typing.engines.smirnoff.ForceField.create_openmm_system)
with a default value of False. Setting it to True routes `openmm.System` creation through
Interchange.
- [PR #1192](https://github.com/openforcefield/openforcefield/pull/1192): Add re-exports for core classes to the new
`openff.toolkit.app` module and re-exports for parameter types to the new `openff.toolkit.topology.parametertypes` module.
This does not affect existing paths and gives some new, easier to remember paths to core objects.

### Behaviors changed and bugfixes

Expand All @@ -96,7 +99,6 @@ print(value_roundtrip)
- [PR #1113](https://github.com/openforcefield/openff-toolkit/pull/1113): Updates the Amber/GROMACS
example to use Interchange.


## 0.10.1 Minor feature and bugfix release

### Behaviors changed and bugfixes
Expand Down
5 changes: 0 additions & 5 deletions docs/topology.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ including offering serialization to a variety of standard formats (including [XM
FrozenMolecule
Molecule
Topology
TopologyMolecule
:::

## Secondary objects
Expand All @@ -36,14 +35,10 @@ including offering serialization to a variety of standard formats (including [XM
Bond
VirtualSite
VirtualParticle
TopologyVirtualParticle
BondChargeVirtualSite
MonovalentLonePairVirtualSite
DivalentLonePairVirtualSite
TrivalentLonePairVirtualSite
TopologyAtom
TopologyBond
TopologyVirtualSite
Comment on lines -44 to -46
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

ValenceDict
ImproperDict
:::
Expand Down
28 changes: 15 additions & 13 deletions docs/typing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,24 +49,25 @@ Parameter Type
These are usually initialized during ``ForceField`` creation, and can be inspected and modified by users via the Python API.
For more information, see ``examples/forcefield_modification``.

.. currentmodule:: openff.toolkit.typing.engines.smirnoff.parameters
.. currentmodule:: openff.toolkit.typing.engines.smirnoff.parametertypes
.. autosummary::
:nosignatures:
:toctree: api/generated/

ParameterType
BondHandler.BondType
AngleHandler.AngleType
ProperTorsionHandler.ProperTorsionType
ImproperTorsionHandler.ImproperTorsionType
vdWHandler.vdWType
LibraryChargeHandler.LibraryChargeType
GBSAHandler.GBSAType
ChargeIncrementModelHandler.ChargeIncrementType
VirtualSiteHandler.VirtualSiteBondChargeType
VirtualSiteHandler.VirtualSiteMonovalentLonePairType
VirtualSiteHandler.VirtualSiteDivalentLonePairType
VirtualSiteHandler.VirtualSiteTrivalentLonePairType
ConstraintType
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤦 Thanks for catching this!

BondType
AngleType
ProperTorsionType
ImproperTorsionType
vdWType
LibraryChargeType
GBSAType
ChargeIncrementType
VirtualSiteBondChargeType
VirtualSiteMonovalentLonePairType
VirtualSiteDivalentLonePairType
VirtualSiteTrivalentLonePairType

Parameter Handlers
~~~~~~~~~~~~~~~~~~
Expand All @@ -81,6 +82,7 @@ During ``System`` creation, each ``ParameterHandler`` registered to a ``ForceFie

ParameterList
ParameterHandler
ConstraintHandler
BondHandler
AngleHandler
ProperTorsionHandler
Expand Down
64 changes: 63 additions & 1 deletion openff/toolkit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,69 @@
A modern, extensible library for molecular mechanics force field science from the Open Force Field Consortium.
"""

import importlib

from ._version import get_versions # type: ignore

__version__ = get_versions()["version"]
del get_versions

__all__ = [
"__version__",
"Molecule",
"Topology",
"ForceField",
"get_available_force_fields",
"GLOBAL_TOOLKIT_REGISTRY",
"AmberToolsToolkitWrapper",
"BuiltInToolkitWrapper",
"OpenEyeToolkitWrapper",
"RDKitToolkitWrapper",
"ToolkitRegistry",
"topology",
"typing",
"utils",
]

# Dictionary of objects to lazily import; maps the object's name to its module path
_lazy_imports_obj = {
"ForceField": "openff.toolkit.typing.engines.smirnoff",
"get_available_force_fields": "openff.toolkit.typing.engines.smirnoff",
"Molecule": "openff.toolkit.topology",
"Topology": "openff.toolkit.topology",
"GLOBAL_TOOLKIT_REGISTRY": "openff.toolkit.utils.toolkits",
"AmberToolsToolkitWrapper": "openff.toolkit.utils.toolkits",
"BuiltInToolkitWrapper": "openff.toolkit.utils.toolkits",
"OpenEyeToolkitWrapper": "openff.toolkit.utils.toolkits",
"RDKitToolkitWrapper": "openff.toolkit.utils.toolkits",
"ToolkitRegistry": "openff.toolkit.utils.toolkits",
}

# Dictionary of modules to lazily import; maps the modules's name to its path
_lazy_imports_mod = {
"topology": "openff.toolkit.topology",
"typing": "openff.toolkit.typing",
"utils": "openff.toolkit.utils",
}


def __getattr__(name):
"""Lazily import objects from _lazy_imports_obj or _lazy_imports_mod

Note that this method is only called by Python if the name cannot be found
in the current module."""
obj_mod = _lazy_imports_obj.get(name)
if obj_mod is not None:
mod = importlib.import_module(obj_mod)
return mod.__dict__[name]

lazy_mod = _lazy_imports_mod.get(name)
if lazy_mod is not None:
return importlib.import_module(lazy_mod)

raise AttributeError(f"module {__name__!r} has no attribute {name!r}")


def __dir__():
"""Add _lazy_imports_obj and _lazy_imports_mod to dir(<module>)"""
keys = (*globals().keys(), *_lazy_imports_obj.keys(), *_lazy_imports_mod.keys())
return sorted(keys)
36 changes: 36 additions & 0 deletions openff/toolkit/tests/test_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
Test classes and function in module openff.toolkit.typing.engines.smirnoff.parameters.

"""
from inspect import isabstract, isclass

import numpy
import pytest
from numpy.testing import assert_almost_equal
from openff.units import unit

import openff.toolkit.typing.engines.smirnoff.parameters
from openff.toolkit.topology import Molecule
from openff.toolkit.typing.engines.smirnoff.parameters import (
BondHandler,
Expand Down Expand Up @@ -2257,6 +2259,40 @@ def test_gbsahandlers_are_compatible(self):
gbsa_handler_1.check_handler_compatibility(gbsa_handler_3)


class TestParameterTypeReExports:
def test_parametertype_reexports(self):
params_module = openff.toolkit.typing.engines.smirnoff.parameters

def subclass_attrs(obj, classinfo):
"""Iterate over members of ``obj`` that are concrete, public subclasses of ``classinfo``"""
return filter(
lambda nv: ( # (name, value)
isclass(nv[1])
and issubclass(nv[1], classinfo)
and not isabstract(nv[1])
and not nv[0].startswith("_")
),
vars(obj).items(),
)

for _, paramhandler in subclass_attrs(params_module, ParameterHandler):
for paramtype_name, paramtype in subclass_attrs(
paramhandler, ParameterType
):
assert paramtype_name in vars(params_module), (
f"ParameterType {paramtype_name!r} is "
f"not re-exported from parameters module"
)
assert vars(params_module)[paramtype_name] is paramtype, (
f"Exported attribute parameters.{paramtype_name} "
f"does not match ParameterType {paramtype_name!r}"
)
assert paramtype_name in params_module.__all__, (
f"ParameterType {paramtype_name!r} "
f"missing from parameters.__all__"
)


# TODO: test_nonbonded_settings (ensure that choices in Electrostatics and vdW tags resolve
# to correct openmm.NonbondedForce subtypes, that setting different cutoffs raises
# exceptions, etc)
Expand Down
3 changes: 3 additions & 0 deletions openff/toolkit/topology/topology.py
Original file line number Diff line number Diff line change
Expand Up @@ -1041,14 +1041,17 @@ def nth_degree_neighbors(self, n_degrees: int):
"""
Return canonicalized pairs of atoms whose shortest separation is `exactly` n bonds.
Only pairs with increasing atom indices are returned.

Parameters
----------
n: int
The number of bonds separating atoms in each pair

Returns
-------
neighbors: iterator of tuple of Atom
Tuples (len 2) of atom that are separated by ``n`` bonds.

Notes
-----
The criteria used here relies on minimum distances; when there are multiple valid
Expand Down
57 changes: 57 additions & 0 deletions openff/toolkit/typing/engines/smirnoff/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,20 @@
"GBSAHandler",
"ToolkitAM1BCCHandler",
"VirtualSiteHandler",
"ParameterType",
"ConstraintType",
"BondType",
"AngleType",
"ProperTorsionType",
"ImproperTorsionType",
"vdWType",
"LibraryChargeType",
"GBSAType",
"ChargeIncrementType",
"VirtualSiteBondChargeType",
"VirtualSiteMonovalentLonePairType",
"VirtualSiteDivalentLonePairType",
"VirtualSiteTrivalentLonePairType",
]
import abc
import copy
Expand Down Expand Up @@ -5261,6 +5275,30 @@ def _add_virtual_site(self, fn, atoms, orientations, *args, **kwargs):

return fn(*args, **kwargs)

@abc.abstractmethod
def add_virtual_site(self, molecule, orientations, replace=False):
"""
Add a virtual site to the molecule

Parameters
----------
molecule : openff.toolkit.topology.molecule.Molecule
The molecule to add the virtual site to
orientations : List[Tuple[int]]
A list of orientation tuples which define the permuations used
to contruct the geometry of the virtual site particles
replace : bool, default=False
Replace this virtual site if it already exists in the molecule

Returns
-------
off_idx : int
The index of the first particle added due to this virtual site

.. warning :: This API is experimental and subject to change.
"""
raise NotImplementedError

class VirtualSiteBondChargeType(VirtualSiteType):
"""A SMIRNOFF virtual site bond charge type

Expand Down Expand Up @@ -5921,6 +5959,25 @@ def _create_openmm_virtual_particle(
return ids


# ======================================================================
# PARAMETERTYPE RE-EXPORTS
# ======================================================================

ConstraintType = ConstraintHandler.ConstraintType
BondType = BondHandler.BondType
AngleType = AngleHandler.AngleType
ProperTorsionType = ProperTorsionHandler.ProperTorsionType
ImproperTorsionType = ImproperTorsionHandler.ImproperTorsionType
vdWType = vdWHandler.vdWType
LibraryChargeType = LibraryChargeHandler.LibraryChargeType
GBSAType = GBSAHandler.GBSAType
ChargeIncrementType = ChargeIncrementModelHandler.ChargeIncrementType
VirtualSiteBondChargeType = VirtualSiteHandler.VirtualSiteBondChargeType
VirtualSiteMonovalentLonePairType = VirtualSiteHandler.VirtualSiteMonovalentLonePairType
VirtualSiteDivalentLonePairType = VirtualSiteHandler.VirtualSiteDivalentLonePairType
VirtualSiteTrivalentLonePairType = VirtualSiteHandler.VirtualSiteTrivalentLonePairType


if __name__ == "__main__":
import doctest

Expand Down
7 changes: 7 additions & 0 deletions openff/toolkit/utils/rdkit_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -1756,17 +1756,24 @@ def to_rdkit(self, molecule, aromaticity_model=DEFAULT_AROMATICITY_MODEL):
"""
Create an RDKit molecule
Requires the RDKit to be installed.

.. warning :: This API is experimental and subject to change.

Parameters
----------

aromaticity_model : str, optional, default=DEFAULT_AROMATICITY_MODEL
The aromaticity model to use

Returns
-------

rdmol : rkit.RDMol
An RDKit molecule

Examples
--------

Convert a molecule to RDKit
>>> from openff.toolkit.topology import Molecule
>>> ethanol = Molecule.from_smiles('CCO')
Expand Down