alumina 0.0.1
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.
- data/.document +5 -0
- data/.gitignore +23 -0
- data/LICENSE +20 -0
- data/README.textile +17 -0
- data/Rakefile +63 -0
- data/VERSION +1 -0
- data/data/elements.yml +355 -0
- data/lib/alumina.rb +32 -0
- data/lib/alumina/atom.rb +93 -0
- data/lib/alumina/element.rb +30 -0
- data/lib/alumina/errors.rb +15 -0
- data/lib/alumina/hin.rb +7 -0
- data/lib/alumina/hin/parser.rb +139 -0
- data/lib/alumina/hin/writer.rb +34 -0
- data/lib/alumina/molecule.rb +89 -0
- data/lib/extensions.rb +59 -0
- data/spec/alumina_spec.rb +7 -0
- data/spec/spec_helper.rb +8 -0
- metadata +113 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Tim Morgan
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.textile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
= Alumina
|
2
|
+
|
3
|
+
Description goes here.
|
4
|
+
|
5
|
+
== Note on Patches/Pull Requests
|
6
|
+
|
7
|
+
* Fork the project.
|
8
|
+
* Make your feature addition or bug fix.
|
9
|
+
* Add tests for it. This is important so I don't break it in a
|
10
|
+
future version unintentionally.
|
11
|
+
* Commit, do not mess with rakefile, version, or history.
|
12
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
13
|
+
* Send me a pull request. Bonus points for topic branches.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2010 Tim Morgan. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "alumina"
|
8
|
+
gem.summary = %Q{Ruby parser for various chemistry-related file formats}
|
9
|
+
gem.description = %Q{This gem parses HyperChem's .HIN files, PDB files, and in the future others, converting them to Ruby objects for easy manipulation}
|
10
|
+
gem.email = "git@timothymorgan.info"
|
11
|
+
gem.homepage = "http://github.com/RISCfuture/alumina"
|
12
|
+
gem.authors = ["Tim Morgan"]
|
13
|
+
gem.add_development_dependency "bacon", ">= 0"
|
14
|
+
gem.add_development_dependency "yard", ">= 0"
|
15
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
|
+
end
|
17
|
+
Jeweler::GemcutterTasks.new
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'rake/testtask'
|
23
|
+
Rake::TestTask.new(:spec) do |spec|
|
24
|
+
spec.libs << 'lib' << 'spec'
|
25
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
26
|
+
spec.verbose = true
|
27
|
+
end
|
28
|
+
|
29
|
+
begin
|
30
|
+
require 'rcov/rcovtask'
|
31
|
+
Rcov::RcovTask.new do |spec|
|
32
|
+
spec.libs << 'spec'
|
33
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
34
|
+
spec.verbose = true
|
35
|
+
end
|
36
|
+
rescue LoadError
|
37
|
+
task :rcov do
|
38
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
task :spec => :check_dependencies
|
43
|
+
|
44
|
+
task :default => :spec
|
45
|
+
|
46
|
+
begin
|
47
|
+
require 'yard'
|
48
|
+
YARD::Rake::YardocTask.new do |doc|
|
49
|
+
doc.options << "-m" << "textile"
|
50
|
+
doc.options << "--protected"
|
51
|
+
doc.options << "-r" << "README.textile"
|
52
|
+
doc.options << "-o" << "doc"
|
53
|
+
doc.options << "--title" << "Alumina Documentation".inspect
|
54
|
+
|
55
|
+
doc.files << "lib/**/*"
|
56
|
+
end
|
57
|
+
rescue LoadError
|
58
|
+
task :yard do
|
59
|
+
abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
task :doc => :yard
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/data/elements.yml
ADDED
@@ -0,0 +1,355 @@
|
|
1
|
+
---
|
2
|
+
1:
|
3
|
+
:symbol: H
|
4
|
+
:name: Hydrogen
|
5
|
+
2:
|
6
|
+
:symbol: He
|
7
|
+
:name: Helium
|
8
|
+
3:
|
9
|
+
:symbol: Li
|
10
|
+
:name: Lithium
|
11
|
+
4:
|
12
|
+
:symbol: Be
|
13
|
+
:name: Beryllium
|
14
|
+
5:
|
15
|
+
:symbol: B
|
16
|
+
:name: Boron
|
17
|
+
6:
|
18
|
+
:symbol: C
|
19
|
+
:name: Carbon
|
20
|
+
7:
|
21
|
+
:symbol: N
|
22
|
+
:name: Nitrogen
|
23
|
+
8:
|
24
|
+
:symbol: O
|
25
|
+
:name: Oxygen
|
26
|
+
9:
|
27
|
+
:symbol: F
|
28
|
+
:name: Fluorine
|
29
|
+
10:
|
30
|
+
:symbol: Ne
|
31
|
+
:name: Neon
|
32
|
+
11:
|
33
|
+
:symbol: Na
|
34
|
+
:name: Sodium
|
35
|
+
12:
|
36
|
+
:symbol: Mg
|
37
|
+
:name: Magnesium
|
38
|
+
13:
|
39
|
+
:symbol: Al
|
40
|
+
:name: Aluminium
|
41
|
+
14:
|
42
|
+
:symbol: Si
|
43
|
+
:name: Silicon
|
44
|
+
15:
|
45
|
+
:symbol: P
|
46
|
+
:name: Phosphorus
|
47
|
+
16:
|
48
|
+
:symbol: S
|
49
|
+
:name: Sulfur
|
50
|
+
17:
|
51
|
+
:symbol: Cl
|
52
|
+
:name: Chlorine
|
53
|
+
18:
|
54
|
+
:symbol: Ar
|
55
|
+
:name: Argon
|
56
|
+
19:
|
57
|
+
:symbol: K
|
58
|
+
:name: Potassium
|
59
|
+
20:
|
60
|
+
:symbol: Ca
|
61
|
+
:name: Calcium
|
62
|
+
21:
|
63
|
+
:symbol: Sc
|
64
|
+
:name: Scandium
|
65
|
+
22:
|
66
|
+
:symbol: Ti
|
67
|
+
:name: Titanium
|
68
|
+
23:
|
69
|
+
:symbol: V
|
70
|
+
:name: Vanadium
|
71
|
+
24:
|
72
|
+
:symbol: Cr
|
73
|
+
:name: Chromium
|
74
|
+
25:
|
75
|
+
:symbol: Mn
|
76
|
+
:name: Manganese
|
77
|
+
26:
|
78
|
+
:symbol: Fe
|
79
|
+
:name: Iron
|
80
|
+
27:
|
81
|
+
:symbol: Co
|
82
|
+
:name: Cobalt
|
83
|
+
28:
|
84
|
+
:symbol: Ni
|
85
|
+
:name: Nickel
|
86
|
+
29:
|
87
|
+
:symbol: Cu
|
88
|
+
:name: Copper
|
89
|
+
30:
|
90
|
+
:symbol: Zn
|
91
|
+
:name: Zinc
|
92
|
+
31:
|
93
|
+
:symbol: Ga
|
94
|
+
:name: Gallium
|
95
|
+
32:
|
96
|
+
:symbol: Ge
|
97
|
+
:name: Germanium
|
98
|
+
33:
|
99
|
+
:symbol: As
|
100
|
+
:name: Arsenic
|
101
|
+
34:
|
102
|
+
:symbol: Se
|
103
|
+
:name: Selenium
|
104
|
+
35:
|
105
|
+
:symbol: Br
|
106
|
+
:name: Bromine
|
107
|
+
36:
|
108
|
+
:symbol: Kr
|
109
|
+
:name: Krypton
|
110
|
+
37:
|
111
|
+
:symbol: Rb
|
112
|
+
:name: Rubidium
|
113
|
+
38:
|
114
|
+
:symbol: Sr
|
115
|
+
:name: Strontium
|
116
|
+
39:
|
117
|
+
:symbol: Y
|
118
|
+
:name: Yttrium
|
119
|
+
40:
|
120
|
+
:symbol: Zr
|
121
|
+
:name: Zirconium
|
122
|
+
41:
|
123
|
+
:symbol: Nb
|
124
|
+
:name: Niobium
|
125
|
+
42:
|
126
|
+
:symbol: Mo
|
127
|
+
:name: Molybdenum
|
128
|
+
43:
|
129
|
+
:symbol: Tc
|
130
|
+
:name: Technetium
|
131
|
+
44:
|
132
|
+
:symbol: Ru
|
133
|
+
:name: Ruthenium
|
134
|
+
45:
|
135
|
+
:symbol: Rh
|
136
|
+
:name: Rhodium
|
137
|
+
46:
|
138
|
+
:symbol: Pd
|
139
|
+
:name: Palladium
|
140
|
+
47:
|
141
|
+
:symbol: Ag
|
142
|
+
:name: Silver
|
143
|
+
48:
|
144
|
+
:symbol: Cd
|
145
|
+
:name: Cadmium
|
146
|
+
49:
|
147
|
+
:symbol: In
|
148
|
+
:name: Indium
|
149
|
+
50:
|
150
|
+
:symbol: Sn
|
151
|
+
:name: Tin
|
152
|
+
51:
|
153
|
+
:symbol: Sb
|
154
|
+
:name: Antimony
|
155
|
+
52:
|
156
|
+
:symbol: Te
|
157
|
+
:name: Tellurium
|
158
|
+
53:
|
159
|
+
:symbol: I
|
160
|
+
:name: Iodine
|
161
|
+
54:
|
162
|
+
:symbol: Xe
|
163
|
+
:name: Xenon
|
164
|
+
55:
|
165
|
+
:symbol: Cs
|
166
|
+
:name: Caesium
|
167
|
+
56:
|
168
|
+
:symbol: Ba
|
169
|
+
:name: Barium
|
170
|
+
57:
|
171
|
+
:symbol: La
|
172
|
+
:name: Lanthanum
|
173
|
+
58:
|
174
|
+
:symbol: Ce
|
175
|
+
:name: Cerium
|
176
|
+
59:
|
177
|
+
:symbol: Pr
|
178
|
+
:name: Praseodymium
|
179
|
+
60:
|
180
|
+
:symbol: Nd
|
181
|
+
:name: Neodymium
|
182
|
+
61:
|
183
|
+
:symbol: Pm
|
184
|
+
:name: Promethium
|
185
|
+
62:
|
186
|
+
:symbol: Sm
|
187
|
+
:name: Samarium
|
188
|
+
63:
|
189
|
+
:symbol: Eu
|
190
|
+
:name: Europium
|
191
|
+
64:
|
192
|
+
:symbol: Gd
|
193
|
+
:name: Gadolinium
|
194
|
+
65:
|
195
|
+
:symbol: Tb
|
196
|
+
:name: Terbium
|
197
|
+
66:
|
198
|
+
:symbol: Dy
|
199
|
+
:name: Dysprosium
|
200
|
+
67:
|
201
|
+
:symbol: Ho
|
202
|
+
:name: Holmium
|
203
|
+
68:
|
204
|
+
:symbol: Er
|
205
|
+
:name: Erbium
|
206
|
+
69:
|
207
|
+
:symbol: Tm
|
208
|
+
:name: Thulium
|
209
|
+
70:
|
210
|
+
:symbol: Yb
|
211
|
+
:name: Ytterbium
|
212
|
+
71:
|
213
|
+
:symbol: Lu
|
214
|
+
:name: Lutetium
|
215
|
+
72:
|
216
|
+
:symbol: Hf
|
217
|
+
:name: Hafnium
|
218
|
+
73:
|
219
|
+
:symbol: Ta
|
220
|
+
:name: Tantalum
|
221
|
+
74:
|
222
|
+
:symbol: W
|
223
|
+
:name: Tungsten
|
224
|
+
75:
|
225
|
+
:symbol: Re
|
226
|
+
:name: Rhenium
|
227
|
+
76:
|
228
|
+
:symbol: Os
|
229
|
+
:name: Osmium
|
230
|
+
77:
|
231
|
+
:symbol: Ir
|
232
|
+
:name: Iridium
|
233
|
+
78:
|
234
|
+
:symbol: Pt
|
235
|
+
:name: Platinum
|
236
|
+
79:
|
237
|
+
:symbol: Au
|
238
|
+
:name: Gold
|
239
|
+
80:
|
240
|
+
:symbol: Hg
|
241
|
+
:name: Mercury
|
242
|
+
81:
|
243
|
+
:symbol: Tl
|
244
|
+
:name: Thallium
|
245
|
+
82:
|
246
|
+
:symbol: Pb
|
247
|
+
:name: Lead
|
248
|
+
83:
|
249
|
+
:symbol: Bi
|
250
|
+
:name: Bismuth
|
251
|
+
84:
|
252
|
+
:symbol: Po
|
253
|
+
:name: Polonium
|
254
|
+
85:
|
255
|
+
:symbol: At
|
256
|
+
:name: Astatine
|
257
|
+
86:
|
258
|
+
:symbol: Rn
|
259
|
+
:name: Radon
|
260
|
+
87:
|
261
|
+
:symbol: Fr
|
262
|
+
:name: Francium
|
263
|
+
88:
|
264
|
+
:symbol: Ra
|
265
|
+
:name: Radium
|
266
|
+
89:
|
267
|
+
:symbol: Ac
|
268
|
+
:name: Actinium
|
269
|
+
90:
|
270
|
+
:symbol: Th
|
271
|
+
:name: Thorium
|
272
|
+
91:
|
273
|
+
:symbol: Pa
|
274
|
+
:name: Protactinium
|
275
|
+
92:
|
276
|
+
:symbol: U
|
277
|
+
:name: Uranium
|
278
|
+
93:
|
279
|
+
:symbol: Np
|
280
|
+
:name: Neptunium
|
281
|
+
94:
|
282
|
+
:symbol: Pu
|
283
|
+
:name: Plutonium
|
284
|
+
95:
|
285
|
+
:symbol: Am
|
286
|
+
:name: Americium
|
287
|
+
96:
|
288
|
+
:symbol: Cm
|
289
|
+
:name: Curium
|
290
|
+
97:
|
291
|
+
:symbol: Bk
|
292
|
+
:name: Berkelium
|
293
|
+
98:
|
294
|
+
:symbol: Cf
|
295
|
+
:name: Californium
|
296
|
+
99:
|
297
|
+
:symbol: Es
|
298
|
+
:name: Einsteinium
|
299
|
+
100:
|
300
|
+
:symbol: Fm
|
301
|
+
:name: Fermium
|
302
|
+
101:
|
303
|
+
:symbol: Md
|
304
|
+
:name: Mendelevium
|
305
|
+
102:
|
306
|
+
:symbol: "No"
|
307
|
+
:name: Nobelium
|
308
|
+
103:
|
309
|
+
:symbol: Lr
|
310
|
+
:name: Lawrencium
|
311
|
+
104:
|
312
|
+
:symbol: Rf
|
313
|
+
:name: Rutherfordium
|
314
|
+
105:
|
315
|
+
:symbol: Db
|
316
|
+
:name: Dubnium
|
317
|
+
106:
|
318
|
+
:symbol: Sg
|
319
|
+
:name: Seaborgium
|
320
|
+
107:
|
321
|
+
:symbol: Bh
|
322
|
+
:name: Bohrium
|
323
|
+
108:
|
324
|
+
:symbol: Hs
|
325
|
+
:name: Hassium
|
326
|
+
109:
|
327
|
+
:symbol: Mt
|
328
|
+
:name: Meitnerium
|
329
|
+
110:
|
330
|
+
:symbol: Ds
|
331
|
+
:name: Darmstadtium
|
332
|
+
111:
|
333
|
+
:symbol: Rg
|
334
|
+
:name: Roentgenium
|
335
|
+
112:
|
336
|
+
:symbol: Cn
|
337
|
+
:name: Copernicium
|
338
|
+
113:
|
339
|
+
:symbol: Uut
|
340
|
+
:name: Ununtrium
|
341
|
+
114:
|
342
|
+
:symbol: Uuq
|
343
|
+
:name: Ununquadium
|
344
|
+
115:
|
345
|
+
:symbol: Uup
|
346
|
+
:name: Ununpentium
|
347
|
+
116:
|
348
|
+
:symbol: Uuh
|
349
|
+
:name: Ununhexium
|
350
|
+
117:
|
351
|
+
:symbol: Uuh
|
352
|
+
:name: Ununseptium
|
353
|
+
118:
|
354
|
+
:symbol: Uuo
|
355
|
+
:name: Ununoctium
|
data/lib/alumina.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'extensions'
|
3
|
+
require 'alumina/errors'
|
4
|
+
require 'alumina/hin/writer'
|
5
|
+
require 'alumina/element'
|
6
|
+
require 'alumina/atom'
|
7
|
+
require 'alumina/molecule'
|
8
|
+
require 'alumina/hin'
|
9
|
+
require 'alumina/hin/parser'
|
10
|
+
|
11
|
+
# Container module for classes of the Alumina gem.
|
12
|
+
|
13
|
+
module Alumina
|
14
|
+
# @private
|
15
|
+
ELEMENTS_PATH = File.expand_path("#{File.dirname(__FILE__)}/../data/elements.yml")
|
16
|
+
# An array of all {Element Elements}, loaded from a YAML data file.
|
17
|
+
ELEMENTS = YAML.load(File.read(ELEMENTS_PATH)).map { |atomic_number, data| Element.new(atomic_number, data[:name], data[:symbol]) }
|
18
|
+
# All elements hashed by their symbol (e.g., "Ag" for silver).
|
19
|
+
ELEMENTS_BY_SYMBOL = ELEMENTS.inject({}) { |hsh, cur| hsh[cur.symbol] = cur ; hsh }
|
20
|
+
# All elements hashed by their atomic number.
|
21
|
+
ELEMENTS_BY_NUMBER = ELEMENTS.inject({}) { |hsh, cur| hsh[cur.atomic_number] = cur ; hsh }
|
22
|
+
|
23
|
+
# Creates a {HIN::Parser} and calls {HIN::Parser#parse} on it.
|
24
|
+
#
|
25
|
+
# @param [IO, String] input The HIN data to parse.
|
26
|
+
# @return [Array<Alumina::Molecule>] The molecules described by the data.
|
27
|
+
# @raise [ParseError] If the HIN data is improperly formatted.
|
28
|
+
|
29
|
+
def self.HIN(input)
|
30
|
+
Alumina::HIN::Parser.new.parse(input)
|
31
|
+
end
|
32
|
+
end
|
data/lib/alumina/atom.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
module Alumina
|
2
|
+
|
3
|
+
# An atom as part of a {Molecule}.
|
4
|
+
|
5
|
+
class Atom
|
6
|
+
include Alumina::HIN::Writer::Atom
|
7
|
+
|
8
|
+
BOND_TYPES = [ :single, :double, :triple, :aromatic ]
|
9
|
+
|
10
|
+
# @return [Fixnum] The unique numerical identifier assigned to the atom.
|
11
|
+
attr_accessor :id
|
12
|
+
# @return [String, nil] The optional label given to the atom.
|
13
|
+
attr_accessor :label
|
14
|
+
# @return [Element] The atomic type.
|
15
|
+
attr_accessor :element
|
16
|
+
# @return [Float] The atom's _x_-coordinate in the molecule.
|
17
|
+
attr_accessor :x
|
18
|
+
# @return [Float] The atom's _y_-coordinate in the molecule.
|
19
|
+
attr_accessor :y
|
20
|
+
# @return [Float] The atom's _z_-coordinate in the molecule.
|
21
|
+
attr_accessor :z
|
22
|
+
# @return [Hash<Atom, Symbol>] The atoms this atom is bonded to, along with
|
23
|
+
# the bond type.
|
24
|
+
attr_accessor :bonds
|
25
|
+
# @return [Fixnum] The partial charge.
|
26
|
+
attr_accessor :partial_charge
|
27
|
+
|
28
|
+
# @private
|
29
|
+
attr_accessor :ignored1, :ignored2
|
30
|
+
|
31
|
+
# Initializes a new atom. After initializing it you can define its bonds
|
32
|
+
# strucrure using the {.bind} method. Atoms are added to
|
33
|
+
# {Molecule Molecules} with the @<<@ method.
|
34
|
+
#
|
35
|
+
# @param [Fixnum] id A unique identifier for this atom. Uniqueness is not
|
36
|
+
# checked here.
|
37
|
+
# @param [Element] element The atom's type.
|
38
|
+
# @param [Float] x The atom's _x_-coordinate in the molecule.
|
39
|
+
# @param [Float] y The atom's _y_-coordinate in the molecule.
|
40
|
+
# @param [Float] z The atom's _z_-coordinate in the molecule.
|
41
|
+
|
42
|
+
def initialize(id, element, x, y, z)
|
43
|
+
@id = id
|
44
|
+
@element = element
|
45
|
+
@x = x
|
46
|
+
@y = y
|
47
|
+
@z = z
|
48
|
+
@bonds = Hash.new
|
49
|
+
end
|
50
|
+
|
51
|
+
# Binds two atoms together.
|
52
|
+
#
|
53
|
+
# @param [Atom] atom1 An atom to bind.
|
54
|
+
# @param [Atom] atom2 An atom to bind.
|
55
|
+
# @param [Symbol] The bond type. (See {BOND_TYPES}.)
|
56
|
+
# @raise [ArgumentError] If an invalid bond type is given.
|
57
|
+
|
58
|
+
def self.bind(atom1, atom2, type)
|
59
|
+
raise ArgumentError, "Invalid bond type #{type.inspect}" unless BOND_TYPES.include?(type)
|
60
|
+
|
61
|
+
atom1.bonds[atom2] = type
|
62
|
+
atom2.bonds[atom1] = type
|
63
|
+
end
|
64
|
+
|
65
|
+
# @return [Fixnum] The number of atoms bound to this atom.
|
66
|
+
|
67
|
+
def bond_count
|
68
|
+
bonds.size
|
69
|
+
end
|
70
|
+
|
71
|
+
# Creates a duplicate of this atom with no bonds.
|
72
|
+
#
|
73
|
+
# @return [Atom] A duplicate of this atom.
|
74
|
+
|
75
|
+
def dup
|
76
|
+
atom = Atom.new(id, element, x, y, z)
|
77
|
+
atom.label = label
|
78
|
+
atom.partial_charge = partial_charge
|
79
|
+
atom.ignored1 = ignored1
|
80
|
+
atom.ignored2 = ignored2
|
81
|
+
return atom
|
82
|
+
end
|
83
|
+
|
84
|
+
# @private
|
85
|
+
def inspect
|
86
|
+
if label then
|
87
|
+
"#<Atom ##{id} #{label} (#{element.symbol})>"
|
88
|
+
else
|
89
|
+
"#<Atom ##{id} #{element.symbol}>"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Alumina
|
2
|
+
|
3
|
+
# Each of the elements has a singleton instance of this class. You do not
|
4
|
+
# create instances of this class; they are loaded from a data file.
|
5
|
+
|
6
|
+
class Element
|
7
|
+
# @return [Fixnum] The element's atomic number (hydrogen is 1).
|
8
|
+
attr_reader :atomic_number
|
9
|
+
# @return [String] The element's name.
|
10
|
+
attr_reader :name
|
11
|
+
# @return [String] The element's symbol (hydrogen is @H@).
|
12
|
+
attr_reader :symbol
|
13
|
+
|
14
|
+
# @private
|
15
|
+
def initialize(atomic_number, name, symbol)
|
16
|
+
@atomic_number = atomic_number
|
17
|
+
@name = name
|
18
|
+
@symbol = symbol
|
19
|
+
end
|
20
|
+
|
21
|
+
# Provides a natural ordering based on atomic number.
|
22
|
+
#
|
23
|
+
# @param [Element] other The other element to compare against.
|
24
|
+
# @return [-1, 0, 1] The sort equivalency.
|
25
|
+
|
26
|
+
def <=>(other)
|
27
|
+
atomic_number <=> other.atomic_number
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Alumina
|
2
|
+
|
3
|
+
# Raised when invalid HIN data is encountered.
|
4
|
+
|
5
|
+
class ParseError < StandardError
|
6
|
+
# @return [Fixnum] The line in the HIN data at which the error occurred.
|
7
|
+
attr_accessor :line
|
8
|
+
|
9
|
+
# @private
|
10
|
+
def initialize(line, reason)
|
11
|
+
@line = line
|
12
|
+
super(reason)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/alumina/hin.rb
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
module Alumina
|
2
|
+
module HIN
|
3
|
+
|
4
|
+
# Class that parses HIN files to produce {Molecule} instances. This class is
|
5
|
+
# not thread-safe! You must instantiate a new parser for each thread of your
|
6
|
+
# program.
|
7
|
+
|
8
|
+
class Parser
|
9
|
+
# Mapping of HIN file bond types to their symbol values for the {Atom}
|
10
|
+
# class.
|
11
|
+
BOND_TYPES = {
|
12
|
+
's' => :single,
|
13
|
+
'd' => :double,
|
14
|
+
't' => :triple,
|
15
|
+
'a' => :aromatic
|
16
|
+
}
|
17
|
+
|
18
|
+
# If @true@, does not raise {ParseError} when
|
19
|
+
#
|
20
|
+
# * unknown commands are encountered,
|
21
|
+
# * the bond count is incorrect, or
|
22
|
+
# * an @endmol@'s ID does not correspond to its @mol@'s ID.
|
23
|
+
attr_accessor :lenient
|
24
|
+
|
25
|
+
# Parses HIN data and returns an array of {Molecule Molecules}.
|
26
|
+
#
|
27
|
+
# @param [IO, String] input The HIN data to parse.
|
28
|
+
# @return [Array<Alumina::Molecule>] The molecules described by the data.
|
29
|
+
# @raise [ParseError] If the HIN data is improperly formatted.
|
30
|
+
|
31
|
+
def parse(input)
|
32
|
+
@molecules = Array.new
|
33
|
+
@current_molecule = nil
|
34
|
+
@line_counter = 0
|
35
|
+
@bonds = Hash.autonew
|
36
|
+
|
37
|
+
input.each_line do |line|
|
38
|
+
@line_counter += 1
|
39
|
+
next if line =~ /^\s*$/
|
40
|
+
next if line[0] == ';' # comment
|
41
|
+
parts = line.split(/\s+/)
|
42
|
+
|
43
|
+
command = parts.shift
|
44
|
+
method = :"parse_#{command}"
|
45
|
+
if respond_to?(method) then
|
46
|
+
send(method, *parts)
|
47
|
+
else
|
48
|
+
if lenient then
|
49
|
+
next
|
50
|
+
else
|
51
|
+
raise ParseError.new(@line_counter, "Unknown command #{command}")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
@bonds.each do |molecule, firsts|
|
57
|
+
firsts.each do |atom1, lasts|
|
58
|
+
lasts.each do |atom2, bond_type|
|
59
|
+
Atom.bind molecule[atom1], molecule[atom2], BOND_TYPES[bond_type]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
return @molecules
|
65
|
+
end
|
66
|
+
|
67
|
+
protected
|
68
|
+
|
69
|
+
def parse_forcefield(*unknowns)
|
70
|
+
#TODO
|
71
|
+
end
|
72
|
+
|
73
|
+
def parse_sys(*unknowns)
|
74
|
+
#TODO
|
75
|
+
end
|
76
|
+
|
77
|
+
def parse_view(*unknowns)
|
78
|
+
#TODO
|
79
|
+
end
|
80
|
+
|
81
|
+
def parse_box(*unknowns)
|
82
|
+
#TODO
|
83
|
+
end
|
84
|
+
|
85
|
+
def parse_seed(*unknowns)
|
86
|
+
#TODO
|
87
|
+
end
|
88
|
+
|
89
|
+
# @private
|
90
|
+
def parse_mol(id, label=nil)
|
91
|
+
if @current_molecule then
|
92
|
+
raise ParseError.new(@line_counter, "Can't have a mol section within another mol section")
|
93
|
+
else
|
94
|
+
label = nil if label.blank?
|
95
|
+
@current_molecule = Molecule.new(id.to_i, label)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# @private
|
100
|
+
def parse_endmol(id)
|
101
|
+
raise ParseError.new(@line_counter, "Given endmol with id #{@id}, but open molecule has ID #{@current_molecule.id}") if id.to_i != @current_molecule.id and not lenient
|
102
|
+
|
103
|
+
@molecules << @current_molecule
|
104
|
+
@current_molecule = nil
|
105
|
+
end
|
106
|
+
|
107
|
+
# @private
|
108
|
+
def parse_atom(id, label, symbol, ignored1, ignored2, partial_charge, x, y, z, bond_count, *bonds)
|
109
|
+
if @current_molecule then
|
110
|
+
label = nil if label.blank?
|
111
|
+
element = ELEMENTS_BY_SYMBOL[symbol]
|
112
|
+
raise ParseError.new("Unknown element #{symbol}") unless element
|
113
|
+
|
114
|
+
atom = Atom.new(id.to_i, element, x.to_f, y.to_f, z.to_f)
|
115
|
+
atom.label = label
|
116
|
+
atom.ignored1 = ignored1
|
117
|
+
atom.ignored2 = ignored2
|
118
|
+
atom.partial_charge = partial_charge.to_i
|
119
|
+
|
120
|
+
bond_count = bond_count.to_i
|
121
|
+
raise ParseError.new(@line_counter, "Expected #{bond_count} bonds but found #{bonds.size/2}") if bond_count != bonds.size/2 and not lenient
|
122
|
+
|
123
|
+
bonds.in_groups_of(2).each do |(atom_id, bond_type)|
|
124
|
+
first_id = [ atom.id, atom_id.to_i ].min
|
125
|
+
last_id = [ atom.id, atom_id.to_i ].max
|
126
|
+
if @bonds[@current_molecule][first_id][last_id] != {} and @bonds[@current_molecule][first_id][last_id] != bond_type then
|
127
|
+
raise ParseError.new(@line_counter, "Assymetric bond between #{first_id} and #{last_id} of type #{bond_type}")
|
128
|
+
end
|
129
|
+
raise ParseError.new(@line_counter, "Unknown bond type #{bond_type}") unless BOND_TYPES.include?(bond_type)
|
130
|
+
@bonds[@current_molecule][first_id][last_id] = bond_type
|
131
|
+
end
|
132
|
+
@current_molecule << atom
|
133
|
+
else
|
134
|
+
raise ParseError.new(@line_counter, "Can't have an atom command outside of a mol section")
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Alumina
|
2
|
+
module HIN
|
3
|
+
|
4
|
+
# Modules that add methods for writing data in HIN format.
|
5
|
+
|
6
|
+
module Writer
|
7
|
+
|
8
|
+
# Adds methods for writing data in HIN format to {Molecule} objects.
|
9
|
+
|
10
|
+
module Molecule
|
11
|
+
|
12
|
+
# Outputs this molecule in HIN format.
|
13
|
+
|
14
|
+
def to_hin
|
15
|
+
lines = atoms.map(&:to_hin)
|
16
|
+
lines.unshift "mol #{id}#{" #{label}" if label}"
|
17
|
+
lines.push "endmol #{id}"
|
18
|
+
lines.join("\n")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Adds methods for writing data in HIN format to {Atom} objects.
|
23
|
+
|
24
|
+
module Atom
|
25
|
+
|
26
|
+
# Outputs this atom in HIN format.
|
27
|
+
|
28
|
+
def to_hin
|
29
|
+
"atom #{id} #{label || '-'} #{element.symbol} #{ignored1} #{ignored2} #{partial_charge} #{x} #{y} #{z} #{bonds.size} " + bonds.map { |atom, type| "#{atom.id} #{HIN::Parser::BOND_TYPES.key(type)}" }.join(' ')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Alumina
|
2
|
+
|
3
|
+
# A molecule as represented by HIN data, consisting of multiple {Atom Atoms}.
|
4
|
+
|
5
|
+
class Molecule
|
6
|
+
include Alumina::HIN::Writer::Molecule
|
7
|
+
|
8
|
+
# @return [Fixnum] The unique numerical identifier for this molecule.
|
9
|
+
attr_accessor :id
|
10
|
+
# @return [String, nil] The optional label given to this molecule.
|
11
|
+
attr_accessor :label
|
12
|
+
|
13
|
+
# Creates a new instance.
|
14
|
+
#
|
15
|
+
# @param [Fixnum] id The molecule's unique ID. Uniqueness is not checked in
|
16
|
+
# this method.
|
17
|
+
# @param [String] label A label to give the molecule.
|
18
|
+
|
19
|
+
def initialize(id, label=nil)
|
20
|
+
@id = id
|
21
|
+
@label = label
|
22
|
+
@atoms = Hash.new
|
23
|
+
end
|
24
|
+
|
25
|
+
# Adds an atom to this molecule. If there is already an atom in this
|
26
|
+
# molecule sharing this atom's {Atom#id ID}, it will be replaced by this
|
27
|
+
# atom.
|
28
|
+
#
|
29
|
+
# @param [Atom] atom The atom to add.
|
30
|
+
|
31
|
+
def <<(atom)
|
32
|
+
@atoms[atom.id] = atom
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [Array<Atom>] An array of atoms in this molecule.
|
36
|
+
|
37
|
+
def atoms
|
38
|
+
@atoms.values
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns an atom for a given unique identifier.
|
42
|
+
#
|
43
|
+
# @param [Fixnum] ident The identifier to search.
|
44
|
+
# @return [Atom, nil] The atom with that identifier, or @nil@ if no such
|
45
|
+
# atom was found.
|
46
|
+
|
47
|
+
def atom(ident)
|
48
|
+
@atoms[ident]
|
49
|
+
end
|
50
|
+
|
51
|
+
alias :[] :atom
|
52
|
+
|
53
|
+
# @return [String] Returns the plain-text molecular formula for this atom;
|
54
|
+
# for example, @C7H5N3O6@ for TNT.
|
55
|
+
|
56
|
+
def molecular_formula
|
57
|
+
counts = atoms.map(&:element).inject(Hash.new(0)) { |hsh, cur| hsh[cur] += 1 ; hsh }
|
58
|
+
counts.keys.sort.reverse.map { |element| "#{element.symbol}#{num_for counts[element]}" }.join
|
59
|
+
end
|
60
|
+
|
61
|
+
# @private
|
62
|
+
def inspect
|
63
|
+
if label then
|
64
|
+
"#<Molecule ##{id} (#{label}): #{molecular_formula}>"
|
65
|
+
else
|
66
|
+
"#<Molecule ##{id}: #{molecular_formula}>"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def num_for(num)
|
73
|
+
num.to_s.chars.map do |char|
|
74
|
+
case char
|
75
|
+
when '0' then ?\u2080
|
76
|
+
when '1' then ?\u2081
|
77
|
+
when '2' then ?\u2082
|
78
|
+
when '3' then ?\u2083
|
79
|
+
when '4' then ?\u2084
|
80
|
+
when '5' then ?\u2085
|
81
|
+
when '6' then ?\u2086
|
82
|
+
when '7' then ?\u2087
|
83
|
+
when '8' then ?\u2088
|
84
|
+
when '9' then ?\u2089
|
85
|
+
end
|
86
|
+
end.join
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/lib/extensions.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# @private
|
2
|
+
class String
|
3
|
+
|
4
|
+
# @private
|
5
|
+
def blank?
|
6
|
+
self =~ /^\s*$/
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
# @private
|
11
|
+
class NilClass
|
12
|
+
|
13
|
+
# @private
|
14
|
+
def blank?
|
15
|
+
true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# @private
|
20
|
+
class Array
|
21
|
+
# @private -- Credit to DHH and the Rails team
|
22
|
+
def in_groups_of(number, fill_with = nil)
|
23
|
+
if fill_with == false
|
24
|
+
collection = self
|
25
|
+
else
|
26
|
+
# size % number gives how many extra we have;
|
27
|
+
# subtracting from number gives how many to add;
|
28
|
+
# modulo number ensures we don't add group of just fill.
|
29
|
+
padding = (number - size % number) % number
|
30
|
+
collection = dup.concat([fill_with] * padding)
|
31
|
+
end
|
32
|
+
|
33
|
+
if block_given?
|
34
|
+
collection.each_slice(number) { |slice| yield(slice) }
|
35
|
+
else
|
36
|
+
returning [] do |groups|
|
37
|
+
collection.each_slice(number) { |group| groups << group }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# @private
|
44
|
+
class Object
|
45
|
+
# @private -- Credit to DHH and the Rails team
|
46
|
+
def returning(value)
|
47
|
+
yield(value)
|
48
|
+
value
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# @private
|
53
|
+
class Hash
|
54
|
+
# @private -- credit to Trans, Jan Molic, Facets team
|
55
|
+
def self.autonew(*args)
|
56
|
+
leet = lambda { |hsh, key| hsh[key] = new( &leet ) }
|
57
|
+
new(*args,&leet)
|
58
|
+
end
|
59
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: alumina
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Tim Morgan
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-06-02 00:00:00 -07:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: bacon
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :development
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: yard
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id002
|
49
|
+
description: This gem parses HyperChem's .HIN files, PDB files, and in the future others, converting them to Ruby objects for easy manipulation
|
50
|
+
email: git@timothymorgan.info
|
51
|
+
executables: []
|
52
|
+
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
extra_rdoc_files:
|
56
|
+
- LICENSE
|
57
|
+
- README.textile
|
58
|
+
files:
|
59
|
+
- .document
|
60
|
+
- .gitignore
|
61
|
+
- LICENSE
|
62
|
+
- README.textile
|
63
|
+
- Rakefile
|
64
|
+
- VERSION
|
65
|
+
- data/elements.yml
|
66
|
+
- lib/alumina.rb
|
67
|
+
- lib/alumina/atom.rb
|
68
|
+
- lib/alumina/element.rb
|
69
|
+
- lib/alumina/errors.rb
|
70
|
+
- lib/alumina/hin.rb
|
71
|
+
- lib/alumina/hin/parser.rb
|
72
|
+
- lib/alumina/hin/writer.rb
|
73
|
+
- lib/alumina/molecule.rb
|
74
|
+
- lib/extensions.rb
|
75
|
+
- spec/alumina_spec.rb
|
76
|
+
- spec/spec_helper.rb
|
77
|
+
has_rdoc: true
|
78
|
+
homepage: http://github.com/RISCfuture/alumina
|
79
|
+
licenses: []
|
80
|
+
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options:
|
83
|
+
- --charset=UTF-8
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
hash: 3
|
92
|
+
segments:
|
93
|
+
- 0
|
94
|
+
version: "0"
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
hash: 3
|
101
|
+
segments:
|
102
|
+
- 0
|
103
|
+
version: "0"
|
104
|
+
requirements: []
|
105
|
+
|
106
|
+
rubyforge_project:
|
107
|
+
rubygems_version: 1.3.7
|
108
|
+
signing_key:
|
109
|
+
specification_version: 3
|
110
|
+
summary: Ruby parser for various chemistry-related file formats
|
111
|
+
test_files:
|
112
|
+
- spec/alumina_spec.rb
|
113
|
+
- spec/spec_helper.rb
|