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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +39 -2
- data/Rakefile +12 -1
- data/lib/openstudio-workflow.rb +31 -4
- data/lib/openstudio/workflow/adapter.rb +8 -9
- data/lib/openstudio/workflow/adapters/local.rb +35 -22
- data/lib/openstudio/workflow/adapters/mongo.rb +82 -92
- data/lib/openstudio/workflow/jobs/lib/apply_measures.rb +229 -0
- data/lib/openstudio/workflow/jobs/run_energyplus/run_energyplus.rb +7 -10
- data/lib/openstudio/workflow/jobs/run_openstudio/run_openstudio.rb +37 -159
- data/lib/openstudio/workflow/jobs/run_postprocess/run_postprocess.rb +53 -492
- data/lib/openstudio/workflow/jobs/run_preflight/run_preflight.rb +1 -5
- data/lib/openstudio/workflow/jobs/{run_postprocess → run_reporting_measures}/packaged_measures/README.md +0 -0
- data/lib/openstudio/workflow/jobs/{run_postprocess → run_reporting_measures}/packaged_measures/StandardReports/measure.rb +81 -87
- data/lib/openstudio/workflow/jobs/{run_postprocess → run_reporting_measures}/packaged_measures/StandardReports/measure.xml +1 -1
- 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
- data/lib/openstudio/workflow/jobs/run_reporting_measures/run_reporting_measures.rb +548 -0
- data/lib/openstudio/workflow/jobs/run_runmanager/run_runmanager.rb +226 -0
- data/lib/openstudio/workflow/jobs/run_xml/run_xml.rb +39 -41
- data/lib/openstudio/workflow/multi_delegator.rb +6 -6
- data/lib/openstudio/workflow/run.rb +95 -39
- data/lib/openstudio/workflow/version.rb +1 -1
- 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],
|
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
|
-
|
52
|
-
|
53
|
-
|
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
|
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
|
-
|
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.
|
73
|
-
File.open("#{@run_directory}/measure_attributes_xml.json", 'w')
|
74
|
-
|
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
|
-
|
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
|
-
|
155
|
-
|
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
|
-
|
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
|
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
|
-
#
|
221
|
-
#
|
222
|
-
#
|
223
|
-
#
|
224
|
-
#
|
225
|
-
#
|
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
|
-
|
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][:
|
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
|
-
|
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,
|
24
|
-
#"#{datetime} (#{$$}) #{msg}\n"
|
25
|
-
#"#{datetime}: #{msg}\n"
|
26
|
-
"[%s %s] %s\n" % [
|
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
|
-
|
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
|
-
|
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
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
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
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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",
|
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
|
116
|
-
|
150
|
+
while state.current_state != :finished && !@error
|
151
|
+
step
|
117
152
|
end
|
118
153
|
|
119
|
-
@logger.info
|
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
|
-
|
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
|
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(:
|
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
|