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