protein_analysis.py

Konrad Hinsen hinsen@ibs.ibs.fr
Thu, 3 Jul 1997 19:21:20 +0200


# This example demonstrates some of the analysis features of MMTK.
# Two protein structures from the PDB are compared in some detail.

# Disclaimer: I know nothing about this protein, and the analysis
# I present here may be of no biological interest.

from mmtk import *

# First we read the two PDB files.

sequences_1 = PDBFile('4q21.pdb').readSequenceWithConfiguration()
sequences_2 = PDBFile('6q21.pdb').readSequenceWithConfiguration()

# The first file contains a monomer, the second one a tetramer in
# which each chain is almost identical to the monomer from the first
# file. We have to cut off the last (incomplete) residue from the
# monomer and the last three residues of each chain of the tetramers
# to get matching sequences. We'll just deal with one of the chains of
# the tetramer here.

monomer = sequences_1[0][:-1]
tetramer_1 = sequences_2[0][:-3]

# Next we build a PeptideChain for the monomer. We have to specify
# that we have no C terminus (missing) and no hydrogens.

chain = PeptideChain(monomer, hydrogens=0, c_terminus=0)

# How big is this beast? For a quick guess, print the size of the smallest
# rectangular box containing it:

p1, p2 = chain.boundingBox()
print "Size of a bounding box: %4.2f x %4.2f x %4.2f nm" % tuple(p2-p1)

# Formalities: we define a universe and put the chain inside.
# Then we make a copy of the configuration of the universe for later use.
# This is necessary because configurations are defined only for
# universes (for various good reasons...).

universe = InfiniteUniverse()
universe.addObject(chain)
monomer_configuration = copy(universe.configuration())

# Next we change the conformation of the protein to that of the first
# tetramer chain.

chain.setConfiguration(tetramer_1)

# Now we want to get rid of the difference in position and orientation
# of the two configurations, so we calculate the transformation that
# minimizes the RMS difference.

transformation, rms = chain.findTransformation(monomer_configuration)

# That's the transformation *from* the current *to* the monomer
# configuration. Let's analyze it:

print "Translation/rotation fit:"
print "  RMS difference:", rms

translation = transformation.translation()
rotation = transformation.rotation()
axis, angle = rotation.axisAndAngle()

print "  Rotation axis: ", axis
print "  Rotation angle: ", angle
print "  Translation: ", translation.displacement()

# Note that order is important: the rotation (around the origin) is
# applied first, and then the translation. If you want to do it the
# other way round, the translation displacement must be different.

# Note also the units: distances in nanometers, angles in radians!
# If you insist on Angstrom and degrees, you'll have to type a bit more:

print "  Rotation angle in degrees: ", angle/Units.deg
print "  Translation in Angstrom: ", translation.displacement()/Units.Ang

# Just for fun we'll do it again, but minimizing the RMS for the
# C_alpha atoms only:

c_alphas = chain.residues().map(lambda residue: residue.peptide.C_alpha)
calpha_transformation, rms = c_alphas.findTransformation(monomer_configuration)

print "C_alpha fit:"
print "  RMS difference:", rms

translation = calpha_transformation.translation()
rotation = calpha_transformation.rotation()
axis, angle = rotation.axisAndAngle()

print "  Rotation axis: ", axis
print "  Rotation angle: ", angle
print "  Translation: ", translation.displacement()

# As expected there is little difference. Now we apply the transformation
# to the current configuration to align it with the reference configuration:

chain.applyTransformation(transformation)

# Action: We show an animation of the two configurations. This requires
# an animation-capable external viewer, e.g. VMD.

viewSequence(chain, [universe.configuration(), monomer_configuration])

# Interesting: there's a floppy loop with residues Gln61, Glu62, and Glu63
# at the tip. It seems to do a funny rotation relative to a closeby helix
# (His94-Lys101) when switching between the two configurations. Let's
# find out what this rotation is!

# First, we define the parts of the chain we are interested in. We don't
# want the sidechains to mess up the picture, so we use just the backbone:

tip = chain[60:64].backbone()
helix = chain[93:101].backbone()

# Then we align the two configurations to make the helix match as well
# as possible:

transformation, rms = helix.findTransformation(monomer_configuration)
chain.applyTransformation(transformation)

# Note that we *fit* only the helix, but *apply* the transformation
# to the whole chain.

# And now another fit for the tip of the loop. This time we don't want
# to apply the transformation, but print out its characteristics:

transformation, rms = tip.findTransformation(monomer_configuration)

print "Movement of the tip of the loop:"
print "  RMS difference:", rms

translation = transformation.translation()
rotation = transformation.rotation()
axis, angle = rotation.axisAndAngle()

print "  Rotation axis: ", axis
print "  Rotation angle: ", angle
print "  Translation: ", translation.displacement()