aims 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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