hydrogen_bondifier 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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