openstudio-workflow 1.2.1 → 1.2.2

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 +93 -48
  4. data/Rakefile +36 -36
  5. data/lib/openstudio-workflow.rb +49 -49
  6. data/lib/openstudio/workflow/adapters/input/local.rb +244 -240
  7. data/lib/openstudio/workflow/adapters/output/local.rb +95 -95
  8. data/lib/openstudio/workflow/adapters/output/socket.rb +91 -91
  9. data/lib/openstudio/workflow/adapters/output/web.rb +66 -66
  10. data/lib/openstudio/workflow/adapters/output_adapter.rb +147 -147
  11. data/lib/openstudio/workflow/job.rb +22 -22
  12. data/lib/openstudio/workflow/jobs/resources/monthly_report.idf +222 -222
  13. data/lib/openstudio/workflow/jobs/run_energyplus.rb +49 -49
  14. data/lib/openstudio/workflow/jobs/run_ep_measures.rb +55 -55
  15. data/lib/openstudio/workflow/jobs/run_initialization.rb +169 -167
  16. data/lib/openstudio/workflow/jobs/run_os_measures.rb +69 -69
  17. data/lib/openstudio/workflow/jobs/run_postprocess.rb +53 -53
  18. data/lib/openstudio/workflow/jobs/run_preprocess.rb +69 -69
  19. data/lib/openstudio/workflow/jobs/run_reporting_measures.rb +98 -98
  20. data/lib/openstudio/workflow/jobs/run_translation.rb +61 -61
  21. data/lib/openstudio/workflow/multi_delegator.rb +46 -46
  22. data/lib/openstudio/workflow/registry.rb +137 -137
  23. data/lib/openstudio/workflow/run.rb +299 -299
  24. data/lib/openstudio/workflow/time_logger.rb +53 -53
  25. data/lib/openstudio/workflow/util.rb +14 -14
  26. data/lib/openstudio/workflow/util/energyplus.rb +566 -564
  27. data/lib/openstudio/workflow/util/io.rb +33 -33
  28. data/lib/openstudio/workflow/util/measure.rb +588 -588
  29. data/lib/openstudio/workflow/util/model.rb +100 -100
  30. data/lib/openstudio/workflow/util/post_process.rb +187 -187
  31. data/lib/openstudio/workflow/util/weather_file.rb +108 -108
  32. data/lib/openstudio/workflow/version.rb +24 -24
  33. data/lib/openstudio/workflow_json.rb +426 -426
  34. data/lib/openstudio/workflow_runner.rb +233 -215
  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,588 +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 == '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
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