honeybee-openstudio 2.30.7 → 2.31.2

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: dbe2c2581aae65bb58d4fc5b13efc1093de9302e9f41a8b21a8b3e35ad909d0a
4
- data.tar.gz: 1a08ed0e4ca2dccf145d961287b9ea2c60ea67034877957482a7c49318203a34
3
+ metadata.gz: ddf8c7c7b9acf4094ea3a7702932246a6423fab131d5a2586b02421a81d24c16
4
+ data.tar.gz: a92d03dc75bd2c562ef5bf63cf6c4f69ecf515cbe36ca3df8f4ac36abf31b92a
5
5
  SHA512:
6
- metadata.gz: a534759f8a48c410354c6c53e131e21fe0c9ef17824a761d6f338bdcc07f7007f1cc79c16406492782e9d580a0eaa8bfdf5bb5e89924157d2f08fc80d62bad12
7
- data.tar.gz: a51dcad87234a081e63575fde99ea0a76a02a7a8d497a3d0a0e977af5ca5713b80db65b966489462f441ebc28575adc89082a86845cb5471d195f146da9c2fc4
6
+ metadata.gz: d0fe77822120f793df5d5ed31c6d91a8ba601a9af26f6a84f21e1a618a4ef97b78b2477fc26ae99bc46894e3df708725c1c2d76497df3601f9e7c01e1920f6c1
7
+ data.tar.gz: a1853e96f594d0329126e5529f87fc3694b1d8a044bb995e38831ddbd72d89af94e81a065f0dc7909be5b5b764e998dcdebfd3bcf3db4ae7da394bdd3a8ee5c6
@@ -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.30.7'
7
+ spec.version = '2.31.2'
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
 
@@ -173,6 +173,11 @@ module Honeybee
173
173
  unless space.daylightingControls.empty?
174
174
  hash[:daylighting_control] = Honeybee::DaylightingControl.from_load(space.daylightingControls[0])
175
175
  end
176
+ unless space.waterUseEquipment.empty?
177
+ # Get floor area
178
+ floor_area = space.floorArea
179
+ hash[:service_hot_water] = Honeybee::ServiceHotWaterAbridged.from_load(space.waterUseEquipment[0], floor_area)
180
+ end
176
181
  thermal_zone = space.thermalZone
177
182
  unless thermal_zone.empty?
178
183
  thermal_zone = space.thermalZone.get
@@ -0,0 +1,60 @@
1
+ # *******************************************************************************
2
+ # Honeybee OpenStudio Gem, Copyright (c) 2020, Alliance for Sustainable
3
+ # Energy, LLC, Ladybug Tools LLC and other contributors. All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are met:
7
+ #
8
+ # (1) Redistributions of source code must retain the above copyright notice,
9
+ # this list of conditions and the following disclaimer.
10
+ #
11
+ # (2) Redistributions in binary form must reproduce the above copyright notice,
12
+ # this list of conditions and the following disclaimer in the documentation
13
+ # and/or other materials provided with the distribution.
14
+ #
15
+ # (3) Neither the name of the copyright holder nor the names of any contributors
16
+ # may be used to endorse or promote products derived from this software without
17
+ # specific prior written permission from the respective party.
18
+ #
19
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS
20
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE
23
+ # UNITED STATES GOVERNMENT, OR THE UNITED STATES DEPARTMENT OF ENERGY, NOR ANY OF
24
+ # THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
26
+ # OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
+ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
+ # *******************************************************************************
31
+
32
+ require 'honeybee/load/service_hot_water'
33
+ require 'to_openstudio/model_object'
34
+
35
+ module Honeybee
36
+ class ServiceHotWaterAbridged
37
+
38
+ def self.from_load(load, floor_area)
39
+ # create an empty hash
40
+ hash = {}
41
+ hash[:type] = 'ServiceHotWaterAbridged'
42
+ # set hash values from OpenStudio Object
43
+ hash[:identifier] = clean_name(load.nameString)
44
+ load_def = load.WaterUseEquipmentDefinition
45
+ # units of peak flow rate are m3/s
46
+ peak_flow = load_def.peakFlowRate
47
+ # unit for flow per area is L/h-m2 (m3/s = 3600000 L/h)
48
+ hash[:flow_per_area] = (peak_flow * 3600000)/floor_area
49
+ unless load_def.flowRateFractionSchedule.empty?
50
+ sch = load_def.flowRateFractionSchedule
51
+ # Translate only Scheudle Ruleset and Schedule Fixed Interval
52
+ if sch.to_ScheduleRuleset.is_initialized or sch.to_ScheduleFixedInterval.is_initialized
53
+ hash[:schedule] = load_def.flowRateFractionSchedule.get.nameString
54
+ end
55
+ end
56
+
57
+ hash
58
+ end
59
+ end #ServiceHotWaterAbridged
60
+ end #Honeybee
@@ -80,6 +80,7 @@ require 'from_openstudio/load/infiltration'
80
80
  require 'from_openstudio/load/ventilation'
81
81
  require 'from_openstudio/load/daylight'
82
82
  require 'from_openstudio/load/process'
83
+ require 'from_openstudio/load/service_hot_water'
83
84
 
84
85
  # extend the program type objects
85
86
  require 'from_openstudio/program_type'
@@ -3,7 +3,7 @@
3
3
  "servers": [],
4
4
  "info": {
5
5
  "description": "Honeybee model schema.",
6
- "version": "1.48.1",
6
+ "version": "1.49.0",
7
7
  "title": "Honeybee Model Schema",
8
8
  "contact": {
9
9
  "name": "Ladybug Tools",
@@ -574,6 +574,31 @@
574
574
  "x-displayName": "RadianceSubFaceStateAbridged",
575
575
  "description": "<SchemaDefinition schemaRef=\"#/components/schemas/RadianceSubFaceStateAbridged\" />\n"
576
576
  },
577
+ {
578
+ "name": "radiant_model",
579
+ "x-displayName": "Radiant",
580
+ "description": "<SchemaDefinition schemaRef=\"#/components/schemas/Radiant\" />\n"
581
+ },
582
+ {
583
+ "name": "radiantequipmenttype_model",
584
+ "x-displayName": "RadiantEquipmentType",
585
+ "description": "<SchemaDefinition schemaRef=\"#/components/schemas/RadiantEquipmentType\" />\n"
586
+ },
587
+ {
588
+ "name": "radiantfacetypes_model",
589
+ "x-displayName": "RadiantFaceTypes",
590
+ "description": "<SchemaDefinition schemaRef=\"#/components/schemas/RadiantFaceTypes\" />\n"
591
+ },
592
+ {
593
+ "name": "radiantwithdoasabridged_model",
594
+ "x-displayName": "RadiantwithDOASAbridged",
595
+ "description": "<SchemaDefinition schemaRef=\"#/components/schemas/RadiantwithDOASAbridged\" />\n"
596
+ },
597
+ {
598
+ "name": "radiantwithdoasequipmenttype_model",
599
+ "x-displayName": "RadiantwithDOASEquipmentType",
600
+ "description": "<SchemaDefinition schemaRef=\"#/components/schemas/RadiantwithDOASEquipmentType\" />\n"
601
+ },
577
602
  {
578
603
  "name": "residential_model",
579
604
  "x-displayName": "Residential",
@@ -1054,6 +1079,11 @@
1054
1079
  "pvavequipmenttype_model",
1055
1080
  "radianceshadestateabridged_model",
1056
1081
  "radiancesubfacestateabridged_model",
1082
+ "radiant_model",
1083
+ "radiantequipmenttype_model",
1084
+ "radiantfacetypes_model",
1085
+ "radiantwithdoasabridged_model",
1086
+ "radiantwithdoasequipmenttype_model",
1057
1087
  "residential_model",
1058
1088
  "residentialequipmenttype_model",
1059
1089
  "roofceilingconstructionset_model",
@@ -3501,10 +3531,7 @@
3501
3531
  },
3502
3532
  "story": {
3503
3533
  "title": "Story",
3504
- "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.",
3505
- "maxLength": 100,
3506
- "minLength": 1,
3507
- "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.",
3508
3535
  "type": "string"
3509
3536
  }
3510
3537
  },
@@ -8199,6 +8226,143 @@
8199
8226
  ],
8200
8227
  "additionalProperties": false
8201
8228
  },
8229
+ "RadiantwithDOASEquipmentType": {
8230
+ "title": "RadiantwithDOASEquipmentType",
8231
+ "description": "An enumeration.",
8232
+ "enum": [
8233
+ "DOAS_Radiant_Chiller_Boiler",
8234
+ "DOAS_Radiant_Chiller_ASHP",
8235
+ "DOAS_Radiant_Chiller_DHW",
8236
+ "DOAS_Radiant_ACChiller_Boiler",
8237
+ "DOAS_Radiant_ACChiller_ASHP",
8238
+ "DOAS_Radiant_ACChiller_DHW",
8239
+ "DOAS_Radiant_DCW_Boiler",
8240
+ "DOAS_Radiant_DCW_ASHP",
8241
+ "DOAS_Radiant_DCW_DHW"
8242
+ ],
8243
+ "type": "string"
8244
+ },
8245
+ "RadiantFaceTypes": {
8246
+ "title": "RadiantFaceTypes",
8247
+ "description": "An enumeration.",
8248
+ "enum": [
8249
+ "Floor",
8250
+ "Ceiling",
8251
+ "FloorWithCarpet",
8252
+ "CeilingMetalPanel"
8253
+ ],
8254
+ "type": "string"
8255
+ },
8256
+ "RadiantwithDOASAbridged": {
8257
+ "title": "RadiantwithDOASAbridged",
8258
+ "description": "Low Temperature Radiant with DOAS HVAC system.",
8259
+ "type": "object",
8260
+ "properties": {
8261
+ "identifier": {
8262
+ "title": "Identifier",
8263
+ "description": "Text string for a unique object ID. This identifier remains constant as the object is mutated, copied, and serialized to different formats (eg. dict, idf, osm). This identifier is also used to reference the object across a Model. It must be < 100 characters, use only ASCII characters and exclude (, ; ! \\n \\t).",
8264
+ "maxLength": 100,
8265
+ "minLength": 1,
8266
+ "pattern": "^[^,;!\\n\\t]+$",
8267
+ "type": "string"
8268
+ },
8269
+ "display_name": {
8270
+ "title": "Display Name",
8271
+ "description": "Display name of the object with no character restrictions.",
8272
+ "type": "string"
8273
+ },
8274
+ "user_data": {
8275
+ "title": "User Data",
8276
+ "description": "Optional dictionary of user data associated with the object.All keys and values of this dictionary should be of a standard data type to ensure correct serialization of the object (eg. str, float, int, list).",
8277
+ "type": "object"
8278
+ },
8279
+ "vintage": {
8280
+ "description": "Text for the vintage of the template system. This will be used to set efficiencies for various pieces of equipment within the system. Further information about these defaults can be found in the version of ASHRAE 90.1 corresponding to the selected vintage. Read-only versions of the standard can be found at: https://www.ashrae.org/technical-resources/standards-and-guidelines/read-only-versions-of-ashrae-standards",
8281
+ "default": "ASHRAE_2019",
8282
+ "allOf": [
8283
+ {
8284
+ "$ref": "#/components/schemas/Vintages"
8285
+ }
8286
+ ]
8287
+ },
8288
+ "sensible_heat_recovery": {
8289
+ "title": "Sensible Heat Recovery",
8290
+ "description": "A number between 0 and 1 for the effectiveness of sensible heat recovery within the system.",
8291
+ "default": 0,
8292
+ "minimum": 0,
8293
+ "maximum": 1,
8294
+ "type": "number",
8295
+ "format": "double"
8296
+ },
8297
+ "latent_heat_recovery": {
8298
+ "title": "Latent Heat Recovery",
8299
+ "description": "A number between 0 and 1 for the effectiveness of latent heat recovery within the system.",
8300
+ "default": 0,
8301
+ "minimum": 0,
8302
+ "maximum": 1,
8303
+ "type": "number",
8304
+ "format": "double"
8305
+ },
8306
+ "demand_controlled_ventilation": {
8307
+ "title": "Demand Controlled Ventilation",
8308
+ "description": "Boolean to note whether demand controlled ventilation should be used on the system, which will vary the amount of ventilation air according to the occupancy schedule of the Rooms.",
8309
+ "default": false,
8310
+ "type": "boolean"
8311
+ },
8312
+ "doas_availability_schedule": {
8313
+ "title": "Doas Availability Schedule",
8314
+ "description": "An optional On/Off discrete schedule to set when the dedicated outdoor air system (DOAS) shuts off. This will not only prevent any outdoor air from flowing thorough the system but will also shut off the fans, which can result in more energy savings when spaces served by the DOAS are completely unoccupied. If None, the DOAS will be always on.",
8315
+ "maxLength": 100,
8316
+ "minLength": 1,
8317
+ "type": "string"
8318
+ },
8319
+ "type": {
8320
+ "title": "Type",
8321
+ "default": "RadiantwithDOASAbridged",
8322
+ "pattern": "^RadiantwithDOASAbridged$",
8323
+ "type": "string",
8324
+ "readOnly": true
8325
+ },
8326
+ "equipment_type": {
8327
+ "description": "Text for the specific type of system equipment from the RadiantwithDOASEquipmentType enumeration.",
8328
+ "default": "DOAS_Radiant_Chiller_Boiler",
8329
+ "allOf": [
8330
+ {
8331
+ "$ref": "#/components/schemas/RadiantwithDOASEquipmentType"
8332
+ }
8333
+ ]
8334
+ },
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
+ ]
8343
+ },
8344
+ "minimum_operation_time": {
8345
+ "title": "Minimum Operation Time",
8346
+ "description": "A number for the minimum number of hours of operation for the radiant system before it shuts off.",
8347
+ "default": 1.0,
8348
+ "exclusiveMinimum": 0,
8349
+ "type": "number",
8350
+ "format": "double"
8351
+ },
8352
+ "switch_over_time": {
8353
+ "title": "Switch Over Time",
8354
+ "description": "A number for the minimum number of hours for when the system can switch between heating and cooling.",
8355
+ "default": 24.0,
8356
+ "exclusiveMinimum": 0,
8357
+ "type": "number",
8358
+ "format": "double"
8359
+ }
8360
+ },
8361
+ "required": [
8362
+ "identifier"
8363
+ ],
8364
+ "additionalProperties": false
8365
+ },
8202
8366
  "FCUEquipmentType": {
8203
8367
  "title": "FCUEquipmentType",
8204
8368
  "description": "An enumeration.",
@@ -8738,6 +8902,101 @@
8738
8902
  ],
8739
8903
  "additionalProperties": false
8740
8904
  },
8905
+ "RadiantEquipmentType": {
8906
+ "title": "RadiantEquipmentType",
8907
+ "description": "An enumeration.",
8908
+ "enum": [
8909
+ "Radiant_Chiller_Boiler",
8910
+ "Radiant_Chiller_ASHP",
8911
+ "Radiant_Chiller_DHW",
8912
+ "Radiant_ACChiller_Boiler",
8913
+ "Radiant_ACChiller_ASHP",
8914
+ "Radiant_ACChiller_DHW",
8915
+ "Radiant_DCW_Boiler",
8916
+ "Radiant_DCW_ASHP",
8917
+ "Radiant_DCW_DHW"
8918
+ ],
8919
+ "type": "string"
8920
+ },
8921
+ "Radiant": {
8922
+ "title": "Radiant",
8923
+ "description": "Low Temperature Radiant system.",
8924
+ "type": "object",
8925
+ "properties": {
8926
+ "identifier": {
8927
+ "title": "Identifier",
8928
+ "description": "Text string for a unique object ID. This identifier remains constant as the object is mutated, copied, and serialized to different formats (eg. dict, idf, osm). This identifier is also used to reference the object across a Model. It must be < 100 characters, use only ASCII characters and exclude (, ; ! \\n \\t).",
8929
+ "maxLength": 100,
8930
+ "minLength": 1,
8931
+ "pattern": "^[^,;!\\n\\t]+$",
8932
+ "type": "string"
8933
+ },
8934
+ "display_name": {
8935
+ "title": "Display Name",
8936
+ "description": "Display name of the object with no character restrictions.",
8937
+ "type": "string"
8938
+ },
8939
+ "user_data": {
8940
+ "title": "User Data",
8941
+ "description": "Optional dictionary of user data associated with the object.All keys and values of this dictionary should be of a standard data type to ensure correct serialization of the object (eg. str, float, int, list).",
8942
+ "type": "object"
8943
+ },
8944
+ "vintage": {
8945
+ "description": "Text for the vintage of the template system. This will be used to set efficiencies for various pieces of equipment within the system. Further information about these defaults can be found in the version of ASHRAE 90.1 corresponding to the selected vintage. Read-only versions of the standard can be found at: https://www.ashrae.org/technical-resources/standards-and-guidelines/read-only-versions-of-ashrae-standards",
8946
+ "default": "ASHRAE_2019",
8947
+ "allOf": [
8948
+ {
8949
+ "$ref": "#/components/schemas/Vintages"
8950
+ }
8951
+ ]
8952
+ },
8953
+ "type": {
8954
+ "title": "Type",
8955
+ "default": "Radiant",
8956
+ "pattern": "^Radiant$",
8957
+ "type": "string",
8958
+ "readOnly": true
8959
+ },
8960
+ "equipment_type": {
8961
+ "description": "Text for the specific type of system equipment from the RadiantEquipmentType enumeration.",
8962
+ "default": "Radiant_Chiller_Boiler",
8963
+ "allOf": [
8964
+ {
8965
+ "$ref": "#/components/schemas/RadiantEquipmentType"
8966
+ }
8967
+ ]
8968
+ },
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
+ ]
8977
+ },
8978
+ "minimum_operation_time": {
8979
+ "title": "Minimum Operation Time",
8980
+ "description": "A number for the minimum number of hours of operation for the radiant system before it shuts off.",
8981
+ "default": 1.0,
8982
+ "exclusiveMinimum": 0,
8983
+ "type": "number",
8984
+ "format": "double"
8985
+ },
8986
+ "switch_over_time": {
8987
+ "title": "Switch Over Time",
8988
+ "description": "A number for the minimum number of hours for when the system can switch between heating and cooling.",
8989
+ "default": 24.0,
8990
+ "exclusiveMinimum": 0,
8991
+ "type": "number",
8992
+ "format": "double"
8993
+ }
8994
+ },
8995
+ "required": [
8996
+ "identifier"
8997
+ ],
8998
+ "additionalProperties": false
8999
+ },
8741
9000
  "SHWEquipmentType": {
8742
9001
  "title": "SHWEquipmentType",
8743
9002
  "description": "An enumeration.",
@@ -10575,6 +10834,9 @@
10575
10834
  {
10576
10835
  "$ref": "#/components/schemas/VRFwithDOASAbridged"
10577
10836
  },
10837
+ {
10838
+ "$ref": "#/components/schemas/RadiantwithDOASAbridged"
10839
+ },
10578
10840
  {
10579
10841
  "$ref": "#/components/schemas/FCU"
10580
10842
  },
@@ -10598,6 +10860,9 @@
10598
10860
  },
10599
10861
  {
10600
10862
  "$ref": "#/components/schemas/GasUnitHeater"
10863
+ },
10864
+ {
10865
+ "$ref": "#/components/schemas/Radiant"
10601
10866
  }
10602
10867
  ]
10603
10868
  }
@@ -13833,7 +14098,7 @@
13833
14098
  "version": {
13834
14099
  "title": "Version",
13835
14100
  "description": "Text string for the current version of the schema.",
13836
- "default": "1.48.1",
14101
+ "default": "1.49.0",
13837
14102
  "pattern": "([0-9]+)\\.([0-9]+)\\.([0-9]+)",
13838
14103
  "type": "string",
13839
14104
  "readOnly": true
@@ -36,9 +36,10 @@ module Honeybee
36
36
  attr_reader :errors, :warnings
37
37
 
38
38
  @@all_air_types = ['VAV', 'PVAV', 'PSZ', 'PTAC', 'ForcedAirFurnace']
39
- @@doas_types = ['FCUwithDOASAbridged', 'WSHPwithDOASAbridged', 'VRFwithDOASAbridged']
39
+ @@doas_types = ['FCUwithDOASAbridged', 'WSHPwithDOASAbridged',
40
+ 'VRFwithDOASAbridged', 'RadiantwithDOASAbridged']
40
41
  @@heat_cool_types = ['FCU', 'WSHP', 'VRF', 'Baseboard', 'EvaporativeCooler',
41
- 'Residential', 'WindowAC', 'GasUnitHeater']
42
+ 'Residential', 'WindowAC', 'GasUnitHeater', 'Radiant']
42
43
  @@types = @@all_air_types + @@doas_types + @@heat_cool_types
43
44
 
44
45
  def allowable_types
@@ -29,9 +29,10 @@
29
29
  # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
30
  # *******************************************************************************
31
31
 
32
- # Note: This file is copied directly from the "Create Typical DOE Building from Model" measure
32
+ # Note: This file is copied from the "Create Typical DOE Building from Model" measure
33
33
  # https://bcl.nrel.gov/node/85019
34
- # It is intended that this file be re-copied if new system types are added
34
+ # https://github.com/NREL/openstudio-model-articulation-gem/tree/develop/lib/measures/create_typical_doe_building_from_model
35
+ # It is intended that this file be updated if new system types are added to the original file
35
36
 
36
37
  class OpenStudio::Model::Model
37
38
  # Adds the HVAC system as derived from the combinations of CBECS 2012 MAINHT and MAINCL fields.
@@ -49,7 +50,7 @@ class OpenStudio::Model::Model
49
50
  system_zones = heated_and_cooled_zones + cooled_only_zones
50
51
 
51
52
  # system type naming convention:
52
- # [ventilation strategy] [ cooling system and plant] [heating system and plant]
53
+ # [ventilation strategy] [cooling system and plant] [heating system and plant]
53
54
 
54
55
  case system_type
55
56
 
@@ -0,0 +1,538 @@
1
+ # *******************************************************************************
2
+ # Honeybee OpenStudio Gem, Copyright (c) 2020, Alliance for Sustainable
3
+ # Energy, LLC, Ladybug Tools LLC and other contributors. All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are met:
7
+ #
8
+ # (1) Redistributions of source code must retain the above copyright notice,
9
+ # this list of conditions and the following disclaimer.
10
+ #
11
+ # (2) Redistributions in binary form must reproduce the above copyright notice,
12
+ # this list of conditions and the following disclaimer in the documentation
13
+ # and/or other materials provided with the distribution.
14
+ #
15
+ # (3) Neither the name of the copyright holder nor the names of any contributors
16
+ # may be used to endorse or promote products derived from this software without
17
+ # specific prior written permission from the respective party.
18
+ #
19
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS
20
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE
23
+ # UNITED STATES GOVERNMENT, OR THE UNITED STATES DEPARTMENT OF ENERGY, NOR ANY OF
24
+ # THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
26
+ # OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
+ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
+ # *******************************************************************************
31
+
32
+ # Note: This file is derived from the "Radiant Slab with DOAS" measure
33
+ # https://github.com/NREL/openstudio-model-articulation-gem/tree/develop/lib/measures/radiant_slab_with_doas
34
+ # It is intended that this file be updated if changes are made to the original measure
35
+
36
+ class OpenStudio::Model::Model
37
+ # Adds a radiant HVAC system to the model
38
+ def add_radiant_hvac_system(std, system_type, zones, rad_props)
39
+ # the 'zones' argument includes zones that have heating, cooling, or both
40
+ conditioned_zones = zones.select { |zone| std.thermal_zone_heated?(zone) && std.thermal_zone_cooled?(zone) }
41
+
42
+ # get the climate zone from the model, which will help set water temerpatures
43
+ climate_zone_obj = self.getClimateZones.getClimateZone('ASHRAE', 2006)
44
+ if climate_zone_obj.empty
45
+ climate_zone_obj = self.getClimateZones.getClimateZone('ASHRAE', 2013)
46
+ end
47
+ if climate_zone_obj.empty || climate_zone_obj.value == ''
48
+ climate_zone = ''
49
+ else
50
+ climate_zone = climate_zone_obj.value
51
+ end
52
+
53
+ # get the radiant hot water temperature based on the climate zone
54
+ case climate_zone
55
+ when '0', '1'
56
+ radiant_htg_dsgn_sup_wtr_temp_f = 90.0
57
+ cz_mult = 2
58
+ when '2', '2A', '2B'
59
+ radiant_htg_dsgn_sup_wtr_temp_f = 100.0
60
+ cz_mult = 2
61
+ when '3', '3A', '3B', '3C'
62
+ radiant_htg_dsgn_sup_wtr_temp_f = 100.0
63
+ cz_mult = 3
64
+ when '4', '4A', '4B', '4C'
65
+ radiant_htg_dsgn_sup_wtr_temp_f = 100.0
66
+ cz_mult = 4
67
+ when '5', '5A', '5B', '5C'
68
+ radiant_htg_dsgn_sup_wtr_temp_f = 110.0
69
+ cz_mult = 4
70
+ when '6', '6A', '6B'
71
+ radiant_htg_dsgn_sup_wtr_temp_f = 120.0
72
+ cz_mult = 4
73
+ when '7', '8'
74
+ radiant_htg_dsgn_sup_wtr_temp_f = 120.0
75
+ cz_mult = 5
76
+ else # unrecognized climate zone; default to climate zone 4
77
+ radiant_htg_dsgn_sup_wtr_temp_f = 100.0
78
+ cz_mult = 4
79
+ end
80
+
81
+ # create the hot water loop
82
+ if system_type.include? 'ASHP'
83
+ boiler_fuel_type = 'ASHP'
84
+ elsif system_type.include? 'Boiler'
85
+ boiler_fuel_type = 'NaturalGas'
86
+ elsif system_type.include? 'DHW'
87
+ boiler_fuel_type = 'DistrictHeating'
88
+ end
89
+ hot_water_loop = std.model_add_hw_loop(
90
+ self,
91
+ boiler_fuel_type,
92
+ dsgn_sup_wtr_temp: radiant_htg_dsgn_sup_wtr_temp_f,
93
+ dsgn_sup_wtr_temp_delt: 10.0)
94
+
95
+ # create the chilled water loop
96
+ if system_type.include? 'Radiant_Chiller' # water-cooled chiller
97
+ # make condenser water loop
98
+ fan_type = std.model_cw_loop_cooling_tower_fan_type(self)
99
+ condenser_water_loop = std.model_add_cw_loop(
100
+ self,
101
+ cooling_tower_type: 'Open Cooling Tower',
102
+ cooling_tower_fan_type: 'Propeller or Axial',
103
+ cooling_tower_capacity_control: fan_type,
104
+ number_of_cells_per_tower: 1,
105
+ number_cooling_towers: 1)
106
+ # make chilled water loop
107
+ chilled_water_loop = std.model_add_chw_loop(
108
+ self,
109
+ chw_pumping_type: 'const_pri_var_sec',
110
+ dsgn_sup_wtr_temp: 55.0,
111
+ dsgn_sup_wtr_temp_delt: 5.0,
112
+ chiller_cooling_type: 'WaterCooled',
113
+ condenser_water_loop: condenser_water_loop)
114
+ elsif system_type.include? 'Radiant_ACChiller' # air-cooled chiller
115
+ chilled_water_loop = std.model_add_chw_loop(
116
+ self,
117
+ chw_pumping_type: 'const_pri_var_sec',
118
+ dsgn_sup_wtr_temp: 55.0,
119
+ dsgn_sup_wtr_temp_delt: 5.0,
120
+ chiller_cooling_type: 'AirCooled')
121
+ else # district chilled water cooled
122
+ chilled_water_loop = std.model_add_chw_loop(
123
+ self,
124
+ cooling_fuel: 'DistrictCooling',
125
+ chw_pumping_type: 'const_pri_var_sec',
126
+ dsgn_sup_wtr_temp: 55.0,
127
+ dsgn_sup_wtr_temp_delt: 5.0)
128
+ end
129
+
130
+ # get the various controls for the radiant system
131
+ if rad_props[:minimum_operation_time]
132
+ minimum_operation = rad_props[:minimum_operation_time]
133
+ else
134
+ minimum_operation = 1
135
+ end
136
+ if rad_props[:switch_over_time]
137
+ switch_over_time = rad_props[:switch_over_time]
138
+ else
139
+ switch_over_time = 24
140
+ end
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
+
145
+ # add radiant system to the conditioned zones
146
+ include_carpet = false
147
+ control_strategy = 'proportional_control'
148
+ if rad_props[:radiant_type]
149
+ radiant_type = rad_props[:radiant_type].downcase
150
+ if radiant_type == 'floorwithcarpet'
151
+ radiant_type = 'floor'
152
+ include_carpet = true
153
+ elsif radiant_type == 'ceilingmetalpanel'
154
+ control_strategy = 'default'
155
+ end
156
+ else
157
+ radiant_type = 'floor'
158
+ end
159
+
160
+ radiant_loops = model_add_low_temp_radiant(
161
+ std, conditioned_zones, hot_water_loop, chilled_water_loop,
162
+ radiant_type: radiant_type,
163
+ control_strategy: control_strategy,
164
+ include_carpet: include_carpet,
165
+ model_occ_hr_start: start_hour,
166
+ model_occ_hr_end: end_hour,
167
+ minimum_operation: minimum_operation,
168
+ switch_over_time: switch_over_time,
169
+ cz_mult: cz_mult)
170
+
171
+ # if the equipment includes a DOAS, then add it
172
+ if system_type.include? 'DOAS_'
173
+ std.model_add_doas(self, conditioned_zones)
174
+ end
175
+
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
+
538
+ end
@@ -51,6 +51,7 @@ module Honeybee
51
51
  # only load openstudio-standards when needed
52
52
  require 'openstudio-standards'
53
53
  require_relative 'Model.hvac'
54
+ require_relative 'radiant'
54
55
 
55
56
  # get the defaults for the specific system type
56
57
  hvac_defaults = defaults(@hash[:type])
@@ -79,8 +80,14 @@ module Honeybee
79
80
  end
80
81
  end
81
82
 
82
- # create the HVAC system and assign the display name to the air loop name if it exists
83
- openstudio_model.add_cbecs_hvac_system(standard, equipment_type, zones)
83
+ # create the HVAC system
84
+ if equipment_type.to_s.include? 'Radiant'
85
+ os_hvac = openstudio_model.add_radiant_hvac_system(standard, equipment_type.to_s, zones, @hash)
86
+ else
87
+ os_hvac = openstudio_model.add_cbecs_hvac_system(standard, equipment_type, zones)
88
+ end
89
+
90
+ # Get the air loops and assign the display name to the air loop name if it exists
84
91
  os_air_loops = []
85
92
  air_loops = openstudio_model.getAirLoopHVACs
86
93
  unless air_loops.length == $air_loop_count # check if any new loops were added
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.30.7
4
+ version: 2.31.2
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-12 00:00:00.000000000 Z
14
+ date: 2022-04-05 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: bundler
@@ -184,6 +184,7 @@ files:
184
184
  - lib/from_openstudio/load/lighting.rb
185
185
  - lib/from_openstudio/load/people.rb
186
186
  - lib/from_openstudio/load/process.rb
187
+ - lib/from_openstudio/load/service_hot_water.rb
187
188
  - lib/from_openstudio/load/ventilation.rb
188
189
  - lib/from_openstudio/material/opaque.rb
189
190
  - lib/from_openstudio/material/opaque_no_mass.rb
@@ -302,6 +303,7 @@ files:
302
303
  - lib/to_openstudio/geometry/shade.rb
303
304
  - lib/to_openstudio/hvac/Model.hvac.rb
304
305
  - lib/to_openstudio/hvac/ideal_air.rb
306
+ - lib/to_openstudio/hvac/radiant.rb
305
307
  - lib/to_openstudio/hvac/template.rb
306
308
  - lib/to_openstudio/internalmass.rb
307
309
  - lib/to_openstudio/load/daylight.rb