Class: CodeRunner::Run

Inherits:
Object show all
Includes:
Log
Defined in:
lib/coderunner/run.rb,
lib/coderunner/fortran_namelist.rb,
lib/coderunner/fortran_namelist_c.rb,
lib/coderunner/heuristic_run_methods.rb

Overview

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.

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.

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 code_runner_info.rb and code_runner_results.rb.

As soon as the simulation is complete, CodeRunner call Run#process_directory to carry out any anlysis, and the results are stored in code_runner_results.rb. For speed CodeRunner also caches the data in the file .code_runner_run_data in binary format. This cache will be used after the initial analysis unless CodeRunner is specifically told to reanalyse this run.

All input parameters and results are available during run time as instance variables of the run class.

The class CodeRunner::Run itself defines a base set of methods which are added to by the code module.

Direct Known Subclasses

FortranNamelist

Defined Under Namespace

Classes: FortranNamelist, FortranNamelistC, NoRunnerError, RunClassPropertyFetcher, SubmitError

Constant Summary collapse

@@run_sys_name =
nil
@@successful_trial_system =
nil
@@successful_trial_class =
nil

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(runner) ⇒ Run

Create a new run. runner should be an instance of the class CodeRunner.

Raises:



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/coderunner/run.rb', line 158

def initialize(runner)
	logf('initialize')
# 	raise "runner must be either a CodeRunner or a RemoteCoderunner: it is a #{runner.class}" unless [CodeRunner, RemoteCodeRunner].include? runner.class
	@runner = runner
# 	raise CRFatal.new("Code not defined: #{CODE}") unless @@code and @@code.class == String and @@code =~ /\S/  
	@sys, @code = rcp.sys, rcp.code
	@naming_pars = rcp.naming_pars.dup
	raise CRFatal.new("@modlet not specified for #{self.class}") if rcp.modlet_required and not rcp.modlet
# 	@modlet = @@modlet; @modlet_location = @@modlet_location
# # 	@executable_location = executable_location
# 		@@necessary_run_variables.each{|v,clas| instance_eval("@#{v} = nil")}
		
# 		initialize_code_specific
	
# 	@script_folder  = @@script_folder
# 	@recalc_all = @@recalc_all
	@wall_mins = @runner.wall_mins if @runner
 	@smaxes = {}; @csmaxes = {}; @max_complete = {};
end

Instance Attribute Details

#codeObject

Returns the value of attribute code.



71
72
73
# File 'lib/coderunner/run.rb', line 71

def code
  @code
end

#code_runner_versionObject

Returns the value of attribute code_runner_version.



71
72
73
# File 'lib/coderunner/run.rb', line 71

def code_runner_version
  @code_runner_version
end

#executable_nameObject

aliold :_dump def _dump(*args) @runner, runner = nil, @runner ans = super(*args) @runner = runner return ans end



71
72
73
# File 'lib/coderunner/run.rb', line 71

def executable_name
  @executable_name
end

#max_completeObject

Returns the value of attribute max_complete.



71
72
73
# File 'lib/coderunner/run.rb', line 71

def max_complete
  @max_complete
end

#maxesObject

Returns the value of attribute maxes.



71
72
73
# File 'lib/coderunner/run.rb', line 71

def maxes
  @maxes
end

#naming_parsObject

Returns the value of attribute naming_pars.



71
72
73
# File 'lib/coderunner/run.rb', line 71

def naming_pars
  @naming_pars
end

#nprocsObject

Returns the value of attribute nprocs.



71
72
73
# File 'lib/coderunner/run.rb', line 71

def nprocs
  @nprocs
end

#real_idObject

Returns the value of attribute real_id.



71
72
73
# File 'lib/coderunner/run.rb', line 71

def real_id
  @real_id
end

#run_test_flagsObject

Purely for testing purposes; see the test suite



75
76
77
# File 'lib/coderunner/run.rb', line 75

def run_test_flags
  @run_test_flags
end

#runnerObject

Returns the value of attribute runner.



71
72
73
# File 'lib/coderunner/run.rb', line 71

def runner
  @runner
end

#sysObject

Returns the value of attribute sys.



71
72
73
# File 'lib/coderunner/run.rb', line 71

def sys
  @sys
end

#versionObject

Returns the value of attribute version.



71
72
73
# File 'lib/coderunner/run.rb', line 71

def version
  @version
end

Class Method Details

.check_and_updateObject

ALL = [] READOUT_LIST = [] CODE = nil



753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
# File 'lib/coderunner/run.rb', line 753

def self.check_and_update
		Log.logf(:check_and_update)

# 	
		finish_setting_up_class
# 				ep self.to_s, constants, self.ancestors

# 		raise CRFatal.new("Code not defined (#{CODE.inspect}) in class #{self.to_s}") unless CODE and CODE.class == String and CODE =~ /\S/  


		NECESSARY_RUN_SYSTEM_METHODS.each do |method|
			raise CRFatal.new("#{method} not defined in #{SYS}_system_runner.rb") unless instance_methods.include?(method)
		end

		NECESSARY_RUN_CODE_METHODS.each do |method|
			raise CRFatal.new("#{method} not defined in #{self.class}_code_runner.rb") unless instance_methods.include?(method)
		end
	
		
		NECESSARY_RUN_CLASS_PROPERTIES.each do |v,class_list|
# 			raise CRFatal.new("#{v} not defined") unless rcp[v]
			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
		end
		
		@readout_list = (rcp.variables+rcp.results) unless rcp.readout_list?

		raise "

Please add the line 

-----------------------------------------------------------
@code_module_folder = folder = File.dirname(File.expand_path(__FILE__)) # i.e. the directory this file is in
---------------------------------------------------------

to your code module.
		
		" unless rcp.code_module_folder?

# 		(variables+results).each{|v| const_get(:READOUT_LIST).push v} unless READOUT_LIST.size > 0

		#if rcp.variables_0_5_0
			#rcp.variables_0_5_0.dup.each do |par, info| #for backwards compatibility only
				#rcp.variables_0_5_0[par] = info[0] if info.class == Array
			#end
		#end





# 		Log.log(:@@variables0, @@variables[0])

		@run_info = rcp.run_info || [] # Run info can optionally be defined in the code module.
# 		ep @run_info
		@run_info = rcp.run_info + ([:preamble, :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
		@all = (rcp.variables + rcp.results + rcp.run_info) #.each{|v| ALL.push v}
# 		ep "GOT HERE"
		(@all + [:directory, :run_name, :modlet, :relative_directory]).each{|var| send(:attr_accessor, var)}
		#eputs "Checking and updating"
    @user_defaults_location = ENV['HOME'] + "/.coderunner/#{rcp.code}crmod/defaults_files"
		#eputs ' user_defaults_location', rcp.user_defaults_location
		define_method(:output_file) do 
			return @output_file if @output_file
			@output_file = super()
		end
		define_method(:error_file) do 
			return @error_file if @error_file
			@error_file = super()
		end

		Dir.chdir(SCRIPT_FOLDER + "/system_modules") do 
		@system_run_classes ||= 	
			Dir.entries(Dir.pwd).find_all{|file| file =~ /^[^\.].+\.rb$/}.inject([]) do |arr, file|
				#p Dir.pwd
				#p 'required', file, Dir.pwd
				require Dir.pwd + '/' + file
# 				p CodeRunner.constants
				sys = file.sub(/\.rb$/, '')
				arr.push(add_a_child_class("#{sys.variable_to_class_name}Run"))
				arr[-1].send(:include, CodeRunner.const_get(sys.variable_to_class_name))
				arr
			end
		end

		
end

.finish_setting_up_classObject

A hook for module developers



746
747
# File 'lib/coderunner/run.rb', line 746

def self.finish_setting_up_class
end

.getsObject



53
54
55
# File 'lib/coderunner/run.rb', line 53

def self.gets
	$stdin.gets
end

.load(dir, runner) ⇒ Object

Load the run object from the file .code_runner_run_data

Raises:



379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
# File 'lib/coderunner/run.rb', line 379

def self.load(dir, runner)
	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"
	begin
		raise CRMild.new("No saved run data") unless FileTest.exist? (dir+"/.code_runner_run_data")
		run = Marshal.load(File.read(dir+"/.code_runner_run_data"))
	rescue ArgumentError => err
		raise err unless err.message =~ /undefined class/
		#NB this means that all code_names have to contain only lowercase letters:
# 		code, modlet = err.message.scan(/CodeRunner\:\:([A-Z][a-z0-9]+)((?:[A-Z]\w+)+)?Run/)[0]
# 		code, modlet = err.message.scan(/CodeRunner\:\:([A-Z][a-z0-9_]+)(?:::([A-Z]\w+))?/)[0]
# 		ep code, modlet; exit
# 		code.gsub!(/([a-z0-9])([A-Z])/, '\1_\2')
# 		modlet.gsub!(/([a-z0-9])([A-Z])/, '\1_\2')
# 		ep err, modlet, code
# 		runner.setup_run_class(code.downcase, modlet: modlet.downcase)
# 		retry
		CodeRunner.repair_marshal_run_class_not_found_error(err)
		run = Marshal.load(File.read(dir+"/.code_runner_run_data"))		
	end
	run.runner = runner
	raise CRFatal.new("Something has gone horribly wrong: runner.class is #{run.runner.class} instead of CodeRunner") unless run.runner.class.to_s == "CodeRunner"
	run.directory = dir
	run.phantom_runs.each{|r| runner.add_phantom_run(r)} if run.phantom_runs
	#@phantom_runs = []
	return run
end

.modify_job_script(runner, runs, script) ⇒ Object

A hook… default is to do nothing



1026
1027
1028
# File 'lib/coderunner/run.rb', line 1026

def self.modify_job_script(runner, runs, script)
	return script
end

.rcpObject

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!)).

E.g. puts rcp.variables



146
147
148
# File 'lib/coderunner/run.rb', line 146

def self.rcp
	@rcp ||= RunClassPropertyFetcher.new(self)
end

.set_modlet(modlet, folder = nil) ⇒ Object



709
710
711
712
713
714
# File 'lib/coderunner/run.rb', line 709

def self.set_modlet(modlet, folder = nil)
	raise 'old method -- needs to be debugged'
	Log.log("self.set_modlet", self.to_s)
	class_eval(File.read("#{folder ? folder + "/" : ""}#{modlet}"))
	check_and_update
end

.update_status(runner) ⇒ Object

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



39
40
41
42
43
44
45
46
47
# File 'lib/coderunner/run.rb', line 39

def self.update_status(runner)
	if runner.no_run
		@@current_status =  ""
	else
		#eputs 'Getting queue status...'
		@@current_status = new(runner).queue_status
	end
# 	puts @@current_status
end

Instance Method Details

#analyse_input_file_text(input_file_text) ⇒ Object



37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/coderunner/heuristic_run_methods.rb', line 37

def analyse_input_file_text(input_file_text)
	logf(:analyse_input_file_text)
	pars = self.class.analyse_input_file_text(input_file_text, rcp.matching_regex)[0]
# 	log("These pars were found in the input file"); logi(pars)
 	logi(rcp.variables[rcp.variables.keys[0]])
	pars.each do |id, hash|
#  		puts hash.inspect
# 		puts @@variables[hash[:name].to_sym].inspect
		if rcp.variables[hash[:name].to_sym]
			set(hash[:name].to_sym, hash[:default].send(rcp.variables[hash[:name].to_sym]))
			# @@variables[hash[:name].to_sym] is the type conversion appropriate for the variable hash[:name]
		end
	end
end

#cacheObject

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.

E.g. cache = something_to_store



82
83
84
85
86
# File 'lib/coderunner/run.rb', line 82

def cache
	@runner.cache[:runs] ||= {} 
	@runner.cache[:runs][@id] ||= {} 
	@runner.cache[:runs][@id]
end

#code_run_environmentObject

A hook… a string which gets put into the job script. Used to load modules, configure the run time environment for a given code. Default is to return an empty string.



1035
1036
1037
# File 'lib/coderunner/run.rb', line 1035

def code_run_environment
	""
end

#comment_lineObject

Used for the status with comments command.



355
356
357
358
# File 'lib/coderunner/run.rb', line 355

def comment_line
	"#{id}: #{@comment}"
	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)
end

#create_phantomObject



844
845
846
847
848
849
850
851
852
# File 'lib/coderunner/run.rb', line 844

def create_phantom
	@phantom_runs ||= []
	new_run = dup
	new_run.is_phantom = true
	new_run.real_id = @id
	@runner.add_phantom_run(new_run)
	@phantom_runs.push new_run
	new_run
end

#data_stringObject

Return a line of data for printing to file in CodeRunner#readout, organised according to the run class property rcp.readout_list



349
350
351
# File 'lib/coderunner/run.rb', line 349

def data_string
	rcp.readout_list.inject(""){|str,var| str+"#{send(var)}code_runner_spacer"}.gsub(/\s/, '_').gsub(/code_runner_spacer/, "\t") + "\n" 
end

#defaults_file_nameObject

Return the name of the defaults file currently in use.



408
409
410
411
412
413
414
# File 'lib/coderunner/run.rb', line 408

def defaults_file_name
	if @runner.defaults_file
		return "#{@runner.defaults_file}_defaults.rb"
	else
		return "#{rcp.code}_defaults.rb"
	end
end

#defaults_locationObject

Return the folder where the default defaults file is located.



418
419
420
421
422
423
424
425
426
427
428
# File 'lib/coderunner/run.rb', line 418

def defaults_location
	#if @runner.defaults_file
		location = [rcp.user_defaults_location, rcp.code_module_folder	+ "/defaults_files"].find{|folder| FileTest.exist? folder and Dir.entries(folder).include? defaults_file_name}
		#raise "Defaults file: #{defaults_file_name} not found" unless location
		raise "Can't find defaults_file #{defaults_file_name} in #{[rcp.user_defaults_location, rcp.code_module_folder	+ "/defaults_files"].join(',')}." unless location
		location
		#return location
	#else
		#location = [rcp.user_defaults_location, rcp.code_module_folder	+ "/defaults_files"].find{|folder| FileTest.exist? folder and Dir.entries(folder).include? defaults_file_name}
	#end
end

#dupObject



840
841
842
# File 'lib/coderunner/run.rb', line 840

def dup
	return self.class.new(@runner).learn_from(self)
end

#error(message) ⇒ Object



1046
1047
1048
# File 'lib/coderunner/run.rb', line 1046

def error(message)
	raise("Error: " + message)
end

#evaluate_defaults_file(filename) ⇒ Object

Update instance variables using the given defaults file. Give warnings if the defaults file contains variables which are not simulation input parameters



452
453
454
455
456
457
458
459
460
461
462
# File 'lib/coderunner/run.rb', line 452

def evaluate_defaults_file(filename)
	text = File.read(filename)
	text.scan(/^\s*@(\w+)/) do
		var_name = $~[1].to_sym
		next if var_name == :defaults_file_description
		unless rcp.variables.include? var_name
			warning("---#{var_name}---, specified in #{File.expand_path(filename)}, is not a variable. This could be an error")
		end
	end
	instance_eval(text)
end

#executable_locationObject



993
994
995
# File 'lib/coderunner/run.rb', line 993

def executable_location
	File.dirname(@executable||=@runner.executable)
end

#execute_submissionObject



534
535
536
537
538
539
540
541
542
543
# File 'lib/coderunner/run.rb', line 534

def execute_submission
	if @runner.test_submission
		log 'testing submission'
		eputs info_file
		File.delete(@runner.root_folder + "/submitting")
		exit
	else
		execute
	end
end

#generate_combined_ids(type) ⇒ Object

Raises:



896
897
898
# File 'lib/coderunner/run.rb', line 896

def generate_combined_ids(type)
	raise CRFatal.new("Can't call generate_combined_ids from a run")
end

#generate_phantom_runsObject



893
894
# File 'lib/coderunner/run.rb', line 893

def generate_phantom_runs
end

#generate_run_nameObject



660
661
662
663
664
665
# File 'lib/coderunner/run.rb', line 660

def generate_run_name
		@run_name = %[v#@version] + @naming_pars.inject("") do |str, par|
			str+="_#{par}_#{send(par).to_s[0...8]}"
		end
		@run_name = @run_name.gsub(/\s+/, "_").gsub(/\//, '') + "_id_#@id"
end

#getsObject

No reading from the command line thank you very much!



50
51
52
# File 'lib/coderunner/run.rb', line 50

def gets #No reading from the command line thank you very much!
	$stdin.gets
end

#hard_cacheObject

The hard cache persists after the current program ceases because it is written in the .code_runner_run_data file. It will disappear if the -a or -A flags are specified at any point If you edit the hard cache you ”must” call save afterwards or your changes may not be kept



93
94
95
96
# File 'lib/coderunner/run.rb', line 93

def hard_cache
	@hard_cache ||={}
	@hard_cache
end

#info_fileObject

private :write_info



673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
# File 'lib/coderunner/run.rb', line 673

def info_file
		@modlet = rcp.modlet if rcp.modlet?
		return <<EOF
# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
# #{rcp.code_long} Input Parameters
# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
# Code: 		#{rcp.code}
# System: 	#{@sys}
# Version:	#{@version}
# Nprocs: 	#{@nprocs}
# Directory:	#{Dir.pwd}
# Runname:	#{@run_name}
# ID:		#{@id}
# #{@modlet ? "Modlet:\t#@modlet" : ""} 
# Classname:	#{self.class.to_s}

# #{@job_no ? "Job_No:		#{@job_no}" : ""}

# Parameters:
#{(rcp.variables + rcp.run_info + [:version, :code, :modlet, :sys] - [:phantom_runs]).inject({}){|hash, var| hash[var] = send(var) unless (!send(var) and send(var) ==  nil); hash}.pretty_inspect}


# Actual Command:
# #{run_command.gsub(/\n/, "\n#")}
# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
EOF

end

#inspectObject

aliold :inspect



717
718
719
720
721
722
# File 'lib/coderunner/run.rb', line 717

def inspect
	old, @runner = @runner, nil
	str = super
	@runner = old
	str
end

#is_completeObject Also known as: ctd

Return true if the run is completed, false otherwise



432
433
434
# File 'lib/coderunner/run.rb', line 432

def is_complete
	@status == :Complete
end

#job_identifierObject

This function is a hook which is used by system modules. For runs it is defined as the id.



440
441
442
# File 'lib/coderunner/run.rb', line 440

def job_identifier
	id
end

#learn_from(run) ⇒ Object



854
855
856
857
858
859
860
861
862
863
864
865
866
# File 'lib/coderunner/run.rb', line 854

def learn_from(run)
	run.instance_variables.each do |var|	
#   		puts var
#  		puts run.instance_variable_get(var)
			instance_variable_set(var,run.instance_variable_get(var)) 
#  				puts instance_variable_get(var)

# 		rescue NoMethodError
# 			next
# 		end
	end
	self
end

#logivObject



871
872
873
874
875
876
877
# File 'lib/coderunner/run.rb', line 871

def logiv
	instance_variables.each do |var|
		unless var == :@runner or var == :@system_triers
			log(var); logi(instance_variable_get(var))
		end
	end
end

#max(variable, complete = false) ⇒ Object

@@maxes = {} @@cmaxes = {}

Raises:

  • (ArgumentError)


903
904
905
906
907
908
909
910
911
912
913
914
# File 'lib/coderunner/run.rb', line 903

def max(variable, complete=false) #does this run have the maximum value of this variable
	raise ArgumentError.new("complete must be true or false") unless [TrueClass, FalseClass].include? complete.class
	@runner.generate_combined_ids
	ids = @runner.combined_ids 
	if complete
		ids = ids.find_all{|id| @runner.combined_run_list[id].status == :Complete}
		max_id = @runner.cmaxes[variable] ||= ids.sort_by{|id| @runner.combined_run_list[id].send(variable)}[-1]
	else
		max_id = @runner.maxes[variable] ||= ids.sort_by{|id| @runner.combined_run_list[id].send(variable)}[-1]
	end
	return @runner.combined_run_list[max_id].send(variable) == send(variable)
end

#min(variable, complete = false) ⇒ Object

does this run have the minimum value of this variable

Raises:

  • (ArgumentError)


920
921
922
923
924
925
926
927
928
929
930
931
932
# File 'lib/coderunner/run.rb', line 920

def min(variable, complete=false) #does this run have the minimum value of this variable
	raise ArgumentError.new("complete must be true or false") unless [TrueClass, FalseClass].include? complete.class
	@runner.generate_combined_ids
	ids = @runner.combined_ids 
	
	if complete
		ids = ids.find_all{|id| @runner.combined_run_list[id].status == :Complete}
		min_id = @runner.cmins[variable] ||= ids.sort_by{|id| @runner.combined_run_list[id].send(variable)}[0]
	else
		min_id = @runner.mins[variable] ||= ids.sort_by{|id| @runner.combined_run_list[id].send(variable)}[0]
	end
	return @runner.combined_run_list[min_id].send(variable) == send(variable)
end

#p(*args) ⇒ Object

Here we redefine the inspect function p to raise an error if anything is written to standard out while in server mode. This is because the server mode uses standard out to communicate and writing to standard out can break it. All messages, debug information and so on, should always be written to standard error.



183
184
185
186
187
188
189
# File 'lib/coderunner/run.rb', line 183

def p(*args)
	if @runner and @runner.server
		raise "Writing to stdout in server mode will break things!"
	else
		super(*args)
	end
end

#prepare_submission(options = {}) ⇒ Object

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.



618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
# File 'lib/coderunner/run.rb', line 618

def prepare_submission(options={})
	raise "Test Submit Error Handling" if @run_test_flags and @run_test_flags[:test_submit_error_handling]
	
	#p '@nprocs', @nprocs
	if @runner
		SUBMIT_OPTIONS.each do |option|
				set(option, @runner.send(option)) if @runner.send(option)
		end
	end
	#p '@nprocs', @nprocs
	
# 		puts send(:q)

	raise CRFatal.new ("Can't find executable: #{executable_location}/#{executable_name}") unless FileTest.exist? File.expand_path("#{executable_location}/#{executable_name}")

	@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}
	@naming_pars.delete(:g_exb_start_timestep)
	unless @dir_name # dir_name can be set in advance to change the default directory name
		@dir_name = %[v#@version]
		@dir_name = @dir_name.gsub(/\s+/, "_") + "/id_#@id"
	end
	generate_run_name unless @run_name
	
	@directory = File.expand_path(@dir_name)
	#@relative_directory = File.expand_path(Dir.pwd).sub(File.expand_path(@runner.root_folder) + '/', '')
	#@relative_directory = @directory.sub(File.expand_path(@runner.root_folder) + '/', '')
	@relative_directory = @dir_name

	raise "Directory #@dir_name contains code_runner_info" if FileTest.exist? @directory and Dir.entries(@directory).include? ["code_runner_info.rb"]

	@job_no = nil
	FileUtils.makedirs(@dir_name)
	Dir.chdir(@dir_name) do 
		generate_input_file
		
		File.open(".code_runner_version.txt", 'w'){|file| file.puts CODE_RUNNER_VERSION}
		#File.open("code_runner_modlet.rb", 'w'){|file| file.puts rcp.modlet_source} if rcp.modlet_required
	end
	@dir_name = nil

end

#pretty_print(q) ⇒ Object

aliold :pretty_inspect



726
727
728
729
730
731
732
# File 'lib/coderunner/run.rb', line 726

def pretty_print(q)
	old, @runner = @runner, nil
	str = q.pp_object(self)
	@runner = old
	str

end

Here we redefine the function print to raise an error if anything is written to standard out while in server mode. This is because the server mode uses standard out to communicate and writing to standard out can break it. All messages, debug information and so on, should always be written to standard error.



209
210
211
212
213
214
215
# File 'lib/coderunner/run.rb', line 209

def print(*args)
	if @runner and @runner.server
		raise "Writing to stdout in server mode will break things!"
	else
		super(*args)
	end
end

#process_directoryObject

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 code_runner_info.rb and code_runner_results.rb, and then calls process_directory_code_specific which is defined in the code module.

Raises:



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
# File 'lib/coderunner/run.rb', line 220

def process_directory
	
	# Clear the cache
	@runner.cache[:runs]||={}
	@runner.cache[:runs][@id] = {}
	logf(:process_directory)
	raise CRFatal.new("Something has gone horribly wrong: runner.class is #{@runner.class} instead of CodeRunner") unless @runner.class.to_s == "CodeRunner"

	begin
		@code_runner_version = Version.new(File.read('.code_runner_version.txt'))
		File.read('code_runner_info.rb')
	rescue Errno::ENOENT # version may be less than 0.5.1 when .code_runner_version.txt was introduced
		@code_runner_version = Version.new('0.5.0')
	end
	
	@directory = Dir.pwd
	@relative_directory = File.expand_path(Dir.pwd).sub(File.expand_path(@runner.root_folder) + '/', '')
# 	p @directory
	@readme = nil
	
	#if @code_runner_version < Version.new('0.5.1')
		#begin
			#update_from_version_0_5_0_and_lower
		#rescue Errno::ENOENT => err # No code runner files were found
			#unless @runner.heuristic_analysis
				#puts err
				#raise CRFatal.new("No code runner files found: suggest using heuristic analysis (flag -H if you are using the code_runner script)")
			#end
			#unless @runner.current_request == :traverse_directories
				#@runner.requests.push :traverse_directories unless @runner.requests.include? :traverse_directories
				#raise CRMild.new("can't begin heuristic analysis until there has been a sweep over all directories") # this will get rescued
			#end
			#@runner.increment_max_id	
			#@id = @runner.max_id
			#@job_no = -1
			#run_heuristic_analysis
		#end
	#end
		begin
			read_info
		rescue Errno::ENOENT => err # No code runner files were found
			unless @runner.heuristic_analysis
				puts err
				raise CRFatal.new("No code runner files found: suggest using heuristic analysis (flag -H if you are using the code_runner script)")
			end
			unless @runner.current_request == :traverse_directories
				@runner.requests.push :traverse_directories unless @runner.requests.include? :traverse_directories
				raise CRMild.new("can't begin heuristic analysis until there has been a sweep over all directories") # this will get rescued
			end
			@runner.increment_max_id	
			@id = @runner.max_id
			@job_no = -1
			run_heuristic_analysis
		end
	
	
	begin
		read_results if FileTest.exist? 'code_runner_results.rb'
	rescue NoMethodError, SyntaxError => err
		puts err
		puts 'Results file possibly corrupted for ' + @run_name
	end	

	@running = (@@current_status =~ Regexp.new(@job_no.to_s)) ? true : false 
	if methods.include? :get_run_status
		@status = get_run_status(@job_no, @@current_status) rescue :Unknown
	else
		@status ||= :Unknown
	end
	#logi '@@current_status', @@current_status, '@job_no', @job_no
	#logi '@running', @running
	process_directory_code_specific	
	
	raise CRFatal.new("status must be one of #{PERMITTED_STATI.inspect}") unless PERMITTED_STATI.include? @status
	@max = {}
	write_results
	generate_phantom_runs
	save
	return self
end

#puts(*args) ⇒ Object

Here we redefine the function puts to raise an error if anything is written to standard out while in server mode. This is because the server mode uses standard out to communicate and writing to standard out can break it. All messages, debug information and so on, should always be written to standard error.



196
197
198
199
200
201
202
# File 'lib/coderunner/run.rb', line 196

def puts(*args)
	if @runner and @runner.server
		raise "Writing to stdout in server mode will break things!"
	else
		super(*args)
	end
end

#rcpObject

Calls Run.rcp



152
153
154
# File 'lib/coderunner/run.rb', line 152

def rcp 
	self.class.rcp
end

#read_infoObject

Read input parameters from the file code_runner_info.rb



303
304
305
306
307
# File 'lib/coderunner/run.rb', line 303

def read_info
	eval(File.read('code_runner_info.rb')).each do |key, value|
		set(key, value)
	end
end

#read_resultsObject

Read results from the file code_runner_results.rb



311
312
313
314
315
316
# File 'lib/coderunner/run.rb', line 311

def read_results
	return if @runner.recalc_all
	eval(File.read('code_runner_results.rb')).each do |key, value|
		set(key, value)
	end
end

#recheckObject



879
880
881
882
883
884
885
886
887
888
889
890
891
# File 'lib/coderunner/run.rb', line 879

def recheck
	logf(:recheck)
	Dir.chdir(@directory) do
# 		puts 'ackack'
		puts "Rechecking #@run_name"
		runner = @runner
		instance_variables.each{|var| instance_variable_set(var, nil) unless var == :@runner}
		begin File.delete("CODE_RUNNER_RUN_DATA") rescue Errno::ENOENT end
		begin File.delete("CODE_RUNNER_RESULTS") rescue Errno::ENOENT end
		process_directory
		save
	end
end

#results_fileObject

Return the text of the results file.



327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
# File 'lib/coderunner/run.rb', line 327

def results_file
	logf(:results_file)
	return <<EOF
# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
# #{rcp.code_long} Results
# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#
# This is a syntactically correct Ruby file, which is used by CodeRunner. Please do not edit unless you know what you are doing.
# Directory:	#{@directory}
# Runname:	#{@run_name}
# ID:		#{@id}

# Results:
#{(rcp.results+rcp.run_info - [:phantom_runs]).inject({}){|hash, (var,type_co)| hash[var] = send(var); hash}.pretty_inspect}

# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
EOF

end

#run_heuristic_analysisObject



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/coderunner/heuristic_run_methods.rb', line 5

def run_heuristic_analysis
	
	begin
		
		puts Dir.pwd
		raise CRMild.new("can't find input file") unless @rcp.input_file_extension
		input_file = Dir.entries.find_all{|file| file =~ Regexp.new("^[^.].*"+Regexp.escape(rcp.input_file_extension) + "$")}[0]
		raise CRMild.new("can't find input file") unless input_file
		log(input_file)
		input_file_text = File.read(input_file)
		@run_name = File.basename(input_file, rcp.input_file_extension) if rcp.use_file_name_as_run_name
		analyse_input_file_text(input_file_text)
		log("Automatic analysis complete")
# 				logiv
	rescue CRMild => err
		log(err)
		input_file = Feedback.get_choice("Unable to find a code runner inputs file. If there is another input file please choose it from this list", Dir.entries(Dir.pwd).find_all{|file| not [".",".."].include? file and File.file? file} + ["not available"])
		if input_file == "not available"
			FileUtils.touch('.CODE_RUNNER_IGNORE_THIS_DIRECTORY')
			raise CRError.new("run could be analysed, or folder does not contain a run")
		end
		input_file_text = File.read(input_file)
		log(input_file_text)
		analyse_input_file_text(input_file_text)
# 				logiv
		rcp.input_file_extension = File.extname(input_file)
		@rcp.use_file_name_as_run_name = Feedback.get_boolean("Do you want to use the input file name (minus the extension) as a run name? (Your answer will apply to all directories with code runner inputs files).") if rcp.use_file_name_as_run_name.class == NilClass
		@run_name = File.basename(input_file, rcp.input_file_extension) if rcp.use_file_name_as_run_name
	end
end

#saveObject

Cache the run object in the file .code_runner_run_data

Raises:



362
363
364
365
366
367
368
369
370
371
372
373
374
# File 'lib/coderunner/run.rb', line 362

def save
	logf(:save)
	raise CRFatal.new("Something has gone horribly wrong: runner.class is #{@runner.class} instead of CodeRunner") unless @runner.class.to_s == "CodeRunner"
	runner, @runner = @runner, nil
	@system_triers, old_triers = nil, @system_triers
	@phantom_runs.each{|run| run.runner = nil} if @phantom_runs
	#@phantom_runs.each{|run| run.runner = nil} if @phantom_runs
# 	logi(self)
	Dir.chdir(@directory){File.open(".code_runner_run_data", 'w'){|file| file.puts Marshal.dump(self)}}
	@runner = runner
	@phantom_runs.each{|run| run.runner = runner} if @phantom_runs
	@system_triers = old_triers 
end

#smax(variable, sweep = nil, complete = nil) ⇒ Object

# Does this run have the maximum value of this variable to be found amoung the filtered runs?

def fmax(variable, complete = false) raise ArgumentError.new(“complete must be true or false”) unless [TrueClass, FalseClass].include complete.class @runner.generate_combined_ids ids = @runner.filtered_ids # o^o-¬ if complete ids = ids.find_all|id| @runner.combined_run_list[id].status == :Complete} max_id = @@cfmaxes[variable] ||= ids.sort_by{|id| @runner.combined_run_list[id].send(variable) else max_id = @@fmaxes ||= ids.sort_by|id| @runner.combined_run_list[id].send(variable) end return @runner.combined_run_list.send(variable) == send(variable) end



953
954
955
956
957
958
959
960
961
962
963
964
# File 'lib/coderunner/run.rb', line 953

def smax(variable,sweep=nil, complete=nil)
	logf(:max)
 	sweep ||= variable
	if complete
		@csmaxes[variable] ||= {}
		max_id = @csmaxes[variable][sweep] = @runner.get_max(self, variable,sweep, complete)
	else
		@smaxes[variable] ||= {}
		max_id = @smaxes[variable][sweep] = @runner.get_max(self, variable,sweep, complete)
	end
	return @runner.combined_run_list[max_id].send(variable) == send(variable)
end

#smin(variable, sweep = nil, complete = nil) ⇒ Object



966
967
968
969
970
971
972
973
974
975
976
977
978
979
# File 'lib/coderunner/run.rb', line 966

def smin(variable,sweep=nil, complete=nil)
	logf(:min)
 	sweep ||= variable
	@smins ||= {}
	@csmins ||= {}
	if complete
		@csmins[variable] ||= {}
		min_id = @csmins[variable][sweep] = @runner.get_min(self, variable,sweep, complete)
	else
		@smins[variable] ||= {}
		min_id = @smins[variable][sweep] = @runner.get_min(self, variable,sweep, complete)
	end
	return @runner.combined_run_list[min_id].send(variable) == send(variable)
end

#try_by_system(expected_return = NilClass, &block) ⇒ Object

Raises:



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/coderunner/heuristic_run_methods.rb', line 56

def try_by_system(expected_return=NilClass, &block)
# 	puts "Trying by system"
# 	puts @@system_run_classes[0].new(@runner).inspect
	@@system_triers ||= rcp.system_run_classes.inject({}){|hash, run_class| 
# 		puts run_class.new(@runner).learn_from(self).inspect
		log hash.class
		hash[run_class] = run_class.new(@runner).learn_from(self).freeze
		hash}
# 	puts @system_triers.inspect
# 	i=0
	answer = nil
	if @@successful_trial_class
		begin
			answer = yield(@@system_triers[@@successful_trial_class].dup, self)
			raise CRError.new("trial returned an answer, but answer was not of the right class") unless expected_return == NilClass or answer.is_a? expected_return
			return answer
		rescue => err
			log err
		end
	end

	@@system_triers.values.each do |trier|
		begin	
# 			puts i
# 			i+=1
#  			puts trier.class.ancestors
# 			puts "asld"
			answer = yield(trier.dup, self)
# 			puts expected_return
# 			puts answer.is_a? Fixnum
# 			puts "Sd"
			raise CRError.new("trial returned an answer, but answer was not of the right class") unless expected_return == NilClass or answer.is_a? expected_return 
#  			puts answer, "HASG"
# 			puts "asfd"
			@@successful_trial_system = trier.class.run_sys_name
			@@successful_trial_class = trier.class
# 			puts @@successful_trial_system
			return answer
		rescue Errno::ENOENT, TypeError, CRMild, CRError => err
# 			puts err
			next
		end
	end
# 	puts answer; gets
# 	answer
	raise CRError.new("try by system was not successful")
end

#try_to_find_job_output_endsObject



191
192
193
194
195
196
197
198
# File 'lib/coderunner/heuristic_run_methods.rb', line 191

def try_to_find_job_output_ends
	output = try_to_get_output_file
	return nil unless output
#  	return try_by_system(Fixnum) do |trier, myself|
	found = File.read(output) =~ /job output ends/i
	return found ? true : false
#  	end
end

#try_to_get_error_fileObject



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/coderunner/heuristic_run_methods.rb', line 155

def try_to_get_error_file
	logf(:try_to_get_error_file)
	begin
		error_file =  try_by_system(String) do |trier, myself|
	# 		trier = trier.dup
	# 		puts trier
			trier.executable_name = "mx123456zz"
			trier.job_no = "mx123456yy"
			trier.version = ""
			scanner = Regexp.new("(?<outputfile>"+Regexp.escape(trier.error_file).sub("mx123456zz", ".+").sub("mx123456yy", "\\d+")+")")
			ans = nil
			Dir.entries.each do |file|
				if file =~ scanner
	# 				puts $~[:outputfile] 
					ans = $~[:outputfile] 
				end
			end
			ans
		end
	rescue CRError => err
 		log(err)
		error_file = nil
	end
	log("Error file was: ", error_file)
	if error_file
		begin
			logi(File.readlines(error_file))
			logi(File.readlines(error_file).size)
			logi(File.readlines(error_file).size.class)
		rescue => err
			log(err)
		end
	end
	return error_file
end

#try_to_get_job_numberObject



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/coderunner/heuristic_run_methods.rb', line 105

def try_to_get_job_number
	begin
		job_no = try_by_system(Fixnum) do |trier, myself|	

			trier.executable_name = "mx123456zz"
			trier.job_no = "mx123456yy"
			trier.version = ""

			scanner = Regexp.new(Regexp.escape(trier.output_file).sub("mx123456zz", ".+").sub("mx123456yy", "(?<jobno>\\d+)$"))
			answer = nil
			Dir.entries(Dir.pwd).each do |file|

					return   $~[:jobno].to_i if file =~ scanner
	# 			end
			end

			nil
		end
	rescue CRError => err
		job_no = -1
	end
	job_no
# 	exit
end

#try_to_get_output_fileObject



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/coderunner/heuristic_run_methods.rb', line 130

def try_to_get_output_file
	#very hacky!
	logf(:try_to_get_output_file)
	begin
		out_file =  try_by_system(String) do |trier, myself|
	# 		trier = trier.dup
	# 		puts trier
			trier.executable_name = "mx123456zz"
			trier.job_no = "mx123456yy"
			trier.version = ""
			scanner = Regexp.new("(?<outputfile>"+Regexp.escape(trier.output_file).sub("mx123456zz", ".+").sub("mx123456yy", "\\d+")+")")
# 			ep scanner
			ans = nil
			Dir.entries(Dir.pwd).each do |file|
				ans = $~[:outputfile] if file =~ scanner 
			end
			ans
		end
	rescue CRError => err
		log(err)
		out_file = nil
	end
	out_file
end

#update_in_queueObject



997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
# File 'lib/coderunner/run.rb', line 997

def update_in_queue
	unless @status == :Queueing
		raise 'Can only updated runs whose status is :Queueing'
	end
	unless methods.include? :batch_script_file
		raise 'Can only update runs which have been submitted using a batch script file'
	end	
	old_run_name = @run_name
	generate_run_name
	new_run_name = @run_name
	#@run_name = old_run_name
	unless FileTest.exist?(filename = @directory + '/' + batch_script_file) or 
	 	FileTest.exist?(filename =  @runner.root_folder + '/' + batch_script_file)
			raise 'Could not find batch_script_file'
	end
	old_batch_script=File.read(filename)
	eputs old_batch_script
eputs old_batch_script.gsub(Regexp.new(Regexp.escape(old_run_name)), new_run_name)
	ep Regexp.new(Regexp.escape(old_run_name))
	File.open(filename, 'w'){|file| file.puts old_batch_script.gsub(Regexp.new(Regexp.escape(old_run_name)), new_run_name)}

	
	generate_input_file
	#throw(:done)
	write_info
end

#update_submission_parameters(parameters, start_from_defaults = true) ⇒ Object

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).



466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
# File 'lib/coderunner/run.rb', line 466

def update_submission_parameters(parameters, start_from_defaults=true)
	logf(:update_submission_parameters)
	if start_from_defaults
		#upgrade_defaults_from_0_5_0 if self.class.constants.include? :DEFAULTS_FILE_NAME_0_5_0
		main_defaults_file = "#{defaults_location}/#{defaults_file_name}"
		main_defaults_file_text = 		File.read(main_defaults_file)
		evaluate_defaults_file(main_defaults_file)

		unless FileTest.exist?(defaults_file_name)
			main_defaults_file_text.gsub!(/^/, "#")
			header = <<EOF
#############################################################
# CodeRunner Local Defaults File 
############################################################
#
# This is a local copy of the central defaults file, which 
# was copied from the central defaults file 
#		#{defaults_file_name} 
# 
# to the folder
# 	#{Dir.pwd}
#
# at 
# 	#{Time.now}
#
# by CodeRunner version #{CODE_RUNNER_VERSION}
#
# All lines in the original file have been commented out:
# they are kept as a reference to make adding local defaults easier. 
# It is suggested that local changes are placed at the top 
# of this file, not in the body of the commented out section.
#
# Local changes override the central defaults file. However,
# if the central defaults file changes, any variables which 
# are not overidden here will change for any future simulations
# in this folder.
#
##############################################################






# Begin Copy of Central Defaults:
#
#
EOF
			main_defaults_file_text = header + main_defaults_file_text
			File.open(defaults_file_name, 'w'){|file| file.puts main_defaults_file_text}
		end
		#FileUtils.cp("#{defaults_location}/#{defaults_file_name}", defaults_file_name) 
		
		evaluate_defaults_file(defaults_file_name)
	end
	return unless parameters
	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/
	@parameter_hash = parameters 
	parameters.each do |var, value|
		raise CRFatal.new('Cannot specify id as a parameter') if var.to_sym == :id
		set(var, value) unless value == :default
		next if [:comment].include? var
		@naming_pars.push var
	end
	@naming_pars.uniq!
	self
end

#warning(message) ⇒ Object



1039
1040
1041
# File 'lib/coderunner/run.rb', line 1039

def warning(message)
	eputs "Warning: " + message; sleep 0.3
end

#write_infoObject



667
668
669
# File 'lib/coderunner/run.rb', line 667

def write_info
	Dir.chdir(@directory){File.open("code_runner_info.rb", 'w'){|file| file.puts info_file}}
end

#write_resultsObject

Write results to the file code_runner_results.rb



320
321
322
323
# File 'lib/coderunner/run.rb', line 320

def write_results
	logf(:write_results)
	Dir.chdir(@directory){File.open("code_runner_results.rb", 'w'){|file| file.puts results_file}}
end