diff --git a/energy_system_retrofit.py b/energy_system_retrofit.py index 2e49b382..b6b8cd31 100644 --- a/energy_system_retrofit.py +++ b/energy_system_retrofit.py @@ -1,4 +1,3 @@ -from scripts.geojson_creator import process_geojson from pathlib import Path import subprocess from scripts.ep_run_enrich import energy_plus_workflow @@ -8,57 +7,92 @@ from hub.imports.construction_factory import ConstructionFactory from hub.imports.usage_factory import UsageFactory from hub.imports.weather_factory import WeatherFactory from hub.imports.results_factory import ResultFactory -from scripts.energy_system_analysis_report import EnergySystemAnalysisReport +from scripts.energy_system_retrofit_report import EnergySystemRetrofitReport +from scripts.geojson_creator import process_geojson from scripts import random_assignation from hub.imports.energy_systems_factory import EnergySystemsFactory from scripts.energy_system_sizing import SystemSizing -from scripts.energy_system_retrofit_results import system_results, new_system_results +from scripts.solar_angles import CitySolarAngles +from scripts.pv_sizing_and_simulation import PVSizingSimulation +from scripts.energy_system_retrofit_results import consumption_data, cost_data from scripts.energy_system_sizing_and_simulation_factory import EnergySystemsSimulationFactory from scripts.costs.cost import Cost -from scripts.costs.constants import SKIN_RETROFIT_AND_SYSTEM_RETROFIT_AND_PV, SYSTEM_RETROFIT_AND_PV +from scripts.costs.constants import SKIN_RETROFIT_AND_SYSTEM_RETROFIT_AND_PV, SYSTEM_RETROFIT_AND_PV, CURRENT_STATUS import hub.helpers.constants as cte from hub.exports.exports_factory import ExportsFactory +from scripts.pv_feasibility import pv_feasibility + # Specify the GeoJSON file path +input_files_path = (Path(__file__).parent / 'input_files') +input_files_path.mkdir(parents=True, exist_ok=True) geojson_file = process_geojson(x=-73.5681295982132, y=45.49218262677643, diff=0.0001) -file_path = (Path(__file__).parent / 'input_files' / 'output_buildings.geojson') -# Specify the output path for the PDF file +geojson_file_path = input_files_path / 'output_buildings.geojson' output_path = (Path(__file__).parent / 'out_files').resolve() -# Create city object from GeoJSON file -city = GeometryFactory('geojson', - path=file_path, +output_path.mkdir(parents=True, exist_ok=True) +energy_plus_output_path = output_path / 'energy_plus_outputs' +energy_plus_output_path.mkdir(parents=True, exist_ok=True) +simulation_results_path = (Path(__file__).parent / 'out_files' / 'simulation_results').resolve() +simulation_results_path.mkdir(parents=True, exist_ok=True) +sra_output_path = output_path / 'sra_outputs' +sra_output_path.mkdir(parents=True, exist_ok=True) +cost_analysis_output_path = output_path / 'cost_analysis' +cost_analysis_output_path.mkdir(parents=True, exist_ok=True) +city = GeometryFactory(file_type='geojson', + path=geojson_file_path, height_field='height', year_of_construction_field='year_of_construction', function_field='function', function_to_hub=Dictionaries().montreal_function_to_hub_function).city -# Enrich city data ConstructionFactory('nrcan', city).enrich() - UsageFactory('nrcan', city).enrich() WeatherFactory('epw', city).enrich() -ExportsFactory('sra', city, output_path).export() -sra_path = (output_path / f'{city.name}_sra.xml').resolve() +ExportsFactory('sra', city, sra_output_path).export() +sra_path = (sra_output_path / f'{city.name}_sra.xml').resolve() subprocess.run(['sra', str(sra_path)]) -ResultFactory('sra', city, output_path).enrich() -energy_plus_workflow(city) +ResultFactory('sra', city, sra_output_path).enrich() +pv_feasibility(-73.5681295982132, 45.49218262677643, 0.0001, selected_buildings=city.buildings) +energy_plus_workflow(city, energy_plus_output_path) +solar_angles = CitySolarAngles(city.name, + city.latitude, + city.longitude, + tilt_angle=45, + surface_azimuth_angle=180).calculate random_assignation.call_random(city.buildings, random_assignation.residential_systems_percentage) EnergySystemsFactory('montreal_custom', city).enrich() SystemSizing(city.buildings).montreal_custom() -current_system = new_system_results(city.buildings) +current_status_energy_consumption = consumption_data(city) +current_status_life_cycle_cost = {} +for building in city.buildings: + cost_retrofit_scenario = CURRENT_STATUS + lcc_dataframe = Cost(building=building, + retrofit_scenario=cost_retrofit_scenario, + fuel_tariffs=['Electricity-D', 'Gas-Energir']).life_cycle + lcc_dataframe.to_csv(cost_analysis_output_path / f'{building.name}_current_status_lcc.csv') + current_status_life_cycle_cost[f'{building.name}'] = cost_data(building, lcc_dataframe, cost_retrofit_scenario) random_assignation.call_random(city.buildings, random_assignation.residential_new_systems_percentage) EnergySystemsFactory('montreal_future', city).enrich() for building in city.buildings: - EnergySystemsSimulationFactory('archetype1', building=building, output_path=output_path).enrich() - print(building.energy_consumption_breakdown[cte.ELECTRICITY][cte.COOLING] + - building.energy_consumption_breakdown[cte.ELECTRICITY][cte.HEATING] + - building.energy_consumption_breakdown[cte.ELECTRICITY][cte.DOMESTIC_HOT_WATER]) -new_system = new_system_results(city.buildings) -# EnergySystemAnalysisReport(city, output_path).create_report(current_system, new_system) + if 'PV' in building.energy_systems_archetype_name: + ghi = [x / cte.WATTS_HOUR_TO_JULES for x in building.roofs[0].global_irradiance[cte.HOUR]] + pv_sizing_simulation = PVSizingSimulation(building, + solar_angles, + tilt_angle=45, + module_height=1, + module_width=2, + ghi=ghi) + pv_sizing_simulation.pv_output() + if building.energy_systems_archetype_name == 'PV+4Pipe+DHW': + EnergySystemsSimulationFactory('archetype13', building=building, output_path=simulation_results_path).enrich() +retrofitted_energy_consumption = consumption_data(city) +retrofitted_life_cycle_cost = {} for building in city.buildings: - costs = Cost(building=building, retrofit_scenario=SYSTEM_RETROFIT_AND_PV).life_cycle - costs.to_csv(output_path / f'{building.name}_lcc.csv') - (costs.loc['global_operational_costs', f'Scenario {SYSTEM_RETROFIT_AND_PV}']. - to_csv(output_path / f'{building.name}_op.csv')) - costs.loc['global_capital_costs', f'Scenario {SYSTEM_RETROFIT_AND_PV}'].to_csv( - output_path / f'{building.name}_cc.csv') - costs.loc['global_maintenance_costs', f'Scenario {SYSTEM_RETROFIT_AND_PV}'].to_csv( - output_path / f'{building.name}_m.csv') \ No newline at end of file + cost_retrofit_scenario = SYSTEM_RETROFIT_AND_PV + lcc_dataframe = Cost(building=building, + retrofit_scenario=cost_retrofit_scenario, + fuel_tariffs=['Electricity-D', 'Gas-Energir']).life_cycle + lcc_dataframe.to_csv(cost_analysis_output_path / f'{building.name}_retrofitted_lcc.csv') + retrofitted_life_cycle_cost[f'{building.name}'] = cost_data(building, lcc_dataframe, cost_retrofit_scenario) +(EnergySystemRetrofitReport(city, output_path, 'PV Implementation and System Retrofit', + current_status_energy_consumption, retrofitted_energy_consumption, + current_status_life_cycle_cost, retrofitted_life_cycle_cost).create_report()) + diff --git a/hub/data/costs/montreal_costs_completed.xml b/hub/data/costs/montreal_costs_completed.xml index fc23634a..6b3fc41f 100644 --- a/hub/data/costs/montreal_costs_completed.xml +++ b/hub/data/costs/montreal_costs_completed.xml @@ -187,7 +187,7 @@ 1.5 3.6 - 0.07 + 0.075 5 diff --git a/report_test.py b/report_test.py deleted file mode 100644 index aa67926b..00000000 --- a/report_test.py +++ /dev/null @@ -1,71 +0,0 @@ -from pathlib import Path -import subprocess -from scripts.ep_run_enrich import energy_plus_workflow -from hub.imports.geometry_factory import GeometryFactory -from hub.helpers.dictionaries import Dictionaries -from hub.imports.construction_factory import ConstructionFactory -from hub.imports.usage_factory import UsageFactory -from hub.imports.weather_factory import WeatherFactory -from hub.imports.results_factory import ResultFactory -from scripts.energy_system_retrofit_report import EnergySystemRetrofitReport -from scripts.geojson_creator import process_geojson -from scripts import random_assignation -from hub.imports.energy_systems_factory import EnergySystemsFactory -from scripts.energy_system_sizing import SystemSizing -from scripts.solar_angles import CitySolarAngles -from scripts.pv_sizing_and_simulation import PVSizingSimulation -from scripts.energy_system_retrofit_results import consumption_data -from scripts.energy_system_sizing_and_simulation_factory import EnergySystemsSimulationFactory -from scripts.costs.cost import Cost -from scripts.costs.constants import SKIN_RETROFIT_AND_SYSTEM_RETROFIT_AND_PV, SYSTEM_RETROFIT_AND_PV -import hub.helpers.constants as cte -from hub.exports.exports_factory import ExportsFactory -from scripts.pv_feasibility import pv_feasibility - -geojson_file = process_geojson(x=-73.5681295982132, y=45.49218262677643, diff=0.0001) -file_path = (Path(__file__).parent / 'input_files' / 'output_buildings.geojson') -output_path = (Path(__file__).parent / 'out_files').resolve() -simulation_results_path = (Path(__file__).parent / 'out_files' / 'simulation_results').resolve() -simulation_results_path.mkdir(parents=True, exist_ok=True) -city = GeometryFactory(file_type='geojson', - path=file_path, - height_field='height', - year_of_construction_field='year_of_construction', - function_field='function', - function_to_hub=Dictionaries().montreal_function_to_hub_function).city -ConstructionFactory('nrcan', city).enrich() -UsageFactory('nrcan', city).enrich() -WeatherFactory('epw', city).enrich() -ExportsFactory('sra', city, output_path).export() -sra_path = (output_path / f'{city.name}_sra.xml').resolve() -subprocess.run(['sra', str(sra_path)]) -ResultFactory('sra', city, output_path).enrich() -pv_feasibility(-73.5681295982132, 45.49218262677643, 0.0001, selected_buildings=city.buildings) -energy_plus_workflow(city) -solar_angles = CitySolarAngles(city.name, - city.latitude, - city.longitude, - tilt_angle=45, - surface_azimuth_angle=180).calculate -random_assignation.call_random(city.buildings, random_assignation.residential_systems_percentage) -EnergySystemsFactory('montreal_custom', city).enrich() -SystemSizing(city.buildings).montreal_custom() -current_status_energy_consumption = consumption_data(city) -random_assignation.call_random(city.buildings, random_assignation.residential_new_systems_percentage) -EnergySystemsFactory('montreal_future', city).enrich() -for building in city.buildings: - if 'PV' in building.energy_systems_archetype_name: - ghi = [x / cte.WATTS_HOUR_TO_JULES for x in building.roofs[0].global_irradiance[cte.HOUR]] - pv_sizing_simulation = PVSizingSimulation(building, - solar_angles, - tilt_angle=45, - module_height=1, - module_width=2, - ghi=ghi) - pv_sizing_simulation.pv_output() - if building.energy_systems_archetype_name == 'PV+4Pipe+DHW': - EnergySystemsSimulationFactory('archetype13', building=building, output_path=simulation_results_path).enrich() -retrofitted_energy_consumption = consumption_data(city) -(EnergySystemRetrofitReport(city, output_path, 'PV Implementation and System Retrofit', - current_status_energy_consumption, retrofitted_energy_consumption).create_report()) - diff --git a/scripts/costs/total_operational_costs.py b/scripts/costs/total_operational_costs.py index dc672fa1..60ed54a9 100644 --- a/scripts/costs/total_operational_costs.py +++ b/scripts/costs/total_operational_costs.py @@ -196,7 +196,7 @@ class TotalOperationalCosts(CostBase): if cooling is not None: hourly += cooling[i] / 3600 if dhw is not None: - dhw += dhw[i] / 3600 + hourly += dhw[i] / 3600 hourly_fuel_consumption.append(hourly) else: heating = None diff --git a/scripts/costs/total_operational_incomes.py b/scripts/costs/total_operational_incomes.py index 2a110761..66d789ed 100644 --- a/scripts/costs/total_operational_incomes.py +++ b/scripts/costs/total_operational_incomes.py @@ -36,11 +36,10 @@ class TotalOperationalIncomes(CostBase): for year in range(1, self._configuration.number_of_years + 1): price_increase_electricity = math.pow(1 + self._configuration.electricity_price_index, year) - # todo: check the adequate assignation of price. Pilar - price_export = archetype.income.electricity_export * cte.WATTS_HOUR_TO_JULES * 1000 # to account for unit change + price_export = archetype.income.electricity_export # to account for unit change self._yearly_operational_incomes.loc[year, 'Incomes electricity'] = ( - onsite_electricity_production * price_export * price_increase_electricity + (onsite_electricity_production / cte.WATTS_HOUR_TO_JULES) * price_export * price_increase_electricity ) self._yearly_operational_incomes.fillna(0, inplace=True) - return self._yearly_operational_incomes + return self._yearly_operational_incomes \ No newline at end of file diff --git a/scripts/energy_system_retrofit_report.py b/scripts/energy_system_retrofit_report.py index 60e82ece..fcfb764f 100644 --- a/scripts/energy_system_retrofit_report.py +++ b/scripts/energy_system_retrofit_report.py @@ -1,11 +1,8 @@ import os import hub.helpers.constants as cte import matplotlib.pyplot as plt -import random -import matplotlib.colors as mcolors from matplotlib import cm from scripts.report_creation import LatexReport -import matplotlib as mpl from matplotlib.ticker import MaxNLocator import numpy as np from pathlib import Path @@ -14,10 +11,12 @@ import glob class EnergySystemRetrofitReport: def __init__(self, city, output_path, retrofit_scenario, current_status_energy_consumption_data, - retrofitted_energy_consumption_data): + retrofitted_energy_consumption_data, current_status_lcc_data, retrofitted_lcc_data): self.city = city self.current_status_data = current_status_energy_consumption_data self.retrofitted_data = retrofitted_energy_consumption_data + self.current_status_lcc = current_status_lcc_data + self.retrofitted_lcc = retrofitted_lcc_data self.output_path = output_path self.content = [] self.retrofit_scenario = retrofit_scenario @@ -334,7 +333,7 @@ class EnergySystemRetrofitReport: ax.spines['right'].set_linewidth(1.1) # Plotting - fig, axs = plt.subplots(3, 1, figsize=(20, 15), dpi=96) + fig, axs = plt.subplots(3, 1, figsize=(20, 25), dpi=96) fig.suptitle(title, fontsize=16, weight='bold', alpha=.8) plot_double_bar_chart(axs[0], 'heating', consumptions['Heating'][1], consumptions['Heating'][2], @@ -356,11 +355,21 @@ class EnergySystemRetrofitReport: return chart_path - def yearly_consumption_comparison(self): - current_total_consumption = self.current_status_data['total_consumption'] - retrofitted_total_consumption = self.retrofitted_data['total_consumption'] - + current_total_consumption = round(self.current_status_data['total_consumption'], 2) + retrofitted_total_consumption = round(self.retrofitted_data['total_consumption'], 2) + text = ( + f'The total yearly energy consumption before and after the retrofit are {current_total_consumption} MWh and ' + f'{retrofitted_total_consumption} MWh, respectively.') + if retrofitted_total_consumption < current_total_consumption: + change = str(round((current_total_consumption - retrofitted_total_consumption) * 100 / current_total_consumption, + 2)) + text += f'Therefore, the total yearly energy consumption decreased by {change} \%.' + else: + change = str(round((retrofitted_total_consumption - current_total_consumption) * 100 / + retrofitted_total_consumption, 2)) + text += f'Therefore, the total yearly energy consumption increased by {change} \%. \par' + self.report.add_text(text) def pv_system(self): self.report.add_text('The first step in PV assessments is evaluating the potential of buildings for installing ' @@ -425,6 +434,85 @@ class EnergySystemRetrofitReport: self.report.add_table(pv_output_table, caption='PV System Simulation Results', first_column_width=3) + def life_cycle_cost_stacked_bar(self, file_name, title): + # Aggregate LCC components for current and retrofitted statuses + current_status_capex = 0 + current_status_opex = 0 + current_status_maintenance = 0 + current_status_end_of_life = 0 + retrofitted_capex = 0 + retrofitted_opex = 0 + retrofitted_maintenance = 0 + retrofitted_end_of_life = 0 + + for building in self.city.buildings: + current_status_capex += self.current_status_lcc[f'{building.name}']['capital_cost_per_sqm'] + retrofitted_capex += self.retrofitted_lcc[f'{building.name}']['capital_cost_per_sqm'] + current_status_opex += self.current_status_lcc[f'{building.name}']['operational_cost_per_sqm'] + retrofitted_opex += self.retrofitted_lcc[f'{building.name}']['operational_cost_per_sqm'] + current_status_maintenance += self.current_status_lcc[f'{building.name}']['maintenance_cost_per_sqm'] + retrofitted_maintenance += self.retrofitted_lcc[f'{building.name}']['maintenance_cost_per_sqm'] + current_status_end_of_life += self.current_status_lcc[f'{building.name}']['end_of_life_cost_per_sqm'] + retrofitted_end_of_life += self.retrofitted_lcc[f'{building.name}']['end_of_life_cost_per_sqm'] + + current_status_lcc_components_sqm = { + 'Capital Cost': current_status_capex / len(self.city.buildings), + 'Operational Cost': current_status_opex / len(self.city.buildings), + 'Maintenance Cost': current_status_maintenance / len(self.city.buildings), + 'End of Life Cost': current_status_end_of_life / len(self.city.buildings) + } + retrofitted_lcc_components_sqm = { + 'Capital Cost': retrofitted_capex / len(self.city.buildings), + 'Operational Cost': retrofitted_opex / len(self.city.buildings), + 'Maintenance Cost': retrofitted_maintenance / len(self.city.buildings), + 'End of Life Cost': retrofitted_end_of_life / len(self.city.buildings) + } + + labels = ['Current Status', 'Retrofitted Status'] + categories = ['Capital Cost', 'Operational Cost', 'Maintenance Cost', 'End of Life Cost'] + current_values = list(current_status_lcc_components_sqm.values()) + retrofitted_values = list(retrofitted_lcc_components_sqm.values()) + colors = ['#2196f3', '#ff5a5f', '#4caf50', '#ffc107'] + + # Data preparation + bar_width = 0.35 + r = np.arange(len(labels)) + + fig, ax = plt.subplots(figsize=(12, 8), dpi=96) + fig.suptitle(title, fontsize=16, weight='bold', alpha=.8) + + # Plotting current status data + bottom = np.zeros(2) + for i, (category, color) in enumerate(zip(categories, colors)): + values = [current_status_lcc_components_sqm[category], retrofitted_lcc_components_sqm[category]] + ax.bar(r, values, bottom=bottom, color=color, edgecolor='white', width=bar_width, label=category) + bottom += values + + # Adding summation annotations at the top of the bars + for idx, (x, total) in enumerate(zip(r, bottom)): + ax.text(x, total, f'{total:.1f}', ha='center', va='bottom', fontsize=12, fontweight='bold') + + # Adding labels, title, and grid + ax.set_xlabel('LCC Components', fontsize=12, labelpad=10) + ax.set_ylabel('Average Cost (CAD/m²)', fontsize=14, labelpad=10) + ax.grid(which="major", axis='y', color='#DAD8D7', alpha=0.5, zorder=1) + ax.set_xticks(r) + ax.set_xticklabels(labels, rotation=45, ha='right') + ax.legend() + + # Adding a white background + fig.patch.set_facecolor('white') + + # Adjusting the margins around the plot area + plt.subplots_adjust(left=0.05, right=0.95, top=0.9, bottom=0.2) + + # Save the plot + chart_path = self.charts_path / f'{file_name}.png' + plt.savefig(chart_path, bbox_inches='tight') + plt.close() + + return chart_path + def create_report(self): # Add sections and text to the report self.report.add_section('Overview of the Current Status in Buildings') @@ -448,6 +536,11 @@ class EnergySystemRetrofitReport: title='Monthly Energy Consumptions') current_consumption_breakdown_path = self.fuel_consumption_breakdown('City_Energy_Consumption_Breakdown', self.current_status_data) + retrofitted_consumption_breakdown_path = self.fuel_consumption_breakdown( + 'fuel_consumption_breakdown_after_retrofit', + self.retrofitted_data) + life_cycle_cost_sqm_stacked_bar_chart_path = self.life_cycle_cost_stacked_bar('lcc_per_sqm', + 'LCC Analysis') # Add current state of energy demands in the city self.report.add_subsection('Current State of Energy Demands in the City') self.report.add_text('The total monthly energy demands in the city are shown in Figure 1. It should be noted ' @@ -487,6 +580,15 @@ class EnergySystemRetrofitReport: self.report.add_image(str(consumption_comparison).replace('\\', '/'), caption='Comparison of Total Monthly Energy Consumption in City Buildings', placement='H') + self.yearly_consumption_comparison() + self.report.add_text('Figure 7 shows the fuel consumption breakdown in the area after the retrofit.') + self.report.add_image(str(retrofitted_consumption_breakdown_path).replace('\\', '/'), + caption=f'Fuel Consumption Breakdown After {self.retrofit_scenario}', + placement='H') + self.report.add_subsection('Life Cycle Cost Analysis') + self.report.add_image(str(life_cycle_cost_sqm_stacked_bar_chart_path).replace('\\', '/'), + caption='Average Life Cycle Cost Components', + placement='H') # Save and compile the report self.report.save_report() diff --git a/scripts/energy_system_retrofit_results.py b/scripts/energy_system_retrofit_results.py index e1082908..9d85d0d9 100644 --- a/scripts/energy_system_retrofit_results.py +++ b/scripts/energy_system_retrofit_results.py @@ -1,15 +1,88 @@ import hub.helpers.constants as cte +def hourly_electricity_consumption_profile(building): + hourly_electricity_consumption = [] + energy_systems = building.energy_systems + appliance = building.appliances_electrical_demand[cte.HOUR] + lighting = building.lighting_electrical_demand[cte.HOUR] + elec_heating = 0 + elec_cooling = 0 + elec_dhw = 0 + if cte.HEATING in building.energy_consumption_breakdown[cte.ELECTRICITY]: + elec_heating = 1 + if cte.COOLING in building.energy_consumption_breakdown[cte.ELECTRICITY]: + elec_cooling = 1 + if cte.DOMESTIC_HOT_WATER in building.energy_consumption_breakdown[cte.ELECTRICITY]: + elec_dhw = 1 + heating = None + cooling = None + dhw = None + if elec_heating == 1: + for energy_system in energy_systems: + if cte.HEATING in energy_system.demand_types: + for generation_system in energy_system.generation_systems: + if generation_system.fuel_type == cte.ELECTRICITY: + if cte.HEATING in generation_system.energy_consumption: + heating = generation_system.energy_consumption[cte.HEATING][cte.HOUR] + else: + if len(energy_system.generation_systems) > 1: + heating = [x / 2 for x in building.heating_consumption[cte.HOUR]] + else: + heating = building.heating_consumption[cte.HOUR] + if elec_dhw == 1: + for energy_system in energy_systems: + if cte.DOMESTIC_HOT_WATER in energy_system.demand_types: + for generation_system in energy_system.generation_systems: + if generation_system.fuel_type == cte.ELECTRICITY: + if cte.DOMESTIC_HOT_WATER in generation_system.energy_consumption: + dhw = generation_system.energy_consumption[cte.DOMESTIC_HOT_WATER][cte.HOUR] + else: + if len(energy_system.generation_systems) > 1: + dhw = [x / 2 for x in building.domestic_hot_water_consumption[cte.HOUR]] + else: + dhw = building.domestic_hot_water_consumption[cte.HOUR] + + if elec_cooling == 1: + for energy_system in energy_systems: + if cte.COOLING in energy_system.demand_types: + for generation_system in energy_system.generation_systems: + if cte.COOLING in generation_system.energy_consumption: + cooling = generation_system.energy_consumption[cte.COOLING][cte.HOUR] + else: + if len(energy_system.generation_systems) > 1: + cooling = [x / 2 for x in building.cooling_consumption[cte.HOUR]] + else: + cooling = building.cooling_consumption[cte.HOUR] + + for i in range(len(building.heating_demand[cte.HOUR])): + hourly = 0 + hourly += appliance[i] / 3600 + hourly += lighting[i] / 3600 + if heating is not None: + hourly += heating[i] / 3600 + if cooling is not None: + hourly += cooling[i] / 3600 + if dhw is not None: + hourly += dhw[i] / 3600 + hourly_electricity_consumption.append(hourly) + return hourly_electricity_consumption + + def consumption_data(city): - current_status_energy_consumption_data = {} + energy_consumption_data = {} for building in city.buildings: - current_status_energy_consumption_data[f'{building.name}'] = {'heating_consumption': building.heating_consumption, - 'cooling_consumption': building.cooling_consumption, - 'domestic_hot_water_consumption': - building.domestic_hot_water_consumption, - 'energy_consumption_breakdown': - building.energy_consumption_breakdown} + hourly_electricity_consumption = hourly_electricity_consumption_profile(building) + energy_consumption_data[f'{building.name}'] = {'heating_consumption': building.heating_consumption, + 'cooling_consumption': building.cooling_consumption, + 'domestic_hot_water_consumption': + building.domestic_hot_water_consumption, + 'energy_consumption_breakdown': + building.energy_consumption_breakdown, + 'hourly_electricity_consumption': hourly_electricity_consumption} + peak_electricity_consumption = 0 + for building in energy_consumption_data: + peak_electricity_consumption += max(energy_consumption_data[building]['hourly_electricity_consumption']) heating_demand_monthly = [] cooling_demand_monthly = [] dhw_demand_monthly = [] @@ -54,14 +127,50 @@ def consumption_data(city): yearly_heating = 0 yearly_cooling = 0 yearly_dhw = 0 + yearly_appliance = 0 + yearly_lighting = 0 for building in city.buildings: - yearly_heating += building.heating_consumption[cte.YEAR][0] / 3.6e6 - yearly_cooling += building.cooling_consumption[cte.YEAR][0] / 3.6e6 - yearly_dhw += building.domestic_hot_water_consumption[cte.YEAR][0] / 3.6e6 + yearly_appliance += building.appliances_electrical_demand[cte.YEAR][0] / 3.6e9 + yearly_lighting += building.lighting_electrical_demand[cte.YEAR][0] / 3.6e9 + yearly_heating += building.heating_consumption[cte.YEAR][0] / 3.6e9 + yearly_cooling += building.cooling_consumption[cte.YEAR][0] / 3.6e9 + yearly_dhw += building.domestic_hot_water_consumption[cte.YEAR][0] / 3.6e9 - total_consumption = yearly_heating + yearly_cooling + yearly_dhw - current_status_energy_consumption_data['monthly_demands'] = monthly_demands - current_status_energy_consumption_data['monthly_consumptions'] = monthly_consumptions - current_status_energy_consumption_data['total_consumption'] = total_consumption + total_consumption = yearly_heating + yearly_cooling + yearly_dhw + yearly_appliance + yearly_lighting + energy_consumption_data['monthly_demands'] = monthly_demands + energy_consumption_data['monthly_consumptions'] = monthly_consumptions + energy_consumption_data['total_consumption'] = total_consumption + energy_consumption_data['maximum_hourly_electricity_consumption'] = peak_electricity_consumption - return current_status_energy_consumption_data + return energy_consumption_data + + +def cost_data(building, lcc_dataframe, cost_retrofit_scenario): + total_floor_area = 0 + for thermal_zone in building.thermal_zones_from_internal_zones: + total_floor_area += thermal_zone.total_floor_area + capital_cost = lcc_dataframe.loc['total_capital_costs_systems', f'Scenario {cost_retrofit_scenario}'] + operational_cost = lcc_dataframe.loc['total_operational_costs', f'Scenario {cost_retrofit_scenario}'] + maintenance_cost = lcc_dataframe.loc['total_maintenance_costs', f'Scenario {cost_retrofit_scenario}'] + end_of_life_cost = lcc_dataframe.loc['end_of_life_costs', f'Scenario {cost_retrofit_scenario}'] + operational_income = lcc_dataframe.loc['operational_incomes', f'Scenario {cost_retrofit_scenario}'] + total_life_cycle_cost = capital_cost + operational_cost + maintenance_cost + end_of_life_cost + operational_income + specific_capital_cost = capital_cost / total_floor_area + specific_operational_cost = operational_cost / total_floor_area + specific_maintenance_cost = maintenance_cost / total_floor_area + specific_end_of_life_cost = end_of_life_cost / total_floor_area + specific_operational_income = operational_income / total_floor_area + specific_life_cycle_cost = total_life_cycle_cost / total_floor_area + life_cycle_cost_analysis = {'capital_cost': capital_cost, + 'capital_cost_per_sqm': specific_capital_cost, + 'operational_cost': operational_cost, + 'operational_cost_per_sqm': specific_operational_cost, + 'maintenance_cost': maintenance_cost, + 'maintenance_cost_per_sqm': specific_maintenance_cost, + 'end_of_life_cost': end_of_life_cost, + 'end_of_life_cost_per_sqm': specific_end_of_life_cost, + 'operational_income': operational_income, + 'operational_income_per_sqm': specific_operational_income, + 'total_life_cycle_cost': total_life_cycle_cost, + 'total_life_cycle_cost_per_sqm': specific_life_cycle_cost} + return life_cycle_cost_analysis diff --git a/scripts/ep_run_enrich.py b/scripts/ep_run_enrich.py index 24ee4b11..68c24c8c 100644 --- a/scripts/ep_run_enrich.py +++ b/scripts/ep_run_enrich.py @@ -9,10 +9,10 @@ from hub.imports.results_factory import ResultFactory sys.path.append('./') -def energy_plus_workflow(city): +def energy_plus_workflow(city, output_path): try: # city = city - out_path = (Path(__file__).parent.parent / 'out_files') + out_path = output_path files = glob.glob(f'{out_path}/*') # for file in files: diff --git a/scripts/pv_feasibility.py b/scripts/pv_feasibility.py index 00488e39..034a5efb 100644 --- a/scripts/pv_feasibility.py +++ b/scripts/pv_feasibility.py @@ -9,10 +9,13 @@ from hub.exports.exports_factory import ExportsFactory def pv_feasibility(current_x, current_y, current_diff, selected_buildings): + input_files_path = (Path(__file__).parent.parent / 'input_files') + output_path = (Path(__file__).parent.parent / 'out_files').resolve() + sra_output_path = output_path / 'sra_outputs' / 'extended_city_sra_outputs' + sra_output_path.mkdir(parents=True, exist_ok=True) new_diff = current_diff * 5 geojson_file = process_geojson(x=current_x, y=current_y, diff=new_diff, expansion=True) - file_path = (Path(__file__).parent.parent / 'input_files' / 'output_buildings_expanded.geojson') - output_path = (Path(__file__).parent.parent / 'out_files').resolve() + file_path = input_files_path / 'output_buildings.geojson' city = GeometryFactory('geojson', path=file_path, height_field='height', @@ -20,10 +23,10 @@ def pv_feasibility(current_x, current_y, current_diff, selected_buildings): function_field='function', function_to_hub=Dictionaries().montreal_function_to_hub_function).city WeatherFactory('epw', city).enrich() - ExportsFactory('sra', city, output_path).export() - sra_path = (output_path / f'{city.name}_sra.xml').resolve() + ExportsFactory('sra', city, sra_output_path).export() + sra_path = (sra_output_path / f'{city.name}_sra.xml').resolve() subprocess.run(['sra', str(sra_path)]) - ResultFactory('sra', city, output_path).enrich() + ResultFactory('sra', city, sra_output_path).enrich() for selected_building in selected_buildings: for building in city.buildings: if selected_building.name == building.name: diff --git a/scripts/system_simulation_models/archetype13.py b/scripts/system_simulation_models/archetype13.py index 892b9f3f..77b52da6 100644 --- a/scripts/system_simulation_models/archetype13.py +++ b/scripts/system_simulation_models/archetype13.py @@ -377,8 +377,8 @@ class Archetype13: self._building.domestic_hot_water_consumption[cte.HOUR] = dhw_consumption self._building.domestic_hot_water_consumption[cte.MONTH] = ( MonthlyValues.get_total_month(self._building.domestic_hot_water_consumption[cte.HOUR])) - self._building.domestic_hot_water_consumption[cte.YEAR] = ( - sum(self._building.domestic_hot_water_consumption[cte.MONTH])) + self._building.domestic_hot_water_consumption[cte.YEAR] = [ + sum(self._building.domestic_hot_water_consumption[cte.MONTH])] file_name = f'energy_system_simulation_results_{self._name}.csv' with open(self._output_path / file_name, 'w', newline='') as csvfile: output_file = csv.writer(csvfile)