Pucker: Difference between revisions

From PyMOLWiki
Jump to navigation Jump to search
No edit summary
No edit summary
 
(3 intermediate revisions by 2 users not shown)
Line 1: Line 1:
{{Infobox script-repo
|type      = script
|filename  = pucker.py
|author    = [[User:Slaw|Sean M. Law]]
|license  = -
}}
===DESCRIPTION===  
===DESCRIPTION===  
'''pucker.py''' is a PyMol script that returns the sugar pucker information (phase, amplitude, pucker) for a given selection.
'''pucker.py''' is a PyMol script that returns the sugar pucker information (phase, amplitude, pucker) for a given selection.
Line 12: Line 19:
<source lang="python">
<source lang="python">
pucker selection
pucker selection
#Calculates the sugar information for the given selection
pucker selection, state=1
#Calculates the sugar information for the given selection in state 1
pucker selection, state=0
#Iterates over all states and calculates the sugar information for the given selection
</source>
</source>


Line 64: Line 78:
from pymol import cmd
from pymol import cmd


def pucker(selection):
def pucker(selection,state=1):


# Author: Sean Law
# Author: Sean Law
# Institute: Michigan State University
# Institute: University of Michigan
# E-mail: slaw_(at)_msu_dot_edu
# E-mail: seanlaw@umich.edu
 
sel=cmd.get_model(selection)
#Comparison to output from 3DNA is identical
#Note that the 3DNA output has the second chain
#printed out in reverse order
state=int(state)
if state == 0:
for state in range(1,cmd.count_states()+1):
sel=cmd.get_model(selection,state)
if state == 1:
print " " #Blank line to separate chain output
print "%5s  %6s  %6s  %8s  Residue" % ("State","Phase","Amp", "Pucker")
get_pucker(sel,iterate=state)
else:
sel=cmd.get_model(selection,state)
get_pucker(sel,iterate=0)
return
 
def get_pucker (sel,iterate=0):
iterate=int(iterate)
first=1
first=1
old=" "
old=" "
Line 80: Line 111:
new=atom.chain+" "+str(atom.resi)
new=atom.chain+" "+str(atom.resi)
newchain=atom.chain+" "+atom.segi
newchain=atom.chain+" "+atom.segi
if (not (oldchain == newchain) and first):
if oldchain != newchain and first:
print " " #Blank line to separate chain output
if iterate == 0:
print "%6s  %6s  %8s  Residue" % ("Phase", "Amp", "Pucker")
print " " #Blank line to separate chain output
if (not(new == old) and (not first)):
print "%6s  %6s  %8s  Residue" % ("Phase", "Amp", "Pucker")
if new != old and not first:
#Check that all 5 atoms exist
#Check that all 5 atoms exist
if(len(residue) == 15):
if len(residue) == 15:
#Construct planes
#Construct planes
get_dihedrals(residue,theta)
get_dihedrals(residue,theta)
Line 93: Line 125:
else:
else:
print "There is no sugar in this residue"
print "There is no sugar in this residue"
if (not (oldchain == newchain)):
if oldchain != newchain:
print " " #Blank line to separate chain output
print " " #Blank line to separate chain output
print "%6s  %6s  %8s  Residue" % ("Phase", "Amp", "Pucker")
print "%6s  %6s  %8s  Residue" % ("Phase", "Amp", "Pucker")
Line 109: Line 141:
#Final Residue
#Final Residue
#Calculate dihedrals for final residue
#Calculate dihedrals for final residue
if (len(residue) == 15):
if len(residue) == 15:
#Construct planes
#Construct planes
get_dihedrals(residue,theta)
get_dihedrals(residue,theta)
#Calculate pucker for final residue
#Calculate pucker for final residue
info = pseudo(residue,theta)
info = pseudo(residue,theta)
print info+"  "+old
if iterate == 0:
print info+"  "+old
else:
print "%5d  %s" % (iterate,info+"  "+old)
else:
else:
print "There is no sugar in this residue"
print "There is no sugar in this residue"
Line 129: Line 164:
amplitude=theta['2']/cos(phase)
amplitude=theta['2']/cos(phase)
phase=math.degrees(phase)
phase=math.degrees(phase)
if (phase < 0):
if phase < 0:
phase=phase+360 # 0 <= Phase < 360
phase+=360 # 0 <= Phase < 360
#Determine pucker
#Determine pucker
if (phase < 36):
if phase < 36:
pucker = "C3'-endo"
pucker = "C3'-endo"
elif (phase < 72):
elif phase < 72:
pucker = "C4'-exo"
pucker = "C4'-exo"
elif (phase <108):
elif phase < 108:
pucker = "O4'-endo"
pucker = "O4'-endo"
elif (phase < 144):
elif phase < 144:
pucker = "C1'-exo"
pucker = "C1'-exo"
elif (phase < 180):
elif phase < 180:
pucker = "C2'-endo"
pucker = "C2'-endo"
elif (phase < 216):
elif phase < 216:
pucker = "C3'-exo"
pucker = "C3'-exo"
elif (phase < 252):
elif phase < 252:
pucker = "C4'-endo"
pucker = "C4'-endo"
elif (phase < 288):
elif phase < 288:
pucker = "O4'-exo"
pucker = "O4'-exo"
elif (phase < 324):
elif phase < 324:
pucker = "C1'-endo"
pucker = "C1'-endo"
elif (phase < 360):
elif phase < 360:
pucker = "C2'-exo"
pucker = "C2'-exo"
else:
else:
Line 159: Line 194:


def store_atom(atom,residue):
def store_atom(atom,residue):
if (atom.name == "O4'" or atom.name == "O4*"):
if atom.name == "O4'" or atom.name == "O4*":
residue['O4*X'] = atom.coord[0]
residue['O4*X'] = atom.coord[0]
residue['O4*Y'] = atom.coord[1]
residue['O4*Y'] = atom.coord[1]
residue['O4*Z'] = atom.coord[2]
residue['O4*Z'] = atom.coord[2]
elif (atom.name == "C1'" or atom.name == "C1*"):
elif atom.name == "C1'" or atom.name == "C1*":
residue['C1*X'] = atom.coord[0]
residue['C1*X'] = atom.coord[0]
residue['C1*Y'] = atom.coord[1]
residue['C1*Y'] = atom.coord[1]
residue['C1*Z'] = atom.coord[2]
residue['C1*Z'] = atom.coord[2]
elif (atom.name == "C2'" or atom.name == "C2*"):
elif atom.name == "C2'" or atom.name == "C2*":
residue['C2*X'] = atom.coord[0]
residue['C2*X'] = atom.coord[0]
residue['C2*Y'] = atom.coord[1]
residue['C2*Y'] = atom.coord[1]
residue['C2*Z'] = atom.coord[2]
residue['C2*Z'] = atom.coord[2]
elif (atom.name == "C3'" or atom.name == "C3*"):
elif atom.name == "C3'" or atom.name == "C3*":
residue['C3*X'] = atom.coord[0]
residue['C3*X'] = atom.coord[0]
residue['C3*Y'] = atom.coord[1]
residue['C3*Y'] = atom.coord[1]
residue['C3*Z'] = atom.coord[2]
residue['C3*Z'] = atom.coord[2]
elif (atom.name == "C4'" or atom.name == "C4*"):
elif atom.name == "C4'" or atom.name == "C4*":
residue['C4*X'] = atom.coord[0]
residue['C4*X'] = atom.coord[0]
residue['C4*Y'] = atom.coord[1]
residue['C4*Y'] = atom.coord[1]
Line 190: Line 225:
for i in range (0,12):
for i in range (0,12):
C.append(residue[ribose[i]])
C.append(residue[ribose[i]])
theta['0']=dihedral(C)
theta['0']=calcdihedral(C)
C = []
C = []
Line 196: Line 231:
for i in range (0,12):
for i in range (0,12):
C.append(residue[ribose[i]])
C.append(residue[ribose[i]])
theta['1']=dihedral(C)
theta['1']=calcdihedral(C)




Line 203: Line 238:
for i in range (0,12):
for i in range (0,12):
C.append(residue[ribose[i]])
C.append(residue[ribose[i]])
theta['2']=dihedral(C)
theta['2']=calcdihedral(C)




Line 210: Line 245:
for i in range (0,12):
for i in range (0,12):
C.append(residue[ribose[i]])
C.append(residue[ribose[i]])
theta['3']=dihedral(C)
theta['3']=calcdihedral(C)


C = []
C = []
Line 216: Line 251:
for i in range (0,12):
for i in range (0,12):
C.append(residue[ribose[i]])
C.append(residue[ribose[i]])
theta['4']=dihedral(C)
theta['4']=calcdihedral(C)
return
return
Line 227: Line 262:
def shift_down(list,value):
def shift_down(list,value):
for i in range (0,value):
for i in range (0,value):
list.insert(len(list),list.pop(0))
list.append(list.pop(0))
return
return


def dihedral(C):
def calcdihedral(C):
DX12=C[0]-C[3]
DX12=C[0]-C[3]
Line 270: Line 305:
TS=1.0-DP12*DP12
TS=1.0-DP12*DP12


if (TS < 0):
if TS < 0:
TS=0
TS=0
else:
else:
Line 283: Line 318:
DP233=PX3*DX23+PY3*DY23+PZ3*DZ23
DP233=PX3*DX23+PY3*DY23+PZ3*DZ23


if (DP233 > 0):
if DP233 > 0:
ANGLE=-ANGLE
ANGLE=-ANGLE


ANGLE=math.degrees(ANGLE)
ANGLE=math.degrees(ANGLE)


if (ANGLE > 180):
if ANGLE > 180:
ANGLE=ANGLE-360
ANGLE-=360
if (ANGLE < -180):
if ANGLE < -180:
ANGLE=ANGLE+360
ANGLE+=360


return ANGLE
return ANGLE


cmd.extend("pucker",pucker)
cmd.extend("pucker",pucker)
</source>
</source>



Latest revision as of 08:52, 19 March 2013

Type Python Script
Download pucker.py
Author(s) Sean M. Law
License -
This code has been put under version control in the project Pymol-script-repo

DESCRIPTION

pucker.py is a PyMol script that returns the sugar pucker information (phase, amplitude, pucker) for a given selection.

This script uses its own dihedral calculation scheme rather than the get_dihedral command. Thus, it is lightning fast!

If a selection does not contain any ribose sugars then an error message is returned.

USAGE

load the script using the run command

pucker selection
#Calculates the sugar information for the given selection

pucker selection, state=1
#Calculates the sugar information for the given selection in state 1

pucker selection, state=0 
#Iterates over all states and calculates the sugar information for the given selection

EXAMPLES

#fetch 1BNA.pdb
fetch 1bna

#Select DNA only
#Otherwise, you will get an error for water not having sugars
select DNA, not solvent

#Execute pucker command
pucker DNA

#The output should look like this
 Phase     Amp    Pucker  Residue
161.22   55.32  C2'-endo  A 1
139.52   41.67   C1'-exo  A 2
 92.82   38.31  O4'-endo  A 3
166.35   48.47  C2'-endo  A 4
128.57   46.30   C1'-exo  A 5
126.92   49.75   C1'-exo  A 6
101.30   47.32  O4'-endo  A 7
115.62   49.23   C1'-exo  A 8
140.44   46.37   C1'-exo  A 9
145.79   53.36  C2'-endo  A 10
147.47   47.04  C2'-endo  A 11
113.80   51.69   C1'-exo  A 12
 
 Phase     Amp    Pucker  Residue
153.24   43.15  C2'-endo  B 13
128.49   45.01   C1'-exo  B 14
 67.74   43.84   C4'-exo  B 15
149.33   41.01  C2'-endo  B 16
169.27   42.30  C2'-endo  B 17
147.03   42.30  C2'-endo  B 18
116.29   47.52   C1'-exo  B 19
129.62   49.92   C1'-exo  B 20
113.61   42.93   C1'-exo  B 21
156.34   50.98  C2'-endo  B 22
116.89   44.21   C1'-exo  B 23
 34.70   45.55  C3'-endo  B 24


PYMOL API

from pymol.cgo import *    # get constants
from math import *
from pymol import cmd

def pucker(selection,state=1):

	# Author: Sean Law
	# Institute: University of Michigan
	# E-mail: seanlaw@umich.edu

	#Comparison to output from 3DNA is identical
	#Note that the 3DNA output has the second chain
	#printed out in reverse order
	state=int(state)
	if state == 0:
		for state in range(1,cmd.count_states()+1):
			sel=cmd.get_model(selection,state)
			if state == 1:
				print " " #Blank line to separate chain output
				print "%5s  %6s  %6s  %8s  Residue" % ("State","Phase","Amp", "Pucker")
			get_pucker(sel,iterate=state)
	else:
		sel=cmd.get_model(selection,state)
		get_pucker(sel,iterate=0)
	return

def get_pucker (sel,iterate=0):
	iterate=int(iterate)
	first=1
	old=" "
	oldchain=" "
	residue={}
	theta={}
	count=0
	for atom in sel.atom:
		new=atom.chain+" "+str(atom.resi)
		newchain=atom.chain+" "+atom.segi
		if oldchain != newchain and first:
			if iterate == 0:
				print " " #Blank line to separate chain output
				print "%6s  %6s  %8s  Residue" % ("Phase", "Amp", "Pucker")
		if new != old and not first:
			#Check that all 5 atoms exist
			if len(residue) == 15:
				#Construct planes
				get_dihedrals(residue,theta)
				#Calculate pucker
				info = pseudo(residue,theta)
				print info+"  "+old
			else:
				print "There is no sugar in this residue"
			if oldchain != newchain:
				print " " #Blank line to separate chain output
				print "%6s  %6s  %8s  Residue" % ("Phase", "Amp", "Pucker")
			#Clear values
			residue={}
			dihedrals={}
			theta={}
			#Store new value
			store_atom(atom,residue)
		else:
			store_atom(atom,residue)
		first=0
		old=new
		oldchain=newchain
	#Final Residue
	#Calculate dihedrals for final residue
	if len(residue) == 15:
		#Construct planes
		get_dihedrals(residue,theta)
		#Calculate pucker for final residue
		info = pseudo(residue,theta)
		if iterate == 0:
			print info+"  "+old
		else:
			print "%5d  %s" % (iterate,info+"  "+old)
	else:
		print "There is no sugar in this residue"
	return

def sele_exists(sele):
	return sele in cmd.get_names("selections");

def pseudo(residue,theta):
	other=2*(sin(math.radians(36.0))+sin(math.radians(72.0)))
	
	#phase=atan2((theta4+theta1)-(theta3+theta0),2*theta2*(sin(math.radians(36.0))+sin(math.radians(72.0))))
	phase=atan2((theta['4']+theta['1'])-(theta['3']+theta['0']),theta['2']*other)
	amplitude=theta['2']/cos(phase)
	phase=math.degrees(phase)
	if phase < 0:
		phase+=360 # 0 <= Phase < 360
	#Determine pucker
	if phase < 36:
		pucker = "C3'-endo"
	elif phase < 72:
		pucker = "C4'-exo"
	elif phase < 108:
		pucker = "O4'-endo"
	elif phase < 144:
		pucker = "C1'-exo"
	elif phase < 180:
		pucker = "C2'-endo"
	elif phase < 216:
		pucker = "C3'-exo"
	elif phase < 252:
		pucker = "C4'-endo"
	elif phase < 288:
		pucker = "O4'-exo"
	elif phase < 324:
		pucker = "C1'-endo"
	elif phase < 360:
		pucker = "C2'-exo"
	else:
		pucker = "Phase is out of range"
	info = "%6.2f  %6.2f  %8s" % (phase, amplitude, pucker)
	return info
	

def store_atom(atom,residue):
	if atom.name == "O4'" or atom.name == "O4*":
		residue['O4*X'] = atom.coord[0]
		residue['O4*Y'] = atom.coord[1]
		residue['O4*Z'] = atom.coord[2]
	elif atom.name == "C1'" or atom.name == "C1*":
		residue['C1*X'] = atom.coord[0]
		residue['C1*Y'] = atom.coord[1]
		residue['C1*Z'] = atom.coord[2]
	elif atom.name == "C2'" or atom.name == "C2*":
		residue['C2*X'] = atom.coord[0]
		residue['C2*Y'] = atom.coord[1]
		residue['C2*Z'] = atom.coord[2]
	elif atom.name == "C3'" or atom.name == "C3*":
		residue['C3*X'] = atom.coord[0]
		residue['C3*Y'] = atom.coord[1]
		residue['C3*Z'] = atom.coord[2]
	elif atom.name == "C4'" or atom.name == "C4*":
		residue['C4*X'] = atom.coord[0]
		residue['C4*Y'] = atom.coord[1]
		residue['C4*Z'] = atom.coord[2]
	return

def get_dihedrals(residue,theta):

	C = []
	ribose = residue.keys()
	ribose.sort()
	
	shift_up(ribose,6)
	for i in range (0,12):
		C.append(residue[ribose[i]])
	theta['0']=calcdihedral(C)
	
	C = []
	shift_down(ribose,3)
	for i in range (0,12):
		C.append(residue[ribose[i]])
	theta['1']=calcdihedral(C)


	C = []
	shift_down(ribose,3)
	for i in range (0,12):
		C.append(residue[ribose[i]])
	theta['2']=calcdihedral(C)


	C = []
	shift_down(ribose,3)
	for i in range (0,12):
		C.append(residue[ribose[i]])
	theta['3']=calcdihedral(C)

	C = []
	shift_down(ribose,3)
	for i in range (0,12):
		C.append(residue[ribose[i]])
	theta['4']=calcdihedral(C)
	
	return

def shift_up(list,value):
	for i in range (0,value):
		list.insert(0,list.pop())
	return

def shift_down(list,value):
	for i in range (0,value):
		list.append(list.pop(0))
	return

def calcdihedral(C):
	
	DX12=C[0]-C[3]
	DY12=C[1]-C[4]
	DZ12=C[2]-C[5]
	
	DX23=C[3]-C[6]
	DY23=C[4]-C[7]
	DZ23=C[5]-C[8]
	
	DX43=C[9]-C[6];
	DY43=C[10]-C[7];
	DZ43=C[11]-C[8];

	#Cross product to get normal
	
	PX1=DY12*DZ23-DY23*DZ12;
	PY1=DZ12*DX23-DZ23*DX12;
	PZ1=DX12*DY23-DX23*DY12;

	NP1=sqrt(PX1*PX1+PY1*PY1+PZ1*PZ1);

	PX1=PX1/NP1
	PY1=PY1/NP1
	PZ1=PZ1/NP1

	PX2=DY43*DZ23-DY23*DZ43;
	PY2=DZ43*DX23-DZ23*DX43;
	PZ2=DX43*DY23-DX23*DY43;

	NP2=sqrt(PX2*PX2+PY2*PY2+PZ2*PZ2);
	
	PX2=PX2/NP2
	PY2=PY2/NP2
	PZ2=PZ2/NP2

	DP12=PX1*PX2+PY1*PY2+PZ1*PZ2

	TS=1.0-DP12*DP12

	if TS < 0:
		TS=0
	else:
		TS=sqrt(TS)
	
	ANGLE=math.pi/2.0-atan2(DP12,TS)

	PX3=PY1*PZ2-PY2*PZ1
	PY3=PZ1*PX2-PZ2*PX1
	PZ3=PX1*PY2-PX2*PY1

	DP233=PX3*DX23+PY3*DY23+PZ3*DZ23

	if DP233 > 0:
		ANGLE=-ANGLE

	ANGLE=math.degrees(ANGLE)

	if ANGLE > 180:
		ANGLE-=360
	if ANGLE < -180:
		ANGLE+=360

	return ANGLE

cmd.extend("pucker",pucker)