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.

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

  • 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._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.

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

  • 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"""

order instance-attribute #

order = order

The formal bond order

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)

Represents residues stored in a topology.

Attributes:

  • name

    The name of the residue.

  • seq_num

    The sequence number 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):
    self.name = name
    """The name of the residue."""
    self.seq_num = seq_num
    """The sequence number 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.

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_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.

  • merge

    Merge multiple topologies.

Attributes:

  • 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

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, chain: Chain)

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.

  • chain (Chain) –

    The parent chain to add to.

Returns:

  • The newly created residue.

Source code in mdtop/_top.py
def add_residue(self, name: str, seq_num: int | None, chain: Chain):
    """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.
        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)
    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,
)

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:

  • 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,
):
    """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)

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:

  • The newly created bond.

Source code in mdtop/_top.py
def add_bond(self, idx_1: int, idx_2: int, order: int | None):
    """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)

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.
    """
    topology = cls()

    for chain_omm in topology_omm.chains():
        chain = topology.add_chain(chain_omm.id)

        for residue_omm in chain_omm.residues():
            residue = topology.add_residue(residue_omm.name, residue_omm.id, chain)

            for atom_omm in residue_omm.atoms():
                is_v_site = atom_omm.element is None

                topology.add_atom(
                    atom_omm.name,
                    atom_omm.element.atomic_number if not is_v_site else 0,
                    None if is_v_site else getattr(atom_omm, "formalCharge", None),
                    atom_omm.id,
                    residue,
                )

    for bond_omm in topology_omm.bonds():
        order = bond_omm.order

        if order is None and bond_omm.type is not None:
            raise NotImplementedError

        topology.add_bond(bond_omm.atom1.index, bond_omm.atom2.index, order)

    if topology_omm.getPeriodicBoxVectors() is not None:
        box = topology_omm.getPeriodicBoxVectors().value_in_unit(
            openmm.unit.angstrom
        )
        topology.box = numpy.array(box) * openmm.unit.angstrom

    return topology

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.
    """
    topology_omm = openmm.app.Topology()

    atoms_omm = []

    for chain in self.chains:
        chain_omm = topology_omm.addChain(chain.id)

        for residue in chain.residues:
            residue_omm = topology_omm.addResidue(
                residue.name, chain_omm, str(residue.seq_num)
            )

            for atom in residue.atoms:
                element = (
                    None
                    if atom.atomic_num == 0
                    else openmm.app.Element.getByAtomicNumber(atom.atomic_num)
                )

                atom_omm = topology_omm.addAtom(
                    atom.name, element, residue_omm, str(atom.serial)
                )

                if hasattr(atom_omm, "formalCharge"):
                    atom_omm.formalCharge = atom.formal_charge

                atoms_omm.append(atom_omm)

    bond_order_to_type = {
        1: openmm.app.Single,
        2: openmm.app.Double,
        3: openmm.app.Triple,
    }

    for bond in self.bonds:
        topology_omm.addBond(
            atoms_omm[bond.idx_1],
            atoms_omm[bond.idx_2],
            bond_order_to_type[bond.order] if bond.order is not None else None,
            bond.order,
        )

    if self.box is not None:
        topology_omm.setPeriodicBoxVectors(self.box)

    return topology_omm

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.
    """

    mol = Chem.AddHs(mol)
    Chem.Kekulize(mol)

    topology = cls()
    topology.add_chain(chain)
    residue = topology.add_residue(residue_name, 1, topology.chains[0])

    symbol_counter = collections.defaultdict(int)

    for atom in mol.GetAtoms():
        if atom.GetPDBResidueInfo() is not None:
            name = atom.GetPDBResidueInfo().GetName()
        elif atom.HasProp("_Name"):
            name = atom.GetProp("_Name")
        else:
            symbol = atom.GetSymbol()
            symbol_counter[symbol] += 1

            name = f"{symbol}{symbol_counter[symbol]}".ljust(4, "x")

        topology.add_atom(
            name=name,
            atomic_num=atom.GetAtomicNum(),
            formal_charge=atom.GetFormalCharge(),
            serial=atom.GetIdx() + 1,
            residue=residue,
        )

    for bond in mol.GetBonds():
        topology.add_bond(
            idx_1=bond.GetBeginAtomIdx(),
            idx_2=bond.GetEndAtomIdx(),
            order=int(bond.GetBondTypeAsDouble()),
        )

    if mol.GetNumConformers() >= 1:
        xyz = mol.GetConformer().GetPositions()
        topology.xyz = numpy.array(xyz) * openmm.unit.angstrom

    return topology

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.
    """

    mol = Chem.RWMol()
    atoms_rd = []

    for atom in self.atoms:
        if atom.formal_charge is None:
            raise ValueError("Formal charges must be set on all atoms.")

        atom_rd = Chem.Atom(atom.atomic_num)
        atom_rd.SetFormalCharge(atom.formal_charge)
        atom_rd.SetProp("_Name", atom.name)

        res_info = Chem.AtomPDBResidueInfo(
            atom.name,
            atom.serial,
            "",
            atom.residue.name,
            atom.residue.seq_num,
            atom.residue.chain.id,
            isHeteroAtom=atom.residue.name not in AMINO_ACID_NAMES,
        )
        atom_rd.SetPDBResidueInfo(res_info)

        atoms_rd.append(mol.AddAtom(atom_rd))

    bond_order_to_type = {
        1: Chem.BondType.SINGLE,
        2: Chem.BondType.DOUBLE,
        3: Chem.BondType.TRIPLE,
    }
    for bond in self.bonds:
        if bond.order is None:
            raise ValueError("Formal bond orders must be set on all bonds.")
        if bond.order not in bond_order_to_type:
            raise NotImplementedError(f"Bond order {bond.order} is not supported.")

        mol.AddBond(bond.idx_1, bond.idx_2, bond_order_to_type[bond.order])

    if self.xyz is not None:
        xyz = self.xyz.value_in_unit(openmm.unit.angstrom)
        conf = Chem.Conformer(len(atoms_rd))

        for idx, pos in enumerate(xyz):
            conf.SetAtomPosition(idx, pos)

        mol.AddConformer(conf, assignId=True)

    Chem.SanitizeMol(mol)
    return Chem.Mol(mol)

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"}:
        mol = Chem.MolFromMolFile(str(path), removeHs=False)
        return cls.from_rdkit(mol)
    elif path.suffix.lower() == ".mol2":
        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 files are supported.

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 files are supported.

    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

    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 idxs_unique or bond.idx_2 not in idxs_unique:
            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

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