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,311 +1,311 @@
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_QAQC
37
- # include any general notes about QAQC method here
38
-
39
- # checks the number of unmet hours in the model
40
- def check_schedules(category, target_standard, min_pass, max_pass, name_only = false)
41
- # summary of the check
42
- check_elems = OpenStudio::AttributeVector.new
43
- check_elems << OpenStudio::Attribute.new('name', 'Schedules')
44
- check_elems << OpenStudio::Attribute.new('category', category)
45
- check_elems << OpenStudio::Attribute.new('description', 'Check schedules for lighting, ventilation, occupant density, plug loads, and equipment based on DOE reference building schedules in terms of full load hours per year.')
46
-
47
- # stop here if only name is requested this is used to populate display name for arguments
48
- if name_only == true
49
- results = []
50
- check_elems.each do |elem|
51
- results << elem.valueAsString
52
- end
53
- return results
54
- end
55
-
56
- # Versions of OpenStudio greater than 2.4.0 use a modified version of
57
- # openstudio-standards with different method calls. These methods
58
- # require a "Standard" object instead of the standard being passed into method calls.
59
- # This Standard object is used throughout the QAQC check.
60
- if OpenStudio::VersionString.new(OpenStudio.openStudioVersion) < OpenStudio::VersionString.new('2.4.3')
61
- use_old_gem_code = true
62
- else
63
- use_old_gem_code = false
64
- std = Standard.build(target_standard)
65
- end
66
-
67
- begin
68
- # loop through all space types used in the model
69
- @model.getSpaceTypes.each do |space_type|
70
- next if space_type.floorArea <= 0
71
-
72
- # load in standard info for this space type
73
- if use_old_gem_code
74
- data = space_type.get_standards_data(target_standard)
75
- else
76
- data = std.space_type_get_standards_data(space_type)
77
- end
78
-
79
- if data.nil? || data.empty?
80
-
81
- # skip if all spaces using this space type are plenums
82
- all_spaces_plenums = true
83
- space_type.spaces.each do |space|
84
- if use_old_gem_code
85
- if !space.plenum?
86
- all_spaces_plenums = false
87
- next
88
- end
89
- else
90
- if !std.space_plenum?(space)
91
- all_spaces_plenums = false
92
- next
93
- end
94
- end
95
- end
96
-
97
- if !all_spaces_plenums
98
- check_elems << OpenStudio::Attribute.new('flag', "Unexpected standards type for #{space_type.name}, can't validate schedules.")
99
- end
100
-
101
- next
102
- end
103
-
104
- # temp model to hold schedules to check
105
- model_temp = OpenStudio::Model::Model.new
106
-
107
- # check lighting schedules
108
- data['lighting_per_area'].nil? ? (target_ip = 0.0) : (target_ip = data['lighting_per_area'])
109
- if target_ip.to_f > 0
110
- if use_old_gem_code
111
- schedule_target = model_temp.add_schedule(data['lighting_schedule'])
112
- else
113
- schedule_target = std.model_add_schedule(model_temp, data['lighting_schedule'])
114
- end
115
- if !schedule_target
116
- check_elems << OpenStudio::Attribute.new('flag', "Didn't find schedule named #{data['lighting_schedule']} in standards json.")
117
- else
118
- # loop through and test individual load instances
119
- if use_old_gem_code
120
- target_hrs = schedule_target.annual_equivalent_full_load_hrs
121
- else
122
- target_hrs = std.schedule_ruleset_annual_equivalent_full_load_hrs(schedule_target)
123
- end
124
- space_type.lights.each do |load_inst|
125
- inst_sch_check = generate_load_insc_sch_check_attribute(target_hrs, load_inst, space_type, check_elems, min_pass, max_pass)
126
- if inst_sch_check then check_elems << inst_sch_check end
127
- end
128
-
129
- end
130
- end
131
-
132
- # check electric equipment schedules
133
- data['electric_equipment_per_area'].nil? ? (target_ip = 0.0) : (target_ip = data['electric_equipment_per_area'])
134
- if target_ip.to_f > 0
135
- if use_old_gem_code
136
- schedule_target = model_temp.add_schedule(data['electric_equipment_schedule'])
137
- else
138
- schedule_target = std.model_add_schedule(model_temp, data['electric_equipment_schedule'])
139
- end
140
- if !schedule_target
141
- check_elems << OpenStudio::Attribute.new('flag', "Didn't find schedule named #{data['electric_equipment_schedule']} in standards json.")
142
- else
143
- # loop through and test individual load instances
144
- if use_old_gem_code
145
- target_hrs = schedule_target.annual_equivalent_full_load_hrs
146
- else
147
- target_hrs = std.schedule_ruleset_annual_equivalent_full_load_hrs(schedule_target)
148
- end
149
-
150
- space_type.electricEquipment.each do |load_inst|
151
- inst_sch_check = generate_load_insc_sch_check_attribute(target_hrs, load_inst, space_type, check_elems, min_pass, max_pass)
152
- if inst_sch_check then check_elems << inst_sch_check end
153
- end
154
- end
155
- end
156
-
157
- # check gas equipment schedules
158
- # todo - update measure test to with space type to check this
159
- data['gas_equipment_per_area'].nil? ? (target_ip = 0.0) : (target_ip = data['gas_equipment_per_area'])
160
- if target_ip.to_f > 0
161
- if use_old_gem_code
162
- schedule_target = model_temp.add_schedule(data['gas_equipment_schedule'])
163
- else
164
- schedule_target = std.model_add_schedule(model_temp, data['gas_equipment_schedule'])
165
- end
166
- if !schedule_target
167
- check_elems << OpenStudio::Attribute.new('flag', "Didn't find schedule named #{data['gas_equipment_schedule']} in standards json.")
168
- else
169
- # loop through and test individual load instances
170
- if use_old_gem_code
171
- target_hrs = schedule_target.annual_equivalent_full_load_hrs
172
- else
173
- target_hrs = std.schedule_ruleset_annual_equivalent_full_load_hrs(schedule_target)
174
- end
175
- space_type.gasEquipment.each do |load_inst|
176
- inst_sch_check = generate_load_insc_sch_check_attribute(target_hrs, load_inst, space_type, check_elems, min_pass, max_pass)
177
- if inst_sch_check then check_elems << inst_sch_check end
178
- end
179
- end
180
- end
181
-
182
- # check occupancy schedules
183
- data['occupancy_per_area'].nil? ? (target_ip = 0.0) : (target_ip = data['occupancy_per_area'])
184
- if target_ip.to_f > 0
185
- if use_old_gem_code
186
- schedule_target = model_temp.add_schedule(data['occupancy_schedule'])
187
- else
188
- schedule_target = std.model_add_schedule(model_temp, data['occupancy_schedule'])
189
- end
190
- if !schedule_target
191
- check_elems << OpenStudio::Attribute.new('flag', "Didn't find schedule named #{data['occupancy_schedule']} in standards json.")
192
- else
193
- # loop through and test individual load instances
194
- if use_old_gem_code
195
- target_hrs = schedule_target.annual_equivalent_full_load_hrs
196
- else
197
- target_hrs = std.schedule_ruleset_annual_equivalent_full_load_hrs(schedule_target)
198
- end
199
- space_type.people.each do |load_inst|
200
- inst_sch_check = generate_load_insc_sch_check_attribute(target_hrs, load_inst, space_type, check_elems, min_pass, max_pass)
201
- if inst_sch_check then check_elems << inst_sch_check end
202
- end
203
-
204
- end
205
- end
206
-
207
- # TODO: - check ventilation schedules
208
- # if objects are in the model should they just be always on schedule, or have a 8760 annual equiv value
209
- # oa_schedule should not exist, or if it does shoudl be always on or have 8760 annual equiv value
210
- if space_type.designSpecificationOutdoorAir.is_initialized
211
- oa = space_type.designSpecificationOutdoorAir.get
212
- if oa.outdoorAirFlowRateFractionSchedule.is_initialized
213
- # TODO: - update measure test to check this
214
- target_hrs = 8760
215
- inst_sch_check = generate_load_insc_sch_check_attribute(target_hrs, oa, space_type, check_elems, min_pass, max_pass)
216
- if inst_sch_check then check_elems << inst_sch_check end
217
- end
218
- end
219
-
220
- # notes
221
- # current logic only looks at 8760 values and not design days
222
- # when multiple instances of a type currently check every schedule by itself. In future could do weighted avg. merge
223
- # not looking at infiltration schedules
224
- # not looking at luminaires
225
- # not looking at space loads, only loads at space type
226
- # only checking schedules where standard shows non zero load value
227
- # model load for space type where standards doesn't have one wont throw flag about mis-matched schedules
228
- end
229
-
230
- # warn if there are spaces in model that don't use space type unless they appear to be plenums
231
- @model.getSpaces.each do |space|
232
- if use_old_gem_code
233
- next if space.plenum?
234
- else
235
- next if std.space_plenum?(space)
236
- end
237
- if !space.spaceType.is_initialized
238
- check_elems << OpenStudio::Attribute.new('flag', "#{space.name} doesn't have a space type assigned, can't validate schedules.")
239
- end
240
- end
241
- rescue StandardError => e
242
- # brief description of ruby error
243
- check_elems << OpenStudio::Attribute.new('flag', "Error prevented QAQC check from running (#{e}).")
244
-
245
- # backtrace of ruby error for diagnostic use
246
- if @error_backtrace then check_elems << OpenStudio::Attribute.new('flag', e.backtrace.join("\n").to_s) end
247
- end
248
-
249
- # add check_elms to new attribute
250
- check_elem = OpenStudio::Attribute.new('check', check_elems)
251
-
252
- return check_elem
253
- # note: registerWarning and registerValue will be added for checks downstream using os_lib_reporting_qaqc.rb
254
- end
255
-
256
- # code for each load instance for different load types will pass through here
257
- # will return nill or a single attribute
258
- def generate_load_insc_sch_check_attribute(target_hrs, load_inst, space_type, check_elems, min_pass, max_pass)
259
- # Versions of OpenStudio greater than 2.4.0 use a modified version of
260
- # openstudio-standards with different method calls. These methods
261
- # require a "Standard" object instead of the standard being passed into method calls.
262
- # This Standard object is used throughout the QAQC check.
263
- if OpenStudio::VersionString.new(OpenStudio.openStudioVersion) < OpenStudio::VersionString.new('2.4.3')
264
- use_old_gem_code = true
265
- else
266
- use_old_gem_code = false
267
- std = Standard.build('90.1-2013')
268
- end
269
-
270
- schedule_inst = nil
271
- inst_hrs = nil
272
-
273
- # get schedule
274
- if (load_inst.class.to_s == 'OpenStudio::Model::People') && load_inst.numberofPeopleSchedule.is_initialized
275
- schedule_inst = load_inst.numberofPeopleSchedule.get
276
- elsif (load_inst.class.to_s == 'OpenStudio::Model::DesignSpecificationOutdoorAir') && load_inst.outdoorAirFlowRateFractionSchedule.is_initialized
277
- schedule_inst = load_inst.outdoorAirFlowRateFractionSchedule .get
278
- elsif load_inst.schedule.is_initialized
279
- schedule_inst = load_inst.schedule.get
280
- else
281
- return OpenStudio::Attribute.new('flag', "#{load_inst.name} in #{space_type.name} doesn't have a schedule assigned.")
282
- end
283
-
284
- # get annual equiv for model schedule
285
- if schedule_inst.to_ScheduleRuleset.is_initialized
286
- if use_old_gem_code
287
- inst_hrs = schedule_inst.to_ScheduleRuleset.get.annual_equivalent_full_load_hrs
288
- else
289
- inst_hrs = std.schedule_ruleset_annual_equivalent_full_load_hrs(schedule_inst.to_ScheduleRuleset.get)
290
- end
291
- elsif schedule_inst.to_ScheduleConstant.is_initialized
292
- if use_old_gem_code
293
- inst_hrs = schedule_inst.to_ScheduleConstant.get.annual_equivalent_full_load_hrs
294
- else
295
- inst_hrs = std.schedule_constant_annual_equivalent_full_load_hrs(schedule_inst.to_ScheduleConstant.get)
296
- end
297
- else
298
- return OpenStudio::Attribute.new('flag', "#{schedule_inst.name} isn't a Ruleset or Constant schedule. Can't calculate annual equivalent full load hours.")
299
- end
300
-
301
- # check instance against target
302
- if inst_hrs < target_hrs * (1.0 - min_pass)
303
- return OpenStudio::Attribute.new('flag', "#{inst_hrs.round} annual equivalent full load hours for #{schedule_inst.name} in #{space_type.name} is more than #{min_pass * 100} (%) below the typical value of #{target_hrs.round} hours from the DOE Prototype building.")
304
- elsif inst_hrs > target_hrs * (1.0 + max_pass)
305
- return OpenStudio::Attribute.new('flag', "#{inst_hrs.round} annual equivalent full load hours for #{schedule_inst.name} in #{space_type.name} is more than #{max_pass * 100} (%) above the typical value of #{target_hrs.round} hours DOE Prototype building.")
306
- end
307
-
308
- # will get to this if no flag was thrown
309
- return false
310
- end
311
- 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_QAQC
37
+ # include any general notes about QAQC method here
38
+
39
+ # checks the number of unmet hours in the model
40
+ def check_schedules(category, target_standard, min_pass, max_pass, name_only = false)
41
+ # summary of the check
42
+ check_elems = OpenStudio::AttributeVector.new
43
+ check_elems << OpenStudio::Attribute.new('name', 'Schedules')
44
+ check_elems << OpenStudio::Attribute.new('category', category)
45
+ check_elems << OpenStudio::Attribute.new('description', 'Check schedules for lighting, ventilation, occupant density, plug loads, and equipment based on DOE reference building schedules in terms of full load hours per year.')
46
+
47
+ # stop here if only name is requested this is used to populate display name for arguments
48
+ if name_only == true
49
+ results = []
50
+ check_elems.each do |elem|
51
+ results << elem.valueAsString
52
+ end
53
+ return results
54
+ end
55
+
56
+ # Versions of OpenStudio greater than 2.4.0 use a modified version of
57
+ # openstudio-standards with different method calls. These methods
58
+ # require a "Standard" object instead of the standard being passed into method calls.
59
+ # This Standard object is used throughout the QAQC check.
60
+ if OpenStudio::VersionString.new(OpenStudio.openStudioVersion) < OpenStudio::VersionString.new('2.4.3')
61
+ use_old_gem_code = true
62
+ else
63
+ use_old_gem_code = false
64
+ std = Standard.build(target_standard)
65
+ end
66
+
67
+ begin
68
+ # loop through all space types used in the model
69
+ @model.getSpaceTypes.each do |space_type|
70
+ next if space_type.floorArea <= 0
71
+
72
+ # load in standard info for this space type
73
+ if use_old_gem_code
74
+ data = space_type.get_standards_data(target_standard)
75
+ else
76
+ data = std.space_type_get_standards_data(space_type)
77
+ end
78
+
79
+ if data.nil? || data.empty?
80
+
81
+ # skip if all spaces using this space type are plenums
82
+ all_spaces_plenums = true
83
+ space_type.spaces.each do |space|
84
+ if use_old_gem_code
85
+ if !space.plenum?
86
+ all_spaces_plenums = false
87
+ next
88
+ end
89
+ else
90
+ if !std.space_plenum?(space)
91
+ all_spaces_plenums = false
92
+ next
93
+ end
94
+ end
95
+ end
96
+
97
+ if !all_spaces_plenums
98
+ check_elems << OpenStudio::Attribute.new('flag', "Unexpected standards type for #{space_type.name}, can't validate schedules.")
99
+ end
100
+
101
+ next
102
+ end
103
+
104
+ # temp model to hold schedules to check
105
+ model_temp = OpenStudio::Model::Model.new
106
+
107
+ # check lighting schedules
108
+ data['lighting_per_area'].nil? ? (target_ip = 0.0) : (target_ip = data['lighting_per_area'])
109
+ if target_ip.to_f > 0
110
+ if use_old_gem_code
111
+ schedule_target = model_temp.add_schedule(data['lighting_schedule'])
112
+ else
113
+ schedule_target = std.model_add_schedule(model_temp, data['lighting_schedule'])
114
+ end
115
+ if !schedule_target
116
+ check_elems << OpenStudio::Attribute.new('flag', "Didn't find schedule named #{data['lighting_schedule']} in standards json.")
117
+ else
118
+ # loop through and test individual load instances
119
+ if use_old_gem_code
120
+ target_hrs = schedule_target.annual_equivalent_full_load_hrs
121
+ else
122
+ target_hrs = std.schedule_ruleset_annual_equivalent_full_load_hrs(schedule_target)
123
+ end
124
+ space_type.lights.each do |load_inst|
125
+ inst_sch_check = generate_load_insc_sch_check_attribute(target_hrs, load_inst, space_type, check_elems, min_pass, max_pass)
126
+ if inst_sch_check then check_elems << inst_sch_check end
127
+ end
128
+
129
+ end
130
+ end
131
+
132
+ # check electric equipment schedules
133
+ data['electric_equipment_per_area'].nil? ? (target_ip = 0.0) : (target_ip = data['electric_equipment_per_area'])
134
+ if target_ip.to_f > 0
135
+ if use_old_gem_code
136
+ schedule_target = model_temp.add_schedule(data['electric_equipment_schedule'])
137
+ else
138
+ schedule_target = std.model_add_schedule(model_temp, data['electric_equipment_schedule'])
139
+ end
140
+ if !schedule_target
141
+ check_elems << OpenStudio::Attribute.new('flag', "Didn't find schedule named #{data['electric_equipment_schedule']} in standards json.")
142
+ else
143
+ # loop through and test individual load instances
144
+ if use_old_gem_code
145
+ target_hrs = schedule_target.annual_equivalent_full_load_hrs
146
+ else
147
+ target_hrs = std.schedule_ruleset_annual_equivalent_full_load_hrs(schedule_target)
148
+ end
149
+
150
+ space_type.electricEquipment.each do |load_inst|
151
+ inst_sch_check = generate_load_insc_sch_check_attribute(target_hrs, load_inst, space_type, check_elems, min_pass, max_pass)
152
+ if inst_sch_check then check_elems << inst_sch_check end
153
+ end
154
+ end
155
+ end
156
+
157
+ # check gas equipment schedules
158
+ # todo - update measure test to with space type to check this
159
+ data['gas_equipment_per_area'].nil? ? (target_ip = 0.0) : (target_ip = data['gas_equipment_per_area'])
160
+ if target_ip.to_f > 0
161
+ if use_old_gem_code
162
+ schedule_target = model_temp.add_schedule(data['gas_equipment_schedule'])
163
+ else
164
+ schedule_target = std.model_add_schedule(model_temp, data['gas_equipment_schedule'])
165
+ end
166
+ if !schedule_target
167
+ check_elems << OpenStudio::Attribute.new('flag', "Didn't find schedule named #{data['gas_equipment_schedule']} in standards json.")
168
+ else
169
+ # loop through and test individual load instances
170
+ if use_old_gem_code
171
+ target_hrs = schedule_target.annual_equivalent_full_load_hrs
172
+ else
173
+ target_hrs = std.schedule_ruleset_annual_equivalent_full_load_hrs(schedule_target)
174
+ end
175
+ space_type.gasEquipment.each do |load_inst|
176
+ inst_sch_check = generate_load_insc_sch_check_attribute(target_hrs, load_inst, space_type, check_elems, min_pass, max_pass)
177
+ if inst_sch_check then check_elems << inst_sch_check end
178
+ end
179
+ end
180
+ end
181
+
182
+ # check occupancy schedules
183
+ data['occupancy_per_area'].nil? ? (target_ip = 0.0) : (target_ip = data['occupancy_per_area'])
184
+ if target_ip.to_f > 0
185
+ if use_old_gem_code
186
+ schedule_target = model_temp.add_schedule(data['occupancy_schedule'])
187
+ else
188
+ schedule_target = std.model_add_schedule(model_temp, data['occupancy_schedule'])
189
+ end
190
+ if !schedule_target
191
+ check_elems << OpenStudio::Attribute.new('flag', "Didn't find schedule named #{data['occupancy_schedule']} in standards json.")
192
+ else
193
+ # loop through and test individual load instances
194
+ if use_old_gem_code
195
+ target_hrs = schedule_target.annual_equivalent_full_load_hrs
196
+ else
197
+ target_hrs = std.schedule_ruleset_annual_equivalent_full_load_hrs(schedule_target)
198
+ end
199
+ space_type.people.each do |load_inst|
200
+ inst_sch_check = generate_load_insc_sch_check_attribute(target_hrs, load_inst, space_type, check_elems, min_pass, max_pass)
201
+ if inst_sch_check then check_elems << inst_sch_check end
202
+ end
203
+
204
+ end
205
+ end
206
+
207
+ # TODO: - check ventilation schedules
208
+ # if objects are in the model should they just be always on schedule, or have a 8760 annual equiv value
209
+ # oa_schedule should not exist, or if it does shoudl be always on or have 8760 annual equiv value
210
+ if space_type.designSpecificationOutdoorAir.is_initialized
211
+ oa = space_type.designSpecificationOutdoorAir.get
212
+ if oa.outdoorAirFlowRateFractionSchedule.is_initialized
213
+ # TODO: - update measure test to check this
214
+ target_hrs = 8760
215
+ inst_sch_check = generate_load_insc_sch_check_attribute(target_hrs, oa, space_type, check_elems, min_pass, max_pass)
216
+ if inst_sch_check then check_elems << inst_sch_check end
217
+ end
218
+ end
219
+
220
+ # notes
221
+ # current logic only looks at 8760 values and not design days
222
+ # when multiple instances of a type currently check every schedule by itself. In future could do weighted avg. merge
223
+ # not looking at infiltration schedules
224
+ # not looking at luminaires
225
+ # not looking at space loads, only loads at space type
226
+ # only checking schedules where standard shows non zero load value
227
+ # model load for space type where standards doesn't have one wont throw flag about mis-matched schedules
228
+ end
229
+
230
+ # warn if there are spaces in model that don't use space type unless they appear to be plenums
231
+ @model.getSpaces.each do |space|
232
+ if use_old_gem_code
233
+ next if space.plenum?
234
+ else
235
+ next if std.space_plenum?(space)
236
+ end
237
+ if !space.spaceType.is_initialized
238
+ check_elems << OpenStudio::Attribute.new('flag', "#{space.name} doesn't have a space type assigned, can't validate schedules.")
239
+ end
240
+ end
241
+ rescue StandardError => e
242
+ # brief description of ruby error
243
+ check_elems << OpenStudio::Attribute.new('flag', "Error prevented QAQC check from running (#{e}).")
244
+
245
+ # backtrace of ruby error for diagnostic use
246
+ if @error_backtrace then check_elems << OpenStudio::Attribute.new('flag', e.backtrace.join("\n").to_s) end
247
+ end
248
+
249
+ # add check_elms to new attribute
250
+ check_elem = OpenStudio::Attribute.new('check', check_elems)
251
+
252
+ return check_elem
253
+ # note: registerWarning and registerValue will be added for checks downstream using os_lib_reporting_qaqc.rb
254
+ end
255
+
256
+ # code for each load instance for different load types will pass through here
257
+ # will return nill or a single attribute
258
+ def generate_load_insc_sch_check_attribute(target_hrs, load_inst, space_type, check_elems, min_pass, max_pass)
259
+ # Versions of OpenStudio greater than 2.4.0 use a modified version of
260
+ # openstudio-standards with different method calls. These methods
261
+ # require a "Standard" object instead of the standard being passed into method calls.
262
+ # This Standard object is used throughout the QAQC check.
263
+ if OpenStudio::VersionString.new(OpenStudio.openStudioVersion) < OpenStudio::VersionString.new('2.4.3')
264
+ use_old_gem_code = true
265
+ else
266
+ use_old_gem_code = false
267
+ std = Standard.build('90.1-2013')
268
+ end
269
+
270
+ schedule_inst = nil
271
+ inst_hrs = nil
272
+
273
+ # get schedule
274
+ if (load_inst.class.to_s == 'OpenStudio::Model::People') && load_inst.numberofPeopleSchedule.is_initialized
275
+ schedule_inst = load_inst.numberofPeopleSchedule.get
276
+ elsif (load_inst.class.to_s == 'OpenStudio::Model::DesignSpecificationOutdoorAir') && load_inst.outdoorAirFlowRateFractionSchedule.is_initialized
277
+ schedule_inst = load_inst.outdoorAirFlowRateFractionSchedule .get
278
+ elsif load_inst.schedule.is_initialized
279
+ schedule_inst = load_inst.schedule.get
280
+ else
281
+ return OpenStudio::Attribute.new('flag', "#{load_inst.name} in #{space_type.name} doesn't have a schedule assigned.")
282
+ end
283
+
284
+ # get annual equiv for model schedule
285
+ if schedule_inst.to_ScheduleRuleset.is_initialized
286
+ if use_old_gem_code
287
+ inst_hrs = schedule_inst.to_ScheduleRuleset.get.annual_equivalent_full_load_hrs
288
+ else
289
+ inst_hrs = std.schedule_ruleset_annual_equivalent_full_load_hrs(schedule_inst.to_ScheduleRuleset.get)
290
+ end
291
+ elsif schedule_inst.to_ScheduleConstant.is_initialized
292
+ if use_old_gem_code
293
+ inst_hrs = schedule_inst.to_ScheduleConstant.get.annual_equivalent_full_load_hrs
294
+ else
295
+ inst_hrs = std.schedule_constant_annual_equivalent_full_load_hrs(schedule_inst.to_ScheduleConstant.get)
296
+ end
297
+ else
298
+ return OpenStudio::Attribute.new('flag', "#{schedule_inst.name} isn't a Ruleset or Constant schedule. Can't calculate annual equivalent full load hours.")
299
+ end
300
+
301
+ # check instance against target
302
+ if inst_hrs < target_hrs * (1.0 - min_pass)
303
+ return OpenStudio::Attribute.new('flag', "#{inst_hrs.round} annual equivalent full load hours for #{schedule_inst.name} in #{space_type.name} is more than #{min_pass * 100} (%) below the typical value of #{target_hrs.round} hours from the DOE Prototype building.")
304
+ elsif inst_hrs > target_hrs * (1.0 + max_pass)
305
+ return OpenStudio::Attribute.new('flag', "#{inst_hrs.round} annual equivalent full load hours for #{schedule_inst.name} in #{space_type.name} is more than #{max_pass * 100} (%) above the typical value of #{target_hrs.round} hours DOE Prototype building.")
306
+ end
307
+
308
+ # will get to this if no flag was thrown
309
+ return false
310
+ end
311
+ end