openstudio-extension 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +29 -26
  3. data/.rspec +3 -3
  4. data/.rubocop.yml +10 -9
  5. data/CHANGELOG.md +16 -0
  6. data/Gemfile +6 -6
  7. data/Jenkinsfile +10 -10
  8. data/README.md +251 -250
  9. data/Rakefile +119 -119
  10. data/bin/console +14 -14
  11. data/bin/setup +8 -8
  12. data/doc_templates/LICENSE.md +26 -26
  13. data/doc_templates/README.md.erb +41 -41
  14. data/doc_templates/copyright_erb.txt +35 -35
  15. data/doc_templates/copyright_js.txt +3 -3
  16. data/doc_templates/copyright_ruby.txt +33 -33
  17. data/init_templates/README.md +37 -37
  18. data/init_templates/gemspec.txt +32 -32
  19. data/init_templates/openstudio_module.rb +50 -50
  20. data/init_templates/spec.rb +47 -47
  21. data/init_templates/spec_helper.rb +49 -49
  22. data/init_templates/template_gemfile.txt +17 -17
  23. data/init_templates/template_rakefile.txt +15 -15
  24. data/init_templates/version.rb +40 -40
  25. data/lib/files/openstudio-extension-gem-test.ddy +536 -536
  26. data/lib/files/openstudio-extension-gem-test.epw +8768 -8768
  27. data/lib/files/openstudio-extension-gem-test.stat +554 -554
  28. data/lib/measures/openstudio_extension_test_measure/LICENSE.md +26 -26
  29. data/lib/measures/openstudio_extension_test_measure/README.md +26 -26
  30. data/lib/measures/openstudio_extension_test_measure/README.md.erb +41 -41
  31. data/lib/measures/openstudio_extension_test_measure/measure.rb +72 -72
  32. data/lib/measures/openstudio_extension_test_measure/measure.xml +83 -83
  33. data/lib/measures/openstudio_extension_test_measure/resources/os_lib_helper_methods.rb +399 -399
  34. data/lib/measures/openstudio_extension_test_measure/tests/{OpenStudioExtensionTestMeasure_Test.rb → openstudio_extension_test_measure_test.rb} +74 -75
  35. data/lib/openstudio-extension.rb +1 -1
  36. data/lib/openstudio/extension.rb +234 -229
  37. data/lib/openstudio/extension/core/CreateResults.rb +886 -886
  38. data/lib/openstudio/extension/core/check_air_sys_temps.rb +190 -190
  39. data/lib/openstudio/extension/core/check_calibration.rb +155 -155
  40. data/lib/openstudio/extension/core/check_cond_zns.rb +84 -84
  41. data/lib/openstudio/extension/core/check_domestic_hot_water.rb +334 -334
  42. data/lib/openstudio/extension/core/check_envelope_conductance.rb +453 -453
  43. data/lib/openstudio/extension/core/check_eui_by_end_use.rb +162 -162
  44. data/lib/openstudio/extension/core/check_eui_reasonableness.rb +135 -135
  45. data/lib/openstudio/extension/core/check_fan_pwr.rb +98 -98
  46. data/lib/openstudio/extension/core/check_internal_loads.rb +393 -393
  47. data/lib/openstudio/extension/core/check_mech_sys_capacity.rb +226 -226
  48. data/lib/openstudio/extension/core/check_mech_sys_efficiency.rb +326 -326
  49. data/lib/openstudio/extension/core/check_mech_sys_part_load_eff.rb +464 -464
  50. data/lib/openstudio/extension/core/check_mech_sys_type.rb +139 -139
  51. data/lib/openstudio/extension/core/check_part_loads.rb +451 -451
  52. data/lib/openstudio/extension/core/check_placeholder.rb +75 -75
  53. data/lib/openstudio/extension/core/check_plant_cap.rb +123 -123
  54. data/lib/openstudio/extension/core/check_plant_temps.rb +159 -159
  55. data/lib/openstudio/extension/core/check_plenum_loads.rb +87 -87
  56. data/lib/openstudio/extension/core/check_pump_pwr.rb +108 -108
  57. data/lib/openstudio/extension/core/check_sch_coord.rb +241 -241
  58. data/lib/openstudio/extension/core/check_schedules.rb +311 -311
  59. data/lib/openstudio/extension/core/check_simultaneous_heating_and_cooling.rb +158 -158
  60. data/lib/openstudio/extension/core/check_supply_air_and_thermostat_temp_difference.rb +148 -148
  61. data/lib/openstudio/extension/core/check_weather_files.rb +132 -132
  62. data/lib/openstudio/extension/core/deer_vintages.rb +311 -311
  63. data/lib/openstudio/extension/core/os_lib_aedg_measures.rb +491 -491
  64. data/lib/openstudio/extension/core/os_lib_cofee.rb +258 -258
  65. data/lib/openstudio/extension/core/os_lib_constructions.rb +378 -378
  66. data/lib/openstudio/extension/core/os_lib_geometry.rb +1022 -1022
  67. data/lib/openstudio/extension/core/os_lib_helper_methods.rb +399 -399
  68. data/lib/openstudio/extension/core/os_lib_hvac.rb +2171 -2171
  69. data/lib/openstudio/extension/core/os_lib_lighting_and_equipment.rb +214 -214
  70. data/lib/openstudio/extension/core/os_lib_model_generation.rb +817 -817
  71. data/lib/openstudio/extension/core/os_lib_model_simplification.rb +1049 -1049
  72. data/lib/openstudio/extension/core/os_lib_outdoorair_and_infiltration.rb +165 -165
  73. data/lib/openstudio/extension/core/os_lib_reporting.rb +4651 -4651
  74. data/lib/openstudio/extension/core/os_lib_reporting_qaqc.rb +200 -200
  75. data/lib/openstudio/extension/core/os_lib_schedules.rb +963 -963
  76. data/lib/openstudio/extension/rake_task.rb +159 -149
  77. data/lib/openstudio/extension/runner.rb +667 -644
  78. data/lib/openstudio/extension/runner_config.rb +114 -0
  79. data/lib/openstudio/extension/version.rb +40 -40
  80. data/openstudio-extension.gemspec +34 -33
  81. metadata +39 -37
@@ -1,886 +1,886 @@
1
- # *******************************************************************************
2
- # OpenStudio(R), Copyright (c) 2008-2019, Alliance for Sustainable Energy, LLC.
3
- # All rights reserved.
4
- # Redistribution and use in source and binary forms, with or without
5
- # modification, are permitted provided that the following conditions are met:
6
- #
7
- # (1) Redistributions of source code must retain the above copyright notice,
8
- # this list of conditions and the following disclaimer.
9
- #
10
- # (2) Redistributions in binary form must reproduce the above copyright notice,
11
- # this list of conditions and the following disclaimer in the documentation
12
- # and/or other materials provided with the distribution.
13
- #
14
- # (3) Neither the name of the copyright holder nor the names of any contributors
15
- # may be used to endorse or promote products derived from this software without
16
- # specific prior written permission from the respective party.
17
- #
18
- # (4) Other than as required in clauses (1) and (2), distributions in any form
19
- # of modifications or other derivative works may not use the "OpenStudio"
20
- # trademark, "OS", "os", or any other confusingly similar designation without
21
- # specific prior written permission from Alliance for Sustainable Energy, LLC.
22
- #
23
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS
24
- # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
25
- # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26
- # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE
27
- # UNITED STATES GOVERNMENT, OR THE UNITED STATES DEPARTMENT OF ENERGY, NOR ANY OF
28
- # THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29
- # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
30
- # OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31
- # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32
- # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33
- # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
- # *******************************************************************************
35
-
36
- module OsLib_CreateResults
37
- # Reports out the detailed simulation results needed by EDAPT.
38
- # Results are output as both OpenStudio::Attributes (for OpenStudio 1.X)
39
- # and runner.registerValue (for OpenStudio 2.X).
40
- # @param skip_weekends [Bool] if true, weekends will not be included in the peak demand window
41
- # @param skip_holidays [Bool] if true, holidays will not be included in the peak demand window
42
- # @param start_mo [String] the start month for the peak demand window
43
- # @param start_day [Integer] the start day for the peak demand window
44
- # @param start_hr [Integer] the start hour for the peak demand window, using 24-hr clock
45
- # @param end_mo [String] the end month for the peak demand window
46
- # @param end_day [Integer] the end day for the peak demand window
47
- # @param end_hr [Integer] the end hour for the peak demand window, using 24-hr clock
48
- # @return [OpenStudio::AttributeVector] a vector of results needed by EDAPT
49
- def create_results(skip_weekends = true,
50
- skip_holidays = true,
51
- start_mo = 'June',
52
- start_day = 1,
53
- start_hr = 14,
54
- end_mo = 'September',
55
- end_day = 30,
56
- end_hr = 18)
57
-
58
- # get the current version of OS being used to determine if sql query
59
- # changes are needed (for when E+ changes).
60
- os_version = OpenStudio::VersionString.new(OpenStudio.openStudioVersion)
61
-
62
- # create an attribute vector to hold results
63
- result_elems = OpenStudio::AttributeVector.new
64
-
65
- # floor_area
66
- floor_area_query = "SELECT Value FROM tabulardatawithstrings WHERE ReportName='AnnualBuildingUtilityPerformanceSummary' AND ReportForString='Entire Facility' AND TableName='Building Area' AND RowName='Net Conditioned Building Area' AND ColumnName='Area' AND Units='m2'"
67
- floor_area = @sql.execAndReturnFirstDouble(floor_area_query)
68
- if floor_area.is_initialized
69
- result_elems << OpenStudio::Attribute.new('floor_area', floor_area.get, 'm^2')
70
- @runner.registerValue('charsfloor_area', floor_area.get, 'm^2')
71
- else
72
- @runner.registerWarning('Building floor area not found')
73
- return false
74
- end
75
-
76
- # inflation approach
77
- inf_appr_query = "SELECT Value FROM tabulardatawithstrings WHERE ReportName='Life-Cycle Cost Report' AND ReportForString='Entire Facility' AND TableName='Life-Cycle Cost Parameters' AND RowName='Inflation Approach' AND ColumnName='Value'"
78
- inf_appr = @sql.execAndReturnFirstString(inf_appr_query)
79
- if inf_appr.is_initialized
80
- if inf_appr.get == 'ConstantDollar'
81
- inf_appr = 'Constant Dollar'
82
- elsif inf_appr.get == 'CurrentDollar'
83
- inf_appr = 'Current Dollar'
84
- else
85
- @runner.registerError("Inflation approach: #{inf_appr.get} not recognized")
86
- return OpenStudio::Attribute.new('report', result_elems)
87
- end
88
- @runner.registerInfo("Inflation approach = #{inf_appr}")
89
- else
90
- @runner.registerError('Could not determine inflation approach used')
91
- return OpenStudio::Attribute.new('report', result_elems)
92
- end
93
-
94
- # base year
95
- base_yr_query = "SELECT Value FROM tabulardatawithstrings WHERE ReportName='Life-Cycle Cost Report' AND ReportForString='Entire Facility' AND TableName='Life-Cycle Cost Parameters' AND RowName='Base Date' AND ColumnName='Value'"
96
- base_yr = @sql.execAndReturnFirstString(base_yr_query)
97
- if base_yr.is_initialized
98
- if base_yr.get =~ /\d\d\d\d/
99
- base_yr = base_yr.get.match(/\d\d\d\d/)[0].to_f
100
- else
101
- @runner.registerError("Could not determine the analysis start year from #{base_yr.get}")
102
- return OpenStudio::Attribute.new('report', result_elems)
103
- end
104
- else
105
- @runner.registerError('Could not determine analysis start year')
106
- return OpenStudio::Attribute.new('report', result_elems)
107
- end
108
-
109
- # analysis length
110
- length_yrs_query = "SELECT Value FROM tabulardatawithstrings WHERE ReportName='Life-Cycle Cost Report' AND ReportForString='Entire Facility' AND TableName='Life-Cycle Cost Parameters' AND RowName='Length of Study Period in Years' AND ColumnName='Value'"
111
- length_yrs = @sql.execAndReturnFirstInt(length_yrs_query)
112
- if length_yrs.is_initialized
113
- @runner.registerInfo "Analysis length = #{length_yrs.get} yrs"
114
- length_yrs = length_yrs.get
115
- else
116
- @runner.registerError('Could not determine analysis length')
117
- return OpenStudio::Attribute.new('report', result_elems)
118
- end
119
-
120
- # cash flows
121
- cash_flow_elems = OpenStudio::AttributeVector.new
122
-
123
- # setup a vector for each type of cash flow
124
- cap_cash_flow_elems = OpenStudio::AttributeVector.new
125
- om_cash_flow_elems = OpenStudio::AttributeVector.new
126
- energy_cash_flow_elems = OpenStudio::AttributeVector.new
127
- water_cash_flow_elems = OpenStudio::AttributeVector.new
128
- tot_cash_flow_elems = OpenStudio::AttributeVector.new
129
-
130
- # add the type to the element
131
- cap_cash_flow_elems << OpenStudio::Attribute.new('type', "#{inf_appr} Capital Costs")
132
- om_cash_flow_elems << OpenStudio::Attribute.new('type', "#{inf_appr} Operating Costs")
133
- energy_cash_flow_elems << OpenStudio::Attribute.new('type', "#{inf_appr} Energy Costs")
134
- water_cash_flow_elems << OpenStudio::Attribute.new('type', "#{inf_appr} Water Costs")
135
- tot_cash_flow_elems << OpenStudio::Attribute.new('type', "#{inf_appr} Total Costs")
136
-
137
- @runner.registerValue('cash_flows_capital_type', "#{inf_appr} Capital Costs")
138
- @runner.registerValue('cash_flows_operating_type', "#{inf_appr} Operating Costs")
139
- @runner.registerValue('cash_flows_energy_type', "#{inf_appr} Energy Costs")
140
- @runner.registerValue('cash_flows_water_type', "#{inf_appr} Water Costs")
141
- @runner.registerValue('cash_flows_total_type', "#{inf_appr} Total Costs")
142
-
143
- # record the cash flow in these hashes
144
- cap_cash_flow = {}
145
- om_cash_flow = {}
146
- energy_cash_flow = {}
147
- water_cash_flow = {}
148
- tot_cash_flow = {}
149
-
150
- # loop through each year and record the cash flow
151
- for i in 0..(length_yrs - 1) do
152
- new_yr = base_yr + i
153
-
154
- yr = nil
155
- if os_version > OpenStudio::VersionString.new('1.5.3')
156
- yr = "January #{new_yr.round}"
157
- else
158
- yr = "January #{new_yr.round}"
159
- end
160
-
161
- ann_cap_cash = 0.0
162
- ann_om_cash = 0.0
163
- ann_energy_cash = 0.0
164
- ann_water_cash = 0.0
165
- ann_tot_cash = 0.0
166
-
167
- # capital cash flow
168
- cap_cash_query = "SELECT Value FROM tabulardatawithstrings WHERE ReportName='Life-Cycle Cost Report' AND ReportForString='Entire Facility' AND TableName='Capital Cash Flow by Category (Without Escalation)' AND RowName='#{yr}' AND ColumnName='Total'"
169
- cap_cash = @sql.execAndReturnFirstDouble(cap_cash_query)
170
- if cap_cash.is_initialized
171
- ann_cap_cash += cap_cash.get
172
- ann_tot_cash += cap_cash.get
173
- end
174
-
175
- # o&m cash flow (excluding utility costs)
176
- om_types = ['Maintenance', 'Repair', 'Operation', 'Replacement', 'MinorOverhaul', 'MajorOverhaul', 'OtherOperational']
177
- om_types.each do |om_type|
178
- om_cash_query = "SELECT Value FROM tabulardatawithstrings WHERE ReportName='Life-Cycle Cost Report' AND ReportForString='Entire Facility' AND TableName='Operating Cash Flow by Category (Without Escalation)' AND RowName='#{yr}' AND ColumnName='#{om_type}'"
179
- om_cash = @sql.execAndReturnFirstDouble(om_cash_query)
180
- if om_cash.is_initialized
181
- ann_om_cash += om_cash.get
182
- ann_tot_cash += om_cash.get
183
- end
184
- end
185
-
186
- # energy cash flow
187
- energy_cash_query = "SELECT Value FROM tabulardatawithstrings WHERE ReportName='Life-Cycle Cost Report' AND ReportForString='Entire Facility' AND TableName='Operating Cash Flow by Category (Without Escalation)' AND RowName='#{yr}' AND ColumnName='Energy'"
188
- energy_cash = @sql.execAndReturnFirstDouble(energy_cash_query)
189
- if energy_cash.is_initialized
190
- ann_energy_cash += energy_cash.get
191
- ann_tot_cash += energy_cash.get
192
- end
193
-
194
- # water cash flow
195
- water_cash_query = "SELECT Value FROM tabulardatawithstrings WHERE ReportName='Life-Cycle Cost Report' AND ReportForString='Entire Facility' AND TableName='Operating Cash Flow by Category (Without Escalation)' AND RowName='#{yr}' AND ColumnName='Water'"
196
- water_cash = @sql.execAndReturnFirstDouble(water_cash_query)
197
- if water_cash.is_initialized
198
- ann_water_cash += water_cash.get
199
- ann_tot_cash += water_cash.get
200
- end
201
-
202
- # log the values for this year
203
- cap_cash_flow[yr] = ann_cap_cash
204
- om_cash_flow[yr] = ann_om_cash
205
- energy_cash_flow[yr] = ann_energy_cash
206
- water_cash_flow[yr] = ann_water_cash
207
- tot_cash_flow[yr] = ann_tot_cash
208
-
209
- cap_cash_flow_elems << OpenStudio::Attribute.new('year', ann_cap_cash, 'dollars')
210
- om_cash_flow_elems << OpenStudio::Attribute.new('year', ann_om_cash, 'dollars')
211
- energy_cash_flow_elems << OpenStudio::Attribute.new('year', ann_energy_cash, 'dollars')
212
- water_cash_flow_elems << OpenStudio::Attribute.new('year', ann_water_cash, 'dollars')
213
- tot_cash_flow_elems << OpenStudio::Attribute.new('year', ann_tot_cash, 'dollars')
214
-
215
- @runner.registerValue("cash_flows_capital_year_#{i + 1}", ann_cap_cash, 'dollars')
216
- @runner.registerValue("cash_flows_operating_year_#{i + 1}", ann_om_cash, 'dollars')
217
- @runner.registerValue("cash_flows_energy_year_#{i + 1}", ann_energy_cash, 'dollars')
218
- @runner.registerValue("cash_flows_water_year_#{i + 1}", ann_water_cash, 'dollars')
219
- @runner.registerValue("cash_flows_total_year_#{i + 1}", ann_tot_cash, 'dollars')
220
-
221
- end # next year
222
-
223
- # end cash flows
224
- cash_flow_elems << OpenStudio::Attribute.new('cash_flow', cap_cash_flow_elems)
225
- cash_flow_elems << OpenStudio::Attribute.new('cash_flow', om_cash_flow_elems)
226
- cash_flow_elems << OpenStudio::Attribute.new('cash_flow', energy_cash_flow_elems)
227
- cash_flow_elems << OpenStudio::Attribute.new('cash_flow', water_cash_flow_elems)
228
- cash_flow_elems << OpenStudio::Attribute.new('cash_flow', tot_cash_flow_elems)
229
- result_elems << OpenStudio::Attribute.new('cash_flows', cash_flow_elems)
230
-
231
- # list of all end uses in OpenStudio
232
- end_use_cat_types = []
233
- OpenStudio::EndUseCategoryType.getValues.each do |end_use_val|
234
- end_use_cat_types << OpenStudio::EndUseCategoryType.new(end_use_val)
235
- end
236
-
237
- # list of all end use fule types in OpenStudio
238
- end_use_fuel_types = []
239
- OpenStudio::EndUseFuelType.getValues.each do |end_use_fuel_type_val|
240
- end_use_fuel_types << OpenStudio::EndUseFuelType.new(end_use_fuel_type_val)
241
- end
242
-
243
- # list of the 12 months of the year in OpenStudio
244
- months = []
245
- OpenStudio::MonthOfYear.getValues.each do |month_of_year_val|
246
- if (month_of_year_val >= 1) && (month_of_year_val <= 12)
247
- months << OpenStudio::MonthOfYear.new(month_of_year_val)
248
- end
249
- end
250
-
251
- # map each end use category type to the name that will be used in the xml
252
- end_use_map = {
253
- OpenStudio::EndUseCategoryType.new('Heating').value => 'heating',
254
- OpenStudio::EndUseCategoryType.new('Cooling').value => 'cooling',
255
- OpenStudio::EndUseCategoryType.new('InteriorLights').value => 'lighting_interior',
256
- OpenStudio::EndUseCategoryType.new('ExteriorLights').value => 'lighting_exterior',
257
- OpenStudio::EndUseCategoryType.new('InteriorEquipment').value => 'equipment_interior',
258
- OpenStudio::EndUseCategoryType.new('ExteriorEquipment').value => 'equipment_exterior',
259
- OpenStudio::EndUseCategoryType.new('Fans').value => 'fans',
260
- OpenStudio::EndUseCategoryType.new('Pumps').value => 'pumps',
261
- OpenStudio::EndUseCategoryType.new('HeatRejection').value => 'heat_rejection',
262
- OpenStudio::EndUseCategoryType.new('Humidifier').value => 'humidification',
263
- OpenStudio::EndUseCategoryType.new('HeatRecovery').value => 'heat_recovery',
264
- OpenStudio::EndUseCategoryType.new('WaterSystems').value => 'water_systems',
265
- OpenStudio::EndUseCategoryType.new('Refrigeration').value => 'refrigeration',
266
- OpenStudio::EndUseCategoryType.new('Generators').value => 'generators'
267
- }
268
-
269
- # map each fuel type in EndUseFuelTypes to a specific FuelTypes
270
- fuel_type_map = {
271
- OpenStudio::EndUseFuelType.new('Electricity').value => OpenStudio::FuelType.new('Electricity'),
272
- OpenStudio::EndUseFuelType.new('Gas').value => OpenStudio::FuelType.new('Gas'),
273
- OpenStudio::EndUseFuelType.new('AdditionalFuel').value => OpenStudio::FuelType.new('Diesel'), # TODO: add other fuel types
274
- OpenStudio::EndUseFuelType.new('DistrictCooling').value => OpenStudio::FuelType.new('DistrictCooling'),
275
- OpenStudio::EndUseFuelType.new('DistrictHeating').value => OpenStudio::FuelType.new('DistrictHeating'),
276
- OpenStudio::EndUseFuelType.new('Water').value => OpenStudio::FuelType.new('Water')
277
- }
278
-
279
- # map each fuel type in EndUseFuelTypes to a specific FuelTypes
280
- fuel_type_alias_map = {
281
- OpenStudio::EndUseFuelType.new('Electricity').value => 'electricity',
282
- OpenStudio::EndUseFuelType.new('Gas').value => 'gas',
283
- OpenStudio::EndUseFuelType.new('AdditionalFuel').value => 'other_energy',
284
- OpenStudio::EndUseFuelType.new('DistrictCooling').value => 'district_cooling',
285
- OpenStudio::EndUseFuelType.new('DistrictHeating').value => 'district_heating',
286
- OpenStudio::EndUseFuelType.new('Water').value => 'water'
287
- }
288
-
289
- # annual "annual"
290
- annual_elems = OpenStudio::AttributeVector.new
291
-
292
- # consumption "consumption"
293
- cons_elems = OpenStudio::AttributeVector.new
294
-
295
- # electricity
296
- electricity = @sql.electricityTotalEndUses
297
- if electricity.is_initialized
298
- cons_elems << OpenStudio::Attribute.new('electricity', electricity.get, 'GJ')
299
- @runner.registerValue('annual_consumption_electricity', electricity.get, 'GJ')
300
- else
301
- cons_elems << OpenStudio::Attribute.new('electricity', 0.0, 'GJ')
302
- @runner.registerValue('annual_consumption_electricity', 0.0, 'GJ')
303
- end
304
-
305
- # gas
306
- gas = @sql.naturalGasTotalEndUses
307
- if gas.is_initialized
308
- cons_elems << OpenStudio::Attribute.new('gas', gas.get, 'GJ')
309
- @runner.registerValue('annual_consumption_gas', gas.get, 'GJ')
310
- else
311
- cons_elems << OpenStudio::Attribute.new('gas', 0.0, 'GJ')
312
- @runner.registerValue('annual_consumption_gas', 0.0, 'GJ')
313
- end
314
-
315
- # other_energy
316
- other_energy = @sql.otherFuelTotalEndUses
317
- if other_energy.is_initialized
318
- cons_elems << OpenStudio::Attribute.new('other_energy', other_energy.get, 'GJ')
319
- @runner.registerValue('annual_consumption_other_energy', other_energy.get, 'GJ')
320
- else
321
- cons_elems << OpenStudio::Attribute.new('other_energy', 0.0, 'GJ')
322
- @runner.registerValue('annual_consumption_other_energy', 0.0, 'GJ')
323
- end
324
-
325
- # district_cooling
326
- district_cooling = @sql.districtCoolingTotalEndUses
327
- if district_cooling.is_initialized
328
- cons_elems << OpenStudio::Attribute.new('district_cooling', district_cooling.get, 'GJ')
329
- @runner.registerValue('annual_consumption_district_cooling', district_cooling.get, 'GJ')
330
- else
331
- cons_elems << OpenStudio::Attribute.new('district_cooling', 0.0, 'GJ')
332
- @runner.registerValue('annual_consumption_district_cooling', 0.0, 'GJ')
333
- end
334
-
335
- # district_heating
336
- district_heating = @sql.districtHeatingTotalEndUses
337
- if district_heating.is_initialized
338
- cons_elems << OpenStudio::Attribute.new('district_heating', district_heating.get, 'GJ')
339
- @runner.registerValue('annual_consumption_district_heating', district_heating.get, 'GJ')
340
- else
341
- cons_elems << OpenStudio::Attribute.new('district_heating', 0.0, 'GJ')
342
- @runner.registerValue('annual_consumption_district_heating', 0.0, 'GJ')
343
- end
344
-
345
- # water
346
- water = @sql.waterTotalEndUses
347
- if water.is_initialized
348
- cons_elems << OpenStudio::Attribute.new('water', water.get, 'm^3')
349
- @runner.registerValue('annual_consumption_water', water.get, 'm^3')
350
- else
351
- cons_elems << OpenStudio::Attribute.new('water', 0.0, 'm^3')
352
- @runner.registerValue('annual_consumption_water', 0.0, 'm^3')
353
- end
354
-
355
- # end consumption
356
- annual_elems << OpenStudio::Attribute.new('consumption', cons_elems)
357
-
358
- # demand "demand"
359
- demand_elems = OpenStudio::AttributeVector.new
360
-
361
- # get the weather file run period (as opposed to design day run period)
362
- ann_env_pd = nil
363
- @sql.availableEnvPeriods.each do |env_pd|
364
- env_type = @sql.environmentType(env_pd)
365
- if env_type.is_initialized
366
- if env_type.get == OpenStudio::EnvironmentType.new('WeatherRunPeriod')
367
- ann_env_pd = env_pd
368
- end
369
- end
370
- end
371
-
372
- # only try to get the annual peak demand if an annual simulation was run
373
- if ann_env_pd
374
-
375
- # create some units to use
376
- joule_unit = OpenStudio.createUnit('J').get
377
- gigajoule_unit = OpenStudio.createUnit('GJ').get
378
- hrs_unit = OpenStudio.createUnit('h').get
379
- kilowatt_unit = OpenStudio.createUnit('kW').get
380
-
381
- # get the annual hours simulated
382
- hrs_sim = '(0 - no partial annual simulation)'
383
- if @sql.hoursSimulated.is_initialized
384
- hrs_sim = @sql.hoursSimulated.get
385
- if hrs_sim != 8760
386
- @runner.registerError("Simulation was only #{hrs_sim} hrs; EDA requires an annual simulation (8760 hrs)")
387
- return OpenStudio::Attribute.new('report', result_elems)
388
- end
389
- end
390
-
391
- # Get the electricity timeseries to determine the year used
392
- elec = @sql.timeSeries(ann_env_pd, 'Zone Timestep', 'Electricity:Facility', '')
393
- timeseries_yr = nil
394
- if elec.is_initialized
395
- timeseries_yr = elec.get.dateTimes[0].date.year
396
- else
397
- @runner.registerError('Peak Demand timeseries (Electricity:Facility at zone timestep) could not be found, cannot determine the informatino needed to calculate savings or incentives.')
398
- end
399
- # Setup the peak demand time window based on input arguments.
400
- # Note that holidays and weekends are not excluded because
401
- # of a bug in EnergyPlus dates.
402
- # This will only impact corner-case buildings that have
403
- # peak demand on weekends or holidays, which is unusual.
404
- @runner.registerInfo("Peak Demand window is #{start_mo} #{start_day} to #{end_mo} #{end_day} from #{start_hr}:00 to #{end_hr}:00.")
405
- start_date = OpenStudio::DateTime.new(OpenStudio::Date.new(OpenStudio::MonthOfYear.new(start_mo), start_day, timeseries_yr), OpenStudio::Time.new(0, 0, 0, 0))
406
- end_date = OpenStudio::DateTime.new(OpenStudio::Date.new(OpenStudio::MonthOfYear.new(end_mo), end_day, timeseries_yr), OpenStudio::Time.new(0, 24, 0, 0))
407
- start_time = OpenStudio::Time.new(0, start_hr, 0, 0)
408
- end_time = OpenStudio::Time.new(0, end_hr, 0, 0)
409
-
410
- # Get the day type timeseries.
411
- day_types = nil
412
- day_type_indices = @sql.timeSeries(ann_env_pd, 'Zone Timestep', 'Site Day Type Index', 'Environment')
413
- if day_type_indices.is_initialized
414
- # Put values into array
415
- day_types = []
416
- day_type_vals = day_type_indices.get.values
417
- for i in 0..(day_type_vals.size - 1)
418
- day_types << day_type_vals[i]
419
- end
420
- else
421
- @runner.registerError('Day Type timeseries (Site Day Type Index at zone timestep) could not be found, cannot accurately determine the peak demand.')
422
- end
423
-
424
- # electricity_peak_demand
425
- electricity_peak_demand = -1.0
426
- electricity_peak_demand_time = nil
427
- # deduce the timestep based on the hours simulated and the number of datapoints in the timeseries
428
- if elec.is_initialized && day_types
429
- elec = elec.get
430
- num_int = elec.values.size
431
- int_len_hrs = OpenStudio::Quantity.new(hrs_sim / num_int, hrs_unit)
432
-
433
- # Put timeseries into array
434
- elec_vals = []
435
- ann_elec_vals = elec.values
436
- for i in 0..(ann_elec_vals.size - 1)
437
- elec_vals << ann_elec_vals[i]
438
- end
439
-
440
- # Put values into array
441
- elec_times = []
442
- ann_elec_times = elec.dateTimes
443
- for i in 0..(ann_elec_times.size - 1)
444
- elec_times << ann_elec_times[i]
445
- end
446
-
447
- # Loop through the time/value pairs and find the peak
448
- # excluding the times outside of the Xcel peak demand window
449
- elec_times.zip(elec_vals).each_with_index do |vs, ind|
450
- date_time = vs[0]
451
- val = vs[1]
452
- day_type = day_types[ind]
453
- time = date_time.time
454
- date = date_time.date
455
- day_of_week = date.dayOfWeek
456
- # Convert the peak demand to kW
457
- val_J_per_hr = val / int_len_hrs.value
458
- val_kW = OpenStudio.convert(val_J_per_hr, 'J/h', 'kW').get
459
-
460
- # puts("#{val_kW}kW; #{date}; #{time}; #{day_of_week.valueName}")
461
-
462
- # Skip times outside of the correct months
463
- next if date_time < start_date || date_time > end_date
464
- # Skip times before 2pm and after 6pm
465
- next if time < start_time || time > end_time
466
- # Skip weekends if asked
467
- if skip_weekends
468
- # Sunday = 1, Saturday = 7
469
- next if day_type == 1 || day_type == 7
470
- end
471
- # Skip holidays if asked
472
- if skip_holidays
473
- # Holiday = 8
474
- next if day_type == 8
475
- end
476
-
477
- # puts("VALID #{val_kW}kW; #{date}; #{time}; #{day_of_week.valueName}")
478
-
479
- # Check peak demand against this timestep
480
- # and update if this timestep is higher.
481
- if val > electricity_peak_demand
482
- electricity_peak_demand = val
483
- electricity_peak_demand_time = date_time
484
- end
485
- end
486
- elec_peak_demand_timestep_J = OpenStudio::Quantity.new(electricity_peak_demand, joule_unit)
487
- num_int = elec.values.size
488
- int_len_hrs = OpenStudio::Quantity.new(hrs_sim / num_int, hrs_unit)
489
- elec_peak_demand_hourly_J_per_hr = elec_peak_demand_timestep_J / int_len_hrs
490
- electricity_peak_demand = OpenStudio.convert(elec_peak_demand_hourly_J_per_hr, kilowatt_unit).get.value
491
- demand_elems << OpenStudio::Attribute.new('electricity_peak_demand', electricity_peak_demand, 'kW')
492
- @runner.registerValue('annual_demand_electricity_peak_demand', electricity_peak_demand, 'kW')
493
- @runner.registerInfo("Peak Demand = #{electricity_peak_demand.round(2)}kW on #{electricity_peak_demand_time}")
494
- else
495
- @runner.registerError('Peak Demand timeseries (Electricity:Facility at zone timestep) could not be found, cannot determine the informatino needed to calculate savings or incentives.')
496
- demand_elems << OpenStudio::Attribute.new('electricity_peak_demand', 0.0, 'kW')
497
- @runner.registerValue('annual_demand_electricity_peak_demand', 0.0, 'kW')
498
- end
499
-
500
- # electricity_annual_avg_peak_demand
501
- val = @sql.electricityTotalEndUses
502
- if val.is_initialized
503
- ann_elec_gj = OpenStudio::Quantity.new(val.get, gigajoule_unit)
504
- ann_hrs = OpenStudio::Quantity.new(hrs_sim, hrs_unit)
505
- elec_ann_avg_peak_demand_hourly_GJ_per_hr = ann_elec_gj / ann_hrs
506
- electricity_annual_avg_peak_demand = OpenStudio.convert(elec_ann_avg_peak_demand_hourly_GJ_per_hr, kilowatt_unit).get.value
507
- demand_elems << OpenStudio::Attribute.new('electricity_annual_avg_peak_demand', electricity_annual_avg_peak_demand, 'kW')
508
- @runner.registerValue('annual_demand_electricity_annual_avg_peak_demand', electricity_annual_avg_peak_demand, 'kW')
509
- else
510
- demand_elems << OpenStudio::Attribute.new('electricity_annual_avg_peak_demand', 0.0, 'kW')
511
- @runner.registerValue('annual_demand_electricity_annual_avg_peak_demand', 0.0, 'kW')
512
- end
513
-
514
- # district_cooling_peak_demand
515
- district_cooling_peak_demand = -1.0
516
- ann_dist_clg_peak_demand_time = nil
517
- dist_clg = @sql.timeSeries(ann_env_pd, 'Zone Timestep', 'DistrictCooling:Facility', '')
518
- # deduce the timestep based on the hours simulated and the number of datapoints in the timeseries
519
- if dist_clg.is_initialized && day_types
520
- dist_clg = dist_clg.get
521
- num_int = dist_clg.values.size
522
- int_len_hrs = OpenStudio::Quantity.new(hrs_sim / num_int, hrs_unit)
523
-
524
- # Put timeseries into array
525
- dist_clg_vals = []
526
- ann_dist_clg_vals = dist_clg.values
527
- for i in 0..(ann_dist_clg_vals.size - 1)
528
- dist_clg_vals << ann_dist_clg_vals[i]
529
- end
530
-
531
- # Put values into array
532
- dist_clg_times = []
533
- ann_dist_clg_times = dist_clg.dateTimes
534
- for i in 0..(ann_dist_clg_times.size - 1)
535
- dist_clg_times << ann_dist_clg_times[i]
536
- end
537
-
538
- # Loop through the time/value pairs and find the peak
539
- # excluding the times outside of the Xcel peak demand window
540
- dist_clg_times.zip(dist_clg_vals).each_with_index do |vs, ind|
541
- date_time = vs[0]
542
- val = vs[1]
543
- day_type = day_types[ind]
544
- time = date_time.time
545
- date = date_time.date
546
- day_of_week = date.dayOfWeek
547
- # Convert the peak demand to kW
548
- val_J_per_hr = val / int_len_hrs.value
549
- val_kW = OpenStudio.convert(val_J_per_hr, 'J/h', 'kW').get
550
-
551
- # puts("#{val_kW}kW; #{date}; #{time}; #{day_of_week.valueName}")
552
-
553
- # Skip times outside of the correct months
554
- next if date_time < start_date || date_time > end_date
555
- # Skip times before 2pm and after 6pm
556
- next if time < start_time || time > end_time
557
- # Skip weekends if asked
558
- if skip_weekends
559
- # Sunday = 1, Saturday = 7
560
- next if day_type == 1 || day_type == 7
561
- end
562
- # Skip holidays if asked
563
- if skip_holidays
564
- # Holiday = 8
565
- next if day_type == 8
566
- end
567
-
568
- # puts("VALID #{val_kW}kW; #{date}; #{time}; #{day_of_week.valueName}")
569
-
570
- # Check peak demand against this timestep
571
- # and update if this timestep is higher.
572
- if val > district_cooling_peak_demand
573
- district_cooling_peak_demand = val
574
- ann_dist_clg_peak_demand_time = date_time
575
- end
576
- end
577
- dist_clg_peak_demand_timestep_J = OpenStudio::Quantity.new(district_cooling_peak_demand, joule_unit)
578
- num_int = dist_clg.values.size
579
- int_len_hrs = OpenStudio::Quantity.new(hrs_sim / num_int, hrs_unit)
580
- dist_clg_peak_demand_hourly_J_per_hr = dist_clg_peak_demand_timestep_J / int_len_hrs
581
- district_cooling_peak_demand = OpenStudio.convert(dist_clg_peak_demand_hourly_J_per_hr, kilowatt_unit).get.value
582
- demand_elems << OpenStudio::Attribute.new('district_cooling_peak_demand', district_cooling_peak_demand, 'kW')
583
- @runner.registerValue('annual_demand_district_cooling_peak_demand', district_cooling_peak_demand, 'kW')
584
- @runner.registerInfo("District Cooling Peak Demand = #{district_cooling_peak_demand.round(2)}kW on #{ann_dist_clg_peak_demand_time}")
585
- else
586
- demand_elems << OpenStudio::Attribute.new('district_cooling_peak_demand', 0.0, 'kW')
587
- @runner.registerValue('annual_demand_district_cooling_peak_demand', 0.0, 'kW')
588
- end
589
-
590
- else
591
- @runner.registerError('Could not find an annual run period')
592
- return OpenStudio::Attribute.new('report', result_elems)
593
- end
594
-
595
- # end demand
596
- annual_elems << OpenStudio::Attribute.new('demand', demand_elems)
597
-
598
- # utility_cost
599
- utility_cost_elems = OpenStudio::AttributeVector.new
600
- annual_utility_cost_map = {}
601
-
602
- # electricity
603
- electricity = @sql.annualTotalCost(OpenStudio::FuelType.new('Electricity'))
604
- if electricity.is_initialized
605
- utility_cost_elems << OpenStudio::Attribute.new('electricity', electricity.get, 'dollars')
606
- @runner.registerValue('annual_utility_cost_electricity', electricity.get, 'dollars')
607
- annual_utility_cost_map[OpenStudio::EndUseFuelType.new('Electricity').valueName] = electricity.get
608
- else
609
- utility_cost_elems << OpenStudio::Attribute.new('electricity', 0.0, 'dollars')
610
- @runner.registerValue('annual_utility_cost_electricity', 0.0, 'dollars')
611
- annual_utility_cost_map[OpenStudio::EndUseFuelType.new('Electricity').valueName] = 0.0
612
- end
613
-
614
- # electricity_consumption_charge and electricity_demand_charge
615
- electric_consumption_charge = 0.0
616
- electric_demand_charge = 0.0
617
-
618
- electric_rate_query = "SELECT value FROM tabulardatawithstrings WHERE ReportName='LEEDsummary' AND ReportForString='Entire Facility' AND TableName='EAp2-3. Energy Type Summary' AND RowName='Electricity' AND ColumnName='Utility Rate'"
619
- electric_rate_name = @sql.execAndReturnFirstString(electric_rate_query)
620
- if electric_rate_name.is_initialized
621
- electric_rate_name = electric_rate_name.get.strip
622
-
623
- # electricity_consumption_charge
624
- electric_consumption_charge_query = "SELECT value FROM tabulardatawithstrings WHERE ReportName='Tariff Report' AND ReportForString='#{electric_rate_name}' AND TableName='Categories' AND RowName='EnergyCharges (~~$~~)' AND ColumnName='Sum'"
625
- val = @sql.execAndReturnFirstDouble(electric_consumption_charge_query)
626
- if val.is_initialized
627
- electric_consumption_charge = val.get
628
- end
629
-
630
- # electricity_demand_charge
631
- electric_demand_charge_query = "SELECT value FROM tabulardatawithstrings WHERE ReportName='Tariff Report' AND ReportForString='#{electric_rate_name}' AND TableName='Categories' AND RowName='DemandCharges (~~$~~)' AND ColumnName='Sum'"
632
- val = @sql.execAndReturnFirstDouble(electric_demand_charge_query)
633
- if val.is_initialized
634
- electric_demand_charge = val.get
635
- end
636
-
637
- end
638
- utility_cost_elems << OpenStudio::Attribute.new('electricity_consumption_charge', electric_consumption_charge, 'dollars')
639
- @runner.registerValue('annual_utility_cost_electricity_consumption_charge', electric_consumption_charge, 'dollars')
640
- utility_cost_elems << OpenStudio::Attribute.new('electricity_demand_charge', electric_demand_charge, 'dollars')
641
- @runner.registerValue('annual_utility_cost_electricity_demand_charge', electric_demand_charge, 'dollars')
642
-
643
- # gas
644
- gas = @sql.annualTotalCost(OpenStudio::FuelType.new('Gas'))
645
- if gas.is_initialized
646
- annual_utility_cost_map[OpenStudio::EndUseFuelType.new('Gas').valueName] = gas.get
647
- else
648
- annual_utility_cost_map[OpenStudio::EndUseFuelType.new('Gas').valueName] = 0.0
649
- end
650
-
651
- # district_cooling
652
- district_cooling_charge = 0.0
653
-
654
- district_cooling_rate_query = "SELECT value FROM tabulardatawithstrings WHERE ReportName='LEEDsummary' AND ReportForString='Entire Facility' AND TableName='EAp2-3. Energy Type Summary' AND RowName='District Cooling' AND ColumnName='Utility Rate'"
655
- district_cooling_rate_name = @sql.execAndReturnFirstString(district_cooling_rate_query)
656
- if district_cooling_rate_name.is_initialized
657
- district_cooling_rate_name = district_cooling_rate_name.get.strip
658
-
659
- # district_cooling_charge
660
- district_cooling_charge_query = "SELECT value FROM tabulardatawithstrings WHERE ReportName='Tariff Report' AND ReportForString='#{district_cooling_rate_name}' AND TableName='Categories' AND RowName='Basis (~~$~~)' AND ColumnName='Sum'"
661
- val = @sql.execAndReturnFirstDouble(district_cooling_charge_query)
662
- if val.is_initialized
663
- district_cooling_charge = val.get
664
- end
665
-
666
- end
667
- annual_utility_cost_map[OpenStudio::EndUseFuelType.new('DistrictCooling').valueName] = district_cooling_charge
668
-
669
- # district_heating
670
- district_heating_charge = 0.0
671
-
672
- district_heating_rate_query = "SELECT value FROM tabulardatawithstrings WHERE ReportName='LEEDsummary' AND ReportForString='Entire Facility' AND TableName='EAp2-3. Energy Type Summary' AND RowName='District Heating' AND ColumnName='Utility Rate'"
673
- district_heating_rate_name = @sql.execAndReturnFirstString(district_heating_rate_query)
674
- if district_heating_rate_name.is_initialized
675
- district_heating_rate_name = district_heating_rate_name.get.strip
676
-
677
- # district_heating_charge
678
- district_heating_charge_query = "SELECT value FROM tabulardatawithstrings WHERE ReportName='Tariff Report' AND ReportForString='#{district_heating_rate_name}' AND TableName='Categories' AND RowName='Basis (~~$~~)' AND ColumnName='Sum'"
679
- val = @sql.execAndReturnFirstDouble(district_heating_charge_query)
680
- if val.is_initialized
681
- district_heating_charge = val.get
682
- end
683
-
684
- end
685
- annual_utility_cost_map[OpenStudio::EndUseFuelType.new('DistrictHeating').valueName] = district_heating_charge
686
-
687
- # water
688
- water = @sql.annualTotalCost(OpenStudio::FuelType.new('Water'))
689
- if water.is_initialized
690
- annual_utility_cost_map[OpenStudio::EndUseFuelType.new('Water').valueName] = water.get
691
- else
692
- annual_utility_cost_map[OpenStudio::EndUseFuelType.new('Water').valueName] = 0.0
693
- end
694
-
695
- # total
696
- total_query = "SELECT Value from tabulardatawithstrings where (reportname = 'Economics Results Summary Report') and (ReportForString = 'Entire Facility') and (TableName = 'Annual Cost') and (ColumnName ='Total') and (((RowName = 'Cost') and (Units = '~~$~~')) or (RowName = 'Cost (~~$~~)'))"
697
- total = @sql.execAndReturnFirstDouble(total_query)
698
-
699
- # other_energy
700
- # Subtract off the already accounted for fuel types from the total
701
- # to account for fuels on custom meters where the fuel type is not known.
702
- prev_tot = 0.0
703
- annual_utility_cost_map.each do |fuel, val|
704
- prev_tot += val
705
- end
706
- if total.is_initialized
707
- other_val = total.get - prev_tot
708
- annual_utility_cost_map[OpenStudio::EndUseFuelType.new('AdditionalFuel').valueName] = other_val
709
- else
710
- annual_utility_cost_map[OpenStudio::EndUseFuelType.new('AdditionalFuel').valueName] = 0.0
711
- end
712
-
713
- # export remaining costs in the correct order
714
- # gas
715
- utility_cost_elems << OpenStudio::Attribute.new('gas', annual_utility_cost_map[OpenStudio::EndUseFuelType.new('Gas').valueName], 'dollars')
716
- @runner.registerValue('annual_utility_cost_gas', annual_utility_cost_map[OpenStudio::EndUseFuelType.new('Gas').valueName], 'dollars')
717
- # other_energy
718
- utility_cost_elems << OpenStudio::Attribute.new('other_energy', annual_utility_cost_map[OpenStudio::EndUseFuelType.new('AdditionalFuel').valueName], 'dollars')
719
- @runner.registerValue('annual_utility_cost_other_energy', annual_utility_cost_map[OpenStudio::EndUseFuelType.new('AdditionalFuel').valueName], 'dollars')
720
- # district_cooling
721
- utility_cost_elems << OpenStudio::Attribute.new('district_cooling', annual_utility_cost_map[OpenStudio::EndUseFuelType.new('DistrictCooling').valueName], 'dollars')
722
- @runner.registerValue('annual_utility_cost_district_cooling', annual_utility_cost_map[OpenStudio::EndUseFuelType.new('DistrictCooling').valueName], 'dollars')
723
- # district_heating
724
- utility_cost_elems << OpenStudio::Attribute.new('district_heating', annual_utility_cost_map[OpenStudio::EndUseFuelType.new('DistrictHeating').valueName], 'dollars')
725
- @runner.registerValue('annual_utility_cost_district_heating', annual_utility_cost_map[OpenStudio::EndUseFuelType.new('DistrictHeating').valueName], 'dollars')
726
- # water
727
- utility_cost_elems << OpenStudio::Attribute.new('water', annual_utility_cost_map[OpenStudio::EndUseFuelType.new('Water').valueName], 'dollars')
728
- @runner.registerValue('annual_utility_cost_water', annual_utility_cost_map[OpenStudio::EndUseFuelType.new('Water').valueName], 'dollars')
729
- # total
730
- if total.is_initialized
731
- utility_cost_elems << OpenStudio::Attribute.new('total', total.get, 'dollars')
732
- @runner.registerValue('annual_utility_cost_total', total.get, 'dollars')
733
- else
734
- utility_cost_elems << OpenStudio::Attribute.new('total', 0.0, 'dollars')
735
- @runner.registerValue('annual_utility_cost_total', 0.0, 'dollars')
736
- end
737
-
738
- # end_uses - utility costs by end use using average blended cost
739
- end_uses_elems = OpenStudio::AttributeVector.new
740
- # map to store the costs by end use
741
- cost_by_end_use = {}
742
-
743
- # fill the map with 0.0's to start
744
- end_use_cat_types.each do |end_use_cat_type|
745
- cost_by_end_use[end_use_cat_type] = 0.0
746
- end
747
-
748
- # only attempt to get monthly data if enduses table is available
749
- if @sql.endUses.is_initialized
750
- end_uses_table = @sql.endUses.get
751
- # loop through all the fuel types
752
- end_use_fuel_types.each do |end_use_fuel_type|
753
- # get the annual total cost for this fuel type
754
- ann_cost = annual_utility_cost_map[end_use_fuel_type.valueName]
755
- # get the total annual usage for this fuel type in all end use categories
756
- # loop through all end uses, adding the annual usage value to the aggregator
757
- ann_usg = 0.0
758
- end_use_cat_types.each do |end_use_cat_type|
759
- ann_usg += end_uses_table.getEndUse(end_use_fuel_type, end_use_cat_type)
760
- end
761
- # figure out the annual blended rate for this fuel type
762
- avg_ann_rate = 0.0
763
- if ann_cost > 0 && ann_usg > 0
764
- avg_ann_rate = ann_cost / ann_usg
765
- end
766
- # for each end use category, figure out the cost if using
767
- # the avg ann rate; add this cost to the map
768
- end_use_cat_types.each do |end_use_cat_type|
769
- cost_by_end_use[end_use_cat_type] += end_uses_table.getEndUse(end_use_fuel_type, end_use_cat_type) * avg_ann_rate
770
- end
771
- end
772
- # loop through the end uses and record the annual total cost based on the avg annual rate
773
- end_use_cat_types.each do |end_use_cat_type|
774
- # record the value
775
- end_uses_elems << OpenStudio::Attribute.new(end_use_map[end_use_cat_type.value], cost_by_end_use[end_use_cat_type], 'dollars')
776
- @runner.registerValue("annual_utility_cost_end_uses_#{end_use_map[end_use_cat_type.value]}", cost_by_end_use[end_use_cat_type], 'dollars')
777
- end
778
- else
779
- @runner.registerError('End-Use table not available in results; could not retrieve monthly costs by end use')
780
- return OpenStudio::Attribute.new('report', result_elems)
781
- end
782
-
783
- # end end_uses
784
- utility_cost_elems << OpenStudio::Attribute.new('end_uses', end_uses_elems)
785
-
786
- # end utility_costs
787
- annual_elems << OpenStudio::Attribute.new('utility_cost', utility_cost_elems)
788
-
789
- # end annual
790
- result_elems << OpenStudio::Attribute.new('annual', annual_elems)
791
-
792
- # monthly
793
- monthly_elems = OpenStudio::AttributeVector.new
794
-
795
- # consumption
796
- cons_elems = OpenStudio::AttributeVector.new
797
- # loop through all end uses
798
- end_use_cat_types.each do |end_use_cat|
799
- end_use_elems = OpenStudio::AttributeVector.new
800
- end_use_name = end_use_map[end_use_cat.value]
801
- # in each end use, loop through all fuel types
802
- end_use_fuel_types.each do |end_use_fuel_type|
803
- fuel_type_elems = OpenStudio::AttributeVector.new
804
- fuel_type_name = fuel_type_alias_map[end_use_fuel_type.value]
805
- ann_energy_cons = 0.0
806
- # in each end use, loop through months and get monthly enedy consumption
807
- months.each_with_index do |month, i|
808
- mon_energy_cons = 0.0
809
- val = @sql.energyConsumptionByMonth(end_use_fuel_type, end_use_cat, month)
810
- if val.is_initialized
811
- monthly_consumption_J = OpenStudio::Quantity.new(val.get, joule_unit)
812
- monthly_consumption_GJ = OpenStudio.convert(monthly_consumption_J, gigajoule_unit).get.value
813
- mon_energy_cons = monthly_consumption_GJ
814
- ann_energy_cons += monthly_consumption_GJ
815
- end
816
- # record the monthly value
817
- if end_use_fuel_type == OpenStudio::EndUseFuelType.new('Water')
818
- fuel_type_elems << OpenStudio::Attribute.new('month', mon_energy_cons, 'm^3')
819
- @runner.registerValue("monthly_consumption_#{end_use_name}_#{fuel_type_name}_month_#{i + 1}", mon_energy_cons, 'm^3')
820
- else
821
- fuel_type_elems << OpenStudio::Attribute.new('month', mon_energy_cons, 'GJ')
822
- @runner.registerValue("monthly_consumption_#{end_use_name}_#{fuel_type_name}_month_#{i + 1}", mon_energy_cons, 'GJ')
823
- end
824
- end
825
- # record the annual total
826
- fuel_type_elems << OpenStudio::Attribute.new('year', ann_energy_cons, 'GJ')
827
- @runner.registerValue("monthly_consumption_#{end_use_name}_#{fuel_type_name}_year", ann_energy_cons, 'GJ')
828
- # add this fuel type
829
- end_use_elems << OpenStudio::Attribute.new(fuel_type_alias_map[end_use_fuel_type.value], fuel_type_elems)
830
- end
831
- # add this end use
832
- cons_elems << OpenStudio::Attribute.new(end_use_map[end_use_cat.value], end_use_elems)
833
- end
834
- # end consumption
835
- monthly_elems << OpenStudio::Attribute.new('consumption', cons_elems)
836
-
837
- # create a unit to use
838
- watt_unit = OpenStudio.createUnit('W').get
839
- kilowatt_unit = OpenStudio.createUnit('kW').get
840
-
841
- # demand
842
- demand_elems = OpenStudio::AttributeVector.new
843
- # loop through all end uses
844
- end_use_cat_types.each do |end_use_cat|
845
- end_use_elems = OpenStudio::AttributeVector.new
846
- end_use_name = end_use_map[end_use_cat.value]
847
- # in each end use, loop through all fuel types
848
- end_use_fuel_types.each do |end_use_fuel_type|
849
- fuel_type_elems = OpenStudio::AttributeVector.new
850
- fuel_type_name = fuel_type_alias_map[end_use_fuel_type.value]
851
- ann_peak_demand = 0.0
852
- # in each end use, loop through months and get monthly enedy consumption
853
- months.each_with_index do |month, i|
854
- mon_peak_demand = 0.0
855
- val = @sql.peakEnergyDemandByMonth(end_use_fuel_type, end_use_cat, month)
856
- if val.is_initialized
857
- mon_peak_demand_W = OpenStudio::Quantity.new(val.get, watt_unit)
858
- mon_peak_demand = OpenStudio.convert(mon_peak_demand_W, kilowatt_unit).get.value
859
- end
860
- # record the monthly value
861
- fuel_type_elems << OpenStudio::Attribute.new('month', mon_peak_demand, 'kW')
862
- @runner.registerValue("monthly_demand_#{end_use_name}_#{fuel_type_name}_month_#{i + 1}", mon_peak_demand, 'kW')
863
- # if month peak demand > ann peak demand make this new ann peak demand
864
- if mon_peak_demand > ann_peak_demand
865
- ann_peak_demand = mon_peak_demand
866
- end
867
- end
868
- # record the annual peak demand
869
- fuel_type_elems << OpenStudio::Attribute.new('year', ann_peak_demand, 'kW')
870
- @runner.registerValue("monthly_demand_#{end_use_name}_#{fuel_type_name}_year", ann_peak_demand, 'kW')
871
- # add this fuel type
872
- end_use_elems << OpenStudio::Attribute.new(fuel_type_alias_map[end_use_fuel_type.value], fuel_type_elems)
873
- end
874
- # add this end use
875
- demand_elems << OpenStudio::Attribute.new(end_use_map[end_use_cat.value], end_use_elems)
876
- end
877
- # end demand
878
- monthly_elems << OpenStudio::Attribute.new('demand', demand_elems)
879
-
880
- # end monthly
881
- result_elems << OpenStudio::Attribute.new('monthly', monthly_elems)
882
-
883
- result_elem = OpenStudio::Attribute.new('results', result_elems)
884
- return result_elem
885
- end # end create_results
886
- end
1
+ # *******************************************************************************
2
+ # OpenStudio(R), Copyright (c) 2008-2019, Alliance for Sustainable Energy, LLC.
3
+ # All rights reserved.
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+ #
7
+ # (1) Redistributions of source code must retain the above copyright notice,
8
+ # this list of conditions and the following disclaimer.
9
+ #
10
+ # (2) Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ #
14
+ # (3) Neither the name of the copyright holder nor the names of any contributors
15
+ # may be used to endorse or promote products derived from this software without
16
+ # specific prior written permission from the respective party.
17
+ #
18
+ # (4) Other than as required in clauses (1) and (2), distributions in any form
19
+ # of modifications or other derivative works may not use the "OpenStudio"
20
+ # trademark, "OS", "os", or any other confusingly similar designation without
21
+ # specific prior written permission from Alliance for Sustainable Energy, LLC.
22
+ #
23
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS
24
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
25
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE
27
+ # UNITED STATES GOVERNMENT, OR THE UNITED STATES DEPARTMENT OF ENERGY, NOR ANY OF
28
+ # THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
30
+ # OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33
+ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
+ # *******************************************************************************
35
+
36
+ module OsLib_CreateResults
37
+ # Reports out the detailed simulation results needed by EDAPT.
38
+ # Results are output as both OpenStudio::Attributes (for OpenStudio 1.X)
39
+ # and runner.registerValue (for OpenStudio 2.X).
40
+ # @param skip_weekends [Bool] if true, weekends will not be included in the peak demand window
41
+ # @param skip_holidays [Bool] if true, holidays will not be included in the peak demand window
42
+ # @param start_mo [String] the start month for the peak demand window
43
+ # @param start_day [Integer] the start day for the peak demand window
44
+ # @param start_hr [Integer] the start hour for the peak demand window, using 24-hr clock
45
+ # @param end_mo [String] the end month for the peak demand window
46
+ # @param end_day [Integer] the end day for the peak demand window
47
+ # @param end_hr [Integer] the end hour for the peak demand window, using 24-hr clock
48
+ # @return [OpenStudio::AttributeVector] a vector of results needed by EDAPT
49
+ def create_results(skip_weekends = true,
50
+ skip_holidays = true,
51
+ start_mo = 'June',
52
+ start_day = 1,
53
+ start_hr = 14,
54
+ end_mo = 'September',
55
+ end_day = 30,
56
+ end_hr = 18)
57
+
58
+ # get the current version of OS being used to determine if sql query
59
+ # changes are needed (for when E+ changes).
60
+ os_version = OpenStudio::VersionString.new(OpenStudio.openStudioVersion)
61
+
62
+ # create an attribute vector to hold results
63
+ result_elems = OpenStudio::AttributeVector.new
64
+
65
+ # floor_area
66
+ floor_area_query = "SELECT Value FROM tabulardatawithstrings WHERE ReportName='AnnualBuildingUtilityPerformanceSummary' AND ReportForString='Entire Facility' AND TableName='Building Area' AND RowName='Net Conditioned Building Area' AND ColumnName='Area' AND Units='m2'"
67
+ floor_area = @sql.execAndReturnFirstDouble(floor_area_query)
68
+ if floor_area.is_initialized
69
+ result_elems << OpenStudio::Attribute.new('floor_area', floor_area.get, 'm^2')
70
+ @runner.registerValue('charsfloor_area', floor_area.get, 'm^2')
71
+ else
72
+ @runner.registerWarning('Building floor area not found')
73
+ return false
74
+ end
75
+
76
+ # inflation approach
77
+ inf_appr_query = "SELECT Value FROM tabulardatawithstrings WHERE ReportName='Life-Cycle Cost Report' AND ReportForString='Entire Facility' AND TableName='Life-Cycle Cost Parameters' AND RowName='Inflation Approach' AND ColumnName='Value'"
78
+ inf_appr = @sql.execAndReturnFirstString(inf_appr_query)
79
+ if inf_appr.is_initialized
80
+ if inf_appr.get == 'ConstantDollar'
81
+ inf_appr = 'Constant Dollar'
82
+ elsif inf_appr.get == 'CurrentDollar'
83
+ inf_appr = 'Current Dollar'
84
+ else
85
+ @runner.registerError("Inflation approach: #{inf_appr.get} not recognized")
86
+ return OpenStudio::Attribute.new('report', result_elems)
87
+ end
88
+ @runner.registerInfo("Inflation approach = #{inf_appr}")
89
+ else
90
+ @runner.registerError('Could not determine inflation approach used')
91
+ return OpenStudio::Attribute.new('report', result_elems)
92
+ end
93
+
94
+ # base year
95
+ base_yr_query = "SELECT Value FROM tabulardatawithstrings WHERE ReportName='Life-Cycle Cost Report' AND ReportForString='Entire Facility' AND TableName='Life-Cycle Cost Parameters' AND RowName='Base Date' AND ColumnName='Value'"
96
+ base_yr = @sql.execAndReturnFirstString(base_yr_query)
97
+ if base_yr.is_initialized
98
+ if base_yr.get =~ /\d\d\d\d/
99
+ base_yr = base_yr.get.match(/\d\d\d\d/)[0].to_f
100
+ else
101
+ @runner.registerError("Could not determine the analysis start year from #{base_yr.get}")
102
+ return OpenStudio::Attribute.new('report', result_elems)
103
+ end
104
+ else
105
+ @runner.registerError('Could not determine analysis start year')
106
+ return OpenStudio::Attribute.new('report', result_elems)
107
+ end
108
+
109
+ # analysis length
110
+ length_yrs_query = "SELECT Value FROM tabulardatawithstrings WHERE ReportName='Life-Cycle Cost Report' AND ReportForString='Entire Facility' AND TableName='Life-Cycle Cost Parameters' AND RowName='Length of Study Period in Years' AND ColumnName='Value'"
111
+ length_yrs = @sql.execAndReturnFirstInt(length_yrs_query)
112
+ if length_yrs.is_initialized
113
+ @runner.registerInfo "Analysis length = #{length_yrs.get} yrs"
114
+ length_yrs = length_yrs.get
115
+ else
116
+ @runner.registerError('Could not determine analysis length')
117
+ return OpenStudio::Attribute.new('report', result_elems)
118
+ end
119
+
120
+ # cash flows
121
+ cash_flow_elems = OpenStudio::AttributeVector.new
122
+
123
+ # setup a vector for each type of cash flow
124
+ cap_cash_flow_elems = OpenStudio::AttributeVector.new
125
+ om_cash_flow_elems = OpenStudio::AttributeVector.new
126
+ energy_cash_flow_elems = OpenStudio::AttributeVector.new
127
+ water_cash_flow_elems = OpenStudio::AttributeVector.new
128
+ tot_cash_flow_elems = OpenStudio::AttributeVector.new
129
+
130
+ # add the type to the element
131
+ cap_cash_flow_elems << OpenStudio::Attribute.new('type', "#{inf_appr} Capital Costs")
132
+ om_cash_flow_elems << OpenStudio::Attribute.new('type', "#{inf_appr} Operating Costs")
133
+ energy_cash_flow_elems << OpenStudio::Attribute.new('type', "#{inf_appr} Energy Costs")
134
+ water_cash_flow_elems << OpenStudio::Attribute.new('type', "#{inf_appr} Water Costs")
135
+ tot_cash_flow_elems << OpenStudio::Attribute.new('type', "#{inf_appr} Total Costs")
136
+
137
+ @runner.registerValue('cash_flows_capital_type', "#{inf_appr} Capital Costs")
138
+ @runner.registerValue('cash_flows_operating_type', "#{inf_appr} Operating Costs")
139
+ @runner.registerValue('cash_flows_energy_type', "#{inf_appr} Energy Costs")
140
+ @runner.registerValue('cash_flows_water_type', "#{inf_appr} Water Costs")
141
+ @runner.registerValue('cash_flows_total_type', "#{inf_appr} Total Costs")
142
+
143
+ # record the cash flow in these hashes
144
+ cap_cash_flow = {}
145
+ om_cash_flow = {}
146
+ energy_cash_flow = {}
147
+ water_cash_flow = {}
148
+ tot_cash_flow = {}
149
+
150
+ # loop through each year and record the cash flow
151
+ for i in 0..(length_yrs - 1) do
152
+ new_yr = base_yr + i
153
+
154
+ yr = nil
155
+ if os_version > OpenStudio::VersionString.new('1.5.3')
156
+ yr = "January #{new_yr.round}"
157
+ else
158
+ yr = "January #{new_yr.round}"
159
+ end
160
+
161
+ ann_cap_cash = 0.0
162
+ ann_om_cash = 0.0
163
+ ann_energy_cash = 0.0
164
+ ann_water_cash = 0.0
165
+ ann_tot_cash = 0.0
166
+
167
+ # capital cash flow
168
+ cap_cash_query = "SELECT Value FROM tabulardatawithstrings WHERE ReportName='Life-Cycle Cost Report' AND ReportForString='Entire Facility' AND TableName='Capital Cash Flow by Category (Without Escalation)' AND RowName='#{yr}' AND ColumnName='Total'"
169
+ cap_cash = @sql.execAndReturnFirstDouble(cap_cash_query)
170
+ if cap_cash.is_initialized
171
+ ann_cap_cash += cap_cash.get
172
+ ann_tot_cash += cap_cash.get
173
+ end
174
+
175
+ # o&m cash flow (excluding utility costs)
176
+ om_types = ['Maintenance', 'Repair', 'Operation', 'Replacement', 'MinorOverhaul', 'MajorOverhaul', 'OtherOperational']
177
+ om_types.each do |om_type|
178
+ om_cash_query = "SELECT Value FROM tabulardatawithstrings WHERE ReportName='Life-Cycle Cost Report' AND ReportForString='Entire Facility' AND TableName='Operating Cash Flow by Category (Without Escalation)' AND RowName='#{yr}' AND ColumnName='#{om_type}'"
179
+ om_cash = @sql.execAndReturnFirstDouble(om_cash_query)
180
+ if om_cash.is_initialized
181
+ ann_om_cash += om_cash.get
182
+ ann_tot_cash += om_cash.get
183
+ end
184
+ end
185
+
186
+ # energy cash flow
187
+ energy_cash_query = "SELECT Value FROM tabulardatawithstrings WHERE ReportName='Life-Cycle Cost Report' AND ReportForString='Entire Facility' AND TableName='Operating Cash Flow by Category (Without Escalation)' AND RowName='#{yr}' AND ColumnName='Energy'"
188
+ energy_cash = @sql.execAndReturnFirstDouble(energy_cash_query)
189
+ if energy_cash.is_initialized
190
+ ann_energy_cash += energy_cash.get
191
+ ann_tot_cash += energy_cash.get
192
+ end
193
+
194
+ # water cash flow
195
+ water_cash_query = "SELECT Value FROM tabulardatawithstrings WHERE ReportName='Life-Cycle Cost Report' AND ReportForString='Entire Facility' AND TableName='Operating Cash Flow by Category (Without Escalation)' AND RowName='#{yr}' AND ColumnName='Water'"
196
+ water_cash = @sql.execAndReturnFirstDouble(water_cash_query)
197
+ if water_cash.is_initialized
198
+ ann_water_cash += water_cash.get
199
+ ann_tot_cash += water_cash.get
200
+ end
201
+
202
+ # log the values for this year
203
+ cap_cash_flow[yr] = ann_cap_cash
204
+ om_cash_flow[yr] = ann_om_cash
205
+ energy_cash_flow[yr] = ann_energy_cash
206
+ water_cash_flow[yr] = ann_water_cash
207
+ tot_cash_flow[yr] = ann_tot_cash
208
+
209
+ cap_cash_flow_elems << OpenStudio::Attribute.new('year', ann_cap_cash, 'dollars')
210
+ om_cash_flow_elems << OpenStudio::Attribute.new('year', ann_om_cash, 'dollars')
211
+ energy_cash_flow_elems << OpenStudio::Attribute.new('year', ann_energy_cash, 'dollars')
212
+ water_cash_flow_elems << OpenStudio::Attribute.new('year', ann_water_cash, 'dollars')
213
+ tot_cash_flow_elems << OpenStudio::Attribute.new('year', ann_tot_cash, 'dollars')
214
+
215
+ @runner.registerValue("cash_flows_capital_year_#{i + 1}", ann_cap_cash, 'dollars')
216
+ @runner.registerValue("cash_flows_operating_year_#{i + 1}", ann_om_cash, 'dollars')
217
+ @runner.registerValue("cash_flows_energy_year_#{i + 1}", ann_energy_cash, 'dollars')
218
+ @runner.registerValue("cash_flows_water_year_#{i + 1}", ann_water_cash, 'dollars')
219
+ @runner.registerValue("cash_flows_total_year_#{i + 1}", ann_tot_cash, 'dollars')
220
+
221
+ end # next year
222
+
223
+ # end cash flows
224
+ cash_flow_elems << OpenStudio::Attribute.new('cash_flow', cap_cash_flow_elems)
225
+ cash_flow_elems << OpenStudio::Attribute.new('cash_flow', om_cash_flow_elems)
226
+ cash_flow_elems << OpenStudio::Attribute.new('cash_flow', energy_cash_flow_elems)
227
+ cash_flow_elems << OpenStudio::Attribute.new('cash_flow', water_cash_flow_elems)
228
+ cash_flow_elems << OpenStudio::Attribute.new('cash_flow', tot_cash_flow_elems)
229
+ result_elems << OpenStudio::Attribute.new('cash_flows', cash_flow_elems)
230
+
231
+ # list of all end uses in OpenStudio
232
+ end_use_cat_types = []
233
+ OpenStudio::EndUseCategoryType.getValues.each do |end_use_val|
234
+ end_use_cat_types << OpenStudio::EndUseCategoryType.new(end_use_val)
235
+ end
236
+
237
+ # list of all end use fule types in OpenStudio
238
+ end_use_fuel_types = []
239
+ OpenStudio::EndUseFuelType.getValues.each do |end_use_fuel_type_val|
240
+ end_use_fuel_types << OpenStudio::EndUseFuelType.new(end_use_fuel_type_val)
241
+ end
242
+
243
+ # list of the 12 months of the year in OpenStudio
244
+ months = []
245
+ OpenStudio::MonthOfYear.getValues.each do |month_of_year_val|
246
+ if (month_of_year_val >= 1) && (month_of_year_val <= 12)
247
+ months << OpenStudio::MonthOfYear.new(month_of_year_val)
248
+ end
249
+ end
250
+
251
+ # map each end use category type to the name that will be used in the xml
252
+ end_use_map = {
253
+ OpenStudio::EndUseCategoryType.new('Heating').value => 'heating',
254
+ OpenStudio::EndUseCategoryType.new('Cooling').value => 'cooling',
255
+ OpenStudio::EndUseCategoryType.new('InteriorLights').value => 'lighting_interior',
256
+ OpenStudio::EndUseCategoryType.new('ExteriorLights').value => 'lighting_exterior',
257
+ OpenStudio::EndUseCategoryType.new('InteriorEquipment').value => 'equipment_interior',
258
+ OpenStudio::EndUseCategoryType.new('ExteriorEquipment').value => 'equipment_exterior',
259
+ OpenStudio::EndUseCategoryType.new('Fans').value => 'fans',
260
+ OpenStudio::EndUseCategoryType.new('Pumps').value => 'pumps',
261
+ OpenStudio::EndUseCategoryType.new('HeatRejection').value => 'heat_rejection',
262
+ OpenStudio::EndUseCategoryType.new('Humidifier').value => 'humidification',
263
+ OpenStudio::EndUseCategoryType.new('HeatRecovery').value => 'heat_recovery',
264
+ OpenStudio::EndUseCategoryType.new('WaterSystems').value => 'water_systems',
265
+ OpenStudio::EndUseCategoryType.new('Refrigeration').value => 'refrigeration',
266
+ OpenStudio::EndUseCategoryType.new('Generators').value => 'generators'
267
+ }
268
+
269
+ # map each fuel type in EndUseFuelTypes to a specific FuelTypes
270
+ fuel_type_map = {
271
+ OpenStudio::EndUseFuelType.new('Electricity').value => OpenStudio::FuelType.new('Electricity'),
272
+ OpenStudio::EndUseFuelType.new('Gas').value => OpenStudio::FuelType.new('Gas'),
273
+ OpenStudio::EndUseFuelType.new('AdditionalFuel').value => OpenStudio::FuelType.new('Diesel'), # TODO: add other fuel types
274
+ OpenStudio::EndUseFuelType.new('DistrictCooling').value => OpenStudio::FuelType.new('DistrictCooling'),
275
+ OpenStudio::EndUseFuelType.new('DistrictHeating').value => OpenStudio::FuelType.new('DistrictHeating'),
276
+ OpenStudio::EndUseFuelType.new('Water').value => OpenStudio::FuelType.new('Water')
277
+ }
278
+
279
+ # map each fuel type in EndUseFuelTypes to a specific FuelTypes
280
+ fuel_type_alias_map = {
281
+ OpenStudio::EndUseFuelType.new('Electricity').value => 'electricity',
282
+ OpenStudio::EndUseFuelType.new('Gas').value => 'gas',
283
+ OpenStudio::EndUseFuelType.new('AdditionalFuel').value => 'other_energy',
284
+ OpenStudio::EndUseFuelType.new('DistrictCooling').value => 'district_cooling',
285
+ OpenStudio::EndUseFuelType.new('DistrictHeating').value => 'district_heating',
286
+ OpenStudio::EndUseFuelType.new('Water').value => 'water'
287
+ }
288
+
289
+ # annual "annual"
290
+ annual_elems = OpenStudio::AttributeVector.new
291
+
292
+ # consumption "consumption"
293
+ cons_elems = OpenStudio::AttributeVector.new
294
+
295
+ # electricity
296
+ electricity = @sql.electricityTotalEndUses
297
+ if electricity.is_initialized
298
+ cons_elems << OpenStudio::Attribute.new('electricity', electricity.get, 'GJ')
299
+ @runner.registerValue('annual_consumption_electricity', electricity.get, 'GJ')
300
+ else
301
+ cons_elems << OpenStudio::Attribute.new('electricity', 0.0, 'GJ')
302
+ @runner.registerValue('annual_consumption_electricity', 0.0, 'GJ')
303
+ end
304
+
305
+ # gas
306
+ gas = @sql.naturalGasTotalEndUses
307
+ if gas.is_initialized
308
+ cons_elems << OpenStudio::Attribute.new('gas', gas.get, 'GJ')
309
+ @runner.registerValue('annual_consumption_gas', gas.get, 'GJ')
310
+ else
311
+ cons_elems << OpenStudio::Attribute.new('gas', 0.0, 'GJ')
312
+ @runner.registerValue('annual_consumption_gas', 0.0, 'GJ')
313
+ end
314
+
315
+ # other_energy
316
+ other_energy = @sql.otherFuelTotalEndUses
317
+ if other_energy.is_initialized
318
+ cons_elems << OpenStudio::Attribute.new('other_energy', other_energy.get, 'GJ')
319
+ @runner.registerValue('annual_consumption_other_energy', other_energy.get, 'GJ')
320
+ else
321
+ cons_elems << OpenStudio::Attribute.new('other_energy', 0.0, 'GJ')
322
+ @runner.registerValue('annual_consumption_other_energy', 0.0, 'GJ')
323
+ end
324
+
325
+ # district_cooling
326
+ district_cooling = @sql.districtCoolingTotalEndUses
327
+ if district_cooling.is_initialized
328
+ cons_elems << OpenStudio::Attribute.new('district_cooling', district_cooling.get, 'GJ')
329
+ @runner.registerValue('annual_consumption_district_cooling', district_cooling.get, 'GJ')
330
+ else
331
+ cons_elems << OpenStudio::Attribute.new('district_cooling', 0.0, 'GJ')
332
+ @runner.registerValue('annual_consumption_district_cooling', 0.0, 'GJ')
333
+ end
334
+
335
+ # district_heating
336
+ district_heating = @sql.districtHeatingTotalEndUses
337
+ if district_heating.is_initialized
338
+ cons_elems << OpenStudio::Attribute.new('district_heating', district_heating.get, 'GJ')
339
+ @runner.registerValue('annual_consumption_district_heating', district_heating.get, 'GJ')
340
+ else
341
+ cons_elems << OpenStudio::Attribute.new('district_heating', 0.0, 'GJ')
342
+ @runner.registerValue('annual_consumption_district_heating', 0.0, 'GJ')
343
+ end
344
+
345
+ # water
346
+ water = @sql.waterTotalEndUses
347
+ if water.is_initialized
348
+ cons_elems << OpenStudio::Attribute.new('water', water.get, 'm^3')
349
+ @runner.registerValue('annual_consumption_water', water.get, 'm^3')
350
+ else
351
+ cons_elems << OpenStudio::Attribute.new('water', 0.0, 'm^3')
352
+ @runner.registerValue('annual_consumption_water', 0.0, 'm^3')
353
+ end
354
+
355
+ # end consumption
356
+ annual_elems << OpenStudio::Attribute.new('consumption', cons_elems)
357
+
358
+ # demand "demand"
359
+ demand_elems = OpenStudio::AttributeVector.new
360
+
361
+ # get the weather file run period (as opposed to design day run period)
362
+ ann_env_pd = nil
363
+ @sql.availableEnvPeriods.each do |env_pd|
364
+ env_type = @sql.environmentType(env_pd)
365
+ if env_type.is_initialized
366
+ if env_type.get == OpenStudio::EnvironmentType.new('WeatherRunPeriod')
367
+ ann_env_pd = env_pd
368
+ end
369
+ end
370
+ end
371
+
372
+ # only try to get the annual peak demand if an annual simulation was run
373
+ if ann_env_pd
374
+
375
+ # create some units to use
376
+ joule_unit = OpenStudio.createUnit('J').get
377
+ gigajoule_unit = OpenStudio.createUnit('GJ').get
378
+ hrs_unit = OpenStudio.createUnit('h').get
379
+ kilowatt_unit = OpenStudio.createUnit('kW').get
380
+
381
+ # get the annual hours simulated
382
+ hrs_sim = '(0 - no partial annual simulation)'
383
+ if @sql.hoursSimulated.is_initialized
384
+ hrs_sim = @sql.hoursSimulated.get
385
+ if hrs_sim != 8760
386
+ @runner.registerError("Simulation was only #{hrs_sim} hrs; EDA requires an annual simulation (8760 hrs)")
387
+ return OpenStudio::Attribute.new('report', result_elems)
388
+ end
389
+ end
390
+
391
+ # Get the electricity timeseries to determine the year used
392
+ elec = @sql.timeSeries(ann_env_pd, 'Zone Timestep', 'Electricity:Facility', '')
393
+ timeseries_yr = nil
394
+ if elec.is_initialized
395
+ timeseries_yr = elec.get.dateTimes[0].date.year
396
+ else
397
+ @runner.registerError('Peak Demand timeseries (Electricity:Facility at zone timestep) could not be found, cannot determine the informatino needed to calculate savings or incentives.')
398
+ end
399
+ # Setup the peak demand time window based on input arguments.
400
+ # Note that holidays and weekends are not excluded because
401
+ # of a bug in EnergyPlus dates.
402
+ # This will only impact corner-case buildings that have
403
+ # peak demand on weekends or holidays, which is unusual.
404
+ @runner.registerInfo("Peak Demand window is #{start_mo} #{start_day} to #{end_mo} #{end_day} from #{start_hr}:00 to #{end_hr}:00.")
405
+ start_date = OpenStudio::DateTime.new(OpenStudio::Date.new(OpenStudio::MonthOfYear.new(start_mo), start_day, timeseries_yr), OpenStudio::Time.new(0, 0, 0, 0))
406
+ end_date = OpenStudio::DateTime.new(OpenStudio::Date.new(OpenStudio::MonthOfYear.new(end_mo), end_day, timeseries_yr), OpenStudio::Time.new(0, 24, 0, 0))
407
+ start_time = OpenStudio::Time.new(0, start_hr, 0, 0)
408
+ end_time = OpenStudio::Time.new(0, end_hr, 0, 0)
409
+
410
+ # Get the day type timeseries.
411
+ day_types = nil
412
+ day_type_indices = @sql.timeSeries(ann_env_pd, 'Zone Timestep', 'Site Day Type Index', 'Environment')
413
+ if day_type_indices.is_initialized
414
+ # Put values into array
415
+ day_types = []
416
+ day_type_vals = day_type_indices.get.values
417
+ for i in 0..(day_type_vals.size - 1)
418
+ day_types << day_type_vals[i]
419
+ end
420
+ else
421
+ @runner.registerError('Day Type timeseries (Site Day Type Index at zone timestep) could not be found, cannot accurately determine the peak demand.')
422
+ end
423
+
424
+ # electricity_peak_demand
425
+ electricity_peak_demand = -1.0
426
+ electricity_peak_demand_time = nil
427
+ # deduce the timestep based on the hours simulated and the number of datapoints in the timeseries
428
+ if elec.is_initialized && day_types
429
+ elec = elec.get
430
+ num_int = elec.values.size
431
+ int_len_hrs = OpenStudio::Quantity.new(hrs_sim / num_int, hrs_unit)
432
+
433
+ # Put timeseries into array
434
+ elec_vals = []
435
+ ann_elec_vals = elec.values
436
+ for i in 0..(ann_elec_vals.size - 1)
437
+ elec_vals << ann_elec_vals[i]
438
+ end
439
+
440
+ # Put values into array
441
+ elec_times = []
442
+ ann_elec_times = elec.dateTimes
443
+ for i in 0..(ann_elec_times.size - 1)
444
+ elec_times << ann_elec_times[i]
445
+ end
446
+
447
+ # Loop through the time/value pairs and find the peak
448
+ # excluding the times outside of the Xcel peak demand window
449
+ elec_times.zip(elec_vals).each_with_index do |vs, ind|
450
+ date_time = vs[0]
451
+ val = vs[1]
452
+ day_type = day_types[ind]
453
+ time = date_time.time
454
+ date = date_time.date
455
+ day_of_week = date.dayOfWeek
456
+ # Convert the peak demand to kW
457
+ val_J_per_hr = val / int_len_hrs.value
458
+ val_kW = OpenStudio.convert(val_J_per_hr, 'J/h', 'kW').get
459
+
460
+ # puts("#{val_kW}kW; #{date}; #{time}; #{day_of_week.valueName}")
461
+
462
+ # Skip times outside of the correct months
463
+ next if date_time < start_date || date_time > end_date
464
+ # Skip times before 2pm and after 6pm
465
+ next if time < start_time || time > end_time
466
+ # Skip weekends if asked
467
+ if skip_weekends
468
+ # Sunday = 1, Saturday = 7
469
+ next if day_type == 1 || day_type == 7
470
+ end
471
+ # Skip holidays if asked
472
+ if skip_holidays
473
+ # Holiday = 8
474
+ next if day_type == 8
475
+ end
476
+
477
+ # puts("VALID #{val_kW}kW; #{date}; #{time}; #{day_of_week.valueName}")
478
+
479
+ # Check peak demand against this timestep
480
+ # and update if this timestep is higher.
481
+ if val > electricity_peak_demand
482
+ electricity_peak_demand = val
483
+ electricity_peak_demand_time = date_time
484
+ end
485
+ end
486
+ elec_peak_demand_timestep_J = OpenStudio::Quantity.new(electricity_peak_demand, joule_unit)
487
+ num_int = elec.values.size
488
+ int_len_hrs = OpenStudio::Quantity.new(hrs_sim / num_int, hrs_unit)
489
+ elec_peak_demand_hourly_J_per_hr = elec_peak_demand_timestep_J / int_len_hrs
490
+ electricity_peak_demand = OpenStudio.convert(elec_peak_demand_hourly_J_per_hr, kilowatt_unit).get.value
491
+ demand_elems << OpenStudio::Attribute.new('electricity_peak_demand', electricity_peak_demand, 'kW')
492
+ @runner.registerValue('annual_demand_electricity_peak_demand', electricity_peak_demand, 'kW')
493
+ @runner.registerInfo("Peak Demand = #{electricity_peak_demand.round(2)}kW on #{electricity_peak_demand_time}")
494
+ else
495
+ @runner.registerError('Peak Demand timeseries (Electricity:Facility at zone timestep) could not be found, cannot determine the informatino needed to calculate savings or incentives.')
496
+ demand_elems << OpenStudio::Attribute.new('electricity_peak_demand', 0.0, 'kW')
497
+ @runner.registerValue('annual_demand_electricity_peak_demand', 0.0, 'kW')
498
+ end
499
+
500
+ # electricity_annual_avg_peak_demand
501
+ val = @sql.electricityTotalEndUses
502
+ if val.is_initialized
503
+ ann_elec_gj = OpenStudio::Quantity.new(val.get, gigajoule_unit)
504
+ ann_hrs = OpenStudio::Quantity.new(hrs_sim, hrs_unit)
505
+ elec_ann_avg_peak_demand_hourly_GJ_per_hr = ann_elec_gj / ann_hrs
506
+ electricity_annual_avg_peak_demand = OpenStudio.convert(elec_ann_avg_peak_demand_hourly_GJ_per_hr, kilowatt_unit).get.value
507
+ demand_elems << OpenStudio::Attribute.new('electricity_annual_avg_peak_demand', electricity_annual_avg_peak_demand, 'kW')
508
+ @runner.registerValue('annual_demand_electricity_annual_avg_peak_demand', electricity_annual_avg_peak_demand, 'kW')
509
+ else
510
+ demand_elems << OpenStudio::Attribute.new('electricity_annual_avg_peak_demand', 0.0, 'kW')
511
+ @runner.registerValue('annual_demand_electricity_annual_avg_peak_demand', 0.0, 'kW')
512
+ end
513
+
514
+ # district_cooling_peak_demand
515
+ district_cooling_peak_demand = -1.0
516
+ ann_dist_clg_peak_demand_time = nil
517
+ dist_clg = @sql.timeSeries(ann_env_pd, 'Zone Timestep', 'DistrictCooling:Facility', '')
518
+ # deduce the timestep based on the hours simulated and the number of datapoints in the timeseries
519
+ if dist_clg.is_initialized && day_types
520
+ dist_clg = dist_clg.get
521
+ num_int = dist_clg.values.size
522
+ int_len_hrs = OpenStudio::Quantity.new(hrs_sim / num_int, hrs_unit)
523
+
524
+ # Put timeseries into array
525
+ dist_clg_vals = []
526
+ ann_dist_clg_vals = dist_clg.values
527
+ for i in 0..(ann_dist_clg_vals.size - 1)
528
+ dist_clg_vals << ann_dist_clg_vals[i]
529
+ end
530
+
531
+ # Put values into array
532
+ dist_clg_times = []
533
+ ann_dist_clg_times = dist_clg.dateTimes
534
+ for i in 0..(ann_dist_clg_times.size - 1)
535
+ dist_clg_times << ann_dist_clg_times[i]
536
+ end
537
+
538
+ # Loop through the time/value pairs and find the peak
539
+ # excluding the times outside of the Xcel peak demand window
540
+ dist_clg_times.zip(dist_clg_vals).each_with_index do |vs, ind|
541
+ date_time = vs[0]
542
+ val = vs[1]
543
+ day_type = day_types[ind]
544
+ time = date_time.time
545
+ date = date_time.date
546
+ day_of_week = date.dayOfWeek
547
+ # Convert the peak demand to kW
548
+ val_J_per_hr = val / int_len_hrs.value
549
+ val_kW = OpenStudio.convert(val_J_per_hr, 'J/h', 'kW').get
550
+
551
+ # puts("#{val_kW}kW; #{date}; #{time}; #{day_of_week.valueName}")
552
+
553
+ # Skip times outside of the correct months
554
+ next if date_time < start_date || date_time > end_date
555
+ # Skip times before 2pm and after 6pm
556
+ next if time < start_time || time > end_time
557
+ # Skip weekends if asked
558
+ if skip_weekends
559
+ # Sunday = 1, Saturday = 7
560
+ next if day_type == 1 || day_type == 7
561
+ end
562
+ # Skip holidays if asked
563
+ if skip_holidays
564
+ # Holiday = 8
565
+ next if day_type == 8
566
+ end
567
+
568
+ # puts("VALID #{val_kW}kW; #{date}; #{time}; #{day_of_week.valueName}")
569
+
570
+ # Check peak demand against this timestep
571
+ # and update if this timestep is higher.
572
+ if val > district_cooling_peak_demand
573
+ district_cooling_peak_demand = val
574
+ ann_dist_clg_peak_demand_time = date_time
575
+ end
576
+ end
577
+ dist_clg_peak_demand_timestep_J = OpenStudio::Quantity.new(district_cooling_peak_demand, joule_unit)
578
+ num_int = dist_clg.values.size
579
+ int_len_hrs = OpenStudio::Quantity.new(hrs_sim / num_int, hrs_unit)
580
+ dist_clg_peak_demand_hourly_J_per_hr = dist_clg_peak_demand_timestep_J / int_len_hrs
581
+ district_cooling_peak_demand = OpenStudio.convert(dist_clg_peak_demand_hourly_J_per_hr, kilowatt_unit).get.value
582
+ demand_elems << OpenStudio::Attribute.new('district_cooling_peak_demand', district_cooling_peak_demand, 'kW')
583
+ @runner.registerValue('annual_demand_district_cooling_peak_demand', district_cooling_peak_demand, 'kW')
584
+ @runner.registerInfo("District Cooling Peak Demand = #{district_cooling_peak_demand.round(2)}kW on #{ann_dist_clg_peak_demand_time}")
585
+ else
586
+ demand_elems << OpenStudio::Attribute.new('district_cooling_peak_demand', 0.0, 'kW')
587
+ @runner.registerValue('annual_demand_district_cooling_peak_demand', 0.0, 'kW')
588
+ end
589
+
590
+ else
591
+ @runner.registerError('Could not find an annual run period')
592
+ return OpenStudio::Attribute.new('report', result_elems)
593
+ end
594
+
595
+ # end demand
596
+ annual_elems << OpenStudio::Attribute.new('demand', demand_elems)
597
+
598
+ # utility_cost
599
+ utility_cost_elems = OpenStudio::AttributeVector.new
600
+ annual_utility_cost_map = {}
601
+
602
+ # electricity
603
+ electricity = @sql.annualTotalCost(OpenStudio::FuelType.new('Electricity'))
604
+ if electricity.is_initialized
605
+ utility_cost_elems << OpenStudio::Attribute.new('electricity', electricity.get, 'dollars')
606
+ @runner.registerValue('annual_utility_cost_electricity', electricity.get, 'dollars')
607
+ annual_utility_cost_map[OpenStudio::EndUseFuelType.new('Electricity').valueName] = electricity.get
608
+ else
609
+ utility_cost_elems << OpenStudio::Attribute.new('electricity', 0.0, 'dollars')
610
+ @runner.registerValue('annual_utility_cost_electricity', 0.0, 'dollars')
611
+ annual_utility_cost_map[OpenStudio::EndUseFuelType.new('Electricity').valueName] = 0.0
612
+ end
613
+
614
+ # electricity_consumption_charge and electricity_demand_charge
615
+ electric_consumption_charge = 0.0
616
+ electric_demand_charge = 0.0
617
+
618
+ electric_rate_query = "SELECT value FROM tabulardatawithstrings WHERE ReportName='LEEDsummary' AND ReportForString='Entire Facility' AND TableName='EAp2-3. Energy Type Summary' AND RowName='Electricity' AND ColumnName='Utility Rate'"
619
+ electric_rate_name = @sql.execAndReturnFirstString(electric_rate_query)
620
+ if electric_rate_name.is_initialized
621
+ electric_rate_name = electric_rate_name.get.strip
622
+
623
+ # electricity_consumption_charge
624
+ electric_consumption_charge_query = "SELECT value FROM tabulardatawithstrings WHERE ReportName='Tariff Report' AND ReportForString='#{electric_rate_name}' AND TableName='Categories' AND RowName='EnergyCharges (~~$~~)' AND ColumnName='Sum'"
625
+ val = @sql.execAndReturnFirstDouble(electric_consumption_charge_query)
626
+ if val.is_initialized
627
+ electric_consumption_charge = val.get
628
+ end
629
+
630
+ # electricity_demand_charge
631
+ electric_demand_charge_query = "SELECT value FROM tabulardatawithstrings WHERE ReportName='Tariff Report' AND ReportForString='#{electric_rate_name}' AND TableName='Categories' AND RowName='DemandCharges (~~$~~)' AND ColumnName='Sum'"
632
+ val = @sql.execAndReturnFirstDouble(electric_demand_charge_query)
633
+ if val.is_initialized
634
+ electric_demand_charge = val.get
635
+ end
636
+
637
+ end
638
+ utility_cost_elems << OpenStudio::Attribute.new('electricity_consumption_charge', electric_consumption_charge, 'dollars')
639
+ @runner.registerValue('annual_utility_cost_electricity_consumption_charge', electric_consumption_charge, 'dollars')
640
+ utility_cost_elems << OpenStudio::Attribute.new('electricity_demand_charge', electric_demand_charge, 'dollars')
641
+ @runner.registerValue('annual_utility_cost_electricity_demand_charge', electric_demand_charge, 'dollars')
642
+
643
+ # gas
644
+ gas = @sql.annualTotalCost(OpenStudio::FuelType.new('Gas'))
645
+ if gas.is_initialized
646
+ annual_utility_cost_map[OpenStudio::EndUseFuelType.new('Gas').valueName] = gas.get
647
+ else
648
+ annual_utility_cost_map[OpenStudio::EndUseFuelType.new('Gas').valueName] = 0.0
649
+ end
650
+
651
+ # district_cooling
652
+ district_cooling_charge = 0.0
653
+
654
+ district_cooling_rate_query = "SELECT value FROM tabulardatawithstrings WHERE ReportName='LEEDsummary' AND ReportForString='Entire Facility' AND TableName='EAp2-3. Energy Type Summary' AND RowName='District Cooling' AND ColumnName='Utility Rate'"
655
+ district_cooling_rate_name = @sql.execAndReturnFirstString(district_cooling_rate_query)
656
+ if district_cooling_rate_name.is_initialized
657
+ district_cooling_rate_name = district_cooling_rate_name.get.strip
658
+
659
+ # district_cooling_charge
660
+ district_cooling_charge_query = "SELECT value FROM tabulardatawithstrings WHERE ReportName='Tariff Report' AND ReportForString='#{district_cooling_rate_name}' AND TableName='Categories' AND RowName='Basis (~~$~~)' AND ColumnName='Sum'"
661
+ val = @sql.execAndReturnFirstDouble(district_cooling_charge_query)
662
+ if val.is_initialized
663
+ district_cooling_charge = val.get
664
+ end
665
+
666
+ end
667
+ annual_utility_cost_map[OpenStudio::EndUseFuelType.new('DistrictCooling').valueName] = district_cooling_charge
668
+
669
+ # district_heating
670
+ district_heating_charge = 0.0
671
+
672
+ district_heating_rate_query = "SELECT value FROM tabulardatawithstrings WHERE ReportName='LEEDsummary' AND ReportForString='Entire Facility' AND TableName='EAp2-3. Energy Type Summary' AND RowName='District Heating' AND ColumnName='Utility Rate'"
673
+ district_heating_rate_name = @sql.execAndReturnFirstString(district_heating_rate_query)
674
+ if district_heating_rate_name.is_initialized
675
+ district_heating_rate_name = district_heating_rate_name.get.strip
676
+
677
+ # district_heating_charge
678
+ district_heating_charge_query = "SELECT value FROM tabulardatawithstrings WHERE ReportName='Tariff Report' AND ReportForString='#{district_heating_rate_name}' AND TableName='Categories' AND RowName='Basis (~~$~~)' AND ColumnName='Sum'"
679
+ val = @sql.execAndReturnFirstDouble(district_heating_charge_query)
680
+ if val.is_initialized
681
+ district_heating_charge = val.get
682
+ end
683
+
684
+ end
685
+ annual_utility_cost_map[OpenStudio::EndUseFuelType.new('DistrictHeating').valueName] = district_heating_charge
686
+
687
+ # water
688
+ water = @sql.annualTotalCost(OpenStudio::FuelType.new('Water'))
689
+ if water.is_initialized
690
+ annual_utility_cost_map[OpenStudio::EndUseFuelType.new('Water').valueName] = water.get
691
+ else
692
+ annual_utility_cost_map[OpenStudio::EndUseFuelType.new('Water').valueName] = 0.0
693
+ end
694
+
695
+ # total
696
+ total_query = "SELECT Value from tabulardatawithstrings where (reportname = 'Economics Results Summary Report') and (ReportForString = 'Entire Facility') and (TableName = 'Annual Cost') and (ColumnName ='Total') and (((RowName = 'Cost') and (Units = '~~$~~')) or (RowName = 'Cost (~~$~~)'))"
697
+ total = @sql.execAndReturnFirstDouble(total_query)
698
+
699
+ # other_energy
700
+ # Subtract off the already accounted for fuel types from the total
701
+ # to account for fuels on custom meters where the fuel type is not known.
702
+ prev_tot = 0.0
703
+ annual_utility_cost_map.each do |fuel, val|
704
+ prev_tot += val
705
+ end
706
+ if total.is_initialized
707
+ other_val = total.get - prev_tot
708
+ annual_utility_cost_map[OpenStudio::EndUseFuelType.new('AdditionalFuel').valueName] = other_val
709
+ else
710
+ annual_utility_cost_map[OpenStudio::EndUseFuelType.new('AdditionalFuel').valueName] = 0.0
711
+ end
712
+
713
+ # export remaining costs in the correct order
714
+ # gas
715
+ utility_cost_elems << OpenStudio::Attribute.new('gas', annual_utility_cost_map[OpenStudio::EndUseFuelType.new('Gas').valueName], 'dollars')
716
+ @runner.registerValue('annual_utility_cost_gas', annual_utility_cost_map[OpenStudio::EndUseFuelType.new('Gas').valueName], 'dollars')
717
+ # other_energy
718
+ utility_cost_elems << OpenStudio::Attribute.new('other_energy', annual_utility_cost_map[OpenStudio::EndUseFuelType.new('AdditionalFuel').valueName], 'dollars')
719
+ @runner.registerValue('annual_utility_cost_other_energy', annual_utility_cost_map[OpenStudio::EndUseFuelType.new('AdditionalFuel').valueName], 'dollars')
720
+ # district_cooling
721
+ utility_cost_elems << OpenStudio::Attribute.new('district_cooling', annual_utility_cost_map[OpenStudio::EndUseFuelType.new('DistrictCooling').valueName], 'dollars')
722
+ @runner.registerValue('annual_utility_cost_district_cooling', annual_utility_cost_map[OpenStudio::EndUseFuelType.new('DistrictCooling').valueName], 'dollars')
723
+ # district_heating
724
+ utility_cost_elems << OpenStudio::Attribute.new('district_heating', annual_utility_cost_map[OpenStudio::EndUseFuelType.new('DistrictHeating').valueName], 'dollars')
725
+ @runner.registerValue('annual_utility_cost_district_heating', annual_utility_cost_map[OpenStudio::EndUseFuelType.new('DistrictHeating').valueName], 'dollars')
726
+ # water
727
+ utility_cost_elems << OpenStudio::Attribute.new('water', annual_utility_cost_map[OpenStudio::EndUseFuelType.new('Water').valueName], 'dollars')
728
+ @runner.registerValue('annual_utility_cost_water', annual_utility_cost_map[OpenStudio::EndUseFuelType.new('Water').valueName], 'dollars')
729
+ # total
730
+ if total.is_initialized
731
+ utility_cost_elems << OpenStudio::Attribute.new('total', total.get, 'dollars')
732
+ @runner.registerValue('annual_utility_cost_total', total.get, 'dollars')
733
+ else
734
+ utility_cost_elems << OpenStudio::Attribute.new('total', 0.0, 'dollars')
735
+ @runner.registerValue('annual_utility_cost_total', 0.0, 'dollars')
736
+ end
737
+
738
+ # end_uses - utility costs by end use using average blended cost
739
+ end_uses_elems = OpenStudio::AttributeVector.new
740
+ # map to store the costs by end use
741
+ cost_by_end_use = {}
742
+
743
+ # fill the map with 0.0's to start
744
+ end_use_cat_types.each do |end_use_cat_type|
745
+ cost_by_end_use[end_use_cat_type] = 0.0
746
+ end
747
+
748
+ # only attempt to get monthly data if enduses table is available
749
+ if @sql.endUses.is_initialized
750
+ end_uses_table = @sql.endUses.get
751
+ # loop through all the fuel types
752
+ end_use_fuel_types.each do |end_use_fuel_type|
753
+ # get the annual total cost for this fuel type
754
+ ann_cost = annual_utility_cost_map[end_use_fuel_type.valueName]
755
+ # get the total annual usage for this fuel type in all end use categories
756
+ # loop through all end uses, adding the annual usage value to the aggregator
757
+ ann_usg = 0.0
758
+ end_use_cat_types.each do |end_use_cat_type|
759
+ ann_usg += end_uses_table.getEndUse(end_use_fuel_type, end_use_cat_type)
760
+ end
761
+ # figure out the annual blended rate for this fuel type
762
+ avg_ann_rate = 0.0
763
+ if ann_cost > 0 && ann_usg > 0
764
+ avg_ann_rate = ann_cost / ann_usg
765
+ end
766
+ # for each end use category, figure out the cost if using
767
+ # the avg ann rate; add this cost to the map
768
+ end_use_cat_types.each do |end_use_cat_type|
769
+ cost_by_end_use[end_use_cat_type] += end_uses_table.getEndUse(end_use_fuel_type, end_use_cat_type) * avg_ann_rate
770
+ end
771
+ end
772
+ # loop through the end uses and record the annual total cost based on the avg annual rate
773
+ end_use_cat_types.each do |end_use_cat_type|
774
+ # record the value
775
+ end_uses_elems << OpenStudio::Attribute.new(end_use_map[end_use_cat_type.value], cost_by_end_use[end_use_cat_type], 'dollars')
776
+ @runner.registerValue("annual_utility_cost_end_uses_#{end_use_map[end_use_cat_type.value]}", cost_by_end_use[end_use_cat_type], 'dollars')
777
+ end
778
+ else
779
+ @runner.registerError('End-Use table not available in results; could not retrieve monthly costs by end use')
780
+ return OpenStudio::Attribute.new('report', result_elems)
781
+ end
782
+
783
+ # end end_uses
784
+ utility_cost_elems << OpenStudio::Attribute.new('end_uses', end_uses_elems)
785
+
786
+ # end utility_costs
787
+ annual_elems << OpenStudio::Attribute.new('utility_cost', utility_cost_elems)
788
+
789
+ # end annual
790
+ result_elems << OpenStudio::Attribute.new('annual', annual_elems)
791
+
792
+ # monthly
793
+ monthly_elems = OpenStudio::AttributeVector.new
794
+
795
+ # consumption
796
+ cons_elems = OpenStudio::AttributeVector.new
797
+ # loop through all end uses
798
+ end_use_cat_types.each do |end_use_cat|
799
+ end_use_elems = OpenStudio::AttributeVector.new
800
+ end_use_name = end_use_map[end_use_cat.value]
801
+ # in each end use, loop through all fuel types
802
+ end_use_fuel_types.each do |end_use_fuel_type|
803
+ fuel_type_elems = OpenStudio::AttributeVector.new
804
+ fuel_type_name = fuel_type_alias_map[end_use_fuel_type.value]
805
+ ann_energy_cons = 0.0
806
+ # in each end use, loop through months and get monthly enedy consumption
807
+ months.each_with_index do |month, i|
808
+ mon_energy_cons = 0.0
809
+ val = @sql.energyConsumptionByMonth(end_use_fuel_type, end_use_cat, month)
810
+ if val.is_initialized
811
+ monthly_consumption_J = OpenStudio::Quantity.new(val.get, joule_unit)
812
+ monthly_consumption_GJ = OpenStudio.convert(monthly_consumption_J, gigajoule_unit).get.value
813
+ mon_energy_cons = monthly_consumption_GJ
814
+ ann_energy_cons += monthly_consumption_GJ
815
+ end
816
+ # record the monthly value
817
+ if end_use_fuel_type == OpenStudio::EndUseFuelType.new('Water')
818
+ fuel_type_elems << OpenStudio::Attribute.new('month', mon_energy_cons, 'm^3')
819
+ @runner.registerValue("monthly_consumption_#{end_use_name}_#{fuel_type_name}_month_#{i + 1}", mon_energy_cons, 'm^3')
820
+ else
821
+ fuel_type_elems << OpenStudio::Attribute.new('month', mon_energy_cons, 'GJ')
822
+ @runner.registerValue("monthly_consumption_#{end_use_name}_#{fuel_type_name}_month_#{i + 1}", mon_energy_cons, 'GJ')
823
+ end
824
+ end
825
+ # record the annual total
826
+ fuel_type_elems << OpenStudio::Attribute.new('year', ann_energy_cons, 'GJ')
827
+ @runner.registerValue("monthly_consumption_#{end_use_name}_#{fuel_type_name}_year", ann_energy_cons, 'GJ')
828
+ # add this fuel type
829
+ end_use_elems << OpenStudio::Attribute.new(fuel_type_alias_map[end_use_fuel_type.value], fuel_type_elems)
830
+ end
831
+ # add this end use
832
+ cons_elems << OpenStudio::Attribute.new(end_use_map[end_use_cat.value], end_use_elems)
833
+ end
834
+ # end consumption
835
+ monthly_elems << OpenStudio::Attribute.new('consumption', cons_elems)
836
+
837
+ # create a unit to use
838
+ watt_unit = OpenStudio.createUnit('W').get
839
+ kilowatt_unit = OpenStudio.createUnit('kW').get
840
+
841
+ # demand
842
+ demand_elems = OpenStudio::AttributeVector.new
843
+ # loop through all end uses
844
+ end_use_cat_types.each do |end_use_cat|
845
+ end_use_elems = OpenStudio::AttributeVector.new
846
+ end_use_name = end_use_map[end_use_cat.value]
847
+ # in each end use, loop through all fuel types
848
+ end_use_fuel_types.each do |end_use_fuel_type|
849
+ fuel_type_elems = OpenStudio::AttributeVector.new
850
+ fuel_type_name = fuel_type_alias_map[end_use_fuel_type.value]
851
+ ann_peak_demand = 0.0
852
+ # in each end use, loop through months and get monthly enedy consumption
853
+ months.each_with_index do |month, i|
854
+ mon_peak_demand = 0.0
855
+ val = @sql.peakEnergyDemandByMonth(end_use_fuel_type, end_use_cat, month)
856
+ if val.is_initialized
857
+ mon_peak_demand_W = OpenStudio::Quantity.new(val.get, watt_unit)
858
+ mon_peak_demand = OpenStudio.convert(mon_peak_demand_W, kilowatt_unit).get.value
859
+ end
860
+ # record the monthly value
861
+ fuel_type_elems << OpenStudio::Attribute.new('month', mon_peak_demand, 'kW')
862
+ @runner.registerValue("monthly_demand_#{end_use_name}_#{fuel_type_name}_month_#{i + 1}", mon_peak_demand, 'kW')
863
+ # if month peak demand > ann peak demand make this new ann peak demand
864
+ if mon_peak_demand > ann_peak_demand
865
+ ann_peak_demand = mon_peak_demand
866
+ end
867
+ end
868
+ # record the annual peak demand
869
+ fuel_type_elems << OpenStudio::Attribute.new('year', ann_peak_demand, 'kW')
870
+ @runner.registerValue("monthly_demand_#{end_use_name}_#{fuel_type_name}_year", ann_peak_demand, 'kW')
871
+ # add this fuel type
872
+ end_use_elems << OpenStudio::Attribute.new(fuel_type_alias_map[end_use_fuel_type.value], fuel_type_elems)
873
+ end
874
+ # add this end use
875
+ demand_elems << OpenStudio::Attribute.new(end_use_map[end_use_cat.value], end_use_elems)
876
+ end
877
+ # end demand
878
+ monthly_elems << OpenStudio::Attribute.new('demand', demand_elems)
879
+
880
+ # end monthly
881
+ result_elems << OpenStudio::Attribute.new('monthly', monthly_elems)
882
+
883
+ result_elem = OpenStudio::Attribute.new('results', result_elems)
884
+ return result_elem
885
+ end # end create_results
886
+ end