openstudio-workflow 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,549 @@
1
+ ######################################################################
2
+ # Copyright (c) 2008-2014, Alliance for Sustainable Energy.
3
+ # All rights reserved.
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License as published by the Free Software Foundation; either
8
+ # version 2.1 of the License, or (at your option) any later version.
9
+ #
10
+ # This library is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ # Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public
16
+ # License along with this library; if not, write to the Free Software
17
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
+ ######################################################################
19
+
20
+ # Run precanned post processing to extract object functions
21
+ # TODO: I hear that measures can step on each other if not run in their own directory
22
+
23
+ require 'csv'
24
+ class RunPostprocess
25
+
26
+ def initialize(directory, logger, adapter, options = {})
27
+ defaults = {}
28
+ @options = defaults.merge(options)
29
+ @directory = directory
30
+ @run_directory = "#{@directory}/run"
31
+ @adapter = adapter
32
+ @logger = logger
33
+ @results = {}
34
+
35
+ # TODO: we shouldn't have to keep loading this file if we need it. It should be availabe for any job.
36
+ # TODO: passing in the options everytime is ridiculuous
37
+ @analysis_json = @adapter.get_problem(@directory, @options)
38
+
39
+ @logger.info "#{self.class} passed the following options #{@options}"
40
+
41
+ @model = load_model @options[:run_openstudio][:osm]
42
+ # TODO: should read the name of the sql output file via the :run_openstudio options hash
43
+ @sql_filename = "#{@run_directory}/eplusout.sql"
44
+ fail "EnergyPlus SQL file did not exist #{@sql_filename}" unless File.exist? @sql_filename
45
+
46
+ @objective_functions = {}
47
+ end
48
+
49
+ def perform
50
+ @logger.info "Calling #{__method__} in the #{self.class} class"
51
+
52
+ if @options[:use_monthly_reports]
53
+ run_monthly_postprocess
54
+ else
55
+ run_standard_postprocess
56
+ end
57
+
58
+ translate_csv_to_json
59
+
60
+ run_packaged_measures
61
+
62
+ run_extract_inputs_and_outputs
63
+
64
+ cleanup
65
+
66
+ @results
67
+ end
68
+
69
+ def cleanup
70
+ # TODO: use Dir glob instead of pathname
71
+ require 'pathname'
72
+ require 'fileutils'
73
+ paths_to_rm = []
74
+ #paths_to_rm << Pathname.glob("#{@run_directory}/*.osm")
75
+ paths_to_rm << Pathname.glob("#{@run_directory}/*.ini")
76
+ paths_to_rm << Pathname.glob("#{@run_directory}/*.idf")
77
+ paths_to_rm << Pathname.glob("#{@run_directory}/ExpandObjects")
78
+ paths_to_rm << Pathname.glob("#{@run_directory}/EnergyPlus")
79
+ paths_to_rm << Pathname.glob("#{@run_directory}/*.so")
80
+ paths_to_rm << Pathname.glob("#{@run_directory}/*.epw")
81
+ paths_to_rm << Pathname.glob("#{@run_directory}/*.idd")
82
+ # paths_to_rm << Pathname.glob("*.audit")
83
+ # paths_to_rm << Pathname.glob("*.bnd")
84
+ paths_to_rm << Pathname.glob("#{@run_directory}/*.mtd")
85
+ paths_to_rm << Pathname.glob("#{@run_directory}/*.rdd")
86
+ paths_to_rm << Pathname.glob("#{@run_directory}/packaged_measures")
87
+ paths_to_rm.each { |p| FileUtils.rm_rf(p) }
88
+ end
89
+
90
+ def run_extract_inputs_and_outputs
91
+ # For xml, the measure attributes are in the measure_attributes_xml.json file
92
+ if File.exist?("#{@run_directory}/measure_attributes_xml.json")
93
+ temp_json = JSON.parse(File.read("#{@run_directory}/measure_attributes_xml.json"), symbolize_names: true)
94
+ @results.merge!(temp_json)
95
+ end
96
+
97
+ # Inputs are in the measure_attributes.json file
98
+ if File.exist?("#{@run_directory}/measure_attributes.json")
99
+ temp_json = JSON.parse(File.read("#{@run_directory}/measure_attributes.json"), symbolize_names: true)
100
+ @results.merge!(temp_json)
101
+ end
102
+
103
+ if File.exist?("#{@run_directory}/standard_report.json")
104
+ @results[:standard_report] = JSON.parse(File.read("#{@run_directory}/standard_report.json"), symbolize_names: true)
105
+ end
106
+
107
+ # Initialize the objective function variable
108
+ @objective_functions = {}
109
+ if File.exist?("#{@run_directory}/standard_report_legacy.json")
110
+ @results[:standard_report_legacy] = JSON.parse(File.read("#{@run_directory}/standard_report_legacy.json"), symbolize_names: true)
111
+ @logger.info "Analysis JSON Output Variables are: #{@analysis_json[:analysis][:output_variables]}"
112
+ # Save the objective functions to the object for sending back to the simulation executive
113
+ @analysis_json[:analysis][:output_variables].each do |variable|
114
+ # determine which ones are the objective functions (code smell: todo: use enumerator)
115
+ if variable[:objective_function]
116
+ @logger.info "Found objective function for #{variable[:name]}"
117
+ if @results[:standard_report_legacy][variable[:name].to_sym]
118
+ @objective_functions["objective_function_#{variable[:objective_function_index] + 1}"] = @results[:standard_report_legacy][variable[:name].to_sym]
119
+ if variable[:objective_function_target]
120
+ @logger.info "Found objective function target for #{variable[:name]}"
121
+ @objective_functions["objective_function_target_#{variable[:objective_function_index] + 1}"] = variable[:objective_function_target].to_f
122
+ end
123
+ if variable[:scaling_factor]
124
+ @logger.info "Found scaling factor for #{variable[:name]}"
125
+ @objective_functions["scaling_factor_#{variable[:objective_function_index] + 1}"] = variable[:scaling_factor].to_f
126
+ end
127
+ if variable['objective_function_group']
128
+ @logger.info "Found objective function group for #{variable[:name]}"
129
+ @objective_functions["objective_function_group_#{variable[:objective_function_index] + 1}"] = variable[:objective_function_group].to_f
130
+ end
131
+ else
132
+ # objective_functions[variable['name']] = nil
133
+ @objective_functions["objective_function_#{variable[:objective_function_index] + 1}"] = Float::MAX
134
+ @objective_functions["objective_function_target_#{variable[:objective_function_index] + 1}"] = nil
135
+ @objective_functions["scaling_factor_#{variable[:objective_function_index] + 1}"] = nil
136
+ @objective_functions["objective_function_group_#{variable[:objective_function_index] + 1}"] = nil
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
142
+
143
+ private
144
+
145
+ # Load in the OpenStudio model. It is required for postprocessing
146
+ def load_model(filename)
147
+ model = nil
148
+ @logger.info 'Loading model'
149
+
150
+ # TODO: wrap this in an exception block and fail as appropriate
151
+ # assume that the seed model has been placed in the directory
152
+ if File.exist? filename
153
+ @logger.info "Reading in model #{filename}"
154
+ translator = OpenStudio::OSVersion::VersionTranslator.new
155
+ model = translator.loadModel(filename)
156
+ fail 'OpenStudio model is empty or could not be loaded' if model.empty?
157
+ model = model.get
158
+ else
159
+ fail "Model '#{filename}' did not exist"
160
+ end
161
+
162
+ model
163
+ end
164
+
165
+ # TODO: loop of the workflow and run any other reporting measures
166
+ # Run the prepackaged measures in the Gem.
167
+ def run_packaged_measures
168
+ @logger.info "Running packaged reporting measures"
169
+
170
+ # HARD CODE the running of the report measure
171
+ measure_path = 'packaged_measures'
172
+ measure_name = 'StandardReports'
173
+
174
+ # when full workflow then do this
175
+ # require "#{File.expand_path(File.join(File.dirname(__FILE__), '..', measure_path, measure_name, 'measure'))}"
176
+ measure_file_path = File.expand_path(File.join(File.dirname(__FILE__), measure_path, measure_name, 'measure.rb'))
177
+ @logger.info "Loading measure in relative path #{measure_path}"
178
+ fail "Packaged measure does not exist for #{measure_file_path}" unless File.exist?(measure_file_path)
179
+
180
+ #measure_file_path = File.expand_path(
181
+ # File.join(@directory, @options[:analysis_root_path], measure_path, 'measure.rb'))
182
+ #fail "Measure file does not exist #{measure_name} in #{measure_file_path}" unless File.exist? measure_file_path
183
+
184
+ require measure_file_path
185
+ measure = Object.const_get(measure_name).new
186
+ runner = OpenStudio::Ruleset::OSRunner.new
187
+
188
+ @logger.info "Run directory for post process: #{@run_directory}"
189
+ runner.setLastOpenStudioModel(@model)
190
+ runner.setLastEnergyPlusSqlFilePath(@sql_filename)
191
+
192
+ # create a new directory before running
193
+ measure_run_dir = "#{@directory}/#{measure_name}"
194
+ @logger.info "Running measure in #{measure_run_dir}"
195
+ FileUtils.mkdir_p(measure_run_dir)
196
+ current_dir = Dir.pwd
197
+ begin
198
+ Dir.chdir(measure_run_dir)
199
+ argument_map = OpenStudio::Ruleset::OSArgumentMap.new
200
+ @logger.info "Calling run on measure"
201
+ measure.run(runner, argument_map)
202
+ measure_result = runner.result
203
+ ensure
204
+ Dir.chdir(current_dir)
205
+ end
206
+
207
+ @logger.info measure_result.initialCondition.get.logMessage unless measure_result.initialCondition.empty?
208
+ @logger.info measure_result.finalCondition.get.logMessage unless measure_result.finalCondition.empty?
209
+
210
+ measure_result.warnings.each { |w| @logger.info w.logMessage }
211
+ measure_result.errors.each { |w| @logger.info w.logMessage }
212
+ measure_result.info.each { |w| @logger.info w.logMessage }
213
+
214
+ report_json = JSON.parse(OpenStudio.toJSON(measure_result.attributes), symbolize_names: true)
215
+
216
+ # only grab the attributes
217
+ standard_report = report_json[:attributes]
218
+ File.open("#{@run_directory}/standard_report.json", 'w') { |f| f << JSON.pretty_generate(standard_report) }
219
+
220
+ @logger.info 'Finished OpenStudio Post Processing'
221
+ end
222
+
223
+ def translate_csv_to_json
224
+ if File.exist?("#{@run_directory}/eplustbl.csv")
225
+ @logger.info 'Translating EnergyPlus table CSV to JSON file'
226
+ results = {}
227
+ csv = CSV.read("#{@run_directory}/eplustbl.csv")
228
+ csv.transpose.each do |k, v|
229
+ longname = k.gsub(/\(.*\)/, '').strip
230
+ short_name = longname.downcase.gsub(' ', '_')
231
+ units = k.match(/\(.*\)/)[0].gsub('(', '').gsub(')', '')
232
+ results[short_name.to_sym] = v.nil? ? nil : v.to_f
233
+ results["#{short_name}_units".to_sym] = units
234
+ results["#{short_name}_display_name".to_sym] = longname
235
+ end
236
+
237
+ @logger.info 'Saving results to json'
238
+
239
+ # save out results
240
+ File.open("#{@run_directory}/standard_report_legacy.json", 'w') { |f| f << JSON.pretty_generate(results) }
241
+ end
242
+ end
243
+
244
+ # TODO: THis is uglier than the one below! sorry.
245
+ def run_monthly_postprocess
246
+ # sql_query method
247
+ def sql_query(sql, report_name, query)
248
+ val = nil
249
+ result = sql.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='#{report_name}' AND #{query}")
250
+ if result
251
+ begin
252
+ val = result.get
253
+ rescue Exception => e
254
+ @logger.info "#{__FILE__} failed with #{e.message}, #{e.backtrace.join("\n")}"
255
+ val = nil
256
+ end
257
+ end
258
+ val
259
+ end
260
+
261
+ def add_element(hash, var_name, value, xpath = nil)
262
+ values_hash = {}
263
+ values_hash['name'] = var_name
264
+
265
+ # store correct datatype
266
+ store_val = nil
267
+ if value.nil?
268
+ store_val = nil
269
+ elsif value == 'true'
270
+ store_val = true
271
+ elsif value == 'false'
272
+ store_val = false
273
+ else
274
+ test = value.to_s
275
+ value = test.match('\.').nil? ? Integer(test) : Float(test) rescue test.to_s
276
+ if value.is_a?(Fixnum) || value.is_a?(Float)
277
+ store_val = value.to_f
278
+ else
279
+ store_val = value.to_s
280
+ end
281
+ end
282
+ values_hash['value'] = store_val
283
+ values_hash['xpath'] = xpath unless xpath.nil?
284
+
285
+ hash['data']['variables'] << values_hash
286
+ end
287
+
288
+ # add results from sql method
289
+ def add_data(sql, query, hdr, area, val)
290
+ row = []
291
+ val = sql_query(sql, 'AnnualBuildingUtilityPerformanceSummary', query) if val.nil?
292
+ row << hdr
293
+ if area.nil?
294
+ row << val
295
+ else
296
+ row << (val * 1000) / area
297
+ end
298
+ row
299
+ end
300
+
301
+ # add results from sql method
302
+ def add_data2(sql, query, hdr, area, val)
303
+ row = []
304
+ val = sql_query(sql, 'BUILDING ENERGY PERFORMANCE - ELECTRICITY', query) if val.nil?
305
+ row << hdr
306
+ if area.nil?
307
+ row << val
308
+ else
309
+ row << (val * 1000) / area
310
+ end
311
+ row
312
+ end
313
+
314
+ # add results from sql method
315
+ def add_data3(sql, query, hdr, area, val)
316
+ row = []
317
+ val = sql_query(sql, 'BUILDING ENERGY PERFORMANCE - NATURAL GAS', query) if val.nil?
318
+ row << hdr
319
+ if area.nil?
320
+ row << val
321
+ else
322
+ row << (val * 1000) / area
323
+ end
324
+ row
325
+ end
326
+
327
+ # add results from sql method
328
+ def add_data4(sql, query, hdr, area, val)
329
+ row = []
330
+
331
+ if val.nil?
332
+ val = 0
333
+
334
+ ["INTERIORLIGHTS:ELECTRICITY", "EXTERIORLIGHTS:ELECTRICITY", "INTERIOREQUIPMENT:ELECTRICITY", "EXTERIOREQUIPMENT:ELECTRICITY",
335
+ "FANS:ELECTRICITY", "PUMPS:ELECTRICITY", "HEATING:ELECTRICITY", "COOLING:ELECTRICITY", "HEATREJECTION:ELECTRICITY",
336
+ "HUMIDIFIER:ELECTRICITY", "HEATRECOVERY:ELECTRICITY", "WATERSYSTEMS:ELECTRICITY", "COGENERATION:ELECTRICITY", "REFRIGERATION:ELECTRICITY"].each do |end_use|
337
+
338
+ tmp_query = query + " AND ColumnName='#{end_use}'"
339
+ tmp_val = sql_query(sql, 'BUILDING ENERGY PERFORMANCE - ELECTRICITY', tmp_query)
340
+ val += tmp_val if not tmp_val.nil?
341
+ end
342
+ end
343
+
344
+ row << hdr
345
+ if area.nil?
346
+ row << val
347
+ else
348
+ row << (val * 1000) / area
349
+ end
350
+ row
351
+ end
352
+
353
+ # open sql file
354
+ sql_file = OpenStudio::SqlFile.new(@sql_filename)
355
+
356
+ # get building area
357
+ bldg_area = sql_query(sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Building Area' AND RowName='Net Conditioned Building Area' AND ColumnName='Area'")
358
+ # populate data array
359
+
360
+ tbl_data = []
361
+ tbl_data << add_data(sql_file, "TableName='Site and Source Energy' AND RowName='Total Site Energy' AND ColumnName='Energy Per Conditioned Building Area'", 'Total Energy (MJ/m2)', nil, nil)
362
+ tbl_data << add_data(sql_file, "TableName='Site and Source Energy' AND RowName='Total Source Energy' AND ColumnName='Energy Per Conditioned Building Area'", 'Total Source Energy (MJ/m2)', nil, nil)
363
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Total End Uses' AND ColumnName='Electricity'", 'Total Electricity (MJ/m2)', bldg_area, nil)
364
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Total End Uses' AND ColumnName='Natural Gas'", 'Total Natural Gas (MJ/m2)', bldg_area, nil)
365
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Heating' AND ColumnName='Electricity'", 'Heating Electricity (MJ/m2)', bldg_area, nil)
366
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Heating' AND ColumnName='Natural Gas'", 'Heating Natural Gas (MJ/m2)', bldg_area, nil)
367
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Cooling' AND ColumnName='Electricity'", 'Cooling Electricity (MJ/m2)', bldg_area, nil)
368
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Interior Lighting' AND ColumnName='Electricity'", 'Interior Lighting Electricity (MJ/m2)', bldg_area, nil)
369
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Exterior Lighting' AND ColumnName='Electricity'", 'Exterior Lighting Electricity (MJ/m2)', bldg_area, nil)
370
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Interior Equipment' AND ColumnName='Electricity'", 'Interior Equipment Electricity (MJ/m2)', bldg_area, nil)
371
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Interior Equipment' AND ColumnName='Natural Gas'", 'Interior Equipment Natural Gas (MJ/m2)', bldg_area, nil)
372
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Exterior Equipment' AND ColumnName='Electricity'", 'Exterior Equipment Electricity (MJ/m2)', bldg_area, nil)
373
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Fans' AND ColumnName='Electricity'", 'Fans Electricity (MJ/m2)', bldg_area, nil)
374
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Pumps' AND ColumnName='Electricity'", 'Pumps Electricity (MJ/m2)', bldg_area, nil)
375
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Heat Rejection' AND ColumnName='Electricity'", 'Heat Rejection Electricity (MJ/m2)', bldg_area, nil)
376
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Humidification' AND ColumnName='Electricity'", 'Humidification Electricity (MJ/m2)', bldg_area, nil)
377
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Water Systems' AND ColumnName='Electricity'", 'Water Systems Electricity (MJ/m2)', bldg_area, nil)
378
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Water Systems' AND ColumnName='Natural Gas'", 'Water Systems Natural Gas (MJ/m2)', bldg_area, nil)
379
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Refrigeration' AND ColumnName='Electricity'", 'Refrigeration Electricity (MJ/m2)', bldg_area, nil)
380
+ htg_hrs = sql_query(sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Comfort and Setpoint Not Met Summary' AND RowName='Time Setpoint Not Met During Occupied Heating' AND ColumnName='Facility'")
381
+ clg_hrs = sql_query(sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Comfort and Setpoint Not Met Summary' AND RowName='Time Setpoint Not Met During Occupied Cooling' AND ColumnName='Facility'")
382
+ tot_hrs = clg_hrs && htg_hrs ? htg_hrs + clg_hrs : nil
383
+ tbl_data << add_data(sql_file, nil, 'Heating Hours Unmet (hr)', nil, htg_hrs)
384
+ tbl_data << add_data(sql_file, nil, 'Cooling Hours Unmet (hr)', nil, clg_hrs)
385
+ tbl_data << add_data(sql_file, nil, 'Total Hours Unmet (hr)', nil, tot_hrs)
386
+ total_cost = sql_query(sql_file, 'Life-Cycle Cost Report', "TableName='Present Value by Category' AND RowName='Grand Total' AND ColumnName='Present Value'")
387
+ tbl_data << add_data(sql_file, nil, 'Total Life Cycle Cost ($)', nil, total_cost)
388
+ # cooling:electricity
389
+ tbl_data << add_data2(sql_file, "RowName='January' AND ColumnName='COOLING:ELECTRICITY'", 'Cooling Electricity Jan (J)', nil, nil)
390
+ tbl_data << add_data2(sql_file, "RowName='February' AND ColumnName='COOLING:ELECTRICITY'", 'Cooling Electricity Feb (J)', nil, nil)
391
+ tbl_data << add_data2(sql_file, "RowName='March' AND ColumnName='COOLING:ELECTRICITY'", 'Cooling Electricity Mar (J)', nil, nil)
392
+ tbl_data << add_data2(sql_file, "RowName='April' AND ColumnName='COOLING:ELECTRICITY'", 'Cooling Electricity Apr (J)', nil, nil)
393
+ tbl_data << add_data2(sql_file, "RowName='May' AND ColumnName='COOLING:ELECTRICITY'", 'Cooling Electricity May (J)', nil, nil)
394
+ tbl_data << add_data2(sql_file, "RowName='June' AND ColumnName='COOLING:ELECTRICITY'", 'Cooling Electricity Jun (J)', nil, nil)
395
+ tbl_data << add_data2(sql_file, "RowName='July' AND ColumnName='COOLING:ELECTRICITY'", 'Cooling Electricity Jul (J)', nil, nil)
396
+ tbl_data << add_data2(sql_file, "RowName='August' AND ColumnName='COOLING:ELECTRICITY'", 'Cooling Electricity Aug (J)', nil, nil)
397
+ tbl_data << add_data2(sql_file, "RowName='September' AND ColumnName='COOLING:ELECTRICITY'", 'Cooling Electricity Sep (J)', nil, nil)
398
+ tbl_data << add_data2(sql_file, "RowName='October' AND ColumnName='COOLING:ELECTRICITY'", 'Cooling Electricity Oct (J)', nil, nil)
399
+ tbl_data << add_data2(sql_file, "RowName='November' AND ColumnName='COOLING:ELECTRICITY'", 'Cooling Electricity Nov (J)', nil, nil)
400
+ tbl_data << add_data2(sql_file, "RowName='December' AND ColumnName='COOLING:ELECTRICITY'", 'Cooling Electricity Dec (J)', nil, nil)
401
+ # heating:gas
402
+ tbl_data << add_data3(sql_file, "RowName='January' AND ColumnName='HEATING:GAS'", 'Heating Gas Jan (J)', nil, nil)
403
+ tbl_data << add_data3(sql_file, "RowName='February' AND ColumnName='HEATING:GAS'", 'Heating Gas Feb (J)', nil, nil)
404
+ tbl_data << add_data3(sql_file, "RowName='March' AND ColumnName='HEATING:GAS'", 'Heating Gas Mar (J)', nil, nil)
405
+ tbl_data << add_data3(sql_file, "RowName='April' AND ColumnName='HEATING:GAS'", 'Heating Gas Apr (J)', nil, nil)
406
+ tbl_data << add_data3(sql_file, "RowName='May' AND ColumnName='HEATING:GAS'", 'Heating Gas May (J)', nil, nil)
407
+ tbl_data << add_data3(sql_file, "RowName='June' AND ColumnName='HEATING:GAS'", 'Heating Gas Jun (J)', nil, nil)
408
+ tbl_data << add_data3(sql_file, "RowName='July' AND ColumnName='HEATING:GAS'", 'Heating Gas Jul (J)', nil, nil)
409
+ tbl_data << add_data3(sql_file, "RowName='August' AND ColumnName='HEATING:GAS'", 'Heating Gas Aug (J)', nil, nil)
410
+ tbl_data << add_data3(sql_file, "RowName='September' AND ColumnName='HEATING:GAS'", 'Heating Gas Sep (J)', nil, nil)
411
+ tbl_data << add_data3(sql_file, "RowName='October' AND ColumnName='HEATING:GAS'", 'Heating Gas Oct (J)', nil, nil)
412
+ tbl_data << add_data3(sql_file, "RowName='November' AND ColumnName='HEATING:GAS'", 'Heating Gas Nov (J)', nil, nil)
413
+ tbl_data << add_data3(sql_file, "RowName='December' AND ColumnName='HEATING:GAS'", 'Heating Gas Dec (J)', nil, nil)
414
+ # total Electricity
415
+ tbl_data << add_data4(sql_file, "RowName='January'", 'Total Electricity Jan (J)', nil, nil)
416
+ tbl_data << add_data4(sql_file, "RowName='February'", 'Total Electricity Feb (J)', nil, nil)
417
+ tbl_data << add_data4(sql_file, "RowName='March'", 'Total Electricity Mar (J)', nil, nil)
418
+ tbl_data << add_data4(sql_file, "RowName='April'", 'Total Electricity Apr (J)', nil, nil)
419
+ tbl_data << add_data4(sql_file, "RowName='May'", 'Total Electricity May (J)', nil, nil)
420
+ tbl_data << add_data4(sql_file, "RowName='June'", 'Total Electricity Jun (J)', nil, nil)
421
+ tbl_data << add_data4(sql_file, "RowName='July'", 'Total Electricity Jul (J)', nil, nil)
422
+ tbl_data << add_data4(sql_file, "RowName='August'", 'Total Electricity Aug (J)', nil, nil)
423
+ tbl_data << add_data4(sql_file, "RowName='September'", 'Total Electricity Sep (J)', nil, nil)
424
+ tbl_data << add_data4(sql_file, "RowName='October'", 'Total Electricity Oct (J)', nil, nil)
425
+ tbl_data << add_data4(sql_file, "RowName='November'", 'Total Electricity Nov (J)', nil, nil)
426
+ tbl_data << add_data4(sql_file, "RowName='December'", 'Total Electricity Dec (J)', nil, nil) # close SQL file
427
+ sql_file.close
428
+ # transpose data
429
+ tbl_rows = tbl_data.transpose
430
+
431
+ @logger.info tbl_rows
432
+ # write electricity data to CSV
433
+ CSV.open("#{@run_directory}/eplustbl.csv", 'wb') do |csv|
434
+ tbl_rows.each do |row|
435
+ csv << row
436
+ end
437
+ end
438
+ end
439
+
440
+
441
+ # TODO: This is ugly. Move this out of here entirely and into a reporting measure if we need it at all
442
+ def run_standard_postprocess
443
+ # sql_query method
444
+ def sql_query(sql, report_name, query)
445
+ val = nil
446
+ result = sql.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='#{report_name}' AND #{query}")
447
+ if result
448
+ begin
449
+ val = result.get
450
+ rescue Exception => e
451
+ @logger.info "#{__FILE__} failed with #{e.message}, #{e.backtrace.join("\n")}"
452
+ val = nil
453
+ end
454
+ end
455
+ val
456
+ end
457
+
458
+ def add_element(hash, var_name, value, xpath = nil)
459
+ values_hash = {}
460
+ values_hash['name'] = var_name
461
+
462
+ # store correct datatype
463
+ store_val = nil
464
+ if value.nil?
465
+ store_val = nil
466
+ elsif value == 'true'
467
+ store_val = true
468
+ elsif value == 'false'
469
+ store_val = false
470
+ else
471
+ test = value.to_s
472
+ value = test.match('\.').nil? ? Integer(test) : Float(test) rescue test.to_s
473
+ if value.is_a?(Fixnum) || value.is_a?(Float)
474
+ store_val = value.to_f
475
+ else
476
+ store_val = value.to_s
477
+ end
478
+ end
479
+ values_hash['value'] = store_val
480
+ values_hash['xpath'] = xpath unless xpath.nil?
481
+
482
+ hash['data']['variables'] << values_hash
483
+ end
484
+
485
+ # add results from sql method
486
+ def add_data(sql, query, hdr, area, val)
487
+ row = []
488
+ val = sql_query(sql, 'AnnualBuildingUtilityPerformanceSummary', query) if val.nil?
489
+ row << hdr
490
+ if area.nil?
491
+ row << val
492
+ else
493
+ row << (val * 1000) / area
494
+ end
495
+ row
496
+ end
497
+
498
+
499
+ # open sql file
500
+ sql_file = OpenStudio::SqlFile.new(@sql_filename)
501
+
502
+ # get building area
503
+ bldg_area = sql_query(sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Building Area' AND RowName='Net Conditioned Building Area' AND ColumnName='Area'")
504
+ # populate data array
505
+
506
+ tbl_data = []
507
+ tbl_data << add_data(sql_file, "TableName='Site and Source Energy' AND RowName='Total Site Energy' AND ColumnName='Energy Per Conditioned Building Area'", 'Total Energy (MJ/m2)', nil, nil)
508
+ tbl_data << add_data(sql_file, "TableName='Site and Source Energy' AND RowName='Total Source Energy' AND ColumnName='Energy Per Conditioned Building Area'", 'Total Source Energy (MJ/m2)', nil, nil)
509
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Total End Uses' AND ColumnName='Electricity'", 'Total Electricity (MJ/m2)', bldg_area, nil)
510
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Total End Uses' AND ColumnName='Natural Gas'", 'Total Natural Gas (MJ/m2)', bldg_area, nil)
511
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Heating' AND ColumnName='Electricity'", 'Heating Electricity (MJ/m2)', bldg_area, nil)
512
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Heating' AND ColumnName='Natural Gas'", 'Heating Natural Gas (MJ/m2)', bldg_area, nil)
513
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Cooling' AND ColumnName='Electricity'", 'Cooling Electricity (MJ/m2)', bldg_area, nil)
514
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Interior Lighting' AND ColumnName='Electricity'", 'Interior Lighting Electricity (MJ/m2)', bldg_area, nil)
515
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Exterior Lighting' AND ColumnName='Electricity'", 'Exterior Lighting Electricity (MJ/m2)', bldg_area, nil)
516
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Interior Equipment' AND ColumnName='Electricity'", 'Interior Equipment Electricity (MJ/m2)', bldg_area, nil)
517
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Interior Equipment' AND ColumnName='Natural Gas'", 'Interior Equipment Natural Gas (MJ/m2)', bldg_area, nil)
518
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Exterior Equipment' AND ColumnName='Electricity'", 'Exterior Equipment Electricity (MJ/m2)', bldg_area, nil)
519
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Fans' AND ColumnName='Electricity'", 'Fans Electricity (MJ/m2)', bldg_area, nil)
520
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Pumps' AND ColumnName='Electricity'", 'Pumps Electricity (MJ/m2)', bldg_area, nil)
521
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Heat Rejection' AND ColumnName='Electricity'", 'Heat Rejection Electricity (MJ/m2)', bldg_area, nil)
522
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Humidification' AND ColumnName='Electricity'", 'Humidification Electricity (MJ/m2)', bldg_area, nil)
523
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Water Systems' AND ColumnName='Electricity'", 'Water Systems Electricity (MJ/m2)', bldg_area, nil)
524
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Water Systems' AND ColumnName='Natural Gas'", 'Water Systems Natural Gas (MJ/m2)', bldg_area, nil)
525
+ tbl_data << add_data(sql_file, "TableName='End Uses' AND RowName='Refrigeration' AND ColumnName='Electricity'", 'Refrigeration Electricity (MJ/m2)', bldg_area, nil)
526
+ htg_hrs = sql_query(sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Comfort and Setpoint Not Met Summary' AND RowName='Time Setpoint Not Met During Occupied Heating' AND ColumnName='Facility'")
527
+ clg_hrs = sql_query(sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Comfort and Setpoint Not Met Summary' AND RowName='Time Setpoint Not Met During Occupied Cooling' AND ColumnName='Facility'")
528
+ tot_hrs = clg_hrs && htg_hrs ? htg_hrs + clg_hrs : nil
529
+ tbl_data << add_data(sql_file, nil, 'Heating Hours Unmet (hr)', nil, htg_hrs)
530
+ tbl_data << add_data(sql_file, nil, 'Cooling Hours Unmet (hr)', nil, clg_hrs)
531
+ tbl_data << add_data(sql_file, nil, 'Total Hours Unmet (hr)', nil, tot_hrs)
532
+ total_cost = sql_query(sql_file, 'Life-Cycle Cost Report', "TableName='Present Value by Category' AND RowName='Grand Total' AND ColumnName='Present Value'")
533
+ tbl_data << add_data(sql_file, nil, 'Total Life Cycle Cost ($)', nil, total_cost)
534
+ # close SQL file
535
+ sql_file.close
536
+ # transpose data
537
+ tbl_rows = tbl_data.transpose
538
+
539
+ @logger.info tbl_rows
540
+ # write electricity data to CSV
541
+ CSV.open("#{@run_directory}/eplustbl.csv", 'wb') do |csv|
542
+ tbl_rows.each do |row|
543
+ csv << row
544
+ end
545
+ end
546
+
547
+ end
548
+
549
+ end