Skip to main content
Energy analysis plans are currently in development. This page documents the planned API based on the available Python analysis functions.

Energy Analysis

Warp-MD provides energy analysis capabilities through high-level Python functions. These use the Amber sander engine for energy calculations.

energy_analysis()

Compute potential energy components for each frame.
from warp_md.analysis import energy_analysis

result = energy_analysis(
    traj,
    system,
    prmtop="system.prmtop",
    frame_indices=None
)
traj
Trajectory
required
Trajectory to analyze
system
System
required
Molecular system
prmtop
str
required
Amber parameter/topology file
frame_indices
list
Specific frames to analyze (None = all frames)

Returns

Dictionary containing energy components (all in kcal/mol):
  • total: Total potential energy
  • bond: Bond energy
  • angle: Angle energy
  • dihedral: Dihedral energy
  • vdw: Van der Waals energy
  • elec: Electrostatic energy
  • vdw_1_4: 1-4 Van der Waals
  • elec_1_4: 1-4 Electrostatic

Example

import warp_md as wmd
from warp_md.analysis import energy_analysis
import matplotlib.pyplot as plt

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

# Compute energies
energies = energy_analysis(
    traj,
    system,
    prmtop="system.prmtop"
)

# Plot energy components
fig, axes = plt.subplots(2, 2, figsize=(12, 8))

axes[0, 0].plot(energies["total"])
axes[0, 0].set_title("Total Energy")
axes[0, 0].set_ylabel("Energy (kcal/mol)")

axes[0, 1].plot(energies["vdw"], label="VDW")
axes[0, 1].plot(energies["elec"], label="Electrostatic")
axes[0, 1].set_title("Non-bonded Energies")
axes[0, 1].legend()

axes[1, 0].plot(energies["bond"], label="Bond")
axes[1, 0].plot(energies["angle"], label="Angle")
axes[1, 0].plot(energies["dihedral"], label="Dihedral")
axes[1, 0].set_title("Bonded Energies")
axes[1, 0].legend()
axes[1, 0].set_xlabel("Frame")

axes[1, 1].plot(energies["vdw_1_4"], label="VDW 1-4")
axes[1, 1].plot(energies["elec_1_4"], label="Elec 1-4")
axes[1, 1].set_title("1-4 Interactions")
axes[1, 1].legend()
axes[1, 1].set_xlabel("Frame")

plt.tight_layout()
plt.show()

print("\nEnergy Statistics (kcal/mol):")
for component, values in energies.items():
    print(f"{component:12s}: {values.mean():10.2f} ± {values.std():8.2f}")

Energy Decomposition

ene_decomp()

Decompose interaction energy between two selections.
from warp_md.analysis import ene_decomp

result = ene_decomp(
    traj,
    system,
    prmtop="system.prmtop",
    selection_a="protein",
    selection_b="resname LIG",
    frame_indices=None
)
selection_a
str
required
First group (e.g., protein)
selection_b
str
required
Second group (e.g., ligand)

Returns

Dictionary with interaction energies:
  • vdw: Van der Waals interaction
  • elec: Electrostatic interaction
  • total: Total interaction energy

Example: Protein-Ligand Binding Energy

import warp_md as wmd
from warp_md.analysis import ene_decomp
import numpy as np

system = wmd.System("complex.prmtop")
traj = wmd.open_trajectory("md.nc", system)

# Protein-ligand interaction energy
interaction = ene_decomp(
    traj,
    system,
    prmtop="complex.prmtop",
    selection_a="protein",
    selection_b="resname LIG"
)

vdw = interaction["vdw"]
elec = interaction["elec"]
total = interaction["total"]

print("Protein-Ligand Interaction Energy:")
print(f"  VDW:   {vdw.mean():8.2f} ± {vdw.std():6.2f} kcal/mol")
print(f"  Elec:  {elec.mean():8.2f} ± {elec.std():6.2f} kcal/mol")
print(f"  Total: {total.mean():8.2f} ± {total.std():6.2f} kcal/mol")

# Plot
import matplotlib.pyplot as plt
fig, axes = plt.subplots(2, 1, figsize=(10, 6))

axes[0].plot(vdw, label="VDW", alpha=0.7)
axes[0].plot(elec, label="Electrostatic", alpha=0.7)
axes[0].plot(total, label="Total", linewidth=2)
axes[0].set_ylabel("Energy (kcal/mol)")
axes[0].legend()
axes[0].set_title("Protein-Ligand Interaction Energy")
axes[0].grid(alpha=0.3)

axes[1].hist(total, bins=50, alpha=0.7)
axes[1].set_xlabel("Total Interaction Energy (kcal/mol)")
axes[1].set_ylabel("Count")
axes[1].axvline(total.mean(), color='r', linestyle='--', 
                label=f'Mean = {total.mean():.1f}')
axes[1].legend()

plt.tight_layout()
plt.show()

Per-Residue Decomposition

import warp_md as wmd
from warp_md.analysis import ene_decomp
import numpy as np
import pandas as pd

system = wmd.System("complex.prmtop")
traj = wmd.open_trajectory("md.nc", system)

# Get protein residues
atom_table = system.atom_table()
protein_resids = np.unique([
    atom_table["resid"][i] 
    for i in range(len(atom_table["resid"]))
    if atom_table["resname"][i] not in ["WAT", "Na+", "Cl-"]
])

# Compute per-residue interaction with ligand
residue_energies = []

for resid in protein_resids:
    interaction = ene_decomp(
        traj,
        system,
        prmtop="complex.prmtop",
        selection_a=f"resid {resid}",
        selection_b="resname LIG"
    )
    
    residue_energies.append({
        "resid": resid,
        "resname": atom_table["resname"][resid-1],  # Approximate
        "vdw": interaction["vdw"].mean(),
        "elec": interaction["elec"].mean(),
        "total": interaction["total"].mean()
    })

# Create DataFrame
df = pd.DataFrame(residue_energies)
df = df.sort_values("total")

print("\nTop 10 favorable interactions:")
print(df.head(10))

print("\nTop 10 unfavorable interactions:")
print(df.tail(10))

# Plot energy footprint
import matplotlib.pyplot as plt
plt.figure(figsize=(12, 6))
plt.bar(df["resid"], df["total"], 
        color=['green' if x < 0 else 'red' for x in df["total"]])
plt.xlabel("Residue ID")
plt.ylabel("Interaction Energy (kcal/mol)")
plt.axhline(0, color='k', linestyle='-', linewidth=0.5)
plt.title("Per-Residue Ligand Interaction Energy")
plt.tight_layout()
plt.show()

Advanced: Free Energy Estimation

LIE (Linear Interaction Energy)

from warp_md.analysis import lie

# Requires separate simulations of:
# 1. Ligand in complex (bound state)
# 2. Ligand in water (free state)

system_complex = wmd.System("complex.prmtop")
traj_complex = wmd.open_trajectory("complex_md.nc", system_complex)

system_water = wmd.System("ligand_water.prmtop")
traj_water = wmd.open_trajectory("ligand_md.nc", system_water)

# Calculate LIE binding free energy
dG = lie(
    traj_complex,
    system_complex,
    traj_water,
    system_water,
    ligand_mask="resname LIG",
    alpha=0.18,  # VDW scaling
    beta=0.50    # Electrostatic scaling
)

print(f"LIE Binding Free Energy: {dG:.2f} kcal/mol")
For rigorous binding free energy calculations, use alchemical methods (TI/FEP) or MM-PBSA. Energy decomposition provides quick estimates and insights into interaction patterns.

Build docs developers (and LLMs) love