nera 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/History.txt +4 -0
  2. data/Manifest.txt +37 -0
  3. data/README.rdoc +47 -0
  4. data/Rakefile +27 -0
  5. data/bin/nera +29 -0
  6. data/bin/nera_addsim +30 -0
  7. data/lib/nera/nera_cui.rb +417 -0
  8. data/lib/nera/nera_database.rb +281 -0
  9. data/lib/nera/nera_db_folders.rb +226 -0
  10. data/lib/nera/nera_dialog.rb +205 -0
  11. data/lib/nera/nera_job_layer_controller.rb +237 -0
  12. data/lib/nera/nera_job_records.rb +111 -0
  13. data/lib/nera/nera_job_script.rb +202 -0
  14. data/lib/nera/nera_parameter_layer_controller.rb +157 -0
  15. data/lib/nera/nera_parameter_records.rb +186 -0
  16. data/lib/nera/nera_run_layer_controller.rb +192 -0
  17. data/lib/nera/nera_run_records.rb +184 -0
  18. data/lib/nera/nera_simulator.rb +26 -0
  19. data/lib/nera/nera_simulator_layer_controller.rb +66 -0
  20. data/lib/nera/nera_simulator_records.rb +84 -0
  21. data/lib/nera.rb +25 -0
  22. data/lib/nera_addsim/make_simulator.rb +307 -0
  23. data/scripts/make_manifest.rb +21 -0
  24. data/test/runner.rb +3 -0
  25. data/test/test_helper.rb +52 -0
  26. data/test/test_nera_database.rb +221 -0
  27. data/test/test_nera_db_folders.rb +209 -0
  28. data/test/test_nera_dialog.rb +134 -0
  29. data/test/test_nera_job_layer_controller.rb +132 -0
  30. data/test/test_nera_job_records.rb +260 -0
  31. data/test/test_nera_parameter_layer_controller.rb +188 -0
  32. data/test/test_nera_parameter_records.rb +285 -0
  33. data/test/test_nera_run_layer_controller.rb +171 -0
  34. data/test/test_nera_run_records.rb +290 -0
  35. data/test/test_nera_simulator.rb +26 -0
  36. data/test/test_nera_simulator_layer_controller.rb +54 -0
  37. data/test/test_nera_simulator_records.rb +140 -0
  38. metadata +125 -0
@@ -0,0 +1,186 @@
1
+ require 'nera_database'
2
+ require 'nera_simulator'
3
+
4
+ module NERA
5
+
6
+ # The parameter tables are accessed from this class
7
+ class ParameterRecords
8
+
9
+ # keys of the parameter table
10
+ ATTRIBUTES_PART = [ [:id, Integer],
11
+ [:created_at, DateTime],
12
+ [:updated_at, DateTime],
13
+ [:in_trashbox?, Symbol] ] # :active or :trashbox
14
+
15
+ # NERA::Database object
16
+ @db
17
+
18
+ # attributes (keys)
19
+ @keys
20
+ attr_reader :keys
21
+
22
+ # simulator_specific keys
23
+ @sim_class
24
+
25
+ # argument is the path to database file
26
+ def initialize( db_file, sim_class)
27
+ unless sim_class.superclass == NERA::Simulator
28
+ raise ArgumentError, "Given simulator class is not a subclass of NERA::Simulator : #{sim_class}"
29
+ end
30
+ @sim_class = sim_class
31
+ @keys = ATTRIBUTES_PART.map do |k|
32
+ k[0]
33
+ end
34
+ sim_keys = @sim_class::Parameters.map do |x|
35
+ x[0]
36
+ end
37
+ found = @keys.find do |a|
38
+ sim_keys.include?(a)
39
+ end
40
+ if found
41
+ raise ArgumentError, "Invalid class. This class has a parameter : #{found}"
42
+ end
43
+ @keys += sim_keys
44
+ @db = NERA::Database.new( db_file)
45
+ end
46
+
47
+ # if file already exists, return false
48
+ def self.create_table( db_file, sim_class)
49
+ unless sim_class.superclass == NERA::Simulator
50
+ raise ArgumentError, "Given simulator class is not a subclass of NERA::Simulator : #{sim_class}"
51
+ end
52
+ ks = ATTRIBUTES_PART.map do |k|
53
+ k[0]
54
+ end
55
+ params = sim_class::Parameters.map do |x|
56
+ x[0]
57
+ end
58
+ found = ks.find do |a|
59
+ params.include?(a)
60
+ end
61
+ if found
62
+ raise ArgumentError, "Invalid class. This class has a parameter : #{found}"
63
+ end
64
+ NERA::Database.create_table( db_file)
65
+ end
66
+
67
+ #
68
+ def add( param_hash)
69
+ # check the argument
70
+ unless param_hash.is_a?(Hash)
71
+ raise ArgumentError, "Argument must be a Hash."
72
+ end
73
+
74
+ # check the argument
75
+ sim_keys = @sim_class::Parameters.map do |x|
76
+ x[0]
77
+ end
78
+ param_hash.each_pair do |param,value|
79
+ unless sim_keys.include?(param)
80
+ raise ArgumentError, "The given parameter set has unknown key."
81
+ end
82
+ a = @sim_class::Parameters.find do |p|
83
+ p[0] == param
84
+ end
85
+ unless value.is_a?( a[1] )
86
+ raise ArgumentError, "The given parameter set has invalid type."
87
+ end
88
+ end
89
+
90
+ # check duplication
91
+ found = @db.find_all do |rec|
92
+ f = true
93
+ @sim_class::Parameters.each do |p|
94
+ # set default values
95
+ rec[ p[0] ] = p[2] if rec[ p[0] ] == nil
96
+ param_hash[ p[0] ] = p[2] if param_hash[ p[0]] == nil
97
+ unless rec[p[0]] == param_hash[p[0]]
98
+ f = false
99
+ break
100
+ end
101
+ end
102
+ f
103
+ end
104
+ if found # the given parameter set already exists.
105
+ return nil
106
+ end
107
+
108
+ # add a parameter
109
+ d = DateTime.now
110
+ h = {:created_at => d, :updated_at => d, :in_trashbox? => :active}
111
+ ps = param_hash.dup
112
+ @sim_class::Parameters.each do |p|
113
+ ps[ p[0]] = p[3] if ps[p[0]] == nil
114
+ end
115
+ h.merge!( ps)
116
+ @db.add(h)
117
+ end
118
+
119
+ def list_all
120
+ list = @db.find_all do |r|
121
+ true
122
+ end
123
+ return list.to_a
124
+ end
125
+
126
+ def list_active
127
+ list = @db.find_all do |r|
128
+ r[:in_trashbox?] == :active
129
+ end
130
+ return list.to_a
131
+ end
132
+
133
+ def list_trashbox
134
+ list = @db.find_all do |r|
135
+ r[:in_trashbox?] == :trashbox
136
+ end
137
+ return list.to_a
138
+ end
139
+
140
+ def find_by_id( id)
141
+ @db.find_by_id( id)
142
+ end
143
+
144
+ def update_to_state_active( id)
145
+ raise ArgumentError unless id.is_a?(Integer)
146
+ found = @db.find_by_id( id)
147
+ return nil unless found
148
+ return nil if found[:in_trashbox?] == :active
149
+
150
+ found[:in_trashbox?] = :active
151
+ @db.update( found)
152
+ end
153
+
154
+ def update_to_state_trashbox( id)
155
+ raise ArgumentError unless id.is_a?(Integer)
156
+ found = @db.find_by_id( id)
157
+ return nil unless found
158
+ return nil if found[:in_trashbox?] == :trashbox
159
+
160
+ found[:in_trashbox?] = :trashbox
161
+ @db.update( found)
162
+ end
163
+
164
+ def touch( id)
165
+ raise ArgumentError unless id.is_a?(Integer)
166
+ found = @db.find_by_id( id)
167
+ return nil unless found
168
+ found[:updated_at] = DateTime.now
169
+ @db.update( found)
170
+ end
171
+
172
+ def destroy( id)
173
+ raise ArgumentError unless id.is_a?(Integer)
174
+ found = @db.find_by_id( id)
175
+ return nil if found == nil or found[:in_trashbox?] == :active
176
+ @db.destroy(id)
177
+ end
178
+
179
+ def transaction
180
+ @db.transaction{
181
+ yield
182
+ }
183
+ end
184
+
185
+ end
186
+ end
@@ -0,0 +1,192 @@
1
+ require 'date'
2
+ require 'nera_db_folders'
3
+ require 'nera_simulator_records'
4
+ require 'nera_parameter_records'
5
+ require 'nera_run_records'
6
+ require 'nera_job_records'
7
+ require 'nera_job_script'
8
+ require 'fileutils'
9
+
10
+ module NERA
11
+
12
+ class RunLayerController
13
+
14
+ # instance of NERA::DbFolders
15
+ @db_folders
16
+ # instance of NERA::Simulator class
17
+ @sim_instance
18
+ # id of the parameter set
19
+ @param_id
20
+ # instance of NERA::RunRecords
21
+ @run_records
22
+
23
+ def initialize( path_db_folder, sim_class, param_id)
24
+ raise ArgumentError unless path_db_folder.is_a?(String)
25
+ unless sim_class.is_a?(Class) and sim_class.superclass == NERA::Simulator and param_id.is_a?(Integer)
26
+ raise ArgumentError, "#{sim_class}, #{param_id} are not appropriate instances"
27
+ end
28
+
29
+ @db_folders = NERA::DbFolders.new( path_db_folder)
30
+ @sim_instance = sim_class.new
31
+ @param_id = param_id
32
+ param_records = NERA::ParameterRecords.new( @db_folders.path_to_parameters_table( sim_class), sim_class)
33
+ found = param_records.find_by_id( @param_id)
34
+ @sim_instance.class::Parameters.each do |pa|
35
+ key = pa[0]
36
+ if found[key]
37
+ @sim_instance.param[key] = found[key]
38
+ else
39
+ @sim_instance.param[key] = pa[2]
40
+ end
41
+ end
42
+
43
+ @run_records = NERA::RunRecords.new( @db_folders.path_to_runs_table( sim_class, @param_id) )
44
+ end
45
+
46
+ def path_to_run_layer
47
+ return @db_folders.path_to_run_layer( @sim_instance.class, @param_id)
48
+ end
49
+
50
+ def all_runs_list_in_csv
51
+ list = @run_records.list_all
52
+ header = @run_records.keys.dup
53
+ csv_list = []
54
+ list.each do |r_hash|
55
+ strings = []
56
+ header.each do |key|
57
+ if r_hash[key].is_a?(DateTime)
58
+ strings << r_hash[key].to_s.split('T')[0]
59
+ else
60
+ strings << r_hash[key].to_s
61
+ end
62
+ end
63
+ csv_list << strings.join(", ")
64
+ end
65
+ return header.join(", "), csv_list
66
+ end
67
+
68
+ def create_jobs( num_jobs, num_runs_per_job = 1)
69
+ unless num_jobs.is_a?(Integer) and num_jobs >= 0
70
+ raise ArgumentError, "num_jobs must be equal to or larger than zero."
71
+ end
72
+
73
+ job_ids = []
74
+ job_recs = NERA::JobRecords.new( @db_folders.path_to_jobs_table)
75
+ js = NERA::JobScript.new( @db_folders)
76
+ job_recs.transaction {
77
+ @run_records.transaction {
78
+ num_jobs.times do |j_idx|
79
+ run_stat = @run_records.add( num_runs_per_job)
80
+ rid = run_stat[0][:id]
81
+ jid = job_recs.add( @sim_instance.class, @param_id, rid, num_runs_per_job)
82
+ @run_records.set_job_id( rid, num_runs_per_job, jid)
83
+ sim_records = NERA::SimulatorRecords.new( @db_folders.path_to_simulators_table)
84
+ found = sim_records.list.find do |rec| rec[:name] == @sim_instance.class.to_s end
85
+ path_script = js.create_script( jid, found[:id], @param_id, @sim_instance, run_stat)
86
+ job_ids << jid
87
+ end
88
+ }
89
+ }
90
+ dump_in_yaml( job_recs)
91
+ return job_ids
92
+ end
93
+
94
+ def not_finished_jobs_list_in_csv
95
+ list = @run_records.list_all_not_finished
96
+ header = "job_id, created_at, run_id, num_runs, omp_threads, mpi_processes"
97
+
98
+ jobids = list.map do |rec|
99
+ rec[:job_id]
100
+ end
101
+ jobids = jobids.sort.uniq
102
+
103
+ csv_list = []
104
+ jobids.each do |jid|
105
+ strings = [ jid]
106
+ found = list.find_all do |r| r[:job_id] == jid end
107
+ found_first = found.first
108
+ strings << found_first[:created_at].to_s.split('T')[0]
109
+ strings << found_first[:id]
110
+ strings << found.size
111
+ strings << found_first[:omp_threads]
112
+ strings << found_first[:mpi_processes]
113
+ csv_list << strings.join(", ")
114
+ end
115
+ return header, csv_list
116
+ end
117
+
118
+ def cancel_jobs( job_ids)
119
+ unless job_ids.is_a?(Array)
120
+ raise ArgumentError, "job_ids must be an Array."
121
+ end
122
+ job_ids.each do |x|
123
+ raise ArgumentError, "each element of job_ids must be an Integer" unless x.is_a?(Integer)
124
+ end
125
+
126
+ job_recs = NERA::JobRecords.new( @db_folders.path_to_jobs_table)
127
+ job_recs.transaction {
128
+ @run_records.transaction {
129
+ job_ids.each do |jid|
130
+ d = job_recs.destroy(jid)
131
+ next unless d
132
+ a = @run_records.destroy_job_id( jid)
133
+ raise "must not happen" unless a
134
+ FileUtils.rm( @db_folders.path_to_job_script(jid) )
135
+ end
136
+ }
137
+ }
138
+ dump_in_yaml( job_recs)
139
+ return true
140
+ end
141
+
142
+ def analysis_methods
143
+ found = @sim_instance.public_methods.find_all do |meth|
144
+ meth =~ /^analyze_/
145
+ end
146
+ return found
147
+ end
148
+
149
+ def analyze( method_str)
150
+ unless method_str.is_a?(String) or method_str.is_a?(Symbol)
151
+ raise ArgumentError, "method_str must be a String or Symbol"
152
+ end
153
+
154
+ return nil unless method_str.to_s =~ /^analyze_/
155
+
156
+ found = @sim_instance.public_methods.find do |m|
157
+ m == method_str.to_s
158
+ end
159
+ return nil unless found
160
+
161
+ FileUtils.cd( @db_folders.path_to_run_layer(@sim_instance.class, @param_id) ) {
162
+ @sim_instance.__send__( method_str)
163
+ }
164
+ end
165
+
166
+ def analyze_all
167
+ found = @sim_instance.public_methods.find_all do |m|
168
+ m =~ /^analyze_/
169
+ end
170
+ return nil unless found
171
+
172
+ FileUtils.cd( @db_folders.path_to_run_layer(@sim_instance.class, @param_id) ) {
173
+ found.each do |meth|
174
+ @sim_instance.__send__( meth)
175
+ end
176
+ }
177
+ end
178
+
179
+ private
180
+ def dump_in_yaml( job_recs)
181
+ File.open( @db_folders.path_to_runs_yaml( @sim_instance.class, @param_id), 'w') do |io|
182
+ YAML.dump( @run_records.list_all, io)
183
+ io.flush
184
+ end
185
+ File.open( @db_folders.path_to_jobs_yaml, 'w') do |io|
186
+ YAML.dump( job_recs.list_all, io)
187
+ io.flush
188
+ end
189
+ end
190
+
191
+ end
192
+ end
@@ -0,0 +1,184 @@
1
+ require 'date'
2
+ require 'nera_database'
3
+
4
+ module NERA
5
+ class RunRecords
6
+
7
+ # keys of run records
8
+ ATTRIBUTES = [ [:id, Integer], [:state, Symbol], [:created_at, DateTime],
9
+ [:job_id, Integer], [:seed, Integer],
10
+ [:start_at, DateTime], [:finish_at, DateTime],
11
+ [:included_at, DateTime], [:real_time, Float],
12
+ [:user_time, Float], [:host_name, String],
13
+ [:omp_threads, Integer], [:mpi_processes, Integer]
14
+ ]
15
+
16
+ # Maximum of the seed
17
+ SEED_MAX = 2**31
18
+
19
+ # NERA::Database object (table object)
20
+ @db
21
+
22
+ # record
23
+ @keys
24
+ attr_accessor :keys
25
+
26
+ # run record
27
+ def initialize( table_file)
28
+ @keys = ATTRIBUTES.map do |k| k[0] end
29
+ @db = NERA::Database.new( table_file)
30
+ end
31
+
32
+ def self.create_table(table_file)
33
+ NERA::Database.create_table( table_file)
34
+ end
35
+
36
+ public
37
+ def add( num_runs, omp_threads = 1, mpi_procs = 1)
38
+ # check arguments
39
+ unless num_runs.is_a?(Integer) and num_runs > 0
40
+ raise ArgumentError, "Number of runs must be a postive integer."
41
+ end
42
+ unless (omp_threads.is_a?(Integer) and omp_threads > 0) or omp_threads == nil
43
+ raise ArgumentError, "omp_threads must be a postive integer or nil"
44
+ end
45
+ unless (mpi_procs.is_a?(Integer) and mpi_procs > 0) or mpi_procs == nil
46
+ raise ArgumentError, "mpi_procs must be a postive integer or nil"
47
+ end
48
+
49
+ stats = []
50
+ @db.transaction {
51
+ seeds = find_unique_seed( num_runs)
52
+ d = DateTime.now
53
+ num_runs.times do |i|
54
+ h = {:state => :not_finished, :created_at => d, :job_id => nil, :seed => seeds[i], :omp_threads => omp_threads, :mpi_processes => mpi_procs}
55
+ new_id = @db.add( h)
56
+ found = @db.find_by_id( new_id)
57
+ stat = Hash.new
58
+ stat[:id] = found[:id]
59
+ stat[:seed] = found[:seed]
60
+ stat[:omp_threads] = found[:omp_threads]
61
+ stat[:mpi_processes] = found[:mpi_processes]
62
+ stats << stat
63
+ end
64
+ }
65
+ return stats
66
+ end
67
+
68
+ private
69
+ def find_unique_seed( num_runs)
70
+ seeds = []
71
+ until seeds.size == num_runs
72
+ sed = rand( SEED_MAX)
73
+ found = @db.find_all do |rec|
74
+ rec[:seed] == sed
75
+ end
76
+ seeds << sed unless found
77
+ end
78
+ return seeds
79
+ end
80
+
81
+ public
82
+ def set_job_id( id, num_runs, j_id)
83
+ # check arguments
84
+ unless id.is_a?(Integer) and num_runs.is_a?(Integer) and j_id.is_a?(Integer)
85
+ raise ArgumentError, "Arguments must be Integers."
86
+ end
87
+
88
+ @db.transaction {
89
+ for i in id...(id+num_runs) do
90
+ r = @db.find_by_id(i)
91
+ raise RuntimeError, "The record #{id} is not found." unless r
92
+ raise RuntimeError, "The record #{id} already has job_id." if r[:job_id]
93
+ r[:job_id] = j_id
94
+ @db.update(r)
95
+ end
96
+ flag = true
97
+ }
98
+ return j_id
99
+ end
100
+
101
+ def list_all
102
+ matched = @db.find_all do |rec|
103
+ true
104
+ end
105
+ return matched.to_a
106
+ end
107
+
108
+ def list_all_finished
109
+ matched = @db.find_all do |rec|
110
+ rec[:state] == :finished
111
+ end
112
+ return matched.to_a
113
+ end
114
+
115
+ def list_all_not_finished
116
+ matched = @db.find_all do |rec|
117
+ rec[:state] == :not_finished
118
+ end
119
+ return matched.to_a
120
+ end
121
+
122
+ def update_state_to_finished( id, start_at, finish_at, included_at, real_time, user_time, host_name)
123
+ unless id.is_a?(Integer) and start_at.is_a?(DateTime) and finish_at.is_a?(DateTime)
124
+ p id, start_at, finish_at
125
+ raise ArgumentError
126
+ end
127
+ unless included_at.is_a?(DateTime) and real_time.is_a?(Float) and user_time.is_a?(Float) and host_name.is_a?(String)
128
+ p included_at, real_time, user_time, host_name
129
+ raise ArgumentError
130
+ end
131
+
132
+ flag = nil
133
+ @db.transaction {
134
+ found = @db.find_by_id( id)
135
+ return nil unless found
136
+ return nil if found[:state] == :finished
137
+ found[:start_at] = start_at
138
+ found[:finish_at] = finish_at
139
+ found[:included_at] = included_at
140
+ found[:real_time] = real_time
141
+ found[:user_time] = user_time
142
+ found[:host_name] = host_name
143
+ found[:state] = :finished
144
+ flag = @db.update( found)
145
+ }
146
+ return flag
147
+ end
148
+
149
+ def destroy( id)
150
+ raise ArgumentError unless id.is_a?(Integer)
151
+
152
+ flag = nil
153
+ @db.transaction {
154
+ found = @db.find_by_id(id)
155
+ return nil unless found
156
+ return nil if found[:state] == :finished
157
+ flag = @db.destroy(id)
158
+ }
159
+ return flag
160
+ end
161
+
162
+ def destroy_job_id( job_id)
163
+ raise ArgumentError unless job_id.is_a?(Integer)
164
+
165
+ @db.transaction {
166
+ found = list_all_not_finished.find_all do |rec|
167
+ rec[:job_id] == job_id
168
+ end
169
+ return nil if found.size == 0
170
+ found.each do |rec|
171
+ f = @db.destroy( rec[:id])
172
+ raise "must not happen" unless f
173
+ end
174
+ }
175
+ return true
176
+ end
177
+
178
+ def transaction
179
+ @db.transaction{
180
+ yield
181
+ }
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,26 @@
1
+ module NERA
2
+
3
+ class Simulator
4
+
5
+ # instance variables
6
+ @param
7
+ attr_accessor :param
8
+
9
+ def initialize
10
+ @param = {}
11
+ end
12
+
13
+ # class methods, variables
14
+ @@inherited = []
15
+
16
+ def self.inherited(subclass)
17
+ @@inherited << subclass
18
+ end
19
+
20
+ public
21
+ def self.inherited_simulators
22
+ return @@inherited
23
+ end
24
+ end
25
+
26
+ end
@@ -0,0 +1,66 @@
1
+ require 'nera_db_folders'
2
+ require 'nera_simulator_records'
3
+ require 'nera_parameter_records'
4
+
5
+ module NERA
6
+
7
+ class SimulatorLayerController
8
+
9
+ # instance of NERA::DbFolders
10
+ @db_folders
11
+ # instance of NERA::SimulatorRecords
12
+ @sim_records
13
+
14
+ def initialize( path_db_folds)
15
+ raise ArgumentError unless path_db_folds.is_a?(String)
16
+
17
+ @db_folders = NERA::DbFolders.new( path_db_folds)
18
+ table_path = @db_folders.path_to_simulators_table
19
+ @sim_records = NERA::SimulatorRecords.new(table_path)
20
+
21
+ NERA::Simulator.inherited_simulators.each do |sim|
22
+ next if list.find do |rec| rec[:name] == sim.to_s end
23
+ found = sim::Parameters.find do |p|
24
+ p[0] == :in_trashbox? or p[0] == :created_at or p[0] == :updated_at
25
+ end
26
+ next if found
27
+ register_a_simulator( sim)
28
+ end
29
+ end
30
+
31
+ def list
32
+ @sim_records.list
33
+ end
34
+
35
+ def path_to_sim_layer
36
+ @db_folders.path_to_simulator_layer
37
+ end
38
+
39
+ private
40
+ def register_a_simulator( sim_klass)
41
+ a = nil
42
+ @sim_records.transaction {
43
+ a = @sim_records.add( sim_klass)
44
+ return nil unless a
45
+ b = @db_folders.create_new_simulator_folder( sim_klass)
46
+ raise "The folder #{sim_klass.to_s}/ already exists." unless b
47
+ path = @db_folders.path_to_parameters_table( sim_klass)
48
+ c = NERA::ParameterRecords.create_table( path, sim_klass)
49
+ raise "The file #{path} already exists." unless c
50
+ }
51
+ dump_in_yaml
52
+ return a
53
+ end
54
+
55
+ private
56
+ def dump_in_yaml
57
+ ymlpath = @db_folders.path_to_simulators_yaml
58
+ File.open( ymlpath, 'w') do |io|
59
+ YAML::dump( @sim_records.list, io)
60
+ io.flush
61
+ end
62
+ end
63
+
64
+ end
65
+
66
+ end