openstudio-workflow 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/lib/openstudio-workflow.rb +8 -6
- data/lib/openstudio/workflow/jobs/lib/apply_measures.rb +5 -2
- data/lib/openstudio/workflow/jobs/run_energyplus/run_energyplus.rb +13 -8
- data/lib/openstudio/workflow/jobs/run_openstudio/run_openstudio.rb +6 -5
- data/lib/openstudio/workflow/jobs/run_postprocess/run_postprocess.rb +2 -1
- data/lib/openstudio/workflow/jobs/run_preflight/run_preflight.rb +5 -4
- data/lib/openstudio/workflow/jobs/run_reporting_measures/run_reporting_measures.rb +51 -15
- data/lib/openstudio/workflow/jobs/run_runmanager/run_runmanager.rb +2 -1
- data/lib/openstudio/workflow/jobs/run_xml/run_xml.rb +2 -1
- data/lib/openstudio/workflow/run.rb +43 -39
- data/lib/openstudio/workflow/time_logger.rb +53 -0
- data/lib/openstudio/workflow/version.rb +1 -1
- metadata +20 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0530685f749da9f5112920053f26b8934ab4861e
|
4
|
+
data.tar.gz: 312ebc0a8b2844f94796214054605e414fc5606f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eebca1c2eac830150a5c8cace44e77724d12090109bd34b224a228645930f85ca7f3bb068bbc254e0437719e9e8a5a23aaf5850aad18fdf45fe439cfe5b29f23
|
7
|
+
data.tar.gz: 6eb722576d2142d9de21aa193703783ee4115ffa203a4d5f99c38438524cfa974e9972dc7485289790fc83736bf1fc97be1aba0bf4c099ac9eb4bf41a036bab8
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,13 @@
|
|
1
1
|
OpenStudio::Workflow Change Log
|
2
2
|
==================================
|
3
3
|
|
4
|
+
Version 0.0.4 (Unreleased)
|
5
|
+
-------------
|
6
|
+
* Include rubyXL gem for reading/writing MS Excel files
|
7
|
+
* Remove invalid characters from OpenStudio Measure Attributes. /[|!@#\$%^&\*\(\)\{\}\\\[\]|;:'",<.>\/?\+=]+/
|
8
|
+
* Fix objective functions to read from any of the results hash, not just the standard report legacy
|
9
|
+
* Add time logger and reporting measure
|
10
|
+
|
4
11
|
Version 0.0.3
|
5
12
|
--------------
|
6
13
|
* Allow measures to set weather file in a measure and have it update what EnergyPlus uses for the weather file.
|
data/lib/openstudio-workflow.rb
CHANGED
@@ -18,6 +18,7 @@
|
|
18
18
|
######################################################################
|
19
19
|
|
20
20
|
require 'pp'
|
21
|
+
require 'rubyXL'
|
21
22
|
require 'multi_json'
|
22
23
|
require 'colored'
|
23
24
|
require 'fileutils'
|
@@ -35,6 +36,7 @@ require 'openstudio/workflow/version'
|
|
35
36
|
require 'openstudio/workflow/multi_delegator'
|
36
37
|
require 'openstudio/workflow/run'
|
37
38
|
require 'openstudio/workflow/jobs/lib/apply_measures'
|
39
|
+
require 'openstudio/workflow/time_logger'
|
38
40
|
|
39
41
|
begin
|
40
42
|
require 'openstudio'
|
@@ -48,10 +50,10 @@ end
|
|
48
50
|
class String
|
49
51
|
def snake_case
|
50
52
|
gsub(/::/, '/')
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
53
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
54
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
55
|
+
.tr(' -', '__')
|
56
|
+
.downcase
|
55
57
|
end
|
56
58
|
end
|
57
59
|
|
@@ -66,11 +68,11 @@ module OpenStudio
|
|
66
68
|
|
67
69
|
# Convert various paths to absolute paths
|
68
70
|
if options[:adapter_options] && options[:adapter_options][:mongoid_path] &&
|
69
|
-
|
71
|
+
(Pathname.new options[:adapter_options][:mongoid_path]).absolute? == false
|
70
72
|
options[:adapter_options][:mongoid_path] = File.expand_path options[:adapter_options][:mongoid_path]
|
71
73
|
end
|
72
74
|
if options[:analysis_root_path] &&
|
73
|
-
|
75
|
+
(Pathname.new options[:analysis_root_path]).absolute? == false
|
74
76
|
options[:analysis_root_path] = File.expand_path options[:analysis_root_path]
|
75
77
|
end
|
76
78
|
unless (Pathname.new run_directory).absolute?
|
@@ -96,7 +96,8 @@ module OpenStudio
|
|
96
96
|
|
97
97
|
def apply_measure(workflow_item)
|
98
98
|
@logger.info "Starting #{__method__} for #{workflow_item[:name]}"
|
99
|
-
|
99
|
+
@time_logger.start("Measure:#{workflow_item[:name]}")
|
100
|
+
#start_time = ::Time.now
|
100
101
|
current_dir = Dir.pwd
|
101
102
|
begin
|
102
103
|
measure_working_directory = "#{@run_directory}/#{workflow_item[:measure_definition_class_name]}"
|
@@ -204,7 +205,9 @@ module OpenStudio
|
|
204
205
|
end
|
205
206
|
ensure
|
206
207
|
Dir.chdir current_dir
|
207
|
-
@
|
208
|
+
@time_logger.stop("Measure:#{workflow_item[:name]}")
|
209
|
+
|
210
|
+
@logger.info "Finished #{__method__} for #{workflow_item[:name]} in #{@time_logger.delta("Measure:#{workflow_item[:name]}")} s"
|
208
211
|
end
|
209
212
|
end
|
210
213
|
|
@@ -21,7 +21,7 @@ class RunEnergyplus
|
|
21
21
|
# Initialize
|
22
22
|
# param directory: base directory where the simulation files are prepared
|
23
23
|
# param logger: logger object in which to write log messages
|
24
|
-
def initialize(directory, logger, adapter, options = {})
|
24
|
+
def initialize(directory, logger, time_logger, adapter, options = {})
|
25
25
|
energyplus_path = nil
|
26
26
|
if /cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM
|
27
27
|
energyplus_path = 'C:/EnergyPlus-8-1-0'
|
@@ -39,6 +39,7 @@ class RunEnergyplus
|
|
39
39
|
@run_directory = "#{@directory}/run"
|
40
40
|
@adapter = adapter
|
41
41
|
@logger = logger
|
42
|
+
@time_logger = time_logger
|
42
43
|
@results = {}
|
43
44
|
|
44
45
|
@logger.info "#{self.class} passed the following options #{@options}"
|
@@ -62,9 +63,9 @@ class RunEnergyplus
|
|
62
63
|
|
63
64
|
# verify that the OSM, IDF, and the Weather files are in the run directory as the 'in.*' format
|
64
65
|
if !weather_file_name &&
|
65
|
-
|
66
|
-
|
67
|
-
|
66
|
+
@options[:run_openstudio] &&
|
67
|
+
@options[:run_openstudio][:weather_filename] &&
|
68
|
+
File.exist?(@options[:run_openstudio][:weather_filename])
|
68
69
|
weather_file_name = @options[:run_openstudio][:weather_filename]
|
69
70
|
end
|
70
71
|
|
@@ -98,6 +99,7 @@ class RunEnergyplus
|
|
98
99
|
|
99
100
|
# can't create symlinks because the /vagrant mount is actually a windows mount
|
100
101
|
@logger.info "Copying EnergyPlus files to run directory: #{@run_directory}"
|
102
|
+
@time_logger.start("Copying EnergyPlus files")
|
101
103
|
FileUtils.copy("#{@options[:energyplus_path]}/libbcvtb.so", "#{@run_directory}/libbcvtb.so")
|
102
104
|
FileUtils.copy("#{@options[:energyplus_path]}/libepexpat.so", "#{@run_directory}/libepexpat.so")
|
103
105
|
FileUtils.copy("#{@options[:energyplus_path]}/libepfmiimport.so", "#{@run_directory}/libepfmiimport.so")
|
@@ -106,8 +108,11 @@ class RunEnergyplus
|
|
106
108
|
FileUtils.copy("#{@options[:energyplus_path]}/ExpandObjects", "#{@run_directory}/ExpandObjects")
|
107
109
|
FileUtils.copy("#{@options[:energyplus_path]}/EnergyPlus", "#{@run_directory}/EnergyPlus")
|
108
110
|
FileUtils.copy("#{@options[:energyplus_path]}/Energy+.idd", "#{@run_directory}/Energy+.idd")
|
111
|
+
@time_logger.stop("Copying EnergyPlus files")
|
109
112
|
|
113
|
+
@time_logger.start("Running EnergyPlus")
|
110
114
|
@results = call_energyplus
|
115
|
+
@time_logger.stop("Running EnergyPlus")
|
111
116
|
|
112
117
|
@results
|
113
118
|
end
|
@@ -156,21 +161,21 @@ class RunEnergyplus
|
|
156
161
|
paths_to_rm.each { |p| FileUtils.rm_rf(p) }
|
157
162
|
|
158
163
|
unless r == 0
|
159
|
-
fail
|
164
|
+
fail 'EnergyPlus returned a non-zero exit code. Check the stdout-energyplus log.'
|
160
165
|
end
|
161
166
|
|
162
167
|
# TODO: check the end or err file
|
163
|
-
if File.
|
168
|
+
if File.exist? 'eplusout.err'
|
164
169
|
eplus_err = File.read('eplusout.err')
|
165
170
|
eplus_err = eplus_err.force_encoding('ISO-8859-1').encode('utf-8', replace: nil)
|
166
171
|
if eplus_err =~ /EnergyPlus Terminated--Fatal Error Detected/
|
167
|
-
fail
|
172
|
+
fail 'EnergyPlus Terminated with a Fatal Error. Check eplusout.err log.'
|
168
173
|
end
|
169
174
|
end
|
170
175
|
rescue => e
|
171
176
|
log_message = "#{__FILE__} failed with #{e.message}, #{e.backtrace.join("\n")}"
|
172
177
|
@logger.error log_message
|
173
|
-
|
178
|
+
raise log_message
|
174
179
|
ensure
|
175
180
|
Dir.chdir(current_dir)
|
176
181
|
@logger.info 'EnergyPlus Completed'
|
@@ -25,7 +25,7 @@ class RunOpenstudio
|
|
25
25
|
# Initialize
|
26
26
|
# param directory: base directory where the simulation files are prepared
|
27
27
|
# param logger: logger object in which to write log messages
|
28
|
-
def initialize(directory, logger, adapter, options = {})
|
28
|
+
def initialize(directory, logger, time_logger, adapter, options = {})
|
29
29
|
defaults = { format: 'hash', use_monthly_reports: false, analysis_root_path: '.' }
|
30
30
|
@options = defaults.merge(options)
|
31
31
|
@directory = directory
|
@@ -34,6 +34,7 @@ class RunOpenstudio
|
|
34
34
|
@adapter = adapter
|
35
35
|
@results = {}
|
36
36
|
@logger = logger
|
37
|
+
@time_logger = time_logger
|
37
38
|
@logger.info "#{self.class} passed the following options #{@options}"
|
38
39
|
|
39
40
|
# initialize instance variables that are needed in the perform section
|
@@ -63,7 +64,9 @@ class RunOpenstudio
|
|
63
64
|
|
64
65
|
apply_measures(:openstudio_measure)
|
65
66
|
|
67
|
+
@time_logger.start("Translating to EnergyPlus")
|
66
68
|
translate_to_energyplus
|
69
|
+
@time_logger.stop("Translating to EnergyPlus")
|
67
70
|
|
68
71
|
apply_measures(:energyplus_measure)
|
69
72
|
|
@@ -86,7 +89,9 @@ class RunOpenstudio
|
|
86
89
|
end
|
87
90
|
end
|
88
91
|
|
92
|
+
@time_logger.start("Saving OSM and IDF")
|
89
93
|
save_osm_and_idf
|
94
|
+
@time_logger.stop("Saving OSM and IDF")
|
90
95
|
|
91
96
|
@results
|
92
97
|
end
|
@@ -95,13 +100,9 @@ class RunOpenstudio
|
|
95
100
|
|
96
101
|
def save_osm_and_idf
|
97
102
|
# save the data
|
98
|
-
a = Time.now
|
99
103
|
osm_filename = "#{@run_directory}/in.osm"
|
100
104
|
File.open(osm_filename, 'w') { |f| f << @model.to_s }
|
101
|
-
b = Time.now
|
102
|
-
@logger.info "OpenStudio write took #{b.to_f - a.to_f}"
|
103
105
|
|
104
|
-
# Run EnergyPlus using run energyplus script
|
105
106
|
idf_filename = "#{@run_directory}/in.idf"
|
106
107
|
File.open(idf_filename, 'w') { |f| f << @model_idf.to_s }
|
107
108
|
|
@@ -27,13 +27,14 @@ class RunPostprocess
|
|
27
27
|
# Mixin the MeasureApplication module to apply measures
|
28
28
|
include OpenStudio::Workflow::ApplyMeasures
|
29
29
|
|
30
|
-
def initialize(directory, logger, adapter, options = {})
|
30
|
+
def initialize(directory, logger, time_logger, adapter, options = {})
|
31
31
|
defaults = {}
|
32
32
|
@options = defaults.merge(options)
|
33
33
|
@directory = directory
|
34
34
|
@run_directory = "#{@directory}/run"
|
35
35
|
@adapter = adapter
|
36
36
|
@logger = logger
|
37
|
+
@time_logger = time_logger
|
37
38
|
@results = {}
|
38
39
|
@output_attributes = {}
|
39
40
|
|
@@ -19,13 +19,14 @@
|
|
19
19
|
|
20
20
|
# Run Prelight job to prepare the directory for simulations.
|
21
21
|
class RunPreflight
|
22
|
-
def initialize(directory, logger, adapter, options = {})
|
22
|
+
def initialize(directory, logger, time_logger, adapter, options = {})
|
23
23
|
defaults = {}
|
24
24
|
@options = defaults.merge(options)
|
25
25
|
@directory = directory
|
26
26
|
@adapter = adapter
|
27
|
-
@results = {}
|
28
27
|
@logger = logger
|
28
|
+
@time_logger = time_logger
|
29
|
+
@results = {}
|
29
30
|
end
|
30
31
|
|
31
32
|
def perform
|
@@ -33,9 +34,9 @@ class RunPreflight
|
|
33
34
|
|
34
35
|
@adapter.communicate_started @directory, @options
|
35
36
|
|
36
|
-
#
|
37
|
+
# At the moment this does nothing.
|
37
38
|
|
38
|
-
# return the results back to the caller--always
|
39
|
+
# return the results back to the caller -- always
|
39
40
|
@results
|
40
41
|
end
|
41
42
|
end
|
@@ -18,8 +18,6 @@
|
|
18
18
|
######################################################################
|
19
19
|
|
20
20
|
# Run precanned post processing to extract object functions
|
21
|
-
# TODO: I hear that measures can step on each other if not run in their own directory
|
22
|
-
|
23
21
|
require 'csv'
|
24
22
|
require 'ostruct'
|
25
23
|
|
@@ -27,13 +25,14 @@ class RunReportingMeasures
|
|
27
25
|
# Mixin the MeasureApplication module to apply measures
|
28
26
|
include OpenStudio::Workflow::ApplyMeasures
|
29
27
|
|
30
|
-
def initialize(directory, logger, adapter, options = {})
|
28
|
+
def initialize(directory, logger, time_logger, adapter, options = {})
|
31
29
|
defaults = {}
|
32
30
|
@options = defaults.merge(options)
|
33
31
|
@directory = directory
|
34
32
|
@run_directory = "#{@directory}/run"
|
35
33
|
@adapter = adapter
|
36
34
|
@logger = logger
|
35
|
+
@time_logger = time_logger
|
37
36
|
@results = {}
|
38
37
|
@output_attributes = {}
|
39
38
|
|
@@ -61,11 +60,13 @@ class RunReportingMeasures
|
|
61
60
|
@datapoint_json = @adapter.get_datapoint(@directory, @options)
|
62
61
|
@analysis_json = @adapter.get_problem(@directory, @options)
|
63
62
|
|
63
|
+
@time_logger.start("Running standard post process")
|
64
64
|
if @options[:use_monthly_reports]
|
65
65
|
run_monthly_postprocess
|
66
66
|
else
|
67
67
|
run_standard_postprocess
|
68
68
|
end
|
69
|
+
@time_logger.stop("Running standard post process")
|
69
70
|
|
70
71
|
translate_csv_to_json
|
71
72
|
|
@@ -77,7 +78,8 @@ class RunReportingMeasures
|
|
77
78
|
|
78
79
|
@logger.info 'Saving reporting measures output attributes JSON'
|
79
80
|
File.open("#{@run_directory}/reporting_measure_attributes.json", 'w') do
|
80
|
-
|
81
|
+
|f|
|
82
|
+
f << JSON.pretty_generate(@output_attributes)
|
81
83
|
end
|
82
84
|
|
83
85
|
run_extract_inputs_and_outputs
|
@@ -99,30 +101,40 @@ class RunReportingMeasures
|
|
99
101
|
# For xml, the measure attributes are in the measure_attributes_xml.json file
|
100
102
|
# TODO: somehow pass the metadata around on which JSONs to suck into the database
|
101
103
|
if File.exist? "#{@run_directory}/measure_attributes_xml.json"
|
102
|
-
|
103
|
-
|
104
|
+
h = JSON.parse(File.read("#{@run_directory}/measure_attributes_xml.json"), symbolize_names: true)
|
105
|
+
h = rename_hash_keys(h)
|
106
|
+
@results.merge! h
|
104
107
|
end
|
105
108
|
|
106
109
|
# Inputs are in the measure_attributes.json file
|
107
110
|
if File.exist? "#{@run_directory}/measure_attributes.json"
|
108
|
-
|
109
|
-
|
111
|
+
h = JSON.parse(File.read("#{@run_directory}/measure_attributes.json"), symbolize_names: true)
|
112
|
+
h = rename_hash_keys(h)
|
113
|
+
@results.merge! h
|
110
114
|
end
|
111
115
|
|
112
116
|
# Inputs are in the reporting_measure_attributes.jsonfile
|
113
117
|
if File.exist? "#{@run_directory}/reporting_measure_attributes.json"
|
114
|
-
|
115
|
-
|
118
|
+
h = JSON.parse(File.read("#{@run_directory}/reporting_measure_attributes.json"), symbolize_names: true)
|
119
|
+
h = rename_hash_keys(h)
|
120
|
+
@results.merge! h
|
116
121
|
end
|
117
122
|
|
118
|
-
# Initialize the objective function variable
|
123
|
+
# Initialize the objective function variable.
|
119
124
|
@objective_functions = {}
|
120
125
|
if File.exist? "#{@run_directory}/standard_report_legacy.json"
|
121
|
-
|
126
|
+
h = JSON.parse(File.read("#{@run_directory}/standard_report_legacy.json"), symbolize_names: true)
|
127
|
+
h = rename_hash_keys(h)
|
128
|
+
@results[:standard_report_legacy] = h
|
129
|
+
end
|
130
|
+
|
131
|
+
@logger.info 'Saving the result hash to file'
|
132
|
+
File.open("#{@run_directory}/results.json", 'w') { |f| f << JSON.pretty_generate(@results) }
|
122
133
|
|
123
|
-
|
124
|
-
|
134
|
+
@logger.info 'Iterating over Analysis JSON Output Variables'
|
135
|
+
# Save the objective functions to the object for sending back to the simulation executive
|
125
136
|
|
137
|
+
if @analysis_json[:analysis] && @analysis_json[:analysis][:output_variables]
|
126
138
|
@analysis_json[:analysis][:output_variables].each do |variable|
|
127
139
|
# determine which ones are the objective functions (code smell: todo: use enumerator)
|
128
140
|
if variable[:objective_function]
|
@@ -240,7 +252,31 @@ class RunReportingMeasures
|
|
240
252
|
end
|
241
253
|
end
|
242
254
|
|
243
|
-
#
|
255
|
+
# Remove any invalid characters in the measure attribute keys.
|
256
|
+
# Periods and Pipes are the most problematic because mongo does not allow hash keys with periods, and the pipes
|
257
|
+
# are used in the map/reduce method that was written to speed up the data write in openstudio-server.
|
258
|
+
# Also remove any trailing underscores and spaces
|
259
|
+
def rename_hash_keys(hash)
|
260
|
+
# TODO: log the name changes?
|
261
|
+
regex = /[|!@#\$%^&\*\(\)\{\}\\\[\]|;:'",<.>\/?\+=]+/
|
262
|
+
|
263
|
+
rename_keys = lambda do |h|
|
264
|
+
if Hash === h
|
265
|
+
h.each_key do |key|
|
266
|
+
if key.to_s =~ regex
|
267
|
+
@logger.warn "Renaming result key '#{key}' to remove invalid characters"
|
268
|
+
end
|
269
|
+
end
|
270
|
+
Hash[h.map { |k, v| [k.to_s.gsub(regex, '_').squeeze('_').gsub(/[_\s]+$/, '').chomp.to_sym, rename_keys[v]] }]
|
271
|
+
else
|
272
|
+
h
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
rename_keys[hash]
|
277
|
+
end
|
278
|
+
|
279
|
+
# TODO: This needs to be cleaned up and tested. This is just ugly. Sorry.
|
244
280
|
def run_monthly_postprocess
|
245
281
|
def sql_query(sql, report_name, query)
|
246
282
|
val = nil
|
@@ -27,7 +27,7 @@ class RunRunmanager
|
|
27
27
|
# Initialize
|
28
28
|
# param directory: base directory where the simulation files are prepared
|
29
29
|
# param logger: logger object in which to write log messages
|
30
|
-
def initialize(directory, logger, adapter, options = {})
|
30
|
+
def initialize(directory, logger, time_logger, adapter, options = {})
|
31
31
|
energyplus_path = nil
|
32
32
|
if /cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM
|
33
33
|
energyplus_path = 'C:/EnergyPlus-8-1-0'
|
@@ -49,6 +49,7 @@ class RunRunmanager
|
|
49
49
|
@results = {}
|
50
50
|
@logger = logger
|
51
51
|
@logger.info "#{self.class} passed the following options #{@options}"
|
52
|
+
@time_logger = time_logger
|
52
53
|
|
53
54
|
# initialize instance variables that are needed in the perform section
|
54
55
|
@model = nil
|
@@ -22,7 +22,7 @@ require 'libxml'
|
|
22
22
|
# This actually belongs as another class that gets added as a state dynamically
|
23
23
|
class RunXml
|
24
24
|
# RunXml
|
25
|
-
def initialize(directory, logger, adapter, options = {})
|
25
|
+
def initialize(directory, logger, time_logger, adapter, options = {})
|
26
26
|
defaults = { use_monthly_reports: false, analysis_root_path: '.', xml_library_file: 'xml_runner.rb' }
|
27
27
|
@options = defaults.merge(options)
|
28
28
|
@directory = directory
|
@@ -31,6 +31,7 @@ class RunXml
|
|
31
31
|
@adapter = adapter
|
32
32
|
@results = {}
|
33
33
|
@logger = logger
|
34
|
+
@time_logger = time_logger
|
34
35
|
@logger.info "#{self.class} passed the following options #{@options}"
|
35
36
|
|
36
37
|
# initialize instance variables that are needed in the perform section
|
@@ -34,12 +34,12 @@ module OpenStudio
|
|
34
34
|
# load the transitions
|
35
35
|
def self.default_transition
|
36
36
|
[
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
37
|
+
{ from: :queued, to: :preflight },
|
38
|
+
{ from: :preflight, to: :openstudio },
|
39
|
+
{ from: :openstudio, to: :energyplus },
|
40
|
+
{ from: :energyplus, to: :reporting_measures },
|
41
|
+
{ from: :reporting_measures, to: :postprocess },
|
42
|
+
{ from: :postprocess, to: :finished }
|
43
43
|
]
|
44
44
|
end
|
45
45
|
|
@@ -48,24 +48,24 @@ module OpenStudio
|
|
48
48
|
def self.default_states
|
49
49
|
warn "[Deprecation Warning] explicitly specifying states will no longer be required in 0.3.0. Method #{__method__}"
|
50
50
|
[
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
51
|
+
{ state: :queued, options: { initial: true } },
|
52
|
+
{ state: :preflight, options: { after_enter: :run_preflight } },
|
53
|
+
{ state: :openstudio, options: { after_enter: :run_openstudio } }, # TODO: this should be run_openstudio_measures and run_energyplus_measures
|
54
|
+
{ state: :energyplus, options: { after_enter: :run_energyplus } },
|
55
|
+
{ state: :reporting_measures, options: { after_enter: :run_reporting_measures } },
|
56
|
+
{ state: :postprocess, options: { after_enter: :run_postprocess } },
|
57
|
+
{ state: :finished },
|
58
|
+
{ state: :errored }
|
59
59
|
]
|
60
60
|
end
|
61
61
|
|
62
62
|
# transitions for pat job
|
63
63
|
def self.pat_transition
|
64
64
|
[
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
65
|
+
{ from: :queued, to: :preflight },
|
66
|
+
{ from: :preflight, to: :runmanager },
|
67
|
+
{ from: :runmanager, to: :postprocess },
|
68
|
+
{ from: :postprocess, to: :finished }
|
69
69
|
]
|
70
70
|
end
|
71
71
|
|
@@ -73,12 +73,12 @@ module OpenStudio
|
|
73
73
|
def self.pat_states
|
74
74
|
warn "[Deprecation Warning] explicitly specifying states will no longer be required in 0.3.0. Method #{__method__}"
|
75
75
|
[
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
76
|
+
{ state: :queued, options: { initial: true } },
|
77
|
+
{ state: :preflight, options: { after_enter: :run_preflight } },
|
78
|
+
{ state: :runmanager, options: { after_enter: :run_runmanager } },
|
79
|
+
{ state: :postprocess, options: { after_enter: :run_postprocess } },
|
80
|
+
{ state: :finished },
|
81
|
+
{ state: :errored }
|
82
82
|
]
|
83
83
|
end
|
84
84
|
|
@@ -94,21 +94,22 @@ module OpenStudio
|
|
94
94
|
@current_state = nil
|
95
95
|
@transitions = {}
|
96
96
|
@directory = directory
|
97
|
+
@time_logger = TimeLogger.new
|
97
98
|
# TODO: run directory is a convention right now. Move to a configuration item
|
98
99
|
@run_directory = "#{@directory}/run"
|
99
100
|
|
100
101
|
defaults = nil
|
101
102
|
if options[:is_pat]
|
102
103
|
defaults = {
|
103
|
-
|
104
|
-
|
105
|
-
|
104
|
+
transitions: OpenStudio::Workflow::Run.pat_transition,
|
105
|
+
states: OpenStudio::Workflow::Run.pat_states,
|
106
|
+
jobs: {}
|
106
107
|
}
|
107
108
|
else
|
108
109
|
defaults = {
|
109
|
-
|
110
|
-
|
111
|
-
|
110
|
+
transitions: OpenStudio::Workflow::Run.default_transition,
|
111
|
+
states: OpenStudio::Workflow::Run.default_states,
|
112
|
+
jobs: {}
|
112
113
|
}
|
113
114
|
end
|
114
115
|
@options = defaults.merge(options)
|
@@ -156,6 +157,7 @@ module OpenStudio
|
|
156
157
|
@adapter.communicate_results @directory, @job_results[:run_runmanager]
|
157
158
|
elsif @job_results[:run_reporting_measures]
|
158
159
|
@logger.info 'Sending the reporting measuers results back to the adapter'
|
160
|
+
@time_logger.save(File.join(@directory,'profile.json'))
|
159
161
|
@adapter.communicate_results @directory, @job_results[:run_reporting_measures]
|
160
162
|
end
|
161
163
|
ensure
|
@@ -166,8 +168,12 @@ module OpenStudio
|
|
166
168
|
end
|
167
169
|
|
168
170
|
@logger.info 'Workflow complete'
|
171
|
+
# Write out the TimeLogger once again in case the run_reporting_measures didn't exist
|
172
|
+
@time_logger.save(File.join(@directory,'profile.json'))
|
169
173
|
|
170
|
-
|
174
|
+
|
175
|
+
|
176
|
+
# TODO: define the outputs and figure out how to show it correctly
|
171
177
|
obj_function_array ||= ['NA']
|
172
178
|
|
173
179
|
# Print the objective functions to the screen even though the file is being used right now
|
@@ -180,13 +186,11 @@ module OpenStudio
|
|
180
186
|
|
181
187
|
# Step through the states, if there is an error (e.g. exception) then go to error
|
182
188
|
def step(*args)
|
183
|
-
|
184
|
-
next_state
|
189
|
+
next_state
|
185
190
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
end
|
191
|
+
send("run_#{@current_state}")
|
192
|
+
rescue => e
|
193
|
+
step_error("#{e.message}:#{e.backtrace.join("\n")}")
|
190
194
|
end
|
191
195
|
|
192
196
|
# call back for when there is an exception running any of the state transitions
|
@@ -281,7 +285,7 @@ module OpenStudio
|
|
281
285
|
|
282
286
|
def next_state
|
283
287
|
@logger.info "Current state: '#{@current_state}'"
|
284
|
-
ns = @transitions.select{ |h| h[:from] == @current_state}.first[:to]
|
288
|
+
ns = @transitions.select { |h| h[:from] == @current_state }.first[:to]
|
285
289
|
@logger.info "Next state will be: '#{ns}'"
|
286
290
|
|
287
291
|
# Set the next state before calling the method
|
@@ -309,7 +313,7 @@ module OpenStudio
|
|
309
313
|
require_relative "jobs/#{from_method}/#{from_method}"
|
310
314
|
klass_name = from_method.to_s.split('_').map(&:capitalize) * ''
|
311
315
|
@logger.info "Getting method for state transition '#{from_method}'"
|
312
|
-
klass = Object.const_get(klass_name).new(@directory, @logger, @adapter, get_job_options)
|
316
|
+
klass = Object.const_get(klass_name).new(@directory, @logger, @time_logger, @adapter, get_job_options)
|
313
317
|
klass
|
314
318
|
end
|
315
319
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Class to store run times in a useful structure. Data are stored in a hash based on a the channel name
|
2
|
+
# There is no concept of multi-levels. The onus is on the user to make sure that they don't add a value to the
|
3
|
+
# logger that may be a level.
|
4
|
+
class TimeLogger
|
5
|
+
attr_reader :channels
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@logger = []
|
9
|
+
@channels = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
# name of the moniker that you are tracking. If the name is already in use, then it restarts the timer.
|
13
|
+
def start(channel)
|
14
|
+
# warning -- "will reset timer for #{moniker}" if @monikers.key? moniker
|
15
|
+
s = ::Time.now
|
16
|
+
@channels[channel] = {start_time_str: "#{s}", start_time: s.to_f}
|
17
|
+
end
|
18
|
+
|
19
|
+
def stop(channel)
|
20
|
+
end_time = ::Time.now.to_f
|
21
|
+
@logger << {
|
22
|
+
channel: channel,
|
23
|
+
start_time: @channels[channel][:start_time],
|
24
|
+
start_time_str: @channels[channel][:start_time_str],
|
25
|
+
end_time: end_time,
|
26
|
+
delta: end_time - @channels[channel][:start_time]
|
27
|
+
}
|
28
|
+
|
29
|
+
# remove the channel
|
30
|
+
@channels.delete(channel) if @channels.key? channel
|
31
|
+
end
|
32
|
+
|
33
|
+
def stop_all
|
34
|
+
@channels.each_key do |channel|
|
35
|
+
stop(channel)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# return the entire report
|
40
|
+
def report
|
41
|
+
@logger
|
42
|
+
end
|
43
|
+
|
44
|
+
# this will report all the values for all the channels with this name.
|
45
|
+
def delta(channel)
|
46
|
+
@logger.map { |k| {channel.to_s => k[:delta]} if k[:channel] == channel }
|
47
|
+
end
|
48
|
+
|
49
|
+
# save the data to a file. This will overwrite the file if it already exists
|
50
|
+
def save(filename)
|
51
|
+
File.open(filename, 'w') { |f| f << JSON.pretty_generate(@logger) }
|
52
|
+
end
|
53
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: openstudio-workflow
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nicholas Long
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-12-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -81,19 +81,33 @@ dependencies:
|
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: 2.0.2
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: rubyXL
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - ~>
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
89
|
+
version: 3.3.0
|
90
90
|
type: :runtime
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - ~>
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
96
|
+
version: 3.3.0
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubyzip
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ~>
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 1.1.6
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ~>
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 1.1.6
|
97
111
|
description: Run OpenStudio based simulations using EnergyPlus
|
98
112
|
email:
|
99
113
|
- nicholas.long@nrel.gov
|
@@ -119,6 +133,7 @@ files:
|
|
119
133
|
- lib/openstudio/workflow/jobs/run_xml/run_xml.rb
|
120
134
|
- lib/openstudio/workflow/multi_delegator.rb
|
121
135
|
- lib/openstudio/workflow/run.rb
|
136
|
+
- lib/openstudio/workflow/time_logger.rb
|
122
137
|
- lib/openstudio/workflow/version.rb
|
123
138
|
- lib/openstudio-workflow.rb
|
124
139
|
- README.md
|