Skip to main content

HbondPlan

Identify and count hydrogen bonds over a trajectory.

Constructor

from warp_md import HbondPlan

plan = HbondPlan(
    donors,
    acceptors,
    dist_cutoff=3.5
)

# With angle criterion
plan = HbondPlan(
    donors,
    acceptors,
    dist_cutoff=3.5
).with_hydrogens(
    hydrogens,
    angle_cutoff_deg=120.0
)
donors
Selection
required
Donor heavy atoms (N, O)
acceptors
Selection
required
Acceptor atoms (O, N)
dist_cutoff
float
default:"3.5"
Maximum donor-acceptor distance in Å
hydrogens
Selection
Hydrogen atoms bonded to donors (for angle criterion)
angle_cutoff_deg
float
default:"120.0"
Minimum D-H…A angle in degrees

run() Method

Returns: Time series of hydrogen bond counts

Example

import warp_md as wmd
import matplotlib.pyplot as plt

system = wmd.System("protein.pdb")
traj = wmd.open_trajectory("md.nc", system)

# Protein backbone hydrogen bonds
donors = system.select("protein and name N")
acceptors = system.select("protein and name O")
hydrogens = system.select("protein and name H")

plan = wmd.HbondPlan(
    donors,
    acceptors,
    dist_cutoff=3.5
).with_hydrogens(
    hydrogens,
    angle_cutoff_deg=120.0
)

hbond_counts = plan.run(traj, system)

# Plot
plt.plot(hbond_counts)
plt.xlabel("Frame")
plt.ylabel("Number of H-bonds")
plt.title("Protein Backbone Hydrogen Bonds")
plt.grid(alpha=0.3)
plt.show()

print(f"Average H-bonds: {hbond_counts.mean():.1f}")
print(f"Std deviation: {hbond_counts.std():.1f}")
print(f"Min: {hbond_counts.min()}, Max: {hbond_counts.max()}")

Protein-Ligand H-bonds

# Specific interaction analysis
protein_donors = system.select("protein and (name N or name O)")
protein_acceptors = system.select("protein and (name O or name N)")

ligand_donors = system.select("resname LIG and (name N or name O)")
ligand_acceptors = system.select("resname LIG and (name O or name N)")

# Protein donates to ligand
plan1 = wmd.HbondPlan(protein_donors, ligand_acceptors, dist_cutoff=3.5)
hbonds_p_to_l = plan1.run(traj, system)

# Ligand donates to protein
plan2 = wmd.HbondPlan(ligand_donors, protein_acceptors, dist_cutoff=3.5)
hbonds_l_to_p = plan2.run(traj, system)

total_hbonds = hbonds_p_to_l + hbonds_l_to_p

print(f"Protein→Ligand H-bonds: {hbonds_p_to_l.mean():.1f}")
print(f"Ligand→Protein H-bonds: {hbonds_l_to_p.mean():.1f}")
print(f"Total: {total_hbonds.mean():.1f}")

NativeContactsPlan

Identify native contacts and monitor their preservation.

Constructor

plan = NativeContactsPlan(
    selection_a,
    selection_b,
    reference_mode="first",
    cutoff=4.5,
    pbc="none"
)
selection_a
Selection
required
First group of atoms
selection_b
Selection
required
Second group of atoms
reference_mode
str
default:"first"
Reference for defining native contacts: "first" or frame index
cutoff
float
default:"4.5"
Distance cutoff in Å for defining contacts
pbc
str
default:"none"
Periodic boundary conditions

run() Method

Returns: Time series of native contact fractions (0.0 to 1.0)

Example

# Monitor protein folding/unfolding
protein = system.select("protein and name CA")

# Intra-protein contacts (same selection)
plan = wmd.NativeContactsPlan(
    protein,
    protein,
    reference_mode="first",  # Reference = folded state
    cutoff=8.0
)

native_fraction = plan.run(traj, system)

import matplotlib.pyplot as plt
plt.plot(native_fraction)
plt.xlabel("Frame")
plt.ylabel("Native Contact Fraction")
plt.ylim(0, 1)
plt.axhline(0.7, color='r', linestyle='--', label='Folded threshold')
plt.legend()
plt.title("Protein Folding Progress")
plt.show()

if native_fraction[-1] > 0.7:
    print("Protein remains folded")
else:
    print("Protein has partially unfolded")

Interface Contacts

# Protein-protein interface
chainA = system.select("protein and chain A")
chainB = system.select("protein and chain B")

plan = wmd.NativeContactsPlan(
    chainA,
    chainB,
    reference_mode="first",
    cutoff=5.0
)

interface_contacts = plan.run(traj, system)

# Identify dissociation events
bound_threshold = 0.5
bound_frames = interface_contacts > bound_threshold

import numpy as np
if bound_frames.all():
    print("Complex remains bound throughout simulation")
else:
    first_unbind = np.where(~bound_frames)[0][0]
    print(f"Complex dissociates at frame {first_unbind}")

MindistPlan

Compute minimum distance between two selections.

Constructor

plan = MindistPlan(
    selection_a,
    selection_b,
    pbc="orthorhombic"
)
selection_a
Selection
required
First group
selection_b
Selection
required
Second group
pbc
str
default:"orthorhombic"
Periodic boundary conditions

run() Method

Returns: 1D array of minimum distances in Å

Example

# Minimum distance between protein and membrane
protein = system.select("protein")
membrane = system.select("resname POPC")

plan = wmd.MindistPlan(protein, membrane, pbc="orthorhombic")
min_dist = plan.run(traj, system)

import matplotlib.pyplot as plt
plt.plot(min_dist)
plt.xlabel("Frame")
plt.ylabel("Minimum Distance (Å)")
plt.axhline(3.0, color='r', linestyle='--', label='Contact threshold')
plt.legend()
plt.title("Protein-Membrane Distance")
plt.show()

# Check for binding
contact_frames = np.sum(min_dist < 3.0)
print(f"Protein in contact for {contact_frames}/{len(min_dist)} frames")
print(f"Contact fraction: {contact_frames/len(min_dist)*100:.1f}%")

Ion Pairing

# Monitor specific ion pair
lys_nz = system.select("resid 42 and name NZ")  # Lysine
glu_oe = system.select("resid 87 and name OE1 OE2")  # Glutamate

plan = wmd.MindistPlan(lys_nz, glu_oe, pbc="none")
salt_bridge_dist = plan.run(traj, system)

# Classify as salt bridge if < 4.0 Å
salt_bridge_formed = salt_bridge_dist < 4.0

import numpy as np
occupancy = np.sum(salt_bridge_formed) / len(salt_bridge_formed)
print(f"Salt bridge occupancy: {occupancy*100:.1f}%")

# Find average distance when formed
formed_dists = salt_bridge_dist[salt_bridge_formed]
if len(formed_dists) > 0:
    print(f"Average distance when formed: {formed_dists.mean():.2f} Å")

Contact Map Analysis

import warp_md as wmd
import numpy as np
import matplotlib.pyplot as plt

system = wmd.System("protein.pdb")
traj = wmd.open_trajectory("md.xtc", system)

# Get C-alpha atoms
ca_atoms = system.select("name CA")
n_residues = len(ca_atoms.indices)

# Compute average contact map
contact_map = np.zeros((n_residues, n_residues))
cutoff = 8.0  # Å

for i in range(n_residues):
    for j in range(i+1, n_residues):
        sel_i = system.select_indices([ca_atoms.indices[i]])
        sel_j = system.select_indices([ca_atoms.indices[j]])
        
        plan = wmd.MindistPlan(sel_i, sel_j, pbc="none")
        distances = plan.run(traj, system)
        
        # Fraction of frames in contact
        contact_fraction = np.sum(distances < cutoff) / len(distances)
        contact_map[i, j] = contact_fraction
        contact_map[j, i] = contact_fraction

# Plot
plt.figure(figsize=(10, 8))
im = plt.imshow(contact_map, cmap='YlOrRd', vmin=0, vmax=1)
plt.colorbar(im, label='Contact Probability')
plt.xlabel("Residue Index")
plt.ylabel("Residue Index")
plt.title(f"Contact Map (cutoff = {cutoff} Å)")
plt.tight_layout()
plt.show()
Contact analysis is essential for understanding:
  • Protein stability (native contacts)
  • Binding interfaces (interface contacts)
  • Conformational changes (contact map changes)

Additional Contact Plans

Additional contact and proximity analysis plans:
  • HausdorffPlan — Hausdorff distance between selections
  • ClosestAtomPlan — Find closest atom from one selection to another
  • ClosestPlan — Find closest molecules/residues
  • SearchNeighborsPlan — Neighbor list construction
  • CheckStructurePlan — Validate structure for clashes
  • CheckChiralityPlan — Verify stereochemistry
  • AtomMapPlan — Map atoms between topologies
  • FixImageBondsPlan — Fix bonds broken by imaging
Use warp-md list-plans --category contacts for detailed documentation.

Build docs developers (and LLMs) love