openstudio-workflow 1.2.0 → 1.2.1

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