From f15cfff55eda3916101a178daf0382337612913f Mon Sep 17 00:00:00 2001 From: jgavalda Date: Fri, 7 Jul 2023 10:12:49 -0400 Subject: [PATCH] Ongoing developments for the course workshop, including graphs implementation --- costs/__main__.py | 116 +++++-- costs/life_cycle_costs.py | 22 +- costs/printing_results.py | 60 ++++ input_files/summerschool_one_building.geojson | 294 ++++++++++++++++++ 4 files changed, 454 insertions(+), 38 deletions(-) create mode 100644 costs/printing_results.py create mode 100644 input_files/summerschool_one_building.geojson diff --git a/costs/__main__.py b/costs/__main__.py index 2a78c41..3c002fc 100644 --- a/costs/__main__.py +++ b/costs/__main__.py @@ -9,7 +9,6 @@ from pathlib import Path import numpy_financial as npf import pandas as pd -from energy_systems_sizing import EnergySystemsSizing from hub.catalog_factories.costs_catalog_factory import CostCatalogFactory from hub.helpers.dictionaries import Dictionaries from hub.imports.construction_factory import ConstructionFactory @@ -19,10 +18,11 @@ from hub.imports.usage_factory import UsageFactory from hub.imports.weather_factory import WeatherFactory from monthly_energy_balance_engine import MonthlyEnergyBalanceEngine from sra_engine import SraEngine - +import numpy as np +from printing_results import * +from hub.helpers import constants as cte 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 @@ -33,8 +33,7 @@ from costs import EMISSION_FACTOR_GAS_QUEBEC, EMISSION_FACTOR_ELECTRICITY_QUEBEC EMISSION_FACTOR_BIOMASS_QUEBEC, EMISSION_FACTOR_FUEL_OIL_QUEBEC, EMISSION_FACTOR_DIESEL_QUEBEC # import paths -from costs import file_path, tmp_folder, out_path - +from results import Results def _npv_from_list(npv_discount_rate, list_cashflow): lcc_value = npf.npv(npv_discount_rate, list_cashflow) @@ -50,36 +49,44 @@ def _search_archetype(costs_catalog, building_function): life_cycle_results = pd.DataFrame() -print('[city creation start]') +file_path = (Path(__file__).parent.parent / 'input_files' / 'summerschool_one_building.geojson') +climate_reference_city = 'Montreal' +weather_format = 'epw' +construction_format = 'nrcan' +usage_format = 'nrcan' +energy_systems_format = 'montreal_custom' +attic_heated_case = 0 +basement_heated_case = 1 + +out_path = (Path(__file__).parent.parent / 'out_files') +tmp_folder = (Path(__file__).parent / 'tmp') + +print('[simulation start]') city = GeometryFactory('geojson', path=file_path, - height_field='heightmax', + height_field='citygml_me', 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).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_SYSTEM_FORMAT, city).enrich() + building.energy_systems_archetype_name = 'system 1 gas pv' +EnergySystemsFactory(energy_systems_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) -print(' sra processed...') -for building in city.buildings: - building.attic_heated = ATTIC_HEATED_CASE - building.basement_heated = BASEMENT_HEATED_CASE +print('exporting:') +sra_file = (tmp_folder / f'{city.name}_sra.xml').resolve() +SraEngine(city, sra_file, tmp_folder) +print(' sra processed...') +catalog = CostCatalogFactory('montreal_custom').catalog for retrofitting_scenario in RETROFITTING_SCENARIOS: @@ -96,13 +103,26 @@ for retrofitting_scenario in RETROFITTING_SCENARIOS: print('enrich systems... done') MonthlyEnergyBalanceEngine(city, tmp_folder) + print(' insel processed...') - EnergySystemsSizing(city).enrich() + for building in city.buildings: + for energy_system in building.energy_systems: + if cte.HEATING in energy_system.demand_types: + energy_system.generation_system.heat_power = building.heating_peak_load[cte.YEAR][0] + if cte.COOLING in energy_system.demand_types: + energy_system.generation_system.cooling_power = building.cooling_peak_load[cte.YEAR][0] + print(f' heating consumption {building.heating_consumption[cte.YEAR][0]}') + print('importing results:') + results = Results(city, out_path) + results.print() + print('results printed...') + + print('[simulation end]') print(f'beginning costing scenario {retrofitting_scenario} systems... done') for building in city.buildings: - + total_floor_area = 0 function = Dictionaries().hub_function_to_montreal_custom_costs_function[building.function] archetype = _search_archetype(catalog, function) print('lcc for first building started') @@ -117,7 +137,7 @@ for retrofitting_scenario in RETROFITTING_SCENARIOS: 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() + global_operational_incomes = lcc.calculate_total_operational_incomes(retrofitting_scenario) full_path_output = Path(out_path / f'output {retrofitting_scenario} {building.name}.xlsx').resolve() with pd.ExcelWriter(full_path_output) as writer: global_capital_costs.to_excel(writer, sheet_name='global_capital_costs') @@ -127,6 +147,31 @@ for retrofitting_scenario in RETROFITTING_SCENARIOS: global_operational_incomes.to_excel(writer, sheet_name='global_operational_incomes') global_capital_incomes.to_excel(writer, sheet_name='global_capital_incomes') + if retrofitting_scenario == 0: + investmentcosts = [global_capital_costs['B2010_opaque_walls'][0], + global_capital_costs['B2020_transparent'][0], + global_capital_costs['B3010_opaque_roof'][0], + global_capital_costs['B10_superstructure'][0], + global_capital_costs['D3020_heat_generating_systems'][0], + global_capital_costs['D3080_other_hvac_ahu'][0], + global_capital_costs['D5020_lighting_and_branch_wiring'][0], + global_capital_costs['D301010_photovoltaic_system'][0]] + investmentcosts = pd.DataFrame(investmentcosts) + + + else: + investmentcosts[f'retrofitting_scenario {retrofitting_scenario}'] = [global_capital_costs['B2010_opaque_walls'][0], + global_capital_costs['B2020_transparent'][0], + global_capital_costs['B3010_opaque_roof'][0], + global_capital_costs['B10_superstructure'][0], + global_capital_costs['D3020_heat_generating_systems'][0], + global_capital_costs['D3080_other_hvac_ahu'][0], + global_capital_costs['D5020_lighting_and_branch_wiring'][0], + global_capital_costs['D301010_photovoltaic_system'][0]] + + investmentcosts.index = ['Opaque walls', 'Transparent walls', 'Opaque roof', 'Superstructure', + 'Heat generation systems', 'Other HVAC AHU', 'Lighting and branch wiring', 'PV systems'] + 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'] @@ -178,14 +223,14 @@ for retrofitting_scenario in RETROFITTING_SCENARIOS: life_cycle_operational_incomes - life_cycle_capital_incomes ) - + total_floor_area += lcc.calculate_total_floor_area() life_cycle_results[f'Scenario {retrofitting_scenario}'] = [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] + -life_cycle_operational_incomes, + -life_cycle_capital_incomes] life_cycle_results.index = ['total_capital_costs_skin', 'total_capital_costs_systems', @@ -195,5 +240,16 @@ for retrofitting_scenario in RETROFITTING_SCENARIOS: 'operational_incomes', 'capital_incomes'] - print(life_cycle_results) print(f'Scenario {retrofitting_scenario} {life_cycle_costs}') + +printing_results(investmentcosts,life_cycle_results,total_floor_area) + + + + + + + + + + diff --git a/costs/life_cycle_costs.py b/costs/life_cycle_costs.py index e2d845f..240d690 100644 --- a/costs/life_cycle_costs.py +++ b/costs/life_cycle_costs.py @@ -102,9 +102,9 @@ class LifeCycleCosts: surface_transparent += thermal_boundary.opaque_area * thermal_boundary.window_ratio 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 + print('kk') + peak_heating = building.heating_peak_load[cte.YEAR][0]/1000 + peak_cooling = building.cooling_peak_load[cte.YEAR][0]/1000 # todo: change area pv when the variable exists roof_area = 0 for roof in building.roofs: @@ -247,6 +247,9 @@ class LifeCycleCosts: self._yearly_end_of_life_costs.fillna(0, inplace=True) return self._yearly_end_of_life_costs + def calculate_total_floor_area(self): + total_floor_area = self._total_floor_area + return total_floor_area @property def calculate_total_operational_costs(self): """ @@ -283,7 +286,7 @@ class LifeCycleCosts: print(f'electricity consumption {total_electricity_consumption}') # todo: change when peak electricity demand is coded. Careful with factor residential - peak_electricity_demand = 100 # self._peak_electricity_demand + peak_electricity_demand = 0.1*total_floor_area # 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 @@ -313,7 +316,7 @@ class LifeCycleCosts: return self._yearly_operational_costs - def calculate_total_operational_incomes(self): + def calculate_total_operational_incomes(self, retrofitting_scenario): """ Calculate total operational incomes :return: pd.DataFrame @@ -322,7 +325,10 @@ class LifeCycleCosts: 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 + if retrofitting_scenario == 0 or retrofitting_scenario == 1: + 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) @@ -348,8 +354,8 @@ class LifeCycleCosts: 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] + peak_heating = building.heating_peak_load[cte.YEAR][0]/1000 + peak_cooling = building.heating_peak_load[cte.YEAR][0]/1000 maintenance_heating_0 = peak_heating * archetype.operational_cost.maintenance_heating maintenance_cooling_0 = peak_cooling * archetype.operational_cost.maintenance_cooling diff --git a/costs/printing_results.py b/costs/printing_results.py new file mode 100644 index 0000000..b735d07 --- /dev/null +++ b/costs/printing_results.py @@ -0,0 +1,60 @@ +import numpy as np + +import plotly.graph_objects as go +import plotly.offline as offline +import matplotlib.pyplot as plt +import plotly.express as px + +def printing_results(investmentcosts, life_cycle_results,total_floor_area): + + labels = investmentcosts.index + values = investmentcosts['retrofitting_scenario 1'] + values2 = investmentcosts['retrofitting_scenario 2'] + values3 = investmentcosts['retrofitting_scenario 3'] + + fig = go.Figure(data=[go.Pie(labels=labels, values=values)]) + fig2 = go.Figure(data=[go.Pie(labels=labels, values=values2)]) + fig3 = go.Figure(data=[go.Pie(labels=labels, values=values3)]) + # Set the layout properties + fig.update_layout( + title='Retrofitting scenario 1', + showlegend=True + ) + fig2.update_layout( + title='Retrofitting scenario 1', + showlegend=True + ) + fig3.update_layout( + title='Retrofitting scenario 1', + showlegend=True + ) + + # Display the chart + fig.show() + fig2.show() + fig3.show() + + df = life_cycle_results / total_floor_area + + # Transpose the DataFrame (swap columns and rows) + df_swapped = df.transpose() + + # Reset the index to make the current index a regular column + df_swapped = df_swapped.reset_index() + + # Assign new column names + df_swapped.columns = ['Scenarios', 'total_capital_costs_skin', + 'total_capital_costs_systems', + 'end_of_life_costs', + 'total_operational_costs', + 'total_maintenance_costs', + 'operational_incomes', + 'capital_incomes'] + + df_swapped.index = df_swapped['Scenarios'] + df_swapped = df_swapped.drop('Scenarios', axis=1) + print(df_swapped) + fig = px.bar(df_swapped, title='Life Cycle Costs for buildings') + fig.show() + # Display the chart + plt.show() \ No newline at end of file diff --git a/input_files/summerschool_one_building.geojson b/input_files/summerschool_one_building.geojson new file mode 100644 index 0000000..0836973 --- /dev/null +++ b/input_files/summerschool_one_building.geojson @@ -0,0 +1,294 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "id": 12, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -73.57945149010348, + 45.49793915473101 + ], + [ + -73.57945502047383, + 45.497935600591106 + ], + [ + -73.57945748913181, + 45.49793681276347 + ], + [ + -73.57945995778985, + 45.49793802493576 + ], + [ + -73.57946108986009, + 45.49793688584562 + ], + [ + -73.57946222064952, + 45.49793574585649 + ], + [ + -73.57946503164756, + 45.497932909392325 + ], + [ + -73.5794800321942, + 45.497917804072586 + ], + [ + -73.57949503273288, + 45.49790269875081 + ], + [ + -73.57950823165471, + 45.49788939886833 + ], + [ + -73.57952143057031, + 45.497876098984314 + ], + [ + -73.57952481016481, + 45.49787269972034 + ], + [ + -73.57952818975889, + 45.49786930045622 + ], + [ + -73.57963374256275, + 45.49776298233438 + ], + [ + -73.57963739684415, + 45.497759299424665 + ], + [ + -73.57956562282082, + 45.49772405755894 + ], + [ + -73.5795624921933, + 45.497722521006246 + ], + [ + -73.57955974509859, + 45.4977252944393 + ], + [ + -73.57953557695755, + 45.497749634054365 + ], + [ + -73.5795114087957, + 45.497773973664174 + ], + [ + -73.57945076790263, + 45.49783505227953 + ], + [ + -73.57939012687844, + 45.49789613086214 + ], + [ + -73.57938759058709, + 45.49789868818189 + ], + [ + -73.57938505429556, + 45.49790124550157 + ], + [ + -73.57941717242674, + 45.49791701633786 + ], + [ + -73.5794136407655, + 45.497920563278754 + ], + [ + -73.57943256542505, + 45.497929854507255 + ], + [ + -73.57944202776348, + 45.49793450461953 + ], + [ + -73.57945149010348, + 45.49793915473101 + ] + ] + ] + }, + "properties": { + "OBJECTID_12": 12, + "gml_id": 1340982, + "gml_parent": "fme-gen-5fa2a82b-c38e-4bf0-9e8f-10a47b9f64f7", + "citygml_ta": "http://www.opengis.net/citygml/building/2.0", + "citygml_fe": "cityObjectMember", + "citygml__1": " ", + "citygml__2": " ", + "gml_descri": " ", + "gml_name": " ", + "citygml_cr": " ", + "citygml_te": " ", + "externalRe": " ", + "external_1": " ", + "external_2": " ", + "citygml_ge": " ", + "citygml_re": " ", + "citygml__3": " ", + "citygml_ap": " ", + "citygml_cl": " ", + "citygml__4": " ", + "citygml_fu": " ", + "citygml__5": " ", + "citygml_us": " ", + "citygml__6": " ", + "citygml_ye": " ", + "citygml__7": " ", + "citygml_ro": " ", + "citygml__8": " ", + "citygml_me": 19.113, + "citygml__9": "#m", + "citygml_st": " ", + "citygml_10": " ", + "citygml_11": " ", + "citygml_12": " ", + "citygml_13": " ", + "citygml_14": " ", + "citygml_ou": " ", + "citygml_in": " ", + "citygml_bo": " ", + "citygml_le": " ", + "citygml_15": " ", + "citygml_co": " ", + "citygml_ad": " ", + "Volume": "2931.350", + "parcelle": " ", + "OBJECTID": 1056, + "gml_id_1": "384b2b1c-2e25-4f6a-b082-d272dba3453f", + "gml_pare_1": 1340982, + "citygml_16": "http://www.opengis.net/citygml/building/2.0", + "citygml_17": "boundedBy", + "citygml_18": " ", + "citygml_19": " ", + "gml_desc_1": " ", + "gml_name_1": " ", + "citygml_20": " ", + "citygml_21": " ", + "external_3": " ", + "external_4": " ", + "external_5": " ", + "citygml_22": " ", + "citygml_23": " ", + "citygml_24": " ", + "citygml_25": " ", + "citygml_26": " ", + "citygml_op": " ", + "Area": 191.404, + "FID_": 0, + "Join_Count": 2, + "TARGET_FID": 1058, + "gml_id_12": 1340982, + "gml_pare_2": "fme-gen-5fa2a82b-c38e-4bf0-9e8f-10a47b9f64f7", + "citygml_27": "http://www.opengis.net/citygml/building/2.0", + "citygml_28": "cityObjectMember", + "citygml_29": " ", + "citygml_30": " ", + "gml_desc_2": " ", + "gml_name_2": " ", + "citygml_31": " ", + "citygml_32": " ", + "external_6": " ", + "external_7": " ", + "external_8": " ", + "citygml_33": " ", + "citygml_34": " ", + "citygml_35": " ", + "citygml_36": " ", + "citygml_37": " ", + "citygml_38": " ", + "citygml_39": " ", + "citygml_40": " ", + "citygml_41": " ", + "citygml_42": " ", + "citygml_43": " ", + "citygml_44": " ", + "citygml_45": " ", + "citygml_46": " ", + "citygml_47": 19.113, + "citygml_48": "#m", + "citygml_49": " ", + "citygml_50": " ", + "citygml_51": " ", + "citygml_52": " ", + "citygml_53": " ", + "citygml_54": " ", + "citygml_55": " ", + "citygml_56": " ", + "citygml_57": " ", + "citygml_58": " ", + "citygml_59": " ", + "citygml_60": " ", + "citygml_61": " ", + "Volume_1": "2931.350", + "Field": 0, + "Field1": 0, + "OBJECTID_1": 1056, + "gml_id_12_": "384b2b1c-2e25-4f6a-b082-d272dba3453f", + "gml_pare_3": 1340982, + "citygml_62": "http://www.opengis.net/citygml/building/2.0", + "citygml_63": "boundedBy", + "citygml_64": " ", + "citygml_65": " ", + "gml_desc_3": " ", + "gml_name_3": " ", + "citygml_66": " ", + "citygml_67": " ", + "external_9": " ", + "externa_10": " ", + "externa_11": " ", + "citygml_68": " ", + "citygml_69": " ", + "citygml_70": " ", + "citygml_71": " ", + "citygml_72": " ", + "citygml_73": " ", + "Area_1": 191.404, + "cityGML_hi": 0, + "Z_Min": 46.1162, + "Z_Max": 64.399, + "Shape_Leng": 63.6906066955, + "ID_UEV": "01036804", + "CIVIQUE_DE": " 2170", + "CIVIQUE_FI": " 2170", + "NOM_RUE": "rue Bishop (MTL)", + "MUNICIPALI": 50, + "ETAGE_HORS": 3, + "NOMBRE_LOG": 1, + "ANNEE_CONS": 1900, + "CODE_UTILI": 6000, + "LIBELLE_UT": "Immeuble à bureaux", + "CATEGORIE_": "Régulier", + "MATRICULE8": "9839-57-7770-3-000-0000", + "SUPERFICIE": 259, + "SUPERFIC_1": 490, + "NO_ARROND_": "REM19", + "Shape_Le_1": 0.00093336765858, + "Shape_Ar_1": 3.0845126501e-8, + "Z_Min_1": null, + "Z_Max_1": null, + "Shape_Length": 63.69060669550123, + "Shape_Area": 174.69050030775531 + } + } + ] +} \ No newline at end of file