Topology Tutorial¶
Learn how to work with Topology objects in MolPy! Topology represents the connectivity graph of your molecular system - atoms, bonds, angles, and dihedrals.
What is Topology?¶
Topology is a graph-based representation of molecular connectivity:
- Atoms: Vertices in the graph
- Bonds: Edges connecting atoms
- Angles: Triplets of connected atoms
- Dihedrals: Quadruplets of connected atoms
- Impropers: Special quadruplets for planarity
It's built on igraph.Graph, giving you powerful graph algorithms!
In [1]:
Copied!
import molpy as mp
from molpy.core.topology import Topology
import molpy as mp
from molpy.core.topology import Topology
Creating a Topology¶
You can create an empty topology or build one from existing data:
In [2]:
Copied!
# Create an empty topology
topo = Topology()
print(f"Empty topology: {topo.n_atoms} atoms, {topo.n_bonds} bonds")
# Create an empty topology
topo = Topology()
print(f"Empty topology: {topo.n_atoms} atoms, {topo.n_bonds} bonds")
Empty topology: 0 atoms, 0 bonds
Adding Atoms and Bonds¶
In [3]:
Copied!
# Add atoms
topo.add_atoms(4) # Add 4 atoms
print(f"After adding atoms: {topo.n_atoms} atoms")
# Add bonds (connecting atoms 0-1, 1-2, 2-3)
topo.add_bond(0, 1)
topo.add_bond(1, 2)
topo.add_bond(2, 3)
print(f"After adding bonds: {topo.n_bonds} bonds")
print(f"Bonds: {topo.bonds}")
# Add atoms
topo.add_atoms(4) # Add 4 atoms
print(f"After adding atoms: {topo.n_atoms} atoms")
# Add bonds (connecting atoms 0-1, 1-2, 2-3)
topo.add_bond(0, 1)
topo.add_bond(1, 2)
topo.add_bond(2, 3)
print(f"After adding bonds: {topo.n_bonds} bonds")
print(f"Bonds: {topo.bonds}")
After adding atoms: 4 atoms After adding bonds: 3 bonds Bonds: [[0 1] [1 2] [2 3]]
Adding Multiple Bonds at Once¶
In [4]:
Copied!
# Create a new topology
topo = Topology()
topo.add_atoms(5)
# Add multiple bonds at once
bond_list = [(0, 1), (1, 2), (2, 3), (3, 4)]
topo.add_bonds(bond_list)
print(f"Topology: {topo.n_atoms} atoms, {topo.n_bonds} bonds")
print(f"Bond list: {topo.bonds}")
# Create a new topology
topo = Topology()
topo.add_atoms(5)
# Add multiple bonds at once
bond_list = [(0, 1), (1, 2), (2, 3), (3, 4)]
topo.add_bonds(bond_list)
print(f"Topology: {topo.n_atoms} atoms, {topo.n_bonds} bonds")
print(f"Bond list: {topo.bonds}")
Topology: 5 atoms, 4 bonds Bond list: [[0 1] [1 2] [2 3] [3 4]]
Accessing Topology Properties¶
In [5]:
Copied!
# Build a simple chain topology
topo = Topology()
topo.add_atoms(4)
topo.add_bonds([(0, 1), (1, 2), (2, 3)])
print(f"Atoms: {topo.n_atoms}")
print(f"Bonds: {topo.n_bonds}")
print(f"Angles: {topo.n_angles}")
print(f"Dihedrals: {topo.n_dihedrals}")
# Access atom and bond arrays
print(f"Atom indices: {topo.atoms}")
print(f"Bond pairs: {topo.bonds}")
print(f"Angle triplets: {topo.angles}")
print(f"Dihedral quadruplets: {topo.dihedrals}")
# Build a simple chain topology
topo = Topology()
topo.add_atoms(4)
topo.add_bonds([(0, 1), (1, 2), (2, 3)])
print(f"Atoms: {topo.n_atoms}")
print(f"Bonds: {topo.n_bonds}")
print(f"Angles: {topo.n_angles}")
print(f"Dihedrals: {topo.n_dihedrals}")
# Access atom and bond arrays
print(f"Atom indices: {topo.atoms}")
print(f"Bond pairs: {topo.bonds}")
print(f"Angle triplets: {topo.angles}")
print(f"Dihedral quadruplets: {topo.dihedrals}")
Atoms: 4 Bonds: 3 Angles: 2 Dihedrals: 1 Atom indices: [0 1 2 3] Bond pairs: [[0 1] [1 2] [2 3]] Angle triplets: [[0 1 2] [1 2 3]] Dihedral quadruplets: [[0 1 2 3]]
Creating Topology from Frame¶
In [6]:
Copied!
# Create a frame with atoms and bonds
frame = mp.Frame()
frame["atoms"] = mp.Block(
{"x": [0.0, 1.0, 2.0, 3.0], "y": [0.0, 0.0, 0.0, 0.0], "z": [0.0, 0.0, 0.0, 0.0]}
)
frame["bonds"] = mp.Block({"i": [0, 1, 2], "j": [1, 2, 3]})
# Get topology from frame
topo = frame.get_topology()
print(f"Topology from frame: {topo.n_atoms} atoms, {topo.n_bonds} bonds")
# Create a frame with atoms and bonds
frame = mp.Frame()
frame["atoms"] = mp.Block(
{"x": [0.0, 1.0, 2.0, 3.0], "y": [0.0, 0.0, 0.0, 0.0], "z": [0.0, 0.0, 0.0, 0.0]}
)
frame["bonds"] = mp.Block({"i": [0, 1, 2], "j": [1, 2, 3]})
# Get topology from frame
topo = frame.get_topology()
print(f"Topology from frame: {topo.n_atoms} atoms, {topo.n_bonds} bonds")
Topology from frame: 4 atoms, 3 bonds
Creating Topology from Atomistic¶
In [7]:
Copied!
# Example: Get topology from Atomistic structure
from molpy.core.atomistic import Atomistic
# Create a simple chain molecule (butane-like)
atomistic = Atomistic()
c1 = atomistic.def_atom(symbol="C", xyz=[0.0, 0.0, 0.0])
c2 = atomistic.def_atom(symbol="C", xyz=[1.5, 0.0, 0.0])
c3 = atomistic.def_atom(symbol="C", xyz=[3.0, 0.0, 0.0])
c4 = atomistic.def_atom(symbol="C", xyz=[4.5, 0.0, 0.0])
atomistic.def_bond(c1, c2)
atomistic.def_bond(c2, c3)
atomistic.def_bond(c3, c4)
print(f"Created molecule: {atomistic}")
# Get topology from atomistic
topo = atomistic.get_topo()
print(f"Topology: {topo.n_atoms} atoms, {topo.n_bonds} bonds")
print(f"Angles: {topo.n_angles}, Dihedrals: {topo.n_dihedrals}")
# Generate angles and dihedrals automatically in the atomistic structure
atomistic.get_topo(gen_angle=True, gen_dihe=True)
print("\nAfter generating angles and dihedrals:")
print(f" Angles in atomistic: {len(atomistic.angles)}")
print(f" Dihedrals in atomistic: {len(atomistic.dihedrals)}")
# Example: Get topology from Atomistic structure
from molpy.core.atomistic import Atomistic
# Create a simple chain molecule (butane-like)
atomistic = Atomistic()
c1 = atomistic.def_atom(symbol="C", xyz=[0.0, 0.0, 0.0])
c2 = atomistic.def_atom(symbol="C", xyz=[1.5, 0.0, 0.0])
c3 = atomistic.def_atom(symbol="C", xyz=[3.0, 0.0, 0.0])
c4 = atomistic.def_atom(symbol="C", xyz=[4.5, 0.0, 0.0])
atomistic.def_bond(c1, c2)
atomistic.def_bond(c2, c3)
atomistic.def_bond(c3, c4)
print(f"Created molecule: {atomistic}")
# Get topology from atomistic
topo = atomistic.get_topo()
print(f"Topology: {topo.n_atoms} atoms, {topo.n_bonds} bonds")
print(f"Angles: {topo.n_angles}, Dihedrals: {topo.n_dihedrals}")
# Generate angles and dihedrals automatically in the atomistic structure
atomistic.get_topo(gen_angle=True, gen_dihe=True)
print("\nAfter generating angles and dihedrals:")
print(f" Angles in atomistic: {len(atomistic.angles)}")
print(f" Dihedrals in atomistic: {len(atomistic.dihedrals)}")
Created molecule: <Atomistic, 4 atoms (C:4), 3 bonds, with coords> Topology: 4 atoms, 3 bonds Angles: 2, Dihedrals: 1 After generating angles and dihedrals: Angles in atomistic: 2 Dihedrals in atomistic: 1
Modifying Topology¶
In [8]:
Copied!
# Create topology
topo = Topology()
topo.add_atoms(5)
topo.add_bonds([(0, 1), (1, 2), (2, 3), (3, 4)])
print(f"Before: {topo.n_atoms} atoms, {topo.n_bonds} bonds")
# Delete a bond
topo.delete_bond(2) # Delete bond at index 2
print(f"After deleting bond: {topo.n_bonds} bonds")
# Delete an atom (and its bonds)
# topo.delete_atom(2)
# print(f"After deleting atom: {topo.n_atoms} atoms")
# Create topology
topo = Topology()
topo.add_atoms(5)
topo.add_bonds([(0, 1), (1, 2), (2, 3), (3, 4)])
print(f"Before: {topo.n_atoms} atoms, {topo.n_bonds} bonds")
# Delete a bond
topo.delete_bond(2) # Delete bond at index 2
print(f"After deleting bond: {topo.n_bonds} bonds")
# Delete an atom (and its bonds)
# topo.delete_atom(2)
# print(f"After deleting atom: {topo.n_atoms} atoms")
Before: 5 atoms, 4 bonds After deleting bond: 3 bonds
Combining Topologies¶
In [9]:
Copied!
# Create two topologies
topo1 = Topology()
topo1.add_atoms(3)
topo1.add_bonds([(0, 1), (1, 2)])
topo2 = Topology()
topo2.add_atoms(3)
topo2.add_bonds([(0, 1), (1, 2)])
# Union them
topo1.union(topo2)
print(f"Combined topology: {topo1.n_atoms} atoms, {topo1.n_bonds} bonds")
# Create two topologies
topo1 = Topology()
topo1.add_atoms(3)
topo1.add_bonds([(0, 1), (1, 2)])
topo2 = Topology()
topo2.add_atoms(3)
topo2.add_bonds([(0, 1), (1, 2)])
# Union them
topo1.union(topo2)
print(f"Combined topology: {topo1.n_atoms} atoms, {topo1.n_bonds} bonds")
--------------------------------------------------------------------------- RecursionError Traceback (most recent call last) Cell In[9], line 11 8 topo2.add_bonds([(0, 1), (1, 2)]) 10 # Union them ---> 11 topo1.union(topo2) 12 print(f"Combined topology: {topo1.n_atoms} atoms, {topo1.n_bonds} bonds") File ~/.asdf/installs/python/3.13.3/lib/python3.13/site-packages/molpy/core/topology.py:99, in Topology.union(self, other) 98 def union(self, other: "Topology"): ---> 99 self.union(other) 100 return self File ~/.asdf/installs/python/3.13.3/lib/python3.13/site-packages/molpy/core/topology.py:99, in Topology.union(self, other) 98 def union(self, other: "Topology"): ---> 99 self.union(other) 100 return self [... skipping similar frames: Topology.union at line 99 (2974 times)] File ~/.asdf/installs/python/3.13.3/lib/python3.13/site-packages/molpy/core/topology.py:99, in Topology.union(self, other) 98 def union(self, other: "Topology"): ---> 99 self.union(other) 100 return self RecursionError: maximum recursion depth exceeded
Graph Algorithms¶
Since Topology is based on igraph, you can use all graph algorithms:
In [10]:
Copied!
# Example: Check connectivity
topo = Topology()
topo.add_atoms(4)
topo.add_bonds([(0, 1), (1, 2), (2, 3)])
# Check if graph is connected
is_connected = topo.is_connected()
print(f"Is connected: {is_connected}")
# Get shortest path
path = topo.get_shortest_paths(0, 3)[0]
print(f"Shortest path from 0 to 3: {path}")
# Get neighbors
neighbors = topo.neighbors(1)
print(f"Neighbors of atom 1: {neighbors}")
# Get all paths between two vertices
paths = topo.get_all_simple_paths(0, 3)
print(f"All simple paths from 0 to 3: {paths}")
# Example: Check connectivity
topo = Topology()
topo.add_atoms(4)
topo.add_bonds([(0, 1), (1, 2), (2, 3)])
# Check if graph is connected
is_connected = topo.is_connected()
print(f"Is connected: {is_connected}")
# Get shortest path
path = topo.get_shortest_paths(0, 3)[0]
print(f"Shortest path from 0 to 3: {path}")
# Get neighbors
neighbors = topo.neighbors(1)
print(f"Neighbors of atom 1: {neighbors}")
# Get all paths between two vertices
paths = topo.get_all_simple_paths(0, 3)
print(f"All simple paths from 0 to 3: {paths}")
Is connected: True Shortest path from 0 to 3: [0, 1, 2, 3] Neighbors of atom 1: [0, 2] All simple paths from 0 to 3: [[0, 1, 2, 3]]