diff --git a/__init__.py b/__init__.py deleted file mode 100644 index 674ec5e..0000000 --- a/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -""" -Cost workflow initialization -""" - - diff --git a/README.md b/costs/README.md similarity index 100% rename from README.md rename to costs/README.md diff --git a/costs/__init__.py b/costs/__init__.py new file mode 100644 index 0000000..3f34dac --- /dev/null +++ b/costs/__init__.py @@ -0,0 +1,40 @@ +""" +Cost workflow initialization +""" +import glob +import os +from pathlib import Path + +CLIMATE_REFERENCE_CITY = 'Montreal' +WEATHER_FILE = 'CAN_PQ_Montreal.Intl.AP.716270_CWEC.epw' +WEATHER_FORMAT = 'epw' +CONSTRUCTION_FORMAT = 'nrcan' +USAGE_FORMAT = 'comnet' +ENERGY_SYSTEM_FORMAT = 'montreal_custom' +ATTIC_HEATED_CASE = 0 +BASEMENT_HEATED_CASE = 1 +CURRENT_STATUS = 0 +SKIN_RETROFIT = 1 +SYSTEM_RETROFIT_AND_PV = 2 +SKIN_RETROFIT_AND_SYSTEM_RETROFIT_AND_PV = 3 +NUMBER_OF_YEARS = 31 +CONSUMER_PRICE_INDEX = 0.04 +ELECTRICITY_PEAK_INDEX = 0.05 +ELECTRICITY_PRICE_INDEX = 0.05 +GAS_PRICE_INDEX = 0.05 +DISCOUNT_RATE = 0.03 +RETROFITTING_YEAR_CONSTRUCTION = 2020 +RETROFITTING_SCENARIOS = [ + CURRENT_STATUS, + SKIN_RETROFIT, + SYSTEM_RETROFIT_AND_PV, + SKIN_RETROFIT_AND_SYSTEM_RETROFIT_AND_PV +] +file_path = Path('./costs/data/selected_building_2864.geojson').resolve() +tmp_folder = Path('./costs/tmp').resolve() +out_path = Path('./costs/outputs').resolve() +files = glob.glob(f'{out_path}/*') +print('path', file_path) +for file in files: + if file != '.gitignore': + os.remove(file) diff --git a/__main__.py b/costs/__main__.py similarity index 69% rename from __main__.py rename to costs/__main__.py index d106f6c..4621339 100644 --- a/__main__.py +++ b/costs/__main__.py @@ -5,8 +5,6 @@ Copyright © 2022 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.mons Code contributor Oriol Gavalda Torrellas oriol.gavalda@concordia.ca """ -import glob -import os from pathlib import Path import numpy_financial as npf @@ -25,6 +23,15 @@ from sra_engine import SraEngine from life_cycle_costs import LifeCycleCosts +# import constants +from costs import CLIMATE_REFERENCE_CITY, WEATHER_FILE, WEATHER_FORMAT, CONSTRUCTION_FORMAT, USAGE_FORMAT +from costs import ENERGY_SYSTEM_FORMAT, ATTIC_HEATED_CASE, BASEMENT_HEATED_CASE, RETROFITTING_SCENARIOS, NUMBER_OF_YEARS +from costs import CONSUMER_PRICE_INDEX, ELECTRICITY_PEAK_INDEX, ELECTRICITY_PRICE_INDEX, GAS_PRICE_INDEX, DISCOUNT_RATE +from costs import SKIN_RETROFIT, SYSTEM_RETROFIT_AND_PV, SKIN_RETROFIT_AND_SYSTEM_RETROFIT_AND_PV + +# import paths +from costs import file_path, tmp_folder, out_path + def _npv_from_list(npv_discount_rate, list_cashflow): lcc_value = npf.npv(npv_discount_rate, list_cashflow) @@ -39,32 +46,6 @@ def _search_archetype(costs_catalog, building_function): raise KeyError('archetype not found') -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' -construction_format = 'nrcan' -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') -out_path = (Path(__file__).parent.parent / 'costs_workflow' / 'out_files') -files = glob.glob(f'{out_path}/*') - -for file in files: - if file != '.gitignore': - os.remove(file) - -number_of_years = 31 -consumer_price_index = 0.04 -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_scenarios = [0, 1, 2, 3] life_cycle_results = pd.DataFrame() print('[city creation start]') city = GeometryFactory('geojson', @@ -73,47 +54,46 @@ city = GeometryFactory('geojson', year_of_construction_field='ANNEE_CONS', function_field='CODE_UTILI', function_to_hub=Dictionaries().montreal_function_to_hub_function).city -city.climate_reference_city = climate_reference_city -city.climate_file = (tmp_folder / f'{climate_reference_city}.cli').resolve() +city.climate_reference_city = CLIMATE_REFERENCE_CITY +city.climate_file = (tmp_folder / f'{CLIMATE_REFERENCE_CITY}.cli').resolve() print(f'city created from {file_path}') -WeatherFactory(weather_format, city, file_name=weather_file).enrich() +WeatherFactory(WEATHER_FORMAT, city, file_name=WEATHER_FILE).enrich() print('enrich weather... done') -ConstructionFactory(construction_format, city).enrich() +ConstructionFactory(CONSTRUCTION_FORMAT, city).enrich() print('enrich constructions... done') -UsageFactory(usage_format, city).enrich() +UsageFactory(USAGE_FORMAT, city).enrich() print('enrich usage... done') for building in city.buildings: building.energy_systems_archetype_name = 'system 1 gas' -EnergySystemsFactory(energy_systems_format, city).enrich() +EnergySystemsFactory(ENERGY_SYSTEM_FORMAT, city).enrich() print('enrich systems... done') print('exporting:') catalog = CostCatalogFactory('montreal_custom').catalog print('costs catalog access... done') sra_file = (tmp_folder / f'{city.name}_sra.xml').resolve() -SraEngine(city, sra_file, tmp_folder, weather_file) +SraEngine(city, sra_file, tmp_folder, WEATHER_FILE) print(' sra processed...') for building in city.buildings: - building.attic_heated = attic_heated_case - building.basement_heated = basement_heated_case + building.attic_heated = ATTIC_HEATED_CASE + building.basement_heated = BASEMENT_HEATED_CASE -for retrofitting_scenario in retrofitting_scenarios: +for retrofitting_scenario in RETROFITTING_SCENARIOS: - if retrofitting_scenario == 1 or retrofitting_scenario == 3: + if retrofitting_scenario in (SKIN_RETROFIT, SYSTEM_RETROFIT_AND_PV): for building in city.buildings: building.year_of_construction = retrofitting_year_of_construction - ConstructionFactory(construction_format, city).enrich() + ConstructionFactory(CONSTRUCTION_FORMAT, city).enrich() print('enrich retrofitted constructions... done') - if retrofitting_scenario == 2 or retrofitting_scenario == 3: + if retrofitting_scenario in (SYSTEM_RETROFIT_AND_PV, SKIN_RETROFIT_AND_SYSTEM_RETROFIT_AND_PV): for building in city.buildings: building.energy_systems_archetype_name = 'system 6 electricity pv' - EnergySystemsFactory(energy_systems_format, city).enrich() + EnergySystemsFactory(ENERGY_SYSTEM_FORMAT, city).enrich() print('enrich systems... done') MonthlyEnergyBalanceEngine(city, tmp_folder) - lightingdemand = city.buildings[0].lighting_electrical_demand[cte.YEAR]['insel meb'] / 1000 - print(f'lighting first {lightingdemand}') + EnergySystemsSizing(city).enrich() print(f'beginning costing scenario {retrofitting_scenario} systems... done') @@ -124,13 +104,12 @@ for retrofitting_scenario in retrofitting_scenarios: archetype = _search_archetype(catalog, function) print('lcc for first building started') if "gas" in building.energy_systems_archetype_name: - fuel_type = 1 + FUEL_TYPE = 1 else: - fuel_type = 0 + 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) + 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) global_capital_costs, global_capital_incomes = lcc.calculate_capital_costs() global_end_of_life_costs = lcc.calculate_end_of_life_costs() global_operational_costs = lcc.calculate_total_operational_costs() @@ -176,13 +155,13 @@ for retrofitting_scenario in retrofitting_scenarios: df_capital_incomes = global_capital_incomes['Subsidies construction'] + global_capital_incomes['Subsidies HVAC'] + \ global_capital_incomes['Subsidies PV'] - life_cycle_costs_capital_skin = _npv_from_list(discount_rate, df_capital_costs_skin.values.tolist()) - life_cycle_costs_capital_systems = _npv_from_list(discount_rate, df_capital_costs_systems.values.tolist()) - life_cycle_costs_end_of_life_costs = _npv_from_list(discount_rate, df_end_of_life_costs.values.tolist()) - life_cycle_operational_costs = _npv_from_list(discount_rate, df_operational_costs.values.tolist()) - 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_capital_incomes = _npv_from_list(discount_rate, df_capital_incomes.values.tolist()) + life_cycle_costs_capital_skin = _npv_from_list(DISCOUNT_RATE, df_capital_costs_skin.values.tolist()) + life_cycle_costs_capital_systems = _npv_from_list(DISCOUNT_RATE, df_capital_costs_systems.values.tolist()) + life_cycle_costs_end_of_life_costs = _npv_from_list(DISCOUNT_RATE, df_end_of_life_costs.values.tolist()) + life_cycle_operational_costs = _npv_from_list(DISCOUNT_RATE, df_operational_costs.values.tolist()) + 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_capital_incomes = _npv_from_list(DISCOUNT_RATE, df_capital_incomes.values.tolist()) life_cycle_costs = ( life_cycle_costs_capital_skin + @@ -202,10 +181,10 @@ for retrofitting_scenario in retrofitting_scenarios: life_cycle_operational_incomes, life_cycle_capital_incomes] - life_cycle_results.index = [f'total_capital_costs_skin', - f'total_capital_costs_systems', f'end_of_life_costs', - f'total_operational_costs', f'total_maintenance_costs', - f'operational_incomes', f'capital_incomes'] + life_cycle_results.index = ['total_capital_costs_skin', + f'total_capital_costs_systems','end_of_life_costs', + 'total_operational_costs', 'total_maintenance_costs', + 'operational_incomes', 'capital_incomes'] print(life_cycle_results) print(f'Scenario {retrofitting_scenario} {life_cycle_costs}') diff --git a/costs/data/.gitignore b/costs/data/.gitignore new file mode 100644 index 0000000..86d0cb2 --- /dev/null +++ b/costs/data/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/life_cycle_costs.py b/costs/life_cycle_costs.py similarity index 94% rename from life_cycle_costs.py rename to costs/life_cycle_costs.py index 3d30bf4..c555334 100644 --- a/life_cycle_costs.py +++ b/costs/life_cycle_costs.py @@ -1,5 +1,5 @@ """ -LifeCycleCosts calculates the life cycle costs of one building +LifeCycleCosts module 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 Code contributor Oriol Gavalda Torrellas oriol.gavalda@concordia.ca @@ -10,14 +10,13 @@ import math import pandas as pd import hub.helpers.constants as cte +from costs import SKIN_RETROFIT, SYSTEM_RETROFIT_AND_PV, SKIN_RETROFIT_AND_SYSTEM_RETROFIT_AND_PV class LifeCycleCosts: - - CURRENT_STATUS = 0 - SKIN_RETROFIT = 1 - SYSTEM_RETROFIT_AND_PV = 2 - SKIN_RETROFIT_AND_SYSTEM_RETROFIT_AND_PV = 3 + """ + Life cycle cost class + """ def __init__(self, building, archetype, number_of_years, consumer_price_index, electricity_peak_index, electricity_price_index, gas_price_index, discount_rate, @@ -66,6 +65,10 @@ class LifeCycleCosts: self._yearly_capital_incomes = pd.DataFrame(index=rng, columns=['Subsidies construction', 'Subsidies HVAC', 'Subsidies PV'], dtype='float') def calculate_capital_costs(self): + """ + Calculate capital cost + :return: pd.DataFrame + """ building = self._building archetype = self._archetype @@ -123,7 +126,7 @@ class LifeCycleCosts: self._yearly_capital_incomes.loc[0, 'Subsidies PV'] = 0 self._yearly_capital_costs.fillna(0, inplace=True) - if self._retrofitting_scenario in (self.SKIN_RETROFIT, self.SKIN_RETROFIT_AND_SYSTEM_RETROFIT_AND_PV): + if self._retrofitting_scenario in (SKIN_RETROFIT, 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] @@ -137,7 +140,7 @@ class LifeCycleCosts: self._yearly_capital_costs.loc[0]['B10_superstructure'] = capital_cost_ground - if self._retrofitting_scenario in (self.SYSTEM_RETROFIT_AND_PV, self.SKIN_RETROFIT_AND_SYSTEM_RETROFIT_AND_PV): + if self._retrofitting_scenario in (SYSTEM_RETROFIT_AND_PV, 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 @@ -183,7 +186,7 @@ class LifeCycleCosts: * costs_increase self._yearly_capital_costs.loc[year, 'D5020_lighting_and_branch_wiring'] = reposition_cost_lighting - if self._retrofitting_scenario == 2 or self._retrofitting_scenario == 3: + if self._retrofitting_scenario in (SYSTEM_RETROFIT_AND_PV, SKIN_RETROFIT_AND_SYSTEM_RETROFIT_AND_PV): if (year % chapter.item('D301010_photovoltaic_system').lifetime) == 0: self._yearly_capital_costs.loc[year]['D301010_photovoltaic_system'] = surface_pv \ * chapter.item( @@ -200,6 +203,10 @@ class LifeCycleCosts: return self._yearly_capital_costs, self._yearly_capital_incomes def calculate_end_of_life_costs(self): + """ + Calculate end of life costs + :return: pd.DataFrame + """ archetype = self._archetype total_floor_area = self._total_floor_area @@ -213,6 +220,10 @@ class LifeCycleCosts: return self._yearly_end_of_life_costs def calculate_total_operational_costs(self): + """ + Calculate total operational costs + :return: pd.DataFrame + """ building = self._building archetype = self._archetype total_floor_area = self._total_floor_area @@ -273,6 +284,10 @@ class LifeCycleCosts: return self._yearly_operational_costs def calculate_total_operational_incomes(self): + """ + Calculate total operational incomes + :return: pd.DataFrame + """ building = self._building archetype = self._archetype if cte.YEAR not in building.onsite_electrical_production: @@ -294,6 +309,10 @@ class LifeCycleCosts: return self._yearly_operational_incomes def calculate_total_maintenance_costs(self): + """ + Calculate total maintenance costs + :return: pd.DataFrame + """ building = self._building archetype = self._archetype # todo: change area pv when the variable exists diff --git a/costs/outputs/.gitignore b/costs/outputs/.gitignore new file mode 100644 index 0000000..86d0cb2 --- /dev/null +++ b/costs/outputs/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/costs/tmp/.gitignore b/costs/tmp/.gitignore new file mode 100644 index 0000000..86d0cb2 --- /dev/null +++ b/costs/tmp/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/input_files/.gitignore b/input_files/.gitignore deleted file mode 100644 index 5465b05..0000000 --- a/input_files/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/input_files/