nera 0.0.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.
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