openstudio-standards 0.8.5 → 0.8.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/data/standards/test_performance_expected_dd_results.csv +90 -90
- data/lib/openstudio-standards/btap/activity.rb +858 -61
- data/lib/openstudio-standards/btap/attributes.rb +297 -103
- data/lib/openstudio-standards/btap/bridging.rb +1335 -593
- data/lib/openstudio-standards/btap/btap.rb +60 -181
- data/lib/openstudio-standards/btap/carbon/btap_carbon.rb +199 -142
- data/lib/openstudio-standards/btap/common/constructions.json +1275 -0
- data/lib/openstudio-standards/btap/common/costs.csv +1969 -0
- data/lib/openstudio-standards/btap/common/costs_local_factors.csv +2315 -0
- data/lib/openstudio-standards/btap/common/materials_glazing.csv +61 -0
- data/lib/openstudio-standards/btap/{common_resources → common}/materials_hvac.csv +1685 -1685
- data/lib/openstudio-standards/btap/{common_resources → common}/materials_lighting.csv +266 -266
- data/lib/openstudio-standards/btap/common/materials_opaque.csv +214 -0
- data/lib/openstudio-standards/btap/common/necb_building_types.csv +35 -0
- data/lib/openstudio-standards/btap/common/necb_space_types.csv +110 -0
- data/lib/openstudio-standards/btap/common/thermal_bridging.csv +73 -0
- data/lib/openstudio-standards/btap/costing/btap_costing.rb +346 -429
- data/lib/openstudio-standards/btap/costing/database.rb +104 -0
- data/lib/openstudio-standards/btap/costing/daylighting_sensor_control_costing.rb +356 -344
- data/lib/openstudio-standards/btap/costing/dcv_costing.rb +282 -296
- data/lib/openstudio-standards/btap/costing/envelope_costing.rb +179 -192
- data/lib/openstudio-standards/btap/costing/heating_cooling_costing.rb +2477 -2316
- data/lib/openstudio-standards/btap/costing/led_lighting_costing.rb +127 -142
- data/lib/openstudio-standards/btap/costing/lighting_costing.rb +216 -198
- data/lib/openstudio-standards/btap/costing/nv_costing.rb +522 -523
- data/lib/openstudio-standards/btap/costing/pv_ground_costing.rb +649 -649
- data/lib/openstudio-standards/btap/costing/shw_costing.rb +626 -625
- data/lib/openstudio-standards/btap/costing/thermal_bridging_costing.rb +72 -0
- data/lib/openstudio-standards/btap/costing/ventilation_costing.rb +2363 -2361
- data/lib/openstudio-standards/btap/deprecated.rb +1033 -0
- data/lib/openstudio-standards/btap/envelope.rb +313 -572
- data/lib/openstudio-standards/btap/fileio.rb +152 -1070
- data/lib/openstudio-standards/btap/geometry.rb +486 -766
- data/lib/openstudio-standards/btap/linear_regression.rb +130 -0
- data/lib/openstudio-standards/btap/paths.rb +46 -0
- data/lib/openstudio-standards/btap/qaqc/analysis.rb +163 -0
- data/lib/openstudio-standards/btap/qaqc/data.rb +2293 -0
- data/lib/openstudio-standards/btap/qaqc/datapoint.rb +641 -0
- data/lib/openstudio-standards/btap/schedules.rb +11 -611
- data/lib/openstudio-standards/btap/structure.rb +574 -189
- data/lib/openstudio-standards/constructions/materials/information.rb +1 -1
- data/lib/openstudio-standards/create_typical/create_typical.rb +1 -1
- data/lib/openstudio-standards/exterior_lighting/create.rb +135 -85
- data/lib/openstudio-standards/exterior_lighting/data/typical_exterior_lighting.json +20 -0
- data/lib/openstudio-standards/exterior_lighting/information.rb +23 -2
- data/lib/openstudio-standards/geometry/create.rb +7 -3
- data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.Model.rb +3 -2
- data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Model.rb +3 -2
- data/lib/openstudio-standards/prototypes/common/data/thermostat_schedule_lookup.json +84 -84
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.hvac_systems.rb +4 -4
- data/lib/openstudio-standards/refrigeration/create_walkin.rb +45 -5
- data/lib/openstudio-standards/refrigeration/data/refrigerated_walkins.csv +75 -75
- data/lib/openstudio-standards/schedules/parametric.rb +6 -0
- data/lib/openstudio-standards/service_water_heating/create_typical.rb +10 -3
- data/lib/openstudio-standards/service_water_heating/create_water_heating_loop.rb +30 -14
- data/lib/openstudio-standards/service_water_heating/information.rb +4 -1
- data/lib/openstudio-standards/sql_file/unmet_hours.rb +2 -1
- data/lib/openstudio-standards/standards/Standards.CoilCoolingDXSingleSpeed.rb +3 -1
- data/lib/openstudio-standards/standards/Standards.CoilHeatingDXSingleSpeed.rb +3 -1
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.boilers.json +12 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.construction_properties.json +18432 -3072
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.boilers.json +26 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.construction_properties.json +8532 -1422
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.boilers.json +26 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.construction_properties.json +8532 -1422
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.boilers.json +16 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.computer_room_acs.json +30 -30
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.construction_properties.json +7560 -1260
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.PumpVariableSpeed.rb +1 -2
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.boilers.json +16 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.construction_properties.json +7560 -1260
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirLoopHVAC.rb +3 -3
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.PumpVariableSpeed.rb +1 -2
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.boilers.json +22 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.construction_properties.json +7236 -1206
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.furnaces.json +223 -19
- data/lib/openstudio-standards/standards/ashrae_90_1/data/ashrae_90_1.schedules.json +37 -1
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.boilers.json +3 -0
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.construction_properties.json +6900 -1150
- data/lib/openstudio-standards/standards/cbes/data/cbes.schedules.json +1 -1
- data/lib/openstudio-standards/standards/deer/data/deer.schedules.json +1 -1
- data/lib/openstudio-standards/standards/necb/BTAP1980TO2010/data/surface_thermal_transmittance.json +76 -40
- data/lib/openstudio-standards/standards/necb/BTAPPRE1980/data/surface_thermal_transmittance.json +76 -40
- data/lib/openstudio-standards/standards/necb/ECMS/hvac_systems.rb +6 -4
- data/lib/openstudio-standards/standards/necb/NECB2011/NECB2011_QAQC.json +2988 -0
- data/lib/openstudio-standards/standards/necb/NECB2011/autozone.rb +25 -3
- data/lib/openstudio-standards/standards/necb/NECB2011/building_envelope.rb +672 -534
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/Hospital.osm +1791 -1792
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPMultiTower.osm +7 -1
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPTownHouse.osm +7 -1
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LargeHotel.osm +5 -6
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LargeOffice.osm +8 -9
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/NorthernEducation.osm +10 -4
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/NorthernHealthCare.osm +14 -15
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/Outpatient.osm +7 -1
- data/lib/openstudio-standards/standards/necb/NECB2011/data/surface_thermal_transmittance.json +76 -40
- data/lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb +14 -6
- data/lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb +542 -130
- data/lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb +30 -63
- data/lib/openstudio-standards/standards/necb/NECB2011/service_water_heating.rb +7 -2
- data/lib/openstudio-standards/standards/necb/NECB2015/data/surface_thermal_transmittance.json +90 -0
- data/lib/openstudio-standards/standards/necb/NECB2015/necb_2015.rb +8 -6
- data/lib/openstudio-standards/standards/necb/NECB2017/data/surface_thermal_transmittance.json +76 -40
- data/lib/openstudio-standards/standards/necb/NECB2017/necb_2017.rb +2 -1
- data/lib/openstudio-standards/standards/necb/NECB2020/building_envelope.rb +0 -117
- data/lib/openstudio-standards/standards/necb/NECB2020/data/surface_thermal_transmittance.json +76 -45
- data/lib/openstudio-standards/standards/necb/NECB2020/necb_2020.rb +218 -0
- data/lib/openstudio-standards/standards/necb/common/construction_defaults.osm +12 -7
- data/lib/openstudio-standards/standards/standard.rb +1 -1
- data/lib/openstudio-standards/version.rb +1 -1
- data/lib/openstudio-standards.rb +9 -4
- metadata +59 -66
- data/data/inventory/thermal_bridging.csv +0 -90
- data/data/standards/OpenStudio_Standards-ashrae_90_1-ALL-comstock(space_types).xlsx +0 -0
- data/data/standards/openstudio_standards_duplicates_log.csv +0 -7962
- data/lib/openstudio-standards/btap/NECB_building_types.csv +0 -35
- data/lib/openstudio-standards/btap/NECB_space_types.csv +0 -109
- data/lib/openstudio-standards/btap/WeatherData1.json +0 -2110
- data/lib/openstudio-standards/btap/WeatherData1.xlsx +0 -0
- data/lib/openstudio-standards/btap/btap_result.rb +0 -1144
- data/lib/openstudio-standards/btap/btap_test_helper.rb +0 -86
- data/lib/openstudio-standards/btap/common_paths.rb +0 -81
- data/lib/openstudio-standards/btap/common_resources/ConstructionProperties.csv +0 -52
- data/lib/openstudio-standards/btap/common_resources/Constructions.csv +0 -37
- data/lib/openstudio-standards/btap/common_resources/construction_sets.csv +0 -1270
- data/lib/openstudio-standards/btap/common_resources/constructions_glazing.csv +0 -121
- data/lib/openstudio-standards/btap/common_resources/constructions_opaque.csv +0 -2256
- data/lib/openstudio-standards/btap/common_resources/costs.csv +0 -1897
- data/lib/openstudio-standards/btap/common_resources/costs_local_factors.csv +0 -2315
- data/lib/openstudio-standards/btap/common_resources/materials_glazing.csv +0 -35
- data/lib/openstudio-standards/btap/common_resources/materials_opaque.csv +0 -164
- data/lib/openstudio-standards/btap/costing/btap_measure_helper.rb +0 -359
- data/lib/openstudio-standards/btap/costing/btap_workflow.rb +0 -117
- data/lib/openstudio-standards/btap/costing/copy_test_results_files_to_expected_results.rb +0 -11
- data/lib/openstudio-standards/btap/costing/cost_building_from_file.rb +0 -136
- data/lib/openstudio-standards/btap/costing/costing_database_wrapper.rb +0 -173
- data/lib/openstudio-standards/btap/costing/dummy.epw +0 -8768
- data/lib/openstudio-standards/btap/costing/dummy.osm +0 -5320
- data/lib/openstudio-standards/btap/costing/neb_end_use_prices.csv +0 -42
- data/lib/openstudio-standards/btap/costing/necb_2011_spacetype_info.csv +0 -225
- data/lib/openstudio-standards/btap/costing/necb_reference_runs.csv +0 -28705
- data/lib/openstudio-standards/btap/costing/parallel_tests.rb +0 -92
- data/lib/openstudio-standards/btap/costing/test_list.txt +0 -15
- data/lib/openstudio-standards/btap/costing/test_run_all_test_locally.rb +0 -27
- data/lib/openstudio-standards/btap/costing/test_run_costing_tests.rb +0 -80
- data/lib/openstudio-standards/btap/necb_2011_spacetype_info.csv +0 -225
- data/lib/openstudio-standards/btap/utilities/Add_template_field_to_json.rb +0 -22
- data/lib/openstudio-standards/btap/utilities/convert_surfaces_to_adiabatic_necb_8426.rb +0 -204
- data/lib/openstudio-standards/btap/utilities/generate_space_types.rb +0 -8
- data/lib/openstudio-standards/btap/utilities/necb_to_epw_map.rb +0 -50
- data/lib/openstudio-standards/btap/utilities/os_sim_extract.rb +0 -48
- data/lib/openstudio-standards/btap/utilities/rename_surfaces.rb +0 -24
- data/lib/openstudio-standards/btap/utilities/round_surf_coords.rb +0 -32
- data/lib/openstudio-standards/btap/utilities/sched_create.rb +0 -145
- data/lib/openstudio-standards/btap/utilities/set_mult_to_adiabatic.rb +0 -53
- data/lib/openstudio-standards/btap/utilities/space_type.rb +0 -78
- data/lib/openstudio-standards/btap/utilities/weatherData1_xlsx_to_json.rb +0 -56
- data/lib/openstudio-standards/btap/vintagizer.rb +0 -161
- data/lib/openstudio-standards/btap/visualization.rb +0 -72
- data/lib/openstudio-standards/standards/necb/NECB2011/data/necb_2011_spacetype_info.csv +0 -225
- data/lib/openstudio-standards/standards/necb/common/btap_analysis.rb +0 -79
- data/lib/openstudio-standards/standards/necb/common/btap_data.rb +0 -2656
- data/lib/openstudio-standards/standards/necb/common/btap_datapoint.rb +0 -653
- data/lib/openstudio-standards/standards/necb/common/necb_reference_runs.csv +0 -28705
- data/lib/openstudio-standards/standards/necb/common/utility_pricing_2025-02-20.csv +0 -92
- /data/lib/openstudio-standards/btap/{common_resources → common}/carbon_frame.csv +0 -0
- /data/lib/openstudio-standards/btap/{common_resources → common}/carbon_glazing.csv +0 -0
- /data/lib/openstudio-standards/btap/{common_resources → common}/carbon_opaque.csv +0 -0
- /data/lib/openstudio-standards/btap/{common_resources → common}/hvac_vent_ahu.csv +0 -0
- /data/lib/openstudio-standards/btap/{common_resources → common}/lighting.csv +0 -0
- /data/lib/openstudio-standards/btap/{common_resources → common}/lighting_sets.csv +0 -0
- /data/lib/openstudio-standards/btap/{common_resources → common}/locations.csv +0 -0
- /data/lib/openstudio-standards/btap/{costing → common}/mech_sizing.json +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# **************************************************************************** /
|
|
2
|
-
# * Copyright (c) 2008-
|
|
2
|
+
# * Copyright (c) 2008-2026, Natural Resources Canada
|
|
3
3
|
# * All rights reserved.
|
|
4
4
|
# *
|
|
5
5
|
# * This library is free software; you can redistribute it and/or
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
# **************************************************************************** /
|
|
19
19
|
|
|
20
20
|
require 'tbd'
|
|
21
|
+
require 'json'
|
|
21
22
|
|
|
22
23
|
module BTAP
|
|
23
24
|
# ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- #
|
|
@@ -29,29 +30,19 @@ module BTAP
|
|
|
29
30
|
# @author: Denis Bourgeois
|
|
30
31
|
|
|
31
32
|
# --- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --- #
|
|
32
|
-
# BTAP/TBD
|
|
33
|
+
# BTAP/TBD parameters are based on BTAP costing data:
|
|
34
|
+
# - paired low-performance (LP) vs high-performance (HP) wall variants
|
|
35
|
+
# - range of clear-field Uo factors for each variant
|
|
36
|
+
# - paired "bad" vs "good" PSI factor sets for each variant
|
|
33
37
|
#
|
|
34
|
-
#
|
|
35
|
-
# - range of PSI factors (i.e. MAJOR thermal bridging), e.g. corners
|
|
36
|
-
#
|
|
37
|
-
# Ref: EVOKE BTAP costing spreadsheet modifications (2022), synced with:
|
|
38
|
+
# Ref: BTAP costing data modifications (2022), synced with:
|
|
38
39
|
# - Building Envelope Thermal Bridging Guide (BETBG)
|
|
39
40
|
# - ASHRAE RP-1365, ISO-12011, etc.
|
|
40
41
|
#
|
|
41
|
-
#
|
|
42
|
-
# BTAP structure/envelope data model/classes.
|
|
42
|
+
# STRUCTURE parameters set paired constructions - see btap/structure.rb.
|
|
43
43
|
|
|
44
44
|
# --- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --- #
|
|
45
|
-
# BTAP
|
|
46
|
-
# hold sub-variants based on cladding/veneer, e.g.:
|
|
47
|
-
#
|
|
48
|
-
# - "BTAP-ExteriorWall-WoodFramed-5" ... brick veneer
|
|
49
|
-
# - "BTAP-ExteriorWall-WoodFramed-1" ... wood siding
|
|
50
|
-
#
|
|
51
|
-
# Not all of these sub-variants are currently used within BTAP, e.g.
|
|
52
|
-
# "BTAP-ExteriorWall-WoodFramed-1" is unused. BTAP/TBD data is limited
|
|
53
|
-
# to the following wall assemblies (paired LP & HP variants), which
|
|
54
|
-
# eventually should be located in a shared file (e.g. CSV, JSON).
|
|
45
|
+
# BTAP/TBD data groups above-grade wall assemblies as paired LP/HP variants.
|
|
55
46
|
#
|
|
56
47
|
# ----- Low Performance (LP) assemblies
|
|
57
48
|
# ID : layers
|
|
@@ -70,7 +61,7 @@ module BTAP
|
|
|
70
61
|
# MASS8 : precast | xps | wool | frame | gypsum ... switch from MASS4
|
|
71
62
|
#
|
|
72
63
|
# Paired LPs & HPs vall variants are critical for 'uprating' cases, e.g.
|
|
73
|
-
# NECB2017/2020. See below, and
|
|
64
|
+
# NECB2017/2020/2025. See below, and additional Notes at the very end.
|
|
74
65
|
|
|
75
66
|
MASS2 = "BTAP-ExteriorWall-Mass-2" # LP wall
|
|
76
67
|
MASS2_BAD = "BTAP-ExteriorWall-Mass-2 bad" # LP "bad" PSI factors
|
|
@@ -100,114 +91,101 @@ module BTAP
|
|
|
100
91
|
STEL2_BAD = "BTAP-ExteriorWall-SteelFramed-2 bad"
|
|
101
92
|
STEL2_GOOD = "BTAP-ExteriorWall-SteelFramed-2 good"
|
|
102
93
|
|
|
103
|
-
|
|
104
|
-
|
|
94
|
+
# Insulated roofs, exposed floors (and attic floors) are also up/de-rated.
|
|
95
|
+
ROOF = "BTAP-ExteriorRoof-IEAD-4" # no LP/HP variants
|
|
96
|
+
FLOOR = "BTAP-ExteriorFloor-SteelFramed-1" # no LP/HP variants
|
|
105
97
|
|
|
106
98
|
# --- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --- #
|
|
107
|
-
#
|
|
108
|
-
# TBD
|
|
99
|
+
# BTAP/TBD is de/activated by setting the NECB 'tbd_option' as follows:
|
|
100
|
+
# - "none" : TBD is deactivated, i.e. no uprating
|
|
101
|
+
# - "bad" or "good": TBD derates envelope
|
|
102
|
+
# - "uprate" : TBD uprates initial Uo ... prior to derating
|
|
109
103
|
#
|
|
110
|
-
#
|
|
111
|
-
# - superclass for BTAP1980TO2010
|
|
112
|
-
# 2. NECB2011
|
|
113
|
-
# - superclass for NECB2015
|
|
114
|
-
# - superclass for NECB2017 (inherits from NECB2015)
|
|
115
|
-
# - superclass for NECB2020 (inherits from NECB2017)
|
|
116
|
-
# - superclass for ECMS
|
|
104
|
+
# - (see necb_2011.rb/apply_thermal_bridging)
|
|
117
105
|
#
|
|
118
|
-
#
|
|
119
|
-
#
|
|
120
|
-
#
|
|
121
|
-
#
|
|
122
|
-
#
|
|
123
|
-
#
|
|
124
|
-
#
|
|
125
|
-
# i.e. 'none' (see the NOTE on this topic at the end of this document). To
|
|
126
|
-
# instead assess prescriptive Ut compliance for vintages NECB2017 and
|
|
127
|
-
# NECB2020, the BTAP/TBD must be set to "uprate" so it can iteratively reset
|
|
128
|
-
# combined Uo & PSI factors towards finding the least expensive, yet
|
|
129
|
-
# compliant, combination. Why? Improved Uo assembly variants are necessarily
|
|
130
|
-
# required, given:
|
|
106
|
+
# For NECB editions prior to NECB2017, the current recommended BTAP policy
|
|
107
|
+
# is to switch off TBD (i.e. 'none') - see Note on this topic at the end of
|
|
108
|
+
# this document. To instead assess prescriptive Ut compliance for NECB
|
|
109
|
+
# editions 2017 through 2025, BTAP/TBD must be set to "uprate" so it can
|
|
110
|
+
# iteratively reset combined Uo & PSI factors towards finding the most
|
|
111
|
+
# affordable (yet compliant) Ut combination. Why? Improved Uo assembly
|
|
112
|
+
# variants are necessarily required, given:
|
|
131
113
|
#
|
|
132
114
|
# Ut = Uo + ( ∑psi x L )/A + ( ∑khi x n )/A (ref: rd2.github.io/tbd)
|
|
133
115
|
#
|
|
134
116
|
# If one ignores linear ("( ∑psi x L )/A") and point ("( ∑khi x n )/A")
|
|
135
|
-
# conductances, Ut simply equates to Uo. Yet for ANY added linear or
|
|
136
|
-
#
|
|
137
|
-
#
|
|
138
|
-
#
|
|
139
|
-
#
|
|
140
|
-
#
|
|
141
|
-
#
|
|
142
|
-
#
|
|
143
|
-
#
|
|
144
|
-
#
|
|
145
|
-
#
|
|
146
|
-
#
|
|
147
|
-
# will find NECB2017 or NECB2020 compliant combinations (prescriptive
|
|
148
|
-
# path) for EVERY OpenStudio model. Read here as to "why?":
|
|
117
|
+
# conductances, Ut simply equates to Uo. Yet for ANY added linear or point
|
|
118
|
+
# conductance, Uo factors must necessarily be lower than required NECB2017+
|
|
119
|
+
# Ut factor requirements. BTAP HP wall assembly variants indeed offer much
|
|
120
|
+
# lower Uo factors (than required Ut), down to 0.1 W/m2.K or ~R70. These
|
|
121
|
+
# BTAP upgrades certainly provide more options for attaining required Ut
|
|
122
|
+
# factors. In practice, this simply implies a thicker insulation layer. For
|
|
123
|
+
# others, it involves more radical assembly changes, such as switching over
|
|
124
|
+
# to the latest commercially-available HP thermally-broken cladding clips.
|
|
125
|
+
# While some solutions are simple (free) detailing changes, most
|
|
126
|
+
# improvements increase construction costs. Despite adding new BTAP HP
|
|
127
|
+
# assemblies, it is unlikely that TBD will find NECB2017+ compliant
|
|
128
|
+
# combinations (prescriptive path) for EVERY OpenStudio model. Why?
|
|
149
129
|
#
|
|
150
|
-
# github.com/rd2/tbd/blob/
|
|
151
|
-
#
|
|
130
|
+
# github.com/rd2/tbd/blob/dd6f12f8f2c24950485918c7eaca57d8f091a64d/spec/
|
|
131
|
+
# tbd_tests_spec.rb#L1674
|
|
152
132
|
#
|
|
153
133
|
# For these reasons, BTAP's use of TBD rests on an ITERATIVE uprating
|
|
154
|
-
# solution for NECB2017
|
|
134
|
+
# solution for NECB2017+:
|
|
155
135
|
#
|
|
156
136
|
# 1. TBD attempts to achieve NECB-required area-weighted Ut factors
|
|
157
137
|
# for above-grade walls (then for roofs and exposed floors),
|
|
158
138
|
# starting with the least expensive combination:
|
|
159
|
-
# -
|
|
160
|
-
# - "bad"
|
|
139
|
+
# - LP variant
|
|
140
|
+
# - "bad" thermal bridging details
|
|
161
141
|
#
|
|
162
142
|
# 2. If, for a given OpenStudio model, required area-weighted Ut
|
|
163
143
|
# factors cannot be achieved, TBD then switches over to "good"
|
|
164
|
-
#
|
|
165
|
-
#
|
|
144
|
+
# thermal bridging detailing for that same assembly, and repeats the
|
|
145
|
+
# exercise.
|
|
166
146
|
#
|
|
167
|
-
# 3. A subsequent failed attempt triggers a switch over to
|
|
168
|
-
#
|
|
147
|
+
# 3. A subsequent failed attempt triggers a switch over to HP variants.
|
|
148
|
+
# For instance:
|
|
169
149
|
# - "BTAP-ExteriorWall-WoodFramed-5" ... switches over to:
|
|
170
150
|
# - "BTAP-ExteriorWall-WoodFramed-7"
|
|
171
151
|
#
|
|
172
152
|
# ... switching over to another assembly this way also means
|
|
173
|
-
# reverting back to "bad"
|
|
153
|
+
# reverting back to "bad" thermal bridging PSI factors.
|
|
174
154
|
#
|
|
175
|
-
# 4. A final switch to "good"
|
|
155
|
+
# 4. A final switch to "good" details is triggered if needed.
|
|
176
156
|
#
|
|
177
157
|
# If NONE of the available combinations are sufficient:
|
|
178
|
-
# - TBD red-flags a failed attempt at NECB2017
|
|
179
|
-
# - TBD keeps iteration #4 Uo + PSI combo, then derates before
|
|
180
|
-
# BTAP simulation run (giving some performance gap indication)
|
|
158
|
+
# - TBD red-flags a failed attempt at NECB2017+ compliance
|
|
159
|
+
# - TBD keeps iteration #4 (HP Uo + "good" PSI combo), then derates before
|
|
160
|
+
# a BTAP simulation run (giving some performance gap indication)
|
|
181
161
|
|
|
182
162
|
# --- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --- #
|
|
183
163
|
# Notes:
|
|
184
164
|
#
|
|
185
|
-
# - Steel-framed assemblies: the
|
|
186
|
-
#
|
|
187
|
-
#
|
|
188
|
-
#
|
|
189
|
-
#
|
|
190
|
-
# apples-to-apples comparison is ensured.
|
|
165
|
+
# - Steel-framed assemblies: the costed HP variant has metal cladding.
|
|
166
|
+
# The costed LP variant is wood-clad - something of an anomaly in
|
|
167
|
+
# commercial construction. By making the switch earlier to metal
|
|
168
|
+
# cladding, everywhere in Canada except (milder) SW BC and SW NS, it is
|
|
169
|
+
# hoped that a more consistent, apples-to-apples comparison is ensured.
|
|
191
170
|
#
|
|
192
|
-
# - ROOF and
|
|
193
|
-
# respectively. This is expected to change in the future @todo.
|
|
171
|
+
# - ROOF and FOOR surfaces refer to a single LP/HP selection respectively.
|
|
194
172
|
|
|
195
173
|
# --- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --- #
|
|
196
174
|
# Preset BTAP/TBD wall assembly parameters.
|
|
197
175
|
@@data = {}
|
|
198
176
|
|
|
199
177
|
# Construction sub-variant identified strictly by Uo, e.g. 0.314 W/m2.K.
|
|
200
|
-
@@data[MASS2] = {uos: [0.314, 0.278, 0.247, 0.210, 0.183]}
|
|
201
|
-
@@data[MASSB] = {uos: [0.130, 0.100]}
|
|
178
|
+
@@data[MASS2] = {uos: [0.314, 0.278, 0.247, 0.210, 0.183]} # :lp
|
|
179
|
+
@@data[MASSB] = {uos: [0.130, 0.100]} # :hp
|
|
202
180
|
|
|
203
|
-
@@data[MASS4] = {uos: [0.314, 0.278, 0.247, 0.210, 0.183]}
|
|
204
|
-
@@data[MASS8] = {uos: [0.130, 0.100]}
|
|
181
|
+
@@data[MASS4] = {uos: [0.314, 0.278, 0.247, 0.210, 0.183]} # :lp
|
|
182
|
+
@@data[MASS8] = {uos: [0.130, 0.100]} # :hp
|
|
205
183
|
|
|
206
|
-
@@data[WOOD5] = {uos: [0.314, 0.278, 0.247, 0.210, 0.183]}
|
|
207
|
-
@@data[WOOD7] = {uos: [0.130]}
|
|
184
|
+
@@data[WOOD5] = {uos: [0.314, 0.278, 0.247, 0.210, 0.183]} # :lp
|
|
185
|
+
@@data[WOOD7] = {uos: [0.130]} # :hp
|
|
208
186
|
|
|
209
|
-
@@data[STEL1] = {uos: [0.314, 0.278]}
|
|
210
|
-
@@data[STEL2] = {uos: [0.247, 0.210, 0.183, 0.130, 0.100, 0.080]}
|
|
187
|
+
@@data[STEL1] = {uos: [0.314, 0.278]} # :lp
|
|
188
|
+
@@data[STEL2] = {uos: [0.247, 0.210, 0.183, 0.130, 0.100, 0.080]} # :hp
|
|
211
189
|
|
|
212
190
|
@@data[FLOOR] = {uos: [0.227, 0.183, 0.162, 0.142, 0.116, 0.101]}
|
|
213
191
|
@@data[ROOF ] = {uos: [0.227, 0.193, 0.183, 0.162, 0.156, 0.142, 0.138, 0.121, 0.100]}
|
|
@@ -221,7 +199,7 @@ module BTAP
|
|
|
221
199
|
|
|
222
200
|
# Thermal bridge types :balcony, :party and :joint are NOT expected to
|
|
223
201
|
# be processed soon within BTAP. They are neither costed out, nor are carbon
|
|
224
|
-
# intensities (
|
|
202
|
+
# intensities (kgCO2-eq/linear meter) associated to them. At some point, it
|
|
225
203
|
# may be wise to do so (notably cantilevered balconies in MURBs) - @todo.
|
|
226
204
|
# Default, generic BETBG PSI factors are nonetheless provided here:
|
|
227
205
|
# - for "bad" BTAP cases : generic BETBG set "bad"
|
|
@@ -405,26 +383,108 @@ module BTAP
|
|
|
405
383
|
# --- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- --- #
|
|
406
384
|
|
|
407
385
|
##
|
|
408
|
-
#
|
|
386
|
+
# Returns current BTAP list of admissible edges (i.e. linear thermal
|
|
387
|
+
# bridges), as Symbols. These are key for 'gen_tallies' & BTAP costing (TBD
|
|
388
|
+
# tracks a wider range of thermal bridge types than BTAP costing currently
|
|
389
|
+
# supports). This may be updated in the future.
|
|
409
390
|
#
|
|
410
|
-
# @
|
|
411
|
-
|
|
412
|
-
|
|
391
|
+
# @return [Array] admissible BTAP-costed edge types (Symbols)
|
|
392
|
+
def self.admissible_edges
|
|
393
|
+
[:rimjoist, :parapet, :fenestration, :corner, :grade]
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
##
|
|
397
|
+
# Returns paired low-performance (LP) & high-performance (HP) BTAP-costed
|
|
398
|
+
# wall assembly identifiers, based on user-provided assembly ID. If valid,
|
|
399
|
+
# solution returns a Hash holding 2 keys (:lp and :hp).
|
|
413
400
|
#
|
|
414
|
-
# @
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
return
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
401
|
+
# @param id [String] a valid BTAP-costed wall assembly identifier
|
|
402
|
+
#
|
|
403
|
+
# @return [Hash] assembly :lp & :hp identifiers (empty if invalid input)
|
|
404
|
+
def assembly_id(id = "")
|
|
405
|
+
return {} unless @@data.key?(id)
|
|
406
|
+
pair = {}
|
|
407
|
+
|
|
408
|
+
if id == MASS2 || id == MASSB
|
|
409
|
+
pair[:lp] = MASS2
|
|
410
|
+
pair[:hp] = MASSB
|
|
411
|
+
elsif id == MASS4 || id == MASS8
|
|
412
|
+
pair[:lp] = MASS4
|
|
413
|
+
pair[:hp] = MASS8
|
|
414
|
+
elsif id == WOOD5 || id == WOOD7
|
|
415
|
+
pair[:lp] = WOOD5
|
|
416
|
+
pair[:hp] = WOOD7
|
|
417
|
+
elsif id == STEL1 || id == STEL2
|
|
418
|
+
pair[:lp] = STEL1
|
|
419
|
+
pair[:hp] = STEL2
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
pair
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
##
|
|
426
|
+
# Returns an above-grade (opaque) BTAP-costed assembly identifier that
|
|
427
|
+
# matches selection criteria. Solution may return an assembly's low
|
|
428
|
+
# performance (:lp) or its paired high-performance (:hp) variant. Defaults
|
|
429
|
+
# to STEL2 (walls) if invalid input.
|
|
430
|
+
#
|
|
431
|
+
# @param [Hash] argh options
|
|
432
|
+
# @option argh [Symbol] :stype (:walls, :floors or :roofs)
|
|
433
|
+
# @option argh [Symbol] :perform (:lp or :hp) wall variant
|
|
434
|
+
# @option argh [Symbol] :framing (:steel, :wood, :cmu)
|
|
435
|
+
# @option argh [Symbol] :cladding (:heavy)
|
|
436
|
+
# @option argh [Symbol] :finish (:heavy)
|
|
437
|
+
# @option argh [String] :id a BTAP-costed assembly (often :lp)
|
|
438
|
+
# @option argh [Float] :uo desired U-factor
|
|
439
|
+
#
|
|
440
|
+
# @return [String] BTAP costed assembly identifier
|
|
441
|
+
def costed_assembly(argh = {})
|
|
442
|
+
return STEL2 unless argh.respond_to?(:keys)
|
|
443
|
+
|
|
444
|
+
# Key input.
|
|
445
|
+
stype = argh.key?(:stype) ? argh[:stype] : :walls
|
|
446
|
+
stype = :walls unless [:roofs, :floors].include?(stype)
|
|
447
|
+
|
|
448
|
+
# No BTAP variants yet for roofs or floors.
|
|
449
|
+
return ROOF if stype == :roofs
|
|
450
|
+
return FLOOR if stype == :floors
|
|
451
|
+
|
|
452
|
+
# Low-performance (:lp) or high-performance (:hp) variant?
|
|
453
|
+
perform = argh.key?(:perform) ? argh[:perform] : :lp
|
|
454
|
+
perform = :lp unless perform == :hp
|
|
455
|
+
|
|
456
|
+
# Prioritizing BTAP-costed wall assembly identifiers.
|
|
457
|
+
if argh.key?(:id)
|
|
458
|
+
pair = assembly_id(argh[:id])
|
|
459
|
+
|
|
460
|
+
unless pair.empty?
|
|
461
|
+
if argh.key?(:uo)
|
|
462
|
+
uo = costed_uo(pair[perform], argh[:uo])
|
|
463
|
+
perform = :hp if uo.nil?
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
return pair[perform]
|
|
467
|
+
end
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
# Relying instead on STRUCTURE-based parameters.
|
|
471
|
+
framing = argh.key?(:framing) ? argh[:framing ] : :steel
|
|
472
|
+
cladding = argh.key?(:cladding) ? argh[:cladding] : :light
|
|
473
|
+
finish = argh.key?(:finish) ? argh[:finish ] : :light
|
|
474
|
+
|
|
475
|
+
framing = :steel unless [:wood, :cmu].include?(framing)
|
|
476
|
+
cladding = :light unless cladding == :heavy
|
|
477
|
+
finish = :light unless finish == :heavy
|
|
478
|
+
|
|
479
|
+
# Select BTAP-costed wall assembly, matching:
|
|
480
|
+
# - BTAP::Structure generated construction parameters (e.g. framing)
|
|
481
|
+
# - requested high- (:hp) vs low-performance (:lp) variant
|
|
482
|
+
pair = {}
|
|
483
|
+
|
|
484
|
+
# Note: Chosen PSI factor sets and matching OpenStudio constructions
|
|
485
|
+
# shouldn't strictly be based on selected BTAP-costed assemblies (e.g.
|
|
426
486
|
# wood-framed vs steel-framed, cladding choice), but also on selected
|
|
427
|
-
# building
|
|
487
|
+
# building STRUCTURE, e.g.:
|
|
428
488
|
#
|
|
429
489
|
# - "wood-framed" MURB
|
|
430
490
|
# - "steel post/beam" office building
|
|
@@ -432,43 +492,32 @@ module BTAP
|
|
|
432
492
|
# - "metal(-building)" warehouse
|
|
433
493
|
# - "mass-timber (CLT)" university pavilion
|
|
434
494
|
#
|
|
435
|
-
#
|
|
436
|
-
# structural loads (and by the same token, 'heat') to a
|
|
437
|
-
# structure. Examples include balconies, parapets and
|
|
438
|
-
# Highly conductive building
|
|
439
|
-
# thermal bridging
|
|
495
|
+
# Linear thermal bridges often consist of structural elements that
|
|
496
|
+
# transmit envelope structural loads (and by the same token, 'heat') to a
|
|
497
|
+
# building's main structure. Examples include balconies, parapets and
|
|
498
|
+
# shelf angles. Highly conductive building structural materials (e.g.
|
|
499
|
+
# steel, concrete) exacerbate thermal bridging - building structural
|
|
500
|
+
# choices should matter.
|
|
440
501
|
#
|
|
441
502
|
# The BTAP::Structure module generates such attributes, yet BTAP's costed
|
|
442
503
|
# thermal bridging database doesn't yet distinguish between building
|
|
443
504
|
# structures - @todo. For the moment, BTAP PSI set selection is strictly
|
|
444
505
|
# based on BTAP::Structure's :framing, :cladding and :finish attributes,
|
|
445
|
-
# which
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
case stype
|
|
450
|
-
when :roofs then return ROOF
|
|
451
|
-
when :floors then return FLOOR
|
|
506
|
+
# which are set prior to initiating BTAP's TBD solution.
|
|
507
|
+
case framing
|
|
508
|
+
when :wood then pair = assembly_id(WOOD5)
|
|
509
|
+
when :cmu then pair = assembly_id(MASS2)
|
|
452
510
|
else
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
else
|
|
461
|
-
if structure.cladding == :heavy && structure.finish == :heavy
|
|
462
|
-
c1 = MASS4
|
|
463
|
-
c2 = MASS8
|
|
464
|
-
else
|
|
465
|
-
c1 = STEL1
|
|
466
|
-
c2 = STEL2
|
|
467
|
-
end
|
|
468
|
-
end
|
|
511
|
+
pair = assembly_id(STEL1)
|
|
512
|
+
pair = assembly_id(MASS4) if cladding == :heavy && finish == :heavy
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
if argh.key?(:uo)
|
|
516
|
+
uo = costed_uo(pair[perform], argh[:uo])
|
|
517
|
+
perform = :hp if uo.nil?
|
|
469
518
|
end
|
|
470
519
|
|
|
471
|
-
perform
|
|
520
|
+
pair[perform]
|
|
472
521
|
end
|
|
473
522
|
|
|
474
523
|
##
|
|
@@ -564,229 +613,399 @@ module BTAP
|
|
|
564
613
|
attr_reader :tally
|
|
565
614
|
|
|
566
615
|
##
|
|
567
|
-
# Initializes
|
|
616
|
+
# Initializes BTAP/TBD data, uprates/derates, generates output.
|
|
568
617
|
#
|
|
569
618
|
# @param model [OpenStudio::Model::Model] a model
|
|
570
619
|
# @param [Hash] argh BTAP/TBD argument hash
|
|
571
|
-
# @option argh [BTAP::Structure] structure a BTAP STRUCTURE
|
|
572
|
-
# @option argh [Hash] walls exterior wall parameters
|
|
573
|
-
# @option argh [Hash] floors exposed floor parameters
|
|
574
|
-
# @option argh [Hash] roofs exterior roof parameters
|
|
575
|
-
# @option argh [:good, :bad] quality
|
|
576
|
-
# @option argh [Boolean] interpolate
|
|
620
|
+
# @option argh [BTAP::Structure] :structure a BTAP STRUCTURE instance
|
|
621
|
+
# @option argh [Hash] :walls exterior wall parameters (:uo, :ut)
|
|
622
|
+
# @option argh [Hash] :floors exposed floor parameters (:uo, :ut)
|
|
623
|
+
# @option argh [Hash] :roofs exterior roof parameters (:uo, :ut)
|
|
624
|
+
# @option argh [:good, :bad] :quality of thermal bridging (derating)
|
|
625
|
+
# @option argh [Boolean] :interpolate between costed Uo factors (uprating)
|
|
577
626
|
def initialize(model = nil, argh = {})
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
@tally = {}
|
|
627
|
+
mth = "BTAP::Bridging::#{__callee__}"
|
|
628
|
+
|
|
629
|
+
# Track warnings & errors.
|
|
582
630
|
@feedback = {logs: []}
|
|
583
|
-
lgs
|
|
631
|
+
lgs = @feedback[:logs]
|
|
632
|
+
|
|
633
|
+
# Store model & arguments.
|
|
634
|
+
@model = {osm: model, argh: argh}
|
|
584
635
|
|
|
585
|
-
#
|
|
636
|
+
# Validate and populate BTAP/TBD & OpenStudio model parameters. This does
|
|
586
637
|
# a safe TBD trial run, returning true if successful. If false, TBD leaves
|
|
587
638
|
# the model unaltered. Check @feedback logs.
|
|
588
|
-
return unless
|
|
639
|
+
return unless populate
|
|
589
640
|
|
|
590
|
-
#
|
|
591
|
-
|
|
592
|
-
complies = false
|
|
593
|
-
comply = {} # specific to :walls, :floors & :roofs (if uprating)
|
|
594
|
-
perform = :lp
|
|
595
|
-
quality = argh[:quality] == :good ? :good : :bad
|
|
596
|
-
combo = "#{perform.to_s}_#{quality.to_s}".to_sym # e.g. :lp_bad
|
|
597
|
-
args = {} # TBD's own argument hash
|
|
598
|
-
|
|
599
|
-
# Initialize surface types & TBD arguments for iterative uprating runs.
|
|
600
|
-
@model[:stypes].each do |stypes|
|
|
601
|
-
next unless argh[stypes].key?(:ut)
|
|
602
|
-
|
|
603
|
-
stype = stypes.to_s.chop
|
|
604
|
-
uprate = "uprate_#{stypes.to_s}".to_sym
|
|
605
|
-
option = "#{stype}_option".to_sym
|
|
606
|
-
ut = "#{stype}_ut".to_sym
|
|
607
|
-
|
|
608
|
-
args[uprate] = true
|
|
609
|
-
args[option] = "ALL #{stype} constructions"
|
|
610
|
-
args[ut ] = argh[stypes][:ut]
|
|
641
|
+
# Tracking uprating success, separately for :walls, :roofs vs :floors.
|
|
642
|
+
comply = {}
|
|
611
643
|
|
|
612
|
-
|
|
613
|
-
|
|
644
|
+
# TRUE if TBD successfully uprates all surface types.
|
|
645
|
+
complies = false
|
|
614
646
|
|
|
615
|
-
#
|
|
616
|
-
@model[:
|
|
617
|
-
args[:io_path] = v[combo] if v.key?(combo)
|
|
618
|
-
end
|
|
647
|
+
# Initialize TBD's own argument hash.
|
|
648
|
+
args = {option: "", io_path: inputs( @model[:quality] )}
|
|
619
649
|
|
|
620
|
-
|
|
650
|
+
# Uprating? Possibly 2x trial runs to identify candidate constructions.
|
|
651
|
+
2.times do |i|
|
|
652
|
+
break unless @model[:uprating]
|
|
653
|
+
break if complies
|
|
654
|
+
break if @model[:quality] == :good && i > 0
|
|
621
655
|
|
|
622
|
-
|
|
656
|
+
# Reset overall PSI factor quality.
|
|
657
|
+
@model[:quality] = :good unless i.zero?
|
|
623
658
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
initial = false
|
|
627
|
-
else
|
|
628
|
-
# Subsequent uprating runs. Upgrade technologies. Reset TBD args.
|
|
629
|
-
if quality == :bad
|
|
630
|
-
quality = :good
|
|
631
|
-
combo = "#{perform.to_s}_#{quality.to_s}".to_sym
|
|
659
|
+
# Reset TBD arguments.
|
|
660
|
+
args = {option: "", io_path: inputs( @model[:quality] )}
|
|
632
661
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
quality = :bad
|
|
640
|
-
combo = "#{perform.to_s}_#{quality.to_s}".to_sym
|
|
641
|
-
|
|
642
|
-
@model[:constructions].values.each do |v|
|
|
643
|
-
args[:io_path] = v[combo] if v.key?(combo)
|
|
644
|
-
end
|
|
645
|
-
end
|
|
662
|
+
# Add surface type (uprating) inputs to TBD argument hash.
|
|
663
|
+
@model[:stypes].each do |stypes, u|
|
|
664
|
+
stype = stypes.to_s.chop # e.g. "wall" (from :walls)
|
|
665
|
+
uprate = "uprate_#{stypes.to_s}".to_sym # e.g. :uprate_walls
|
|
666
|
+
option = "#{stype}_option".to_sym # e.g. :wall_option
|
|
667
|
+
ut = "#{stype}_ut".to_sym # e.g. :wall_ut
|
|
646
668
|
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
669
|
+
args[uprate] = true
|
|
670
|
+
args[option] = "ALL #{stype} constructions"
|
|
671
|
+
args[ut ] = u[:ut]
|
|
650
672
|
|
|
651
|
-
|
|
652
|
-
args.delete(uo) if args.key?(uo)
|
|
653
|
-
end
|
|
673
|
+
comply[stypes] = false
|
|
654
674
|
end
|
|
655
675
|
|
|
656
|
-
#
|
|
676
|
+
# Trial runs on cloned OpenStudio model.
|
|
657
677
|
mdl = OpenStudio::Model::Model.new
|
|
658
678
|
mdl.addObjects(model.toIdfFile.objects)
|
|
659
679
|
TBD.clean!
|
|
680
|
+
TBD.process(mdl, args)
|
|
681
|
+
|
|
682
|
+
# If uprating is successful, TBD 'args' hash holds (new) uprated Uo
|
|
683
|
+
# keys/values for :walls, :floors and/or :roofs.
|
|
684
|
+
complies = true
|
|
685
|
+
|
|
686
|
+
# Even if all constructions were successfully uprated by TBD, BTAP
|
|
687
|
+
# may not hold costed assembly variants that match required targets.
|
|
688
|
+
# If TBD-uprated Uo factors are lower than any of these admissible
|
|
689
|
+
# BTAP Uo factors, the solution logs the failed attempt and defaults
|
|
690
|
+
# to the lowest Uo factors in the BTAP costing database.
|
|
691
|
+
@model[:stypes].keys.each do |stypes|
|
|
692
|
+
break unless complies
|
|
693
|
+
|
|
694
|
+
# Initialize.
|
|
695
|
+
comply[stypes] = true
|
|
660
696
|
|
|
661
|
-
|
|
662
|
-
|
|
697
|
+
stype_uo = "#{stypes.to_s.chop}_uo".to_sym
|
|
698
|
+
target = args.key?(stype_uo) ? args[stype_uo] : nil
|
|
699
|
+
|
|
700
|
+
unless target
|
|
701
|
+
comply[stypes] = false
|
|
702
|
+
complies = false
|
|
703
|
+
next
|
|
704
|
+
else
|
|
705
|
+
bOPTs = {} # building
|
|
706
|
+
bOPTs[:id ] = @model[:building][stypes][:id]
|
|
707
|
+
bOPTs[:stype] = stypes
|
|
708
|
+
bOPTs[:uo ] = target
|
|
709
|
+
bID = costed_assembly(bOPTs)
|
|
710
|
+
|
|
711
|
+
unless costed_uo(bID, target)
|
|
712
|
+
comply[stypes] = false
|
|
713
|
+
complies = false
|
|
714
|
+
next
|
|
715
|
+
end
|
|
716
|
+
|
|
717
|
+
unless @model[:attic][stypes].empty?
|
|
718
|
+
types = case stypes
|
|
719
|
+
when :roofs then :floors
|
|
720
|
+
when :floors then :roofs
|
|
721
|
+
else :walls
|
|
722
|
+
end
|
|
723
|
+
|
|
724
|
+
aOPTs = {} # attic(s)
|
|
725
|
+
aOPTs[:id ] = @model[:attic][types][:id]
|
|
726
|
+
aOPTs[:stype] = types
|
|
727
|
+
aOPTs[:uo ] = target
|
|
728
|
+
aID = costed_assembly(aOPTs)
|
|
729
|
+
|
|
730
|
+
unless costed_uo(aID, target)
|
|
731
|
+
comply[types] = false
|
|
732
|
+
complies = false
|
|
733
|
+
next
|
|
734
|
+
end
|
|
735
|
+
end
|
|
663
736
|
|
|
664
|
-
|
|
737
|
+
@model[:spaces].each do |id, sp|
|
|
738
|
+
break unless comply[stypes]
|
|
665
739
|
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
740
|
+
sOPTs = {} # customized spaces
|
|
741
|
+
sOPTs[:id ] = sp[stypes][:id]
|
|
742
|
+
sOPTs[:stype] = stypes
|
|
743
|
+
sOPTs[:uo ] = target
|
|
744
|
+
sID = costed_assembly(sOPTs)
|
|
745
|
+
|
|
746
|
+
unless costed_uo(sID, target)
|
|
747
|
+
comply[stypes] = false
|
|
748
|
+
complies = false
|
|
749
|
+
next
|
|
750
|
+
end
|
|
751
|
+
end
|
|
752
|
+
end
|
|
671
753
|
end
|
|
672
754
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
# constructions rather than roof or floor constructions, due to the
|
|
678
|
-
# typically larger density of linear thermal bridging per surface area
|
|
679
|
-
# Yet even if all constructions were successfully uprated by TBD, one
|
|
680
|
-
# must then determine if BTAP holds admissible (i.e. costed) assembly
|
|
681
|
-
# variants with corresponding Uo factors. If TBD-uprated Uo factors are
|
|
682
|
-
# lower than any of these admissible BTAP Uo factors, then no
|
|
683
|
-
# commercially available solution can been identified.
|
|
684
|
-
@model[:stypes].each do |stypes|
|
|
685
|
-
next unless comply.key?(stypes) # true only if uprating
|
|
686
|
-
|
|
687
|
-
# TBD-estimated Uo target to meet NECB-required Ut - nil if invalid.
|
|
755
|
+
# Skip if first iteration fails.
|
|
756
|
+
next if i.zero? && complies == false
|
|
757
|
+
|
|
758
|
+
@model[:stypes].each do |stypes, u|
|
|
688
759
|
stype_uo = "#{stypes.to_s.chop}_uo".to_sym
|
|
689
760
|
target = args.key?(stype_uo) ? args[stype_uo] : nil
|
|
690
|
-
assembly = self.costed_assembly(argh[:structure], stypes, perform)
|
|
691
761
|
|
|
692
|
-
|
|
762
|
+
if target
|
|
763
|
+
u[:uo] = target
|
|
764
|
+
|
|
765
|
+
bOPTs = {} # building
|
|
766
|
+
bOPTs[:id ] = @model[:building][stypes][:id]
|
|
767
|
+
bOPTs[:stype] = stypes
|
|
768
|
+
bOPTs[:uo ] = target
|
|
769
|
+
bID = costed_assembly(bOPTs)
|
|
770
|
+
|
|
771
|
+
@model[:building][stypes][:id] = bID
|
|
772
|
+
bUo = costed_uo(bID, target)
|
|
773
|
+
|
|
774
|
+
if bUo
|
|
775
|
+
bUo = target if @model[:interpolate]
|
|
693
776
|
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
777
|
+
# Candidate construction successfully identified.
|
|
778
|
+
@model[:building][stypes][:compliant] = true
|
|
779
|
+
@model[:building][stypes][:uo ] = bUo
|
|
780
|
+
else
|
|
781
|
+
bUo = lowest_uo(bID)
|
|
782
|
+
comply[stypes] = false
|
|
783
|
+
complies = false
|
|
784
|
+
|
|
785
|
+
# Fallback.
|
|
786
|
+
@model[:building][stypes][:compliant] = false
|
|
787
|
+
@model[:building][stypes][:uo ] = bUo
|
|
788
|
+
end
|
|
789
|
+
|
|
790
|
+
unless @model[:attic][stypes].empty?
|
|
791
|
+
types = case stypes
|
|
792
|
+
when :roofs then :floors
|
|
793
|
+
when :floors then :roofs
|
|
794
|
+
else :walls
|
|
795
|
+
end
|
|
796
|
+
|
|
797
|
+
aOPTs = {} # attic
|
|
798
|
+
aOPTs[:id ] = @model[:attic][types][:id]
|
|
799
|
+
aOPTs[:stype] = types
|
|
800
|
+
aOPTs[:uo ] = target
|
|
801
|
+
aID = costed_assembly(aOPTs)
|
|
802
|
+
|
|
803
|
+
@model[:attic][types][:id] = aID
|
|
804
|
+
aUo = costed_uo(aID, target)
|
|
805
|
+
|
|
806
|
+
if aUo
|
|
807
|
+
aUo = target if @model[:interpolate]
|
|
808
|
+
|
|
809
|
+
# Candidate construction successfully identified.
|
|
810
|
+
@model[:attic][types][:compliant] = true
|
|
811
|
+
@model[:attic][types][:uo ] = aUo
|
|
812
|
+
else
|
|
813
|
+
aUo = lowest_uo(aID)
|
|
814
|
+
comply[types] = false
|
|
815
|
+
complies = false
|
|
816
|
+
|
|
817
|
+
# Fallback.
|
|
818
|
+
@model[:attic][types][:compliant] = false
|
|
819
|
+
@model[:attic][types][:uo ] = aUo
|
|
820
|
+
end
|
|
821
|
+
end
|
|
822
|
+
|
|
823
|
+
@model[:spaces].each do |id, sp|
|
|
824
|
+
sOPTs = {} # customized space(s)
|
|
825
|
+
sOPTs[:id ] = sp[stypes][:id]
|
|
826
|
+
sOPTs[:stype] = stypes
|
|
827
|
+
sOPTs[:uo ] = target
|
|
828
|
+
sID = costed_assembly(sOPTs)
|
|
829
|
+
|
|
830
|
+
sp[stypes][:id] = sID
|
|
831
|
+
sUo = costed_uo(sID, target)
|
|
832
|
+
|
|
833
|
+
if sUo
|
|
834
|
+
sUo = target if @model[:interpolate]
|
|
835
|
+
|
|
836
|
+
# Candidate construction successfully identified.
|
|
837
|
+
sp[stypes][:compliant] = true
|
|
838
|
+
sp[stypes][:uo ] = sUo
|
|
839
|
+
else
|
|
840
|
+
sUo = lowest_uo(sID)
|
|
841
|
+
comply[stypes] = false
|
|
842
|
+
complies = false
|
|
843
|
+
|
|
844
|
+
# Fallback.
|
|
845
|
+
sp[stypes][:compliant] = false
|
|
846
|
+
sp[stypes][:uo] = sUo
|
|
847
|
+
end
|
|
848
|
+
end
|
|
697
849
|
else
|
|
698
|
-
uo = self.lowest_uo(assembly)
|
|
699
850
|
comply[stypes] = false
|
|
700
|
-
|
|
851
|
+
complies = false
|
|
852
|
+
|
|
853
|
+
bID = @model[:building][stypes][:id]
|
|
854
|
+
bUo = lowest_uo(bID)
|
|
855
|
+
|
|
856
|
+
bOPTs = {}
|
|
857
|
+
bOPTs[:id ] = bID
|
|
858
|
+
bOPTs[:stype] = stypes
|
|
859
|
+
bOPTs[:uo ] = bUo
|
|
860
|
+
bID = costed_assembly(bOPTs)
|
|
861
|
+
bUo = costed_uo(bID, bUo)
|
|
862
|
+
|
|
863
|
+
@model[:building][stypes][:compliant] = false
|
|
864
|
+
@model[:building][stypes][:id ] = bID
|
|
865
|
+
@model[:building][stypes][:uo ] = bUo
|
|
866
|
+
|
|
867
|
+
u[:uo] = bUo
|
|
868
|
+
|
|
869
|
+
unless @model[:attic][stypes].empty?
|
|
870
|
+
types = case stypes
|
|
871
|
+
when :roofs then :floors
|
|
872
|
+
when :floors then :roofs
|
|
873
|
+
else :walls
|
|
874
|
+
end
|
|
875
|
+
|
|
876
|
+
aID = @model[:building][types][:id]
|
|
877
|
+
aUo = lowest_uo(aID)
|
|
878
|
+
|
|
879
|
+
aOPTs = {}
|
|
880
|
+
aOPTs[:id ] = aID
|
|
881
|
+
aOPTs[:stype] = types
|
|
882
|
+
aOPTs[:uo ] = aUo
|
|
883
|
+
aID = costed_assembly(aOPTs)
|
|
884
|
+
aUo = costed_uo(aID, aUo)
|
|
885
|
+
|
|
886
|
+
@model[:attic][types][:compliant] = false
|
|
887
|
+
@model[:attic][types][:id ] = aID
|
|
888
|
+
@model[:attic][types][:uo ] = aUo
|
|
889
|
+
end
|
|
701
890
|
|
|
702
|
-
|
|
703
|
-
|
|
891
|
+
@model[:spaces].each do |id, sp|
|
|
892
|
+
sID = sp[stypes][:id]
|
|
893
|
+
sUo = lowest_uo(sID)
|
|
704
894
|
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
895
|
+
sOPTs = {}
|
|
896
|
+
sOPTs[:id ] = sID
|
|
897
|
+
sOPTs[:stype] = stypes
|
|
898
|
+
sOPTs[:uo ] = sUo
|
|
899
|
+
sID = costed_assembly(sOPTs)
|
|
900
|
+
sUo = costed_uo(sID, sUo)
|
|
708
901
|
|
|
709
|
-
|
|
902
|
+
sp[stypes][:compliant] = false
|
|
903
|
+
sp[stypes][:id ] = sID
|
|
904
|
+
sp[stypes][:uo ] = sUo
|
|
905
|
+
end
|
|
906
|
+
end
|
|
710
907
|
end
|
|
711
|
-
|
|
712
|
-
# Exit if successful or if final BTAP uprating option.
|
|
713
|
-
break if combo == :hp_good
|
|
714
|
-
break if complies
|
|
715
908
|
end
|
|
716
909
|
|
|
717
910
|
# Post-loop steps (if uprating).
|
|
718
|
-
@model[:stypes].each do |stypes|
|
|
911
|
+
@model[:stypes].each do |stypes, u|
|
|
719
912
|
next unless comply.key?(stypes) # true only if uprating
|
|
720
913
|
|
|
721
|
-
# Cancel uprating request before final derating.
|
|
722
|
-
stype = stypes.to_s.chop
|
|
723
|
-
uprate = "uprate_#{stypes.to_s}".to_sym
|
|
724
|
-
option = "#{stype}_option".to_sym
|
|
725
|
-
ut = "#{stype}_ut".to_sym
|
|
726
|
-
args.delete(uprate)
|
|
727
|
-
args.delete(option)
|
|
728
|
-
args.delete(ut)
|
|
729
|
-
|
|
730
914
|
# Reset uprated Uo factor for each 'deratable' construction.
|
|
731
|
-
@model[:
|
|
732
|
-
|
|
915
|
+
bLC = @model[:building][stypes][:lc]
|
|
916
|
+
bFr = @model[:building][stypes][:film]
|
|
917
|
+
bIx = @model[:building][stypes][:indx]
|
|
918
|
+
bUo = @model[:building][stypes][:uo]
|
|
919
|
+
bRSI = TBD.resetUo(bLC, bFr, bIx, bUo)
|
|
920
|
+
|
|
921
|
+
# Re/set Uo as AdditionalProperty:
|
|
922
|
+
addprop(bLC, "btap_uo", bUo)
|
|
923
|
+
|
|
924
|
+
# Repeat for attics (if applicable).
|
|
925
|
+
unless @model[:attic][stypes].empty?
|
|
926
|
+
types = case stypes
|
|
927
|
+
when :roofs then :floors
|
|
928
|
+
when :floors then :roofs
|
|
929
|
+
else :walls
|
|
930
|
+
end
|
|
931
|
+
|
|
932
|
+
aLC = @model[:attic][types][:lc]
|
|
933
|
+
aFr = @model[:attic][types][:film]
|
|
934
|
+
aIx = @model[:attic][types][:indx]
|
|
935
|
+
aUo = @model[:attic][types][:uo]
|
|
936
|
+
aRSI = TBD.resetUo(aLC, aFr, aIx, aUo)
|
|
937
|
+
|
|
938
|
+
# Re/set Uo as AdditionalProperty:
|
|
939
|
+
addprop(aLC, "btap_uo", aUo)
|
|
940
|
+
end
|
|
733
941
|
|
|
734
|
-
|
|
942
|
+
# Repeat for customized spaces (if applicable).
|
|
943
|
+
@model[:spaces].values.each do |sp|
|
|
944
|
+
sLC = sp[stypes][:lc]
|
|
945
|
+
sFr = sp[stypes][:film]
|
|
946
|
+
sIx = sp[stypes][:indx]
|
|
947
|
+
sUo = sp[stypes][:uo]
|
|
948
|
+
sRSI = TBD.resetUo(sLC, sFr, sIx, sUo)
|
|
949
|
+
|
|
950
|
+
# Re/set Uo as AdditionalProperty:
|
|
951
|
+
addprop(sLC, "btap_uo", sUo)
|
|
735
952
|
end
|
|
736
953
|
end
|
|
737
954
|
|
|
738
|
-
@model[:
|
|
739
|
-
@model[:
|
|
740
|
-
@model[:
|
|
741
|
-
|
|
742
|
-
|
|
955
|
+
complies = true unless @model[:uprating]
|
|
956
|
+
@model[:comply ] = comply # true/false, specific to surface type
|
|
957
|
+
@model[:complies] = complies # true/false for entire model
|
|
958
|
+
|
|
959
|
+
# Log NECB Part 3 prescriptive compliance as AdditionalProperty.
|
|
960
|
+
if @model[:uprating]
|
|
961
|
+
model.getBuilding.additionalProperties.setFeature("tbd_compliance", complies)
|
|
962
|
+
end
|
|
743
963
|
|
|
744
964
|
# Run "process" TBD one last time, on "model" (not cloned "mdl").
|
|
965
|
+
args = {option: "", io_path: inputs( @model[:quality] )}
|
|
745
966
|
TBD.clean!
|
|
746
|
-
|
|
967
|
+
TBD.process(model, args)
|
|
747
968
|
|
|
748
|
-
@model[:
|
|
749
|
-
@model[:
|
|
750
|
-
@model[:
|
|
751
|
-
@model[:args ] = args # last TBD inputs (i.e. "tbd.json")
|
|
969
|
+
@model[:args ] = args # last TBD inputs (i.e. "tbd.json")
|
|
970
|
+
@model[:io ] = args[:io]
|
|
971
|
+
@model[:surfaces] = args[:surfaces]
|
|
752
972
|
|
|
753
|
-
|
|
754
|
-
|
|
973
|
+
gen_tallies # tallies for BTAP costing
|
|
974
|
+
gen_feedback # log success messages for BTAP
|
|
755
975
|
end
|
|
756
976
|
|
|
757
977
|
##
|
|
758
|
-
#
|
|
759
|
-
# bridging. This also does a safe TBD trial
|
|
760
|
-
# successful. Check @feedback logs if false.
|
|
761
|
-
#
|
|
762
|
-
# @param model [OpenStudio::Model::Model] a model
|
|
763
|
-
# @param [Hash] argh BTAP/TBD argument hash
|
|
764
|
-
# @option argh [BTAP::Structure] structure a BTAP STRUCTURE object
|
|
765
|
-
# @option argh [Hash] walls exterior wall parameters e.g. :uo, :ut
|
|
766
|
-
# @option argh [Hash] floors exposed floor parameters e.g. :uo, :ut
|
|
767
|
-
# @option argh [Hash] roofs exterior roof parameters e.g. :uo, :ut
|
|
768
|
-
# @option argh [Symbol] quality derating option (if not uprating)
|
|
769
|
-
# @option argh [Boolean] interpolate if TBD should pick between Uo values
|
|
978
|
+
# Minimally validates an OpenStudio model, as well as BTAP/TBD parameters,
|
|
979
|
+
# towards thermal bridging calculations. This also does a safe TBD trial
|
|
980
|
+
# run, returning true if successful. Check @feedback logs if false.
|
|
981
|
+
# AdditionalProperties may be re/set in the OpenStudio model.
|
|
770
982
|
#
|
|
771
983
|
# @return [Boolean] true if valid (check @feedback logs if false)
|
|
772
|
-
def populate(
|
|
773
|
-
mth
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
984
|
+
def populate()
|
|
985
|
+
mth = "BTAP::Bridging::#{__callee__}"
|
|
986
|
+
lgs = @feedback[:logs]
|
|
987
|
+
cl = OpenStudio::Model::LayeredConstruction
|
|
988
|
+
|
|
989
|
+
model = @model[:osm]
|
|
990
|
+
argh = @model[:argh]
|
|
777
991
|
|
|
992
|
+
# Valid model?
|
|
778
993
|
unless model.is_a?(OpenStudio::Model::Model)
|
|
779
994
|
lgs << "Invalid OpenStudio model to de/up-rate (#{mth})"
|
|
780
995
|
return false
|
|
781
996
|
end
|
|
782
997
|
|
|
998
|
+
# Valid argument hash?
|
|
783
999
|
unless argh.is_a?(Hash)
|
|
784
1000
|
lgs << "Invalid BTAP/TBD argument Hash (#{mth})"
|
|
785
1001
|
return false
|
|
786
1002
|
end
|
|
787
1003
|
|
|
1004
|
+
# Valid BTAP::Structure instance?
|
|
788
1005
|
if argh.key?(:structure)
|
|
789
|
-
|
|
1006
|
+
stc = argh[:structure]
|
|
1007
|
+
|
|
1008
|
+
unless stc.is_a?(BTAP::Structure)
|
|
790
1009
|
lgs << "Invalid BTAP::Structure (#{mth})"
|
|
791
1010
|
return false
|
|
792
1011
|
end
|
|
@@ -795,15 +1014,10 @@ module BTAP
|
|
|
795
1014
|
return false
|
|
796
1015
|
end
|
|
797
1016
|
|
|
798
|
-
#
|
|
799
|
-
|
|
800
|
-
# cladding, etc. This will need to adapt to upcoming story/space
|
|
801
|
-
# construction/PSI customization - @todo.
|
|
802
|
-
strc = argh[:structure]
|
|
803
|
-
|
|
804
|
-
argh[:interpolate] = false unless argh.key?(:interpolate)
|
|
805
|
-
argh[:interpolate] = false unless [true, false].include?(argh[:interpolate])
|
|
1017
|
+
# Uprating? Initialize.
|
|
1018
|
+
@model[:uprating] = false
|
|
806
1019
|
|
|
1020
|
+
# All 3 surface types initiated?
|
|
807
1021
|
[:walls, :floors, :roofs].each do |stypes|
|
|
808
1022
|
unless argh.key?(stypes)
|
|
809
1023
|
lgs << "Missing BTAP/TBD #{stypes} (#{mth})"
|
|
@@ -824,6 +1038,8 @@ module BTAP
|
|
|
824
1038
|
|
|
825
1039
|
next unless argh[stypes].key?(:ut)
|
|
826
1040
|
|
|
1041
|
+
# Uprating!
|
|
1042
|
+
@model[:uprating] = true if stypes == :walls
|
|
827
1043
|
ut = argh[stypes][:ut]
|
|
828
1044
|
|
|
829
1045
|
unless ut.is_a?(Numeric) && ut.between?(TBD::UMIN, TBD::UMAX)
|
|
@@ -832,230 +1048,802 @@ module BTAP
|
|
|
832
1048
|
end
|
|
833
1049
|
end
|
|
834
1050
|
|
|
835
|
-
#
|
|
1051
|
+
# When uprating: interpolating between discrete Uo factors?
|
|
1052
|
+
@model[:interpolate] = argh.key?(:interpolate) ? argh[:interpolate] : true
|
|
1053
|
+
@model[:interpolate] = false unless @model[:interpolate] == true
|
|
1054
|
+
|
|
1055
|
+
# Selected PSI factor quality (:good or :bad):
|
|
1056
|
+
# - required when strictly derating
|
|
1057
|
+
# - iteratively reset when uprating
|
|
1058
|
+
@model[:quality] = argh.key?(:quality) ? argh[:quality] : :bad
|
|
1059
|
+
@model[:quality] = :bad unless @model[:quality] == :good
|
|
1060
|
+
|
|
1061
|
+
# NECB 2011-2015 Uo targets.
|
|
1062
|
+
wUo = argh[:walls ][:uo]
|
|
1063
|
+
rUo = argh[:roofs ][:uo]
|
|
1064
|
+
fUo = argh[:floors][:uo]
|
|
1065
|
+
|
|
1066
|
+
# NECB 2017-2025 Ut targets - Uo is reset when uprating.
|
|
1067
|
+
wUt = argh[:walls ].key?(:ut) ? argh[:walls ][:ut] : wUo
|
|
1068
|
+
rUt = argh[:roofs ].key?(:ut) ? argh[:roofs ][:ut] : rUo
|
|
1069
|
+
fUt = argh[:floors].key?(:ut) ? argh[:floors][:ut] : fUo
|
|
1070
|
+
|
|
1071
|
+
# Track building, attic(s), customized spaces & actual surface types.
|
|
1072
|
+
@model[:building] = {walls: {}, roofs: {}, floors: {}}
|
|
1073
|
+
@model[:attic ] = {walls: {}, roofs: {}, floors: {}}
|
|
1074
|
+
@model[:spaces ] = {}
|
|
1075
|
+
@model[:stypes ] = {}
|
|
1076
|
+
|
|
1077
|
+
# Track constructions. REMOVE @todo.
|
|
1078
|
+
@model[:constructions] = {}
|
|
1079
|
+
|
|
1080
|
+
# Track 'building' default construction set.
|
|
1081
|
+
bldg = model.getBuilding
|
|
1082
|
+
bset = bldg.defaultConstructionSet
|
|
1083
|
+
|
|
1084
|
+
if bset.empty?
|
|
1085
|
+
lgs << "No BUILDING default construction set (#{mth})"
|
|
1086
|
+
return false
|
|
1087
|
+
end
|
|
1088
|
+
|
|
1089
|
+
bset = bset.get
|
|
1090
|
+
extC = bset.defaultExteriorSurfaceConstructions
|
|
1091
|
+
|
|
1092
|
+
if extC.empty?
|
|
1093
|
+
lgs << "No BUILDING default exterior constructions (#{mth})"
|
|
1094
|
+
return false
|
|
1095
|
+
end
|
|
1096
|
+
|
|
1097
|
+
wallC = extC.get.wallConstruction
|
|
1098
|
+
roofC = extC.get.roofCeilingConstruction
|
|
1099
|
+
floorC = extC.get.floorConstruction
|
|
1100
|
+
|
|
1101
|
+
if wallC.empty? || roofC.empty? || floorC.empty?
|
|
1102
|
+
lgs << "Missing BUILDING default exterior constructions (#{mth})"
|
|
1103
|
+
return false
|
|
1104
|
+
end
|
|
1105
|
+
|
|
1106
|
+
wallC = wallC.get.to_LayeredConstruction
|
|
1107
|
+
roofC = roofC.get.to_LayeredConstruction
|
|
1108
|
+
floorC = floorC.get.to_LayeredConstruction
|
|
1109
|
+
|
|
1110
|
+
if wallC.empty? || roofC.empty? || floorC.empty?
|
|
1111
|
+
lgs << "BUILDING default exterior layered constructions (#{mth})"
|
|
1112
|
+
return false
|
|
1113
|
+
end
|
|
1114
|
+
|
|
1115
|
+
wallC = wallC.get
|
|
1116
|
+
roofC = roofC.get
|
|
1117
|
+
floorC = floorC.get
|
|
1118
|
+
walliL = TBD.insulatingLayer(wallC)
|
|
1119
|
+
roofiL = TBD.insulatingLayer(roofC)
|
|
1120
|
+
flooriL = TBD.insulatingLayer(floorC)
|
|
1121
|
+
|
|
1122
|
+
if walliL[:index].nil? || roofiL[:index].nil? || flooriL.nil?
|
|
1123
|
+
lgs << "Missing BUILDING insulating layer(s) (#{mth})"
|
|
1124
|
+
return false
|
|
1125
|
+
end
|
|
1126
|
+
|
|
1127
|
+
# Previously-assigned BTAP-costed surface air film resistances?
|
|
1128
|
+
wFR = prop(wallC, "btap_film", Float)
|
|
1129
|
+
rFR = prop(roofC, "btap_film", Float)
|
|
1130
|
+
fFR = prop(floorC, "btap_film", Float)
|
|
1131
|
+
wFR = 0 unless wFR
|
|
1132
|
+
rFR = 0 unless rFR
|
|
1133
|
+
fFR = 0 unless fFR
|
|
1134
|
+
|
|
1135
|
+
# Previously-assigned construction net area?
|
|
1136
|
+
wM2 = prop(wallC, "btap_area", Float)
|
|
1137
|
+
rM2 = prop(roofC, "btap_area", Float)
|
|
1138
|
+
fM2 = prop(floorC, "btap_area", Float)
|
|
1139
|
+
|
|
1140
|
+
wM2 = wallC.getNetArea unless wM2
|
|
1141
|
+
rM2 = roofC.getNetArea unless rM2
|
|
1142
|
+
fM2 = floorC.getNetArea unless fM2
|
|
1143
|
+
|
|
1144
|
+
# Previously-assigned BTAP-costed construction IDs?
|
|
1145
|
+
wID = prop(wallC, "btap_id", String)
|
|
1146
|
+
rID = prop(roofC, "btap_id", String)
|
|
1147
|
+
fID = prop(floorC, "btap_id", String)
|
|
1148
|
+
wID = "" unless wID
|
|
1149
|
+
rID = "" unless rID
|
|
1150
|
+
fID = "" unless fID
|
|
1151
|
+
wID = "" unless data.include?(wID)
|
|
1152
|
+
rID = "" unless data.include?(rID)
|
|
1153
|
+
fID = "" unless data.include?(fID)
|
|
1154
|
+
|
|
1155
|
+
# Fallback: BTAP-costed wall construction from STRUCTURE parameters.
|
|
1156
|
+
if wID.empty?
|
|
1157
|
+
opts = {}
|
|
1158
|
+
opts[:framing ] = stc.framing
|
|
1159
|
+
opts[:cladding] = stc.cladding
|
|
1160
|
+
opts[:finish ] = stc.finish
|
|
1161
|
+
opts[:perform ] = :lp
|
|
1162
|
+
opts[:stype ] = :walls
|
|
1163
|
+
opts[:uo ] = wUo
|
|
1164
|
+
wID = BTAP::Bridging.costed_assembly(argh)
|
|
1165
|
+
end
|
|
1166
|
+
|
|
1167
|
+
# Fallback: BTAP-costed roof construction from STRUCTURE parameters.
|
|
1168
|
+
if rID.empty?
|
|
1169
|
+
opts = {}
|
|
1170
|
+
opts[:framing ] = stc.framing
|
|
1171
|
+
opts[:cladding] = stc.cladding
|
|
1172
|
+
opts[:finish ] = stc.finish
|
|
1173
|
+
opts[:perform ] = :lp
|
|
1174
|
+
opts[:stype ] = :roofs
|
|
1175
|
+
opts[:uo ] = rUo
|
|
1176
|
+
rID = BTAP::Bridging.costed_assembly(argh)
|
|
1177
|
+
end
|
|
1178
|
+
|
|
1179
|
+
# Fallback: BTAP-costed floor construction from STRUCTURE parameters.
|
|
1180
|
+
if fID.empty?
|
|
1181
|
+
opts = {}
|
|
1182
|
+
opts[:framing ] = stc.framing
|
|
1183
|
+
opts[:cladding] = stc.cladding
|
|
1184
|
+
opts[:finish ] = stc.finish
|
|
1185
|
+
opts[:perform ] = :lp
|
|
1186
|
+
opts[:stype ] = :floors
|
|
1187
|
+
opts[:uo ] = fUo
|
|
1188
|
+
fID = BTAP::Bridging.costed_assembly(argh)
|
|
1189
|
+
end
|
|
1190
|
+
|
|
1191
|
+
addprop(bldg, "btap_ut", wUt)
|
|
1192
|
+
addprop(bldg, "btap_uo", wUo)
|
|
1193
|
+
addprop(bldg, "btap_id", wID)
|
|
1194
|
+
addprop(bldg, "btap_area", wM2)
|
|
1195
|
+
addprop(bldg, "btap_film", wFR)
|
|
1196
|
+
addprop(wallC, "btap_ut", wUt)
|
|
1197
|
+
addprop(wallC, "btap_uo", wUo)
|
|
1198
|
+
addprop(wallC, "btap_id", wID)
|
|
1199
|
+
addprop(wallC, "btap_area", wM2)
|
|
1200
|
+
addprop(wallC, "btap_film", wFR)
|
|
1201
|
+
addprop(roofC, "btap_ut", rUt)
|
|
1202
|
+
addprop(roofC, "btap_uo", rUo)
|
|
1203
|
+
addprop(roofC, "btap_id", rID)
|
|
1204
|
+
addprop(roofC, "btap_area", rM2)
|
|
1205
|
+
addprop(roofC, "btap_film", rFR)
|
|
1206
|
+
addprop(floorC, "btap_ut", fUt)
|
|
1207
|
+
addprop(floorC, "btap_uo", fUo)
|
|
1208
|
+
addprop(floorC, "btap_id", fID)
|
|
1209
|
+
addprop(floorC, "btap_area", fM2)
|
|
1210
|
+
addprop(floorC, "btap_film", fFR)
|
|
1211
|
+
|
|
1212
|
+
# Building shortcuts.
|
|
1213
|
+
@model[:building][:walls ][:lc ] = wallC
|
|
1214
|
+
@model[:building][:roofs ][:lc ] = roofC
|
|
1215
|
+
@model[:building][:floors][:lc ] = floorC
|
|
1216
|
+
@model[:building][:walls ][:indx] = walliL[:index]
|
|
1217
|
+
@model[:building][:roofs ][:indx] = roofiL[:index]
|
|
1218
|
+
@model[:building][:floors][:indx] = flooriL[:index]
|
|
1219
|
+
@model[:building][:walls ][:area] = wM2
|
|
1220
|
+
@model[:building][:roofs ][:area] = rM2
|
|
1221
|
+
@model[:building][:floors][:area] = fM2
|
|
1222
|
+
@model[:building][:walls ][:id ] = wID
|
|
1223
|
+
@model[:building][:roofs ][:id ] = rID
|
|
1224
|
+
@model[:building][:floors][:id ] = fID
|
|
1225
|
+
@model[:building][:walls ][:film] = wFR
|
|
1226
|
+
@model[:building][:roofs ][:film] = rFR
|
|
1227
|
+
@model[:building][:floors][:film] = fFR
|
|
1228
|
+
@model[:building][:walls ][:ut ] = wUt
|
|
1229
|
+
@model[:building][:roofs ][:ut ] = rUt
|
|
1230
|
+
@model[:building][:floors][:ut ] = fUt
|
|
1231
|
+
@model[:building][:walls ][:uo ] = wUo
|
|
1232
|
+
@model[:building][:roofs ][:uo ] = rUo
|
|
1233
|
+
@model[:building][:floors][:uo ] = fUo
|
|
1234
|
+
|
|
1235
|
+
# Repeat for unoccupied, unconditioned spaces (e.g. crawlspaces, attics).
|
|
1236
|
+
attics = []
|
|
1237
|
+
|
|
1238
|
+
model.getSpaces.each do |space|
|
|
1239
|
+
next if space.partofTotalFloorArea
|
|
1240
|
+
next unless TBD.unconditioned?(space)
|
|
1241
|
+
|
|
1242
|
+
attics << space
|
|
1243
|
+
end
|
|
1244
|
+
|
|
1245
|
+
# All attic/crawlspace spaces reference the same default construction set.
|
|
1246
|
+
unless attics.empty?
|
|
1247
|
+
attic = attics.first
|
|
1248
|
+
aset = attic.defaultConstructionSet
|
|
1249
|
+
|
|
1250
|
+
unless aset.empty?
|
|
1251
|
+
aset = aset.get
|
|
1252
|
+
|
|
1253
|
+
if aset == bset
|
|
1254
|
+
lgs << "BUILDING/ATTIC shared default construction set (#{mth})"
|
|
1255
|
+
return false
|
|
1256
|
+
end
|
|
1257
|
+
|
|
1258
|
+
# Attic/crawlspace interzone constructions are insulated.
|
|
1259
|
+
intC = aset.defaultInteriorSurfaceConstructions
|
|
1260
|
+
|
|
1261
|
+
if intC.empty?
|
|
1262
|
+
lgs << "No ATTIC default interior constructions (#{mth})"
|
|
1263
|
+
return false
|
|
1264
|
+
end
|
|
1265
|
+
|
|
1266
|
+
wallC = intC.get.wallConstruction
|
|
1267
|
+
roofC = intC.get.roofCeilingConstruction
|
|
1268
|
+
floorC = intC.get.floorConstruction
|
|
1269
|
+
|
|
1270
|
+
if wallC.empty? || roofC.empty? || floorC.empty?
|
|
1271
|
+
lgs << "Missing ATTIC default exterior constructions (#{mth})"
|
|
1272
|
+
return false
|
|
1273
|
+
end
|
|
1274
|
+
|
|
1275
|
+
wallC = wallC.get.to_LayeredConstruction
|
|
1276
|
+
roofC = roofC.get.to_LayeredConstruction
|
|
1277
|
+
floorC = floorC.get.to_LayeredConstruction
|
|
1278
|
+
|
|
1279
|
+
if wallC.empty? || roofC.empty? || floorC.empty?
|
|
1280
|
+
lgs << "ATTIC default interior layered constructions (#{mth})"
|
|
1281
|
+
return false
|
|
1282
|
+
end
|
|
1283
|
+
|
|
1284
|
+
wallC = wallC.get
|
|
1285
|
+
roofC = roofC.get
|
|
1286
|
+
floorC = floorC.get
|
|
1287
|
+
walliL = TBD.insulatingLayer(wallC)
|
|
1288
|
+
roofiL = TBD.insulatingLayer(roofC)
|
|
1289
|
+
flooriL = TBD.insulatingLayer(floorC)
|
|
1290
|
+
|
|
1291
|
+
if walliL[:index].nil? || roofiL[:index].nil? || flooriL.nil?
|
|
1292
|
+
lgs << "Missing ATTIC insulating layer(s) (#{mth})"
|
|
1293
|
+
return false
|
|
1294
|
+
end
|
|
1295
|
+
|
|
1296
|
+
# Previously-assigned BTAP-costed surface air film resistances?
|
|
1297
|
+
wFR = prop(wallC, "btap_film", Float)
|
|
1298
|
+
rFR = prop(roofC, "btap_film", Float)
|
|
1299
|
+
fFR = prop(floorC, "btap_film", Float)
|
|
1300
|
+
wFR = 0 unless wFR
|
|
1301
|
+
rFR = 0 unless rFR
|
|
1302
|
+
fFR = 0 unless fFR
|
|
1303
|
+
|
|
1304
|
+
# Previously-assigned construction net area?
|
|
1305
|
+
wM2 = prop(wallC, "btap_area", Float)
|
|
1306
|
+
rM2 = prop(roofC, "btap_area", Float)
|
|
1307
|
+
fM2 = prop(floorC, "btap_area", Float)
|
|
1308
|
+
wM2 = wallC.getNetArea unless wM2
|
|
1309
|
+
rM2 = roofC.getNetArea unless rM2
|
|
1310
|
+
fM2 = floorC.getNetArea unless fM2
|
|
1311
|
+
|
|
1312
|
+
# Previously-assigned BTAP-costed wall construction ID?
|
|
1313
|
+
wID = prop(wallC, "btap_id", String)
|
|
1314
|
+
rID = prop(roofC, "btap_id", String)
|
|
1315
|
+
fID = prop(floorC, "btap_id", String)
|
|
1316
|
+
wID = "" unless wID
|
|
1317
|
+
rID = "" unless rID
|
|
1318
|
+
fID = "" unless fID
|
|
1319
|
+
wID = "" unless data.include?(wID)
|
|
1320
|
+
rID = "" unless data.include?(rID)
|
|
1321
|
+
fID = "" unless data.include?(fID)
|
|
1322
|
+
|
|
1323
|
+
# Fallback: BTAP-costed wall construction from STRUCTURE parameters.
|
|
1324
|
+
if wID.empty?
|
|
1325
|
+
opts = {}
|
|
1326
|
+
opts[:framing ] = stc.framing
|
|
1327
|
+
opts[:cladding] = stc.cladding
|
|
1328
|
+
opts[:finish ] = stc.finish
|
|
1329
|
+
opts[:perform ] = :lp
|
|
1330
|
+
opts[:stype ] = :walls
|
|
1331
|
+
opts[:uo ] = wUo
|
|
1332
|
+
wID = BTAP::Bridging.costed_assembly(argh)
|
|
1333
|
+
end
|
|
1334
|
+
|
|
1335
|
+
# Fallback: BTAP-costed roof construction from STRUCTURE parameters.
|
|
1336
|
+
if rID.empty?
|
|
1337
|
+
opts = {}
|
|
1338
|
+
opts[:framing ] = stc.framing
|
|
1339
|
+
opts[:cladding] = stc.cladding
|
|
1340
|
+
opts[:finish ] = stc.finish
|
|
1341
|
+
opts[:perform ] = :lp
|
|
1342
|
+
opts[:stype ] = :floors # not roofs:
|
|
1343
|
+
opts[:uo ] = fUo # not rUo
|
|
1344
|
+
rID = BTAP::Bridging.costed_assembly(argh)
|
|
1345
|
+
end
|
|
1346
|
+
|
|
1347
|
+
# Fallback: BTAP-costed floor construction from STRUCTURE parameters.
|
|
1348
|
+
if fID.empty?
|
|
1349
|
+
opts = {}
|
|
1350
|
+
opts[:framing ] = stc.framing
|
|
1351
|
+
opts[:cladding] = stc.cladding
|
|
1352
|
+
opts[:finish ] = stc.finish
|
|
1353
|
+
opts[:perform ] = :lp
|
|
1354
|
+
opts[:stype ] = :roofs # not floors
|
|
1355
|
+
opts[:uo ] = rUo # not fUo
|
|
1356
|
+
fID = BTAP::Bridging.costed_assembly(argh)
|
|
1357
|
+
end
|
|
1358
|
+
|
|
1359
|
+
# Re/set AdditionalProperties of ATTIC interior constructions.
|
|
1360
|
+
addprop(wallC, "btap_ut", wUt)
|
|
1361
|
+
addprop(wallC, "btap_uo", wUo)
|
|
1362
|
+
addprop(wallC, "btap_id", wID)
|
|
1363
|
+
addprop(wallC, "btap_area", wM2)
|
|
1364
|
+
addprop(wallC, "btap_film", wFR)
|
|
1365
|
+
addprop(roofC, "btap_ut", fUt) # not roof Ut
|
|
1366
|
+
addprop(roofC, "btap_uo", fUo) # not roof Uo
|
|
1367
|
+
addprop(roofC, "btap_id", rID)
|
|
1368
|
+
addprop(roofC, "btap_area", rM2)
|
|
1369
|
+
addprop(roofC, "btap_film", rFR)
|
|
1370
|
+
addprop(floorC, "btap_ut", rUt) # not floor Ut
|
|
1371
|
+
addprop(floorC, "btap_uo", rUo) # not floor Uo
|
|
1372
|
+
addprop(floorC, "btap_id", fID)
|
|
1373
|
+
addprop(floorC, "btap_area", fM2)
|
|
1374
|
+
addprop(floorC, "btap_film", fFR)
|
|
1375
|
+
|
|
1376
|
+
# Add interior wall parameters to attic spaces.
|
|
1377
|
+
attics.each do |att|
|
|
1378
|
+
addprop(att, "btap_ut", wUt)
|
|
1379
|
+
addprop(att, "btap_uo", wUo)
|
|
1380
|
+
addprop(att, "btap_id", wID)
|
|
1381
|
+
addprop(att, "btap_area", wM2)
|
|
1382
|
+
addprop(att, "btap_film", wFR)
|
|
1383
|
+
end
|
|
1384
|
+
|
|
1385
|
+
# Shortcuts.
|
|
1386
|
+
@model[:attic][:walls ][:lc ] = wallC
|
|
1387
|
+
@model[:attic][:roofs ][:lc ] = roofC
|
|
1388
|
+
@model[:attic][:floors][:lc ] = floorC
|
|
1389
|
+
@model[:attic][:walls ][:indx] = walliL[:index]
|
|
1390
|
+
@model[:attic][:roofs ][:indx] = roofiL[:index]
|
|
1391
|
+
@model[:attic][:floors][:indx] = flooriL[:index]
|
|
1392
|
+
@model[:attic][:walls ][:area] = wM2
|
|
1393
|
+
@model[:attic][:roofs ][:area] = rM2
|
|
1394
|
+
@model[:attic][:floors][:area] = fM2
|
|
1395
|
+
@model[:attic][:walls ][:id ] = wID
|
|
1396
|
+
@model[:attic][:roofs ][:id ] = rID
|
|
1397
|
+
@model[:attic][:floors][:id ] = fID
|
|
1398
|
+
@model[:attic][:walls ][:film] = wFR
|
|
1399
|
+
@model[:attic][:roofs ][:film] = rFR
|
|
1400
|
+
@model[:attic][:floors][:film] = fFR
|
|
1401
|
+
@model[:attic][:walls ][:ut ] = wUt
|
|
1402
|
+
@model[:attic][:roofs ][:ut ] = fUt
|
|
1403
|
+
@model[:attic][:floors][:ut ] = rUt
|
|
1404
|
+
@model[:attic][:walls ][:uo ] = wUo
|
|
1405
|
+
@model[:attic][:roofs ][:uo ] = fUo
|
|
1406
|
+
@model[:attic][:floors][:uo ] = rUo
|
|
1407
|
+
end
|
|
1408
|
+
end
|
|
1409
|
+
|
|
1410
|
+
# Repeat for customized spaces.
|
|
1411
|
+
stc.spaces.each do |id, sp|
|
|
1412
|
+
space = model.getSpaceByName(id)
|
|
1413
|
+
next if space.empty?
|
|
1414
|
+
|
|
1415
|
+
space = space.get
|
|
1416
|
+
sset = space.defaultConstructionSet
|
|
1417
|
+
next if sset.empty?
|
|
1418
|
+
|
|
1419
|
+
sset = sset.get
|
|
1420
|
+
next if sset == bset
|
|
1421
|
+
|
|
1422
|
+
extC = sset.defaultExteriorSurfaceConstructions
|
|
1423
|
+
next if extC.empty?
|
|
1424
|
+
|
|
1425
|
+
wallC = extC.get.wallConstruction
|
|
1426
|
+
roofC = extC.get.roofCeilingConstruction
|
|
1427
|
+
floorC = extC.get.floorConstruction
|
|
1428
|
+
next if wallC.empty?
|
|
1429
|
+
next if roofC.empty?
|
|
1430
|
+
next if floorC.empty?
|
|
1431
|
+
|
|
1432
|
+
wallC = wallC.get.to_LayeredConstruction
|
|
1433
|
+
roofC = roofC.get.to_LayeredConstruction
|
|
1434
|
+
floorC = floorC.get.to_LayeredConstruction
|
|
1435
|
+
next if wallC.empty?
|
|
1436
|
+
next if roofC.empty?
|
|
1437
|
+
next if floorC.empty?
|
|
1438
|
+
|
|
1439
|
+
wallC = wallC.get
|
|
1440
|
+
roofC = roofC.get
|
|
1441
|
+
floorC = floorC.get
|
|
1442
|
+
walliL = TBD.insulatingLayer(wallC)
|
|
1443
|
+
roofiL = TBD.insulatingLayer(roofC)
|
|
1444
|
+
flooriL = TBD.insulatingLayer(floorC)
|
|
1445
|
+
next if walliL[:index].nil?
|
|
1446
|
+
next if roofiL[:index].nil?
|
|
1447
|
+
next if flooriL[:index].nil?
|
|
1448
|
+
|
|
1449
|
+
# Previously-assigned BTAP-costed surface air film resistances?
|
|
1450
|
+
wFR = prop(wallC, "btap_film", Float)
|
|
1451
|
+
rFR = prop(roofC, "btap_film", Float)
|
|
1452
|
+
fFR = prop(floorC, "btap_film", Float)
|
|
1453
|
+
wFR = 0 unless wFR
|
|
1454
|
+
rFR = 0 unless rFR
|
|
1455
|
+
fFR = 0 unless fFR
|
|
1456
|
+
|
|
1457
|
+
# Previously-assigned construction net area?
|
|
1458
|
+
wM2 = prop(wallC, "btap_area", Float)
|
|
1459
|
+
rM2 = prop(roofC, "btap_area", Float)
|
|
1460
|
+
fM2 = prop(floorC, "btap_area", Float)
|
|
1461
|
+
wM2 = wallC.getNetArea unless wM2
|
|
1462
|
+
rM2 = roofC.getNetArea unless rM2
|
|
1463
|
+
fM2 = floorC.getNetArea unless fM2
|
|
1464
|
+
|
|
1465
|
+
# Previously-assigned BTAP-costed construction IDs?
|
|
1466
|
+
wID = prop(wallC, "btap_id", String)
|
|
1467
|
+
rID = prop(roofC, "btap_id", String)
|
|
1468
|
+
fID = prop(floorC, "btap_id", String)
|
|
1469
|
+
wID = "" unless wID
|
|
1470
|
+
rID = "" unless rID
|
|
1471
|
+
fID = "" unless fID
|
|
1472
|
+
wID = "" unless data.include?(wID)
|
|
1473
|
+
rID = "" unless data.include?(rID)
|
|
1474
|
+
fID = "" unless data.include?(fID)
|
|
1475
|
+
|
|
1476
|
+
# Fallback: BTAP-costed wall construction from STRUCTURE parameters.
|
|
1477
|
+
if wID.empty?
|
|
1478
|
+
opts = {}
|
|
1479
|
+
opts[:framing ] = sp[:framing]
|
|
1480
|
+
opts[:cladding] = sp[:cladding]
|
|
1481
|
+
opts[:finish ] = sp[:finish]
|
|
1482
|
+
opts[:perform ] = :lp
|
|
1483
|
+
opts[:stype ] = :walls
|
|
1484
|
+
opts[:uo ] = wUo
|
|
1485
|
+
wID = BTAP::Bridging.costed_assembly(argh)
|
|
1486
|
+
end
|
|
1487
|
+
|
|
1488
|
+
# Fallback: BTAP-costed roof construction from STRUCTURE parameters.
|
|
1489
|
+
if rID.empty?
|
|
1490
|
+
opts = {}
|
|
1491
|
+
opts[:framing ] = sp[:framing]
|
|
1492
|
+
opts[:cladding] = sp[:cladding]
|
|
1493
|
+
opts[:finish ] = sp[:finish]
|
|
1494
|
+
opts[:perform ] = :lp
|
|
1495
|
+
opts[:stype ] = :roofs
|
|
1496
|
+
opts[:uo ] = rUo
|
|
1497
|
+
rID = BTAP::Bridging.costed_assembly(argh)
|
|
1498
|
+
end
|
|
1499
|
+
|
|
1500
|
+
# Fallback: BTAP-costed floor construction from STRUCTURE parameters.
|
|
1501
|
+
if fID.empty?
|
|
1502
|
+
opts = {}
|
|
1503
|
+
opts[:framing ] = sp[:framing]
|
|
1504
|
+
opts[:cladding] = sp[:cladding]
|
|
1505
|
+
opts[:finish ] = sp[:finish]
|
|
1506
|
+
opts[:perform ] = :lp
|
|
1507
|
+
opts[:stype ] = :floors
|
|
1508
|
+
opts[:uo ] = fUo
|
|
1509
|
+
fID = BTAP::Bridging.costed_assembly(argh)
|
|
1510
|
+
end
|
|
1511
|
+
|
|
1512
|
+
# Re/set AdditionalProperties to space exterior constructions.
|
|
1513
|
+
addprop(space, "btap_ut", wUt)
|
|
1514
|
+
addprop(space, "btap_uo", wUo)
|
|
1515
|
+
addprop(space, "btap_id", wID)
|
|
1516
|
+
addprop(space, "btap_area", wM2)
|
|
1517
|
+
addprop(space, "btap_film", wFR)
|
|
1518
|
+
addprop(wallC, "btap_ut", wUt)
|
|
1519
|
+
addprop(wallC, "btap_uo", wUo)
|
|
1520
|
+
addprop(wallC, "btap_id", wID)
|
|
1521
|
+
addprop(wallC, "btap_area", wM2)
|
|
1522
|
+
addprop(wallC, "btap_film", wFR)
|
|
1523
|
+
addprop(roofC, "btap_ut", rUt)
|
|
1524
|
+
addprop(roofC, "btap_uo", rUo)
|
|
1525
|
+
addprop(roofC, "btap_id", rID)
|
|
1526
|
+
addprop(roofC, "btap_area", rM2)
|
|
1527
|
+
addprop(roofC, "btap_film", rFR)
|
|
1528
|
+
addprop(floorC, "btap_ut", fUt)
|
|
1529
|
+
addprop(floorC, "btap_uo", fUo)
|
|
1530
|
+
addprop(floorC, "btap_id", fID)
|
|
1531
|
+
addprop(floorC, "btap_area", fM2)
|
|
1532
|
+
addprop(floorC, "btap_film", fFR)
|
|
1533
|
+
|
|
1534
|
+
@model[:spaces][id] = {walls: {}, roofs: {}, floors: {}}
|
|
1535
|
+
|
|
1536
|
+
# Custom space shortcuts.
|
|
1537
|
+
@model[:spaces][id][:walls ][:lc ] = wallC
|
|
1538
|
+
@model[:spaces][id][:roofs ][:lc ] = roofC
|
|
1539
|
+
@model[:spaces][id][:floors][:lc ] = floorC
|
|
1540
|
+
@model[:spaces][id][:walls ][:indx] = walliL[:index]
|
|
1541
|
+
@model[:spaces][id][:roofs ][:indx] = roofiL[:index]
|
|
1542
|
+
@model[:spaces][id][:floors][:indx] = flooriL[:index]
|
|
1543
|
+
@model[:spaces][id][:walls ][:area] = wM2
|
|
1544
|
+
@model[:spaces][id][:roofs ][:area] = rM2
|
|
1545
|
+
@model[:spaces][id][:floors][:area] = fM2
|
|
1546
|
+
@model[:spaces][id][:walls ][:id ] = wID
|
|
1547
|
+
@model[:spaces][id][:roofs ][:id ] = rID
|
|
1548
|
+
@model[:spaces][id][:floors][:id ] = fID
|
|
1549
|
+
@model[:spaces][id][:walls ][:film] = wFR
|
|
1550
|
+
@model[:spaces][id][:roofs ][:film] = rFR
|
|
1551
|
+
@model[:spaces][id][:floors][:film] = fFR
|
|
1552
|
+
@model[:spaces][id][:walls ][:ut ] = wUt
|
|
1553
|
+
@model[:spaces][id][:roofs ][:ut ] = rUt
|
|
1554
|
+
@model[:spaces][id][:floors][:ut ] = fUt
|
|
1555
|
+
@model[:spaces][id][:walls ][:uo ] = wUo
|
|
1556
|
+
@model[:spaces][id][:roofs ][:uo ] = rUo
|
|
1557
|
+
@model[:spaces][id][:floors][:uo ] = fUo
|
|
1558
|
+
end
|
|
1559
|
+
|
|
1560
|
+
# Dry TBD run on a clone of the OpenStudio model.
|
|
836
1561
|
mdl = OpenStudio::Model::Model.new
|
|
837
1562
|
mdl.addObjects(model.toIdfFile.objects)
|
|
1563
|
+
|
|
1564
|
+
args = {option: "", io_path: inputs( @model[:quality] )}
|
|
838
1565
|
TBD.clean!
|
|
839
|
-
|
|
1566
|
+
TBD.process(mdl, args)
|
|
840
1567
|
|
|
841
|
-
# TBD validation of OpenStudio model.
|
|
842
1568
|
if TBD.fatal? || TBD.error?
|
|
843
1569
|
lgs << "TBD-identified FATAL error(s):" if TBD.fatal?
|
|
844
1570
|
lgs << "TBD-identified non-FATAL error(s):" if TBD.error?
|
|
845
|
-
|
|
846
1571
|
TBD.logs.each { |log| lgs << log[:message] }
|
|
847
1572
|
return false if TBD.fatal?
|
|
848
1573
|
end
|
|
849
1574
|
|
|
850
|
-
|
|
851
|
-
stories = model.getBuilding.standardsNumberOfAboveGroundStories
|
|
852
|
-
stories = stories.get unless stories.empty?
|
|
853
|
-
stories = model.getBuildingStorys.size unless stories.is_a?(Integer)
|
|
854
|
-
|
|
855
|
-
# Story/space construction/PSI customization is yet to be implemented.
|
|
856
|
-
# Keeping placeholders for now - @todo.
|
|
857
|
-
@model[:stories] = stories.clamp(1, 999)
|
|
858
|
-
@model[:spaces ] = {}
|
|
859
|
-
|
|
860
|
-
# Initialize deratable opaque, layered constructions & surface types.
|
|
861
|
-
@model[:constructions] = {}
|
|
862
|
-
@model[:stypes ] = []
|
|
863
|
-
|
|
864
|
-
if res[:surfaces].nil?
|
|
1575
|
+
if args[:surfaces].nil? || args[:surfaces].empty?
|
|
865
1576
|
lgs << "No deratable surfaces in model (#{mth})"
|
|
866
1577
|
return false
|
|
867
1578
|
end
|
|
868
1579
|
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
1580
|
+
if args[:io][:edges].empty?
|
|
1581
|
+
lgs << "No deratable linear thermal bridge edges in model (#{mth})"
|
|
1582
|
+
return false
|
|
1583
|
+
end
|
|
1584
|
+
|
|
1585
|
+
# Process surfaces deemed 'deratable' by TBD.
|
|
1586
|
+
args[:surfaces].each do |identifier, surface|
|
|
875
1587
|
next unless surface.key?(:type) # :wall, :ceiling or :floor
|
|
876
|
-
next unless surface.key?(:
|
|
1588
|
+
next unless surface.key?(:net) # surface net area
|
|
877
1589
|
next unless surface.key?(:index) # deratable layer index
|
|
878
1590
|
next unless surface.key?(:r) # deratable layer RSi
|
|
879
1591
|
next unless surface.key?(:deratable) # true or false
|
|
880
|
-
|
|
881
1592
|
next unless surface[:deratable]
|
|
882
|
-
next unless surface[:index
|
|
1593
|
+
next unless surface[:index]
|
|
883
1594
|
|
|
884
1595
|
stypes = case surface[:type]
|
|
885
1596
|
when :wall then :walls
|
|
886
1597
|
when :floor then :floors
|
|
887
1598
|
when :ceiling then :roofs
|
|
888
|
-
else
|
|
1599
|
+
else next
|
|
889
1600
|
end
|
|
890
1601
|
|
|
891
|
-
|
|
1602
|
+
# Track deratable surface types, e.g. any exposed :floors?
|
|
1603
|
+
unless @model[:stypes].include?(stypes)
|
|
1604
|
+
@model[:stypes][stypes] = {uo: argh[stypes][:uo]}
|
|
892
1605
|
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
if srf.empty?
|
|
900
|
-
lgs << "Mismatched surface: #{id} (#{mth})?"
|
|
901
|
-
return false
|
|
1606
|
+
if argh[stypes].key?(:ut)
|
|
1607
|
+
@model[:stypes][stypes][:ut] = argh[stypes][:ut]
|
|
1608
|
+
else
|
|
1609
|
+
@model[:stypes][stypes][:ut] = argh[stypes][:uo]
|
|
1610
|
+
end
|
|
902
1611
|
end
|
|
1612
|
+
end
|
|
903
1613
|
|
|
904
|
-
|
|
905
|
-
|
|
1614
|
+
true
|
|
1615
|
+
end
|
|
906
1616
|
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
1617
|
+
##
|
|
1618
|
+
# Assigns a BTAP attribute (as AdditionalProperty) to an OpenStudio Model
|
|
1619
|
+
# object, e.g. a building, one or more spaces, layered constructions.
|
|
1620
|
+
#
|
|
1621
|
+
# @param obj [-] one or more OpenStudio::Model objects
|
|
1622
|
+
# @param tag [#to_sym] attribute identifier
|
|
1623
|
+
# @param prp [#to_sym or Numeric] attribute (or property)
|
|
1624
|
+
#
|
|
1625
|
+
# @return [Boolean] true if successful
|
|
1626
|
+
def addprop(obj = nil, tag = "", prp = "")
|
|
1627
|
+
return false unless tag.respond_to?(:to_sym)
|
|
911
1628
|
|
|
912
|
-
|
|
913
|
-
|
|
1629
|
+
# 1x OpenStudio::Model object, or multiple similar objects?
|
|
1630
|
+
obj = obj.respond_to?(:to_a) ? obj.to_a : [obj]
|
|
1631
|
+
return false unless obj.first.respond_to?(:model)
|
|
914
1632
|
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
end
|
|
1633
|
+
cls = obj.first.class
|
|
1634
|
+
tag = tag.to_s
|
|
1635
|
+
return false if tag.empty?
|
|
919
1636
|
|
|
920
|
-
|
|
1637
|
+
obj.each { |ob| return false unless ob.is_a?(cls) }
|
|
921
1638
|
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
1639
|
+
if prp.respond_to?(:to_sym)
|
|
1640
|
+
prp = prp.to_s
|
|
1641
|
+
return false if prp.empty?
|
|
1642
|
+
else
|
|
1643
|
+
return false unless prp.is_a?(Numeric)
|
|
1644
|
+
end
|
|
926
1645
|
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
unless @model[:constructions].key?(lc)
|
|
930
|
-
@model[:constructions][lc] = {}
|
|
931
|
-
@model[:constructions][lc][:index ] = surface[:index] # material
|
|
932
|
-
@model[:constructions][lc][:r ] = surface[:r] # material
|
|
933
|
-
@model[:constructions][lc][:filmRSI ] = surface[:filmRSI] # assembly
|
|
934
|
-
@model[:constructions][lc][:uo ] = nil # assembly
|
|
935
|
-
@model[:constructions][lc][:compliant] = nil # assembly
|
|
936
|
-
@model[:constructions][lc][:stypes ] = []
|
|
937
|
-
@model[:constructions][lc][:surfaces ] = []
|
|
938
|
-
@model[:constructions][lc][:spaces ] = []
|
|
939
|
-
|
|
940
|
-
# Generate TBD input hashes for both :good & :bad PSI factor sets.
|
|
941
|
-
# This depends solely on assigned wall constructions (e.g. steel- vs
|
|
942
|
-
# wood-framed) - not roof or floor constructions. Until space- and
|
|
943
|
-
# storey-specific structure/construction customization is enabled in
|
|
944
|
-
# BTAP, this is set for the entire building. In other words - for now
|
|
945
|
-
# - there should be a single assigned layered construction for all
|
|
946
|
-
# walls in a BTAP-altered OpenStudio model.
|
|
947
|
-
if stypes == :walls
|
|
948
|
-
@model[:constructions][lc][:lp_bad ] = self.inputs(strc, :lp, :bad )
|
|
949
|
-
@model[:constructions][lc][:lp_good] = self.inputs(strc, :lp, :good)
|
|
950
|
-
@model[:constructions][lc][:hp_bad ] = self.inputs(strc, :hp, :bad )
|
|
951
|
-
@model[:constructions][lc][:hp_good] = self.inputs(strc, :hp, :good)
|
|
952
|
-
end
|
|
953
|
-
end
|
|
1646
|
+
obj.each { |ob| ob.additionalProperties.setFeature(tag, prp) }
|
|
954
1647
|
|
|
955
|
-
|
|
956
|
-
|
|
1648
|
+
true
|
|
1649
|
+
end
|
|
957
1650
|
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
1651
|
+
##
|
|
1652
|
+
# Fetches a BTAP attribute (as AdditionalProperty), from an OpenStudio Model
|
|
1653
|
+
# object, e.g. a building, a space, a layered construction.
|
|
1654
|
+
#
|
|
1655
|
+
# @param obj [#model] an OpenStudio::Model object
|
|
1656
|
+
# @param tag [#to_sym] attribute tag
|
|
1657
|
+
# @param typ [-] attribute type, e.g. String, Symbol, Integer
|
|
1658
|
+
#
|
|
1659
|
+
# @return [String or Float] attribute value (nil if invalid input)
|
|
1660
|
+
def prop(obj = nil, tag = "", typ = nil)
|
|
1661
|
+
return nil unless obj.respond_to?(:model)
|
|
1662
|
+
return nil unless tag.respond_to?(:to_sym)
|
|
1663
|
+
|
|
1664
|
+
tag = tag.to_s
|
|
1665
|
+
return nil if tag.empty?
|
|
1666
|
+
|
|
1667
|
+
if typ == Integer
|
|
1668
|
+
prp = obj.additionalProperties.getFeatureAsInteger(tag)
|
|
1669
|
+
elsif typ <= Numeric
|
|
1670
|
+
prp = obj.additionalProperties.getFeatureAsDouble(tag)
|
|
1671
|
+
else
|
|
1672
|
+
prp = obj.additionalProperties.getFeatureAsString(tag)
|
|
962
1673
|
end
|
|
963
1674
|
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
@model[:constructions].values.each { |v| v[:stypes].uniq! }
|
|
967
|
-
nb = 0
|
|
1675
|
+
prp.empty? ? nil : prp.get
|
|
1676
|
+
end
|
|
968
1677
|
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
1678
|
+
##
|
|
1679
|
+
# Generates TBD input hash. Relies on OpenStudio model, BTAP::Structure
|
|
1680
|
+
# attributes and additional parameters set at initialization.
|
|
1681
|
+
#
|
|
1682
|
+
# @param quality [:good, :bad] thermal bridging PSI set quality
|
|
1683
|
+
#
|
|
1684
|
+
# @return [Hash] TBD inputs (empty if invalid input)
|
|
1685
|
+
def inputs(quality = :good)
|
|
1686
|
+
argh = @model[:argh]
|
|
1687
|
+
mdl = @model[:osm]
|
|
975
1688
|
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
surface = model.getSurfaceByName(id)
|
|
979
|
-
next if surface.empty?
|
|
1689
|
+
# PSI set quality?
|
|
1690
|
+
quality = :good unless quality == :bad
|
|
980
1691
|
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
1692
|
+
# Initialize TBD input hash.
|
|
1693
|
+
input = {}
|
|
1694
|
+
psis = {} # construction-specific PSI sets
|
|
1695
|
+
spaces = {} # custom space-specific PSI references
|
|
984
1696
|
|
|
985
|
-
|
|
986
|
-
|
|
1697
|
+
# A single, default PSI-factor set for the building.
|
|
1698
|
+
bldg = @model[:building]
|
|
1699
|
+
bID = bldg[:walls][:id]
|
|
1700
|
+
bPSI = set(bID, quality)
|
|
1701
|
+
psis[ bPSI[:id] ] = bPSI
|
|
987
1702
|
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
1703
|
+
# Repeat for customized spaces, e.g.
|
|
1704
|
+
# - "cmu" gymnasium walls in an otherwise "steel" post/frame school
|
|
1705
|
+
@model[:spaces].each do |id, sp|
|
|
1706
|
+
cID = sp[:walls ][:id]
|
|
1707
|
+
next if cID == bID
|
|
992
1708
|
|
|
993
|
-
|
|
1709
|
+
# Fetch BTAP PSI factor set identifier, e.g. STEL1_GOOD
|
|
1710
|
+
cPSI = set(cID, quality)
|
|
1711
|
+
next if psis.key?( cPSI[:id] )
|
|
994
1712
|
|
|
995
|
-
|
|
996
|
-
|
|
1713
|
+
# Append customized PSI-factor set.
|
|
1714
|
+
psis[ cPSI[:id] ] = cPSI
|
|
997
1715
|
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
# @param structure [BTAP::Structure] a BTAP STRUCTURE object
|
|
1002
|
-
# @param perform [Symbol] :lp or :hp wall variant
|
|
1003
|
-
# @param quality [Symbol] :bad or :good PSI-factor
|
|
1004
|
-
#
|
|
1005
|
-
# @return [Hash] TBD inputs
|
|
1006
|
-
def inputs(structure = nil, perform = :hp, quality = :good)
|
|
1007
|
-
input = {}
|
|
1008
|
-
psis = {} # construction-specific PSI sets
|
|
1009
|
-
perform = :hp unless [:lp, :hp].include?(perform)
|
|
1010
|
-
quality = :good unless [:bad, :good].include?(quality)
|
|
1011
|
-
|
|
1012
|
-
# A single PSI set for the entire building, based strictly on exterior
|
|
1013
|
-
# wall selection. Adapt once BTAP::Structure supports STRUCTURE
|
|
1014
|
-
# assignements per OpenStudio's building-to-space hierarchy, e.g. "cmu"
|
|
1015
|
-
# gymnasium walls in an otherwise "steel"post/frame school. @todo
|
|
1016
|
-
assembly = self.costed_assembly(structure, :walls, perform)
|
|
1017
|
-
building_psi = self.set(assembly, quality)
|
|
1018
|
-
|
|
1019
|
-
psis[ building_psi[:id] ] = building_psi
|
|
1716
|
+
# Add reference to customized space PSI-factor set.
|
|
1717
|
+
spaces[id] = { id: id, psi: cPSI[:id] }
|
|
1718
|
+
end
|
|
1020
1719
|
|
|
1021
1720
|
# TBD JSON schema added as a reminder. No schema validation in BTAP.
|
|
1022
1721
|
schema = "https://github.com/rd2/tbd/blob/master/tbd.schema.json"
|
|
1023
1722
|
|
|
1024
1723
|
input[:schema ] = schema
|
|
1025
|
-
input[:description] = "TBD input for BTAP"
|
|
1026
|
-
input[:psis ] = psis.values
|
|
1027
|
-
input[:
|
|
1724
|
+
input[:description] = "TBD input for BTAP"
|
|
1725
|
+
input[:psis ] = psis.values
|
|
1726
|
+
input[:spaces ] = spaces.values unless spaces.empty?
|
|
1727
|
+
input[:building ] = { psi: bPSI[:id] }
|
|
1028
1728
|
|
|
1029
1729
|
input
|
|
1030
1730
|
end
|
|
1031
1731
|
|
|
1032
1732
|
##
|
|
1033
|
-
# Generate BTAP/TBD tallies
|
|
1733
|
+
# Generate BTAP/TBD tallies, namely for QAQC, costing & embodied carbon.
|
|
1034
1734
|
#
|
|
1035
1735
|
# @return [Boolean] true if BTAP/TBD tally is successful
|
|
1036
1736
|
def gen_tallies
|
|
1037
|
-
edges = {}
|
|
1038
1737
|
return false unless @model.key?(:io)
|
|
1039
|
-
return false unless @model
|
|
1040
|
-
|
|
1738
|
+
return false unless @model[:io][:edges]
|
|
1739
|
+
|
|
1740
|
+
# Initialize.
|
|
1741
|
+
@tally = {edges: {}}
|
|
1742
|
+
model = @model[:osm]
|
|
1743
|
+
bldg = @model[:osm].getBuilding
|
|
1744
|
+
|
|
1745
|
+
# TBD/BTAP track a wider range of thermal bridge types than BTAP costing
|
|
1746
|
+
# currently supports. Current BTAP-costed, linear thermal bridge types:
|
|
1747
|
+
@model[:bridges] = BTAP::BridgingData.admissible_edges
|
|
1041
1748
|
|
|
1042
1749
|
@model[:io][:edges].each do |e|
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
#
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1750
|
+
pID = e[:psi]
|
|
1751
|
+
next if e[:length] < 0.025
|
|
1752
|
+
|
|
1753
|
+
# Prune away concave/convex suffixes.
|
|
1754
|
+
type = e[:type].to_s.gsub(/concave|convex/, "")
|
|
1755
|
+
|
|
1756
|
+
# Recast linear bridge types: match ~limited costed options.
|
|
1757
|
+
type = case type
|
|
1758
|
+
when /balcony/ then :rimjoist
|
|
1759
|
+
when /roof/ then :parapet
|
|
1760
|
+
when /sky/ then :fenestration
|
|
1761
|
+
when /sill/ then :fenestration
|
|
1762
|
+
when /jamb/ then :fenestration
|
|
1763
|
+
when /head/ then :fenestration
|
|
1764
|
+
when /door/ then ""
|
|
1765
|
+
when /party/ then ""
|
|
1766
|
+
when /joint/ then ""
|
|
1767
|
+
else type.to_sym
|
|
1768
|
+
end
|
|
1769
|
+
|
|
1770
|
+
next unless @model[:bridges].include?(type)
|
|
1771
|
+
|
|
1772
|
+
# Split PSI identifier: construction 'ID' + PSI 'quality'.
|
|
1773
|
+
cID, quality = pID.split(" ")
|
|
1774
|
+
|
|
1775
|
+
# Valid?
|
|
1776
|
+
quality = quality.to_sym
|
|
1777
|
+
next unless data.key?(cID)
|
|
1778
|
+
next unless data[cID].key?(quality)
|
|
1779
|
+
next unless data[cID][quality].key?(type)
|
|
1780
|
+
next unless data[cID][quality][:id] == pID
|
|
1781
|
+
|
|
1782
|
+
# Determine multiplier(s).
|
|
1783
|
+
mSb = 1
|
|
1784
|
+
mSp = 1
|
|
1785
|
+
|
|
1786
|
+
# SubSurface multiplier.
|
|
1787
|
+
e[:surfaces].each do |srf|
|
|
1788
|
+
if type == :fenestration
|
|
1789
|
+
unless model.getSubSurfaceByName(srf).empty?
|
|
1790
|
+
mlt = model.getSubSurfaceByName(srf).get.multiplier
|
|
1791
|
+
mSb = mlt if mlt > mSb
|
|
1792
|
+
end
|
|
1793
|
+
end
|
|
1794
|
+
|
|
1795
|
+
next if model.getSurfaceByName(srf).empty?
|
|
1796
|
+
next if model.getSurfaceByName(srf).get.space.empty?
|
|
1797
|
+
|
|
1798
|
+
# Space multiplier.
|
|
1799
|
+
mlt = model.getSurfaceByName(srf).get.space.get.multiplier
|
|
1800
|
+
mSp = mlt if mlt > mSp
|
|
1801
|
+
end
|
|
1802
|
+
|
|
1803
|
+
@tally[:edges][type] = {} unless @tally[:edges].key?(type)
|
|
1804
|
+
@tally[:edges][type][pID] = 0 unless @tally[:edges][type].key?(pID)
|
|
1805
|
+
@tally[:edges][type][pID] += e[:length] * mSb * mSp
|
|
1051
1806
|
end
|
|
1052
1807
|
|
|
1053
|
-
|
|
1808
|
+
addprop(bldg, "psi_quality", @model[:quality])
|
|
1054
1809
|
|
|
1055
|
-
|
|
1056
|
-
@tally[:
|
|
1810
|
+
# Add (to a model's building) PSI tallies as AdditionalProperties.
|
|
1811
|
+
@tally[:edges].each do |type, edge|
|
|
1812
|
+
edge.each.with_index(1) do |(pID, m), i|
|
|
1813
|
+
next if m < 0.025
|
|
1057
1814
|
|
|
1058
|
-
|
|
1815
|
+
tag = "PSI" + type.to_s + i.to_s
|
|
1816
|
+
val = "#{pID} #{m.round(2)}"
|
|
1817
|
+
addprop(bldg, tag, val)
|
|
1818
|
+
end
|
|
1819
|
+
end
|
|
1820
|
+
|
|
1821
|
+
# Update other related parameters (as building AdditionalProperties).
|
|
1822
|
+
addprop(bldg, "btap_uo", @model[:building][:walls][:uo])
|
|
1823
|
+
addprop(bldg, "btap_id", @model[:building][:walls][:id])
|
|
1824
|
+
|
|
1825
|
+
# Repeat for attic(s) - if applicable.
|
|
1826
|
+
unless @model[:attic][:walls].empty?
|
|
1827
|
+
model.getSpaces.each do |space|
|
|
1828
|
+
next if space.partofTotalFloorArea
|
|
1829
|
+
next unless TBD.unconditioned?(space)
|
|
1830
|
+
|
|
1831
|
+
addprop(space, "btap_uo", @model[:attic][:walls][:uo])
|
|
1832
|
+
addprop(space, "btap_id", @model[:attic][:walls][:id])
|
|
1833
|
+
end
|
|
1834
|
+
end
|
|
1835
|
+
|
|
1836
|
+
# Repeat for customized spaces.
|
|
1837
|
+
@model[:spaces].each do |id, sp|
|
|
1838
|
+
space = model.getSpaceByName(id)
|
|
1839
|
+
next if space.empty?
|
|
1840
|
+
|
|
1841
|
+
space = space.get
|
|
1842
|
+
addprop(space, "btap_uo", sp[:walls][:uo])
|
|
1843
|
+
addprop(space, "btap_id", sp[:walls][:id])
|
|
1844
|
+
end
|
|
1845
|
+
|
|
1846
|
+
@tally[:edges].empty? ? false : true
|
|
1059
1847
|
end
|
|
1060
1848
|
|
|
1061
1849
|
##
|
|
@@ -1066,151 +1854,117 @@ module BTAP
|
|
|
1066
1854
|
lgs = @feedback[:logs]
|
|
1067
1855
|
return false unless @model.key?(:complies) # all model constructions
|
|
1068
1856
|
return false unless @model.key?(:comply) # surface type specific ...
|
|
1069
|
-
return false unless @model.key?(:argh) # BTAP/TBD inputs + ouputs
|
|
1070
1857
|
return false unless @model.key?(:stypes) # :walls, :roofs, :floors
|
|
1071
1858
|
|
|
1072
|
-
argh = @model[:argh]
|
|
1073
|
-
|
|
1074
1859
|
# Uprating. Report first on surface types (compliant or not).
|
|
1075
|
-
@model[:stypes].each do |stypes|
|
|
1860
|
+
@model[:stypes].each do |stypes, u|
|
|
1861
|
+
next unless @model[:uprating]
|
|
1076
1862
|
next unless @model[:comply].key?(stypes)
|
|
1077
1863
|
|
|
1078
|
-
ut = format("%.3f", argh[stypes][:ut])
|
|
1079
1864
|
lg = @model[:comply][stypes] ? "Compliant " : "Non-compliant "
|
|
1080
|
-
lg +=
|
|
1865
|
+
lg += stypes.to_s
|
|
1866
|
+
lgs << lg
|
|
1867
|
+
|
|
1868
|
+
bUt = format("%.3f", @model[:building][stypes][:ut])
|
|
1869
|
+
bUo = format("%.3f", @model[:building][stypes][:uo])
|
|
1870
|
+
bCP = @model[:building][stypes][:compliant]
|
|
1871
|
+
bID = @model[:building][stypes][:id]
|
|
1872
|
+
lg = " - "
|
|
1873
|
+
lg += bCP ? " Compliant " : " Non-compliant "
|
|
1874
|
+
lg += "BUILDING: Ut #{bUt} W/m2.K "
|
|
1875
|
+
lg += "(Uo #{bUo} W/m2.K, #{bID})"
|
|
1876
|
+
lg += " - #{@model[:quality]}" if stypes == :walls
|
|
1081
1877
|
lgs << lg
|
|
1082
1878
|
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
lg
|
|
1879
|
+
unless @model[:attic][stypes].empty?
|
|
1880
|
+
types = case stypes
|
|
1881
|
+
when :roofs then :floors
|
|
1882
|
+
when :floors then :roofs
|
|
1883
|
+
else :walls
|
|
1884
|
+
end
|
|
1885
|
+
|
|
1886
|
+
aUt = format("%.3f", @model[:attic][types][:ut])
|
|
1887
|
+
aUo = format("%.3f", @model[:attic][types][:uo])
|
|
1888
|
+
aCP = @model[:attic][types][:compliant]
|
|
1889
|
+
aID = @model[:attic][types][:id]
|
|
1890
|
+
lg = " - "
|
|
1891
|
+
lg += aCP ? " Compliant " : " Non-compliant "
|
|
1892
|
+
lg += "ATTIC: Ut #{aUt} W/m2.K "
|
|
1893
|
+
lg += "(Uo #{aUo} W/m2.K, #{aID})"
|
|
1894
|
+
lg += " - #{@model[:quality]}" if stypes == :walls
|
|
1095
1895
|
lgs << lg
|
|
1096
1896
|
end
|
|
1097
|
-
end
|
|
1098
1897
|
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1898
|
+
@model[:spaces].each do |id, sp|
|
|
1899
|
+
sUt = format("%.3f", sp[stypes][:ut])
|
|
1900
|
+
sUo = format("%.3f", sp[stypes][:uo])
|
|
1901
|
+
sCP = sp[stypes][:compliant]
|
|
1902
|
+
sID = sp[stypes][:id]
|
|
1903
|
+
sQY =
|
|
1904
|
+
lg = " - "
|
|
1905
|
+
lg += sCP ? " Compliant " : " Non-compliant "
|
|
1906
|
+
lg += "SPACE #{id}: Ut #{sUt} W/m2.K "
|
|
1907
|
+
lg += "(Uo #{sUo} W/m2.K, #{sID})"
|
|
1908
|
+
lg += " - #{@model[:quality]}" if stypes == :walls
|
|
1909
|
+
lgs << lg
|
|
1910
|
+
end
|
|
1911
|
+
end
|
|
1107
1912
|
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
rsi = format("%.1f", rsi)
|
|
1111
|
-
area = format("%.1f", lc.getNetArea) + " m2"
|
|
1913
|
+
# Report on edges (as linear thermal bridges).
|
|
1914
|
+
lgs << "Edges (i.e. linear thermal bridges):"
|
|
1112
1915
|
|
|
1113
|
-
|
|
1114
|
-
|
|
1916
|
+
@tally[:edges].each do |type, edge|
|
|
1917
|
+
edge.each.with_index(1) do |(pID, m), i|
|
|
1918
|
+
next if m < 0.025
|
|
1115
1919
|
|
|
1116
|
-
|
|
1117
|
-
if @tally.key?(:edges)
|
|
1118
|
-
@tally[:edges].each do |type, e|
|
|
1119
|
-
next if type == :transition
|
|
1920
|
+
lgs << " #{type}: ".rjust(15) + pID.ljust(37) + " #{m.round(1)} m".rjust(9)
|
|
1120
1921
|
|
|
1121
|
-
|
|
1922
|
+
# LargeOffice (without factoring space/subsurface multipliers):
|
|
1923
|
+
# fenestration: BTAP-ExteriorWall-SteelFramed-2 good 1802.1 m
|
|
1924
|
+
# grade: BTAP-ExteriorWall-SteelFramed-2 good 243.7 m
|
|
1925
|
+
# rimjoist: BTAP-ExteriorWall-SteelFramed-2 good 487.4 m
|
|
1926
|
+
# parapet: BTAP-ExteriorWall-SteelFramed-2 good 243.7 m
|
|
1927
|
+
# corner: BTAP-ExteriorWall-SteelFramed-2 good 47.6 m
|
|
1122
1928
|
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1929
|
+
# LargeOffice (with factoring space/subsurface multipliers):
|
|
1930
|
+
# fenestration: BTAP-ExteriorWall-SteelFramed-2 good 6280.0 m
|
|
1931
|
+
# grade: BTAP-ExteriorWall-SteelFramed-2 good 243.7 m
|
|
1932
|
+
# rimjoist: BTAP-ExteriorWall-SteelFramed-2 good 2680.6 m
|
|
1933
|
+
# parapet: BTAP-ExteriorWall-SteelFramed-2 good 243.7 m
|
|
1934
|
+
# corner: BTAP-ExteriorWall-SteelFramed-2 good 190.2 m
|
|
1127
1935
|
end
|
|
1128
1936
|
end
|
|
1129
1937
|
|
|
1130
1938
|
true
|
|
1131
1939
|
end
|
|
1132
|
-
|
|
1133
|
-
# def get_material_quantities()
|
|
1134
|
-
# material_quantities = {}
|
|
1135
|
-
# csv = CSV.read("#{File.dirname(__FILE__)}/../../../data/inventory/thermal_bridging.csv", headers: true)
|
|
1136
|
-
# tally_edges = @tally[:edges].transform_keys(&:to_s)
|
|
1137
|
-
#
|
|
1138
|
-
# tally_edges.each do |edge_type_full, value|
|
|
1139
|
-
# edge_type = edge_type_full.delete_suffix('convex')
|
|
1140
|
-
# edge_type = 'fenestration' if ['head', 'jamb', 'sill'].include?(edge_type)
|
|
1141
|
-
#
|
|
1142
|
-
# value.each do |wall_ref_and_quality, quantity|
|
|
1143
|
-
# /(.*)\s(.*)/ =~ wall_ref_and_quality
|
|
1144
|
-
# wall_reference = $1
|
|
1145
|
-
# quality = $2
|
|
1146
|
-
#
|
|
1147
|
-
# if wall_reference =='BTAP-ExteriorWall-SteelFramed-1'
|
|
1148
|
-
# wall_reference = 'BTAP-ExteriorWall-SteelFramed-2'
|
|
1149
|
-
# end
|
|
1150
|
-
#
|
|
1151
|
-
# next if edge_type == 'transition'
|
|
1152
|
-
#
|
|
1153
|
-
# result = csv.find { |row| row['edge_type'] == edge_type &&
|
|
1154
|
-
# row['quality'] == quality &&
|
|
1155
|
-
# row['wall_reference'] == wall_reference
|
|
1156
|
-
# }
|
|
1157
|
-
#
|
|
1158
|
-
# if result.nil?
|
|
1159
|
-
# puts ("#{edge_type}-#{wall_reference}-#{quality}")
|
|
1160
|
-
# puts "not found in tb database"
|
|
1161
|
-
# next
|
|
1162
|
-
# end
|
|
1163
|
-
#
|
|
1164
|
-
# # Split
|
|
1165
|
-
# material_opaque_id_layers = result['material_opaque_id_layers'].split(",")
|
|
1166
|
-
# id_layers_quantity_multipliers = result['id_layers_quantity_multipliers'].split(",")
|
|
1167
|
-
#
|
|
1168
|
-
# material_opaque_id_layers.zip(id_layers_quantity_multipliers).each do |id, scale|
|
|
1169
|
-
# material_quantities[id] = 0.0 if material_quantities[id].nil?
|
|
1170
|
-
# material_quantities[id] = material_quantities[id] + scale.to_f * quantity.to_f
|
|
1171
|
-
# end
|
|
1172
|
-
# end
|
|
1173
|
-
# end
|
|
1174
|
-
#
|
|
1175
|
-
# material_opaque_id_quantities = []
|
|
1176
|
-
#
|
|
1177
|
-
# material_quantities.each do |id,quantity|
|
|
1178
|
-
# material_opaque_id_quantities << { 'materials_opaque_id' => id, 'quantity' => quantity, 'domain'=> 'thermal_bridging' }
|
|
1179
|
-
# end
|
|
1180
|
-
#
|
|
1181
|
-
# return material_opaque_id_quantities
|
|
1182
|
-
# end
|
|
1183
1940
|
end
|
|
1184
1941
|
end
|
|
1185
1942
|
|
|
1186
1943
|
# ----- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----- #
|
|
1187
|
-
#
|
|
1944
|
+
# Note: BTAP supports Uo variants for each of the aforementioned wall
|
|
1188
1945
|
# constructions, e.g. meeting NECB2011 and NECB2015 prescriptive "Uo"
|
|
1189
1946
|
# requirements for each NECB climate zone. By definition, these Uo
|
|
1190
|
-
# variants ignore the effects of MAJOR thermal bridging, such as
|
|
1947
|
+
# variants ignore the effects of MAJOR linear thermal bridging, such as
|
|
1191
1948
|
# intermediate slab edges. This does not imply that NECB2011 and NECB2015
|
|
1192
|
-
# do not hold prescriptive requirements for MAJOR thermal bridging.
|
|
1193
|
-
# are indeed a handful of general,
|
|
1194
|
-
# MNECB1997) that would make NECB2011- and NECB2015-compliant
|
|
1195
|
-
# slightly better than BTAPPRE1980
|
|
1196
|
-
# but likely not any better than circa 1990s
|
|
1197
|
-
# construction. Currently, BTAP does not assess the impact of
|
|
1198
|
-
# thermal bridging for vintages < NECB2017. But ideally it
|
|
1199
|
-
# goal remains a fair assessment of the (relative)
|
|
1200
|
-
# recent NECB requirements (e.g.
|
|
1201
|
-
|
|
1202
|
-
#
|
|
1203
|
-
# spandrel inserts above/below fenestration for certain spacetypes
|
|
1204
|
-
#
|
|
1205
|
-
# test/necb/unit_tests/resources/btap_spandrels.png
|
|
1206
|
-
#
|
|
1207
|
-
# This is yet to be implemented. This note is an "aide-mémoire" for future
|
|
1949
|
+
# do not hold prescriptive requirements for MAJOR linear thermal bridging.
|
|
1950
|
+
# There are indeed a handful of general, descriptive requirements (those
|
|
1951
|
+
# of the MNECB1997) that would make NECB2011- and NECB2015-compliant
|
|
1952
|
+
# buildings slightly better than BTAPPRE1980 bottom-of-the-barrel
|
|
1953
|
+
# construction, but likely not any better than circa 1990s run-of-the-mill
|
|
1954
|
+
# commercial construction. Currently, BTAP does not assess the impact of
|
|
1955
|
+
# MAJOR linear thermal bridging for vintages < NECB2017. But ideally it
|
|
1956
|
+
# SHOULD, if the goal remains a fair assessment of the (relative)
|
|
1957
|
+
# contribution of more recent NECB requirements (e.g. 2025).
|
|
1958
|
+
|
|
1959
|
+
# Note: The original BTAP costing database holds entries for curtain wall (CW)
|
|
1960
|
+
# spandrel inserts above/below fenestration for certain spacetypes. This
|
|
1961
|
+
# is yet to be implemented. This note is an "aide-mémoire" for future
|
|
1208
1962
|
# consideration. The BETBG does not hold any CW glazed spandrels achieving
|
|
1209
|
-
# U
|
|
1210
|
-
# NECB climate zone. Same for the Guide to Low Thermal Energy Demand for
|
|
1211
|
-
# Large Buildings.
|
|
1212
|
-
# "Metal-2" and "Metal-3" as HP CW spandrels ACTUALLY achieving
|
|
1213
|
-
# prescriptive targets, which could only be possible in practice at
|
|
1963
|
+
# U-factors ANYWHERE near NECB requirements, regardless of NECB vintage or
|
|
1964
|
+
# NECB climate zone. Same for the 'Guide to Low Thermal Energy Demand for
|
|
1965
|
+
# Large Buildings'. It seems the original intent was to rely on BTAP
|
|
1966
|
+
# variants "Metal-2" and "Metal-3" as HP CW spandrels ACTUALLY achieving
|
|
1967
|
+
# NECB prescriptive targets, which could only be possible in practice at
|
|
1214
1968
|
# great cost and effort (e.g. a 2nd insulated wall behind the spandrel).
|
|
1215
1969
|
#
|
|
1216
1970
|
# If TBD's uprating calculations (e.g. NECB 2017) were in theory no longer
|
|
@@ -1219,39 +1973,34 @@ end
|
|
|
1219
1973
|
# to be altered. Otherwise, adaptations would be required. PSI factors are
|
|
1220
1974
|
# noticeably different for spandrels (obviously no lintels/shelf-angles).
|
|
1221
1975
|
# More importantly, the default assumption with CW technology is that
|
|
1222
|
-
# there wouldn't be any
|
|
1223
|
-
#
|
|
1224
|
-
#
|
|
1225
|
-
# hand, vision jambs along non-CW assemblies (i.e. original BTAP
|
|
1226
|
-
#
|
|
1976
|
+
# there wouldn't be any linear thermal bridges along shared vision vs
|
|
1977
|
+
# spandrel edges (as perimeter heat loss would already have been taken
|
|
1978
|
+
# into as consideration, as per NFRC or CSA rating methodologies). On the
|
|
1979
|
+
# other hand, CW vision jambs along non-CW assemblies (i.e. original BTAP
|
|
1980
|
+
# goal) most certainly constitute (new) MAJOR linear thermal bridges to
|
|
1227
1981
|
# consider (just as with shared edges between spandrels and other wall
|
|
1228
1982
|
# assemblies). Again, none of these features are currently implemented
|
|
1229
1983
|
# within BTAP. Recommended (future) solution, if desired:
|
|
1230
1984
|
#
|
|
1231
1985
|
# - Automated OSM façade-splitting feature
|
|
1232
1986
|
# - insert spandrels above/below windows
|
|
1233
|
-
# > ~200 lines of Ruby code
|
|
1234
1987
|
# - simple cases only e.g., vertical, no overlaps, h > 200mm
|
|
1235
|
-
#
|
|
1236
|
-
# > potentially another 200 lines to catch invalid input
|
|
1988
|
+
# - split above/below plenum walls as well
|
|
1237
1989
|
#
|
|
1238
1990
|
# - Further develop PSI sets to cover CWs (see below)
|
|
1239
1991
|
# - e.g. PSI factors for CW vision "jamb" transitions
|
|
1240
1992
|
# - e.g. PSI factors for CW spandrel "jamb" transitions
|
|
1241
1993
|
#
|
|
1242
1994
|
# These added features would simplify the process tremendously. Yet
|
|
1243
|
-
# without admissible CW spandrel U
|
|
1244
|
-
#
|
|
1245
|
-
#
|
|
1246
|
-
|
|
1247
|
-
#
|
|
1248
|
-
# within BTAP.
|
|
1249
|
-
|
|
1250
|
-
# NOTE: Some of the aforementioned constructions have exterior brick veneer.
|
|
1995
|
+
# without admissible CW spandrel U-factors down to 0.130 or 0.100 W/m2.K,
|
|
1996
|
+
# this would make it pretty much impossible to identify NECB2017 through
|
|
1997
|
+
# NECB2025 compliant combinations of Uo+PSI factors.
|
|
1998
|
+
|
|
1999
|
+
# Note: Some of the aforementioned constructions have exterior brick veneer.
|
|
1251
2000
|
# For 2-story OpenStudio models with punch windows (i.e. not strip
|
|
1252
2001
|
# windows), one would NOT expect a continuous steel shelf angle along the
|
|
1253
|
-
# intermediate floor slab edge (typically a MAJOR thermal bridge).
|
|
1254
|
-
# would instead expect loose lintels above punch windows, just as with
|
|
2002
|
+
# intermediate floor slab edge (typically a MAJOR linear thermal bridge).
|
|
2003
|
+
# One would instead expect loose lintels above punch windows, just as with
|
|
1255
2004
|
# doors. Loose lintels usually compound heat loss along window head edges,
|
|
1256
2005
|
# but are currently considered as factored in the retained PSI factors for
|
|
1257
2006
|
# window and door head details (a postulate that likely needs revision).
|
|
@@ -1268,10 +2017,3 @@ end
|
|
|
1268
2017
|
# costing ($/m2), yet should be limited to doors (@todo). A more flexible,
|
|
1269
2018
|
# general solution would be required for 3rd-party OpenStudio models
|
|
1270
2019
|
# (without strip windows as a basic fenestration layout).
|
|
1271
|
-
#
|
|
1272
|
-
# NOTE: BTAP costing: In addition to the listed items for parapets as MAJOR
|
|
1273
|
-
# thermal bridges (eventually generating an overall $ per linear meter),
|
|
1274
|
-
# BTAP costing requires extending the areas (m2) of OpenStudio wall
|
|
1275
|
-
# surfaces (along parapet edges) by 3'-6" (1.1 m) x parapet lengths, to
|
|
1276
|
-
# account for the extra cost of completely wrapping the parapet in
|
|
1277
|
-
# insulation for "good" (HP) details. See final TBD tally - @todo.
|