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,2656 +0,0 @@
|
|
|
1
|
-
class BTAPData
|
|
2
|
-
attr_accessor :osa_file
|
|
3
|
-
attr_accessor :osw_file
|
|
4
|
-
attr_accessor :osm_file
|
|
5
|
-
attr_accessor :sqlite_file
|
|
6
|
-
attr_accessor :btap_data
|
|
7
|
-
|
|
8
|
-
def initialize(
|
|
9
|
-
model:,
|
|
10
|
-
runner: nil,
|
|
11
|
-
cost_result:,
|
|
12
|
-
carbon_result:,
|
|
13
|
-
baseline_cost_equipment_total_cost_per_m_sq: -1.0,
|
|
14
|
-
baseline_cost_utility_neb_total_cost_per_m_sq: -1.0,
|
|
15
|
-
baseline_energy_eui_total_gj_per_m_sq: -1.0,
|
|
16
|
-
qaqc:,
|
|
17
|
-
npv_start_year: 2022,
|
|
18
|
-
npv_end_year: 2041,
|
|
19
|
-
npv_discount_rate: 0.03,
|
|
20
|
-
npv_discount_rate_carbon: 0.03,
|
|
21
|
-
oerd_utility_pricing: nil,
|
|
22
|
-
utility_pricing_year: 2020)
|
|
23
|
-
|
|
24
|
-
@model = model
|
|
25
|
-
@error_warning = []
|
|
26
|
-
# sets sql file.
|
|
27
|
-
set_sql_file(model.sqlFile)
|
|
28
|
-
@standard = Standard.build('NECB2011')
|
|
29
|
-
@standards_data = @standard.load_standards_database_new()
|
|
30
|
-
@btap_data = {}
|
|
31
|
-
@btap_results_version = 1.00
|
|
32
|
-
@neb_prices_csv_file_name = File.join(__dir__, 'neb_end_use_prices.csv')
|
|
33
|
-
@neb_prices_csv_file_name = File.join(__dir__, 'utility_pricing_2025-02-20.csv') if oerd_utility_pricing.to_bool
|
|
34
|
-
@necb_reference_runs_csv_file_name = File.join(__dir__, 'necb_reference_runs.csv')
|
|
35
|
-
@eccc_ghg_electricity = File.join(__dir__, 'eccc_electric_grid_intensity_20250311.csv') #REF (Retrieved March 11, 2025) for ECCC's emissions factors for the current and future years for GHG "without Biomass and RNG CO2 emissions": https://data-donnees.az.ec.gc.ca/data/substances/monitor/canada-s-greenhouse-gas-emissions-projections/Current-Projections-Actuelles/Energy-Energie/AM%20Scenario%20AMS/Grid-O%26G-Intensities-Intensites-Reseau-Delectricite-P%26G?lang=en
|
|
36
|
-
@nir_ghg_gas = File.join(__dir__, 'nir_gas_grid_intensity_20250311.csv') #REF (Retrieved March 11, 2025) for Year 2022 of "Table A6.1–1 CO2 Emission Factors for Marketable Natural Gas" of National Inventory Report: https://data-donnees.az.ec.gc.ca/api/file?path=%2Fsubstances%2Fmonitor%2Fcanada-s-official-greenhouse-gas-inventory%2FD-Emission-Factors%2FEN_Annex6_Emission_Factors.pdf
|
|
37
|
-
|
|
38
|
-
# Conditioned floor area is used so much. May as well make it a object variable.
|
|
39
|
-
# setup the queries
|
|
40
|
-
command = "SELECT Value
|
|
41
|
-
FROM TabularDataWithStrings
|
|
42
|
-
WHERE ReportName='AnnualBuildingUtilityPerformanceSummary'
|
|
43
|
-
AND ReportForString='Entire Facility'
|
|
44
|
-
AND TableName='Building Area'
|
|
45
|
-
AND RowName = 'Net Conditioned Building Area'
|
|
46
|
-
AND ColumnName='Area'"
|
|
47
|
-
area = @sqlite_file.get.execAndReturnFirstDouble(command)
|
|
48
|
-
# make sure all the data are available
|
|
49
|
-
if area.empty?
|
|
50
|
-
@conditioned_floor_area_m_sq = 0.0
|
|
51
|
-
else
|
|
52
|
-
@conditioned_floor_area_m_sq = area.get
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
@btap_data['simulation_btap_data_version'] = '0.1'
|
|
57
|
-
# @btap_data["simulation_openstudio_version"] = open("| \"#{OpenStudio.getOpenStudioCLI}\" openstudio_version").read().strip
|
|
58
|
-
# @btap_data["simulation_energyplus_version"] = open("| \"#{OpenStudio.getOpenStudioCLI}\" energyplus_version").read().strip
|
|
59
|
-
@btap_data['simulation_os_standards_revision'] = OpenstudioStandards.git_revision
|
|
60
|
-
@btap_data['simulation_os_standards_version'] = OpenstudioStandards::VERSION
|
|
61
|
-
@btap_data['simulation_date'] = Time.now
|
|
62
|
-
@btap_data.merge!(building_data)
|
|
63
|
-
@btap_data.merge!(building_costing_data(cost_result)) unless cost_result.nil?
|
|
64
|
-
@btap_data.merge!(climate_data)
|
|
65
|
-
@btap_data.merge!(service_water_heating_data)
|
|
66
|
-
@btap_data.merge!(energy_eui_data(model))
|
|
67
|
-
@btap_data.merge!(energy_peak_data)
|
|
68
|
-
@btap_data.merge!(utility(model: model, utility_pricing_year: utility_pricing_year))
|
|
69
|
-
@btap_data.merge!(unmet_hours(model))
|
|
70
|
-
@btap_data.merge!outdoor_air_data(model)
|
|
71
|
-
|
|
72
|
-
# Data in tables...
|
|
73
|
-
@btap_data.merge!('measures_data_table' => measures_data_table(runner)) unless runner.nil?
|
|
74
|
-
@btap_data.merge!('envelope_exterior_surface_table' => envelope_exterior_surface_table)
|
|
75
|
-
@btap_data.merge!('space_table' => space_table(model, cost_result))
|
|
76
|
-
@btap_data.merge!('space_type_table' => space_type_table(model))
|
|
77
|
-
# This does not work with the new VRF or CCASHP systems. Commenting it for now.
|
|
78
|
-
# @btap_data.merge!({'zone_table' => thermal_zones_table(model, cost_result)['table']})
|
|
79
|
-
@btap_data.merge!('zone_equip_table' => thermal_zones_equipment_table(model))
|
|
80
|
-
# This does not work with the new VRF or CCASHP systems. Commenting it for now.
|
|
81
|
-
# @btap_data.merge!({'air_loop_table' => air_loops_table(model, cost_result)})
|
|
82
|
-
# @btap_data.merge!({'sql_raw_data' => sql_data_tables(model)})
|
|
83
|
-
@btap_data.merge!('eplusout_err_table' => eplusout_err_table(model))
|
|
84
|
-
|
|
85
|
-
# Remainder of costing data in separate tables:
|
|
86
|
-
@btap_data.merge!('envelope_construction_cost_table' => cost_result['envelope']['construction_costs']) unless cost_result.nil?
|
|
87
|
-
@btap_data.merge!('lighting_fixture_cost_table' => cost_result['lighting']['fixture_report']) unless cost_result.nil?
|
|
88
|
-
ideal_air = true
|
|
89
|
-
model.getThermalZones.each do |zone|
|
|
90
|
-
ideal_air = false if zone.useIdealAirLoads == false
|
|
91
|
-
end
|
|
92
|
-
unless ideal_air
|
|
93
|
-
@btap_data.merge!('h_and_c_plant_equipment_cost_table' => cost_result['heating_and_cooling']['plant_equipment']) unless cost_result.nil?
|
|
94
|
-
@btap_data.merge!('h_and_c_plant_zonal_systems_cost_table' => cost_result['heating_and_cooling']['zonal_systems']) unless cost_result.nil?
|
|
95
|
-
# This does not work with the new VRF or CCASHP systems. Commenting it for now.
|
|
96
|
-
# @btap_data.merge!('system_coils_cost_table' => coil_cost_table(cost_result))
|
|
97
|
-
# This does not work with the new VRF or CCASHP systems. Commenting it for now.
|
|
98
|
-
# @btap_data.merge!('terminal_VAV_cost_table' => terminal_VAV_cost_table(cost_result))
|
|
99
|
-
# This does not work with the new VRF or CCASHP systems. Commenting it for now.
|
|
100
|
-
# @btap_data.merge!('trunk_ducts_cost_table' => trunk_ducts_cost_table(cost_result))
|
|
101
|
-
end
|
|
102
|
-
# calculate energy demands and peak loads calculations as per PHIUS and NECB and compare them
|
|
103
|
-
phius_performance_indicators(model)
|
|
104
|
-
# The below method calculates energy performance indicators (i.e. TEDI and MEUI) as per BC Energy Step Code
|
|
105
|
-
bc_energy_step_code_performance_indicators
|
|
106
|
-
# calculates annual peak electricity cost (dollar)
|
|
107
|
-
annual_peak_kW_per_m_sq = oerd_electricity_cost(model: model, utility_pricing_year: utility_pricing_year, oerd_utility_pricing: oerd_utility_pricing)
|
|
108
|
-
# calculate net present value
|
|
109
|
-
net_present_value(npv_start_year: npv_start_year, npv_end_year: npv_end_year, npv_discount_rate: npv_discount_rate, npv_discount_rate_carbon: npv_discount_rate_carbon, oerd_utility_pricing: oerd_utility_pricing, annual_peak_kW_per_m_sq: annual_peak_kW_per_m_sq) unless cost_result.nil?
|
|
110
|
-
|
|
111
|
-
measure_metrics(qaqc)
|
|
112
|
-
@btap_data
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
# Oct-2019 JTB: This function must be passed a hash and will flatten mixtures of hashes
|
|
116
|
-
# and arrays of hashes. Embedded arrays are enumerated (starting at 1).
|
|
117
|
-
def flatten_mix(hash)
|
|
118
|
-
hash.each_with_object({}) do |(k, v), h|
|
|
119
|
-
if v.is_a?(Hash)
|
|
120
|
-
flatten_mix(v).map do |h_k, h_v|
|
|
121
|
-
h["#{k}.#{h_k}".to_sym] = h_v
|
|
122
|
-
end
|
|
123
|
-
elsif v.is_a?(Array)
|
|
124
|
-
v.map.with_index do |e, ndx|
|
|
125
|
-
if e.is_a?(Hash)
|
|
126
|
-
flatten_mix(e).map do |e_k, e_v|
|
|
127
|
-
h["#{k}.#{e_k}.#{ndx + 1}".to_sym] = e_v
|
|
128
|
-
end
|
|
129
|
-
# if there is another array within the array v, flatten more
|
|
130
|
-
# but this is as deep as we go with embedded arrays!
|
|
131
|
-
elsif e.is_a?(Array)
|
|
132
|
-
e.map.with_index do |e1, ndx1|
|
|
133
|
-
if e1.is_a?(Hash)
|
|
134
|
-
flatten_mix(e1).map do |e1_k, e1_v|
|
|
135
|
-
h["#{k}.#{e1_k}.#{ndx1 + 1}".to_sym] = e1_v
|
|
136
|
-
end
|
|
137
|
-
else
|
|
138
|
-
# Stop flattening here!
|
|
139
|
-
h[k] = v
|
|
140
|
-
end
|
|
141
|
-
end
|
|
142
|
-
else
|
|
143
|
-
h[k] = v
|
|
144
|
-
end
|
|
145
|
-
end
|
|
146
|
-
else
|
|
147
|
-
h[k] = v
|
|
148
|
-
end
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
# General Building Data that there is alway either zero of 1 of.
|
|
153
|
-
def building_data
|
|
154
|
-
# Store Building data.
|
|
155
|
-
building_data = {}
|
|
156
|
-
building_data['bldg_name'] = @model.building.get.name.get
|
|
157
|
-
building_data['bldg_conditioned_floor_area_m_sq'] = @conditioned_floor_area_m_sq
|
|
158
|
-
building_data['bldg_exterior_area_m_sq'] = @model.building.get.exteriorSurfaceArea # m_sq
|
|
159
|
-
building_data['bldg_volume_m_cu'] = @model.building.get.airVolume # m_cu
|
|
160
|
-
building_data['bldg_standards_template'] = @model.building.get.standardsTemplate.empty? ? nil : @model.building.get.standardsTemplate.get
|
|
161
|
-
building_data['bldg_standards_building_type'] = @model.building.get.standardsBuildingType.empty? ? nil : @model.building.get.standardsBuildingType.get
|
|
162
|
-
building_data['bldg_standards_number_of_stories'] = @model.building.get.standardsNumberOfStories.empty? ? nil : @model.building.get.standardsNumberOfStories.get
|
|
163
|
-
building_data['bldg_standards_number_of_above_ground_stories'] = @model.building.get.standardsNumberOfAboveGroundStories.empty? ? nil : @model.building.get.standardsNumberOfAboveGroundStories.get
|
|
164
|
-
building_data['bldg_standards_number_of_living_units'] = @model.building.get.standardsNumberOfLivingUnits.empty? ? nil : @model.building.get.standardsNumberOfAboveGroundStories.get
|
|
165
|
-
building_data['bldg_nominal_floor_to_ceiling_height'] = @model.building.get.nominalFloortoCeilingHeight.empty? ? nil : @model.building.get.nominalFloortoCeilingHeight.get
|
|
166
|
-
building_data['bldg_nominal_floor_to_floor_height'] = @model.building.get.nominalFloortoFloorHeight.empty? ? nil : @model.building.get.nominalFloortoFloorHeight.get
|
|
167
|
-
building_data['bldg_surface_to_volume_ratio'] = @model.building.get.exteriorSurfaceArea / @model.building.get.airVolume
|
|
168
|
-
building_data['bldg_fdwr'] = (BTAP::Geometry.get_fwdr(@model) * 100.0).round(1)
|
|
169
|
-
building_data['bldg_srr'] = (BTAP::Geometry.get_srr(@model) * 100.0).round(1)
|
|
170
|
-
|
|
171
|
-
return building_data
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
def building_costing_data(cost_result)
|
|
175
|
-
building_data = {}
|
|
176
|
-
building_data['cost_province_state'] = cost_result['province_state']
|
|
177
|
-
building_data['cost_city'] = cost_result['city']
|
|
178
|
-
building_data['cost_equipment_envelope_total_cost_per_m_sq'] = (cost_result['totals']['envelope']) / @conditioned_floor_area_m_sq
|
|
179
|
-
building_data['cost_equipment_thermal_bridging_total_cost_per_m_sq'] = (cost_result['totals']['thermal_bridging']) / @conditioned_floor_area_m_sq
|
|
180
|
-
building_data['cost_equipment_lighting_total_cost_per_m_sq'] = (cost_result['totals']['lighting']) / @conditioned_floor_area_m_sq
|
|
181
|
-
building_data['cost_equipment_heating_and_cooling_total_cost_per_m_sq'] = (cost_result['totals']['heating_and_cooling']) / @conditioned_floor_area_m_sq
|
|
182
|
-
building_data['cost_equipment_shw_total_cost_per_m_sq'] = (cost_result['totals']['shw']) / @conditioned_floor_area_m_sq
|
|
183
|
-
building_data['cost_equipment_ventilation_total_cost_per_m_sq'] = (cost_result['totals']['ventilation']) / @conditioned_floor_area_m_sq
|
|
184
|
-
building_data['cost_equipment_renewables_total_cost_per_m_sq'] = (cost_result['totals']['renewables']) / @conditioned_floor_area_m_sq
|
|
185
|
-
building_data['cost_equipment_total_cost_per_m_sq'] = (cost_result['totals']['grand_total']) / @conditioned_floor_area_m_sq
|
|
186
|
-
# building_data.merge!(cost_result['envelope'].select{|k,v| k!='construction_costs' && k!='total_envelope_cost'})
|
|
187
|
-
# building_data.merge!(cost_result['shw'].select{|k,v| k!='shw_total'})
|
|
188
|
-
# building_data.merge!(flatten_mix(cost_result['ventilation'].select{|k,v| k=='mech_to_roof'.to_sym}))
|
|
189
|
-
return building_data
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
def net_present_value(npv_start_year: 2022, npv_end_year: 2041, npv_discount_rate: 0.03, npv_discount_rate_carbon: 0.03, oerd_utility_pricing: false, annual_peak_kW_per_m_sq: nil)
|
|
193
|
-
|
|
194
|
-
# Find end year in the neb data
|
|
195
|
-
neb_header = CSV.read(@neb_prices_csv_file_name, headers: true).headers
|
|
196
|
-
neb_header.delete_if { |item| ["building_type", "province", "fuel_type"].include?(item) } # remove "building_type", "province", "fuel_type" from neb_header in order to have only years in neb_header
|
|
197
|
-
neb_header.delete_if { |item| ["rate_class", "units", "references", "links"].include?(item) } if oerd_utility_pricing # remove additional headers if the OERD data is used
|
|
198
|
-
neb_header.map(&:to_f) #convert years to float
|
|
199
|
-
year_max = neb_header.max
|
|
200
|
-
|
|
201
|
-
# Convert a string to a float
|
|
202
|
-
if npv_start_year.instance_of?(String) && npv_start_year != 'NECB_Default' && npv_start_year != 'none' && !npv_start_yeay.nil?
|
|
203
|
-
npv_start_year = npv_start_year.to_f
|
|
204
|
-
end
|
|
205
|
-
if npv_end_year.instance_of?(String) && npv_end_year != 'NECB_Default' && npv_end_year != 'none' && !npv_end_year.nil?
|
|
206
|
-
npv_end_year = npv_end_year.to_f
|
|
207
|
-
end
|
|
208
|
-
if npv_discount_rate.instance_of?(String) && npv_discount_rate != 'NECB_Default' && npv_discount_rate != 'none' && !npv_discount_rate.nil?
|
|
209
|
-
npv_discount_rate = npv_discount_rate.to_f
|
|
210
|
-
end
|
|
211
|
-
if npv_discount_rate_carbon.instance_of?(String) && npv_discount_rate_carbon != 'NECB_Default' && npv_discount_rate_carbon != 'none' && !npv_discount_rate_carbon.nil?
|
|
212
|
-
npv_discount_rate_carbon = npv_discount_rate_carbon.to_f
|
|
213
|
-
end
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
# Set default npv_start_year as 2022, npv_end_year as 2041, npv_discount_rate as 3%
|
|
217
|
-
if npv_start_year == 'NECB_Default' || npv_start_year.nil? || npv_start_year == 'none'
|
|
218
|
-
npv_start_year = 2022
|
|
219
|
-
end
|
|
220
|
-
if npv_end_year == 'NECB_Default' || npv_end_year.nil? || npv_end_year == 'none'
|
|
221
|
-
npv_end_year = 2041
|
|
222
|
-
end
|
|
223
|
-
if npv_discount_rate == 'NECB_Default' || npv_discount_rate.nil? || npv_discount_rate == 'none'
|
|
224
|
-
npv_discount_rate = 0.03
|
|
225
|
-
end
|
|
226
|
-
if npv_discount_rate_carbon == 'NECB_Default' || npv_discount_rate_carbon.nil? || npv_discount_rate_carbon == 'none'
|
|
227
|
-
npv_discount_rate_carbon = 0.03
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
# Set npv_end_year as year_max if users' input > neb's end year
|
|
231
|
-
if npv_end_year > year_max.to_f
|
|
232
|
-
npv_end_year = year_max.to_f
|
|
233
|
-
warn "WARNING: Your npv_end_year for the calculation of net present value is larger than that in Canada Energy Regulator (CER) (i.e. #{year_max}). So, npv_end_year has been reset as #{year_max}."
|
|
234
|
-
end
|
|
235
|
-
# puts "npv_start_year is #{npv_start_year}"
|
|
236
|
-
# puts "npv_end_year is #{npv_end_year}"
|
|
237
|
-
# puts "npv_discount_rate is #{npv_discount_rate}"
|
|
238
|
-
|
|
239
|
-
# Get energy end-use prices (CER data from https://apps.cer-rec.gc.ca/ftrppndc/dflt.aspx?GoCTemplateCulture=en-CA)
|
|
240
|
-
# @neb_prices_csv_file_name = "#{File.dirname(__FILE__)}/neb_end_use_prices.csv"
|
|
241
|
-
|
|
242
|
-
# Create a hash of the neb data.
|
|
243
|
-
neb_data = CSV.parse(File.read(@neb_prices_csv_file_name), headers: true, converters: :numeric).map(&:to_h)
|
|
244
|
-
|
|
245
|
-
# Create a hash of the ECCC's electricity emissions factors for the current and future years for GHG "without Biomass and RNG CO2 emissions".
|
|
246
|
-
eccc_ghg_elec_emission_factors = CSV.parse(File.read(@eccc_ghg_electricity), headers: true, converters: :numeric).map(&:to_h)
|
|
247
|
-
|
|
248
|
-
# Find which province the proposed building is located in
|
|
249
|
-
building_type = 'Commercial'
|
|
250
|
-
geography_data = climate_data
|
|
251
|
-
province_abbreviation = geography_data['location_state_province_region']
|
|
252
|
-
province = @standards_data['province_map'][province_abbreviation]
|
|
253
|
-
|
|
254
|
-
# Note: If there is on-site energy generation (e.g. PV), it should be considered in the calculation of EUI for the calculation of energy use cost and NPV.
|
|
255
|
-
# To do so, it has been assumed that on-site energy generation is only for electricity.
|
|
256
|
-
# Electricity EUI of a building is re-calculated for NPV. It will be: ['energy_eui_electricity_gj_per_m_sq' - ('total_site_eui_gj_per_m_sq' - 'net_site_eui_gj_per_m_sq')]
|
|
257
|
-
# Note that if there is no on-site energy generation, 'total_site_eui_gj_per_m_sq' and 'net_site_eui_gj_per_m_sq' will be equal.
|
|
258
|
-
# Note: 'total_site_eui_gj_per_m_sq' is the gross energy consumed by the building (REF: https://unmethours.com/question/25416/what-is-the-difference-between-site-energy-and-source-energy/)
|
|
259
|
-
# Note: 'net_site_eui_gj_per_m_sq' is the final energy consumed by the building after accounting for on-site energy generations (e.g. PV) (REF: https://unmethours.com/question/25416/what-is-the-difference-between-site-energy-and-source-energy/)
|
|
260
|
-
|
|
261
|
-
# Calculate npv of electricity
|
|
262
|
-
onsite_elec_generation = @btap_data['total_site_eui_gj_per_m_sq'] - @btap_data['net_site_eui_gj_per_m_sq']
|
|
263
|
-
if onsite_elec_generation > 0.0
|
|
264
|
-
eui_elec = @btap_data['energy_eui_electricity_gj_per_m_sq'] - onsite_elec_generation
|
|
265
|
-
else
|
|
266
|
-
eui_elec = @btap_data['energy_eui_electricity_gj_per_m_sq']
|
|
267
|
-
end
|
|
268
|
-
# Find the appropriate row for electricity GHG emmission factors
|
|
269
|
-
row_emm_factor = eccc_ghg_elec_emission_factors.detect do |data|
|
|
270
|
-
(data['province'] == province)
|
|
271
|
-
end
|
|
272
|
-
# Find the appropriate row for electricity pricing
|
|
273
|
-
row = neb_data.detect do |data|
|
|
274
|
-
(data['building_type'] == building_type) && (data['province'] == province) && (data['fuel_type'] == 'Electricity')
|
|
275
|
-
end
|
|
276
|
-
row_peak = nil
|
|
277
|
-
if oerd_utility_pricing && !annual_peak_kW_per_m_sq.nil?
|
|
278
|
-
row_peak = neb_data.detect do |data|
|
|
279
|
-
(data['building_type'] == building_type) && (data['province'] == province) && (data['fuel_type'] == 'Electricity Peak')
|
|
280
|
-
end
|
|
281
|
-
end
|
|
282
|
-
npv_elec = 0.0
|
|
283
|
-
year_index = 1.0
|
|
284
|
-
npv_elec_ghg = 0.0
|
|
285
|
-
npv_elec_peak = 0.0
|
|
286
|
-
emm_factor_conv = 3600.0 #GWH to GJ
|
|
287
|
-
if eui_elec > 0.0
|
|
288
|
-
for year in npv_start_year.to_int..npv_end_year.to_int
|
|
289
|
-
npv_elec += (eui_elec * row[year.to_s]) / (1+npv_discount_rate)**year_index
|
|
290
|
-
npv_elec_ghg += (((eui_elec * row_emm_factor[year.to_i.to_s].to_f) / emm_factor_conv) * get_national_ghg_cost(year: year.to_i)) / (1+npv_discount_rate_carbon)**year_index
|
|
291
|
-
npv_elec_peak += (annual_peak_kW_per_m_sq * row_peak[year.to_i.to_s]).to_f / (1+npv_discount_rate)**year_index unless row_peak.nil? || annual_peak_kW_per_m_sq.nil?
|
|
292
|
-
year_index += 1.0
|
|
293
|
-
end
|
|
294
|
-
end
|
|
295
|
-
|
|
296
|
-
# Calculate npv of natural gas
|
|
297
|
-
eui_ngas= @btap_data['energy_eui_natural_gas_gj_per_m_sq']
|
|
298
|
-
# Convert to tonnes of ghg/m^2
|
|
299
|
-
ghg_ngas = @btap_data['cost_utility_ghg_natural gas_kg_per_m_sq'].to_f/1000.0
|
|
300
|
-
row = neb_data.detect do |data|
|
|
301
|
-
(data['building_type'] == building_type) && (data['province'] == province) && (data['fuel_type'] == 'Natural Gas')
|
|
302
|
-
end
|
|
303
|
-
npv_ngas = 0.0
|
|
304
|
-
npv_ngas_ghg = 0.0
|
|
305
|
-
year_index = 1.0
|
|
306
|
-
for year in npv_start_year.to_int..npv_end_year.to_int
|
|
307
|
-
npv_ngas += (eui_ngas * row[year.to_s]) / (1+npv_discount_rate)**year_index
|
|
308
|
-
npv_ngas_ghg += (ghg_ngas * get_national_ghg_cost(year: year.to_i)) / (1+npv_discount_rate_carbon)**year_index
|
|
309
|
-
year_index += 1.0
|
|
310
|
-
end
|
|
311
|
-
# puts "npv_ngas is #{npv_ngas}"
|
|
312
|
-
|
|
313
|
-
# Calculate npv of oil
|
|
314
|
-
eui_oil= @btap_data['energy_eui_additional_fuel_gj_per_m_sq']
|
|
315
|
-
# Convert to tonnes of ghg/m^2
|
|
316
|
-
ghg_oil = @btap_data['cost_utility_ghg_oil_kg_per_m_sq'].to_f/1000.0
|
|
317
|
-
row = neb_data.detect do |data|
|
|
318
|
-
(data['building_type'] == building_type) && (data['province'] == province) && (data['fuel_type'] == 'Oil')
|
|
319
|
-
end
|
|
320
|
-
npv_oil = 0.0
|
|
321
|
-
npv_oil_ghg = 0.0
|
|
322
|
-
year_index = 1.0
|
|
323
|
-
for year in npv_start_year.to_int..npv_end_year.to_int
|
|
324
|
-
npv_oil += (eui_oil * row[year.to_s]) / (1+npv_discount_rate)**year_index
|
|
325
|
-
npv_oil_ghg += (ghg_oil * get_national_ghg_cost(year: year.to_i)) / (1+npv_discount_rate_carbon)**year_index
|
|
326
|
-
year_index += 1.0
|
|
327
|
-
end
|
|
328
|
-
# puts "npv_oil is #{npv_oil}"
|
|
329
|
-
|
|
330
|
-
# Calculate total npv
|
|
331
|
-
npv_total = @btap_data['cost_equipment_total_cost_per_m_sq'] + npv_elec + npv_ngas + npv_oil
|
|
332
|
-
npv_ghg_total = npv_elec_ghg + npv_ngas_ghg + npv_oil_ghg
|
|
333
|
-
npv_total_with_ghg_and_peak = npv_ghg_total + npv_total + npv_elec_peak
|
|
334
|
-
@btap_data.merge!('npv_total_per_m_sq' => npv_total)
|
|
335
|
-
@btap_data.merge!('npv_carbon_pricing_per_m_sq' => npv_ghg_total)
|
|
336
|
-
@btap_data.merge!('npv_peak_elec_per_m_sq' => npv_elec_peak)
|
|
337
|
-
@btap_data.merge!('npv_with_carbon_and_peak_pricing_total_per_m_sq' => npv_total_with_ghg_and_peak)
|
|
338
|
-
end
|
|
339
|
-
|
|
340
|
-
def envelope(model)
|
|
341
|
-
data = {}
|
|
342
|
-
# Get OSM surface information
|
|
343
|
-
surfaces = model.getSurfaces.sort
|
|
344
|
-
interior_surfaces = BTAP::Geometry::Surfaces.filter_by_boundary_condition(surfaces, ['Surface', 'Adiabatic'])
|
|
345
|
-
interior_floors = BTAP::Geometry::Surfaces.filter_by_surface_types(interior_surfaces, 'Floor')
|
|
346
|
-
outdoor_surfaces = BTAP::Geometry::Surfaces.filter_by_boundary_condition(surfaces, 'Outdoors')
|
|
347
|
-
outdoor_walls = BTAP::Geometry::Surfaces.filter_by_surface_types(outdoor_surfaces, 'Wall')
|
|
348
|
-
outdoor_roofs = BTAP::Geometry::Surfaces.filter_by_surface_types(outdoor_surfaces, 'RoofCeiling')
|
|
349
|
-
outdoor_floors = BTAP::Geometry::Surfaces.filter_by_surface_types(outdoor_surfaces, 'Floor')
|
|
350
|
-
outdoor_subsurfaces = outdoor_surfaces.flat_map(&:subSurfaces)
|
|
351
|
-
ground_surfaces = BTAP::Geometry::Surfaces.filter_by_boundary_condition(surfaces, ['Ground', 'Foundation'])
|
|
352
|
-
ground_walls = BTAP::Geometry::Surfaces.filter_by_surface_types(ground_surfaces, 'Wall')
|
|
353
|
-
ground_roofs = BTAP::Geometry::Surfaces.filter_by_surface_types(ground_surfaces, 'RoofCeiling')
|
|
354
|
-
ground_floors = BTAP::Geometry::Surfaces.filter_by_surface_types(ground_surfaces, 'Floor')
|
|
355
|
-
windows = BTAP::Geometry::Surfaces.filter_subsurfaces_by_types(outdoor_subsurfaces, ['FixedWindow', 'OperableWindow'])
|
|
356
|
-
skylights = BTAP::Geometry::Surfaces.filter_subsurfaces_by_types(outdoor_subsurfaces, ['Skylight', 'TubularDaylightDiffuser', 'TubularDaylightDome'])
|
|
357
|
-
doors = BTAP::Geometry::Surfaces.filter_subsurfaces_by_types(outdoor_subsurfaces, ['Door', 'GlassDoor'])
|
|
358
|
-
overhead_doors = BTAP::Geometry::Surfaces.filter_subsurfaces_by_types(outdoor_subsurfaces, ['OverheadDoor'])
|
|
359
|
-
|
|
360
|
-
# Get Areas
|
|
361
|
-
data['outdoor_walls_area_m_sq'] = outdoor_walls.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier }
|
|
362
|
-
data['outdoor_roofs_area_m_sq'] = outdoor_roofs.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier }
|
|
363
|
-
data['outdoor_floors_area_m_sq'] = outdoor_floors.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier }
|
|
364
|
-
data['ground_walls_area_m_sq'] = ground_walls.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier }
|
|
365
|
-
data['ground_roofs_area_m_sq'] = ground_roofs.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier }
|
|
366
|
-
data['ground_floors_area_m_sq'] = ground_floors.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier }
|
|
367
|
-
data['interior_floors_area_m_sq'] = interior_floors.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier }
|
|
368
|
-
|
|
369
|
-
# Subsurface areas
|
|
370
|
-
data['windows_area_m_sq'] = windows.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier * e.multiplier }
|
|
371
|
-
data['skylights_area_m_sq'] = skylights.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier * e.multiplier }
|
|
372
|
-
data['doors_area_m_sq'] = doors.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier * e.multiplier }
|
|
373
|
-
data['overhead_doors_area_m_sq'] = overhead_doors.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier * e.multiplier }
|
|
374
|
-
|
|
375
|
-
# Total Building Ground Surface Area.
|
|
376
|
-
data['total_ground_area_m_sq'] = data['ground_walls_area_m_sq'] +
|
|
377
|
-
data['ground_roofs_area_m_sq'] +
|
|
378
|
-
data['ground_floors_area_m_sq']
|
|
379
|
-
# Total Building Outdoor Surface Area.
|
|
380
|
-
data['total_outdoor_area_m_sq'] = data['outdoor_walls_area_m_sq'] +
|
|
381
|
-
data['outdoor_roofs_area_m_sq'] +
|
|
382
|
-
data['outdoor_floors_area_m_sq'] +
|
|
383
|
-
data['windows_area_m_sq'] +
|
|
384
|
-
data['skylights_area_m_sq'] +
|
|
385
|
-
data['doors_area_m_sq'] +
|
|
386
|
-
data['overhead_doors_area_m_sq']
|
|
387
|
-
|
|
388
|
-
# Average Conductances by surface Type
|
|
389
|
-
data['outdoor_walls_average_conductance_w_per_m_sq_k'] = OpenstudioStandards::Constructions.surfaces_get_conductance(outdoor_walls).round(4) if !outdoor_walls.empty?
|
|
390
|
-
data['outdoor_roofs_average_conductance_w_per_m_sq_k'] = OpenstudioStandards::Constructions.surfaces_get_conductance(outdoor_roofs).round(4) if !outdoor_roofs.empty?
|
|
391
|
-
data['outdoor_floors_average_conductance_w_per_m_sq_k'] = OpenstudioStandards::Constructions.surfaces_get_conductance(outdoor_floors).round(4) if !outdoor_floors.empty?
|
|
392
|
-
data['ground_walls_average_conductance_w_per_m_sq_k'] = OpenstudioStandards::Constructions.surfaces_get_conductance(ground_walls).round(4) if !ground_walls.empty?
|
|
393
|
-
data['ground_roofs_average_conductance_w_per_m_sq_k'] = OpenstudioStandards::Constructions.surfaces_get_conductance(ground_roofs).round(4) if !ground_roofs.empty?
|
|
394
|
-
data['ground_floors_average_conductance_w_per_m_sq_k'] = OpenstudioStandards::Constructions.surfaces_get_conductance(ground_floors).round(4) if !ground_floors.empty?
|
|
395
|
-
data['windows_average_conductance_w_per_m_sq_k'] = OpenstudioStandards::Constructions.surfaces_get_conductance(windows).round(4) if !windows.empty?
|
|
396
|
-
data['skylights_average_conductance_w_per_m_sq_k'] = OpenstudioStandards::Constructions.surfaces_get_conductance(skylights).round(4) if !skylights.empty?
|
|
397
|
-
data['doors_average_conductance_w_per_m_sq_k'] = OpenstudioStandards::Constructions.surfaces_get_conductance(doors).round(4) if !doors.empty?
|
|
398
|
-
data['overhead_doors_average_conductance_w_per_m_sq_k'] = OpenstudioStandards::Constructions.surfaces_get_conductance(overhead_doors).round(4) if !overhead_doors.empty?
|
|
399
|
-
|
|
400
|
-
# #Average Conductances for building whole weight factors
|
|
401
|
-
!outdoor_walls.empty? ? o_wall_cond_weight = data['outdoor_walls_average_conductance_w_per_m_sq_k'] * data['outdoor_walls_area_m_sq'] : o_wall_cond_weight = 0
|
|
402
|
-
!outdoor_roofs.empty? ? o_roof_cond_weight = data['outdoor_roofs_average_conductance_w_per_m_sq_k'] * data['outdoor_roofs_area_m_sq'] : o_roof_cond_weight = 0
|
|
403
|
-
!outdoor_floors.empty? ? o_floor_cond_weight = data['outdoor_floors_average_conductance_w_per_m_sq_k'] * data['outdoor_floors_area_m_sq'] : o_floor_cond_weight = 0
|
|
404
|
-
!ground_walls.empty? ? g_wall_cond_weight = data['ground_walls_average_conductance_w_per_m_sq_k'] * data['ground_walls_area_m_sq'] : g_wall_cond_weight = 0
|
|
405
|
-
!ground_roofs.empty? ? g_roof_cond_weight = data['ground_roofs_average_conductance_w_per_m_sq_k'] * data['ground_roofs_area_m_sq'] : g_roof_cond_weight = 0
|
|
406
|
-
!ground_floors.empty? ? g_floor_cond_weight = data['ground_floors_average_conductance_w_per_m_sq_k'] * data['ground_floors_area_m_sq'] : g_floor_cond_weight = 0
|
|
407
|
-
!windows.empty? ? win_cond_weight = data['windows_average_conductance_w_per_m_sq_k'] * data['windows_area_m_sq'] : win_cond_weight = 0
|
|
408
|
-
# doors.size > 0 ? sky_cond_weight = data["skylights_average_conductance_w_per_m_sq_k"] * data["skylights_area_m_sq"] : sky_cond_weight = 0
|
|
409
|
-
if !doors.empty? && !data['skylights_average_conductance_w_per_m_sq_k'].nil? && !data['skylights_area_m_sq'].nil?
|
|
410
|
-
sky_cond_weight = data['skylights_average_conductance_w_per_m_sq_k'] * data['skylights_area_m_sq']
|
|
411
|
-
else
|
|
412
|
-
sky_cond_weight = 0
|
|
413
|
-
end
|
|
414
|
-
!overhead_doors.empty? ? door_cond_weight = data['doors_average_conductance_w_per_m_sq_k'] * data['doors_area_m_sq'] : door_cond_weight = 0
|
|
415
|
-
!overhead_doors.empty? ? overhead_door_cond_weight = data['overhead_doors_average_conductance_w_per_m_sq_k'] * data['overhead_doors_area_m_sq'] : overhead_door_cond_weight = 0
|
|
416
|
-
|
|
417
|
-
# Building Average Conductance
|
|
418
|
-
data['outdoor_average_conductance_w_per_m_sq_k'] = (
|
|
419
|
-
o_floor_cond_weight +
|
|
420
|
-
o_roof_cond_weight +
|
|
421
|
-
o_wall_cond_weight +
|
|
422
|
-
win_cond_weight +
|
|
423
|
-
sky_cond_weight +
|
|
424
|
-
door_cond_weight +
|
|
425
|
-
overhead_door_cond_weight) / data['total_outdoor_area_m_sq']
|
|
426
|
-
|
|
427
|
-
# Building Average Ground Conductance
|
|
428
|
-
data['ground_average_conductance_w_per_m_sq_k'] = (
|
|
429
|
-
g_floor_cond_weight +
|
|
430
|
-
g_roof_cond_weight +
|
|
431
|
-
g_wall_cond_weight) / data['total_ground_area_m_sq']
|
|
432
|
-
|
|
433
|
-
# Building Average Conductance
|
|
434
|
-
data['average_conductance_w_per_m_sq_k'] = (
|
|
435
|
-
(data['average_conductance_w_per_m_sq_k'] * data['total_ground_area_m_sq']) +
|
|
436
|
-
(data['outdoor_average_conductance_w_per_m_sq_k'] * data['total_outdoor_area_m_sq'])
|
|
437
|
-
) /
|
|
438
|
-
(data['total_ground_area_m_sq'] + data['total_outdoor_area_m_sq'])
|
|
439
|
-
prefix = 'envel_'
|
|
440
|
-
return Hash[data.map { |k, v| ["#{prefix}_#{k}", v] }]
|
|
441
|
-
end
|
|
442
|
-
|
|
443
|
-
def envelope_summary(qaqc)
|
|
444
|
-
@btap_data['envelope-outdoor_walls_average_conductance-w_per_m_sq_k'] = qaqc[:envelope][:outdoor_walls_average_conductance_w_per_m2_k]
|
|
445
|
-
@btap_data['envelope-outdoor_roofs_average_conductance-w_per_m_sq_k'] = qaqc[:envelope][:outdoor_roofs_average_conductance_w_per_m2_k]
|
|
446
|
-
@btap_data['envelope-outdoor_floors_average_conductance-w_per_m_sq_k'] = qaqc[:envelope][:outdoor_floors_average_conductance_w_per_m2_k]
|
|
447
|
-
@btap_data['envelope-ground_walls_average_conductance-w_per_m_sq_k'] = qaqc[:envelope][:ground_walls_average_conductance_w_per_m2_k]
|
|
448
|
-
@btap_data['envelope-ground_roofs_average_conductance-w_per_m_sq_k'] = qaqc[:envelope][:ground_roofs_average_conductance_w_per_m2_k]
|
|
449
|
-
@btap_data['envelope-ground_floors_average_conductance-w_per_m_sq_k'] = qaqc[:envelope][:ground_floors_average_conductance_w_per_m2_k]
|
|
450
|
-
@btap_data['envelope-outdoor_windows_average_conductance-w_per_m_sq_k'] = qaqc[:envelope][:windows_average_conductance_w_per_m2_k]
|
|
451
|
-
@btap_data['envelope-outdoor_doors_average_conductance-w_per_m_sq_k'] = qaqc[:envelope][:doors_average_conductance_w_per_m2_k]
|
|
452
|
-
@btap_data['envelope-outdoor_overhead_doors_average_conductance-w_per_m_sq_k'] = qaqc[:envelope][:overhead_doors_average_conductance_w_per_m2_k]
|
|
453
|
-
@btap_data['envelope-skylights_average_conductance-w_per_m_sq_k'] = qaqc[:envelope][:skylights_average_conductance_w_per_m2_k]
|
|
454
|
-
end
|
|
455
|
-
|
|
456
|
-
def envelope_exterior_surface_table
|
|
457
|
-
surfaces = @model.getSurfaces.sort
|
|
458
|
-
outdoor_surfaces = BTAP::Geometry::Surfaces.filter_by_boundary_condition(surfaces, 'Outdoors')
|
|
459
|
-
ground_surfaces = BTAP::Geometry::Surfaces.filter_by_boundary_condition(surfaces, ['Ground', 'Foundation'])
|
|
460
|
-
exterior_opaque_surfaces = outdoor_surfaces + ground_surfaces
|
|
461
|
-
# outdoor_surfaces.each { |surface| puts surface.name}
|
|
462
|
-
# get surface table from sql
|
|
463
|
-
table = get_sql_table_to_json(@model, 'EnvelopeSummary', 'Entire Facility', 'Opaque Exterior')
|
|
464
|
-
raise('Could not get opaque surface table from E+ sql') if table.empty?
|
|
465
|
-
|
|
466
|
-
# add space name to table.
|
|
467
|
-
table['table'].each do |row|
|
|
468
|
-
surface = exterior_opaque_surfaces.detect { |curr_surface| curr_surface.name.get.downcase == row['name'].downcase }
|
|
469
|
-
raise("Could not find surface #{row['name'].downcase} in #{outdoor_surfaces.map { |curr_surface| curr_surface.name.get.downcase }}") if surface.nil?
|
|
470
|
-
|
|
471
|
-
row['os_type'] = surface.surfaceType
|
|
472
|
-
row['boundary_condition'] = surface.outsideBoundaryCondition
|
|
473
|
-
space_includes_surface = @model.getSpaces.detect { |space| space.surfaces.include?(surface) }
|
|
474
|
-
row['space_name'] = space_includes_surface.nil? || !space_includes_surface.name.is_initialized ? 'NA' : space_includes_surface.name.get
|
|
475
|
-
end
|
|
476
|
-
opaque = table
|
|
477
|
-
|
|
478
|
-
# Fenestrations
|
|
479
|
-
fenestrations = BTAP::Geometry::Surfaces.filter_subsurfaces_by_types(@model.getSubSurfaces.sort, ['GlassDoor', 'FixedWindow', 'OperableWindow', 'Skylight', 'TubularDaylightDiffuser', 'TubularDaylightDome'])
|
|
480
|
-
# get surface table from sql
|
|
481
|
-
table = get_sql_table_to_json(@model, 'EnvelopeSummary', 'Entire Facility', 'Exterior Fenestration')
|
|
482
|
-
# Exclude totals and averages by deleting row with that in their name.
|
|
483
|
-
table['table'].delete_if { |row| !!(row['name'] =~ /Total|Average/) }
|
|
484
|
-
raise('Could not get fenestration surface table from E+ sql') if table.empty?
|
|
485
|
-
|
|
486
|
-
# add space name to table.
|
|
487
|
-
table['table'].each do |row|
|
|
488
|
-
subsurface = fenestrations.detect { |surface| surface.name.get.downcase == row['name'].downcase }
|
|
489
|
-
raise("Could not find surface #{row['name'].downcase} in #{fenestrations.map { |surface| surface.name.get.downcase }}") if subsurface.nil?
|
|
490
|
-
|
|
491
|
-
row['os_type'] = subsurface.subSurfaceType
|
|
492
|
-
row['boundary_condition'] = subsurface.outsideBoundaryCondition
|
|
493
|
-
parent_surface = subsurface.surface.get
|
|
494
|
-
space_includes_surface = @model.getSpaces.detect { |space| space.surfaces.include?(parent_surface) }
|
|
495
|
-
row['space_name'] = space_includes_surface.nil? || !space_includes_surface.name.is_initialized ? 'NA' : space_includes_surface.name.get
|
|
496
|
-
end
|
|
497
|
-
glazing = table
|
|
498
|
-
|
|
499
|
-
# return as a single table.
|
|
500
|
-
|
|
501
|
-
return glazing['table'] + opaque['table']
|
|
502
|
-
end
|
|
503
|
-
|
|
504
|
-
def space_table(model, cost_result)
|
|
505
|
-
# Store Space data.
|
|
506
|
-
table = []
|
|
507
|
-
model.getSpaces.sort.each do |space|
|
|
508
|
-
spaceinfo = {}
|
|
509
|
-
table << spaceinfo
|
|
510
|
-
spaceinfo['thermal_zone_name'] = space.thermalZone.empty? ? 'None' : space.thermalZone.get.name.get # should be assigned a thermalzone name.
|
|
511
|
-
spaceinfo['space_name'] = space.name.get # name should be defined test
|
|
512
|
-
spaceinfo['multiplier'] = space.multiplier
|
|
513
|
-
spaceinfo['volume'] = space.volume # should be greater than zero
|
|
514
|
-
spaceinfo['exterior_wall_area'] = space.exteriorWallArea # just for information.
|
|
515
|
-
spaceinfo['space_type_name'] = space.spaceType.get.name.get unless space.spaceType.empty? # should have a space types name defined.
|
|
516
|
-
spaceinfo['breathing_zone_outdoor_airflow_vbz'] = -1
|
|
517
|
-
spaceinfo['infiltration_flow_per_m_sq'] = space.infiltrationDesignFlowPerExteriorSurfaceArea
|
|
518
|
-
spaceinfo['floor_area_m2'] = space.floorArea
|
|
519
|
-
spaceinfo['building_type'] = space.spaceType.get.standardsBuildingType.empty? ? 'None' : space.spaceType.get.standardsBuildingType.get
|
|
520
|
-
spaceinfo['is_conditioned'] = space.thermalZone.get.isConditioned.get unless space.thermalZone.empty? or space.thermalZone.get.isConditioned.empty?
|
|
521
|
-
# shw
|
|
522
|
-
spaceinfo['shw_peak_flow_rate_m_cu_per_s'] = 0
|
|
523
|
-
spaceinfo['shw_peak_flow_rate_per_floor_area_m_cu_per_s_per_m_sq'] = 0
|
|
524
|
-
space.waterUseEquipment.each do |equipment|
|
|
525
|
-
spaceinfo['shw_peak_flow_rate_m_cu_per_s'] += equipment.waterUseEquipmentDefinition.peakFlowRate
|
|
526
|
-
spaceinfo['shw_peak_flow_rate_per_floor_area_m_cu_per_s_per_m_sq'] += equipment.waterUseEquipmentDefinition.peakFlowRate / space.floorArea
|
|
527
|
-
area_per_occ = space.spaceType.get.getFloorAreaPerPerson(space.floorArea)
|
|
528
|
-
# Watt per person = m_cu/s/m_cu * 1000W/kW * (specific heat * dT) * m_sq/person
|
|
529
|
-
spaceinfo['shw_watts_per_person'] = spaceinfo['shw_peak_flow_rate_per_floor_area_m_cu_per_s_per_m_sq'] * 1000 * (4.19 * 44.4) * 1000 * area_per_occ
|
|
530
|
-
end
|
|
531
|
-
unless cost_result.nil?
|
|
532
|
-
# Including space level lighting costs in the existing space table...
|
|
533
|
-
spaceLgtInfo = cost_result['lighting']['space_report'].detect { |curr_spaceLgtInfo| curr_spaceLgtInfo['zone'].downcase == spaceinfo['thermal_zone_name'].downcase }
|
|
534
|
-
raise("Could not find zone name \"#{spaceinfo['thermal_zone_name']}\" in lighting space_report") if spaceLgtInfo.nil?
|
|
535
|
-
|
|
536
|
-
spaceinfo['space_type'] = spaceLgtInfo['space_type']
|
|
537
|
-
spaceinfo['fixture_type'] = spaceLgtInfo['fixture_type']
|
|
538
|
-
# Note spelling mistake of "description" in cost_result hash fixed below in copy
|
|
539
|
-
spaceinfo['fixture_description'] = spaceLgtInfo['fixture_desciption']
|
|
540
|
-
spaceinfo['height_avg_ft'] = spaceLgtInfo['height_avg_ft']
|
|
541
|
-
spaceinfo['floor_area_ft2'] = spaceLgtInfo['floor_area_ft2']
|
|
542
|
-
spaceinfo['lighting_cost'] = spaceLgtInfo['cost']
|
|
543
|
-
spaceinfo['lighting_cost_per_ft2'] = spaceLgtInfo['cost_per_ft2']
|
|
544
|
-
spaceinfo['lighting_note'] = spaceLgtInfo['note']
|
|
545
|
-
end
|
|
546
|
-
end
|
|
547
|
-
table.sort_by! { |spaceinfo| [spaceinfo['thermal_zone_name'], spaceinfo['space_name']] }
|
|
548
|
-
return table
|
|
549
|
-
end
|
|
550
|
-
|
|
551
|
-
def climate_data
|
|
552
|
-
# Store Geography Data
|
|
553
|
-
geography_data = {}
|
|
554
|
-
geography_data['location_necb_hdd'] = @standard.get_necb_hdd18(model: @model, necb_hdd: true)
|
|
555
|
-
geography_data['location_weather_file'] = File.basename(@model.getWeatherFile.path.get.to_s)
|
|
556
|
-
weather_file_path = @model.weatherFile.get.path.get.to_s
|
|
557
|
-
stat_file_path = weather_file_path.gsub('.epw', '.stat')
|
|
558
|
-
stat_file = OpenstudioStandards::Weather::StatFile.new(stat_file_path)
|
|
559
|
-
geography_data['location_epw_cdd'] = stat_file.cdd18
|
|
560
|
-
geography_data['location_epw_hdd'] = stat_file.hdd18
|
|
561
|
-
geography_data['location_necb_climate_zone'] = @standard.get_climate_zone_name(geography_data['location_necb_hdd'])
|
|
562
|
-
geography_data['location_city'] = @model.getWeatherFile.city
|
|
563
|
-
geography_data['location_state_province_region'] = @model.getWeatherFile.stateProvinceRegion
|
|
564
|
-
geography_data['location_country'] = @model.getWeatherFile.country
|
|
565
|
-
geography_data['location_latitude'] = @model.getWeatherFile.latitude
|
|
566
|
-
geography_data['location_longitude'] = @model.getWeatherFile.longitude
|
|
567
|
-
return geography_data
|
|
568
|
-
end
|
|
569
|
-
|
|
570
|
-
### The 'annual_peak_electricity_cost_dollar' function calculates annual peak electricity cost (dollar)
|
|
571
|
-
### for OEE-Electrification project using the OERD's electricity rates for commercial buildings
|
|
572
|
-
### that Danielle Krauel of OEE provided to CE-O
|
|
573
|
-
def oerd_electricity_cost(model:, utility_pricing_year: 2020, oerd_utility_pricing: false)
|
|
574
|
-
unless oerd_utility_pricing
|
|
575
|
-
@btap_data.merge!('cost_utility_oerd_electricity_peak_annual_cost_per_m_sq' => 0.00) unless oerd_utility_pricing
|
|
576
|
-
@btap_data.merge!('cost_utility_oerd_electricity_energy_annual_cost_per_m_sq' => 0.00) unless oerd_utility_pricing
|
|
577
|
-
return 0.00
|
|
578
|
-
end
|
|
579
|
-
|
|
580
|
-
#===================================================================================================================
|
|
581
|
-
### OERD's electricity rates for commercial buildings
|
|
582
|
-
neb_data = CSV.parse(File.read(@neb_prices_csv_file_name), headers: true, converters: :numeric).map(&:to_h)
|
|
583
|
-
#===================================================================================================================
|
|
584
|
-
#===================================================================================================================
|
|
585
|
-
#===================================================================================================================
|
|
586
|
-
#===================================================================================================================
|
|
587
|
-
#===================================================================================================================
|
|
588
|
-
##################### Calculate annual electricity peak cost using OERD's electricity rate #########################
|
|
589
|
-
#===================================================================================================================
|
|
590
|
-
#===================================================================================================================
|
|
591
|
-
### Find index for hourly data of 'ElectricityNet:Facility' (J)
|
|
592
|
-
query = "
|
|
593
|
-
SELECT ReportDataDictionaryIndex
|
|
594
|
-
FROM ReportDataDictionary
|
|
595
|
-
WHERE Name=='ElectricityNet:Facility' AND ReportingFrequency == 'Hourly' AND Units == 'J'
|
|
596
|
-
"
|
|
597
|
-
index_timestep_electricity = model.sqlFile.get.execAndReturnVectorOfInt(query).get[0]
|
|
598
|
-
number_of_timesteps_per_hour = 1
|
|
599
|
-
puts "Trying hourly ElectricityNet:Facility"
|
|
600
|
-
|
|
601
|
-
### Find index for timestep data of 'Electricity:Facility' (J) if 'ElectricityNet:Facility' data is not available
|
|
602
|
-
if index_timestep_electricity.nil?
|
|
603
|
-
query = "
|
|
604
|
-
SELECT ReportDataDictionaryIndex
|
|
605
|
-
FROM ReportDataDictionary
|
|
606
|
-
WHERE Name=='Electricity:Facility' AND ReportingFrequency == 'Zone Timestep' AND Units == 'J'
|
|
607
|
-
"
|
|
608
|
-
index_timestep_electricity = model.sqlFile.get.execAndReturnVectorOfInt(query).get[0]
|
|
609
|
-
number_of_timesteps_per_hour = model.getTimestep.numberOfTimestepsPerHour
|
|
610
|
-
puts "Trying timestep Electricity:Facility"
|
|
611
|
-
end
|
|
612
|
-
|
|
613
|
-
### Find index for hourly data of 'Electricity:Facility' (J) if timestep data is not available
|
|
614
|
-
if index_timestep_electricity.nil?
|
|
615
|
-
query = "
|
|
616
|
-
SELECT ReportDataDictionaryIndex
|
|
617
|
-
FROM ReportDataDictionary
|
|
618
|
-
WHERE Name=='Electricity:Facility' AND ReportingFrequency == 'Hourly' AND Units == 'J'
|
|
619
|
-
"
|
|
620
|
-
index_timestep_electricity = model.sqlFile.get.execAndReturnVectorOfInt(query).get[0]
|
|
621
|
-
number_of_timesteps_per_hour = 1
|
|
622
|
-
puts "Trying hourly Electricity:Facility"
|
|
623
|
-
end
|
|
624
|
-
|
|
625
|
-
if index_timestep_electricity.nil?
|
|
626
|
-
puts "No hourly or timestep data available. Peak data not available"
|
|
627
|
-
@btap_data.merge!('cost_utility_oerd_electricity_peak_annual_cost_per_m_sq' => 0.00) unless oerd_utility_pricing
|
|
628
|
-
@btap_data.merge!('cost_utility_oerd_electricity_energy_annual_cost_per_m_sq' => 0.00) unless oerd_utility_pricing
|
|
629
|
-
return 0.00
|
|
630
|
-
end
|
|
631
|
-
### Get number of timesteps from the model
|
|
632
|
-
timestep_second = 3600 / number_of_timesteps_per_hour
|
|
633
|
-
#===================================================================================================================
|
|
634
|
-
### Calculate number of timesteps of the whole year, and create dates/times for the whole year
|
|
635
|
-
number_of_timesteps_of_year = 365 * 24 * number_of_timesteps_per_hour
|
|
636
|
-
timesteps_of_year = []
|
|
637
|
-
d = Time.new(2006, 1, 1, 0)
|
|
638
|
-
(0...number_of_timesteps_of_year).each do |increment|
|
|
639
|
-
timesteps_of_year << (d + (60 * 60 / number_of_timesteps_per_hour) * increment).strftime('%Y-%m-%d %H:%M')
|
|
640
|
-
end
|
|
641
|
-
#===================================================================================================================
|
|
642
|
-
|
|
643
|
-
#===================================================================================================================
|
|
644
|
-
### Get timestep output for Electricity:Facility output.
|
|
645
|
-
query = "
|
|
646
|
-
Select VariableValue
|
|
647
|
-
FROM ReportVariableData
|
|
648
|
-
WHERE
|
|
649
|
-
ReportVariableDataDictionaryIndex = #{index_timestep_electricity}
|
|
650
|
-
"
|
|
651
|
-
timestep_values = model.sqlFile.get.execAndReturnVectorOfDouble(query).get
|
|
652
|
-
#===================================================================================================================
|
|
653
|
-
### Calculate electricity peak (J) for each month of the year
|
|
654
|
-
# Find month of each timestep
|
|
655
|
-
keys_month = timesteps_of_year.map{ |timestep| DateTime.parse(timestep).to_date.month }
|
|
656
|
-
# Create a hash where its keys are months and values for eahc month are electricity energy use (J) of all timesteps for that month
|
|
657
|
-
hash_months_electricity = keys_month.zip(timestep_values).group_by(&:first).map{|key, value| [key, value.map(&:last)]}.to_h
|
|
658
|
-
# Find maximum of electricity energy use (J) among all timesteps for each month of the year
|
|
659
|
-
monthly_peak_J = hash_months_electricity.map { |key, value| [key, value.max()] }.to_h
|
|
660
|
-
#===================================================================================================================
|
|
661
|
-
### Convert J to kW of maximum electricity energy use (J)
|
|
662
|
-
monthly_peak_kW = monthly_peak_J.map { |key, value| [key, value * 0.001 / timestep_second] }.to_h
|
|
663
|
-
### Normalize by building floor area
|
|
664
|
-
annual_peak_kW_per_m_sq = monthly_peak_kW.values.sum / @btap_data['bldg_conditioned_floor_area_m_sq'].to_f
|
|
665
|
-
#===================================================================================================================
|
|
666
|
-
### Get province of the model and find its peak electricity price from the utility_pricing_2025-02-20.csv
|
|
667
|
-
province = @btap_data['location_state_province_region']
|
|
668
|
-
# Find which province the proposed building is located in
|
|
669
|
-
building_type = 'Commercial'
|
|
670
|
-
geography_data = climate_data
|
|
671
|
-
province_abbreviation = geography_data['location_state_province_region']
|
|
672
|
-
province = @standards_data['province_map'][province_abbreviation]
|
|
673
|
-
row_peak = neb_data.detect do |data|
|
|
674
|
-
(data['building_type'] == building_type) && (data['province'] == province) && (data['fuel_type'] == 'Electricity Peak')
|
|
675
|
-
end
|
|
676
|
-
province_dollar_kW_month = row_peak[utility_pricing_year.to_i.to_s]
|
|
677
|
-
#===================================================================================================================
|
|
678
|
-
### Calculate cost of peak electricity for each month
|
|
679
|
-
monthly_peak_cost = monthly_peak_kW.map { |key, value| [key, value * province_dollar_kW_month] }.to_h
|
|
680
|
-
#===================================================================================================================
|
|
681
|
-
### Calculate cost of peak electricity for the whole year in dollar
|
|
682
|
-
annual_peak_cost_dollar = monthly_peak_cost.values.sum
|
|
683
|
-
#===================================================================================================================
|
|
684
|
-
### Calculate cost of peak electricity for the whole year in dollar normalized by floor area
|
|
685
|
-
annual_peak_cost_dollar_per_m_sq = annual_peak_cost_dollar / @btap_data['bldg_conditioned_floor_area_m_sq']
|
|
686
|
-
#===================================================================================================================
|
|
687
|
-
### Merge the calculated value to @btap_data
|
|
688
|
-
@btap_data.merge!('cost_utility_oerd_electricity_peak_annual_cost_per_m_sq' => annual_peak_cost_dollar_per_m_sq.round(2))
|
|
689
|
-
#===================================================================================================================
|
|
690
|
-
#===================================================================================================================
|
|
691
|
-
#===================================================================================================================
|
|
692
|
-
#===================================================================================================================
|
|
693
|
-
#===================================================================================================================
|
|
694
|
-
################### Calculate annual electricity energy use cost using OERD's electricity rate #####################
|
|
695
|
-
#===================================================================================================================
|
|
696
|
-
### Get province of the model and find its peak electricity price from the pricing_2025-02-20.csv
|
|
697
|
-
row_elec = neb_data.detect do |data|
|
|
698
|
-
(data['building_type'] == building_type) && (data['province'] == province) && (data['fuel_type'] == 'Electricity')
|
|
699
|
-
end
|
|
700
|
-
province_dollar_kWh = row_elec[utility_pricing_year.to_i.to_s]
|
|
701
|
-
#===================================================================================================================
|
|
702
|
-
# Calculate cost of energy electricity for the whole year in dollar normalized by floor area
|
|
703
|
-
annual_energy_cost_dollar_per_m_sq = @btap_data['energy_eui_electricity_gj_per_m_sq'] * 277.778 * province_dollar_kWh
|
|
704
|
-
#===================================================================================================================
|
|
705
|
-
### Merge the calculated value to @btap_data
|
|
706
|
-
@btap_data.merge!('cost_utility_oerd_electricity_energy_annual_cost_per_m_sq' => annual_energy_cost_dollar_per_m_sq.round(2))
|
|
707
|
-
#===================================================================================================================
|
|
708
|
-
return annual_peak_kW_per_m_sq
|
|
709
|
-
end
|
|
710
|
-
|
|
711
|
-
def utility(model:, utility_pricing_year: 2020)
|
|
712
|
-
economics_data = {}
|
|
713
|
-
building_type = 'Commercial'
|
|
714
|
-
province = @standards_data['province_map'][model.getWeatherFile.stateProvinceRegion]
|
|
715
|
-
neb_eplus_fuel_map = {'Natural Gas' => {eplus_fuel_name: 'NaturalGas',
|
|
716
|
-
eplus_table_name: 'Annual and Peak Values - Natural Gas',
|
|
717
|
-
eplus_row_name: 'NaturalGas:Facility',
|
|
718
|
-
eplus_column_name: 'Natural Gas Annual Value'},
|
|
719
|
-
'Electricity' => {eplus_fuel_name: 'Electricity',
|
|
720
|
-
eplus_table_name: 'Annual and Peak Values - Electricity',
|
|
721
|
-
eplus_row_name: 'Electricity:Facility',
|
|
722
|
-
eplus_column_name: 'Electricity Annual Value'},
|
|
723
|
-
'Oil' => {eplus_fuel_name: 'FuelOilNo2',
|
|
724
|
-
eplus_table_name: 'Annual and Peak Values - Other',
|
|
725
|
-
eplus_row_name: 'FuelOilNo2:Facility',
|
|
726
|
-
eplus_column_name: 'Annual Value'}
|
|
727
|
-
}
|
|
728
|
-
economics_data['cost_utility_neb_total_cost_per_m_sq'] = 0.0
|
|
729
|
-
economics_data['cost_utility_ghg_total_kg_per_m_sq'] = 0.0
|
|
730
|
-
# Create a hash of the neb data.
|
|
731
|
-
neb_data = CSV.parse(File.read(@neb_prices_csv_file_name), headers: true, converters: :numeric).map(&:to_h)
|
|
732
|
-
|
|
733
|
-
neb_eplus_fuel_map.each do |neb_fuel, ep_fuel|
|
|
734
|
-
next if neb_fuel.to_s.downcase == 'electricity peak'
|
|
735
|
-
row = neb_data.detect do |data|
|
|
736
|
-
(data['building_type'].to_s == building_type) &&
|
|
737
|
-
(data['province'].to_s == province) &&
|
|
738
|
-
(data['fuel_type'].to_s == neb_fuel)
|
|
739
|
-
end
|
|
740
|
-
utility_pricing_year_s = utility_pricing_year.to_i.to_s
|
|
741
|
-
neb_fuel_cost = row[utility_pricing_year_s]
|
|
742
|
-
fuel_consumption_gj = 0.0
|
|
743
|
-
sql_command = "SELECT Value FROM tabulardatawithstrings
|
|
744
|
-
WHERE ReportName='EnergyMeters'
|
|
745
|
-
AND ReportForString='Entire Facility'
|
|
746
|
-
AND TableName='#{ep_fuel[:eplus_table_name]}'
|
|
747
|
-
AND RowName='#{ep_fuel[:eplus_row_name]}'
|
|
748
|
-
AND ColumnName='#{ep_fuel[:eplus_column_name]}'
|
|
749
|
-
AND Units='GJ'"
|
|
750
|
-
fuel_consumption_gj = model.sqlFile.get.execAndReturnFirstDouble(sql_command).is_initialized ? model.sqlFile.get.execAndReturnFirstDouble(sql_command).get : 0.0
|
|
751
|
-
|
|
752
|
-
# Determine costs in $$
|
|
753
|
-
economics_data["cost_utility_neb_#{neb_fuel.downcase}_cost_per_m_sq"] = fuel_consumption_gj * neb_fuel_cost.to_f / @conditioned_floor_area_m_sq
|
|
754
|
-
economics_data['cost_utility_neb_total_cost_per_m_sq'] += economics_data["cost_utility_neb_#{neb_fuel.downcase}_cost_per_m_sq"]
|
|
755
|
-
# Determine cost in GHG kg of CO2
|
|
756
|
-
if neb_fuel.downcase == 'electricity' # Use ECCC data for utility emission factors rather than NEB data.
|
|
757
|
-
# Create a hash of the ECCC's electricity emissions factors for the current and future years for GHG "without Biomass and RNG CO2 emissions".
|
|
758
|
-
eccc_ghg_elec_emission_factors = CSV.parse(File.read(@eccc_ghg_electricity), headers: true, converters: :numeric).map(&:to_h)
|
|
759
|
-
# Find the appropriate row for electricity GHG emmission factors
|
|
760
|
-
row_emm_factor = eccc_ghg_elec_emission_factors.detect { |data| (data['province'] == province) }
|
|
761
|
-
emm_factor_conv = 3600.0
|
|
762
|
-
economics_data["cost_utility_ghg_#{neb_fuel.downcase}_kg_per_m_sq"] = ((fuel_consumption_gj * row_emm_factor[utility_pricing_year.to_i.to_s].to_f) / emm_factor_conv) * 1000 / @conditioned_floor_area_m_sq
|
|
763
|
-
else
|
|
764
|
-
economics_data["cost_utility_ghg_#{neb_fuel.downcase}_kg_per_m_sq"] = fuel_consumption_gj * get_utility_ghg_kg_per_gj(province: model.getWeatherFile.stateProvinceRegion, fuel_type: ep_fuel[:eplus_fuel_name]) / @conditioned_floor_area_m_sq
|
|
765
|
-
end
|
|
766
|
-
economics_data['cost_utility_ghg_total_kg_per_m_sq'] += economics_data["cost_utility_ghg_#{neb_fuel.downcase}_kg_per_m_sq"]
|
|
767
|
-
end
|
|
768
|
-
# Commenting out block charge rates for now....
|
|
769
|
-
|
|
770
|
-
# Fuel cost based local utility rates
|
|
771
|
-
# sql_command = "SELECT RowName FROM TabularDataWithStrings
|
|
772
|
-
# WHERE ReportName='LEEDsummary'
|
|
773
|
-
# AND ReportForString='Entire Facility'
|
|
774
|
-
# AND TableName='EAp2-7. Energy Cost Summary'
|
|
775
|
-
# AND ColumnName='Total Energy Cost'"
|
|
776
|
-
# costing_rownames = model.sqlFile().get().execAndReturnVectorOfString(sql_command)
|
|
777
|
-
|
|
778
|
-
#==> ["Electricity", "Natural Gas", "Additional", "Total"]
|
|
779
|
-
# costing_rownames = validate_optional(costing_rownames, model, "N/A")
|
|
780
|
-
# unless costing_rownames == "N/A"
|
|
781
|
-
# costing_rownames.each do |rowname|
|
|
782
|
-
# sql_command = "SELECT Value FROM TabularDataWithStrings
|
|
783
|
-
# WHERE ReportName='LEEDsummary'
|
|
784
|
-
# AND ReportForString='Entire Facility'
|
|
785
|
-
# AND TableName='EAp2-7. Energy Cost Summary'
|
|
786
|
-
# AND ColumnName='Total Energy Cost'
|
|
787
|
-
# AND RowName='#{rowname}'"
|
|
788
|
-
# case rowname
|
|
789
|
-
# when "Electricity"
|
|
790
|
-
# economics_data["cost_utility_block_electricity_cost_per_m_sq"] = model.sqlFile().get().execAndReturnFirstDouble(sql_command).get / @conditioned_floor_area_m_sq
|
|
791
|
-
# when "Natural Gas"
|
|
792
|
-
# economics_data["cost_utility_block_natural_gas_cost_per_m_sq"] = model.sqlFile().get().execAndReturnFirstDouble(sql_command).get / @conditioned_floor_area_m_sq
|
|
793
|
-
# when "Additional"
|
|
794
|
-
# economics_data["cost_utility_block_additional_cost_per_m_sq"] = model.sqlFile().get().execAndReturnFirstDouble(sql_command).get / @conditioned_floor_area_m_sq
|
|
795
|
-
# when "Total"
|
|
796
|
-
# economics_data["cost_utility_block_total_cost_per_m_sq"] = model.sqlFile().get().execAndReturnFirstDouble(sql_command).get / @conditioned_floor_area_m_sq
|
|
797
|
-
# end
|
|
798
|
-
# end
|
|
799
|
-
# else
|
|
800
|
-
# @error_warning << "costing is unavailable because the sql statement is nil RowName FROM TabularDataWithStrings WHERE ReportName='LEEDsummary' AND ReportForString='Entire Facility' AND TableName='EAp2-7. Energy Cost Summary' AND ColumnName='Total Energy Cost'"
|
|
801
|
-
# end
|
|
802
|
-
return economics_data
|
|
803
|
-
end
|
|
804
|
-
|
|
805
|
-
def space_type_table(model)
|
|
806
|
-
table = []
|
|
807
|
-
model.getSpaceTypes.sort.each do |spaceType|
|
|
808
|
-
next if spaceType.floorArea == 0
|
|
809
|
-
|
|
810
|
-
# data for space type breakdown
|
|
811
|
-
display = spaceType.name.get
|
|
812
|
-
floor_area_si = 0
|
|
813
|
-
# loop through spaces so I can skip if not included in floor area
|
|
814
|
-
spaceType.spaces.sort.each do |space|
|
|
815
|
-
next if !space.partofTotalFloorArea
|
|
816
|
-
|
|
817
|
-
floor_area_si += space.floorArea * space.multiplier
|
|
818
|
-
end
|
|
819
|
-
space_type_info = {}
|
|
820
|
-
space_type_info['name'] = spaceType.name.get
|
|
821
|
-
space_type_info['floor_m_sq'] = floor_area_si
|
|
822
|
-
space_type_info['percent_area'] = (floor_area_si / @conditioned_floor_area_m_sq * 100.0).round(2)
|
|
823
|
-
space_type_info['occ_per_m_sq'] = !spaceType.peoplePerFloorArea.empty? ? spaceType.peoplePerFloorArea.get : nil
|
|
824
|
-
space_type_info['occ_schedule'] = !spaceType.defaultScheduleSet.empty? && !spaceType.defaultScheduleSet.get.numberofPeopleSchedule.empty? ? spaceType.defaultScheduleSet.get.numberofPeopleSchedule.get.name.get : nil
|
|
825
|
-
space_type_info['lighting_w_per_m_sq'] = !spaceType.lightingPowerPerFloorArea.empty? ? spaceType.lightingPowerPerFloorArea.get : nil
|
|
826
|
-
space_type_info['electric_w_per_m_sq'] = !spaceType.electricEquipmentPowerPerFloorArea.empty? ? spaceType.electricEquipmentPowerPerFloorArea.get : nil
|
|
827
|
-
space_type_info['gas_w_per_m_sq'] = !spaceType.gasEquipmentPowerPerFloorArea.empty? ? spaceType.gasEquipmentPowerPerFloorArea.get : nil
|
|
828
|
-
table << space_type_info
|
|
829
|
-
end
|
|
830
|
-
return table
|
|
831
|
-
end
|
|
832
|
-
|
|
833
|
-
def thermal_zones_table(model, cost_result)
|
|
834
|
-
# Get E+ zone table.
|
|
835
|
-
zones = @model.getThermalZones
|
|
836
|
-
# get surface table from sql
|
|
837
|
-
table = get_sql_table_to_json(@model, 'InputVerificationandResultsSummary', 'Entire Facility', 'Zone Summary')
|
|
838
|
-
# Get rid of totals and averages.
|
|
839
|
-
table['table'].delete_if { |row| !!(row['name'] =~ /Total|Average/) }
|
|
840
|
-
raise('Could not get zone table from E+ sql') if table.empty?
|
|
841
|
-
|
|
842
|
-
# Go through zone objects
|
|
843
|
-
zones.each do |zone|
|
|
844
|
-
# get E+ zone row
|
|
845
|
-
row = table['table'].detect { |curr_row| zone.name.get.downcase == curr_row['name'].downcase }
|
|
846
|
-
raise("Could not find zone #{row['name']} in #{zones.map { |curr_zone| curr_zone.name.get }}") if row.nil?
|
|
847
|
-
|
|
848
|
-
row['is_ideal_air_loads'] = zone.useIdealAirLoads
|
|
849
|
-
row['heating_sizing_factor'] = zone.sizingZone.zoneHeatingSizingFactor.empty? ? -1.0 : zone.sizingZone.zoneHeatingSizingFactor.get
|
|
850
|
-
row['cooling_sizing_factor'] = zone.sizingZone.zoneCoolingSizingFactor.empty? ? -1.0 : zone.sizingZone.zoneCoolingSizingFactor.get
|
|
851
|
-
row['zone_heating_design_supply_air_temperature'] = zone.sizingZone.zoneHeatingDesignSupplyAirTemperature
|
|
852
|
-
row['zone_cooling_design_supply_air_temperature'] = zone.sizingZone.zoneCoolingDesignSupplyAirTemperature
|
|
853
|
-
# Get Air loop that it is connected to if possible
|
|
854
|
-
row['air_loop_name'] = nil
|
|
855
|
-
model.getAirLoopHVACs.sort.each do |air_loop|
|
|
856
|
-
row['air_loop_name'] = air_loop.name.get if air_loop.thermalZones.include?(zone)
|
|
857
|
-
end
|
|
858
|
-
# Get Breathing zone outdoor air flow.
|
|
859
|
-
sql_command = "
|
|
860
|
-
SELECT Value FROM TabularDataWithStrings
|
|
861
|
-
WHERE ReportName='Standard62.1Summary'
|
|
862
|
-
AND ReportForString='Entire Facility'
|
|
863
|
-
AND TableName='Zone Ventilation Parameters'
|
|
864
|
-
AND ColumnName='Breathing Zone Outdoor Airflow - Vbz'
|
|
865
|
-
AND Units='m3/s'
|
|
866
|
-
AND RowName='#{row['name']}'
|
|
867
|
-
"
|
|
868
|
-
breathing_zone_outdoor_airflow_vbz = model.sqlFile.get.execAndReturnFirstDouble(sql_command)
|
|
869
|
-
row['breathing_zone_outdoor_airflow_vbz'] = breathing_zone_outdoor_airflow_vbz.empty? ? nil : breathing_zone_outdoor_airflow_vbz.get
|
|
870
|
-
|
|
871
|
-
if zone.useIdealAirLoads == false
|
|
872
|
-
# Including ventilation tz_distribution cost data into zone_table
|
|
873
|
-
zoneName = row['name'].downcase
|
|
874
|
-
storyHash = cost_result['ventilation']['tz_distribution'.to_sym][0].detect { |currstoryHash| zoneName.include?(currstoryHash[:Story].to_s.downcase) }
|
|
875
|
-
if storyHash
|
|
876
|
-
tzHash = storyHash[:thermal_zones].detect { |currtzHash| zoneName == currtzHash[:ThermalZone].to_s.downcase && tzHash[:ducting_direction].to_s.downcase == 'supply' }
|
|
877
|
-
if tzHash
|
|
878
|
-
row['ducting_direction'] = tzHash[:ducting_direction]
|
|
879
|
-
row['tz_mult'] = tzHash[:tz_mult]
|
|
880
|
-
row['airflow_m3ps'] = tzHash[:airflow_m3ps]
|
|
881
|
-
row['num_diff'] = tzHash[:num_diff]
|
|
882
|
-
row['ducting_lbs'] = tzHash[:ducting_lbs]
|
|
883
|
-
row['duct_insulation_ft2'] = tzHash[:duct_insulation_ft2]
|
|
884
|
-
row['flex_duct_sz_in'] = tzHash[:flex_duct_sz_in]
|
|
885
|
-
row['flex_duct_length_ft'] = tzHash[:flex_duct_length_ft]
|
|
886
|
-
row['duct_cost'] = tzHash[:cost]
|
|
887
|
-
# Check if there is a return duct hash and, if so, modify ducting direction & cost to include
|
|
888
|
-
# Return duct. Note that all other return duct costing values are identical to Supply duct
|
|
889
|
-
tzHash1 = storyHash[:thermal_zones].detect { |currtzHash1| zoneName == currtzHash1[:ThermalZone].to_s.downcase && currtzHash1[:ducting_direction].to_s.downcase == 'return' }
|
|
890
|
-
if !tzHash1.nil?
|
|
891
|
-
row['ducting_direction'] = 'Supply & Return'
|
|
892
|
-
row['duct_cost'] += tzHash1[:cost]
|
|
893
|
-
end
|
|
894
|
-
end
|
|
895
|
-
end
|
|
896
|
-
|
|
897
|
-
# Including thermal zone HRV return ducting distribution cost information
|
|
898
|
-
floorHash = cost_result['ventilation']['hrv_return_ducting'.to_sym].detect { |currfloorHash| zoneName.include?(currfloorHash[:floor].to_s.downcase) }
|
|
899
|
-
if floorHash
|
|
900
|
-
airSysArr = floorHash[:air_systems].select { |airSys| airSys[:air_system].to_s.downcase == row['air_loop_name'].downcase }
|
|
901
|
-
if !airSysArr.empty?
|
|
902
|
-
airSysArr.each do |airSysHash|
|
|
903
|
-
airSys_tz_hash = airSysHash[:tz_dist].detect { |curr_airSys_tz_hash| curr_airSys_tz_hash[:tz].to_s.downcase == zoneName }
|
|
904
|
-
if airSys_tz_hash
|
|
905
|
-
row['hrv_ret_dist_m'] = airSys_tz_hash[:hrv_ret_dist_m]
|
|
906
|
-
row['hrv_ret_size_in'] = airSys_tz_hash[:hrv_ret_size_in]
|
|
907
|
-
row['hrv_ret_duct_cost'] = airSys_tz_hash[:cost]
|
|
908
|
-
end
|
|
909
|
-
end
|
|
910
|
-
end
|
|
911
|
-
end
|
|
912
|
-
end
|
|
913
|
-
end
|
|
914
|
-
|
|
915
|
-
return table
|
|
916
|
-
end
|
|
917
|
-
|
|
918
|
-
def thermal_zones_equipment_table(model)
|
|
919
|
-
# Store Thermal zone data
|
|
920
|
-
table = []
|
|
921
|
-
model.getThermalZones.sort.each do |zone|
|
|
922
|
-
zone.equipmentInHeatingOrder.each do |equipment|
|
|
923
|
-
item = {}
|
|
924
|
-
item['air_loop_name'] = nil
|
|
925
|
-
model.getAirLoopHVACs.sort.each do |air_loop|
|
|
926
|
-
if air_loop.thermalZones.include?(zone)
|
|
927
|
-
item['air_loop_name'] = air_loop.name.empty? ? 'None' : air_loop.name.get
|
|
928
|
-
else
|
|
929
|
-
item['air_loop_name'] = 'None'
|
|
930
|
-
end
|
|
931
|
-
end
|
|
932
|
-
item['thermal_zone_name'] = zone.name.empty? ? 'None' : zone.name.get
|
|
933
|
-
item['zone_equipment_name'] = equipment.name.empty? ? 'None' : equipment.name.get
|
|
934
|
-
item['type'] = get_actual_child_object(equipment).class.name
|
|
935
|
-
table << item
|
|
936
|
-
end
|
|
937
|
-
end
|
|
938
|
-
|
|
939
|
-
table.sort_by! { |item| [item['air_loop_name'], item['thermal_zone_name'], item['zone_equipment_name']] }
|
|
940
|
-
return table
|
|
941
|
-
end
|
|
942
|
-
|
|
943
|
-
def air_loops_table(model, cost_result)
|
|
944
|
-
# Store Air Loop Information
|
|
945
|
-
table = []
|
|
946
|
-
model.getAirLoopHVACs.sort.each do |air_loop|
|
|
947
|
-
air_loop_info = {}
|
|
948
|
-
air_loop_info['name'] = air_loop.name.get
|
|
949
|
-
sql_command = " SELECT Value FROM TabularDataWithStrings
|
|
950
|
-
WHERE ReportName='Standard62.1Summary'
|
|
951
|
-
AND ReportForString='Entire Facility'
|
|
952
|
-
AND TableName='System Ventilation Parameters'
|
|
953
|
-
AND ColumnName='Area Outdoor Air Rate - Ra'
|
|
954
|
-
AND Units='m3/s-m2'
|
|
955
|
-
AND RowName='#{air_loop.name.get.to_s.upcase}' "
|
|
956
|
-
air_loop_info['area_outdoor_air_rate_m_cu_per_s_m_sq'] = validate_optional(model.sqlFile.get.execAndReturnFirstDouble(sql_command), model, -1.0)
|
|
957
|
-
|
|
958
|
-
air_loop_info['total_floor_area_served'] = 0.0
|
|
959
|
-
air_loop_info['total_breathing_zone_outdoor_airflow_vbz'] = 0.0
|
|
960
|
-
air_loop.thermalZones.sort.each do |zone|
|
|
961
|
-
sql_command = " SELECT Value FROM TabularDataWithStrings
|
|
962
|
-
WHERE ReportName='Standard62.1Summary'
|
|
963
|
-
AND ReportForString='Entire Facility'
|
|
964
|
-
AND TableName='Zone Ventilation Parameters'
|
|
965
|
-
AND ColumnName='Breathing Zone Outdoor Airflow - Vbz'
|
|
966
|
-
AND Units='m3/s'
|
|
967
|
-
AND RowName='#{zone.name.get.to_s.upcase}' "
|
|
968
|
-
vbz = validate_optional(model.sqlFile.get.execAndReturnFirstDouble(sql_command), model, 0)
|
|
969
|
-
air_loop_info['total_breathing_zone_outdoor_airflow_vbz'] += vbz
|
|
970
|
-
air_loop_info['total_floor_area_served'] += zone.floorArea
|
|
971
|
-
end
|
|
972
|
-
air_loop_info['outdoor_air_l_per_s'] = -1.0
|
|
973
|
-
unless air_loop_info['area_outdoor_air_rate_m_cu_per_s_m_sq'] == -1.0
|
|
974
|
-
air_loop_info['outdoor_air_l_per_s'] = air_loop_info['area_outdoor_air_rate_m_cu_per_s_m_sq'] * air_loop_info['total_floor_area_served'] * 1000
|
|
975
|
-
end
|
|
976
|
-
|
|
977
|
-
# SUpply Fan
|
|
978
|
-
unless air_loop.supplyFan.empty?
|
|
979
|
-
if air_loop.supplyFan.get.to_FanConstantVolume.is_initialized
|
|
980
|
-
air_loop_info['supply_fan_type'] = 'CV'
|
|
981
|
-
fan = air_loop.supplyFan.get.to_FanConstantVolume.get
|
|
982
|
-
elsif air_loop.supplyFan.get.to_FanVariableVolume.is_initialized
|
|
983
|
-
air_loop_info['supply_fan_type'] = 'VV'
|
|
984
|
-
fan = air_loop.supplyFan.get.to_FanVariableVolume.get
|
|
985
|
-
end
|
|
986
|
-
air_loop_info['supply_fan_name'] = fan.name.get
|
|
987
|
-
air_loop_info['supply_fan_efficiency'] = fan.fanEfficiency
|
|
988
|
-
air_loop_info['supply_fan_motor_efficiency'] = fan.motorEfficiency
|
|
989
|
-
air_loop_info['supply_fan_pressure_rise'] = fan.pressureRise
|
|
990
|
-
air_loop_info['supply_fan_max_air_flow_rate_m_cu_per_s'] = -1.0
|
|
991
|
-
sql_command = " SELECT RowName FROM TabularDataWithStrings
|
|
992
|
-
WHERE ReportName='EquipmentSummary'
|
|
993
|
-
AND ReportForString='Entire Facility'
|
|
994
|
-
AND TableName='Fans'
|
|
995
|
-
AND ColumnName='Max Air Flow Rate'
|
|
996
|
-
AND Units='m3/s' "
|
|
997
|
-
max_air_flow_info = model.sqlFile.get.execAndReturnVectorOfString(sql_command)
|
|
998
|
-
max_air_flow_info = validate_optional(max_air_flow_info, model, 'N/A')
|
|
999
|
-
if max_air_flow_info != 'N/A'
|
|
1000
|
-
if max_air_flow_info.include?(air_loop_info['supply_fan_name'].to_s.upcase)
|
|
1001
|
-
sql_command = " SELECT Value FROM TabularDataWithStrings
|
|
1002
|
-
WHERE ReportName='EquipmentSummary'
|
|
1003
|
-
AND ReportForString='Entire Facility'
|
|
1004
|
-
AND TableName='Fans'
|
|
1005
|
-
AND ColumnName='Max Air Flow Rate'
|
|
1006
|
-
AND Units='m3/s'
|
|
1007
|
-
AND RowName='#{air_loop_info['supply_fan_name'].upcase}' "
|
|
1008
|
-
air_loop_info['supply_fan_max_air_flow_rate_m_cu_per_s'] = model.sqlFile.get.execAndReturnFirstDouble(sql_command).get
|
|
1009
|
-
sql_coommand = " SELECT Value FROM TabularDataWithStrings
|
|
1010
|
-
WHERE ReportName='EquipmentSummary'
|
|
1011
|
-
AND ReportForString='Entire Facility'
|
|
1012
|
-
AND TableName='Fans'
|
|
1013
|
-
AND ColumnName='Rated Electric Power'
|
|
1014
|
-
AND Units='W'
|
|
1015
|
-
AND RowName='#{air_loop_info['supply_fan_name'].upcase}' "
|
|
1016
|
-
air_loop_info['supply_fan_rated_electric_power_w'] = model.sqlFile.get.execAndReturnFirstDouble(sql_coommand).get
|
|
1017
|
-
else
|
|
1018
|
-
@error_warning << "#{air_loop_info['supply_fan_name']} does not exist in sql file WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Fans' AND ColumnName='Max Air Flow Rate' AND Units='m3/s'"
|
|
1019
|
-
end
|
|
1020
|
-
else
|
|
1021
|
-
@error_warning << "max_air_flow_info is nil because the following sql statement returned nil: RowName FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Fans' AND ColumnName='Max Air Flow Rate' AND Units='m3/s' "
|
|
1022
|
-
end
|
|
1023
|
-
end
|
|
1024
|
-
|
|
1025
|
-
# Fan
|
|
1026
|
-
unless air_loop.returnFan.empty?
|
|
1027
|
-
air_loop_info['return_fan'] = {}
|
|
1028
|
-
if air_loop.returnFan.get.to_FanConstantVolume.is_initialized
|
|
1029
|
-
air_loop_info['return_fan_type'] = 'CV'
|
|
1030
|
-
fan = air_loop.returnFan.get.to_FanConstantVolume.get
|
|
1031
|
-
elsif air_loop.returnFan.get.to_FanVariableVolume.is_initialized
|
|
1032
|
-
air_loop_info['return_fan_type'] = 'VV'
|
|
1033
|
-
fan = air_loop.returnFan.get.to_FanVariableVolume.get
|
|
1034
|
-
end
|
|
1035
|
-
air_loop_info['return_fan_name'] = fan.name.get
|
|
1036
|
-
air_loop_info['return_fan_efficiency'] = fan.fanEfficiency
|
|
1037
|
-
air_loop_info['return_fan_motor_efficiency'] = fan.motorEfficiency
|
|
1038
|
-
air_loop_info['return_fan_pressure_rise'] = fan.pressureRise
|
|
1039
|
-
air_loop_info['return_fan_max_air_flow_rate_m_cu_per_s'] = -1.0
|
|
1040
|
-
sql_command = " SELECT RowName FROM TabularDataWithStrings
|
|
1041
|
-
WHERE ReportName='EquipmentSummary'
|
|
1042
|
-
AND ReportForString='Entire Facility'
|
|
1043
|
-
AND TableName='Fans'
|
|
1044
|
-
AND ColumnName='Max Air Flow Rate'
|
|
1045
|
-
AND Units='m3/s' "
|
|
1046
|
-
max_air_flow_info = model.sqlFile.get.execAndReturnVectorOfString(sql_command)
|
|
1047
|
-
max_air_flow_info = validate_optional(max_air_flow_info, model, 'N/A')
|
|
1048
|
-
if max_air_flow_info != 'N/A'
|
|
1049
|
-
if max_air_flow_info.include?(air_loop_info['return_fan_name'].to_s.upcase.to_s)
|
|
1050
|
-
sql_command = " SELECT Value FROM TabularDataWithStrings
|
|
1051
|
-
WHERE ReportName='EquipmentSummary'
|
|
1052
|
-
AND ReportForString='Entire Facility'
|
|
1053
|
-
AND TableName='Fans'
|
|
1054
|
-
AND ColumnName='Max Air Flow Rate'
|
|
1055
|
-
AND Units='m3/s'
|
|
1056
|
-
AND RowName='#{air_loop_info['return_fan_name'].upcase}' "
|
|
1057
|
-
air_loop_info['return_fan_max_air_flow_rate_m_cu_per_s'] = model.sqlFile.get.execAndReturnFirstDouble(sql_command).get
|
|
1058
|
-
sql_coommand = " SELECT Value FROM TabularDataWithStrings
|
|
1059
|
-
WHERE ReportName='EquipmentSummary'
|
|
1060
|
-
AND ReportForString='Entire Facility'
|
|
1061
|
-
AND TableName='Fans'
|
|
1062
|
-
AND ColumnName='Rated Electric Power'
|
|
1063
|
-
AND Units='W'
|
|
1064
|
-
AND RowName='#{air_loop_info['return_fan_name'].upcase}' "
|
|
1065
|
-
air_loop_info['return_fan_rated_electric_power_w'] = model.sqlFile.get.execAndReturnFirstDouble(sql_coommand).get
|
|
1066
|
-
else
|
|
1067
|
-
@error_warning << "#{air_loop_info['return_fan_name']} does not exist in sql file WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Fans' AND ColumnName='Max Air Flow Rate' AND Units='m3/s'"
|
|
1068
|
-
end
|
|
1069
|
-
else
|
|
1070
|
-
@error_warning << "max_air_flow_info is nil because the following sql statement returned nil: RowName FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Fans' AND ColumnName='Max Air Flow Rate' AND Units='m3/s' "
|
|
1071
|
-
end
|
|
1072
|
-
end
|
|
1073
|
-
|
|
1074
|
-
# economizer
|
|
1075
|
-
air_loop_info['economizer_name'] = air_loop.airLoopHVACOutdoorAirSystem.get.getControllerOutdoorAir.name.get
|
|
1076
|
-
air_loop_info['economizer_control_type'] = air_loop.airLoopHVACOutdoorAirSystem.get.getControllerOutdoorAir.getEconomizerControlType
|
|
1077
|
-
|
|
1078
|
-
# Include air system costs from ventilation costs
|
|
1079
|
-
sysNum = air_loop_info['name'][4].to_i
|
|
1080
|
-
sysCostInfo = cost_result['ventilation']["system_#{sysNum}".to_s.to_sym].detect { |currsysCostInfo| currsysCostInfo[:name].to_s.downcase == air_loop_info['name'].downcase }
|
|
1081
|
-
if sysCostInfo.nil? && sysNum == 4
|
|
1082
|
-
# 04-Nov-2019 JTB: CK says that system_4 is handled the same way as system_1 so may see "Sys_4" substrings in the name of a system_1 airloop!
|
|
1083
|
-
sysCostInfo = cost_result['ventilation']['system_1'.to_s.to_sym].detect { |currsysCostInfo| currsysCostInfo[:name].to_s.downcase == air_loop_info['name'].downcase }
|
|
1084
|
-
raise("System name \"#{air_loop_info['name']}\" not found in ventilation cost info for system_1") if sysCostInfo.nil?
|
|
1085
|
-
else
|
|
1086
|
-
raise("System name \"#{air_loop_info['name']}\" not found in ventilation cost info for System_#{sysNum}") if sysCostInfo.nil?
|
|
1087
|
-
end
|
|
1088
|
-
if !sysCostInfo.nil?
|
|
1089
|
-
air_loop_info['airloop_flow_m3_per_s'] = sysCostInfo[:airloop_flow_m3_per_s]
|
|
1090
|
-
air_loop_info['num_rooftop_units'] = sysCostInfo[:num_rooftop_units]
|
|
1091
|
-
# air_loop_info['ahu_counter'] = sysCostInfo[:ahu_counter]
|
|
1092
|
-
# air_loop_info['ahu_l_per_s'] = sysCostInfo[:ahu_l_per_s]
|
|
1093
|
-
air_loop_info['base_ahu_cost'] = sysCostInfo[:base_ahu_cost]
|
|
1094
|
-
air_loop_info['revised_base_ahu_cost'] = sysCostInfo[:revised_base_ahu_cost]
|
|
1095
|
-
# Promote hrv data from it's own hash, if it isn't empty!
|
|
1096
|
-
if sysCostInfo[:hrv].empty?
|
|
1097
|
-
air_loop_info['hrv_type'] = sysCostInfo[:hrv]
|
|
1098
|
-
else
|
|
1099
|
-
air_loop_info['hrv_type'] = sysCostInfo[:hrv][:hrv_type]
|
|
1100
|
-
air_loop_info['hrv_name'] = sysCostInfo[:hrv][:hrv_name]
|
|
1101
|
-
air_loop_info['hrv_size_m3ps'] = sysCostInfo[:hrv][:hrv_size_m3ps]
|
|
1102
|
-
air_loop_info['hrv_return_fan_size_m3ps'] = sysCostInfo[:hrv][:hrv_return_fan_size_m3ps]
|
|
1103
|
-
air_loop_info['hrv_cost'] = sysCostInfo[:hrv][:hrv_cost]
|
|
1104
|
-
air_loop_info['revised_hrv_cost'] = sysCostInfo[:hrv][:revised_hrv_cost]
|
|
1105
|
-
end
|
|
1106
|
-
end
|
|
1107
|
-
|
|
1108
|
-
# Also include hrv_return_ducting information from ventilation costs.
|
|
1109
|
-
# Note that there can be multiple hrv return duct runs for the same air loop name
|
|
1110
|
-
# (on different floors) plus a system level trunk return duct section.
|
|
1111
|
-
hrv_retduct_byflr = cost_result['ventilation']['hrv_return_ducting'.to_sym].reject { |arr| arr[:floor].nil? }
|
|
1112
|
-
hrv_retduct_bysys = cost_result['ventilation']['hrv_return_ducting'.to_sym].reject { |arr| arr[:air_system].nil? }
|
|
1113
|
-
|
|
1114
|
-
# Floor level hrv return ducts...
|
|
1115
|
-
hrv_retduct_byflr.each do |arr1|
|
|
1116
|
-
flrNum = arr1[:floor].to_s[15].to_i
|
|
1117
|
-
airsys_byflr = arr1[:air_systems].select { |arr2| arr2[:air_system].to_s.downcase == air_loop_info['name'].to_s.downcase }
|
|
1118
|
-
airsys_byflr.each do |arr3|
|
|
1119
|
-
air_loop_info["flr#{flrNum}_floor_mult".to_sym] = arr3[:floor_mult]
|
|
1120
|
-
if !arr3[:hrv_ret_trunk].empty?
|
|
1121
|
-
# The hrv_ret_trunk embedded hash is not empty -- promote it
|
|
1122
|
-
air_loop_info["flr#{flrNum}_hrv_ret_trunk_len_m".to_sym] = arr3[:hrv_ret_trunk][:duct_length_m]
|
|
1123
|
-
air_loop_info["flr#{flrNum}_hrv_ret_trunk_dia_in".to_sym] = arr3[:hrv_ret_trunk][:dia_in]
|
|
1124
|
-
air_loop_info["flr#{flrNum}_hrv_ret_trunk_cost".to_sym] = arr3[:hrv_ret_trunk][:cost]
|
|
1125
|
-
# If not don't include anything!
|
|
1126
|
-
end
|
|
1127
|
-
# The individual thermal zone (by floor) ret duct distribution (tz_dist) added to zone_table
|
|
1128
|
-
end
|
|
1129
|
-
end
|
|
1130
|
-
# System level trunk hrv return ducts...
|
|
1131
|
-
hrv_retduct_bysys.each do |arr1|
|
|
1132
|
-
if arr1[:air_system].to_s.downcase == air_loop_info['name'].to_s.downcase
|
|
1133
|
-
air_loop_info[:hrv_building_trunk_length_m] = arr1[:hrv_building_trunk_length_m]
|
|
1134
|
-
air_loop_info[:hrv_building_trunk_dia_in] = arr1[:hrv_building_trunk_dia_in]
|
|
1135
|
-
air_loop_info["sys#{sysNum}_hrv_ret_duct_cost".to_sym] = arr1[:cost]
|
|
1136
|
-
end
|
|
1137
|
-
end
|
|
1138
|
-
|
|
1139
|
-
table << air_loop_info
|
|
1140
|
-
end
|
|
1141
|
-
|
|
1142
|
-
return table
|
|
1143
|
-
end
|
|
1144
|
-
|
|
1145
|
-
def coil_table
|
|
1146
|
-
table = get_sql_table_to_json(model, 'CoilSizingDetails', 'Entire Facility', 'Coils')['table']
|
|
1147
|
-
model.getAirLoopHVACs.sort.each do |air_loop|
|
|
1148
|
-
air_loop.supplyComponents.each do |supply_comp|
|
|
1149
|
-
object = get_actual_child_object(supply_comp)
|
|
1150
|
-
coil_types = [
|
|
1151
|
-
'openstudio::model::CoilCoolingCooledBeam',
|
|
1152
|
-
'openstudio::model::CoilCoolingDXMultiSpeed',
|
|
1153
|
-
'openstudio::model::CoilCoolingDXSingleSpeed',
|
|
1154
|
-
'openstudio::model::CoilCoolingDXTwoSpeed',
|
|
1155
|
-
'openstudio::model::CoilCoolingDXTwoStageWithHumidityControlMode',
|
|
1156
|
-
'openstudio::model::CoilCoolingDXVariableSpeed',
|
|
1157
|
-
'openstudio::model::CoilCoolingFourPipeBeam',
|
|
1158
|
-
'openstudio::model::CoilCoolingLowTempRadiantConstFlow',
|
|
1159
|
-
'openstudio::model::CoilCoolingLowTempRadiantVarFlow',
|
|
1160
|
-
'openstudio::model::CoilHeatingDesuperheater',
|
|
1161
|
-
'openstudio::model::CoilHeatingDXMultiSpeed',
|
|
1162
|
-
'openstudio::model::CoilHeatingDXSingleSpeed',
|
|
1163
|
-
'openstudio::model::CoilHeatingDXVariableSpeed',
|
|
1164
|
-
'openstudio::model::CoilHeatingElectric',
|
|
1165
|
-
'openstudio::model::CoilHeatingFourPipeBeam',
|
|
1166
|
-
'openstudio::model::CoilHeatingGas',
|
|
1167
|
-
'openstudio::model::CoilHeatingGasMultiStage',
|
|
1168
|
-
'openstudio::model::CoilHeatingLowTempRadiantConstFlow',
|
|
1169
|
-
'openstudio::model::CoilHeatingLowTempRadiantVarFlow',
|
|
1170
|
-
'openstudio::model::CoilHeatingWaterBaseboard',
|
|
1171
|
-
'openstudio::model::CoilHeatingWaterBaseboardRadiant',
|
|
1172
|
-
'openstudio::model::CoilSystemCoolingDXHeatExchangerAssisted',
|
|
1173
|
-
'openstudio::model::CoilSystemCoolingWaterHeatExchangerAssisted',
|
|
1174
|
-
'openstudio::model::CoilWaterHeatingDesuperheater'
|
|
1175
|
-
]
|
|
1176
|
-
# Is it a heating coil?
|
|
1177
|
-
if coil_types.include?(object.class.name) && object.class.name.include?('Heating')
|
|
1178
|
-
|
|
1179
|
-
case object.class.name
|
|
1180
|
-
when 'CoilHeatingGas'
|
|
1181
|
-
coil = {}
|
|
1182
|
-
coil['name'] = object.get.name
|
|
1183
|
-
coil['type'] = 'Gas'
|
|
1184
|
-
coil['efficency'] = gas.gasBurnerEfficiency
|
|
1185
|
-
sql_command = "SELECT Value FROM TabularDataWithStrings
|
|
1186
|
-
WHERE ReportName='EquipmentSummary'
|
|
1187
|
-
AND ReportForString='Entire Facility'
|
|
1188
|
-
AND TableName='Heating Coils'
|
|
1189
|
-
AND ColumnName='Nominal Total Capacity'
|
|
1190
|
-
AND RowName='#{coil['name'].to_s.upcase}'"
|
|
1191
|
-
coil['nominal_total_capacity_w'] = validate_optional(@model.sqlFile.get.execAndReturnFirstDouble(sql_command), @model, -1.0)
|
|
1192
|
-
|
|
1193
|
-
when 'CoilHeatingElectric'
|
|
1194
|
-
when 'CoilHeatingWater'
|
|
1195
|
-
end
|
|
1196
|
-
|
|
1197
|
-
if supply_comp.to_CoilHeatingGas.is_initialized
|
|
1198
|
-
coil = {}
|
|
1199
|
-
air_loop_info['heating_coils']['coil_heating_gas'] << coil
|
|
1200
|
-
gas = supply_comp.to_CoilHeatingGas.get
|
|
1201
|
-
coil['name'] = gas.name.get
|
|
1202
|
-
coil['type'] = 'Gas'
|
|
1203
|
-
coil['efficency'] = gas.gasBurnerEfficiency
|
|
1204
|
-
sql_command = "SELECT Value FROM TabularDataWithStrings
|
|
1205
|
-
WHERE ReportName='EquipmentSummary'
|
|
1206
|
-
AND ReportForString='Entire Facility'
|
|
1207
|
-
AND TableName='Heating Coils'
|
|
1208
|
-
AND ColumnName='Nominal Total Capacity'
|
|
1209
|
-
AND RowName='#{coil['name'].to_s.upcase}'"
|
|
1210
|
-
coil['nominal_total_capacity_w'] = validate_optional(@model.sqlFile.get.execAndReturnFirstDouble(sql_command), @model, -1.0)
|
|
1211
|
-
end
|
|
1212
|
-
if supply_comp.to_CoilHeatingElectric.is_initialized
|
|
1213
|
-
coil = {}
|
|
1214
|
-
air_loop_info['heating_coils']['coil_heating_electric'] << coil
|
|
1215
|
-
electric = supply_comp.to_CoilHeatingElectric.get
|
|
1216
|
-
coil['name'] = electric.name.get
|
|
1217
|
-
coil['type'] = 'Electric'
|
|
1218
|
-
sql_command = "SELECT Value FROM TabularDataWithStrings
|
|
1219
|
-
WHERE ReportName='EquipmentSummary'
|
|
1220
|
-
AND ReportForString='Entire Facility'
|
|
1221
|
-
AND TableName='Heating Coils'
|
|
1222
|
-
AND ColumnName='Nominal Total Capacity'
|
|
1223
|
-
AND RowName='#{coil['name'].to_s.upcase}'"
|
|
1224
|
-
coil['nominal_total_capacity_w'] = validate_optional(model.sqlFile.get.execAndReturnFirstDouble(sql_command), model, -1.0)
|
|
1225
|
-
end
|
|
1226
|
-
if supply_comp.to_CoilHeatingWater.is_initialized
|
|
1227
|
-
coil = {}
|
|
1228
|
-
air_loop_info['heating_coils']['coil_heating_water'] << coil
|
|
1229
|
-
water = supply_comp.to_CoilHeatingWater.get
|
|
1230
|
-
coil['name'] = water.name.get
|
|
1231
|
-
coil['type'] = 'Water'
|
|
1232
|
-
sql_command = "SELECT Value FROM TabularDataWithStrings
|
|
1233
|
-
WHERE ReportName='EquipmentSummary'
|
|
1234
|
-
AND ReportForString='Entire Facility'
|
|
1235
|
-
AND TableName='Heating Coils'
|
|
1236
|
-
AND ColumnName='Nominal Total Capacity'
|
|
1237
|
-
AND RowName='#{coil['name'].to_s.upcase}'"
|
|
1238
|
-
coil['nominal_total_capacity_w'] = validate_optional(model.sqlFile.get.execAndReturnFirstDouble(sql_command), model, -1.0)
|
|
1239
|
-
end
|
|
1240
|
-
end
|
|
1241
|
-
|
|
1242
|
-
# I dont think i need to get the type of heating coil from the sql file, because the coils are differentiated by class, and I have hard coded the information
|
|
1243
|
-
# model.sqlFile().get().execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName= 'Heating Coils' AND ColumnName='Type' ").get #padmussen to complete #AND RowName='#{air_loop_info["heating_coils"]["name"].upcase}'
|
|
1244
|
-
#
|
|
1245
|
-
# Collect all the fans into the the array.
|
|
1246
|
-
air_loop.supplyComponents.each do |curr_supply_comp|
|
|
1247
|
-
if curr_supply_comp.to_CoilCoolingDXSingleSpeed.is_initialized
|
|
1248
|
-
coil = {}
|
|
1249
|
-
air_loop_info['cooling_coils']['dx_single_speed'] << coil
|
|
1250
|
-
single_speed = curr_supply_comp.to_CoilCoolingDXSingleSpeed.get
|
|
1251
|
-
coil['name'] = single_speed.name.get
|
|
1252
|
-
coil['cop'] = single_speed.getRatedCOP.get
|
|
1253
|
-
sql_command = "SELECT Value FROM TabularDataWithStrings
|
|
1254
|
-
WHERE ReportName='EquipmentSummary'
|
|
1255
|
-
AND ReportForString='Entire Facility'
|
|
1256
|
-
AND TableName='Cooling Coils'
|
|
1257
|
-
AND ColumnName='Nominal Total Capacity'
|
|
1258
|
-
AND RowName='#{coil['name'].to_s.upcase}'"
|
|
1259
|
-
coil['nominal_total_capacity_w'] = validate_optional(model.sqlFile.get.execAndReturnFirstDouble(sql_command), model, -1.0)
|
|
1260
|
-
end
|
|
1261
|
-
if curr_supply_comp.to_CoilCoolingDXTwoSpeed.is_initialized
|
|
1262
|
-
coil = {}
|
|
1263
|
-
air_loop_info['cooling_coils']['dx_two_speed'] << coil
|
|
1264
|
-
two_speed = curr_supply_comp.to_CoilCoolingDXTwoSpeed.get
|
|
1265
|
-
coil['name'] = two_speed.name.get
|
|
1266
|
-
coil['cop_low'] = two_speed.getRatedLowSpeedCOP.get
|
|
1267
|
-
coil['cop_high'] = two_speed.getRatedHighSpeedCOP.get
|
|
1268
|
-
sql_command = "SELECT Value FROM TabularDataWithStrings
|
|
1269
|
-
WHERE ReportName='EquipmentSummary'
|
|
1270
|
-
AND ReportForString='Entire Facility'
|
|
1271
|
-
AND TableName='Cooling Coils'
|
|
1272
|
-
AND ColumnName='Nominal Total Capacity'
|
|
1273
|
-
AND RowName='#{coil['name'].to_s.upcase}'"
|
|
1274
|
-
coil['nominal_total_capacity_w'] = validate_optional(model.sqlFile.get.execAndReturnFirstDouble(sql_command), model, -1.0)
|
|
1275
|
-
end
|
|
1276
|
-
if curr_supply_comp.to_CoilCoolingWater.is_initialized
|
|
1277
|
-
coil = {}
|
|
1278
|
-
air_loop_info['cooling_coils']['coil_cooling_water'] << coil
|
|
1279
|
-
coil_cooling_water = curr_supply_comp.to_CoilCoolingWater.get
|
|
1280
|
-
coil['name'] = coil_cooling_water.name.get
|
|
1281
|
-
sql_command = "SELECT Value FROM TabularDataWithStrings
|
|
1282
|
-
WHERE ReportName='EquipmentSummary'
|
|
1283
|
-
AND ReportForString='Entire Facility'
|
|
1284
|
-
AND TableName='Cooling Coils'
|
|
1285
|
-
AND ColumnName='Nominal Total Capacity'
|
|
1286
|
-
AND RowName='#{coil['name'].to_s.upcase}'"
|
|
1287
|
-
coil['nominal_total_capacity_w'] = validate_optional(model.sqlFile.get.execAndReturnFirstDouble(sql_command), model, -1.0)
|
|
1288
|
-
sql_command = "SELECT Value FROM TabularDataWithStrings
|
|
1289
|
-
WHERE ReportName='EquipmentSummary'
|
|
1290
|
-
AND ReportForString='Entire Facility'
|
|
1291
|
-
AND TableName='Cooling Coils'
|
|
1292
|
-
AND ColumnName='Nominal Sensible Heat Ratio'
|
|
1293
|
-
AND RowName='#{coil['name'].upcase}' "
|
|
1294
|
-
coil['nominal_sensible_heat_ratio'] = validate_optional(model.sqlFile.get.execAndReturnFirstDouble(sql_command), model, -1.0)
|
|
1295
|
-
end
|
|
1296
|
-
end
|
|
1297
|
-
end
|
|
1298
|
-
end
|
|
1299
|
-
end
|
|
1300
|
-
|
|
1301
|
-
def coil_cost_table(cost_result)
|
|
1302
|
-
sys_coils_table = []
|
|
1303
|
-
(1..7).each do |sysNum|
|
|
1304
|
-
sys_table = cost_result['ventilation']["system_#{sysNum}".to_s.to_sym].reject(&:empty?)
|
|
1305
|
-
sys_table.each do |sysHash|
|
|
1306
|
-
equip_info_table = sysHash[:equipment_info].reject(&:empty?)
|
|
1307
|
-
equip_info_table.each do |equipHash|
|
|
1308
|
-
sysCoilsInfo = {}
|
|
1309
|
-
sys_coils_table << sysCoilsInfo
|
|
1310
|
-
sysCoilsInfo[:sys_type] = sysNum
|
|
1311
|
-
sysCoilsInfo[:sys_name] = equipHash[:name]
|
|
1312
|
-
sysCoilsInfo[:eq_category] = equipHash[:eq_category]
|
|
1313
|
-
sysCoilsInfo[:heating_fuel] = equipHash[:heating_fuel]
|
|
1314
|
-
sysCoilsInfo[:cooling_type] = equipHash[:cooling_type]
|
|
1315
|
-
sysCoilsInfo[:capacity_kw] = equipHash[:capacity_kw]
|
|
1316
|
-
sysCoilsInfo[:coil_cost] = equipHash[:cost]
|
|
1317
|
-
end
|
|
1318
|
-
end
|
|
1319
|
-
end
|
|
1320
|
-
return sys_coils_table
|
|
1321
|
-
end
|
|
1322
|
-
|
|
1323
|
-
def terminal_VAV_cost_table(cost_result)
|
|
1324
|
-
terminal_VAV_table = []
|
|
1325
|
-
(1..7).each do |sysNum|
|
|
1326
|
-
ndx = 0
|
|
1327
|
-
until cost_result['ventilation']["system_#{sysNum}".to_s.to_sym][ndx].nil?
|
|
1328
|
-
ndx1 = 0
|
|
1329
|
-
until cost_result['ventilation']["system_#{sysNum}".to_s.to_sym][ndx][:reheat_recool][ndx1].nil?
|
|
1330
|
-
sysTerminalInfo = {}
|
|
1331
|
-
terminal_VAV_table << sysTerminalInfo
|
|
1332
|
-
sysTerminalInfo[:sys_type] = sysNum
|
|
1333
|
-
sysTerminalInfo[:sys_name] = cost_result['ventilation']["system_#{sysNum}".to_s.to_sym][ndx][:name]
|
|
1334
|
-
sysTerminalInfo[:terminal] = cost_result['ventilation']["system_#{sysNum}".to_s.to_sym][ndx][:reheat_recool][ndx1][:terminal]
|
|
1335
|
-
sysTerminalInfo[:zone_mult] = cost_result['ventilation']["system_#{sysNum}".to_s.to_sym][ndx][:reheat_recool][ndx1][:zone_mult]
|
|
1336
|
-
sysTerminalInfo[:box_type] = cost_result['ventilation']["system_#{sysNum}".to_s.to_sym][ndx][:reheat_recool][ndx1][:box_type]
|
|
1337
|
-
sysTerminalInfo[:box_name] = cost_result['ventilation']["system_#{sysNum}".to_s.to_sym][ndx][:reheat_recool][ndx1][:box_name]
|
|
1338
|
-
sysTerminalInfo[:unit_size_kw] = cost_result['ventilation']["system_#{sysNum}".to_s.to_sym][ndx][:reheat_recool][ndx1][:unit_info][:size_kw]
|
|
1339
|
-
sysTerminalInfo[:unit_air_flow_m3_per_s] = cost_result['ventilation']["system_#{sysNum}".to_s.to_sym][ndx][:reheat_recool][ndx1][:unit_info][:air_flow_m3_per_s]
|
|
1340
|
-
sysTerminalInfo[:unit_pipe_dist_m] = cost_result['ventilation']["system_#{sysNum}".to_s.to_sym][ndx][:reheat_recool][ndx1][:unit_info][:pipe_dist_m]
|
|
1341
|
-
sysTerminalInfo[:unit_elect_dist_m] = cost_result['ventilation']["system_#{sysNum}".to_s.to_sym][ndx][:reheat_recool][ndx1][:unit_info][:elect_dist_m]
|
|
1342
|
-
sysTerminalInfo[:unit_num_units] = cost_result['ventilation']["system_#{sysNum}".to_s.to_sym][ndx][:reheat_recool][ndx1][:unit_info][:num_units]
|
|
1343
|
-
sysTerminalInfo[:cost] = cost_result['ventilation']["system_#{sysNum}".to_s.to_sym][ndx][:reheat_recool][ndx1][:cost]
|
|
1344
|
-
ndx1 += 1
|
|
1345
|
-
end
|
|
1346
|
-
ndx += 1
|
|
1347
|
-
end
|
|
1348
|
-
end
|
|
1349
|
-
return terminal_VAV_table
|
|
1350
|
-
end
|
|
1351
|
-
|
|
1352
|
-
def trunk_ducts_cost_table(cost_result)
|
|
1353
|
-
# Create a trunk ducts cost table that combines the building trunk duct with the floor trunk ducts
|
|
1354
|
-
trunk_ducts_table = []
|
|
1355
|
-
ndx = 0
|
|
1356
|
-
insert_bld_trunk_duct = true
|
|
1357
|
-
until cost_result['ventilation']['floor_trunk_ducts'.to_sym][0][ndx].nil?
|
|
1358
|
-
if insert_bld_trunk_duct
|
|
1359
|
-
trunkDuctsInfo = {}
|
|
1360
|
-
trunk_ducts_table << trunkDuctsInfo
|
|
1361
|
-
trunkDuctsInfo[:Floor] = 'building_trunk'
|
|
1362
|
-
trunkDuctsInfo[:Predominant_space_type] = 'n/a'
|
|
1363
|
-
trunkDuctsInfo[:SupplyDuctSize_in] = cost_result['ventilation']['trunk_duct'.to_sym][ndx][:DuctSize_in]
|
|
1364
|
-
trunkDuctsInfo[:SupplyDuctLength_m] = cost_result['ventilation']['trunk_duct'.to_sym][ndx][:DuctLength_m]
|
|
1365
|
-
if cost_result['ventilation']['trunk_duct'.to_sym][ndx][:NumberRuns] == 2
|
|
1366
|
-
trunkDuctsInfo[:ReturnDuctSize_in] = cost_result['ventilation']['trunk_duct'.to_sym][ndx][:DuctSize_in]
|
|
1367
|
-
trunkDuctsInfo[:ReturnDuctLength_m] = cost_result['ventilation']['trunk_duct'.to_sym][ndx][:DuctLength_m]
|
|
1368
|
-
else
|
|
1369
|
-
trunkDuctsInfo[:ReturnDuctSize_in] = 0
|
|
1370
|
-
trunkDuctsInfo[:ReturnDuctLength_m] = 0
|
|
1371
|
-
end
|
|
1372
|
-
trunkDuctsInfo[:TotalDuctCost] = cost_result['ventilation']['trunk_duct'.to_sym][ndx][:DuctCost]
|
|
1373
|
-
trunkDuctsInfo[:Multiplier] = 1.0
|
|
1374
|
-
insert_bld_trunk_duct = false
|
|
1375
|
-
else
|
|
1376
|
-
trunkDuctsInfo = {}
|
|
1377
|
-
trunk_ducts_table << trunkDuctsInfo
|
|
1378
|
-
cost_result['ventilation']['floor_trunk_ducts'.to_sym][0][ndx].each do |k, v|
|
|
1379
|
-
trunkDuctsInfo[k] = v
|
|
1380
|
-
end
|
|
1381
|
-
ndx += 1
|
|
1382
|
-
end
|
|
1383
|
-
end
|
|
1384
|
-
return trunk_ducts_table
|
|
1385
|
-
end
|
|
1386
|
-
|
|
1387
|
-
def plant_loop_table(model)
|
|
1388
|
-
table = []
|
|
1389
|
-
model.getPlantLoops.sort.each do |plant_loop|
|
|
1390
|
-
plant_loop_info = {}
|
|
1391
|
-
table << plant_loop_info
|
|
1392
|
-
plant_loop_info['name'] = plant_loop.name.get
|
|
1393
|
-
|
|
1394
|
-
sizing = plant_loop.sizingPlant
|
|
1395
|
-
plant_loop_info['design_loop_exit_temperature'] = sizing.getDesignLoopExitTemperature.value
|
|
1396
|
-
plant_loop_info['loop_design_temperature_difference'] = sizing.getLoopDesignTemperatureDifference.value
|
|
1397
|
-
|
|
1398
|
-
# Create Container for plant equipment arrays.
|
|
1399
|
-
plant_loop_info['pumps'] = []
|
|
1400
|
-
plant_loop_info['boilers'] = []
|
|
1401
|
-
plant_loop_info['chiller_electric_eir'] = []
|
|
1402
|
-
plant_loop_info['cooling_tower_single_speed'] = []
|
|
1403
|
-
plant_loop_info['water_heater_mixed'] = []
|
|
1404
|
-
plant_loop.supplyComponents.each do |supply_comp|
|
|
1405
|
-
# Collect Constant Speed
|
|
1406
|
-
if supply_comp.to_PumpConstantSpeed.is_initialized
|
|
1407
|
-
pump = supply_comp.to_PumpConstantSpeed.get
|
|
1408
|
-
pump_info = {}
|
|
1409
|
-
plant_loop_info['pumps'] << pump_info
|
|
1410
|
-
pump_info['name'] = pump.name.get
|
|
1411
|
-
pump_info['type'] = 'Pump:ConstantSpeed'
|
|
1412
|
-
sql_command = " SELECT Value FROM TabularDataWithStrings
|
|
1413
|
-
WHERE ReportName='EquipmentSummary'
|
|
1414
|
-
AND ReportForString='Entire Facility'
|
|
1415
|
-
AND TableName='Pumps'
|
|
1416
|
-
AND ColumnName='Head'
|
|
1417
|
-
AND RowName='#{pump_info['name'].upcase}' "
|
|
1418
|
-
pump_info['head_pa'] = validate_optional(model.sqlFile.get.execAndReturnFirstDouble(sql_command), model, -1.0)
|
|
1419
|
-
sql_command = " SELECT Value FROM TabularDataWithStrings
|
|
1420
|
-
WHERE ReportName='EquipmentSummary'
|
|
1421
|
-
AND ReportForString='Entire Facility'
|
|
1422
|
-
AND TableName='Pumps'
|
|
1423
|
-
AND ColumnName='Water Flow'
|
|
1424
|
-
AND RowName='#{pump_info['name'].upcase}' "
|
|
1425
|
-
pump_info['water_flow_m_cu_per_s'] = validate_optional(model.sqlFile.get.execAndReturnFirstDouble(sql_command), model, -1.0)
|
|
1426
|
-
sql_command = " SELECT Value FROM TabularDataWithStrings
|
|
1427
|
-
WHERE ReportName='EquipmentSummary'
|
|
1428
|
-
AND ReportForString='Entire Facility'
|
|
1429
|
-
AND TableName='Pumps'
|
|
1430
|
-
AND ColumnName='Electric Power'
|
|
1431
|
-
AND RowName='#{pump_info['name'].upcase}' "
|
|
1432
|
-
pump_info['electric_power_w'] = validate_optional(model.sqlFile.get.execAndReturnFirstDouble(sql_command), model, -1.0)
|
|
1433
|
-
pump_info['motor_efficency'] = pump.getMotorEfficiency.value
|
|
1434
|
-
end
|
|
1435
|
-
|
|
1436
|
-
# Collect Variable Speed
|
|
1437
|
-
if supply_comp.to_PumpVariableSpeed.is_initialized
|
|
1438
|
-
pump = supply_comp.to_PumpVariableSpeed.get
|
|
1439
|
-
pump_info = {}
|
|
1440
|
-
plant_loop_info['pumps'] << pump_info
|
|
1441
|
-
pump_info['name'] = pump.name.get
|
|
1442
|
-
pump_info['type'] = 'Pump:VariableSpeed'
|
|
1443
|
-
sql_command = " SELECT Value FROM TabularDataWithStrings
|
|
1444
|
-
WHERE ReportName='EquipmentSummary'
|
|
1445
|
-
AND ReportForString='Entire Facility'
|
|
1446
|
-
AND TableName='Pumps'
|
|
1447
|
-
AND ColumnName='Head'
|
|
1448
|
-
AND RowName='#{pump_info['name'].upcase}' "
|
|
1449
|
-
pump_info['head_pa'] = validate_optional(model.sqlFile.get.execAndReturnFirstDouble(sql_command), model, -1.0)
|
|
1450
|
-
sql_command = " SELECT Value FROM TabularDataWithStrings
|
|
1451
|
-
WHERE ReportName='EquipmentSummary'
|
|
1452
|
-
AND ReportForString='Entire Facility'
|
|
1453
|
-
AND TableName='Pumps'
|
|
1454
|
-
AND ColumnName='Water Flow'
|
|
1455
|
-
AND RowName='#{pump_info['name'].upcase}' "
|
|
1456
|
-
pump_info['water_flow_m_cu_per_s'] = validate_optional(model.sqlFile.get.execAndReturnFirstDouble(sql_command), model, -1.0)
|
|
1457
|
-
sql_command = " SELECT Value FROM TabularDataWithStrings
|
|
1458
|
-
WHERE ReportName='EquipmentSummary'
|
|
1459
|
-
AND ReportForString='Entire Facility'
|
|
1460
|
-
AND TableName='Pumps'
|
|
1461
|
-
AND ColumnName='Electric Power'
|
|
1462
|
-
AND RowName='#{pump_info['name'].upcase}' "
|
|
1463
|
-
pump_info['electric_power_w'] = validate_optional(model.sqlFile.get.execAndReturnFirstDouble(sql_command), model, -1.0)
|
|
1464
|
-
pump_info['motor_efficency'] = pump.getMotorEfficiency.value
|
|
1465
|
-
end
|
|
1466
|
-
|
|
1467
|
-
# Collect HotWaterBoilers
|
|
1468
|
-
if supply_comp.to_BoilerHotWater.is_initialized
|
|
1469
|
-
boiler = supply_comp.to_BoilerHotWater.get
|
|
1470
|
-
boiler_info = {}
|
|
1471
|
-
plant_loop_info['boilers'] << boiler_info
|
|
1472
|
-
boiler_info['name'] = boiler.name.get
|
|
1473
|
-
boiler_info['type'] = 'Boiler:HotWater'
|
|
1474
|
-
boiler_info['fueltype'] = boiler.fuelType
|
|
1475
|
-
boiler_info['nominal_capacity'] = validate_optional(boiler.nominalCapacity, model, -1.0)
|
|
1476
|
-
end
|
|
1477
|
-
|
|
1478
|
-
# Collect ChillerElectricEIR
|
|
1479
|
-
if supply_comp.to_ChillerElectricEIR.is_initialized
|
|
1480
|
-
chiller = supply_comp.to_ChillerElectricEIR.get
|
|
1481
|
-
chiller_info = {}
|
|
1482
|
-
plant_loop_info['chiller_electric_eir'] << chiller_info
|
|
1483
|
-
chiller_info['name'] = chiller.name.get
|
|
1484
|
-
chiller_info['type'] = 'Chiller:Electric:EIR'
|
|
1485
|
-
chiller_info['reference_capacity'] = validate_optional(chiller.referenceCapacity, model, -1.0)
|
|
1486
|
-
chiller_info['reference_leaving_chilled_water_temperature'] = chiller.referenceLeavingChilledWaterTemperature
|
|
1487
|
-
end
|
|
1488
|
-
|
|
1489
|
-
# Collect CoolingTowerSingleSpeed
|
|
1490
|
-
if supply_comp.to_CoolingTowerSingleSpeed.is_initialized
|
|
1491
|
-
coolingTower = supply_comp.to_CoolingTowerSingleSpeed.get
|
|
1492
|
-
coolingTower_info = {}
|
|
1493
|
-
plant_loop_info['cooling_tower_single_speed'] << coolingTower_info
|
|
1494
|
-
coolingTower_info['name'] = coolingTower.name.get
|
|
1495
|
-
coolingTower_info['type'] = 'CoolingTower:SingleSpeed'
|
|
1496
|
-
coolingTower_info['fan_power_at_design_air_flow_rate'] = validate_optional(coolingTower.fanPoweratDesignAirFlowRate, model, -1.0)
|
|
1497
|
-
end
|
|
1498
|
-
|
|
1499
|
-
# Collect WaterHeaterMixed
|
|
1500
|
-
if supply_comp.to_WaterHeaterMixed.is_initialized
|
|
1501
|
-
waterHeaterMixed = supply_comp.to_WaterHeaterMixed.get
|
|
1502
|
-
waterHeaterMixed_info = {}
|
|
1503
|
-
plant_loop_info['water_heater_mixed'] << waterHeaterMixed_info
|
|
1504
|
-
waterHeaterMixed_info['name'] = waterHeaterMixed.name.get
|
|
1505
|
-
waterHeaterMixed_info['type'] = 'WaterHeater:Mixed'
|
|
1506
|
-
waterHeaterMixed_info['heater_thermal_efficiency'] = waterHeaterMixed.heaterThermalEfficiency.get unless waterHeaterMixed.heaterThermalEfficiency.empty?
|
|
1507
|
-
waterHeaterMixed_info['heater_fuel_type'] = waterHeaterMixed.heaterFuelType
|
|
1508
|
-
end
|
|
1509
|
-
end
|
|
1510
|
-
end
|
|
1511
|
-
return table
|
|
1512
|
-
end
|
|
1513
|
-
|
|
1514
|
-
def eplusout_err_table(model)
|
|
1515
|
-
table = []
|
|
1516
|
-
warnings = model.sqlFile.get.execAndReturnVectorOfString("SELECT ErrorMessage FROM Errors WHERE ErrorType='0' ")
|
|
1517
|
-
warnings = validate_optional(warnings, model, 'N/A')
|
|
1518
|
-
unless warnings == 'N/A'
|
|
1519
|
-
messages = model.sqlFile.get.execAndReturnVectorOfString("SELECT ErrorMessage FROM Errors WHERE ErrorType='0' ").get
|
|
1520
|
-
messages.each do |message|
|
|
1521
|
-
table << { 'error_type' => 'warning', 'message' => message }
|
|
1522
|
-
end
|
|
1523
|
-
messages = model.sqlFile.get.execAndReturnVectorOfString("SELECT ErrorMessage FROM Errors WHERE ErrorType='1' ").get
|
|
1524
|
-
messages.each do |message|
|
|
1525
|
-
table << { 'error_type' => 'severe', 'message' => message }
|
|
1526
|
-
end
|
|
1527
|
-
messages = model.sqlFile.get.execAndReturnVectorOfString("SELECT ErrorMessage FROM Errors WHERE ErrorType='2' ").get
|
|
1528
|
-
messages.each do |message|
|
|
1529
|
-
table << { 'error_type' => 'fatal', 'message' => message }
|
|
1530
|
-
end
|
|
1531
|
-
end
|
|
1532
|
-
return table
|
|
1533
|
-
end
|
|
1534
|
-
|
|
1535
|
-
def energy_peak_data
|
|
1536
|
-
# Primary heaing source
|
|
1537
|
-
data = {}
|
|
1538
|
-
command = "SELECT Value
|
|
1539
|
-
FROM TabularDataWithStrings
|
|
1540
|
-
WHERE ReportName='LEEDsummary'
|
|
1541
|
-
AND ReportForString='Entire Facility'
|
|
1542
|
-
AND TableName='Sec1.1A-General Information'
|
|
1543
|
-
AND RowName = 'Principal Heating Source'
|
|
1544
|
-
AND ColumnName='Data'"
|
|
1545
|
-
value = @sqlite_file.get.execAndReturnFirstString(command)
|
|
1546
|
-
# make sure all the data are availalbe
|
|
1547
|
-
|
|
1548
|
-
data['energy_principal_heating_source'] = 'unknown'
|
|
1549
|
-
unless value.empty?
|
|
1550
|
-
data['energy_principal_heating_source'] = value.get
|
|
1551
|
-
end
|
|
1552
|
-
|
|
1553
|
-
# Peaks
|
|
1554
|
-
electric_peak = @model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM tabulardatawithstrings WHERE ReportName='EnergyMeters'" \
|
|
1555
|
-
" AND ReportForString='Entire Facility' AND TableName='Annual and Peak Values - Electricity' AND RowName='Electricity:Facility'" \
|
|
1556
|
-
" AND ColumnName='Electricity Maximum Value' AND Units='W'")
|
|
1557
|
-
natural_gas_peak = @model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM tabulardatawithstrings WHERE ReportName='EnergyMeters'" \
|
|
1558
|
-
" AND ReportForString='Entire Facility' AND TableName='Annual and Peak Values - Natural Gas' AND RowName='NaturalGas:Facility'" \
|
|
1559
|
-
" AND ColumnName='Natural Gas Maximum Value' AND Units='W'")
|
|
1560
|
-
data['energy_peak_electric_w_per_m_sq'] = electric_peak.empty? ? 0.0 : electric_peak.get / @conditioned_floor_area_m_sq
|
|
1561
|
-
data['energy_peak_natural_gas_w_per_m_sq'] = natural_gas_peak.empty? ? 0.0 : natural_gas_peak.get / @conditioned_floor_area_m_sq
|
|
1562
|
-
|
|
1563
|
-
# Peak heating load # @todo IMPORTANT NOTE: Peak heating load must be updated if a combination of fuel types is used in a building model.
|
|
1564
|
-
command = "SELECT Value
|
|
1565
|
-
FROM TabularDataWithStrings
|
|
1566
|
-
WHERE ReportName='EnergyMeters'
|
|
1567
|
-
AND ReportForString='Entire Facility'
|
|
1568
|
-
AND TableName='Annual and Peak Values - Electricity'
|
|
1569
|
-
AND RowName='Heating:Electricity'
|
|
1570
|
-
AND ColumnName='Electricity Maximum Value'
|
|
1571
|
-
AND Units='W'"
|
|
1572
|
-
heating_peak_w_electricity = @sqlite_file.get.execAndReturnFirstDouble(command)
|
|
1573
|
-
command = "SELECT Value
|
|
1574
|
-
FROM TabularDataWithStrings
|
|
1575
|
-
WHERE ReportName='EnergyMeters'
|
|
1576
|
-
AND ReportForString='Entire Facility'
|
|
1577
|
-
AND TableName='Annual and Peak Values - Natural Gas'
|
|
1578
|
-
AND RowName='Heating:NaturalGas'
|
|
1579
|
-
AND ColumnName='Natural Gas Maximum Value'
|
|
1580
|
-
AND Units='W'"
|
|
1581
|
-
heating_peak_w_gas = @sqlite_file.get.execAndReturnFirstDouble(command)
|
|
1582
|
-
heating_peak_w = [heating_peak_w_electricity.to_f, heating_peak_w_gas.to_f].max
|
|
1583
|
-
data['heating_peak_w_per_m_sq'] = heating_peak_w / @conditioned_floor_area_m_sq
|
|
1584
|
-
|
|
1585
|
-
# Peak cooling load # @todo IMPORTANT NOTE: Peak cooling load must be updated if a combination of fuel types is used in a building model.
|
|
1586
|
-
command = "SELECT Value
|
|
1587
|
-
FROM TabularDataWithStrings
|
|
1588
|
-
WHERE ReportName='EnergyMeters'
|
|
1589
|
-
AND ReportForString='Entire Facility'
|
|
1590
|
-
AND TableName='Annual and Peak Values - Electricity'
|
|
1591
|
-
AND RowName='Cooling:Electricity'
|
|
1592
|
-
AND ColumnName='Electricity Maximum Value'
|
|
1593
|
-
AND Units='W'"
|
|
1594
|
-
cooling_peak_w_electricity = @sqlite_file.get.execAndReturnFirstDouble(command)
|
|
1595
|
-
command = "SELECT Value
|
|
1596
|
-
FROM TabularDataWithStrings
|
|
1597
|
-
WHERE ReportName='EnergyMeters'
|
|
1598
|
-
AND ReportForString='Entire Facility'
|
|
1599
|
-
AND TableName='Annual and Peak Values - Natural Gas'
|
|
1600
|
-
AND RowName='Cooling:Electricity'
|
|
1601
|
-
AND ColumnName='Electricity Maximum Value'
|
|
1602
|
-
AND Units='W'"
|
|
1603
|
-
cooling_peak_w_gas = @sqlite_file.get.execAndReturnFirstDouble(command)
|
|
1604
|
-
cooling_peak_w = [cooling_peak_w_electricity.to_f, cooling_peak_w_gas.to_f].max
|
|
1605
|
-
data['cooling_peak_w_per_m_sq'] = cooling_peak_w / @conditioned_floor_area_m_sq
|
|
1606
|
-
|
|
1607
|
-
# Peak water system load # @todo IMPORTANT NOTE: Peak water system load must be updated if a combination of fuel types is used in a building model.
|
|
1608
|
-
command = "SELECT Value
|
|
1609
|
-
FROM TabularDataWithStrings
|
|
1610
|
-
WHERE ReportName='EnergyMeters'
|
|
1611
|
-
AND ReportForString='Entire Facility'
|
|
1612
|
-
AND TableName='Annual and Peak Values - Electricity'
|
|
1613
|
-
AND RowName='WaterSystems:Electricity'
|
|
1614
|
-
AND ColumnName='Electricity Maximum Value'
|
|
1615
|
-
AND Units='W'"
|
|
1616
|
-
water_heating_peak_w_electricity = @sqlite_file.get.execAndReturnFirstDouble(command)
|
|
1617
|
-
command = "SELECT Value
|
|
1618
|
-
FROM TabularDataWithStrings
|
|
1619
|
-
WHERE ReportName='EnergyMeters'
|
|
1620
|
-
AND ReportForString='Entire Facility'
|
|
1621
|
-
AND TableName='Annual and Peak Values - Natural Gas'
|
|
1622
|
-
AND RowName='WaterSystems:NaturalGas'
|
|
1623
|
-
AND ColumnName='Natural Gas Maximum Value'
|
|
1624
|
-
AND Units='W'"
|
|
1625
|
-
water_heating_peak_w_gas = @sqlite_file.get.execAndReturnFirstDouble(command)
|
|
1626
|
-
water_heating_peak_w = [water_heating_peak_w_electricity.to_f, water_heating_peak_w_gas.to_f].max
|
|
1627
|
-
data['energy_peak_water_systems_w_per_m_sq'] = water_heating_peak_w / @conditioned_floor_area_m_sq
|
|
1628
|
-
|
|
1629
|
-
#===================================================================================================================
|
|
1630
|
-
# Winter peak values
|
|
1631
|
-
### Get number of timesteps from the model
|
|
1632
|
-
number_of_timesteps_per_hour = 1
|
|
1633
|
-
timestep_second = 3600 / number_of_timesteps_per_hour
|
|
1634
|
-
#===================================================================================================================
|
|
1635
|
-
### Calculate number of timesteps of the whole year, and create dates/times for the whole year
|
|
1636
|
-
number_of_timesteps_of_year = 365 * 24 * number_of_timesteps_per_hour
|
|
1637
|
-
timesteps_of_year = []
|
|
1638
|
-
d = Time.new(2006, 1, 1, 0)
|
|
1639
|
-
(0...number_of_timesteps_of_year).each do |increment|
|
|
1640
|
-
timesteps_of_year << (d + (60 * 60 / number_of_timesteps_per_hour) * increment).strftime('%Y-%m-%d %H:%M')
|
|
1641
|
-
end
|
|
1642
|
-
# Find month of each timestep
|
|
1643
|
-
keys_month = timesteps_of_year.map{ |timestep| DateTime.parse(timestep).to_date.month }
|
|
1644
|
-
# Peak electricity in winter
|
|
1645
|
-
query = "
|
|
1646
|
-
SELECT ReportDataDictionaryIndex
|
|
1647
|
-
FROM ReportDataDictionary
|
|
1648
|
-
WHERE Name=='Electricity:Facility' AND ReportingFrequency == 'Hourly' AND Units == 'J'
|
|
1649
|
-
"
|
|
1650
|
-
index_timestep_electricity = @model.sqlFile.get.execAndReturnVectorOfInt(query).get[0]
|
|
1651
|
-
number_of_timesteps_per_hour = 1
|
|
1652
|
-
if index_timestep_electricity.nil?
|
|
1653
|
-
puts "No hourly or timestep data available. Peak data not available"
|
|
1654
|
-
data['energy_peak_electric_w_per_m_sq_winter'] = 0
|
|
1655
|
-
else
|
|
1656
|
-
### Get timestep output for Electricity:Facility output.
|
|
1657
|
-
query = "
|
|
1658
|
-
Select VariableValue
|
|
1659
|
-
FROM ReportVariableData
|
|
1660
|
-
WHERE
|
|
1661
|
-
ReportVariableDataDictionaryIndex = #{index_timestep_electricity}
|
|
1662
|
-
"
|
|
1663
|
-
timestep_values = @model.sqlFile.get.execAndReturnVectorOfDouble(query).get
|
|
1664
|
-
# Create a hash where its keys are months and values for eahc month are electricity energy use (J) of all timesteps for that month
|
|
1665
|
-
hash_months_electricity = keys_month.zip(timestep_values).group_by(&:first).map{|key, value| [key, value.map(&:last)]}.to_h
|
|
1666
|
-
# Find maximum of electricity energy use (J) among all timesteps for each month of the year
|
|
1667
|
-
monthly_peak_J = hash_months_electricity.map { |key, value| [key, value.max()] }.to_h
|
|
1668
|
-
### Convert J to W of maximum electricity energy use (J)
|
|
1669
|
-
monthly_peak_W = monthly_peak_J.map { |key, value| [key, value / timestep_second] }.to_h
|
|
1670
|
-
# Get winter peak
|
|
1671
|
-
winter_peak_W = [monthly_peak_W[12].to_f, monthly_peak_W[1].to_f, monthly_peak_W[2].to_f].max
|
|
1672
|
-
### Normalize by building floor area
|
|
1673
|
-
annual_peak_W_per_m_sq = winter_peak_W / @btap_data['bldg_conditioned_floor_area_m_sq'].to_f
|
|
1674
|
-
data['energy_peak_electric_w_per_m_sq_winter'] = annual_peak_W_per_m_sq
|
|
1675
|
-
end
|
|
1676
|
-
|
|
1677
|
-
# Peak electricity heating in winter
|
|
1678
|
-
query = "
|
|
1679
|
-
SELECT ReportDataDictionaryIndex
|
|
1680
|
-
FROM ReportDataDictionary
|
|
1681
|
-
WHERE Name=='Heating:Electricity' AND ReportingFrequency == 'Hourly' AND Units == 'J'
|
|
1682
|
-
"
|
|
1683
|
-
index_timestep_electricity = @model.sqlFile.get.execAndReturnVectorOfInt(query).get[0]
|
|
1684
|
-
number_of_timesteps_per_hour = 1
|
|
1685
|
-
if index_timestep_electricity.nil?
|
|
1686
|
-
puts "No hourly or timestep data available. Peak data not available"
|
|
1687
|
-
data['energy_peak_electric_heating_w_per_m_sq_winter'] = 0
|
|
1688
|
-
else
|
|
1689
|
-
### Get timestep output for Electricity:Facility output.
|
|
1690
|
-
query = "
|
|
1691
|
-
Select VariableValue
|
|
1692
|
-
FROM ReportVariableData
|
|
1693
|
-
WHERE
|
|
1694
|
-
ReportVariableDataDictionaryIndex = #{index_timestep_electricity}
|
|
1695
|
-
"
|
|
1696
|
-
timestep_values = @model.sqlFile.get.execAndReturnVectorOfDouble(query).get
|
|
1697
|
-
# Create a hash where its keys are months and values for eahc month are electricity energy use (J) of all timesteps for that month
|
|
1698
|
-
hash_months_electricity = keys_month.zip(timestep_values).group_by(&:first).map{|key, value| [key, value.map(&:last)]}.to_h
|
|
1699
|
-
# Find maximum of electricity energy use (J) among all timesteps for each month of the year
|
|
1700
|
-
monthly_peak_J = hash_months_electricity.map { |key, value| [key, value.max()] }.to_h
|
|
1701
|
-
### Convert J to W of maximum electricity energy use (J)
|
|
1702
|
-
monthly_peak_W = monthly_peak_J.map { |key, value| [key, value / timestep_second] }.to_h
|
|
1703
|
-
# Get winter peak
|
|
1704
|
-
winter_peak_W = [monthly_peak_W[12].to_f, monthly_peak_W[1].to_f, monthly_peak_W[2].to_f].max
|
|
1705
|
-
### Normalize by building floor area
|
|
1706
|
-
annual_peak_W_per_m_sq = winter_peak_W / @btap_data['bldg_conditioned_floor_area_m_sq'].to_f
|
|
1707
|
-
data['energy_peak_electric_heating_w_per_m_sq_winter'] = annual_peak_W_per_m_sq
|
|
1708
|
-
end
|
|
1709
|
-
|
|
1710
|
-
# Peak water system electricity heating in winter
|
|
1711
|
-
query = "
|
|
1712
|
-
SELECT ReportDataDictionaryIndex
|
|
1713
|
-
FROM ReportDataDictionary
|
|
1714
|
-
WHERE Name=='WaterSystems:Electricity' AND ReportingFrequency == 'Hourly' AND Units == 'J'
|
|
1715
|
-
"
|
|
1716
|
-
index_timestep_electricity = @model.sqlFile.get.execAndReturnVectorOfInt(query).get[0]
|
|
1717
|
-
number_of_timesteps_per_hour = 1
|
|
1718
|
-
if index_timestep_electricity.nil?
|
|
1719
|
-
puts "No hourly or timestep data available. Peak data not available"
|
|
1720
|
-
data['energy_peak_electric_water_systems_w_per_m_sq_winter'] = 0
|
|
1721
|
-
else
|
|
1722
|
-
### Get timestep output for Electricity:Facility output.
|
|
1723
|
-
query = "
|
|
1724
|
-
Select VariableValue
|
|
1725
|
-
FROM ReportVariableData
|
|
1726
|
-
WHERE
|
|
1727
|
-
ReportVariableDataDictionaryIndex = #{index_timestep_electricity}
|
|
1728
|
-
"
|
|
1729
|
-
timestep_values = @model.sqlFile.get.execAndReturnVectorOfDouble(query).get
|
|
1730
|
-
# Create a hash where its keys are months and values for eahc month are electricity energy use (J) of all timesteps for that month
|
|
1731
|
-
hash_months_electricity = keys_month.zip(timestep_values).group_by(&:first).map{|key, value| [key, value.map(&:last)]}.to_h
|
|
1732
|
-
# Find maximum of electricity energy use (J) among all timesteps for each month of the year
|
|
1733
|
-
monthly_peak_J = hash_months_electricity.map { |key, value| [key, value.max()] }.to_h
|
|
1734
|
-
### Convert J to W of maximum electricity energy use (J)
|
|
1735
|
-
monthly_peak_W = monthly_peak_J.map { |key, value| [key, value / timestep_second] }.to_h
|
|
1736
|
-
# Get winter peak
|
|
1737
|
-
winter_peak_W = [monthly_peak_W[12].to_f, monthly_peak_W[1].to_f, monthly_peak_W[2].to_f].max
|
|
1738
|
-
### Normalize by building floor area
|
|
1739
|
-
annual_peak_W_per_m_sq = winter_peak_W / @btap_data['bldg_conditioned_floor_area_m_sq'].to_f
|
|
1740
|
-
data['energy_peak_electric_water_systems_w_per_m_sq_winter'] = annual_peak_W_per_m_sq
|
|
1741
|
-
end
|
|
1742
|
-
|
|
1743
|
-
return data
|
|
1744
|
-
end
|
|
1745
|
-
|
|
1746
|
-
def energy_eui_data(model)
|
|
1747
|
-
data = {}
|
|
1748
|
-
# default to zero to start.
|
|
1749
|
-
['energy_eui_fans_gj_per_m_sq',
|
|
1750
|
-
'energy_eui_heating_gj_per_m_sq',
|
|
1751
|
-
'energy_eui_cooling_gj_per_m_sq',
|
|
1752
|
-
'energy_eui_interior equipment_gj_per_m_sq',
|
|
1753
|
-
'energy_eui_natural_gas_gj_per_m_sq',
|
|
1754
|
-
'energy_eui_pumps_gj_per_m_sq',
|
|
1755
|
-
'energy_eui_total_gj_per_m_sq',
|
|
1756
|
-
'energy_eui_heat recovery_gj_per_m_sq',
|
|
1757
|
-
'energy_eui_water systems_gj_per_m_sq'].each { |end_use| data[end_use] = 0.0 }
|
|
1758
|
-
|
|
1759
|
-
# Check if the HVAC of the model is GSHP
|
|
1760
|
-
plant_loops = model.getPlantLoops
|
|
1761
|
-
model_has_how_many_GSHP = 0.0
|
|
1762
|
-
plant_loops.each do |plantloop|
|
|
1763
|
-
if plantloop.name.to_s.upcase.include? "GLHX"
|
|
1764
|
-
model_has_how_many_GSHP += 1.0
|
|
1765
|
-
end
|
|
1766
|
-
end
|
|
1767
|
-
|
|
1768
|
-
# Get E+ End use table from sql
|
|
1769
|
-
table = get_sql_table_to_json(@model, 'AnnualBuildingUtilityPerformanceSummary', 'Entire Facility', 'End Uses')['table']
|
|
1770
|
-
# Get rid of totals and averages rows.. I want just the
|
|
1771
|
-
table.delete_if { |row| !!(row['name'] =~ /Total|Average/) }
|
|
1772
|
-
table.each do |row|
|
|
1773
|
-
# skip name and water_m3 columns
|
|
1774
|
-
energy_columns = row.select { |k, v| (k != 'name') && (k != 'water_m3') }
|
|
1775
|
-
# Store eui by use name.
|
|
1776
|
-
data["energy_eui_#{row['name'].downcase}_gj_per_m_sq"] = energy_columns.inject(0) { |sum, tuple| sum += tuple[1] } / @conditioned_floor_area_m_sq
|
|
1777
|
-
end
|
|
1778
|
-
|
|
1779
|
-
data['energy_eui_total_gj_per_m_sq'] = 0.0
|
|
1780
|
-
|
|
1781
|
-
['natural_gas_GJ', 'electricity_GJ', 'additional_fuel_GJ', 'district_cooling_GJ', 'district_heating_GJ'].each do |column|
|
|
1782
|
-
data["energy_eui_#{column.downcase}_per_m_sq"] = table.inject(0) { |sum, row| sum + (row[column].nil? ? 0.0 : row[column]) } / @conditioned_floor_area_m_sq
|
|
1783
|
-
data['energy_eui_total_gj_per_m_sq'] += data["energy_eui_#{column.downcase}_per_m_sq"] unless data["energy_eui_#{column.downcase}_per_m_sq"].nil?
|
|
1784
|
-
end
|
|
1785
|
-
|
|
1786
|
-
# If the HVAC of the model is GSHP, district heating and cooling must be removed from EUIs for heating and cooling and total EUI
|
|
1787
|
-
# NOTE: it has been assumed that if a model has GSHP, that is the only HVAC type in the model. This assumption means that any district heating/cooling in the model is related to GSHP.
|
|
1788
|
-
if model_has_how_many_GSHP > 0.0
|
|
1789
|
-
data['energy_eui_heating_gj_per_m_sq'] -= data['energy_eui_district_heating_gj_per_m_sq']
|
|
1790
|
-
data['energy_eui_cooling_gj_per_m_sq'] -= data['energy_eui_district_cooling_gj_per_m_sq']
|
|
1791
|
-
data['energy_eui_total_gj_per_m_sq'] -= (data['energy_eui_district_heating_gj_per_m_sq'] + data['energy_eui_district_cooling_gj_per_m_sq'])
|
|
1792
|
-
end
|
|
1793
|
-
|
|
1794
|
-
# Get total and net site energy use intensity
|
|
1795
|
-
# Note: 'Total Site Energy' is the "gross" energy used by a building.
|
|
1796
|
-
# Note: 'Net Site Energy' is the final energy used by the building after considering any on-site energy generation (e.g. PV).
|
|
1797
|
-
# Reference: https://unmethours.com/question/25416/what-is-the-difference-between-site-energy-and-source-energy/
|
|
1798
|
-
# Reference: https://designbuilder.co.uk/helpv6.0/Content/KPIs.htm
|
|
1799
|
-
data['total_site_eui_gj_per_m_sq'] = data['energy_eui_total_gj_per_m_sq'].to_f
|
|
1800
|
-
command = "SELECT Value
|
|
1801
|
-
FROM TabularDataWithStrings
|
|
1802
|
-
WHERE ReportName='AnnualBuildingUtilityPerformanceSummary'
|
|
1803
|
-
AND ReportForString='Entire Facility'
|
|
1804
|
-
AND TableName='Site and Source Energy'
|
|
1805
|
-
AND RowName='Net Site Energy'
|
|
1806
|
-
AND ColumnName='Energy Per Conditioned Building Area'
|
|
1807
|
-
AND Units='MJ/m2'"
|
|
1808
|
-
net_site_eui_mj_per_m_sq = @sqlite_file.get.execAndReturnFirstDouble(command)
|
|
1809
|
-
data['net_site_eui_gj_per_m_sq'] = OpenStudio.convert(net_site_eui_mj_per_m_sq.to_f, 'MJ', 'GJ').get
|
|
1810
|
-
return data
|
|
1811
|
-
end
|
|
1812
|
-
|
|
1813
|
-
def unmet_hours(model)
|
|
1814
|
-
# Store unmet hour data
|
|
1815
|
-
unmet_hours = {}
|
|
1816
|
-
unmet_hours['unmet_hours_cooling'] = model.getFacility.hoursCoolingSetpointNotMet.get unless model.getFacility.hoursCoolingSetpointNotMet.empty?
|
|
1817
|
-
unmet_hours['unmet_hours_heating'] = model.getFacility.hoursHeatingSetpointNotMet.get unless model.getFacility.hoursHeatingSetpointNotMet.empty?
|
|
1818
|
-
command = "SELECT Value
|
|
1819
|
-
FROM TabularDataWithStrings
|
|
1820
|
-
WHERE ReportName='AnnualBuildingUtilityPerformanceSummary'
|
|
1821
|
-
AND ReportForString='Entire Facility'
|
|
1822
|
-
AND TableName='Comfort and Setpoint Not Met Summary'
|
|
1823
|
-
AND RowName='Time Setpoint Not Met During Occupied Cooling'
|
|
1824
|
-
AND ColumnName='Facility'
|
|
1825
|
-
AND Units='Hours'"
|
|
1826
|
-
unmet_hours['unmet_hours_cooling_during_occupied'] = @sqlite_file.get.execAndReturnFirstDouble(command).to_f
|
|
1827
|
-
command = "SELECT Value
|
|
1828
|
-
FROM TabularDataWithStrings
|
|
1829
|
-
WHERE ReportName='AnnualBuildingUtilityPerformanceSummary'
|
|
1830
|
-
AND ReportForString='Entire Facility'
|
|
1831
|
-
AND TableName='Comfort and Setpoint Not Met Summary'
|
|
1832
|
-
AND RowName='Time Setpoint Not Met During Occupied Heating'
|
|
1833
|
-
AND ColumnName='Facility'
|
|
1834
|
-
AND Units='Hours'"
|
|
1835
|
-
unmet_hours['unmet_hours_heating_during_occupied'] = @sqlite_file.get.execAndReturnFirstDouble(command).to_f
|
|
1836
|
-
return unmet_hours
|
|
1837
|
-
end
|
|
1838
|
-
|
|
1839
|
-
def service_water_heating_data
|
|
1840
|
-
service_water_heating = {}
|
|
1841
|
-
service_water_heating['shw_total_nominal_occupancy'] = -1
|
|
1842
|
-
# service_water_heating["total_nominal_occupancy"]=@model.sqlFile().get().execAndReturnVectorOfDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='OutdoorAirSummary' AND ReportForString='Entire Facility' AND TableName='Average Outdoor Air During Occupied Hours' AND ColumnName='Nominal Number of Occupants'").get.inject(0, :+)
|
|
1843
|
-
service_water_heating['shw_total_nominal_occupancy'] = get_total_nominal_capacity(@model)
|
|
1844
|
-
|
|
1845
|
-
service_water_heating['shw_electricity_per_year'] = @model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='AnnualBuildingUtilityPerformanceSummary' AND ReportForString='Entire Facility' AND TableName='End Uses' AND ColumnName='Electricity' AND RowName='Water Systems'")
|
|
1846
|
-
service_water_heating['shw_electricity_per_year'] = validate_optional(service_water_heating['shw_electricity_per_year'], @model, -1)
|
|
1847
|
-
|
|
1848
|
-
service_water_heating['shw_electricity_per_day'] = service_water_heating['shw_electricity_per_year'] / 365.5
|
|
1849
|
-
service_water_heating['shw_electricity_per_day_per_occupant'] = service_water_heating['shw_electricity_per_day'] / service_water_heating['shw_total_nominal_occupancy']
|
|
1850
|
-
|
|
1851
|
-
service_water_heating['shw_natural_gas_per_year'] = @model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='AnnualBuildingUtilityPerformanceSummary' AND ReportForString='Entire Facility' AND TableName='End Uses' AND ColumnName='Natural Gas' AND RowName='Water Systems'")
|
|
1852
|
-
service_water_heating['shw_natural_gas_per_year'] = validate_optional(service_water_heating['shw_natural_gas_per_year'], @model, -1)
|
|
1853
|
-
|
|
1854
|
-
service_water_heating['shw_additional_fuel_per_year'] = @model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='AnnualBuildingUtilityPerformanceSummary' AND ReportForString='Entire Facility' AND TableName='End Uses' AND ColumnName='Additional Fuel' AND RowName='Water Systems'")
|
|
1855
|
-
service_water_heating['shw_additional_fuel_per_year'] = validate_optional(service_water_heating['shw_additional_fuel_per_year'], @model, -1)
|
|
1856
|
-
|
|
1857
|
-
service_water_heating['shw_water_m_cu_per_year'] = @model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='AnnualBuildingUtilityPerformanceSummary' AND ReportForString='Entire Facility' AND TableName='End Uses' AND ColumnName='Water' AND RowName='Water Systems'")
|
|
1858
|
-
service_water_heating['shw_water_m_cu_per_year'] = validate_optional(service_water_heating['shw_water_m_cu_per_year'], @model, -1)
|
|
1859
|
-
|
|
1860
|
-
service_water_heating['shw_water_m_cu_per_day'] = service_water_heating['shw_water_m_cu_per_year'] / 365.5
|
|
1861
|
-
service_water_heating['shw_water_m_cu_per_day_per_occupant'] = service_water_heating['shw_water_m_cu_per_day'] / service_water_heating['shw_total_nominal_occupancy']
|
|
1862
|
-
return service_water_heating
|
|
1863
|
-
end
|
|
1864
|
-
|
|
1865
|
-
# The below method (outdoor_air_data extract a couple of outputs related to outdoor air from the .html output file)
|
|
1866
|
-
def outdoor_air_data(model)
|
|
1867
|
-
# Store outdoor air data
|
|
1868
|
-
outdoor_air_data = {}
|
|
1869
|
-
#===============================================================================================================
|
|
1870
|
-
airloops_total_outdoor_air_mechanical_ventilation_m3 = 0.0
|
|
1871
|
-
airloops_total_outdoor_air_natural_ventilation_m3 = 0.0
|
|
1872
|
-
zones_total_outdoor_air_mechanical_ventilation_m3 = 0.0
|
|
1873
|
-
zones_total_outdoor_air_natural_ventilation_m3 = 0.0
|
|
1874
|
-
zones_total_outdoor_air_infiltration_m3 = 0.0
|
|
1875
|
-
#===============================================================================================================
|
|
1876
|
-
# Total outdoor air by airLoop
|
|
1877
|
-
model.getAirLoopHVACs.sort.each do |air_loop|
|
|
1878
|
-
air_loop_name = air_loop.name.get.upcase
|
|
1879
|
-
|
|
1880
|
-
# Mechanical ventilation of all airloops
|
|
1881
|
-
command = "SELECT Value
|
|
1882
|
-
FROM TabularDataWithStrings
|
|
1883
|
-
WHERE ReportName='OutdoorAirDetails'
|
|
1884
|
-
AND ReportForString='Entire Facility'
|
|
1885
|
-
AND TableName='Total Outdoor Air by AirLoop'
|
|
1886
|
-
AND RowName='#{air_loop_name}'
|
|
1887
|
-
AND ColumnName='Mechanical Ventilation'
|
|
1888
|
-
AND Units='m3'"
|
|
1889
|
-
airloops_total_outdoor_air_mechanical_ventilation_m3 += @sqlite_file.get.execAndReturnFirstDouble(command).to_f
|
|
1890
|
-
|
|
1891
|
-
# Natural ventilation of all airloops
|
|
1892
|
-
command = "SELECT Value
|
|
1893
|
-
FROM TabularDataWithStrings
|
|
1894
|
-
WHERE ReportName='OutdoorAirDetails'
|
|
1895
|
-
AND ReportForString='Entire Facility'
|
|
1896
|
-
AND TableName='Total Outdoor Air by AirLoop'
|
|
1897
|
-
AND RowName='#{air_loop_name}'
|
|
1898
|
-
AND ColumnName='Natural Ventilation'
|
|
1899
|
-
AND Units='m3'"
|
|
1900
|
-
airloops_total_outdoor_air_natural_ventilation_m3 += @sqlite_file.get.execAndReturnFirstDouble(command).to_f
|
|
1901
|
-
|
|
1902
|
-
end
|
|
1903
|
-
|
|
1904
|
-
# Not-normalized mechanical/natural.
|
|
1905
|
-
outdoor_air_data['airloops_total_outdoor_air_mechanical_ventilation_m3'] = airloops_total_outdoor_air_mechanical_ventilation_m3
|
|
1906
|
-
outdoor_air_data['airloops_total_outdoor_air_natural_ventilation_m3'] = airloops_total_outdoor_air_natural_ventilation_m3
|
|
1907
|
-
|
|
1908
|
-
# Normalized mechanical/natural: ACH (air changes per hour)
|
|
1909
|
-
outdoor_air_data['airloops_total_outdoor_air_mechanical_ventilation_ach_1_per_hr'] = airloops_total_outdoor_air_mechanical_ventilation_m3 / (@btap_data['bldg_volume_m_cu'] * 365 * 24)
|
|
1910
|
-
outdoor_air_data['airloops_total_outdoor_air_natural_ventilation_ach_1_per_hr'] = airloops_total_outdoor_air_natural_ventilation_m3 / (@btap_data['bldg_volume_m_cu'] * 365 * 24)
|
|
1911
|
-
|
|
1912
|
-
# Normalized mechanical/natural: normalized by conditioned floor area
|
|
1913
|
-
outdoor_air_data['airloops_total_outdoor_air_mechanical_ventilation_flow_per_conditioned_floor_area_m3_per_s_m2'] = airloops_total_outdoor_air_mechanical_ventilation_m3 / (@conditioned_floor_area_m_sq * 365 * 24 * 3600)
|
|
1914
|
-
outdoor_air_data['airloops_total_outdoor_air_natural_ventilation_flow_per_conditioned_floor_area_m3_per_s_m2'] = airloops_total_outdoor_air_natural_ventilation_m3 / (@conditioned_floor_area_m_sq * 365 * 24 * 3600)
|
|
1915
|
-
|
|
1916
|
-
# Normalized mechanical/natural: normalized by exterior area
|
|
1917
|
-
outdoor_air_data['airloops_total_outdoor_air_mechanical_ventilation_flow_per_exterior_area_m3_per_s_m2'] = airloops_total_outdoor_air_mechanical_ventilation_m3 / (@btap_data['bldg_exterior_area_m_sq'] * 365 * 24 * 3600)
|
|
1918
|
-
outdoor_air_data['airloops_total_outdoor_air_natural_ventilation_flow_per_exterior_area_m3_per_s_m2'] = airloops_total_outdoor_air_natural_ventilation_m3 / (@btap_data['bldg_exterior_area_m_sq'] * 365 * 24 * 3600)
|
|
1919
|
-
|
|
1920
|
-
#===============================================================================================================
|
|
1921
|
-
# Total outdoor air by zone
|
|
1922
|
-
total_outdoor_air_mechanical_ventilation_zones_m3 = 0.0
|
|
1923
|
-
model.getThermalZones.sort.each do |zone|
|
|
1924
|
-
zone_name = zone.name.get.upcase
|
|
1925
|
-
|
|
1926
|
-
# Mechanical ventilation of all zones
|
|
1927
|
-
command = "SELECT Value
|
|
1928
|
-
FROM TabularDataWithStrings
|
|
1929
|
-
WHERE ReportName='OutdoorAirDetails'
|
|
1930
|
-
AND ReportForString='Entire Facility'
|
|
1931
|
-
AND TableName='Total Outdoor Air by Zone'
|
|
1932
|
-
AND RowName='#{zone_name}'
|
|
1933
|
-
AND ColumnName='Mechanical Ventilation'
|
|
1934
|
-
AND Units='m3'"
|
|
1935
|
-
zones_total_outdoor_air_mechanical_ventilation_m3 += @sqlite_file.get.execAndReturnFirstDouble(command).to_f
|
|
1936
|
-
|
|
1937
|
-
# Natural ventilation of all zones
|
|
1938
|
-
command = "SELECT Value
|
|
1939
|
-
FROM TabularDataWithStrings
|
|
1940
|
-
WHERE ReportName='OutdoorAirDetails'
|
|
1941
|
-
AND ReportForString='Entire Facility'
|
|
1942
|
-
AND TableName='Total Outdoor Air by Zone'
|
|
1943
|
-
AND RowName='#{zone_name}'
|
|
1944
|
-
AND ColumnName='Natural Ventilation'
|
|
1945
|
-
AND Units='m3'"
|
|
1946
|
-
zones_total_outdoor_air_natural_ventilation_m3 += @sqlite_file.get.execAndReturnFirstDouble(command).to_f
|
|
1947
|
-
|
|
1948
|
-
# Infiltration of all zones
|
|
1949
|
-
command = "SELECT Value
|
|
1950
|
-
FROM TabularDataWithStrings
|
|
1951
|
-
WHERE ReportName='OutdoorAirDetails'
|
|
1952
|
-
AND ReportForString='Entire Facility'
|
|
1953
|
-
AND TableName='Total Outdoor Air by Zone'
|
|
1954
|
-
AND RowName='#{zone_name}'
|
|
1955
|
-
AND ColumnName='Infiltration'
|
|
1956
|
-
AND Units='m3'"
|
|
1957
|
-
zones_total_outdoor_air_infiltration_m3 += @sqlite_file.get.execAndReturnFirstDouble(command).to_f
|
|
1958
|
-
|
|
1959
|
-
end
|
|
1960
|
-
|
|
1961
|
-
# Not-normalized mechanical/natural/infiltration.
|
|
1962
|
-
outdoor_air_data['zones_total_outdoor_air_mechanical_ventilation_m3'] = zones_total_outdoor_air_mechanical_ventilation_m3
|
|
1963
|
-
outdoor_air_data['zones_total_outdoor_air_natural_ventilation_m3'] = zones_total_outdoor_air_natural_ventilation_m3
|
|
1964
|
-
outdoor_air_data['zones_total_outdoor_air_infiltration_m3'] = zones_total_outdoor_air_infiltration_m3
|
|
1965
|
-
|
|
1966
|
-
# Normalized mechanical/natural/infiltration: ACH (air changes per hour)
|
|
1967
|
-
outdoor_air_data['zones_total_outdoor_air_mechanical_ventilation_ach_1_per_hr'] = zones_total_outdoor_air_mechanical_ventilation_m3 / (@btap_data['bldg_volume_m_cu'] * 365 * 24)
|
|
1968
|
-
outdoor_air_data['zones_total_outdoor_air_natural_ventilation_ach_1_per_hr'] = zones_total_outdoor_air_natural_ventilation_m3 / (@btap_data['bldg_volume_m_cu'] * 365 * 24)
|
|
1969
|
-
outdoor_air_data['zones_total_outdoor_air_infiltration_ach_1_per_hr'] = zones_total_outdoor_air_infiltration_m3 / (@btap_data['bldg_volume_m_cu'] * 365 * 24)
|
|
1970
|
-
|
|
1971
|
-
# Normalized mechanical/natural/infiltration: normalized by conditioned floor area
|
|
1972
|
-
outdoor_air_data['zones_total_outdoor_air_mechanical_ventilation_flow_per_conditioned_floor_area_m3_per_s_m2'] = zones_total_outdoor_air_mechanical_ventilation_m3 / (@conditioned_floor_area_m_sq * 365 * 24 * 3600)
|
|
1973
|
-
outdoor_air_data['zones_total_outdoor_air_natural_ventilation_flow_per_conditioned_floor_area_m3_per_s_m2'] = zones_total_outdoor_air_natural_ventilation_m3 / (@conditioned_floor_area_m_sq * 365 * 24 * 3600)
|
|
1974
|
-
outdoor_air_data['zones_total_outdoor_air_infiltration_flow_per_conditioned_floor_area_m3_per_s_m2'] = zones_total_outdoor_air_infiltration_m3 / (@conditioned_floor_area_m_sq * 365 * 24 * 3600)
|
|
1975
|
-
|
|
1976
|
-
# Normalized mechanical/natural/infiltration: normalized by exterior area
|
|
1977
|
-
outdoor_air_data['zones_total_outdoor_air_mechanical_ventilation_flow_per_exterior_area_m3_per_s_m2'] = zones_total_outdoor_air_mechanical_ventilation_m3 / (@btap_data['bldg_exterior_area_m_sq'] * 365 * 24 * 3600)
|
|
1978
|
-
outdoor_air_data['zones_total_outdoor_air_natural_ventilation_flow_per_exterior_area_m3_per_s_m2'] = zones_total_outdoor_air_natural_ventilation_m3 / (@btap_data['bldg_exterior_area_m_sq'] * 365 * 24 * 3600)
|
|
1979
|
-
outdoor_air_data['zones_total_outdoor_air_infiltration_flow_per_exterior_area_m3_per_s_m2'] = zones_total_outdoor_air_infiltration_m3 / (@btap_data['bldg_exterior_area_m_sq'] * 365 * 24 * 3600)
|
|
1980
|
-
#===============================================================================================================
|
|
1981
|
-
|
|
1982
|
-
return outdoor_air_data
|
|
1983
|
-
end
|
|
1984
|
-
|
|
1985
|
-
def sql_data_tables(model)
|
|
1986
|
-
puts 'Getting SQL Data into json...'
|
|
1987
|
-
start = Time.now
|
|
1988
|
-
sql_data = []
|
|
1989
|
-
|
|
1990
|
-
[
|
|
1991
|
-
['AnnualBuildingUtilityPerformanceSummary', 'Entire Facility', 'End Uses']
|
|
1992
|
-
# ["AnnualBuildingUtilityPerformanceSummary", "Entire Facility", "Site and Source Energy"],
|
|
1993
|
-
# ["AnnualBuildingUtilityPerformanceSummary", "Entire Facility", "On-Site Thermal Sources"],
|
|
1994
|
-
# ["AnnualBuildingUtilityPerformanceSummary", "Entire Facility", "Comfort and Setpoint Not Met Summary"],
|
|
1995
|
-
# ["InputVerificationandResultsSummary", "Entire Facility", "Window-Wall Ratio"],
|
|
1996
|
-
# ["InputVerificationandResultsSummary", "Entire Facility", "Conditioned Window-Wall Ratio"],
|
|
1997
|
-
# ["InputVerificationandResultsSummary", "Entire Facility", "Skylight-Roof Ratio"],
|
|
1998
|
-
# ["DemandEndUseComponentsSummary", "Entire Facility", "End Uses"],
|
|
1999
|
-
# ["ComponentSizingSummary", "Entire Facility", "AirLoopHVAC"],
|
|
2000
|
-
# ["EnergyMeters", "Entire Facility", 'Annual and Peak Values - Natural Gas'],
|
|
2001
|
-
# ["EnergyMeters", "Entire Facility", 'Annual and Peak Values - Electricity'],
|
|
2002
|
-
# ["EnergyMeters", "Entire Facility", 'Annual and Peak Values - FuelOilNo2'],
|
|
2003
|
-
# ["EnergyMeters", "Entire Facility", 'Annual and Peak Values - Other'],
|
|
2004
|
-
# ["LEEDsummary", "Entire Facility", "EAp2-7. Energy Cost Summary"],
|
|
2005
|
-
# ["Standard62.1Summary", "Entire Facility", "Zone Ventilation Parameters"],
|
|
2006
|
-
# ["EquipmentSummary", "Entire Facility", "Fans"],
|
|
2007
|
-
# ["EquipmentSummary", "Entire Facility", "Heating Coils"],
|
|
2008
|
-
# ["EquipmentSummary", "Entire Facility", "Cooling Coils"],
|
|
2009
|
-
# ["EquipmentSummary", "Entire Facility", "Pumps"],
|
|
2010
|
-
# ["CoilSizingDetails", "Entire Facility", "Coils"] # Do not use! Takes very long to parse.
|
|
2011
|
-
].each do |table|
|
|
2012
|
-
start = Time.now
|
|
2013
|
-
puts "Parsing #{table[0]}-#{table[1]}-#{table[2]}"
|
|
2014
|
-
sql_data << get_sql_table_to_json(model, table[0], table[1], table[2])
|
|
2015
|
-
finish = Time.now
|
|
2016
|
-
puts "....finish parsing in #{finish - start} seconds and stored in sql_data_tables hash."
|
|
2017
|
-
end
|
|
2018
|
-
end
|
|
2019
|
-
|
|
2020
|
-
# This measure will return an array of hashes with the varialbles used in the previous measures.
|
|
2021
|
-
def measures_data_table(runner)
|
|
2022
|
-
# Array to store hash row data.
|
|
2023
|
-
measure_variables_table = []
|
|
2024
|
-
# Go through each workstep.
|
|
2025
|
-
runner.workflow.workflowSteps.each do |step|
|
|
2026
|
-
# Check if the ws is a measure.
|
|
2027
|
-
if step.to_MeasureStep.is_initialized
|
|
2028
|
-
measure_step = step.to_MeasureStep.get
|
|
2029
|
-
# Set measure name using either the folder name or the measureStep name if possible.
|
|
2030
|
-
measure_name = measure_step.name.is_initialized ? measure_step.name.get : measure_step.measureDirName
|
|
2031
|
-
measures_to_skip = ['openstudio_results']
|
|
2032
|
-
unless measures_to_skip.include?(measure_name.to_s)
|
|
2033
|
-
# Check if the 'result' (?) is initialized?
|
|
2034
|
-
if measure_step.result.is_initialized
|
|
2035
|
-
result = measure_step.result.get
|
|
2036
|
-
# Iterate through the result object stepValues to obtain the arg values. I am assuming this is for arg, var. pivot, continous...
|
|
2037
|
-
result.stepValues.each do |arg|
|
|
2038
|
-
# Store the row /hash for the table.
|
|
2039
|
-
units = arg.units.empty? ? nil : arg.units
|
|
2040
|
-
value = nil
|
|
2041
|
-
case arg.variantType.value
|
|
2042
|
-
when 0
|
|
2043
|
-
value = arg.valueAsBoolean
|
|
2044
|
-
when 1..2
|
|
2045
|
-
value = arg.valueAsDouble
|
|
2046
|
-
when 3
|
|
2047
|
-
value = arg.valueAsString
|
|
2048
|
-
end
|
|
2049
|
-
measure_variables_table << { 'measure_name' => measure_name, 'arg_name' => arg.name, 'value' => value, 'units' => units, 'type' => arg.variantType.value }
|
|
2050
|
-
end
|
|
2051
|
-
end
|
|
2052
|
-
end
|
|
2053
|
-
end
|
|
2054
|
-
end
|
|
2055
|
-
return measure_variables_table
|
|
2056
|
-
end
|
|
2057
|
-
|
|
2058
|
-
# This should be done last.
|
|
2059
|
-
|
|
2060
|
-
def get_sql_table_to_json(model, report_name, report_for_string, table_name)
|
|
2061
|
-
table = []
|
|
2062
|
-
query_row_names = "
|
|
2063
|
-
SELECT DISTINCT
|
|
2064
|
-
RowName
|
|
2065
|
-
FROM
|
|
2066
|
-
tabulardatawithstrings
|
|
2067
|
-
WHERE
|
|
2068
|
-
ReportName='#{report_name}'
|
|
2069
|
-
AND
|
|
2070
|
-
ReportForString='#{report_for_string}'
|
|
2071
|
-
AND
|
|
2072
|
-
TableName='#{table_name}'"
|
|
2073
|
-
row_names = model.sqlFile.get.execAndReturnVectorOfString(query_row_names).get
|
|
2074
|
-
|
|
2075
|
-
# get Columns
|
|
2076
|
-
query_col_names = "
|
|
2077
|
-
SELECT DISTINCT
|
|
2078
|
-
ColumnName
|
|
2079
|
-
FROM tabulardatawithstrings
|
|
2080
|
-
WHERE ReportName='#{report_name}'
|
|
2081
|
-
AND ReportForString='#{report_for_string}'
|
|
2082
|
-
AND TableName='#{table_name}'"
|
|
2083
|
-
col_names = model.sqlFile.get.execAndReturnVectorOfString(query_col_names).get
|
|
2084
|
-
|
|
2085
|
-
# get units
|
|
2086
|
-
query_unit_names = "
|
|
2087
|
-
SELECT DISTINCT
|
|
2088
|
-
Units
|
|
2089
|
-
FROM tabulardatawithstrings
|
|
2090
|
-
WHERE ReportName='#{report_name}'
|
|
2091
|
-
AND ReportForString='#{report_for_string}'
|
|
2092
|
-
AND TableName='#{table_name}'"
|
|
2093
|
-
unit_names = model.sqlFile.get.execAndReturnVectorOfString(query_unit_names).get
|
|
2094
|
-
|
|
2095
|
-
row_names.each do |row|
|
|
2096
|
-
next if row.nil? || row == ''
|
|
2097
|
-
|
|
2098
|
-
row_hash = {}
|
|
2099
|
-
row_hash['name'] = row
|
|
2100
|
-
col_names.each do |col|
|
|
2101
|
-
unit_names.each do |unit|
|
|
2102
|
-
query = "
|
|
2103
|
-
SELECT
|
|
2104
|
-
Value
|
|
2105
|
-
FROM
|
|
2106
|
-
tabulardatawithstrings
|
|
2107
|
-
WHERE
|
|
2108
|
-
ReportName='#{report_name}'
|
|
2109
|
-
AND
|
|
2110
|
-
ReportForString='#{report_for_string}'
|
|
2111
|
-
AND
|
|
2112
|
-
TableName='#{table_name}'
|
|
2113
|
-
AND
|
|
2114
|
-
RowName='#{row}'
|
|
2115
|
-
AND
|
|
2116
|
-
ColumnName='#{col}'
|
|
2117
|
-
AND
|
|
2118
|
-
Units='#{unit}'
|
|
2119
|
-
"
|
|
2120
|
-
column_name = col.to_s.gsub(/\s+/, '_').downcase
|
|
2121
|
-
column_name += "_#{unit}" if unit != ''
|
|
2122
|
-
value = model.sqlFile.get.execAndReturnFirstString(query)
|
|
2123
|
-
next if value.empty? || value.get.nil?
|
|
2124
|
-
|
|
2125
|
-
value = value.get.strip
|
|
2126
|
-
# check is value is a number. The last chunk checks if the string includes an E, if not return true since it
|
|
2127
|
-
# is a regular number, if not it checks if it is in the E+ exponent format and returns the bool result of that.
|
|
2128
|
-
if (begin
|
|
2129
|
-
Float(value)
|
|
2130
|
-
rescue StandardError
|
|
2131
|
-
false
|
|
2132
|
-
end) && value.to_f != 0 && (value.include?('E') || value.include?('e') ? value =~ /\d*\.\d*E[+|-]\d*/ : true)
|
|
2133
|
-
row_hash[column_name] = value.to_f
|
|
2134
|
-
# Check if value is a date
|
|
2135
|
-
elsif unit == '' && value =~ /\d\d-\D\D\D-\d\d:\d\d/
|
|
2136
|
-
row_hash[column_name] = DateTime.parse(value)
|
|
2137
|
-
# skip if value in an empty string or a zero value
|
|
2138
|
-
elsif value != '' && value != '0.00'
|
|
2139
|
-
row_hash[column_name] = value
|
|
2140
|
-
end
|
|
2141
|
-
end
|
|
2142
|
-
end
|
|
2143
|
-
if row_hash.size > 1
|
|
2144
|
-
table << row_hash
|
|
2145
|
-
end
|
|
2146
|
-
end
|
|
2147
|
-
result = { 'report_name' => report_name, 'report_for_string' => report_for_string, 'table_name' => table_name, 'table' => table }
|
|
2148
|
-
return result
|
|
2149
|
-
end
|
|
2150
|
-
|
|
2151
|
-
def merge_recursively(a, b)
|
|
2152
|
-
a.merge(b) { |key, a_item, b_item| merge_recursively(a_item, b_item) }
|
|
2153
|
-
end
|
|
2154
|
-
|
|
2155
|
-
def validate_optional(var, model, return_value = 'N/A')
|
|
2156
|
-
return return_value if var.nil? || var.empty?
|
|
2157
|
-
|
|
2158
|
-
return var.get
|
|
2159
|
-
end
|
|
2160
|
-
|
|
2161
|
-
# @todo SQL command units may have been converted wrong.
|
|
2162
|
-
|
|
2163
|
-
def get_actual_child_object(object)
|
|
2164
|
-
# monkey patch class to have a decendants static method to return all possible subclasses of the object.
|
|
2165
|
-
object.class.class_eval do
|
|
2166
|
-
def self.descendants
|
|
2167
|
-
ObjectSpace.each_object(Class).select { |k| k < self } << self
|
|
2168
|
-
end
|
|
2169
|
-
end
|
|
2170
|
-
# Dont try and match the class that the object is already in! So get decendants that are not of the current object class
|
|
2171
|
-
subclass_array = object.class.descendants.map { |classtype| classtype if classtype != object.class }.reject(&:nil?)
|
|
2172
|
-
subclass_array.each do |class_type|
|
|
2173
|
-
# convert class name to not have prefix.
|
|
2174
|
-
matches = class_type.name.match(/OpenStudio::Model::(?<object_name>.*)/)
|
|
2175
|
-
new_object = nil
|
|
2176
|
-
# Use eval (I know this is the devil) to try to cast to a subclass.
|
|
2177
|
-
eval_info = "new_object = object.to_#{matches['object_name']}"
|
|
2178
|
-
eval(eval_info)
|
|
2179
|
-
# if it does then clean it up and return it.
|
|
2180
|
-
if new_object.is_initialized
|
|
2181
|
-
new_object = new_object.get
|
|
2182
|
-
return get_actual_child_object(new_object)
|
|
2183
|
-
end
|
|
2184
|
-
end
|
|
2185
|
-
# It is not cast-able to any subclass.. so returning original object.
|
|
2186
|
-
return object
|
|
2187
|
-
end
|
|
2188
|
-
|
|
2189
|
-
def set_sql_file(file)
|
|
2190
|
-
@sqlite_file = file
|
|
2191
|
-
end
|
|
2192
|
-
|
|
2193
|
-
def measure_metrics(qaqc)
|
|
2194
|
-
# Store mesure metric data that will be used in analysis tools.
|
|
2195
|
-
@btap_data['env_outdoor_walls_average_conductance-w_per_m_sq_k'] = qaqc[:envelope][:outdoor_walls_average_conductance_w_per_m2_k]
|
|
2196
|
-
@btap_data['env_outdoor_roofs_average_conductance-w_per_m_sq_k'] = qaqc[:envelope][:outdoor_roofs_average_conductance_w_per_m2_k]
|
|
2197
|
-
@btap_data['env_outdoor_floors_average_conductance-w_per_m_sq_k'] = qaqc[:envelope][:outdoor_floors_average_conductance_w_per_m2_k]
|
|
2198
|
-
@btap_data['env_ground_walls_average_conductance-w_per_m_sq_k'] = qaqc[:envelope][:ground_walls_average_conductance_w_per_m2_k]
|
|
2199
|
-
@btap_data['env_ground_roofs_average_conductance-w_per_m_sq_k'] = qaqc[:envelope][:ground_roofs_average_conductance_w_per_m2_k]
|
|
2200
|
-
@btap_data['env_ground_floors_average_conductance-w_per_m_sq_k'] = qaqc[:envelope][:ground_floors_average_conductance_w_per_m2_k]
|
|
2201
|
-
@btap_data['env_outdoor_windows_average_conductance-w_per_m_sq_k'] = qaqc[:envelope][:windows_average_conductance_w_per_m2_k]
|
|
2202
|
-
@btap_data['env_outdoor_doors_average_conductance-w_per_m_sq_k'] = qaqc[:envelope][:doors_average_conductance_w_per_m2_k]
|
|
2203
|
-
@btap_data['env_outdoor_overhead_doors_average_conductance-w_per_m_sq_k'] = qaqc[:envelope][:overhead_doors_average_conductance_w_per_m2_k]
|
|
2204
|
-
@btap_data['env_skylights_average_conductance-w_per_m_sq_k'] = qaqc[:envelope][:skylights_average_conductance_w_per_m2_k]
|
|
2205
|
-
@btap_data['env_fdwr'] = (BTAP::Geometry.get_fwdr(@model) * 100.0).round(1)
|
|
2206
|
-
@btap_data['env_srr'] = (BTAP::Geometry.get_srr(@model) * 100.0).round(1)
|
|
2207
|
-
unless @btap_data['measures_data_table'].nil?
|
|
2208
|
-
if @btap_data['measures_data_table'].detect { |item| item['measure_name'] == 'btap_standard_building_type_geometry' }.nil?
|
|
2209
|
-
@btap_data['env_x_scale'] = 1.0
|
|
2210
|
-
@btap_data['env_y_scale'] = 1.0
|
|
2211
|
-
@btap_data['env_z_scale'] = 1.0
|
|
2212
|
-
@btap_data['env_rotation'] = 0.0
|
|
2213
|
-
else
|
|
2214
|
-
@btap_data['env_x_scale'] = @btap_data['measures_data_table'].detect { |item| item['measure_name'] == 'btap_standard_building_type_geometry' && item['arg_name'] == 'x_scale' }['value']
|
|
2215
|
-
@btap_data['env_y_scale'] = @btap_data['measures_data_table'].detect { |item| item['measure_name'] == 'btap_standard_building_type_geometry' && item['arg_name'] == 'y_scale' }['value']
|
|
2216
|
-
@btap_data['env_z_scale'] = @btap_data['measures_data_table'].detect { |item| item['measure_name'] == 'btap_standard_building_type_geometry' && item['arg_name'] == 'z_scale' }['value']
|
|
2217
|
-
@btap_data['env_rotation'] = @btap_data['measures_data_table'].detect { |item| item['measure_name'] == 'btap_standard_building_type_geometry' && item['arg_name'] == 'relative_building_rotation' }['value']
|
|
2218
|
-
end
|
|
2219
|
-
end
|
|
2220
|
-
|
|
2221
|
-
# This does not work with the new VRF or CCASHP systems. Commenting it for now.
|
|
2222
|
-
# Determine dominant system type by air loop
|
|
2223
|
-
|
|
2224
|
-
# systems = {}
|
|
2225
|
-
# @btap_data["air_loop_table"].each do |loop|
|
|
2226
|
-
# Get system name part from regex
|
|
2227
|
-
# system_name = loop["name"].match(/(^.{0,6}).*/)[1]
|
|
2228
|
-
# systems[system_name] = 0.0 if systems[system_name] == nil
|
|
2229
|
-
# systems[system_name] += loop["total_floor_area_served"]
|
|
2230
|
-
# end
|
|
2231
|
-
# if systems.empty?
|
|
2232
|
-
# @btap_data["mm_hvac_dominant_system_type"] = "Unknown/IdealHVAC"
|
|
2233
|
-
# else
|
|
2234
|
-
# @btap_data["mm_hvac_dominant_system_type"] = systems.key(systems.values.max)
|
|
2235
|
-
# end
|
|
2236
|
-
return @btap_data
|
|
2237
|
-
end
|
|
2238
|
-
|
|
2239
|
-
def get_utility_ghg_kg_per_gj(province:, fuel_type:)
|
|
2240
|
-
ghg_data = [
|
|
2241
|
-
# Obtained from Portfolio Manager https://portfoliomanager.energystar.gov/pdf/reference/Emissions.pdf 10/10/2020
|
|
2242
|
-
#{ "province": 'AB', "fuel_type": 'NaturalGas', "CO2eq Emissions (kg/MBtu)": 53.24, "CO2eq Emissions (g/m3)": 1939.0 },
|
|
2243
|
-
#{ "province": 'BC', "fuel_type": 'NaturalGas', "CO2eq Emissions (kg/MBtu)": 53.19, "CO2eq Emissions (g/m3)": 1937.0 },
|
|
2244
|
-
#{ "province": 'MB', "fuel_type": 'NaturalGas', "CO2eq Emissions (kg/MBtu)": 52.09, "CO2eq Emissions (g/m3)": 1897.0 },
|
|
2245
|
-
#{ "province": 'NB', "fuel_type": 'NaturalGas', "CO2eq Emissions (kg/MBtu)": 52.50, "CO2eq Emissions (g/m3)": 1912.0 },
|
|
2246
|
-
#{ "province": 'NL', "fuel_type": 'NaturalGas', "CO2eq Emissions (kg/MBtu)": 52.50, "CO2eq Emissions (g/m3)": 1912.0 },
|
|
2247
|
-
#{ "province": 'NT', "fuel_type": 'NaturalGas', "CO2eq Emissions (kg/MBtu)": 52.50, "CO2eq Emissions (g/m3)": 1912.0 },
|
|
2248
|
-
#{ "province": 'NS', "fuel_type": 'NaturalGas', "CO2eq Emissions (kg/MBtu)": 52.50, "CO2eq Emissions (g/m3)": 1912.0 },
|
|
2249
|
-
#{ "province": 'NU', "fuel_type": 'NaturalGas', "CO2eq Emissions (kg/MBtu)": 52.50, "CO2eq Emissions (g/m3)": 1912.0 },
|
|
2250
|
-
#{ "province": 'ON', "fuel_type": 'NaturalGas', "CO2eq Emissions (kg/MBtu)": 52.14, "CO2eq Emissions (g/m3)": 1912.0 },
|
|
2251
|
-
#{ "province": 'PE', "fuel_type": 'NaturalGas', "CO2eq Emissions (kg/MBtu)": 52.50, "CO2eq Emissions (g/m3)": 1912.0 },
|
|
2252
|
-
#{ "province": 'QC', "fuel_type": 'NaturalGas', "CO2eq Emissions (kg/MBtu)": 52.12, "CO2eq Emissions (g/m3)": 1898.0 },
|
|
2253
|
-
#{ "province": 'SK', "fuel_type": 'NaturalGas', "CO2eq Emissions (kg/MBtu)": 50.53, "CO2eq Emissions (g/m3)": 1840.0 },
|
|
2254
|
-
#{ "province": 'YT', "fuel_type": 'NaturalGas', "CO2eq Emissions (kg/MBtu)": 52.50, "CO2eq Emissions (g/m3)": 1912.0 },
|
|
2255
|
-
|
|
2256
|
-
#REF (Retrieved March 11, 2025) for Year 2022 of "Table A6.1–1 CO2 Emission Factors for Marketable Natural Gas" of National Inventory Report: https://data-donnees.az.ec.gc.ca/api/file?path=%2Fsubstances%2Fmonitor%2Fcanada-s-official-greenhouse-gas-inventory%2FD-Emission-Factors%2FEN_Annex6_Emission_Factors.pdf
|
|
2257
|
-
{ "province": 'AB', "fuel_type": 'NaturalGas', "CO2eq Emissions (kg/MBtu)": 53.87, "CO2eq Emissions (g/m3)": 1962.0 },
|
|
2258
|
-
{ "province": 'BC', "fuel_type": 'NaturalGas', "CO2eq Emissions (kg/MBtu)": 53.98, "CO2eq Emissions (g/m3)": 1966.0 },
|
|
2259
|
-
{ "province": 'MB', "fuel_type": 'NaturalGas', "CO2eq Emissions (kg/MBtu)": 52.58, "CO2eq Emissions (g/m3)": 1915.0 },
|
|
2260
|
-
{ "province": 'NB', "fuel_type": 'NaturalGas', "CO2eq Emissions (kg/MBtu)": 52.69, "CO2eq Emissions (g/m3)": 1919.0 },
|
|
2261
|
-
{ "province": 'NL', "fuel_type": 'NaturalGas', "CO2eq Emissions (kg/MBtu)": 52.69, "CO2eq Emissions (g/m3)": 1919.0 },
|
|
2262
|
-
{ "province": 'NT', "fuel_type": 'NaturalGas', "CO2eq Emissions (kg/MBtu)": 53.98, "CO2eq Emissions (g/m3)": 1966.0 },
|
|
2263
|
-
{ "province": 'NS', "fuel_type": 'NaturalGas', "CO2eq Emissions (kg/MBtu)": 52.69, "CO2eq Emissions (g/m3)": 1919.0 },
|
|
2264
|
-
{ "province": 'NU', "fuel_type": 'NaturalGas', "CO2eq Emissions (kg/MBtu)": 53.98, "CO2eq Emissions (g/m3)": 1966.0 },
|
|
2265
|
-
{ "province": 'ON', "fuel_type": 'NaturalGas', "CO2eq Emissions (kg/MBtu)": 52.75, "CO2eq Emissions (g/m3)": 1921.0 },
|
|
2266
|
-
{ "province": 'PE', "fuel_type": 'NaturalGas', "CO2eq Emissions (kg/MBtu)": 52.69, "CO2eq Emissions (g/m3)": 1919.0 },
|
|
2267
|
-
{ "province": 'QC', "fuel_type": 'NaturalGas', "CO2eq Emissions (kg/MBtu)": 52.88, "CO2eq Emissions (g/m3)": 1926.0 },
|
|
2268
|
-
{ "province": 'SK', "fuel_type": 'NaturalGas', "CO2eq Emissions (kg/MBtu)": 52.72, "CO2eq Emissions (g/m3)": 1920.0 },
|
|
2269
|
-
{ "province": 'YT', "fuel_type": 'NaturalGas', "CO2eq Emissions (kg/MBtu)": 56.98, "CO2eq Emissions (g/m3)": 1966.0 },
|
|
2270
|
-
|
|
2271
|
-
# Obtained from Portfolio Manager https://portfoliomanager.energystar.gov/pdf/reference/Emissions.pdf 10/10/2020
|
|
2272
|
-
{ "province": 'AB', "fuel_type": 'FuelOilNo2', "CO2eq Emissions (kg/MBtu)": 75.13, "CO2eq Emissions (g/m3)": 2763.0 },
|
|
2273
|
-
{ "province": 'BC', "fuel_type": 'FuelOilNo2', "CO2eq Emissions (kg/MBtu)": 75.13, "CO2eq Emissions (g/m3)": 2763.0 },
|
|
2274
|
-
{ "province": 'MB', "fuel_type": 'FuelOilNo2', "CO2eq Emissions (kg/MBtu)": 75.13, "CO2eq Emissions (g/m3)": 2763.0 },
|
|
2275
|
-
{ "province": 'NB', "fuel_type": 'FuelOilNo2', "CO2eq Emissions (kg/MBtu)": 75.13, "CO2eq Emissions (g/m3)": 2763.0 },
|
|
2276
|
-
{ "province": 'NL', "fuel_type": 'FuelOilNo2', "CO2eq Emissions (kg/MBtu)": 75.13, "CO2eq Emissions (g/m3)": 2763.0 },
|
|
2277
|
-
{ "province": 'NT', "fuel_type": 'FuelOilNo2', "CO2eq Emissions (kg/MBtu)": 75.13, "CO2eq Emissions (g/m3)": 2763.0 },
|
|
2278
|
-
{ "province": 'NS', "fuel_type": 'FuelOilNo2', "CO2eq Emissions (kg/MBtu)": 75.13, "CO2eq Emissions (g/m3)": 2763.0 },
|
|
2279
|
-
{ "province": 'NU', "fuel_type": 'FuelOilNo2', "CO2eq Emissions (kg/MBtu)": 75.13, "CO2eq Emissions (g/m3)": 2763.0 },
|
|
2280
|
-
{ "province": 'ON', "fuel_type": 'FuelOilNo2', "CO2eq Emissions (kg/MBtu)": 75.13, "CO2eq Emissions (g/m3)": 2763.0 },
|
|
2281
|
-
{ "province": 'PE', "fuel_type": 'FuelOilNo2', "CO2eq Emissions (kg/MBtu)": 75.13, "CO2eq Emissions (g/m3)": 2763.0 },
|
|
2282
|
-
{ "province": 'QC', "fuel_type": 'FuelOilNo2', "CO2eq Emissions (kg/MBtu)": 75.13, "CO2eq Emissions (g/m3)": 2763.0 },
|
|
2283
|
-
{ "province": 'SK', "fuel_type": 'FuelOilNo2', "CO2eq Emissions (kg/MBtu)": 75.13, "CO2eq Emissions (g/m3)": 2763.0 },
|
|
2284
|
-
{ "province": 'YT', "fuel_type": 'FuelOilNo2', "CO2eq Emissions (kg/MBtu)": 75.13, "CO2eq Emissions (g/m3)": 2763.0 },
|
|
2285
|
-
|
|
2286
|
-
{ "province": 'AB', "fuel_type": 'Propane', "CO2eq Emissions (kg/MBtu)": 64.25, "CO2eq Emissions (g/m3)": 1548.00 },
|
|
2287
|
-
{ "province": 'BC', "fuel_type": 'Propane', "CO2eq Emissions (kg/MBtu)": 64.25, "CO2eq Emissions (g/m3)": 1548.00 },
|
|
2288
|
-
{ "province": 'MB', "fuel_type": 'Propane', "CO2eq Emissions (kg/MBtu)": 64.25, "CO2eq Emissions (g/m3)": 1548.00 },
|
|
2289
|
-
{ "province": 'NB', "fuel_type": 'Propane', "CO2eq Emissions (kg/MBtu)": 64.25, "CO2eq Emissions (g/m3)": 1548.00 },
|
|
2290
|
-
{ "province": 'NL', "fuel_type": 'Propane', "CO2eq Emissions (kg/MBtu)": 64.25, "CO2eq Emissions (g/m3)": 1548.00 },
|
|
2291
|
-
{ "province": 'NT', "fuel_type": 'Propane', "CO2eq Emissions (kg/MBtu)": 64.25, "CO2eq Emissions (g/m3)": 1548.00 },
|
|
2292
|
-
{ "province": 'NS', "fuel_type": 'Propane', "CO2eq Emissions (kg/MBtu)": 64.25, "CO2eq Emissions (g/m3)": 1548.00 },
|
|
2293
|
-
{ "province": 'NU', "fuel_type": 'Propane', "CO2eq Emissions (kg/MBtu)": 64.25, "CO2eq Emissions (g/m3)": 1548.00 },
|
|
2294
|
-
{ "province": 'ON', "fuel_type": 'Propane', "CO2eq Emissions (kg/MBtu)": 64.25, "CO2eq Emissions (g/m3)": 1548.00 },
|
|
2295
|
-
{ "province": 'PE', "fuel_type": 'Propane', "CO2eq Emissions (kg/MBtu)": 64.25, "CO2eq Emissions (g/m3)": 1548.00 },
|
|
2296
|
-
{ "province": 'QC', "fuel_type": 'Propane', "CO2eq Emissions (kg/MBtu)": 64.25, "CO2eq Emissions (g/m3)": 1548.00 },
|
|
2297
|
-
{ "province": 'SK', "fuel_type": 'Propane', "CO2eq Emissions (kg/MBtu)": 64.25, "CO2eq Emissions (g/m3)": 1548.00 },
|
|
2298
|
-
{ "province": 'YT', "fuel_type": 'Propane', "CO2eq Emissions (kg/MBtu)": 64.25, "CO2eq Emissions (g/m3)": 1548.00 },
|
|
2299
|
-
|
|
2300
|
-
{ "province": 'AB', "fuel_type": 'Electricity', "CO2eq Emissions (kg/MBtu)": 202.23, "CO2eq Emissions (g/m3)": 690.0 },
|
|
2301
|
-
{ "province": 'BC', "fuel_type": 'Electricity', "CO2eq Emissions (kg/MBtu)": 3.84, "CO2eq Emissions (g/m3)": 13.1 },
|
|
2302
|
-
{ "province": 'MB', "fuel_type": 'Electricity', "CO2eq Emissions (kg/MBtu)": 0.41, "CO2eq Emissions (g/m3)": 1.4 },
|
|
2303
|
-
{ "province": 'NB', "fuel_type": 'Electricity', "CO2eq Emissions (kg/MBtu)": 84.99, "CO2eq Emissions (g/m3)": 290.0 },
|
|
2304
|
-
{ "province": 'NL', "fuel_type": 'Electricity', "CO2eq Emissions (kg/MBtu)": 7.91, "CO2eq Emissions (g/m3)": 27.0 },
|
|
2305
|
-
{ "province": 'NT', "fuel_type": 'Electricity', "CO2eq Emissions (kg/MBtu)": 46.89, "CO2eq Emissions (g/m3)": 160.0 },
|
|
2306
|
-
{ "province": 'NS', "fuel_type": 'Electricity', "CO2eq Emissions (kg/MBtu)": 216.88, "CO2eq Emissions (g/m3)": 740.0 },
|
|
2307
|
-
{ "province": 'NU', "fuel_type": 'Electricity', "CO2eq Emissions (kg/MBtu)": 260.84, "CO2eq Emissions (g/m3)": 890.0 },
|
|
2308
|
-
{ "province": 'ON', "fuel_type": 'Electricity', "CO2eq Emissions (kg/MBtu)": 8.79, "CO2eq Emissions (g/m3)": 30.0 },
|
|
2309
|
-
{ "province": 'PE', "fuel_type": 'Electricity', "CO2eq Emissions (kg/MBtu)": 84.99, "CO2eq Emissions (g/m3)": 290.0 },
|
|
2310
|
-
{ "province": 'QC', "fuel_type": 'Electricity', "CO2eq Emissions (kg/MBtu)": 0.47, "CO2eq Emissions (g/m3)": 1.6 },
|
|
2311
|
-
{ "province": 'SK', "fuel_type": 'Electricity', "CO2eq Emissions (kg/MBtu)": 219.81, "CO2eq Emissions (g/m3)": 750.0 },
|
|
2312
|
-
{ "province": 'YT', "fuel_type": 'Electricity', "CO2eq Emissions (kg/MBtu)": 23.15, "CO2eq Emissions (g/m3)": 79.0 }
|
|
2313
|
-
]
|
|
2314
|
-
mbtu_to_gj = 1.05505585
|
|
2315
|
-
factor = ghg_data.detect { |item| (item[:province] == province) && (item[:fuel_type] == fuel_type) }
|
|
2316
|
-
raise "could not find ghg factor for province name #{province} and fuel_type #{fuel_type}" if factor.nil?
|
|
2317
|
-
|
|
2318
|
-
return factor[:"CO2eq Emissions (kg/MBtu)"] / mbtu_to_gj
|
|
2319
|
-
end
|
|
2320
|
-
|
|
2321
|
-
def bc_energy_step_code_performance_indicators
|
|
2322
|
-
# TEDI (Thermal Energy Demand Intensity) [kWh/(m2.year)]
|
|
2323
|
-
command = "SELECT Value
|
|
2324
|
-
FROM TabularDataWithStrings
|
|
2325
|
-
WHERE ReportName='EnergyMeters'
|
|
2326
|
-
AND ReportForString='Entire Facility'
|
|
2327
|
-
AND TableName='Annual and Peak Values - Other'
|
|
2328
|
-
AND RowName='Baseboard:EnergyTransfer'
|
|
2329
|
-
AND ColumnName='Annual Value'
|
|
2330
|
-
AND Units='GJ'"
|
|
2331
|
-
baseboard_energy_transfer_gj = @sqlite_file.get.execAndReturnFirstDouble(command)
|
|
2332
|
-
baseboard_energy_transfer_kwh = OpenStudio.convert(baseboard_energy_transfer_gj.to_f, 'GJ', 'kWh')
|
|
2333
|
-
command = "SELECT Value
|
|
2334
|
-
FROM TabularDataWithStrings
|
|
2335
|
-
WHERE ReportName='EnergyMeters'
|
|
2336
|
-
AND ReportForString='Entire Facility'
|
|
2337
|
-
AND TableName='Annual and Peak Values - Other'
|
|
2338
|
-
AND RowName='HeatingCoils:EnergyTransfer'
|
|
2339
|
-
AND ColumnName='Annual Value'
|
|
2340
|
-
AND Units='GJ'"
|
|
2341
|
-
heating_coils_energy_transfer_gj = @sqlite_file.get.execAndReturnFirstDouble(command)
|
|
2342
|
-
heating_coils_energy_transfer_kwh = OpenStudio.convert(heating_coils_energy_transfer_gj.to_f, 'GJ', 'kWh')
|
|
2343
|
-
tedi_kwh_per_m_sq = (baseboard_energy_transfer_kwh.to_f + heating_coils_energy_transfer_kwh.to_f) / @btap_data['bldg_conditioned_floor_area_m_sq']
|
|
2344
|
-
@btap_data.merge!('bc_step_code_tedi_kwh_per_m_sq' => tedi_kwh_per_m_sq)
|
|
2345
|
-
|
|
2346
|
-
# MEUI (Mechanical Energy Use Intensity) [kWh/(m2.year)]
|
|
2347
|
-
meui_gj_per_m_sq = @btap_data['energy_eui_heating_gj_per_m_sq'].to_f +
|
|
2348
|
-
@btap_data['energy_eui_cooling_gj_per_m_sq'].to_f +
|
|
2349
|
-
@btap_data['energy_eui_fans_gj_per_m_sq'].to_f +
|
|
2350
|
-
@btap_data['energy_eui_pumps_gj_per_m_sq'].to_f +
|
|
2351
|
-
@btap_data['energy_eui_water systems_gj_per_m_sq'].to_f
|
|
2352
|
-
meui_kwh_per_m_sq = OpenStudio.convert(meui_gj_per_m_sq, 'GJ', 'kWh').to_f
|
|
2353
|
-
@btap_data.merge!('bc_step_code_meui_kwh_per_m_sq' => meui_kwh_per_m_sq)
|
|
2354
|
-
end
|
|
2355
|
-
|
|
2356
|
-
# The below method calculates energy demands and peak loads calculations as per PHIUS and NECB; and compares them to see if NECB meets PHIUS' performance criteria.
|
|
2357
|
-
### References:
|
|
2358
|
-
### (1) PHIUS 2021 Passive Building Standard Standard-Setting Documentation. Available at https://www.phius.org/phius-certification-for-buildings-products/project-certification/phius-2021-emissions-down-scale-up
|
|
2359
|
-
### (2) Wright, L. (2019). Setting the Heating/Cooling Performance Criteria for the PHIUS 2018 Passive Building Standard. In ASHRAE Topical Conference Proceedings, pp. 399-409
|
|
2360
|
-
def phius_performance_indicators(model)
|
|
2361
|
-
### Envelope to Floor Area ratio (EnvFlr)
|
|
2362
|
-
### Note: 'Floor Area' has been considered as iCFA (interior conditioned floor area) as per REF: Wright (2019)
|
|
2363
|
-
bldg_exterior_area_m_sq = @btap_data['bldg_exterior_area_m_sq']
|
|
2364
|
-
bldg_conditioned_floor_area_m_sq = @btap_data['bldg_conditioned_floor_area_m_sq']
|
|
2365
|
-
bldg_conditioned_floor_area_ft_sq = OpenStudio.convert(bldg_conditioned_floor_area_m_sq, 'm^2', 'ft^2').get
|
|
2366
|
-
envelope_to_floor_area_ratio = bldg_exterior_area_m_sq / bldg_conditioned_floor_area_m_sq
|
|
2367
|
-
|
|
2368
|
-
### UnitDens: Unit density (1/ft2) (inverse of the floor area per unit) in PHIUS, 2021
|
|
2369
|
-
# Note: if commercial buildings, set the number of units to 1 and divide by the floor area
|
|
2370
|
-
# This is the list of building types considered as some sort of residential buildings when the whole building method is used
|
|
2371
|
-
building_type_names_necb_2011 = ['Dormitory', 'Hospital', 'Hotel', 'Motel', 'Multi-unit residential', 'Penitentiary']
|
|
2372
|
-
building_type_names_necb_2015 = ['Dormitory', 'Health care clinic', 'Hospital', 'Hotel/Motel', 'Long-term care - dwelling units', 'Long-term care - other', 'Multi-unit residential building', 'Penitentiary']
|
|
2373
|
-
building_type_names_necb_2017 = ['Dormitory', 'Health care clinic', 'Hospital', 'Hotel/Motel', 'Long-term care - dwelling units', 'Long-term care - other', 'Multi-unit residential building', 'Penitentiary']
|
|
2374
|
-
building_type_names_list = building_type_names_necb_2011 + building_type_names_necb_2015 + building_type_names_necb_2017
|
|
2375
|
-
building_type_names_list = building_type_names_list.uniq
|
|
2376
|
-
# This is the list of space types considered as some sort of residential spaces when the space type method is used
|
|
2377
|
-
space_type_names_necb_2011 = ['Dormitory - living quarters', 'Dwelling Unit(s)', 'Hotel/Motel - rooms', 'Hway lodging - rooms']
|
|
2378
|
-
space_type_names_necb_2015 = ['Guest room', 'Dormitory living quarters', 'Dwelling units general', 'Dwelling units long-term', 'Fire station sleeping quarters', 'Health care facility patient room', 'Health care facility recovery room']
|
|
2379
|
-
space_type_names_necb_2017 = ['Guest room', 'Dormitory living quarters', 'Dwelling units general', 'Dwelling units long-term', 'Fire station sleeping quarters', 'Health care facility patient room', 'Health care facility recovery room']
|
|
2380
|
-
space_type_names_list = space_type_names_necb_2011 + space_type_names_necb_2015 + space_type_names_necb_2017
|
|
2381
|
-
space_type_names_list = space_type_names_list.uniq
|
|
2382
|
-
sum_handle = 0.0
|
|
2383
|
-
number_of_dwelling_units = 0.0
|
|
2384
|
-
@btap_data['space_table'].each do |space_info|
|
|
2385
|
-
building_type_name = space_info['building_type'].sub! 'building', ''
|
|
2386
|
-
building_type_name = building_type_name.strip unless building_type_name.nil?
|
|
2387
|
-
space_type_name = space_info['space_type_name']
|
|
2388
|
-
if !space_type_name.include?('WholeBuilding')
|
|
2389
|
-
# puts "This_is_the_space_type_method"
|
|
2390
|
-
space_type_name = space_info['space_type_name'].sub! 'Space Function ', '' # This removes 'Space Function' from space type name
|
|
2391
|
-
if space_type_names_list.include?(space_type_name)
|
|
2392
|
-
number_of_dwelling_units += 1.0 * space_info['multiplier']
|
|
2393
|
-
sum_handle += OpenStudio.convert(space_info['floor_area_m2'], 'm^2', 'ft^2').get * space_info['multiplier']
|
|
2394
|
-
end
|
|
2395
|
-
elsif space_type_name.include?('WholeBuilding') && building_type_names_list.include?(building_type_name) && space_info['is_conditioned'] == 'Yes'
|
|
2396
|
-
# puts "This_is_the_whole_building_method"
|
|
2397
|
-
number_of_dwelling_units += 1.0 * space_info['multiplier']
|
|
2398
|
-
sum_handle += OpenStudio.convert(space_info['floor_area_m2'], 'm^2', 'ft^2').get * space_info['multiplier']
|
|
2399
|
-
end
|
|
2400
|
-
end
|
|
2401
|
-
# Calculate what percentage of conditioned floor area has space types of the 'space_type_names_list' list.
|
|
2402
|
-
# This percentage is used to determine if most of a building model is sort of dwelling type or not.
|
|
2403
|
-
# The threshold for this percentage has been set to 60% based on the below reference:
|
|
2404
|
-
# GSA (2012), Circulation: Defining and planning, U.S. General Services Administration, Available at Https://Www.Gsa.Gov/
|
|
2405
|
-
# The above reference says: 'As a general planning rule of thumb, Circulation Area comprises roughly 25 to 40% of the total Usable Area.'
|
|
2406
|
-
# Moreover, ~63% and ~41% of the SmallHotel and LargeHotel archetype, respectively, are guest rooms. So, the threshold has been chosen as 40%.
|
|
2407
|
-
percentage_dwelling = 100.0 * sum_handle / bldg_conditioned_floor_area_ft_sq
|
|
2408
|
-
# now, calculate UnitDens depending on whether a building model is sort of dwelling type or not
|
|
2409
|
-
if percentage_dwelling >= 40.0 && number_of_dwelling_units > 0.0
|
|
2410
|
-
unit_density_per_ft_sq = 1.0 / (sum_handle / number_of_dwelling_units)
|
|
2411
|
-
else # i.e. if commercial buildings, set the number of units to 1 and divide by the floor area
|
|
2412
|
-
unit_density_per_ft_sq = 1.0 / bldg_conditioned_floor_area_ft_sq
|
|
2413
|
-
end
|
|
2414
|
-
|
|
2415
|
-
### Get weather file
|
|
2416
|
-
weather_file_path = model.weatherFile.get.path.get.to_s
|
|
2417
|
-
epw_file = model.weatherFile.get.file.get
|
|
2418
|
-
stat_file_path = weather_file_path.gsub('.epw', '.stat')
|
|
2419
|
-
stat_file = OpenstudioStandards::Weather::StatFile.new(stat_file_path)
|
|
2420
|
-
|
|
2421
|
-
### Cooling Degree Days, base 50degF
|
|
2422
|
-
cdd10_degree_c_days = stat_file.cdd10
|
|
2423
|
-
cdd50_degree_f_days = cdd10_degree_c_days * 9.0 / 5.0
|
|
2424
|
-
|
|
2425
|
-
### Heating Degree Days, base 65degF (note that base temperature of 18degC has been considered)
|
|
2426
|
-
hdd18_degree_c_days = stat_file.hdd18
|
|
2427
|
-
hdd65_degree_f_days = hdd18_degree_c_days * 9.0 / 5.0
|
|
2428
|
-
|
|
2429
|
-
### Dehumidification degree days
|
|
2430
|
-
### ('Dehumidification degree-days, base 0.010' in REF: Wright (2019))
|
|
2431
|
-
dehumidification_degree_days = OpenstudioStandards::Weather.epw_file_get_dehumidification_degree_days(epw_file)
|
|
2432
|
-
|
|
2433
|
-
### annual global horizontal irradiance (GHI)
|
|
2434
|
-
|
|
2435
|
-
# Workaround for case when the weather file contains the February from a leap year but that February only has 28
|
|
2436
|
-
# days of data.
|
|
2437
|
-
has_leap_day = false
|
|
2438
|
-
|
|
2439
|
-
# Find the first day in February
|
|
2440
|
-
feb_index = epw_file.data.find_index { |entry| entry.date.monthOfYear.value == 2 }
|
|
2441
|
-
|
|
2442
|
-
# Find the year for February
|
|
2443
|
-
feb_year = epw_file.data[feb_index].year
|
|
2444
|
-
# Determine if February's year is a leap year
|
|
2445
|
-
leap_year = false
|
|
2446
|
-
if (feb_year % 100) > 0
|
|
2447
|
-
leap_year = true if (feb_year % 4) == 0
|
|
2448
|
-
else
|
|
2449
|
-
leap_year = true if (feb_year % 400) == 0
|
|
2450
|
-
end
|
|
2451
|
-
# If the February is from a leap year determine if it contains a leap day
|
|
2452
|
-
if leap_year
|
|
2453
|
-
|
|
2454
|
-
day = epw_file.data[feb_index].date.dayOfMonth
|
|
2455
|
-
inc = 0
|
|
2456
|
-
|
|
2457
|
-
while epw_file.data[feb_index].date.dayOfMonth == day
|
|
2458
|
-
feb_index += 1
|
|
2459
|
-
inc += 1
|
|
2460
|
-
end
|
|
2461
|
-
|
|
2462
|
-
has_leap_day = epw_file.data[feb_index + (inc * 28)].date.dayOfMonth == 29
|
|
2463
|
-
end
|
|
2464
|
-
|
|
2465
|
-
# If the February is from a leap year and there is no leap day then do not use the faulty OpenStudio Epw
|
|
2466
|
-
# .getTimeSeries method. Otherwise, use the method.
|
|
2467
|
-
if has_leap_day || !leap_year
|
|
2468
|
-
ghi_timeseries = epw_file.getTimeSeries('Global Horizontal Radiation').get.values
|
|
2469
|
-
else
|
|
2470
|
-
# Access the data directly instead of using the OpenStudio API to avoid the faulty OpenStudioEpw
|
|
2471
|
-
# .getTimeSeries method.
|
|
2472
|
-
|
|
2473
|
-
# Open the weather file
|
|
2474
|
-
regex_csv = /[^,]+/
|
|
2475
|
-
regex_num = /[0-9]/
|
|
2476
|
-
f = File.open(epw_file.path.to_s, 'r')
|
|
2477
|
-
i = 0
|
|
2478
|
-
|
|
2479
|
-
# Skip the header
|
|
2480
|
-
i += 1 until f.readline[0] =~ regex_num
|
|
2481
|
-
|
|
2482
|
-
# Get all of the hourly weather data
|
|
2483
|
-
lines = IO.readlines(f)[i..-1]
|
|
2484
|
-
|
|
2485
|
-
# Get hourly weather data for a specific column
|
|
2486
|
-
ghi_timeseries = lines.map {|line| Float(line.scan(regex_csv)[13])}
|
|
2487
|
-
end
|
|
2488
|
-
|
|
2489
|
-
annual_ghi_kwh_per_m_sq = ghi_timeseries.sum / 1000.0
|
|
2490
|
-
|
|
2491
|
-
### THD-1 Temperature at the colder of the two heating design conditions in PHIUS, 2021
|
|
2492
|
-
### ('Heating design temperature' in REF: Wright (2019))
|
|
2493
|
-
thd_degree_c = stat_file.heating_design_info[1]
|
|
2494
|
-
thd_degree_f = OpenStudio.convert(thd_degree_c, 'C', 'F').get
|
|
2495
|
-
|
|
2496
|
-
### TCD Temperature at the cooling design condition in PHIUS, 2021
|
|
2497
|
-
### ('Cooling design temperature' in REF: Wright (2019))
|
|
2498
|
-
tcd_degree_c = stat_file.cooling_design_info[2]
|
|
2499
|
-
tcd_degree_f = OpenStudio.convert(tcd_degree_c.to_f, 'C', 'F').get
|
|
2500
|
-
|
|
2501
|
-
### IGHL (Irradiance, Global, at the heating design condition) (Btu/h.ft2) in PHIUS, 2021
|
|
2502
|
-
average_daily_global_irradiance_w_per_m2_array = []
|
|
2503
|
-
model.getDesignDays.each do |design_day|
|
|
2504
|
-
next unless design_day.dayType == 'WinterDesignDay'
|
|
2505
|
-
|
|
2506
|
-
average_daily_global_irradiance_w_per_m2 = OpenstudioStandards::Weather.design_day_average_global_irradiance(design_day)
|
|
2507
|
-
average_daily_global_irradiance_w_per_m2_array << average_daily_global_irradiance_w_per_m2
|
|
2508
|
-
end
|
|
2509
|
-
solar_irradiance_on_heating_design_day_w_per_m_sq = average_daily_global_irradiance_w_per_m2_array.min
|
|
2510
|
-
solar_irradiance_on_heating_design_day_btu_per_hr_ft_sq = OpenStudio.convert(solar_irradiance_on_heating_design_day_w_per_m_sq.to_f, 'W/m^2', 'Btu/ft^2*h').get
|
|
2511
|
-
|
|
2512
|
-
### IGCL (Irradiance, Global, at the cooling design condition) (Btu/h.ft2) in PHIUS, 2021
|
|
2513
|
-
average_daily_global_irradiance_w_per_m2_array = []
|
|
2514
|
-
model.getDesignDays.each do |design_day|
|
|
2515
|
-
next unless design_day.dayType == 'SummerDesignDay'
|
|
2516
|
-
|
|
2517
|
-
average_daily_global_irradiance_w_per_m2 = OpenstudioStandards::Weather.design_day_average_global_irradiance(design_day)
|
|
2518
|
-
average_daily_global_irradiance_w_per_m2_array << average_daily_global_irradiance_w_per_m2
|
|
2519
|
-
end
|
|
2520
|
-
solar_irradiance_on_cooling_design_day_w_per_m_sq = average_daily_global_irradiance_w_per_m2_array.max
|
|
2521
|
-
solar_irradiance_on_cooling_design_day_btu_per_hr_ft_sq = OpenStudio.convert(solar_irradiance_on_cooling_design_day_w_per_m_sq.to_f, 'W/m^2', 'Btu/ft^2*h').get
|
|
2522
|
-
|
|
2523
|
-
### occupant density (persons per ft2 of floor area)
|
|
2524
|
-
sum_handle = 0.0
|
|
2525
|
-
@btap_data['space_type_table'].each do |space_info|
|
|
2526
|
-
unless space_info['occ_per_m_sq'].nil?
|
|
2527
|
-
sum_handle += space_info['floor_m_sq'] * space_info['occ_per_m_sq']
|
|
2528
|
-
end
|
|
2529
|
-
end
|
|
2530
|
-
occ_density_person_per_m_sq = sum_handle / bldg_conditioned_floor_area_m_sq
|
|
2531
|
-
occ_density_person_per_ft_sq = OpenStudio.convert(occ_density_person_per_m_sq, 'ft^2', 'm^2').get
|
|
2532
|
-
|
|
2533
|
-
### marginal electricity price ($/kWh)
|
|
2534
|
-
### ('Electricity price' in REF: Wright (2019))
|
|
2535
|
-
electricity_price_per_gj = @btap_data['cost_utility_neb_electricity_cost_per_m_sq'] / @btap_data['energy_eui_electricity_gj_per_m_sq']
|
|
2536
|
-
electricity_price_per_kwh = OpenStudio.convert(electricity_price_per_gj, 'kWh', 'GJ').get # note: this is not GJ to kWh since 1/GJ should be converted to 1/kWh.
|
|
2537
|
-
|
|
2538
|
-
### Calculate annual heating and cooling energy demands based on PHIUS
|
|
2539
|
-
# REF: page 27 of PHIUS 2021 Passive Building Standard Standard-Setting Documentation. Available at https://www.phius.org/phius-certification-for-buildings-products/project-certification/phius-2021-emissions-down-scale-up
|
|
2540
|
-
annual_heating_demand_kbtu_per_ft_sq_phius = 3.2606827206 +
|
|
2541
|
-
1.1634499236 * envelope_to_floor_area_ratio +
|
|
2542
|
-
904.39163818 * unit_density_per_ft_sq +
|
|
2543
|
-
0.000604853 * hdd65_degree_f_days +
|
|
2544
|
-
-0.001645777 * annual_ghi_kwh_per_m_sq +
|
|
2545
|
-
-11.87299596 * electricity_price_per_kwh +
|
|
2546
|
-
(envelope_to_floor_area_ratio - 1.766) * (envelope_to_floor_area_ratio - 1.766) * 0.8314860529 +
|
|
2547
|
-
(envelope_to_floor_area_ratio - 1.766) * (hdd65_degree_f_days - 5860.0833333) * 0.0002310823 +
|
|
2548
|
-
(hdd65_degree_f_days - 5860.0833333) * (hdd65_degree_f_days - 5860.0833333) * -5.736435e-8 +
|
|
2549
|
-
(hdd65_degree_f_days - 5860.0833333) * (annual_ghi_kwh_per_m_sq - 1451.0633333) * -3.260379e-7 +
|
|
2550
|
-
(envelope_to_floor_area_ratio - 1.766) * (electricity_price_per_kwh - -0.2029193333) * -3.851052937 +
|
|
2551
|
-
(hdd65_degree_f_days - 5860.0833333) * (electricity_price_per_kwh - -0.2029193333) * -0.001897043
|
|
2552
|
-
annual_heating_demand_kwh_per_m_sq_phius = OpenStudio.convert(annual_heating_demand_kbtu_per_ft_sq_phius, 'kBtu/ft^2', 'kWh/m^2').get
|
|
2553
|
-
@btap_data.merge!('phius_annual_heating_demand_kwh_per_m_sq' => annual_heating_demand_kwh_per_m_sq_phius)
|
|
2554
|
-
|
|
2555
|
-
# REF: page 28 of PHIUS 2021 Passive Building Standard Standard-Setting Documentation. Available at https://www.phius.org/phius-certification-for-buildings-products/project-certification/phius-2021-emissions-down-scale-up
|
|
2556
|
-
annual_cooling_demand_kbtu_per_ft_sq_phius = -6.510791255 +
|
|
2557
|
-
-0.749993351 * envelope_to_floor_area_ratio +
|
|
2558
|
-
0.0004550801 * cdd50_degree_f_days +
|
|
2559
|
-
0.004990109 * annual_ghi_kwh_per_m_sq +
|
|
2560
|
-
7.9460878688 * dehumidification_degree_days +
|
|
2561
|
-
(envelope_to_floor_area_ratio - 1.766) * (envelope_to_floor_area_ratio - 1.766) * 1.6367059356 +
|
|
2562
|
-
(cdd50_degree_f_days - 4104.8333333) * (cdd50_degree_f_days - 4104.8333333) * 8.6952014e-8 +
|
|
2563
|
-
(envelope_to_floor_area_ratio - 1.766) * (annual_ghi_kwh_per_m_sq - 1451.0633333) * 0.001671947 +
|
|
2564
|
-
(cdd50_degree_f_days - 4104.8333333) * (annual_ghi_kwh_per_m_sq - 1451.0633333) * 0.0000013639 +
|
|
2565
|
-
(unit_density_per_ft_sq - 0.0008646735) * (dehumidification_degree_days - 0.3233057481) * 5547.7542211 +
|
|
2566
|
-
(dehumidification_degree_days - 0.3233057481) * (electricity_price_per_kwh - 0.2029193333) * -15.67511944 +
|
|
2567
|
-
1624.6144639 * unit_density_per_ft_sq
|
|
2568
|
-
annual_cooling_demand_kwh_per_m_sq_phius = OpenStudio.convert(annual_cooling_demand_kbtu_per_ft_sq_phius, 'kBtu/ft^2', 'kWh/m^2').get
|
|
2569
|
-
@btap_data.merge!('phius_annual_cooling_demand_kwh_per_m_sq' => annual_cooling_demand_kwh_per_m_sq_phius)
|
|
2570
|
-
|
|
2571
|
-
### Calculate peak heating and cooling loads based on PHIUS
|
|
2572
|
-
# REF: page 29 of PHIUS 2021 Passive Building Standard Standard-Setting Documentation. Available at https://www.phius.org/phius-certification-for-buildings-products/project-certification/phius-2021-emissions-down-scale-up
|
|
2573
|
-
peak_heating_load_btu_per_hr_ft_sq_phius = 4.6700403241 +
|
|
2574
|
-
0.6774809481 * envelope_to_floor_area_ratio +
|
|
2575
|
-
239.08369574 * occ_density_person_per_ft_sq +
|
|
2576
|
-
596.681543 * unit_density_per_ft_sq +
|
|
2577
|
-
-0.000177742 * hdd65_degree_f_days +
|
|
2578
|
-
-0.076727655 * thd_degree_f +
|
|
2579
|
-
-0.03316804 * solar_irradiance_on_heating_design_day_btu_per_hr_ft_sq +
|
|
2580
|
-
-4.140193817 * electricity_price_per_kwh +
|
|
2581
|
-
(envelope_to_floor_area_ratio - 1.766) * (envelope_to_floor_area_ratio - 1.766) * 0.8449921713 +
|
|
2582
|
-
(hdd65_degree_f_days - 5860.0833333) * (hdd65_degree_f_days - 5860.0833333) * 2.8376386e-8 +
|
|
2583
|
-
(envelope_to_floor_area_ratio - 1.766) * (thd_degree_f - 14.7102) * -0.013821021 +
|
|
2584
|
-
(unit_density_per_ft_sq - 0.0008646735) * (thd_degree_f - 14.7102) * -20.10551451 +
|
|
2585
|
-
(hdd65_degree_f_days - 5860.0833333) * (thd_degree_f - 14.7102) * 5.1870203e-6 +
|
|
2586
|
-
(thd_degree_f - 14.7102) * (electricity_price_per_kwh - 0.2029193333) * 0.1264922802
|
|
2587
|
-
peak_heating_load_w_per_m_sq_phius = OpenStudio.convert(peak_heating_load_btu_per_hr_ft_sq_phius, 'Btu/ft^2*h', 'W/m^2').get
|
|
2588
|
-
@btap_data.merge!('phius_peak_heating_load_w_per_m_sq' => peak_heating_load_w_per_m_sq_phius)
|
|
2589
|
-
|
|
2590
|
-
# REF: page 30 of PHIUS 2021 Passive Building Standard Standard-Setting Documentation. Available at https://www.phius.org/phius-certification-for-buildings-products/project-certification/phius-2021-emissions-down-scale-up
|
|
2591
|
-
peak_cooling_load_btu_per_hr_ft_sq_phius = -7.289806442 +
|
|
2592
|
-
98.245977611 * occ_density_person_per_ft_sq +
|
|
2593
|
-
236.93351876 * unit_density_per_ft_sq +
|
|
2594
|
-
0.0967328928 * tcd_degree_f +
|
|
2595
|
-
0.010777725 * solar_irradiance_on_cooling_design_day_btu_per_hr_ft_sq +
|
|
2596
|
-
(cdd50_degree_f_days - 4104.8333333) * (cdd50_degree_f_days - 4104.8333333) * 1.7699655e-8 +
|
|
2597
|
-
(cdd50_degree_f_days - 4104.8333333) * (tcd_degree_f - 78.127) * 6.5268802e-6 +
|
|
2598
|
-
(tcd_degree_f - 78.127) * (envelope_to_floor_area_ratio - 1.766) * 0.0165401721 +
|
|
2599
|
-
(tcd_degree_f - 78.127) * (occ_density_person_per_ft_sq - 0.0027218) * 8.0465528305 +
|
|
2600
|
-
(cdd50_degree_f_days - 4104.8333333) * (envelope_to_floor_area_ratio - 1.766) * 0.0000322288 +
|
|
2601
|
-
(envelope_to_floor_area_ratio - 1.766) * (envelope_to_floor_area_ratio - 1.766) * 0.6579032913
|
|
2602
|
-
peak_cooling_load_w_per_m_sq_phius = OpenStudio.convert(peak_cooling_load_btu_per_hr_ft_sq_phius, 'Btu/ft^2*h', 'W/m^2').get
|
|
2603
|
-
@btap_data.merge!('phius_peak_cooling_load_w_per_m_sq' => peak_cooling_load_w_per_m_sq_phius)
|
|
2604
|
-
|
|
2605
|
-
### Gather annual heating and cooling energy demands based on NECB
|
|
2606
|
-
|
|
2607
|
-
annual_heating_demand_kwh_per_m_sq_necb = OpenStudio.convert(@btap_data['energy_eui_heating_gj_per_m_sq'], 'GJ', 'kWh') unless @btap_data['energy_eui_heating_gj_per_m_sq'].nil?
|
|
2608
|
-
annual_cooling_demand_kwh_per_m_sq_necb = OpenStudio.convert(@btap_data['energy_eui_cooling_gj_per_m_sq'], 'GJ', 'kWh') unless @btap_data['energy_eui_cooling_gj_per_m_sq'].nil?
|
|
2609
|
-
|
|
2610
|
-
### Gather peak heating and cooling loads based on NECB
|
|
2611
|
-
peak_heating_load_w_per_m_sq_necb = @btap_data['heating_peak_w_per_m_sq']
|
|
2612
|
-
peak_cooling_load_w_per_m_sq_necb = @btap_data['cooling_peak_w_per_m_sq']
|
|
2613
|
-
@btap_data.merge!('peak_heating_load_w_per_m_sq_necb' => peak_heating_load_w_per_m_sq_necb)
|
|
2614
|
-
@btap_data.merge!('peak_cooling_load_w_per_m_sq_necb' => peak_cooling_load_w_per_m_sq_necb)
|
|
2615
|
-
|
|
2616
|
-
### Compare annual heating and cooling energy demands of NECB with PHIUS to see if NECB meets PHIUS
|
|
2617
|
-
if annual_heating_demand_kwh_per_m_sq_necb.to_f <= annual_heating_demand_kwh_per_m_sq_phius.to_f
|
|
2618
|
-
@btap_data.merge!('phius_necb_meet_heating_demand' => 'True')
|
|
2619
|
-
else
|
|
2620
|
-
@btap_data.merge!('phius_necb_meet_heating_demand' => 'False')
|
|
2621
|
-
end
|
|
2622
|
-
if annual_cooling_demand_kwh_per_m_sq_necb.to_f <= annual_cooling_demand_kwh_per_m_sq_phius.to_f
|
|
2623
|
-
@btap_data.merge!('phius_necb_meet_cooling_demand' => 'True')
|
|
2624
|
-
else
|
|
2625
|
-
@btap_data.merge!('phius_necb_meet_cooling_demand' => 'False')
|
|
2626
|
-
end
|
|
2627
|
-
|
|
2628
|
-
### Compare peak heating and cooling loads of NECB with PHIUS to see if NECB meets PHIUS
|
|
2629
|
-
if peak_heating_load_w_per_m_sq_necb.to_f <= peak_heating_load_w_per_m_sq_phius.to_f
|
|
2630
|
-
@btap_data.merge!('phius_necb_meet_heating_peak_load' => 'True')
|
|
2631
|
-
else
|
|
2632
|
-
@btap_data.merge!('phius_necb_meet_heating_peak_load' => 'False')
|
|
2633
|
-
end
|
|
2634
|
-
if peak_cooling_load_w_per_m_sq_necb.to_f <= peak_cooling_load_w_per_m_sq_phius.to_f
|
|
2635
|
-
@btap_data.merge!('phius_necb_meet_cooling_peak_load' => 'True')
|
|
2636
|
-
else
|
|
2637
|
-
@btap_data.merge!('phius_necb_meet_cooling_peak_load' => 'False')
|
|
2638
|
-
end
|
|
2639
|
-
# def phius_metrics(model)
|
|
2640
|
-
end
|
|
2641
|
-
|
|
2642
|
-
def get_national_ghg_cost(year:)
|
|
2643
|
-
raise "Nil year submitted for national ghg cost" if year.nil?
|
|
2644
|
-
year = year.to_i
|
|
2645
|
-
if year < 2019
|
|
2646
|
-
price = 0.0
|
|
2647
|
-
elsif (year >= 2019) && (year <= 2030)
|
|
2648
|
-
ghg_price_data = @standards_data["tables"]["canada_national_carbon_pricing"]["table"]
|
|
2649
|
-
year_price = ghg_price_data.detect { |item| (item["year"].to_i == year) }
|
|
2650
|
-
price = year_price["price_per_tonne"].to_f
|
|
2651
|
-
elsif (year > 2030)
|
|
2652
|
-
price = 170.0
|
|
2653
|
-
end
|
|
2654
|
-
return price.to_f
|
|
2655
|
-
end
|
|
2656
|
-
end
|