diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..674ec5e --- /dev/null +++ b/__init__.py @@ -0,0 +1,5 @@ +""" +Cost workflow initialization +""" + + diff --git a/main.py b/__main__.py similarity index 69% rename from main.py rename to __main__.py index 15ce7e9..7098c41 100644 --- a/main.py +++ b/__main__.py @@ -1,7 +1,8 @@ """ Costs Workflow SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2022 Project Author Pilar Monsalvete Álvarez de Uribarri pilar.monsalvete@concordia.ca +Copyright © 2022 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +Code contributor Oriol Gavalda Torrellas oriol.gavalda@concordia.ca """ import glob @@ -28,6 +29,7 @@ def _npv_from_list(npv_discount_rate, list_cashflow): lcc_value = npf.npv(npv_discount_rate, list_cashflow) return lcc_value + def _search_archetype(costs_catalog, building_function): costs_archetypes = costs_catalog.entries('archetypes').archetypes for building_archetype in costs_archetypes: @@ -35,7 +37,8 @@ def _search_archetype(costs_catalog, building_function): return building_archetype raise KeyError('archetype not found') -file_path = (Path(__file__).parent.parent/'costs_workflow'/'input_files'/'selected_building_2864.geojson') + +file_path = (Path(__file__).parent.parent / 'costs_workflow' / 'input_files' / 'selected_building_2864.geojson') climate_reference_city = 'Montreal' weather_file = 'CAN_PQ_Montreal.Intl.AP.716270_CWEC.epw' weather_format = 'epw' @@ -44,7 +47,7 @@ usage_format = 'comnet' energy_systems_format = 'montreal_custom' attic_heated_case = 0 basement_heated_case = 1 -tmp_folder = (Path(__file__).parent.parent/'monthly_energy_balance_workflow'/'tmp') +tmp_folder = (Path(__file__).parent.parent / 'monthly_energy_balance_workflow' / 'tmp') out_path = (Path(__file__).parent.parent / 'costs_workflow' / 'out_files') files = glob.glob(f'{out_path}/*') @@ -58,7 +61,7 @@ electricity_peak_index = 0.05 electricity_price_index = 0.05 gas_price_index = 0.05 discount_rate = 0.03 -retrofitting_year_of_construction =2020 +retrofitting_year_of_construction = 2020 retrofitting_scenarios = [0, 1, 2, 3] life_cycle_results = pd.DataFrame() @@ -96,10 +99,10 @@ for building in city.buildings: for retrofitting_scenario in retrofitting_scenarios: if retrofitting_scenario == 1 or retrofitting_scenario == 3: - for building in city.buildings: - building.year_of_construction=2020 - ConstructionFactory(construction_format, city).enrich() - print('enrich retrofitted constructions... done') + for building in city.buildings: + building.year_of_construction = 2020 + ConstructionFactory(construction_format, city).enrich() + print('enrich retrofitted constructions... done') if retrofitting_scenario == 2 or retrofitting_scenario == 3: for building in city.buildings: @@ -114,50 +117,49 @@ for retrofitting_scenario in retrofitting_scenarios: print(f'beginning costing scenario {retrofitting_scenario} systems... done') for building in city.buildings: - #try: + function = Dictionaries().hub_function_to_montreal_custom_costs_function[building.function] archetype = _search_archetype(catalog, function) - #except KeyError: - # logger.error(f'Building {building.name} has unknown costs archetype for building function: ' - # f'{building.function}\n') - # sys.stderr.write(f'Building {building.name} has unknown costs archetype for building function: ' - # f'{building.function}\n') - #continue print('lcc for first building started') if "gas" in building.energy_systems_archetype_name: - #0 to electricity, 1 to gas fuel_type = 1 else: fuel_type = 0 print(f'fuel type {fuel_type}') lcc = LifeCycleCosts(building, archetype, number_of_years, consumer_price_index, electricity_peak_index, - electricity_price_index, gas_price_index, discount_rate, retrofitting_scenario,fuel_type) + electricity_price_index, gas_price_index, discount_rate, retrofitting_scenario, fuel_type) global_capital_costs = lcc.calculate_capital_costs() global_end_of_life_costs = lcc.calculate_end_of_life_costs() global_operational_costs = lcc.calculate_total_operational_costs() global_maintenance_costs = lcc.calculate_total_maintenance_costs() global_operational_incomes = lcc.calculate_total_operational_incomes() - df_capital_costs_skin = global_capital_costs['B2010_opaque_walls']+\ - global_capital_costs['B2020_transparent']+\ - global_capital_costs['B3010_opaque_roof']+\ - global_capital_costs['B10_superstructure'] - df_capital_costs_systems = global_capital_costs['D3020_heat_generating_systems']+\ - global_capital_costs['D3030_cooling_generation_systems']+\ - global_capital_costs['D3080_other_hvac_ahu']+\ - global_capital_costs['D5020_lighting_and_branch_wiring']+\ - global_capital_costs['D301010_photovoltaic_system'] + df_capital_costs_skin = ( + global_capital_costs['B2010_opaque_walls'] + global_capital_costs['B2020_transparent'] + + global_capital_costs['B3010_opaque_roof'] + global_capital_costs['B10_superstructure'] + ) + df_capital_costs_systems = ( + global_capital_costs['D3020_heat_generating_systems'] + + global_capital_costs['D3030_cooling_generation_systems'] + + global_capital_costs['D3080_other_hvac_ahu'] + + global_capital_costs['D5020_lighting_and_branch_wiring'] + + global_capital_costs['D301010_photovoltaic_system'] + ) df_end_of_life_costs = global_end_of_life_costs['End_of_life_costs'] - df_operational_costs = global_operational_costs['Fixed_costs_electricity_peak']+\ - global_operational_costs['Fixed_costs_electricity_monthly']+\ - global_operational_costs['Fixed_costs_electricity_peak']+\ - global_operational_costs['Fixed_costs_electricity_monthly']+\ - global_operational_costs['Variable_costs_electricity']+ \ - global_operational_costs['Fixed_costs_gas']+ \ - global_operational_costs['Variable_costs_gas'] - df_maintenance_costs = global_maintenance_costs['Heating_maintenance']+\ - global_maintenance_costs['Cooling_maintenance']+\ - global_maintenance_costs['PV_maintenance'] + df_operational_costs = ( + global_operational_costs['Fixed_costs_electricity_peak'] + + global_operational_costs['Fixed_costs_electricity_monthly'] + + global_operational_costs['Fixed_costs_electricity_peak'] + + global_operational_costs['Fixed_costs_electricity_monthly'] + + global_operational_costs['Variable_costs_electricity'] + + global_operational_costs['Fixed_costs_gas'] + + global_operational_costs['Variable_costs_gas'] + ) + df_maintenance_costs = ( + global_maintenance_costs['Heating_maintenance'] + + global_maintenance_costs['Cooling_maintenance'] + + global_maintenance_costs['PV_maintenance'] + ) df_operational_incomes = global_operational_incomes['Incomes electricity'] life_cycle_costs_capital_skin = _npv_from_list(discount_rate, df_capital_costs_skin.values.tolist()) @@ -167,12 +169,17 @@ for retrofitting_scenario in retrofitting_scenarios: life_cycle_maintenance_costs = _npv_from_list(discount_rate, df_maintenance_costs.values.tolist()) life_cycle_operational_incomes = _npv_from_list(discount_rate, df_operational_incomes.values.tolist()) - life_cycle_costs = life_cycle_costs_capital_skin + life_cycle_costs_capital_systems + \ - life_cycle_costs_end_of_life_costs + life_cycle_operational_costs + \ - life_cycle_maintenance_costs - life_cycle_operational_incomes + life_cycle_costs = ( + life_cycle_costs_capital_skin + + life_cycle_costs_capital_systems + + life_cycle_costs_end_of_life_costs + + life_cycle_operational_costs + + life_cycle_maintenance_costs - + life_cycle_operational_incomes + ) life_cycle_results[f'Scenario {retrofitting_scenario}'] = [life_cycle_costs_capital_skin, - life_cycle_costs_capital_systems , + life_cycle_costs_capital_systems, life_cycle_costs_end_of_life_costs, life_cycle_operational_costs, life_cycle_maintenance_costs, @@ -185,6 +192,3 @@ for retrofitting_scenario in retrofitting_scenarios: print(life_cycle_results) print(f'Scenario {retrofitting_scenario} {life_cycle_costs}') - - - diff --git a/life_cycle_costs.py b/life_cycle_costs.py index a13504f..e859e74 100644 --- a/life_cycle_costs.py +++ b/life_cycle_costs.py @@ -2,18 +2,23 @@ LifeCycleCosts calculates the life cycle costs of one building SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2022 Project Author Pilar Monsalvete Alvarez de Uribarri pilar_monsalvete@concordia.ca -Project contributor © 2023 Author Oriol Gavaldà Torrellas oriol.gavalda@concordia.ca +Code contributor Oriol Gavalda Torrellas oriol.gavalda@concordia.ca """ import math + import pandas as pd -import numpy as np -from datetime import date import hub.helpers.constants as cte class LifeCycleCosts: + + CURRENT_STATUS = 0 + SKIN_RETROFIT = 1 + SYSTEM_RETROFIT_AND_PV = 2 + SKIN_RETROFIT_AND_SYSTEM_RETROFIT_AND_PV = 3 + def __init__(self, building, archetype, number_of_years, consumer_price_index, electricity_peak_index, electricity_price_index, gas_price_index, discount_rate, retrofitting_scenario, fuel_type): @@ -80,61 +85,62 @@ class LifeCycleCosts: surface_transparent += thermal_boundary.opaque_area * thermal_boundary.window_ratio chapters = archetype.capital_cost - capital_cost_skin = 0 - capital_cost_services = 0 - capital_cost_pv = 0 peak_heating = building.heating_peak_load[cte.YEAR].values[0] peak_cooling = building.cooling_peak_load[cte.YEAR].values[0] - # todo: put the value of area_pv when it exists - surface_pv = 10 # building.area_pv - self._yearly_capital_costs.loc[0, 'B2010_opaque_walls'], self._yearly_capital_costs.loc[0]['B2020_transparent'], \ - self._yearly_capital_costs.loc[0, 'B3010_opaque_roof'], self._yearly_capital_costs.loc[0]['B10_superstructure'], \ - self._yearly_capital_costs.loc[0, 'B_Shell'] \ - = [0, 0, 0, 0, 0] - self._yearly_capital_costs.loc[0, 'D3020_heat_generating_systems'], \ - self._yearly_capital_costs.loc[0, 'D3030_cooling_generation_systems'], \ - self._yearly_capital_costs.loc[0, 'D3040_distribution_systems'], \ - self._yearly_capital_costs.loc[0, 'D3080_other_hvac_ahu'], \ - self._yearly_capital_costs.loc[0, 'D5020_lighting_and_branch_wiring'] \ - = [0, 0, 0, 0, 0] + # todo: change area pv when the variable exists + roof_area = 0 + for roof in building.roofs: + roof_area += roof.solid_polygon.area + surface_pv = roof_area * 0.5 + + self._yearly_capital_costs.loc[0, 'B2010_opaque_walls'] = 0 + self._yearly_capital_costs.loc[0]['B2020_transparent'] = 0 + self._yearly_capital_costs.loc[0, 'B3010_opaque_roof'] = 0 + self._yearly_capital_costs.loc[0]['B10_superstructure'] = 0 + self._yearly_capital_costs.loc[0, 'B_Shell'] = 0 + + self._yearly_capital_costs.loc[0, 'D3020_heat_generating_systems'] = 0 + self._yearly_capital_costs.loc[0, 'D3030_cooling_generation_systems'] = 0 + self._yearly_capital_costs.loc[0, 'D3040_distribution_systems'] = 0 + self._yearly_capital_costs.loc[0, 'D3080_other_hvac_ahu'] = 0 + self._yearly_capital_costs.loc[0, 'D5020_lighting_and_branch_wiring'] = 0 + self._yearly_capital_costs.fillna(0, inplace=True) - if self._retrofitting_scenario == 1 or self._retrofitting_scenario == 3: + if self._retrofitting_scenario in (self.SKIN_RETROFIT, self.SKIN_RETROFIT_AND_SYSTEM_RETROFIT_AND_PV): chapter = chapters.chapter('B_shell') capital_cost_opaque = surface_opaque * chapter.item('B2010_opaque_walls').refurbishment[0] capital_cost_transparent = surface_transparent * chapter.item('B2020_transparent').refurbishment[0] capital_cost_roof = surface_roof * chapter.item('B3010_opaque_roof').refurbishment[0] capital_cost_ground = surface_ground * chapter.item('B10_superstructure').refurbishment[0] capital_cost_skin = capital_cost_opaque + capital_cost_transparent + capital_cost_roof + capital_cost_ground - self._yearly_capital_costs.loc[0, 'B2010_opaque_walls'], self._yearly_capital_costs.loc[0]['B2020_transparent'], \ - self._yearly_capital_costs.loc[0, 'B3010_opaque_roof'], self._yearly_capital_costs.loc[0]['B10_superstructure'], \ - self._yearly_capital_costs.loc[0, 'B_Shell'] \ - = [capital_cost_opaque, capital_cost_transparent, capital_cost_roof, capital_cost_ground, capital_cost_skin] + self._yearly_capital_costs.loc[0, 'B2010_opaque_walls'] = capital_cost_opaque + self._yearly_capital_costs.loc[0]['B2020_transparent'] = capital_cost_transparent + self._yearly_capital_costs.loc[0, 'B3010_opaque_roof'] = capital_cost_roof + self._yearly_capital_costs.loc[0]['B10_superstructure'] = capital_cost_ground + self._yearly_capital_costs.loc[0, 'B_Shell'] = capital_cost_skin - if self._retrofitting_scenario == 2 or self._retrofitting_scenario == 3: + if self._retrofitting_scenario in (self.SYSTEM_RETROFIT_AND_PV , self.SKIN_RETROFIT_AND_SYSTEM_RETROFIT_AND_PV): chapter = chapters.chapter('D_services') - capital_cost_pv = surface_pv * chapter.item('D301010_photovoltaic_system').initial_investment[0] - self._yearly_capital_costs.loc[0]['D301010_photovoltaic_system'] = capital_cost_pv - - capital_cost_heating_equipment = peak_heating \ - * chapter.item('D3020_heat_generating_systems').initial_investment[0] - capital_cost_cooling_equipment = peak_cooling \ - * chapter.item('D3030_cooling_generation_systems').initial_investment[0] - capital_cost_distribution_equipment = peak_cooling \ - * chapter.item('D3040_distribution_systems').initial_investment[0] + capital_cost_heating_equipment = ( + peak_heating * chapter.item('D3020_heat_generating_systems').initial_investment[0] + ) + capital_cost_cooling_equipment = ( + peak_cooling * chapter.item('D3030_cooling_generation_systems').initial_investment[0] + ) + capital_cost_distribution_equipment = ( + peak_cooling * chapter.item('D3040_distribution_systems').initial_investment[0] + ) capital_cost_other_hvac_ahu = peak_cooling * chapter.item('D3080_other_hvac_ahu').initial_investment[0] - capital_cost_lighting = total_floor_area * chapter.item('D5020_lighting_and_branch_wiring').initial_investment[0] + self._yearly_capital_costs.loc[0, 'D3020_heat_generating_systems'], = capital_cost_heating_equipment + self._yearly_capital_costs.loc[0, 'D3030_cooling_generation_systems'] = capital_cost_cooling_equipment + self._yearly_capital_costs.loc[0, 'D3040_distribution_systems'] = capital_cost_distribution_equipment + self._yearly_capital_costs.loc[0, 'D3080_other_hvac_ahu'] = capital_cost_other_hvac_ahu + self._yearly_capital_costs.loc[0, 'D5020_lighting_and_branch_wiring'] = capital_cost_lighting - self._yearly_capital_costs.loc[0, 'D3020_heat_generating_systems'], \ - self._yearly_capital_costs.loc[0, 'D3030_cooling_generation_systems'], \ - self._yearly_capital_costs.loc[0, 'D3040_distribution_systems'], \ - self._yearly_capital_costs.loc[0, 'D3080_other_hvac_ahu'], \ - self._yearly_capital_costs.loc[0, 'D5020_lighting_and_branch_wiring'] \ - = [capital_cost_heating_equipment, capital_cost_cooling_equipment, capital_cost_distribution_equipment, - capital_cost_other_hvac_ahu, capital_cost_lighting] for year in range(1, self._number_of_years): chapter = chapters.chapter('D_services') costs_increase = math.pow(1 + self._consumer_price_index, year) / math.pow(1 + self._discount_rate, year) @@ -269,7 +275,7 @@ class LifeCycleCosts: maintenance_heating_0 = peak_heating * archetype.operational_cost.maintenance_heating maintenance_cooling_0 = peak_cooling * archetype.operational_cost.maintenance_cooling maintenance_pv_0 = surface_pv * archetype.operational_cost.maintenance_pv - + for year in range(1, self._number_of_years + 1): costs_increase = math.pow(1 + self._consumer_price_index, year) self._yearly_maintenance_costs.loc[year, 'Heating_maintenance'] = maintenance_heating_0 * \