openstudio-workflow 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (24) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/README.md +39 -2
  4. data/Rakefile +12 -1
  5. data/lib/openstudio-workflow.rb +31 -4
  6. data/lib/openstudio/workflow/adapter.rb +8 -9
  7. data/lib/openstudio/workflow/adapters/local.rb +35 -22
  8. data/lib/openstudio/workflow/adapters/mongo.rb +82 -92
  9. data/lib/openstudio/workflow/jobs/lib/apply_measures.rb +229 -0
  10. data/lib/openstudio/workflow/jobs/run_energyplus/run_energyplus.rb +7 -10
  11. data/lib/openstudio/workflow/jobs/run_openstudio/run_openstudio.rb +37 -159
  12. data/lib/openstudio/workflow/jobs/run_postprocess/run_postprocess.rb +53 -492
  13. data/lib/openstudio/workflow/jobs/run_preflight/run_preflight.rb +1 -5
  14. data/lib/openstudio/workflow/jobs/{run_postprocess → run_reporting_measures}/packaged_measures/README.md +0 -0
  15. data/lib/openstudio/workflow/jobs/{run_postprocess → run_reporting_measures}/packaged_measures/StandardReports/measure.rb +81 -87
  16. data/lib/openstudio/workflow/jobs/{run_postprocess → run_reporting_measures}/packaged_measures/StandardReports/measure.xml +1 -1
  17. data/lib/openstudio/workflow/jobs/{run_postprocess/packaged_measures/StandardReports/resources/report.html.in → run_reporting_measures/packaged_measures/StandardReports/resources/report.html.erb} +0 -0
  18. data/lib/openstudio/workflow/jobs/run_reporting_measures/run_reporting_measures.rb +548 -0
  19. data/lib/openstudio/workflow/jobs/run_runmanager/run_runmanager.rb +226 -0
  20. data/lib/openstudio/workflow/jobs/run_xml/run_xml.rb +39 -41
  21. data/lib/openstudio/workflow/multi_delegator.rb +6 -6
  22. data/lib/openstudio/workflow/run.rb +95 -39
  23. data/lib/openstudio/workflow/version.rb +1 -1
  24. metadata +9 -6
@@ -0,0 +1,226 @@
1
+ ######################################################################
2
+ # Copyright (c) 2008-2014, Alliance for Sustainable Energy.
3
+ # All rights reserved.
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License as published by the Free Software Foundation; either
8
+ # version 2.1 of the License, or (at your option) any later version.
9
+ #
10
+ # This library is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ # Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public
16
+ # License along with this library; if not, write to the Free Software
17
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
+ ######################################################################
19
+
20
+ require 'openstudio'
21
+
22
+ # TODO: I hear that measures can step on each other if not run in their own directory
23
+ class RunRunmanager
24
+ # Mixin the MeasureApplication module to apply measures
25
+ include OpenStudio::Workflow::ApplyMeasures
26
+
27
+ # Initialize
28
+ # param directory: base directory where the simulation files are prepared
29
+ # param logger: logger object in which to write log messages
30
+ def initialize(directory, logger, adapter, options = {})
31
+ energyplus_path = nil
32
+ if /cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM
33
+ energyplus_path = 'C:/EnergyPlus-8-1-0'
34
+ else
35
+ energyplus_path = '/usr/local/EnergyPlus-8-1-0'
36
+ end
37
+
38
+ defaults = {
39
+ analysis_root_path: '.',
40
+ energyplus_path: energyplus_path
41
+ }
42
+ @options = defaults.merge(options)
43
+
44
+ @analysis_root_path = OpenStudio::Path.new(options[:analysis_root_path])
45
+ @directory = OpenStudio::Path.new(directory)
46
+ # TODO: there is a base number of arguments that each job will need including @run_directory. abstract it out.
47
+ @run_directory = @directory / OpenStudio::Path.new('run')
48
+ @adapter = adapter
49
+ @results = {}
50
+ @logger = logger
51
+ @logger.info "#{self.class} passed the following options #{@options}"
52
+
53
+ # initialize instance variables that are needed in the perform section
54
+ @model = nil
55
+ @model_idf = nil
56
+ @initial_weather_file = nil
57
+ @weather_file_path = nil
58
+ @analysis_json = nil
59
+ # TODO: rename datapoint_json to just datapoint
60
+ @datapoint_json = nil
61
+ @output_attributes = {}
62
+ @report_measures = []
63
+ end
64
+
65
+ def perform
66
+ @logger.info "Calling #{__method__} in the #{self.class} class"
67
+ @logger.info "Current directory is #{@directory}"
68
+
69
+ begin
70
+
71
+ @logger.info 'Retrieving datapoint and problem'
72
+ @datapoint_json = @adapter.get_datapoint(@directory.to_s, @options)
73
+ @analysis_json = @adapter.get_problem(@directory.to_s, @options)
74
+
75
+ # @results[:weather_filename]
76
+ # File.open("#{@run_directory}/measure_attributes.json", 'w') do
77
+ # |f| f << JSON.pretty_generate(@output_attributes)
78
+ # end
79
+
80
+ if @analysis_json && @datapoint_json
81
+
82
+ if @analysis_json[:openstudio_version].nil?
83
+ @logger.info 'analysis_json missing openstudio_version'
84
+ if @analysis_json[:analysis] && @analysis_json[:analysis][:openstudio_version]
85
+ version = @analysis_json[:analysis][:openstudio_version]
86
+ @logger.info "setting analysis_json openstudio_version to '#{version}'"
87
+ @analysis_json[:openstudio_version] = version
88
+ end
89
+ end
90
+
91
+ if @datapoint_json[:openstudio_version].nil?
92
+ @logger.info 'datapoint_json missing openstudio_version'
93
+ if @analysis_json[:analysis] && @analysis_json[:analysis][:openstudio_version]
94
+ version = @analysis_json[:analysis][:openstudio_version]
95
+ @logger.info "setting datapoint_json openstudio_version to '#{version}'"
96
+ @datapoint_json[:openstudio_version] = version
97
+ end
98
+ end
99
+
100
+ # set up log file
101
+ logSink = OpenStudio::FileLogSink.new(@run_directory / OpenStudio::Path.new('openstudio.log'))
102
+ # logSink.setLogLevel(OpenStudio::Debug)
103
+ logSink.setLogLevel(OpenStudio::Trace)
104
+ OpenStudio::Logger.instance.standardOutLogger.disable
105
+
106
+ @logger.info 'Parsing Analysis JSON input'
107
+
108
+ # load problem formulation
109
+ loadResult = OpenStudio::Analysis.loadJSON(JSON.pretty_generate(@analysis_json))
110
+ if loadResult.analysisObject.empty?
111
+ loadResult.errors.each { |error|
112
+ @logger.warn error.logMessage # DLM: is this right?
113
+ }
114
+ fail 'Unable to load analysis json.'
115
+ end
116
+
117
+ @logger.info 'Get Analysis From OpenStudio'
118
+ analysis = loadResult.analysisObject.get.to_Analysis.get
119
+
120
+ # fix up paths
121
+ @logger.info 'Fix Paths'
122
+ analysis.updateInputPathData(loadResult.projectDir, @analysis_root_path)
123
+
124
+ # save for reference only
125
+ analysis_options = OpenStudio::Analysis::AnalysisSerializationOptions.new(@analysis_root_path)
126
+ analysis.saveJSON(@run_directory / OpenStudio::Path.new('formulation_final.json'), analysis_options, true)
127
+
128
+ @logger.info 'Parsing DataPoint JSON input'
129
+
130
+ # load data point to run
131
+ loadResult = OpenStudio::Analysis.loadJSON(JSON.pretty_generate(@datapoint_json))
132
+ if loadResult.analysisObject.empty?
133
+ loadResult.errors.each { |error|
134
+ @logger.warn error.logMessage
135
+ }
136
+ fail 'Unable to load data point json.'
137
+ end
138
+ data_point = loadResult.analysisObject.get.to_DataPoint.get
139
+ analysis.addDataPoint(data_point) # also hooks up real copy of problem
140
+
141
+ @logger.info 'Creating RunManager'
142
+
143
+ # create a RunManager
144
+ run_manager_path = @run_directory / OpenStudio::Path.new('run.db')
145
+ run_manager = OpenStudio::Runmanager::RunManager.new(run_manager_path, true, false, false)
146
+
147
+ # have problem create the workflow
148
+ @logger.info 'Creating Workflow'
149
+ workflow = analysis.problem.createWorkflow(data_point, OpenStudio::Path.new($OpenStudio_Dir))
150
+ params = OpenStudio::Runmanager::JobParams.new
151
+ params.append('cleanoutfiles', 'standard')
152
+ workflow.add(params)
153
+
154
+ tools = OpenStudio::Runmanager::ConfigOptions.makeTools(OpenStudio::Path.new(@options[:energyplus_path]),
155
+ OpenStudio::Path.new,
156
+ OpenStudio::Path.new,
157
+ $OpenStudio_RubyExeDir,
158
+ OpenStudio::Path.new)
159
+ workflow.add(tools)
160
+ # DLM: Elaine somehow we need to add info to data point to avoid this error:
161
+ # [openstudio.analysis.AnalysisObject] <1> The json string cannot be parsed as an
162
+ # OpenStudio analysis framework json file, because Unable to find ToolInfo object
163
+ # at expected location.
164
+
165
+ # queue the RunManager job
166
+ @logger.info 'Queue RunManager Job'
167
+ url_search_paths = OpenStudio::URLSearchPathVector.new
168
+ weather_file_path = OpenStudio::Path.new
169
+ if analysis.weatherFile
170
+ weather_file_path = analysis.weatherFile.get.path
171
+ end
172
+ job = workflow.create(@run_directory, analysis.seed.path, weather_file_path, url_search_paths)
173
+ OpenStudio::Runmanager::JobFactory.optimizeJobTree(job)
174
+ analysis.setDataPointRunInformation(data_point, job, OpenStudio::PathVector.new)
175
+ run_manager.enqueue(job, false)
176
+
177
+ @logger.info 'Waiting for simulation to finish'
178
+
179
+ if false
180
+ # Get some introspection on what the current running job is. For now just
181
+ # look at the directories that are being generated
182
+ job_dirs = []
183
+ while run_manager.workPending
184
+ sleep 1
185
+ OpenStudio::Application.instance.processEvents
186
+
187
+ # check if there are any new folders that were creates
188
+ temp_dirs = Dir[File.join(@run_directory.to_s, '*/')].map { |d| d.split('/').pop }.sort
189
+ if (temp_dirs + job_dirs).uniq != job_dirs
190
+ @logger.info "#{(temp_dirs - job_dirs).join(',')}"
191
+ job_dirs = temp_dirs
192
+ end
193
+ end
194
+ else
195
+ run_manager.waitForFinished
196
+ end
197
+
198
+ @logger.info 'Simulation finished'
199
+
200
+ # use the completed job to populate data_point with results
201
+ @logger.info 'Updating OpenStudio DataPoint object'
202
+ analysis.problem.updateDataPoint(data_point, job)
203
+
204
+ @logger.info data_point
205
+
206
+ @results = { pat_data_point: ::MultiJson.load(data_point.toJSON, symbolize_names: true) }
207
+
208
+ # Savet this to the directory for debugging purposes
209
+ File.open("#{@run_directory}/data_point_result.json", 'w') { |f| f << MultiJson.dump(@results, pretty: true) }
210
+
211
+ fail 'Simulation Failed' if data_point.failed
212
+ else
213
+ fail 'Could not find analysis_json and datapoint_json'
214
+ end
215
+
216
+ rescue => e
217
+ log_message = "#{__FILE__} failed with #{e.message}, #{e.backtrace.join("\n")}"
218
+ @logger.error log_message
219
+
220
+ # do not raise as the results will never end up in the datapoint
221
+ # raise log_message
222
+ end
223
+
224
+ @results
225
+ end
226
+ end
@@ -21,11 +21,9 @@ require 'libxml'
21
21
 
22
22
  # This actually belongs as another class that gets added as a state dynamically
23
23
  class RunXml
24
-
25
- CRASH_ON_NO_WORKFLOW_VARIABLE = FALSE
26
24
  # RunXml
27
25
  def initialize(directory, logger, adapter, options = {})
28
- defaults = {use_monthly_reports: false, analysis_root_path: '.', xml_library_file: 'xml_runner.rb'}
26
+ defaults = { use_monthly_reports: false, analysis_root_path: '.', xml_library_file: 'xml_runner.rb' }
29
27
  @options = defaults.merge(options)
30
28
  @directory = directory
31
29
  # TODO: there is a base number of arguments that each job will need including @run_directory. abstract it out.
@@ -37,7 +35,7 @@ class RunXml
37
35
 
38
36
  # initialize instance variables that are needed in the perform section
39
37
  @weather_filename = nil
40
- @weather_directory = File.expand_path(File.join(@options[:analysis_root_path], "weather"))
38
+ @weather_directory = File.expand_path(File.join(@options[:analysis_root_path], 'weather'))
41
39
  @logger.info "Weather directory is: #{@weather_directory}"
42
40
  @model_xml = nil
43
41
  @model = nil
@@ -48,18 +46,17 @@ class RunXml
48
46
  @output_attributes = {}
49
47
  @report_measures = []
50
48
  @measure_type_lookup = {
51
- :openstudio_measure => 'RubyMeasure',
52
- :energyplus_measure => 'EnergyPlusMeasure',
53
- :reporting_measure => 'ReportingMeasure'
49
+ openstudio_measure: 'RubyMeasure',
50
+ energyplus_measure: 'EnergyPlusMeasure',
51
+ reporting_measure: 'ReportingMeasure'
54
52
  }
55
53
  end
56
54
 
57
-
58
55
  def perform
59
56
  @logger.info "Calling #{__method__} in the #{self.class} class"
60
57
  @logger.info "Current directory is #{@directory}"
61
58
 
62
- @logger.info "Retrieving datapoint and problem"
59
+ @logger.info 'Retrieving datapoint and problem'
63
60
  @datapoint_json = @adapter.get_datapoint(@directory, @options)
64
61
  @analysis_json = @adapter.get_problem(@directory, @options)
65
62
 
@@ -67,12 +64,18 @@ class RunXml
67
64
  @model_xml = load_xml_model
68
65
  @weather_filename = load_weather_file
69
66
 
70
- apply_xml_measures
67
+ begin
68
+ apply_xml_measures
69
+ rescue => e
70
+ log_message = "Exception during 'apply_xml_measure' with #{e.message}, #{e.backtrace.join("\n")}"
71
+ raise log_message
72
+ end
71
73
 
72
- @logger.info "XML measure output attributes JSON is #{@output_attributes}"
73
- File.open("#{@run_directory}/measure_attributes_xml.json", 'w') {
74
- |f| f << JSON.pretty_generate(@output_attributes)
75
- }
74
+ # @logger.debug "XML measure output attributes JSON is #{@output_attributes}"
75
+ File.open("#{@run_directory}/measure_attributes_xml.json", 'w') do
76
+ |f|
77
+ f << JSON.pretty_generate(@output_attributes)
78
+ end
76
79
  end
77
80
 
78
81
  create_osm_from_xml
@@ -126,7 +129,6 @@ class RunXml
126
129
  @logger.warn "Could not find weather file for simulation #{weather_filename}. Will continue because may change"
127
130
  end
128
131
 
129
-
130
132
  else
131
133
  fail 'No weather file path defined'
132
134
  end
@@ -143,18 +145,20 @@ class RunXml
143
145
  @model_xml.save(xml_filename)
144
146
 
145
147
  @logger.info 'Starting XML to OSM translation'
148
+ begin
149
+ # set the lib path first -- very specific for this application right now
150
+ @space_lib_path = File.expand_path("#{File.dirname(@options[:xml_library_file])}/space_types")
151
+ require @options[:xml_library_file]
146
152
 
147
- # set the lib path first -- very specific for this applciation right now
148
- @space_lib_path = File.expand_path("#{File.dirname(@options[:xml_library_file])}/space_types")
149
- require @options[:xml_library_file]
153
+ @logger.info "The weather file is #{@weather_filename}"
150
154
 
151
- @logger.info "The weather file is #{@weather_filename}"
152
- begin
153
155
  osxt = Main.new(@weather_directory, @space_lib_path)
154
- osm, idf, new_xml, building_name, weather_file = osxt.process(@model_xml.to_s, false, true)
155
- rescue Exception => e
156
+ # def process(as_xml, ideal_loads=false, optimized_model=false, return_objects=false)
157
+ osm, idf, new_xml, building_name, weather_file = osxt.process(@model_xml.to_s, false, false, true)
158
+ # return [model, idf_model, zones_xml, building_name, weather_file]
159
+ rescue => e
156
160
  log_message = "Runner error #{__FILE__} failed with #{e.message}, #{e.backtrace.join("\n")}"
157
- fail log_message
161
+ raise log_message
158
162
  end
159
163
 
160
164
  if osm
@@ -168,7 +172,7 @@ class RunXml
168
172
 
169
173
  @results[:osm_filename] = File.expand_path(osm_filename)
170
174
  @results[:xml_filename] = File.expand_path(xml_filename)
171
- @results[:weather_filename] = File.expand_path(File.join(@weather_directory,@weather_filename))
175
+ @results[:weather_filename] = File.expand_path(File.join(@weather_directory, @weather_filename))
172
176
  end
173
177
 
174
178
  def apply_xml_measures
@@ -217,12 +221,12 @@ class RunXml
217
221
  if wf[:variables]
218
222
  wf[:variables].each do |wf_var|
219
223
  # Argument hash in workflow looks like the following
220
- # "argument": {
221
- # "display_name": "Window-To-Wall Ratio",
222
- # "machine_name": "window_to_wall_ratio",
223
- # "name": "value",
224
- # "uuid": "a0618d15-bb0b-4494-a72f-8ad628693a7e",
225
- # "version_uuid": "b33cf6b0-f1aa-4706-afab-9470e6bd1912"
224
+ # argument: {
225
+ # display_name: "Window-To-Wall Ratio",
226
+ # display_name_short: "Window-To-Wall Ratio",
227
+ # name: "value",
228
+ # value_type: "double",
229
+ # uuid: "27909cb0-f8c9-0131-9b05-14109fdf0b37"
226
230
  # },
227
231
  variable_uuid = wf_var[:uuid].to_sym # this is what the variable value is set to
228
232
  if wf_var[:argument]
@@ -231,11 +235,11 @@ class RunXml
231
235
  # Get the value from the data point json that was set via R / Problem Formulation
232
236
  if @datapoint_json[:data_point]
233
237
  if @datapoint_json[:data_point][:set_variable_values]
234
- if @datapoint_json[:data_point][:set_variable_values][variable_uuid]
235
- @logger.info "Setting variable #{variable_name} to #{@datapoint_json[:data_point][:set_variable_values][variable_uuid]}"
238
+ unless @datapoint_json[:data_point][:set_variable_values][variable_uuid].nil?
239
+ @logger.info "Setting variable '#{variable_name}' to '#{@datapoint_json[:data_point][:set_variable_values][variable_uuid]}'"
236
240
 
237
241
  args[wf_var[:argument][:name].to_sym] = @datapoint_json[:data_point][:set_variable_values][variable_uuid]
238
- args["#{wf_var[:argument][:name]}_machine_name".to_sym] = wf_var[:argument][:machine_name]
242
+ args["#{wf_var[:argument][:name]}_machine_name".to_sym] = wf_var[:argument][:display_name].snake_case
239
243
  args["#{wf_var[:argument][:name]}_type".to_sym] = wf_var[:value_type] if wf_var[:value_type]
240
244
  @logger.info "Setting the machine name for argument '#{wf_var[:argument][:name]}' to '#{args["#{wf_var[:argument][:name]}_machine_name".to_sym]}'"
241
245
 
@@ -245,9 +249,7 @@ class RunXml
245
249
  @weather_filename = @datapoint_json[:data_point][:set_variable_values][variable_uuid]
246
250
  end
247
251
  else
248
- @logger.info "Value for variable '#{variable_name}:#{variable_uuid}' not set in datapoint object"
249
- fail "Value for variable '#{variable_name}:#{variable_uuid}' not set in datapoint object" if CRASH_ON_NO_WORKFLOW_VARIABLE
250
- break
252
+ fail "[ERROR] Value for variable '#{variable_name}:#{variable_uuid}' not set in datapoint object"
251
253
  end
252
254
  else
253
255
  fail 'No block for set_variable_values in data point record'
@@ -267,13 +269,9 @@ class RunXml
267
269
  @output_attributes[wf[:name].to_sym] = measure.variable_values
268
270
  measure.results_to_json("#{@run_directory}/#{wf[:name]}_results.json")
269
271
 
270
- # TODO: do we want to do this?
271
- #ros.communicate_intermediate_result(measure.variable_values)
272
-
273
272
  @logger.info "Finished applying measure workflow #{wf[:name]} with change flag set to '#{xml_changed}'"
274
273
  end
275
274
  end
276
275
  end
277
-
278
276
  end
279
- end
277
+ end
@@ -20,10 +20,10 @@
20
20
  require 'logger'
21
21
 
22
22
  class Logger
23
- def format_message(severity, datetime, progname, msg)
24
- #"#{datetime} (#{$$}) #{msg}\n"
25
- #"#{datetime}: #{msg}\n"
26
- "[%s %s] %s\n" % [ datetime.strftime("%H:%M:%S.%6N"), severity, msg ]
23
+ def format_message(severity, datetime, _progname, msg)
24
+ # "#{datetime} (#{$$}) #{msg}\n"
25
+ # "#{datetime}: #{msg}\n"
26
+ "[%s %s] %s\n" % [datetime.strftime('%H:%M:%S.%6N'), severity, msg]
27
27
  end
28
28
  end
29
29
 
@@ -43,6 +43,6 @@ class MultiDelegator
43
43
  end
44
44
 
45
45
  class <<self
46
- alias to new
46
+ alias_method :to, :new
47
47
  end
48
- end
48
+ end
@@ -32,19 +32,19 @@ module OpenStudio
32
32
  attr_reader :final_state
33
33
  attr_reader :job_results
34
34
 
35
-
36
35
  # Create a nice name for the state object instead of aasm
37
- alias state aasm
36
+ alias_method :state, :aasm
38
37
 
39
38
  # load the transitions
40
39
  def self.default_transition
41
40
  # TODO: replace these with dynamic states from a config file of some sort
42
41
  [
43
- {from: :queued, to: :preflight},
44
- {from: :preflight, to: :openstudio},
45
- {from: :openstudio, to: :energyplus},
46
- {from: :energyplus, to: :postprocess},
47
- {from: :postprocess, to: :finished},
42
+ { from: :queued, to: :preflight },
43
+ { from: :preflight, to: :openstudio },
44
+ { from: :openstudio, to: :energyplus },
45
+ { from: :energyplus, to: :reporting_measures },
46
+ { from: :reporting_measures, to: :postprocess },
47
+ { from: :postprocess, to: :finished }
48
48
  ]
49
49
  end
50
50
 
@@ -53,13 +53,38 @@ module OpenStudio
53
53
  def self.default_states
54
54
  # TODO: replace this with some sort of dynamic store
55
55
  [
56
- {state: :queued, :options => {initial: true}},
57
- {state: :preflight, :options => {after_enter: :run_preflight}},
58
- {state: :openstudio, :options => {after_enter: :run_openstudio}},
59
- {state: :energyplus, :options => {after_enter: :run_energyplus}},
60
- {state: :postprocess, :options => {after_enter: :run_postprocess}},
61
- {state: :finished},
62
- {state: :errored}
56
+ { state: :queued, options: { initial: true } },
57
+ { state: :preflight, options: { after_enter: :run_preflight } },
58
+ { state: :openstudio, options: { after_enter: :run_openstudio } }, # TODO: this should be run_openstudio_measures and run_energyplus_measures
59
+ { state: :energyplus, options: { after_enter: :run_energyplus } },
60
+ { state: :reporting_measures, options: { after_enter: :run_reporting_measures } },
61
+ { state: :postprocess, options: { after_enter: :run_postprocess } },
62
+ { state: :finished },
63
+ { state: :errored }
64
+ ]
65
+ end
66
+
67
+ # transitions for pat job
68
+ def self.pat_transition
69
+ # TODO: replace these with dynamic states from a config file of some sort
70
+ [
71
+ { from: :queued, to: :preflight },
72
+ { from: :preflight, to: :runmanager },
73
+ { from: :runmanager, to: :postprocess },
74
+ { from: :postprocess, to: :finished }
75
+ ]
76
+ end
77
+
78
+ # states for pat job
79
+ def self.pat_states
80
+ # TODO: replace this with some sort of dynamic store
81
+ [
82
+ { state: :queued, options: { initial: true } },
83
+ { state: :preflight, options: { after_enter: :run_preflight } },
84
+ { state: :runmanager, options: { after_enter: :run_runmanager } },
85
+ { state: :postprocess, options: { after_enter: :run_postprocess } },
86
+ { state: :finished },
87
+ { state: :errored }
63
88
  ]
64
89
  end
65
90
 
@@ -74,11 +99,20 @@ module OpenStudio
74
99
  # TODO: run directory is a convention right now. Move to a configuration item
75
100
  @run_directory = "#{@directory}/run"
76
101
 
77
- defaults = {
102
+ defaults = nil
103
+ if options[:is_pat]
104
+ defaults = {
105
+ transitions: OpenStudio::Workflow::Run.pat_transition,
106
+ states: OpenStudio::Workflow::Run.pat_states,
107
+ jobs: {}
108
+ }
109
+ else
110
+ defaults = {
78
111
  transitions: OpenStudio::Workflow::Run.default_transition,
79
112
  states: OpenStudio::Workflow::Run.default_states,
80
113
  jobs: {}
81
- }
114
+ }
115
+ end
82
116
  @options = defaults.merge(options)
83
117
 
84
118
  @error = false
@@ -90,7 +124,7 @@ module OpenStudio
90
124
  FileUtils.mkdir_p(@run_directory)
91
125
 
92
126
  # There is a namespace conflict when OpenStudio is loaded: be careful!
93
- log_file = File.open("#{@run_directory}/run.log", "a")
127
+ log_file = File.open("#{@run_directory}/run.log", 'a')
94
128
 
95
129
  l = @adapter.get_logger @directory, @options
96
130
  if l
@@ -100,6 +134,7 @@ module OpenStudio
100
134
  end
101
135
 
102
136
  @logger.info "Initializing directory #{@directory} for simulation with options #{@options}"
137
+ @logger.info "OpenStudio loaded: '#{$openstudio_gem}'"
103
138
 
104
139
  super()
105
140
 
@@ -110,29 +145,32 @@ module OpenStudio
110
145
  # run the simulations.
111
146
  # TODO: add a catch if any job fails; TODO: make a block method to provide feedback
112
147
  def run
113
- @logger.info "Starting workflow"
148
+ @logger.info "Starting workflow in #{@directory}"
114
149
  begin
115
- while self.state.current_state != :finished && !@error
116
- self.step
150
+ while state.current_state != :finished && !@error
151
+ step
117
152
  end
118
153
 
119
- @logger.info "Finished workflow"
154
+ @logger.info 'Finished workflow - communicating results and zipping files'
120
155
 
156
+ # TODO: this should be a job that handles the use case with a :guard on if @job_results[:run_postprocess]
157
+ # or @job_results[:run_reporting_measures]
158
+ # these are the results that need to be sent back to adapter
159
+ if @job_results[:run_runmanager]
160
+ @logger.info 'Sending the run_runmananger results back to the adapter'
161
+ @adapter.communicate_results @directory, @job_results[:run_runmanager]
162
+ elsif @job_results[:run_reporting_measures]
163
+ @logger.info 'Sending the reporting measuers results back to the adapter'
164
+ @adapter.communicate_results @directory, @job_results[:run_reporting_measures]
165
+ end
166
+ ensure
121
167
  if @error
122
- # need to tell the system that this failed
123
168
  @adapter.communicate_failure @directory
169
+ else
170
+ @adapter.communicate_complete @directory
124
171
  end
125
172
 
126
- # TODO: this should be a job that handles the use case with a :guard on if @job_results[:run_postprocess]
127
- if @job_results[:run_postprocess]
128
- # these are the results that need to be sent back to adapter
129
- @logger.info "Sending the results back to the adapter"
130
- #@logger.info "Sending communicate_results the following options #{@job_results}"
131
- @adapter.communicate_results @directory, @job_results[:run_postprocess]
132
- end
133
- ensure
134
- @adapter.communicate_complete @directory
135
- @logger.info "Running workflow from #{__FILE__} complete"
173
+ @logger.info 'Workflow complete'
136
174
 
137
175
  # TODO: define the outputs and figure out how to show it correctory
138
176
  obj_function_array ||= ['NA']
@@ -174,6 +212,24 @@ module OpenStudio
174
212
  @job_results[__method__.to_sym] = klass.perform
175
213
  end
176
214
 
215
+ # run a pat file using runmanager
216
+ def run_runmanager
217
+ @logger.info "Running #{__method__}"
218
+ klass = get_run_class(__method__)
219
+
220
+ # TODO: save the resulting filenames to an array
221
+ @job_results[__method__.to_sym] = klass.perform
222
+ end
223
+
224
+ # run reporting measures
225
+ def run_reporting_measures
226
+ @logger.info "Running #{__method__}"
227
+ klass = get_run_class(__method__)
228
+
229
+ # TODO: save the resulting filenames to an array
230
+ @job_results[__method__.to_sym] = klass.perform
231
+ end
232
+
177
233
  def run_postprocess
178
234
  @logger.info "Running #{__method__}"
179
235
  klass = get_run_class(__method__)
@@ -208,7 +264,7 @@ module OpenStudio
208
264
  # a single event of :step which steps through the transitions defined in the Hash in default_transitions
209
265
  # and calls the actions defined in the states in the Hash of default_states
210
266
  def machine
211
- @logger.info "Initializing state machine"
267
+ @logger.info 'Initializing state machine'
212
268
  @options[:states].each do |s|
213
269
  s[:options] ? o = s[:options] : o = {}
214
270
  OpenStudio::Workflow::Run.aasm.states << AASM::State.new(s[:state], self.class, o)
@@ -228,18 +284,18 @@ module OpenStudio
228
284
  # Add in special event to error_out the state machine
229
285
  new_event = OpenStudio::Workflow::Run.aasm.event(:error_out)
230
286
  event = OpenStudio::Workflow::Run.aasm.events[:error_out]
231
- event.transitions(:to => :errored)
287
+ event.transitions(to: :errored)
232
288
  end
233
289
 
234
290
  # Get any options that may have been sent into the class defining the workflow step
235
291
  def get_job_options
236
292
  result = {}
237
- #if @options[:jobs].has_key?(state.current_state)
238
- #logger.info "Retrieving job options from the @options array for #{state.current_state}"
293
+ # if @options[:jobs].has_key?(state.current_state)
294
+ # logger.info "Retrieving job options from the @options array for #{state.current_state}"
239
295
  # result = @options[:jobs][state.current_state]
240
- #end
296
+ # end
241
297
 
242
- #result
298
+ # result
243
299
 
244
300
  # TODO fix this so that it gets the base config options plus its job options. Need to
245
301
  # also merge in all the former job results.
@@ -255,4 +311,4 @@ module OpenStudio
255
311
  end
256
312
  end
257
313
  end
258
- end
314
+ end