Merge remote-tracking branch 'origin/main'

# Conflicts:
#	costs/__main__.py
This commit is contained in:
Oriol Gavalda 2023-05-31 12:33:07 -04:00
commit 57ee5c9873
9 changed files with 119 additions and 75 deletions

View File

@ -1,5 +0,0 @@
"""
Cost workflow initialization
"""

40
costs/__init__.py Normal file
View File

@ -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)

View File

@ -5,8 +5,6 @@ Copyright © 2022 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.mons
Code contributor Oriol Gavalda Torrellas oriol.gavalda@concordia.ca Code contributor Oriol Gavalda Torrellas oriol.gavalda@concordia.ca
""" """
import glob
import os
from pathlib import Path from pathlib import Path
import numpy_financial as npf import numpy_financial as npf
@ -25,6 +23,15 @@ from sra_engine import SraEngine
from life_cycle_costs import LifeCycleCosts 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): def _npv_from_list(npv_discount_rate, list_cashflow):
lcc_value = npf.npv(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') 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() life_cycle_results = pd.DataFrame()
print('[city creation start]') print('[city creation start]')
city = GeometryFactory('geojson', city = GeometryFactory('geojson',
@ -73,47 +54,46 @@ city = GeometryFactory('geojson',
year_of_construction_field='ANNEE_CONS', year_of_construction_field='ANNEE_CONS',
function_field='CODE_UTILI', function_field='CODE_UTILI',
function_to_hub=Dictionaries().montreal_function_to_hub_function).city function_to_hub=Dictionaries().montreal_function_to_hub_function).city
city.climate_reference_city = climate_reference_city city.climate_reference_city = CLIMATE_REFERENCE_CITY
city.climate_file = (tmp_folder / f'{climate_reference_city}.cli').resolve() city.climate_file = (tmp_folder / f'{CLIMATE_REFERENCE_CITY}.cli').resolve()
print(f'city created from {file_path}') 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') print('enrich weather... done')
ConstructionFactory(construction_format, city).enrich() ConstructionFactory(CONSTRUCTION_FORMAT, city).enrich()
print('enrich constructions... done') print('enrich constructions... done')
UsageFactory(usage_format, city).enrich() UsageFactory(USAGE_FORMAT, city).enrich()
print('enrich usage... done') print('enrich usage... done')
for building in city.buildings: for building in city.buildings:
building.energy_systems_archetype_name = 'system 1 gas' 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('enrich systems... done')
print('exporting:') print('exporting:')
catalog = CostCatalogFactory('montreal_custom').catalog catalog = CostCatalogFactory('montreal_custom').catalog
print('costs catalog access... done') print('costs catalog access... done')
sra_file = (tmp_folder / f'{city.name}_sra.xml').resolve() 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...') print(' sra processed...')
for building in city.buildings: for building in city.buildings:
building.attic_heated = attic_heated_case building.attic_heated = ATTIC_HEATED_CASE
building.basement_heated = basement_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: for building in city.buildings:
building.year_of_construction = retrofitting_year_of_construction building.year_of_construction = retrofitting_year_of_construction
ConstructionFactory(construction_format, city).enrich() ConstructionFactory(CONSTRUCTION_FORMAT, city).enrich()
print('enrich retrofitted constructions... done') 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: for building in city.buildings:
building.energy_systems_archetype_name = 'system 6 electricity pv' 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') print('enrich systems... done')
MonthlyEnergyBalanceEngine(city, tmp_folder) MonthlyEnergyBalanceEngine(city, tmp_folder)
lightingdemand = city.buildings[0].lighting_electrical_demand[cte.YEAR]['insel meb'] / 1000
print(f'lighting first {lightingdemand}')
EnergySystemsSizing(city).enrich() EnergySystemsSizing(city).enrich()
print(f'beginning costing scenario {retrofitting_scenario} systems... done') print(f'beginning costing scenario {retrofitting_scenario} systems... done')
@ -124,13 +104,12 @@ for retrofitting_scenario in retrofitting_scenarios:
archetype = _search_archetype(catalog, function) archetype = _search_archetype(catalog, function)
print('lcc for first building started') print('lcc for first building started')
if "gas" in building.energy_systems_archetype_name: if "gas" in building.energy_systems_archetype_name:
fuel_type = 1 FUEL_TYPE = 1
else: 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,
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, global_capital_incomes = lcc.calculate_capital_costs() global_capital_costs, global_capital_incomes = lcc.calculate_capital_costs()
global_end_of_life_costs = lcc.calculate_end_of_life_costs() global_end_of_life_costs = lcc.calculate_end_of_life_costs()
global_operational_costs = lcc.calculate_total_operational_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'] + \ df_capital_incomes = global_capital_incomes['Subsidies construction'] + global_capital_incomes['Subsidies HVAC'] + \
global_capital_incomes['Subsidies PV'] 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_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_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_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_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_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_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_capital_incomes = _npv_from_list(DISCOUNT_RATE, df_capital_incomes.values.tolist())
life_cycle_costs = ( life_cycle_costs = (
life_cycle_costs_capital_skin + life_cycle_costs_capital_skin +
@ -202,10 +181,10 @@ for retrofitting_scenario in retrofitting_scenarios:
life_cycle_operational_incomes, life_cycle_operational_incomes,
life_cycle_capital_incomes] life_cycle_capital_incomes]
life_cycle_results.index = [f'total_capital_costs_skin', life_cycle_results.index = ['total_capital_costs_skin',
f'total_capital_costs_systems', f'end_of_life_costs', f'total_capital_costs_systems','end_of_life_costs',
f'total_operational_costs', f'total_maintenance_costs', 'total_operational_costs', 'total_maintenance_costs',
f'operational_incomes', f'capital_incomes'] 'operational_incomes', 'capital_incomes']
print(life_cycle_results) print(life_cycle_results)
print(f'Scenario {retrofitting_scenario} {life_cycle_costs}') print(f'Scenario {retrofitting_scenario} {life_cycle_costs}')

4
costs/data/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View File

@ -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 SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Project Author Pilar Monsalvete Alvarez 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 Code contributor Oriol Gavalda Torrellas oriol.gavalda@concordia.ca
@ -10,14 +10,13 @@ import math
import pandas as pd import pandas as pd
import hub.helpers.constants as cte import hub.helpers.constants as cte
from costs import SKIN_RETROFIT, SYSTEM_RETROFIT_AND_PV, SKIN_RETROFIT_AND_SYSTEM_RETROFIT_AND_PV
class LifeCycleCosts: class LifeCycleCosts:
"""
CURRENT_STATUS = 0 Life cycle cost class
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, def __init__(self, building, archetype, number_of_years, consumer_price_index, electricity_peak_index,
electricity_price_index, gas_price_index, discount_rate, 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', self._yearly_capital_incomes = pd.DataFrame(index=rng, columns=['Subsidies construction',
'Subsidies HVAC', 'Subsidies PV'], dtype='float') 'Subsidies HVAC', 'Subsidies PV'], dtype='float')
def calculate_capital_costs(self): def calculate_capital_costs(self):
"""
Calculate capital cost
:return: pd.DataFrame
"""
building = self._building building = self._building
archetype = self._archetype archetype = self._archetype
@ -123,7 +126,7 @@ class LifeCycleCosts:
self._yearly_capital_incomes.loc[0, 'Subsidies PV'] = 0 self._yearly_capital_incomes.loc[0, 'Subsidies PV'] = 0
self._yearly_capital_costs.fillna(0, inplace=True) 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') chapter = chapters.chapter('B_shell')
capital_cost_opaque = surface_opaque * chapter.item('B2010_opaque_walls').refurbishment[0] 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_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 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') chapter = chapters.chapter('D_services')
capital_cost_pv = surface_pv * chapter.item('D301010_photovoltaic_system').initial_investment[0] 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 self._yearly_capital_costs.loc[0]['D301010_photovoltaic_system'] = capital_cost_pv
@ -183,7 +186,7 @@ class LifeCycleCosts:
* costs_increase * costs_increase
self._yearly_capital_costs.loc[year, 'D5020_lighting_and_branch_wiring'] = reposition_cost_lighting 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: if (year % chapter.item('D301010_photovoltaic_system').lifetime) == 0:
self._yearly_capital_costs.loc[year]['D301010_photovoltaic_system'] = surface_pv \ self._yearly_capital_costs.loc[year]['D301010_photovoltaic_system'] = surface_pv \
* chapter.item( * chapter.item(
@ -200,6 +203,10 @@ class LifeCycleCosts:
return self._yearly_capital_costs, self._yearly_capital_incomes return self._yearly_capital_costs, self._yearly_capital_incomes
def calculate_end_of_life_costs(self): def calculate_end_of_life_costs(self):
"""
Calculate end of life costs
:return: pd.DataFrame
"""
archetype = self._archetype archetype = self._archetype
total_floor_area = self._total_floor_area total_floor_area = self._total_floor_area
@ -213,6 +220,10 @@ class LifeCycleCosts:
return self._yearly_end_of_life_costs return self._yearly_end_of_life_costs
def calculate_total_operational_costs(self): def calculate_total_operational_costs(self):
"""
Calculate total operational costs
:return: pd.DataFrame
"""
building = self._building building = self._building
archetype = self._archetype archetype = self._archetype
total_floor_area = self._total_floor_area total_floor_area = self._total_floor_area
@ -273,6 +284,10 @@ class LifeCycleCosts:
return self._yearly_operational_costs return self._yearly_operational_costs
def calculate_total_operational_incomes(self): def calculate_total_operational_incomes(self):
"""
Calculate total operational incomes
:return: pd.DataFrame
"""
building = self._building building = self._building
archetype = self._archetype archetype = self._archetype
if cte.YEAR not in building.onsite_electrical_production: if cte.YEAR not in building.onsite_electrical_production:
@ -294,6 +309,10 @@ class LifeCycleCosts:
return self._yearly_operational_incomes return self._yearly_operational_incomes
def calculate_total_maintenance_costs(self): def calculate_total_maintenance_costs(self):
"""
Calculate total maintenance costs
:return: pd.DataFrame
"""
building = self._building building = self._building
archetype = self._archetype archetype = self._archetype
# todo: change area pv when the variable exists # todo: change area pv when the variable exists

4
costs/outputs/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

4
costs/tmp/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View File

@ -1 +0,0 @@
/input_files/