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,284 @@
|
|
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
|
+
# These are the only envelope costing items we are considering for envelopes..
|
14
|
+
costed_surfaces = [
|
15
|
+
"ExteriorWall",
|
16
|
+
"ExteriorRoof",
|
17
|
+
"ExteriorFloor",
|
18
|
+
"ExteriorFixedWindow",
|
19
|
+
"ExteriorOperableWindow",
|
20
|
+
"ExteriorSkylight",
|
21
|
+
"ExteriorTubularDaylightDiffuser",
|
22
|
+
"ExteriorTubularDaylightDome",
|
23
|
+
"ExteriorDoor",
|
24
|
+
"ExteriorGlassDoor",
|
25
|
+
"ExteriorOverheadDoor",
|
26
|
+
"GroundContactWall",
|
27
|
+
"GroundContactRoof",
|
28
|
+
"GroundContactFloor"
|
29
|
+
]
|
30
|
+
costed_surfaces.each do |surface_type|
|
31
|
+
@costing_report["envelope"]["#{surface_type.underscore}_cost"] = 0.00
|
32
|
+
@costing_report["envelope"]["#{surface_type.underscore}_area_m2"] = 0.0
|
33
|
+
@costing_report["envelope"]["#{surface_type.underscore}_cost_per_m2"] = 0.00
|
34
|
+
end
|
35
|
+
|
36
|
+
@costing_report["envelope"]["construction_costs"] = []
|
37
|
+
|
38
|
+
# Store number of stories. Required for envelope costing logic.
|
39
|
+
num_of_above_ground_stories = model.getBuilding.standardsNumberOfAboveGroundStories.to_i
|
40
|
+
|
41
|
+
template_type = prototype_creator.template
|
42
|
+
|
43
|
+
closest_loc = get_closest_cost_location(model.getWeatherFile.latitude, model.getWeatherFile.longitude)
|
44
|
+
generate_construction_cost_database_for_city(@costing_report["city"], @costing_report["province_state"])
|
45
|
+
|
46
|
+
totEnvCost = 0
|
47
|
+
|
48
|
+
# Iterate through the thermal zones.
|
49
|
+
model.getThermalZones.sort.each do |zone|
|
50
|
+
# Iterate through spaces.
|
51
|
+
zone.spaces.sort.each do |space|
|
52
|
+
# Get SpaceType defined for space.. if not defined it will skip the spacetype. May have to deal with Attic spaces.
|
53
|
+
if space.spaceType.empty? or space.spaceType.get.standardsSpaceType.empty? or space.spaceType.get.standardsBuildingType.empty?
|
54
|
+
raise ("standards Space type and building type is not defined for space:#{space.name.get}. Skipping this space for costing.")
|
55
|
+
end
|
56
|
+
|
57
|
+
# Get space type standard names.
|
58
|
+
space_type = space.spaceType.get.standardsSpaceType
|
59
|
+
building_type = space.spaceType.get.standardsBuildingType
|
60
|
+
|
61
|
+
# Get standard constructions based on collected information (spacetype, no of stories, etc..)
|
62
|
+
# This is a standard way to search a hash.
|
63
|
+
construction_set = @costing_database['raw']['construction_sets'].select { |data|
|
64
|
+
data['template'].to_s.gsub(/\s*/, '') == template_type and
|
65
|
+
data['building_type'].to_s.downcase == building_type.to_s.downcase and
|
66
|
+
data['space_type'].to_s.downcase == space_type.to_s.downcase and
|
67
|
+
data['min_stories'].to_i <= num_of_above_ground_stories and
|
68
|
+
data['max_stories'].to_i >= num_of_above_ground_stories
|
69
|
+
}.first
|
70
|
+
|
71
|
+
|
72
|
+
# Create Hash to store surfaces for this space by surface type
|
73
|
+
surfaces = {}
|
74
|
+
#Exterior
|
75
|
+
exterior_surfaces = BTAP::Geometry::Surfaces::filter_by_boundary_condition(space.surfaces, "Outdoors")
|
76
|
+
surfaces["ExteriorWall"] = BTAP::Geometry::Surfaces::filter_by_surface_types(exterior_surfaces, "Wall")
|
77
|
+
surfaces["ExteriorRoof"] = BTAP::Geometry::Surfaces::filter_by_surface_types(exterior_surfaces, "RoofCeiling")
|
78
|
+
surfaces["ExteriorFloor"] = BTAP::Geometry::Surfaces::filter_by_surface_types(exterior_surfaces, "Floor")
|
79
|
+
# Exterior Subsurface
|
80
|
+
exterior_subsurfaces = exterior_surfaces.flat_map(&:subSurfaces)
|
81
|
+
surfaces["ExteriorFixedWindow"] = BTAP::Geometry::Surfaces::filter_subsurfaces_by_types(exterior_subsurfaces, ["FixedWindow"])
|
82
|
+
surfaces["ExteriorOperableWindow"] = BTAP::Geometry::Surfaces::filter_subsurfaces_by_types(exterior_subsurfaces, ["OperableWindow"])
|
83
|
+
surfaces["ExteriorSkylight"] = BTAP::Geometry::Surfaces::filter_subsurfaces_by_types(exterior_subsurfaces, ["Skylight"])
|
84
|
+
surfaces["ExteriorTubularDaylightDiffuser"] = BTAP::Geometry::Surfaces::filter_subsurfaces_by_types(exterior_subsurfaces, ["TubularDaylightDiffuser"])
|
85
|
+
surfaces["ExteriorTubularDaylightDome"] = BTAP::Geometry::Surfaces::filter_subsurfaces_by_types(exterior_subsurfaces, ["TubularDaylightDome"])
|
86
|
+
surfaces["ExteriorDoor"] = BTAP::Geometry::Surfaces::filter_subsurfaces_by_types(exterior_subsurfaces, ["Door"])
|
87
|
+
surfaces["ExteriorGlassDoor"] = BTAP::Geometry::Surfaces::filter_subsurfaces_by_types(exterior_subsurfaces, ["GlassDoor"])
|
88
|
+
surfaces["ExteriorOverheadDoor"] = BTAP::Geometry::Surfaces::filter_subsurfaces_by_types(exterior_subsurfaces, ["OverheadDoor"])
|
89
|
+
|
90
|
+
# Ground Surfaces
|
91
|
+
ground_surfaces = BTAP::Geometry::Surfaces::filter_by_boundary_condition(space.surfaces, "Ground")
|
92
|
+
ground_surfaces += BTAP::Geometry::Surfaces::filter_by_boundary_condition(space.surfaces, "Foundation")
|
93
|
+
surfaces["GroundContactWall"] = BTAP::Geometry::Surfaces::filter_by_surface_types(ground_surfaces, "Wall")
|
94
|
+
surfaces["GroundContactRoof"] = BTAP::Geometry::Surfaces::filter_by_surface_types(ground_surfaces, "RoofCeiling")
|
95
|
+
surfaces["GroundContactFloor"] = BTAP::Geometry::Surfaces::filter_by_surface_types(ground_surfaces, "Floor")
|
96
|
+
|
97
|
+
|
98
|
+
# Iterate through
|
99
|
+
costed_surfaces.each do |surface_type|
|
100
|
+
# Get Costs for this construction type. This will get the cost for the particular construction type
|
101
|
+
# for all rsi levels for this location. This has been collected by the API costs data. Note that a space_type
|
102
|
+
# of "- undefined -" will create a nil construction_set!
|
103
|
+
|
104
|
+
|
105
|
+
if construction_set.nil?
|
106
|
+
cost_range_hash = {}
|
107
|
+
else
|
108
|
+
cost_range_hash = @costing_database['constructions_costs'].select { |construction|
|
109
|
+
construction['construction_type_name'] == construction_set[surface_type] &&
|
110
|
+
construction['province_state'] == @costing_report["province_state"] &&
|
111
|
+
construction['city'] == @costing_report["city"]
|
112
|
+
}
|
113
|
+
end
|
114
|
+
|
115
|
+
# We don't need all the information, just the rsi and cost. However, for windows rsi = 1/u_w_per_m2_k
|
116
|
+
surfaceIsGlazing = (surface_type == 'ExteriorFixedWindow' || surface_type == 'ExteriorOperableWindow' ||
|
117
|
+
surface_type == 'ExteriorSkylight' || surface_type == 'ExteriorTubularDaylightDiffuser' ||
|
118
|
+
surface_type == 'ExteriorTubularDaylightDome' || surface_type == 'ExteriorGlassDoor')
|
119
|
+
if surfaceIsGlazing
|
120
|
+
cost_range_array = cost_range_hash.map { |cost|
|
121
|
+
[
|
122
|
+
(1.0 / cost['u_w_per_m2_k'].to_f),
|
123
|
+
cost['total_cost_with_op']
|
124
|
+
]
|
125
|
+
}
|
126
|
+
else
|
127
|
+
cost_range_array = cost_range_hash.map { |cost|
|
128
|
+
[
|
129
|
+
cost['rsi_k_m2_per_w'],
|
130
|
+
cost['total_cost_with_op']
|
131
|
+
]
|
132
|
+
}
|
133
|
+
end
|
134
|
+
# Sorted based on rsi.
|
135
|
+
cost_range_array.sort! { |a, b| a[0] <=> b[0] }
|
136
|
+
|
137
|
+
# Iterate through actual surfaces in the model of surface_type.
|
138
|
+
numSurfType = 0
|
139
|
+
surfaces[surface_type].sort.each do |surface|
|
140
|
+
numSurfType = numSurfType + 1
|
141
|
+
|
142
|
+
# Get RSI of existing model surface (actually returns rsi for glazings too!).
|
143
|
+
# Make an array of constructions to use with surfaces_get_conductance method which replaces the get_rsi
|
144
|
+
# method
|
145
|
+
rsi = 1 / (OpenstudioStandards::Constructions.construction_get_conductance(OpenStudio::Model::getConstructionByName(surface.model, surface.construction.get.name.to_s).get))
|
146
|
+
|
147
|
+
|
148
|
+
#Check to see if it is in range
|
149
|
+
|
150
|
+
|
151
|
+
# Use the cost_range_array to interpolate the estimated cost for the given rsi.
|
152
|
+
# Note that window costs in the API data use U-value, which was converted to rsi for cost_range_array above
|
153
|
+
exterpolate_percentage_range = 30.0
|
154
|
+
cost = interpolate(x_y_array: cost_range_array, x2: rsi, exterpolate_percentage_range: exterpolate_percentage_range)
|
155
|
+
|
156
|
+
|
157
|
+
# If the cost is nil, that means the rsi is out of range. Flag in the report.
|
158
|
+
if cost.nil?
|
159
|
+
if !cost_range_array.empty?
|
160
|
+
notes = "Warning! RSI out of the range (#{'%.2f' % rsi}) or cost is 0!. Range for #{construction_set[surface_type]} is #{'%.2f' % cost_range_array.first[0]}-#{'%.2f' % cost_range_array.last[0]}."
|
161
|
+
cost = 0.0
|
162
|
+
else
|
163
|
+
notes = "No cost found for this! So Cost is set to 0.0!"
|
164
|
+
cost = 0.0
|
165
|
+
end
|
166
|
+
elsif cost.nan?
|
167
|
+
raise("the values for cost and conductance for #{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. ")
|
168
|
+
else
|
169
|
+
#Tell user if we are extrapolating outside of library.
|
170
|
+
array = cost_range_array.sort { |a, b| a[0] <=> b[0] }
|
171
|
+
if rsi < (array.first[0].to_f) || rsi > (array.last[0].to_f)
|
172
|
+
notes = "RSI out of the range (#{'%.2f' % rsi}). Range for #{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. "
|
173
|
+
else
|
174
|
+
notes = "OK"
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# Calculate SHGC/film cost
|
179
|
+
film_cost = 0.0
|
180
|
+
if surfaceIsGlazing
|
181
|
+
#Get SHGC from surface.
|
182
|
+
shgc = OpenstudioStandards::Constructions.construction_get_solar_transmittance(surface.construction.get.to_Construction.get)
|
183
|
+
# Get the closest value in materials_glazing sheet of SolarFilms.
|
184
|
+
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}
|
185
|
+
standard_film_cost = getCost(material_row['description'], material_row, 1.0)
|
186
|
+
regional_factors = get_regional_cost_factors(@costing_report['province_state'], @costing_report['city'], material_row)
|
187
|
+
# 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.
|
188
|
+
film_cost = standard_film_cost.zip(regional_factors).map{|cost,region_factor| cost * region_factor / 100.0}.inject(0, :+)
|
189
|
+
end
|
190
|
+
|
191
|
+
|
192
|
+
testSurfName = surface.name.to_s
|
193
|
+
testSpaceName = space.name.to_s
|
194
|
+
surfArea = surface.netArea * zone.multiplier
|
195
|
+
surfAreaft = (OpenStudio.convert(surfArea, "m^2", "ft^2").get).to_f
|
196
|
+
surfCost = (cost + film_cost) * surfAreaft
|
197
|
+
totEnvCost = totEnvCost + surfCost
|
198
|
+
name = ""
|
199
|
+
|
200
|
+
# Bin the costing by construction standard type and rsi
|
201
|
+
if construction_set.nil?
|
202
|
+
name = "undefined space type_#{(1.0 / rsi).round(3)}"
|
203
|
+
else
|
204
|
+
name = "#{construction_set[surface_type]}"
|
205
|
+
end
|
206
|
+
row = @costing_report["envelope"]["construction_costs"].detect { |row| (row['name'] == name) && (row['conductance'].round(3) == ((1.0 / rsi).round(3))) }
|
207
|
+
if row.nil?
|
208
|
+
@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}"}
|
209
|
+
else
|
210
|
+
# Not using += for @costing_report additions so that output can be properly rounded
|
211
|
+
row['area'] = (row['area'] + surfArea).round(2)
|
212
|
+
row['cost'] = (row['cost'] + surfCost).round(2)
|
213
|
+
row['cost_per_area'] = ((row['cost'] / row['area']).to_f.round(2))
|
214
|
+
row['note'] += " / #{numSurfType}: #{notes}"
|
215
|
+
end
|
216
|
+
# Not using += for @costing_report additions so that output can be properly rounded
|
217
|
+
@costing_report["envelope"]["#{surface_type.underscore}_cost"] = (@costing_report["envelope"]["#{surface_type.underscore}_cost"] + surfCost).round(2)
|
218
|
+
@costing_report["envelope"]["#{surface_type.underscore}_area_m2"] = (@costing_report["envelope"]["#{surface_type.underscore}_area_m2"] + surfArea).round(2)
|
219
|
+
@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)
|
220
|
+
end # surfaces of surface type
|
221
|
+
end # surface_type
|
222
|
+
end # spaces
|
223
|
+
end # thermalzone
|
224
|
+
|
225
|
+
@costing_report["envelope"]['total_envelope_cost'] = totEnvCost.to_f.round(2)
|
226
|
+
puts "\nEnvelope costing data successfully generated. Total envelope cost is $#{totEnvCost.to_f.round(2)}"
|
227
|
+
|
228
|
+
return totEnvCost
|
229
|
+
end
|
230
|
+
|
231
|
+
def cost_construction(construction, location, type = 'opaque')
|
232
|
+
|
233
|
+
material_layers = "material_#{type}_id_layers"
|
234
|
+
material_id = "materials_#{type}_id"
|
235
|
+
materials_database = @costing_database["raw"]["materials_#{type}"]
|
236
|
+
|
237
|
+
total_with_op = 0.0
|
238
|
+
material_cost_pairs = []
|
239
|
+
construction[material_layers].split(',').reject { |c| c.empty? }.each do |material_index|
|
240
|
+
material = materials_database.find { |data| data[material_id].to_s == material_index.to_s }
|
241
|
+
if material.nil?
|
242
|
+
puts "material error..could not find material #{material_index} in #{materials_database}"
|
243
|
+
raise()
|
244
|
+
else
|
245
|
+
costing_data = @costing_database['costs'].detect { |data| data['id'].to_s.upcase == material['id'].to_s.upcase }
|
246
|
+
if costing_data.nil?
|
247
|
+
puts "This material id #{material['id']} was not found in the costing database. Skipping. This construction will be inaccurate. "
|
248
|
+
raise()
|
249
|
+
else
|
250
|
+
regional_material, regional_installation = get_regional_cost_factors(location['province_state'], location['city'], material)
|
251
|
+
|
252
|
+
# Get cost information from lookup.
|
253
|
+
# Note that "glazing" types don't have a 'quantity' hash entry!
|
254
|
+
# Don't need "and" below but using in-case this hash field is added in the future.
|
255
|
+
if type == 'glazing' and material['quantity'].to_f == 0.0
|
256
|
+
material['quantity'] = '1.0'
|
257
|
+
end
|
258
|
+
material_cost = costing_data['baseCosts']['materialOpCost'].to_f * material['material_mult'].to_f
|
259
|
+
labour_cost = costing_data['baseCosts']['laborOpCost'].to_f * material['labour_mult'].to_f
|
260
|
+
equipment_cost = costing_data['baseCosts']['equipmentOpCost'].to_f
|
261
|
+
layer_cost = (((material_cost * regional_material / 100.0) + (labour_cost * regional_installation / 100.0) + equipment_cost) * material['quantity'].to_f).round(2)
|
262
|
+
material_cost_pairs << {material_id.to_s => material_index,
|
263
|
+
'cost' => layer_cost}
|
264
|
+
total_with_op += layer_cost
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
new_construction = {
|
269
|
+
'province_state' => location['province_state'],
|
270
|
+
'city' => location['city'],
|
271
|
+
"construction_type_name" => construction["construction_type_name"],
|
272
|
+
'description' => construction["description"],
|
273
|
+
'intended_surface_type' => construction["intended_surface_type"],
|
274
|
+
'standards_construction_type' => construction["standards_construction_type"],
|
275
|
+
'rsi_k_m2_per_w' => construction['rsi_k_m2_per_w'].to_f,
|
276
|
+
'zone' => construction['climate_zone'],
|
277
|
+
'fenestration_type' => construction['fenestration_type'],
|
278
|
+
'u_w_per_m2_k' => construction['u_w_per_m2_k'],
|
279
|
+
'materials' => material_cost_pairs,
|
280
|
+
'total_cost_with_op' => total_with_op}
|
281
|
+
|
282
|
+
@costing_database['constructions_costs'] << new_construction
|
283
|
+
end
|
284
|
+
end
|