openstudio-standards 0.8.2 → 0.8.4

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 (152) hide show
  1. checksums.yaml +4 -4
  2. data/data/standards/OpenStudio_Standards-ashrae_90_1-ALL-comstock(space_types).xlsx +0 -0
  3. data/data/standards/openstudio_standards_duplicates_log.csv +7962 -0
  4. data/lib/openstudio-standards/btap/costing/README.md +502 -0
  5. data/lib/openstudio-standards/btap/costing/btap_costing.rb +473 -0
  6. data/lib/openstudio-standards/btap/costing/btap_measure_helper.rb +359 -0
  7. data/lib/openstudio-standards/btap/costing/btap_workflow.rb +117 -0
  8. data/lib/openstudio-standards/btap/costing/common_paths.rb +78 -0
  9. data/lib/openstudio-standards/btap/costing/common_resources/ConstructionProperties.csv +52 -0
  10. data/lib/openstudio-standards/btap/costing/common_resources/Constructions.csv +37 -0
  11. data/lib/openstudio-standards/btap/costing/common_resources/construction_sets.csv +1270 -0
  12. data/lib/openstudio-standards/btap/costing/common_resources/constructions_glazing.csv +61 -0
  13. data/lib/openstudio-standards/btap/costing/common_resources/constructions_opaque.csv +2256 -0
  14. data/lib/openstudio-standards/btap/costing/common_resources/costs.csv +1904 -0
  15. data/lib/openstudio-standards/btap/costing/common_resources/costs_local_factors.csv +2315 -0
  16. data/lib/openstudio-standards/btap/costing/common_resources/hvac_vent_ahu.csv +925 -0
  17. data/lib/openstudio-standards/btap/costing/common_resources/lighting.csv +364 -0
  18. data/lib/openstudio-standards/btap/costing/common_resources/lighting_sets.csv +2667 -0
  19. data/lib/openstudio-standards/btap/costing/common_resources/locations.csv +75 -0
  20. data/lib/openstudio-standards/btap/costing/common_resources/materials_glazing.csv +35 -0
  21. data/lib/openstudio-standards/btap/costing/common_resources/materials_hvac.csv +1699 -0
  22. data/lib/openstudio-standards/btap/costing/common_resources/materials_lighting.csv +267 -0
  23. data/lib/openstudio-standards/btap/costing/common_resources/materials_opaque.csv +164 -0
  24. data/lib/openstudio-standards/btap/costing/copy_test_results_files_to_expected_results.rb +11 -0
  25. data/lib/openstudio-standards/btap/costing/cost_building_from_file.rb +136 -0
  26. data/lib/openstudio-standards/btap/costing/costing_database_wrapper.rb +177 -0
  27. data/lib/openstudio-standards/btap/costing/daylighting_sensor_control_costing.rb +353 -0
  28. data/lib/openstudio-standards/btap/costing/dcv_costing.rb +314 -0
  29. data/lib/openstudio-standards/btap/costing/dummy.epw +8768 -0
  30. data/lib/openstudio-standards/btap/costing/dummy.osm +5320 -0
  31. data/lib/openstudio-standards/btap/costing/envelope_costing.rb +284 -0
  32. data/lib/openstudio-standards/btap/costing/heating_cooling_costing.rb +2584 -0
  33. data/lib/openstudio-standards/btap/costing/led_lighting_costing.rb +155 -0
  34. data/lib/openstudio-standards/btap/costing/lighting_costing.rb +209 -0
  35. data/lib/openstudio-standards/btap/costing/mech_sizing.json +502 -0
  36. data/lib/openstudio-standards/btap/costing/neb_end_use_prices.csv +42 -0
  37. data/lib/openstudio-standards/btap/costing/necb_2011_spacetype_info.csv +225 -0
  38. data/lib/openstudio-standards/btap/costing/necb_reference_runs.csv +28705 -0
  39. data/lib/openstudio-standards/btap/costing/nv_costing.rb +547 -0
  40. data/lib/openstudio-standards/btap/costing/parallel_tests.rb +92 -0
  41. data/lib/openstudio-standards/btap/costing/pv_ground_costing.rb +687 -0
  42. data/lib/openstudio-standards/btap/costing/shw_costing.rb +705 -0
  43. data/lib/openstudio-standards/btap/costing/test_list.txt +17 -0
  44. data/lib/openstudio-standards/btap/costing/test_run_all_test_locally.rb +26 -0
  45. data/lib/openstudio-standards/btap/costing/test_run_costing_tests.rb +80 -0
  46. data/lib/openstudio-standards/btap/costing/ventilation_costing.rb +2616 -0
  47. data/lib/openstudio-standards/constructions/modify.rb +2 -1
  48. data/lib/openstudio-standards/refrigeration/create_case.rb +58 -21
  49. data/lib/openstudio-standards/refrigeration/create_typical_refrigeration.rb +4 -2
  50. data/lib/openstudio-standards/refrigeration/create_walkin.rb +57 -22
  51. data/lib/openstudio-standards/refrigeration/data/refrigerated_cases.csv +31 -31
  52. data/lib/openstudio-standards/refrigeration/data/refrigerated_walkins.csv +76 -76
  53. data/lib/openstudio-standards/service_water_heating/create_typical.rb +10 -10
  54. data/lib/openstudio-standards/service_water_heating/create_water_heater.rb +10 -0
  55. data/lib/openstudio-standards/service_water_heating/create_water_heating_loop.rb +16 -3
  56. data/lib/openstudio-standards/service_water_heating/data/convert_data.rb +9 -9
  57. data/lib/openstudio-standards/service_water_heating/data/typical_water_use_equipment.csv +49 -49
  58. data/lib/openstudio-standards/service_water_heating/data/typical_water_use_equipment.json +159 -159
  59. data/lib/openstudio-standards/standards/Standards.CoilCoolingDXMultiSpeed.rb +7 -18
  60. data/lib/openstudio-standards/standards/Standards.CoilCoolingDXSingleSpeed.rb +10 -20
  61. data/lib/openstudio-standards/standards/Standards.CoilCoolingDXTwoSpeed.rb +6 -15
  62. data/lib/openstudio-standards/standards/Standards.CoilCoolingWaterToAirHeatPumpEquationFit.rb +5 -6
  63. data/lib/openstudio-standards/standards/Standards.CoilDX.rb +93 -43
  64. data/lib/openstudio-standards/standards/Standards.CoilHeatingDXMultiSpeed.rb +5 -5
  65. data/lib/openstudio-standards/standards/Standards.CoilHeatingDXSingleSpeed.rb +135 -37
  66. data/lib/openstudio-standards/standards/Standards.CoilHeatingWaterToAirHeatPumpEquationFit.rb +2 -2
  67. data/lib/openstudio-standards/standards/Standards.Model.rb +48 -13
  68. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.computer_room_acs.json +302 -140
  69. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.heat_pumps.json +648 -326
  70. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.heat_pumps_heating.json +371 -90
  71. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.water_heaters.json +66 -22
  72. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.computer_room_acs.json +302 -140
  73. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.heat_pumps.json +1012 -296
  74. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.heat_pumps_heating.json +443 -79
  75. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.water_heaters.json +66 -22
  76. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.computer_room_acs.json +302 -140
  77. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.heat_pumps.json +672 -306
  78. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.heat_pumps_heating.json +354 -74
  79. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.water_heaters.json +72 -24
  80. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.computer_room_acs.json +302 -140
  81. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.energy_recovery.json +8 -8
  82. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.heat_pumps.json +930 -604
  83. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.heat_pumps_heating.json +415 -111
  84. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.water_heaters.json +72 -24
  85. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.computer_room_acs.json +602 -140
  86. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.heat_pumps.json +1005 -333
  87. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.heat_pumps_heating.json +642 -88
  88. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.water_heaters.json +78 -26
  89. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.computer_room_acs.json +722 -140
  90. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.heat_pumps.json +1741 -426
  91. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.heat_pumps_heating.json +1108 -111
  92. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.water_heaters.json +186 -62
  93. data/lib/openstudio-standards/standards/ashrae_90_1/data/ashrae_90_1.schedules.json +62 -232
  94. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoilCoolingDXSingleSpeed.rb +2 -3
  95. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoilCoolingDXTwoSpeed.rb +1 -1
  96. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoilDX.rb +7 -18
  97. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoilHeatingDXSingleSpeed.rb +9 -7
  98. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoilHeatingGas.rb +1 -1
  99. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.Model.rb +2 -2
  100. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.heat_pumps.json +154 -69
  101. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.heat_pumps_heating.json +72 -72
  102. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.water_heaters.json +382 -295
  103. data/lib/openstudio-standards/standards/ashrae_90_1_prm/userdata_csv/ashrae_90_1_prm.UserData.rb +6 -1
  104. data/lib/openstudio-standards/standards/deer/data/deer.schedules.json +62 -232
  105. data/lib/openstudio-standards/standards/necb/BTAPPRE1980/btap_pre1980.rb +2 -27
  106. data/lib/openstudio-standards/standards/necb/BTAPPRE1980/data/heat_pumps.json +16 -0
  107. data/lib/openstudio-standards/standards/necb/BTAPPRE1980/data/heat_pumps_heating.json +6 -0
  108. data/lib/openstudio-standards/standards/necb/BTAPPRE1980/hvac_system_3_and_8_single_speed.rb +68 -27
  109. data/lib/openstudio-standards/standards/necb/BTAPPRE1980/hvac_system_4.rb +64 -25
  110. data/lib/openstudio-standards/standards/necb/BTAPPRE1980/hvac_system_6.rb +9 -14
  111. data/lib/openstudio-standards/standards/necb/ECMS/hvac_systems.rb +46 -20
  112. data/lib/openstudio-standards/standards/necb/NECB2011/autozone.rb +635 -248
  113. data/lib/openstudio-standards/standards/necb/NECB2011/data/constants.json +43 -7
  114. data/lib/openstudio-standards/standards/necb/NECB2011/data/fuel_type_sets.json +7 -1
  115. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/HighriseApartmentMult.osm +14272 -0
  116. data/lib/openstudio-standards/standards/necb/NECB2011/data/heat_pumps.json +16 -0
  117. data/lib/openstudio-standards/standards/necb/NECB2011/data/heat_pumps_heating.json +6 -0
  118. data/lib/openstudio-standards/standards/necb/NECB2011/data/necb_2015_table_c1.json +1 -1
  119. data/lib/openstudio-standards/standards/necb/NECB2011/data/space_types.json +437 -437
  120. data/lib/openstudio-standards/standards/necb/NECB2011/data/systems.json +516 -0
  121. data/lib/openstudio-standards/standards/necb/NECB2011/data/systems_including_sys5.json +588 -0
  122. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_namer.rb +489 -0
  123. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_1_single_speed.rb +16 -6
  124. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_2_and_5.rb +48 -5
  125. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_3_and_8_multi_speed.rb +2 -2
  126. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_3_and_8_single_speed.rb +35 -27
  127. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_4.rb +34 -23
  128. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_6.rb +8 -6
  129. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb +43 -14
  130. data/lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb +214 -25
  131. data/lib/openstudio-standards/standards/necb/NECB2011/system_fuels.rb +61 -1
  132. data/lib/openstudio-standards/standards/necb/NECB2015/data/heat_pumps.json +16 -0
  133. data/lib/openstudio-standards/standards/necb/NECB2015/data/heat_pumps_heating.json +8 -0
  134. data/lib/openstudio-standards/standards/necb/NECB2015/data/space_types.json +636 -636
  135. data/lib/openstudio-standards/standards/necb/NECB2015/data/unitary_acs.json +38 -38
  136. data/lib/openstudio-standards/standards/necb/NECB2015/hvac_systems.rb +15 -6
  137. data/lib/openstudio-standards/standards/necb/NECB2017/data/space_types.json +636 -636
  138. data/lib/openstudio-standards/standards/necb/NECB2020/data/chillers.json +71 -71
  139. data/lib/openstudio-standards/standards/necb/NECB2020/data/heat_pumps.json +20 -0
  140. data/lib/openstudio-standards/standards/necb/NECB2020/data/heat_pumps_heating.json +10 -0
  141. data/lib/openstudio-standards/standards/necb/README.md +343 -0
  142. data/lib/openstudio-standards/standards/necb/common/btap_data.rb +190 -28
  143. data/lib/openstudio-standards/standards/necb/common/btap_datapoint.rb +14 -5
  144. data/lib/openstudio-standards/standards/necb/common/eccc_electric_grid_intensity_20250311.csv +14 -0
  145. data/lib/openstudio-standards/standards/necb/common/nir_gas_grid_intensity_20250311.csv +14 -0
  146. data/lib/openstudio-standards/standards/necb/common/system_types.yaml +0 -0
  147. data/lib/openstudio-standards/utilities/logging.rb +18 -14
  148. data/lib/openstudio-standards/utilities/simulation.rb +3 -2
  149. data/lib/openstudio-standards/version.rb +1 -1
  150. data/lib/openstudio-standards/weather/modify.rb +2 -2
  151. data/lib/openstudio-standards.rb +12 -0
  152. metadata +56 -3
@@ -0,0 +1,177 @@
1
+ require 'singleton'
2
+ require 'json'
3
+ require 'csv'
4
+ require_relative 'common_paths.rb'
5
+
6
+ # Singleton class to centralize all database operations
7
+ class CostingDatabase
8
+ include Singleton
9
+
10
+ def initialize
11
+ @cp = CommonPaths.instance # Stores paths
12
+ @db = Hash.new # Stores the costing database
13
+ end
14
+
15
+ # Load the database from the individual CSV files
16
+ def load_database
17
+
18
+ # Load costing data
19
+ @db['costs'] = [] # Costing data
20
+ @db['localization_factors'] = [] # Local costing factors
21
+ @db['raw'] = {} # Raw data
22
+ @db['db_errors'] = []
23
+
24
+ data_costs = CSV.read(@cp.costs_path)
25
+
26
+ 1.upto data_costs.length - 1 do |i|
27
+ row = data_costs[i]
28
+ index = row.each
29
+ item = Hash.new
30
+ item["baseCosts"] = Hash.new
31
+ costs = item["baseCosts"]
32
+
33
+ item["id"] = index.next
34
+ item["sheet"] = index.next
35
+ item["source"] = index.next
36
+ item["description"] = index.next
37
+ item["city"] = index.next
38
+ item["province_state"] = index.next
39
+ costs["materialOpCost"] = index.next.to_f
40
+ costs["laborOpCost"] = index.next.to_f
41
+ costs["equipmentOpCost"] = index.next.to_f
42
+
43
+ @db["costs"] << item
44
+ end
45
+
46
+ # Load the localization factors
47
+ data_factors = CSV.read(@cp.costs_local_factors_path)
48
+
49
+ 1.upto data_factors.length - 1 do |i|
50
+ row = data_factors[i]
51
+ index = row.each
52
+ item = Hash.new
53
+
54
+ item["province_state"] = index.next
55
+ item["city"] = index.next
56
+ item["division"] = index.next
57
+ item["code_prefix"] = index.next
58
+ item["material"] = index.next.to_f
59
+ item["installation"] = index.next.to_f
60
+ item["total"] = index.next.to_f
61
+
62
+ @db["localization_factors"] << item
63
+ end
64
+
65
+ # Load the raw data
66
+ raw_data_names = [
67
+ 'locations',
68
+ 'construction_sets',
69
+ 'constructions_opaque',
70
+ 'materials_opaque',
71
+ 'constructions_glazing',
72
+ 'materials_glazing',
73
+ 'Constructions',
74
+ 'ConstructionProperties',
75
+ 'lighting_sets',
76
+ 'lighting',
77
+ 'materials_lighting',
78
+ 'hvac_vent_ahu',
79
+ 'materials_hvac'
80
+ ]
81
+
82
+ 0.upto(raw_data_names.length - 1) do |i|
83
+ data_path = @cp.raw_paths[i]
84
+ unless File.exist?(data_path)
85
+ raise("Error: Could not find #{data_path}")
86
+ end
87
+ @db['raw'][raw_data_names[i]] = CSV.read(data_path, headers: true).map { |row| row.to_hash}
88
+ end
89
+ end
90
+
91
+ # Validate the construction sets and the AHU items.
92
+ def validate_database()
93
+ validate_constructions_sets()
94
+ validate_ahu_items_and_quantities()
95
+ end
96
+
97
+ def validate_constructions_sets()
98
+ construction_sets = @db['raw']['construction_sets']
99
+ failed = false
100
+ templates = ["NECB2011", "NECB2015", "NECB2017", "NECB2020", "BTAPPRE1980", "BTAP1980TO2010"]
101
+ bad_records = {}
102
+ bad_records[:invalid_space_type_names] = []
103
+ bad_records[:min_max_floor_range_errors] = []
104
+ # CHECK if spacetype names are valid in costing database
105
+ valid_space_types = []
106
+ templates.each do |template|
107
+ valid_space_types += Standard.build(template.gsub(/\s+/, "")).get_all_spacetype_names.map { |spacetype| (template + '-' + spacetype[0].to_s + '-' + spacetype[1].to_s).strip }
108
+ end
109
+ # construction_sets
110
+
111
+ construction_sets.each do |row|
112
+ target_space_type = "#{row['template'].gsub(/\s+/, "") + '-' + row['building_type']}-#{row['space_type']}".strip
113
+ unless valid_space_types.include?(target_space_type.to_s)
114
+ bad_records[:invalid_space_type_names] << {template: row['template'].gsub(/\s+/, ""), space_type: target_space_type}
115
+ end
116
+ end
117
+
118
+
119
+ # Check if # of floors contains 1 to 999
120
+ #Get Unique spacetypes.
121
+ bad_evelope_story_ranges = []
122
+ space_types = construction_sets.map { |row| {template: row["template"], building_type: row["building_type"], space_type: row["space_type"]} }.uniq
123
+ space_types.each do |space_type|
124
+ range = Array.new
125
+ instances = construction_sets.select { |row| row['template'] == space_type[:template] && row['building_type'] == space_type[:building_type] && row['space_type'] == space_type[:space_type] }
126
+ instances.each do |instance|
127
+ min_val = instance['min_stories'].to_i
128
+ min_val = 0 if min_val == 1
129
+ max_val = instance['max_stories'].to_i
130
+ range << min_val
131
+ range << max_val
132
+ failed = true
133
+ end
134
+ range.sort!
135
+ incomplete_range = (range.first != 0 or range.last < 999)
136
+ possible_duplicate = (range.uniq.size != range.size)
137
+ if incomplete_range or possible_duplicate
138
+ space_type[:range] = range
139
+ space_type[:error] = {incomplete_range: incomplete_range, possible_duplicate: possible_duplicate}
140
+ bad_records[:min_max_floor_range_errors] << space_type
141
+ end
142
+ end
143
+ if bad_records[:min_max_floor_range_errors].size > 0 or bad_records[:invalid_space_type_names].size > 0
144
+ puts "Errors in ConstructionSets Costing Table."
145
+ puts JSON.pretty_generate(bad_records)
146
+ raise("costing spreadsheet validation failed")
147
+ end
148
+ end
149
+
150
+
151
+ # This method verifies that, for a given row the number of items listed in the 'id_layers' column is the same as the
152
+ # number of quantities listed in the 'Id_layers_quantity_multipliers' column in the 'hvac_vent_ahu' sheet in the
153
+ # costing spreadsheet. If there is a difference in the number of items and number of quantities in a row then that
154
+ # row needs to be investigated and fixed.
155
+ def validate_ahu_items_and_quantities()
156
+ # Find out if there are a different number of items and number oof quantities in any row of the 'hvac_vent_ahu'
157
+ # sheet.
158
+ diff_id_quantities = @db['raw']['hvac_vent_ahu'].select{|data| data['id_layers'].to_s.split(',').size != data['Id_layers_quantity_multipliers'].to_s.split(',').size}
159
+ # If there is a difference (that is the diff_id_quantities has something in it) then raise an error.
160
+ unless diff_id_quantities.empty?
161
+ puts "Errors in the hvac_vent_ahu Costing Table. The number of id_layers does not match the number of"
162
+ puts "Id_layers_quantity_multipliers for the following item(s):"
163
+ puts JSON.pretty_generate(diff_id_quantities)
164
+ raise("costing spreadsheet validation failed")
165
+ end
166
+ end
167
+
168
+ # Overload the element of operator for database accesses
169
+ def [](element)
170
+ @db[element]
171
+ end
172
+
173
+ # Overload the element assignment operator for inputting additional data
174
+ def []=(element, value)
175
+ @db[element] = value
176
+ end
177
+ end
@@ -0,0 +1,353 @@
1
+ class BTAPCosting
2
+
3
+ def cost_audit_daylighting_sensor_control(model:, prototype_creator:)
4
+ @costing_report["lighting"]["daylighting_sensor_control"] = []
5
+ # NOTE: Number of daylighting sensors is based on how many a daylighted space needs sensors as per Mike Lubun's costing spec, rather than daylighting sensor control measure.
6
+ standards_template = model.building.get.standardsTemplate.to_s
7
+ if standards_template.include?('NECB')
8
+ standards_template = standards_template.gsub(/(?<=\p{L})(?=\d)/, ' ') #insert a space between NECB and 2011/2015/2017
9
+ end
10
+
11
+ #-------------------------------------------------------------------------------------------------------------------
12
+ dsc_cost_total = 0.0
13
+ all_tz_primary_sidelighted_quatity = 0.0
14
+ all_tz_skylight_quatity = 0.0
15
+ #-------------------------------------------------------------------------------------------------------------------
16
+ model.getThermalZones.sort.each do |tz|
17
+ if tz.primaryDaylightingControl.is_initialized
18
+ tz_cost_primary_sidelighted = 0.0
19
+ tz_cost_skylight = 0.0
20
+ tz_multiplier = tz.multiplier()
21
+ daylight_spaces = []
22
+ primary_sidelighted_area_hash = {}
23
+ daylighted_area_under_skylights_hash = {}
24
+ primary_sidelighted_area = 0.0
25
+ daylighted_under_skylight_area = 0.0
26
+ tz_area = 0.0
27
+ tz_number_fixtures = 0.0
28
+ tz_primary_sidelighted_ratio_daylight_area = 0.0
29
+ tz_primary_sidelighted_number_fixtures = 0.0
30
+ tz_primary_sidelighted_number_sensors = 0.0
31
+ tz_skylights_ratio_daylight_area = 0.0
32
+ tz_skylights_number_fixtures = 0.0
33
+ tz_skylights_number_sensors = 0.0
34
+ if !tz.primaryDaylightingControl.get.name().empty? && tz.fractionofZoneControlledbyPrimaryDaylightingControl() > 0.00
35
+ tz.spaces().sort.each do |space|
36
+ daylight_spaces << space
37
+ end
38
+ end
39
+
40
+ #-------------------------------------------------------------------------------------------------------------------
41
+ ##### Calculate tz_primary_sidelighted_area AND tz_daylighted_area_under_skylights.
42
+ ##### The above two area values are required for the calculation of tz_primary_sidelighted_number_fixtures AND tz_skylights_number_fixtures
43
+ daylight_spaces.sort.each do |daylight_space|
44
+ # Go to the next space if the current space's space type is undefined.
45
+ next if daylight_space.spaceType.get.name.to_s.downcase.include? "undefined"
46
+
47
+ area_weighted_vt_handle = 0.0
48
+ window_area_sum = 0.0
49
+ skylight_area_weighted_vt_handle = 0.0
50
+ skylight_area_sum = 0.0
51
+
52
+ ##### Find lights_type in each daylight_space
53
+ led_lights = 0
54
+ daylight_space_type = daylight_space.spaceType()
55
+ daylight_space_type.get.lights.sort.each do |inst|
56
+ daylight_space_lights_definition = inst.lightsDefinition
57
+ daylight_space_lights_definition_name = daylight_space_lights_definition.name
58
+ if daylight_space_lights_definition_name.to_s.include?('LED lighting')
59
+ led_lights += 1
60
+ end
61
+ end
62
+ if (led_lights > 0) or (standards_template == 'NECB 2020')
63
+ lights_type = 'LED'
64
+ else
65
+ lights_type = 'CFL'
66
+ end
67
+
68
+ ##### Find height of daylight_space
69
+ max_space_height_m = 0.0
70
+ daylight_space.surfaces.sort.select { |surface| surface.surfaceType == 'Wall' }.each do |wall_surface|
71
+ # Find the vertex with the max z value.
72
+ vertex_with_max_height = wall_surface.vertices.max_by(&:z)
73
+ # Replace max if this surface has something bigger.
74
+ max_space_height_m = vertex_with_max_height.z if vertex_with_max_height.z > max_space_height_m
75
+ end
76
+ max_space_height_ft = (OpenStudio.convert(max_space_height_m, 'm', 'ft').get) #Convert height to ft
77
+
78
+ ##### Find area, floor_surface, and floor_vertices of daylight_space
79
+ floor_surface = nil
80
+ floor_area = 0.0
81
+ floor_vertices = []
82
+ daylight_space.surfaces.sort.each do |surface|
83
+ if surface.surfaceType == 'Floor'
84
+ floor_surface = surface
85
+ floor_area += surface.netArea
86
+ floor_vertices << surface.vertices
87
+ end
88
+ end
89
+
90
+ ##### COSTING-related step: Find fixture type that should be used in the daylight_space based on space_type, template, and lights_type
91
+ search_fixture_type = {
92
+ row_id_1: daylight_space.spaceType.get.standardsSpaceType.to_s, #space_type
93
+ row_id_2: standards_template,
94
+ row_id_3: lights_type
95
+ }
96
+ sheet_name = 'lighting_sets'
97
+ if max_space_height_ft < 7.88
98
+ column_search = 'Fixture_type_less_than_7.88ft_ht'
99
+ elsif max_space_height_ft >= 7.88 && max_space_height_ft < 15.75
100
+ column_search = 'Fixture_type_7.88_to_15.75ft_ht'
101
+ else #i.e. max_space_height_ft >= 15.75ft_ht
102
+ column_search = 'Fixture_type_greater_than_>15.75ft_ht'
103
+ end
104
+ row_search_1 = 'space_type'
105
+ row_search_2 = 'template'
106
+ row_search_3 = 'Type'
107
+ fixture_type = get_fixture_type_id(fixture_info: search_fixture_type, sheet_name: sheet_name, row_name_1: row_search_1, row_name_2: row_search_2, row_name_3: row_search_3, column_search: column_search)
108
+
109
+ ##### COSTING-related step: Find number_fixtures_per_1000_ft2 that should be considered in the daylight_space based on fixture_type
110
+ search_fixtures_per_1000_ft2 = @costing_database['raw']['lighting'].select { |data|
111
+ data['lighting_type_id'].to_f.round(1) == fixture_type.to_f.round(1)
112
+ }.first
113
+ if search_fixtures_per_1000_ft2.nil?
114
+ puts("No data found for #{search_fixtures_per_1000_ft2}!")
115
+ raise
116
+ end
117
+ number_fixtures_per_1000_ft2 = search_fixtures_per_1000_ft2['Fix_1000ft'].to_i
118
+
119
+ ##### COSTING-related step: Calculate number_fixtures_space that should be considered in the daylight_space based on number_fixtures_per_1000_ft2 and area of daylight_space
120
+ floor_area_ft2 = (OpenStudio.convert(floor_area, 'm^2', 'ft^2').get) #convert floor_area to ft2
121
+ number_fixtures_space = (floor_area_ft2 / 1000) * number_fixtures_per_1000_ft2
122
+ number_fixtures_space = number_fixtures_space.ceil
123
+ tz_number_fixtures += number_fixtures_space
124
+
125
+ #-----------------------------------------------------------------------------------------------------------------
126
+ ############################## Calculate 'primary_sidelighted_area' of the thermal zone ##########################
127
+ primary_sidelighted_area, area_weighted_vt_handle, window_area_sum =
128
+ prototype_creator.get_parameters_sidelighting(daylight_space: daylight_space,
129
+ floor_surface: floor_surface,
130
+ floor_vertices: floor_vertices,
131
+ floor_area: floor_area,
132
+ primary_sidelighted_area: primary_sidelighted_area,
133
+ area_weighted_vt_handle: area_weighted_vt_handle,
134
+ window_area_sum: window_area_sum)
135
+
136
+ primary_sidelighted_area_hash[daylight_space.name.to_s] = primary_sidelighted_area
137
+ #-----------------------------------------------------------------------------------------------------------------
138
+ ########################### Calculate 'daylighted_under_skylight_area' of the thermal zone #########################
139
+ ##### Loop through the surfaces of each daylight_space to calculate daylighted_area_under_skylights and skylight_effective_aperture for each daylight_space
140
+ daylighted_under_skylight_area, skylight_area_weighted_vt_handle, skylight_area_sum =
141
+ prototype_creator.get_parameters_skylight(daylight_space: daylight_space,
142
+ skylight_area_weighted_vt_handle: skylight_area_weighted_vt_handle,
143
+ skylight_area_sum: skylight_area_sum,
144
+ daylighted_under_skylight_area: daylighted_under_skylight_area)
145
+
146
+ daylighted_area_under_skylights_hash[daylight_space.name.to_s] = daylighted_under_skylight_area
147
+ #-----------------------------------------------------------------------------------------------------------------
148
+
149
+ tz_area += floor_area
150
+
151
+ end #daylight_spaces.sort.each do |daylight_space|
152
+
153
+ #-------------------------------------------------------------------------------------------------------------------
154
+ # If no fixtures or daylighting is defined then go to the next thermal zone
155
+ next if tz_number_fixtures.to_f == 0.0 || tz_primary_sidelighted_ratio_daylight_area.to_f.nan?
156
+ ##### COSTING-related step: Calculate number of fixtures in thermal zones with window(s)-------------------------------------------------
157
+ tz_primary_sidelighted_ratio_daylight_area = primary_sidelighted_area / tz_area
158
+ tz_primary_sidelighted_number_fixtures = (tz_number_fixtures * tz_primary_sidelighted_ratio_daylight_area).ceil
159
+ tz_primary_sidelighted_number_sensors = (tz_primary_sidelighted_number_fixtures / 4.0).ceil
160
+ all_tz_primary_sidelighted_quatity += tz_primary_sidelighted_number_sensors * tz_multiplier
161
+
162
+ if tz_primary_sidelighted_number_sensors > 0.0
163
+ tags = ['lighting', 'daylighting_sensor_control']
164
+ # cost of daylighting sensor
165
+ quantity_tz_primary_sidelighted_daylighting_sensor = 1.0 * tz_primary_sidelighted_number_sensors * tz_multiplier
166
+ search_tz_primary_sidelighted_daylighting_sensor = {
167
+ row_id_1: 'Ea',
168
+ row_id_2: 407
169
+ }
170
+ sheet_name = 'materials_lighting'
171
+ column_1 = 'unit'
172
+ column_2 = 'lighting_type_id'
173
+ cost_tz_primary_sidelighted_daylighting_sensor = assembly_cost(cost_info:search_tz_primary_sidelighted_daylighting_sensor,
174
+ sheet_name:sheet_name,
175
+ column_1:column_1,
176
+ column_2:column_2,
177
+ quantity:quantity_tz_primary_sidelighted_daylighting_sensor,
178
+ tags: tags)
179
+ # cost of wiring
180
+ quantity_tz_primary_sidelighted_wiring = (30.0 / 100.0) * tz_primary_sidelighted_number_sensors * tz_multiplier
181
+ search_tz_primary_sidelighted_wiring = {
182
+ row_id_1: 'CLF',
183
+ row_id_2: 10
184
+ }
185
+ sheet_name = 'materials_lighting'
186
+ column_1 = 'unit'
187
+ column_2 = 'lighting_type_id'
188
+ cost_tz_primary_sidelighted_wiring = assembly_cost(cost_info:search_tz_primary_sidelighted_wiring,
189
+ sheet_name:sheet_name,
190
+ column_1:column_1,
191
+ column_2:column_2,
192
+ quantity:quantity_tz_primary_sidelighted_wiring,
193
+ tags: tags)
194
+ # cost of pvc conduit
195
+ quantity_tz_primary_sidelighted_pvc_conduit = 30.0 * tz_primary_sidelighted_number_sensors * tz_multiplier
196
+ search_tz_primary_sidelighted_pvc_conduit = {
197
+ row_id_1: 'LF',
198
+ row_id_2: 17
199
+ }
200
+ sheet_name = 'materials_lighting'
201
+ column_1 = 'unit'
202
+ column_2 = 'lighting_type_id'
203
+ cost_tz_primary_sidelighted_pvc_conduit = assembly_cost(cost_info:search_tz_primary_sidelighted_pvc_conduit,
204
+ sheet_name:sheet_name,
205
+ column_1:column_1,
206
+ column_2:column_2,
207
+ quantity:quantity_tz_primary_sidelighted_pvc_conduit,
208
+ tags: tags)
209
+ # cost of box
210
+ quantity_tz_primary_sidelighted_box = 1.0 * tz_primary_sidelighted_number_sensors * tz_multiplier
211
+ search_tz_primary_sidelighted_box = {
212
+ row_id_1: 'Ea',
213
+ row_id_2: 14
214
+ }
215
+ sheet_name = 'materials_lighting'
216
+ column_1 = 'unit'
217
+ column_2 = 'lighting_type_id'
218
+ cost_tz_primary_sidelighted_box = assembly_cost(cost_info:search_tz_primary_sidelighted_box,
219
+ sheet_name:sheet_name,
220
+ column_1:column_1,
221
+ column_2:column_2,
222
+ quantity:quantity_tz_primary_sidelighted_box,
223
+ tags: tags)
224
+ # total cost for this zone
225
+ tz_cost_primary_sidelighted = cost_tz_primary_sidelighted_daylighting_sensor +
226
+ cost_tz_primary_sidelighted_wiring +
227
+ cost_tz_primary_sidelighted_pvc_conduit +
228
+ cost_tz_primary_sidelighted_box
229
+ dsc_cost_total += tz_cost_primary_sidelighted
230
+ end
231
+
232
+ ##### COSTING-related step: Calculate number of fixtures in thermal zones with skylight(s)-------------------------------------------------
233
+ tz_skylights_ratio_daylight_area = daylighted_under_skylight_area / tz_area
234
+ tz_skylights_number_fixtures = (tz_number_fixtures * tz_skylights_ratio_daylight_area).ceil
235
+ tz_skylights_number_sensors = (tz_skylights_number_fixtures / 4.0).ceil
236
+ all_tz_skylight_quatity += tz_skylights_number_sensors * tz_multiplier
237
+
238
+ if tz_skylights_number_sensors > 0.0
239
+ tags = ['lighting', 'daylighting_sensor_control']
240
+ # cost of daylighting sensor
241
+ quantity_tz_skylights_daylighting_sensor = 1.0 * tz_skylights_number_sensors * tz_multiplier
242
+ search_tz_skylights_daylighting_sensor = {
243
+ row_id_1: 'Ea',
244
+ row_id_2: 407
245
+ }
246
+ sheet_name = 'materials_lighting'
247
+ column_1 = 'unit'
248
+ column_2 = 'lighting_type_id'
249
+ cost_tz_skylights_daylighting_sensor = assembly_cost(cost_info:search_tz_skylights_daylighting_sensor,
250
+ sheet_name:sheet_name,
251
+ column_1:column_1,
252
+ column_2:column_2,
253
+ quantity:quantity_tz_skylights_daylighting_sensor,
254
+ tags: tags)
255
+
256
+ # cost of wiring
257
+ quantity_tz_skylights_wiring = (30.0 / 100.0) * tz_skylights_number_sensors * tz_multiplier
258
+ search_tz_skylights_wiring = {
259
+ row_id_1: 'CLF',
260
+ row_id_2: 10
261
+ }
262
+ sheet_name = 'materials_lighting'
263
+ column_1 = 'unit'
264
+ column_2 = 'lighting_type_id'
265
+ cost_tz_skylights_wiring = assembly_cost(cost_info:search_tz_skylights_wiring,
266
+ sheet_name:sheet_name,
267
+ column_1:column_1,
268
+ column_2:column_2,
269
+ quantity:quantity_tz_skylights_wiring,
270
+ tags: tags)
271
+
272
+ # cost of pvc conduit
273
+ quantity_tz_skylights_pvc_conduit = 30.0 * tz_skylights_number_sensors * tz_multiplier
274
+ search_tz_skylights_pvc_conduit = {
275
+ row_id_1: 'LF',
276
+ row_id_2: 17
277
+ }
278
+ sheet_name = 'materials_lighting'
279
+ column_1 = 'unit'
280
+ column_2 = 'lighting_type_id'
281
+ cost_tz_skylights_pvc_conduit = assembly_cost(cost_info:search_tz_skylights_pvc_conduit,
282
+ sheet_name:sheet_name,
283
+ column_1:column_1,
284
+ column_2:column_2,
285
+ quantity:quantity_tz_skylights_pvc_conduit,
286
+ tags: tags)
287
+
288
+ # cost of box
289
+ quantity_tz_skylights_box = 1.0 * tz_skylights_number_sensors * tz_multiplier
290
+ search_tz_skylights_box = {
291
+ row_id_1: 'Ea',
292
+ row_id_2: 14
293
+ }
294
+ sheet_name = 'materials_lighting'
295
+ column_1 = 'unit'
296
+ column_2 = 'lighting_type_id'
297
+ cost_tz_skylights_box = assembly_cost(cost_info:search_tz_skylights_box,
298
+ sheet_name:sheet_name,
299
+ column_1:column_1,
300
+ column_2:column_2,
301
+ quantity:quantity_tz_skylights_box,
302
+ tags: tags)
303
+
304
+ # total cost for this zone
305
+ tz_cost_skylight = cost_tz_skylights_daylighting_sensor +
306
+ cost_tz_skylights_wiring +
307
+ cost_tz_skylights_pvc_conduit +
308
+ cost_tz_skylights_box
309
+
310
+ dsc_cost_total += tz_cost_skylight
311
+ end
312
+
313
+ ##### Gather information for reporting
314
+ @costing_report["lighting"]["daylighting_sensor_control"] << {
315
+ 'zone' => tz.name.to_s,
316
+ 'zone_area' => tz_area,
317
+ 'zone_multiplier' => tz_multiplier,
318
+ 'number_of_fixtures_required_without_considering_daylighted_area_under_sidelighting_and_skylights' => tz_number_fixtures,
319
+ 'primary_sidelighted_area' => primary_sidelighted_area,
320
+ 'primary_sidelighted_number_fixtures' => tz_primary_sidelighted_number_fixtures,
321
+ 'primary_sidelighted_number_sensors' => tz_primary_sidelighted_number_sensors,
322
+ 'skylights_daylighted_area' => daylighted_under_skylight_area,
323
+ 'skylights_number_fixtures' => tz_skylights_number_fixtures,
324
+ 'skylights_number_sensors' => tz_skylights_number_sensors,
325
+ 'daylighting_sensor_control_cost_for_this_zone' => tz_cost_primary_sidelighted + tz_cost_skylight
326
+ }
327
+
328
+ end #tz.primaryDaylightingControl.is_initialized
329
+ end #model.getThermalZones.sort.each do |tz|
330
+ #-------------------------------------------------------------------------------------------------------------------
331
+
332
+ puts "\nDaylighting sensor controls costing data successfully generated. Total DSC costs: $#{dsc_cost_total.round(2)}"
333
+
334
+ return dsc_cost_total
335
+
336
+ end #cost_audit_daylighting_sensor_control(model, prototype_creator)
337
+
338
+
339
+ def get_fixture_type_id(fixture_info:, sheet_name:, row_name_1:, row_name_2:, row_name_3:, column_search:)
340
+ fixture_type = nil
341
+ fixture_type = @costing_database['raw'][sheet_name].select { |data|
342
+ data[row_name_1].to_s.upcase == fixture_info[:row_id_1].to_s.upcase and
343
+ data[row_name_2].to_s.upcase == fixture_info[:row_id_2].to_s.upcase and
344
+ data[row_name_3].to_s.upcase == fixture_info[:row_id_3].to_s.upcase
345
+ }.first
346
+ if fixture_type.nil?
347
+ puts("No data found for #{fixture_type}!")
348
+ raise
349
+ end
350
+ return fixture_type[column_search]
351
+ end
352
+
353
+ end