alumina 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,23 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+ .yardoc
21
+ doc
22
+
23
+ ## PROJECT::SPECIFIC
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.
@@ -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.
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,7 @@
1
+ module Alumina
2
+
3
+ # Container module for classes working with HyperChem's HIN file format.
4
+
5
+ module HIN
6
+ end
7
+ end
@@ -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
@@ -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
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Alumina" do
4
+ it "fails" do
5
+ should.flunk "hey buddy, you should probably rename this file and start specing for real"
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'bacon'
3
+
4
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ require 'alumina'
7
+
8
+ Bacon.summary_on_exit
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