openstudio-standards 0.8.5 → 0.8.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/data/standards/test_performance_expected_dd_results.csv +90 -90
- data/lib/openstudio-standards/btap/activity.rb +858 -61
- data/lib/openstudio-standards/btap/attributes.rb +297 -103
- data/lib/openstudio-standards/btap/bridging.rb +1335 -593
- data/lib/openstudio-standards/btap/btap.rb +60 -181
- data/lib/openstudio-standards/btap/carbon/btap_carbon.rb +199 -142
- data/lib/openstudio-standards/btap/common/constructions.json +1275 -0
- data/lib/openstudio-standards/btap/common/costs.csv +1969 -0
- data/lib/openstudio-standards/btap/common/costs_local_factors.csv +2315 -0
- data/lib/openstudio-standards/btap/common/materials_glazing.csv +61 -0
- data/lib/openstudio-standards/btap/{common_resources → common}/materials_hvac.csv +1685 -1685
- data/lib/openstudio-standards/btap/{common_resources → common}/materials_lighting.csv +266 -266
- data/lib/openstudio-standards/btap/common/materials_opaque.csv +214 -0
- data/lib/openstudio-standards/btap/common/necb_building_types.csv +35 -0
- data/lib/openstudio-standards/btap/common/necb_space_types.csv +110 -0
- data/lib/openstudio-standards/btap/common/thermal_bridging.csv +73 -0
- data/lib/openstudio-standards/btap/costing/btap_costing.rb +346 -429
- data/lib/openstudio-standards/btap/costing/database.rb +104 -0
- data/lib/openstudio-standards/btap/costing/daylighting_sensor_control_costing.rb +356 -344
- data/lib/openstudio-standards/btap/costing/dcv_costing.rb +282 -296
- data/lib/openstudio-standards/btap/costing/envelope_costing.rb +179 -192
- data/lib/openstudio-standards/btap/costing/heating_cooling_costing.rb +2477 -2316
- data/lib/openstudio-standards/btap/costing/led_lighting_costing.rb +127 -142
- data/lib/openstudio-standards/btap/costing/lighting_costing.rb +216 -198
- data/lib/openstudio-standards/btap/costing/nv_costing.rb +522 -523
- data/lib/openstudio-standards/btap/costing/pv_ground_costing.rb +649 -649
- data/lib/openstudio-standards/btap/costing/shw_costing.rb +626 -625
- data/lib/openstudio-standards/btap/costing/thermal_bridging_costing.rb +72 -0
- data/lib/openstudio-standards/btap/costing/ventilation_costing.rb +2363 -2361
- data/lib/openstudio-standards/btap/deprecated.rb +1033 -0
- data/lib/openstudio-standards/btap/envelope.rb +313 -572
- data/lib/openstudio-standards/btap/fileio.rb +152 -1070
- data/lib/openstudio-standards/btap/geometry.rb +486 -766
- data/lib/openstudio-standards/btap/linear_regression.rb +130 -0
- data/lib/openstudio-standards/btap/paths.rb +46 -0
- data/lib/openstudio-standards/btap/qaqc/analysis.rb +163 -0
- data/lib/openstudio-standards/btap/qaqc/data.rb +2293 -0
- data/lib/openstudio-standards/btap/qaqc/datapoint.rb +641 -0
- data/lib/openstudio-standards/btap/schedules.rb +11 -611
- data/lib/openstudio-standards/btap/structure.rb +574 -189
- data/lib/openstudio-standards/constructions/materials/information.rb +1 -1
- data/lib/openstudio-standards/create_typical/create_typical.rb +1 -1
- data/lib/openstudio-standards/exterior_lighting/create.rb +135 -85
- data/lib/openstudio-standards/exterior_lighting/data/typical_exterior_lighting.json +20 -0
- data/lib/openstudio-standards/exterior_lighting/information.rb +23 -2
- data/lib/openstudio-standards/geometry/create.rb +7 -3
- data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.Model.rb +3 -2
- data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Model.rb +3 -2
- data/lib/openstudio-standards/prototypes/common/data/thermostat_schedule_lookup.json +84 -84
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.hvac_systems.rb +4 -4
- data/lib/openstudio-standards/refrigeration/create_walkin.rb +45 -5
- data/lib/openstudio-standards/refrigeration/data/refrigerated_walkins.csv +75 -75
- data/lib/openstudio-standards/schedules/parametric.rb +6 -0
- data/lib/openstudio-standards/service_water_heating/create_typical.rb +10 -3
- data/lib/openstudio-standards/service_water_heating/create_water_heating_loop.rb +30 -14
- data/lib/openstudio-standards/service_water_heating/information.rb +4 -1
- data/lib/openstudio-standards/sql_file/unmet_hours.rb +2 -1
- data/lib/openstudio-standards/standards/Standards.CoilCoolingDXSingleSpeed.rb +3 -1
- data/lib/openstudio-standards/standards/Standards.CoilHeatingDXSingleSpeed.rb +3 -1
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.boilers.json +12 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.construction_properties.json +18432 -3072
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.boilers.json +26 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.construction_properties.json +8532 -1422
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.boilers.json +26 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.construction_properties.json +8532 -1422
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.boilers.json +16 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.computer_room_acs.json +30 -30
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.construction_properties.json +7560 -1260
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.PumpVariableSpeed.rb +1 -2
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.boilers.json +16 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.construction_properties.json +7560 -1260
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirLoopHVAC.rb +3 -3
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.PumpVariableSpeed.rb +1 -2
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.boilers.json +22 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.construction_properties.json +7236 -1206
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.furnaces.json +223 -19
- data/lib/openstudio-standards/standards/ashrae_90_1/data/ashrae_90_1.schedules.json +37 -1
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.boilers.json +3 -0
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.construction_properties.json +6900 -1150
- data/lib/openstudio-standards/standards/cbes/data/cbes.schedules.json +1 -1
- data/lib/openstudio-standards/standards/deer/data/deer.schedules.json +1 -1
- data/lib/openstudio-standards/standards/necb/BTAP1980TO2010/data/surface_thermal_transmittance.json +76 -40
- data/lib/openstudio-standards/standards/necb/BTAPPRE1980/data/surface_thermal_transmittance.json +76 -40
- data/lib/openstudio-standards/standards/necb/ECMS/hvac_systems.rb +6 -4
- data/lib/openstudio-standards/standards/necb/NECB2011/NECB2011_QAQC.json +2988 -0
- data/lib/openstudio-standards/standards/necb/NECB2011/autozone.rb +25 -3
- data/lib/openstudio-standards/standards/necb/NECB2011/building_envelope.rb +672 -534
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/Hospital.osm +1791 -1792
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPMultiTower.osm +7 -1
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPTownHouse.osm +7 -1
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LargeHotel.osm +5 -6
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LargeOffice.osm +8 -9
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/NorthernEducation.osm +10 -4
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/NorthernHealthCare.osm +14 -15
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/Outpatient.osm +7 -1
- data/lib/openstudio-standards/standards/necb/NECB2011/data/surface_thermal_transmittance.json +76 -40
- data/lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb +14 -6
- data/lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb +542 -130
- data/lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb +30 -63
- data/lib/openstudio-standards/standards/necb/NECB2011/service_water_heating.rb +7 -2
- data/lib/openstudio-standards/standards/necb/NECB2015/data/surface_thermal_transmittance.json +90 -0
- data/lib/openstudio-standards/standards/necb/NECB2015/necb_2015.rb +8 -6
- data/lib/openstudio-standards/standards/necb/NECB2017/data/surface_thermal_transmittance.json +76 -40
- data/lib/openstudio-standards/standards/necb/NECB2017/necb_2017.rb +2 -1
- data/lib/openstudio-standards/standards/necb/NECB2020/building_envelope.rb +0 -117
- data/lib/openstudio-standards/standards/necb/NECB2020/data/surface_thermal_transmittance.json +76 -45
- data/lib/openstudio-standards/standards/necb/NECB2020/necb_2020.rb +218 -0
- data/lib/openstudio-standards/standards/necb/common/construction_defaults.osm +12 -7
- data/lib/openstudio-standards/standards/standard.rb +1 -1
- data/lib/openstudio-standards/version.rb +1 -1
- data/lib/openstudio-standards.rb +9 -4
- metadata +59 -66
- data/data/inventory/thermal_bridging.csv +0 -90
- data/data/standards/OpenStudio_Standards-ashrae_90_1-ALL-comstock(space_types).xlsx +0 -0
- data/data/standards/openstudio_standards_duplicates_log.csv +0 -7962
- data/lib/openstudio-standards/btap/NECB_building_types.csv +0 -35
- data/lib/openstudio-standards/btap/NECB_space_types.csv +0 -109
- data/lib/openstudio-standards/btap/WeatherData1.json +0 -2110
- data/lib/openstudio-standards/btap/WeatherData1.xlsx +0 -0
- data/lib/openstudio-standards/btap/btap_result.rb +0 -1144
- data/lib/openstudio-standards/btap/btap_test_helper.rb +0 -86
- data/lib/openstudio-standards/btap/common_paths.rb +0 -81
- data/lib/openstudio-standards/btap/common_resources/ConstructionProperties.csv +0 -52
- data/lib/openstudio-standards/btap/common_resources/Constructions.csv +0 -37
- data/lib/openstudio-standards/btap/common_resources/construction_sets.csv +0 -1270
- data/lib/openstudio-standards/btap/common_resources/constructions_glazing.csv +0 -121
- data/lib/openstudio-standards/btap/common_resources/constructions_opaque.csv +0 -2256
- data/lib/openstudio-standards/btap/common_resources/costs.csv +0 -1897
- data/lib/openstudio-standards/btap/common_resources/costs_local_factors.csv +0 -2315
- data/lib/openstudio-standards/btap/common_resources/materials_glazing.csv +0 -35
- data/lib/openstudio-standards/btap/common_resources/materials_opaque.csv +0 -164
- data/lib/openstudio-standards/btap/costing/btap_measure_helper.rb +0 -359
- data/lib/openstudio-standards/btap/costing/btap_workflow.rb +0 -117
- data/lib/openstudio-standards/btap/costing/copy_test_results_files_to_expected_results.rb +0 -11
- data/lib/openstudio-standards/btap/costing/cost_building_from_file.rb +0 -136
- data/lib/openstudio-standards/btap/costing/costing_database_wrapper.rb +0 -173
- data/lib/openstudio-standards/btap/costing/dummy.epw +0 -8768
- data/lib/openstudio-standards/btap/costing/dummy.osm +0 -5320
- data/lib/openstudio-standards/btap/costing/neb_end_use_prices.csv +0 -42
- data/lib/openstudio-standards/btap/costing/necb_2011_spacetype_info.csv +0 -225
- data/lib/openstudio-standards/btap/costing/necb_reference_runs.csv +0 -28705
- data/lib/openstudio-standards/btap/costing/parallel_tests.rb +0 -92
- data/lib/openstudio-standards/btap/costing/test_list.txt +0 -15
- data/lib/openstudio-standards/btap/costing/test_run_all_test_locally.rb +0 -27
- data/lib/openstudio-standards/btap/costing/test_run_costing_tests.rb +0 -80
- data/lib/openstudio-standards/btap/necb_2011_spacetype_info.csv +0 -225
- data/lib/openstudio-standards/btap/utilities/Add_template_field_to_json.rb +0 -22
- data/lib/openstudio-standards/btap/utilities/convert_surfaces_to_adiabatic_necb_8426.rb +0 -204
- data/lib/openstudio-standards/btap/utilities/generate_space_types.rb +0 -8
- data/lib/openstudio-standards/btap/utilities/necb_to_epw_map.rb +0 -50
- data/lib/openstudio-standards/btap/utilities/os_sim_extract.rb +0 -48
- data/lib/openstudio-standards/btap/utilities/rename_surfaces.rb +0 -24
- data/lib/openstudio-standards/btap/utilities/round_surf_coords.rb +0 -32
- data/lib/openstudio-standards/btap/utilities/sched_create.rb +0 -145
- data/lib/openstudio-standards/btap/utilities/set_mult_to_adiabatic.rb +0 -53
- data/lib/openstudio-standards/btap/utilities/space_type.rb +0 -78
- data/lib/openstudio-standards/btap/utilities/weatherData1_xlsx_to_json.rb +0 -56
- data/lib/openstudio-standards/btap/vintagizer.rb +0 -161
- data/lib/openstudio-standards/btap/visualization.rb +0 -72
- data/lib/openstudio-standards/standards/necb/NECB2011/data/necb_2011_spacetype_info.csv +0 -225
- data/lib/openstudio-standards/standards/necb/common/btap_analysis.rb +0 -79
- data/lib/openstudio-standards/standards/necb/common/btap_data.rb +0 -2656
- data/lib/openstudio-standards/standards/necb/common/btap_datapoint.rb +0 -653
- data/lib/openstudio-standards/standards/necb/common/necb_reference_runs.csv +0 -28705
- data/lib/openstudio-standards/standards/necb/common/utility_pricing_2025-02-20.csv +0 -92
- /data/lib/openstudio-standards/btap/{common_resources → common}/carbon_frame.csv +0 -0
- /data/lib/openstudio-standards/btap/{common_resources → common}/carbon_glazing.csv +0 -0
- /data/lib/openstudio-standards/btap/{common_resources → common}/carbon_opaque.csv +0 -0
- /data/lib/openstudio-standards/btap/{common_resources → common}/hvac_vent_ahu.csv +0 -0
- /data/lib/openstudio-standards/btap/{common_resources → common}/lighting.csv +0 -0
- /data/lib/openstudio-standards/btap/{common_resources → common}/lighting_sets.csv +0 -0
- /data/lib/openstudio-standards/btap/{common_resources → common}/locations.csv +0 -0
- /data/lib/openstudio-standards/btap/{costing → common}/mech_sizing.json +0 -0
|
@@ -1,469 +1,386 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
x_mean = mean(@xs)
|
|
19
|
-
y_mean = mean(@ys)
|
|
1
|
+
module BTAP
|
|
2
|
+
class Costing
|
|
3
|
+
|
|
4
|
+
# Class to initialize BTAP Costing.
|
|
5
|
+
# @param costs_csv [String] Path to custom costing.
|
|
6
|
+
# @param factors_csv [String] Path to custom localization factors.
|
|
7
|
+
# @param attributes [BTAP::Atributes] Methods and helpers for model
|
|
8
|
+
# attributes, mostly envelopes and
|
|
9
|
+
# constructions.
|
|
10
|
+
def initialize(costs_csv: nil, factors_csv: nil, attributes:)
|
|
11
|
+
@costing_database = Database.instance
|
|
12
|
+
@attributes = attributes
|
|
13
|
+
|
|
14
|
+
# If the path for custom costing is defined, use custom costing.
|
|
15
|
+
if (not costs_csv.nil?) and File.exist?(costs_csv)
|
|
16
|
+
Paths.costs_path = costs_csv
|
|
17
|
+
end
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
# If the path for custom factors is defined, use custom factors.
|
|
20
|
+
if (not factors_csv.nil?) and File.exist?(factors_csv)
|
|
21
|
+
Paths.costs_local_factors_path = factors_csv
|
|
22
|
+
end
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
def validate_database()
|
|
26
|
+
@costing_database.validate_ahu_items_and_quantities
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
29
|
+
def cost_audit_all(model:,
|
|
30
|
+
prototype_creator:,
|
|
31
|
+
envelope_costing: true,
|
|
32
|
+
lighting_costing: true,
|
|
33
|
+
boilers_costing: true,
|
|
34
|
+
chillers_costing: true,
|
|
35
|
+
cooling_towers_costing: true,
|
|
36
|
+
shw_costing: true,
|
|
37
|
+
ventilation_costing: true,
|
|
38
|
+
zone_system_costing: true,
|
|
39
|
+
renewables_costing: true,
|
|
40
|
+
thermal_bridging_costing: @attributes.tbd_enabled,
|
|
41
|
+
template_type: nil)
|
|
42
|
+
|
|
43
|
+
@costing_report = {}
|
|
44
|
+
|
|
45
|
+
# Use the closest city.
|
|
46
|
+
closest_loc = get_closest_cost_location(model.getWeatherFile.latitude, model.getWeatherFile.longitude)
|
|
47
|
+
@costing_report['city'] = closest_loc['city']
|
|
48
|
+
@costing_report['province_state'] = closest_loc['province_state']
|
|
49
|
+
|
|
50
|
+
# Create an array to collect costed item information. First element is the
|
|
51
|
+
# costing location.
|
|
52
|
+
@cost_items = {
|
|
53
|
+
'City' => closest_loc['city'],
|
|
54
|
+
'Province' => closest_loc['province_state'],
|
|
55
|
+
'Items' => []
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
# Create nested hashes for each category of costing.
|
|
59
|
+
@costing_report['envelope'] = {}
|
|
60
|
+
@costing_report['lighting'] = {}
|
|
61
|
+
@costing_report['lighting']['daylighting_sensor_control'] = []
|
|
62
|
+
@costing_report['lighting']['led_lighting'] = []
|
|
63
|
+
@costing_report['heating_and_cooling'] = {}
|
|
64
|
+
@costing_report['heating_and_cooling']['plant_equipment'] = []
|
|
65
|
+
@costing_report['heating_and_cooling']['zonal_systems'] = []
|
|
66
|
+
@costing_report['shw'] = {}
|
|
67
|
+
@costing_report['ventilation'] = {}
|
|
68
|
+
@costing_report['renewables'] = {}
|
|
69
|
+
@costing_report['renewables']['pv'] = []
|
|
70
|
+
@costing_report['totals'] = {}
|
|
71
|
+
|
|
72
|
+
# Check to see if standards building type and the number of stories has
|
|
73
|
+
# been defined. The former may be omitted in the future.
|
|
74
|
+
if model.getBuilding.standardsBuildingType.empty? or model.getBuilding.standardsNumberOfAboveGroundStories.empty?
|
|
75
|
+
raise("Building information is not complete, please ensure that the standardsBuildingType and standardsNumberOfAboveGroundStories are entered in the model. ")
|
|
76
|
+
end
|
|
47
77
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
78
|
+
# Find the mechanical room.
|
|
79
|
+
mech_room, cond_spaces = prototype_creator.find_mech_room(model)
|
|
80
|
+
|
|
81
|
+
envCost = envelope_costing ? self.cost_audit_envelope(prototype_creator) : 0.0
|
|
82
|
+
thermalBridgingCost = thermal_bridging_costing ? self.cost_audit_thermal_bridging(prototype_creator) : 0.0
|
|
83
|
+
lgtCost = lighting_costing ? self.cost_audit_lighting(model, prototype_creator) : 0.0
|
|
84
|
+
boilerCost = boilers_costing ? self.boiler_costing(model, prototype_creator) : 0.0
|
|
85
|
+
chillerCost = chillers_costing ? self.chiller_costing(model, prototype_creator) : 0.0
|
|
86
|
+
coolingTowerCost = cooling_towers_costing ? self.coolingtower_costing(model, prototype_creator) : 0.0
|
|
87
|
+
shwCost = shw_costing ? self.shw_costing(model, prototype_creator) : 0.0
|
|
88
|
+
ventCost = ventilation_costing ? self.ventilation_costing(
|
|
89
|
+
model, prototype_creator,template_type, mech_room, cond_spaces) : 0.0
|
|
90
|
+
zonalSystemCost = zone_system_costing ? self.zonalsys_costing(
|
|
91
|
+
model, prototype_creator, mech_room, cond_spaces) : 0.0
|
|
92
|
+
pvGroundCost = renewables_costing ? self.cost_audit_pv_ground(model, prototype_creator) : 0.0
|
|
93
|
+
|
|
94
|
+
@costing_report["totals"] = {
|
|
95
|
+
'envelope' => envCost.round(0),
|
|
96
|
+
'thermal_bridging' => thermalBridgingCost.round(0),
|
|
97
|
+
'lighting' => lgtCost.round(0),
|
|
98
|
+
'heating_and_cooling' => (boilerCost + chillerCost + coolingTowerCost + zonalSystemCost).round(0),
|
|
99
|
+
'shw' => shwCost.round(0),
|
|
100
|
+
'ventilation' => ventCost.round(0),
|
|
101
|
+
'renewables' => pvGroundCost.round(0),
|
|
102
|
+
'grand_total' => (
|
|
103
|
+
envCost + thermalBridgingCost + lgtCost + boilerCost + chillerCost + coolingTowerCost + shwCost + ventCost +
|
|
104
|
+
zonalSystemCost + pvGroundCost).round(0)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return @costing_report, @cost_items
|
|
51
108
|
end
|
|
52
109
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
110
|
+
def get_regional_cost_factors(province_state, city, material)
|
|
111
|
+
@costing_database['localization_factors'].select { |code|
|
|
112
|
+
code['province_state'] == province_state && code['city'] == city }.each do |code|
|
|
113
|
+
prefix_id = material['id'][0..1]
|
|
114
|
+
prefix_stored = code['code_prefix']
|
|
115
|
+
if prefix_id == prefix_stored
|
|
116
|
+
return code['material'], code['installation'], code['total']
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
error = [material, "Could not find regional adjustment factor for material used in #{city}, #{province_state}."]
|
|
120
|
+
@costing_database['db_errors'] << error unless @costing_database['db_errors'].include?(error)
|
|
121
|
+
return 100.0, 100.0, 100.0
|
|
56
122
|
end
|
|
57
|
-
end
|
|
58
123
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
124
|
+
# Enter in [latitude, longitude] for each loc and this method will return the distance.
|
|
125
|
+
def distance(loc1, loc2)
|
|
126
|
+
rad_per_deg = Math::PI / 180 # PI / 180
|
|
127
|
+
rkm = 6371 # Earth radius in kilometers
|
|
128
|
+
rm = rkm * 1000 # Radius in meters
|
|
62
129
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
@costing_database['raw']['locations'].each do |location|
|
|
66
|
-
province_state = location["province_state"]
|
|
67
|
-
city = location['city']
|
|
68
|
-
result.concat(generate_construction_cost_database_for_city(city, province_state))
|
|
69
|
-
end
|
|
70
|
-
return result
|
|
71
|
-
end
|
|
130
|
+
dlat_rad = (loc2[0] - loc1[0]) * rad_per_deg # Delta, converted to rad
|
|
131
|
+
dlon_rad = (loc2[1] - loc1[1]) * rad_per_deg
|
|
72
132
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
puts "Costing for: #{province_state},#{city}"
|
|
76
|
-
@costing_database["raw"]['constructions_opaque'].each do |construction|
|
|
77
|
-
cost_construction(construction, {"province_state" => province_state, "city" => city}, 'opaque')
|
|
78
|
-
end
|
|
79
|
-
@costing_database["raw"]['constructions_glazing'].each do |construction|
|
|
80
|
-
cost_construction(construction, {"province_state" => province_state, "city" => city}, 'glazing')
|
|
81
|
-
end
|
|
82
|
-
puts "#{@costing_database['constructions_costs'].size} Costed Constructions for #{province_state},#{city}."
|
|
83
|
-
return @costing_database['constructions_costs']
|
|
84
|
-
end
|
|
133
|
+
lat1_rad, lon1_rad = loc1.map { |i| i * rad_per_deg }
|
|
134
|
+
lat2_rad, lon2_rad = loc2.map { |i| i * rad_per_deg }
|
|
85
135
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
envelope_costing: true,
|
|
90
|
-
lighting_costing: true,
|
|
91
|
-
boilers_costing: true,
|
|
92
|
-
chillers_costing: true,
|
|
93
|
-
cooling_towers_costing: true,
|
|
94
|
-
shw_costing: true,
|
|
95
|
-
ventilation_costing: true,
|
|
96
|
-
zone_system_costing: true,
|
|
97
|
-
renewables_costing: true,
|
|
98
|
-
template_type: nil
|
|
99
|
-
)
|
|
100
|
-
# Create a Hash to collect costing data.
|
|
101
|
-
@costing_report = {}
|
|
102
|
-
|
|
103
|
-
#Use closest city.
|
|
104
|
-
closest_loc = get_closest_cost_location(model.getWeatherFile.latitude, model.getWeatherFile.longitude)
|
|
105
|
-
@costing_report['city'] = closest_loc['city']
|
|
106
|
-
@costing_report['province_state'] = closest_loc['province_state']
|
|
107
|
-
|
|
108
|
-
# Create array to collect costed item information. First element is the costing location.
|
|
109
|
-
@cost_items = {
|
|
110
|
-
'City' => closest_loc['city'],
|
|
111
|
-
'Province' => closest_loc['province_state'],
|
|
112
|
-
'Items' => []
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
# Create a Hash in the hash for categories of costing.
|
|
116
|
-
@costing_report['envelope'] = {}
|
|
117
|
-
@costing_report['lighting'] = {}
|
|
118
|
-
@costing_report['lighting']['daylighting_sensor_control'] = []
|
|
119
|
-
@costing_report['lighting']['led_lighting'] = []
|
|
120
|
-
@costing_report['heating_and_cooling'] = {}
|
|
121
|
-
@costing_report['heating_and_cooling']['plant_equipment'] = []
|
|
122
|
-
@costing_report['heating_and_cooling']['zonal_systems'] = []
|
|
123
|
-
@costing_report['shw'] = {}
|
|
124
|
-
@costing_report['ventilation'] = {}
|
|
125
|
-
@costing_report['renewables'] = {}
|
|
126
|
-
@costing_report['renewables']['pv'] = []
|
|
127
|
-
@costing_report['totals'] = {}
|
|
128
|
-
|
|
129
|
-
# Check to see if standards building type and the number of stories has been defined. The former may be omitted in the future.
|
|
130
|
-
if model.getBuilding.standardsBuildingType.empty? or model.getBuilding.standardsNumberOfAboveGroundStories.empty?
|
|
131
|
-
raise("Building information is not complete, please ensure that the standardsBuildingType and standardsNumberOfAboveGroundStories are entered in the model. ")
|
|
136
|
+
a = Math.sin(dlat_rad / 2) ** 2 + Math.cos(lat1_rad) * Math.cos(lat2_rad) * Math.sin(dlon_rad / 2) ** 2
|
|
137
|
+
c = 2 * Math::atan2(Math::sqrt(a), Math::sqrt(1 - a))
|
|
138
|
+
rm * c # Delta in meters
|
|
132
139
|
end
|
|
133
140
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
shwCost = shw_costing ? self.shw_costing(model, prototype_creator) : 0.0
|
|
143
|
-
ventCost = ventilation_costing ? self.ventilation_costing(model, prototype_creator,template_type, mech_room, cond_spaces) : 0.0
|
|
144
|
-
zonalSystemCost = zone_system_costing ? self.zonalsys_costing(model, prototype_creator, mech_room, cond_spaces) : 0.0
|
|
145
|
-
pvGroundCost = renewables_costing ? self.cost_audit_pv_ground(model, prototype_creator) : 0.0
|
|
146
|
-
thermalBridgingCost = 0.0
|
|
147
|
-
|
|
148
|
-
@costing_report["totals"] = {
|
|
149
|
-
'envelope' => envCost.round(0),
|
|
150
|
-
'thermal_bridging' => thermalBridgingCost.round(0),
|
|
151
|
-
'lighting' => lgtCost.round(0),
|
|
152
|
-
'heating_and_cooling' => (boilerCost + chillerCost + coolingTowerCost + zonalSystemCost).round(0),
|
|
153
|
-
'shw' => shwCost.round(0),
|
|
154
|
-
'ventilation' => ventCost.round(0),
|
|
155
|
-
'renewables' => pvGroundCost.round(0),
|
|
156
|
-
'grand_total' => (envCost + thermalBridgingCost + lgtCost + boilerCost + chillerCost + coolingTowerCost +
|
|
157
|
-
shwCost + ventCost + zonalSystemCost + pvGroundCost).round(0)
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
return @costing_report, @cost_items
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
def get_regional_cost_factors(provinceState, city, material)
|
|
164
|
-
@costing_database['localization_factors'].select { |code|
|
|
165
|
-
code['province_state'] == provinceState && code['city'] == city }.each do |code|
|
|
166
|
-
prefix_id = material['id'][0..1]
|
|
167
|
-
prefix_stored = code['code_prefix']
|
|
168
|
-
if prefix_id == prefix_stored
|
|
169
|
-
return code['material'], code['installation'], code['total']
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
end
|
|
173
|
-
error = [material, "Could not find regional adjustment factor for material used in #{city}, #{provinceState}."]
|
|
174
|
-
@costing_database['db_errors'] << error unless @costing_database['db_errors'].include?(error)
|
|
175
|
-
return 100.0, 100.0, 100.0
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
# Interpolate array of hashes that contain 2 values (key=rsi, data=cost)
|
|
179
|
-
def interpolate(x_y_array:, x2:, exterpolate_percentage_range: 30.0)
|
|
180
|
-
ratio_range = exterpolate_percentage_range / 100.0
|
|
181
|
-
array = x_y_array.uniq.sort { |a, b| a[0] <=> b[0] }
|
|
182
|
-
#if there is only one...return what you got.
|
|
183
|
-
if array.size == 1
|
|
184
|
-
return array.first[1].to_f
|
|
185
|
-
end
|
|
186
|
-
# Check if value x2 is within range of array for interpolation
|
|
187
|
-
# Extrapolate when x2 is out-of-range by +/- 10% of end values.
|
|
188
|
-
if array.empty? || x2 < ((1.0 - ratio_range) * array.first[0].to_f) || x2 > ((1.0 + ratio_range) * array.last[0].to_f)
|
|
189
|
-
return nil
|
|
190
|
-
elsif x2 < array.first[0].to_f
|
|
191
|
-
# Extrapolate down using first and second cost value to this out-of-range input
|
|
192
|
-
x_array = [array[0][0].to_f, array[1][0].to_f]
|
|
193
|
-
y_array = [array[0][1].to_f, array[1][1].to_f]
|
|
194
|
-
linear_model = SimpleLinearRegression.new(x_array, y_array)
|
|
195
|
-
y2 = linear_model.y_intercept + linear_model.slope * x2
|
|
196
|
-
return y2
|
|
197
|
-
elsif x2 > array.last[0].to_f
|
|
198
|
-
# Extrapolate up using second to last and last cost value to this out-of-range input
|
|
199
|
-
x_array = [array[-2][0].to_f, array[-1][0].to_f]
|
|
200
|
-
y_array = [array[-2][1].to_f, array[-1][1].to_f]
|
|
201
|
-
linear_model = SimpleLinearRegression.new(x_array, y_array)
|
|
202
|
-
y2 = linear_model.y_intercept + linear_model.slope * x2
|
|
203
|
-
return y2
|
|
204
|
-
else
|
|
205
|
-
array.each_index do |counter|
|
|
206
|
-
|
|
207
|
-
# skip last value.
|
|
208
|
-
next if array[counter] == array.last
|
|
209
|
-
|
|
210
|
-
x0 = array[counter][0]
|
|
211
|
-
y0 = array[counter][1]
|
|
212
|
-
x1 = array[counter + 1][0]
|
|
213
|
-
y1 = array[counter + 1][1]
|
|
214
|
-
|
|
215
|
-
# skip to next if x2 is not between x0 and x1
|
|
216
|
-
next if x2 < x0 || x2 > x1
|
|
217
|
-
|
|
218
|
-
# Do interpolation
|
|
219
|
-
y2 = y0 # just in-case x0, x1 and x2 are identical!
|
|
220
|
-
if (x1 - x0) > 0.0
|
|
221
|
-
y2 = y0.to_f + ((y1 - y0).to_f * (x2 - x0).to_f / (x1 - x0).to_f)
|
|
141
|
+
def get_closest_cost_location(lat, long)
|
|
142
|
+
dist = 1000000000000000000000.0
|
|
143
|
+
closest_loc = nil
|
|
144
|
+
# province_state city latitude longitude source
|
|
145
|
+
@costing_database['raw']['locations'].each do |location|
|
|
146
|
+
if distance([lat, long], [location['latitude'].to_f, location['longitude'].to_f]) < dist
|
|
147
|
+
closest_loc = location
|
|
148
|
+
dist = distance([lat, long], [location['latitude'].to_f, location['longitude'].to_f])
|
|
222
149
|
end
|
|
223
|
-
return y2
|
|
224
150
|
end
|
|
151
|
+
return closest_loc
|
|
225
152
|
end
|
|
226
|
-
end
|
|
227
153
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
rkm = 6371 # Earth radius in kilometers
|
|
232
|
-
rm = rkm * 1000 # Radius in meters
|
|
154
|
+
def read_mech_sizing()
|
|
155
|
+
return JSON.parse(File.read(Paths::MECH_SIZING_PATH))
|
|
156
|
+
end
|
|
233
157
|
|
|
234
|
-
|
|
235
|
-
|
|
158
|
+
# This adds costed items to the array of cousted items which end up in
|
|
159
|
+
# btap_items.json. Note that the array this method uses is created in the
|
|
160
|
+
# cost_audit_all method. The array is created with an initial element that
|
|
161
|
+
# contains the city and province whose localiazation factors are used for
|
|
162
|
+
# costing.
|
|
163
|
+
# @param id [String] The costing database id for the item being costed.
|
|
164
|
+
# @param quantity [Float] The total amount of the item being costed in
|
|
165
|
+
# whatever units the item is costed in. This should include all multiplier
|
|
166
|
+
# used to determine this cost (e.g. such as thermal_zone multipliers).
|
|
167
|
+
# As an example, if 32 ft. of wire were required for a piece of equipment
|
|
168
|
+
# used in a thermal zone with a multiplier of 10, the quantity would be
|
|
169
|
+
# 3.2 (32 ft. * 10 / 100 since wire is costed per 100 ft.).
|
|
170
|
+
#
|
|
171
|
+
# @param material_mult [Float] The multiplier used to estimate the cost
|
|
172
|
+
# of an item from the base cost. For example, high efficiency SHW tanks
|
|
173
|
+
# are estimated to cost 30% higher than regular SHW tanks so the cost of
|
|
174
|
+
# these tanks are calculated by the cost * 1.3. Thus, the material_mult
|
|
175
|
+
# for high efficiency tanks would be 1.3. This is defauted to 1.0 if it is
|
|
176
|
+
# not provided.
|
|
177
|
+
#
|
|
178
|
+
# @param labour_mult [Float] Similar to material_mult only applied to labour
|
|
179
|
+
# costs. The labour_mult can be different than the material_mult. This is
|
|
180
|
+
# defaulted to 1.0 if it is not provided. equipment_mult: (float) Similar
|
|
181
|
+
# to material_mult and labour_mult only for equipment. This will always be
|
|
182
|
+
# 1.0 until equipment costs are supported.
|
|
183
|
+
#
|
|
184
|
+
# @param tags [Array[String]] This is an array which links the costed
|
|
185
|
+
# item to a component of the model that is being costed. For example,
|
|
186
|
+
# a material_id related to a boiler pump might have tags like ["boiler",
|
|
187
|
+
# "pump"].
|
|
188
|
+
def add_costed_item(material_id:, quantity:, material_mult: 1.0, labour_mult: 1.0, equip_mult: 1.0, tags: [])
|
|
189
|
+
|
|
190
|
+
# Do some error handling for the tags argument.
|
|
191
|
+
tags_out = [tags] if tags.kind_of?(String)
|
|
192
|
+
tags_out = tags if tags.kind_of?(Array)
|
|
193
|
+
|
|
194
|
+
# Validate the type of the arguments.
|
|
195
|
+
if (tags_out.kind_of?(Array) == false)
|
|
196
|
+
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.")
|
|
197
|
+
end
|
|
236
198
|
|
|
237
|
-
|
|
238
|
-
|
|
199
|
+
if (material_id.kind_of?(String) == false)
|
|
200
|
+
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.")
|
|
201
|
+
end
|
|
239
202
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
end
|
|
203
|
+
if (quantity.kind_of?(Float) == false)
|
|
204
|
+
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.")
|
|
205
|
+
end
|
|
244
206
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
closest_loc = nil
|
|
248
|
-
# province_state city latitude longitude source
|
|
249
|
-
@costing_database['raw']['locations'].each do |location|
|
|
250
|
-
if distance([lat, long], [location['latitude'].to_f, location['longitude'].to_f]) < dist
|
|
251
|
-
closest_loc = location
|
|
252
|
-
dist = distance([lat, long], [location['latitude'].to_f, location['longitude'].to_f])
|
|
207
|
+
if (material_mult.kind_of?(Float) == false)
|
|
208
|
+
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.")
|
|
253
209
|
end
|
|
254
|
-
end
|
|
255
|
-
return closest_loc
|
|
256
|
-
end
|
|
257
210
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
# Note that the proper abbreviation for Quebec is QC not PQ. However, we've used PQ in openstudio-standards!
|
|
262
|
-
Hash provAbbrev = {"AB" => "ALBERTA",
|
|
263
|
-
"BC" => "BRITISH COLUMBIA",
|
|
264
|
-
"MB" => "MANITOBA",
|
|
265
|
-
"NB" => "NEW BRUNSWICK",
|
|
266
|
-
"NL" => "NEWFOUNDLAND AND LABRADOR",
|
|
267
|
-
"NT" => "NORTHWEST TERRITORIES",
|
|
268
|
-
"NS" => "NOVA SCOTIA",
|
|
269
|
-
"NU" => "NUNAVUT",
|
|
270
|
-
"ON" => "ONTARIO",
|
|
271
|
-
"PE" => "PRINCE EDWARD ISLAND",
|
|
272
|
-
"PQ" => "QUEBEC",
|
|
273
|
-
"SK" => "SASKATCHEWAN",
|
|
274
|
-
"YT" => "YUKON"
|
|
275
|
-
}
|
|
276
|
-
return provAbbrev[abbrev]
|
|
277
|
-
end
|
|
211
|
+
if (labour_mult.kind_of?(Float) == false)
|
|
212
|
+
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.")
|
|
213
|
+
end
|
|
278
214
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
end
|
|
215
|
+
if (equip_mult.kind_of?(Float) == false)
|
|
216
|
+
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.")
|
|
217
|
+
end
|
|
283
218
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
# 100 ft.).
|
|
294
|
-
# material_mult: (float) The multiplier used to estimate the cost of an item from the base cost. For example, high
|
|
295
|
-
# efficiency SHW tanks are estimated to cost 30% higher than regular SHW tanks so the cost of these
|
|
296
|
-
# tanks are calculated by the cost * 1.3. Thus, the material_mult for high efficiency tanks
|
|
297
|
-
# would be 1.3. This is defauted to 1.0 if it is not provided.
|
|
298
|
-
# labour_mult: (float) Similar to material_mult only applied to labour costs. The labour_mult can be different than
|
|
299
|
-
# the material_mult. This is defaulted to 1.0 if it is not provided.
|
|
300
|
-
# equipment_mult: (float) Similar to material_mult and labour_mult only for equipment. This will always be 1.0 until
|
|
301
|
-
# equipment costs are supported.
|
|
302
|
-
# tags: (array of strings) This is an array which links the costed item to a component of the model that is being
|
|
303
|
-
# costed. For example, a material_id related to a boiler pump might have tags like ["boiler", "pump"].
|
|
304
|
-
#
|
|
305
|
-
def add_costed_item(material_id:, quantity:, material_mult: 1.0, labour_mult: 1.0, equip_mult: 1.0, tags: [])
|
|
306
|
-
# Do some error handling for the tags argument
|
|
307
|
-
tags_out = [tags] if tags.kind_of?(String)
|
|
308
|
-
tags_out = tags if tags.kind_of?(Array)
|
|
309
|
-
|
|
310
|
-
# Validate the type of the arguments.
|
|
311
|
-
if (tags_out.kind_of?(Array) == false)
|
|
312
|
-
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.")
|
|
219
|
+
# Add the costed item to the output output hash.
|
|
220
|
+
@cost_items['Items'] << {
|
|
221
|
+
'id' => material_id,
|
|
222
|
+
'quantity' => quantity,
|
|
223
|
+
'material_mult' => material_mult,
|
|
224
|
+
'labour_mult' => labour_mult,
|
|
225
|
+
'equipment_mult' => equip_mult,
|
|
226
|
+
'tags' => tags_out
|
|
227
|
+
}
|
|
313
228
|
end
|
|
314
229
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
230
|
+
# This method takes the list of costed items in the building generated with
|
|
231
|
+
# the help of the above add_costed_item method and finds the costs for the
|
|
232
|
+
# list of items.
|
|
233
|
+
# @param btap_items [Array[Hash]] This array contains all the items that
|
|
234
|
+
# must be costed. The first element of the array is:
|
|
235
|
+
# {
|
|
236
|
+
# City: [String] City used for cost lacalization factor
|
|
237
|
+
# Province: [String] Province used for cost localization factor
|
|
238
|
+
# }
|
|
239
|
+
# The remaining arrays look like:
|
|
240
|
+
# {
|
|
241
|
+
# id: [String] ID of the coested item in question.
|
|
242
|
+
# quantity: [Float] Amount of costed item (should include all
|
|
243
|
+
# multipliers except localization factors, material_mult,
|
|
244
|
+
# labour_mult, equipment_mult).
|
|
245
|
+
#
|
|
246
|
+
# material_mult: [Float] Material multiplier from cost spreadsheet used
|
|
247
|
+
# mainly for higher performance equipment (for example, regular and
|
|
248
|
+
# high performance boilers share the same id but high performance
|
|
249
|
+
# boilers have a material_mult of around 1.3-that is they are
|
|
250
|
+
# estimated to be 1.3 times as expensive as regular boilers).
|
|
251
|
+
#
|
|
252
|
+
# labour_mult: [Float] Same idea as material_mult only for labour
|
|
253
|
+
# (often this will be 1.0 even if material_mult is something else).
|
|
254
|
+
#
|
|
255
|
+
# equipment_mult: [Float] Same idea as labour_mult only for equipment.
|
|
256
|
+
# It will always be 1.0 until equipment costs are implemented in
|
|
257
|
+
# costing.
|
|
258
|
+
#
|
|
259
|
+
# tags: [Array[String]] An array of strings used to define what
|
|
260
|
+
# part of the building is being costed (e.g. an component for
|
|
261
|
+
# a ccashp might have these tags: "Ventilation", "CCASHP",
|
|
262
|
+
# "ccashp_condensor")
|
|
263
|
+
# }
|
|
264
|
+
#
|
|
265
|
+
# @param custom_costing [Array[Hash]] A custom costing database if you do
|
|
266
|
+
# not want to use the default one. This must have the same format as that
|
|
267
|
+
# found by @costing_database['costs']
|
|
268
|
+
#
|
|
269
|
+
# @param custCity [String] A custom cost localization city if you do not
|
|
270
|
+
# want to use the one in the first item in the btap_itmes hash.
|
|
271
|
+
#
|
|
272
|
+
# @param custProvince [String] A custom cost localization province if you do
|
|
273
|
+
# not want to use the one in the first item in the btap_items hash.
|
|
274
|
+
#
|
|
275
|
+
# @return [Hash] A hash containing these summary costs:
|
|
276
|
+
# {
|
|
277
|
+
# envelope: [Float] Building envelope costs (to 2 decimal places).
|
|
278
|
+
# lighting: [Float] Ligting costs (to 2 decimal places).
|
|
279
|
+
# heating_and_cooling: [Float] Heating and cooling costs (not related to
|
|
280
|
+
# ventilation) (to 2 decimal places).
|
|
281
|
+
#
|
|
282
|
+
# shw: [Float] Service hot water costs (to 2 decimal places).
|
|
283
|
+
# ventilation: [Float] Ventilation (including ventilation air heating
|
|
284
|
+
# and cooling) costs (to 2 decimal places).
|
|
285
|
+
#
|
|
286
|
+
# grand_total: [Float] Total costs (to 2 decimal places).
|
|
287
|
+
# }
|
|
288
|
+
def cost_list_items(btap_items:, custom_costing: nil, custCity: nil, custProvince: nil)
|
|
289
|
+
|
|
290
|
+
# Check if costing is for a custom city and province. If not use the city
|
|
291
|
+
# and province found in the first entry of the array of costed items.
|
|
292
|
+
if custCity.nil? || custProvince.nil?
|
|
293
|
+
costCity = btap_items['City'].to_s
|
|
294
|
+
costProvince = btap_items['Province'].to_s
|
|
295
|
+
else
|
|
296
|
+
costCity = custCity
|
|
297
|
+
costProvince = custProvince
|
|
298
|
+
end
|
|
318
299
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
300
|
+
totCost = 0.0
|
|
301
|
+
envCost = 0.0
|
|
302
|
+
lightCost = 0.0
|
|
303
|
+
heatCoolCost = 0.0
|
|
304
|
+
shwCost = 0.0
|
|
305
|
+
ventCost = 0.0
|
|
306
|
+
renewCost = 0.0
|
|
322
307
|
|
|
323
|
-
|
|
324
|
-
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.")
|
|
325
|
-
end
|
|
308
|
+
custom_costing.nil? ? costingDB = @costing_database['costs'] : costingDB = custom_costing
|
|
326
309
|
|
|
327
|
-
|
|
328
|
-
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.")
|
|
329
|
-
end
|
|
310
|
+
btap_items['Items'].each do |costing_item|
|
|
330
311
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
end
|
|
312
|
+
# Look for the costing information for the piece of equipment in the costing database.
|
|
313
|
+
costing_data = costingDB.detect {|data| data['id'].to_s.upcase == costing_item['id'].to_s.upcase}
|
|
334
314
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
'material_mult' => material_mult,
|
|
340
|
-
'labour_mult' => labour_mult,
|
|
341
|
-
'equipment_mult' => equip_mult,
|
|
342
|
-
'tags' => tags_out
|
|
343
|
-
}
|
|
344
|
-
end
|
|
315
|
+
# If no costing information is found then return an error.
|
|
316
|
+
if costing_data.nil?
|
|
317
|
+
raise "Error: no costing information available for material id #{costing_item['id']}!"
|
|
318
|
+
elsif costing_data['baseCosts']['materialOpCost'].nil? && costing_data['baseCosts']['laborOpCost'].nil?
|
|
345
319
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
# Province: (string) Province used for cost localization factor
|
|
353
|
-
# }
|
|
354
|
-
# The remaining arrays look like:
|
|
355
|
-
# {
|
|
356
|
-
# id: (string) ID of the coested item in question.
|
|
357
|
-
# quantity: (float) Amount of costed item (should include all multipliers except localization factors,
|
|
358
|
-
# material_mult, labour_mult, equipment_mult).
|
|
359
|
-
# material_mult: (float) Material multiplier from cost spreadsheet used mainly for higher performance equipment
|
|
360
|
-
# (for example, regular and high performance boilers share the same id but high performance
|
|
361
|
-
# boilers have a material_mult of around 1.3-that is they are estimated to be 1.3 times as
|
|
362
|
-
# expensive as regular boilers).
|
|
363
|
-
# labour_mult: (float) Same idea as material_mult only for labour (often this will be 1.0 even if material_mult
|
|
364
|
-
# is something else).
|
|
365
|
-
# equipment_mult: (float) Same idea as labour_mult only for equipment. It will always be 1.0 until equipment
|
|
366
|
-
# costs are implemented in costing.
|
|
367
|
-
# tags: (array of strings) An array of strings used to define what part of the building is being costed (e.g.
|
|
368
|
-
# an component for a ccashp might have these tags: "Ventilation", "CCASHP", "ccashp_condensor")
|
|
369
|
-
# }
|
|
370
|
-
# custom_costing: (array of hashes) A custom costing database if you do not want to use the default one. This must
|
|
371
|
-
# have the same format as that found by @costing_database['costs']
|
|
372
|
-
# custCity: (string) A custom cost localization city if you do not want to use the one in the first item in the
|
|
373
|
-
# btap_itmes hash.
|
|
374
|
-
# custProvince: (string) A custom cost localization province if you do not want to use the one in the first item in
|
|
375
|
-
# the btap_items hash.
|
|
376
|
-
#
|
|
377
|
-
# The output of the method is a hash containing these summary costs:
|
|
378
|
-
# costRetHash = {
|
|
379
|
-
# envelope: (float) Building envelope costs (to 2 decimal places).
|
|
380
|
-
# lighting: (float) Ligting costs (to 2 decimal places).
|
|
381
|
-
# heating_and_cooling: (float) Heating and cooling costs (not related to ventilation) (to 2 decimal places).
|
|
382
|
-
# shw: (float) Service hot water costs (to 2 decimal places).
|
|
383
|
-
# ventilation: (float) Ventilation (including ventilation air heating and cooling) costs (to 2 decimal places).
|
|
384
|
-
# grand_total: (float) Total costs (to 2 decimal places).
|
|
385
|
-
# }
|
|
386
|
-
#
|
|
387
|
-
def cost_list_items(btap_items:, custom_costing: nil, custCity: nil, custProvince: nil)
|
|
388
|
-
# Check if costing is for a custom city and province. If not use the city and province found in the first entry
|
|
389
|
-
# of the array of costed items.
|
|
390
|
-
if custCity.nil? || custProvince.nil?
|
|
391
|
-
costCity = btap_items['City'].to_s
|
|
392
|
-
costProvince = btap_items['Province'].to_s
|
|
393
|
-
else
|
|
394
|
-
costCity = custCity
|
|
395
|
-
costProvince = custProvince
|
|
396
|
-
end
|
|
320
|
+
# This is a stub for some work that needs to be done to account for
|
|
321
|
+
# equipment costing. For now this is zeroed out. A similar test is
|
|
322
|
+
# done on reading the data from the database and collected in the
|
|
323
|
+
# error file when the costing database is generated.
|
|
324
|
+
raise "Error: costing information for material id #{costing_item['id']} is nil. Please check costing data."
|
|
325
|
+
end
|
|
397
326
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
#
|
|
418
|
-
#
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
327
|
+
costing_data['baseCosts']['equipmentOpCost'].nil? ? equip_base_cost = 0.0 : equip_base_cost =
|
|
328
|
+
costing_data['baseCosts']['equipmentOpCost'].to_f
|
|
329
|
+
costing_data['baseCosts']['materialOpCost'].nil? ? mat_base_cost = 0.0 : mat_base_cost =
|
|
330
|
+
costing_data['baseCosts']['materialOpCost'].to_f
|
|
331
|
+
costing_data['baseCosts']['laborOpCost'].nil? ? lab_base_cost = 0.0 : lab_base_cost =
|
|
332
|
+
costing_data['baseCosts']['laborOpCost'].to_f
|
|
333
|
+
|
|
334
|
+
# The costs from the costing database are US national average costs
|
|
335
|
+
# (for placeholder costs) or whatever is in the 'province_state' and
|
|
336
|
+
# 'city' fields (for custom costs). These costs need to be adjusted
|
|
337
|
+
# to reflect the costs expected in the location of interest. The
|
|
338
|
+
# 'get_regional_cost_factors' method finds the appropriate cost
|
|
339
|
+
# adjustment factors.
|
|
340
|
+
mat_mult, inst_mult, eq_mult = get_regional_cost_factors(costProvince, costCity, costing_item)
|
|
341
|
+
|
|
342
|
+
if mat_mult.nil? || inst_mult.nil?
|
|
343
|
+
raise("Error: no localization information available for material id #{costing_item['material_id']}!")
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
# Get any associated material or labour multiplier for the equipment
|
|
347
|
+
# present in the 'materials_hvac' sheet in the costing spreadsheet.
|
|
348
|
+
costing_item['material_mult'].to_f == 0 ? mat_quant = 1.0 : mat_quant = costing_item['material_mult'].to_f
|
|
349
|
+
costing_item['labour_mult'].to_f == 0 ? lab_quant = 1.0 : lab_quant = costing_item['labour_mult'].to_f
|
|
350
|
+
costing_item['equipment_mult'].to_f == 0 || costing_item['equipment_mult'].nil? ? eq_quant = 1.0 :
|
|
351
|
+
eq_quant = costing_item['equipment_mult'].to_f
|
|
352
|
+
|
|
353
|
+
# Calculate the adjusted material and labour costs.
|
|
354
|
+
mat_cost = mat_base_cost * (mat_mult / 100.0) * mat_quant
|
|
355
|
+
lab_cost = lab_base_cost * (inst_mult / 100.0) * lab_quant
|
|
356
|
+
eq_cost = equip_base_cost * (eq_mult / 100.0) * eq_quant
|
|
357
|
+
|
|
358
|
+
# Calculate the total item cost.
|
|
359
|
+
item_cost = (mat_cost + lab_cost + eq_cost)*(costing_item["quantity"].to_f)
|
|
360
|
+
|
|
361
|
+
# Add cost to sub-type counters.
|
|
362
|
+
envCost += item_cost unless (costing_item['tags'].select { |data| data.to_s.upcase == "ENVELOPE" }).empty?
|
|
363
|
+
lightCost += item_cost unless (costing_item['tags'].select { |data| data.to_s.upcase == "LIGHTING" }).empty?
|
|
364
|
+
heatCoolCost += item_cost unless (costing_item['tags'].select { |data|
|
|
365
|
+
data.to_s.upcase == "HEATING_COOLING" }).empty?
|
|
366
|
+
shwCost += item_cost unless (costing_item['tags'].select { |data| data.to_s.upcase == "SHW" }).empty?
|
|
367
|
+
ventCost += item_cost unless (costing_item['tags'].select { |data|
|
|
368
|
+
data.to_s.upcase == "VENTILATION" }).empty?
|
|
369
|
+
renewCost += item_cost unless (costing_item['tags'].select { |data| data.to_s.upcase == "RENEWABLES" }).empty?
|
|
370
|
+
totCost += item_cost
|
|
433
371
|
end
|
|
434
|
-
# Get any associated material or labour multiplier for the equipment present in the 'materials_hvac' sheet in the
|
|
435
|
-
# costing spreadsheet.
|
|
436
|
-
costing_item['material_mult'].to_f == 0 ? mat_quant = 1.0 : mat_quant = costing_item['material_mult'].to_f
|
|
437
|
-
costing_item['labour_mult'].to_f == 0 ? lab_quant = 1.0 : lab_quant = costing_item['labour_mult'].to_f
|
|
438
|
-
costing_item['equipment_mult'].to_f == 0 || costing_item['equipment_mult'].nil? ? eq_quant = 1.0 : eq_quant = costing_item['equipment_mult'].to_f
|
|
439
|
-
# Calculate the adjusted material and labour costs.
|
|
440
|
-
mat_cost = mat_base_cost*(mat_mult/100.0)*mat_quant
|
|
441
|
-
lab_cost = lab_base_cost*(inst_mult/100.0)*lab_quant
|
|
442
|
-
eq_cost = equip_base_cost*(eq_mult/100.0)*eq_quant
|
|
443
|
-
# Calculate the total item cost.
|
|
444
|
-
item_cost = (mat_cost + lab_cost + eq_cost)*(costing_item["quantity"].to_f)
|
|
445
|
-
|
|
446
|
-
# Add cost to sub-type counters
|
|
447
|
-
envCost += item_cost unless (costing_item['tags'].select{|data| data.to_s.upcase == "ENVELOPE"}).empty?
|
|
448
|
-
lightCost += item_cost unless (costing_item['tags'].select{|data| data.to_s.upcase == "LIGHTING"}).empty?
|
|
449
|
-
heatCoolCost += item_cost unless (costing_item['tags'].select{|data| data.to_s.upcase == "HEATING_COOLING"}).empty?
|
|
450
|
-
shwCost += item_cost unless (costing_item['tags'].select{|data| data.to_s.upcase == "SHW"}).empty?
|
|
451
|
-
ventCost += item_cost unless (costing_item['tags'].select{|data| data.to_s.upcase == "VENTILATION"}).empty?
|
|
452
|
-
renewCost += item_cost unless (costing_item['tags'].select{|data| data.to_s.upcase == "RENEWABLES"}).empty?
|
|
453
|
-
totCost += item_cost
|
|
454
|
-
end
|
|
455
372
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
373
|
+
# Create and return hash containing costing results.
|
|
374
|
+
costRetHash = {
|
|
375
|
+
'envelope' => envCost.round(2),
|
|
376
|
+
'lighting' => lightCost.round(2),
|
|
377
|
+
'heating_and_cooling' => heatCoolCost.round(2),
|
|
378
|
+
'shw' => shwCost.round(2),
|
|
379
|
+
'ventilation' => ventCost.round(2),
|
|
380
|
+
'renewables' => renewCost.round(2),
|
|
381
|
+
'grand_total' => totCost.round(2)
|
|
382
|
+
}
|
|
383
|
+
return costRetHash
|
|
384
|
+
end
|
|
467
385
|
end
|
|
468
|
-
|
|
469
386
|
end
|