Skip to content

Commit

Permalink
fixed and improved helanal_main()
Browse files Browse the repository at this point in the history
- return data as dict (instead of just logging)
- fixed missing np.mean
- use lib.mdamath were possible
- added regression test
  • Loading branch information
orbeckst committed Jan 7, 2016
1 parent 48c411e commit 0b3a486
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 26 deletions.
90 changes: 66 additions & 24 deletions package/MDAnalysis/analysis/helanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@
import MDAnalysis
from MDAnalysis import FinishTimeException
from MDAnalysis.lib.log import ProgressMeter
from MDAnalysis.lib import mdamath

import logging
logger = logging.getLogger("MDAnalysis.analysis.helanal")
Expand All @@ -128,10 +129,7 @@ def center(coordinates):
"""
return np.mean(coordinates, axis=0)

def veclength(v):
"""Length of vector *v*."""
# note: this is 3 times faster than np.linalg.norm
return np.sqrt(np.dot(v, v))
veclength = mdamath.norm

def vecnorm(a):
"""Return a/|a|"""
Expand All @@ -143,7 +141,7 @@ def vecangle(a, b):
If one of the lengths is 0 then the angle is returned as 0
(instead of `nan`).
"""
angle = np.arccos(np.dot(a, b) / (veclength(a) * veclength(b)))
angle = mdamath.angle(a, b)
if np.isnan(angle):
return 0.0
return angle
Expand Down Expand Up @@ -189,9 +187,9 @@ def helanal_trajectory(universe, selection="name CA", start=None, end=None, begi
*selection*
selection string that selects Calpha atoms [``"name CA"``]
*start*
start residue
start residue resid
*end*
end residue
end residue resid
*begin*
start analysing for time (ps) >= *begin*; ``None`` starts from the
beginning [``None``]
Expand Down Expand Up @@ -227,12 +225,13 @@ def helanal_trajectory(universe, selection="name CA", start=None, end=None, begi
:Raises:
FinishTimeException
If the specified finish time precedes the specified start time or current time stamp of trajectory object.
If the specified finish time precedes the specified start time or
current time stamp of trajectory object.
.. versionchanged:: 0.13.0
New *quiet* keyword to silence frame progress output and most of the
output that used to be printed to stdout is now logged to the logger
MDAnalysis.analysis.helanal (at logelevel *INFO*).
*MDAnalysis.analysis.helanal* (at logelevel *INFO*).
"""
if ref_axis is None:
ref_axis = np.array([0., 0., 1.])
Expand Down Expand Up @@ -482,8 +481,38 @@ def stats(some_list):
def helanal_main(pdbfile, selection="name CA", start=None, end=None, ref_axis=None, permissive=False):
"""Simple HELANAL run on a single frame PDB/GRO.
Computed data are returned as a dict and also logged at level INFO to the
logger *MDAnalysis.analysis.helanal*. A simple way to enable a logger is to
use :func:`~MDAnalysis.lib.log.start_logging`.
.. Note:: Only a single helix is analyzed. Use the selection to specify
the helix, e.g. with "name CA and resid 1:20".
Returns
-------
Dict with keys for
* Height: mean, stdev, abs dev
* Twist: mean, stdev, abs dev
* Residues/turn: mean, stdev, abs dev
* Local bending angles: array for computed angles (per residue)
* Unit twist angles: array for computed angles (per residue)
* Best fit tilt
* Rotation angles: local screw angles (per residue)
Example
-------
Analyze helix 8 in AdK (PDB 4AKE); the standard logger is started and
writes output to the file ``MDAnalysis.log``::
MDAnalysis.start_logging()
data = MDAnalysis.analysis.helanal_main("4ake_A.pdb", selection="name CA and resnum 161-187")
.. versionchanged:: 0.13.0
All output is returned as a dict and logged to the logger
*MDAnalysis.analysis.helanal* instead of being printed to stdout.
"""

universe = MDAnalysis.Universe(pdbfile, permissive=permissive)
Expand All @@ -504,8 +533,8 @@ def helanal_main(pdbfile, selection="name CA", start=None, end=None, ref_axis=No
#print current_origin
#print origins

max_angle = max(bending_angles)
mean_angle = mean(bending_angles)
max_angle = np.max(bending_angles)
mean_angle = np.mean(bending_angles)
#sd calculated using n-1 to replicate original fortran- assumes a limited sample so uses the sample standard
# deviation
sd_angle = sample_sd(bending_angles, mean_angle)
Expand All @@ -514,16 +543,17 @@ def helanal_main(pdbfile, selection="name CA", start=None, end=None, ref_axis=No
#print max_angle, mean_angle, sd_angle, mean_absolute_deviation_angle

#calculate local bending matrix(now it is looking at all i, j combinations)
for i in local_helix_axes:
for j in local_helix_axes:
if (i == j).all():
angle = 0.
else:
angle = np.rad2deg(np.arccos(np.dot(i, j)))
string_angle = "%6.0f\t" % angle
#print string_angle,
#print ''
#TESTED- local bending matrix!
# (not used for helanal_main())
# for i in local_helix_axes:
# for j in local_helix_axes:
# if (i == j).all():
# angle = 0.
# else:
# angle = np.rad2deg(np.arccos(np.dot(i, j)))
# #string_angle = "%6.0f\t" % angle
# #print string_angle,
# #print ''
# #TESTED- local bending matrix!

#Average helical parameters
mean_twist = np.mean(twist)
Expand All @@ -541,6 +571,19 @@ def helanal_main(pdbfile, selection="name CA", start=None, end=None, ref_axis=No
abdev_height = mean_abs_dev(height, mean_height)
#TESTED- average rises

#calculate best fit vector and tilt of said vector
fit_vector, fit_tilt = vector_of_best_fit(origins)

data = {
'Height': np.array([mean_height, sd_height, abdev_height]),
'Twist': np.array([mean_twist, sd_twist, abdev_twist]),
'Residues/turn': np.array([mean_rnou, sd_rnou, abdev_rnou]),
'Local bending angles': np.asarray(bending_angles),
'Unit twist angles': np.asarray(twist),
'Best fit tilt': fit_tilt,
'Rotation Angles': np.asarray(local_screw_angles),
}

logger.info("Height: %g SD: %g ABDEV: %g (Angstroem)", mean_height, sd_height, abdev_height)
logger.info("Twist: %g SD: %g ABDEV: %g", mean_twist, sd_twist, abdev_twist)
logger.info("Residues/turn: %g SD: %g ABDEV: %g", mean_rnou, sd_rnou, abdev_rnou)
Expand All @@ -551,13 +594,12 @@ def helanal_main(pdbfile, selection="name CA", start=None, end=None, ref_axis=No
output = " ".join(["%8.1f\t" % twist_ang for twist_ang in twist])
logger.info("Unit twist angles: %s", output)

#calculate best fit vector and tilt of said vector
fit_vector, fit_tilt = vector_of_best_fit(origins)
logger.info("Best fit tilt: %g", fit_tilt)

output = " ".join(["%.1f" % item for item in local_screw_angles])
logger.info("Rotation Angles from 1 to n-1: %s", output)
logger.info("Rotation Angles from 1 to n-1 (local screw angles): %s", output)

return data

def origin_pdb(origins, pdbfile):
"""Write origins to PDB (multi-frame).
Expand Down
50 changes: 48 additions & 2 deletions testsuite/MDAnalysisTests/analysis/test_helanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,43 @@
import MDAnalysis as mda
import MDAnalysis.analysis.helanal
from MDAnalysis import FinishTimeException
from MDAnalysisTests.datafiles import GRO, XTC, PSF, DCD, HELANAL_BENDING_MATRIX

from MDAnalysisTests.datafiles import (GRO, XTC, PSF, DCD, PDB_small,
HELANAL_BENDING_MATRIX)

# reference data from a single PDB file:
# data = MDAnalysis.analysis.helanal.helanal_main(PDB_small,
# selection="name CA and resnum 161-187")
HELANAL_SINGLE_DATA = {
'Best fit tilt': 1.3309656332019535,
'Height': np.array([ 1.5286051 , 0.19648294, 0.11384312],
dtype=np.float32),
'Local bending angles':
np.array([ 3.44526005, 4.85425806, 4.69548464, 2.39473653,
3.56172442, 3.97128344, 3.41563916, 1.86140978,
5.22997046, 5.41381264, 27.49601364, 39.69839478,
35.05921936, 21.78928566, 9.8632431 , 8.80066967,
5.5344553 , 6.14356709, 10.15450764, 11.07686138,
9.23541832], dtype=np.float32),
'Residues/turn': np.array([ 3.64864163, 0.152694 , 0.1131402 ]),
'Rotation Angles':
np.array([ 87.80540079, -171.86019984, -75.2341296 , 24.61695962,
121.77104796, -134.94786976, -35.07857424, 58.9621866 ,
159.40210233, -104.83368122, -7.54816243, 87.40202629,
-176.13071955, -89.13196878, 17.17321345, 105.26627814,
-147.00075298, -49.36850775, 54.24557615, 156.06486532,
-110.82698327, -5.72138626, 85.36050546, -167.28218858,
-68.23076936]),
'Twist': np.array([ 98.83011627, 4.08171701, 3.07253003],
dtype=np.float32),
'Unit twist angles':
np.array([ 97.23709869, 99.09676361, 97.25350952, 101.76019287,
100.42689514, 97.08784485, 97.07430267, 98.33553314,
97.86578369, 95.45792389, 97.10089111, 95.26415253,
87.93136597, 108.38458252, 95.27167511, 104.01931763,
100.7199707 , 101.48034668, 99.64170074, 94.78946686,
102.50147247, 97.25154877, 104.54204559, 101.42829895],
dtype=np.float32),
}

def read_bending_matrix(fn):
"""Read helanal_bending_matrix.dat into dict of numpy arrays.
Expand Down Expand Up @@ -87,6 +122,17 @@ def test_helanal_trajectory(reference=HELANAL_BENDING_MATRIX,
assert_array_almost_equal(bendingmatrix[label], ref[label],
err_msg="bending matrix stats for {0} mismatch".format(label))

def test_helanal_main(reference=HELANAL_SINGLE_DATA):
u = mda.Universe(PDB_small)
# Helix 8: 161 - 187 http://www.rcsb.org/pdb/explore.do?structureId=4AKE
data = MDAnalysis.analysis.helanal.helanal_main(PDB_small,
selection="name CA and resnum 161-187")
ref = reference
assert_equal(sorted(data.keys()), sorted(ref.keys()),
err_msg="different contents in data dict")
for label in ref.keys():
assert_array_almost_equal(data[label], ref[label], decimal=5,
err_msg="data[{0}] mismatch".format(label))

def test_xtc_striding():
"""testing MDAnalysis.analysis.helanal xtc striding: Check for resolution of Issue #188."""
Expand Down

0 comments on commit 0b3a486

Please sign in to comment.