aims_project 0.3.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.
- data/README +5 -0
- data/bin/AimsCalc +367 -0
- data/bin/AimsProject +88 -0
- data/bin/AimsProjectManager +39 -0
- data/lib/aims_project.rb +158 -0
- data/lib/aims_project/aims_project_exception.rb +42 -0
- data/lib/aims_project/aims_project_geometry.rb +52 -0
- data/lib/aims_project/app_controller.rb +298 -0
- data/lib/aims_project/atom.rb +95 -0
- data/lib/aims_project/calculation.rb +406 -0
- data/lib/aims_project/calculation_tree.rb +65 -0
- data/lib/aims_project/calculation_window.rb +141 -0
- data/lib/aims_project/crystal_viewer.rb +981 -0
- data/lib/aims_project/crystal_viewer_options.rb +103 -0
- data/lib/aims_project/geometry_editor.rb +111 -0
- data/lib/aims_project/geometry_file.rb +184 -0
- data/lib/aims_project/geometry_window.rb +172 -0
- data/lib/aims_project/green_arrow.jpg +0 -0
- data/lib/aims_project/inspector.rb +183 -0
- data/lib/aims_project/material.rb +30 -0
- data/lib/aims_project/octree.rb +5 -0
- data/lib/aims_project/pan.gif +0 -0
- data/lib/aims_project/project.rb +102 -0
- data/lib/aims_project/project_tree.rb +62 -0
- data/lib/aims_project/rotate.gif +0 -0
- data/lib/aims_project/thread_callback_event.rb +19 -0
- data/lib/aims_project/zoom.gif +0 -0
- data/skeleton/Capfile +37 -0
- data/skeleton/config/aims.sh +58 -0
- data/skeleton/config/tasks.rb +145 -0
- data/skeleton/config/user_variables.rb +37 -0
- data/skeleton/control/example.erb +41 -0
- data/skeleton/geometry/example +1 -0
- metadata +136 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#
|
|
2
|
+
# copyright 2012, Joshua Shapiro
|
|
3
|
+
# joshua.shapiro@gmail.com
|
|
4
|
+
#
|
|
5
|
+
# This file is part of AimsProjectManager.
|
|
6
|
+
#
|
|
7
|
+
# AimsProjectManager is free software: you can redistribute it and/or modify
|
|
8
|
+
# it under the terms of the GNU General Public License as published by
|
|
9
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
10
|
+
# (at your option) any later version.
|
|
11
|
+
#
|
|
12
|
+
# AimsProjectManager is distributed in the hope that it will be useful,
|
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15
|
+
# GNU General Public License for more details.
|
|
16
|
+
#
|
|
17
|
+
#
|
|
18
|
+
module Aims
|
|
19
|
+
|
|
20
|
+
# Adds display behavior to atoms.
|
|
21
|
+
class Atom
|
|
22
|
+
|
|
23
|
+
@@black = nil
|
|
24
|
+
@@white = nil
|
|
25
|
+
@@blue = nil
|
|
26
|
+
@@red = nil
|
|
27
|
+
@@green = nil
|
|
28
|
+
@@dark = nil
|
|
29
|
+
@@light = nil
|
|
30
|
+
@@yellow = nil
|
|
31
|
+
@@orange = nil
|
|
32
|
+
@@graypurple = nil
|
|
33
|
+
|
|
34
|
+
def material
|
|
35
|
+
unless @@yellow
|
|
36
|
+
@@yellow = AimsProject::Material.new(1,1,0,1)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
unless @@black
|
|
40
|
+
@@lback = AimsProject::Material.new(0,0,0,1)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
unless @@white
|
|
44
|
+
@@lback = AimsProject::Material.new(1,1,1,1)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
unless @@blue
|
|
48
|
+
@@blue = AimsProject::Material.new(0,0,1,1)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
unless @@red
|
|
52
|
+
@@red = AimsProject::Material.new(1,0.4,0.4,1)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
unless @@green
|
|
56
|
+
@@green = AimsProject::Material.new(0,1,0,1)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
unless @@orange
|
|
60
|
+
@@orange = AimsProject::Material.new(1, 0.5, 0, 1)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
unless @@graypurple
|
|
64
|
+
@@graypurple = AimsProject::Material.new(0.45, 0.55, 0.55, 1)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
unless @@dark
|
|
68
|
+
@@dark = AimsProject::Material.new(0.2,0.2,0.2,1)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
unless @@light
|
|
72
|
+
@@light = AimsProject::Material.new(1,1,1,1)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
case self.species
|
|
76
|
+
when /Ga/
|
|
77
|
+
@@dark
|
|
78
|
+
when /As/
|
|
79
|
+
@@light
|
|
80
|
+
when /In/
|
|
81
|
+
@@graypurple
|
|
82
|
+
when /P/
|
|
83
|
+
@@orange
|
|
84
|
+
when /Si/
|
|
85
|
+
@@yellow
|
|
86
|
+
when /C/
|
|
87
|
+
@@red
|
|
88
|
+
else
|
|
89
|
+
@@blue
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
end
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
|
|
2
|
+
require 'fileutils'
|
|
3
|
+
require 'erb'
|
|
4
|
+
require 'date'
|
|
5
|
+
|
|
6
|
+
#
|
|
7
|
+
# A calculation is a combination of a geometry, a control file, and an output
|
|
8
|
+
# Each calculation runs in its own directory
|
|
9
|
+
# A special file named .calc_status in the calculation directory reveals the status of the calculation
|
|
10
|
+
# The format of this file is still under consideration.
|
|
11
|
+
# possible options are:
|
|
12
|
+
# • A single word indicating the status
|
|
13
|
+
# • A single word on the first line and unstructured text following (for comments, history, errors)
|
|
14
|
+
# • YAML
|
|
15
|
+
#
|
|
16
|
+
# The calculation progresses through the following state machine
|
|
17
|
+
# STAGED:
|
|
18
|
+
# This is the initial stage, before the calculation has been
|
|
19
|
+
# moved to the computation server and queued for execution.
|
|
20
|
+
# Valid inputs are CANCEL, ENQUEUE
|
|
21
|
+
# QUEUED:
|
|
22
|
+
# Calculation is uploaded to computation server and queued for execution.
|
|
23
|
+
# Valid inputs are CANCEL, PROGRESS
|
|
24
|
+
# RUNNING:
|
|
25
|
+
# Calculation is in progress.
|
|
26
|
+
# Valid inputs are CANCEL
|
|
27
|
+
# COMPLETE:
|
|
28
|
+
# Calculation is completed
|
|
29
|
+
# No valid inputs
|
|
30
|
+
# ABORTED:
|
|
31
|
+
module AimsProject
|
|
32
|
+
|
|
33
|
+
class Calculation
|
|
34
|
+
|
|
35
|
+
# The name of the geometry file
|
|
36
|
+
attr_accessor :geometry
|
|
37
|
+
|
|
38
|
+
# The name of the control file
|
|
39
|
+
attr_accessor :control
|
|
40
|
+
|
|
41
|
+
# The current calculation status
|
|
42
|
+
attr_accessor :status
|
|
43
|
+
|
|
44
|
+
# The calculation subdirectory, (can be nil)
|
|
45
|
+
attr_accessor :calc_subdir
|
|
46
|
+
|
|
47
|
+
# An array of (#to_s) items that are the calculation history
|
|
48
|
+
attr_accessor :history
|
|
49
|
+
|
|
50
|
+
# Timestamp indicating creation of this calculation
|
|
51
|
+
attr_writer :created_at
|
|
52
|
+
def created_at
|
|
53
|
+
@created_at = cast_as_date(@created_at)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Timestamp indicating last update of this calculation
|
|
57
|
+
# (currently only updates when saved)
|
|
58
|
+
attr_writer :updated_at
|
|
59
|
+
def updated_at
|
|
60
|
+
# Cast value to Date
|
|
61
|
+
# Do this in the accessor because loading from YAML bypasses the setter method
|
|
62
|
+
@updated_at = cast_as_date(@updated_at)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def cast_as_date(obj)
|
|
66
|
+
if obj.is_a? Date or obj.is_a? Time or obj.is_a? DateTime
|
|
67
|
+
obj.to_datetime
|
|
68
|
+
elsif obj.is_a? String
|
|
69
|
+
DateTime.parse(obj)
|
|
70
|
+
# unless s
|
|
71
|
+
# s = DateTime.strptime(obj, "%F %T %z")
|
|
72
|
+
# end
|
|
73
|
+
# unless s
|
|
74
|
+
# s = DateTime.strptime(obj, "%FT%s%z")
|
|
75
|
+
# end
|
|
76
|
+
# s
|
|
77
|
+
else
|
|
78
|
+
DateTime.new(0)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Find all calculations in the current directory
|
|
83
|
+
# with a given status
|
|
84
|
+
def Calculation.find_all(status)
|
|
85
|
+
# Catch all root calculations
|
|
86
|
+
calculations = Dir.glob File.join(AimsProject::CALCULATION_DIR, "*", AimsProject::CALC_STATUS_FILENAME)
|
|
87
|
+
# Catch all sub calculations
|
|
88
|
+
calculations << Dir.glob(File.join(AimsProject::CALCULATION_DIR, "*", "*", AimsProject::CALC_STATUS_FILENAME))
|
|
89
|
+
calculations.collect{|calc_status_file|
|
|
90
|
+
calc_dir = File.dirname(calc_status_file)
|
|
91
|
+
calc = Calculation.load(calc_dir)
|
|
92
|
+
if (status == calc.status)
|
|
93
|
+
calc
|
|
94
|
+
else
|
|
95
|
+
nil
|
|
96
|
+
end
|
|
97
|
+
}.compact
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Load a calculation from the serialized yaml file in the given directory
|
|
101
|
+
# raises an *ObjectFileNotFoundException* if the specified directory does
|
|
102
|
+
# not contain a yaml serialization of the calculation.
|
|
103
|
+
# raises a *CorruptObjectFileException* if the yaml file cannot be de-serialized
|
|
104
|
+
def Calculation.load(dir)
|
|
105
|
+
|
|
106
|
+
calc_file = File.join(dir, AimsProject::CALC_STATUS_FILENAME)
|
|
107
|
+
raise ObjectFileNotFoundException.new(calc_file) unless File.exists?(calc_file)
|
|
108
|
+
|
|
109
|
+
f = File.open(calc_file, 'r')
|
|
110
|
+
calc_obj = YAML.load(f)
|
|
111
|
+
f.close
|
|
112
|
+
|
|
113
|
+
raise CorruptObjectFileException.new(calc_file) unless calc_obj
|
|
114
|
+
return calc_obj
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Create a calculation and the corresponding
|
|
118
|
+
# directory structure given a geometry and a control
|
|
119
|
+
# This method will search for the files geometry.#{geometry}.in
|
|
120
|
+
# and control.#{control}.in in the project directory, then
|
|
121
|
+
# create a calculation directory that is the merger of those two
|
|
122
|
+
# filenames, and finally copy the geometry and control files into
|
|
123
|
+
# the calculation directory and rename them geometry.in and control.in
|
|
124
|
+
# @param [String] geometry The filename of the geometry file to use to initialize the calculation
|
|
125
|
+
# @param [String] control The filename of the control file to use to initialize the calculation
|
|
126
|
+
# @param [Hash<Symbol, Object>] user_vars A symbol=>Object hash of variables that will be available when
|
|
127
|
+
# evaluating the geometry and control files using embedded ruby
|
|
128
|
+
# This hash is also used to generate a calculation subdirectory
|
|
129
|
+
def Calculation.create(project, geometry, control, user_vars = {})
|
|
130
|
+
|
|
131
|
+
calc = Calculation.new(geometry, control)
|
|
132
|
+
calc.created_at = Time.new
|
|
133
|
+
|
|
134
|
+
control_in = calc.control_file
|
|
135
|
+
geometry_in = calc.geometry_file
|
|
136
|
+
|
|
137
|
+
# Define the calculation sub-directory if user_vars exists
|
|
138
|
+
unless user_vars.empty?
|
|
139
|
+
calc.calc_subdir = user_vars.keys.collect{|k| (k.to_s + "=" + user_vars[k].to_s).gsub('@', '').gsub(' ','_') }.join("..")
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Add configuration variables to the calculation binding
|
|
143
|
+
uvars_file = File.join(AimsProject::CONFIG_DIR, "user_variables.rb")
|
|
144
|
+
calc.get_binding.eval(File.read(uvars_file)) if File.exists?(uvars_file)
|
|
145
|
+
|
|
146
|
+
# Merge project variables into calcuation binding
|
|
147
|
+
if project
|
|
148
|
+
project.instance_variables.each{|v|
|
|
149
|
+
if v == :@name # Ignore the project name
|
|
150
|
+
calc.instance_variable_set(:@project_name, project.instance_variable_get(v))
|
|
151
|
+
else
|
|
152
|
+
calc.instance_variable_set(v, project.instance_variable_get(v))
|
|
153
|
+
end
|
|
154
|
+
}
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Merge user-vars to the calculation binding
|
|
158
|
+
user_vars.each_pair{|sym, val|
|
|
159
|
+
calc.instance_variable_set(sym, val)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
# Check file existence
|
|
164
|
+
raise "Unable to locate #{control_in}" unless File.exists?(control_in)
|
|
165
|
+
raise "Unable to locate #{geometry_in}" unless File.exists?(geometry_in)
|
|
166
|
+
|
|
167
|
+
# Validate the files
|
|
168
|
+
raise "#{geometry_in} has changed since last use" unless check_version(geometry_in)
|
|
169
|
+
raise "#{control_in} has changed since last use" unless check_version(control_in)
|
|
170
|
+
|
|
171
|
+
# Validate that the directory doesn't already exist
|
|
172
|
+
if Dir.exists? calc.calculation_directory
|
|
173
|
+
raise "Could not create calculation.\n #{calc.calculation_directory} already exists. \n\n If you really want to re-create this calculation, then manually delete it and try again. \n"
|
|
174
|
+
end
|
|
175
|
+
FileUtils.mkdir_p calc.calculation_directory
|
|
176
|
+
|
|
177
|
+
erb = ERB.new(File.read(control_in))
|
|
178
|
+
File.open File.join(calc.calculation_directory, "control.in"), "w" do |f|
|
|
179
|
+
f.puts erb.result(calc.get_binding)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
erb = ERB.new(File.read(geometry_in))
|
|
183
|
+
File.open File.join(calc.calculation_directory, "geometry.in"), "w" do |f|
|
|
184
|
+
f.puts erb.result(calc.get_binding)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
calc.status = AimsProject::STAGED
|
|
189
|
+
calc.save
|
|
190
|
+
|
|
191
|
+
return calc
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Get the binding for this calculation
|
|
195
|
+
def get_binding
|
|
196
|
+
binding()
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# The name of this calculation
|
|
200
|
+
def name
|
|
201
|
+
"#{geometry}.#{control}"
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Intended to replace @geometry, but needs lots of regression testing
|
|
205
|
+
# for now, will just generate this on the fly
|
|
206
|
+
def input_geometry
|
|
207
|
+
unless @actual_geometry
|
|
208
|
+
@input_geometry = Aims::GeometryParser.parse(File.join(self.calculation_directory, "geometry.in"))
|
|
209
|
+
end
|
|
210
|
+
@input_geometry
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Set the status to HOLD.
|
|
214
|
+
# Only possible if status is currently STAGED
|
|
215
|
+
def hold
|
|
216
|
+
if STAGED == status
|
|
217
|
+
self.status = HOLD
|
|
218
|
+
save
|
|
219
|
+
return true
|
|
220
|
+
else
|
|
221
|
+
return false
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Set the status to STAGED if current status is HOLD
|
|
226
|
+
def release
|
|
227
|
+
if HOLD == status
|
|
228
|
+
self.status = STAGED
|
|
229
|
+
save
|
|
230
|
+
return true
|
|
231
|
+
else
|
|
232
|
+
return false
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# Create a new calculation that will restart a geometry relaxation
|
|
237
|
+
# calculation using the last available geometry.
|
|
238
|
+
# This method will generate a new file in
|
|
239
|
+
# the geometry directory with the extension +restartN+, where
|
|
240
|
+
# N will be incremented if the filename already exists.
|
|
241
|
+
# A new calculation will be created with the new geometry and
|
|
242
|
+
# the original control.
|
|
243
|
+
def restart_relaxation
|
|
244
|
+
|
|
245
|
+
# Create a new geometry file
|
|
246
|
+
geometry_orig = self.geometry_file
|
|
247
|
+
|
|
248
|
+
# Append the subdirectory if a subdir
|
|
249
|
+
if @calc_subdir
|
|
250
|
+
geometry_orig = geometry_orig + ".#{calc_subdir}"
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
# If restarting a restart, then increment the digit
|
|
254
|
+
if (geometry_orig.split(".").last =~ /restart(\d+)/)
|
|
255
|
+
n = $1.to_i
|
|
256
|
+
geometry_orig = geometry_orig.split(".")[0...-1].join(".")
|
|
257
|
+
else
|
|
258
|
+
n = 0
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
begin
|
|
262
|
+
n += 1
|
|
263
|
+
geometry_new = geometry_orig + ".restart#{n}"
|
|
264
|
+
end while File.exist?(geometry_new)
|
|
265
|
+
|
|
266
|
+
File.open(geometry_new, 'w') do |f|
|
|
267
|
+
f.puts "# Final geometry from #{calculation_directory}"
|
|
268
|
+
f.puts self.geometry_next_step.format_geometry_in
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
Calculation.create(nil, geometry_new, self.control)
|
|
272
|
+
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# Initialize a new calculation. Consider using Calculation#create
|
|
276
|
+
# to generate the directory structure as well.
|
|
277
|
+
# @param [String] geometry the filename of the input geometry
|
|
278
|
+
# @param [String] control the filename of the input control
|
|
279
|
+
def initialize(geometry, control)
|
|
280
|
+
self.geometry = File.basename(geometry)
|
|
281
|
+
self.control = File.basename(control)
|
|
282
|
+
self.history = Array.new
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
# Serialize this calculation as a yaml file
|
|
286
|
+
def save(dir = nil)
|
|
287
|
+
self.updated_at = Time.new
|
|
288
|
+
dir = calculation_directory unless dir
|
|
289
|
+
File.open(File.join(dir, AimsProject::CALC_STATUS_FILENAME), 'w') do |f|
|
|
290
|
+
f.puts YAML.dump(self)
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# Reload this calculation from the serialized YAML file
|
|
295
|
+
def reload
|
|
296
|
+
c = Calculation.load(self.calculation_directory)
|
|
297
|
+
self.geometry = c.geometry
|
|
298
|
+
@input_geometry = c.input_geometry
|
|
299
|
+
self.control = c.control
|
|
300
|
+
self.status = c.status
|
|
301
|
+
return self
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
# Determine the name of the control.in file from the
|
|
305
|
+
# control variable.
|
|
306
|
+
def control_file
|
|
307
|
+
File.join(AimsProject::CONTROL_DIR, self.control)
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
# Determine the name of the geometr.in file from the
|
|
311
|
+
# geometry variable
|
|
312
|
+
def geometry_file
|
|
313
|
+
File.join(AimsProject::GEOMETRY_DIR, self.geometry)
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
#
|
|
317
|
+
# Check for the existence of a cached version of the input file
|
|
318
|
+
# If it exists, check if the cached version is the same
|
|
319
|
+
# as the working version, and return true if they are, false if they are not.
|
|
320
|
+
# If the cached version does not exist, then cache the working version and return true.
|
|
321
|
+
def Calculation.check_version(file)
|
|
322
|
+
cache_dir = ".input_cache"
|
|
323
|
+
unless File.exists? cache_dir
|
|
324
|
+
Dir.mkdir cache_dir
|
|
325
|
+
Dir.mkdir File.join(cache_dir, AimsProject::GEOMETRY_DIR)
|
|
326
|
+
Dir.mkdir File.join(cache_dir, AimsProject::CONTROL_DIR)
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
return false unless File.exists?(file)
|
|
330
|
+
cache_version = File.join(cache_dir, file)
|
|
331
|
+
if File.exists?(cache_version)
|
|
332
|
+
return FileUtils.compare_file(file, cache_version)
|
|
333
|
+
else
|
|
334
|
+
FileUtils.cp_r file, cache_version
|
|
335
|
+
return true
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
# The path of this calculation relative to the project
|
|
341
|
+
def relative_path
|
|
342
|
+
calculation_directory
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
#
|
|
346
|
+
# Return the directory for this calculation
|
|
347
|
+
#
|
|
348
|
+
def calculation_directory
|
|
349
|
+
File.join AimsProject::CALCULATION_DIR, self.name, (@calc_subdir || "")
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def load_output(output_pattern = "*output*")
|
|
353
|
+
output_files = Dir.glob(File.join(calculation_directory, output_pattern))
|
|
354
|
+
if output_files.empty?
|
|
355
|
+
@output = nil
|
|
356
|
+
else
|
|
357
|
+
@output = Aims::OutputParser.parse(output_files.last)
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
# Search the calculation directory for the calculation output.
|
|
362
|
+
# If found, parse it and return the Aims::AimsOutput object, otherwise
|
|
363
|
+
# return nil.
|
|
364
|
+
# If multiple output files are found, use the last one in the list
|
|
365
|
+
# when sorted alpha-numerically. (This is assumed to be the most recent calculation)
|
|
366
|
+
def output(output_pattern = "*output*")
|
|
367
|
+
unless @output
|
|
368
|
+
load_output(output_pattern)
|
|
369
|
+
end
|
|
370
|
+
@output
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
# Parse the calculation output and return the final geometry
|
|
374
|
+
# of this calculation. Return nil if no output is found.
|
|
375
|
+
def final_geometry
|
|
376
|
+
# ouput is not cached, so we only retrieve it once
|
|
377
|
+
o = self.output
|
|
378
|
+
if o
|
|
379
|
+
o.final_geometry
|
|
380
|
+
else
|
|
381
|
+
nil
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
# Parse the geometry.in.next_step file in the calculation directory
|
|
386
|
+
# if it exists and return the Aims::Geometry object or nil
|
|
387
|
+
def geometry_next_step
|
|
388
|
+
g_file = File.join(calculation_directory, "geometry.in.next_step")
|
|
389
|
+
if File.exists?(g_file)
|
|
390
|
+
Aims::GeometryParser.parse(g_file)
|
|
391
|
+
else
|
|
392
|
+
nil
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
# Return whether this calculation is converged or not
|
|
397
|
+
def converged?
|
|
398
|
+
if output.nil?
|
|
399
|
+
false
|
|
400
|
+
else
|
|
401
|
+
output.geometry_converged
|
|
402
|
+
end
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
end
|
|
406
|
+
end
|