buildingsync 0.2.0

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 (62) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/continuous_integration.yml +146 -0
  3. data/.gitignore +33 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +10 -0
  6. data/CHANGELOG.md +50 -0
  7. data/Gemfile +31 -0
  8. data/Jenkinsfile +10 -0
  9. data/LICENSE.md +29 -0
  10. data/README.md +105 -0
  11. data/Rakefile +77 -0
  12. data/bin/console +15 -0
  13. data/bin/setup +8 -0
  14. data/buildingsync.gemspec +37 -0
  15. data/config.rb.in +26 -0
  16. data/doc_templates/LICENSE.md +29 -0
  17. data/doc_templates/README.md.erb +42 -0
  18. data/doc_templates/copyright_erb.txt +38 -0
  19. data/doc_templates/copyright_js.txt +5 -0
  20. data/doc_templates/copyright_ruby.txt +36 -0
  21. data/lib/buildingsync.rb +43 -0
  22. data/lib/buildingsync/all_resource_total.rb +54 -0
  23. data/lib/buildingsync/audit_date.rb +54 -0
  24. data/lib/buildingsync/constants.rb +49 -0
  25. data/lib/buildingsync/contact.rb +54 -0
  26. data/lib/buildingsync/extension.rb +57 -0
  27. data/lib/buildingsync/generator.rb +584 -0
  28. data/lib/buildingsync/get_bcl_weather_file.rb +326 -0
  29. data/lib/buildingsync/helpers/Model.hvac.rb +216 -0
  30. data/lib/buildingsync/helpers/helper.rb +494 -0
  31. data/lib/buildingsync/helpers/xml_get_set.rb +215 -0
  32. data/lib/buildingsync/makers/phase_zero_base.osw +178 -0
  33. data/lib/buildingsync/makers/workflow_maker.json +811 -0
  34. data/lib/buildingsync/makers/workflow_maker.rb +581 -0
  35. data/lib/buildingsync/makers/workflow_maker_base.rb +167 -0
  36. data/lib/buildingsync/model_articulation/building.rb +1119 -0
  37. data/lib/buildingsync/model_articulation/building_and_system_types.json +121 -0
  38. data/lib/buildingsync/model_articulation/building_section.rb +190 -0
  39. data/lib/buildingsync/model_articulation/building_system.rb +49 -0
  40. data/lib/buildingsync/model_articulation/envelope_system.rb +102 -0
  41. data/lib/buildingsync/model_articulation/exterior_floor_system_type.rb +64 -0
  42. data/lib/buildingsync/model_articulation/facility.rb +439 -0
  43. data/lib/buildingsync/model_articulation/foundation_system_type.rb +64 -0
  44. data/lib/buildingsync/model_articulation/hvac_system.rb +395 -0
  45. data/lib/buildingsync/model_articulation/lighting_system.rb +102 -0
  46. data/lib/buildingsync/model_articulation/loads_system.rb +287 -0
  47. data/lib/buildingsync/model_articulation/location_element.rb +129 -0
  48. data/lib/buildingsync/model_articulation/measure.rb +57 -0
  49. data/lib/buildingsync/model_articulation/roof_system_type.rb +64 -0
  50. data/lib/buildingsync/model_articulation/service_hot_water_system.rb +87 -0
  51. data/lib/buildingsync/model_articulation/site.rb +242 -0
  52. data/lib/buildingsync/model_articulation/spatial_element.rb +343 -0
  53. data/lib/buildingsync/model_articulation/wall_system_type.rb +64 -0
  54. data/lib/buildingsync/report.rb +217 -0
  55. data/lib/buildingsync/resource_use.rb +55 -0
  56. data/lib/buildingsync/scenario.rb +622 -0
  57. data/lib/buildingsync/selection_tool.rb +98 -0
  58. data/lib/buildingsync/time_series.rb +85 -0
  59. data/lib/buildingsync/translator.rb +167 -0
  60. data/lib/buildingsync/utility.rb +67 -0
  61. data/lib/buildingsync/version.rb +45 -0
  62. metadata +223 -0
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ # *******************************************************************************
4
+ # OpenStudio(R), Copyright (c) 2008-2020, Alliance for Sustainable Energy, LLC.
5
+ # BuildingSync(R), Copyright (c) 2015-2020, Alliance for Sustainable Energy, LLC.
6
+ # All rights reserved.
7
+ #
8
+ # Redistribution and use in source and binary forms, with or without
9
+ # modification, are permitted provided that the following conditions are met:
10
+ #
11
+ # (1) Redistributions of source code must retain the above copyright notice,
12
+ # this list of conditions and the following disclaimer.
13
+ #
14
+ # (2) Redistributions in binary form must reproduce the above copyright notice,
15
+ # this list of conditions and the following disclaimer in the documentation
16
+ # and/or other materials provided with the distribution.
17
+ #
18
+ # (3) Neither the name of the copyright holder nor the names of any contributors
19
+ # may be used to endorse or promote products derived from this software without
20
+ # specific prior written permission from the respective party.
21
+ #
22
+ # (4) Other than as required in clauses (1) and (2), distributions in any form
23
+ # of modifications or other derivative works may not use the "OpenStudio"
24
+ # trademark, "OS", "os", or any other confusingly similar designation without
25
+ # specific prior written permission from Alliance for Sustainable Energy, LLC.
26
+ #
27
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS
28
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
29
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE
31
+ # UNITED STATES GOVERNMENT, OR THE UNITED STATES DEPARTMENT OF ENERGY, NOR ANY OF
32
+ # THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
33
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
34
+ # OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
36
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37
+ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
+ # *******************************************************************************
39
+
40
+ module BuildingSync
41
+ # Foundation System Type
42
+ class FoundationSystemType
43
+ # initialize a foundation system type given a ref
44
+ # @param doc [REXML::Document]
45
+ # @param ns [String]
46
+ # @param ref [String]
47
+ def initialize(doc, ns, ref)
48
+ @id = nil
49
+ doc.elements.each("#{ns}:Systems/#{ns}:FoundationSystems/#{ns}:FoundationSystem") do |foundation_system|
50
+ if foundation_system.attributes['ID'] == ref
51
+ read(foundation_system, ns)
52
+ end
53
+ end
54
+ end
55
+
56
+ # read
57
+ # @param foundation_system [REXML:Element]
58
+ # @param ns [String]
59
+ def read(foundation_system, ns)
60
+ # ID
61
+ @id = foundation_system.attributes['ID'] if foundation_system.attributes['ID']
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,395 @@
1
+ # frozen_string_literal: true
2
+
3
+ # *******************************************************************************
4
+ # OpenStudio(R), Copyright (c) 2008-2020, Alliance for Sustainable Energy, LLC.
5
+ # BuildingSync(R), Copyright (c) 2015-2020, Alliance for Sustainable Energy, LLC.
6
+ # All rights reserved.
7
+ #
8
+ # Redistribution and use in source and binary forms, with or without
9
+ # modification, are permitted provided that the following conditions are met:
10
+ #
11
+ # (1) Redistributions of source code must retain the above copyright notice,
12
+ # this list of conditions and the following disclaimer.
13
+ #
14
+ # (2) Redistributions in binary form must reproduce the above copyright notice,
15
+ # this list of conditions and the following disclaimer in the documentation
16
+ # and/or other materials provided with the distribution.
17
+ #
18
+ # (3) Neither the name of the copyright holder nor the names of any contributors
19
+ # may be used to endorse or promote products derived from this software without
20
+ # specific prior written permission from the respective party.
21
+ #
22
+ # (4) Other than as required in clauses (1) and (2), distributions in any form
23
+ # of modifications or other derivative works may not use the "OpenStudio"
24
+ # trademark, "OS", "os", or any other confusingly similar designation without
25
+ # specific prior written permission from Alliance for Sustainable Energy, LLC.
26
+ #
27
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS
28
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
29
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE
31
+ # UNITED STATES GOVERNMENT, OR THE UNITED STATES DEPARTMENT OF ENERGY, NOR ANY OF
32
+ # THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
33
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
34
+ # OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
36
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37
+ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
+ # *******************************************************************************
39
+ require 'buildingsync/helpers/helper'
40
+ require 'buildingsync/helpers/xml_get_set'
41
+
42
+ module BuildingSync
43
+ # HVACSystem class
44
+ class HVACSystem < BuildingSystem
45
+ include BuildingSync::Helper
46
+ include BuildingSync::XmlGetSet
47
+ # initialize
48
+ # @param system_element [REXML::Element]
49
+ # @param ns [String]
50
+ def initialize(base_xml, ns = 'auc')
51
+ @base_xml = base_xml
52
+ @ns = ns
53
+
54
+ help_element_class_type_check(base_xml, 'HVACSystem')
55
+
56
+ # code to initialize
57
+ read_xml
58
+ end
59
+
60
+ # read xml
61
+ def read_xml; end
62
+
63
+ def get_linked_ids; end
64
+
65
+ # get principal hvac system type
66
+ # @return [String]
67
+ def get_principal_hvac_system_type
68
+ return xget_text('PrincipalHVACSystemType')
69
+ end
70
+
71
+ # adding the principal hvac system type to the hvac systems, overwrite existing values or create new elements if none are present
72
+ # @param id [String]
73
+ # @param principal_hvac_type [String]
74
+ def set_principal_hvac_system_type(principal_hvac_type)
75
+ xset_or_create('PrincipalHVACSystemType', principal_hvac_type)
76
+ end
77
+
78
+ # add exhaust
79
+ # @param model [OpenStudio::Model]
80
+ # @param standard [Standard]
81
+ # @param kitchen_makeup [String]
82
+ # @param remove_objects [Boolean]
83
+ def add_exhaust(model, standard, kitchen_makeup, remove_objects)
84
+ # remove exhaust objects
85
+ if remove_objects
86
+ model.getFanZoneExhausts.each(&:remove)
87
+ end
88
+
89
+ zone_exhaust_fans = standard.model_add_exhaust(model, kitchen_makeup) # second argument is strategy for finding makeup zones for exhaust zones
90
+ zone_exhaust_fans.each do |k, v|
91
+ max_flow_rate_ip = OpenStudio.convert(k.maximumFlowRate.get, 'm^3/s', 'cfm').get
92
+ if v.key?(:zone_mixing)
93
+ zone_mixing = v[:zone_mixing]
94
+ mixing_source_zone_name = zone_mixing.sourceZone.get.name
95
+ mixing_design_flow_rate_ip = OpenStudio.convert(zone_mixing.designFlowRate.get, 'm^3/s', 'cfm').get
96
+ OpenStudio.logFree(OpenStudio::Info, 'BuildingSync.HVACSystem.add_exhaust', "Adding #{OpenStudio.toNeatString(max_flow_rate_ip, 0, true)} (cfm) of exhaust to #{k.thermalZone.get.name}, with #{OpenStudio.toNeatString(mixing_design_flow_rate_ip, 0, true)} (cfm) of makeup air from #{mixing_source_zone_name}")
97
+ else
98
+ OpenStudio.logFree(OpenStudio::Info, 'BuildingSync.HVACSystem.add_exhaust', "Adding #{OpenStudio.toNeatString(max_flow_rate_ip, 0, true)} (cfm) of exhaust to #{k.thermalZone.get.name}")
99
+ end
100
+ end
101
+ return true
102
+ end
103
+
104
+ # add thermostats
105
+ # @param model [OpenStudio::Model]
106
+ # @param standard [Standard]
107
+ # @param remove_objects [Boolean]
108
+ def add_thermostats(model, standard, remove_objects)
109
+ # remove thermostats
110
+ if remove_objects
111
+ model.getThermostatSetpointDualSetpoints.each(&:remove)
112
+ end
113
+
114
+ model.getSpaceTypes.each do |space_type|
115
+ # create thermostat schedules
116
+ # apply internal load schedules
117
+ # the last bool test it to make thermostat schedules. They are added to the model but not assigned
118
+ standard.space_type_apply_internal_load_schedules(space_type, false, false, false, false, false, false, true)
119
+
120
+ # identify thermal thermostat and apply to zones (apply_internal_load_schedules names )
121
+ model.getThermostatSetpointDualSetpoints.each do |thermostat|
122
+ next if !thermostat.name.to_s.include?(space_type.name.to_s)
123
+ if !thermostat.coolingSetpointTemperatureSchedule.is_initialized
124
+ OpenStudio.logFree(OpenStudio::Warn, 'BuildingSync.HVACSystem.add_thermostats', "#{thermostat.name} has no cooling setpoint.")
125
+ end
126
+ if !thermostat.heatingSetpointTemperatureSchedule.is_initialized
127
+ OpenStudio.logFree(OpenStudio::Warn, 'BuildingSync.HVACSystem.add_thermostats', "#{thermostat.name} has no heating setpoint.")
128
+ end
129
+
130
+ OpenStudio.logFree(OpenStudio::Info, 'BuildingSync.HVACSystem.add_thermostats', "Assigning #{thermostat.name} to thermal zones with #{space_type.name} assigned.")
131
+ puts "BuildingSync.HVACSystem.add_thermostats - Assigning #{thermostat.name} to thermal zones with #{space_type.name} assigned."
132
+ space_type.spaces.each do |space|
133
+ next if !space.thermalZone.is_initialized
134
+
135
+ space.thermalZone.get.setThermostatSetpointDualSetpoint(thermostat)
136
+ end
137
+ end
138
+ end
139
+ puts "ThermalZones: #{model.getThermalZones.size}"
140
+ puts "ThermostatDSPs: #{model.getThermostatSetpointDualSetpoints.size}"
141
+ add_setpoints_to_thermostats_if_none(model)
142
+ return true
143
+ end
144
+
145
+ # @return [Boolean] true if ALL thermostats have heating and cooling setpoints
146
+ def add_setpoints_to_thermostats_if_none(model)
147
+ successful = true
148
+
149
+ # seperate out thermostats that need heating vs. cooling schedules
150
+ tstats_cooling = []
151
+ tstats_heating = []
152
+ model.getThermalZones.each do |tz|
153
+ if tz.thermostatSetpointDualSetpoint.is_initialized
154
+ tstat = tz.thermostatSetpointDualSetpoint.get
155
+ tstats_cooling << tstat if !tstat.coolingSetpointTemperatureSchedule.is_initialized
156
+ tstats_heating << tstat if !tstat.heatingSetpointTemperatureSchedule.is_initialized
157
+ end
158
+ end
159
+
160
+ puts "BuildingSync.HVACSystem.add_setpoints_to_thermostats_if_none - (#{tstats_cooling.size}) thermostats needing cooling schedule"
161
+ puts "BuildingSync.HVACSystem.add_setpoints_to_thermostats_if_none - (#{tstats_heating.size}) thermostats needing heating schedule"
162
+
163
+ htg_setpoints = [
164
+ # [Time.new(days, hours, mins seconds), temp_value_celsius]
165
+ [OpenStudio::Time.new(0, 9, 0, 0), 17],
166
+ [OpenStudio::Time.new(0, 17, 0, 0), 20],
167
+ [OpenStudio::Time.new(0, 24, 0, 0), 17]
168
+ ]
169
+ clg_setpoints = [
170
+ # [Time.new(days, hours, mins seconds), temp_value_celsius]
171
+ [OpenStudio::Time.new(0, 9, 0, 0), 23],
172
+ [OpenStudio::Time.new(0, 17, 0, 0), 20],
173
+ [OpenStudio::Time.new(0, 24, 0, 0), 23]
174
+ ]
175
+
176
+ heating_sp_schedule = create_schedule_ruleset(model, htg_setpoints, 'Thermostat Heating SP')
177
+ cooling_sp_schedule = create_schedule_ruleset(model, clg_setpoints, 'Thermostat Cooling SP')
178
+
179
+ tstats_cooling.each do |thermostat|
180
+ success = thermostat.setCoolingSetpointTemperatureSchedule(cooling_sp_schedule)
181
+ if success
182
+ OpenStudio.logFree(OpenStudio::Info, 'BuildingSync.HVACSystem.add_setpoints_to_thermostats_if_none', "Cooling Schedule (#{cooling_sp_schedule.nameString}) added to Thermostat: #{thermostat.nameString}")
183
+ puts "BuildingSync.HVACSystem.add_setpoints_to_thermostats_if_none - Cooling Schedule (#{cooling_sp_schedule.nameString}) added to Thermostat: #{thermostat.nameString}"
184
+ else
185
+ successful = false
186
+ OpenStudio.logFree(OpenStudio::Warn, 'BuildingSync.HVACSystem.add_setpoints_to_thermostats_if_none', "No Cooling Schedule for Thermostat: #{thermostat.nameString}")
187
+ puts "BuildingSync.HVACSystem.add_setpoints_to_thermostats_if_none - No Cooling Schedule for Thermostat: #{thermostat.nameString}"
188
+ end
189
+ end
190
+
191
+ tstats_heating.each do |thermostat|
192
+ success = thermostat.setHeatingSetpointTemperatureSchedule(heating_sp_schedule)
193
+ if success
194
+ OpenStudio.logFree(OpenStudio::Info, 'BuildingSync.HVACSystem.add_setpoints_to_thermostats_if_none', "Heating Schedule (#{heating_sp_schedule.nameString}) added to Thermostat: #{thermostat.nameString}")
195
+ puts "BuildingSync.HVACSystem.add_setpoints_to_thermostats_if_none - Heating Schedule (#{heating_sp_schedule.nameString}) added to Thermostat: #{thermostat.nameString}"
196
+ else
197
+ successful = false
198
+ OpenStudio.logFree(OpenStudio::Warn, 'BuildingSync.HVACSystem.add_setpoints_to_thermostats_if_none', "No Heating Schedule for Thermostat: #{thermostat.nameString}")
199
+ puts "BuildingSync.HVACSystem.add_setpoints_to_thermostats_if_none - No Heating Schedule for Thermostat: #{thermostat.nameString}"
200
+ end
201
+ end
202
+ return successful
203
+ end
204
+
205
+ # @param model [OpenStudio::Model::Model]
206
+ # @param values [Array<Array<OpenStudio::Time, Float>>] [[cutoff_time, value_until_cutoff]]
207
+ # @return [OpenStudio::Model::ScheduleRuleset] a new schedule ruleset with values added to the default day
208
+ def create_schedule_ruleset(model, values, name)
209
+ ruleset = OpenStudio::Model::ScheduleRuleset.new(model)
210
+ ruleset.setName(name)
211
+ dd = ruleset.defaultDaySchedule
212
+ values.each do |v|
213
+ dd.addValue(v[0], v[1])
214
+ end
215
+ return ruleset
216
+ end
217
+
218
+ # map principal hvac system type to cbecs system type
219
+ # @param principal_hvac_system_type [String]
220
+ # @param fallback_system_type [String] the default system_type to use if the other is not found
221
+ # @return [String]
222
+ def map_to_cbecs(principal_hvac_system_type, fallback_system_type)
223
+ case principal_hvac_system_type
224
+ when 'Packaged Terminal Air Conditioner'
225
+ return 'PTAC with hot water heat'
226
+ when 'Packaged Terminal Heat Pump'
227
+ return 'PTHP'
228
+ when 'Packaged Rooftop Air Conditioner'
229
+ return 'PSZ-AC with gas coil heat'
230
+ when 'Packaged Rooftop Heat Pump'
231
+ return 'PSZ-HP'
232
+ when 'Packaged Rooftop VAV with Hot Water Reheat'
233
+ return 'PVAV with reheat'
234
+ when 'Packaged Rooftop VAV with Electric Reheat'
235
+ return 'PVAV with PFP boxes'
236
+ when 'VAV with Hot Water Reheat'
237
+ return 'VAV with reheat'
238
+ when 'VAV with Electric Reheat'
239
+ return 'VAV with PFP boxes'
240
+ else
241
+ OpenStudio.logFree(OpenStudio::Error, 'BuildingSync.HVACSystem.map_to_cbecs', "HVACSystem ID: #{xget_id}: No mapping for #{principal_hvac_system_type} to CBECS. Using the system type from standards: #{fallback_system_type}")
242
+ return fallback_system_type
243
+ end
244
+ end
245
+
246
+ # add hvac
247
+ # @param model [OpenStudio::Model]
248
+ # @param zone_hash [hash]
249
+ # @param standard [Standard]
250
+ # @param system_type [String]
251
+ # @param hvac_delivery_type [String]
252
+ # @param htg_src [String]
253
+ # @param clg_src [String]
254
+ # @param remove_objects [Boolean]
255
+ # @return [Boolean]
256
+ def add_hvac(model, zone_hash, standard, system_type, hvac_delivery_type = 'Forced Air', htg_src = 'NaturalGas', clg_src = 'Electricity', remove_objects = false)
257
+ # remove HVAC objects
258
+ if remove_objects
259
+ standard.model_remove_prm_hvac(model)
260
+ end
261
+
262
+ puts "HVAC System ID: #{xget_id}. System_type derived from standards: #{system_type} and principal hvac system type override is: #{get_principal_hvac_system_type}"
263
+ temp = get_principal_hvac_system_type
264
+ if !temp.nil? && !temp.empty?
265
+ previous_system_type = system_type
266
+ system_type = map_to_cbecs(get_principal_hvac_system_type, previous_system_type)
267
+ OpenStudio.logFree(OpenStudio::Info, 'BuildingSync.HVACSystem.add_hvac', "HVAC System ID: #{xget_id}. System type derived from standards: #{previous_system_type}, overriden to #{system_type}")
268
+ end
269
+
270
+ case system_type
271
+ when 'Inferred'
272
+
273
+ # Get the hvac delivery type enum
274
+ hvac_delivery = case hvac_delivery_type
275
+ when 'Forced Air'
276
+ 'air'
277
+ when 'Hydronic'
278
+ 'hydronic'
279
+ end
280
+
281
+ # Group the zones by occupancy type. Only split out
282
+ # non-dominant groups if their total area exceeds the limit.
283
+ sys_groups = standard.model_group_zones_by_type(model, OpenStudio.convert(20_000, 'ft^2', 'm^2').get)
284
+
285
+ # For each group, infer the HVAC system type.
286
+ sys_groups.each do |sys_group|
287
+ # Infer the principal system type
288
+ # OpenStudio.logFree(OpenStudio::Info, 'BuildingSync.Facility.create_building_system', "template = #{template}, climate_zone = #{climate_zone}, occ_type = #{sys_group['type']}, hvac_delivery = #{hvac_delivery}, htg_src = #{htg_src}, clg_src = #{clg_src}, area_ft2 = #{sys_group['area_ft2']}, num_stories = #{sys_group['stories']}")
289
+ sys_type, central_htg_fuel, zone_htg_fuel, clg_fuel = standard.model_typical_hvac_system_type(model,
290
+ climate_zone,
291
+ sys_group['type'],
292
+ hvac_delivery,
293
+ htg_src,
294
+ clg_src,
295
+ OpenStudio.convert(sys_group['area_ft2'], 'ft^2', 'm^2').get,
296
+ sys_group['stories'])
297
+
298
+ # Infer the secondary system type for multizone systems
299
+ sec_sys_type = case sys_type
300
+ when 'PVAV Reheat', 'VAV Reheat'
301
+ 'PSZ-AC'
302
+ when 'PVAV PFP Boxes', 'VAV PFP Boxes'
303
+ 'PSZ-HP'
304
+ else
305
+ sys_type # same as primary system type
306
+ end
307
+
308
+ # Group zones by story
309
+ story_zone_lists = standard.model_group_zones_by_story(model, sys_group['zones'])
310
+
311
+ # On each story, add the primary system to the primary zones
312
+ # and add the secondary system to any zones that are different.
313
+ story_zone_lists.each do |story_group|
314
+ # Differentiate primary and secondary zones, based on
315
+ # operating hours and internal loads (same as 90.1 PRM)
316
+ pri_sec_zone_lists = standard.model_differentiate_primary_secondary_thermal_zones(model, story_group)
317
+ # Add the primary system to the primary zones
318
+ standard.model_add_hvac_system(model, sys_type, central_htg_fuel, zone_htg_fuel, clg_fuel, pri_sec_zone_lists['primary'])
319
+ # Add the secondary system to the secondary zones (if any)
320
+ if !pri_sec_zone_lists['secondary'].empty?
321
+ standard.model_add_hvac_system(model, sec_sys_type, central_htg_fuel, zone_htg_fuel, clg_fuel, pri_sec_zone_lists['secondary'])
322
+ end
323
+ end
324
+ end
325
+ else
326
+ # Group the zones by story
327
+ story_groups = standard.model_group_zones_by_story(model, model.getThermalZones)
328
+
329
+ # Add the user specified HVAC system for each story.
330
+ # Single-zone systems will get one per zone.
331
+ story_groups.each do |zones|
332
+ new_system_type = get_system_type_from_zone(zone_hash, zones, system_type)
333
+ puts "setting system: #{new_system_type} for zone names: #{help_get_zone_name_list(zones)}"
334
+ model.add_cbecs_hvac_system(standard, new_system_type, zones)
335
+ end
336
+ end
337
+ return true
338
+ end
339
+
340
+ # get system type from zone
341
+ # @param zone_hash [hash]
342
+ # @param zones [array<OpenStudio::Model::ThermalZone>]
343
+ # @param system_type [String]
344
+ # @return [String]
345
+ def get_system_type_from_zone(zone_hash, zones, system_type)
346
+ zone_hash&.each do |id, zone_list|
347
+ zone_name_list = help_get_zone_name_list(zone_list)
348
+ zones.each do |zone|
349
+ if zone_name_list.include? zone.name.get
350
+ return map_to_cbecs(get_principal_hvac_system_type, system_type)
351
+ end
352
+ end
353
+ end
354
+ return system_type
355
+ end
356
+
357
+ # apply sizing and assumptions
358
+ # @param model [OpenStudio::Model]
359
+ # @param output_path [String]
360
+ # @param standard [Standard]
361
+ # @param primary_bldg_type [String]
362
+ # @param system_type [String]
363
+ # @param climate_zone [String]
364
+ # @return [Boolean]
365
+ def apply_sizing_and_assumptions(model, output_path, standard, primary_bldg_type, system_type, climate_zone)
366
+ case system_type
367
+ when 'Ideal Air Loads'
368
+
369
+ else
370
+ # Set the heating and cooling sizing parameters
371
+ standard.model_apply_prm_sizing_parameters(model)
372
+
373
+ # Perform a sizing run
374
+ if standard.model_run_sizing_run(model, "#{output_path}/SR") == false
375
+ return false
376
+ end
377
+
378
+ # If there are any multizone systems, reset damper positions
379
+ # to achieve a 60% ventilation effectiveness minimum for the system
380
+ # following the ventilation rate procedure from 62.1
381
+ standard.model_apply_multizone_vav_outdoor_air_sizing(model)
382
+
383
+ # Apply the prototype HVAC assumptions
384
+ standard.model_apply_prototype_hvac_assumptions(model, primary_bldg_type, climate_zone)
385
+
386
+ # Apply the HVAC efficiency standard
387
+ standard.model_apply_hvac_efficiency_standard(model, climate_zone)
388
+ end
389
+ return true
390
+ end
391
+
392
+ # principal hvac system type
393
+ attr_reader :principal_hvac_system_type
394
+ end
395
+ end