openstudio-extension 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +9 -0
  3. data/.rubocop.yml +9 -0
  4. data/Gemfile +3 -1
  5. data/Jenkinsfile +10 -0
  6. data/README.md +230 -12
  7. data/Rakefile +88 -3
  8. data/bin/console +3 -3
  9. data/doc_templates/LICENSE.md +27 -0
  10. data/doc_templates/README.md.erb +42 -0
  11. data/doc_templates/copyright_erb.txt +36 -0
  12. data/doc_templates/copyright_js.txt +4 -0
  13. data/doc_templates/copyright_ruby.txt +34 -0
  14. data/init_templates/README.md +37 -0
  15. data/init_templates/gemspec.txt +32 -0
  16. data/init_templates/openstudio_module.rb +50 -0
  17. data/init_templates/spec.rb +47 -0
  18. data/init_templates/spec_helper.rb +49 -0
  19. data/init_templates/template_gemfile.txt +17 -0
  20. data/init_templates/template_rakefile.txt +15 -0
  21. data/init_templates/version.rb +40 -0
  22. data/lib/files/openstudio-extension-gem-test.ddy +536 -0
  23. data/lib/files/openstudio-extension-gem-test.epw +8768 -0
  24. data/lib/files/openstudio-extension-gem-test.stat +554 -0
  25. data/lib/measures/openstudio_extension_test_measure/LICENSE.md +27 -0
  26. data/lib/measures/openstudio_extension_test_measure/README.md +26 -0
  27. data/lib/measures/openstudio_extension_test_measure/README.md.erb +42 -0
  28. data/lib/measures/openstudio_extension_test_measure/measure.rb +72 -0
  29. data/lib/measures/openstudio_extension_test_measure/measure.xml +83 -0
  30. data/lib/measures/openstudio_extension_test_measure/resources/os_lib_helper_methods.rb +399 -0
  31. data/lib/measures/openstudio_extension_test_measure/tests/OpenStudioExtensionTestMeasure_Test.rb +75 -0
  32. data/lib/openstudio/extension.rb +220 -0
  33. data/lib/openstudio/extension/core/CreateResults.rb +879 -0
  34. data/lib/openstudio/extension/core/check_air_sys_temps.rb +190 -0
  35. data/lib/openstudio/extension/core/check_calibration.rb +155 -0
  36. data/lib/openstudio/extension/core/check_cond_zns.rb +84 -0
  37. data/lib/openstudio/extension/core/check_domestic_hot_water.rb +334 -0
  38. data/lib/openstudio/extension/core/check_envelope_conductance.rb +453 -0
  39. data/lib/openstudio/extension/core/check_eui_by_end_use.rb +162 -0
  40. data/lib/openstudio/extension/core/check_eui_reasonableness.rb +135 -0
  41. data/lib/openstudio/extension/core/check_fan_pwr.rb +98 -0
  42. data/lib/openstudio/extension/core/check_internal_loads.rb +393 -0
  43. data/lib/openstudio/extension/core/check_mech_sys_capacity.rb +226 -0
  44. data/lib/openstudio/extension/core/check_mech_sys_efficiency.rb +326 -0
  45. data/lib/openstudio/extension/core/check_mech_sys_part_load_eff.rb +464 -0
  46. data/lib/openstudio/extension/core/check_mech_sys_type.rb +139 -0
  47. data/lib/openstudio/extension/core/check_part_loads.rb +451 -0
  48. data/lib/openstudio/extension/core/check_placeholder.rb +75 -0
  49. data/lib/openstudio/extension/core/check_plant_cap.rb +123 -0
  50. data/lib/openstudio/extension/core/check_plant_temps.rb +159 -0
  51. data/lib/openstudio/extension/core/check_plenum_loads.rb +87 -0
  52. data/lib/openstudio/extension/core/check_pump_pwr.rb +108 -0
  53. data/lib/openstudio/extension/core/check_sch_coord.rb +241 -0
  54. data/lib/openstudio/extension/core/check_schedules.rb +311 -0
  55. data/lib/openstudio/extension/core/check_simultaneous_heating_and_cooling.rb +158 -0
  56. data/lib/openstudio/extension/core/check_supply_air_and_thermostat_temp_difference.rb +148 -0
  57. data/lib/openstudio/extension/core/check_weather_files.rb +132 -0
  58. data/lib/openstudio/extension/core/deer_vintages.rb +311 -0
  59. data/lib/openstudio/extension/core/os_lib_aedg_measures.rb +491 -0
  60. data/lib/openstudio/extension/core/os_lib_cofee.rb +259 -0
  61. data/lib/openstudio/extension/core/os_lib_constructions.rb +378 -0
  62. data/lib/openstudio/extension/core/os_lib_geometry.rb +1022 -0
  63. data/lib/openstudio/extension/core/os_lib_helper_methods.rb +399 -0
  64. data/lib/openstudio/extension/core/os_lib_hvac.rb +2171 -0
  65. data/lib/openstudio/extension/core/os_lib_lighting_and_equipment.rb +214 -0
  66. data/lib/openstudio/extension/core/os_lib_model_generation.rb +817 -0
  67. data/lib/openstudio/extension/core/os_lib_model_simplification.rb +1049 -0
  68. data/lib/openstudio/extension/core/os_lib_outdoorair_and_infiltration.rb +165 -0
  69. data/lib/openstudio/extension/core/os_lib_reporting.rb +4652 -0
  70. data/lib/openstudio/extension/core/os_lib_reporting_qaqc.rb +200 -0
  71. data/lib/openstudio/extension/core/os_lib_schedules.rb +963 -0
  72. data/lib/openstudio/extension/rake_task.rb +149 -0
  73. data/lib/openstudio/extension/runner.rb +644 -0
  74. data/lib/openstudio/extension/version.rb +40 -0
  75. data/openstudio-extension.gemspec +20 -15
  76. metadata +150 -14
  77. data/.travis.yml +0 -7
  78. data/lib/OpenStudio/Extension/rake_task.rb +0 -84
  79. data/lib/OpenStudio/Extension/version.rb +0 -33
  80. data/lib/OpenStudio/extension.rb +0 -65
@@ -0,0 +1,241 @@
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
+ # Determine the hour when the schedule first exceeds the starting value and when
38
+ # it goes back down to the ending value at the end of the day.
39
+ # This method only works for ScheduleRuleset schedules.
40
+ def get_start_and_end_times(schedule_ruleset)
41
+ # Ensure that this is a ScheduleRuleset
42
+ schedule_ruleset = schedule_ruleset.to_ScheduleRuleset
43
+ return [nil, nil] if schedule_ruleset.empty?
44
+ schedule_ruleset = schedule_ruleset.get
45
+
46
+ # Define the start and end date
47
+ year_start_date = nil
48
+ year_end_date = nil
49
+ if schedule_ruleset.model.yearDescription.is_initialized
50
+ year_description = schedule_ruleset.model.yearDescription.get
51
+ year = year_description.assumedYear
52
+ year_start_date = OpenStudio::Date.new(OpenStudio::MonthOfYear.new('January'), 1, year)
53
+ year_end_date = OpenStudio::Date.new(OpenStudio::MonthOfYear.new('December'), 31, year)
54
+ else
55
+ year_start_date = OpenStudio::Date.new(OpenStudio::MonthOfYear.new('January'), 1, 2009)
56
+ year_end_date = OpenStudio::Date.new(OpenStudio::MonthOfYear.new('December'), 31, 2009)
57
+ end
58
+
59
+ # Get the ordered list of all the day schedules that are used by this schedule ruleset
60
+ day_schs = schedule_ruleset.getDaySchedules(year_start_date, year_end_date)
61
+
62
+ # Get a 365-value array of which schedule is used on each day of the year,
63
+ day_schs_used_each_day = schedule_ruleset.getActiveRuleIndices(year_start_date, year_end_date)
64
+
65
+ # Create a map that shows how many days each schedule is used
66
+ day_sch_freq = day_schs_used_each_day.group_by { |n| n }
67
+ day_sch_freq = day_sch_freq.sort_by { |freq| freq[1].size }
68
+ common_day_freq = day_sch_freq.last
69
+
70
+ # Build a hash that maps schedule day index to schedule day
71
+ schedule_index_to_day = {}
72
+ day_schs.each_with_index do |day_sch, i|
73
+ schedule_index_to_day[day_schs_used_each_day[i]] = day_sch
74
+ end
75
+
76
+ # Get the most common day schedule
77
+ sch_index = common_day_freq[0]
78
+ number_of_days_sch_used = common_day_freq[1].size
79
+
80
+ # Get the day schedule at this index
81
+ day_sch = if sch_index == -1 # If index = -1, this day uses the default day schedule (not a rule)
82
+ schedule_ruleset.defaultDaySchedule
83
+ else
84
+ schedule_index_to_day[sch_index]
85
+ end
86
+
87
+ # Determine the full load hours for just one day
88
+ values = []
89
+ times = []
90
+ day_sch.times.each_with_index do |time, i|
91
+ times << day_sch.times[i]
92
+ values << day_sch.values[i]
93
+ end
94
+
95
+ # Get the minimum value
96
+ start_val = values.first
97
+ end_val = values.last
98
+
99
+ # Get the start time (first time value goes above minimum)
100
+ start_time = nil
101
+ values.each_with_index do |val, i|
102
+ break if i == values.size - 1 # Stop if we reach end of array
103
+ if val == start_val && values[i + 1] > start_val
104
+ start_time = times[i + 1]
105
+ break
106
+ end
107
+ end
108
+
109
+ # Get the end time (first time value goes back down to minimum)
110
+ end_time = nil
111
+ values.each_with_index do |val, i|
112
+ if i < values.size - 1
113
+ if val > end_val && values[i + 1] == end_val
114
+ end_time = times[i]
115
+ break
116
+ end
117
+ else
118
+ if val > end_val && values[0] == start_val # Check first hour of day for schedules that end at midnight
119
+ end_time = OpenStudio::Time.new(0, 24, 0, 0)
120
+ break
121
+ end
122
+ end
123
+ end
124
+
125
+ return [start_time, end_time]
126
+ end
127
+
128
+ # Check that the lighting, equipment, and HVAC setpoint schedules
129
+ # coordinate with the occupancy schedules. This is defined as having start and end
130
+ # times within the specified number of hours away from the occupancy schedule.
131
+ def check_sch_coord(category, target_standard, max_hrs, name_only = false)
132
+ # summary of the check
133
+ check_elems = OpenStudio::AttributeVector.new
134
+ check_elems << OpenStudio::Attribute.new('name', 'Conditioned Zones')
135
+ check_elems << OpenStudio::Attribute.new('category', category)
136
+ check_elems << OpenStudio::Attribute.new('description', 'Check that lighting, equipment, and HVAC schedules coordinate with occupancy.')
137
+
138
+ # stop here if only name is requested this is used to populate display name for arguments
139
+ if name_only == true
140
+ results = []
141
+ check_elems.each do |elem|
142
+ results << elem.valueAsString
143
+ end
144
+ return results
145
+ end
146
+
147
+ std = Standard.build(target_standard)
148
+
149
+ begin
150
+ # Convert max hr limit to OpenStudio Time
151
+ max_hrs = OpenStudio::Time.new(0, max_hrs, 0, 0)
152
+
153
+ # Check schedules in each space
154
+ @model.getSpaces.each do |space|
155
+ # Occupancy, Lighting, and Equipment Schedules
156
+ coord_schs = []
157
+ occ_schs = []
158
+ # Get the space type (optional)
159
+ space_type = space.spaceType
160
+
161
+ # Occupancy
162
+ occs = []
163
+ occs += space.people # From space directly
164
+ occs += space_type.get.people if space_type.is_initialized # Inherited from space type
165
+ occs.each do |occ|
166
+ occ_schs << occ.numberofPeopleSchedule.get if occ.numberofPeopleSchedule.is_initialized
167
+ end
168
+
169
+ # Lights
170
+ lts = []
171
+ lts += space.lights # From space directly
172
+ lts += space_type.get.lights if space_type.is_initialized # Inherited from space type
173
+ lts.each do |lt|
174
+ coord_schs << lt.schedule.get if lt.schedule.is_initialized
175
+ end
176
+
177
+ # Equip
178
+ plugs = []
179
+ plugs += space.electricEquipment # From space directly
180
+ plugs += space_type.get.electricEquipment if space_type.is_initialized # Inherited from space type
181
+ plugs.each do |plug|
182
+ coord_schs << plug.schedule.get if plug.schedule.is_initialized
183
+ end
184
+
185
+ # HVAC Schedule (airloop-served zones only)
186
+ if space.thermalZone.is_initialized
187
+ zone = space.thermalZone.get
188
+ if zone.airLoopHVAC.is_initialized
189
+ coord_schs << zone.airLoopHVAC.get.availabilitySchedule
190
+ end
191
+ end
192
+
193
+ # Cannot check spaces with no occupancy schedule to compare against
194
+ next if occ_schs.empty?
195
+
196
+ # Get start and end occupancy times from the first occupancy schedule
197
+ occ_start_time, occ_end_time = get_start_and_end_times(occ_schs[0])
198
+
199
+ # Cannot check a space where the occupancy start time or end time cannot be determined
200
+ next if occ_start_time.nil? || occ_end_time.nil?
201
+
202
+ # Check all schedules against occupancy
203
+
204
+ # Lights should have a start and end within X hrs of the occupancy start and end
205
+ coord_schs.each do |coord_sch|
206
+ # Get start and end time of load/HVAC schedule
207
+ start_time, end_time = get_start_and_end_times(coord_sch)
208
+ if start_time.nil?
209
+ check_elems << OpenStudio::Attribute.new('flag', "Could not determine start time of a schedule called #{coord_sch.name}, cannot determine if schedule coordinates with occupancy schedule.")
210
+ next
211
+ elsif end_time.nil?
212
+ check_elems << OpenStudio::Attribute.new('flag', "Could not determine end time of a schedule called #{coord_sch.name}, cannot determine if schedule coordinates with occupancy schedule.")
213
+ next
214
+ end
215
+
216
+ # Check start time
217
+ if (occ_start_time - start_time) > max_hrs || (start_time - occ_start_time) > max_hrs
218
+ check_elems << OpenStudio::Attribute.new('flag', "The start time of #{coord_sch.name} is #{start_time}, which is more than #{max_hrs} away from the occupancy schedule start time of #{occ_start_time} for #{occ_schs[0].name} in #{space.name}. Schedules do not coordinate.")
219
+ end
220
+
221
+ # Check end time
222
+ if (occ_end_time - end_time) > max_hrs || (end_time - occ_end_time) > max_hrs
223
+ check_elems << OpenStudio::Attribute.new('flag', "The end time of #{coord_sch.name} is #{end_time}, which is more than #{max_hrs} away from the occupancy schedule end time of #{occ_end_time} for #{occ_schs[0].name} in #{space.name}. Schedules do not coordinate.")
224
+ end
225
+ end
226
+ end
227
+ rescue StandardError => e
228
+ # brief description of ruby error
229
+ check_elems << OpenStudio::Attribute.new('flag', "Error prevented QAQC check from running (#{e}).")
230
+
231
+ # backtrace of ruby error for diagnostic use
232
+ if @error_backtrace then check_elems << OpenStudio::Attribute.new('flag', e.backtrace.join("\n").to_s) end
233
+ end
234
+
235
+ # add check_elms to new attribute
236
+ check_elem = OpenStudio::Attribute.new('check', check_elems)
237
+
238
+ return check_elem
239
+ # note: registerWarning and registerValue will be added for checks downstream using os_lib_reporting_qaqc.rb
240
+ end
241
+ end
@@ -0,0 +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