Source code for honeybee_energy.result.emissions

"""Module for converting EnergyPlus results into operational carbon emissions."""
import os
from collections import OrderedDict

from ladybug.sql import SQLiteResult


[docs] def emissions_region(location): """Get the region of carbon emissions associated with a location. Args: location: A ladybug Location object, which will be used to determine the subregion. Returns: A Tuple of text for the eGrid subregion for the location. The first value is the future emissions region, the second is the historical region, and the last is the historic hourly region. This will be None if the location cannot be mapped to a region. """ # create the map from states to electric regions region_map = { 'FL': ('FRCCc', 'FRCC', 'Florida'), 'MS': ('SRMVc', 'SRMV', 'Midwest'), 'NE': ('MROWc', 'MROW', 'Midwest'), 'OR': ('NWPPc', 'NWPP', 'Northwest'), 'CA': ('CAMXc', 'CAMX', 'California'), 'VA': ('SRVCc', 'SRVC', 'Carolinas'), 'AR': ('SRMVc', 'SRMV', 'Midwest'), 'TX': ('ERCTc', 'ERCT', 'Texas'), 'OH': ('RFCWc', 'RFCW', 'Midwest'), 'UT': ('NWPPc', 'NWPP', 'Northwest'), 'MT': ('NWPPc', 'NWPP', 'Northwest'), 'TN': ('SRTVc', 'SRTV', 'Tennessee'), 'ID': ('NWPPc', 'NWPP', 'Northwest'), 'WI': ('MROEc', 'MROE', 'Midwest'), 'WV': ('RFCWc', 'RFCW', 'Midwest'), 'NC': ('SRVCc', 'SRVC', 'Carolinas'), 'LA': ('SRMVc', 'SRMV', 'Midwest'), 'IL': ('SRMWc', 'SRMW', 'Midwest'), 'OK': ('SPSOc', 'SPSO', 'Central'), 'IA': ('MROWc', 'MROW', 'Midwest'), 'WA': ('NWPPc', 'NWPP', 'Northwest'), 'SD': ('MROWc', 'MROW', 'Midwest'), 'MN': ('MROWc', 'MROW', 'Midwest'), 'KY': ('SRTVc', 'SRTV', 'Tennessee'), 'MI': ('RFCMc', 'RFCM', 'Midwest'), 'KS': ('SPNOc', 'SPNO', 'Central'), 'NJ': ('RFCEc', 'RFCE', 'Mid-Atlantic'), 'NY': ('NYSTc', 'NYCW', 'New York'), 'IN': ('RFCWc', 'RFCW', 'Midwest'), 'VT': ('NEWEc', 'NEWE', 'New England'), 'NM': ('AZNMc', 'AZNM', 'Southwest'), 'WY': ('RMPAc', 'RMPA', 'Rocky Mountains'), 'GA': ('SRSOc', 'SRSO', 'SRSO'), 'MO': ('SRMWc', 'SRMW', 'Midwest'), 'DC': ('RFCEc', 'RFCE', 'Mid-Atlantic'), 'SC': ('SRVCc', 'SRVC', 'Carolinas'), 'PA': ('RFCEc', 'RFCE', 'Mid-Atlantic'), 'CO': ('RMPAc', 'RMPA', 'Rocky Mountains'), 'AZ': ('AZNMc', 'AZNM', 'Southwest'), 'ME': ('NEWEc', 'NEWE', 'New England'), 'AL': ('SRSOc', 'SRSO', 'Southeast'), 'MD': ('RFCEc', 'RFCE', 'Mid-Atlantic'), 'NH': ('NEWEc', 'NEWE', 'New England'), 'MA': ('NEWEc', 'NEWE', 'New England'), 'ND': ('MROWc', 'MROW', 'Midwest'), 'NV': ('NWPPc', 'NWPP', 'Northwest'), 'CT': ('NEWEc', 'NEWE', 'New England'), 'DE': ('RFCEc', 'RFCE', 'Mid-Atlantic'), 'RI': ('NEWEc', 'NEWE', 'New England') } # return the region try: return region_map[location.state] except KeyError: # location could not be mapped to a region return None
[docs] def future_electricity_emissions(location, year=2030): """Get the future carbon emissions of the electric grid associated with a location. Args: location: A ladybug Location object, which will be used to determine the subregion. year: An integer for the future year for which carbon emissions will be estimated. Values must be an even number and be between 2020 and 2050. (Default: 2030). Returns: A number for the electric grid carbon emissions in kg CO2 per MWh. """ # create the map between regions, years, and carbon years = (2020, 2022, 2024, 2026, 2028, 2030, 2032, 2034, 2036, 2038, 2040, 2042, 2044, 2046, 2048, 2050) region_map = { 'AZNMc': (352, 412, 404, 389, 335, 301, 288, 279, 243, 208, 179, 182, 144, 136, 132, 126), 'CAMXc': (212, 216, 198, 180, 159, 150, 147, 140, 130, 112, 101, 94, 82, 77, 75, 69), 'ERCTc': (344, 342, 302, 302, 271, 208, 169, 158, 156, 141, 147, 128, 120, 105, 103, 88), 'FRCCc': (368, 377, 381, 399, 355, 288, 273, 277, 268, 259, 251, 228, 192, 193, 190, 179), 'MROEc': (411, 419, 411, 427, 389, 335, 285, 156, 149, 156, 146, 145, 121, 107, 122, 99), 'MROWc': (375, 386, 354, 325, 298, 185, 149, 133, 127, 126, 129, 110, 88, 89, 79, 70), 'NEWEc': (136, 148, 132, 108, 96, 81, 82, 70, 73, 67, 67, 58, 60, 59, 58, 59), 'NWPPc': (177, 225, 185, 170, 148, 137, 138, 136, 135, 124, 111, 108, 103, 91, 87, 66), 'NYSTc': (189, 206, 153, 134, 111, 92, 92, 79, 86, 83, 77, 78, 79, 78, 83, 83), 'RFCEc': (276, 285, 254, 250, 238, 209, 206, 199, 203, 206, 198, 192, 187, 191, 170, 162), 'RFCMc': (613, 634, 523, 527, 480, 393, 374, 357, 338, 319, 291, 287, 290, 227, 203, 156), 'RFCWc': (493, 508, 467, 477, 463, 415, 390, 363, 335, 316, 287, 259, 242, 215, 217, 197), 'RMPAc': (528, 580, 577, 584, 545, 444, 421, 354, 302, 301, 302, 259, 253, 194, 174, 168), 'SPNOc': (461, 447, 249, 235, 228, 182, 162, 162, 156, 157, 155, 152, 117, 122, 130, 132), 'SPSOc': (287, 277, 161, 143, 134, 119, 87, 82, 72, 64, 60, 56, 47, 49, 58, 56), 'SRMVc': (421, 411, 358, 349, 329, 281, 269, 264, 253, 248, 241, 223, 206, 234, 233, 216), 'SRMWc': (610, 637, 551, 542, 476, 366, 382, 366, 348, 335, 284, 246, 211, 187, 186, 175), 'SRSOc': (334, 322, 327, 347, 263, 222, 206, 208, 207, 188, 185, 161, 145, 123, 126, 112), 'SRTVc': (517, 554, 487, 513, 503, 408, 379, 355, 327, 325, 309, 281, 261, 230, 209, 176), 'SRVCc': (293, 303, 301, 282, 259, 212, 203, 191, 188, 185, 179, 156, 149, 128, 121, 98), } # return the carbon intensity of electricity yr_i = years.index(year) region_str = emissions_region(location) if region_str is not None: return region_map[region_str[0]][yr_i]
[docs] def emissions_from_sql(sql_results, electricity_emissions): """Get a dictionary of Carbon Emissions Intensity results from EnergyPlus SQLs. This input emissions of electricity will be used to compute carbon intensity for both electricity and district heating/cooling. Fixed numbers will be used to convert the following on-site fuel sources: * Natural Gas -- 277.358 kg/MWh * Propane -- 323.897 kg/MWh * Fuel Oil -- 294.962 kg/MWh * District Heating -- 369.811 kg/MWh * District Cooling -- [electricity_emissions] / 3.5 kg/MWh Args: sql_results: The file path of the SQL result file that has been generated from an energy simulation. This can also be a list of SQL result files in which case EUI will be computed across all files. Lastly, it can be a directory or list of directories containing results, in which case, EUI will be calculated form all files ending in .sql. electricity_emissions: A number for the electric grid carbon emissions in kg CO2 per MWh. For locations in the USA, this can be obtained from the future_electricity_emissions method. For locations outside of the USA where specific data is unavailable, the following rules of thumb may be used as a guide: * 800 kg/MWh - for an inefficient coal or oil-dominated grid * 400 kg/MWh - for the US (energy mixed) grid around 2020 * 100-200 kg/MWh - for grids with majority renewable/nuclear composition * 0-100 kg/MWh - for grids with renewables and storage Returns: A dictionary with several keys. - carbon_intensity -- A number for the total annual carbon intensity. This is the sum of all operational carbon emissions divided by the gross floor area (including both conditioned and unconditioned spaces). Units are kg CO2/m2. - total_floor_area -- A number for the gross floor area of the building in m2. This excludes Rooms with True exclude_floor_area property. - conditioned_floor_area -- A number for the conditioned floor area of the building in m2. This excludes Rooms with True exclude_floor_area property. - total_carbon -- A number for the total annual operational carbon in kg of Co2. - end_uses -- A dictionary with the carbon intensity for each of the end uses of the building (eg. heating, cooling, lighting, etc.). - sources -- A dictionary with the carbon intensity for each of the energy sources of the building (eg. electricity, natural_gas, district_heat, etc.). """ # create a list of sql file path that were either passed directly or are # contained within passed folders if not isinstance(sql_results, (list, tuple)): sql_results = [sql_results] sql_paths = [] for file_or_folder_path in sql_results: if os.path.isdir(file_or_folder_path): for file_path in os.listdir(file_or_folder_path): if file_path.endswith('.sql'): sql_paths.append(os.path.join(file_or_folder_path, file_path)) else: sql_paths.append(file_or_folder_path) # set initial values that will be computed based on results total_floor_area, conditioned_floor_area = 0, 0 tot_elec, tot_gas, tot_pro, tot_oil, tot_heat, tot_cool = 0, 0, 0, 0, 0, 0 end_uses = OrderedDict() # loop through the sql files and add the energy use for sql_path in sql_paths: # parse the SQL file sql_obj = SQLiteResult(sql_path) # get the total floor area of the model area_dict = sql_obj.tabular_data_by_name('Building Area') areas = tuple(area_dict.values()) total_floor_area += areas[0][0] conditioned_floor_area += areas[1][0] # get the energy use eui_dict = sql_obj.tabular_data_by_name('End Uses By Subcategory') for category, vals in eui_dict.items(): total_use = sum([val for val in vals[:12]]) if total_use != 0: elec = (vals[0] * electricity_emissions) / 1000 gas = (vals[1] * 277.358) / 1000 pro = (vals[7] * 323.897) / 1000 oil = (vals[6] * 294.962) / 1000 heat = (vals[11] * 369.811) / 1000 cool = (vals[10] * (electricity_emissions / 3.5)) / 1000 tot_elec += elec tot_gas += gas tot_pro += pro tot_oil += oil tot_heat += heat tot_cool += cool carb = sum((elec, gas, pro, oil, heat, cool)) cat, sub_cat = category.split(':') eu_cat = cat if sub_cat == 'General' or sub_cat == 'Other' \ else sub_cat try: end_uses[eu_cat] += carb except KeyError: end_uses[eu_cat] = carb # compute the total carbon emissions sources = OrderedDict([ ('electricity', tot_elec), ('natural_gas', tot_gas), ('propane', tot_pro), ('oil', tot_oil), ('district_heat', tot_heat), ('district_cool', tot_cool) ]) total_carbon = sum(sources.values()) # assemble all of the results into a final dictionary if total_floor_area != 0: result_dict = { 'carbon_intensity': round(total_carbon / total_floor_area, 3), 'total_floor_area': total_floor_area, 'conditioned_floor_area': conditioned_floor_area, 'total_carbon': round(total_carbon, 3) } result_dict['end_uses'] = OrderedDict( [(key, round(val / total_floor_area, 3)) for key, val in end_uses.items()] ) result_dict['sources'] = OrderedDict( [(key, round(val / total_floor_area, 3)) for key, val in sources.items() if val != 0] ) else: result_dict = { 'carbon_intensity': 0.0, 'total_floor_area': total_floor_area, 'conditioned_floor_area': conditioned_floor_area, 'total_carbon': round(total_carbon, 3) } result_dict['end_uses'] = OrderedDict([(key, 0.0) for key in end_uses.keys()]) result_dict['sources'] = OrderedDict( [(key, 0.0) for key, val in sources.items() if val != 0] ) return result_dict