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.
- checksums.yaml +4 -4
- data/data/standards/OpenStudio_Standards-ashrae_90_1-ALL-comstock(space_types).xlsx +0 -0
- data/data/standards/openstudio_standards_duplicates_log.csv +7962 -0
- 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/refrigeration/create_case.rb +58 -21
- data/lib/openstudio-standards/refrigeration/create_typical_refrigeration.rb +4 -2
- data/lib/openstudio-standards/refrigeration/create_walkin.rb +57 -22
- data/lib/openstudio-standards/refrigeration/data/refrigerated_cases.csv +31 -31
- data/lib/openstudio-standards/refrigeration/data/refrigerated_walkins.csv +76 -76
- data/lib/openstudio-standards/service_water_heating/create_typical.rb +10 -10
- data/lib/openstudio-standards/service_water_heating/create_water_heater.rb +10 -0
- data/lib/openstudio-standards/service_water_heating/create_water_heating_loop.rb +16 -3
- data/lib/openstudio-standards/service_water_heating/data/convert_data.rb +9 -9
- data/lib/openstudio-standards/service_water_heating/data/typical_water_use_equipment.csv +49 -49
- data/lib/openstudio-standards/service_water_heating/data/typical_water_use_equipment.json +159 -159
- data/lib/openstudio-standards/standards/Standards.CoilCoolingDXMultiSpeed.rb +7 -18
- data/lib/openstudio-standards/standards/Standards.CoilCoolingDXSingleSpeed.rb +10 -20
- data/lib/openstudio-standards/standards/Standards.CoilCoolingDXTwoSpeed.rb +6 -15
- data/lib/openstudio-standards/standards/Standards.CoilCoolingWaterToAirHeatPumpEquationFit.rb +5 -6
- data/lib/openstudio-standards/standards/Standards.CoilDX.rb +93 -43
- data/lib/openstudio-standards/standards/Standards.CoilHeatingDXMultiSpeed.rb +5 -5
- data/lib/openstudio-standards/standards/Standards.CoilHeatingDXSingleSpeed.rb +135 -37
- data/lib/openstudio-standards/standards/Standards.CoilHeatingWaterToAirHeatPumpEquationFit.rb +2 -2
- data/lib/openstudio-standards/standards/Standards.Model.rb +48 -13
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.computer_room_acs.json +302 -140
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.heat_pumps.json +648 -326
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.heat_pumps_heating.json +371 -90
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.water_heaters.json +66 -22
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.computer_room_acs.json +302 -140
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.heat_pumps.json +1012 -296
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.heat_pumps_heating.json +443 -79
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.water_heaters.json +66 -22
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.computer_room_acs.json +302 -140
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.heat_pumps.json +672 -306
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.heat_pumps_heating.json +354 -74
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.water_heaters.json +72 -24
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.computer_room_acs.json +302 -140
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.energy_recovery.json +8 -8
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.heat_pumps.json +930 -604
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.heat_pumps_heating.json +415 -111
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.water_heaters.json +72 -24
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.computer_room_acs.json +602 -140
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.heat_pumps.json +1005 -333
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.heat_pumps_heating.json +642 -88
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.water_heaters.json +78 -26
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.computer_room_acs.json +722 -140
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.heat_pumps.json +1741 -426
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.heat_pumps_heating.json +1108 -111
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.water_heaters.json +186 -62
- data/lib/openstudio-standards/standards/ashrae_90_1/data/ashrae_90_1.schedules.json +62 -232
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoilCoolingDXSingleSpeed.rb +2 -3
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoilCoolingDXTwoSpeed.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoilDX.rb +7 -18
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoilHeatingDXSingleSpeed.rb +9 -7
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoilHeatingGas.rb +1 -1
- 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/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.heat_pumps.json +154 -69
- 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
- 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
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/userdata_csv/ashrae_90_1_prm.UserData.rb +6 -1
- data/lib/openstudio-standards/standards/deer/data/deer.schedules.json +62 -232
- data/lib/openstudio-standards/standards/necb/BTAPPRE1980/btap_pre1980.rb +2 -27
- data/lib/openstudio-standards/standards/necb/BTAPPRE1980/data/heat_pumps.json +16 -0
- data/lib/openstudio-standards/standards/necb/BTAPPRE1980/data/heat_pumps_heating.json +6 -0
- 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/heat_pumps.json +16 -0
- data/lib/openstudio-standards/standards/necb/NECB2011/data/heat_pumps_heating.json +6 -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 +43 -14
- 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/heat_pumps.json +16 -0
- data/lib/openstudio-standards/standards/necb/NECB2015/data/heat_pumps_heating.json +8 -0
- 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/NECB2020/data/heat_pumps.json +20 -0
- data/lib/openstudio-standards/standards/necb/NECB2020/data/heat_pumps_heating.json +10 -0
- 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/utilities/simulation.rb +3 -2
- 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 +56 -3
@@ -0,0 +1,473 @@
|
|
1
|
+
require 'json'
|
2
|
+
require_relative 'costing_database_wrapper.rb'
|
3
|
+
require_relative 'common_paths.rb'
|
4
|
+
|
5
|
+
class SimpleLinearRegression
|
6
|
+
#https://gist.github.com/rweald/3516193#file-full-slr-class-snippet-rb
|
7
|
+
def initialize(xs, ys)
|
8
|
+
@xs, @ys = xs, ys
|
9
|
+
if @xs.length != @ys.length
|
10
|
+
raise "Unbalanced data. xs need to be same length as ys"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def y_intercept
|
15
|
+
return mean(@ys) - (slope * mean(@xs))
|
16
|
+
end
|
17
|
+
|
18
|
+
def slope
|
19
|
+
x_mean = mean(@xs)
|
20
|
+
y_mean = mean(@ys)
|
21
|
+
|
22
|
+
numerator = (0...@xs.length).reduce(0) do |sum, i|
|
23
|
+
sum + ((@xs[i] - x_mean) * (@ys[i] - y_mean))
|
24
|
+
end
|
25
|
+
|
26
|
+
denominator = @xs.reduce(0) do |sum, x|
|
27
|
+
sum + ((x - x_mean) ** 2)
|
28
|
+
end
|
29
|
+
|
30
|
+
return (numerator / denominator)
|
31
|
+
end
|
32
|
+
|
33
|
+
def mean(values)
|
34
|
+
total = values.reduce(0) { |sum, x| x + sum }
|
35
|
+
return Float(total) / Float(values.length)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
class BTAPCosting
|
41
|
+
# May be initialized with custom databases:
|
42
|
+
# costs_csv: Path to custom costing
|
43
|
+
# factors_csv: Path to custom localization factors
|
44
|
+
def initialize(costs_csv: nil, factors_csv: nil)
|
45
|
+
@cp = CommonPaths.instance
|
46
|
+
@costing_database = CostingDatabase.instance
|
47
|
+
|
48
|
+
# If the path for custom costing is defined, use custom costing.
|
49
|
+
if (not costs_csv.nil?) and File.exist?(costs_csv)
|
50
|
+
@cp.costs_path = costs_csv
|
51
|
+
end
|
52
|
+
|
53
|
+
# If the path for custom factors is defined, use custom factors.
|
54
|
+
if (not factors_csv.nil?) and File.exist?(factors_csv)
|
55
|
+
@cp.costs_local_factors_path = factors_csv
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def load_database()
|
60
|
+
@costing_database.load_database
|
61
|
+
end
|
62
|
+
|
63
|
+
def validate_database()
|
64
|
+
@costing_database.validate_database
|
65
|
+
end
|
66
|
+
|
67
|
+
def generate_construction_cost_database_for_all_cities()
|
68
|
+
result = Array.new
|
69
|
+
@costing_database['raw']['locations'].each do |location|
|
70
|
+
province_state = location["province_state"]
|
71
|
+
city = location['city']
|
72
|
+
result.concat(generate_construction_cost_database_for_city(city, province_state))
|
73
|
+
end
|
74
|
+
return result
|
75
|
+
end
|
76
|
+
|
77
|
+
def generate_construction_cost_database_for_city(city, province_state)
|
78
|
+
@costing_database['constructions_costs'] = Array.new
|
79
|
+
puts "Costing for: #{province_state},#{city}"
|
80
|
+
@costing_database["raw"]['constructions_opaque'].each do |construction|
|
81
|
+
cost_construction(construction, {"province_state" => province_state, "city" => city}, 'opaque')
|
82
|
+
end
|
83
|
+
@costing_database["raw"]['constructions_glazing'].each do |construction|
|
84
|
+
cost_construction(construction, {"province_state" => province_state, "city" => city}, 'glazing')
|
85
|
+
end
|
86
|
+
puts "#{@costing_database['constructions_costs'].size} Costed Constructions for #{province_state},#{city}."
|
87
|
+
return @costing_database['constructions_costs']
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
def cost_audit_all(model:,
|
92
|
+
prototype_creator:,
|
93
|
+
envelope_costing: true,
|
94
|
+
lighting_costing: true,
|
95
|
+
boilers_costing: true,
|
96
|
+
chillers_costing: true,
|
97
|
+
cooling_towers_costing: true,
|
98
|
+
shw_costing: true,
|
99
|
+
ventilation_costing: true,
|
100
|
+
zone_system_costing: true,
|
101
|
+
renewables_costing: true,
|
102
|
+
template_type: nil
|
103
|
+
)
|
104
|
+
# Create a Hash to collect costing data.
|
105
|
+
@costing_report = {}
|
106
|
+
|
107
|
+
#Use closest city.
|
108
|
+
closest_loc = get_closest_cost_location(model.getWeatherFile.latitude, model.getWeatherFile.longitude)
|
109
|
+
@costing_report['city'] = closest_loc['city']
|
110
|
+
@costing_report['province_state'] = closest_loc['province_state']
|
111
|
+
|
112
|
+
# Create array to collect costed item information. First element is the costing location.
|
113
|
+
@cost_items = {
|
114
|
+
'City' => closest_loc['city'],
|
115
|
+
'Province' => closest_loc['province_state'],
|
116
|
+
'Items' => []
|
117
|
+
}
|
118
|
+
|
119
|
+
# Create a Hash in the hash for categories of costing.
|
120
|
+
@costing_report['envelope'] = {}
|
121
|
+
@costing_report['lighting'] = {}
|
122
|
+
@costing_report['lighting']['daylighting_sensor_control'] = []
|
123
|
+
@costing_report['lighting']['led_lighting'] = []
|
124
|
+
@costing_report['heating_and_cooling'] = {}
|
125
|
+
@costing_report['heating_and_cooling']['plant_equipment'] = []
|
126
|
+
@costing_report['heating_and_cooling']['zonal_systems'] = []
|
127
|
+
@costing_report['shw'] = {}
|
128
|
+
@costing_report['ventilation'] = {}
|
129
|
+
@costing_report['renewables'] = {}
|
130
|
+
@costing_report['renewables']['pv'] = []
|
131
|
+
@costing_report['totals'] = {}
|
132
|
+
|
133
|
+
# Check to see if standards building type and the number of stories has been defined. The former may be omitted in the future.
|
134
|
+
if model.getBuilding.standardsBuildingType.empty? or model.getBuilding.standardsNumberOfAboveGroundStories.empty?
|
135
|
+
raise("Building information is not complete, please ensure that the standardsBuildingType and standardsNumberOfAboveGroundStories are entered in the model. ")
|
136
|
+
end
|
137
|
+
|
138
|
+
# Find the mechanical room
|
139
|
+
mech_room, cond_spaces = prototype_creator.find_mech_room(model)
|
140
|
+
|
141
|
+
envCost = envelope_costing ? self.cost_audit_envelope(model, prototype_creator) : 0.0
|
142
|
+
lgtCost = lighting_costing ? self.cost_audit_lighting(model, prototype_creator) : 0.0
|
143
|
+
boilerCost = boilers_costing ? self.boiler_costing(model, prototype_creator) : 0.0
|
144
|
+
chillerCost = chillers_costing ? self.chiller_costing(model, prototype_creator) : 0.0
|
145
|
+
coolingTowerCost = cooling_towers_costing ? self.coolingtower_costing(model, prototype_creator) : 0.0
|
146
|
+
shwCost = shw_costing ? self.shw_costing(model, prototype_creator) : 0.0
|
147
|
+
ventCost = ventilation_costing ? self.ventilation_costing(model, prototype_creator,template_type, mech_room, cond_spaces) : 0.0
|
148
|
+
zonalSystemCost = zone_system_costing ? self.zonalsys_costing(model, prototype_creator, mech_room, cond_spaces) : 0.0
|
149
|
+
pvGroundCost = renewables_costing ? self.cost_audit_pv_ground(model, prototype_creator) : 0.0
|
150
|
+
thermalBridgingCost = 0.0
|
151
|
+
|
152
|
+
@costing_report["totals"] = {
|
153
|
+
'envelope' => envCost.round(0),
|
154
|
+
'thermal_bridging' => thermalBridgingCost.round(0),
|
155
|
+
'lighting' => lgtCost.round(0),
|
156
|
+
'heating_and_cooling' => (boilerCost + chillerCost + coolingTowerCost + zonalSystemCost).round(0),
|
157
|
+
'shw' => shwCost.round(0),
|
158
|
+
'ventilation' => ventCost.round(0),
|
159
|
+
'renewables' => pvGroundCost.round(0),
|
160
|
+
'grand_total' => (envCost + thermalBridgingCost + lgtCost + boilerCost + chillerCost + coolingTowerCost +
|
161
|
+
shwCost + ventCost + zonalSystemCost + pvGroundCost).round(0)
|
162
|
+
}
|
163
|
+
|
164
|
+
return @costing_report, @cost_items
|
165
|
+
end
|
166
|
+
|
167
|
+
def get_regional_cost_factors(provinceState, city, material)
|
168
|
+
@costing_database['localization_factors'].select { |code|
|
169
|
+
code['province_state'] == provinceState && code['city'] == city }.each do |code|
|
170
|
+
prefix_id = material['id'][0..1]
|
171
|
+
prefix_stored = code['code_prefix']
|
172
|
+
if prefix_id == prefix_stored
|
173
|
+
return code['material'], code['installation'], code['total']
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
error = [material, "Could not find regional adjustment factor for material used in #{city}, #{provinceState}."]
|
178
|
+
@costing_database['db_errors'] << error unless @costing_database['db_errors'].include?(error)
|
179
|
+
return 100.0, 100.0, 100.0
|
180
|
+
end
|
181
|
+
|
182
|
+
# Interpolate array of hashes that contain 2 values (key=rsi, data=cost)
|
183
|
+
def interpolate(x_y_array:, x2:, exterpolate_percentage_range: 30.0)
|
184
|
+
ratio_range = exterpolate_percentage_range / 100.0
|
185
|
+
array = x_y_array.uniq.sort { |a, b| a[0] <=> b[0] }
|
186
|
+
#if there is only one...return what you got.
|
187
|
+
if array.size == 1
|
188
|
+
return array.first[1].to_f
|
189
|
+
end
|
190
|
+
# Check if value x2 is within range of array for interpolation
|
191
|
+
# Extrapolate when x2 is out-of-range by +/- 10% of end values.
|
192
|
+
if array.empty? || x2 < ((1.0 - ratio_range) * array.first[0].to_f) || x2 > ((1.0 + ratio_range) * array.last[0].to_f)
|
193
|
+
return nil
|
194
|
+
elsif x2 < array.first[0].to_f
|
195
|
+
# Extrapolate down using first and second cost value to this out-of-range input
|
196
|
+
x_array = [array[0][0].to_f, array[1][0].to_f]
|
197
|
+
y_array = [array[0][1].to_f, array[1][1].to_f]
|
198
|
+
linear_model = SimpleLinearRegression.new(x_array, y_array)
|
199
|
+
y2 = linear_model.y_intercept + linear_model.slope * x2
|
200
|
+
return y2
|
201
|
+
elsif x2 > array.last[0].to_f
|
202
|
+
# Extrapolate up using second to last and last cost value to this out-of-range input
|
203
|
+
x_array = [array[-2][0].to_f, array[-1][0].to_f]
|
204
|
+
y_array = [array[-2][1].to_f, array[-1][1].to_f]
|
205
|
+
linear_model = SimpleLinearRegression.new(x_array, y_array)
|
206
|
+
y2 = linear_model.y_intercept + linear_model.slope * x2
|
207
|
+
return y2
|
208
|
+
else
|
209
|
+
array.each_index do |counter|
|
210
|
+
|
211
|
+
# skip last value.
|
212
|
+
next if array[counter] == array.last
|
213
|
+
|
214
|
+
x0 = array[counter][0]
|
215
|
+
y0 = array[counter][1]
|
216
|
+
x1 = array[counter + 1][0]
|
217
|
+
y1 = array[counter + 1][1]
|
218
|
+
|
219
|
+
# skip to next if x2 is not between x0 and x1
|
220
|
+
next if x2 < x0 || x2 > x1
|
221
|
+
|
222
|
+
# Do interpolation
|
223
|
+
y2 = y0 # just in-case x0, x1 and x2 are identical!
|
224
|
+
if (x1 - x0) > 0.0
|
225
|
+
y2 = y0.to_f + ((y1 - y0).to_f * (x2 - x0).to_f / (x1 - x0).to_f)
|
226
|
+
end
|
227
|
+
return y2
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
# Enter in [latitude, longitude] for each loc and this method will return the distance.
|
233
|
+
def distance(loc1, loc2)
|
234
|
+
rad_per_deg = Math::PI / 180 # PI / 180
|
235
|
+
rkm = 6371 # Earth radius in kilometers
|
236
|
+
rm = rkm * 1000 # Radius in meters
|
237
|
+
|
238
|
+
dlat_rad = (loc2[0] - loc1[0]) * rad_per_deg # Delta, converted to rad
|
239
|
+
dlon_rad = (loc2[1] - loc1[1]) * rad_per_deg
|
240
|
+
|
241
|
+
lat1_rad, lon1_rad = loc1.map { |i| i * rad_per_deg }
|
242
|
+
lat2_rad, lon2_rad = loc2.map { |i| i * rad_per_deg }
|
243
|
+
|
244
|
+
a = Math.sin(dlat_rad / 2) ** 2 + Math.cos(lat1_rad) * Math.cos(lat2_rad) * Math.sin(dlon_rad / 2) ** 2
|
245
|
+
c = 2 * Math::atan2(Math::sqrt(a), Math::sqrt(1 - a))
|
246
|
+
rm * c # Delta in meters
|
247
|
+
end
|
248
|
+
|
249
|
+
def get_closest_cost_location(lat, long)
|
250
|
+
dist = 1000000000000000000000.0
|
251
|
+
closest_loc = nil
|
252
|
+
# province_state city latitude longitude source
|
253
|
+
@costing_database['raw']['locations'].each do |location|
|
254
|
+
if distance([lat, long], [location['latitude'].to_f, location['longitude'].to_f]) < dist
|
255
|
+
closest_loc = location
|
256
|
+
dist = distance([lat, long], [location['latitude'].to_f, location['longitude'].to_f])
|
257
|
+
end
|
258
|
+
end
|
259
|
+
return closest_loc
|
260
|
+
end
|
261
|
+
|
262
|
+
# This will expand the two letter province abbreviation to a full uppercase province name
|
263
|
+
def expandProvAbbrev(abbrev)
|
264
|
+
|
265
|
+
# Note that the proper abbreviation for Quebec is QC not PQ. However, we've used PQ in openstudio-standards!
|
266
|
+
Hash provAbbrev = {"AB" => "ALBERTA",
|
267
|
+
"BC" => "BRITISH COLUMBIA",
|
268
|
+
"MB" => "MANITOBA",
|
269
|
+
"NB" => "NEW BRUNSWICK",
|
270
|
+
"NL" => "NEWFOUNDLAND AND LABRADOR",
|
271
|
+
"NT" => "NORTHWEST TERRITORIES",
|
272
|
+
"NS" => "NOVA SCOTIA",
|
273
|
+
"NU" => "NUNAVUT",
|
274
|
+
"ON" => "ONTARIO",
|
275
|
+
"PE" => "PRINCE EDWARD ISLAND",
|
276
|
+
"PQ" => "QUEBEC",
|
277
|
+
"SK" => "SASKATCHEWAN",
|
278
|
+
"YT" => "YUKON"
|
279
|
+
}
|
280
|
+
return provAbbrev[abbrev]
|
281
|
+
end
|
282
|
+
|
283
|
+
def read_mech_sizing()
|
284
|
+
file = File.read(@cp.mech_sizing_data_file)
|
285
|
+
return JSON.parse(file)
|
286
|
+
end
|
287
|
+
|
288
|
+
# This adds costed items to the array of cousted items which end up in btap_itmes.json. Note that the array this
|
289
|
+
# method uses is created in the cost_audit_all method. The array is created with an initial element that contains the
|
290
|
+
# city and province whose localiazation factors are used for costing.
|
291
|
+
# The inputs are:
|
292
|
+
# id: (string) The costing database id for the item being costed.
|
293
|
+
# quantity: (float) The total amount of the item being costed in whatever units the item is costed in. This should
|
294
|
+
# include all multiplier used to determine this cost (e.g. such as thermal_zone multipliers). As
|
295
|
+
# an example, if 32 ft. of wire were required for a piece of equipment used in a thermal zone with
|
296
|
+
# a multiplier of 10, the quantity would be 3.2 (32 ft. * 10 / 100 since wire is costed per
|
297
|
+
# 100 ft.).
|
298
|
+
# material_mult: (float) The multiplier used to estimate the cost of an item from the base cost. For example, high
|
299
|
+
# efficiency SHW tanks are estimated to cost 30% higher than regular SHW tanks so the cost of these
|
300
|
+
# tanks are calculated by the cost * 1.3. Thus, the material_mult for high efficiency tanks
|
301
|
+
# would be 1.3. This is defauted to 1.0 if it is not provided.
|
302
|
+
# labour_mult: (float) Similar to material_mult only applied to labour costs. The labour_mult can be different than
|
303
|
+
# the material_mult. This is defaulted to 1.0 if it is not provided.
|
304
|
+
# equipment_mult: (float) Similar to material_mult and labour_mult only for equipment. This will always be 1.0 until
|
305
|
+
# equipment costs are supported.
|
306
|
+
# tags: (array of strings) This is an array which links the costed item to a component of the model that is being
|
307
|
+
# costed. For example, a material_id related to a boiler pump might have tags like ["boiler", "pump"].
|
308
|
+
#
|
309
|
+
def add_costed_item(material_id:, quantity:, material_mult: 1.0, labour_mult: 1.0, equip_mult: 1.0, tags: [])
|
310
|
+
# Do some error handling for the tags argument
|
311
|
+
tags_out = [tags] if tags.kind_of?(String)
|
312
|
+
tags_out = tags if tags.kind_of?(Array)
|
313
|
+
|
314
|
+
# Validate the type of the arguments.
|
315
|
+
if (tags_out.kind_of?(Array) == false)
|
316
|
+
raise("The tags for the item #{material_id} were not properly defined. Please search for where the item is being added to the @cost_items hash via the add_costed_item method and correct the entry.")
|
317
|
+
end
|
318
|
+
|
319
|
+
if (material_id.kind_of?(String) == false)
|
320
|
+
raise("The material_id for the item #{material_id} is not a string. Please search for where the item is being added to the @cost_items hash via the add_costed_item method and correct the entry.")
|
321
|
+
end
|
322
|
+
|
323
|
+
if (quantity.kind_of?(Float) == false)
|
324
|
+
raise("The quantity for the item #{material_id} is not a float. Please search for where the item is being added to the @cost_items hash via the add_costed_item method and correct the entry.")
|
325
|
+
end
|
326
|
+
|
327
|
+
if (material_mult.kind_of?(Float) == false)
|
328
|
+
raise("The material_mult for the item #{material_id} is not a float. Please search for where the item is being added to the @cost_items hash via the add_costed_item method and correct the entry.")
|
329
|
+
end
|
330
|
+
|
331
|
+
if (labour_mult.kind_of?(Float) == false)
|
332
|
+
raise("The labour_mult for the item #{material_id} is not a float. Please search for where the item is being added to the @cost_items hash via the add_costed_item method and correct the entry.")
|
333
|
+
end
|
334
|
+
|
335
|
+
if (equip_mult.kind_of?(Float) == false)
|
336
|
+
raise("The equip_mult for the item #{material_id} is not a float. Please search for where the item is being added to the @cost_items hash via the add_costed_item method and correct the entry.")
|
337
|
+
end
|
338
|
+
|
339
|
+
# Add the costed item to the output output hash.
|
340
|
+
@cost_items['Items'] << {
|
341
|
+
'id' => material_id,
|
342
|
+
'quantity' => quantity,
|
343
|
+
'material_mult' => material_mult,
|
344
|
+
'labour_mult' => labour_mult,
|
345
|
+
'equipment_mult' => equip_mult,
|
346
|
+
'tags' => tags_out
|
347
|
+
}
|
348
|
+
end
|
349
|
+
|
350
|
+
# This method takes the list of costed items in the building generated with the help of the above add_costed_item
|
351
|
+
# method and finds the costs for the list of items. It takes in:
|
352
|
+
# btap_items: (array of hashes) This array contains all the items that must be costed. The first element of the
|
353
|
+
# array is:
|
354
|
+
# {
|
355
|
+
# City: (string) City used for cost lacalization factor
|
356
|
+
# Province: (string) Province used for cost localization factor
|
357
|
+
# }
|
358
|
+
# The remaining arrays look like:
|
359
|
+
# {
|
360
|
+
# id: (string) ID of the coested item in question.
|
361
|
+
# quantity: (float) Amount of costed item (should include all multipliers except localization factors,
|
362
|
+
# material_mult, labour_mult, equipment_mult).
|
363
|
+
# material_mult: (float) Material multiplier from cost spreadsheet used mainly for higher performance equipment
|
364
|
+
# (for example, regular and high performance boilers share the same id but high performance
|
365
|
+
# boilers have a material_mult of around 1.3-that is they are estimated to be 1.3 times as
|
366
|
+
# expensive as regular boilers).
|
367
|
+
# labour_mult: (float) Same idea as material_mult only for labour (often this will be 1.0 even if material_mult
|
368
|
+
# is something else).
|
369
|
+
# equipment_mult: (float) Same idea as labour_mult only for equipment. It will always be 1.0 until equipment
|
370
|
+
# costs are implemented in costing.
|
371
|
+
# tags: (array of strings) An array of strings used to define what part of the building is being costed (e.g.
|
372
|
+
# an component for a ccashp might have these tags: "Ventilation", "CCASHP", "ccashp_condensor")
|
373
|
+
# }
|
374
|
+
# custom_costing: (array of hashes) A custom costing database if you do not want to use the default one. This must
|
375
|
+
# have the same format as that found by @costing_database['costs']
|
376
|
+
# custCity: (string) A custom cost localization city if you do not want to use the one in the first item in the
|
377
|
+
# btap_itmes hash.
|
378
|
+
# custProvince: (string) A custom cost localization province if you do not want to use the one in the first item in
|
379
|
+
# the btap_items hash.
|
380
|
+
#
|
381
|
+
# The output of the method is a hash containing these summary costs:
|
382
|
+
# costRetHash = {
|
383
|
+
# envelope: (float) Building envelope costs (to 2 decimal places).
|
384
|
+
# lighting: (float) Ligting costs (to 2 decimal places).
|
385
|
+
# heating_and_cooling: (float) Heating and cooling costs (not related to ventilation) (to 2 decimal places).
|
386
|
+
# shw: (float) Service hot water costs (to 2 decimal places).
|
387
|
+
# ventilation: (float) Ventilation (including ventilation air heating and cooling) costs (to 2 decimal places).
|
388
|
+
# grand_total: (float) Total costs (to 2 decimal places).
|
389
|
+
# }
|
390
|
+
#
|
391
|
+
def cost_list_items(btap_items:, custom_costing: nil, custCity: nil, custProvince: nil)
|
392
|
+
# Check if costing is for a custom city and province. If not use the city and province found in the first entry
|
393
|
+
# of the array of costed items.
|
394
|
+
if custCity.nil? || custProvince.nil?
|
395
|
+
costCity = btap_items['City'].to_s
|
396
|
+
costProvince = btap_items['Province'].to_s
|
397
|
+
else
|
398
|
+
costCity = custCity
|
399
|
+
costProvince = custProvince
|
400
|
+
end
|
401
|
+
|
402
|
+
# Initialize cost counters
|
403
|
+
totCost = 0.0
|
404
|
+
envCost = 0.0
|
405
|
+
lightCost = 0.0
|
406
|
+
heatCoolCost = 0.0
|
407
|
+
shwCost = 0.0
|
408
|
+
ventCost = 0.0
|
409
|
+
renewCost = 0.0
|
410
|
+
|
411
|
+
custom_costing.nil? ? costingDB = @costing_database['costs'] : costingDB = custom_costing
|
412
|
+
|
413
|
+
btap_items['Items'].each do |costing_item|
|
414
|
+
# Look for the costing information for the piece of equipment in the costing database.
|
415
|
+
costing_data = costingDB.detect {|data| data['id'].to_s.upcase == costing_item['id'].to_s.upcase}
|
416
|
+
# If no costing information is found then return an error.
|
417
|
+
if costing_data.nil?
|
418
|
+
raise "Error: no costing information available for material id #{costing_item['id']}!"
|
419
|
+
elsif costing_data['baseCosts']['materialOpCost'].nil? && costing_data['baseCosts']['laborOpCost'].nil?
|
420
|
+
#This is a stub for some work that needs to be done to account for equipment costing. For now this is zeroed out.
|
421
|
+
# A similar test is done on reading the data from the database and collected in the error file when the
|
422
|
+
# costing database is generated.
|
423
|
+
raise "Error: costing information for material id #{costing_item['id']} is nil. Please check costing data."
|
424
|
+
end
|
425
|
+
costing_data['baseCosts']['equipmentOpCost'].nil? ? equip_base_cost = 0.0 : equip_base_cost = costing_data['baseCosts']['equipmentOpCost'].to_f
|
426
|
+
costing_data['baseCosts']['materialOpCost'].nil? ? mat_base_cost = 0.0 : mat_base_cost = costing_data['baseCosts']['materialOpCost'].to_f
|
427
|
+
costing_data['baseCosts']['laborOpCost'].nil? ? lab_base_cost = 0.0 : lab_base_cost = costing_data['baseCosts']['laborOpCost'].to_f
|
428
|
+
|
429
|
+
# The costs from the costing database are US national average costs (for placeholder costs) or whatever is in the
|
430
|
+
# 'province_state' and 'city' fields (for custom costs). These costs need to be adjusted to reflect the costs
|
431
|
+
# expected in the location of interest. The 'get_regional_cost_factors' method finds the appropriate cost
|
432
|
+
# adjustment factors.
|
433
|
+
|
434
|
+
mat_mult, inst_mult, eq_mult = get_regional_cost_factors(costProvince, costCity, costing_item)
|
435
|
+
if mat_mult.nil? || inst_mult.nil?
|
436
|
+
raise("Error: no localization information available for material id #{costing_item['material_id']}!")
|
437
|
+
end
|
438
|
+
# Get any associated material or labour multiplier for the equipment present in the 'materials_hvac' sheet in the
|
439
|
+
# costing spreadsheet.
|
440
|
+
costing_item['material_mult'].to_f == 0 ? mat_quant = 1.0 : mat_quant = costing_item['material_mult'].to_f
|
441
|
+
costing_item['labour_mult'].to_f == 0 ? lab_quant = 1.0 : lab_quant = costing_item['labour_mult'].to_f
|
442
|
+
costing_item['equipment_mult'].to_f == 0 || costing_item['equipment_mult'].nil? ? eq_quant = 1.0 : eq_quant = costing_item['equipment_mult'].to_f
|
443
|
+
# Calculate the adjusted material and labour costs.
|
444
|
+
mat_cost = mat_base_cost*(mat_mult/100.0)*mat_quant
|
445
|
+
lab_cost = lab_base_cost*(inst_mult/100.0)*lab_quant
|
446
|
+
eq_cost = equip_base_cost*(eq_mult/100.0)*eq_quant
|
447
|
+
# Calculate the total item cost.
|
448
|
+
item_cost = (mat_cost + lab_cost + eq_cost)*(costing_item["quantity"].to_f)
|
449
|
+
|
450
|
+
# Add cost to sub-type counters
|
451
|
+
envCost += item_cost unless (costing_item['tags'].select{|data| data.to_s.upcase == "ENVELOPE"}).empty?
|
452
|
+
lightCost += item_cost unless (costing_item['tags'].select{|data| data.to_s.upcase == "LIGHTING"}).empty?
|
453
|
+
heatCoolCost += item_cost unless (costing_item['tags'].select{|data| data.to_s.upcase == "HEATING_COOLING"}).empty?
|
454
|
+
shwCost += item_cost unless (costing_item['tags'].select{|data| data.to_s.upcase == "SHW"}).empty?
|
455
|
+
ventCost += item_cost unless (costing_item['tags'].select{|data| data.to_s.upcase == "VENTILATION"}).empty?
|
456
|
+
renewCost += item_cost unless (costing_item['tags'].select{|data| data.to_s.upcase == "RENEWABLES"}).empty?
|
457
|
+
totCost += item_cost
|
458
|
+
end
|
459
|
+
|
460
|
+
# Create and return hash containing costing results
|
461
|
+
costRetHash = {
|
462
|
+
'envelope' => envCost.round(2),
|
463
|
+
'lighting' => lightCost.round(2),
|
464
|
+
'heating_and_cooling' => heatCoolCost.round(2),
|
465
|
+
'shw' => shwCost.round(2),
|
466
|
+
'ventilation' => ventCost.round(2),
|
467
|
+
'renewables' => renewCost.round(2),
|
468
|
+
'grand_total' => totCost.round(2)
|
469
|
+
}
|
470
|
+
return costRetHash
|
471
|
+
end
|
472
|
+
|
473
|
+
end
|