aims_project_windows 0.3.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/README +5 -0
- data/bin/AimsCalc +367 -0
- data/bin/AimsProject +88 -0
- data/bin/AimsProjectManager +39 -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 +245 -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 +994 -0
- data/lib/aims_project/crystal_viewer_options.rb +103 -0
- data/lib/aims_project/geometry_console.rb +155 -0
- data/lib/aims_project/geometry_editor.rb +83 -0
- data/lib/aims_project/geometry_file.rb +183 -0
- data/lib/aims_project/geometry_window.rb +160 -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/lib/aims_project.rb +158 -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 +137 -0
@@ -0,0 +1,183 @@
|
|
1
|
+
module AimsProject
|
2
|
+
class Inspector < Wx::Frame
|
3
|
+
|
4
|
+
include Wx
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def control_value_accessor(*args)
|
8
|
+
args.each do |cb|
|
9
|
+
class_eval <<-EOS
|
10
|
+
def #{cb.to_s}
|
11
|
+
@#{cb.to_s}.get_value
|
12
|
+
end
|
13
|
+
EOS
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
control_value_accessor :bond_length
|
19
|
+
control_value_accessor :show_bonds, :show_lighting, :show_cell
|
20
|
+
control_value_accessor :show_clip_planes, :show_xclip, :show_yclip, :show_zclip
|
21
|
+
control_value_accessor :x_repeat, :y_repeat, :z_repeat
|
22
|
+
control_value_accessor :correct
|
23
|
+
control_value_accessor :transparent_bg
|
24
|
+
|
25
|
+
attr_accessor :update_unit_cell
|
26
|
+
|
27
|
+
def initialize(app, frame)
|
28
|
+
|
29
|
+
super(frame, :style => (CLOSE_BOX | CAPTION | FRAME_FLOAT_ON_PARENT))
|
30
|
+
|
31
|
+
@app = app
|
32
|
+
|
33
|
+
# Array to track the inspector windows being shown
|
34
|
+
@windows = []
|
35
|
+
|
36
|
+
self.sizer = BoxSizer.new(VERTICAL)
|
37
|
+
set_auto_layout(true)
|
38
|
+
self.sizer.set_size_hints(self)
|
39
|
+
self.sizer.fit(self)
|
40
|
+
|
41
|
+
self.update_unit_cell = false
|
42
|
+
self.evt_close {|event| self.hide }
|
43
|
+
end
|
44
|
+
|
45
|
+
# Create a window to show in the inspector. Default is to not show this window.
|
46
|
+
# call show_inspector_window() to show it
|
47
|
+
# options are Wx:Sizer options
|
48
|
+
# return the window so that caller can add controls to it
|
49
|
+
def add_inspector_window(options = {})
|
50
|
+
window = Panel.new(self)
|
51
|
+
@windows << window
|
52
|
+
self.sizer.add_item(window, options)
|
53
|
+
self.sizer.show(window, false)
|
54
|
+
return window
|
55
|
+
end
|
56
|
+
|
57
|
+
# Show the inspector window
|
58
|
+
def show_inspector_window(window)
|
59
|
+
@windows.each {|w|
|
60
|
+
self.sizer.show(w, w == window)
|
61
|
+
}
|
62
|
+
self.sizer.fit(self)
|
63
|
+
end
|
64
|
+
|
65
|
+
# The view options
|
66
|
+
def view_options_panel(parent)
|
67
|
+
|
68
|
+
# Default padding around each element
|
69
|
+
border = 5
|
70
|
+
|
71
|
+
panel = Wx::VBoxSizer.new
|
72
|
+
parent.sizer = panel
|
73
|
+
|
74
|
+
# Panel 1
|
75
|
+
panel1 = Wx::VBoxSizer.new
|
76
|
+
|
77
|
+
@show_bonds = Wx::CheckBox.new(parent, :label => 'Show Bonds')
|
78
|
+
panel1.add_item(@show_bonds, :flag => ALL,:border => border)
|
79
|
+
|
80
|
+
@show_lighting = Wx::CheckBox.new(parent, :label => 'Show Lighting')
|
81
|
+
panel1.add_item(@show_lighting, :flag => ALL, :border => border)
|
82
|
+
|
83
|
+
@show_cell = Wx::CheckBox.new(parent, :label => "Show Unit Cell")
|
84
|
+
panel1.add_item(@show_cell, :flag => ALL, :border => border)
|
85
|
+
|
86
|
+
@correct = Wx::CheckBox.new(parent, :label => "Correct Geometry")
|
87
|
+
panel1.add_item(@correct, :flag => ALL, :border => border)
|
88
|
+
|
89
|
+
@transparent_bg = Wx::CheckBox.new(parent, :label => "Transparent BG")
|
90
|
+
panel1.add_item(@transparent_bg, :flag => ALL, :border => border)
|
91
|
+
|
92
|
+
gsizer = GridSizer.new(2,1)
|
93
|
+
gsizer.add(StaticText.new(parent, :label => "Bond Length"))
|
94
|
+
@bond_length = SpinCtrl.new(parent, :min => 1, :max => 100, :value => "4")
|
95
|
+
gsizer.add_item(@bond_length, :flag => EXPAND)
|
96
|
+
panel1.add_item(gsizer)
|
97
|
+
|
98
|
+
evt_checkbox(@show_bonds) {|evt| @app.update_viewer}
|
99
|
+
evt_checkbox(@show_lighting) {|evt| @app.update_viewer}
|
100
|
+
evt_checkbox(@show_cell) {|evt| @app.update_viewer}
|
101
|
+
evt_checkbox(@transparent_bg) {|evt| @app.update_viewer}
|
102
|
+
evt_checkbox(@correct) {|evt|
|
103
|
+
self.update_unit_cell = true
|
104
|
+
@app.update_viewer
|
105
|
+
}
|
106
|
+
evt_spinctrl(@bond_length) {|evt| @app.update_viewer }
|
107
|
+
|
108
|
+
# Panel 2 : Clip Planes
|
109
|
+
panel2 = Wx::VBoxSizer.new
|
110
|
+
panel2.add_item(StaticText.new(parent, -1, "Clip Planes:", :style => ALIGN_LEFT))
|
111
|
+
@show_xclip = Wx::CheckBox.new(parent, :label => 'x')
|
112
|
+
panel2.add_item(@show_xclip, 0, Wx::ALIGN_CENTER, border)
|
113
|
+
@show_yclip = Wx::CheckBox.new(parent, :label => 'y')
|
114
|
+
panel2.add_item(@show_yclip, 0, Wx::ALIGN_CENTER, border)
|
115
|
+
@show_zclip = Wx::CheckBox.new(parent, :label => 'z')
|
116
|
+
panel2.add_item(@show_zclip, 0, Wx::ALIGN_CENTER, border)
|
117
|
+
|
118
|
+
|
119
|
+
evt_checkbox(@show_xclip) {|evt| @app.update_viewer}
|
120
|
+
evt_checkbox(@show_yclip) {|evt| @app.update_viewer}
|
121
|
+
evt_checkbox(@show_zclip) {|evt| @app.update_viewer}
|
122
|
+
|
123
|
+
# Panel 3: Repeat
|
124
|
+
panel3 = Wx::VBoxSizer.new
|
125
|
+
panel3.add_item(StaticText.new(parent, -1, "Repeat:", :style => ALIGN_LEFT), 0, EXPAND)
|
126
|
+
|
127
|
+
panel3grid = GridSizer.new(3,2, border, border)
|
128
|
+
|
129
|
+
@x_repeat = Wx::SpinCtrl.new(parent, :value => "1", :min => 1, :max => 100, :initial => 1)
|
130
|
+
@y_repeat = Wx::SpinCtrl.new(parent, :value => "1", :min => 1, :max => 100, :initial => 1)
|
131
|
+
@z_repeat = Wx::SpinCtrl.new(parent, :value => "1", :min => 1, :max => 100, :initial => 1)
|
132
|
+
|
133
|
+
evt_spinctrl(@x_repeat) {|evt|
|
134
|
+
self.update_unit_cell = true
|
135
|
+
@app.update_viewer
|
136
|
+
}
|
137
|
+
evt_spinctrl(@y_repeat) {|evt|
|
138
|
+
self.update_unit_cell = true
|
139
|
+
@app.update_viewer
|
140
|
+
}
|
141
|
+
evt_spinctrl(@z_repeat) {|evt|
|
142
|
+
self.update_unit_cell = true
|
143
|
+
@app.update_viewer
|
144
|
+
}
|
145
|
+
|
146
|
+
panel3grid.add(StaticText.new(parent, :label => "x", :style => ALIGN_RIGHT),1, EXPAND)
|
147
|
+
panel3grid.add(@x_repeat,2)
|
148
|
+
|
149
|
+
panel3grid.add(StaticText.new(parent, :label => "y",:style => ALIGN_RIGHT),1, EXPAND)
|
150
|
+
panel3grid.add(@y_repeat,2)
|
151
|
+
|
152
|
+
panel3grid.add(StaticText.new(parent, :label => "z",:style => ALIGN_RIGHT),1, EXPAND)
|
153
|
+
panel3grid.add(@z_repeat,2)
|
154
|
+
|
155
|
+
panel3.add(panel3grid, 0, EXPAND | ALIGN_CENTER)
|
156
|
+
|
157
|
+
# Add sub-panels
|
158
|
+
panel.add(panel1, 0, Wx::ALL)
|
159
|
+
panel.add_spacer(5)
|
160
|
+
panel.add(panel2, 0, Wx::EXPAND)
|
161
|
+
panel.add_spacer(5)
|
162
|
+
panel.add(panel3, 0, Wx::EXPAND)
|
163
|
+
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
# Called when a new file is loaded
|
168
|
+
def update(viewer)
|
169
|
+
@show_bonds.set_value(viewer.show_bonds)
|
170
|
+
@show_lighting.set_value(viewer.lighting)
|
171
|
+
@show_cell.set_value(viewer.show_supercell)
|
172
|
+
@correct.set_value(false)
|
173
|
+
@show_xclip.set_value(viewer.show_xclip)
|
174
|
+
@show_yclip.set_value(viewer.show_yclip)
|
175
|
+
@show_zclip.set_value(viewer.show_zclip)
|
176
|
+
@bond_length.set_value(viewer.bond_length)
|
177
|
+
@x_repeat.set_value(1)
|
178
|
+
@y_repeat.set_value(1)
|
179
|
+
@z_repeat.set_value(1)
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module AimsProject
|
2
|
+
class Material
|
3
|
+
|
4
|
+
include Gl
|
5
|
+
|
6
|
+
attr_accessor :r, :g, :b, :alpha
|
7
|
+
|
8
|
+
def Material.black
|
9
|
+
Material.new(0,0,0)
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(r,g,b,alpha=1)
|
13
|
+
self.r = r
|
14
|
+
self.g = g
|
15
|
+
self.b = b
|
16
|
+
self.alpha = alpha
|
17
|
+
end
|
18
|
+
|
19
|
+
def apply(lighting = true)
|
20
|
+
if lighting
|
21
|
+
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, [self.r, self.g, self.b, self.alpha])
|
22
|
+
glMaterialfv(GL_FRONT, GL_SPECULAR, [self.r, self.g, self.b, self.alpha])
|
23
|
+
glMaterialf(GL_FRONT, GL_SHININESS, 50)
|
24
|
+
else
|
25
|
+
glColor3f(self.r, self.g, self.b)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
Binary file
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module AimsProject
|
4
|
+
|
5
|
+
class Project
|
6
|
+
|
7
|
+
# A project is a collection of calculations
|
8
|
+
# and tools for managing the execution and analysis of the calculations
|
9
|
+
|
10
|
+
# The name of this project
|
11
|
+
attr_accessor :name
|
12
|
+
|
13
|
+
# Load a Project from the serialized yaml file
|
14
|
+
def Project.load(filename)
|
15
|
+
yamlfile = [filename, filename+".yaml"].find{|f| File.exists?(f)}
|
16
|
+
|
17
|
+
File.open(yamlfile, 'r') do |f|
|
18
|
+
YAML.load(f)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Create a new Project with the given name
|
23
|
+
# This will also create the directory structure
|
24
|
+
# for the project. Returns true if successful, false otherwise
|
25
|
+
def Project.create(name)
|
26
|
+
|
27
|
+
return nil unless name
|
28
|
+
|
29
|
+
p = Project.new(name)
|
30
|
+
|
31
|
+
# Create the project directory
|
32
|
+
FileUtils.mkdir(p.relative_path)
|
33
|
+
p.save(p.relative_path)
|
34
|
+
#
|
35
|
+
# # Create the config directory
|
36
|
+
# FileUtils.mkdir(File.join(p.relative_path, "config"))
|
37
|
+
#
|
38
|
+
# # Create the geometry directory
|
39
|
+
# FileUtils.mkdir(File.join(p.relative_path, AimsProject::GEOMETRY_DIR))
|
40
|
+
#
|
41
|
+
# # Create the control directory
|
42
|
+
# FileUtils.mkdir(File.join(p.relative_path, AimsProject::CONTROL_DIR))
|
43
|
+
#
|
44
|
+
# # Create the calculations directory
|
45
|
+
# FileUtils.mkdir(File.join(p.relative_path, AimsProject::CALCULATION_DIR))
|
46
|
+
|
47
|
+
return p
|
48
|
+
end
|
49
|
+
|
50
|
+
# Initialize a new project object with the given name.
|
51
|
+
# This does not create the project directory structure, use create for that.
|
52
|
+
def initialize(name)
|
53
|
+
self.name = name
|
54
|
+
end
|
55
|
+
|
56
|
+
# Return the binding for this project
|
57
|
+
def get_binding
|
58
|
+
binding
|
59
|
+
end
|
60
|
+
|
61
|
+
# The path of this project relative to project_root_dir
|
62
|
+
def relative_path
|
63
|
+
name
|
64
|
+
end
|
65
|
+
|
66
|
+
# The filename of the serialized yaml file representing this project
|
67
|
+
def serialized_filename
|
68
|
+
"#{self.name}.yaml"
|
69
|
+
end
|
70
|
+
|
71
|
+
# The full path to this project locally
|
72
|
+
def full_path
|
73
|
+
File.dirname(File.expand_path(serialized_filename))
|
74
|
+
end
|
75
|
+
|
76
|
+
# Serialize this project to a yaml file named after this project
|
77
|
+
# in the given directory
|
78
|
+
def save(dir = ".")
|
79
|
+
File.open(File.join(dir, "#{name}.yaml"), 'w') do |f|
|
80
|
+
f.print YAML.dump(self)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Retreive the calcluations managed by this project.
|
85
|
+
# This array is loaded directly from serialized yaml
|
86
|
+
# files in each calculation directory
|
87
|
+
def calculations
|
88
|
+
calc_status_files = Dir.glob(File.join(AimsProject::CALCULATION_DIR, "*", AimsProject::CALC_STATUS_FILENAME))
|
89
|
+
# pick up sub-calculations
|
90
|
+
calc_status_files += Dir.glob(File.join(AimsProject::CALCULATION_DIR, "*", "*", AimsProject::CALC_STATUS_FILENAME))
|
91
|
+
calc_status_files.collect{|f|
|
92
|
+
Calculation.load(File.dirname(f))
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
# Retreive the geometries managed by this project.
|
97
|
+
def geometries
|
98
|
+
Dir.glob(File.join(AimsProject::GEOMETRY_DIR, "*"))
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module AimsProject
|
2
|
+
|
3
|
+
class ProjectTree < Wx::ScrolledWindow
|
4
|
+
|
5
|
+
include Wx
|
6
|
+
|
7
|
+
ROOT = "Root"
|
8
|
+
GEOMETRY = "Geometry"
|
9
|
+
CONTROL = "Control"
|
10
|
+
CALCULATIONS = "Calculations"
|
11
|
+
|
12
|
+
|
13
|
+
attr_accessor :app, :treeControl
|
14
|
+
|
15
|
+
def initialize(app, window)
|
16
|
+
super(window)
|
17
|
+
self.app = app
|
18
|
+
|
19
|
+
init_project_tree
|
20
|
+
|
21
|
+
sizer = BoxSizer.new(VERTICAL)
|
22
|
+
sizer.add(self.treeControl, 1, EXPAND | ALL, 5)
|
23
|
+
|
24
|
+
set_auto_layout(true)
|
25
|
+
set_sizer(sizer)
|
26
|
+
end
|
27
|
+
|
28
|
+
def init_project_tree
|
29
|
+
@treeControl = Wx::TreeCtrl.new(self)
|
30
|
+
@tree_map = {}
|
31
|
+
if self.app.project
|
32
|
+
@tree_map[ROOT] = self.treeControl.add_root(self.app.project.name)
|
33
|
+
@tree_map[GEOMETRY] = self.treeControl.append_item(@tree_map[ROOT], GEOMETRY)
|
34
|
+
@tree_map[CONTROL] = self.treeControl.append_item(@tree_map[ROOT], CONTROL)
|
35
|
+
@tree_map[CALCULATIONS] = self.treeControl.append_item(@tree_map[ROOT], CALCULATIONS)
|
36
|
+
|
37
|
+
self.app.project.geometries.each{|geom|
|
38
|
+
self.treeControl.append_item(@tree_map[GEOMETRY], geom)
|
39
|
+
}
|
40
|
+
|
41
|
+
self.app.project.calculations.each{|calc|
|
42
|
+
calcid = self.treeControl.append_item(@tree_map[CALCULATIONS], calc.name)
|
43
|
+
@tree_map[calcid] = calc
|
44
|
+
}
|
45
|
+
|
46
|
+
evt_tree_sel_changed(self.treeControl) {|evt|
|
47
|
+
itemid = evt.get_item
|
48
|
+
if @tree_map[GEOMETRY] == self.treeControl.get_item_parent(itemid)
|
49
|
+
self.app.open_file(self.treeControl.get_item_text(itemid))
|
50
|
+
elsif @tree_map[CALCULATIONS] == self.treeControl.get_item_parent(itemid)
|
51
|
+
calc = @tree_map[itemid]
|
52
|
+
self.app.show_calculation(calc)
|
53
|
+
end
|
54
|
+
}
|
55
|
+
else
|
56
|
+
@tree_map[ROOT] = self.treeControl.add_root(ROOT)
|
57
|
+
end
|
58
|
+
@treeControl.expand(@tree_map[ROOT])
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
Binary file
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module AimsProject
|
2
|
+
class ThreadCallbackEvent < Wx::CommandEvent
|
3
|
+
|
4
|
+
@@event_type_id = 0
|
5
|
+
|
6
|
+
def ThreadCallbackEvent.set_event_type(id)
|
7
|
+
@@event_type_id = id
|
8
|
+
end
|
9
|
+
|
10
|
+
def ThreadCallbackEvent.event_type
|
11
|
+
@@event_type_id
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
super(@@event_type_id)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
Binary file
|
data/lib/aims_project.rb
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
|
2
|
+
# AimsProject is an application for visualizing and managing projects for
|
3
|
+
# the FHI-AIMS package
|
4
|
+
|
5
|
+
require "rubygems"
|
6
|
+
require 'aims'
|
7
|
+
require 'yaml'
|
8
|
+
|
9
|
+
require 'aims_project/project.rb'
|
10
|
+
require 'aims_project/calculation.rb'
|
11
|
+
require 'aims_project/geometry_file.rb'
|
12
|
+
require 'aims_project/aims_project_exception.rb'
|
13
|
+
|
14
|
+
# AimsProject is a set of tools for managing and organizing
|
15
|
+
# calculations for executaion on high-performance computing clusters.
|
16
|
+
# It is designed specifically for the FHI-AIMS package published
|
17
|
+
# by the Fritz-Haber Institute (https://aimsclub.fhi-berlin.mpg.de),
|
18
|
+
# but can probably be generatlized to other codes.
|
19
|
+
#
|
20
|
+
# Author: Joshua Shapiro (email:joshua.shapiro@gmail.com)
|
21
|
+
# Copyright: 2012 Joshua Shapiro
|
22
|
+
# License: TBD
|
23
|
+
#
|
24
|
+
# = Why should I use AimsProject?
|
25
|
+
# Good organization is crucial to obtaining meaningful results with
|
26
|
+
# the FHI-AIMS package. Careful testing of convergence
|
27
|
+
# across parameters in the control and geometry files easily requires
|
28
|
+
# dozens of calculations. The novice and expert user alike can quickly
|
29
|
+
# lose track of which calculations are complete, which are still pending,
|
30
|
+
# which calculations were errors, and which calculations failed or were aborted.
|
31
|
+
# In this framework, even the most experienced user can and will make mistakes.
|
32
|
+
#
|
33
|
+
# <em>The aim of this tool is to simplify and streamline the calculation
|
34
|
+
# pipeline, so the user can focus on the results.</em>
|
35
|
+
#
|
36
|
+
# = Features
|
37
|
+
# * Automated generation of calculations from control & geometry files.
|
38
|
+
# * Automated synchronization between a workstation and the compute cluster.
|
39
|
+
# * Automated job submission of pending calculations to the queue
|
40
|
+
# * Status tracking of calculations from creation to completion.
|
41
|
+
# * Simple organizational structure with human readable metadata.
|
42
|
+
#
|
43
|
+
# = Planned Features
|
44
|
+
# * Input file validation (To catch mistakes before submitting to a queue)
|
45
|
+
# * Geometry input and output visualization
|
46
|
+
#
|
47
|
+
# = Quick Start
|
48
|
+
# == Installation
|
49
|
+
#
|
50
|
+
# gem install aims_project
|
51
|
+
#
|
52
|
+
# == Creating a Project
|
53
|
+
#
|
54
|
+
# Type:
|
55
|
+
# AimsProject myProject
|
56
|
+
#
|
57
|
+
# This will create the directory structure
|
58
|
+
# myProject
|
59
|
+
# -> calculations/
|
60
|
+
# -> config/
|
61
|
+
# -> control/
|
62
|
+
# -> geometry/
|
63
|
+
# -> Capfile
|
64
|
+
# -> myProject.yaml
|
65
|
+
#
|
66
|
+
#
|
67
|
+
# +calculations+:: Contains one subdirectory for each calculation.
|
68
|
+
# +config+:: Contains special configuration files for automation.
|
69
|
+
# +geometry+:: This is where you will place all your geometry files.
|
70
|
+
# +control+:: This is where you place all your control files.
|
71
|
+
# +Capfile+:: Location where you customize the interaction with the compute cluster
|
72
|
+
# +myProject.yaml+:: Human readable metadata related to this project.
|
73
|
+
#
|
74
|
+
# == Creating a calculation
|
75
|
+
#
|
76
|
+
# Assume you are investigating two atomic configurations _alpha_ and _beta_, and
|
77
|
+
# you want to calculate them with the _light_ and _tight_ settings.
|
78
|
+
#
|
79
|
+
# Create the FHI-AIMS formatted input files and name them
|
80
|
+
# * +geometry/alpha+
|
81
|
+
# * +geometry/beta+
|
82
|
+
# * +control/light+
|
83
|
+
# * +control/tight+
|
84
|
+
#
|
85
|
+
# Now run
|
86
|
+
# > AimsCalc create alpha light
|
87
|
+
# > AimsCalc create beta light
|
88
|
+
# > AimsCalc create alpha tight
|
89
|
+
# > AimsCalc create beta tight
|
90
|
+
#
|
91
|
+
# This will create four subdirectories inside +calculations/+.
|
92
|
+
# > ls calculations/*
|
93
|
+
# calculations/alpha.light:
|
94
|
+
# calc_status.yaml control.in geometry.in
|
95
|
+
#
|
96
|
+
# calculations/alpha.tight:
|
97
|
+
# calc_status.yaml control.in geometry.in
|
98
|
+
#
|
99
|
+
# calculations/beta.light:
|
100
|
+
# calc_status.yaml control.in geometry.in
|
101
|
+
#
|
102
|
+
# calculations/beta.tight:
|
103
|
+
# calc_status.yaml control.in geometry.in
|
104
|
+
#
|
105
|
+
# Notice that each calculation directory has the required +control.in+ and
|
106
|
+
# +geometry.in+ file. These were directly copied from the geometry and control files
|
107
|
+
# passed to AimsCalc. *Note* It is possible to embed variables inside the control
|
108
|
+
# and geometry files, see AimsCalc for more details. Each directory also contains
|
109
|
+
# a file named +calc_status.yaml+. This is a metadata file used for tracking
|
110
|
+
# the history and status of the calculation. Currently this file looks something like
|
111
|
+
# --- !ruby/object:AimsProject::Calculation
|
112
|
+
# control: light
|
113
|
+
# geometry: alpha
|
114
|
+
# status: STAGED
|
115
|
+
#
|
116
|
+
#
|
117
|
+
# Feel free to get more creative with the geometry and control file names. You should encode
|
118
|
+
# as much information in the file name as possible, as this will make it easy
|
119
|
+
# for you to identify the calculation later. For example, a geometry file
|
120
|
+
# named +alpha2x2_5layers_relaxed+ and a control file named +light_tier1_norelax_6x6x1_lomem+
|
121
|
+
# will result in a calulation directory named +alpha2x2_5layers_relaxed.light_tier1_norelax_6x6x1_lomem+
|
122
|
+
#
|
123
|
+
# == Running a Calculation
|
124
|
+
# At this point the calculation status is +STAGED+. To run the calculation, first customize
|
125
|
+
# the +Capfile+ with details necessary for running FHI-AIMS on the computing cluster.
|
126
|
+
# Check this file carefully, this is where you define the name and location of the aims executable,
|
127
|
+
# the name of the server, and the method for submitting jobs to the queue. Once this file
|
128
|
+
# is properly configured, the calculations are submitted with one line:
|
129
|
+
#
|
130
|
+
# cap aims:enqueue
|
131
|
+
#
|
132
|
+
# This command invokes custom tasks in +Capistrano+, a 3rd party tool for automated deployment,
|
133
|
+
# that will upload the calculations to the server and submit them to the queue.
|
134
|
+
#
|
135
|
+
|
136
|
+
|
137
|
+
|
138
|
+
module AimsProject
|
139
|
+
# Constants
|
140
|
+
STAGED = "STAGED"
|
141
|
+
HOLD = "HOLD"
|
142
|
+
QUEUED = "QUEUED"
|
143
|
+
RUNNING = "RUNNING"
|
144
|
+
COMPLETE = "COMPLETE"
|
145
|
+
ABORTED = "ABORTED"
|
146
|
+
CANCELED = "CANCELED"
|
147
|
+
|
148
|
+
CONFIG_DIR = "config"
|
149
|
+
CALCULATION_DIR = "calculations"
|
150
|
+
GEOMETRY_DIR = "geometry"
|
151
|
+
CONTROL_DIR = "control"
|
152
|
+
CALC_STATUS_FILENAME = "calc_status.yaml"
|
153
|
+
PROJECT_VARIABLES_FILENAME = "project_vars.rb"
|
154
|
+
|
155
|
+
GLOBAL_CONFIG_DIR = File.join(ENV["HOME"], ".aims_project")
|
156
|
+
GLOBAL_VARIABLES_FILENAME = "aims_project_vars.rb"
|
157
|
+
|
158
|
+
end
|
data/skeleton/Capfile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'aims_project'
|
2
|
+
|
3
|
+
load 'config/tasks.rb'
|
4
|
+
|
5
|
+
# The name of this project
|
6
|
+
set :project_name, '__PROJECT_NAME__'
|
7
|
+
|
8
|
+
# The following settings apply to calculations occuring on the remote host
|
9
|
+
|
10
|
+
# The base directory on the remote host
|
11
|
+
set :remote_project_dir, 'AimsProjects'
|
12
|
+
|
13
|
+
# Query the user for the number of parallel nodes when queueing calculations
|
14
|
+
set(:nodes) {Capistrano::CLI.ui.ask("How many compute nodes?: ")}
|
15
|
+
|
16
|
+
# The memory for each calculation
|
17
|
+
set :memory, 1024
|
18
|
+
|
19
|
+
# The time limit for each calculation
|
20
|
+
set :time, 24
|
21
|
+
|
22
|
+
# Set the qstat command
|
23
|
+
set :qstat_cmd, "qstat -u jns"
|
24
|
+
|
25
|
+
# The qsub command to execute
|
26
|
+
# There are some extra commands in here to forward SIGUSR1 and SIGUSR2 to aims_script
|
27
|
+
# so that the job status can be properly updated if the job is aborted
|
28
|
+
# aims_script is predefined to be 'aims.sh', if this is unsuitable
|
29
|
+
# it can be reset with: set :aims_script, 'foo'
|
30
|
+
set(:qsub) {
|
31
|
+
"parallel.q -n #{nodes} -d #{memory} -t #{time} -ns -k #{aims_script}; sed '/\\btime\\b/ i\\ trap : SIGUSR1 SIGUSR2' #{aims_script}.cmd > #{aims_script}.cmd.new; mv #{aims_script}.cmd.new #{aims_script}.cmd; qsub -notify #{aims_script}.cmd"
|
32
|
+
}
|
33
|
+
|
34
|
+
# The remote host for data transfer of large files
|
35
|
+
role :data_transfer, "Your Data Transfer Server Here"
|
36
|
+
# The remote host for job submission
|
37
|
+
role :queue_submission, "Your Compute Cluster Server Here"
|
@@ -0,0 +1,58 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
# Setup environment
|
4
|
+
export OMP_NUM_THREADS=1
|
5
|
+
export MKL_NUM_THREADS=1
|
6
|
+
export MKL_DYNAMIC=FALSE
|
7
|
+
|
8
|
+
export LD_LIBRARY_PATH=/u/local/compilers/intel/11.1/073/mkl/lib/em64t:/u/local/compilers/intel/11.1/073//lib/intel64:/u/local/compilers/intel/11.1/073/ipp/em64t/sharedlib:/u/local/compilers/intel/11.1/073/mkl/lib/em64t:/u/local/compilers/intel/11.1/073/tbb/intel64/cc4.1.0_libc2.4_kernel2.6.16.21/lib:/u/local/apps/scalapack/current/ib:/u/local/compilers/intel/11.0/current/mkl/lib/em64t/:/u/local/intel/11.1/openmpi/1.4.5/lib:/u/local/compilers/intel/11.1/080/mkl/lib/em64t:/u/local/compilers/intel/11.1/080/lib/intel64
|
9
|
+
|
10
|
+
STATUSFILE=calc_status.yaml
|
11
|
+
LOCKFILE=${STATUSFILE}.lock
|
12
|
+
MACHINEFILE=hfile
|
13
|
+
|
14
|
+
# Required by openmpi
|
15
|
+
source /u/local/Modules/default/init/modules.sh
|
16
|
+
module load intel/11.1
|
17
|
+
module load openmpi/1.4
|
18
|
+
|
19
|
+
|
20
|
+
# Set the jobid in the status file
|
21
|
+
function setJobID() {
|
22
|
+
if [ ! -f $LOCKFILE ]; then
|
23
|
+
touch $LOCKFILE;
|
24
|
+
echo "jobid: ${JOB_ID}" >> $STATUSFILE
|
25
|
+
rm $LOCKFILE;
|
26
|
+
fi
|
27
|
+
}
|
28
|
+
|
29
|
+
# Define a function for modifying the status of the calculation
|
30
|
+
function setStatus() {
|
31
|
+
if [ ! -f $LOCKFILE ]; then
|
32
|
+
touch $LOCKFILE;
|
33
|
+
status=$1;
|
34
|
+
date_str=`date +'%Y-%m-%d %T %:z'`
|
35
|
+
TMPFILE=calc_status.tmp;
|
36
|
+
sed "s/status: .*/status: ${status}/; s/updated_at: .*/updated_at: ${date_str}/" $STATUSFILE > $TMPFILE;
|
37
|
+
mv $TMPFILE $STATUSFILE;
|
38
|
+
rm $LOCKFILE;
|
39
|
+
fi
|
40
|
+
}
|
41
|
+
|
42
|
+
|
43
|
+
|
44
|
+
# setup traps for early program termination
|
45
|
+
# qsub with the -notify flag will send SIGUSR1(30) or SIGUSR2(31) before killing a job
|
46
|
+
trap 'setStatus "ABORTED"; exit;' SIGUSR1 SIGUSR2
|
47
|
+
|
48
|
+
# Set the status to running
|
49
|
+
setJobID
|
50
|
+
setStatus "RUNNING"
|
51
|
+
|
52
|
+
# Run aims
|
53
|
+
# $NSLOTS is set by the sun-grid-engine when qsub is invoked.
|
54
|
+
awk '{print $1 " slots=" $2}' $PE_HOSTFILE > $MACHINEFILE
|
55
|
+
mpiexec -n $NSLOTS -machinefile $MACHINEFILE $HOME/bin/aims.071711_6.scalapack.mpi.x
|
56
|
+
|
57
|
+
# Set the status to complete
|
58
|
+
setStatus "COMPLETE"
|