honeybee-openstudio 2.31.0 → 2.31.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e468a9bfb50550224bd130a49e06f492331f0d0770e7099e884454628da250f5
4
- data.tar.gz: a7c066b0cd26e9193f3fbadc9687ae59f74a96dd24a3ecbb216fb64fc682509d
3
+ metadata.gz: f9b620209ce330fea574204c6257db0a2e22f6e898de0d0b27a7d20360fd5296
4
+ data.tar.gz: 3f294beea759ed0789cd3928d023edbd4f97697c2179cb77d5e7be971d4ed0a7
5
5
  SHA512:
6
- metadata.gz: 7c02241cca2adcbce09295d25b9ddf9bf9aa684b0f08426924f2bc1564b84143fd4032dcdbcb24a9efe214dbfca16f5bfa424ddbe1d4b02ba5a1d769265028f5
7
- data.tar.gz: 28f59d45b148e6a7be923e82834d796df8da4290ecebbb14371e756eca43e0ccb21f98512a59ac8eee53f7c19637a39abd2f9b9c0106e9bad07f13b05249e537
6
+ metadata.gz: dffa3a850ebe2f9b0bc03e67cba8329d1a8d7b4a4c7081cf0a751e3e423795dfb7b90028975efa92cf1d78405117b3dacd3d68e6f1e513ba93e772914ebe8475
7
+ data.tar.gz: 1b244c65a2df322052c5d522564199a0ac0e49b7dc1aadb7d6c297c24e4ed48d26d19305e3ec49caee382939ea96b7ea3f54aff3d4b02b1127d904e394b1560f
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = 'honeybee-openstudio'
7
- spec.version = '2.31.0'
7
+ spec.version = '2.31.1'
8
8
  spec.authors = ['Tanushree Charan', 'Dan Macumber', 'Chris Mackey', 'Mostapha Sadeghipour Roudsari']
9
9
  spec.email = ['tanushree.charan@nrel.gov', 'chris@ladybug.tools']
10
10
 
@@ -3,7 +3,7 @@
3
3
  "servers": [],
4
4
  "info": {
5
5
  "description": "Honeybee model schema.",
6
- "version": "1.48.2",
6
+ "version": "1.49.0",
7
7
  "title": "Honeybee Model Schema",
8
8
  "contact": {
9
9
  "name": "Ladybug Tools",
@@ -3531,10 +3531,7 @@
3531
3531
  },
3532
3532
  "story": {
3533
3533
  "title": "Story",
3534
- "description": "Text string for the story identifier to which this Room belongs. Rooms sharing the same story identifier are considered part of the same story in a Model.",
3535
- "maxLength": 100,
3536
- "minLength": 1,
3537
- "pattern": "[.A-Za-z0-9_-]",
3534
+ "description": "Text string for the story identifier to which this Room belongs. Rooms sharing the same story identifier are considered part of the same story in a Model. Note that this property has no character restrictions.",
3538
3535
  "type": "string"
3539
3536
  }
3540
3537
  },
@@ -8251,7 +8248,8 @@
8251
8248
  "enum": [
8252
8249
  "Floor",
8253
8250
  "Ceiling",
8254
- "FloorWithCarpet"
8251
+ "FloorWithCarpet",
8252
+ "CeilingMetalPanel"
8255
8253
  ],
8256
8254
  "type": "string"
8257
8255
  },
@@ -8334,14 +8332,14 @@
8334
8332
  }
8335
8333
  ]
8336
8334
  },
8337
- "proportional_gain": {
8338
- "title": "Proportional Gain",
8339
- "description": "A fractional number for the proportional gain constant. Recommended values are 0.3 or less.",
8340
- "default": 0.3,
8341
- "minimum": 0,
8342
- "maximum": 1,
8343
- "type": "number",
8344
- "format": "double"
8335
+ "radiant_face_type": {
8336
+ "description": "Text to indicate which faces are thermally active by default. Note that this property has no effect when the rooms to which the HVAC system is assigned have constructions with internal source materials. In this case, those constructions will dictate the thermally active surfaces.",
8337
+ "default": "Floor",
8338
+ "allOf": [
8339
+ {
8340
+ "$ref": "#/components/schemas/RadiantFaceTypes"
8341
+ }
8342
+ ]
8345
8343
  },
8346
8344
  "minimum_operation_time": {
8347
8345
  "title": "Minimum Operation Time",
@@ -8358,15 +8356,6 @@
8358
8356
  "exclusiveMinimum": 0,
8359
8357
  "type": "number",
8360
8358
  "format": "double"
8361
- },
8362
- "radiant_face_type": {
8363
- "description": "Text to indicate which faces are thermally active by default. Note that this property has no effect when the rooms to which the HVAC system is assigned have constructions with internal source materials. In this case, those constructions will dictate the thermally active surfaces.",
8364
- "default": "Floor",
8365
- "allOf": [
8366
- {
8367
- "$ref": "#/components/schemas/RadiantFaceTypes"
8368
- }
8369
- ]
8370
8359
  }
8371
8360
  },
8372
8361
  "required": [
@@ -8977,14 +8966,14 @@
8977
8966
  }
8978
8967
  ]
8979
8968
  },
8980
- "proportional_gain": {
8981
- "title": "Proportional Gain",
8982
- "description": "A fractional number for the proportional gain constant. Recommended values are 0.3 or less.",
8983
- "default": 0.3,
8984
- "minimum": 0,
8985
- "maximum": 1,
8986
- "type": "number",
8987
- "format": "double"
8969
+ "radiant_face_type": {
8970
+ "description": "Text to indicate which faces are thermally active by default. Note that this property has no effect when the rooms to which the HVAC system is assigned have constructions with internal source materials. In this case, those constructions will dictate the thermally active surfaces.",
8971
+ "default": "Floor",
8972
+ "allOf": [
8973
+ {
8974
+ "$ref": "#/components/schemas/RadiantFaceTypes"
8975
+ }
8976
+ ]
8988
8977
  },
8989
8978
  "minimum_operation_time": {
8990
8979
  "title": "Minimum Operation Time",
@@ -9001,15 +8990,6 @@
9001
8990
  "exclusiveMinimum": 0,
9002
8991
  "type": "number",
9003
8992
  "format": "double"
9004
- },
9005
- "radiant_face_type": {
9006
- "description": "Text to indicate which faces are thermally active by default. Note that this property has no effect when the rooms to which the HVAC system is assigned have constructions with internal source materials. In this case, those constructions will dictate the thermally active surfaces.",
9007
- "default": "Floor",
9008
- "allOf": [
9009
- {
9010
- "$ref": "#/components/schemas/RadiantFaceTypes"
9011
- }
9012
- ]
9013
8993
  }
9014
8994
  },
9015
8995
  "required": [
@@ -14118,7 +14098,7 @@
14118
14098
  "version": {
14119
14099
  "title": "Version",
14120
14100
  "description": "Text string for the current version of the schema.",
14121
- "default": "1.48.2",
14101
+ "default": "1.49.0",
14122
14102
  "pattern": "([0-9]+)\\.([0-9]+)\\.([0-9]+)",
14123
14103
  "type": "string",
14124
14104
  "readOnly": true
@@ -54,20 +54,28 @@ class OpenStudio::Model::Model
54
54
  case climate_zone
55
55
  when '0', '1'
56
56
  radiant_htg_dsgn_sup_wtr_temp_f = 90.0
57
+ cz_mult = 2
57
58
  when '2', '2A', '2B'
58
59
  radiant_htg_dsgn_sup_wtr_temp_f = 100.0
60
+ cz_mult = 2
59
61
  when '3', '3A', '3B', '3C'
60
62
  radiant_htg_dsgn_sup_wtr_temp_f = 100.0
63
+ cz_mult = 3
61
64
  when '4', '4A', '4B', '4C'
62
65
  radiant_htg_dsgn_sup_wtr_temp_f = 100.0
66
+ cz_mult = 4
63
67
  when '5', '5A', '5B', '5C'
64
68
  radiant_htg_dsgn_sup_wtr_temp_f = 110.0
69
+ cz_mult = 4
65
70
  when '6', '6A', '6B'
66
71
  radiant_htg_dsgn_sup_wtr_temp_f = 120.0
72
+ cz_mult = 4
67
73
  when '7', '8'
68
74
  radiant_htg_dsgn_sup_wtr_temp_f = 120.0
75
+ cz_mult = 5
69
76
  else # unrecognized climate zone; default to climate zone 4
70
77
  radiant_htg_dsgn_sup_wtr_temp_f = 100.0
78
+ cz_mult = 4
71
79
  end
72
80
 
73
81
  # create the hot water loop
@@ -120,11 +128,6 @@ class OpenStudio::Model::Model
120
128
  end
121
129
 
122
130
  # get the various controls for the radiant system
123
- if rad_props[:proportional_gain]
124
- proportional_gain = rad_props[:proportional_gain]
125
- else
126
- proportional_gain = 0.3
127
- end
128
131
  if rad_props[:minimum_operation_time]
129
132
  minimum_operation = rad_props[:minimum_operation_time]
130
133
  else
@@ -136,24 +139,34 @@ class OpenStudio::Model::Model
136
139
  switch_over_time = 24
137
140
  end
138
141
 
142
+ # get the start and end hour from the input zones
143
+ start_hour, end_hour = start_end_hour_from_zones_occupancy(zones)
144
+
139
145
  # add radiant system to the conditioned zones
140
146
  include_carpet = false
141
- if rad_props[:radiant_face_type]
142
- radiant_type = rad_props[:radiant_face_type].downcase
147
+ control_strategy = 'proportional_control'
148
+ if rad_props[:radiant_type]
149
+ radiant_type = rad_props[:radiant_type].downcase
143
150
  if radiant_type == 'floorwithcarpet'
144
151
  radiant_type = 'floor'
145
152
  include_carpet = true
153
+ elsif radiant_type == 'ceilingmetalpanel'
154
+ control_strategy = 'default'
146
155
  end
147
156
  else
148
157
  radiant_type = 'floor'
149
158
  end
150
- radiant_loops = std.model_add_low_temp_radiant(
151
- self, conditioned_zones, hot_water_loop, chilled_water_loop,
159
+
160
+ radiant_loops = model_add_low_temp_radiant(
161
+ std, conditioned_zones, hot_water_loop, chilled_water_loop,
152
162
  radiant_type: radiant_type,
163
+ control_strategy: control_strategy,
153
164
  include_carpet: include_carpet,
154
- proportional_gain: proportional_gain,
165
+ model_occ_hr_start: start_hour,
166
+ model_occ_hr_end: end_hour,
155
167
  minimum_operation: minimum_operation,
156
- switch_over_time: switch_over_time)
168
+ switch_over_time: switch_over_time,
169
+ cz_mult: cz_mult)
157
170
 
158
171
  # if the equipment includes a DOAS, then add it
159
172
  if system_type.include? 'DOAS_'
@@ -161,4 +174,365 @@ class OpenStudio::Model::Model
161
174
  end
162
175
 
163
176
  end
177
+
178
+ # get the start and end hour from the occupancy schedules of thermal zones
179
+ def start_end_hour_from_zones_occupancy(thermal_zones, threshold: 0.1)
180
+ # set the default start and end hour in the event there's no occupancy
181
+ start_hour = 12
182
+ end_hour = 12
183
+ # loop through the occupancy schedules and get the lowest start hour; highest end hour
184
+ thermal_zones.each do |zone|
185
+ zone.spaces.each do |space|
186
+ # gather all of the people objects assigned to the sapce
187
+ peoples = []
188
+ unless space.spaceType.empty?
189
+ space_type = space.spaceType.get
190
+ unless space_type.people.empty?
191
+ space_type.people.each do |ppl|
192
+ peoples << ppl
193
+ end
194
+ end
195
+ end
196
+ space.people.each do |ppl|
197
+ peoples << ppl
198
+ end
199
+ # loop through the pople and gather all occupancy schedules
200
+ peoples.each do |people|
201
+ occupancy_sch_opt = people.numberofPeopleSchedule
202
+ unless occupancy_sch_opt.empty?
203
+ occupancy_sch = occupancy_sch_opt.get
204
+ if occupancy_sch.to_ScheduleRuleset.is_initialized
205
+ occupancy_sch = occupancy_sch.to_ScheduleRuleset.get
206
+ # gather all of the day schedules across the schedule ruleset
207
+ schedule_days, day_ids = [], []
208
+ required_days = [
209
+ occupancy_sch.defaultDaySchedule,
210
+ occupancy_sch.summerDesignDaySchedule,
211
+ occupancy_sch.winterDesignDaySchedule,
212
+ occupancy_sch.holidaySchedule
213
+ ]
214
+ required_days.each do |day_sch|
215
+ unless day_ids.include? day_sch.nameString
216
+ schedule_days << day_sch
217
+ day_ids << day_sch.nameString
218
+ end
219
+ end
220
+ occupancy_sch.scheduleRules.each do |schedule_rule|
221
+ day_sch = schedule_rule.daySchedule
222
+ unless day_ids.include? day_sch.nameString
223
+ schedule_days << day_sch
224
+ day_ids << day_sch.nameString
225
+ end
226
+ end
227
+ # loop through the day schedules and see if the start and end hours should be changed
228
+ schedule_days.each do |day_sch|
229
+ time_until = [1]
230
+ day_sch.times.each do |time|
231
+ time_until << time.hours
232
+ end
233
+ final_time = time_until[-2]
234
+ day_sch.values.zip(time_until).each do |value, time|
235
+ if value > threshold
236
+ if time < start_hour
237
+ start_hour = time
238
+ end
239
+ if time > end_hour
240
+ end_hour = time
241
+ end
242
+ if time == final_time
243
+ start_hour = 1
244
+ end_hour = 24
245
+ end
246
+ end
247
+ end
248
+ end
249
+ end
250
+ end
251
+ end
252
+ end
253
+ end
254
+ # if no values were set, just set the system to be on all of the time
255
+ if start_hour == 12 or start_hour == 0
256
+ start_hour = 1
257
+ end
258
+ if end_hour == 12
259
+ end_hour = 24
260
+ end
261
+ return start_hour, end_hour
262
+ end
263
+
264
+ def model_add_low_temp_radiant(std,
265
+ thermal_zones,
266
+ hot_water_loop,
267
+ chilled_water_loop,
268
+ radiant_type: 'floor',
269
+ include_carpet: true,
270
+ carpet_thickness_in: 0.25,
271
+ model_occ_hr_start: 1.0,
272
+ model_occ_hr_end: 24.0,
273
+ control_strategy: 'proportional_control',
274
+ proportional_gain: 0.3,
275
+ minimum_operation: 1,
276
+ weekend_temperature_reset: 2,
277
+ early_reset_out_arg: 20,
278
+ switch_over_time: 24.0,
279
+ cz_mult: 4)
280
+
281
+ # create internal source constructions for surfaces
282
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.Model.Model', "Replacing constructions with new radiant slab constructions.")
283
+
284
+ # create materials
285
+ mat_concrete_3_5in = OpenStudio::Model::StandardOpaqueMaterial.new(self, 'MediumRough', 0.0889, 2.31, 2322, 832)
286
+ mat_concrete_3_5in.setName('Radiant Slab Concrete - 3.5 in.')
287
+ mat_concrete_1_5in = OpenStudio::Model::StandardOpaqueMaterial.new(self, 'MediumRough', 0.0381, 2.31, 2322, 832)
288
+ mat_concrete_1_5in.setName('Radiant Slab Concrete - 1.5 in')
289
+
290
+ metal_mat = OpenStudio::Model::StandardOpaqueMaterial.new(self, 'MediumSmooth', 0.003175, 30, 7680, 418)
291
+ metal_mat.setName('Radiant Metal Layer - 0.125 in')
292
+ air_gap_map = OpenStudio::Model::MasslessOpaqueMaterial.new(self, 'Smooth', 0.004572)
293
+ air_gap_map.setName('Generic Ceiling Air Gap - R 0.025')
294
+
295
+ mat_refl_roof_membrane = self.getStandardOpaqueMaterialByName('Roof Membrane - Highly Reflective')
296
+ if mat_refl_roof_membrane.is_initialized
297
+ mat_refl_roof_membrane = self.getStandardOpaqueMaterialByName('Roof Membrane - Highly Reflective').get
298
+ else
299
+ mat_refl_roof_membrane = OpenStudio::Model::StandardOpaqueMaterial.new(self, 'VeryRough', 0.0095, 0.16, 1121.29, 1460)
300
+ mat_refl_roof_membrane.setThermalAbsorptance(0.75)
301
+ mat_refl_roof_membrane.setSolarAbsorptance(0.45)
302
+ mat_refl_roof_membrane.setVisibleAbsorptance(0.7)
303
+ mat_refl_roof_membrane.setName('Roof Membrane - Highly Reflective')
304
+ end
305
+
306
+ if include_carpet
307
+ carpet_thickness_m = OpenStudio.convert(carpet_thickness_in / 12.0, 'ft', 'm').get
308
+ conductivity_si = 0.06
309
+ conductivity_ip = OpenStudio.convert(conductivity_si, 'W/m*K', 'Btu*in/hr*ft^2*R').get
310
+ r_value_ip = carpet_thickness_in * (1 / conductivity_ip)
311
+ mat_thin_carpet_tile = OpenStudio::Model::StandardOpaqueMaterial.new(self, 'MediumRough', carpet_thickness_m, conductivity_si, 288, 1380)
312
+ mat_thin_carpet_tile.setThermalAbsorptance(0.9)
313
+ mat_thin_carpet_tile.setSolarAbsorptance(0.7)
314
+ mat_thin_carpet_tile.setVisibleAbsorptance(0.8)
315
+ mat_thin_carpet_tile.setName("Radiant Slab Thin Carpet Tile R-#{r_value_ip.round(2)}")
316
+ end
317
+
318
+ # set exterior slab insulation thickness based on climate zone
319
+ slab_insulation_thickness_m = 0.0254 * cz_mult
320
+ mat_slab_insulation = OpenStudio::Model::StandardOpaqueMaterial.new(self, 'Rough', slab_insulation_thickness_m, 0.02, 56.06, 1210)
321
+ mat_slab_insulation.setName("Radiant Ground Slab Insulation - #{cz_mult} in.")
322
+
323
+ ext_insulation_thickness_m = 0.0254 * (cz_mult + 1)
324
+ mat_ext_insulation = OpenStudio::Model::StandardOpaqueMaterial.new(self, 'Rough', ext_insulation_thickness_m, 0.02, 56.06, 1210)
325
+ mat_ext_insulation.setName("Radiant Exterior Slab Insulation - #{cz_mult + 1} in.")
326
+
327
+ roof_insulation_thickness_m = 0.0254 * (cz_mult + 1) * 2
328
+ mat_roof_insulation = OpenStudio::Model::StandardOpaqueMaterial.new(self, 'Rough', roof_insulation_thickness_m, 0.02, 56.06, 1210)
329
+ mat_roof_insulation.setName("Radiant Exterior Ceiling Insulation - #{(cz_mult + 1) * 2} in.")
330
+
331
+ # create radiant internal source constructions
332
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.Model.Model', 'New constructions exclude the metal deck, as high thermal diffusivity materials cause errors in EnergyPlus internal source construction calculations.')
333
+
334
+ layers = []
335
+ layers << mat_slab_insulation
336
+ layers << mat_concrete_3_5in
337
+ layers << mat_concrete_1_5in
338
+ layers << mat_thin_carpet_tile if include_carpet
339
+ radiant_ground_slab_construction = OpenStudio::Model::ConstructionWithInternalSource.new(layers)
340
+ radiant_ground_slab_construction.setName('Radiant Ground Slab Construction')
341
+ radiant_ground_slab_construction.setSourcePresentAfterLayerNumber(2)
342
+ radiant_ground_slab_construction.setTemperatureCalculationRequestedAfterLayerNumber(3)
343
+ radiant_ground_slab_construction.setTubeSpacing(0.2286) # 9 inches
344
+
345
+ layers = []
346
+ layers << mat_ext_insulation
347
+ layers << mat_concrete_3_5in
348
+ layers << mat_concrete_1_5in
349
+ layers << mat_thin_carpet_tile if include_carpet
350
+ radiant_exterior_slab_construction = OpenStudio::Model::ConstructionWithInternalSource.new(layers)
351
+ radiant_exterior_slab_construction.setName('Radiant Exterior Slab Construction')
352
+ radiant_exterior_slab_construction.setSourcePresentAfterLayerNumber(2)
353
+ radiant_exterior_slab_construction.setTemperatureCalculationRequestedAfterLayerNumber(3)
354
+ radiant_exterior_slab_construction.setTubeSpacing(0.2286) # 9 inches
355
+
356
+ layers = []
357
+ layers << mat_concrete_3_5in
358
+ layers << mat_concrete_1_5in
359
+ layers << mat_thin_carpet_tile if include_carpet
360
+ radiant_interior_floor_slab_construction = OpenStudio::Model::ConstructionWithInternalSource.new(layers)
361
+ radiant_interior_floor_slab_construction.setName('Radiant Interior Floor Slab Construction')
362
+ radiant_interior_floor_slab_construction.setSourcePresentAfterLayerNumber(1)
363
+ radiant_interior_floor_slab_construction.setTemperatureCalculationRequestedAfterLayerNumber(2)
364
+ radiant_interior_floor_slab_construction.setTubeSpacing(0.2286) # 9 inches
365
+
366
+ layers = []
367
+ layers << mat_thin_carpet_tile if include_carpet
368
+ layers << mat_concrete_3_5in
369
+ layers << mat_concrete_1_5in
370
+ radiant_interior_ceiling_slab_construction = OpenStudio::Model::ConstructionWithInternalSource.new(layers)
371
+ radiant_interior_ceiling_slab_construction.setName('Radiant Interior Ceiling Slab Construction')
372
+ slab_src_loc = include_carpet ? 2 : 1
373
+ radiant_interior_ceiling_slab_construction.setSourcePresentAfterLayerNumber(slab_src_loc)
374
+ radiant_interior_ceiling_slab_construction.setTemperatureCalculationRequestedAfterLayerNumber(slab_src_loc + 1)
375
+ radiant_interior_ceiling_slab_construction.setTubeSpacing(0.2286) # 9 inches
376
+
377
+ layers = []
378
+ layers << mat_refl_roof_membrane
379
+ layers << mat_roof_insulation
380
+ layers << mat_concrete_3_5in
381
+ layers << mat_concrete_1_5in
382
+ radiant_ceiling_slab_construction = OpenStudio::Model::ConstructionWithInternalSource.new(layers)
383
+ radiant_ceiling_slab_construction.setName('Radiant Exterior Ceiling Slab Construction')
384
+ radiant_ceiling_slab_construction.setSourcePresentAfterLayerNumber(3)
385
+ radiant_ceiling_slab_construction.setTemperatureCalculationRequestedAfterLayerNumber(4)
386
+ radiant_ceiling_slab_construction.setTubeSpacing(0.2286) # 9 inches
387
+
388
+ layers = []
389
+ layers << mat_concrete_3_5in
390
+ layers << air_gap_map
391
+ layers << metal_mat
392
+ layers << metal_mat
393
+ radiant_interior_ceiling_metal_construction = OpenStudio::Model::ConstructionWithInternalSource.new(layers)
394
+ radiant_interior_ceiling_metal_construction.setName('Radiant Interior Ceiling Metal Construction')
395
+ radiant_interior_ceiling_metal_construction.setSourcePresentAfterLayerNumber(3)
396
+ radiant_interior_ceiling_metal_construction.setTemperatureCalculationRequestedAfterLayerNumber(4)
397
+ radiant_interior_ceiling_metal_construction.setTubeSpacing(0.1524) # 6 inches
398
+
399
+ layers = []
400
+ layers << mat_refl_roof_membrane
401
+ layers << mat_roof_insulation
402
+ layers << mat_concrete_3_5in
403
+ layers << air_gap_map
404
+ layers << metal_mat
405
+ layers << metal_mat
406
+ radiant_ceiling_metal_construction = OpenStudio::Model::ConstructionWithInternalSource.new(layers)
407
+ radiant_ceiling_metal_construction.setName('Radiant Ceiling Metal Construction')
408
+ radiant_ceiling_metal_construction.setSourcePresentAfterLayerNumber(5)
409
+ radiant_ceiling_metal_construction.setTemperatureCalculationRequestedAfterLayerNumber(6)
410
+ radiant_ceiling_metal_construction.setTubeSpacing(0.1524) # 6 inches
411
+
412
+ # default temperature controls for radiant system
413
+ zn_radiant_htg_dsgn_temp_f = 68.0
414
+ zn_radiant_htg_dsgn_temp_c = OpenStudio.convert(zn_radiant_htg_dsgn_temp_f, 'F', 'C').get
415
+ zn_radiant_clg_dsgn_temp_f = 74.0
416
+ zn_radiant_clg_dsgn_temp_c = OpenStudio.convert(zn_radiant_clg_dsgn_temp_f, 'F', 'C').get
417
+
418
+ htg_control_temp_sch = std.model_add_constant_schedule_ruleset(
419
+ self,
420
+ zn_radiant_htg_dsgn_temp_c,
421
+ name = "Zone Radiant Loop Heating Threshold Temperature Schedule - #{zn_radiant_htg_dsgn_temp_f.round(0)}F")
422
+ clg_control_temp_sch = std.model_add_constant_schedule_ruleset(
423
+ self,
424
+ zn_radiant_clg_dsgn_temp_c,
425
+ name = "Zone Radiant Loop Cooling Threshold Temperature Schedule - #{zn_radiant_clg_dsgn_temp_f.round(0)}F")
426
+ throttling_range_f = 4.0 # 2 degF on either side of control temperature
427
+ throttling_range_c = OpenStudio.convert(throttling_range_f, 'F', 'C').get
428
+
429
+ # make a low temperature radiant loop for each zone
430
+ radiant_loops = []
431
+ thermal_zones.each do |zone|
432
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.Model.Model', "Adding radiant loop for #{zone.name}.")
433
+ if zone.name.to_s.include? ':'
434
+ OpenStudio.logFree(OpenStudio::Error, 'openstudio.Model.Model', "Thermal zone '#{zone.name}' has a restricted character ':' in the name and will not work with some EMS and output reporting objects. Please rename the zone.")
435
+ end
436
+
437
+ # create radiant coils
438
+ if hot_water_loop
439
+ radiant_loop_htg_coil = OpenStudio::Model::CoilHeatingLowTempRadiantVarFlow.new(self, htg_control_temp_sch)
440
+ radiant_loop_htg_coil.setName("#{zone.name} Radiant Loop Heating Coil")
441
+ radiant_loop_htg_coil.setHeatingControlThrottlingRange(throttling_range_c)
442
+ hot_water_loop.addDemandBranchForComponent(radiant_loop_htg_coil)
443
+ else
444
+ OpenStudio.logFree(OpenStudio::Error, 'openstudio.Model.Model', 'Radiant loops require a hot water loop, but none was provided.')
445
+ end
446
+
447
+ if chilled_water_loop
448
+ radiant_loop_clg_coil = OpenStudio::Model::CoilCoolingLowTempRadiantVarFlow.new(self, clg_control_temp_sch)
449
+ radiant_loop_clg_coil.setName("#{zone.name} Radiant Loop Cooling Coil")
450
+ radiant_loop_clg_coil.setCoolingControlThrottlingRange(throttling_range_c)
451
+ chilled_water_loop.addDemandBranchForComponent(radiant_loop_clg_coil)
452
+ else
453
+ OpenStudio.logFree(OpenStudio::Error, 'openstudio.Model.Model', 'Radiant loops require a chilled water loop, but none was provided.')
454
+ end
455
+
456
+ radiant_avail_sch = self.alwaysOnDiscreteSchedule
457
+ radiant_loop = OpenStudio::Model::ZoneHVACLowTempRadiantVarFlow.new(self,
458
+ radiant_avail_sch,
459
+ radiant_loop_htg_coil,
460
+ radiant_loop_clg_coil)
461
+
462
+ # assign internal source construction to floors in zone
463
+ zone.spaces.each do |space|
464
+ space.surfaces.each do |surface|
465
+ if radiant_type == 'floor'
466
+ if surface.surfaceType == 'Floor'
467
+ if surface.outsideBoundaryCondition == 'Ground'
468
+ surface.setConstruction(radiant_ground_slab_construction)
469
+ elsif surface.outsideBoundaryCondition == 'Outdoors'
470
+ surface.setConstruction(radiant_exterior_slab_construction)
471
+ else # interior floor
472
+ surface.setConstruction(radiant_interior_floor_slab_construction)
473
+ end
474
+ end
475
+ elsif radiant_type == 'ceiling'
476
+ if surface.surfaceType == 'RoofCeiling'
477
+ if surface.outsideBoundaryCondition == 'Outdoors'
478
+ surface.setConstruction(radiant_ceiling_slab_construction)
479
+ else # interior ceiling
480
+ surface.setConstruction(radiant_interior_ceiling_slab_construction)
481
+ end
482
+ end
483
+ elsif radiant_type == 'ceilingmetalpanel'
484
+ if surface.surfaceType == 'RoofCeiling'
485
+ if surface.outsideBoundaryCondition == 'Outdoors'
486
+ surface.setConstruction(radiant_ceiling_metal_construction)
487
+ else # interior ceiling
488
+ surface.setConstruction(radiant_interior_ceiling_metal_construction)
489
+ end
490
+ end
491
+ end
492
+ end
493
+ end
494
+
495
+ # radiant loop surfaces
496
+ radiant_loop.setName("#{zone.name} Radiant Loop")
497
+ if radiant_type == 'floor'
498
+ radiant_loop.setRadiantSurfaceType('Floors')
499
+ elsif radiant_type == 'ceiling'
500
+ radiant_loop.setRadiantSurfaceType('Ceilings')
501
+ elsif radiant_type == 'ceilingmetalpanel'
502
+ radiant_loop.setRadiantSurfaceType('Ceilings')
503
+ end
504
+
505
+ # radiant loop layout details
506
+ radiant_loop.setHydronicTubingInsideDiameter(0.015875) # 5/8 in. ID, 3/4 in. OD
507
+ # @todo include a method to determine tubing length in the zone
508
+ # loop_length = 7*zone.floorArea
509
+ # radiant_loop.setHydronicTubingLength()
510
+ radiant_loop.setNumberofCircuits('CalculateFromCircuitLength')
511
+ radiant_loop.setCircuitLength(106.7)
512
+
513
+ # radiant loop controls
514
+ radiant_loop.setTemperatureControlType('MeanAirTemperature')
515
+ radiant_loop.addToThermalZone(zone)
516
+ radiant_loops << radiant_loop
517
+
518
+ # rename nodes before adding EMS code
519
+ std.rename_plant_loop_nodes(self)
520
+
521
+ # set radiant loop controls
522
+ if control_strategy == 'proportional_control'
523
+ std.model_add_radiant_proportional_controls(self, zone, radiant_loop,
524
+ radiant_type: radiant_type,
525
+ model_occ_hr_start: model_occ_hr_start,
526
+ model_occ_hr_end: model_occ_hr_end,
527
+ proportional_gain: proportional_gain,
528
+ minimum_operation: minimum_operation,
529
+ weekend_temperature_reset: weekend_temperature_reset,
530
+ early_reset_out_arg: early_reset_out_arg,
531
+ switch_over_time: switch_over_time)
532
+ end
533
+ end
534
+
535
+ return radiant_loops
536
+ end
537
+
164
538
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: honeybee-openstudio
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.31.0
4
+ version: 2.31.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tanushree Charan
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: exe
13
13
  cert_chain: []
14
- date: 2022-03-14 00:00:00.000000000 Z
14
+ date: 2022-03-27 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: bundler