Source code for openff.interchange.models

"""Custom Pydantic models."""
from typing import Optional, Tuple

from openff.units import unit
from pydantic import BaseModel, Field
from typing_extensions import Literal

from openff.interchange.types import custom_quantity_encoder, json_loader


[docs]class DefaultModel(BaseModel): """A custom Pydantic model used by other components.""" class Config: """Custom Pydantic configuration.""" json_encoders = { unit.Quantity: custom_quantity_encoder, } json_loads = json_loader validate_assignment = True arbitrary_types_allowed = True
[docs]class TopologyKey(DefaultModel): """ A unique identifier of a segment of a chemical topology. These refer to a single portion of a chemical graph, i.e. a single valence term, (a bond, angle, or dihedral) or a single atom. These target only the information in the chemical graph and do not store physics parameters. For example, a TopologyKey corresponding to a bond would store the indices of the two atoms that compose the bond, but not the force constant or equilibrium bond length as determined by the force field. Examples -------- Create a TopologyKey identifying some speicfic angle .. code-block:: pycon >>> from openff.interchange.models import TopologyKey >>> this_angle = TopologyKey(atom_indices=(2, 1, 3)) >>> this_angle TopologyKey(atom_indices=(2, 1, 3), mult=None, bond_order=None) Create a TopologyKey indentifying just one atom .. code-block:: pycon >>> this_atom = TopologyKey(atom_indices=(4,)) >>> this_atom TopologyKey(atom_indices=(4,), mult=None, bond_order=None) Layer multiple TopologyKey objects that point to the same torsion .. code-block:: pycon >>> key1 = TopologyKey(atom_indices=(1, 2, 5, 6), mult=0) >>> key2 = TopologyKey(atom_indices=(1, 2, 5, 6), mult=1) >>> assert key1 != key2 """ atom_indices: Tuple[int, ...] = Field( tuple(), description="The indices of the atoms occupied by this interaction" ) mult: Optional[int] = Field( None, description="The index of this duplicate interaction" ) bond_order: Optional[float] = Field( None, description="If this key represents as topology component subject to interpolation " "between multiple parameters(s), the bond order determining the coefficients of the wrapped potentials.", ) def __hash__(self): return hash((self.atom_indices, self.mult, self.bond_order))
[docs]class VirtualSiteKey(DefaultModel): """A unique identifier of a virtual site in the scope of a chemical topology.""" atom_indices: Tuple[int, ...] = Field( tuple(), description="The indices of the atoms that anchor this virtual site" ) type: str = Field(description="The type of this virtual site") match: Literal["once", "all_permutations"] = Field( description="The `match` attribute of the associated virtual site type" ) def __hash__(self): return hash((self.atom_indices, self.type, self.match))
[docs]class PotentialKey(DefaultModel): """ A unique identifier of an instance of physical parameters as applied to a segment of a chemical topology. These refer to a single term in a force field as applied to a single segment of a chemical topology, i.e. a single atom or dihedral. For example, a PotentialKey corresponding to a bond would store the the force constant and the equilibrium bond length as determined by the force field. These keys to not have direct knowledge of where in a topology they have been applied. Examples -------- Create a PotentialKey corresponding to the parameter with id `b55` in OpenFF "Parsley" 1.0.0 .. code-block:: pycon >>> from openff.interchange.models import PotentialKey >>> from openff.toolkit.typing.engines.smirnoff import ForceField >>> parsley = ForceField("openff-1.0.0.offxml") >>> param = parsley["Bonds"].get_parameter({"id": "b55"})[0] >>> bond_55 = PotentialKey(id=param.smirks) >>> bond_55 PotentialKey(id='[#16X4,#16X3:1]-[#8X2:2]', mult=None, associated_handler=None, bond_order=None) Create a PotentialKey corresponding to the angle parameters in OPLS-AA defined between atom types opls_135, opls_135, and opls_140 .. code-block:: pycon >>> oplsaa_angle = PotentialKey(id="opls_135-opls_135-opls_140") >>> oplsaa_angle PotentialKey(id='opls_135-opls_135-opls_140', mult=None, associated_handler=None, bond_order=None) """ id: str = Field( ..., description="A unique identifier of this potential, i.e. a SMARTS pattern or an atom type", ) mult: Optional[int] = Field( None, description="The index of this duplicate interaction" ) associated_handler: Optional[str] = Field( None, description="The type of handler this potential key is associated with, " "i.e. 'Bonds', 'vdW', or 'LibraryCharges", ) bond_order: Optional[float] = Field( None, description="If this is a key to a WrappedPotential interpolating multiple parameter(s), " "the bond order determining the coefficients of the wrapped potentials.", ) def __hash__(self): return hash((self.id, self.mult, self.associated_handler, self.bond_order))