reorganized the exports factories

This commit is contained in:
Pilar 2022-11-25 12:34:11 -05:00
parent f387a4be4e
commit 07e2ee9773
14 changed files with 88 additions and 204 deletions

View File

@ -1,9 +1,7 @@
import numpy as np import numpy as np
from pathlib import Path 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 from imports.weather.helpers.weather import Weather
import helpers.constants as cte 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'): 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._radiation_calculation_method = radiation_calculation_method
self._weather_format = weather_format self._weather_format = weather_format
self._contents = [] self._contents = []
@ -41,26 +39,6 @@ class MonthlyEnergyBalance(Insel):
insel_file.write(content) insel_file.write(content)
return 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 @staticmethod
def generate_meb_template(building, insel_outputs_path, radiation_calculation_method, weather_format): def generate_meb_template(building, insel_outputs_path, radiation_calculation_method, weather_format):
file = "" file = ""
@ -174,7 +152,8 @@ class MonthlyEnergyBalance(Insel):
for i, surface in enumerate(surfaces): for i, surface in enumerate(surfaces):
i_block = 101 + i i_block = 101 + i
inputs = ['1 % Monthly surface radiation (W/sqm)'] 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': if surface.type != 'Ground':
global_irradiance = surface.global_irradiance[cte.MONTH] global_irradiance = surface.global_irradiance[cte.MONTH]

View File

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

View File

@ -6,8 +6,6 @@ Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
""" """
from pathlib import Path 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.obj import Obj
from exports.formats.simplified_radiosity_algorithm import SimplifiedRadiosityAlgorithm from exports.formats.simplified_radiosity_algorithm import SimplifiedRadiosityAlgorithm
from exports.formats.stl import Stl from exports.formats.stl import Stl
@ -38,14 +36,6 @@ class ExportsFactory:
def _collada(self): def _collada(self):
raise NotImplementedError raise NotImplementedError
@property
def _energy_ade(self):
"""
Export to citygml with application domain extensions
:return: None
"""
return EnergyAde(self._city, self._path)
@property @property
def _stl(self): def _stl(self):
""" """
@ -70,25 +60,6 @@ class ExportsFactory:
""" """
return Obj(self._city, self._path).to_ground_points() 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 @property
def _sra(self): def _sra(self):
""" """

View File

@ -6,16 +6,13 @@ Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
""" """
from abc import ABC from abc import ABC
import os
from pathlib import Path
class Insel(ABC): class Insel(ABC):
def __init__(self, city, path, keep_files=False): def __init__(self, city, path):
self._city = city self._city = city
self._path = path self._path = path
self._results = None self._results = None
self._keep_files = keep_files
@staticmethod @staticmethod
def _add_block(file, block_number, block_type, inputs='', parameters=''): def _add_block(file, block_number, block_type, inputs='', parameters=''):
@ -28,16 +25,6 @@ class Insel(ABC):
file += str(block_parameter) + "\n" file += str(block_parameter) + "\n"
return file 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): def _export(self):
raise NotImplementedError raise NotImplementedError

View File

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

View File

@ -9,7 +9,7 @@ from unittest import TestCase
from imports.geometry_factory import GeometryFactory from imports.geometry_factory import GeometryFactory
from imports.usage_factory import UsageFactory from imports.usage_factory import UsageFactory
from imports.construction_factory import ConstructionFactory from imports.construction_factory import ConstructionFactory
from exports.exports_factory import ExportsFactory from exports.energy_building_exports_factory import EnergyBuildingsExportsFactory
class TestBuildings(TestCase): class TestBuildings(TestCase):
@ -32,7 +32,7 @@ class TestBuildings(TestCase):
building.year_of_construction = 2006 building.year_of_construction = 2006
ConstructionFactory('nrel', city).enrich() ConstructionFactory('nrel', city).enrich()
UsageFactory('comnet', city).enrich() UsageFactory('comnet', city).enrich()
ExportsFactory('idf', city, output_path).export() EnergyBuildingsExportsFactory('idf', city, output_path).export()
self.assertEqual(1, len(city.buildings)) self.assertEqual(1, len(city.buildings))
for building in city.buildings: for building in city.buildings:

View File

@ -8,11 +8,9 @@ from pathlib import Path
from unittest import TestCase from unittest import TestCase
from numpy import inf from numpy import inf
from pyproj import Proj, transform
from imports.geometry_factory import GeometryFactory from imports.geometry_factory import GeometryFactory
from imports.construction_factory import ConstructionFactory from imports.construction_factory import ConstructionFactory
import geopandas
class TestGeometryFactory(TestCase): class TestGeometryFactory(TestCase):
@ -169,16 +167,3 @@ class TestGeometryFactory(TestCase):
self._check_surfaces(building) self._check_surfaces(building)
self.assertEqual(1912.0898135701814, building.volume) self.assertEqual(1912.0898135701814, building.volume)
self.assertEqual(146.19493345171213, building.floor_area) 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')

View File

@ -11,7 +11,7 @@ from unittest import TestCase
from imports.geometry_factory import GeometryFactory from imports.geometry_factory import GeometryFactory
from imports.usage_factory import UsageFactory from imports.usage_factory import UsageFactory
from imports.construction_factory import ConstructionFactory 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.vegetation import Vegetation
from city_model_structure.greenery.soil import Soil from city_model_structure.greenery.soil import Soil
from city_model_structure.greenery.plant import Plant from city_model_structure.greenery.plant import Plant
@ -66,7 +66,7 @@ class GreeneryInIdf(TestCase):
if surface.type == cte.ROOF: if surface.type == cte.ROOF:
surface.vegetation = vegetation surface.vegetation = vegetation
_idf_2 = ExportsFactory('idf', city, output_path).export_debug() _idf_2 = EnergyBuildingsExportsFactory('idf', city, output_path).export_debug()
_idf_2.run() _idf_2.run()
with open((output_path / f'{city.name}_out.csv').resolve()) as f: with open((output_path / f'{city.name}_out.csv').resolve()) as f:
reader = csv.reader(f, delimiter=',') reader = csv.reader(f, delimiter=',')
@ -85,7 +85,7 @@ class GreeneryInIdf(TestCase):
building.year_of_construction = 2006 building.year_of_construction = 2006
ConstructionFactory('nrel', city).enrich() ConstructionFactory('nrel', city).enrich()
UsageFactory('comnet', city).enrich() UsageFactory('comnet', city).enrich()
_idf = ExportsFactory('idf', city, output_path).export() _idf = EnergyBuildingsExportsFactory('idf', city, output_path).export()
_idf.run() _idf.run()
with open((output_path / f'{city.name}_out.csv').resolve()) as f: with open((output_path / f'{city.name}_out.csv').resolve()) as f:
reader = csv.reader(f, delimiter=',') reader = csv.reader(f, delimiter=',')

View File

@ -13,7 +13,7 @@ from helpers.monthly_values import MonthlyValues
from imports.geometry_factory import GeometryFactory from imports.geometry_factory import GeometryFactory
from imports.construction_factory import ConstructionFactory from imports.construction_factory import ConstructionFactory
from imports.usage_factory import UsageFactory 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 from imports.weather_factory import WeatherFactory
@ -77,7 +77,7 @@ class TestExports(TestCase):
month_new_value = MonthlyValues().get_mean_values(new_value) month_new_value = MonthlyValues().get_mean_values(new_value)
surface.global_irradiance[cte.MONTH] = month_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 export to Insel MonthlyEnergyBalance
""" """
@ -144,6 +144,6 @@ class TestExports(TestCase):
# export files # export files
try: try:
InselExportsFactory('monthly_energy_balance', city, self._output_path).export_debug() EnergyBuildingsExportsFactory('insel_monthly_energy_balance', city, self._output_path).export_debug()
except Exception: except Exception:
self.fail("Insel MonthlyEnergyBalance ExportsFactory raised ExceptionType unexpectedly!") self.fail("Insel MonthlyEnergyBalance ExportsFactory raised ExceptionType unexpectedly!")

View File

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