Created
February 7, 2011 06:39
-
-
Save sunhwan/814075 to your computer and use it in GitHub Desktop.
RMSD alignment for membrane protein
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import numpy as np | |
def usage(): | |
print """ | |
This script calculates C-alpha RMSD between 2 PDB structures, | |
and gererates optimal superposition of the 2nd structure to the | |
1st structure. | |
Transmembrane proteins are often aligned along the Z-axis, because, | |
conventionally, the bilayer normal is parallel to the Z-axis. To | |
preserve the orientation of protein with respect to the bilayer, | |
the optimal rotation matrix will be constructed in a way that the | |
rotation will be carried along the Z-axis only. | |
Usage: python align.py pdb-file1 pdb-file2 selection | |
selection: string of residue number separated by colon, e.g. 10:40. | |
C-alpha atoms of selected residues will be used for alignment. Currently, | |
only a PDB that contains single chain can be used. | |
""" | |
class Atom: | |
def __init__(self, line): | |
self.id = int(line[6:11].strip()) | |
self.name = line[11:16].strip().upper() | |
self.resname = line[17:21].strip() | |
self.chain = line[21:22].strip() | |
self.x = float(line[30:38]) | |
self.y = float(line[38:46]) | |
self.z = float(line[46:54]) | |
self.alt = line[16] | |
resid = line[22:27].strip() | |
type = line[77:80].strip() | |
try: | |
self.resid = int(resid) | |
except: | |
self.resid = int(resid[:-1]) | |
if not type: type = self.name[0] | |
self.type = type | |
def __getitem__(self, key): | |
return self.__dict__[key] | |
class PDB: | |
def __init__(self, pdbfile): | |
self.residues = {} | |
self.atoms = [] | |
self._parse(pdbfile) | |
def _parse(self, pdbfile): | |
for line in open(pdbfile, 'r').readlines(): | |
if line.startswith('ATOM'): | |
atom = Atom(line) | |
self.atoms.append(atom) | |
if not self.residues.has_key(atom.resid): | |
self.residues[atom.resid] = {} | |
self.residues[atom.resid][atom.name] = atom | |
def get_com(self, selection): | |
atoms = self.get_selection(selection) | |
com = np.sum([(atom.x, atom.y, atom.z) for atom in atoms], axis=0)/len(atoms) | |
return com | |
def orient(self, selection): | |
atoms = self.get_selection(selection) | |
com = np.sum([(atom.x, atom.y, atom.z) for atom in atoms], axis=0)/len(atoms) | |
for i in self.residues.keys(): | |
for j in self.residues[i].keys(): | |
self.residues[i][j].x -= com[0] | |
self.residues[i][j].y -= com[1] | |
self.residues[i][j].z -= com[2] | |
def translate(self, dist): | |
for atom in self.atoms: | |
atom.x += dist[0] | |
atom.y += dist[1] | |
atom.z += dist[2] | |
def get_selection(self, selection): | |
selections = [] | |
for rid in range(selection[0], selection[1]+1): | |
selections.append(self.residues[rid]['CA']) | |
selections.append(self.residues[rid]['N']) | |
selections.append(self.residues[rid]['O']) | |
selections.append(self.residues[rid]['C']) | |
return selections | |
def get_crds(self, selection): | |
atoms = self.get_selection(selection) | |
crds = np.zeros((len(atoms), 3), float) | |
for i, a in enumerate(atoms): | |
crds[i,0] = a.x | |
crds[i,1] = a.y | |
crds[i,2] = a.z | |
return crds | |
def transform(self, matrix): | |
for atom in self.atoms: | |
pos = (atom.x, atom.y, atom.z) | |
atom.x = matrix[0,0] * pos[0] + matrix[0,1] * pos[1] + matrix[0,2] * pos[2] | |
atom.y = matrix[1,0] * pos[0] + matrix[1,1] * pos[1] + matrix[1,2] * pos[2] | |
atom.z = matrix[2,0] * pos[0] + matrix[2,1] * pos[1] + matrix[2,2] * pos[2] | |
def write_pdb(self, filename): | |
fp = open(filename, 'w') | |
for atom in self.atoms: | |
atomdata = (atom.id, atom.name, atom.resname, atom.resid, atom.x, atom.y, atom.z, 0.0, 0.0) | |
if len(atom.name) == 4: | |
fp.write("ATOM %6i %4s%5s%5i %8.3f%8.3f%8.3f%6.2f%6.2f\n" % atomdata) | |
else: | |
fp.write("ATOM %6i %3s%5s%5i %8.3f%8.3f%8.3f%6.2f%6.2f\n" % atomdata) | |
fp.close() | |
if __name__ == '__main__': | |
import sys | |
try: | |
pdbfile1 = sys.argv[1] | |
pdbfile2 = sys.argv[2] | |
selection = map(int, sys.argv[3].split(':')) | |
except: | |
usage() | |
sys.exit(0) | |
pdb1 = PDB(pdbfile1) | |
com1 = pdb1.get_com(selection) | |
pdb1.translate((-com1[0], -com1[1], 0)) | |
crds1 = pdb1.get_crds(selection) | |
pdb2 = PDB(pdbfile2) | |
com2 = pdb2.get_com(selection) | |
pdb2.translate((-com2[0], -com2[1], 0)) | |
crds2 = pdb2.get_crds(selection) | |
# calculate rotation matrix | |
correlation_matrix = np.dot(np.transpose(crds1), crds2) | |
v, s, w = np.linalg.svd(correlation_matrix) | |
is_reflection = (np.linalg.det(np.transpose(v)) * np.linalg.det(np.transpose(w))) < 0.0 | |
if is_reflection: | |
v[-1,:] = -v[-1,:] | |
u = np.dot(v, w) | |
psi = -np.arctan2(u[0,2], u[1,2]) | |
z = np.zeros(9).reshape(3,3) | |
z[2,2] = 1 | |
z[0,0] = z[1,1] = np.cos(psi) | |
z[0,1] = -np.sin(psi) | |
z[1,0] = np.sin(psi) | |
if u[0,1] > 0 and z[0,1] < 0: | |
z[0,1] = -z[0,1] | |
z[1,0] = -z[1,0] | |
pdb2.transform(u) | |
pdb2.translate((com1[0], com1[1], 0)) | |
pdb2.write_pdb('xxx.pdb') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment