buildingsync 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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