From d0d596e2080390d01c10b9129a1f3e3eb772e0ef Mon Sep 17 00:00:00 2001 From: g_gutierrez Date: Thu, 1 Jun 2023 17:11:05 -0400 Subject: [PATCH] First commit --- README.md | 3 + costs/__init__.py | 25 +++ costs/configuration.py | 202 ++++++++++++++++++++++++ costs/cost.py | 161 +++++++++++++++++++ costs/life_cycle_costs.py | 315 ++++++++++++++++++++++++++++++++++++++ resources.txt | 2 + tests/test_costs.py | 9 ++ 7 files changed, 717 insertions(+) create mode 100644 README.md create mode 100644 costs/__init__.py create mode 100644 costs/configuration.py create mode 100644 costs/cost.py create mode 100644 costs/life_cycle_costs.py create mode 100644 resources.txt create mode 100644 tests/test_costs.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..d173c7e --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# costs + +Object-oriented generalization for the cost workflow diff --git a/costs/__init__.py b/costs/__init__.py new file mode 100644 index 0000000..7ec7382 --- /dev/null +++ b/costs/__init__.py @@ -0,0 +1,25 @@ +""" +Cost workflow initialization +""" +import glob +import os +from pathlib import Path + + +# constants +CURRENT_STATUS = 0 +SKIN_RETROFIT = 1 +SYSTEM_RETROFIT_AND_PV = 2 +SKIN_RETROFIT_AND_SYSTEM_RETROFIT_AND_PV = 3 +RETROFITTING_SCENARIOS = [ + CURRENT_STATUS, + SKIN_RETROFIT, + SYSTEM_RETROFIT_AND_PV, + SKIN_RETROFIT_AND_SYSTEM_RETROFIT_AND_PV +] +tmp_folder = Path('./tmp').resolve() +out_path = Path('./outputs').resolve() +files = glob.glob(f'{out_path}/*') +for file in files: + if file != '.gitignore': + os.remove(file) diff --git a/costs/configuration.py b/costs/configuration.py new file mode 100644 index 0000000..2d174b9 --- /dev/null +++ b/costs/configuration.py @@ -0,0 +1,202 @@ +""" +Configuration module +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2022 Project Author Guille Gutierrez Guillermo.GutierrezMorote@concordia.ca +Code contributor Pilar Monsalvete Alvarez de Uribarri pilar_monsalvete@concordia.ca +Code contributor Oriol Gavalda Torrellas oriol.gavalda@concordia.ca +""" +from hub.catalog_factories.costs_catalog_factory import CostCatalogFactory +from hub.catalog_factories.catalog import Catalog + + +class Configuration: + """ + Configuration class + """ + + def __init__(self, + number_of_years, + percentage_credit, + interest_rate, + credit_years, + consumer_price_index, + electricity_peak_index, + electricity_price_index, + gas_price_index, + discount_rate, + retrofitting_year_construction, + factories_handler + ): + self._number_of_years = number_of_years + self._percentage_credit = percentage_credit + self._interest_rate = interest_rate + self._credit_years = credit_years + self._consumer_price_index = consumer_price_index + self._electricity_peak_index = electricity_peak_index + self._electricity_price_index = electricity_price_index + self._gas_price_index = gas_price_index + self._discount_rate = discount_rate + self._retrofitting_year_construction = retrofitting_year_construction + self._factories_handler = factories_handler + self._cost_catalog = CostCatalogFactory(factories_handler).catalog + + @property + def number_of_years(self): + """ + Get number of years + """ + return self._number_of_years + + @number_of_years.setter + def number_of_years(self, value): + """ + Set number of years + """ + self._number_of_years = value + + @property + def percentage_credit(self): + """ + Get percentage credit + """ + return self._percentage_credit + + @percentage_credit.setter + def percentage_credit(self, value): + """ + Set percentage credit + """ + self._percentage_credit = value + + @property + def interest_rate(self): + """ + Get interest rate + """ + return self._interest_rate + + @interest_rate.setter + def interest_rate(self, value): + """ + Set interest rate + """ + self._interest_rate = value + + @property + def credit_years(self): + """ + Get credit years + """ + return self._credit_years + + @credit_years.setter + def credit_years(self, value): + """ + Set credit years + """ + self._credit_years = value + + @property + def consumer_price_index(self): + """ + Get consumer price index + """ + return self._consumer_price_index + + @consumer_price_index.setter + def consumer_price_index(self, value): + """ + Set consumer price index + """ + self._consumer_price_index = value + + @property + def electricity_peak_index(self): + """ + Get electricity peak index + """ + return self._electricity_peak_index + + @electricity_peak_index.setter + def electricity_peak_index(self, value): + """ + Set electricity peak index + """ + self._electricity_peak_index = value + + @property + def electricity_price_index(self): + """ + Get electricity price index + """ + return self._electricity_price_index + + @electricity_price_index.setter + def electricity_price_index(self, value): + """ + Set electricity price index + """ + self._electricity_price_index = value + + @property + def gas_price_index(self): + """ + Get gas price index + """ + return self._gas_price_index + + @gas_price_index.setter + def gas_price_index(self, value): + """ + Set gas price index + """ + self._gas_price_index = value + + @property + def discount_rate(self): + """ + Get discount rate + """ + return self._discount_rate + + @discount_rate.setter + def discount_rate(self, value): + """ + Set discount rate + """ + self._discount_rate = value + + @property + def retrofitting_year_construction(self): + """ + Get retrofitting year construction + """ + return self._retrofitting_year_construction + + @retrofitting_year_construction.setter + def retrofitting_year_construction(self, value): + """ + Set retrofitting year construction + """ + self._retrofitting_year_construction = value + + @property + def factories_handler(self): + """ + Get factories handler + """ + return self._factories_handler + + @factories_handler.setter + def factories_handler(self, value): + """ + Set factories handler + """ + self._factories_handler = value + + @property + def cost_catalog(self) -> Catalog: + """ + Get cost catalog + """ + return self._cost_catalog diff --git a/costs/cost.py b/costs/cost.py new file mode 100644 index 0000000..29ae4cc --- /dev/null +++ b/costs/cost.py @@ -0,0 +1,161 @@ +""" +Cost module +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2022 Project Author Guille Gutierrez Guillermo.GutierrezMorote@concordia.ca +Code contributor Pilar Monsalvete Alvarez de Uribarri pilar_monsalvete@concordia.ca +Code contributor Oriol Gavalda Torrellas oriol.gavalda@concordia.ca +""" + +from pathlib import Path + +import numpy_financial as npf +import pandas as pd +from hub.persistence.models.city_object import CityObject + +from configuration import Configuration +from life_cycle_costs import LifeCycleCosts + + +class Cost: + """ + Cost class + """ + + def __init__(self, + buildings: [CityObject], + buildings_results: dict, + number_of_years=31, + percentage_credit=0, + interest_rate=0.04, + credit_years=15, + 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, + factories_handler='montreal_custom'): + self._buildings = buildings + self._buildings_results = buildings_results + self._configuration = Configuration(number_of_years, + percentage_credit, + interest_rate, credit_years, + consumer_price_index, + electricity_peak_index, + electricity_price_index, + gas_price_index, + discount_rate, + retrofitting_year_construction, + factories_handler) + self._global_capital_costs = None + self._global_capital_incomes = None + self._global_end_of_life_costs = None + self._global_operational_costs = None + self._global_maintenance_costs = None + self._global_operational_incomes = None + + def _life_cycle_costs(self, building: CityObject, building_results: dict): + lcc = LifeCycleCosts(building, building_results, self._configuration) + self._global_capital_costs, self._global_capital_incomes = lcc.calculate_capital_costs + self._global_end_of_life_costs = lcc.calculate_end_of_life_costs + self._global_operational_costs = lcc.calculate_total_operational_costs + self._global_maintenance_costs = lcc.calculate_total_maintenance_costs + self._global_operational_incomes = lcc.calculate_total_operational_incomes + + @property + def life_cycle(self) -> pd.DataFrame: + """ + Get complete life cycle costs + :return: DataFrame + """ + results = pd.DataFrame() + for building_index, building in enumerate(self._buildings): + self._life_cycle_costs(building, self._buildings_results[building_index]) + df_capital_costs_skin = ( + self._global_capital_costs['B2010_opaque_walls'] + + self._global_capital_costs['B2020_transparent'] + + self._global_capital_costs['B3010_opaque_roof'] + + self._global_capital_costs['B10_superstructure'] + ) + df_capital_costs_systems = ( + self._global_capital_costs['D3020_heat_generating_systems'] + + self._global_capital_costs['D3030_cooling_generation_systems'] + + self._global_capital_costs['D3080_other_hvac_ahu'] + + self._global_capital_costs['D5020_lighting_and_branch_wiring'] + + self._global_capital_costs['D301010_photovoltaic_system'] + ) + df_end_of_life_costs = self._global_end_of_life_costs['End_of_life_costs'] + df_operational_costs = ( + self._global_operational_costs['Fixed_costs_electricity_peak'] + + self._global_operational_costs['Fixed_costs_electricity_monthly'] + + self._global_operational_costs['Fixed_costs_electricity_peak'] + + self._global_operational_costs['Fixed_costs_electricity_monthly'] + + self._global_operational_costs['Variable_costs_electricity'] + + self._global_operational_costs['Fixed_costs_gas'] + + self._global_operational_costs['Variable_costs_gas'] + ) + df_maintenance_costs = ( + self._global_maintenance_costs['Heating_maintenance'] + + self._global_maintenance_costs['Cooling_maintenance'] + + self._global_maintenance_costs['PV_maintenance'] + ) + df_operational_incomes = self._global_operational_incomes['Incomes electricity'] + df_capital_incomes = ( + self._global_capital_incomes['Subsidies construction'] + + self._global_capital_incomes['Subsidies HVAC'] + + self._global_capital_incomes['Subsidies PV'] + ) + life_cycle_costs_capital_skin = npf.npv( + self._configuration.discount_rate, df_capital_costs_skin.values.tolist() + ) + life_cycle_costs_capital_systems = npf.npv( + self._configuration.discount_rate, df_capital_costs_systems.values.tolist() + ) + life_cycle_costs_end_of_life_costs = npf.npv( + self._configuration.discount_rate, df_end_of_life_costs.values.tolist() + ) + life_cycle_operational_costs = npf.npv( + self._configuration.discount_rate, df_operational_costs.values.tolist() + ) + life_cycle_maintenance_costs = npf.npv( + self._configuration.discount_rate, df_maintenance_costs.values.tolist() + ) + life_cycle_operational_incomes = npf.npv( + self._configuration.discount_rate, df_operational_incomes.values.tolist() + ) + life_cycle_capital_incomes = npf.npv( + self._configuration.discount_rate, df_capital_incomes.values.tolist() + ) + + results[f'{building.name}_{building.city_id}'] = [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_capital_incomes] + + results.index = ['total_capital_costs_skin', + 'total_capital_costs_systems', + 'end_of_life_costs', + 'total_operational_costs', + 'total_maintenance_costs', + 'operational_incomes', + 'capital_incomes'] + return results + + def to_xlsx(self, path: Path): + """ + Export life cycle costs to xls file + :return: none + """ + for building_index, building in enumerate(self._buildings): + _path = (path / f'{building.name}_{building.city_id}').resolve() + self._life_cycle_costs(building, self._buildings_results[building_index]) + with pd.ExcelWriter(path) as writer: + self._global_capital_costs.to_excel(writer, sheet_name='global_capital_costs') + self._global_end_of_life_costs.to_excel(writer, sheet_name='global_end_of_life_costs') + self._global_operational_costs.to_excel(writer, sheet_name='global_operational_costs') + self._global_maintenance_costs.to_excel(writer, sheet_name='global_maintenance_costs') + self._global_operational_incomes.to_excel(writer, sheet_name='global_operational_incomes') + self._global_capital_incomes.to_excel(writer, sheet_name='global_capital_incomes') diff --git a/costs/life_cycle_costs.py b/costs/life_cycle_costs.py new file mode 100644 index 0000000..6f216ef --- /dev/null +++ b/costs/life_cycle_costs.py @@ -0,0 +1,315 @@ +""" +Life cycle cost module +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2022 Project Author Guille Gutierrez Guillermo.GutierrezMorote@concordia.ca +Code contributor Pilar Monsalvete Alvarez de Uribarri pilar_monsalvete@concordia.ca +Code contributor Oriol Gavalda Torrellas oriol.gavalda@concordia.ca +""" + +from hub.persistence.models.city_object import CityObject +from configuration import Configuration + + +class LifeCycleCosts: + """ + Life cycle costs class + """ + def __init__(self, building: CityObject, building_results: dict, configuration: Configuration): + self._building = building + self._building_results = building_results + self._configuration = configuration + self._archetype = None + for archetype in self._configuration.cost_catalog.entries('archetypes').archetype: + if str(building.function) == str(archetype.function): + self._archetype = archetype + break + if not self._archetype: + raise KeyError('archetype not found') + + @property + def calculate_capital_costs(self): + """ + Calculate capital cost + :return: pd.DataFrame + """ + capital_cost_pv = 0 + capital_cost_opaque = 0 + capital_cost_ground = 0 + capital_cost_transparent = 0 + capital_cost_roof = 0 + capital_cost_heating_equipment = 0 + capital_cost_cooling_equipment = 0 + capital_cost_distribution_equipment = 0 + capital_cost_other_hvac_ahu = 0 + capital_cost_lighting = 0 + + + + chapters = archetype.capital_cost + + peak_heating = building.heating_peak_load[cte.YEAR].values[0]/1000 + peak_cooling = building.cooling_peak_load[cte.YEAR].values[0]/1000 + # 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, '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_incomes.loc[0, 'Subsidies construction'] = 0 + self._yearly_capital_incomes.loc[0, 'Subsidies HVAC'] = 0 + self._yearly_capital_incomes.loc[0, 'Subsidies PV'] = 0 + + self._yearly_capital_costs.fillna(0, inplace=True) + if self._retrofitting_scenario in (SKIN_RETROFIT, SKIN_RETROFIT_AND_SYSTEM_RETROFIT_AND_PV): + chapter = chapters.chapter('B_shell') + capital_cost_opaque = self._building.wall_area * chapter.item('B2010_opaque_walls').refurbishment[0] + capital_cost_transparent = self._building.windows_area * chapter.item('B2020_transparent').refurbishment[0] + capital_cost_roof = self._building.roof_area * chapter.item('B3010_opaque_roof').refurbishment[0] + capital_cost_ground = self._building.area * chapter.item('B10_superstructure').refurbishment[0] + + + self._yearly_capital_costs.loc[0, 'B2010_opaque_walls'] = capital_cost_opaque * (1-PERCENTAGE_CREDIT) + self._yearly_capital_costs.loc[0]['B2020_transparent'] = capital_cost_transparent * (1-PERCENTAGE_CREDIT) + self._yearly_capital_costs.loc[0, 'B3010_opaque_roof'] = capital_cost_roof * (1-PERCENTAGE_CREDIT) + self._yearly_capital_costs.loc[0]['B10_superstructure'] = capital_cost_ground * (1-PERCENTAGE_CREDIT) + + + 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 + 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 = self._building.total_heating_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 * (1-PERCENTAGE_CREDIT) + self._yearly_capital_costs.loc[0, 'D3030_cooling_generation_systems'] = capital_cost_cooling_equipment * (1-PERCENTAGE_CREDIT) + self._yearly_capital_costs.loc[0, 'D3040_distribution_systems'] = capital_cost_distribution_equipment * (1-PERCENTAGE_CREDIT) + self._yearly_capital_costs.loc[0, 'D3080_other_hvac_ahu'] = capital_cost_other_hvac_ahu * (1-PERCENTAGE_CREDIT) + self._yearly_capital_costs.loc[0, 'D5020_lighting_and_branch_wiring'] = capital_cost_lighting * (1-PERCENTAGE_CREDIT) + + for year in range(1, self._number_of_years): + chapter = chapters.chapter('D_services') + costs_increase = math.pow(1 + self._consumer_price_index, year) + self._yearly_capital_costs.loc[year, 'B2010_opaque_walls'] = -npf.pmt(INTEREST_RATE, CREDIT_YEARS, + capital_cost_opaque * (PERCENTAGE_CREDIT)) + self._yearly_capital_costs.loc[year, 'B2020_transparent'] = -npf.pmt(INTEREST_RATE, CREDIT_YEARS, + capital_cost_transparent * (PERCENTAGE_CREDIT) + ) + self._yearly_capital_costs.loc[year, 'B3010_opaque_roof'] = -npf.pmt(INTEREST_RATE, CREDIT_YEARS,capital_cost_roof + * (PERCENTAGE_CREDIT)) + self._yearly_capital_costs.loc[year, 'B10_superstructure'] = -npf.pmt(INTEREST_RATE, CREDIT_YEARS, + capital_cost_ground * (PERCENTAGE_CREDIT)) + self._yearly_capital_costs.loc[year, 'D3020_heat_generating_systems'] = -npf.pmt(INTEREST_RATE,CREDIT_YEARS, + capital_cost_heating_equipment + * (PERCENTAGE_CREDIT)) + self._yearly_capital_costs.loc[year, 'D3030_cooling_generation_systems'] = -npf.pmt(INTEREST_RATE, CREDIT_YEARS, + capital_cost_cooling_equipment + * (PERCENTAGE_CREDIT)) + self._yearly_capital_costs.loc[year, 'D3040_distribution_systems'] = -npf.pmt(INTEREST_RATE, CREDIT_YEARS, + capital_cost_distribution_equipment + * (PERCENTAGE_CREDIT)) + self._yearly_capital_costs.loc[year, 'D3080_other_hvac_ahu'] = -npf.pmt(INTEREST_RATE, CREDIT_YEARS, + capital_cost_other_hvac_ahu + * (PERCENTAGE_CREDIT)) + self._yearly_capital_costs.loc[year, 'D5020_lighting_and_branch_wiring'] = -npf.pmt(INTEREST_RATE, CREDIT_YEARS, + capital_cost_lighting + * (PERCENTAGE_CREDIT)) + if (year % chapter.item('D3020_heat_generating_systems').lifetime) == 0: + reposition_cost_heating_equipment = peak_heating * chapter.item('D3020_heat_generating_systems').reposition[0] \ + * costs_increase + self._yearly_capital_costs.loc[year, 'D3020_heat_generating_systems'] += reposition_cost_heating_equipment + + if (year % chapter.item('D3030_cooling_generation_systems').lifetime) == 0: + reposition_cost_cooling_equipment = peak_cooling \ + * chapter.item('D3030_cooling_generation_systems').reposition[0] \ + * costs_increase + self._yearly_capital_costs.loc[year, 'D3030_cooling_generation_systems'] += reposition_cost_cooling_equipment + + if (year % chapter.item('D3080_other_hvac_ahu').lifetime) == 0: + reposition_cost_hvac_ahu = peak_cooling * chapter.item('D3080_other_hvac_ahu').reposition[0] * costs_increase + self._yearly_capital_costs.loc[year, 'D3080_other_hvac_ahu'] = reposition_cost_hvac_ahu + + if (year % chapter.item('D5020_lighting_and_branch_wiring').lifetime) == 0: + reposition_cost_lighting = self._building.total_heating_area * chapter.item('D5020_lighting_and_branch_wiring').reposition[0] \ + * costs_increase + self._yearly_capital_costs.loc[year, 'D5020_lighting_and_branch_wiring'] += reposition_cost_lighting + + 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( + 'D301010_photovoltaic_system').reposition[0] * costs_increase + capital_cost_skin = capital_cost_opaque + capital_cost_ground + capital_cost_transparent + capital_cost_roof + capital_cost_hvac = ( + capital_cost_heating_equipment + + capital_cost_cooling_equipment + + capital_cost_distribution_equipment + + capital_cost_other_hvac_ahu + capital_cost_lighting + ) + + self._yearly_capital_incomes.loc[0, 'Subsidies construction'] = ( + capital_cost_skin * archetype.income.construction_subsidy/100 + ) + self._yearly_capital_incomes.loc[0, 'Subsidies HVAC'] = capital_cost_hvac * archetype.income.hvac_subsidy/100 + self._yearly_capital_incomes.loc[0, 'Subsidies PV'] = capital_cost_pv * archetype.income.photovoltaic_subsidy/100 + self._yearly_capital_incomes.fillna(0, inplace=True) + return self._yearly_capital_costs, self._yearly_capital_incomes + + @property + def calculate_end_of_life_costs(self): + """ + Calculate end of life costs + :return: pd.DataFrame + """ + archetype = self._archetype + + for year in range(1, self._number_of_years + 1): + price_increase = math.pow(1 + self._consumer_price_index, year) + if year == self._number_of_years: + self._yearly_end_of_life_costs.at[ + year, 'End_of_life_costs'] = self._building.total_heating_area * archetype.end_of_life_cost * price_increase + self._yearly_end_of_life_costs.fillna(0, inplace=True) + return self._yearly_end_of_life_costs + + @property + def calculate_total_operational_costs(self): + """ + Calculate total operational costs + :return: pd.DataFrame + """ + building = self._building + archetype = self._archetype + + factor_residential = self._building.total_heating_area / 80 + # todo: split the heating between fuels + fixed_gas_cost_year_0 = 0 + variable_gas_cost_year_0 = 0 + electricity_heating = 0 + domestic_hot_water_electricity = 0 + if self._fuel_type == 1: + fixed_gas_cost_year_0 = archetype.operational_cost.fuels[1].fixed_monthly * 12 * factor_residential + variable_gas_cost_year_0 = ( + (building.heating_consumption[cte.YEAR][0] + building.domestic_hot_water_consumption[cte.YEAR][0]) / 1000 * + archetype.operational_cost.fuels[1].variable[0] + ) + if self._fuel_type == 0: + electricity_heating = building.heating_consumption[cte.YEAR][0] / 1000 + domestic_hot_water_electricity = building.domestic_hot_water_consumption[cte.YEAR][0] / 1000 + + electricity_cooling = building.cooling_consumption[cte.YEAR][0] / 1000 + electricity_lighting = building.lighting_electrical_demand[cte.YEAR]['insel meb'] / 1000 + electricity_plug_loads = building.appliances_electrical_demand[cte.YEAR]['insel meb'] / 1000 + electricity_distribution = 0 + total_electricity_consumption = ( + electricity_heating + electricity_cooling + electricity_lighting + domestic_hot_water_electricity + + electricity_plug_loads + electricity_distribution + ) + + # todo: change when peak electricity demand is coded. Careful with factor residential + peak_electricity_demand = 100 # self._peak_electricity_demand + variable_electricity_cost_year_0 = total_electricity_consumption * archetype.operational_cost.fuels[0].variable[0] + peak_electricity_cost_year_0 = peak_electricity_demand * archetype.operational_cost.fuels[0].fixed_power * 12 + monthly_electricity_cost_year_0 = archetype.operational_cost.fuels[0].fixed_monthly * 12 * factor_residential + + for year in range(1, self._number_of_years + 1): + price_increase_electricity = math.pow(1 + self._electricity_price_index, year) + price_increase_peak_electricity = math.pow(1 + self._electricity_peak_index, year) + price_increase_gas = math.pow(1 + self._gas_price_index, year) + self._yearly_operational_costs.at[year, 'Fixed_costs_electricity_peak'] = ( + peak_electricity_cost_year_0 * price_increase_peak_electricity + ) + + self._yearly_operational_costs.at[year, 'Fixed_costs_electricity_monthly'] = ( + monthly_electricity_cost_year_0 * price_increase_peak_electricity + ) + self._yearly_operational_costs.at[year, 'Variable_costs_electricity'] = float( + variable_electricity_cost_year_0 * price_increase_electricity + ) + self._yearly_operational_costs.at[year, 'Fixed_costs_gas'] = fixed_gas_cost_year_0 * price_increase_gas + self._yearly_operational_costs.at[year, 'Variable_costs_gas'] = ( + variable_gas_cost_year_0 * price_increase_peak_electricity + ) + self._yearly_operational_costs.at[year, 'Variable_costs_gas'] = ( + variable_gas_cost_year_0 * price_increase_peak_electricity + ) + self._yearly_operational_costs.fillna(0, inplace=True) + + return self._yearly_operational_costs + + @property + def calculate_total_operational_incomes(self): + """ + Calculate total operational incomes + :return: pd.DataFrame + """ + building = self._building + if cte.YEAR not in building.onsite_electrical_production: + onsite_electricity_production = 0 + else: + onsite_electricity_production = building.onsite_electrical_production[cte.YEAR][0]/1000 + + for year in range(1, self._number_of_years + 1): + price_increase_electricity = math.pow(1 + self._electricity_price_index, year) + # todo: check the adequate assignation of price. Pilar + price_export = 0.075 # archetype.income.electricity_export + self._yearly_operational_incomes.loc[year, 'Incomes electricity'] = ( + onsite_electricity_production * price_export * price_increase_electricity + ) + + self._yearly_operational_incomes.fillna(0, inplace=True) + return self._yearly_operational_incomes + + @property + 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 + roof_area = 0 + for roof in building.roofs: + roof_area += roof.solid_polygon.area + surface_pv = roof_area * 0.5 + + peak_heating = building.heating_peak_load[cte.YEAR][cte.HEATING_PEAK_LOAD][0] + peak_cooling = building.cooling_peak_load[cte.YEAR][cte.COOLING_PEAK_LOAD][0] + + 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 * costs_increase + ) + self._yearly_maintenance_costs.loc[year, 'Cooling_maintenance'] = ( + maintenance_cooling_0 * costs_increase + ) + self._yearly_maintenance_costs.loc[year, 'PV_maintenance'] = ( + maintenance_pv_0 * costs_increase + ) + self._yearly_maintenance_costs.fillna(0, inplace=True) + return self._yearly_maintenance_costs diff --git a/resources.txt b/resources.txt new file mode 100644 index 0000000..bc8992f --- /dev/null +++ b/resources.txt @@ -0,0 +1,2 @@ +numpy_financial +cerc_hub \ No newline at end of file diff --git a/tests/test_costs.py b/tests/test_costs.py new file mode 100644 index 0000000..535f168 --- /dev/null +++ b/tests/test_costs.py @@ -0,0 +1,9 @@ +import unittest +from costs.cost import Cost + + +class TestCosts(unittest.TestCase): + def test_costs(self): + cost = Cost() + self.assertEqual(True, False) # add assertion here +