diff --git a/exports/formats/energy_ade.py b/exports/building_energy/energy_ade.py similarity index 100% rename from exports/formats/energy_ade.py rename to exports/building_energy/energy_ade.py diff --git a/exports/formats/idf.py b/exports/building_energy/idf.py similarity index 100% rename from exports/formats/idf.py rename to exports/building_energy/idf.py diff --git a/exports/formats/idf_files/Energy+.idd b/exports/building_energy/idf_files/Energy+.idd similarity index 100% rename from exports/formats/idf_files/Energy+.idd rename to exports/building_energy/idf_files/Energy+.idd diff --git a/exports/formats/idf_files/Minimal.idf b/exports/building_energy/idf_files/Minimal.idf similarity index 100% rename from exports/formats/idf_files/Minimal.idf rename to exports/building_energy/idf_files/Minimal.idf diff --git a/exports/insel/templates/monthly_energy_balance.py b/exports/building_energy/insel/insel_monthly_energy_balance.py similarity index 88% rename from exports/insel/templates/monthly_energy_balance.py rename to exports/building_energy/insel/insel_monthly_energy_balance.py index 2186fa02..b2471cfd 100644 --- a/exports/insel/templates/monthly_energy_balance.py +++ b/exports/building_energy/insel/insel_monthly_energy_balance.py @@ -1,9 +1,7 @@ import numpy as np from pathlib import Path -import pandas as pd -import csv -from exports.insel.insel import Insel +from exports.formats.insel import Insel from imports.weather.helpers.weather import Weather import helpers.constants as cte @@ -18,10 +16,10 @@ _CONSTRUCTION_CODE = { } -class MonthlyEnergyBalance(Insel): +class InselMonthlyEnergyBalance(Insel): def __init__(self, city, path, radiation_calculation_method='sra', weather_format='epw'): - super().__init__(city, path, False) + super().__init__(city, path) self._radiation_calculation_method = radiation_calculation_method self._weather_format = weather_format self._contents = [] @@ -41,26 +39,6 @@ class MonthlyEnergyBalance(Insel): insel_file.write(content) return - def results(self, insel_outputs_paths): - monthly_heatings = [] - monthly_coolings = [] - for insel_outputs_path in insel_outputs_paths: - heating = [] - cooling = [] - with open(Path(insel_outputs_path).resolve()) as csv_file: - csv_reader = csv.reader(csv_file) - for line in csv_reader: - demand = str(line).replace("['", '').replace("']", '').split() - for i in range(0, 2): - if demand[i] != 'NaN': - aux = float(demand[i]) * 1000 # kWh to Wh - demand[i] = str(aux) - heating.append(demand[0]) - cooling.append(demand[1]) - monthly_heatings.append(pd.DataFrame(heating, columns=['INSEL'])) - monthly_coolings.append(pd.DataFrame(cooling, columns=['INSEL'])) - return monthly_heatings, monthly_coolings - @staticmethod def generate_meb_template(building, insel_outputs_path, radiation_calculation_method, weather_format): file = "" @@ -174,7 +152,8 @@ class MonthlyEnergyBalance(Insel): for i, surface in enumerate(surfaces): i_block = 101 + i inputs = ['1 % Monthly surface radiation (W/sqm)'] - parameters = [f'12 % Azimuth {np.rad2deg(surface.azimuth)}, inclination {np.rad2deg(surface.inclination)} degrees'] + parameters = [f'12 % Azimuth {np.rad2deg(surface.azimuth)}, ' + f'inclination {np.rad2deg(surface.inclination)} degrees'] if surface.type != 'Ground': global_irradiance = surface.global_irradiance[cte.MONTH] diff --git a/exports/energy_building_exports_factory.py b/exports/energy_building_exports_factory.py new file mode 100644 index 00000000..7b3be9fd --- /dev/null +++ b/exports/energy_building_exports_factory.py @@ -0,0 +1,74 @@ +""" +EnergyBuildingsExportsFactory exports a city into several formats related to energy in buildings +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2022 Concordia CERC group +Project Coder Pilar Monsalvete Alvarez de uribarri pilar.monsalvete@concordia.ca +""" + +from pathlib import Path +from exports.building_energy.energy_ade import EnergyAde +from exports.building_energy.idf import Idf +from exports.building_energy.insel.insel_monthly_energy_balance import InselMonthlyEnergyBalance + + +class EnergyBuildingsExportsFactory: + """ + Energy Buildings exports factory class + """ + def __init__(self, export_type, city, path, target_buildings=None, adjacent_buildings=None): + self._city = city + self._export_type = '_' + export_type.lower() + if isinstance(path, str): + path = Path(path) + self._path = path + self._target_buildings = target_buildings + self._adjacent_buildings = adjacent_buildings + + @property + def _energy_ade(self): + """ + Export to citygml with application domain extensions + :return: None + """ + return EnergyAde(self._city, self._path) + + @property + def _idf(self): + """ + Export the city to Energy+ idf format + + When target_buildings is set, only those will be calculated and their energy consumption output, non adjacent + buildings will be considered shading objects and adjacent buildings will be considered adiabatic. + + Adjacent buildings are provided they will be considered heated so energy plus calculations are more precise but + no results will be calculated to speed up the calculation process. + + :return: None + """ + idf_data_path = (Path(__file__).parent / './building_energy/idf_files/').resolve() + # todo: create a get epw file function based on the city + weather_path = (Path(__file__).parent / '../data/weather/epw/CAN_PQ_Montreal.Intl.AP.716270_CWEC.epw').resolve() + return Idf(self._city, self._path, (idf_data_path / 'Minimal.idf'), (idf_data_path / 'Energy+.idd'), weather_path, + target_buildings=self._target_buildings, adjacent_buildings=self._adjacent_buildings) + + @property + def _insel_monthly_energy_balance(self): + """ + Export to Insel MonthlyEnergyBalance + :return: None + """ + return InselMonthlyEnergyBalance(self._city, self._path) + + def export(self): + """ + Export the city given to the class using the given export type handler + :return: None + """ + return getattr(self, self._export_type, lambda: None) + + def export_debug(self): + """ + Export the city given to the class using the given export type handler + :return: None + """ + return getattr(self, self._export_type) diff --git a/exports/exports_factory.py b/exports/exports_factory.py index 4409545d..6c564f9f 100644 --- a/exports/exports_factory.py +++ b/exports/exports_factory.py @@ -6,8 +6,6 @@ Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca """ from pathlib import Path -from exports.formats.energy_ade import EnergyAde -from exports.formats.idf import Idf from exports.formats.obj import Obj from exports.formats.simplified_radiosity_algorithm import SimplifiedRadiosityAlgorithm from exports.formats.stl import Stl @@ -38,14 +36,6 @@ class ExportsFactory: def _collada(self): raise NotImplementedError - @property - def _energy_ade(self): - """ - Export to citygml with application domain extensions - :return: None - """ - return EnergyAde(self._city, self._path) - @property def _stl(self): """ @@ -70,25 +60,6 @@ class ExportsFactory: """ return Obj(self._city, self._path).to_ground_points() - @property - def _idf(self): - """ - Export the city to Energy+ idf format - - When target_buildings is set, only those will be calculated and their energy consumption output, non adjacent - buildings will be considered shading objects and adjacent buildings will be considered adiabatic. - - Adjacent buildings are provided they will be considered heated so energy plus calculations are more precise but - no results will be calculated to speed up the calculation process. - - :return: None - """ - idf_data_path = (Path(__file__).parent / './formats/idf_files/').resolve() - # todo: create a get epw file function based on the city - weather_path = (Path(__file__).parent / '../data/weather/epw/CAN_PQ_Montreal.Intl.AP.716270_CWEC.epw').resolve() - return Idf(self._city, self._path, (idf_data_path / 'Minimal.idf'), (idf_data_path / 'Energy+.idd'), weather_path, - target_buildings=self._target_buildings, adjacent_buildings=self._adjacent_buildings) - @property def _sra(self): """ diff --git a/exports/insel/insel.py b/exports/formats/insel.py similarity index 63% rename from exports/insel/insel.py rename to exports/formats/insel.py index 80529641..b4ff309e 100644 --- a/exports/insel/insel.py +++ b/exports/formats/insel.py @@ -6,16 +6,13 @@ Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca """ from abc import ABC -import os -from pathlib import Path class Insel(ABC): - def __init__(self, city, path, keep_files=False): + def __init__(self, city, path): self._city = city self._path = path self._results = None - self._keep_files = keep_files @staticmethod def _add_block(file, block_number, block_type, inputs='', parameters=''): @@ -28,16 +25,6 @@ class Insel(ABC): file += str(block_parameter) + "\n" return file - def run(self, insel_models): - for insel_model in insel_models: - finish = os.system('insel ' + str(Path(insel_model).resolve())) - os.close(finish) - if not self._keep_files: - os.remove((Path(self._path) / insel_model).resolve()) - - def results(self, insel_models): - raise NotImplementedError - def _export(self): raise NotImplementedError diff --git a/exports/insel_exports_factory.py b/exports/insel_exports_factory.py deleted file mode 100644 index 13d304a6..00000000 --- a/exports/insel_exports_factory.py +++ /dev/null @@ -1,43 +0,0 @@ -""" -InselExportsFactory export a city into several formats -SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2022 Concordia CERC group -Project Coder Pilar Monsalvete Alvarez de uribarri pilar.monsalvete@concordia.ca -""" - -from pathlib import Path -from exports.insel.templates.monthly_energy_balance import MonthlyEnergyBalance - - -class InselExportsFactory: - """ - Insel exports factory class - """ - def __init__(self, export_type, city, path): - self._city = city - self._export_type = '_' + export_type.lower() - if isinstance(path, str): - path = Path(path) - self._path = path - - @property - def _monthly_energy_balance(self): - """ - Export to Insel MonthlyEnergyBalance - :return: None - """ - return MonthlyEnergyBalance(self._city, self._path) - - def export(self): - """ - Export the city given to the class using the given export type handler - :return: None - """ - return getattr(self, self._export_type, lambda: None) - - def export_debug(self): - """ - Export the city given to the class using the given export type handler - :return: None - """ - return getattr(self, self._export_type) diff --git a/unittests/test_doe_idf.py b/unittests/test_doe_idf.py index 0afb40b9..5981eae0 100644 --- a/unittests/test_doe_idf.py +++ b/unittests/test_doe_idf.py @@ -9,7 +9,7 @@ from unittest import TestCase from imports.geometry_factory import GeometryFactory from imports.usage_factory import UsageFactory from imports.construction_factory import ConstructionFactory -from exports.exports_factory import ExportsFactory +from exports.energy_building_exports_factory import EnergyBuildingsExportsFactory class TestBuildings(TestCase): @@ -32,7 +32,7 @@ class TestBuildings(TestCase): building.year_of_construction = 2006 ConstructionFactory('nrel', city).enrich() UsageFactory('comnet', city).enrich() - ExportsFactory('idf', city, output_path).export() + EnergyBuildingsExportsFactory('idf', city, output_path).export() self.assertEqual(1, len(city.buildings)) for building in city.buildings: diff --git a/unittests/test_geometry_factory.py b/unittests/test_geometry_factory.py index 580fd207..888b8674 100644 --- a/unittests/test_geometry_factory.py +++ b/unittests/test_geometry_factory.py @@ -8,11 +8,9 @@ from pathlib import Path from unittest import TestCase from numpy import inf -from pyproj import Proj, transform from imports.geometry_factory import GeometryFactory from imports.construction_factory import ConstructionFactory -import geopandas class TestGeometryFactory(TestCase): @@ -169,16 +167,3 @@ class TestGeometryFactory(TestCase): self._check_surfaces(building) self.assertEqual(1912.0898135701814, building.volume) self.assertEqual(146.19493345171213, building.floor_area) - - # osm - def test_subway(self): - """ - Test subway parsing - :return: - """ - file_path = (self._example_path / 'subway.osm').resolve() - - city = GeometryFactory('osm_subway', path=file_path).city - - self.assertIsNotNone(city, 'subway entrances is none') - self.assertEqual(len(city.city_objects), 20, 'Wrong number of subway entrances') diff --git a/unittests/test_greenery_in_idf.py b/unittests/test_greenery_in_idf.py index fe251329..bef10a57 100644 --- a/unittests/test_greenery_in_idf.py +++ b/unittests/test_greenery_in_idf.py @@ -11,7 +11,7 @@ from unittest import TestCase from imports.geometry_factory import GeometryFactory from imports.usage_factory import UsageFactory from imports.construction_factory import ConstructionFactory -from exports.exports_factory import ExportsFactory +from exports.energy_building_exports_factory import EnergyBuildingsExportsFactory from city_model_structure.greenery.vegetation import Vegetation from city_model_structure.greenery.soil import Soil from city_model_structure.greenery.plant import Plant @@ -66,7 +66,7 @@ class GreeneryInIdf(TestCase): if surface.type == cte.ROOF: surface.vegetation = vegetation - _idf_2 = ExportsFactory('idf', city, output_path).export_debug() + _idf_2 = EnergyBuildingsExportsFactory('idf', city, output_path).export_debug() _idf_2.run() with open((output_path / f'{city.name}_out.csv').resolve()) as f: reader = csv.reader(f, delimiter=',') @@ -85,7 +85,7 @@ class GreeneryInIdf(TestCase): building.year_of_construction = 2006 ConstructionFactory('nrel', city).enrich() UsageFactory('comnet', city).enrich() - _idf = ExportsFactory('idf', city, output_path).export() + _idf = EnergyBuildingsExportsFactory('idf', city, output_path).export() _idf.run() with open((output_path / f'{city.name}_out.csv').resolve()) as f: reader = csv.reader(f, delimiter=',') diff --git a/unittests/test_insel_exports.py b/unittests/test_insel_exports.py index e5390b73..fbadd3d8 100644 --- a/unittests/test_insel_exports.py +++ b/unittests/test_insel_exports.py @@ -13,7 +13,7 @@ from helpers.monthly_values import MonthlyValues from imports.geometry_factory import GeometryFactory from imports.construction_factory import ConstructionFactory from imports.usage_factory import UsageFactory -from exports.insel_exports_factory import InselExportsFactory +from exports.energy_building_exports_factory import EnergyBuildingsExportsFactory from imports.weather_factory import WeatherFactory @@ -77,7 +77,7 @@ class TestExports(TestCase): month_new_value = MonthlyValues().get_mean_values(new_value) surface.global_irradiance[cte.MONTH] = month_new_value - def test_monthly_energy_balance_export(self): + def test_insel_monthly_energy_balance_export(self): """ export to Insel MonthlyEnergyBalance """ @@ -144,6 +144,6 @@ class TestExports(TestCase): # export files try: - InselExportsFactory('monthly_energy_balance', city, self._output_path).export_debug() + EnergyBuildingsExportsFactory('insel_monthly_energy_balance', city, self._output_path).export_debug() except Exception: self.fail("Insel MonthlyEnergyBalance ExportsFactory raised ExceptionType unexpectedly!") diff --git a/unittests/test_schedules_factory.py b/unittests/test_schedules_factory.py deleted file mode 100644 index 3d7f5180..00000000 --- a/unittests/test_schedules_factory.py +++ /dev/null @@ -1,69 +0,0 @@ -""" -TestSchedulesFactory test and validate the city model structure schedules -SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2022 Concordia CERC group -Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca -""" -from pathlib import Path -from unittest import TestCase - -from imports.geometry_factory import GeometryFactory -from imports.usage_factory import UsageFactory -from imports.construction_factory import ConstructionFactory -from imports.schedules_factory import SchedulesFactory -from imports.geometry.helpers.geometry_helper import GeometryHelper - - -class TestSchedulesFactory(TestCase): - """ - TestSchedulesFactory TestCase - """ - - def setUp(self) -> None: - """ - Configure test environment - :return: - """ - self._example_path = (Path(__file__).parent / 'tests_data').resolve() - - def _get_citygml(self, file): - file_path = (self._example_path / file).resolve() - _city = GeometryFactory('citygml', path=file_path).city - for building in _city.buildings: - building.year_of_construction = 2006 - ConstructionFactory('nrel', _city).enrich() - self.assertIsNotNone(_city, 'city is none') - for building in _city.buildings: - building.function = GeometryHelper.libs_function_from_hft(building.function) - building.year_of_construction = 2005 - UsageFactory('hft', _city).enrich() - return _city - - def test_doe_idf_archetypes(self): - """ - Enrich the city with doe_idf schedule archetypes and verify it - """ - file = (self._example_path / 'C40_Final.gml').resolve() - city = self._get_citygml(file) - occupancy_handler = 'doe_idf' - SchedulesFactory(occupancy_handler, city).enrich() - for building in city.buildings: - for internal_zone in building.internal_zones: - self.assertTrue(len(internal_zone.usage_zones) > 0) - for usage_zone in internal_zone.usage_zones: - self.assertIsNot(len(usage_zone.occupancy.occupancy_schedules), 0, 'no occupancy schedules defined') - for schedule in usage_zone.occupancy.occupancy_schedules: - self.assertIsNotNone(schedule.type) - self.assertIsNotNone(schedule.values) - self.assertIsNotNone(schedule.data_type) - self.assertIsNotNone(schedule.time_step) - self.assertIsNotNone(schedule.time_range) - self.assertIsNotNone(schedule.day_types) - self.assertIsNot(len(usage_zone.lighting.schedules), 0, 'no lighting schedules defined') - for schedule in usage_zone.lighting.schedules: - self.assertIsNotNone(schedule.type) - self.assertIsNotNone(schedule.values) - self.assertIsNotNone(schedule.data_type) - self.assertIsNotNone(schedule.time_step) - self.assertIsNotNone(schedule.time_range) - self.assertIsNotNone(schedule.day_types)