coderunner 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/Gemfile +13 -0
- data/LICENSE.txt +20 -0
- data/README.md +4 -0
- data/README.rdoc +19 -0
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/lib/coderunner.rb +419 -0
- data/lib/coderunner/class_methods.rb +727 -0
- data/lib/coderunner/fortran_namelist.rb +1281 -0
- data/lib/coderunner/graphs_and_films.rb +744 -0
- data/lib/coderunner/heuristic_run_methods.rb +202 -0
- data/lib/coderunner/instance_methods.rb +1741 -0
- data/lib/coderunner/interactive_methods.rb +776 -0
- data/lib/coderunner/long_regexen.rb +201 -0
- data/lib/coderunner/merged_code_runner.rb +40 -0
- data/lib/coderunner/run.rb +1000 -0
- data/test/helper.rb +18 -0
- data/test/test_coderunner.rb +7 -0
- metadata +139 -0
@@ -0,0 +1,201 @@
|
|
1
|
+
# $script_folder = File.dirname(File.expand_path(__FILE__))
|
2
|
+
require File.dirname(File.expand_path(__FILE__)) + "/box_of_tricks.rb"
|
3
|
+
|
4
|
+
module LongRegexen
|
5
|
+
|
6
|
+
FLOAT = /(?<float>\-?(?:(?>\d+\.\d*)|(?>\d*\.\d+))(?:[eE][+-]?\d+)?)/
|
7
|
+
|
8
|
+
FLOAT_WSP = /(?<float>\-?\s*(?:(?>\d+\.\d*)|(?>\d*\.\d+))(?:[eE][+-]?\d+)?)/ # allows a space between the minus sign and the number
|
9
|
+
|
10
|
+
|
11
|
+
INT = INTEGER = /(?<int>\-?\d++)/
|
12
|
+
|
13
|
+
NUMBER = Regexp.new("(?<number>#{FLOAT.verbatim}|#{INTEGER.verbatim})")
|
14
|
+
|
15
|
+
STRING = /(?:"(?<string>(?:[^"]|\\")*)")|(?:'(?<string>(?:[^']|\\")*)')/
|
16
|
+
|
17
|
+
EQUALITY = /\b #a word boundary
|
18
|
+
|
19
|
+
(?<name>[A-Za-z_]\w*) # the name, which must be a single word (not beginning
|
20
|
+
# with a digit) followed by
|
21
|
+
|
22
|
+
\s*=\s* # an equals sign (possibly with whitespace either side), then
|
23
|
+
|
24
|
+
(?<default>(?> # the default answer, which can be either:
|
25
|
+
|
26
|
+
(?<float>\-?(?:(?>\d+\.\d*)|(?>\d*\.\d+))(?:[eE][+-]?\d+)?) # a floating point number
|
27
|
+
|
28
|
+
| #or
|
29
|
+
|
30
|
+
(?<int>\-?\d++) # an integer
|
31
|
+
|
32
|
+
| #or
|
33
|
+
|
34
|
+
(?:(?<delim>["'])(?<string>.*?)\k<delim>) # a string in quotes: '' or ""
|
35
|
+
|
36
|
+
| # or
|
37
|
+
|
38
|
+
(?:(?<word>\S+)(?=\s|\)|\]|[\n\r]+)) # a single word containing no spaces
|
39
|
+
# which must be followed by a space or ) or ] or a new line
|
40
|
+
|
41
|
+
))/x
|
42
|
+
|
43
|
+
KEYVALUE = /<key> # <key> then
|
44
|
+
|
45
|
+
(?<name>[A-Za-z_]\w*) # the name, which must be a single word (not beginning
|
46
|
+
# with a digit) followed by
|
47
|
+
|
48
|
+
<\/key>\s* # <\/key> (possibly with following whitespace), then
|
49
|
+
|
50
|
+
<value> # <value>, then
|
51
|
+
|
52
|
+
(?<default> # the default answer, which can be either:
|
53
|
+
|
54
|
+
|
55
|
+
(?<float>\-?(?:(?>\d+\.\d*)|(?>\d*\.\d+))(?:[eE]\-?\d+)?) # a floating point number
|
56
|
+
|
57
|
+
| #or
|
58
|
+
|
59
|
+
(?<int>\-?\d+) # an integer
|
60
|
+
|
61
|
+
| #or
|
62
|
+
|
63
|
+
(?<string>[^<]*) # a string (containing no '<'s)
|
64
|
+
|
65
|
+
)
|
66
|
+
|
67
|
+
<value> # <\/value>
|
68
|
+
|
69
|
+
/x
|
70
|
+
|
71
|
+
TAG = /< # <
|
72
|
+
|
73
|
+
[^\/\S>]+ # some word (containing no '\/'s or '>'s)
|
74
|
+
|
75
|
+
\s+ #some whitespace
|
76
|
+
|
77
|
+
name=
|
78
|
+
|
79
|
+
(?<delim>["']) # a double or single quote
|
80
|
+
|
81
|
+
(?<name>[A-Za-z_]\w*) # the name, which must be a single word (not beginning
|
82
|
+
# with a digit) followed by
|
83
|
+
|
84
|
+
\k<delim> #a closing quote
|
85
|
+
|
86
|
+
\s+ #some whitespace
|
87
|
+
|
88
|
+
(?<delimnext>["']) # a double or single quote
|
89
|
+
|
90
|
+
(?<default> # the default answer, which can be either:
|
91
|
+
|
92
|
+
|
93
|
+
(?<float>\-?(?:(?>\d+\.\d*)|(?>\d*\.\d+))(?:[eE]\-?\d+)?) # a floating point number
|
94
|
+
|
95
|
+
| #or
|
96
|
+
|
97
|
+
(?<int>\-?\d+) # an integer
|
98
|
+
|
99
|
+
| #or
|
100
|
+
|
101
|
+
(?<string>[^"']*) # a string (containing no quotation marks)
|
102
|
+
|
103
|
+
)
|
104
|
+
|
105
|
+
\k<delimnext> #a closing quote
|
106
|
+
|
107
|
+
\s* #some whitespace
|
108
|
+
|
109
|
+
> # >
|
110
|
+
|
111
|
+
/x
|
112
|
+
|
113
|
+
#<comp> : only complete sets of delimiters [], "", '', \/\/ or complete method calls: meth(params) or no delimiters at all: ensures that any following expressions are not inside open square brackets, quotation marks or regexen, or inside method parameter lists
|
114
|
+
NAMRSQBQS = NOT_IN_A_METHOD_OR_IN_A_REGEXP_OR_IN_SQUARE_BRACKETS_OR_IN_QUOTES_OR_A_SYMBOL =
|
115
|
+
|
116
|
+
/(?<comp>
|
117
|
+
(?: #a set of three options
|
118
|
+
(?: #option 1: complete sets of delimiters
|
119
|
+
(?<nest> #sub-expression denoting nested delimiters
|
120
|
+
(?: # A: meth(stuff)
|
121
|
+
(?:[A-Za-z_]\d*?) #method name ending in numbers or a letter
|
122
|
+
\( #begin brackets
|
123
|
+
(?:
|
124
|
+
[^()\[\]"\/'] #no delimiters
|
125
|
+
|
|
126
|
+
\g<nest> #complete nested sub-expression
|
127
|
+
)*
|
128
|
+
\)
|
129
|
+
)
|
130
|
+
|
|
131
|
+
(?: # B: [stuff]
|
132
|
+
\[ #begin square brackets
|
133
|
+
(?:
|
134
|
+
[^()\[\]"\/'] #no delimiters
|
135
|
+
|
|
136
|
+
\g<nest> #complete nested sub-expression
|
137
|
+
)*
|
138
|
+
\]
|
139
|
+
)
|
140
|
+
|
|
141
|
+
(?: # C: \/stuff\/ or "stuff" or 'stuff'
|
142
|
+
(?<delim>[\/"']) #begin symmetrical delimiters
|
143
|
+
(?:
|
144
|
+
[^()\[\]"\/'] #no delimiters
|
145
|
+
|
|
146
|
+
\g<nest> #complete nested sub-expression
|
147
|
+
)*
|
148
|
+
\k<delim> #end symmetrical delimiters
|
149
|
+
)
|
150
|
+
) # end sub-expression denoting correctly nested delimiters
|
151
|
+
)
|
152
|
+
|
|
153
|
+
[^()\[\]"\/'] #option 2: not delimiters
|
154
|
+
|
|
155
|
+
(?: # option 3: open brackets with no methods call before them
|
156
|
+
(?:
|
157
|
+
\W\d* # possibly some kind of number but not a name
|
158
|
+
|
|
159
|
+
^
|
160
|
+
)
|
161
|
+
\(
|
162
|
+
)
|
163
|
+
)*? #as many of the above options as you like
|
164
|
+
)
|
165
|
+
(?: #Here we specify the immediately preceding letter (it cannot be a dot or a colon)
|
166
|
+
[^.:()\[\]"\/'] #Not a dot, a colon or a delimiter
|
167
|
+
|
|
168
|
+
(?: # not a method call
|
169
|
+
\W\d*
|
170
|
+
|
|
171
|
+
^
|
172
|
+
)
|
173
|
+
\( #followed by an open bracket
|
174
|
+
|
|
175
|
+
^ #we can match the beginning of the string
|
176
|
+
)/x #THE END!
|
177
|
+
|
178
|
+
|
179
|
+
=begin
|
180
|
+
Ensures that any following expression is not inside a method(...) pair of brackets, by demanding that any preceding opening bracket '\\(', is
|
181
|
+
either
|
182
|
+
-followed by a closing bracket '\\)' to form a complete set of correctly nested brackets (the <par> subexpression):
|
183
|
+
(?<begin>(?:(?:(?<par>\\((?:[^()]|\\g<par>)*\\))
|
184
|
+
or
|
185
|
+
-not preceded by a word character (A-Za-z_) (all method brackets are assumed to have a word character before them; other brackets (e.g. in a logical expression) are assumed not to):
|
186
|
+
(?:(?:[^A-Za-z_]|^)\\())*?)
|
187
|
+
=end
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
|
192
|
+
if $0 == __FILE__
|
193
|
+
puts " j(KWE) [0]" =~ Regexp.new("(?<b>#{LongRegexen::NAMRSQBQS})")
|
194
|
+
par = "q"
|
195
|
+
regex1 = Regexp.new("(?<b>#{LongRegexen::NAMRSQBQS.verbatim})\\#?(?<variable>#{Regexp.escape(par)})(?<end>[^A-Za-z_])", Regexp::EXTENDED)
|
196
|
+
puts " (q) " =~ regex1
|
197
|
+
puts $~.inspect
|
198
|
+
puts "t= 0.2000000000E+03 aky= 1.40 akx= -0.02 om= " =~ LongRegexen::FLOAT
|
199
|
+
puts "asd"
|
200
|
+
puts " -9.81730399614593e+38; // phi(2,33,43,2)" =~ Regexp.new("^\\s*?#{LongRegexen::FLOAT.verbatim}.*?\\sphi\\(\\d,(?<theta>\\d+),(?<kx>\\d+)")
|
201
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class CodeRunner
|
2
|
+
|
3
|
+
def merge(*others)
|
4
|
+
Merged.new(*others.unshift(self))
|
5
|
+
end
|
6
|
+
|
7
|
+
def merge_method(meth, *args)
|
8
|
+
send(meth, *args)
|
9
|
+
end
|
10
|
+
|
11
|
+
class Merged < CodeRunner
|
12
|
+
def each
|
13
|
+
@runners.each{|r| yield(r)}
|
14
|
+
end
|
15
|
+
include Enumerable
|
16
|
+
def initialize(*runners)
|
17
|
+
@runners = []
|
18
|
+
r = runners[0]
|
19
|
+
r.instance_variables.each do |v|
|
20
|
+
instance_variable_set(v, r.instance_variable_get(v))
|
21
|
+
end
|
22
|
+
@run_list = {}
|
23
|
+
runners.each{|runner| add_runner(runner)}
|
24
|
+
end
|
25
|
+
def add_runner(runner)
|
26
|
+
@runners.push runner
|
27
|
+
runner.run_list.each do |id, run|
|
28
|
+
#raise "Duplicate ids: #{id}" if @run_list[id]
|
29
|
+
@run_list[id] = run
|
30
|
+
end
|
31
|
+
@ids = @run_list.keys
|
32
|
+
end
|
33
|
+
def merge_method(meth, *args, &block)
|
34
|
+
results = @runners.map{|r| r.send(meth, *args)}
|
35
|
+
return results.inject{|o,n| yield(o,n)}
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,1000 @@
|
|
1
|
+
class CodeRunner
|
2
|
+
|
3
|
+
# Every code module defines a custom child class of CodeRunner::Run. This class will also include a module which customizes it to work on the current system. The result is a class which knows how to run and analyses the results of the given code on the given system.
|
4
|
+
#
|
5
|
+
# Every simulation that is carried out has an instance of this custom class created for it, and this object, known as a run, contains both the results and the input parameters pertaining to that simulation. All these runs are then stored in the variable run_list in a runner (an instance of the CodeRunner class). The result is that every run has a runner that it can talk to, and that runner has a set of runs that it can talk to.
|
6
|
+
#
|
7
|
+
# Every run has its own directory, where all the input and output files from the simulation are stored. CodeRunner data for the run is stored in this folder in the files <tt>code_runner_info.rb</tt> and <tt>code_runner_results.rb</tt>.
|
8
|
+
#
|
9
|
+
# As soon as the simulation is complete, CodeRunner call Run#process_directory to carry out any anlysis, and the results are stored in <tt>code_runner_results.rb</tt>. For speed CodeRunner also caches the data in the file <tt>.code_runner_run_data</tt> in binary format. This cache will be used after the initial analysis unless CodeRunner is specifically told to reanalyse this run.
|
10
|
+
#
|
11
|
+
# All input parameters and results are available during run time as instance variables of the run class.
|
12
|
+
#
|
13
|
+
# The class CodeRunner::Run itself defines a base set of methods which are added to by the code module.
|
14
|
+
|
15
|
+
class Run
|
16
|
+
include Log
|
17
|
+
# # include CodeRunner::HeuristicRunMethods
|
18
|
+
|
19
|
+
# class_vars = [:conditions, :sort, :sys, :debug, :script_folder, :necessary_system_runner_methods, :necessary_code_runner_methods, :recalc_all, :parallel]
|
20
|
+
|
21
|
+
|
22
|
+
# @@ruby_command = "ruby" #redefine if necessary
|
23
|
+
# @@input_file_extension = nil
|
24
|
+
# @@use_file_name_as_run_name = nil
|
25
|
+
# @@successful_trial_system = nil
|
26
|
+
#
|
27
|
+
# @@readout_real_run_list = true
|
28
|
+
# @@print_out_real_run_list = true
|
29
|
+
# @@readout_phantom_run_list = false
|
30
|
+
# @@print_out_phantom_run_list = false
|
31
|
+
|
32
|
+
|
33
|
+
# class_accessor :readout_real_run_list, :print_out_real_run_list, :readout_phantom_run_list, :print_out_phantom_run_list,
|
34
|
+
|
35
|
+
class_accessor :current_status, :runner
|
36
|
+
|
37
|
+
# Use the instance method #queue_status (defined in the system module) to put a list of current jobs obtained from the system into the class variable @@current_status
|
38
|
+
|
39
|
+
def self.update_status(runner)
|
40
|
+
if runner.no_run
|
41
|
+
@@current_status = ""
|
42
|
+
else
|
43
|
+
#eputs 'Getting queue status...'
|
44
|
+
@@current_status = new(runner).queue_status
|
45
|
+
end
|
46
|
+
# puts @@current_status
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
def gets #No reading from the command line thank you very much!
|
51
|
+
$stdin.gets
|
52
|
+
end
|
53
|
+
def self.gets
|
54
|
+
$stdin.gets
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
(SUBMIT_OPTIONS + [:code, :version, :readout_list, :naming_pars, :other_pars, :all, :ruby_command, :run_sys_name, :modlet, :executable]).each do |variable|
|
59
|
+
#define accessors for class options and instance options
|
60
|
+
# class_accessor(variable)
|
61
|
+
attr_accessor variable
|
62
|
+
# set(variable, nil) unless class_variables.include? ("@@" + variable).to_sym
|
63
|
+
end
|
64
|
+
|
65
|
+
class_accessor :run_sys_name
|
66
|
+
@@run_sys_name = nil
|
67
|
+
# @@necessary_class_variables.keys.each{|v| send(:attr_accessor, v)}
|
68
|
+
|
69
|
+
# @runnmaxes = {}
|
70
|
+
|
71
|
+
attr_accessor :maxes, :max_complete, :version, :code, :nprocs, :executable_name, :runner, :sys, :naming_pars, :code_runner_version, :real_id
|
72
|
+
|
73
|
+
# Purely for testing purposes; see the test suite
|
74
|
+
|
75
|
+
attr_accessor :run_test_flags
|
76
|
+
|
77
|
+
# Access to a hash which is stored in the runner (not the run). The hash will persist while the runner is in the object space.
|
78
|
+
#
|
79
|
+
# E.g.
|
80
|
+
# cache[:my_stuff_to_store] = something_to_store
|
81
|
+
|
82
|
+
def cache
|
83
|
+
@runner.cache[:runs] ||= {}
|
84
|
+
@runner.cache[:runs][@id] ||= {}
|
85
|
+
@runner.cache[:runs][@id]
|
86
|
+
end
|
87
|
+
|
88
|
+
# The hard cache persists after the current program ceases because
|
89
|
+
# it is written in the .code_runner_run_data file.
|
90
|
+
# It will disappear if the -a or -A flags are specified at any point
|
91
|
+
# If you edit the hard cache you ''must'' call <tt>save</tt> afterwards
|
92
|
+
# or your changes may not be kept
|
93
|
+
def hard_cache
|
94
|
+
@hard_cache ||={}
|
95
|
+
@hard_cache
|
96
|
+
end
|
97
|
+
#def save_hard_cache
|
98
|
+
#Dir.chdir(@directory)
|
99
|
+
|
100
|
+
|
101
|
+
|
102
|
+
|
103
|
+
class RunClassPropertyFetcher
|
104
|
+
def initialize(the_class)
|
105
|
+
@my_class = the_class
|
106
|
+
end
|
107
|
+
def method_missing(method, value=nil)
|
108
|
+
if method.to_s =~ /=$/
|
109
|
+
raise 'rcps should not be set outside class methods'
|
110
|
+
@my_class.instance_variable_set("@"+method.to_s.sub(/=/, ''), value)
|
111
|
+
else
|
112
|
+
the_class = @my_class
|
113
|
+
loop do
|
114
|
+
# p the_class, method
|
115
|
+
# p the_class.instance_variables
|
116
|
+
# p the_class.instance_variables.map{|v| the_class.instance_variable_get(v)}
|
117
|
+
return the_class.instance_variable_get("@"+method.to_s) if the_class.instance_variables.include?(("@"+method.to_s).to_sym)
|
118
|
+
the_class = the_class.superclass
|
119
|
+
return nil unless the_class
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def [](prop)
|
126
|
+
send(prop.to_sym)
|
127
|
+
end
|
128
|
+
# def []=(prop, value)
|
129
|
+
# set(prop.to_sym, value)
|
130
|
+
# end
|
131
|
+
# end
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
# Access properties of the run class. These properties are stored as instance variables of the run class object. (Remember, in Ruby, every Class is an Object (and Object is a Class!)).
|
136
|
+
#
|
137
|
+
# E.g.
|
138
|
+
# puts rcp.variables
|
139
|
+
|
140
|
+
def self.rcp
|
141
|
+
@rcp ||= RunClassPropertyFetcher.new(self)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Calls Run.rcp
|
145
|
+
|
146
|
+
def rcp
|
147
|
+
self.class.rcp
|
148
|
+
end
|
149
|
+
|
150
|
+
# Create a new run. <tt>runner</tt> should be an instance of the class CodeRunner.
|
151
|
+
|
152
|
+
def initialize(runner)
|
153
|
+
logf('initialize')
|
154
|
+
# raise "runner must be either a CodeRunner or a RemoteCoderunner: it is a #{runner.class}" unless [CodeRunner, RemoteCodeRunner].include? runner.class
|
155
|
+
@runner = runner
|
156
|
+
# raise CRFatal.new("Code not defined: #{CODE}") unless @@code and @@code.class == String and @@code =~ /\S/
|
157
|
+
@sys, @code = rcp.sys, rcp.code
|
158
|
+
@naming_pars = rcp.naming_pars.dup
|
159
|
+
raise CRFatal.new("@modlet not specified for #{self.class}") if rcp.modlet_required and not rcp.modlet
|
160
|
+
# @modlet = @@modlet; @modlet_location = @@modlet_location
|
161
|
+
# # @executable_location = executable_location
|
162
|
+
# @@necessary_run_variables.each{|v,clas| instance_eval("@#{v} = nil")}
|
163
|
+
|
164
|
+
# initialize_code_specific
|
165
|
+
|
166
|
+
# @script_folder = @@script_folder
|
167
|
+
# @recalc_all = @@recalc_all
|
168
|
+
@wall_mins = @runner.wall_mins if @runner
|
169
|
+
@smaxes = {}; @csmaxes = {}; @max_complete = {};
|
170
|
+
end
|
171
|
+
|
172
|
+
# Here we redefine the inspect function p to raise an error if anything is written to standard out
|
173
|
+
# while in server mode. This is because the server mode uses standard out to communicate
|
174
|
+
# and writing to standard out can break it. All messages, debug information and so on, should always
|
175
|
+
# be written to standard error.
|
176
|
+
|
177
|
+
def p(*args)
|
178
|
+
if @runner and @runner.server
|
179
|
+
raise "Writing to stdout in server mode will break things!"
|
180
|
+
else
|
181
|
+
super(*args)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# Here we redefine the function puts to raise an error if anything is written to standard out
|
186
|
+
# while in server mode. This is because the server mode uses standard out to communicate
|
187
|
+
# and writing to standard out can break it. All messages, debug information and so on, should always
|
188
|
+
# be written to standard error.
|
189
|
+
|
190
|
+
def puts(*args)
|
191
|
+
if @runner and @runner.server
|
192
|
+
raise "Writing to stdout in server mode will break things!"
|
193
|
+
else
|
194
|
+
super(*args)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# Here we redefine the function print to raise an error if anything is written to standard out
|
199
|
+
# while in server mode. This is because the server mode uses standard out to communicate
|
200
|
+
# and writing to standard out can break it. All messages, debug information and so on, should always
|
201
|
+
# be written to standard error.
|
202
|
+
|
203
|
+
def print(*args)
|
204
|
+
if @runner and @runner.server
|
205
|
+
raise "Writing to stdout in server mode will break things!"
|
206
|
+
else
|
207
|
+
super(*args)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
|
212
|
+
# Analyse the directory of the run. This should be called from the directory where the files of the run are located. This method reads in the CodeRunner data already available in <tt>code_runner_info.rb</tt> and <tt>code_runner_results.rb</tt>, and then calls <tt>process_directory_code_specific</tt> which is defined in the code module.
|
213
|
+
|
214
|
+
def process_directory
|
215
|
+
|
216
|
+
# Clear the cache
|
217
|
+
@runner.cache[:runs]||={}
|
218
|
+
@runner.cache[:runs][@id] = {}
|
219
|
+
logf(:process_directory)
|
220
|
+
raise CRFatal.new("Something has gone horribly wrong: runner.class is #{@runner.class} instead of CodeRunner") unless @runner.class.to_s == "CodeRunner"
|
221
|
+
|
222
|
+
begin
|
223
|
+
@code_runner_version = Version.new(File.read('.code_runner_version.txt'))
|
224
|
+
File.read('code_runner_info.rb')
|
225
|
+
rescue Errno::ENOENT # version may be less than 0.5.1 when .code_runner_version.txt was introduced
|
226
|
+
@code_runner_version = Version.new('0.5.0')
|
227
|
+
end
|
228
|
+
|
229
|
+
@directory = Dir.pwd
|
230
|
+
@relative_directory = File.expand_path(Dir.pwd).sub(File.expand_path(@runner.root_folder) + '/', '')
|
231
|
+
# p @directory
|
232
|
+
@readme = nil
|
233
|
+
|
234
|
+
if @code_runner_version < Version.new('0.5.1')
|
235
|
+
begin
|
236
|
+
update_from_version_0_5_0_and_lower
|
237
|
+
rescue Errno::ENOENT => err # No code runner files were found
|
238
|
+
unless @runner.heuristic_analysis
|
239
|
+
puts err
|
240
|
+
raise CRFatal.new("No code runner files found: suggest using heuristic analysis (flag -H if you are using the code_runner script)")
|
241
|
+
end
|
242
|
+
unless @runner.current_request == :traverse_directories
|
243
|
+
@runner.requests.push :traverse_directories unless @runner.requests.include? :traverse_directories
|
244
|
+
raise CRMild.new("can't begin heuristic analysis until there has been a sweep over all directories") # this will get rescued
|
245
|
+
end
|
246
|
+
@runner.increment_max_id
|
247
|
+
@id = @runner.max_id
|
248
|
+
@job_no = -1
|
249
|
+
run_heuristic_analysis
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
|
254
|
+
read_info
|
255
|
+
begin
|
256
|
+
read_results if FileTest.exist? 'code_runner_results.rb'
|
257
|
+
rescue NoMethodError, SyntaxError => err
|
258
|
+
puts err
|
259
|
+
puts 'Results file possibly corrupted for ' + @run_name
|
260
|
+
end
|
261
|
+
|
262
|
+
if methods.include? :get_run_status
|
263
|
+
@status = get_run_status(@job_no, @@current_status) rescue :Unknown
|
264
|
+
else
|
265
|
+
@status ||= :Unknown
|
266
|
+
end
|
267
|
+
@running = (@@current_status =~ Regexp.new(@job_no.to_s)) ? true : false
|
268
|
+
#logi '@@current_status', @@current_status, '@job_no', @job_no
|
269
|
+
#logi '@running', @running
|
270
|
+
process_directory_code_specific
|
271
|
+
|
272
|
+
raise CRFatal.new("status must be one of #{PERMITTED_STATI.inspect}") unless PERMITTED_STATI.include? @status
|
273
|
+
@max = {}
|
274
|
+
write_results
|
275
|
+
generate_phantom_runs
|
276
|
+
save
|
277
|
+
return self
|
278
|
+
end
|
279
|
+
|
280
|
+
# Read input parameters from the file <tt>code_runner_info.rb</tt>
|
281
|
+
|
282
|
+
def read_info
|
283
|
+
eval(File.read('code_runner_info.rb')).each do |key, value|
|
284
|
+
set(key, value)
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
# Read results from the file <tt>code_runner_results.rb</tt>
|
289
|
+
|
290
|
+
def read_results
|
291
|
+
return if @runner.recalc_all
|
292
|
+
eval(File.read('code_runner_results.rb')).each do |key, value|
|
293
|
+
set(key, value)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
# Write results to the file <tt>code_runner_results.rb</tt>
|
298
|
+
|
299
|
+
def write_results
|
300
|
+
logf(:write_results)
|
301
|
+
Dir.chdir(@directory){File.open("code_runner_results.rb", 'w'){|file| file.puts results_file}}
|
302
|
+
end
|
303
|
+
|
304
|
+
# Return the text of the results file.
|
305
|
+
|
306
|
+
def results_file
|
307
|
+
logf(:results_file)
|
308
|
+
return <<EOF
|
309
|
+
# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
310
|
+
# #{rcp.code_long} Results
|
311
|
+
# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
312
|
+
#
|
313
|
+
# This is a syntactically correct Ruby file, which is used by CodeRunner. Please do not edit unless you know what you are doing.
|
314
|
+
# Directory: #{@directory}
|
315
|
+
# Runname: #{@run_name}
|
316
|
+
# ID: #{@id}
|
317
|
+
|
318
|
+
# Results:
|
319
|
+
#{(rcp.results+rcp.run_info).inject({}){|hash, (var,type_co)| hash[var] = send(var); hash}.pretty_inspect}
|
320
|
+
|
321
|
+
# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
322
|
+
EOF
|
323
|
+
|
324
|
+
end
|
325
|
+
|
326
|
+
# Return a line of data for printing to file in CodeRunner#readout, organised according to the run class property rcp.readout_list
|
327
|
+
|
328
|
+
def data_string
|
329
|
+
rcp.readout_list.inject(""){|str,var| str+"#{send(var)}code_runner_spacer"}.gsub(/\s/, '_').gsub(/code_runner_spacer/, "\t") + "\n"
|
330
|
+
end
|
331
|
+
|
332
|
+
# Used for the status with comments command.
|
333
|
+
|
334
|
+
def comment_line
|
335
|
+
"#{id}: #{@comment}"
|
336
|
+
sprintf("%2d:%d %1s:%2.1f(%s) %3s%1s %s", @id, @job_no, @status.to_s[0,1], @run_time.to_f / 60.0, @nprocs.to_s, percent_complete, "%", @comment)
|
337
|
+
end
|
338
|
+
|
339
|
+
# Cache the run object in the file <tt>.code_runner_run_data</tt>
|
340
|
+
|
341
|
+
def save
|
342
|
+
logf(:save)
|
343
|
+
raise CRFatal.new("Something has gone horribly wrong: runner.class is #{@runner.class} instead of CodeRunner") unless @runner.class.to_s == "CodeRunner"
|
344
|
+
runner, @runner = @runner, nil
|
345
|
+
@system_triers, old_triers = nil, @system_triers
|
346
|
+
@phantom_runs.each{|run| run.runner = nil} if @phantom_runs
|
347
|
+
# logi(self)
|
348
|
+
Dir.chdir(@directory){File.open(".code_runner_run_data", 'w'){|file| file.puts Marshal.dump(self)}}
|
349
|
+
@runner = runner
|
350
|
+
@phantom_runs.each{|run| run.runner = runner} if @phantom_runs
|
351
|
+
@system_triers = old_triers
|
352
|
+
end
|
353
|
+
|
354
|
+
|
355
|
+
# Load the run object from the file <tt>.code_runner_run_data</tt>
|
356
|
+
|
357
|
+
def self.load(dir, runner)
|
358
|
+
raise CRFatal.new("runner supplied in Run.load was not an instance of code runner; runner.class = #{runner.class}") unless runner.class.to_s == "CodeRunner"
|
359
|
+
begin
|
360
|
+
raise CRMild.new("No saved run data") unless FileTest.exist? (dir+"/.code_runner_run_data")
|
361
|
+
run = Marshal.load(File.read(dir+"/.code_runner_run_data"))
|
362
|
+
rescue ArgumentError => err
|
363
|
+
raise err unless err.message =~ /undefined class/
|
364
|
+
#NB this means that all code_names have to contain only lowercase letters:
|
365
|
+
# code, modlet = err.message.scan(/CodeRunner\:\:([A-Z][a-z0-9]+)((?:[A-Z]\w+)+)?Run/)[0]
|
366
|
+
# code, modlet = err.message.scan(/CodeRunner\:\:([A-Z][a-z0-9_]+)(?:::([A-Z]\w+))?/)[0]
|
367
|
+
# ep code, modlet; exit
|
368
|
+
# code.gsub!(/([a-z0-9])([A-Z])/, '\1_\2')
|
369
|
+
# modlet.gsub!(/([a-z0-9])([A-Z])/, '\1_\2')
|
370
|
+
# ep err, modlet, code
|
371
|
+
# runner.setup_run_class(code.downcase, modlet: modlet.downcase)
|
372
|
+
# retry
|
373
|
+
CodeRunner.repair_marshal_run_class_not_found_error(err)
|
374
|
+
run = Marshal.load(File.read(dir+"/.code_runner_run_data"))
|
375
|
+
end
|
376
|
+
run.runner = runner
|
377
|
+
raise CRFatal.new("Something has gone horribly wrong: runner.class is #{run.runner.class} instead of CodeRunner") unless run.runner.class.to_s == "CodeRunner"
|
378
|
+
run.directory = dir
|
379
|
+
run.phantom_runs.each{|r| runner.add_phantom_run(r)} if run.phantom_runs
|
380
|
+
#@phantom_runs = []
|
381
|
+
return run
|
382
|
+
end
|
383
|
+
|
384
|
+
# Return the name of the defaults file currently in use.
|
385
|
+
|
386
|
+
def defaults_file_name
|
387
|
+
if @runner.defaults_file
|
388
|
+
return "#{@runner.defaults_file}_defaults.rb"
|
389
|
+
else
|
390
|
+
return "#{rcp.code}_defaults.rb"
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
# Return the folder where the default defaults file is located.
|
395
|
+
|
396
|
+
def defaults_location
|
397
|
+
if @runner.defaults_file
|
398
|
+
location = ["#{SCRIPT_FOLDER}/code_modules/#@code/my_defaults_files", "#{SCRIPT_FOLDER}/code_modules/#@code/defaults_files"].find{|folder| FileTest.exist? folder and Dir.entries(folder).include? defaults_file_name}
|
399
|
+
raise "Defaults file: #{defaults_file_name} not found" unless location
|
400
|
+
return location
|
401
|
+
else
|
402
|
+
return "#{SCRIPT_FOLDER}/code_modules/#@code"
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
# Return true if the run is completed, false otherwise
|
407
|
+
|
408
|
+
def is_complete
|
409
|
+
@status == :Complete
|
410
|
+
end
|
411
|
+
|
412
|
+
alias :ctd :is_complete
|
413
|
+
|
414
|
+
# This function is a hook which is used by system modules. For runs it is defined as the id.
|
415
|
+
|
416
|
+
def job_identifier
|
417
|
+
id
|
418
|
+
end
|
419
|
+
|
420
|
+
class NoRunnerError < StandardError
|
421
|
+
def new(mess)
|
422
|
+
super("No Runner: a runner was needed for this Run method call "+mess)
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
# Update instance variables using the given defaults file. Give warnings if the defaults file contains variables which are not simulation input parameters
|
427
|
+
|
428
|
+
def evaluate_defaults_file(filename)
|
429
|
+
text = File.read(filename)
|
430
|
+
text.scan(/^\s*@(\w+)/) do
|
431
|
+
var_name = $~[1].to_sym
|
432
|
+
next if var_name == :defaults_file_description
|
433
|
+
unless rcp.variables.include? var_name
|
434
|
+
warning("---#{var_name}---, specified in #{File.expand_path(filename)}, is not a variable. This could be an error")
|
435
|
+
end
|
436
|
+
end
|
437
|
+
instance_eval(text)
|
438
|
+
end
|
439
|
+
|
440
|
+
# This function set the input parameters of the run using the following sources in ascending order of priority: main defaults file (in the code module folder), local defaults file (in the local Directory), parameters (an inspected hash usually specified on the command line).
|
441
|
+
|
442
|
+
def update_submission_parameters(parameters, start_from_defaults=true)
|
443
|
+
logf(:update_submission_parameters)
|
444
|
+
if start_from_defaults
|
445
|
+
upgrade_defaults_from_0_5_0 if self.class.constants.include? :DEFAULTS_FILE_NAME_0_5_0
|
446
|
+
main_defaults_file = "#{defaults_location}/#{defaults_file_name}"
|
447
|
+
main_defaults_file_text = File.read(main_defaults_file)
|
448
|
+
evaluate_defaults_file(main_defaults_file)
|
449
|
+
|
450
|
+
unless FileTest.exist?(defaults_file_name)
|
451
|
+
main_defaults_file_text.gsub!(/^/, "#")
|
452
|
+
header = <<EOF
|
453
|
+
#############################################################
|
454
|
+
# CodeRunner Local Defaults File
|
455
|
+
############################################################
|
456
|
+
#
|
457
|
+
# This is a local copy of the central defaults file, which
|
458
|
+
# was copied from the central defaults file
|
459
|
+
# #{defaults_file_name}
|
460
|
+
#
|
461
|
+
# to the folder
|
462
|
+
# #{Dir.pwd}
|
463
|
+
#
|
464
|
+
# at
|
465
|
+
# #{Time.now}
|
466
|
+
#
|
467
|
+
# by CodeRunner version #{CODE_RUNNER_VERSION}
|
468
|
+
#
|
469
|
+
# All lines in the original file have been commented out:
|
470
|
+
# they are kept as a reference to make adding local defaults easier.
|
471
|
+
# It is suggested that local changes are placed at the top
|
472
|
+
# of this file, not in the body of the commented out section.
|
473
|
+
#
|
474
|
+
# Local changes override the central defaults file. However,
|
475
|
+
# if the central defaults file changes, any variables which
|
476
|
+
# are not overidden here will change for any future simulations
|
477
|
+
# in this folder.
|
478
|
+
#
|
479
|
+
##############################################################
|
480
|
+
|
481
|
+
|
482
|
+
|
483
|
+
|
484
|
+
|
485
|
+
|
486
|
+
# Begin Copy of Central Defaults:
|
487
|
+
#
|
488
|
+
#
|
489
|
+
EOF
|
490
|
+
main_defaults_file_text = header + main_defaults_file_text
|
491
|
+
File.open(defaults_file_name, 'w'){|file| file.puts main_defaults_file_text}
|
492
|
+
end
|
493
|
+
#FileUtils.cp("#{defaults_location}/#{defaults_file_name}", defaults_file_name)
|
494
|
+
|
495
|
+
evaluate_defaults_file(defaults_file_name)
|
496
|
+
end
|
497
|
+
return unless parameters
|
498
|
+
raise "parameters: #{parameters.inspect} must be a string which evaluates to a hash" unless parameters.class == String and parameters = eval(parameters) and parameters.class == Hash # parameters.class == String and parameters =~ /\S/
|
499
|
+
@parameter_hash = parameters
|
500
|
+
parameters.each do |var, value|
|
501
|
+
raise CRFatal.new('Cannot specify id as a parameter') if var.to_sym == :id
|
502
|
+
set(var, value) unless value == :default
|
503
|
+
next if [:comment].include? var
|
504
|
+
@naming_pars.push var
|
505
|
+
end
|
506
|
+
@naming_pars.uniq!
|
507
|
+
self
|
508
|
+
end
|
509
|
+
|
510
|
+
def execute_submission
|
511
|
+
if @runner.test_submission
|
512
|
+
log 'testing submission'
|
513
|
+
eputs info_file
|
514
|
+
File.delete(@runner.root_folder + "/submitting")
|
515
|
+
exit
|
516
|
+
else
|
517
|
+
execute
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
521
|
+
# def submit
|
522
|
+
# logf(:submit)
|
523
|
+
# logi(:@nprocs, @nprocs)
|
524
|
+
#
|
525
|
+
# raise "Test Submit Error Handling" if @run_test_flags and @run_test_flags[:test_submit_error_handling]
|
526
|
+
#
|
527
|
+
# eputs "System " + SYS
|
528
|
+
# eputs "nprocs " + @nprocs
|
529
|
+
# # puts send(:q)
|
530
|
+
#
|
531
|
+
# raise CRFatal.new ("Can't find executable: #{executable_location}/#{executable_name}") unless FileTest.exist? File.expand_path("#{executable_location}/#{executable_name}")
|
532
|
+
#
|
533
|
+
# @naming_pars.each{|par| raise CRFatal.new("@naming_par #{par} is not listed in variables") unless rcp.variables.include? par}
|
534
|
+
# # @other_pars = []
|
535
|
+
# # @@variables.dup.each do |par, type_co|
|
536
|
+
# # @other_pars.push par unless @naming_pars.include? par
|
537
|
+
# # end
|
538
|
+
#
|
539
|
+
# # @dir_name = %[v#@version] + @naming_pars.inject("") do |str, par|
|
540
|
+
# # str+="/#{par}_#{send(par)}"
|
541
|
+
# # end
|
542
|
+
#
|
543
|
+
# @dir_name = %[v#@version]
|
544
|
+
#
|
545
|
+
# @run_name = %[v#@version] + @naming_pars.inject("") do |str, par|
|
546
|
+
# str+="_#{par}_#{send(par)}"
|
547
|
+
# end
|
548
|
+
# @dir_name = @dir_name.gsub(/\s+/, "_") + "/id_#@id"
|
549
|
+
# @run_name = @run_name.gsub(/\s+/, "_") + "_id_#@id"
|
550
|
+
# @directory = File.expand_path(@dir_name)
|
551
|
+
#
|
552
|
+
# @job_no = nil
|
553
|
+
# qstat = queue_status #|| ""
|
554
|
+
# # puts qstat;# gets
|
555
|
+
# # qstat = qstat =~ /\S/ ? qstat : nil
|
556
|
+
# if qstat and qstat.class == String
|
557
|
+
# FileUtils.makedirs(@dir_name)
|
558
|
+
# Dir.chdir(@dir_name) do
|
559
|
+
# generate_input_file
|
560
|
+
# job_nos = qstat.scan(/^\s*(\d+)/).map{|match| match[0].to_i} if qstat
|
561
|
+
# # logi(:job_nos, job_nos)
|
562
|
+
# ##################
|
563
|
+
# execute_submission
|
564
|
+
# ##################
|
565
|
+
# qstat = queue_status #|| ""
|
566
|
+
# # qstat = qstat=~/\S/ ? qstat : nil
|
567
|
+
# 5.times do # job_no may not appear instantly
|
568
|
+
# new_job_nos = qstat.scan(/^\s*(\d+)/).map{|match| match[0].to_i}
|
569
|
+
# # puts new_job_nos
|
570
|
+
# logi(:new_minus_old, new_job_nos-job_nos)
|
571
|
+
# @job_no = (new_job_nos-job_nos).sort[-1]
|
572
|
+
# break if @job_no
|
573
|
+
# sleep 0.2
|
574
|
+
# qstat = queue_status
|
575
|
+
# end
|
576
|
+
# @job_no ||= -1 # some functions don't work if job_no couldn't be found, but most are ok
|
577
|
+
# eputs info_file
|
578
|
+
# @sys = SYS
|
579
|
+
# write_info
|
580
|
+
# File.open(".code_runner_version.txt", 'w'){|file| file.puts CODE_RUNNER_VERSION}
|
581
|
+
# File.open("code_runner_modlet.rb", 'w'){|file| file.puts rcp.modlet_source} if rcp.modlet_required
|
582
|
+
# end
|
583
|
+
#
|
584
|
+
#
|
585
|
+
# else
|
586
|
+
# File.delete("submitting")
|
587
|
+
# raise CRFatal.new("queue_status did not return a string; submission cancelled. Suggest editing system_modules/#{SYS}.rb")
|
588
|
+
# end
|
589
|
+
#
|
590
|
+
# end
|
591
|
+
|
592
|
+
# Generate the run name and the directory name, and check that the directory is empty. The run name, i.e. @run_name can be set beforehand, in which case it will not be changed. The directory name choice can be influenced by the variable @dir_name, which is not used outside this function.
|
593
|
+
|
594
|
+
def prepare_submission(options={})
|
595
|
+
raise "Test Submit Error Handling" if @run_test_flags and @run_test_flags[:test_submit_error_handling]
|
596
|
+
|
597
|
+
#p '@nprocs', @nprocs
|
598
|
+
if @runner
|
599
|
+
SUBMIT_OPTIONS.each do |option|
|
600
|
+
set(option, @runner.send(option)) if @runner.send(option)
|
601
|
+
end
|
602
|
+
end
|
603
|
+
#p '@nprocs', @nprocs
|
604
|
+
|
605
|
+
# puts send(:q)
|
606
|
+
|
607
|
+
raise CRFatal.new ("Can't find executable: #{executable_location}/#{executable_name}") unless FileTest.exist? File.expand_path("#{executable_location}/#{executable_name}")
|
608
|
+
|
609
|
+
@naming_pars.each{|par| raise CRFatal.new("@naming_par #{par} is not listed in variables or run_info") unless (rcp.variables + rcp.run_info).include? par}
|
610
|
+
@naming_pars.delete(:g_exb_start_timestep)
|
611
|
+
unless @dir_name # dir_name can be set in advance to change the default directory name
|
612
|
+
@dir_name = %[v#@version]
|
613
|
+
@dir_name = @dir_name.gsub(/\s+/, "_") + "/id_#@id"
|
614
|
+
end
|
615
|
+
generate_run_name unless @run_name
|
616
|
+
|
617
|
+
@directory = File.expand_path(@dir_name)
|
618
|
+
#@relative_directory = File.expand_path(Dir.pwd).sub(File.expand_path(@runner.root_folder) + '/', '')
|
619
|
+
#@relative_directory = @directory.sub(File.expand_path(@runner.root_folder) + '/', '')
|
620
|
+
@relative_directory = @dir_name
|
621
|
+
|
622
|
+
raise "Directory #@dir_name contains code_runner_info" if FileTest.exist? @directory and Dir.entries(@directory).include? ["code_runner_info.rb"]
|
623
|
+
|
624
|
+
@job_no = nil
|
625
|
+
FileUtils.makedirs(@dir_name)
|
626
|
+
Dir.chdir(@dir_name) do
|
627
|
+
generate_input_file
|
628
|
+
|
629
|
+
File.open(".code_runner_version.txt", 'w'){|file| file.puts CODE_RUNNER_VERSION}
|
630
|
+
File.open("code_runner_modlet.rb", 'w'){|file| file.puts rcp.modlet_source} if rcp.modlet_required
|
631
|
+
end
|
632
|
+
@dir_name = nil
|
633
|
+
|
634
|
+
end
|
635
|
+
|
636
|
+
def generate_run_name
|
637
|
+
@run_name = %[v#@version] + @naming_pars.inject("") do |str, par|
|
638
|
+
str+="_#{par}_#{send(par).to_s[0...8]}"
|
639
|
+
end
|
640
|
+
@run_name = @run_name.gsub(/\s+/, "_").gsub(/\//, '') + "_id_#@id"
|
641
|
+
end
|
642
|
+
|
643
|
+
def write_info
|
644
|
+
Dir.chdir(@directory){File.open("code_runner_info.rb", 'w'){|file| file.puts info_file}}
|
645
|
+
end
|
646
|
+
|
647
|
+
# private :write_info
|
648
|
+
|
649
|
+
def info_file
|
650
|
+
@modlet = rcp.modlet
|
651
|
+
return <<EOF
|
652
|
+
# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
653
|
+
# #{rcp.code_long} Input Parameters
|
654
|
+
# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
655
|
+
# Code: #{rcp.code}
|
656
|
+
# System: #{@sys}
|
657
|
+
# Version: #{@version}
|
658
|
+
# Nprocs: #{@nprocs}
|
659
|
+
# Directory: #{Dir.pwd}
|
660
|
+
# Runname: #{@run_name}
|
661
|
+
# ID: #{@id}
|
662
|
+
# #{rcp.modlet_required ? "Modlet:\t#{rcp.modlet}" : ""}
|
663
|
+
# Classname: #{self.class.to_s}
|
664
|
+
|
665
|
+
# #{@job_no ? "Job_No: #{@job_no}" : ""}
|
666
|
+
|
667
|
+
# Parameters:
|
668
|
+
#{(rcp.variables + rcp.run_info + [:version, :code, :modlet, :sys]).inject({}){|hash, var| hash[var] = send(var) unless (!send(var) and send(var) == nil); hash}.pretty_inspect}
|
669
|
+
|
670
|
+
|
671
|
+
# Actual Command:
|
672
|
+
# #{run_command}
|
673
|
+
# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
674
|
+
EOF
|
675
|
+
|
676
|
+
end
|
677
|
+
|
678
|
+
def set_modlet(modlet, folder=nil)
|
679
|
+
logf(:set_modlet)
|
680
|
+
rcp.modlet.sub!(/.*/, modlet)
|
681
|
+
self.class.set_modlet(modlet, folder)
|
682
|
+
end
|
683
|
+
private :set_modlet
|
684
|
+
|
685
|
+
def self.set_modlet(modlet, folder = nil)
|
686
|
+
raise 'old method -- needs to be debugged'
|
687
|
+
Log.log("self.set_modlet", self.to_s)
|
688
|
+
class_eval(File.read("#{folder ? folder + "/" : ""}#{modlet}"))
|
689
|
+
check_and_update
|
690
|
+
end
|
691
|
+
|
692
|
+
# aliold :inspect
|
693
|
+
def inspect
|
694
|
+
old, @runner = @runner, nil
|
695
|
+
str = super
|
696
|
+
@runner = old
|
697
|
+
str
|
698
|
+
end
|
699
|
+
|
700
|
+
|
701
|
+
# aliold :pretty_inspect
|
702
|
+
def pretty_print(q)
|
703
|
+
old, @runner = @runner, nil
|
704
|
+
str = q.pp_object(self)
|
705
|
+
@runner = old
|
706
|
+
str
|
707
|
+
|
708
|
+
end
|
709
|
+
|
710
|
+
# def pretty_print(q)
|
711
|
+
# if /\(Kernel\)#/ !~ Kernel.instance_method(:method).bind(self).call(:inspect).inspect
|
712
|
+
# q.text self.inspect
|
713
|
+
# elsif /\(Kernel\)#/ !~ Kernel.instance_method(:method).bind(self).call(:to_s).inspect && instance_variables.empty?
|
714
|
+
# q.text self.to_s
|
715
|
+
# else
|
716
|
+
# q.pp_object(self)
|
717
|
+
# end
|
718
|
+
# end
|
719
|
+
|
720
|
+
# A hook for module developers
|
721
|
+
|
722
|
+
def self.finish_setting_up_class
|
723
|
+
end
|
724
|
+
|
725
|
+
# ALL = []
|
726
|
+
# READOUT_LIST = []
|
727
|
+
# CODE = nil
|
728
|
+
|
729
|
+
def self.check_and_update
|
730
|
+
Log.logf(:check_and_update)
|
731
|
+
|
732
|
+
#
|
733
|
+
finish_setting_up_class
|
734
|
+
# ep self.to_s, constants, self.ancestors
|
735
|
+
|
736
|
+
# raise CRFatal.new("Code not defined (#{CODE.inspect}) in class #{self.to_s}") unless CODE and CODE.class == String and CODE =~ /\S/
|
737
|
+
|
738
|
+
|
739
|
+
NECESSARY_RUN_SYSTEM_METHODS.each do |method|
|
740
|
+
raise CRFatal.new("#{method} not defined in #{SYS}_system_runner.rb") unless instance_methods.include?(method)
|
741
|
+
end
|
742
|
+
|
743
|
+
NECESSARY_RUN_CODE_METHODS.each do |method|
|
744
|
+
raise CRFatal.new("#{method} not defined in #{self.class}_code_runner.rb") unless instance_methods.include?(method)
|
745
|
+
end
|
746
|
+
|
747
|
+
|
748
|
+
NECESSARY_RUN_CLASS_PROPERTIES.each do |v,class_list|
|
749
|
+
# raise CRFatal.new("#{v} not defined") unless rcp[v]
|
750
|
+
raise CRFatal.new("#{v} not defined correctly: class is #{rcp[v].class} instead of one of #{class_list.to_s}") unless class_list.include? rcp[v].class
|
751
|
+
end
|
752
|
+
|
753
|
+
@readout_list = (rcp.variables+rcp.results) unless rcp.readout_list
|
754
|
+
# (variables+results).each{|v| const_get(:READOUT_LIST).push v} unless READOUT_LIST.size > 0
|
755
|
+
|
756
|
+
if rcp.variables_0_5_0
|
757
|
+
rcp.variables_0_5_0.dup.each do |par, info| #for backwards compatibility only
|
758
|
+
rcp.variables_0_5_0[par] = info[0] if info.class == Array
|
759
|
+
end
|
760
|
+
end
|
761
|
+
|
762
|
+
|
763
|
+
|
764
|
+
|
765
|
+
|
766
|
+
# Log.log(:@@variables0, @@variables[0])
|
767
|
+
|
768
|
+
@run_info = rcp.run_info || [] # Run info can optionally be defined in the code module.
|
769
|
+
# ep @run_info
|
770
|
+
@run_info = rcp.run_info + ([:job_no, :running, :id, :status, :sys, :is_phantom, :naming_pars, :run_name, :resubmit_id, :real_id, :phantom_runs, :parameter_hash, :output_file, :error_file] + SUBMIT_OPTIONS) #.each{|v| RUN_INFO.push v} unless RUN_INFO.include? :job_no
|
771
|
+
@all = (rcp.variables + rcp.results + rcp.run_info) #.each{|v| ALL.push v}
|
772
|
+
# ep "GOT HERE"
|
773
|
+
(@all + [:directory, :run_name, :modlet, :relative_directory]).each{|var| send(:attr_accessor, var)}
|
774
|
+
define_method(:output_file) do
|
775
|
+
return @output_file if @output_file
|
776
|
+
@output_file = super()
|
777
|
+
end
|
778
|
+
define_method(:error_file) do
|
779
|
+
return @error_file if @error_file
|
780
|
+
@error_file = super()
|
781
|
+
end
|
782
|
+
|
783
|
+
Dir.chdir(SCRIPT_FOLDER + "/system_modules") do
|
784
|
+
@system_run_classes ||=
|
785
|
+
Dir.entries(Dir.pwd).find_all{|file| file =~ /^[^\.].+\.rb$/}.inject([]) do |arr, file|
|
786
|
+
#p Dir.pwd
|
787
|
+
#p 'required', file, Dir.pwd
|
788
|
+
require Dir.pwd + '/' + file
|
789
|
+
# p CodeRunner.constants
|
790
|
+
sys = file.sub(/\.rb$/, '')
|
791
|
+
arr.push(add_a_child_class("#{sys.variable_to_class_name}Run"))
|
792
|
+
arr[-1].send(:include, CodeRunner.const_get(sys.variable_to_class_name))
|
793
|
+
arr
|
794
|
+
end
|
795
|
+
end
|
796
|
+
|
797
|
+
|
798
|
+
end
|
799
|
+
|
800
|
+
def dup
|
801
|
+
return self.class.new(@runner).learn_from(self)
|
802
|
+
end
|
803
|
+
|
804
|
+
def create_phantom
|
805
|
+
@phantom_runs ||= []
|
806
|
+
new_run = dup
|
807
|
+
new_run.is_phantom = true
|
808
|
+
new_run.real_id = @id
|
809
|
+
@runner.add_phantom_run(new_run)
|
810
|
+
@phantom_runs.push new_run
|
811
|
+
new_run
|
812
|
+
end
|
813
|
+
|
814
|
+
def learn_from(run)
|
815
|
+
run.instance_variables.each do |var|
|
816
|
+
# puts var
|
817
|
+
# puts run.instance_variable_get(var)
|
818
|
+
instance_variable_set(var,run.instance_variable_get(var))
|
819
|
+
# puts instance_variable_get(var)
|
820
|
+
|
821
|
+
# rescue NoMethodError
|
822
|
+
# next
|
823
|
+
# end
|
824
|
+
end
|
825
|
+
self
|
826
|
+
end
|
827
|
+
|
828
|
+
|
829
|
+
|
830
|
+
|
831
|
+
def logiv
|
832
|
+
instance_variables.each do |var|
|
833
|
+
unless var == :@runner or var == :@system_triers
|
834
|
+
log(var); logi(instance_variable_get(var))
|
835
|
+
end
|
836
|
+
end
|
837
|
+
end
|
838
|
+
|
839
|
+
def recheck
|
840
|
+
logf(:recheck)
|
841
|
+
Dir.chdir(@directory) do
|
842
|
+
# puts 'ackack'
|
843
|
+
puts "Rechecking #@run_name"
|
844
|
+
runner = @runner
|
845
|
+
instance_variables.each{|var| instance_variable_set(var, nil) unless var == :@runner}
|
846
|
+
begin File.delete("CODE_RUNNER_RUN_DATA") rescue Errno::ENOENT end
|
847
|
+
begin File.delete("CODE_RUNNER_RESULTS") rescue Errno::ENOENT end
|
848
|
+
process_directory
|
849
|
+
save
|
850
|
+
end
|
851
|
+
end
|
852
|
+
|
853
|
+
def generate_phantom_runs
|
854
|
+
end
|
855
|
+
|
856
|
+
def generate_combined_ids(type)
|
857
|
+
raise CRFatal.new("Can't call generate_combined_ids from a run")
|
858
|
+
end
|
859
|
+
|
860
|
+
# @@maxes = {}
|
861
|
+
# @@cmaxes = {}
|
862
|
+
|
863
|
+
def max(variable, complete=false) #does this run have the maximum value of this variable
|
864
|
+
raise ArgumentError.new("complete must be true or false") unless [TrueClass, FalseClass].include? complete.class
|
865
|
+
@runner.generate_combined_ids
|
866
|
+
ids = @runner.combined_ids
|
867
|
+
if complete
|
868
|
+
ids = ids.find_all{|id| @runner.combined_run_list[id].status == :Complete}
|
869
|
+
max_id = @runner.cmaxes[variable] ||= ids.sort_by{|id| @runner.combined_run_list[id].send(variable)}[-1]
|
870
|
+
else
|
871
|
+
max_id = @runner.maxes[variable] ||= ids.sort_by{|id| @runner.combined_run_list[id].send(variable)}[-1]
|
872
|
+
end
|
873
|
+
return @runner.combined_run_list[max_id].send(variable) == send(variable)
|
874
|
+
end
|
875
|
+
|
876
|
+
# # @@mins = {}
|
877
|
+
# @@cmins = {}
|
878
|
+
|
879
|
+
|
880
|
+
def min(variable, complete=false) #does this run have the minimum value of this variable
|
881
|
+
raise ArgumentError.new("complete must be true or false") unless [TrueClass, FalseClass].include? complete.class
|
882
|
+
@runner.generate_combined_ids
|
883
|
+
ids = @runner.combined_ids
|
884
|
+
|
885
|
+
if complete
|
886
|
+
ids = ids.find_all{|id| @runner.combined_run_list[id].status == :Complete}
|
887
|
+
min_id = @runner.cmins[variable] ||= ids.sort_by{|id| @runner.combined_run_list[id].send(variable)}[0]
|
888
|
+
else
|
889
|
+
min_id = @runner.mins[variable] ||= ids.sort_by{|id| @runner.combined_run_list[id].send(variable)}[0]
|
890
|
+
end
|
891
|
+
return @runner.combined_run_list[min_id].send(variable) == send(variable)
|
892
|
+
end
|
893
|
+
|
894
|
+
# @@fmaxes = {}
|
895
|
+
# @@cfmaxes = {}
|
896
|
+
|
897
|
+
# # Does this run have the maximum value of this variable to be found amoung the filtered runs?
|
898
|
+
#
|
899
|
+
# def fmax(variable, complete = false)
|
900
|
+
# raise ArgumentError.new("complete must be true or false") unless [TrueClass, FalseClass].include complete.class
|
901
|
+
# @runner.generate_combined_ids
|
902
|
+
# ids = @runner.filtered_ids # o^o-¬
|
903
|
+
# if complete
|
904
|
+
# ids = ids.find_all{|id| @runner.combined_run_list[id].status == :Complete}
|
905
|
+
# max_id = @@cfmaxes[variable] ||= ids.sort_by{|id| @runner.combined_run_list[id].send(variable)}[-1]
|
906
|
+
# else
|
907
|
+
# max_id = @@fmaxes[variable] ||= ids.sort_by{|id| @runner.combined_run_list[id].send(variable)}[-1]
|
908
|
+
# end
|
909
|
+
# return @runner.combined_run_list[max_id].send(variable) == send(variable)
|
910
|
+
# end
|
911
|
+
#
|
912
|
+
|
913
|
+
def smax(variable,sweep=nil, complete=nil)
|
914
|
+
logf(:max)
|
915
|
+
sweep ||= variable
|
916
|
+
if complete
|
917
|
+
@csmaxes[variable] ||= {}
|
918
|
+
max_id = @csmaxes[variable][sweep] = @runner.get_max(self, variable,sweep, complete)
|
919
|
+
else
|
920
|
+
@smaxes[variable] ||= {}
|
921
|
+
max_id = @smaxes[variable][sweep] = @runner.get_max(self, variable,sweep, complete)
|
922
|
+
end
|
923
|
+
return @runner.combined_run_list[max_id].send(variable) == send(variable)
|
924
|
+
end
|
925
|
+
|
926
|
+
def smin(variable,sweep=nil, complete=nil)
|
927
|
+
logf(:min)
|
928
|
+
sweep ||= variable
|
929
|
+
@smins ||= {}
|
930
|
+
@csmins ||= {}
|
931
|
+
if complete
|
932
|
+
@csmins[variable] ||= {}
|
933
|
+
min_id = @csmins[variable][sweep] = @runner.get_min(self, variable,sweep, complete)
|
934
|
+
else
|
935
|
+
@smins[variable] ||= {}
|
936
|
+
min_id = @smins[variable][sweep] = @runner.get_min(self, variable,sweep, complete)
|
937
|
+
end
|
938
|
+
return @runner.combined_run_list[min_id].send(variable) == send(variable)
|
939
|
+
end
|
940
|
+
|
941
|
+
# aliold :_dump
|
942
|
+
# def _dump(*args)
|
943
|
+
# @runner, runner = nil, @runner
|
944
|
+
# ans = super(*args)
|
945
|
+
# @runner = runner
|
946
|
+
# return ans
|
947
|
+
# end
|
948
|
+
|
949
|
+
def executable_name
|
950
|
+
File.basename(@executable||=@runner.executable)
|
951
|
+
end
|
952
|
+
|
953
|
+
def executable_location
|
954
|
+
File.dirname(@executable||=@runner.executable)
|
955
|
+
end
|
956
|
+
|
957
|
+
def update_in_queue
|
958
|
+
unless @status == :Queueing
|
959
|
+
raise 'Can only updated runs whose status is :Queueing'
|
960
|
+
end
|
961
|
+
unless methods.include? :batch_script_file
|
962
|
+
raise 'Can only update runs which have been submitted using a batch script file'
|
963
|
+
end
|
964
|
+
old_run_name = @run_name
|
965
|
+
generate_run_name
|
966
|
+
new_run_name = @run_name
|
967
|
+
#@run_name = old_run_name
|
968
|
+
unless FileTest.exist?(filename = @directory + '/' + batch_script_file) or
|
969
|
+
FileTest.exist?(filename = @runner.root_folder + '/' + batch_script_file)
|
970
|
+
raise 'Could not find batch_script_file'
|
971
|
+
end
|
972
|
+
old_batch_script=File.read(filename)
|
973
|
+
eputs old_batch_script
|
974
|
+
eputs old_batch_script.gsub(Regexp.new(Regexp.escape(old_run_name)), new_run_name)
|
975
|
+
ep Regexp.new(Regexp.escape(old_run_name))
|
976
|
+
File.open(filename, 'w'){|file| file.puts old_batch_script.gsub(Regexp.new(Regexp.escape(old_run_name)), new_run_name)}
|
977
|
+
|
978
|
+
|
979
|
+
generate_input_file
|
980
|
+
#throw(:done)
|
981
|
+
write_info
|
982
|
+
end
|
983
|
+
|
984
|
+
# A hook... default is to do nothing
|
985
|
+
|
986
|
+
def self.modify_job_script(runner, runs, script)
|
987
|
+
return script
|
988
|
+
end
|
989
|
+
|
990
|
+
# A hook... a string which gets put into the job
|
991
|
+
# script. Used to load modules, configure the run
|
992
|
+
# time environment for a given code. Default
|
993
|
+
# is to return an empty string.
|
994
|
+
|
995
|
+
def code_run_environment
|
996
|
+
""
|
997
|
+
end
|
998
|
+
end
|
999
|
+
|
1000
|
+
end
|