openstudio-standards 0.8.3 → 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.
- checksums.yaml +4 -4
- data/lib/openstudio-standards/btap/costing/README.md +502 -0
- data/lib/openstudio-standards/btap/costing/btap_costing.rb +473 -0
- data/lib/openstudio-standards/btap/costing/btap_measure_helper.rb +359 -0
- data/lib/openstudio-standards/btap/costing/btap_workflow.rb +117 -0
- data/lib/openstudio-standards/btap/costing/common_paths.rb +78 -0
- data/lib/openstudio-standards/btap/costing/common_resources/ConstructionProperties.csv +52 -0
- data/lib/openstudio-standards/btap/costing/common_resources/Constructions.csv +37 -0
- data/lib/openstudio-standards/btap/costing/common_resources/construction_sets.csv +1270 -0
- data/lib/openstudio-standards/btap/costing/common_resources/constructions_glazing.csv +61 -0
- data/lib/openstudio-standards/btap/costing/common_resources/constructions_opaque.csv +2256 -0
- data/lib/openstudio-standards/btap/costing/common_resources/costs.csv +1904 -0
- data/lib/openstudio-standards/btap/costing/common_resources/costs_local_factors.csv +2315 -0
- data/lib/openstudio-standards/btap/costing/common_resources/hvac_vent_ahu.csv +925 -0
- data/lib/openstudio-standards/btap/costing/common_resources/lighting.csv +364 -0
- data/lib/openstudio-standards/btap/costing/common_resources/lighting_sets.csv +2667 -0
- data/lib/openstudio-standards/btap/costing/common_resources/locations.csv +75 -0
- data/lib/openstudio-standards/btap/costing/common_resources/materials_glazing.csv +35 -0
- data/lib/openstudio-standards/btap/costing/common_resources/materials_hvac.csv +1699 -0
- data/lib/openstudio-standards/btap/costing/common_resources/materials_lighting.csv +267 -0
- data/lib/openstudio-standards/btap/costing/common_resources/materials_opaque.csv +164 -0
- data/lib/openstudio-standards/btap/costing/copy_test_results_files_to_expected_results.rb +11 -0
- data/lib/openstudio-standards/btap/costing/cost_building_from_file.rb +136 -0
- data/lib/openstudio-standards/btap/costing/costing_database_wrapper.rb +177 -0
- data/lib/openstudio-standards/btap/costing/daylighting_sensor_control_costing.rb +353 -0
- data/lib/openstudio-standards/btap/costing/dcv_costing.rb +314 -0
- data/lib/openstudio-standards/btap/costing/dummy.epw +8768 -0
- data/lib/openstudio-standards/btap/costing/dummy.osm +5320 -0
- data/lib/openstudio-standards/btap/costing/envelope_costing.rb +284 -0
- data/lib/openstudio-standards/btap/costing/heating_cooling_costing.rb +2584 -0
- data/lib/openstudio-standards/btap/costing/led_lighting_costing.rb +155 -0
- data/lib/openstudio-standards/btap/costing/lighting_costing.rb +209 -0
- data/lib/openstudio-standards/btap/costing/mech_sizing.json +502 -0
- data/lib/openstudio-standards/btap/costing/neb_end_use_prices.csv +42 -0
- data/lib/openstudio-standards/btap/costing/necb_2011_spacetype_info.csv +225 -0
- data/lib/openstudio-standards/btap/costing/necb_reference_runs.csv +28705 -0
- data/lib/openstudio-standards/btap/costing/nv_costing.rb +547 -0
- data/lib/openstudio-standards/btap/costing/parallel_tests.rb +92 -0
- data/lib/openstudio-standards/btap/costing/pv_ground_costing.rb +687 -0
- data/lib/openstudio-standards/btap/costing/shw_costing.rb +705 -0
- data/lib/openstudio-standards/btap/costing/test_list.txt +17 -0
- data/lib/openstudio-standards/btap/costing/test_run_all_test_locally.rb +26 -0
- data/lib/openstudio-standards/btap/costing/test_run_costing_tests.rb +80 -0
- data/lib/openstudio-standards/btap/costing/ventilation_costing.rb +2616 -0
- data/lib/openstudio-standards/constructions/modify.rb +2 -1
- data/lib/openstudio-standards/standards/Standards.Model.rb +39 -9
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.Model.rb +2 -2
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/userdata_csv/ashrae_90_1_prm.UserData.rb +6 -1
- data/lib/openstudio-standards/standards/necb/BTAPPRE1980/btap_pre1980.rb +2 -27
- data/lib/openstudio-standards/standards/necb/BTAPPRE1980/hvac_system_3_and_8_single_speed.rb +68 -27
- data/lib/openstudio-standards/standards/necb/BTAPPRE1980/hvac_system_4.rb +64 -25
- data/lib/openstudio-standards/standards/necb/BTAPPRE1980/hvac_system_6.rb +9 -14
- data/lib/openstudio-standards/standards/necb/ECMS/hvac_systems.rb +46 -20
- data/lib/openstudio-standards/standards/necb/NECB2011/autozone.rb +635 -248
- data/lib/openstudio-standards/standards/necb/NECB2011/data/constants.json +43 -7
- data/lib/openstudio-standards/standards/necb/NECB2011/data/fuel_type_sets.json +7 -1
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/HighriseApartmentMult.osm +14272 -0
- data/lib/openstudio-standards/standards/necb/NECB2011/data/necb_2015_table_c1.json +1 -1
- data/lib/openstudio-standards/standards/necb/NECB2011/data/space_types.json +437 -437
- data/lib/openstudio-standards/standards/necb/NECB2011/data/systems.json +516 -0
- data/lib/openstudio-standards/standards/necb/NECB2011/data/systems_including_sys5.json +588 -0
- data/lib/openstudio-standards/standards/necb/NECB2011/hvac_namer.rb +489 -0
- data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_1_single_speed.rb +16 -6
- data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_2_and_5.rb +48 -5
- data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_3_and_8_multi_speed.rb +2 -2
- data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_3_and_8_single_speed.rb +35 -27
- data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_4.rb +34 -23
- data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_6.rb +8 -6
- data/lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb +42 -13
- data/lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb +214 -25
- data/lib/openstudio-standards/standards/necb/NECB2011/system_fuels.rb +61 -1
- data/lib/openstudio-standards/standards/necb/NECB2015/data/space_types.json +636 -636
- data/lib/openstudio-standards/standards/necb/NECB2015/data/unitary_acs.json +38 -38
- data/lib/openstudio-standards/standards/necb/NECB2015/hvac_systems.rb +15 -6
- data/lib/openstudio-standards/standards/necb/NECB2017/data/space_types.json +636 -636
- data/lib/openstudio-standards/standards/necb/NECB2020/data/chillers.json +71 -71
- data/lib/openstudio-standards/standards/necb/README.md +343 -0
- data/lib/openstudio-standards/standards/necb/common/btap_data.rb +190 -28
- data/lib/openstudio-standards/standards/necb/common/btap_datapoint.rb +14 -5
- data/lib/openstudio-standards/standards/necb/common/eccc_electric_grid_intensity_20250311.csv +14 -0
- data/lib/openstudio-standards/standards/necb/common/nir_gas_grid_intensity_20250311.csv +14 -0
- data/lib/openstudio-standards/standards/necb/common/system_types.yaml +0 -0
- data/lib/openstudio-standards/utilities/logging.rb +18 -14
- data/lib/openstudio-standards/version.rb +1 -1
- data/lib/openstudio-standards/weather/modify.rb +2 -2
- data/lib/openstudio-standards.rb +12 -0
- metadata +53 -2
@@ -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
|