hydrogen_bondifier 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/.gitignore +3 -0
  2. data/History +9 -0
  3. data/LICENSE +23 -0
  4. data/README.rdoc +67 -0
  5. data/Rakefile +54 -0
  6. data/VERSION +1 -0
  7. data/bin/hydrogen_bondifier.rb +159 -0
  8. data/lib/hydrogen_bondifier/utils.rb +39 -0
  9. data/lib/pymol/connections.rb +55 -0
  10. data/lib/pymol/hydrogen_bonds.rb +157 -0
  11. data/lib/pymol/orientation.rb +23 -0
  12. data/lib/pymol/surface.rb +35 -0
  13. data/lib/pymol.rb +93 -0
  14. data/reference/all_connections.py +24 -0
  15. data/reference/campbell_find_hb.py +18 -0
  16. data/reference/list_hbonds.py +47 -0
  17. data/reference/test_pymol.rb +125 -0
  18. data/spec/pymol/connections_spec.rb +25 -0
  19. data/spec/pymol/hydrogen_bonds_spec.rb +38 -0
  20. data/spec/pymol/surface_spec.rb +47 -0
  21. data/spec/pymol_spec.rb +41 -0
  22. data/spec/scripts/confirm_angle_and_h_dist.rb +126 -0
  23. data/spec/scripts/confirm_distances.rb +83 -0
  24. data/spec/scripts/mins.rb +9 -0
  25. data/spec/scripts/obj_ranges.rb +22 -0
  26. data/spec/scripts/pdb_ranges.rb +30 -0
  27. data/spec/spec_helper.rb +6 -0
  28. data/spec/testfiles/1YQS.pdb +6655 -0
  29. data/spec/testfiles/1YQS_h_added.pdb +7924 -0
  30. data/spec/testfiles/2ERK_Hbond.out +302 -0
  31. data/spec/testfiles/2pERK2_HBOND.out +303 -0
  32. data/spec/testfiles/2pERK2_Hadded.pdb +5767 -0
  33. data/spec/testfiles/2pERK2_dis-surf.txt +330 -0
  34. data/spec/testfiles/little.pdb +210 -0
  35. data/validation/jtp_vs_insight_angles.png +0 -0
  36. data/validation/jtp_vs_insight_distances.png +0 -0
  37. data/validation/jtp_vs_insight_surface_distances.png +0 -0
  38. data/validation/jtp_vs_insight_surface_distances_aa_geometric.png +0 -0
  39. data/validation/jtp_vs_insight_surface_distances_center_of_grav.png +0 -0
  40. data/validation/jtp_vs_insight_surface_distances_closest_atom.png +0 -0
  41. metadata +113 -0
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ .*.swp
2
+ *.gemspec
3
+ pkg
data/History ADDED
@@ -0,0 +1,9 @@
1
+ == 0.0.2 / 2010-02-10
2
+
3
+ * switched to system calls using temp files. Note that Tempfiles on windows do not work at least the way I am trying to use them.
4
+
5
+ == 0.0.1 / 2009-12
6
+
7
+ * using pipes
8
+
9
+
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