hydrogen_bondifier 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/History +9 -0
- data/LICENSE +23 -0
- data/README.rdoc +67 -0
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/bin/hydrogen_bondifier.rb +159 -0
- data/lib/hydrogen_bondifier/utils.rb +39 -0
- data/lib/pymol/connections.rb +55 -0
- data/lib/pymol/hydrogen_bonds.rb +157 -0
- data/lib/pymol/orientation.rb +23 -0
- data/lib/pymol/surface.rb +35 -0
- data/lib/pymol.rb +93 -0
- data/reference/all_connections.py +24 -0
- data/reference/campbell_find_hb.py +18 -0
- data/reference/list_hbonds.py +47 -0
- data/reference/test_pymol.rb +125 -0
- data/spec/pymol/connections_spec.rb +25 -0
- data/spec/pymol/hydrogen_bonds_spec.rb +38 -0
- data/spec/pymol/surface_spec.rb +47 -0
- data/spec/pymol_spec.rb +41 -0
- data/spec/scripts/confirm_angle_and_h_dist.rb +126 -0
- data/spec/scripts/confirm_distances.rb +83 -0
- data/spec/scripts/mins.rb +9 -0
- data/spec/scripts/obj_ranges.rb +22 -0
- data/spec/scripts/pdb_ranges.rb +30 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/testfiles/1YQS.pdb +6655 -0
- data/spec/testfiles/1YQS_h_added.pdb +7924 -0
- data/spec/testfiles/2ERK_Hbond.out +302 -0
- data/spec/testfiles/2pERK2_HBOND.out +303 -0
- data/spec/testfiles/2pERK2_Hadded.pdb +5767 -0
- data/spec/testfiles/2pERK2_dis-surf.txt +330 -0
- data/spec/testfiles/little.pdb +210 -0
- data/validation/jtp_vs_insight_angles.png +0 -0
- data/validation/jtp_vs_insight_distances.png +0 -0
- data/validation/jtp_vs_insight_surface_distances.png +0 -0
- data/validation/jtp_vs_insight_surface_distances_aa_geometric.png +0 -0
- data/validation/jtp_vs_insight_surface_distances_center_of_grav.png +0 -0
- data/validation/jtp_vs_insight_surface_distances_closest_atom.png +0 -0
- metadata +113 -0
data/.gitignore
ADDED
data/History
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2009 Howard Hughes Medical Institute
|
4
|
+
Authored by John T. Prince with the help of Kevin Sours and guidance of
|
5
|
+
Natalie G. Ahn
|
6
|
+
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
9
|
+
in the Software without restriction, including without limitation the rights
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
12
|
+
furnished to do so, subject to the following conditions:
|
13
|
+
|
14
|
+
The above copyright notice and this permission notice shall be included in
|
15
|
+
all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
23
|
+
THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
= HydrogenBondifier
|
2
|
+
|
3
|
+
Provides a scriptable interface to pymol, a few basic commands for structure information, and an executable for determining hydrogen bond characteristics as would be useful to those doing hydrogen exchange experiments.
|
4
|
+
|
5
|
+
== Examples
|
6
|
+
|
7
|
+
=== Pymol Interface
|
8
|
+
|
9
|
+
outfile = "file_with_h_added.pdb"
|
10
|
+
Pymol.run do |pm|
|
11
|
+
pm.cmd "load file.pdb, mymodel"
|
12
|
+
pm.cmd "h_add"
|
13
|
+
pm.cmd "save #{outfile}"
|
14
|
+
end
|
15
|
+
|
16
|
+
The real power lies in scripting pymol. Here's an example of extracting out all connections in a model:
|
17
|
+
|
18
|
+
# this script causes pymol to output all atom connections
|
19
|
+
cnx_script = %Q{
|
20
|
+
|
21
|
+
from pymol import cmd
|
22
|
+
|
23
|
+
def all_connections(selection):
|
24
|
+
"""
|
25
|
+
USAGE
|
26
|
+
|
27
|
+
all_connections selection
|
28
|
+
|
29
|
+
returns lines: "CONNECTION: id - id"
|
30
|
+
"""
|
31
|
+
stored.xs = []
|
32
|
+
cmd.iterate(selection, 'stored.xs.append( index )')
|
33
|
+
for i in stored.xs:
|
34
|
+
selName = "neighbor%s" % i
|
35
|
+
ids = cmd.select(selName, ("%s and neighbor id %s" % (selection, i)))
|
36
|
+
base = "CONNECTION: %s - " % i
|
37
|
+
to_print = base + "%s"
|
38
|
+
print_string = 'print "' + to_print + '" % index'
|
39
|
+
cmd.iterate(selName, print_string )
|
40
|
+
|
41
|
+
cmd.extend("all_connections", all_connections)
|
42
|
+
}
|
43
|
+
output = Pymol.run(:script => cnx_script) do |pm|
|
44
|
+
pm.cmd "load file.pdb, mymodel"
|
45
|
+
pm.cmd "all_connections mymodel"
|
46
|
+
end
|
47
|
+
|
48
|
+
# now we just parse the output
|
49
|
+
values_of_output_lines = output.map {|line| line.match(/^CONNECTION: (.*)/)[1] }.compact
|
50
|
+
|
51
|
+
connection_pairs = values_of_output_lines.map do |v|
|
52
|
+
v.split(' - ').map {|v| v.to_i }.sort
|
53
|
+
end.uniq
|
54
|
+
|
55
|
+
=== Basic methods
|
56
|
+
|
57
|
+
Some methods have been completely wrapped in a script and parser to deliver desired output:
|
58
|
+
|
59
|
+
# all atomic pairs as atom ids
|
60
|
+
connections = Pymol::Connections.from_pdb("file.pdb")
|
61
|
+
|
62
|
+
# coordinates of the molecules surface
|
63
|
+
surface_coords = Pymol::Surface.from_pdb("file.pdb")
|
64
|
+
|
65
|
+
== Copyright
|
66
|
+
|
67
|
+
See LICENSE
|
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rake'
|
4
|
+
require 'jeweler'
|
5
|
+
require 'rake/testtask'
|
6
|
+
# require 'rcov/rcovtask'
|
7
|
+
|
8
|
+
NAME = "hydrogen_bondifier"
|
9
|
+
WEBSITE_BASE = "website"
|
10
|
+
WEBSITE_OUTPUT = WEBSITE_BASE + "/output"
|
11
|
+
|
12
|
+
gemspec = Gem::Specification.new do |s|
|
13
|
+
s.name = NAME
|
14
|
+
s.authors = ["John T. Prince"]
|
15
|
+
s.email = "jtprince@gmail.com"
|
16
|
+
s.homepage = "http://jtprince.github.com/" + NAME
|
17
|
+
s.summary = "finds hydrogen bonds using pymol"
|
18
|
+
s.description = "uses pymol"
|
19
|
+
#s.rubyforge_project = 'mspire'
|
20
|
+
# s.add_dependency("ms-core", ">= 0.0.2")
|
21
|
+
# s.add_development_dependency("ms-testdata", ">= 0.18.0")
|
22
|
+
s.add_development_dependency("spec-more")
|
23
|
+
s.files << "VERSION"
|
24
|
+
end
|
25
|
+
|
26
|
+
Jeweler::Tasks.new(gemspec)
|
27
|
+
|
28
|
+
Rake::TestTask.new(:spec) do |spec|
|
29
|
+
spec.libs << 'lib' << 'spec'
|
30
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
31
|
+
spec.verbose = true
|
32
|
+
end
|
33
|
+
|
34
|
+
#Rcov::RcovTask.new do |spec|
|
35
|
+
# spec.libs << 'spec'
|
36
|
+
# spec.pattern = 'spec/**/*_spec.rb'
|
37
|
+
# spec.verbose = true
|
38
|
+
#end
|
39
|
+
|
40
|
+
require 'rake/rdoctask'
|
41
|
+
Rake::RDocTask.new do |rdoc|
|
42
|
+
base_rdoc_output_dir = WEBSITE_OUTPUT + '/rdoc'
|
43
|
+
version = File.read('VERSION')
|
44
|
+
rdoc.rdoc_dir = base_rdoc_output_dir + "/#{version}"
|
45
|
+
rdoc.title = NAME + ' ' + version
|
46
|
+
rdoc.rdoc_files.include('README*')
|
47
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
48
|
+
end
|
49
|
+
|
50
|
+
task :default => :spec
|
51
|
+
|
52
|
+
task :build => :gemspec
|
53
|
+
|
54
|
+
# credit: Rakefile modeled after Jeweler's
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.2
|
@@ -0,0 +1,159 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Previous users of Insight(?) used a (<60deg acute angle) cutoff (>120 obtuse)
|
4
|
+
# and a 2.85, 2.90, or 3.0 Angstrom cutoff for the hydrogen
|
5
|
+
|
6
|
+
# see this on hydrogen bond finding in pymol:
|
7
|
+
# http://www.mail-archive.com/pymol-users@lists.sourceforge.net/msg06680.html
|
8
|
+
|
9
|
+
require 'yaml'
|
10
|
+
require 'narray'
|
11
|
+
require 'optparse'
|
12
|
+
require 'bio/db/pdb'
|
13
|
+
require 'pymol/surface'
|
14
|
+
require 'pymol/connections'
|
15
|
+
require 'pymol/hydrogen_bonds'
|
16
|
+
require 'hydrogen_bondifier/utils'
|
17
|
+
|
18
|
+
def putsv(*args)
|
19
|
+
puts(*args) if $VERBOSE
|
20
|
+
end
|
21
|
+
|
22
|
+
opt = {
|
23
|
+
:max_dist => 3.2,
|
24
|
+
:max_angle => 60,
|
25
|
+
:exclude_water => true,
|
26
|
+
:add_hydrogen => true,
|
27
|
+
:delim => ','
|
28
|
+
}
|
29
|
+
|
30
|
+
$VERBOSE = true
|
31
|
+
|
32
|
+
output_postfix = "_hbonds.csv"
|
33
|
+
|
34
|
+
opts = OptionParser.new do |op|
|
35
|
+
op.banner = "usage: #{File.basename(__FILE__)} <file>.pdb ..."
|
36
|
+
op.separator "outputs: <file>#{output_postfix}"
|
37
|
+
op.separator " "
|
38
|
+
op.separator "cutoffs: "
|
39
|
+
op.on("-d", "--max-distance <#{opt[:max_dist]}>", Float, "max distance between donor and acceptor") {|v| opt[:max_dist] = v }
|
40
|
+
op.on("-a", "--max-angle <#{opt[:max_angle]}>", Float, "max angle in degrees") {|v| opt[:max_angle] = v }
|
41
|
+
|
42
|
+
op.separator " "
|
43
|
+
op.separator "options: "
|
44
|
+
op.on("--radians", "output angles in radians") {|v| opt[:radians] = v }
|
45
|
+
op.on("--no-exclude-water", "leaves water molecules in the model") {|v| opt[:exclude_water] = false }
|
46
|
+
op.on("--no-add-hydrogen", "can use if pdb contains all hydrogens") {|v| opt[:add_hydrogen] = false }
|
47
|
+
op.on("--aa-to-surf", "outputs min dist to surface for amino acids") {|v| opt[:aa_to_surf] = v }
|
48
|
+
op.on("-q", "--quiet", "no unnecessary output") {|v| $VERBOSE = false }
|
49
|
+
op.separator " "
|
50
|
+
op.separator " * if pymol executable cannot be found, you can specify it as the value of the"
|
51
|
+
op.separator " environmental variable 'PYMOL_EXE'"
|
52
|
+
op.separator " * your working directory should be writeable and directory with file"
|
53
|
+
op.separator " * in output: D=donor, H=hydrogen, A=acceptor"
|
54
|
+
end
|
55
|
+
|
56
|
+
opts.parse!
|
57
|
+
|
58
|
+
if ARGV.size == 0
|
59
|
+
puts opts
|
60
|
+
exit
|
61
|
+
end
|
62
|
+
|
63
|
+
files = ARGV.map
|
64
|
+
ARGV.clear
|
65
|
+
|
66
|
+
categories = %w(D_id H_id A_id H_name D_res D_res_id D_name A_res A_res_id A_name angle D_A_dist H_A_dist H_dist_to_surf)
|
67
|
+
|
68
|
+
files.each do |file|
|
69
|
+
|
70
|
+
####################
|
71
|
+
# need to implement copying of file, etc......
|
72
|
+
####################
|
73
|
+
|
74
|
+
# create filenames for output files
|
75
|
+
base = file.chomp(File.extname(file))
|
76
|
+
|
77
|
+
pdb_with_hydrogens =
|
78
|
+
if opt[:add_hydrogen]
|
79
|
+
pdb_plus_h_added = base + '_Hadded.pdb'
|
80
|
+
putsv "writing to: #{pdb_plus_h_added}"
|
81
|
+
Pymol::HydrogenBonds.pdb_with_hydrogens(file, pdb_plus_h_added)
|
82
|
+
else
|
83
|
+
file
|
84
|
+
end
|
85
|
+
|
86
|
+
base_h_added = pdb_with_hydrogens.chomp(File.extname(pdb_with_hydrogens))
|
87
|
+
|
88
|
+
hbond_arrays = Pymol::HydrogenBonds.from_pdb(pdb_with_hydrogens, opt)
|
89
|
+
|
90
|
+
# http://pymolwiki.org/index.php/Surface#Exporting_Surface.2FMesh_Coordinates_to_File
|
91
|
+
surface_coords = Pymol::Surface.from_pdb(pdb_with_hydrogens)
|
92
|
+
|
93
|
+
sc_sz = surface_coords.size
|
94
|
+
(xs, ys, zs) = [nil,nil,nil].map { NArray.float(sc_sz) }
|
95
|
+
surface_coords.each_with_index do |xyz, i|
|
96
|
+
xs[i] = xyz[0]
|
97
|
+
ys[i] = xyz[1]
|
98
|
+
zs[i] = xyz[2]
|
99
|
+
end
|
100
|
+
|
101
|
+
# get the distance from hydrogen to surface
|
102
|
+
# 0 => donor
|
103
|
+
# 1 => hydrogen
|
104
|
+
# 2 => acceptor
|
105
|
+
which_atom = 1
|
106
|
+
|
107
|
+
# just output distance to the surface of the amino acid
|
108
|
+
if opt[:aa_to_surf]
|
109
|
+
amino_acids = []
|
110
|
+
hbond_arrays.each do |a,b,c|
|
111
|
+
[a,c].each {|atom| amino_acids << atom.residue }
|
112
|
+
end
|
113
|
+
amino_acids.uniq.each do |res|
|
114
|
+
|
115
|
+
#### CENTER OF GRAVITY
|
116
|
+
#coord = res.centreOfGravity
|
117
|
+
#### GEOMETRIC CENTER
|
118
|
+
#coord = res.geometricCentre
|
119
|
+
#min_dist = Bio::PDB::Utils.distance_to_many(coord, [xs, ys, zs] ).min
|
120
|
+
|
121
|
+
#### MINIMUM DISTANCE TO ANY ATOM
|
122
|
+
min_dist = res.atoms.map {|atom| Bio::PDB::Utils.distance_to_many(atom.xyz, [xs, ys, zs] ).min }.min
|
123
|
+
# output to mimic older output
|
124
|
+
puts "#{res.resName}#{res.id} minimum_distance_to_surface #{min_dist}"
|
125
|
+
end
|
126
|
+
|
127
|
+
next
|
128
|
+
end
|
129
|
+
|
130
|
+
putsv "calculating distances to surface ..."
|
131
|
+
# also we are gathering all the data we need.
|
132
|
+
characterized = hbond_arrays.map do |data|
|
133
|
+
coords = Array.new(3)
|
134
|
+
na_coords = Array.new(3)
|
135
|
+
data[0,3].each_with_index do |atom,i|
|
136
|
+
coords[i] = atom.xyz
|
137
|
+
na_coords[i] = NArray.to_na(coords[i].to_a)
|
138
|
+
end
|
139
|
+
|
140
|
+
dists_to_surface = Bio::PDB::Utils.distance_to_many(coords[which_atom], [xs, ys, zs] )
|
141
|
+
|
142
|
+
data[3] = Bio::PDB::Utils.rad2deg(data[3]) unless opt[:radians]
|
143
|
+
ids = data[0,3].map {|atom| atom.serial }
|
144
|
+
(don, acc) = [data[0], data[2]].map {|at| [at.resName, at.residue.id, at.name] }
|
145
|
+
ids.push(data[1].name)
|
146
|
+
id_part = ids.push(*don).push(*acc)
|
147
|
+
id_part.push(*(data[3,3]))
|
148
|
+
id_part.push(dists_to_surface.min)
|
149
|
+
end
|
150
|
+
|
151
|
+
final_output = base_h_added + output_postfix
|
152
|
+
File.open(final_output, 'w') do |out|
|
153
|
+
out.puts categories.join(opt[:delim])
|
154
|
+
characterized.each do |array|
|
155
|
+
out.puts array.join(opt[:delim])
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'narray'
|
2
|
+
|
3
|
+
module Bio
|
4
|
+
class PDB
|
5
|
+
module Utils
|
6
|
+
|
7
|
+
module_function
|
8
|
+
# calculates the angle between 2 Narray vecs (in radians)
|
9
|
+
def angle_between_vectors(vec1, vec2)
|
10
|
+
vec1 = NArray.to_na(vec1.to_a) unless vec1.is_a?(NArray)
|
11
|
+
vec2 = NArray.to_na(vec2.to_a) unless vec2.is_a?(NArray)
|
12
|
+
nil_vec = NArray[0.0, 0.0, 0.0]
|
13
|
+
return nil if (vec1 == nil_vec or vec2 == nil_vec)
|
14
|
+
(mag_a, mag_b) = [vec1,vec2].map {|vec| Math::sqrt((vec*vec).sum) }
|
15
|
+
# acos(dotprod / |a||b|)
|
16
|
+
Bio::PDB::Utils.acos( (vec1 * vec2).sum.to_f / (mag_a * mag_b) )
|
17
|
+
end
|
18
|
+
|
19
|
+
def angle_from_coords(triplet)
|
20
|
+
a = triplet.last - triplet[1]
|
21
|
+
b = triplet.first - triplet[1]
|
22
|
+
angle_between_vectors(a,b)
|
23
|
+
end
|
24
|
+
|
25
|
+
# other is 3 parallel NArray objects with the x, y and z coordinates
|
26
|
+
# or a triplet like coord
|
27
|
+
def distance_to_many(coord, other)
|
28
|
+
# distance may be another narray or an array of vecs
|
29
|
+
sq_diffs = []
|
30
|
+
(0...(coord.size)).each do |i|
|
31
|
+
pos = coord[i]
|
32
|
+
oth = other[i]
|
33
|
+
sq_diffs << (oth - pos)**2
|
34
|
+
end
|
35
|
+
NMath.sqrt(sq_diffs.inject {|sum, vec| sum + vec })
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'pymol'
|
2
|
+
|
3
|
+
class Pymol
|
4
|
+
module Connections
|
5
|
+
module Script
|
6
|
+
module_function
|
7
|
+
|
8
|
+
def all_connections_script
|
9
|
+
%Q{
|
10
|
+
from pymol import cmd
|
11
|
+
|
12
|
+
def all_connections(selection):
|
13
|
+
"""
|
14
|
+
USAGE
|
15
|
+
|
16
|
+
all_connections selection
|
17
|
+
|
18
|
+
returns lines: "CONNECTION: id - id"
|
19
|
+
"""
|
20
|
+
stored.xs = []
|
21
|
+
cmd.iterate(selection, 'stored.xs.append( index )')
|
22
|
+
for i in stored.xs:
|
23
|
+
selName = "neighbor%s" % i
|
24
|
+
ids = cmd.select(selName, ("%s and neighbor id %s" % (selection, i)))
|
25
|
+
base = "CONNECTION: %s - " % i
|
26
|
+
to_print = base + "%s"
|
27
|
+
print_string = 'print "' + to_print + '" % index'
|
28
|
+
cmd.iterate(selName, print_string )
|
29
|
+
|
30
|
+
cmd.extend("all_connections", all_connections)
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
# returns an array of all pairs of atom IDs with no redundancy
|
35
|
+
def all_connections_parser(reply_from_all_connections, flag=/^CONNECTION: /)
|
36
|
+
pairs = reply_from_all_connections.split("\n").select {|v| v =~ flag }.map do |line|
|
37
|
+
line.split(':').last.split(' - ').map {|v| v.to_i }.sort
|
38
|
+
end
|
39
|
+
pairs.uniq
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
module_function
|
45
|
+
|
46
|
+
# returns all connections as pairs of ID's (all uniq)
|
47
|
+
def from_pdb(pdb)
|
48
|
+
reply = Pymol.run(:msg => 'getting all atom connections', :script => Pymol::Connections::Script.all_connections_script) do |pm|
|
49
|
+
pm.cmd "load #{pdb}, mymodel"
|
50
|
+
pm.cmd "all_connections mymodel"
|
51
|
+
end
|
52
|
+
Pymol::Connections::Script.all_connections_parser(reply)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'pymol'
|
2
|
+
require 'pymol/connections'
|
3
|
+
|
4
|
+
require 'bio/db/pdb'
|
5
|
+
require 'hydrogen_bondifier/utils'
|
6
|
+
|
7
|
+
class Pymol
|
8
|
+
EXCLUDE_WATER_FILTER = " &! resn hoh"
|
9
|
+
module HydrogenBonds
|
10
|
+
|
11
|
+
DEFAULT_FIND_PAIRS_OPTS = {:max_dist => 3.2, :max_angle => 60, :exclude_water => true }
|
12
|
+
DEFAULT_H_BOND_OPTS = {
|
13
|
+
:select_donor => "and (elem n,o and (neighbor hydro))",
|
14
|
+
:select_acceptor => "and (elem o or (elem n and not (neighbor hydro)))",
|
15
|
+
}
|
16
|
+
|
17
|
+
module_function
|
18
|
+
|
19
|
+
def pdb_with_hydrogens(pdb_filename, newname=nil)
|
20
|
+
pfile = pdb_filename
|
21
|
+
newname = pfile.chomp(File.extname(pfile)) + "_plus_h.pdb" unless newname
|
22
|
+
Pymol.run(:msg => 'creating pdb with hydrogens') do |pm|
|
23
|
+
pm.cmd "load #{pfile}, mymodel"
|
24
|
+
pm.cmd "h_add"
|
25
|
+
pm.cmd "save #{newname}"
|
26
|
+
end
|
27
|
+
newname
|
28
|
+
end
|
29
|
+
|
30
|
+
# returns [id1, id2, distance] for each atom
|
31
|
+
def find_pairs(file, sel1, sel2, opt={})
|
32
|
+
opt = DEFAULT_FIND_PAIRS_OPTS.merge( opt )
|
33
|
+
exclude_water_command = opt[:exclude_water] ? EXCLUDE_WATER_FILTER : ""
|
34
|
+
hbond_script = Pymol::HydrogenBonds.list_hb_script(sel1, sel2)
|
35
|
+
reply = Pymol.run(:msg => "getting hydrogen bonds", :script => hbond_script) do |pm|
|
36
|
+
pm.cmd "load #{file}, mymodel"
|
37
|
+
pm.cmd "list_hb mymodel#{exclude_water_command}, cutoff=#{opt[:max_dist]}, angle=#{opt[:max_angle]}"
|
38
|
+
end
|
39
|
+
Pymol::HydrogenBonds.list_hb_parser(reply)
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
# returns [donor, hydrogen, acceptor, angle, don_to_acc_dist, h_to_acc_dist]
|
44
|
+
# The first three are Bio::PDB::Record::ATOM structs.
|
45
|
+
# respects DEFAULT_FIND_PAIRS_OPTS and DEFAULT_H_BOND_OPTS
|
46
|
+
# expects that hydrogen bonds are already specified in the PDB file
|
47
|
+
# returns an array triplet atom IDs [donor, hydrogen, acceptor]
|
48
|
+
# :connections can be passed in (an array of arrays of all unique pairwise
|
49
|
+
# connections [by ID])
|
50
|
+
def from_pdb(file, opt={})
|
51
|
+
opt = DEFAULT_H_BOND_OPTS.merge(opt)
|
52
|
+
|
53
|
+
pairs = find_pairs(file, opt[:select_donor], opt[:select_acceptor], opt)
|
54
|
+
|
55
|
+
connection_pairs = Pymol::Connections.from_pdb(file)
|
56
|
+
connection_index = Hash.new {|h,k| h[k] = [] }
|
57
|
+
connection_pairs.each do |pair|
|
58
|
+
connection_index[pair.first] << pair.last
|
59
|
+
connection_index[pair.last] << pair.first
|
60
|
+
end
|
61
|
+
|
62
|
+
# make an index of the atoms
|
63
|
+
pdb = Bio::PDB.new(IO.read(file))
|
64
|
+
pdb.extend(Bio::PDB::AtomFinder)
|
65
|
+
atom_index = []
|
66
|
+
pdb.each_atom do |atom|
|
67
|
+
atom_index[atom.serial] = atom
|
68
|
+
end
|
69
|
+
|
70
|
+
max_angle = opt[:max_angle] || DEFAULT_FIND_PAIRS_OPTS[:max_angle]
|
71
|
+
cutoff_in_degress = max_angle
|
72
|
+
|
73
|
+
hbonds = []
|
74
|
+
puts "calculating angles and distances" if $VERBOSE
|
75
|
+
pairs.each do |don_id, acc_id, don_to_acc_dist|
|
76
|
+
donor = atom_index[don_id]
|
77
|
+
acceptor = atom_index[acc_id]
|
78
|
+
next if (acceptor.element == 'H' or donor.element == 'H') # check for sloppy queries
|
79
|
+
acceptor_xyz = acceptor.xyz
|
80
|
+
donor_xyz = donor.xyz
|
81
|
+
connection_index[don_id].each do |id|
|
82
|
+
hydrogen = atom_index[id]
|
83
|
+
next if hydrogen.element != 'H'
|
84
|
+
|
85
|
+
#puts "ACCEPT ID: "
|
86
|
+
#puts acc_id
|
87
|
+
#p acceptor_xyz
|
88
|
+
#puts "HYDRO ID: "
|
89
|
+
#p id
|
90
|
+
#p hydrogen.xyz
|
91
|
+
|
92
|
+
angle = Bio::PDB::Utils.angle_from_coords([donor_xyz, hydrogen.xyz, acceptor_xyz])
|
93
|
+
h_to_acc_dist = Bio::PDB::Utils.distance(hydrogen.xyz, acceptor_xyz)
|
94
|
+
|
95
|
+
#puts "DISTANCES: "
|
96
|
+
#p don_to_acc_dist
|
97
|
+
#p h_to_acc_dist
|
98
|
+
# abort 'here'
|
99
|
+
|
100
|
+
# I'm not sure why the angle cutoff is not being respected, but we
|
101
|
+
# can enforce it right here since we want the angles anyway
|
102
|
+
if (180.0 - Bio::PDB::Utils.rad2deg(angle)) <= cutoff_in_degress
|
103
|
+
hbonds << [donor, hydrogen, acceptor, angle, don_to_acc_dist, h_to_acc_dist]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
hbonds
|
108
|
+
end
|
109
|
+
|
110
|
+
def list_hb_script(select1, select2)
|
111
|
+
%Q{
|
112
|
+
# modified by JTP from here:
|
113
|
+
# Dr. Robert L. Campbell
|
114
|
+
# http://pldserver1.biochem.queensu.ca/~rlc/work/pymol/
|
115
|
+
# find_pairs is an undocumented method but mode==1 is hydrogen bond finding
|
116
|
+
|
117
|
+
from pymol import cmd
|
118
|
+
|
119
|
+
def list_hb(selection,cutoff=3.2,angle=55,hb_list_name='hbonds'):
|
120
|
+
"""
|
121
|
+
USAGE
|
122
|
+
|
123
|
+
list_hb selection, [cutoff (default=3.2)], [angle (default=55)], [hb_list_name]
|
124
|
+
|
125
|
+
e.g.
|
126
|
+
list_hb 1abc & c. a &! r. hoh, cutoff=3.2, hb_list_name=abc-hbonds
|
127
|
+
"""
|
128
|
+
cutoff=float(cutoff)
|
129
|
+
angle=float(angle)
|
130
|
+
hb = cmd.find_pairs("((byres "+selection+") #{select1})","((byres "+selection+") #{select2})",mode=1,cutoff=cutoff,angle=angle)
|
131
|
+
# sort the list for easier reading
|
132
|
+
hb.sort(lambda x,y:(cmp(x[0][1],y[0][1])))
|
133
|
+
|
134
|
+
for pairs in hb:
|
135
|
+
print "PAIR:",
|
136
|
+
for ind in [0,1]:
|
137
|
+
cmd.iterate("%s and index %s" % (pairs[ind][0],pairs[ind][1]), 'print "%s/%3s`%s/%s/%s " % (chain,resn,resi,name,index),')
|
138
|
+
print "%.2f" % cmd.distance(hb_list_name,"%s and index %s" % (pairs[0][0],pairs[0][1]),"%s and index %s" % (pairs[1][0],pairs[1][1]))
|
139
|
+
|
140
|
+
cmd.extend("list_hb",list_hb)
|
141
|
+
}
|
142
|
+
end
|
143
|
+
|
144
|
+
# takes output of hb_script and returns an array of triplets [id1, id2, distance]
|
145
|
+
def list_hb_parser(pymol_hb_script_reply, flag=/^PAIR: /)
|
146
|
+
# grab each line of output with specified header, then remove the header
|
147
|
+
hbond_lines = pymol_hb_script_reply.split("\n").select {|line| line =~ flag }.map {|line| line.sub(flag,'') }
|
148
|
+
|
149
|
+
ids_and_distances = hbond_lines.map do |line|
|
150
|
+
# A/THR`325/N/2478 A/ALA`323/O/2468 3.05
|
151
|
+
(first, second, dist) = line.split(/\s+/)
|
152
|
+
(id1, id2) = [first, second].map {|v| v.split('/').last.to_i }
|
153
|
+
[id1, id2, dist.to_f]
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Pymol
|
2
|
+
module Orientation
|
3
|
+
|
4
|
+
module_function
|
5
|
+
# http://www.mail-archive.com/pymol-users@lists.sourceforge.net/msg06973.html
|
6
|
+
def orient_to_pdb_coords_script
|
7
|
+
%q{
|
8
|
+
def orient_to_pdb_coords():
|
9
|
+
"""
|
10
|
+
USAGE
|
11
|
+
|
12
|
+
orient_to_pdb_coords
|
13
|
+
"""
|
14
|
+
cmd.reset()
|
15
|
+
cmd.origin(position=[0,0,0])
|
16
|
+
cmd.center("origin")
|
17
|
+
cmd.move('z',-cmd.get_view()[11])
|
18
|
+
|
19
|
+
cmd.extend("orient_to_pdb_coords", orient_to_pdb_coords)
|
20
|
+
}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'pymol'
|
2
|
+
require 'pymol/orientation'
|
3
|
+
|
4
|
+
class Pymol
|
5
|
+
module Surface
|
6
|
+
module_function
|
7
|
+
# returns three arrays for the x,y,z coords
|
8
|
+
def obj_file_to_coords(file)
|
9
|
+
coords = []
|
10
|
+
IO.foreach(file) do |line|
|
11
|
+
if line =~ /^v /
|
12
|
+
pieces = line.split(' ')
|
13
|
+
pieces.shift # remove the 'v'
|
14
|
+
coords << pieces.map {|v| v.to_f }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
coords
|
18
|
+
end
|
19
|
+
|
20
|
+
# returns coordinates
|
21
|
+
# http://pymolwiki.org/index.php/Surface#Exporting_Surface.2FMesh_Coordinates_to_File
|
22
|
+
def from_pdb(file, postfix = "_surface.obj", delete_tmp=true)
|
23
|
+
outfile = file.chomp(File.extname(file)) + postfix
|
24
|
+
Pymol.run(:msg => 'creating surface', :script => Pymol::Orientation.orient_to_pdb_coords_script) do |pm|
|
25
|
+
pm.cmd "load #{file}, mymodel"
|
26
|
+
pm.cmd "orient_to_pdb_coords"
|
27
|
+
pm.cmd "show surface, mymodel"
|
28
|
+
pm.cmd "save #{outfile}"
|
29
|
+
end
|
30
|
+
coords = self.obj_file_to_coords(outfile)
|
31
|
+
File.unlink outfile if delete_tmp
|
32
|
+
coords
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|