openstudio-workflow 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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