Skip to content

mdtop #

Store topological information about a system to simulate.

Modules:

  • tests

Classes:

  • Atom

    Represents atoms and virtual sites stored in a topology.

  • Bond

    Represents a bond between two atoms.

  • Chain

    Represents chains stored in a topology.

  • Residue

    Represents residues stored in a topology.

  • Topology

    Represents topological information about a system.

Functions:

Attributes:

AMINO_ACID_NAMES module-attribute #

AMINO_ACID_NAMES: frozenset[str] = frozenset(
    [
        "ACE",
        "NME",
        "NMA",
        "ALA",
        "CYS",
        "ASP",
        "GLU",
        "PHE",
        "GLY",
        "HIS",
        "ILE",
        "LYS",
        "LEU",
        "MET",
        "ASN",
        "PRO",
        "GLN",
        "ARG",
        "SER",
        "THR",
        "VAL",
        "TRP",
        "TYR",
        "CYD",
        "CYZ",
        "HID",
        "HIE",
        "HIP",
    ]
)

Common natural amino acid residue names.

ION_RES_NAMES module-attribute #

ION_RES_NAMES: frozenset[str] = frozenset(
    [
        "NA+",
        "NA",
        "K+",
        "K",
        "LI+",
        "LI",
        "CL-",
        "CL",
        "BR-",
        "BR",
        "I-",
        "I",
        "F-",
        "F",
        "MG+2",
        "MG",
        "CA+2",
        "CA",
        "ZN+2",
        "ZN",
        "FE+3",
        "FE+2",
        "FE",
    ]
)

Residue names for common ions.

WATER_RES_NAMES module-attribute #

WATER_RES_NAMES: frozenset[str] = frozenset(
    ["HOH", "WAT", "TIP", "TIP2", "TIP3", "TIP4"]
)

Common water residue names.

Atom #

Atom(
    name: str,
    atomic_num: int,
    formal_charge: int | None,
    serial: int,
)

Represents atoms and virtual sites stored in a topology.

Attributes:

  • name (str) –

    The name of the atom.

  • atomic_num (int) –

    The atomic number, or 0 if this is a virtual site.

  • formal_charge (int | None) –

    The formal charge on the atom.

  • serial (int) –

    The index of this atom in its original source (e.g. the serial defined

  • meta (dict[str, str | float | int | bool]) –

    Extra metadata associated with the atom.

  • symbol (str) –

    The chemical symbol of the atom, or 'X' if this is a virtual site.

  • residue (Optional[Residue]) –

    The residue that the atom belongs to.

  • chain (Optional[Chain]) –

    The chain that the atom belongs to.

  • index (int | None) –

    The index of the atom in the parent topology

Source code in mdtop/_top.py
def __init__(
    self, name: str, atomic_num: int, formal_charge: int | None, serial: int
):
    self.name: str = name
    """The name of the atom."""

    self.atomic_num: int = atomic_num
    """The atomic number, or 0 if this is a virtual site."""
    self.formal_charge: int | None = formal_charge
    """The formal charge on the atom."""

    self.serial: int = serial
    """The index of this atom in its original source (e.g. the serial defined
    in a PDB). This may not be zero-index or contiguous."""

    self.meta: dict[str, str | float | int | bool] = {}
    """Extra metadata associated with the atom."""

    self._residue: typing.Optional["Residue"] = None
    self._index: int | None = None

name instance-attribute #

name: str = name

The name of the atom.

atomic_num instance-attribute #

atomic_num: int = atomic_num

The atomic number, or 0 if this is a virtual site.

formal_charge instance-attribute #

formal_charge: int | None = formal_charge

The formal charge on the atom.

serial instance-attribute #

serial: int = serial

The index of this atom in its original source (e.g. the serial defined in a PDB). This may not be zero-index or contiguous.

meta instance-attribute #

meta: dict[str, str | float | int | bool] = {}

Extra metadata associated with the atom.

symbol property #

symbol: str

The chemical symbol of the atom, or 'X' if this is a virtual site.

residue property #

residue: Optional[Residue]

The residue that the atom belongs to.

chain property #

chain: Optional[Chain]

The chain that the atom belongs to.

index property #

index: int | None

The index of the atom in the parent topology

Bond #

Bond(idx_1: int, idx_2: int, order: int | None)

Represents a bond between two atoms.

Attributes:

  • order

    The formal bond order

  • meta (dict[str, str | float | int | bool]) –

    Extra metadata associated with the bond.

  • idx_1 (int) –

    The index of the first atom.

  • idx_2 (int) –

    The index of the second atom.

Source code in mdtop/_top.py
def __init__(self, idx_1: int, idx_2: int, order: int | None):
    self._idx_1 = idx_1
    self._idx_2 = idx_2
    self.order = order
    """The formal bond order"""

    self.meta: dict[str, str | float | int | bool] = {}
    """Extra metadata associated with the bond."""

order instance-attribute #

order = order

The formal bond order

meta instance-attribute #

meta: dict[str, str | float | int | bool] = {}

Extra metadata associated with the bond.

idx_1 property #

idx_1: int

The index of the first atom.

idx_2 property #

idx_2: int

The index of the second atom.

Chain #

Chain(id_: str)

Represents chains stored in a topology.

Attributes:

  • id

    The ID of the chain.

  • topology (Optional[Topology]) –

    The topology the chain belongs to (if any).

  • residues (tuple[Residue, ...]) –

    The residues associated with the chain.

  • n_residues (int) –

    The number of chains in the chain.

  • atoms (tuple[Atom, ...]) –

    The atoms associated with the chain.

  • n_atoms (int) –

    The number of atoms in the chain.

  • index (int | None) –

    The index of the chain in the parent topology

Source code in mdtop/_top.py
def __init__(self, id_: str):
    self.id = id_
    """The ID of the chain."""

    self._topology: typing.Optional["Topology"] | None = None
    self._residues: list[Residue] = []

    self._index: int | None = None

id instance-attribute #

id = id_

The ID of the chain.

topology property #

topology: Optional[Topology]

The topology the chain belongs to (if any).

residues property #

residues: tuple[Residue, ...]

The residues associated with the chain.

n_residues property #

n_residues: int

The number of chains in the chain.

atoms property #

atoms: tuple[Atom, ...]

The atoms associated with the chain.

n_atoms property #

n_atoms: int

The number of atoms in the chain.

index property #

index: int | None

The index of the chain in the parent topology

Residue #

Residue(name: str, seq_num: int, insertion_code: str = '')

Represents residues stored in a topology.

Attributes:

  • name

    The name of the residue.

  • seq_num

    The sequence number of the residue.

  • insertion_code

    The insertion code of the residue.

  • chain (Optional[Chain]) –

    The chain the residue belongs to (if any).

  • topology (Optional[Topology]) –

    The topology the residue belongs to (if any).

  • atoms (tuple[Atom, ...]) –

    The atoms associated with the residue.

  • n_atoms (int) –

    The number of atoms in the residue.

  • index (int | None) –

    The index of the residue in the parent topology

Source code in mdtop/_top.py
def __init__(self, name: str, seq_num: int, insertion_code: str = ""):
    self.name = name
    """The name of the residue."""
    self.seq_num = seq_num
    """The sequence number of the residue."""
    self.insertion_code = insertion_code
    """The insertion code of the residue."""

    self._chain: typing.Optional["Chain"] = None
    self._atoms: list[Atom] = []

    self._index: int | None = None

name instance-attribute #

name = name

The name of the residue.

seq_num instance-attribute #

seq_num = seq_num

The sequence number of the residue.

insertion_code instance-attribute #

insertion_code = insertion_code

The insertion code of the residue.

chain property #

chain: Optional[Chain]

The chain the residue belongs to (if any).

topology property #

topology: Optional[Topology]

The topology the residue belongs to (if any).

atoms property #

atoms: tuple[Atom, ...]

The atoms associated with the residue.

n_atoms property #

n_atoms: int

The number of atoms in the residue.

index property #

index: int | None

The index of the residue in the parent topology

Topology #

Topology()

Represents topological information about a system.

Methods:

  • add_chain

    Add a new chain to the topology.

  • add_residue

    Add a new residue to the topology.

  • add_atom

    Add a new atom to the topology.

  • add_bond

    Add a new bond to the topology.

  • from_openmm

    Create a topology from an OpenMM topology.

  • to_openmm

    Convert the topology to an OpenMM topology.

  • from_rdkit

    Create a topology from an RDKit molecule.

  • to_rdkit

    Convert the Topology to an RDKit Mol object.

  • from_openeye

    Create a topology from an OpenEye molecule.

  • to_openeye

    Convert the Topology to an OpenEye molecule object.

  • from_file

    Load the topology from a file.

  • to_file

    Write the topology to a file.

  • select

    Select atoms from the topology using a selection expression.

  • subset

    Create a subset of the topology.

  • split

    Split the topology into multiple topologies, such that bound atoms are

  • merge

    Merge multiple topologies.

Attributes:

  • meta (dict[str, str | float | int | bool]) –

    Extra metadata associated with the atom.

  • chains (tuple[Chain, ...]) –

    The chains associated with the topology.

  • n_chains (int) –

    The number of chains in the topology.

  • residues (tuple[Residue, ...]) –

    The residues associated with the topology.

  • n_residues (int) –

    The number of residues in the topology.

  • atoms (tuple[Atom, ...]) –

    The atoms associated with the topology.

  • n_atoms (int) –

    The number of atoms in the topology.

  • bonds (tuple[Bond, ...]) –

    The bonds associated with the topology.

  • n_bonds (int) –

    The number of bonds in the topology.

  • xyz (Quantity | None) –

    The coordinates of the atoms in the topology.

  • box (Quantity | None) –

    The box vectors of the simulation box.

Source code in mdtop/_top.py
def __init__(self):
    self._chains: list[Chain] = []
    self._bonds: list[Bond] = []

    self._n_atoms: int = 0
    self._n_residues: int = 0

    self._xyz: openmm.unit.Quantity | None = None
    self._box: openmm.unit.Quantity | None = None

    self.meta: dict[str, str | float | int | bool] = {}
    """Extra metadata associated with the atom."""

meta instance-attribute #

meta: dict[str, str | float | int | bool] = {}

Extra metadata associated with the atom.

chains property #

chains: tuple[Chain, ...]

The chains associated with the topology.

n_chains property #

n_chains: int

The number of chains in the topology.

residues property #

residues: tuple[Residue, ...]

The residues associated with the topology.

n_residues property #

n_residues: int

The number of residues in the topology.

atoms property #

atoms: tuple[Atom, ...]

The atoms associated with the topology.

n_atoms property #

n_atoms: int

The number of atoms in the topology.

bonds property #

bonds: tuple[Bond, ...]

The bonds associated with the topology.

n_bonds property #

n_bonds: int

The number of bonds in the topology.

xyz property writable #

xyz: Quantity | None

The coordinates of the atoms in the topology.

box property writable #

box: Quantity | None

The box vectors of the simulation box.

add_chain #

add_chain(id_: str) -> Chain

Add a new chain to the topology.

Parameters:

  • id_ (str) –

    The ID of the chain to add.

Returns:

  • Chain

    The newly created chain.

Source code in mdtop/_top.py
def add_chain(self, id_: str) -> Chain:
    """Add a new chain to the topology.

    Args:
        id_: The ID of the chain to add.

    Returns:
         The newly created chain.
    """
    chain = Chain(id_=id_)
    chain._topology = self
    chain._index = self.n_chains

    self._chains.append(chain)

    self._n_atoms += chain.n_atoms
    self._n_residues += chain.n_residues

    return chain

add_residue #

add_residue(
    name: str,
    seq_num: int | None,
    insertion_code: str,
    chain: Chain,
) -> Residue

Add a new residue to the topology.

Parameters:

  • name (str) –

    The name of the residue to add

  • seq_num (int | None) –

    The sequence number of the residue. If None, the index in the topology will be used.

  • insertion_code (str) –

    The insertion code of the residue.

  • chain (Chain) –

    The parent chain to add to.

Returns:

  • Residue

    The newly created residue.

Source code in mdtop/_top.py
def add_residue(
    self, name: str, seq_num: int | None, insertion_code: str, chain: Chain
) -> Residue:
    """Add a new residue to the topology.

    Args:
        name: The name of the residue to add
        seq_num: The sequence number of the residue. If ``None``, the index in the
            topology will be used.
        insertion_code: The insertion code of the residue.
        chain: The parent chain to add to.

    Returns:
         The newly created residue.
    """

    if chain.topology != self:
        raise ValueError(f"{chain} does not belong to this topology.")

    seq_num = int(self.n_residues if seq_num is None else seq_num)

    residue = Residue(name=name, seq_num=seq_num, insertion_code=insertion_code)
    residue._chain = chain
    residue._index = self.n_residues

    chain._residues.append(residue)

    self._n_atoms += residue.n_atoms
    self._n_residues += 1

    return residue

add_atom #

add_atom(
    name: str,
    atomic_num: int,
    formal_charge: int | None,
    serial: int | None,
    residue: Residue,
) -> Atom

Add a new atom to the topology.

Parameters:

  • name (str) –

    The name of the atom to add

  • atomic_num (int) –

    The atomic number of the atom to add, or 0 for virtual sites.

  • formal_charge (int | None) –

    The formal charge on the atom (if defined).

  • serial (int | None) –

    The index of this atom in its original source (e.g. the serial defined in a PDB), which may not be zero-index or contiguous. If None, the index in the topology will be used.

  • residue (Residue) –

    The parent residue to add to.

Returns:

  • Atom

    The newly created atom

Source code in mdtop/_top.py
def add_atom(
    self,
    name: str,
    atomic_num: int,
    formal_charge: int | None,
    serial: int | None,
    residue: Residue,
) -> Atom:
    """Add a new atom to the topology.

    Args:
        name: The name of the atom to add
        atomic_num: The atomic number of the atom to add, or 0 for virtual sites.
        formal_charge: The formal charge on the atom (if defined).
        serial: The index of this atom in its original source (e.g. the serial
            defined in a PDB), which may not be zero-index or contiguous. If
            ``None``, the index in the topology will be used.
        residue: The parent residue to add to.

    Returns:
        The newly created atom
    """
    if residue.topology != self:
        raise ValueError(f"{residue} does not belong to this topology.")

    serial = int(self.n_atoms if serial is None else serial)

    atom = Atom(
        name=name, atomic_num=atomic_num, formal_charge=formal_charge, serial=serial
    )
    atom._residue = residue
    atom._index = self.n_atoms

    residue._atoms.append(atom)

    self._n_atoms += 1

    return atom

add_bond #

add_bond(idx_1: int, idx_2: int, order: int | None) -> Bond

Add a new bond to the topology.

Parameters:

  • idx_1 (int) –

    The index of the first atom.

  • idx_2 (int) –

    The index of the second atom.

  • order (int | None) –

    The formal bond order (if defined).

Returns:

  • Bond

    The newly created bond.

Source code in mdtop/_top.py
def add_bond(self, idx_1: int, idx_2: int, order: int | None) -> Bond:
    """Add a new bond to the topology.

    Args:
        idx_1: The index of the first atom.
        idx_2: The index of the second atom.
        order: The formal bond order (if defined).

    Returns:
        The newly created bond.
    """

    if idx_1 >= self.n_atoms:
        raise ValueError("Index 1 is out of range.")
    if idx_2 >= self.n_atoms:
        raise ValueError("Index 2 is out of range.")

    bond = Bond(idx_1=idx_1, idx_2=idx_2, order=order)
    self._bonds.append(bond)

    return bond

from_openmm classmethod #

from_openmm(topology_omm: Topology) -> Topology

Create a topology from an OpenMM topology.

Parameters:

  • topology_omm (Topology) –

    The OpenMM topology to convert.

Returns:

Source code in mdtop/_top.py
@classmethod
def from_openmm(cls, topology_omm: openmm.app.Topology) -> "Topology":
    """Create a topology from an OpenMM topology.

    Args:
        topology_omm: The OpenMM topology to convert.

    Returns:
        The converted topology.
    """
    from mdtop._io.openmm import from_openmm

    return from_openmm(topology_omm)

to_openmm #

to_openmm() -> Topology

Convert the topology to an OpenMM topology.

Returns:

  • Topology

    The OpenMM topology.

Source code in mdtop/_top.py
def to_openmm(self) -> openmm.app.Topology:
    """Convert the topology to an OpenMM topology.

    Returns:
        The OpenMM topology.
    """
    from mdtop._io.openmm import to_openmm

    return to_openmm(self)

from_rdkit classmethod #

from_rdkit(
    mol: Mol, residue_name: str = "LIG", chain: str = ""
) -> Topology

Create a topology from an RDKit molecule.

Parameters:

  • mol (Mol) –

    The RDKit molecule to convert.

  • residue_name (str, default: 'LIG' ) –

    The residue name to use for the ligand.

  • chain (str, default: '' ) –

    The chain ID to use for the ligand.

Returns:

Source code in mdtop/_top.py
@classmethod
def from_rdkit(
    cls, mol: "Chem.Mol", residue_name: str = "LIG", chain: str = ""
) -> "Topology":
    """Create a topology from an RDKit molecule.

    Args:
        mol: The RDKit molecule to convert.
        residue_name: The residue name to use for the ligand.
        chain: The chain ID to use for the ligand.

    Returns:
        The converted topology.
    """
    from mdtop._io.rdkit import from_rdkit

    return from_rdkit(mol, residue_name, chain)

to_rdkit #

to_rdkit() -> Mol

Convert the Topology to an RDKit Mol object.

Notes
  • Currently this requires formal charges to be set on the atoms, and formal bond orders to be set on the bonds.

Returns:

  • Mol

    The RDKit Mol object.

Source code in mdtop/_top.py
def to_rdkit(self) -> "Chem.Mol":
    """Convert the Topology to an RDKit Mol object.

    Notes:
        * Currently this requires formal charges to be set on the atoms, and
          formal bond orders to be set on the bonds.

    Returns:
        The RDKit Mol object.
    """
    from mdtop._io.rdkit import to_rdkit

    return to_rdkit(self)

from_openeye classmethod #

from_openeye(mol: OEMol) -> Topology

Create a topology from an OpenEye molecule.

Parameters:

  • mol (OEMol) –

    The RDKit molecule to convert.

Returns:

Source code in mdtop/_top.py
@classmethod
def from_openeye(cls, mol: "oechem.OEMol") -> "Topology":  # pragma: no cover
    """Create a topology from an OpenEye molecule.

    Args:
        mol: The RDKit molecule to convert.

    Returns:
        The converted topology.
    """

    from mdtop._io.openeye import from_openeye

    return from_openeye(mol)

to_openeye #

to_openeye() -> OEMol

Convert the Topology to an OpenEye molecule object.

Notes
  • Currently this requires formal charges to be set on the atoms, and formal bond orders to be set on the bonds.

Returns:

  • OEMol

    The OpenEye molecule.

Source code in mdtop/_top.py
def to_openeye(self) -> "oechem.OEMol":  # pragma: no cover
    """Convert the Topology to an OpenEye molecule object.

    Notes:
        * Currently this requires formal charges to be set on the atoms, and
          formal bond orders to be set on the bonds.

    Returns:
        The OpenEye molecule.
    """
    from mdtop._io.openeye import to_openeye

    return to_openeye(self)

_from_pdb classmethod #

_from_pdb(path: Path) -> Topology

Load the topology from a PDB file.

Source code in mdtop/_top.py
@classmethod
def _from_pdb(cls, path: pathlib.Path) -> "Topology":
    """Load the topology from a PDB file."""
    pdb = openmm.app.PDBFile(str(path))

    topology = cls.from_openmm(pdb.topology)

    xyz = pdb.positions.value_in_unit(openmm.unit.angstrom)
    topology.xyz = numpy.array(xyz) * openmm.unit.angstrom

    return topology

from_file classmethod #

from_file(path: Path | str) -> Topology

Load the topology from a file.

Notes
  • Currently PDB, SDF, and MOL2 files are supported.

Parameters:

  • path (Path | str) –

    The path to the file to load.

Returns:

Source code in mdtop/_top.py
@classmethod
def from_file(cls, path: pathlib.Path | str) -> "Topology":
    """Load the topology from a file.

    Notes:
        * Currently PDB, SDF, and MOL2 files are supported.

    Args:
        path: The path to the file to load.

    Returns:
        The loaded topology.
    """
    path = pathlib.Path(path)

    if path.suffix.lower() == ".pdb":
        return cls._from_pdb(path)
    elif path.suffix.lower() in {".mol", ".sdf"}:
        from rdkit import Chem

        mol = Chem.MolFromMolFile(str(path), removeHs=False)
        return cls.from_rdkit(mol)
    elif path.suffix.lower() == ".mol2":
        from rdkit import Chem

        mol = Chem.MolFromMol2File(str(path), removeHs=False)
        return cls.from_rdkit(mol)

    raise NotImplementedError(f"{path.suffix} files are not supported.")

to_file #

to_file(path: Path | str)

Write the topology to a file.

Notes
  • Currently PDB, MOL, and SDF files are supported.
  • SDF / MOL writing requires that all atoms have formal charges set, and all bonds have formal bond orders set, as reading and writing is via RDKit.
  • Not all metadata will be preserved when writing to files, including residue and chain information.

Parameters:

  • path (Path | str) –

    The path to write the topology to.

Source code in mdtop/_top.py
def to_file(self, path: pathlib.Path | str):
    """Write the topology to a file.

    Notes:
        * Currently PDB, MOL, and SDF files are supported.
        * SDF / MOL writing requires that all atoms have formal charges set, and
          all bonds have formal bond orders set, as reading and writing is via
          RDKit.
        * Not all metadata will be preserved when writing to files, including
          residue and chain information.

    Args:
        path: The path to write the topology to.
    """
    path = pathlib.Path(path)

    if path.suffix.lower() == ".pdb":
        xyz = self.xyz if self.xyz is not None else numpy.zeros((self.n_atoms, 3))
        openmm.app.PDBFile.writeFile(self.to_openmm(), xyz, str(path))
        return
    elif path.suffix.lower() in {".mol", ".sdf"}:
        from rdkit import Chem

        with Chem.SDWriter(str(path)) as writer:
            writer.write(self.to_rdkit())
        return

    raise NotImplementedError(f"{path.suffix} files are not supported.")

select #

select(expr: str) -> ndarray

Select atoms from the topology using a selection expression.

The selection expression should be expressed in terms of the PyMol selection language. For example, to select all atoms in chain A:

selection = topology.select("chain A")

or all atoms within 5 Å of the ligand:

selection = topology.select("all within 5 of resn LIG")
Notes

An Amber-style selection mask can also be used, but this is deprecated and will be removed in a future version.

Parameters:

  • expr (str) –

    The selection expression.

Source code in mdtop/_top.py
def select(self, expr: str) -> numpy.ndarray:
    """Select atoms from the topology using a selection expression.

    The selection expression should be expressed in terms of the PyMol
    selection language. For example, to select all atoms in chain A:

    ```python
    selection = topology.select("chain A")
    ```

    or all atoms within 5 Å of the ligand:

    ```python
    selection = topology.select("all within 5 of resn LIG")
    ```

    Notes:
        An Amber-style selection mask can also be used, but this is deprecated
        and will be removed in a future version.

    Args:
        expr: The selection expression.
    """
    idxs = self._select_amber(expr)

    if idxs is not None:
        return idxs

    return select(self, expr)

subset #

subset(idxs: Iterable[int]) -> Topology

Create a subset of the topology.

Parameters:

  • idxs (Iterable[int]) –

    The indices of the atoms to include in the subset. Note the order of the atoms in the subset will be the same as in the original topology, regardless of the order of the indices.

Returns:

  • Topology

    The subset of the topology.

Source code in mdtop/_top.py
def subset(self, idxs: typing.Iterable[int]) -> "Topology":
    """Create a subset of the topology.

    Args:
        idxs: The indices of the atoms to include in the subset. Note the order of
            the atoms in the subset will be the same as in the original topology,
            regardless of the order of the indices.

    Returns:
        The subset of the topology.
    """
    idxs = numpy.array(idxs)
    idxs_unique = set(idxs)

    if len(idxs_unique) != len(idxs):
        raise ValueError("Indices are not unique.")

    subset = Topology()

    idx_old_to_new = {}

    for chain in self.chains:
        has_chain = any(
            atom.index in idxs_unique
            for residue in chain.residues
            for atom in residue.atoms
        )

        if not has_chain:
            continue

        chain_new = subset.add_chain(chain.id)

        for residue in chain.residues:
            has_residue = any(atom.index in idxs_unique for atom in residue.atoms)

            if not has_residue:
                continue

            residue_new = subset.add_residue(
                residue.name, residue.seq_num, "", chain_new
            )

            for atom in residue.atoms:
                if atom.index not in idxs_unique:
                    continue

                atom_new = subset.add_atom(
                    atom.name,
                    atom.atomic_num,
                    atom.formal_charge,
                    atom.serial,
                    residue_new,
                )
                idx_old_to_new[atom.index] = atom_new.index

    for bond in self.bonds:
        if bond.idx_1 not in idx_old_to_new or bond.idx_2 not in idx_old_to_new:
            continue

        subset.add_bond(
            idx_old_to_new[bond.idx_1], idx_old_to_new[bond.idx_2], bond.order
        )

    subset.box = self.box
    subset.xyz = None if self.xyz is None else self.xyz[idxs]

    return subset

split #

split() -> list[Topology]

Split the topology into multiple topologies, such that bound atoms are in the same topology.

This is useful, for example, when your topology contains multiple ligands, and you want to split them into separate topologies.

Returns:

  • list[Topology]

    An list of the split topologies.

Source code in mdtop/_top.py
def split(self) -> list["Topology"]:
    """Split the topology into multiple topologies, such that bound atoms are
    in the same topology.

    This is useful, for example, when your topology contains multiple ligands,
    and you want to split them into separate topologies.

    Returns:
        An list of the split topologies.
    """

    open_list = set(range(self.n_atoms))
    closed_list = set()

    neighs = {idx: set() for idx in range(self.n_atoms)}

    for bond in self.bonds:
        neighs[bond.idx_1].add(bond.idx_2)
        neighs[bond.idx_2].add(bond.idx_1)

    frags = []

    while len(open_list) > 0:
        idx = open_list.pop()

        queue = collections.deque([idx])
        frag = []

        closed_list.add(idx)

        while len(queue) > 0:
            idx = queue.popleft()
            frag.append(idx)

            for neigh in neighs[idx]:
                if neigh in closed_list:
                    continue

                queue.append(neigh)
                closed_list.add(neigh)

        frags.append(frag)
        open_list -= set(frag)

    return [self.subset(frag) for frag in frags]

merge classmethod #

merge(*topologies: Topology) -> Topology

Merge multiple topologies.

Notes
  • The box vectors of the first topology will be used.
  • Topologies without coordinates will be treated as if they have all zero coordinates.

Parameters:

  • topologies (Topology, default: () ) –

    The topologies to merge together.

Returns:

Source code in mdtop/_top.py
@classmethod
def merge(cls, *topologies: "Topology") -> "Topology":
    """Merge multiple topologies.

    Notes:
        * The box vectors of the first topology will be used.
        * Topologies without coordinates will be treated as if they have all zero
          coordinates.

    Args:
        topologies: The topologies to merge together.

    Returns:
        The merged topology.
    """

    if len(topologies) == 0:
        return cls()

    merged = copy.deepcopy(topologies[0])

    for topology in topologies[1:]:
        merged += topology

    return merged

box_from_geometry #

box_from_geometry(
    a: float,
    b: float,
    c: float,
    alpha: float,
    beta: float,
    gamma: float,
) -> Quantity

Convert box geometry parameters to a box.

Parameters:

  • a (float) –

    The length [Å] of the first box vector.

  • b (float) –

    The length [Å] of the second box vector.

  • c (float) –

    The length [Å] of the third box vector.

  • alpha (float) –

    The angle [deg] between the second and third box vectors.

  • beta (float) –

    The angle [deg] between the third and first box vectors.

  • gamma (float) –

    The angle [deg] between the first and second box vectors.

Returns:

  • Quantity

    The box vectors with shape=(3, 3).

Source code in mdtop/_utils.py
def box_from_geometry(
    a: float, b: float, c: float, alpha: float, beta: float, gamma: float
) -> openmm.unit.Quantity:
    """Convert box geometry parameters to a box.

    Args:
        a: The length [Å] of the first box vector.
        b: The length [Å] of the second box vector.
        c: The length [Å] of the third box vector.
        alpha: The angle [deg] between the second and third box vectors.
        beta: The angle [deg] between the third and first box vectors.
        gamma: The angle [deg] between the first and second box vectors.

    Returns:
        The box vectors with ``shape=(3, 3)``.
    """

    alpha, beta, gamma = numpy.deg2rad([alpha, beta, gamma])

    cx = c * numpy.cos(beta)
    cy = c * (numpy.cos(alpha) - numpy.cos(beta) * numpy.cos(gamma)) / numpy.sin(gamma)
    cz = numpy.sqrt(c**2 - cx**2 - cy**2)

    a_vec = numpy.array([a, 0, 0])
    b_vec = numpy.array([b * numpy.cos(gamma), b * numpy.sin(gamma), 0])
    c_vec = numpy.array([cx, cy, cz])

    return numpy.array([a_vec, b_vec, c_vec]) * openmm.unit.angstrom

box_to_geometry #

box_to_geometry(
    box: Quantity,
) -> tuple[float, float, float, float, float, float]

Convert a box to its geometry parameters.

Parameters:

  • box (Quantity) –

    The box vectors with shape=(3, 3).

Returns:

  • tuple[float, float, float, float, float, float]

    The box lengths and angles in the order (a, b, c, alpha, beta, gamma), where all lengths are in angstroms and all angles are in degrees.

Source code in mdtop/_utils.py
def box_to_geometry(
    box: openmm.unit.Quantity,
) -> tuple[float, float, float, float, float, float]:
    """Convert a box to its geometry parameters.

    Args:
        box: The box vectors with ``shape=(3, 3)``.

    Returns:
        The box lengths and angles in the order ``(a, b, c, alpha, beta, gamma)``,
        where all lengths are in angstroms and all angles are in degrees.
    """

    box = box.value_in_unit(openmm.unit.angstrom)
    assert box.shape == (3, 3)

    a, b, c = box[0], box[1], box[2]

    lengths = numpy.linalg.norm([a, b, c], axis=1)

    alpha = numpy.rad2deg(numpy.arccos(numpy.dot(b, c) / (lengths[1] * lengths[2])))
    beta = numpy.rad2deg(numpy.arccos(numpy.dot(c, a) / (lengths[2] * lengths[0])))
    gamma = numpy.rad2deg(numpy.arccos(numpy.dot(a, b) / (lengths[0] * lengths[1])))

    # round the box vectors so that future checks for orthorhombic boxes are more robust
    for vec in [a, b, c]:
        vec[numpy.abs(vec) < _EPSILON] = 0.0

    return lengths[0], lengths[1], lengths[2], alpha, beta, gamma