openstudio-workflow 1.0.0.pat1 → 1.0.0
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 +12 -0
- data/README.md +16 -68
- data/Rakefile +9 -9
- data/bin/openstudio_cli +786 -0
- data/lib/openstudio/workflow/adapters/input/local.rb +97 -0
- data/lib/openstudio/workflow/adapters/output/local.rb +90 -0
- data/lib/openstudio/workflow/adapters/output/socket.rb +70 -0
- data/lib/openstudio/workflow/{jobs/run_preflight/run_preflight.rb → adapters/output/web.rb} +37 -19
- data/lib/openstudio/workflow/{adapter.rb → adapters/output_adapter.rb} +53 -51
- data/lib/openstudio/workflow/job.rb +22 -0
- data/lib/openstudio/workflow/jobs/{run_energyplus → resources}/monthly_report.idf +0 -0
- data/lib/openstudio/workflow/jobs/run_energyplus.rb +49 -0
- data/lib/openstudio/workflow/jobs/run_ep_measures.rb +55 -0
- data/lib/openstudio/workflow/jobs/run_initialization.rb +136 -0
- data/lib/openstudio/workflow/jobs/run_os_measures.rb +59 -0
- data/lib/openstudio/workflow/jobs/run_postprocess.rb +53 -0
- data/lib/openstudio/workflow/jobs/run_preprocess.rb +81 -0
- data/lib/openstudio/workflow/jobs/run_reporting_measures.rb +86 -0
- data/lib/openstudio/workflow/jobs/run_translation.rb +49 -0
- data/lib/openstudio/workflow/multi_delegator.rb +1 -3
- data/lib/openstudio/workflow/registry.rb +137 -0
- data/lib/openstudio/workflow/run.rb +182 -221
- data/lib/openstudio/workflow/time_logger.rb +1 -1
- data/lib/openstudio/workflow/util/energyplus.rb +564 -0
- data/lib/openstudio/workflow/util/io.rb +33 -0
- data/lib/openstudio/workflow/util/measure.rb +520 -0
- data/lib/openstudio/workflow/util/model.rb +100 -0
- data/lib/openstudio/workflow/util/post_process.rb +177 -0
- data/lib/openstudio/workflow/util/weather_file.rb +108 -0
- data/lib/openstudio/workflow/util.rb +14 -0
- data/lib/openstudio/workflow/version.rb +1 -1
- data/lib/openstudio/workflow_json.rb +399 -0
- data/lib/openstudio/workflow_runner.rb +213 -0
- data/lib/openstudio-workflow.rb +13 -118
- metadata +45 -85
- data/lib/openstudio/extended_runner.rb +0 -105
- data/lib/openstudio/workflow/adapters/local.rb +0 -101
- data/lib/openstudio/workflow/adapters/mongo.rb +0 -227
- data/lib/openstudio/workflow/jobs/lib/apply_measures.rb +0 -253
- data/lib/openstudio/workflow/jobs/run_energyplus/run_energyplus.rb +0 -314
- data/lib/openstudio/workflow/jobs/run_openstudio/run_openstudio.rb +0 -230
- data/lib/openstudio/workflow/jobs/run_postprocess/run_postprocess.rb +0 -110
- data/lib/openstudio/workflow/jobs/run_reporting_measures/run_reporting_measures.rb +0 -471
- data/lib/openstudio/workflow/jobs/run_runmanager/run_runmanager.rb +0 -247
- data/lib/openstudio/workflow/jobs/run_xml/run_xml.rb +0 -279
@@ -0,0 +1,520 @@
|
|
1
|
+
module OpenStudio
|
2
|
+
module Workflow
|
3
|
+
module Util
|
4
|
+
|
5
|
+
# Handles all interaction with measure objects in the gem. This includes measure.xml and measure.rb files
|
6
|
+
#
|
7
|
+
module Measure
|
8
|
+
|
9
|
+
# Wrapper method around #apply_measure to allow all measures of a type to be executed
|
10
|
+
#
|
11
|
+
# @param [String] measure_type Accepts OpenStudio::MeasureType argument
|
12
|
+
# @param [Object] registry Hash access to objects
|
13
|
+
# @param [Hash] options ({}) User-specified options used to override defaults
|
14
|
+
# @option options [Object] :time_logger A special logger used to debug performance issues
|
15
|
+
# @option options [Object] :output_adapter An output adapter to register measure transitions to
|
16
|
+
# @option options [Object] :energyplus_output_requests If true then the energyPlusOutputRequests is called instead of the run method
|
17
|
+
# @return [Void]
|
18
|
+
#
|
19
|
+
def apply_measures(measure_type, registry, options = {})
|
20
|
+
|
21
|
+
# DLM: time_logger is in the registry but docs say it is in options?
|
22
|
+
registry[:time_logger].start "#{measure_type.valueName}:apply_measures" if registry[:time_logger]
|
23
|
+
|
24
|
+
logger = registry[:logger]
|
25
|
+
workflow_json = registry[:workflow_json]
|
26
|
+
|
27
|
+
workflow_steps = workflow_json.workflowSteps
|
28
|
+
fail "The 'steps' array of the OSW is required." unless workflow_steps
|
29
|
+
|
30
|
+
logger.debug "Finding measures of type #{measure_type.valueName}"
|
31
|
+
workflow_steps.each do |step|
|
32
|
+
|
33
|
+
if @registry[:openstudio_2]
|
34
|
+
if !step.to_MeasureStep.empty?
|
35
|
+
step = step.to_MeasureStep.get
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
measure_dir_name = step.measureDirName
|
40
|
+
|
41
|
+
measure_dir = workflow_json.findMeasure(measure_dir_name)
|
42
|
+
fail "Cannot find #{measure_dir_name}" if measure_dir.empty?
|
43
|
+
measure_dir = measure_dir.get
|
44
|
+
|
45
|
+
measure = OpenStudio::BCLMeasure.load(measure_dir)
|
46
|
+
fail "Cannot load measure at #{measure_dir}" if measure.empty?
|
47
|
+
measure = measure.get
|
48
|
+
|
49
|
+
class_name = measure.className
|
50
|
+
measure_instance_type = measure.measureType
|
51
|
+
if measure_instance_type == measure_type
|
52
|
+
if options[:energyplus_output_requests]
|
53
|
+
logger.info "Found measure #{class_name} of type #{measure_type.valueName}. Collecting EnergyPlus Output Requests now."
|
54
|
+
apply_measure(registry, step, options)
|
55
|
+
else
|
56
|
+
logger.info "Found measure #{class_name} of type #{measure_type.valueName}. Applying now."
|
57
|
+
|
58
|
+
# DLM: why is output_adapter in options instead of registry?
|
59
|
+
options[:output_adapter].communicate_transition("Applying #{class_name}", :measure) if options[:output_adapter]
|
60
|
+
apply_measure(registry, step, options)
|
61
|
+
options[:output_adapter].communicate_transition("Applied #{class_name}", :measure) if options[:output_adapter]
|
62
|
+
end
|
63
|
+
|
64
|
+
logger.info 'Moving to the next workflow step.'
|
65
|
+
else
|
66
|
+
logger.debug "Passing measure #{class_name} of type #{measure_type.valueName}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
registry[:time_logger].stop "#{measure_type.valueName}:apply_measures" if registry[:time_logger]
|
71
|
+
end
|
72
|
+
|
73
|
+
# Determine if a given workflow can find and load all measures defined in steps
|
74
|
+
#
|
75
|
+
# @param [Hash] workflow See the schema for an OSW defined in the spec folder of this repo. Note that this
|
76
|
+
# method requires the OSW to have been loaded with symbolized keys
|
77
|
+
# @param [String] directory The directory that will be passed to the find_measure_dir method
|
78
|
+
# @return [true] If the method doesn't fail the workflow measures were validated
|
79
|
+
#
|
80
|
+
def validate_measures(registry, logger)
|
81
|
+
|
82
|
+
logger = registry[:logger] if logger.nil?
|
83
|
+
workflow_json = registry[:workflow_json]
|
84
|
+
|
85
|
+
state = 'ModelMeasure'.to_MeasureType
|
86
|
+
steps = workflow_json.workflowSteps
|
87
|
+
steps.each_with_index do |step, index|
|
88
|
+
begin
|
89
|
+
logger.debug "Validating step #{index}"
|
90
|
+
|
91
|
+
if @registry[:openstudio_2]
|
92
|
+
if !step.to_MeasureStep.empty?
|
93
|
+
step = step.to_MeasureStep.get
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Verify the existence of the required files
|
98
|
+
measure_dir_name = step.measureDirName
|
99
|
+
|
100
|
+
measure_dir = workflow_json.findMeasure(measure_dir_name)
|
101
|
+
fail "Cannot find measure #{measure_dir_name}" if measure_dir.empty?
|
102
|
+
measure_dir = measure_dir.get
|
103
|
+
|
104
|
+
measure = OpenStudio::BCLMeasure.load(measure_dir)
|
105
|
+
fail "Cannot load measure at #{measure_dir}" if measure.empty?
|
106
|
+
measure = measure.get
|
107
|
+
|
108
|
+
class_name = measure.className
|
109
|
+
measure_instance_type = measure.measureType
|
110
|
+
|
111
|
+
# Ensure that measures are in order, i.e. no OS after E+, E+ or OS after Reporting
|
112
|
+
if measure_instance_type == 'ModelMeasure'.to_MeasureType
|
113
|
+
fail "OpenStudio measure #{measure_dir} called after transition to EnergyPlus." if state != 'ModelMeasure'.to_MeasureType
|
114
|
+
elsif measure_instance_type == "EnergyPlusMeasure".to_MeasureType
|
115
|
+
state = 'EnergyPlusMeasure'.to_MeasureType if state == 'ModelMeasure'.to_MeasureType
|
116
|
+
fail "EnergyPlus measure #{measure_dir} called after Energyplus simulation." if state == 'ReportingMeasure'.to_MeasureType
|
117
|
+
elsif measure_instance_type == 'ReportingMeasure'.to_MeasureType
|
118
|
+
state = 'ReportingMeasure'.to_MeasureType if state == 'EnergyPlusMeasure'.to_MeasureType
|
119
|
+
else
|
120
|
+
fail "Error: MeasureType #{measure_instance_type.valueName} of measure #{measure_dir} is not supported"
|
121
|
+
end
|
122
|
+
|
123
|
+
logger.debug "Validated step #{index}"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Sets the argument map for argument_map argument pair
|
129
|
+
#
|
130
|
+
# @param [Object] argument_map See the OpenStudio SDK for a description of the OSArgumentMap structure
|
131
|
+
# @param [Object] argument_name, user defined argument name
|
132
|
+
# @param [Object] argument_value, user defined argument value
|
133
|
+
# @param [Object] logger, logger object
|
134
|
+
# @return [Object] Returns an updated ArgumentMap object
|
135
|
+
#
|
136
|
+
def apply_arguments(argument_map, argument_name, argument_value, logger)
|
137
|
+
unless argument_value.nil?
|
138
|
+
logger.info "Setting argument value '#{argument_name}' to '#{argument_value}'"
|
139
|
+
|
140
|
+
v = argument_map[argument_name.to_s]
|
141
|
+
fail "Could not find argument '#{argument_name}' in argument_map" unless v
|
142
|
+
value_set = v.setValue(argument_value)
|
143
|
+
fail "Could not set argument '#{argument_name}' to value '#{argument_value}'" unless value_set
|
144
|
+
argument_map[argument_name.to_s] = v.clone
|
145
|
+
else
|
146
|
+
logger.warn "Value for argument '#{argument_name}' not set in argument list therefore will use default"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def apply_arguments_2(argument_map, argument_name, argument_value, logger)
|
151
|
+
unless argument_value.nil?
|
152
|
+
logger.info "Setting argument value '#{argument_name}' to '#{argument_value}'"
|
153
|
+
|
154
|
+
v = argument_map[argument_name.to_s]
|
155
|
+
fail "Could not find argument '#{argument_name}' in argument_map" unless v
|
156
|
+
value_set = false
|
157
|
+
variant_type = argument_value.variantType
|
158
|
+
if variant_type == "String".to_VariantType
|
159
|
+
argument_value = argument_value.valueAsString
|
160
|
+
value_set = v.setValue(argument_value)
|
161
|
+
elsif variant_type == "Double".to_VariantType
|
162
|
+
argument_value = argument_value.valueAsDouble
|
163
|
+
value_set = v.setValue(argument_value)
|
164
|
+
elsif variant_type == "Integer".to_VariantType
|
165
|
+
argument_value = argument_value.valueAsInteger
|
166
|
+
value_set = v.setValue(argument_value)
|
167
|
+
elsif variant_type == "Boolean".to_VariantType
|
168
|
+
argument_value = argument_value.valueAsBoolean
|
169
|
+
value_set = v.setValue(argument_value)
|
170
|
+
end
|
171
|
+
fail "Could not set argument '#{argument_name}' to value '#{argument_value}'" unless value_set
|
172
|
+
argument_map[argument_name.to_s] = v.clone
|
173
|
+
else
|
174
|
+
logger.warn "Value for argument '#{argument_name}' not set in argument list therefore will use default"
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# Method to allow for a single measure of any type to be run
|
179
|
+
#
|
180
|
+
# @param [String] directory Location of the datapoint directory to run. This is needed
|
181
|
+
# independent of the adapter that is being used. Note that the simulation will actually run in 'run'
|
182
|
+
# @param [Object] adapter An instance of the adapter class
|
183
|
+
# @param [String] current_weather_filepath The path which will be used to set the runner and returned to update
|
184
|
+
# the OSW for future measures and the simulation
|
185
|
+
# @param [Object] model The model object being used in the measure, either a OSM or IDF
|
186
|
+
# @param [Hash] step Definition of the to be run by the workflow
|
187
|
+
# @option step [String] :measure_dir_name The name of the directory which contains the measure files
|
188
|
+
# @option step [Object] :arguments name value hash which defines the arguments to the measure, e.g.
|
189
|
+
# {has_bool: true, cost: 3.1}
|
190
|
+
# @param output_attributes [Hash] The results of previous measure applications which are persisted through the
|
191
|
+
# runner to allow measures to react to previous events in the workflow
|
192
|
+
# @param [Hash] options ({}) User-specified options used to override defaults
|
193
|
+
# @option options [Array] :measure_search_array Ordered set of measure directories used to search for
|
194
|
+
# step[:measure_dir_name], e.g. ['measures', '../../measures']
|
195
|
+
# @option options [Object] :time_logger Special logger used to debug performance issues
|
196
|
+
# @option options [Object] :energyplus_output_requests If true then the energyPlusOutputRequests is called instead of the run method
|
197
|
+
# @return [Hash, String] Returns two objects. The first is the (potentially) updated output_attributes hash, and
|
198
|
+
# the second is the (potentially) updated current_weather_filepath
|
199
|
+
#
|
200
|
+
def apply_measure(registry, step, options = {})
|
201
|
+
|
202
|
+
logger = registry[:logger]
|
203
|
+
runner = registry[:runner]
|
204
|
+
workflow_json = registry[:workflow_json]
|
205
|
+
measure_dir_name = step.measureDirName
|
206
|
+
|
207
|
+
run_dir = registry[:run_dir]
|
208
|
+
fail 'No run directory set in the registry' unless run_dir
|
209
|
+
|
210
|
+
output_attributes = registry[:output_attributes]
|
211
|
+
|
212
|
+
# todo: get weather file from appropriate location
|
213
|
+
@wf = registry[:wf]
|
214
|
+
@model = registry[:model]
|
215
|
+
@model_idf = registry[:model_idf]
|
216
|
+
@sql_filename = registry[:sql]
|
217
|
+
|
218
|
+
runner.setLastOpenStudioModel(@model) if @model
|
219
|
+
#runner.setLastOpenStudioModelPath(const openstudio::path& lastOpenStudioModelPath); #DLM - deprecate?
|
220
|
+
runner.setLastEnergyPlusWorkspace(@model_idf) if @model_idf
|
221
|
+
#runner.setLastEnergyPlusWorkspacePath(const openstudio::path& lastEnergyPlusWorkspacePath); #DLM - deprecate?
|
222
|
+
runner.setLastEnergyPlusSqlFilePath(@sql_filename) if @sql_filename
|
223
|
+
runner.setLastEpwFilePath(@wf) if @wf
|
224
|
+
|
225
|
+
logger.debug "Starting #{__method__} for #{measure_dir_name}"
|
226
|
+
registry[:time_logger].start("Measure:#{measure_dir_name}") if registry[:time_logger]
|
227
|
+
current_dir = Dir.pwd
|
228
|
+
|
229
|
+
success = nil
|
230
|
+
begin
|
231
|
+
|
232
|
+
measure_dir = workflow_json.findMeasure(measure_dir_name)
|
233
|
+
fail "Cannot find #{measure_dir_name}" if measure_dir.empty?
|
234
|
+
measure_dir = measure_dir.get
|
235
|
+
|
236
|
+
measure = OpenStudio::BCLMeasure.load(measure_dir)
|
237
|
+
fail "Cannot load measure at #{measure_dir}" if measure.empty?
|
238
|
+
measure = measure.get
|
239
|
+
|
240
|
+
step_index = workflow_json.currentStepIndex
|
241
|
+
|
242
|
+
measure_run_dir = File.join(run_dir, "#{step_index.to_s.rjust(3,'0')}_#{measure_dir_name}")
|
243
|
+
logger.debug "Creating run directory for measure in #{measure_run_dir}"
|
244
|
+
FileUtils.mkdir_p measure_run_dir
|
245
|
+
Dir.chdir measure_run_dir
|
246
|
+
|
247
|
+
if options[:energyplus_output_requests]
|
248
|
+
logger.debug "energyPlusOutputRequests running in #{Dir.pwd}"
|
249
|
+
else
|
250
|
+
logger.debug "Apply measure running in #{Dir.pwd}"
|
251
|
+
end
|
252
|
+
|
253
|
+
class_name = measure.className
|
254
|
+
measure_type = measure.measureType
|
255
|
+
|
256
|
+
measure_path = measure.primaryRubyScriptPath
|
257
|
+
fail "Measure does not have a primary ruby script specified" if measure_path.empty?
|
258
|
+
measure_path = measure_path.get
|
259
|
+
fail "#{measure_path} file does not exist" unless File.exist?(measure_path.to_s)
|
260
|
+
|
261
|
+
logger.debug "Loading Measure from #{measure_path}"
|
262
|
+
|
263
|
+
measure_object = nil
|
264
|
+
result = nil
|
265
|
+
begin
|
266
|
+
load measure_path.to_s
|
267
|
+
measure_object = Object.const_get(class_name).new
|
268
|
+
rescue => e
|
269
|
+
|
270
|
+
# add the error to the osw.out
|
271
|
+
runner.registerError("#{e.message}\n\t#{e.backtrace.join("\n\t")}")
|
272
|
+
|
273
|
+
# @todo (rhorsey) Clean up the error class here.
|
274
|
+
log_message = "Error requiring measure #{__FILE__}. Failed with #{e.message}, #{e.backtrace.join("\n")}"
|
275
|
+
raise log_message
|
276
|
+
end
|
277
|
+
|
278
|
+
arguments = nil
|
279
|
+
skip_measure = false
|
280
|
+
begin
|
281
|
+
|
282
|
+
# Initialize arguments which may be model dependent, don't allow arguments method access to real model in case it changes something
|
283
|
+
if measure_type == 'ModelMeasure'.to_MeasureType
|
284
|
+
arguments = measure_object.arguments(@model.clone(true).to_Model)
|
285
|
+
elsif measure_type == 'EnergyPlusMeasure'.to_MeasureType
|
286
|
+
arguments = measure_object.arguments(@model_idf.clone(true))
|
287
|
+
else measure_type == 'ReportingMeasure'.to_MeasureType
|
288
|
+
arguments = measure_object.arguments
|
289
|
+
end
|
290
|
+
|
291
|
+
# Create argument map and initialize all the arguments
|
292
|
+
argument_map = OpenStudio::Ruleset::OSArgumentMap.new
|
293
|
+
if arguments
|
294
|
+
arguments.each do |v|
|
295
|
+
argument_map[v.name] = v.clone
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
# Set argument values if they exist
|
300
|
+
logger.debug "Iterating over arguments for workflow item '#{measure_dir_name}'"
|
301
|
+
if step.arguments
|
302
|
+
step.arguments.each do |argument_name, argument_value|
|
303
|
+
if argument_name.to_s == '__SKIP__'
|
304
|
+
if registry[:openstudio_2]
|
305
|
+
variant_type = argument_value.variantType
|
306
|
+
if variant_type == "String".to_VariantType
|
307
|
+
argument_value = argument_value.valueAsString
|
308
|
+
elsif variant_type == "Double".to_VariantType
|
309
|
+
argument_value = argument_value.valueAsDouble
|
310
|
+
elsif variant_type == "Integer".to_VariantType
|
311
|
+
argument_value = argument_value.valueAsInteger
|
312
|
+
elsif variant_type == "Boolean".to_VariantType
|
313
|
+
argument_value = argument_value.valueAsBoolean
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
if argument_value.class == String
|
318
|
+
argument_value = argument_value.downcase
|
319
|
+
if argument_value == "false"
|
320
|
+
skip_measure = false
|
321
|
+
else
|
322
|
+
skip_measure = true
|
323
|
+
end
|
324
|
+
elsif argument_value.class == Fixnum
|
325
|
+
skip_measure = (argument_value != 0)
|
326
|
+
elsif argument_value.class == Float
|
327
|
+
skip_measure = (argument_value != 0.0)
|
328
|
+
elsif argument_value.class == FalseClass
|
329
|
+
skip_measure = false
|
330
|
+
elsif argument_value.class == TrueClass
|
331
|
+
skip_measure = true
|
332
|
+
elsif argument_value.class == NilClass
|
333
|
+
skip_measure = false
|
334
|
+
end
|
335
|
+
else
|
336
|
+
# regular argument
|
337
|
+
if registry[:openstudio_2]
|
338
|
+
success = apply_arguments_2(argument_map, argument_name, argument_value, logger)
|
339
|
+
else
|
340
|
+
success = apply_arguments(argument_map, argument_name, argument_value, logger)
|
341
|
+
end
|
342
|
+
fail 'Could not set arguments' unless success
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
# map any choice display names to choice values, in either set values or defaults
|
348
|
+
argument_map.each_key do |argument_name|
|
349
|
+
v = argument_map[argument_name]
|
350
|
+
choice_values = v.choiceValues
|
351
|
+
if !choice_values.empty?
|
352
|
+
value = nil
|
353
|
+
value = v.defaultValueAsString if v.hasDefaultValue
|
354
|
+
value = v.valueAsString if v.hasValue
|
355
|
+
if value && choice_values.index(value).nil?
|
356
|
+
display_names = v.choiceValueDisplayNames
|
357
|
+
i = display_names.index(value)
|
358
|
+
if i && choice_values[i]
|
359
|
+
logger.debug "Mapping display name '#{value}' to value '#{choice_values[i]}' for argument '#{argument_name}'"
|
360
|
+
value_set = v.setValue(choice_values[i])
|
361
|
+
fail "Could not set argument '#{argument_name}' to mapped value '#{choice_values[i]}'" unless value_set
|
362
|
+
argument_map[argument_name.to_s] = v.clone
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
rescue => e
|
369
|
+
|
370
|
+
# add the error to the osw.out
|
371
|
+
runner.registerError("#{e.message}\n\t#{e.backtrace.join("\n\t")}")
|
372
|
+
|
373
|
+
log_message = "Error assigning argument in measure #{__FILE__}. Failed with #{e.message}, #{e.backtrace.join("\n")}"
|
374
|
+
raise log_message
|
375
|
+
end
|
376
|
+
|
377
|
+
if skip_measure
|
378
|
+
if !options[:energyplus_output_requests]
|
379
|
+
# just increment
|
380
|
+
logger.debug "Skipping measure '#{measure_dir_name}'"
|
381
|
+
runner.prepareForUserScriptRun(measure_object)
|
382
|
+
runner.validateUserArguments(arguments, argument_map)
|
383
|
+
current_result = runner.result
|
384
|
+
runner.incrementStep
|
385
|
+
current_result.setStepResult('Skip'.to_StepResult)
|
386
|
+
end
|
387
|
+
else
|
388
|
+
|
389
|
+
begin
|
390
|
+
if options[:energyplus_output_requests]
|
391
|
+
logger.debug "Calling measure.energyPlusOutputRequests for '#{measure_dir_name}'"
|
392
|
+
idf_objects = measure_object.energyPlusOutputRequests(runner, argument_map)
|
393
|
+
num_added = 0
|
394
|
+
idf_objects.each do |idf_object|
|
395
|
+
num_added += OpenStudio::Workflow::Util::EnergyPlus.add_energyplus_output_request(@model_idf, idf_object)
|
396
|
+
end
|
397
|
+
logger.debug "Finished measure.energyPlusOutputRequests for '#{measure_dir_name}', #{num_added} output requests added"
|
398
|
+
else
|
399
|
+
logger.debug "Calling measure.run for '#{measure_dir_name}'"
|
400
|
+
if measure_type == 'ModelMeasure'.to_MeasureType
|
401
|
+
measure_object.run(@model, runner, argument_map)
|
402
|
+
elsif measure_type == 'EnergyPlusMeasure'.to_MeasureType
|
403
|
+
measure_object.run(@model_idf, runner, argument_map)
|
404
|
+
elsif measure_type == 'ReportingMeasure'.to_MeasureType
|
405
|
+
measure_object.run(runner, argument_map)
|
406
|
+
end
|
407
|
+
logger.debug "Finished measure.run for '#{measure_dir_name}'"
|
408
|
+
end
|
409
|
+
|
410
|
+
# Run garbage collector after every measure to help address race conditions
|
411
|
+
GC.start
|
412
|
+
rescue => e
|
413
|
+
|
414
|
+
# add the error to the osw.out
|
415
|
+
runner.registerError("#{e.message}\n\t#{e.backtrace.join("\n\t")}")
|
416
|
+
|
417
|
+
if !options[:energyplus_output_requests]
|
418
|
+
# incrementStep must be called after run
|
419
|
+
runner.incrementStep
|
420
|
+
end
|
421
|
+
|
422
|
+
log_message = "Runner error #{__FILE__} failed with #{e.message}, #{e.backtrace.join("\n")}"
|
423
|
+
raise log_message
|
424
|
+
end
|
425
|
+
|
426
|
+
# if doing output requests we are done now
|
427
|
+
if options[:energyplus_output_requests]
|
428
|
+
registry.register(:model_idf) { @model_idf }
|
429
|
+
return
|
430
|
+
end
|
431
|
+
|
432
|
+
result = nil
|
433
|
+
begin
|
434
|
+
result = runner.result
|
435
|
+
|
436
|
+
# incrementStep must be called after run
|
437
|
+
runner.incrementStep
|
438
|
+
|
439
|
+
errors = result.stepErrors
|
440
|
+
|
441
|
+
fail "Measure #{measure_dir_name} reported an error with #{errors}" if errors.size != 0
|
442
|
+
logger.debug "Running of measure '#{measure_dir_name}' completed. Post-processing measure output"
|
443
|
+
|
444
|
+
# TODO: fix this
|
445
|
+
#unless @wf == runner.weatherfile_path
|
446
|
+
# logger.debug "Updating the weather file to be '#{runner.weatherfile_path}'"
|
447
|
+
# registry.register(:wf) { runner.weatherfile_path }
|
448
|
+
#end
|
449
|
+
|
450
|
+
# @todo add note about why reassignment and not eval
|
451
|
+
registry.register(:model) { @model }
|
452
|
+
registry.register(:model_idf) { @model_idf }
|
453
|
+
registry.register(:sql) { @sql_filename }
|
454
|
+
|
455
|
+
rescue => e
|
456
|
+
log_message = "Runner error #{__FILE__} failed with #{e.message}, #{e.backtrace.join("\n")}"
|
457
|
+
raise log_message
|
458
|
+
end
|
459
|
+
|
460
|
+
# DLM: this section creates the measure_attributes.json file which should be deprecated
|
461
|
+
begin
|
462
|
+
# DLM: which name do we want?
|
463
|
+
measure_name = class_name
|
464
|
+
#measure_name = measure_dir_name
|
465
|
+
|
466
|
+
# DLM: do measure results from sequential measures with the same name clobber each other?
|
467
|
+
output_attributes[measure_name.to_sym] = {} if output_attributes[measure_name.to_sym].nil?
|
468
|
+
|
469
|
+
result.stepValues.each do |step_value|
|
470
|
+
step_value_name = step_value.name
|
471
|
+
step_value_type = step_value.variantType
|
472
|
+
|
473
|
+
value = nil
|
474
|
+
if (step_value_type == "String".to_VariantType)
|
475
|
+
value = step_value.valueAsString
|
476
|
+
elsif (step_value_type == "Double".to_VariantType)
|
477
|
+
value = step_value.valueAsDouble
|
478
|
+
elsif (step_value_type == "Integer".to_VariantType)
|
479
|
+
value = step_value.valueAsInteger
|
480
|
+
elsif (step_value_type == "Boolean".to_VariantType)
|
481
|
+
value = step_value.valueAsBoolean
|
482
|
+
end
|
483
|
+
|
484
|
+
output_attributes[measure_name.to_sym][step_value_name] = value
|
485
|
+
end
|
486
|
+
|
487
|
+
# Add an applicability flag to all the measure results
|
488
|
+
step_result = result.stepResult
|
489
|
+
fail "Step Result not set" if step_result.empty?
|
490
|
+
step_result = step_result.get
|
491
|
+
|
492
|
+
if (step_result == "Skip".to_StepResult) || (step_result == "NA".to_StepResult)
|
493
|
+
output_attributes[measure_name.to_sym][:applicable] = false
|
494
|
+
else
|
495
|
+
output_attributes[measure_name.to_sym][:applicable] = true
|
496
|
+
end
|
497
|
+
registry.register(:output_attributes) { output_attributes }
|
498
|
+
rescue => e
|
499
|
+
log_message = "#{__FILE__} failed with #{e.message}, #{e.backtrace.join("\n")}"
|
500
|
+
logger.error log_message
|
501
|
+
raise log_message
|
502
|
+
end
|
503
|
+
|
504
|
+
end
|
505
|
+
|
506
|
+
rescue => e
|
507
|
+
log_message = "#{__FILE__} failed with message #{e.message} in #{e.backtrace.join("\n")}"
|
508
|
+
logger.error log_message
|
509
|
+
raise log_message
|
510
|
+
ensure
|
511
|
+
Dir.chdir current_dir
|
512
|
+
registry[:time_logger].stop("Measure:#{measure_dir_name}") if registry[:time_logger]
|
513
|
+
|
514
|
+
logger.info "Finished #{__method__} for #{measure_dir_name} in #{@registry[:time_logger].delta("Measure:#{measure_dir_name}")} s"
|
515
|
+
end
|
516
|
+
end
|
517
|
+
end
|
518
|
+
end
|
519
|
+
end
|
520
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module OpenStudio
|
2
|
+
module Workflow
|
3
|
+
module Util
|
4
|
+
# Manages routine tasks involving OpenStudio::Model or OpenStudio::Workflow objects, such as loading, saving, and
|
5
|
+
# translating them.
|
6
|
+
#
|
7
|
+
module Model
|
8
|
+
# Method to create / load an OSM file
|
9
|
+
#
|
10
|
+
# @param [String] osm_path The full path to an OSM file to load
|
11
|
+
# @param [Object] logger An optional logger to use for finding the OSM model
|
12
|
+
# @return [Object] The return from this method is a loaded OSM or a failure.
|
13
|
+
#
|
14
|
+
def load_osm(osm_path, logger)
|
15
|
+
logger.info 'Loading OSM model'
|
16
|
+
|
17
|
+
# Load the model and return it
|
18
|
+
logger.info "Reading in OSM model #{osm_path}"
|
19
|
+
|
20
|
+
loaded_model = nil
|
21
|
+
begin
|
22
|
+
translator = OpenStudio::OSVersion::VersionTranslator.new
|
23
|
+
loaded_model = translator.loadModel(osm_path)
|
24
|
+
rescue
|
25
|
+
# TODO: get translator working in embedded.
|
26
|
+
# Need to embed idd files
|
27
|
+
logger.warn 'OpenStudio VersionTranslator could not be loaded'
|
28
|
+
loaded_model = OpenStudio::Model::Model.load(osm_path)
|
29
|
+
end
|
30
|
+
raise "Failed to load OSM file #{osm_path}" if loaded_model.empty?
|
31
|
+
loaded_model.get
|
32
|
+
end
|
33
|
+
|
34
|
+
# Method to create / load an IDF file
|
35
|
+
#
|
36
|
+
# @param [String] idf_path Full path to the IDF
|
37
|
+
# @param [Object] logger An optional logger to use for finding the idf model
|
38
|
+
# @return [Object] The return from this method is a loaded IDF or a failure.
|
39
|
+
#
|
40
|
+
def load_idf(idf_path, logger)
|
41
|
+
logger.info 'Loading IDF model'
|
42
|
+
|
43
|
+
# Load the IDF into a workspace object and return it
|
44
|
+
logger.info "Reading in IDF model #{idf_path}"
|
45
|
+
|
46
|
+
idf = OpenStudio::Workspace.load(idf_path).get
|
47
|
+
raise "Failed to load IDF file #{idf_path}" if idf.empty?
|
48
|
+
idf.get
|
49
|
+
end
|
50
|
+
|
51
|
+
# Translates a OpenStudio model object into an OpenStudio IDF object
|
52
|
+
#
|
53
|
+
# @param [Object] model the OpenStudio::Model instance to translate into an OpenStudio::Workspace object -- see
|
54
|
+
# the OpenStudio SDK for details on the process
|
55
|
+
# @return [Object] Returns and OpenStudio::Workspace object
|
56
|
+
# @todo (rhorsey) rescue errors here
|
57
|
+
#
|
58
|
+
def translate_to_energyplus(model, logger = nil)
|
59
|
+
logger = ::Logger.new(STDOUT) unless logger
|
60
|
+
logger.info 'Translate object to EnergyPlus IDF in preparation for EnergyPlus'
|
61
|
+
a = ::Time.now
|
62
|
+
# ensure objects exist for reporting purposes
|
63
|
+
model.getFacility
|
64
|
+
model.getBuilding
|
65
|
+
forward_translator = OpenStudio::EnergyPlus::ForwardTranslator.new
|
66
|
+
model_idf = forward_translator.translateModel(model)
|
67
|
+
b = ::Time.now
|
68
|
+
logger.info "Translate object to EnergyPlus IDF took #{b.to_f - a.to_f}"
|
69
|
+
model_idf
|
70
|
+
end
|
71
|
+
|
72
|
+
# Saves an OpenStudio model object to file
|
73
|
+
#
|
74
|
+
# @param [Object] model The OpenStudio::Model instance to save to file
|
75
|
+
# @param [String] save_directory Folder to save the model in
|
76
|
+
# @param [String] name ('in.osm') Option to define a non-standard name
|
77
|
+
# @return [String] OSM file name
|
78
|
+
#
|
79
|
+
def save_osm(model, save_directory, name = 'in.osm')
|
80
|
+
osm_filename = File.join(save_directory.to_s, name.to_s)
|
81
|
+
File.open(osm_filename, 'w') { |f| f << model.to_s }
|
82
|
+
osm_filename
|
83
|
+
end
|
84
|
+
|
85
|
+
# Saves an OpenStudio IDF model object to file
|
86
|
+
#
|
87
|
+
# @param [Object] model The OpenStudio::Workspace instance to save to file
|
88
|
+
# @param [String] save_directory Folder to save the model in
|
89
|
+
# @param [String] name ('in.osm') Option to define a non-standard name
|
90
|
+
# @return [String] IDF file name
|
91
|
+
#
|
92
|
+
def save_idf(model_idf, save_directory, name = 'in.idf')
|
93
|
+
idf_filename = File.join(save_directory.to_s, name.to_s)
|
94
|
+
File.open(idf_filename, 'w') { |f| f << model_idf.to_s }
|
95
|
+
idf_filename
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|