honeybee-openstudio 2.30.6 → 2.31.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/honeybee-openstudio.gemspec +1 -1
- data/lib/honeybee/_defaults/model.json +271 -6
- data/lib/honeybee/hvac/template.rb +3 -2
- data/lib/to_openstudio/hvac/Model.hvac.rb +4 -3
- data/lib/to_openstudio/hvac/radiant.rb +538 -0
- data/lib/to_openstudio/hvac/template.rb +9 -2
- data/lib/to_openstudio/simulation/parameter_model.rb +67 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f9b620209ce330fea574204c6257db0a2e22f6e898de0d0b27a7d20360fd5296
|
4
|
+
data.tar.gz: 3f294beea759ed0789cd3928d023edbd4f97697c2179cb77d5e7be971d4ed0a7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dffa3a850ebe2f9b0bc03e67cba8329d1a8d7b4a4c7081cf0a751e3e423795dfb7b90028975efa92cf1d78405117b3dacd3d68e6f1e513ba93e772914ebe8475
|
7
|
+
data.tar.gz: 1b244c65a2df322052c5d522564199a0ac0e49b7dc1aadb7d6c297c24e4ed48d26d19305e3ec49caee382939ea96b7ea3f54aff3d4b02b1127d904e394b1560f
|
data/honeybee-openstudio.gemspec
CHANGED
@@ -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.
|
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.
|
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.
|
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',
|
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
|
32
|
+
# Note: This file is copied from the "Create Typical DOE Building from Model" measure
|
33
33
|
# https://bcl.nrel.gov/node/85019
|
34
|
-
#
|
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] [
|
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
|
83
|
-
|
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
|
@@ -154,6 +154,7 @@ module Honeybee
|
|
154
154
|
end
|
155
155
|
end
|
156
156
|
end
|
157
|
+
|
157
158
|
# use the average of design day temperatures to set the water mains temperature
|
158
159
|
os_water_mains = @openstudio_model.getSiteWaterMainsTemperature
|
159
160
|
os_water_mains.setCalculationMethod('Correlation')
|
@@ -164,6 +165,62 @@ module Honeybee
|
|
164
165
|
end
|
165
166
|
os_water_mains.setMaximumDifferenceInMonthlyAverageOutdoorAirTemperatures(4)
|
166
167
|
|
168
|
+
# set the climate zone from design days assuming 0.4% extremes and normal distribution
|
169
|
+
climate_zone_objs = @openstudio_model.getClimateZones
|
170
|
+
ashrae_zones = climate_zone_objs.getClimateZones('ASHRAE')
|
171
|
+
if ashrae_zones.empty? && db_temps.length > 0
|
172
|
+
# generate temperatures according to a normal distribution
|
173
|
+
mean_temp = (db_temps.max + db_temps.min) / 2
|
174
|
+
dist_to_mean = db_temps.max - mean_temp
|
175
|
+
st_dev = dist_to_mean / 2.65
|
176
|
+
vals = []
|
177
|
+
for i in 0..4379
|
178
|
+
step_seed = i.to_f / 4380
|
179
|
+
add_val1, add_val2 = gaussian(mean_temp, st_dev, step_seed)
|
180
|
+
vals << add_val1
|
181
|
+
vals << add_val2
|
182
|
+
end
|
183
|
+
|
184
|
+
# compute the number of heating and cooling degree days
|
185
|
+
cooling_deg_days, heating_deg_days = 0, 0
|
186
|
+
vals.each do |temp|
|
187
|
+
if temp > 10
|
188
|
+
cdd = (temp - 10) / 24
|
189
|
+
cooling_deg_days += cdd
|
190
|
+
end
|
191
|
+
if temp < 18
|
192
|
+
hdd = (18 - temp) / 24
|
193
|
+
heating_deg_days += hdd
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
# determine the climate zone from the degree-day distribution
|
198
|
+
if cooling_deg_days > 5000
|
199
|
+
cz = '1'
|
200
|
+
elsif cooling_deg_days > 3500
|
201
|
+
cz = '2A'
|
202
|
+
elsif cooling_deg_days > 2500
|
203
|
+
cz = '3A'
|
204
|
+
elsif cooling_deg_days <= 2500 and heating_deg_days <= 2000
|
205
|
+
cz = '3C'
|
206
|
+
elsif cooling_deg_days <= 2500 and heating_deg_days <= 3000
|
207
|
+
cz = '4A'
|
208
|
+
elsif heating_deg_days <= 3000
|
209
|
+
cz = '4C'
|
210
|
+
elsif heating_deg_days <= 4000
|
211
|
+
cz = '5A'
|
212
|
+
elsif heating_deg_days <= 5000
|
213
|
+
cz = '6A'
|
214
|
+
elsif heating_deg_days <= 7000
|
215
|
+
cz = '7'
|
216
|
+
else
|
217
|
+
cz = '8'
|
218
|
+
end
|
219
|
+
|
220
|
+
# set the climate zone
|
221
|
+
climate_zone_objs.setClimateZone('ASHRAE', cz)
|
222
|
+
end
|
223
|
+
|
167
224
|
# set Outputs for the simulation
|
168
225
|
if @hash[:output]
|
169
226
|
if @hash[:output][:outputs]
|
@@ -251,5 +308,15 @@ module Honeybee
|
|
251
308
|
|
252
309
|
end
|
253
310
|
|
311
|
+
def gaussian(mean, stddev, seed)
|
312
|
+
# generate a gaussian distribution of values
|
313
|
+
theta = 2 * Math::PI * seed
|
314
|
+
rho = Math.sqrt(-2 * Math.log(1 - seed))
|
315
|
+
scale = stddev * rho
|
316
|
+
x = mean + scale * Math.cos(theta)
|
317
|
+
y = mean + scale * Math.sin(theta)
|
318
|
+
return x, y
|
319
|
+
end
|
320
|
+
|
254
321
|
end #SimulationParameter
|
255
322
|
end #Honeybee
|
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.
|
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
|
+
date: 2022-03-27 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: bundler
|
@@ -302,6 +302,7 @@ files:
|
|
302
302
|
- lib/to_openstudio/geometry/shade.rb
|
303
303
|
- lib/to_openstudio/hvac/Model.hvac.rb
|
304
304
|
- lib/to_openstudio/hvac/ideal_air.rb
|
305
|
+
- lib/to_openstudio/hvac/radiant.rb
|
305
306
|
- lib/to_openstudio/hvac/template.rb
|
306
307
|
- lib/to_openstudio/internalmass.rb
|
307
308
|
- lib/to_openstudio/load/daylight.rb
|