aims 0.2.0

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.
@@ -0,0 +1,100 @@
1
+
2
+ = What is the Aims Gem?
3
+
4
+ The Aims gem is a ruby interface to the FHI-Aims ab-initio molecular simulation package published by the Fritz-Haber Institute [https://aimsclub.fhi-berlin.mpg.de/]. It provides a set of tools for parsing and generating the input and output files for AIMS.
5
+
6
+ The aims gem is written by Joshua Shapiro and release under the MIT license.
7
+
8
+ Copyright (c) Joshua Shapiro 2012
9
+
10
+ = Why should I use the Aims Gem?
11
+
12
+ If you like Ruby, and you like AIMS, then this gem provides you with a pure Ruby object-oriented interface to AIMS. You can use this library to:
13
+
14
+ * Simplify the generation of complex geometries
15
+ * Bulk and common surface geometries of ZincBlende and Wurtzite are predefined.
16
+ * Automate the generation of geometry and control files
17
+ * Parse the output of Aims into ruby objects for analysis
18
+ * Summarize the output of AIMS using simple scripts that come with the gem
19
+ * Whatever else you can imagine..
20
+
21
+ = Installation
22
+
23
+ To use this gem, you will need to have an installation of ruby. If you own a mac or a linux machine, then you already have ruby. If you have a windows machine, then you need to install ruby. The recommended way to install Ruby on windows (as of June 2012) is via http://rubyinstaller.org .
24
+
25
+ Once you have ruby, then just invoke the following from a terminal window.
26
+
27
+ gem install aims
28
+
29
+ = Usage
30
+ == Using the Aims GEM to generate geometry.in files
31
+
32
+ The following code can be used interactively in an +irb+ ruby interpreter, or can be
33
+ invoked in a ruby script.
34
+
35
+ === Example 1: Generate a primitive unit cell of Silicon
36
+
37
+ require 'aims'
38
+ include Aims
39
+
40
+ # Define the lattice constant
41
+ lattice_const = 5.43
42
+
43
+ # Define the basis
44
+ a1 = Atom.new(0,0,0, "Si")
45
+ a2 = Atom.new(lattice_const/4, lattice_const/4, lattice_const/4, "Si")
46
+
47
+ # Define the primitive vectors
48
+ v1 = [lattice_const/2, lattice_const/2, 0]
49
+ v2 = [lattice_const/2, 0, lattice_const/2]
50
+ v3 = [0, lattice_const/2, lattice_const/2]
51
+
52
+ # Define the unit cell
53
+ uc = Geometry.new([a1, a2], [v1, v2, v3])
54
+
55
+ # Output the unit cell
56
+ puts uc.format_geometry_in
57
+
58
+ === Example 2: Shortcut for generating a primitive unit cell of Zinc-Blende
59
+
60
+ require 'aims'
61
+ include Aims
62
+
63
+ zb = ZincBlende.new("Ga", "As", 5.65)
64
+
65
+ # Get the bulk geometry
66
+ puts zb.get_bulk.format_geometry_in
67
+
68
+ And here is how you get a (100) surface with 7 layers and 20 angstrom of vacuum
69
+ layers = 7
70
+ vacuum = 20
71
+ puts zb.get_001_surface(layers, vacuum)
72
+
73
+ And here is how you can constrain the bottom three layers
74
+ constrain = 3
75
+ puts zb.get_001_surface(layers, vacuum, constrain)
76
+
77
+ == Scripts that come with the Aims GEM
78
+
79
+ There are currently two scripts that come with the GEM
80
+
81
+ === aims_output.rb
82
+ Quickly output the total energy, and timing information from the
83
+ calculation to make sure everything went smoothly.
84
+ Don't use this as a replacement for actually looking at the output of Aims.
85
+
86
+ usage: aims_output.rb [options] file1 [file2 ...]
87
+ -s, --step [N] Output information for relaxation step.
88
+ Specify an integer, 'first', 'last', or 'all'
89
+ Default is 'all'
90
+ --debug Debug output
91
+ --geometry-delta Display change from input geometry to final geometry
92
+ -c, --self-consistency Output self-consistency information
93
+ -f Output max force component for each geometry relaxation step
94
+ -t Output timings
95
+
96
+ === aims_summary.rb
97
+ Display a one-line summary for a list of calculations in tabular form.
98
+ Useful for copying and pasting into a spreadsheet.
99
+
100
+ usage: aims_summary.rb file1 [file2] ...
@@ -0,0 +1,175 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'aims'
5
+
6
+
7
+
8
+ options = {:step => :all}
9
+
10
+ optParser = OptionParser.new do |opts|
11
+ opts.banner = "usage: #{File.basename $0} [options] file1 [file2 ...]"
12
+ opts.on('-s', '--step [N]', 'Output information for relaxation step.',
13
+ "Specify an integer, 'first', 'last', or 'all'",
14
+ "Default is 'all'") do |s|
15
+ case s
16
+ when /([1-9]+)/
17
+ options[:step] = $1.to_i
18
+ when "first"
19
+ options[:step] = :first
20
+ when "last"
21
+ options[:step] = :last
22
+ else
23
+ options[:step] = :all
24
+ end
25
+ end
26
+
27
+ opts.on('--debug', 'Debug output') do
28
+ options[:debug] = true
29
+ end
30
+
31
+ opts.on('--geometry-delta', 'Display change from input geometry to final geometry') do
32
+ options[:geometry_delta] = true
33
+ end
34
+
35
+ opts.on('-c', '--self-consistency', 'Output self-consistency information') do
36
+ options[:self_consistency] = true
37
+ end
38
+
39
+ opts.on('-f', 'Output max force component for each geometry relaxation step') do
40
+ options[:forces] = true
41
+ end
42
+
43
+ opts.on('-t', 'Output timings') do
44
+ options[:timings] = true
45
+ end
46
+
47
+ end
48
+
49
+ begin
50
+ optParser.parse!(ARGV)
51
+
52
+ files = ARGV
53
+ if files.empty?
54
+ puts optParser.help
55
+ exit
56
+ end
57
+ outputs = files.collect{|f|
58
+ Aims::OutputParser.parse(f)
59
+ }
60
+
61
+ total_sc_iterations = 0
62
+ total_relaxations = 0
63
+
64
+ outputs.each{|output|
65
+
66
+ puts "**************************************************************************"
67
+ puts "**"
68
+ puts "** #{output.original_file}"
69
+ puts "**"
70
+ puts "**************************************************************************"
71
+
72
+ steps = case options[:step]
73
+ when Integer
74
+ stepno = options[:step]
75
+ if stepno < 0
76
+ [output.geometry_steps.last]
77
+ elsif stepno < output.geometry_steps.size
78
+ [output.geometry_steps[stepno]]
79
+ else
80
+ [output.geometry_steps.last]
81
+ end
82
+ when :first
83
+ [output.geometry_steps.first]
84
+ when :last
85
+ [output.geometry_steps.last]
86
+ else
87
+ output.geometry_steps
88
+ end
89
+
90
+ steps.each_with_index{|step, i|
91
+
92
+ total_relaxations += 1
93
+ total_sc_iterations += step.sc_iterations.size
94
+
95
+ sciter_format = "%-20s %20i"
96
+ timings_format = "%-35s %20.5f"
97
+ energy_format = "%-35s %20.5f"
98
+ force_format = "%-35s %20.5e"
99
+
100
+ puts "= Relaxation Step #{step.step_num} ="
101
+
102
+ indent = " "
103
+ puts indent + sciter_format % ["SC Iterations", step.sc_iterations.size]
104
+ puts indent + energy_format % ["Total Energy", step.total_energy]
105
+ puts indent + timings_format % ["Total CPU time", step.total_cpu_time]
106
+ puts indent + timings_format % ["Total Wall time", step.total_wall_time]
107
+ if options[:forces] and not step.forces.empty?
108
+ puts indent + force_format % ["Max Force", step.forces.max{|a,b| a.r <=> b.r}.r]
109
+ end
110
+ if options[:timings]
111
+ puts " Cumulative SC Timings:"
112
+ step.timings.each{|t| puts " " +timings_format % [t[:description], t[:cpu_time]]}
113
+ end
114
+
115
+ if options[:self_consistency]
116
+
117
+ indent = " "
118
+
119
+ # Iterate over each sc iteration
120
+ step.sc_iterations.each_with_index{|sc_iter, iter|
121
+ # SC Iteration Header
122
+ puts " == SC Iteration #{iter} =="
123
+
124
+ # Output convergence criterion
125
+ puts indent + energy_format % ["Change in total energy", sc_iter.d_etot]
126
+ puts indent + energy_format % ["Change in sum of eigenvalues", sc_iter.d_eev]
127
+ puts indent + energy_format % ["Change in charge density", sc_iter.d_rho]
128
+
129
+ # Output timings if requested
130
+ if options[:timings]
131
+ if sc_iter.timings
132
+ sc_iter.timings.each{|t|
133
+ puts indent + timings_format % [t[:description], t[:cpu_time]]
134
+ }
135
+ else
136
+ puts "No timing data available."
137
+ end
138
+ end
139
+ puts ""
140
+ }
141
+ end
142
+
143
+ puts "\n\n"
144
+
145
+
146
+ }
147
+
148
+ unless output.geometry_converged
149
+ puts "Warning Geometry not converged!"
150
+ end
151
+
152
+ if options[:geometry_delta]
153
+ puts "= Change in atomic positions for calculation"
154
+ puts output.geometry_steps.last.geometry.delta(output.geometry_steps.first.geometry)
155
+ end
156
+ }
157
+
158
+
159
+
160
+ # puts "Total relaxation steps: #{total_relaxations}"
161
+ # puts "Total sc iterations: #{total_sc_iterations}"
162
+
163
+ rescue
164
+ puts ""
165
+ puts "Sorry. There was an error parsing the remainder of the file."
166
+ if options[:debug]
167
+ puts $!.message
168
+ puts $!.backtrace
169
+ else
170
+ puts "Rerun with --debug for more info"
171
+ end
172
+ puts ""
173
+ exit
174
+ end
175
+
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'aims'
4
+ files = ARGV
5
+ if files.empty?
6
+ puts "usage: #{File.basename $0} file1 [file2] ..."
7
+ exit
8
+ end
9
+
10
+ STDOUT.sync = true
11
+ puts "%-10s \t %-20s \t %-15s \t %9s \t %7s \t %10s \t %12s \t %8s \t %10s" % %w(RUN FILE TOTAL_ENERGY NUM_ATOMS K-GRID CONVERGED RELAX_STEPS SC_ITERS TOTAL_TIME)
12
+ format = "%-10s \t %-20s \t %+15e \t %9i \t %7s \t %10s \t %12i \t %8i \t %10.2f"
13
+ files.each{|f|
14
+ run = f.split(".").last
15
+ begin
16
+ o = Aims::OutputParser.parse(f)
17
+ puts format % [run, f[0...20],
18
+ (o.total_energy.nan? ? Float::NAN : o.total_energy),
19
+ (o.n_atoms or -1),
20
+ (o.k_grid ? o.k_grid.squeeze : "-"),
21
+ o.geometry_converged,
22
+ (o.n_relaxation_steps or -1),
23
+ (o.n_sc_iterations or -1),
24
+ o.total_cpu_time]
25
+ rescue
26
+ puts [run, f, "***ERROR***", $!.message].join("\t")
27
+ end
28
+ }
@@ -0,0 +1,35 @@
1
+ # The Aims RubyGem is distributed under the MIT license
2
+ #
3
+ # Copyright (c) 2012 Joshua Shapiro
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this
6
+ # software and associated documentation files (the "Software"), to deal in the Software
7
+ # without restriction, including without limitation the rights to use, copy, modify,
8
+ # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to the following
10
+ # conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all copies
13
+ # or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16
+ # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
17
+ # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
18
+ # FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19
+ # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20
+ # DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'aims/vectorize.rb'
23
+ require 'aims/atom.rb'
24
+ require 'aims/bond.rb'
25
+ require 'aims/geometry_parser.rb'
26
+ require 'aims/output.rb'
27
+ require 'aims/plane.rb'
28
+ require 'aims/geometry.rb'
29
+ require 'aims/volume.rb'
30
+ require 'aims/wurtzite.rb'
31
+ require 'aims/zinc_blende.rb'
32
+
33
+ # :include:README.rdoc
34
+ module Aims
35
+ end
@@ -0,0 +1,197 @@
1
+ module Aims
2
+
3
+ class Atom
4
+ # The last id assigned to an atom
5
+ @@lastid = 0
6
+
7
+ # The x coordinate of the atom in angstrom
8
+ attr_accessor :x
9
+ # The y coordinate of the atom in angstrom
10
+ attr_accessor :y
11
+ # The z coordinate of the atom in angstrom
12
+ attr_accessor :z
13
+ # The +id+ of this atom. Every atom has a unique id
14
+ attr_accessor :id
15
+ # The species of this atom
16
+ attr_accessor :species
17
+ # The relaxation constraints of this atom
18
+ attr_accessor :constrain
19
+ # Two atoms are equal if their coordinates are the same to this precision
20
+ attr_accessor :precision
21
+
22
+ include Enumerable
23
+
24
+ # Create an atom of the specified species at the given coordinates
25
+ # * +x+ The x coordinate of the atom in angstrom
26
+ # * +y+ The y coordinate of the atom in angstrom
27
+ # * +z+ The z coordinate of the atom in angstrom
28
+ # * +s+ The atomic species ex. "C", "Si", "S", etc. (can be nil)
29
+ # * +c+ The relaxation constraints. valid values are TRUE, FALSE, ".true.", ".false.", "x", "y", "z" or %w(x y z)
30
+ def initialize(x=nil, y=nil, z=nil, s=nil, c=Array.new)
31
+ self.x = x
32
+ self.y = y
33
+ self.z = z
34
+ self.species = s
35
+ self.precision = 0.0001
36
+ self.id = (@@lastid +=1)
37
+ self.constrain = c
38
+ end
39
+
40
+ # A boolean value,
41
+ # True if the atom has relaxation constraints
42
+ def constrained?
43
+ if self.constrain
44
+ if self.constrain == true
45
+ true
46
+ elsif self.constrain.is_a? String
47
+ true
48
+ elsif self.constrain.is_a? Array and not self.constrain.empty?
49
+ true
50
+ else
51
+ false
52
+ end
53
+ else
54
+ false
55
+ end
56
+ end
57
+
58
+ # Two atoms are equal if their coordinates are equal and they are the same species
59
+ def ==(atom)
60
+ ((self.x-atom.x).abs < self.precision) &
61
+ ((self.y-atom.y).abs < self.precision) &
62
+ ((self.z-atom.z).abs < self.precision) &
63
+ (self.species == atom.species)
64
+ end
65
+ alias_method :eql?, :==
66
+
67
+ # Implementation for Hash equality testing
68
+ def hash
69
+ (self.x*self.y + self.z).abs.ceil
70
+ end
71
+
72
+ # Enumerate over each coordinate (x,y,z)
73
+ def each
74
+ [self.x, self.y, self.z].each{|i| yield i}
75
+ end
76
+
77
+ # Index into the Atom's coordinates (x,y,z)
78
+ def [](i)
79
+ case i
80
+ when 0
81
+ self.x
82
+ when 1
83
+ self.y
84
+ when 2
85
+ self.z
86
+ else
87
+ raise "Index Out of Bounds"
88
+ end
89
+ end
90
+
91
+ # Return the distance to another atom
92
+ def distance_to(atom)
93
+ Math.sqrt((self.x - atom.x)**2 + (self.y - atom.y)**2 + (self.z - atom.z)**2)
94
+ end
95
+
96
+ # A deep copy of the atom
97
+ def copy
98
+ Atom.new(self.x, self.y, self.z, self.species, self.constrain)
99
+ end
100
+
101
+ # Return a new atom with the same species and relaxation constraints
102
+ # but with coordinates displaced by +x+, +y+, +z+
103
+ def displace(x,y,z)
104
+ Atom.new(self.x+x, self.y+y, self.z+z, self.species, self.constrain)
105
+ end
106
+
107
+ # Displace this atom in place
108
+ def displace!(x,y,z)
109
+ self.x += x
110
+ self.y += y
111
+ self.z += z
112
+ end
113
+
114
+ # Return an atom rotated about the z-axis using the origin as the center-point.
115
+ # * +angle+ Is the amount to rotate in degrees (or it can respond to :sin and :cos)
116
+ def rotate_Z(angle)
117
+ sinA = if angle.respond_to? :sin
118
+ angle.sine
119
+ else
120
+ Math.sin(angle*Math::PI/180)
121
+ end
122
+ cosA = if angle.respond_to? :cos
123
+ angle.cos
124
+ else
125
+ Math.cos(angle*Math::PI/180)
126
+ end
127
+
128
+ mat = Matrix[[cosA, -1*sinA, 0],[sinA, cosA, 0], [0,0,1]]
129
+ rotate(mat)
130
+ end
131
+
132
+ # Return an atom rotated about the x-axis using the origin as the center-point.
133
+ # * +angle+ Is the amount to rotate in degrees (or it can respond to :sin and :cos)
134
+ def rotate_X(angle)
135
+ sinA = if angle.respond_to? :sin
136
+ angle.sine
137
+ else
138
+ Math.sin(angle*Math::PI/180)
139
+ end
140
+ cosA = if angle.respond_to? :cos
141
+ angle.cos
142
+ else
143
+ Math.cos(angle*Math::PI/180)
144
+ end
145
+ mat = Matrix[[1, 0, 0], [0, cosA, -1*sinA],[0, sinA, cosA]]
146
+ rotate(mat)
147
+ end
148
+
149
+ # Return an atom rotated about the y-axis using the origin as the center-point.
150
+ # * +angle+ Is the amount to rotate in degrees (or it can respond to :sin and :cos)
151
+ def rotate_Y(angle)
152
+ sinA = if angle.respond_to? :sin
153
+ angle.sine
154
+ else
155
+ Math.sin(angle*Math::PI/180)
156
+ end
157
+ cosA = if angle.respond_to? :cos
158
+ angle.cos
159
+ else
160
+ Math.cos(angle*Math::PI/180)
161
+ end
162
+ mat = Matrix[[cosA, 0, -1*sinA],[0, 1, 0], [sinA, 0, cosA]]
163
+ rotate(mat)
164
+ end
165
+
166
+ # Return a new rotated atom about the origin using the given 3x3 Math::Matrix.
167
+ def rotate(mat)
168
+ v = Vector[self.x, self.y, self.z]
169
+ newv = mat*v
170
+ Atom.new(newv[0], newv[1], newv[2], self.species, self.constrain)
171
+ end
172
+
173
+ # Print a string representation of this atom
174
+ def to_s
175
+ "%s %16.6f %16.6f %16.6f" % [self.species, self.x, self.y, self.z]
176
+ end
177
+
178
+ # Print a string representation of this atom formatted in the
179
+ # geometry.in format used by Aims
180
+ def format_geometry_in
181
+ line = "atom %16.6f %16.6f %16.6f %s" % [self.x, self.y, self.z, self.species]
182
+ if self.constrain
183
+ if self.constrain == true
184
+ line << "\nconstrain_relaxation .true."
185
+ elsif self.constrain.is_a? String
186
+ line << "\nconstrain_relaxation #{self.constrain}"
187
+ elsif self.constrain.is_a? Array and not self.constrain.empty?
188
+ self.constrain.each{|c|
189
+ line << "\nconstrain_relaxation #{c}"
190
+ }
191
+ line << "\n"
192
+ end
193
+ end
194
+ line
195
+ end
196
+ end
197
+ end