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.
Files changed (174) hide show
  1. checksums.yaml +4 -4
  2. data/data/standards/test_performance_expected_dd_results.csv +90 -90
  3. data/lib/openstudio-standards/btap/activity.rb +858 -61
  4. data/lib/openstudio-standards/btap/attributes.rb +297 -103
  5. data/lib/openstudio-standards/btap/bridging.rb +1335 -593
  6. data/lib/openstudio-standards/btap/btap.rb +60 -181
  7. data/lib/openstudio-standards/btap/carbon/btap_carbon.rb +199 -142
  8. data/lib/openstudio-standards/btap/common/constructions.json +1275 -0
  9. data/lib/openstudio-standards/btap/common/costs.csv +1969 -0
  10. data/lib/openstudio-standards/btap/common/costs_local_factors.csv +2315 -0
  11. data/lib/openstudio-standards/btap/common/materials_glazing.csv +61 -0
  12. data/lib/openstudio-standards/btap/{common_resources → common}/materials_hvac.csv +1685 -1685
  13. data/lib/openstudio-standards/btap/{common_resources → common}/materials_lighting.csv +266 -266
  14. data/lib/openstudio-standards/btap/common/materials_opaque.csv +214 -0
  15. data/lib/openstudio-standards/btap/common/necb_building_types.csv +35 -0
  16. data/lib/openstudio-standards/btap/common/necb_space_types.csv +110 -0
  17. data/lib/openstudio-standards/btap/common/thermal_bridging.csv +73 -0
  18. data/lib/openstudio-standards/btap/costing/btap_costing.rb +346 -429
  19. data/lib/openstudio-standards/btap/costing/database.rb +104 -0
  20. data/lib/openstudio-standards/btap/costing/daylighting_sensor_control_costing.rb +356 -344
  21. data/lib/openstudio-standards/btap/costing/dcv_costing.rb +282 -296
  22. data/lib/openstudio-standards/btap/costing/envelope_costing.rb +179 -192
  23. data/lib/openstudio-standards/btap/costing/heating_cooling_costing.rb +2477 -2316
  24. data/lib/openstudio-standards/btap/costing/led_lighting_costing.rb +127 -142
  25. data/lib/openstudio-standards/btap/costing/lighting_costing.rb +216 -198
  26. data/lib/openstudio-standards/btap/costing/nv_costing.rb +522 -523
  27. data/lib/openstudio-standards/btap/costing/pv_ground_costing.rb +649 -649
  28. data/lib/openstudio-standards/btap/costing/shw_costing.rb +626 -625
  29. data/lib/openstudio-standards/btap/costing/thermal_bridging_costing.rb +72 -0
  30. data/lib/openstudio-standards/btap/costing/ventilation_costing.rb +2363 -2361
  31. data/lib/openstudio-standards/btap/deprecated.rb +1033 -0
  32. data/lib/openstudio-standards/btap/envelope.rb +313 -572
  33. data/lib/openstudio-standards/btap/fileio.rb +152 -1070
  34. data/lib/openstudio-standards/btap/geometry.rb +486 -766
  35. data/lib/openstudio-standards/btap/linear_regression.rb +130 -0
  36. data/lib/openstudio-standards/btap/paths.rb +46 -0
  37. data/lib/openstudio-standards/btap/qaqc/analysis.rb +163 -0
  38. data/lib/openstudio-standards/btap/qaqc/data.rb +2293 -0
  39. data/lib/openstudio-standards/btap/qaqc/datapoint.rb +641 -0
  40. data/lib/openstudio-standards/btap/schedules.rb +11 -611
  41. data/lib/openstudio-standards/btap/structure.rb +574 -189
  42. data/lib/openstudio-standards/constructions/materials/information.rb +1 -1
  43. data/lib/openstudio-standards/create_typical/create_typical.rb +1 -1
  44. data/lib/openstudio-standards/exterior_lighting/create.rb +135 -85
  45. data/lib/openstudio-standards/exterior_lighting/data/typical_exterior_lighting.json +20 -0
  46. data/lib/openstudio-standards/exterior_lighting/information.rb +23 -2
  47. data/lib/openstudio-standards/geometry/create.rb +7 -3
  48. data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.Model.rb +3 -2
  49. data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Model.rb +3 -2
  50. data/lib/openstudio-standards/prototypes/common/data/thermostat_schedule_lookup.json +84 -84
  51. data/lib/openstudio-standards/prototypes/common/objects/Prototype.hvac_systems.rb +4 -4
  52. data/lib/openstudio-standards/refrigeration/create_walkin.rb +45 -5
  53. data/lib/openstudio-standards/refrigeration/data/refrigerated_walkins.csv +75 -75
  54. data/lib/openstudio-standards/schedules/parametric.rb +6 -0
  55. data/lib/openstudio-standards/service_water_heating/create_typical.rb +10 -3
  56. data/lib/openstudio-standards/service_water_heating/create_water_heating_loop.rb +30 -14
  57. data/lib/openstudio-standards/service_water_heating/information.rb +4 -1
  58. data/lib/openstudio-standards/sql_file/unmet_hours.rb +2 -1
  59. data/lib/openstudio-standards/standards/Standards.CoilCoolingDXSingleSpeed.rb +3 -1
  60. data/lib/openstudio-standards/standards/Standards.CoilHeatingDXSingleSpeed.rb +3 -1
  61. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.boilers.json +12 -0
  62. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.construction_properties.json +18432 -3072
  63. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.boilers.json +26 -0
  64. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.construction_properties.json +8532 -1422
  65. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.boilers.json +26 -0
  66. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.construction_properties.json +8532 -1422
  67. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.boilers.json +16 -0
  68. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.computer_room_acs.json +30 -30
  69. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.construction_properties.json +7560 -1260
  70. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.PumpVariableSpeed.rb +1 -2
  71. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.boilers.json +16 -0
  72. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.construction_properties.json +7560 -1260
  73. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirLoopHVAC.rb +3 -3
  74. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.PumpVariableSpeed.rb +1 -2
  75. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.boilers.json +22 -0
  76. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.construction_properties.json +7236 -1206
  77. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.furnaces.json +223 -19
  78. data/lib/openstudio-standards/standards/ashrae_90_1/data/ashrae_90_1.schedules.json +37 -1
  79. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.boilers.json +3 -0
  80. 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
  81. data/lib/openstudio-standards/standards/cbes/data/cbes.schedules.json +1 -1
  82. data/lib/openstudio-standards/standards/deer/data/deer.schedules.json +1 -1
  83. data/lib/openstudio-standards/standards/necb/BTAP1980TO2010/data/surface_thermal_transmittance.json +76 -40
  84. data/lib/openstudio-standards/standards/necb/BTAPPRE1980/data/surface_thermal_transmittance.json +76 -40
  85. data/lib/openstudio-standards/standards/necb/ECMS/hvac_systems.rb +6 -4
  86. data/lib/openstudio-standards/standards/necb/NECB2011/NECB2011_QAQC.json +2988 -0
  87. data/lib/openstudio-standards/standards/necb/NECB2011/autozone.rb +25 -3
  88. data/lib/openstudio-standards/standards/necb/NECB2011/building_envelope.rb +672 -534
  89. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/Hospital.osm +1791 -1792
  90. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPMultiTower.osm +7 -1
  91. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPTownHouse.osm +7 -1
  92. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LargeHotel.osm +5 -6
  93. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LargeOffice.osm +8 -9
  94. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/NorthernEducation.osm +10 -4
  95. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/NorthernHealthCare.osm +14 -15
  96. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/Outpatient.osm +7 -1
  97. data/lib/openstudio-standards/standards/necb/NECB2011/data/surface_thermal_transmittance.json +76 -40
  98. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb +14 -6
  99. data/lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb +542 -130
  100. data/lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb +30 -63
  101. data/lib/openstudio-standards/standards/necb/NECB2011/service_water_heating.rb +7 -2
  102. data/lib/openstudio-standards/standards/necb/NECB2015/data/surface_thermal_transmittance.json +90 -0
  103. data/lib/openstudio-standards/standards/necb/NECB2015/necb_2015.rb +8 -6
  104. data/lib/openstudio-standards/standards/necb/NECB2017/data/surface_thermal_transmittance.json +76 -40
  105. data/lib/openstudio-standards/standards/necb/NECB2017/necb_2017.rb +2 -1
  106. data/lib/openstudio-standards/standards/necb/NECB2020/building_envelope.rb +0 -117
  107. data/lib/openstudio-standards/standards/necb/NECB2020/data/surface_thermal_transmittance.json +76 -45
  108. data/lib/openstudio-standards/standards/necb/NECB2020/necb_2020.rb +218 -0
  109. data/lib/openstudio-standards/standards/necb/common/construction_defaults.osm +12 -7
  110. data/lib/openstudio-standards/standards/standard.rb +1 -1
  111. data/lib/openstudio-standards/version.rb +1 -1
  112. data/lib/openstudio-standards.rb +9 -4
  113. metadata +59 -66
  114. data/data/inventory/thermal_bridging.csv +0 -90
  115. data/data/standards/OpenStudio_Standards-ashrae_90_1-ALL-comstock(space_types).xlsx +0 -0
  116. data/data/standards/openstudio_standards_duplicates_log.csv +0 -7962
  117. data/lib/openstudio-standards/btap/NECB_building_types.csv +0 -35
  118. data/lib/openstudio-standards/btap/NECB_space_types.csv +0 -109
  119. data/lib/openstudio-standards/btap/WeatherData1.json +0 -2110
  120. data/lib/openstudio-standards/btap/WeatherData1.xlsx +0 -0
  121. data/lib/openstudio-standards/btap/btap_result.rb +0 -1144
  122. data/lib/openstudio-standards/btap/btap_test_helper.rb +0 -86
  123. data/lib/openstudio-standards/btap/common_paths.rb +0 -81
  124. data/lib/openstudio-standards/btap/common_resources/ConstructionProperties.csv +0 -52
  125. data/lib/openstudio-standards/btap/common_resources/Constructions.csv +0 -37
  126. data/lib/openstudio-standards/btap/common_resources/construction_sets.csv +0 -1270
  127. data/lib/openstudio-standards/btap/common_resources/constructions_glazing.csv +0 -121
  128. data/lib/openstudio-standards/btap/common_resources/constructions_opaque.csv +0 -2256
  129. data/lib/openstudio-standards/btap/common_resources/costs.csv +0 -1897
  130. data/lib/openstudio-standards/btap/common_resources/costs_local_factors.csv +0 -2315
  131. data/lib/openstudio-standards/btap/common_resources/materials_glazing.csv +0 -35
  132. data/lib/openstudio-standards/btap/common_resources/materials_opaque.csv +0 -164
  133. data/lib/openstudio-standards/btap/costing/btap_measure_helper.rb +0 -359
  134. data/lib/openstudio-standards/btap/costing/btap_workflow.rb +0 -117
  135. data/lib/openstudio-standards/btap/costing/copy_test_results_files_to_expected_results.rb +0 -11
  136. data/lib/openstudio-standards/btap/costing/cost_building_from_file.rb +0 -136
  137. data/lib/openstudio-standards/btap/costing/costing_database_wrapper.rb +0 -173
  138. data/lib/openstudio-standards/btap/costing/dummy.epw +0 -8768
  139. data/lib/openstudio-standards/btap/costing/dummy.osm +0 -5320
  140. data/lib/openstudio-standards/btap/costing/neb_end_use_prices.csv +0 -42
  141. data/lib/openstudio-standards/btap/costing/necb_2011_spacetype_info.csv +0 -225
  142. data/lib/openstudio-standards/btap/costing/necb_reference_runs.csv +0 -28705
  143. data/lib/openstudio-standards/btap/costing/parallel_tests.rb +0 -92
  144. data/lib/openstudio-standards/btap/costing/test_list.txt +0 -15
  145. data/lib/openstudio-standards/btap/costing/test_run_all_test_locally.rb +0 -27
  146. data/lib/openstudio-standards/btap/costing/test_run_costing_tests.rb +0 -80
  147. data/lib/openstudio-standards/btap/necb_2011_spacetype_info.csv +0 -225
  148. data/lib/openstudio-standards/btap/utilities/Add_template_field_to_json.rb +0 -22
  149. data/lib/openstudio-standards/btap/utilities/convert_surfaces_to_adiabatic_necb_8426.rb +0 -204
  150. data/lib/openstudio-standards/btap/utilities/generate_space_types.rb +0 -8
  151. data/lib/openstudio-standards/btap/utilities/necb_to_epw_map.rb +0 -50
  152. data/lib/openstudio-standards/btap/utilities/os_sim_extract.rb +0 -48
  153. data/lib/openstudio-standards/btap/utilities/rename_surfaces.rb +0 -24
  154. data/lib/openstudio-standards/btap/utilities/round_surf_coords.rb +0 -32
  155. data/lib/openstudio-standards/btap/utilities/sched_create.rb +0 -145
  156. data/lib/openstudio-standards/btap/utilities/set_mult_to_adiabatic.rb +0 -53
  157. data/lib/openstudio-standards/btap/utilities/space_type.rb +0 -78
  158. data/lib/openstudio-standards/btap/utilities/weatherData1_xlsx_to_json.rb +0 -56
  159. data/lib/openstudio-standards/btap/vintagizer.rb +0 -161
  160. data/lib/openstudio-standards/btap/visualization.rb +0 -72
  161. data/lib/openstudio-standards/standards/necb/NECB2011/data/necb_2011_spacetype_info.csv +0 -225
  162. data/lib/openstudio-standards/standards/necb/common/btap_analysis.rb +0 -79
  163. data/lib/openstudio-standards/standards/necb/common/btap_data.rb +0 -2656
  164. data/lib/openstudio-standards/standards/necb/common/btap_datapoint.rb +0 -653
  165. data/lib/openstudio-standards/standards/necb/common/necb_reference_runs.csv +0 -28705
  166. data/lib/openstudio-standards/standards/necb/common/utility_pricing_2025-02-20.csv +0 -92
  167. /data/lib/openstudio-standards/btap/{common_resources → common}/carbon_frame.csv +0 -0
  168. /data/lib/openstudio-standards/btap/{common_resources → common}/carbon_glazing.csv +0 -0
  169. /data/lib/openstudio-standards/btap/{common_resources → common}/carbon_opaque.csv +0 -0
  170. /data/lib/openstudio-standards/btap/{common_resources → common}/hvac_vent_ahu.csv +0 -0
  171. /data/lib/openstudio-standards/btap/{common_resources → common}/lighting.csv +0 -0
  172. /data/lib/openstudio-standards/btap/{common_resources → common}/lighting_sets.csv +0 -0
  173. /data/lib/openstudio-standards/btap/{common_resources → common}/locations.csv +0 -0
  174. /data/lib/openstudio-standards/btap/{costing → common}/mech_sizing.json +0 -0
@@ -1,215 +1,202 @@
1
- class String
2
- def underscore
3
- self.gsub(/::/, '/').
4
- gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
5
- gsub(/([a-z\d])([A-Z])/, '\1_\2').
6
- tr("-", "_").
7
- downcase
8
- end
9
- end
10
-
11
- class BTAPCosting
12
- def cost_audit_envelope(model, prototype_creator)
13
- @attributes.surface_types.each do |surface_type|
14
- @costing_report["envelope"]["#{surface_type.underscore}_cost"] = 0.00
15
- @costing_report["envelope"]["#{surface_type.underscore}_area_m2"] = 0.0
16
- @costing_report["envelope"]["#{surface_type.underscore}_cost_per_m2"] = 0.00
17
- end
18
-
19
- @costing_report["envelope"]["construction_costs"] = []
1
+ module BTAP
2
+ class Costing
3
+ # @param prototype_creator [Standard]
4
+ def cost_audit_envelope(prototype_creator)
20
5
 
21
- generate_construction_cost_database_for_city(@costing_report["city"], @costing_report["province_state"])
22
-
23
- totEnvCost = 0
6
+ # Populate the `costing_report` hash to report costing details per surface
7
+ # type.
8
+ @attributes.surface_types.each do |surface_type|
9
+ @costing_report["envelope"]["#{@attributes.surface_types_to_snake[surface_type]}_cost"] = 0.0
10
+ @costing_report["envelope"]["#{@attributes.surface_types_to_snake[surface_type]}_area_m2"] = 0.0
11
+ @costing_report["envelope"]["#{@attributes.surface_types_to_snake[surface_type]}_cost_per_m2"] = 0.0
12
+ end
24
13
 
25
- @attributes.spaces.each do |space|
26
- # Get SpaceType defined for space.. if not defined it will skip the spacetype. May have to deal with Attic spaces.
27
- if space.spaceType.empty? or space.spaceType.get.standardsSpaceType.empty? or space.spaceType.get.standardsBuildingType.empty?
28
- raise ("standards Space type and building type is not defined for space:#{space.name.get}. Skipping this space for costing.")
14
+ # Cost all the constructions found by BTAP Attributes.
15
+ @attributes.constructions.each_value do |construction|
16
+ cost_construction(construction, @costing_report['province_state'], @costing_report['city'])
29
17
  end
30
18
 
19
+ @costing_report["envelope"]["surface_type_costs"] = []
20
+ @costing_report["envelope"]["construction_costs"] = {}
21
+
22
+ total_envelope_cost = 0
31
23
  @attributes.surface_types.each do |surface_type|
32
- # Get Costs for this construction type. This will get the cost for the particular construction type
33
- # for all rsi levels for this location. This has been collected by the API costs data. Note that a space_type
34
- # of "- undefined -" will create a nil construction_set!
35
-
36
- if space.construction_set.nil?
37
- cost_range_hash = {}
38
- else
39
- cost_range_hash = @costing_database['constructions_costs'].select { |construction|
40
- construction['construction_type_name'] == space.construction_set[surface_type] &&
41
- construction['province_state'] == @costing_report["province_state"] &&
42
- construction['city'] == @costing_report["city"]
43
- }
44
- end
24
+ unless @attributes.surface_types_to_assembly_tallies[surface_type].empty?
25
+ @attributes.surface_types_to_assembly_tallies[surface_type].each_pair do |assembly_name, tallies|
26
+
27
+ # Tallies collect surface areas for all surfaces in a type that
28
+ # contain the same assembly. More than one assembly can be present
29
+ # in the case of custom building types.
30
+ total_area = tallies["area"]
31
+ construction = @attributes.constructions[assembly_name]
32
+ rsi = construction["rsi"]
33
+ surface_is_glazing = construction["type"] == "glazing"
34
+
35
+ # Use the cost_range_array to interpolate the estimated cost for the
36
+ # RSI of the current surface.
37
+ cost_range_array = construction["subsets"].map { |subset| [subset["rsi"], subset["cost"]] }
38
+ cost, notes = BTAP::LinearRegression.interpolate(x_y_array: cost_range_array, x2: rsi)
39
+
40
+ # Calculate SHGC/film cost.
41
+ film_cost = 0.0
42
+ if construction["type"] == "glazing"
43
+
44
+ # Get the closest value in materials_glazing sheet of SolarFilms.
45
+ material_row = @costing_database["raw"]["materials_glazing"].select { |row|
46
+ row['material_type'] == 'Solarfilms' }.min_by {|row|
47
+ (construction["shgc"].to_f - row['solar_heat_gain_coefficient'].to_f).abs}
48
+
49
+ standard_film_cost = getCost(material_row['description'], material_row, 1.0)
50
+ regional_factors = get_regional_cost_factors(
51
+ @costing_report['province_state'],
52
+ @costing_report['city'],
53
+ material_row)
54
+
55
+ # Multiply regional cost and sum costs. Zip adds the arrays
56
+ # together, map multiplies each row and divides by 100.0 since the
57
+ # regional factor is a percentage.
58
+ film_cost = standard_film_cost.zip(regional_factors).map { |cost,region_factor|
59
+ cost * region_factor / 100.0 }.inject(0, :+)
60
+ end
45
61
 
46
- # We don't need all the information, just the rsi and cost. However, for windows rsi = 1/u_w_per_m2_k
47
- surfaceIsGlazing = (
48
- surface_type == 'ExteriorFixedWindow' || surface_type == 'ExteriorOperableWindow' ||
49
- surface_type == 'ExteriorSkylight' || surface_type == 'ExteriorTubularDaylightDiffuser' ||
50
- surface_type == 'ExteriorTubularDaylightDome' || surface_type == 'ExteriorGlassDoor')
51
- if surfaceIsGlazing
52
- cost_range_array = cost_range_hash.map { |cost|
53
- [
54
- (1.0 / cost['u_w_per_m2_k'].to_f),
55
- cost['total_cost_with_op']
56
- ]
57
- }
58
- else
59
- cost_range_array = cost_range_hash.map { |cost|
60
- [
61
- cost['rsi_k_m2_per_w'],
62
- cost['total_cost_with_op']
63
- ]
64
- }
62
+ total_area_feet = (OpenStudio.convert(total_area, "m^2", "ft^2").get).to_f
63
+ total_cost = (cost + film_cost) * total_area_feet
64
+ total_envelope_cost += total_cost
65
+
66
+ # Bin the cost to tally it up per-construction later.
67
+ tallies["cost"] = total_cost
68
+
69
+ # Bin the costing by construction type and rsi.
70
+ @costing_report["envelope"]["surface_type_costs"] << {
71
+ "assembly_name" => assembly_name,
72
+ "surface_type" => surface_type,
73
+ "conductance" => rsi.round(3),
74
+ "area" => total_area.round(2),
75
+ "cost" => total_cost.round(2),
76
+ "cost_per_area" => (total_cost / total_area).round(2),
77
+ "notes" => notes
78
+ }
79
+
80
+ @costing_report["envelope"]["#{@attributes.surface_types_to_snake[surface_type]}_cost"] += total_cost
81
+ @costing_report["envelope"]["#{@attributes.surface_types_to_snake[surface_type]}_area_m2"] += total_area
82
+ end
83
+ @costing_report["envelope"]["#{@attributes.surface_types_to_snake[surface_type]}_cost_per_m2"] = (
84
+ @costing_report["envelope"]["#{@attributes.surface_types_to_snake[surface_type]}_cost"] /
85
+ @costing_report["envelope"]["#{@attributes.surface_types_to_snake[surface_type]}_area_m2"])
86
+
87
+ if @costing_report["envelope"]["#{@attributes.surface_types_to_snake[surface_type]}_cost_per_m2"].nan?
88
+ @costing_report["envelope"]["#{@attributes.surface_types_to_snake[surface_type]}_cost_per_m2"] = 0.0
89
+ end
65
90
  end
66
- # Sorted based on rsi.
67
- cost_range_array.sort! { |a, b| a[0] <=> b[0] }
91
+ end
68
92
 
69
- # Iterate through actual surfaces in the model of surface_type.
70
- numSurfType = 0
71
- space.surfaces_hash[surface_type].each do |surface|
72
- numSurfType = numSurfType + 1
93
+ # Bin costs by entire constructions.
94
+ @attributes.surface_types_to_assembly_tallies.values.filter {|tallies| not tallies.empty?}.each do |entry|
95
+ entry.each_pair do |assembly_name, tallies|
96
+ if @costing_report["envelope"]["construction_costs"].has_key?(assembly_name)
97
+ bin = @costing_report["envelope"]["construction_costs"][assembly_name]
98
+ bin["area"] = (bin["area"] + tallies["area"]).round(2)
99
+ bin["cost"] = (bin["cost"] + tallies["cost"]).round(2)
100
+ bin["cost_per_area"] = (bin["cost"] / bin["area"]).round(2)
101
+ else
102
+ @costing_report["envelope"]["construction_costs"][assembly_name] = {
103
+ "conductance" => @attributes.constructions[assembly_name]["rsi"].round(3),
104
+ "area" => tallies["area"].round(2),
105
+ "cost" => tallies["cost"].round(2),
106
+ "cost_per_area" => (tallies["cost"] / tallies["area"]).round(2)
107
+ }
108
+ end
109
+ end
110
+ end
73
111
 
74
- # Get RSI of existing model surface (actually returns rsi for glazings too!).
75
- # Make an array of constructions to use with surfaces_get_conductance method which replaces the get_rsi
76
- # method
77
- rsi = 1 / (OpenstudioStandards::Constructions.construction_get_conductance(OpenStudio::Model::getConstructionByName(surface.model, surface.construction.get.name.to_s).get))
112
+ # Parapets aren't explicitly modeled in an OpenStudio model. If TBD was run,
113
+ # account for parapets by taking the calculated parapet length and multiply
114
+ # it by 1m to factor it into the total cost. So, take the cost of the
115
+ # exterior walls per m^2 and multiply it buy the total parapet length.
116
+ if @attributes.tbd_enabled
117
+ wall_cost_per_m2 = @costing_report["envelope"]["exterior_wall_cost_per_m2"]
118
+ parapet_cost = @attributes.tbd_edge_tallies["parapet"].values.first * wall_cost_per_m2
119
+ @costing_report["envelope"]["parapet_cost"] = parapet_cost.round(2)
120
+ total_envelope_cost += parapet_cost
121
+ end
78
122
 
123
+ # When using thermal bridging it may create too demanding of a model, or
124
+ # when data is insufficient, some R-factors will be outside of the data
125
+ # and BTAP::LinearRegression will refuse to extrapolate beyond its default
126
+ # range. If that occured, don't raise an error but add a really high
127
+ # number to the envelope cost to make the user aware of what happened.
128
+ if BTAP::LinearRegression.extrapolation_boundaries_exceeded?
129
+ revolutionary_engineering_technology_fudge_factor = 1000000000000
130
+ total_envelope_cost += revolutionary_engineering_technology_fudge_factor
131
+ @costing_report["envelope"]["unrealistic_assembly_cost"] = revolutionary_engineering_technology_fudge_factor
132
+ @costing_report["envelope"]["unrealistic_assembly_note"] = \
133
+ "Could not extrapolate beyond the given range. The given model might be unrealistic to build because no " \
134
+ "assemblies exist in the database with the given heat transfer requirements. This could be that the " \
135
+ "thermal bridging module created too demanding of a model given the performance constraints. Try changing " \
136
+ "the `tbd_option` parameter in your run options."
137
+ end
79
138
 
80
- #Check to see if it is in range
139
+ # The following attribute establishes for sure whether the TBD module
140
+ # was able to produce all compliant assemblies when uprating.
141
+ unless @attributes.tbd_compliance.nil?
142
+ @costing_report["envelope"]["tbd_compliance"] = @attributes.tbd_compliance
143
+ end
81
144
 
145
+ # Round everything at the end.
146
+ @attributes.surface_types.each do |surface_type|
147
+ @costing_report["envelope"]["#{@attributes.surface_types_to_snake[surface_type]}_cost"] = \
148
+ @costing_report["envelope"]["#{@attributes.surface_types_to_snake[surface_type]}_cost"].round(2)
149
+ @costing_report["envelope"]["#{@attributes.surface_types_to_snake[surface_type]}_area_m2"] = \
150
+ @costing_report["envelope"]["#{@attributes.surface_types_to_snake[surface_type]}_area_m2"].round(2)
151
+ @costing_report["envelope"]["#{@attributes.surface_types_to_snake[surface_type]}_cost_per_m2"] = \
152
+ @costing_report["envelope"]["#{@attributes.surface_types_to_snake[surface_type]}_cost_per_m2"].round(2)
153
+ end
82
154
 
83
- # Use the cost_range_array to interpolate the estimated cost for the given rsi.
84
- # Note that window costs in the API data use U-value, which was converted to rsi for cost_range_array above
85
- exterpolate_percentage_range = 30.0
86
- cost = interpolate(x_y_array: cost_range_array, x2: rsi, exterpolate_percentage_range: exterpolate_percentage_range)
155
+ @costing_report["envelope"]["total_envelope_cost"] = total_envelope_cost.to_f.round(2)
156
+ puts "\nEnvelope costing data successfully generated. Total envelope cost is $#{total_envelope_cost.to_f.round(2)}"
87
157
 
158
+ return total_envelope_cost
159
+ end
88
160
 
89
- # If the cost is nil, that means the rsi is out of range. Flag in the report.
90
- if cost.nil?
91
- if !cost_range_array.empty?
92
- notes = "Warning! RSI out of the range (#{'%.2f' % rsi}) or cost is 0!. Range for #{space.construction_set[surface_type]} is #{'%.2f' % cost_range_array.first[0]}-#{'%.2f' % cost_range_array.last[0]}."
93
- cost = 0.0
94
- else
95
- notes = "No cost found for this! So Cost is set to 0.0!"
96
- cost = 0.0
97
- end
98
- elsif cost.nan?
99
- raise("the values for cost and conductance for #{space.construction_set[surface_type]} cannot be interpolated...cannot create an equation of a line from #{cost_range_array.sort.uniq}. Check construction database and either eliminate the errant row, or set the x value to an appropriate number. ")
161
+ # Append the "cost" key-value pair to a construction hash by calculating
162
+ # the cost from each layer from either the opaque (materials_opaque.csv)
163
+ # or glazing (materials_glazing.csv) while also accounting for regional and
164
+ # installation factors.
165
+ #
166
+ # @param construction [Hash]
167
+ # @param province_state [String]
168
+ # @param city [String]
169
+ def cost_construction(construction, province_state, city)
170
+ material_id = "materials_#{construction["type"]}_id"
171
+ materials_database = @costing_database["raw"]["materials_#{construction["type"]}"]
172
+ total_with_op = 0.0
173
+
174
+ construction["subsets"].each do |subset|
175
+ total_with_op = 0
176
+ subset["id_layers"].each do |material_index|
177
+ material = materials_database.find { |data| data[material_id] == material_index }
178
+ if material.nil?
179
+ raise("Material ID #{material_index} was not found in the materials_#{construction["type"]} database.")
100
180
  else
101
- #Tell user if we are extrapolating outside of library.
102
- array = cost_range_array.sort { |a, b| a[0] <=> b[0] }
103
- if rsi < (array.first[0].to_f) || rsi > (array.last[0].to_f)
104
- notes = "RSI out of the range (#{'%.2f' % rsi}). Range for #{space.construction_set[surface_type]} is #{'%.2f' % cost_range_array.first[0]}-#{'%.2f' % cost_range_array.last[0]}.Using extrapolation up to +/-30% of library boundaries. "
181
+ costing_data = @costing_database['costs'].detect { |data| data['id'] == material['id'] }
182
+ if costing_data.nil?
183
+ raise("Material ID #{material_index} was not found in the costing database")
105
184
  else
106
- notes = "OK"
185
+ regional_material, regional_installation = get_regional_cost_factors(province_state, city, material)
186
+
187
+ # Get cost information from lookup.
188
+ material_cost = costing_data['baseCosts']['materialOpCost'] * material['material_mult'].to_f
189
+ labour_cost = costing_data['baseCosts']['laborOpCost'] * material['labour_mult'].to_f
190
+ equipment_cost = costing_data['baseCosts']['equipmentOpCost']
191
+ layer_cost = (((material_cost * regional_material / 100.0) + \
192
+ (labour_cost * regional_installation / 100.0) + equipment_cost) * \
193
+ material['quantity'].to_f).round(2)
194
+ total_with_op += layer_cost
107
195
  end
108
196
  end
109
-
110
- # Calculate SHGC/film cost
111
- film_cost = 0.0
112
- if surfaceIsGlazing
113
- #Get SHGC from surface.
114
- shgc = OpenstudioStandards::Constructions.construction_get_solar_transmittance(surface.construction.get.to_Construction.get)
115
- # Get the closest value in materials_glazing sheet of SolarFilms.
116
- material_row = @costing_database["raw"]["materials_glazing"].select{ |row| row['material_type'] == 'Solarfilms' }.min_by {|row| (shgc.to_f - row['solar_heat_gain_coefficient'].to_f).abs}
117
- standard_film_cost = getCost(material_row['description'], material_row, 1.0)
118
- regional_factors = get_regional_cost_factors(@costing_report['province_state'], @costing_report['city'], material_row)
119
- # mult regional cost and sum costs. Zip adds the arrays together, map multiplies each row and divides by 100.0 since the regional factor is in percents.
120
- film_cost = standard_film_cost.zip(regional_factors).map{|cost,region_factor| cost * region_factor / 100.0}.inject(0, :+)
121
- end
122
-
123
-
124
- testSurfName = surface.name.to_s
125
- testSpaceName = space.name.to_s
126
- surfArea = surface.netArea * space.thermalZone.get.multiplier
127
- surfAreaft = (OpenStudio.convert(surfArea, "m^2", "ft^2").get).to_f
128
- surfCost = (cost + film_cost) * surfAreaft
129
- totEnvCost = totEnvCost + surfCost
130
- name = ""
131
-
132
- # Bin the costing by construction standard type and rsi
133
- if space.construction_set.nil?
134
- name = "undefined space type_#{(1.0 / rsi).round(3)}"
135
- else
136
- name = "#{space.construction_set[surface_type]}"
137
- end
138
- row = @costing_report["envelope"]["construction_costs"].detect { |row| (row['name'] == name) && (row['conductance'].round(3) == ((1.0 / rsi).round(3))) }
139
- if row.nil?
140
- @costing_report["envelope"]["construction_costs"] << {'name' => name, 'conductance' => ((1.0 / rsi).round(3)), 'area' => (surfArea.round(2)), 'cost' => (surfCost.round(2)), 'cost_per_area' => (surfCost / surfArea).round(2), 'note' => "Surf ##{numSurfType}: #{notes}"}
141
- else
142
- # Not using += for @costing_report additions so that output can be properly rounded
143
- row['area'] = (row['area'] + surfArea).round(2)
144
- row['cost'] = (row['cost'] + surfCost).round(2)
145
- row['cost_per_area'] = ((row['cost'] / row['area']).to_f.round(2))
146
- row['note'] += " / #{numSurfType}: #{notes}"
147
- end
148
- # Not using += for @costing_report additions so that output can be properly rounded
149
- @costing_report["envelope"]["#{surface_type.underscore}_cost"] = (@costing_report["envelope"]["#{surface_type.underscore}_cost"] + surfCost).round(2)
150
- @costing_report["envelope"]["#{surface_type.underscore}_area_m2"] = (@costing_report["envelope"]["#{surface_type.underscore}_area_m2"] + surfArea).round(2)
151
- @costing_report["envelope"]["#{surface_type.underscore}_cost_per_m2"] = (@costing_report["envelope"]["#{surface_type.underscore}_cost"] / @costing_report["envelope"]["#{surface_type.underscore}_area_m2"]).round(2)
152
- end # surfaces of surface type
153
- end # surface_type
154
- end # spaces
155
-
156
- @costing_report["envelope"]['total_envelope_cost'] = totEnvCost.to_f.round(2)
157
- puts "\nEnvelope costing data successfully generated. Total envelope cost is $#{totEnvCost.to_f.round(2)}"
158
-
159
- return totEnvCost
160
- end
161
-
162
- def cost_construction(construction, location, type = 'opaque')
163
-
164
- material_layers = "material_#{type}_id_layers"
165
- material_id = "materials_#{type}_id"
166
- materials_database = @costing_database["raw"]["materials_#{type}"]
167
-
168
- total_with_op = 0.0
169
- material_cost_pairs = []
170
- construction[material_layers].split(',').reject { |c| c.empty? }.each do |material_index|
171
- material = materials_database.find { |data| data[material_id].to_s == material_index.to_s }
172
- if material.nil?
173
- puts "material error..could not find material #{material_index} in #{materials_database}"
174
- raise()
175
- else
176
- costing_data = @costing_database['costs'].detect { |data| data['id'].to_s.upcase == material['id'].to_s.upcase }
177
- if costing_data.nil?
178
- puts "This material id #{material['id']} was not found in the costing database. Skipping. This construction will be inaccurate. "
179
- raise()
180
- else
181
- regional_material, regional_installation = get_regional_cost_factors(location['province_state'], location['city'], material)
182
-
183
- # Get cost information from lookup.
184
- # Note that "glazing" types don't have a 'quantity' hash entry!
185
- # Don't need "and" below but using in-case this hash field is added in the future.
186
- if type == 'glazing' and material['quantity'].to_f == 0.0
187
- material['quantity'] = '1.0'
188
- end
189
- material_cost = costing_data['baseCosts']['materialOpCost'].to_f * material['material_mult'].to_f
190
- labour_cost = costing_data['baseCosts']['laborOpCost'].to_f * material['labour_mult'].to_f
191
- equipment_cost = costing_data['baseCosts']['equipmentOpCost'].to_f
192
- layer_cost = (((material_cost * regional_material / 100.0) + (labour_cost * regional_installation / 100.0) + equipment_cost) * material['quantity'].to_f).round(2)
193
- material_cost_pairs << {material_id.to_s => material_index,
194
- 'cost' => layer_cost}
195
- total_with_op += layer_cost
196
197
  end
198
+ subset["cost"] = total_with_op
197
199
  end
198
200
  end
199
- new_construction = {
200
- 'province_state' => location['province_state'],
201
- 'city' => location['city'],
202
- "construction_type_name" => construction["construction_type_name"],
203
- 'description' => construction["description"],
204
- 'intended_surface_type' => construction["intended_surface_type"],
205
- 'standards_construction_type' => construction["standards_construction_type"],
206
- 'rsi_k_m2_per_w' => construction['rsi_k_m2_per_w'].to_f,
207
- 'zone' => construction['climate_zone'],
208
- 'fenestration_type' => construction['fenestration_type'],
209
- 'u_w_per_m2_k' => construction['u_w_per_m2_k'],
210
- 'materials' => material_cost_pairs,
211
- 'total_cost_with_op' => total_with_op}
212
-
213
- @costing_database['constructions_costs'] << new_construction
214
201
  end
215
- end
202
+ end