NmrIredPlan
Compute iRED (isotropic reorientational eigenmode dynamics) vectors and correlation matrices for NMR relaxation analysis.
Constructor
from warp_md import NmrIredPlan
plan = NmrIredPlan(
vector_pairs,
order=2,
length_scale=0.1,
pbc="none",
corr_mode="tensor",
return_corr=True
)
List of atom index pairs [(atom1, atom2), ...] defining bond vectors (e.g., N-H bonds)
Legendre polynomial order (1 or 2)
Coordinate scaling factor (use 0.1 to convert Å to nm)
Periodic boundary conditions: "none" or "orthorhombic"
Correlation calculation mode:
"tensor": Compute full correlation matrix
"timecorr": Compute time correlation functions
Return correlation data (True) or just vectors (False)
run() Method
When return_corr=True:
- Returns: Tuple of
(vectors, correlations)
vectors: 3D array of shape (n_frames, n_vectors, 3) containing unit vectors
correlations: Correlation matrix or time series
When return_corr=False:
- Returns: 3D array of vectors only
Example: N-H Order Parameters
import warp_md as wmd
import numpy as np
system = wmd.System("protein.pdb")
traj = wmd.open_trajectory("md.nc", system)
# Define N-H bond vectors
atom_table = system.atom_table()
nh_pairs = []
for i, atom in enumerate(atom_table["name"]):
if atom == "N":
resid = atom_table["resid"][i]
# Find corresponding H
for j, h_atom in enumerate(atom_table["name"]):
if h_atom == "H" and atom_table["resid"][j] == resid:
nh_pairs.append((i, j))
break
print(f"Found {len(nh_pairs)} N-H vectors")
# Calculate iRED vectors and correlation matrix
plan = wmd.NmrIredPlan(
nh_pairs,
order=2,
length_scale=0.1,
corr_mode="tensor",
return_corr=True
)
vectors, corr_matrix = plan.run(traj, system)
# Calculate order parameters S2
n_frames = vectors.shape[0]
m = np.einsum('fpi,fpj->pij', vectors, vectors) / n_frames
tr_m2 = np.einsum('pij,pij->p', m, m)
s2 = 1.5 * tr_m2 - 0.5
print("Order parameters S²:")
for i, (n_idx, h_idx) in enumerate(nh_pairs[:10]):
resid = atom_table["resid"][n_idx]
print(f" Residue {resid}: S² = {s2[i]:.3f}")
Order parameter S² ranges from 0 (isotropic motion) to 1 (completely rigid). Typical values:
- Rigid secondary structure: S² > 0.8
- Flexible loops: S² < 0.6
- Disordered regions: S² < 0.4
Python Helper Functions
For convenience, use the high-level Python API:
nh_order_parameters()
Direct calculation of N-H order parameters.
from warp_md.analysis import nh_order_parameters
# Automatic detection using selection
s2 = nh_order_parameters(
traj,
system,
vector_pairs=None,
selection="protein and (name N or name H)",
order=2,
method="tensor"
)
print(f"Order parameters shape: {s2.shape}")
print(f"Mean S²: {s2.mean():.3f}")
Explicit N-H pairs, or None to auto-detect from selection
Atom selection string for auto-detection
Calculation method:
"tensor": Direct from correlation tensor
"timecorr_fit": Fit time correlation decay
Time step in ps (for method="timecorr_fit")
Correlation time cutoff in ps
Example: Mapping Order Parameters to Structure
import matplotlib.pyplot as plt
import numpy as np
from warp_md.analysis import nh_order_parameters
# Calculate order parameters
s2 = nh_order_parameters(
traj,
system,
selection="protein and backbone",
method="tensor"
)
# Get residue numbers
atom_table = system.atom_table()
n_indices = [i for i, name in enumerate(atom_table["name"]) if name == "N"]
resids = [atom_table["resid"][i] for i in n_indices[:len(s2)]]
# Plot
plt.figure(figsize=(12, 4))
plt.plot(resids, s2, 'o-')
plt.axhline(0.8, color='r', linestyle='--', alpha=0.5, label='Rigid')
plt.axhline(0.6, color='orange', linestyle='--', alpha=0.5, label='Flexible')
plt.xlabel("Residue Number")
plt.ylabel("Order Parameter S²")
plt.ylim(0, 1)
plt.legend()
plt.title("Backbone Dynamics")
plt.grid(alpha=0.3)
plt.show()
# Identify flexible regions
flexible_residues = [resids[i] for i, val in enumerate(s2) if val < 0.6]
print(f"Flexible regions: {flexible_residues}")
jcoupling()
Compute scalar J-coupling constants from dihedral angles using Karplus equations.
from warp_md.analysis import jcoupling
# Define dihedral angles (4 atoms each)
dihedrals = [
(10, 11, 12, 13), # Example: H-N-C-H dihedral
(20, 21, 22, 23),
]
j_values = jcoupling(
traj,
system,
dihedral_indices=dihedrals,
karplus=(6.4, -1.4, 1.9), # A, B, C parameters
phase_deg=0.0
)
print(f"J-coupling shape: {j_values.shape}")
print(f"Average J-coupling: {j_values.mean(axis=0)} Hz")
List of 4-atom tuples defining dihedral angles
karplus
tuple
default:"(6.4, -1.4, 1.9)"
Karplus equation parameters (A, B, C) for J = A cos²θ + B cosθ + C
Path to file containing Karplus parameters
Also return dihedral angles
Karplus Parameters
Common parameter sets:
# ³J(Hᴺ-Hᴿ) for proteins (Vuister & Bax)
karplus_hn_ha = (7.09, -1.42, 1.55)
# ³J(H-H) for nucleic acids
karplus_nucleic = (9.5, -1.1, 0.9)
# ³J(C-C) for carbohydrates
karplus_carb = (3.5, -0.9, 0.3)
Advanced Example: Validating Against Experiment
import numpy as np
import matplotlib.pyplot as plt
from warp_md.analysis import jcoupling
# Experimental J-coupling data
exp_j_couplings = {
5: 8.2, # Residue: J value (Hz)
6: 7.5,
7: 9.1,
# ...
}
# Calculate from simulation
dihedrals = []
residues = []
for resid in exp_j_couplings.keys():
# H-N-Ca-Ha dihedral
dih = (
system.select(f"resid {resid} and name H").indices[0],
system.select(f"resid {resid} and name N").indices[0],
system.select(f"resid {resid} and name CA").indices[0],
system.select(f"resid {resid} and name HA").indices[0],
)
dihedrals.append(dih)
residues.append(resid)
calc_j = jcoupling(
traj,
system,
dihedral_indices=dihedrals,
karplus=(7.09, -1.42, 1.55)
)
# Average over trajectory
calc_j_avg = calc_j.mean(axis=0)
# Compare
plt.figure(figsize=(8, 6))
exp_values = [exp_j_couplings[r] for r in residues]
plt.scatter(exp_values, calc_j_avg)
plt.plot([6, 10], [6, 10], 'r--', label='Perfect agreement')
plt.xlabel("Experimental ³J (Hz)")
plt.ylabel("Calculated ³J (Hz)")
plt.title("J-coupling Validation")
plt.legend()
plt.grid(alpha=0.3)
plt.show()
# Calculate RMSD
rmsd = np.sqrt(np.mean((np.array(exp_values) - calc_j_avg)**2))
print(f"RMSD from experiment: {rmsd:.2f} Hz")
NMR observables are excellent for validating MD simulations. Good agreement (RMSD < 1 Hz for J-couplings, S² within 0.1) indicates accurate force field and sampling.