openstudio-workflow 1.2.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
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