openstudio-workflow 0.0.3 → 0.0.4
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 +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
|