openstudio-workflow 1.0.0.pat1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/README.md +16 -68
  4. data/Rakefile +9 -9
  5. data/bin/openstudio_cli +786 -0
  6. data/lib/openstudio/workflow/adapters/input/local.rb +97 -0
  7. data/lib/openstudio/workflow/adapters/output/local.rb +90 -0
  8. data/lib/openstudio/workflow/adapters/output/socket.rb +70 -0
  9. data/lib/openstudio/workflow/{jobs/run_preflight/run_preflight.rb → adapters/output/web.rb} +37 -19
  10. data/lib/openstudio/workflow/{adapter.rb → adapters/output_adapter.rb} +53 -51
  11. data/lib/openstudio/workflow/job.rb +22 -0
  12. data/lib/openstudio/workflow/jobs/{run_energyplus → resources}/monthly_report.idf +0 -0
  13. data/lib/openstudio/workflow/jobs/run_energyplus.rb +49 -0
  14. data/lib/openstudio/workflow/jobs/run_ep_measures.rb +55 -0
  15. data/lib/openstudio/workflow/jobs/run_initialization.rb +136 -0
  16. data/lib/openstudio/workflow/jobs/run_os_measures.rb +59 -0
  17. data/lib/openstudio/workflow/jobs/run_postprocess.rb +53 -0
  18. data/lib/openstudio/workflow/jobs/run_preprocess.rb +81 -0
  19. data/lib/openstudio/workflow/jobs/run_reporting_measures.rb +86 -0
  20. data/lib/openstudio/workflow/jobs/run_translation.rb +49 -0
  21. data/lib/openstudio/workflow/multi_delegator.rb +1 -3
  22. data/lib/openstudio/workflow/registry.rb +137 -0
  23. data/lib/openstudio/workflow/run.rb +182 -221
  24. data/lib/openstudio/workflow/time_logger.rb +1 -1
  25. data/lib/openstudio/workflow/util/energyplus.rb +564 -0
  26. data/lib/openstudio/workflow/util/io.rb +33 -0
  27. data/lib/openstudio/workflow/util/measure.rb +520 -0
  28. data/lib/openstudio/workflow/util/model.rb +100 -0
  29. data/lib/openstudio/workflow/util/post_process.rb +177 -0
  30. data/lib/openstudio/workflow/util/weather_file.rb +108 -0
  31. data/lib/openstudio/workflow/util.rb +14 -0
  32. data/lib/openstudio/workflow/version.rb +1 -1
  33. data/lib/openstudio/workflow_json.rb +399 -0
  34. data/lib/openstudio/workflow_runner.rb +213 -0
  35. data/lib/openstudio-workflow.rb +13 -118
  36. metadata +45 -85
  37. data/lib/openstudio/extended_runner.rb +0 -105
  38. data/lib/openstudio/workflow/adapters/local.rb +0 -101
  39. data/lib/openstudio/workflow/adapters/mongo.rb +0 -227
  40. data/lib/openstudio/workflow/jobs/lib/apply_measures.rb +0 -253
  41. data/lib/openstudio/workflow/jobs/run_energyplus/run_energyplus.rb +0 -314
  42. data/lib/openstudio/workflow/jobs/run_openstudio/run_openstudio.rb +0 -230
  43. data/lib/openstudio/workflow/jobs/run_postprocess/run_postprocess.rb +0 -110
  44. data/lib/openstudio/workflow/jobs/run_reporting_measures/run_reporting_measures.rb +0 -471
  45. data/lib/openstudio/workflow/jobs/run_runmanager/run_runmanager.rb +0 -247
  46. 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