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