rubabel 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,189 @@
1
+ # rubabel
2
+
3
+ Ruby interface to the openbabel ruby bindings (or the openbabel gem). The
4
+ interface attempts to be a ruby-ish analogue of
5
+ [pybel](http://openbabel.org/docs/current/UseTheLibrary/Python_PybelAPI.html).
6
+
7
+ ## Examples
8
+
9
+ The [Chemistry Toolkit Rosetta Wiki](http://ctr.wikia.com/wiki/Chemistry_Toolkit_Rosetta_Wiki) has a lot of examples you can check out.
10
+
11
+ ### Creating a molecule
12
+
13
+ #### From a string
14
+
15
+ require 'rubabel'
16
+
17
+ # by default, reads in smiles strings
18
+ serine = Rubabel["C(C(C(=O)O)N)O"]
19
+ # more formally:
20
+ serine = Rubabel::Molecule.from_string("C(C(C(=O)O)N)O")
21
+
22
+ # also any other format openbabel supports, for example inchi
23
+ serine = Rubabel["InChI=1S/C3H7NO3/c4-2(1-5)3(6)7/h2,5H,1,4H2,(H,6,7)", :inchi]
24
+
25
+ # from the internet:
26
+ mol = Rubabel[some_molecule, Rubabel.format_from_mime(some_mime_type)]
27
+
28
+ Find out all the formats Rubabel supports (hash is format key pointing to the description):
29
+
30
+ hash = Rubabel.in_formats
31
+ hash = Rubabel.out_formats
32
+
33
+ #### From a file
34
+
35
+ Reading multiple entries from a file:
36
+
37
+ Rubabel.foreach("file.sdf") do |mol|
38
+ puts mol.exact_mass
39
+ end
40
+
41
+ Foreach returns an enumerator that can be chained:
42
+
43
+ # return an array of every unique atom type in the file
44
+ uniq_atom_types = Rubabel.foreach("file.mol").flat_map {|mol| mol.map(&:type) }.uniq
45
+
46
+ Read a single molecule from a file (reads only the first molecule)
47
+
48
+ mol = Rubabel::Molecule.from_file("file.sdf")
49
+ # handles gzipped files seamlessly:
50
+ mol = Rubabel::Molecule.from_file("file.sdf.gz")
51
+ mol = Rubabel.molecule_from_file("file.sdf") # alternative
52
+
53
+ # explicit format for uninformative/wrong extensions:
54
+ mol = Rubabel::Molecule.from_file("file", :sdf)
55
+
56
+ ### Writing/Drawing
57
+
58
+ #### create string output
59
+
60
+ mol = Rubabel["OCC"] # ethanol
61
+
62
+ mol.to_s # canonical smiles -> "CCO"
63
+ mol.csmiles # same thing
64
+
65
+ mol.to_s(:smi) # smiles -> "OCC"
66
+ mol.smiles # same thing
67
+
68
+ For inclusion in a file with standard smiles formatting (SMILES\tID\n):
69
+
70
+ can_smiles_string = mol.write_string # -> "CCO\t\n"
71
+ mol.title = "ethanol"
72
+ can_smiles_string = mol.write(:can) # -> "CCO\tethanol\n"
73
+
74
+ Other formats in the same manner:
75
+
76
+ pdb_string = mol.write(:pdb)
77
+
78
+ Write to a file directly (single molecule only; depends on file extension for type):
79
+
80
+ # write to a smiles file
81
+ mol.write("file.smi")
82
+ mol.write_file("file.smi")
83
+
84
+ Write multiple molecules to a file:
85
+
86
+ File.open("somefile.pdb", 'w') do |out|
87
+ molecules.each {|mol| out.print mol.write(:pdb) }
88
+ end
89
+
90
+ #### Drawing
91
+
92
+ If you write to svg or png (png uses mini_magick to convert from svg) then the
93
+ molecule is automatically drawn in 2D:
94
+
95
+ mol = Rubabel["NCC(O)C(=O)O"]
96
+ mol.write("file.svg")
97
+
98
+ # must have imagemagick ('convert' command) and mini_magick gem installed
99
+ mol.write("file.png")
100
+
101
+ ### Searching and Splitting
102
+
103
+ *each_match*, *matches*, *matches?*, *smarts_indices* all take the same input (SMARTS
104
+ string or object and optional boolean specifying uniqueness of results):
105
+
106
+ mol = Rubabel["NCC(O)C(=O)O"]
107
+ mol.each_match("CO") do |match|
108
+ # match is just an array of atoms that matched
109
+ match.first.el # => :c
110
+ match.last.el # => :o
111
+ end
112
+
113
+ # matches returns all the matches in an array
114
+ all_matches = mol.matches("CO")
115
+ # all the match routines take a boolean to alter uniqueness
116
+ all_matches = mol.matches("CO", false) # some matches may not be uniq
117
+
118
+ Have some bonds to break?, split makes new molecules split from that bond(s)
119
+
120
+ bonds = mol.matches("CO").map {|c, o| c.get_bond(o) }
121
+ mol.split(*bonds) # splits between every carbon single bonded to oxygen
122
+
123
+ ### Add & Delete atoms/bonds
124
+
125
+ #### Adding
126
+
127
+ mol = Rubabel["OCC"]
128
+ # adds a carbon, then an oxygen to last indexed atom by atomic number
129
+ mol << 6 << 8 # #<Mol "OCCCO">
130
+ mol << :c << :o # same thing
131
+
132
+ # add an ethyl group specifically to second carbon
133
+ mol = Rubabel["OCC"]
134
+ mol[1] << :c << :c
135
+
136
+ # add a vinyl group to second carbon (use method notation and parenthesis
137
+ # because we are going to specify 2 arguments (the bond order):
138
+ ( mol[1] << :c).<<(:c, 2)
139
+
140
+ #### Deleting
141
+
142
+ # delete an atom:
143
+ mol = Rubabel["NCO"]
144
+ mol.delete(mol[0]) # -> #<Mol CO>
145
+
146
+ # delete a bond:
147
+ bond = mol[0].get_bond(mol[1])
148
+ mol.delete(bond) # -> #<Mol C.O>
149
+
150
+ ## Installing
151
+
152
+ First, many thanks to Andreas Maunz for packaging openbabel as a gem which makes this install quite painless.
153
+
154
+ ### Quick Install
155
+
156
+ On a POSIX system, make sure you have openbabel (including header files), cmake, curl, tar, sed and make {see openbabel instructions}[https://github.com/amaunz/openbabel-gem]. On ubuntu/debian:
157
+
158
+ sudo apt-get install openbabel libopenbabel-dev cmake make curl
159
+
160
+ Then install the gem (which should install the openbabel gem, too):
161
+
162
+ gem install rubabel
163
+
164
+ ### Building from Source
165
+
166
+ 1. download openbabel
167
+ 2. swap out Init_OpenBabel for Init_openbabel in scripts/ruby/openbabel-ruby.cpp (see [here](http://forums.openbabel.org/Ruby-Open-Babel-in-2-1-1-td957640.html)). Some versions have this fixed already, apparently.
168
+ 3. make sure you have the right [dependencies to compile](http://openbabel.org/docs/2.3.1/Installation/install.html#compiling-open-babel)
169
+
170
+ Here's a complete example of compiling for a single user on Ubuntu 11.10 and probably will be generally forward compatible for some time. This will compile bindings on whichever ruby comes up with '/usr/bin/env ruby':
171
+
172
+ # install the dependencies:
173
+ sudo apt-get install libeigen2-dev cmake libwxgtk2.8-dev libxml2-dev libcairo2-dev
174
+ # unpack it:
175
+ tar -xzvf openbabel-2.3.1.tar.gz
176
+ # swap out buggy lines in ruby bindings:
177
+ sed -i 's/Init_OpenBabel/Init_openbabel/g' openbabel-2.3.1/scripts/ruby/openbabel-ruby.cpp
178
+ # make a separate build directory for building in:
179
+ mkdir build-rvmruby1.9.3
180
+ cd build-rvmruby1.9.3
181
+ mkdir ~/tools
182
+ cmake ../openbabel-2.3.1 -DRUBY_BINDINGS=ON -DCMAKE_INSTALL_PREFIX=~/tools/openbabel-rvmruby1.9.3
183
+ make && make install
184
+
185
+ [[Still need directions to install the gem on top of a build from source]]
186
+
187
+ ## Copyright
188
+
189
+ MIT License. See LICENSE for further details.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.2.1
@@ -14,14 +14,30 @@ module Rubabel
14
14
  include Enumerable
15
15
 
16
16
  class << self
17
- # takes an element symbol and creates that atom. If el_sym is set to
18
- # nil or 0, then an atom of atomic number 0 is used
19
- def [](el_sym=:h, id=nil)
20
- ob_atom = OpenBabel::OBAtom.new
21
- ob_atom.set_id(id) if id
22
- ob_atom.set_atomic_num(Rubabel::EL_TO_NUM[el_sym] || 0)
23
- self.new(ob_atom)
24
- end
17
+ # I cannot figure out how to get the atom attached properly into a
18
+ # molecule. Until I figure that out, no sense in having this method
19
+ # exposed:
20
+ #
21
+ # takes an atomic number or element symbol and creates that atom. If
22
+ # arg is set to nil, then an atom of atomic number 0 is used
23
+ # (represented by '*' in smiles)
24
+ #def [](arg=:c)
25
+ # ob_atom = OpenBabel::OBAtom.new
26
+ # atomic_number =
27
+ # if arg.nil? then 0
28
+ # elsif arg.is_a?(Symbol)
29
+ # Rubabel::EL_TO_NUM[arg]
30
+ # else
31
+ # arg
32
+ # end
33
+ # ob_atom.set_atomic_num(atomic_number)
34
+ # self.new(ob_atom)
35
+ #end
36
+ end
37
+
38
+ # adds an atom and returns the added atom (allows chaining)
39
+ def <<(atom, bond_order=1)
40
+ add_atom!(atom, bond_order)
25
41
  end
26
42
 
27
43
  # returns the molecule that is parent of this atom
@@ -59,14 +75,14 @@ module Rubabel
59
75
  NUM_TO_ELEMENT[atomic_num]
60
76
  end
61
77
 
62
- # creates a bond and adds it to both atoms
63
- def add_atom!(other)
64
- obbond = OpenBabel::OBBond.new
65
- obbond.set_begin(self.ob)
66
- obbond.set_end(other.ob)
67
- @ob.add_bond(obbond)
68
- other.ob.add_bond(obbond)
69
- self
78
+ # creates a bond and adds it to both atoms. Returns the passed in or
79
+ # newly created atom.
80
+ def add_atom!(arg, bond_order=1)
81
+ unless arg.is_a?(Rubabel::Atom)
82
+ arg = mol.add_atom!(arg)
83
+ end
84
+ @ob.get_parent.add_bond(self.ob.get_idx, arg.ob.get_idx, bond_order)
85
+ arg
70
86
  end
71
87
 
72
88
  def each_bond(&block)
@@ -108,14 +108,28 @@ module Rubabel
108
108
  end
109
109
  end
110
110
 
111
- # arg is an atomic number. returns the newly created atom
112
- def add_atom!(atomic_num=1)
113
- # jtp implementation:
114
- # @ob.add_atom(atom.ob)
115
- new_obatom = @ob.new_atom
116
- new_obatom.set_atomic_num(atomic_num)
117
- #@ob.add_atom(new_obatom)
118
- Rubabel::Atom.new(new_obatom)
111
+ # returns the atom passed in or that was created. arg is a pre-existing
112
+ # atom, an atomic number or an element symbol (e.g. :c). default is to
113
+ # add carbon.
114
+ def add_atom!(arg=6, attach_to=nil, bond_order=1)
115
+ if attach_to
116
+ attach_to.add_atom!(arg, bond_order)
117
+ else
118
+ if arg.is_a?(Rubabel::Atom)
119
+ @ob.add_atom(arg.ob)
120
+ arg
121
+ else
122
+ new_obatom = @ob.new_atom
123
+ arg = Rubabel::EL_TO_NUM[arg] if arg.is_a?(Symbol)
124
+ new_obatom.set_atomic_num(arg)
125
+ Rubabel::Atom.new(new_obatom)
126
+ end
127
+ end
128
+ end
129
+
130
+ # retrieves the atom by index (accepts everything an array would)
131
+ def [](*args)
132
+ atoms[*args]
119
133
  end
120
134
 
121
135
  def delete_atom(atom)
@@ -145,8 +159,8 @@ module Rubabel
145
159
  # to add_h!
146
160
  def formula() @ob.get_formula end
147
161
 
148
- def initialize(obmol)
149
- @ob = obmol
162
+ def initialize(obmol=nil)
163
+ @ob = obmol.nil? ? OpenBabel::OBMol.new : obmol
150
164
  end
151
165
 
152
166
  # returns a list of atom indices matching the patterns (corresponds to the
@@ -476,6 +490,18 @@ module Rubabel
476
490
  Rubabel::MoleculeData.new(@ob)
477
491
  end
478
492
 
493
+ # adds the atom (takes atomic number, element symbol or preexisting atom)
494
+ # and returns self
495
+ def <<(arg, bond_order=1)
496
+ last_atom = atoms[-1]
497
+ if last_atom
498
+ last_atom.add_atom!(arg, bond_order)
499
+ else
500
+ add_atom!(arg)
501
+ end
502
+ self
503
+ end
504
+
479
505
  # sensitive to add_h!
480
506
  def num_atoms() @ob.num_atoms end
481
507
  def num_bonds() @ob.num_bonds end
@@ -568,9 +594,9 @@ module Rubabel
568
594
  end
569
595
  end
570
596
 
571
- # writes to the file based on the extension given. If type is given
572
- # explicitly, then it is used. If png is the extension or format, the
573
- # png is generated from an svg.
597
+ # writes to the file based on the extension given (must be recognized by
598
+ # OpenBabel). If png is the extension or format, the png is generated
599
+ # from an svg.
574
600
  def write_file(filename, out_options={})
575
601
  type = Rubabel.filetype(filename)
576
602
  File.write(filename, write_string(type, out_options))
@@ -5,7 +5,7 @@ require 'rubabel/atom'
5
5
 
6
6
  describe Rubabel::Atom do
7
7
 
8
- it 'can be created given an element symbol' do
8
+ xit 'can be created given an element symbol' do
9
9
  hydrogen = Rubabel::Atom[:h]
10
10
  hydrogen.el.should == :h
11
11
 
@@ -17,6 +17,19 @@ describe Rubabel::Atom do
17
17
  chlorine.id.should == 3
18
18
  end
19
19
 
20
+ describe 'attaching another atom to itself (as part of a molecule)' do
21
+
22
+ it 'attaches preformed atom' do
23
+ end
24
+
25
+ it 'attaches given an atomic number' do
26
+ end
27
+
28
+ it 'attaches given an element symbol' do
29
+ end
30
+
31
+ end
32
+
20
33
  specify 'equality' do
21
34
  mol = Rubabel["CCO"]
22
35
  oxygen = mol.atoms[2]
@@ -76,13 +89,19 @@ describe Rubabel::Atom do
76
89
  end
77
90
  end
78
91
 
92
+ specify '#<<(atom) adds an atom and returns the added atom' do
93
+ mol = Rubabel["C"]
94
+ reply = (mol[0] << :n << :o)
95
+ reply.should be_a(Rubabel::Atom)
96
+ mol.csmiles.should == 'CNO'
97
+ end
79
98
 
80
99
  it '#mol retrieves the parent molecule' do
81
100
  @atom.mol.should == @mol
82
101
 
83
- # no parent molecule
84
- h = Rubabel::Atom[:h]
85
- h.mol.should be_nil
102
+ ## no parent molecule
103
+ #h = Rubabel::Atom[:h]
104
+ #h.mol.should be_nil
86
105
  end
87
106
 
88
107
  it 'can get the bonds' do
@@ -92,8 +111,7 @@ describe Rubabel::Atom do
92
111
  @atom.bonds.size.should == 4
93
112
  end
94
113
 
95
- it 'can add a bond' do
96
- end
114
+ it 'can add a bond'
97
115
 
98
116
  it 'can get the neighboring atoms' do
99
117
  @atom.id.should == 0
@@ -8,6 +8,17 @@ describe Rubabel::Molecule do
8
8
  mol = Rubabel["CC(O)O"]
9
9
  mol.csmiles.should == "CC(O)O"
10
10
  end
11
+
12
+ it 'can be made with an existing obmol object' do
13
+ mol = Rubabel["CC(O)O"]
14
+ mol_dup = Rubabel::Molecule.new(mol.ob)
15
+ end
16
+
17
+ it 'can be made fresh' do
18
+ mol = Rubabel::Molecule.new
19
+ mol.atoms.size == 0
20
+ mol.title.should == ''
21
+ end
11
22
  end
12
23
 
13
24
  #xit 'can add a hydrogen to the formula' do
@@ -58,25 +69,59 @@ describe Rubabel::Molecule do
58
69
  mol1 = Rubabel["CCO"]
59
70
  mol2 = Rubabel["OCC"]
60
71
  (mol1 == mol2).should be_true
61
- mol2.atoms[0].charge += 1
72
+ mol2[0].charge += 1
62
73
  (mol1 == mol2).should be_false
63
74
  mol3 = Rubabel["CCCO"]
64
75
  (mol1 == mol3).should be_false
65
76
  end
66
77
 
67
- specify '#add_atom! adds an atom given an atomic number and returns it' do
68
- mol = Rubabel["CCO"]
69
- before_size = mol.atoms.size
70
- atom = mol.add_atom!(6)
71
- atom.el.should == :c
72
- (mol.atoms.size - before_size).should == 1
73
- mol.csmiles.should == "CCO.C"
78
+ specify '#[] retrieves atom by index' do
79
+ mol = Rubabel["NCO"]
80
+ mol[0].el.should == :n
81
+ mol[1].el.should == :c
82
+ mol[2].el.should == :o
83
+ mol[-1].el.should == :o
84
+ mol[-2].el.should == :c
85
+ ar = mol[1..-1]
86
+ ar.first.el.should == :c
87
+ ar.last.el.should == :o
88
+ ar.size.should == 2
89
+ end
90
+
91
+ describe 'adding an atom' do
92
+ it 'can be added but not attached' do
93
+ mol = Rubabel["CCO"]
94
+ atom = mol.add_atom!(:n)
95
+ atom.el.should == :n
96
+ atom = mol.add_atom!(8)
97
+ atom.el.should == :o
98
+ mol.csmiles.should == "CCO.N.O"
99
+ end
100
+
101
+ it "can be added and attached by el symbol or atomic number" do
102
+ mol = Rubabel["CCO"]
103
+ first_carbon = mol[0]
104
+ mol.add_atom!(:n, first_carbon)
105
+ mol.csmiles.should == "NCCO"
106
+
107
+ mol.add_atom!(16, first_carbon)
108
+ mol.csmiles.should == "NC(CO)S"
109
+ end
110
+
111
+ end
112
+
113
+ specify '#<< adds atom to the last atom and returns the mol' do
114
+ mol = Rubabel::Molecule.new
115
+ # by element symbol or atomic number
116
+ reply = (mol << :n << :c)
117
+ reply.should be_a(Rubabel::Molecule)
118
+ reply.csmiles.should == 'CN'
74
119
  end
75
120
 
76
121
  specify '#dup duplicates the molecule' do
77
122
  mol = Rubabel["CCO"]
78
123
  dup_mol = mol.dup
79
- mol.atoms[0].ob.set_atomic_num(9)
124
+ mol[0].ob.set_atomic_num(9)
80
125
  mol.csmiles.should == "OCF"
81
126
  dup_mol.csmiles.should == "CCO"
82
127
  end
@@ -87,7 +132,7 @@ describe Rubabel::Molecule do
87
132
  specify 'given two atoms' do
88
133
  mol = Rubabel["CCO"]
89
134
  atom = mol.add_atom!(0)
90
- mol.add_bond!(mol.atoms[1], atom)
135
+ mol.add_bond!(mol[1], atom)
91
136
  mol.csmiles.should == '*C(O)C'
92
137
  end
93
138
  end
@@ -148,7 +193,7 @@ describe Rubabel::Molecule do
148
193
  subject { mol = Rubabel["CC"] }
149
194
  it 'can be turned into a carbocation' do
150
195
  mol = subject
151
- c = mol.atoms[0]
196
+ c = mol[0]
152
197
  c.ob.set_spin_multiplicity 2
153
198
  c.charge += 1
154
199
  mol.csmiles.should == "C[CH2+]"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubabel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -116,12 +116,12 @@ executables:
116
116
  extensions: []
117
117
  extra_rdoc_files:
118
118
  - LICENSE
119
- - README.rdoc
119
+ - README.md
120
120
  files:
121
121
  - .document
122
122
  - .rspec
123
123
  - LICENSE
124
- - README.rdoc
124
+ - README.md
125
125
  - Rakefile
126
126
  - VERSION
127
127
  - bin/fragmenter.rb
@@ -1,56 +0,0 @@
1
- = rubabel
2
-
3
- Ruby interface to the openbabel ruby bindings (or the openbabel gem). The
4
- interface attempts to be a ruby-ish analogue of
5
- {pybel}[http://openbabel.org/docs/current/UseTheLibrary/Python_PybelAPI.html].
6
-
7
- == Examples
8
-
9
- Rubabel.foreach("file.sdf") do |mol|
10
- print mol.write # -> canonical smiles: "smiles_string\tname\n"
11
- puts mol # .to_s -> canonical smiles string with no name or newline
12
- puts mol.exact_mass
13
- end
14
-
15
- uniq_atom_types = Rubabel.foreach("file.mol").map(&:type).uniq
16
-
17
- == Installing
18
-
19
- First, many thanks to Andreas Maunz for packaging openbabel as a gem which makes this install quite painless.
20
-
21
- === Quick Install
22
-
23
- On a POSIX system, make sure you have openbabel (including header files), cmake, curl, tar, sed and make {see openbabel instructions}[https://github.com/amaunz/openbabel-gem]. On ubuntu/debian:
24
-
25
- sudo apt-get install openbabel libopenbabel-dev cmake make curl
26
-
27
- Then install the gem (which should install the openbabel gem, too):
28
-
29
- gem install rubabel
30
-
31
- === Building from Source
32
-
33
- 1. download openbabel
34
- 2. swap out Init_OpenBabel for Init_openbabel in scripts/ruby/openbabel-ruby.cpp (see here[http://forums.openbabel.org/Ruby-Open-Babel-in-2-1-1-td957640.html]). Some versions have this fixed already, apparently.
35
- 3. make sure you have the right {dependencies to compile}(http://openbabel.org/docs/2.3.1/Installation/install.html#compiling-open-babel)
36
-
37
- Here's a complete example of compiling for a single user on Ubuntu 11.10 and probably will be generally forward compatible for some time. This will compile bindings on whichever ruby comes up with '/usr/bin/env ruby':
38
-
39
- # install the dependencies:
40
- sudo apt-get install libeigen2-dev cmake libwxgtk2.8-dev libxml2-dev libcairo2-dev
41
- # unpack it:
42
- tar -xzvf openbabel-2.3.1.tar.gz
43
- # swap out buggy lines in ruby bindings:
44
- sed -i 's/Init_OpenBabel/Init_openbabel/g' openbabel-2.3.1/scripts/ruby/openbabel-ruby.cpp
45
- # make a separate build directory for building in:
46
- mkdir build-rvmruby1.9.3
47
- cd build-rvmruby1.9.3
48
- mkdir ~/tools
49
- cmake ../openbabel-2.3.1 -DRUBY_BINDINGS=ON -DCMAKE_INSTALL_PREFIX=~/tools/openbabel-rvmruby1.9.3
50
- make && make install
51
-
52
- [I still need to make directions to install the gem on top of a build from source]
53
-
54
- == Copyright
55
-
56
- MIT License. See LICENSE for further details.