openstudio-standards 0.2.15 → 0.2.16.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/data/geometry/ASHRAEHighriseApartment.osm +0 -27
  3. data/data/standards/OpenStudio_Standards-ashrae_90_1.xlsx +0 -0
  4. data/data/standards/OpenStudio_Standards-ashrae_90_1_28Jan2022.xlsx +0 -0
  5. data/data/standards/test_performance_expected_dd_results.csv +710 -710
  6. data/lib/openstudio-standards/btap/btap_result.rb +2 -2
  7. data/lib/openstudio-standards/btap/reporting.rb +2 -2
  8. data/lib/openstudio-standards/btap/simmanager.rb +2 -2
  9. data/lib/openstudio-standards/hvac_sizing/Siz.ControllerOutdoorAir.rb +0 -54
  10. data/lib/openstudio-standards/hvac_sizing/Siz.HeatingCoolingFuels.rb +11 -1
  11. data/lib/openstudio-standards/hvac_sizing/Siz.Model.rb +1 -1
  12. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.College.rb +26 -5
  13. data/lib/openstudio-standards/prototypes/common/objects/Prototype.CoilCoolingWaterToAirHeatPumpEquationFit.rb +35 -16
  14. data/lib/openstudio-standards/prototypes/common/objects/Prototype.CoilHeatingWaterToAirHeatPumpEquationFit.rb +23 -10
  15. data/lib/openstudio-standards/prototypes/common/objects/Prototype.Model.rb +36 -0
  16. data/lib/openstudio-standards/prototypes/common/objects/Prototype.ServiceWaterHeating.rb +6 -6
  17. data/lib/openstudio-standards/prototypes/common/objects/Prototype.hvac_systems.rb +0 -3
  18. data/lib/openstudio-standards/standards/Standards.AirLoopHVAC.rb +2 -2
  19. data/lib/openstudio-standards/standards/Standards.CoilCoolingWaterToAirHeatPumpEquationFit.rb +9 -3
  20. data/lib/openstudio-standards/standards/Standards.CoilHeatingGas.rb +2 -0
  21. data/lib/openstudio-standards/standards/Standards.Construction.rb +12 -6
  22. data/lib/openstudio-standards/standards/Standards.Model.rb +38 -7
  23. data/lib/openstudio-standards/standards/Standards.Space.rb +1 -1
  24. data/lib/openstudio-standards/standards/Standards.SpaceType.rb +7 -0
  25. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/ashrae_90_1_2004.Model.rb +32 -11
  26. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.construction_properties.json +22 -742
  27. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.construction_sets.json +2 -2
  28. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.prototype_inputs.json +3 -3
  29. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.spc_typ.json +6 -6
  30. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.construction_properties.json +19 -559
  31. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.construction_sets.json +2 -2
  32. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.prototype_inputs.json +3 -3
  33. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.spc_typ.json +6 -6
  34. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.construction_properties.json +19 -559
  35. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.construction_sets.json +2 -2
  36. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.prototype_inputs.json +5 -5
  37. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.spc_typ.json +7 -7
  38. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.construction_properties.json +19 -559
  39. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.construction_sets.json +2 -2
  40. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.prototype_inputs.json +5 -5
  41. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.spc_typ.json +7 -7
  42. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.construction_properties.json +370 -910
  43. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.construction_sets.json +2 -2
  44. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.prototype_inputs.json +6 -6
  45. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.refrigeration_system.json +0 -8
  46. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.spc_typ.json +12 -12
  47. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirLoopHVAC.rb +19 -6
  48. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.construction_properties.json +2380 -1300
  49. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.construction_sets.json +2 -2
  50. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.prototype_inputs.json +6 -6
  51. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.refrigeration_system.json +0 -8
  52. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.spc_typ.json +12 -12
  53. data/lib/openstudio-standards/standards/ashrae_90_1/data/ashrae_90_1.constructions.json +140 -0
  54. data/lib/openstudio-standards/standards/ashrae_90_1/data/ashrae_90_1.schedules.json +1176 -312
  55. data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_1980_2004/data/doe_ref_1980_2004.construction_properties.json +172 -1132
  56. data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_1980_2004/data/doe_ref_1980_2004.construction_sets.json +14 -14
  57. data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_1980_2004/data/doe_ref_1980_2004.prototype_inputs.json +2 -2
  58. data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_1980_2004/data/doe_ref_1980_2004.spc_typ.json +9 -9
  59. data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_pre_1980/data/doe_ref_pre_1980.construction_properties.json +180 -1140
  60. data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_pre_1980/data/doe_ref_pre_1980.construction_sets.json +14 -14
  61. data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_pre_1980/data/doe_ref_pre_1980.prototype_inputs.json +2 -2
  62. data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_pre_1980/data/doe_ref_pre_1980.spc_typ.json +10 -10
  63. data/lib/openstudio-standards/standards/ashrae_90_1/nrel_zne_ready_2017/data/nrel_zne_ready_2017.construction_properties.json +9 -9
  64. data/lib/openstudio-standards/standards/ashrae_90_1/ze_aedg_multifamily/data/ze_aedg_multifamily.construction_properties.json +9 -9
  65. data/lib/openstudio-standards/standards/necb/BTAPPRE1980/hvac_system_3_and_8_single_speed.rb +12 -6
  66. data/lib/openstudio-standards/standards/necb/BTAPPRE1980/hvac_system_4.rb +12 -6
  67. data/lib/openstudio-standards/standards/necb/BTAPPRE1980/hvac_system_6.rb +16 -8
  68. data/lib/openstudio-standards/standards/necb/ECMS/ecms.rb +10 -20
  69. data/lib/openstudio-standards/standards/necb/ECMS/hvac_systems.rb +209 -37
  70. data/lib/openstudio-standards/standards/necb/ECMS/loads.rb +1 -0
  71. data/lib/openstudio-standards/standards/necb/ECMS/pv_ground.rb +8 -6
  72. data/lib/openstudio-standards/standards/necb/NECB2011/autozone.rb +16 -9
  73. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/HighriseApartment.osm +1 -1
  74. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LowriseApartment.osm +1 -1
  75. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/MidriseApartment.osm +1 -1
  76. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_1_multi_speed.rb +9 -5
  77. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_1_single_speed.rb +10 -6
  78. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_2_and_5.rb +9 -5
  79. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_3_and_8_multi_speed.rb +14 -8
  80. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_3_and_8_single_speed.rb +14 -8
  81. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_4.rb +13 -6
  82. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_6.rb +12 -6
  83. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb +4 -2
  84. data/lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb +38 -19
  85. data/lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb +2 -2
  86. data/lib/openstudio-standards/standards/necb/NECB2011/service_water_heating.rb +15 -4
  87. data/lib/openstudio-standards/standards/necb/NECB2020/building_envelope.rb +10 -651
  88. data/lib/openstudio-standards/standards/necb/NECB2020/necb_2020.rb +8 -38
  89. data/lib/openstudio-standards/standards/necb/NECB2020/service_water_heating.rb +159 -0
  90. data/lib/openstudio-standards/standards/necb/common/btap_data.rb +41 -43
  91. data/lib/openstudio-standards/standards/necb/common/btap_datapoint.rb +7 -4
  92. data/lib/openstudio-standards/version.rb +1 -1
  93. data/lib/openstudio-standards.rb +1 -0
  94. metadata +4 -2
@@ -1,314 +1,5 @@
1
- class NECB2020
2
-
3
- # Reduces the WWR to the values specified by the NECB
4
- # NECB 3.2.1.4
5
- def apply_standard_window_to_wall_ratio(model:, fdwr_set: -1.0)
6
- # NECB FDWR limit
7
- hdd = self.get_necb_hdd18(model)
8
-
9
- # Get the maximum NECB fdwr
10
- # fdwr_set settings:
11
- # 0-1: Remove all windows and add windows to match this fdwr
12
- # -1: Remove all windows and add windows to match max fdwr from NECB
13
- # -2: Do not apply any fdwr changes, leave windows alone (also works for fdwr > 1)
14
- # -3: Use old method which reduces existing window size (if necessary) to meet maximum NECB fdwr limit
15
- # <-3.1: Remove all the windows
16
- # > 1: Do nothing
17
-
18
- if fdwr_set.to_f > 1.0
19
- return
20
- elsif fdwr_set.to_f >= 0.0 and fdwr_set <= 1.0
21
- apply_max_fdwr_nrcan(model: model, fdwr_lim: fdwr_set.to_f)
22
- return
23
- elsif fdwr_set.to_f >= -1.1 and fdwr_set <= -0.9
24
- fdwr_lim = (max_fwdr(hdd)).round(3)
25
- apply_max_fdwr_nrcan(model: model, fdwr_lim: fdwr_lim.to_f)
26
- return
27
- elsif fdwr_set.to_f >= -2.1 and fdwr_set <= -1.9
28
- return
29
- elsif fdwr_set.to_f >= -3.1 and fdwr_set <= -2.9
30
- fdwr_lim = (max_fwdr(hdd) * 100.0).round(1)
31
- return apply_limit_fdwr(model: model, fdwr_lim: fdwr_lim.to_f)
32
- elsif fdwr_set < -3.1
33
- apply_max_fdwr_nrcan(model: model, fdwr_lim: fdwr_set.to_f)
34
- return
35
- end
36
- end
37
-
38
- def apply_limit_fdwr(model:, fdwr_lim:)
39
- # Loop through all spaces in the model, and
40
- # per the PNNL PRM Reference Manual, find the areas
41
- # of each space conditioning category (res, nonres, semi-heated)
42
- # separately. Include space multipliers.
43
- nr_wall_m2 = 0.001 # Avoids divide by zero errors later
44
- nr_wind_m2 = 0
45
- res_wall_m2 = 0.001
46
- res_wind_m2 = 0
47
- sh_wall_m2 = 0.001
48
- sh_wind_m2 = 0
49
- total_wall_m2 = 0.001
50
- total_subsurface_m2 = 0.0
51
- # Store the space conditioning category for later use
52
- space_cats = {}
53
- model.getSpaces.sort.each do |space|
54
- # Loop through all surfaces in this space
55
- wall_area_m2 = 0
56
- wind_area_m2 = 0
57
- space.surfaces.sort.each do |surface|
58
- # Skip non-outdoor surfaces
59
- next unless surface.outsideBoundaryCondition == 'Outdoors'
60
- # Skip non-walls
61
- next unless surface.surfaceType.casecmp('wall').zero?
62
- # This wall's gross area (including window area)
63
- wall_area_m2 += surface.grossArea * space.multiplier
64
- # Subsurfaces in this surface
65
- surface.subSurfaces.sort.each do |ss|
66
- wind_area_m2 += ss.netArea * space.multiplier
67
- end
68
- end
69
-
70
- # Determine the space category
71
- # zTODO This should really use the heating/cooling loads
72
- # from the proposed building. However, in an attempt
73
- # to avoid another sizing run just for this purpose,
74
- # conditioned status is based on heating/cooling
75
- # setpoints. If heated-only, will be assumed Semiheated.
76
- # The full-bore method is on the next line in case needed.
77
- # cat = thermal_zone_conditioning_category(space, template, climate_zone)
78
- cooled = space_cooled?(space)
79
- heated = space_heated?(space)
80
- cat = 'Unconditioned'
81
- # Unconditioned
82
- if !heated && !cooled
83
- cat = 'Unconditioned'
84
- # Heated-Only
85
- elsif heated && !cooled
86
- cat = 'Semiheated'
87
- # Heated and Cooled
88
- else
89
- res = thermal_zone_residential?(space.thermalZone.get)
90
- cat = if res
91
- 'ResConditioned'
92
- else
93
- 'NonResConditioned'
94
- end
95
- end
96
- space_cats[space] = cat
97
- # NECB2011 keep track of totals for NECB regardless of conditioned or not.
98
- total_wall_m2 += wall_area_m2
99
- total_subsurface_m2 += wind_area_m2 # this contains doors as well.
100
-
101
- # Add to the correct category
102
- case cat
103
- when 'Unconditioned'
104
- next # Skip unconditioned spaces
105
- when 'NonResConditioned'
106
- nr_wall_m2 += wall_area_m2
107
- nr_wind_m2 += wind_area_m2
108
- when 'ResConditioned'
109
- res_wall_m2 += wall_area_m2
110
- res_wind_m2 += wind_area_m2
111
- when 'Semiheated'
112
- sh_wall_m2 += wall_area_m2
113
- sh_wind_m2 += wind_area_m2
114
- end
115
- end
116
-
117
- # Calculate the WWR of each category
118
- wwr_nr = ((nr_wind_m2 / nr_wall_m2) * 100.0).round(1)
119
- wwr_res = ((res_wind_m2 / res_wall_m2) * 100).round(1)
120
- wwr_sh = ((sh_wind_m2 / sh_wall_m2) * 100).round(1)
121
- fdwr = ((total_subsurface_m2 / total_wall_m2) * 100).round(1) # used by NECB2011
122
-
123
- # Convert to IP and report
124
- nr_wind_ft2 = OpenStudio.convert(nr_wind_m2, 'm^2', 'ft^2').get
125
- nr_wall_ft2 = OpenStudio.convert(nr_wall_m2, 'm^2', 'ft^2').get
126
-
127
- res_wind_ft2 = OpenStudio.convert(res_wind_m2, 'm^2', 'ft^2').get
128
- res_wall_ft2 = OpenStudio.convert(res_wall_m2, 'm^2', 'ft^2').get
129
-
130
- sh_wind_ft2 = OpenStudio.convert(sh_wind_m2, 'm^2', 'ft^2').get
131
- sh_wall_ft2 = OpenStudio.convert(sh_wall_m2, 'm^2', 'ft^2').get
132
-
133
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "WWR NonRes = #{wwr_nr.round}%; window = #{nr_wind_ft2.round} ft2, wall = #{nr_wall_ft2.round} ft2.")
134
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "WWR Res = #{wwr_res.round}%; window = #{res_wind_ft2.round} ft2, wall = #{res_wall_ft2.round} ft2.")
135
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "WWR Semiheated = #{wwr_sh.round}%; window = #{sh_wind_ft2.round} ft2, wall = #{sh_wall_ft2.round} ft2.")
136
-
137
- # WWR limit
138
- wwr_lim = 40.0
139
-
140
- # Check against WWR limit
141
- red_nr = wwr_nr > wwr_lim
142
- red_res = wwr_res > wwr_lim
143
- red_sh = wwr_sh > wwr_lim
144
-
145
- # puts "Current FDWR is #{fdwr}, must be less than #{fdwr_lim}."
146
- # puts "Current subsurf area is #{total_subsurface_m2} and gross surface area is #{total_wall_m2}"
147
- # Stop here unless windows / doors need reducing
148
- return true unless fdwr > fdwr_lim
149
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "Reducing the size of all windows (by raising sill height) to reduce window area down to the limit of #{wwr_lim.round}%.")
150
- # Determine the factors by which to reduce the window / door area
151
- mult = fdwr_lim / fdwr
152
- # Reduce the window area if any of the categories necessary
153
- model.getSpaces.sort.each do |space|
154
- # Loop through all surfaces in this space
155
- space.surfaces.sort.each do |surface|
156
- # Skip non-outdoor surfaces
157
- next unless surface.outsideBoundaryCondition == 'Outdoors'
158
- # Skip non-walls
159
- next unless surface.surfaceType == 'Wall'
160
- # Subsurfaces in this surface
161
- surface.subSurfaces.sort.each do |ss|
162
- # Reduce the size of the window
163
- red = 1.0 - mult
164
- sub_surface_reduce_area_by_percent_by_raising_sill(ss, red)
165
- end
166
- end
167
- end
168
- return true
169
- end
170
-
171
- # Reduces the SRR to the values specified by the PRM. SRR reduction
172
- # will be done by shrinking vertices toward the centroid.
173
- #
174
- def apply_standard_skylight_to_roof_ratio(model:, srr_set: -1.0)
175
-
176
- # If srr_set is between 1.0 and 1.2 set it to the maximum allowed by the NECB. If srr_set is between 0.0 and 1.0
177
- # apply whatever was passed. If srr_set >= 1.2 then set the existing srr of the building to be the necb maximum
178
- # only if the the srr exceeds this maximum (otherwise leave it to be whatever was modeled).
179
-
180
- # srr_set settings:
181
- # 0-1: Remove all skylights and add skylights to match this srr
182
- # -1: Remove all skylights and add skylights to match max srr from NECB
183
- # -2: Do not apply any srr changes, leave skylights alone (also works for srr > 1)
184
- # -3: Use old method which reduces existing skylight size (if necessary) to meet maximum NECB skylight limit
185
- # <-3.1: Remove all the skylights
186
- # > 1: Do nothing
187
-
188
- if srr_set.to_f > 1.0
189
- return
190
- elsif srr_set.to_f >= 0.0 && srr_set <= 1.0
191
- apply_max_srr_nrcan(model: model, srr_lim: srr_set.to_f)
192
- return
193
- elsif srr_set.to_f >= -1.1 && srr_set <= -0.9
194
- # Get the maximum NECB srr
195
- srr_lim = self.get_standards_constant('skylight_to_roof_ratio_max_value')
196
- apply_max_srr_nrcan(model: model, srr_lim: srr_lim.to_f)
197
- return
198
- elsif srr_set.to_f >= -2.1 && srr_set <= -1.9
199
- return
200
- elsif srr_set.to_f >= -3.1 && srr_set <= -2.9
201
- # Continue with the rest of this method, use old method which reduces existing skylight size (if necessary) to
202
- # meet maximum srr limit
203
- elsif srr_set < -3.1
204
- apply_max_srr_nrcan(model: model, srr_lim: srr_set.to_f)
205
- return
206
- else
207
- return
208
- end
209
-
210
- # SRR limit
211
- srr_lim = self.get_standards_constant('skylight_to_roof_ratio_max_value') * 100.0
212
-
213
- # Loop through all spaces in the model, and
214
- # per the PNNL PRM Reference Manual, find the areas
215
- # of each space conditioning category (res, nonres, semi-heated)
216
- # separately. Include space multipliers.
217
- nr_wall_m2 = 0.001 # Avoids divide by zero errors later
218
- nr_sky_m2 = 0
219
- res_wall_m2 = 0.001
220
- res_sky_m2 = 0
221
- sh_wall_m2 = 0.001
222
- sh_sky_m2 = 0
223
- total_roof_m2 = 0.001
224
- total_subsurface_m2 = 0
225
- model.getSpaces.sort.each do |space|
226
- # Loop through all surfaces in this space
227
- wall_area_m2 = 0
228
- sky_area_m2 = 0
229
- space.surfaces.sort.each do |surface|
230
- # Skip non-outdoor surfaces
231
- next unless surface.outsideBoundaryCondition == 'Outdoors'
232
- # Skip non-walls
233
- next unless surface.surfaceType == 'RoofCeiling'
234
- # This wall's gross area (including skylight area)
235
- wall_area_m2 += surface.grossArea * space.multiplier
236
- # Subsurfaces in this surface
237
- surface.subSurfaces.sort.each do |ss|
238
- sky_area_m2 += ss.netArea * space.multiplier
239
- end
240
- end
241
-
242
- # Determine the space category
243
- cat = 'NonRes'
244
- if space_residential?(space)
245
- cat = 'Res'
246
- end
247
- # if space.is_semiheated
248
- # cat = 'Semiheated'
249
- # end
250
-
251
- # Add to the correct category
252
- case cat
253
- when 'NonRes'
254
- nr_wall_m2 += wall_area_m2
255
- nr_sky_m2 += sky_area_m2
256
- when 'Res'
257
- res_wall_m2 += wall_area_m2
258
- res_sky_m2 += sky_area_m2
259
- when 'Semiheated'
260
- sh_wall_m2 += wall_area_m2
261
- sh_sky_m2 += sky_area_m2
262
- end
263
- total_roof_m2 += wall_area_m2
264
- total_subsurface_m2 += sky_area_m2
265
- end
266
-
267
- # Calculate the SRR of each category
268
- srr_nr = ((nr_sky_m2 / nr_wall_m2) * 100).round(1)
269
- srr_res = ((res_sky_m2 / res_wall_m2) * 100).round(1)
270
- srr_sh = ((sh_sky_m2 / sh_wall_m2) * 100).round(1)
271
- srr = ((total_subsurface_m2 / total_roof_m2) * 100.0).round(1)
272
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "The skylight to roof ratios (SRRs) are: NonRes: #{srr_nr.round}%, Res: #{srr_res.round}%.")
273
-
274
- # Check against SRR limit
275
- red_nr = srr_nr > srr_lim
276
- red_res = srr_res > srr_lim
277
- red_sh = srr_sh > srr_lim
278
1
 
279
- # Stop here unless windows need reducing
280
- return true unless srr > srr_lim
281
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "Reducing the size of all windows (by raising sill height) to reduce window area down to the limit of #{srr_lim.round}%.")
282
- # Determine the factors by which to reduce the window / door area
283
- mult = srr_lim / srr
284
-
285
- # Reduce the subsurface areas
286
- model.getSpaces.sort.each do |space|
287
- # Loop through all surfaces in this space
288
- space.surfaces.sort.each do |surface|
289
- # Skip non-outdoor surfaces
290
- next unless surface.outsideBoundaryCondition == 'Outdoors'
291
- # Skip non-walls
292
- next unless surface.surfaceType == 'RoofCeiling'
293
- # Subsurfaces in this surface
294
- surface.subSurfaces.sort.each do |ss|
295
- # Reduce the size of the subsurface
296
- red = 1.0 - mult
297
- sub_surface_reduce_area_by_percent_by_shrinking_toward_centroid(ss, red)
298
- end
299
- end
300
- end
301
-
302
- return true
303
- end
304
-
305
- # @author phylroy.lopez@nrcan.gc.ca
306
- # @param hdd [Float]
307
- # @return [Double] a constant float
308
- def max_fwdr(hdd)
309
- # get formula from json database.
310
- return eval(get_standards_formula('fdwr_formula'))
311
- end
2
+ class NECB2020
312
3
 
313
4
  # Go through the default construction sets and hard-assigned
314
5
  # constructions. Clone the existing constructions and set their
@@ -365,20 +56,21 @@ class NECB2020
365
56
  return false
366
57
  end
367
58
 
59
+ # hdd required to get correct conductance values from the json file.
60
+ hdd = get_necb_hdd18(model)
61
+
368
62
  # Lambdas are preferred over methods in methods for small utility methods.
369
63
  correct_cond = lambda do |conductivity, surface_type|
370
- # hdd required in scope for eval function.
371
- hdd = get_necb_hdd18(model)
372
- return conductivity.nil? || conductivity.to_f <= 0.0 || conductivity =="NECB_Default" ? eval(model_find_objects(@standards_data['surface_thermal_transmittance'], surface_type)[0]['formula']) : conductivity.to_f
64
+ return conductivity.nil? || conductivity.to_f <= 0.0 || conductivity == 'NECB_Default' ? eval(model_find_objects(@standards_data['surface_thermal_transmittance'], surface_type)[0]['formula']) : conductivity.to_f
373
65
  end
374
66
 
375
67
  # Converts trans and vis to nil if requesting default.. or casts the string to a float.
376
68
  correct_vis_trans = lambda do |value|
377
- return value.nil? || value.to_f <= 0.0 || value =="NECB_Default" ? nil : value.to_f
69
+ return value.nil? || value.to_f <= 0.0 || value == 'NECB_Default' ? nil : value.to_f
378
70
  end
379
71
 
380
72
  BTAP::Resources::Envelope::ConstructionSets.customize_default_surface_construction_set!(model: model,
381
- name: "#{default_surface_construction_set.name.get} at hdd = #{get_necb_hdd18(model)}",
73
+ name: "#{default_surface_construction_set.name.get} at hdd = #{hdd}",
382
74
  default_surface_construction_set: default_surface_construction_set,
383
75
  # ext surfaces
384
76
  ext_wall_cond: correct_cond.call(ext_wall_cond, {'boundary_condition' => 'Outdoors', 'surface' => 'Wall'}),
@@ -416,57 +108,12 @@ class NECB2020
416
108
  tubular_daylight_diffuser_solar_trans: correct_vis_trans.call(tubular_daylight_diffuser_solar_trans),
417
109
  tubular_daylight_diffuser_vis_trans: correct_vis_trans.call(tubular_daylight_diffuser_vis_trans)
418
110
  )
419
-
420
-
421
111
  end
422
112
  # sets all surfaces to use default constructions sets except adiabatic, where it does a hard assignment of the interior wall construction type.
423
113
  model.getPlanarSurfaces.sort.each(&:resetConstruction)
424
114
  # if the default construction set is defined..try to assign the interior wall to the adiabatic surfaces
425
115
  BTAP::Resources::Envelope.assign_interior_surface_construction_to_adiabatic_surfaces(model, nil)
426
116
  BTAP.runner_register('Info', ' apply_standard_construction_properties was sucessful.', runner)
427
-
428
- end
429
-
430
- # Set all external surface conductances to NECB values.
431
- # @author phylroy.lopez@nrcan.gc.ca
432
- # @param surface [String]
433
- # @param hdd [Float]
434
- # @param is_radiant [Boolian]
435
- # @param scaling_factor [Float]
436
- # @return [String] surface as RSI
437
- def set_necb_external_surface_conductance(surface, hdd, is_radiant = false, scaling_factor = 1.0)
438
- conductance_value = 0
439
-
440
- if surface.outsideBoundaryCondition.casecmp('outdoors').zero?
441
-
442
- case surface.surfaceType.downcase
443
- when 'wall'
444
- conductance_value = @standards_data['conductances']['Wall'].find { |i| i['hdd'] > hdd }['thermal_transmittance'] * scaling_factor
445
- when 'floor'
446
- conductance_value = @standards_data['conductances']['Floor'].find { |i| i['hdd'] > hdd }['thermal_transmittance'] * scaling_factor
447
- when 'roofceiling'
448
- conductance_value = @standards_data['conductances']['Roof'].find { |i| i['hdd'] > hdd }['thermal_transmittance'] * scaling_factor
449
- end
450
- if is_radiant
451
- conductance_value *= 0.80
452
- end
453
- return BTAP::Geometry::Surfaces.set_surfaces_construction_conductance([surface], conductance_value)
454
- end
455
-
456
- if surface.outsideBoundaryCondition.downcase =~ /ground/
457
- case surface.surfaceType.downcase
458
- when 'wall'
459
- conductance_value = @standards_data['conductances']['GroundWall'].find { |i| i['hdd'] > hdd }['thermal_transmittance'] * scaling_factor
460
- when 'floor'
461
- conductance_value = @standards_data['conductances']['GroundFloor'].find { |i| i['hdd'] > hdd }['thermal_transmittance'] * scaling_factor
462
- when 'roofceiling'
463
- conductance_value = @standards_data['conductances']['GroundRoof'].find { |i| i['hdd'] > hdd }['thermal_transmittance'] * scaling_factor
464
- end
465
- if is_radiant
466
- conductance_value *= 0.80
467
- end
468
- return BTAP::Geometry::Surfaces.set_surfaces_construction_conductance([surface], conductance_value)
469
- end
470
117
  end
471
118
 
472
119
  # Set all external subsurfaces (doors, windows, skylights) to NECB values.
@@ -480,300 +127,12 @@ class NECB2020
480
127
  case subsurface.subSurfaceType.downcase
481
128
  when /window/
482
129
  conductance_value = @standards_data['conductances']['Window'].find { |i| i['hdd'] > hdd }['thermal_transmittance'] * scaling_factor
130
+ when /skylight/
131
+ conductance_value = @standards_data['conductances']['Skylight'].find { |i| i['hdd'] > hdd }['thermal_transmittance'] * scaling_factor
483
132
  when /door/
484
133
  conductance_value = @standards_data['conductances']['Door'].find { |i| i['hdd'] > hdd }['thermal_transmittance'] * scaling_factor
485
134
  end
486
135
  subsurface.setRSI(1 / conductance_value)
487
136
  end
488
137
  end
489
-
490
- # Adds code-minimum constructions based on the building type
491
- # as defined in the OpenStudio_Standards_construction_sets.json file.
492
- # Where there is a separate construction set specified for the
493
- # individual space type, this construction set will be created and applied
494
- # to this space type, overriding the whole-building construction set.
495
- #
496
- # @param building_type [String] the type of building
497
- # @param climate_zone [String] the name of the climate zone the building is in
498
- # @return [Bool] returns true if successful, false if not
499
- def model_add_constructions(model)
500
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', 'Started applying constructions')
501
-
502
- # Assign construction to adiabatic construction
503
- # Assign a material to all internal mass objects
504
- assign_contruction_to_adiabatic_surfaces(model)
505
- # The constructions lookup table uses a slightly different list of
506
- # building types.
507
- apply_building_default_constructionset(model)
508
- # Make a construction set for each space type, if one is specified
509
- # apply_default_constructionsets_to_spacetypes(climate_zone, model)
510
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', 'Finished applying constructions')
511
- return true
512
- end
513
-
514
- def apply_building_default_constructionset(model)
515
-
516
- bldg_def_const_set = model_add_construction_set_from_osm(model: model)
517
- model.getBuilding.setDefaultConstructionSet(bldg_def_const_set)
518
-
519
- end
520
-
521
- def apply_default_constructionsets_to_spacetypes(climate_zone, model)
522
- model.getSpaceTypes.sort.each do |space_type|
523
- # Get the standards building type
524
- stds_building_type = nil
525
- if space_type.standardsBuildingType.is_initialized
526
- stds_building_type = space_type.standardsBuildingType.get
527
- else
528
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "Space type called '#{space_type.name}' has no standards building type.")
529
- end
530
-
531
- # Get the standards space type
532
- stds_spc_type = nil
533
- if space_type.standardsSpaceType.is_initialized
534
- stds_spc_type = space_type.standardsSpaceType.get
535
- else
536
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "Space type called '#{space_type.name}' has no standards space type.")
537
- end
538
-
539
- # If the standards space type is Attic,
540
- # the building type should be blank.
541
- if stds_spc_type == 'Attic'
542
- stds_building_type = ''
543
- end
544
-
545
- # Attempt to make a construction set for this space type
546
- # and assign it if it can be created.
547
- spc_type_const_set = model_add_construction_set_from_osm(model: model)
548
- if spc_type_const_set.is_initialized
549
- space_type.setDefaultConstructionSet(spc_type_const_set.get)
550
- end
551
- end
552
- end
553
-
554
- def model_add_construction_set_from_osm(model:,
555
- construction_set_name: 'BTAP-Mass',
556
- osm_path: File.absolute_path(File.join(__FILE__, '..', '..', 'common/construction_defaults.osm')))
557
- # load resources model
558
- construction_library = BTAP::FileIO::load_osm(osm_path)
559
-
560
- if not construction_library.getDefaultConstructionSetByName(construction_set_name.to_s).is_initialized
561
- runner.registerError('Did not find the expected construction in library.')
562
- return false
563
- end
564
- selected_construction_set = construction_library.getDefaultConstructionSetByName(construction_set_name.to_s).get
565
- new_construction_set = selected_construction_set.clone(model).to_DefaultConstructionSet.get
566
- return new_construction_set
567
- end
568
-
569
- def assign_contruction_to_adiabatic_surfaces(model)
570
- cp02_carpet_pad = OpenStudio::Model::MasslessOpaqueMaterial.new(model)
571
- cp02_carpet_pad.setName('CP02 CARPET PAD')
572
- cp02_carpet_pad.setRoughness('VeryRough')
573
- cp02_carpet_pad.setThermalResistance(0.21648)
574
- cp02_carpet_pad.setThermalAbsorptance(0.9)
575
- cp02_carpet_pad.setSolarAbsorptance(0.7)
576
- cp02_carpet_pad.setVisibleAbsorptance(0.8)
577
-
578
- normalweight_concrete_floor = OpenStudio::Model::StandardOpaqueMaterial.new(model)
579
- normalweight_concrete_floor.setName('100mm Normalweight concrete floor')
580
- normalweight_concrete_floor.setRoughness('MediumSmooth')
581
- normalweight_concrete_floor.setThickness(0.1016)
582
- normalweight_concrete_floor.setConductivity(2.31)
583
- normalweight_concrete_floor.setDensity(2322)
584
- normalweight_concrete_floor.setSpecificHeat(832)
585
-
586
- nonres_floor_insulation = OpenStudio::Model::MasslessOpaqueMaterial.new(model)
587
- nonres_floor_insulation.setName('Nonres_Floor_Insulation')
588
- nonres_floor_insulation.setRoughness('MediumSmooth')
589
- nonres_floor_insulation.setThermalResistance(2.88291975297193)
590
- nonres_floor_insulation.setThermalAbsorptance(0.9)
591
- nonres_floor_insulation.setSolarAbsorptance(0.7)
592
- nonres_floor_insulation.setVisibleAbsorptance(0.7)
593
-
594
- floor_adiabatic_construction = OpenStudio::Model::Construction.new(model)
595
- floor_adiabatic_construction.setName('Floor Adiabatic construction')
596
- floor_layers = OpenStudio::Model::MaterialVector.new
597
- floor_layers << cp02_carpet_pad
598
- floor_layers << normalweight_concrete_floor
599
- floor_layers << nonres_floor_insulation
600
- floor_adiabatic_construction.setLayers(floor_layers)
601
-
602
- g01_13mm_gypsum_board = OpenStudio::Model::StandardOpaqueMaterial.new(model)
603
- g01_13mm_gypsum_board.setName('G01 13mm gypsum board')
604
- g01_13mm_gypsum_board.setRoughness('Smooth')
605
- g01_13mm_gypsum_board.setThickness(0.0127)
606
- g01_13mm_gypsum_board.setConductivity(0.1600)
607
- g01_13mm_gypsum_board.setDensity(800)
608
- g01_13mm_gypsum_board.setSpecificHeat(1090)
609
- g01_13mm_gypsum_board.setThermalAbsorptance(0.9)
610
- g01_13mm_gypsum_board.setSolarAbsorptance(0.7)
611
- g01_13mm_gypsum_board.setVisibleAbsorptance(0.5)
612
-
613
- wall_adiabatic_construction = OpenStudio::Model::Construction.new(model)
614
- wall_adiabatic_construction.setName('Wall Adiabatic construction')
615
- wall_layers = OpenStudio::Model::MaterialVector.new
616
- wall_layers << g01_13mm_gypsum_board
617
- wall_layers << g01_13mm_gypsum_board
618
- wall_adiabatic_construction.setLayers(wall_layers)
619
-
620
- m10_200mm_concrete_block_basement_wall = OpenStudio::Model::StandardOpaqueMaterial.new(model)
621
- m10_200mm_concrete_block_basement_wall.setName('M10 200mm concrete block basement wall')
622
- m10_200mm_concrete_block_basement_wall.setRoughness('MediumRough')
623
- m10_200mm_concrete_block_basement_wall.setThickness(0.2032)
624
- m10_200mm_concrete_block_basement_wall.setConductivity(1.326)
625
- m10_200mm_concrete_block_basement_wall.setDensity(1842)
626
- m10_200mm_concrete_block_basement_wall.setSpecificHeat(912)
627
-
628
- basement_wall_construction = OpenStudio::Model::Construction.new(model)
629
- basement_wall_construction.setName('Basement Wall construction')
630
- basement_wall_layers = OpenStudio::Model::MaterialVector.new
631
- basement_wall_layers << m10_200mm_concrete_block_basement_wall
632
- basement_wall_construction.setLayers(basement_wall_layers)
633
-
634
- basement_floor_construction = OpenStudio::Model::Construction.new(model)
635
- basement_floor_construction.setName('Basement Floor construction')
636
- basement_floor_layers = OpenStudio::Model::MaterialVector.new
637
- basement_floor_layers << m10_200mm_concrete_block_basement_wall
638
- basement_floor_layers << cp02_carpet_pad
639
- basement_floor_construction.setLayers(basement_floor_layers)
640
-
641
- model.getSurfaces.sort.each do |surface|
642
- if surface.outsideBoundaryCondition.to_s == 'Adiabatic'
643
- if surface.surfaceType.to_s == 'Wall'
644
- surface.setConstruction(wall_adiabatic_construction)
645
- else
646
- surface.setConstruction(floor_adiabatic_construction)
647
- end
648
- elsif surface.outsideBoundaryCondition.to_s == 'OtherSideCoefficients'
649
- # Ground
650
- if surface.surfaceType.to_s == 'Wall'
651
- surface.setOutsideBoundaryCondition('Ground')
652
- surface.setConstruction(basement_wall_construction)
653
- else
654
- surface.setOutsideBoundaryCondition('Ground')
655
- surface.setConstruction(basement_floor_construction)
656
- end
657
- end
658
- end
659
- end
660
-
661
- def scale_model_geometry(model, x_scale, y_scale, z_scale)
662
- # Identity matrix for setting space origins
663
- m = OpenStudio::Matrix.new(4, 4, 0)
664
-
665
- m[0, 0] = 1.0 / x_scale
666
- m[1, 1] = 1.0 / y_scale
667
- m[2, 2] = 1.0 / z_scale
668
- m[3, 3] = 1.0
669
- t = OpenStudio::Transformation.new(m)
670
- model.getPlanarSurfaceGroups().each do |planar_surface|
671
- planar_surface.changeTransformation(t)
672
- end
673
- return model
674
- end
675
-
676
- # This method applies the maximum fenestration and door to wall ratio to a building as per NECB 2011 8.4.4.3 and
677
- # 3.2.1.4 (or equivalent in other versions of the NECB). It first checks for al exterior walls adjacent to conditioned
678
- # spaces. It distinguishes between plenums and other conditioned spaces. It uses both to calculate the maximum window
679
- # area to be applied to the building but attempts to put these windows only on non-plenum conditioned spaces (if
680
- # possible).
681
- def apply_max_fdwr_nrcan(model:, fdwr_lim:)
682
- # First determine which vertical (between 89 and 91 degrees from horizontal) walls are adjacent to conditioned
683
- # spaces.
684
- exp_surf_info = find_exposed_conditioned_vertical_surfaces(model)
685
- # If there are none (or very few) then throw a warning.
686
- if exp_surf_info["total_exp_wall_area_m2"] < 0.1
687
- OpenStudio.logFree(OpenStudio::Warn, 'openstudio.model.Model', "This building has no exposed walls adjacent to heated spaces.")
688
- return false
689
- end
690
-
691
- construct_set = model.getBuilding.defaultConstructionSet.get
692
- fixed_window_construct_set = construct_set.defaultExteriorSubSurfaceConstructions.get.fixedWindowConstruction.get
693
-
694
- # IF FDWR is greater than 1 then something is wrong raise an error. If it is less than 0.001 assume all the windows
695
- # should go.
696
- if fdwr_lim > 1
697
- OpenStudio.logFree(OpenStudio::Error, 'openstudio.model.Model', "This building requires a larger window area than there is wall area.")
698
- return false
699
- elsif fdwr_lim < 0.001
700
- exp_surf_info["exp_nonplenum_walls"].sort.each do |exp_surf|
701
- remove_All_Subsurfaces(surface: exp_surf)
702
- end
703
- return true
704
- end
705
- # Get the required window area.
706
- win_area = fdwr_lim * exp_surf_info["total_exp_wall_area_m2"]
707
- # Try to put the windows on non-plenum walls if possible. So determine if you can fit the required window area
708
- # on the non-plenum wall area.
709
- if win_area <= exp_surf_info["exp_nonplenum_wall_area_m2"]
710
- # If you can fit the windows on the non-plenum wall area then recalculate the window ratio so that is is only for
711
- # the non-plenum walls.
712
- nonplenum_fdwr = win_area / exp_surf_info["exp_nonplenum_wall_area_m2"]
713
- exp_surf_info["exp_nonplenum_walls"].sort.each do |exp_surf|
714
- # Remove any subsurfaces, add the window, set the name to be whatever the surface name is plus the subsurface
715
- # type (which will be 'fixedwindow')
716
- remove_All_Subsurfaces(surface: exp_surf)
717
- set_Window_To_Wall_Ratio_set_name(surface: exp_surf, area_fraction: nonplenum_fdwr, construction: fixed_window_construct_set)
718
- end
719
- else
720
- # There was not enough non-plenum wall area so add the windows to both the plenum and non-plenum walls. This is
721
- # done separately because the 'find_exposed_conditioned_vertical_surfaces' method returns the plenum and
722
- # non-plenum walls separately.
723
- exp_surf_info["exp_nonplenum_walls"].sort.each do |exp_surf|
724
- # Remove any subsurfaces, add the window, set the name to be whatever the surface name is plus the subsurface
725
- # type (which will be 'fixedwindow')
726
- remove_All_Subsurfaces(surface: exp_surf)
727
- set_Window_To_Wall_Ratio_set_name(surface: exp_surf, area_fraction: fdwr_lim, construction: fixed_window_construct_set)
728
- end
729
- exp_surf_info["exp_plenum_walls"].sort.each do |exp_surf|
730
- # Remove any subsurfaces, add the window, set the name to be whatever the surface name is plus the subsurface
731
- # type (which will be 'fixedwindow')
732
- remove_All_Subsurfaces(surface: exp_surf)
733
- set_Window_To_Wall_Ratio_set_name(surface: exp_surf, area_fraction: fdwr_lim, construction: fixed_window_construct_set)
734
- end
735
- end
736
- return true
737
- end
738
-
739
- # This method is similar to the 'apply_max_fdwr' method above but applies the maximum skylight to roof area ratio to a
740
- # building as per NECB 2011 8.4.4.3 and 3.2.1.4 (or equivalent in other versions of the NECB). It first checks for all
741
- # exterior roofs adjacent to conditioned spaces. It distinguishes between plenums and other conditioned spaces. It
742
- # uses only the non-plenum roof area to calculate the maximum skylight area to be applied to the building.
743
- def apply_max_srr_nrcan(model:, srr_lim:)
744
- # First determine which roof surfaces are adjacent to heated spaces (both plenum and non-plenum).
745
- exp_surf_info = find_exposed_conditioned_roof_surfaces(model)
746
- # If the non-plenum roof area is very small raise a warning. It may be perfectly fine but it is probably a good
747
- # idea to warn the user.
748
- if exp_surf_info["exp_nonplenum_roof_area_m2"] < 0.1
749
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "This building has no exposed ceilings adjacent to spaces that are not attics or plenums. No skylights will be added.")
750
- return false
751
- end
752
-
753
- # If the SRR is greater than one something is seriously wrong so raise an error. If it is less than 0.001 assume
754
- # all the skylights should go.
755
- if srr_lim > 1
756
- OpenStudio.logFree(OpenStudio::Error, 'openstudio.model.Model', "This building requires a larger skylight area than there is roof area.")
757
- return false
758
- elsif srr_lim < 0.001
759
- exp_surf_info["exp_nonplenum_roofs"].sort.each do |exp_surf|
760
- remove_All_Subsurfaces(surface: exp_surf)
761
- end
762
- return true
763
- end
764
-
765
- construct_set = model.getBuilding.defaultConstructionSet.get
766
- skylight_construct_set = construct_set.defaultExteriorSubSurfaceConstructions.get.skylightConstruction.get
767
-
768
- # Go through all of exposed roofs adjacent to heated, non-plenum spaces, remove any existing subsurfaces, and add
769
- # a skylight in the centroid of the surface, with the same shape of the surface, only scaled to be the area
770
- # determined by the SRR. The name of the skylight will be the surface name with the subsurface type attached
771
- # ('skylight' in this case). Note that this method will only work if the surface does not fold into itself (like an
772
- # L or a V).
773
- exp_surf_info["exp_nonplenum_roofs"].sort.each do |roof|
774
- # sub_surface_create_centered_subsurface_from_scaled_surface(roof, srr_lim, model)
775
- sub_surface_create_scaled_subsurfaces_from_surface(surface: roof, area_fraction: srr_lim, model: model, consturction: skylight_construct_set)
776
- end
777
- return true
778
- end
779
- end
138
+ end