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