diff --git a/.idea/misc.xml b/.idea/misc.xml index dc7953c5..757b558a 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,7 @@ + + \ No newline at end of file diff --git a/hub/imports/construction/nrcan_physics_parameters.py b/hub/imports/construction/nrcan_physics_parameters.py index e57aa22e..e39774e7 100644 --- a/hub/imports/construction/nrcan_physics_parameters.py +++ b/hub/imports/construction/nrcan_physics_parameters.py @@ -32,10 +32,21 @@ class NrcanPhysicsParameters: city = self._city nrcan_catalog = ConstructionCatalogFactory('nrcan').catalog for building in city.buildings: - if building.function not in Dictionaries().hub_function_to_nrcan_construction_function: - logging.error('Building %s has an unknown building function %s', building.name, building.function) + main_function = None + functions = building.function.split('_') + if len(functions) > 1: + maximum_percentage = 0 + for function in functions: + percentage_and_function = function.split('-') + if float(percentage_and_function[0]) > maximum_percentage: + maximum_percentage = float(percentage_and_function[0]) + main_function = percentage_and_function[-1] + else: + main_function = functions[-1] + if main_function not in Dictionaries().hub_function_to_nrcan_construction_function: + logging.error('Building %s has an unknown building function %s', building.name, main_function) continue - function = Dictionaries().hub_function_to_nrcan_construction_function[building.function] + function = Dictionaries().hub_function_to_nrcan_construction_function[main_function] try: archetype = self._search_archetype(nrcan_catalog, function, building.year_of_construction, self._climate_zone) diff --git a/hub/imports/geometry/geojson.py b/hub/imports/geometry/geojson.py index b07a6a7b..3505c27a 100644 --- a/hub/imports/geometry/geojson.py +++ b/hub/imports/geometry/geojson.py @@ -127,6 +127,19 @@ class Geojson: function = None if self._function_field is not None: function = str(feature['properties'][self._function_field]) + if function == 'Mixed use' or function == 'mixed use': + function_parts = [] + for key, value in feature['properties'].items(): + if key.startswith("mixed_type_") and not key.endswith("_percentage"): + type_key = key + percentage_key = f"{key}_percentage" + if percentage_key in feature['properties']: + if self._function_to_hub is not None and feature['properties'][type_key] in self._function_to_hub: + usage_function = self._function_to_hub[feature['properties'][type_key]] + function_parts.append(f"{feature['properties'][percentage_key]}-{usage_function}") + else: + function_parts.append(f"{feature['properties'][percentage_key]}-{feature['properties'][type_key]}") + function = "_".join(function_parts) if self._function_to_hub is not None: # use the transformation dictionary to retrieve the proper function if function in self._function_to_hub: diff --git a/hub/imports/usage/comnet_usage_parameters.py b/hub/imports/usage/comnet_usage_parameters.py index 02f9b77e..08d8175c 100644 --- a/hub/imports/usage/comnet_usage_parameters.py +++ b/hub/imports/usage/comnet_usage_parameters.py @@ -35,29 +35,60 @@ class ComnetUsageParameters: city = self._city comnet_catalog = UsageCatalogFactory('comnet').catalog for building in city.buildings: - usage_name = Dictionaries().hub_usage_to_comnet_usage[building.function] - try: - archetype_usage = self._search_archetypes(comnet_catalog, usage_name) - except KeyError: - logging.error('Building %s has unknown usage archetype for usage %s', building.name, usage_name) - continue - - for internal_zone in building.internal_zones: - if internal_zone.area is None: - raise TypeError('Internal zone area not defined, ACH cannot be calculated') - if internal_zone.volume is None: - raise TypeError('Internal zone volume not defined, ACH cannot be calculated') - if internal_zone.area <= 0: - raise TypeError('Internal zone area is zero, ACH cannot be calculated') - volume_per_area = internal_zone.volume / internal_zone.area - usage = Usage() - usage.name = usage_name - self._assign_values(usage, archetype_usage, volume_per_area, building.cold_water_temperature) - usage.percentage = 1 - self._calculate_reduced_values_from_extended_library(usage, archetype_usage) - - internal_zone.usages = [usage] + usages = [] + comnet_archetype_usages = [] + building_functions = building.function.split('_') + for function in building_functions: + usages.append(function.split('-')) + for usage in usages: + comnet_usage_name = Dictionaries().hub_usage_to_comnet_usage[usage[-1]] + try: + comnet_archetype_usage = self._search_archetypes(comnet_catalog, comnet_usage_name) + comnet_archetype_usages.append(comnet_archetype_usage) + except KeyError: + logging.error('Building %s has unknown usage archetype for usage %s', building.name, comnet_usage_name) + continue + for (i, internal_zone) in enumerate(building.internal_zones): + internal_zone_usages = [] + if len(building.internal_zones) > 1: + volume_per_area = 0 + if internal_zone.area is None: + logging.error('Building %s has internal zone area not defined, ACH cannot be calculated for usage %s', + building.name, usages[i][-1]) + continue + if internal_zone.volume is None: + logging.error('Building %s has internal zone volume not defined, ACH cannot be calculated for usage %s', + building.name, usages[i][-1]) + continue + if internal_zone.area <= 0: + logging.error('Building %s has internal zone area equal to 0, ACH cannot be calculated for usage %s', + building.name, usages[i][-1]) + continue + volume_per_area += internal_zone.volume / internal_zone.area + usage = Usage() + usage.name = usages[i][-1] + self._assign_values(usage, comnet_archetype_usages[i], volume_per_area, building.cold_water_temperature) + usage.percentage = 1 + self._calculate_reduced_values_from_extended_library(usage, comnet_archetype_usages[i]) + internal_zone_usages.append(usage) + else: + if building.storeys_above_ground is None: + logging.error('Building %s no number of storeys assigned, ACH cannot be calculated for usage %s', + building.name, usages) + continue + volume_per_area = building.volume / building.floor_area / building.storeys_above_ground + for (j, mixed_usage) in enumerate(usages): + usage = Usage() + usage.name = mixed_usage[-1] + if len(usages) > 1: + usage.percentage = float(mixed_usage[0]) / 100 + else: + usage.percentage = 1 + self._assign_values(usage, comnet_archetype_usages[j], volume_per_area, building.cold_water_temperature) + self._calculate_reduced_values_from_extended_library(usage, comnet_archetype_usages[j]) + internal_zone_usages.append(usage) + internal_zone.usages = internal_zone_usages @staticmethod def _search_archetypes(comnet_catalog, usage_name): comnet_archetypes = comnet_catalog.entries('archetypes').usages diff --git a/hub/imports/usage/nrcan_usage_parameters.py b/hub/imports/usage/nrcan_usage_parameters.py index 9d718d33..4ff99a29 100644 --- a/hub/imports/usage/nrcan_usage_parameters.py +++ b/hub/imports/usage/nrcan_usage_parameters.py @@ -33,53 +33,72 @@ class NrcanUsageParameters: city = self._city nrcan_catalog = UsageCatalogFactory('nrcan').catalog comnet_catalog = UsageCatalogFactory('comnet').catalog - for building in city.buildings: - usage_name = Dictionaries().hub_usage_to_nrcan_usage[building.function] - try: - archetype_usage = self._search_archetypes(nrcan_catalog, usage_name) - except KeyError: - logging.error('Building %s has unknown usage archetype for usage %s', building.name, usage_name) - continue + usages = [] + nrcan_archetype_usages = [] + comnet_archetype_usages = [] + building_functions = building.function.split('_') + for function in building_functions: + usages.append(function.split('-')) + for usage in usages: + usage_name = Dictionaries().hub_usage_to_nrcan_usage[usage[-1]] + try: + archetype_usage = self._search_archetypes(nrcan_catalog, usage_name) + nrcan_archetype_usages.append(archetype_usage) + except KeyError: + logging.error('Building %s has unknown usage archetype for usage %s', building.name, usage_name) + continue + comnet_usage_name = Dictionaries().hub_usage_to_comnet_usage[usage[-1]] + try: + comnet_archetype_usage = self._search_archetypes(comnet_catalog, comnet_usage_name) + comnet_archetype_usages.append(comnet_archetype_usage) + except KeyError: + logging.error('Building %s has unknown usage archetype for usage %s', building.name, comnet_usage_name) + continue - comnet_usage_name = Dictionaries().hub_usage_to_comnet_usage[building.function] - try: - comnet_archetype_usage = self._search_archetypes(comnet_catalog, comnet_usage_name) - except KeyError: - logging.error('Building %s has unknown usage archetype for usage %s', building.name, comnet_usage_name) - continue - - for internal_zone in building.internal_zones: + for (i, internal_zone) in enumerate(building.internal_zones): + internal_zone_usages = [] if len(building.internal_zones) > 1: volume_per_area = 0 if internal_zone.area is None: logging.error('Building %s has internal zone area not defined, ACH cannot be calculated for usage %s', - building.name, usage_name) + building.name, usages[i][-1]) continue if internal_zone.volume is None: logging.error('Building %s has internal zone volume not defined, ACH cannot be calculated for usage %s', - building.name, usage_name) + building.name, usages[i][-1]) continue if internal_zone.area <= 0: logging.error('Building %s has internal zone area equal to 0, ACH cannot be calculated for usage %s', - building.name, usage_name) + building.name, usages[i][-1]) continue volume_per_area += internal_zone.volume / internal_zone.area + usage = Usage() + usage.name = usages[i][-1] + self._assign_values(usage, nrcan_archetype_usages[i], volume_per_area, building.cold_water_temperature) + self._assign_comnet_extra_values(usage, comnet_archetype_usages[i], nrcan_archetype_usages[i].occupancy.occupancy_density) + usage.percentage = 1 + self._calculate_reduced_values_from_extended_library(usage, nrcan_archetype_usages[i]) + internal_zone_usages.append(usage) else: if building.storeys_above_ground is None: logging.error('Building %s no number of storeys assigned, ACH cannot be calculated for usage %s', - building.name, usage_name) + building.name, usages) continue volume_per_area = building.volume / building.floor_area / building.storeys_above_ground + for (j, mixed_usage) in enumerate(usages): + usage = Usage() + usage.name = mixed_usage[-1] + if len(usages) > 1: + usage.percentage = float(mixed_usage[0]) / 100 + else: + usage.percentage = 1 + self._assign_values(usage, nrcan_archetype_usages[j], volume_per_area, building.cold_water_temperature) + self._assign_comnet_extra_values(usage, comnet_archetype_usages[j], nrcan_archetype_usages[j].occupancy.occupancy_density) + self._calculate_reduced_values_from_extended_library(usage, nrcan_archetype_usages[j]) + internal_zone_usages.append(usage) - usage = Usage() - usage.name = usage_name - self._assign_values(usage, archetype_usage, volume_per_area, building.cold_water_temperature) - self._assign_comnet_extra_values(usage, comnet_archetype_usage, archetype_usage.occupancy.occupancy_density) - usage.percentage = 1 - self._calculate_reduced_values_from_extended_library(usage, archetype_usage) - - internal_zone.usages = [usage] + internal_zone.usages = internal_zone_usages @staticmethod def _search_archetypes(catalog, usage_name): diff --git a/input_files/test_geojson.geojson b/input_files/test_geojson.geojson new file mode 100644 index 00000000..df9f0c4c --- /dev/null +++ b/input_files/test_geojson.geojson @@ -0,0 +1,55 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -73.58000127109773, + 45.49613461675315 + ], + [ + -73.57962787855432, + 45.496524875557746 + ], + [ + -73.57996357265695, + 45.49668114195629 + ], + [ + -73.57996427397713, + 45.496680342403664 + ], + [ + -73.58034707390021, + 45.49625804233725 + ], + [ + -73.58034697395713, + 45.496257942524835 + ], + [ + -73.58000127109773, + 45.49613461675315 + ] + ] + ] + }, + "id": 179764, + "properties": { + "name": "01119274", + "address": "rue Guy (MTL) 2157", + "function": "Mixed use", + "mixed_type_1": "commercial", + "mixed_type_1_percentage": 50, + "mixed_type_2": "6000", + "mixed_type_2_percentage": 50, + "height": 62, + "year_of_construction": 1954 + } + } + ] +} \ No newline at end of file diff --git a/main.py b/main.py index e69de29b..edce68d2 100644 --- a/main.py +++ b/main.py @@ -0,0 +1,86 @@ +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, 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, CURRENT_STATUS +import hub.helpers.constants as cte +from hub.exports.exports_factory import ExportsFactory +from scripts.pv_feasibility import pv_feasibility +import matplotlib.pyplot as plt +import numpy as np +# Specify the GeoJSON file path +data = {} +input_files_path = (Path(__file__).parent / 'input_files') +input_files_path.mkdir(parents=True, exist_ok=True) +# geojson_file = process_geojson(x=-73.58001358793511, y=45.496445294438715, diff=0.0001) +geojson_file_path = input_files_path / 'test_geojson.geojson' +output_path = (Path(__file__).parent / 'out_files').resolve() +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 +ConstructionFactory('nrcan', city).enrich() +UsageFactory('nrcan', city).enrich() +WeatherFactory('epw', city).enrich() +energy_plus_workflow(city, energy_plus_output_path) +data[f'{city.buildings[0].function}'] = city.buildings[0].heating_demand[cte.YEAR][0] / 3.6e9 +city.buildings[0].function = cte.COMMERCIAL +ConstructionFactory('nrcan', city).enrich() +UsageFactory('nrcan', city).enrich() +energy_plus_workflow(city, energy_plus_output_path) +data[f'{city.buildings[0].function}'] = city.buildings[0].heating_demand[cte.YEAR][0] / 3.6e9 +city.buildings[0].function = cte.MEDIUM_OFFICE +ConstructionFactory('nrcan', city).enrich() +UsageFactory('nrcan', city).enrich() +energy_plus_workflow(city, energy_plus_output_path) +data[f'{city.buildings[0].function}'] = city.buildings[0].heating_demand[cte.YEAR][0] / 3.6e9 +categories = list(data.keys()) +values = list(data.values()) +# Plotting +fig, ax = plt.subplots(figsize=(10, 6), dpi=96) +fig.suptitle('Impact of different usages on yearly heating demand', fontsize=16, weight='bold', alpha=.8) +ax.bar(categories, values, color=['#2196f3', '#ff5a5f', '#4caf50'], width=0.6, zorder=2) +ax.grid(which="major", axis='x', color='#DAD8D7', alpha=0.5, zorder=1) +ax.grid(which="major", axis='y', color='#DAD8D7', alpha=0.5, zorder=1) +ax.set_xlabel('Building Type', fontsize=12, labelpad=10) +ax.set_ylabel('Energy Consumption (MWh)', fontsize=14, labelpad=10) +ax.yaxis.set_major_locator(plt.MaxNLocator(integer=True)) +ax.set_xticks(np.arange(len(categories))) +ax.set_xticklabels(categories, rotation=45, ha='right') +ax.bar_label(ax.containers[0], padding=3, color='black', fontsize=12, rotation=0) +ax.spines[['top', 'left', 'bottom']].set_visible(False) +ax.spines['right'].set_linewidth(1.1) +# Set a white background +fig.patch.set_facecolor('white') +# Adjust the margins around the plot area +plt.subplots_adjust(left=0.1, right=0.9, top=0.85, bottom=0.25) +# Save the plot +plt.savefig('plot_nrcan.png', bbox_inches='tight') +plt.close() +print('test') \ No newline at end of file diff --git a/plot_nrcan.png b/plot_nrcan.png new file mode 100644 index 00000000..c780de3f Binary files /dev/null and b/plot_nrcan.png differ