diff --git a/city_model_structure/building_demand/thermal_boundary.py b/city_model_structure/building_demand/thermal_boundary.py index 94f39e89..0e1d8ad2 100644 --- a/city_model_structure/building_demand/thermal_boundary.py +++ b/city_model_structure/building_demand/thermal_boundary.py @@ -16,9 +16,9 @@ class ThermalBoundary: """ ThermalBoundary class """ - def __init__(self, surface, delimits): + def __init__(self, surface, thermal_zones): self._surface = surface - self._delimits = delimits + self._thermal_zones = thermal_zones # ToDo: up to at least LOD2 will be just one thermal opening per Thermal boundary only if window_ratio > 0, # review for LOD3 and LOD4 self._thermal_openings = None @@ -50,12 +50,12 @@ class ThermalBoundary: return self._surface @property - def delimits(self) -> List[ThermalZone]: + def thermal_zones(self) -> List[ThermalZone]: """ Get the thermal zones delimited by the thermal boundary :return: [ThermalZone] """ - return self._delimits + return self._thermal_zones @property def azimuth(self): diff --git a/imports/geometry/citygml.py b/imports/geometry/citygml.py index a244db1a..10503e32 100644 --- a/imports/geometry/citygml.py +++ b/imports/geometry/citygml.py @@ -7,10 +7,10 @@ import numpy as np import xmltodict from city_model_structure.city import City from city_model_structure.building import Building +from city_model_structure.parts_consisting_building import PartsConsistingBuilding from helpers.geometry_helper import GeometryHelper from imports.geometry.citygml_classes.citygml_lod2 import CityGmlLod2 from imports.geometry.citygml_classes.citygml_lod1 import CityGmlLod1 -from city_model_structure.parts_consisting_building import PartsConsistingBuilding class CityGml: diff --git a/imports/geometry/citygml_classes/citygml_lod1.py b/imports/geometry/citygml_classes/citygml_lod1.py index 860a724a..dc7bc765 100644 --- a/imports/geometry/citygml_classes/citygml_lod1.py +++ b/imports/geometry/citygml_classes/citygml_lod1.py @@ -1,5 +1,6 @@ """ -CityGmlLod1 module parses citygml_classes files with level of detail 1 and import the geometry into the city model structure +CityGmlLod1 module parses citygml_classes files with level of detail 1 and import the geometry into the city model +structure SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2021 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca """ @@ -18,14 +19,6 @@ class CityGmlLod1(CityGmlBase): def _multi_curve(cls, o): pass - @classmethod - def _multi_surface(cls, o): - pass - - @classmethod - def _solid(cls, o): - pass - def __init__(self, o): super().__init__() self._o = o @@ -35,14 +28,16 @@ class CityGmlLod1(CityGmlBase): def _identify(cls, o): if 'lod1Solid' in o: return cls._solid(o) - elif 'lod1MultiSurface' in o: + if 'lod1MultiSurface' in o: return cls._multi_surface(o) + raise NotImplementedError(o) - @staticmethod - def _solid(o): + @classmethod + def _solid(cls, o): try: solid_points = [ - CityGmlBase._solid_points(CityGmlBase._remove_last_point(s['Polygon']['exterior']['LinearRing']['posList']['#text'])) + CityGmlBase._solid_points(CityGmlBase._remove_last_point(s['Polygon']['exterior']['LinearRing']['posList'] + ['#text'])) for s in o['lod1Solid']['Solid']['exterior']['CompositeSurface']['surfaceMember']] except TypeError: solid_points = [ @@ -51,9 +46,10 @@ class CityGmlLod1(CityGmlBase): return [Surface(Polygon(sp), Polygon(sp)) for sp in solid_points] - @staticmethod - def _multi_surface(o): + @classmethod + def _multi_surface(cls, o): print("lod1_multi") - solid_points = [CityGmlBase._solid_points(CityGmlBase._remove_last_point(s['Polygon']['exterior']['LinearRing']['posList'])) + solid_points = [CityGmlBase._solid_points(CityGmlBase._remove_last_point(s['Polygon']['exterior']['LinearRing'] + ['posList'])) for s in o['Building']['lod1MultiSurface']['MultiSurface']['surfaceMember']] - return [Surface(Polygon(sp), Polygon(sp)) for sp in solid_points] \ No newline at end of file + return [Surface(Polygon(sp), Polygon(sp)) for sp in solid_points] diff --git a/imports/geometry/citygml_classes/citygml_lod2.py b/imports/geometry/citygml_classes/citygml_lod2.py index c169d58b..11f5a7ca 100644 --- a/imports/geometry/citygml_classes/citygml_lod2.py +++ b/imports/geometry/citygml_classes/citygml_lod2.py @@ -1,13 +1,14 @@ """ -CityGmlLod1 module parses citygml_classes files with level of detail 1 and import the geometry into the city model structure +CityGmlLod1 module parses citygml_classes files with level of detail 1 and import the geometry into the city model +structure SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2021 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca """ from imports.geometry.citygml_classes.citygml_base import CityGmlBase +from imports.geometry.helpers.geometry_helper import GeometryHelper from city_model_structure.building_demand.surface import Surface from city_model_structure.attributes.polygon import Polygon -from imports.geometry.helpers.geometry_helper import GeometryHelper class CityGmlLod2(CityGmlBase): @@ -23,10 +24,11 @@ class CityGmlLod2(CityGmlBase): def _identify(cls, o): if 'lod2Solid' in o: return cls._solid(o) - elif 'lod2MultiSurface' in o: + if 'lod2MultiSurface' in o: return cls._multi_surface(o) - elif 'lod2MultiCurve' in o: + if 'lod2MultiCurve' in o: return cls._multi_curve(o) + raise NotImplementedError(o) @staticmethod def _surface_encoding(surfaces): diff --git a/imports/geometry/helpers/geometry_helper.py b/imports/geometry/helpers/geometry_helper.py index 384897d6..9c432e15 100644 --- a/imports/geometry/helpers/geometry_helper.py +++ b/imports/geometry/helpers/geometry_helper.py @@ -8,6 +8,9 @@ import helpers.constants as cte class GeometryHelper: + """ + Geometry helper + """ # function pluto_to_function = { 'A0': 'single family house', @@ -307,22 +310,13 @@ class GeometryHelper: points = points.reshape(rows, 3) return points - def almost_equal(self, delta_max, v1, v2): - """ - Compare two points and decides if they are almost equal (distance under delta_max) - :param delta_max: maximum distance to be considered same point - :param v1: [x,y,z] - :param v2: [x,y,z] - :return: Boolean - """ - delta = self.distance_between_points(v1, v2) - return delta <= delta_max - @staticmethod def gml_surface_to_libs(surface): + """ + Transform citygml surface names into libs names + """ if surface == 'WallSurface': return 'Wall' - elif surface == 'GroundSurface': + if surface == 'GroundSurface': return 'Ground' - else: - return 'Roof' \ No newline at end of file + return 'Roof' diff --git a/imports/geometry/obj.py b/imports/geometry/obj.py index 54ceae34..3c7a724f 100644 --- a/imports/geometry/obj.py +++ b/imports/geometry/obj.py @@ -21,8 +21,8 @@ class Obj: with open(path, 'r') as file: self._scene = trimesh.exchange.load.load(file, file_type='obj', force='scene') self._corners = self._scene.bounds_corners - _bound_corner_min = None - _bound_corner_max = None + _bound_corner_min = [] + _bound_corner_max = [] for corner in self._corners: if _bound_corner_min is None: _bound_corner_min = corner @@ -40,10 +40,16 @@ class Obj: @property def scene(self) -> Scene: + """ + Obj scene + """ return self._scene @property def city(self) -> City: + """ + Create a city out of an obj file + """ if self._city is None: # todo: refactor this method to clearly choose the obj type # todo: where do we get this information from? diff --git a/imports/geometry/osm_subway.py b/imports/geometry/osm_subway.py index 937493ae..82349182 100644 --- a/imports/geometry/osm_subway.py +++ b/imports/geometry/osm_subway.py @@ -4,12 +4,19 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca """ +import sys import xmltodict +from pyproj import Transformer +from city_model_structure.city import City from city_model_structure.subway_entrance import SubwayEntrance class OsmSubway: + """ + Open street map subway + """ def __init__(self, path): + self._city = None self._subway_entrances = [] with open(path) as osm: self._osm = xmltodict.parse(osm.read(), force_list='tag') @@ -24,5 +31,24 @@ class OsmSubway: self._subway_entrances.append(subway_entrance) @property - def subway_entrances(self): - return self._subway_entrances + def city(self) -> City: + """ + City subway entrances + """ + transformer = Transformer.from_crs("EPSG:4326", "EPSG:3857") + lower_corner = [sys.float_info.max, sys.float_info.max, 0] + upper_corner = [sys.float_info.min, sys.float_info.min, 0] + x = 0 + y = 1 + for subway_entrance in self._subway_entrances: + coordinate = transformer.transform(subway_entrance.longitude, subway_entrance.latitude) + if coordinate[x] >= upper_corner[x]: + upper_corner[x] = coordinate[x] + if coordinate[y] >= upper_corner[y]: + upper_corner[y] = coordinate[y] + if coordinate[x] < lower_corner[x]: + lower_corner[x] = coordinate[x] + if coordinate[y] < lower_corner[y]: + lower_corner[y] = coordinate[y] + + return City(lower_corner, upper_corner, 'unknown') diff --git a/imports/geometry_factory.py b/imports/geometry_factory.py index a3d3677a..3c18392a 100644 --- a/imports/geometry_factory.py +++ b/imports/geometry_factory.py @@ -3,13 +3,11 @@ GeometryFactory retrieve the specific geometric module to load the given format SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca """ -from trimesh.scene import Scene from city_model_structure.city import City -from city_model_structure.city_object import CityObject from imports.geometry.citygml import CityGml -from imports.geometry.osm_subway import OsmSubway from imports.geometry.obj import Obj +from imports.geometry.osm_subway import OsmSubway class GeometryFactory: @@ -24,21 +22,13 @@ class GeometryFactory: def _citygml(self): return CityGml(self._path).city - @property - def _stl(self): - raise Exception('Not implemented') - @property def _obj(self): - return Obj(self._path).scene + return Obj(self._path).city @property - def _geojson(self): - raise Exception('Not implemented') - - @property - def _bim(self): - raise Exception('Not implemented') + def _osm_subway(self): + return OsmSubway(self._path).city @property def city(self) -> City: @@ -47,37 +37,3 @@ class GeometryFactory: :return: City """ return getattr(self, self._file_type, lambda: None) - - @property - def scene(self) -> Scene: - """ - Load the city model structure from a geometry source - :return: Trimesh scene - """ - return getattr(self, self._file_type, lambda: None) - - @property - def _scene_debug(self): - """ - just for debug. More information is provided without the lambda parameter - """ - return getattr(self, self._file_type) - - @property - def _city_debug(self): - """ - just for debug. More information is provided without the lambda parameter - """ - return CityGml(self._path).city - - @property - def _osm_subway(self): - return OsmSubway(self._path).subway_entrances - - @property - def features(self) -> [CityObject]: - """ - Load the city model structure from a geometry source - :return: [CityObject] - """ - return getattr(self, self._file_type, lambda: None) diff --git a/imports/schedules/comnet_schedules_parameters.py b/imports/schedules/comnet_schedules_parameters.py index b4dbae41..61964e02 100644 --- a/imports/schedules/comnet_schedules_parameters.py +++ b/imports/schedules/comnet_schedules_parameters.py @@ -9,7 +9,9 @@ from imports.schedules.helpers.schedules_helper import SchedulesHelper class ComnetSchedules: - + """ + Commet based schedules + """ def __init__(self, city, base_path): self._city = city self._comnet_schedules_path = base_path / 'comnet_archetypes.xlsx' diff --git a/imports/schedules/doe_idf.py b/imports/schedules/doe_idf.py index 88522509..13ba9a36 100644 --- a/imports/schedules/doe_idf.py +++ b/imports/schedules/doe_idf.py @@ -23,12 +23,12 @@ class DoeIdf: def __init__(self, city, base_path): self._hours = [] panda_hours = pd.timedelta_range(0, periods=24, freq='H') - for i, hour in enumerate(panda_hours): + for _, hour in enumerate(panda_hours): self._hours.append(str(hour).replace('0 days ', '').replace(':00:00', ':00')) self._city = city self._idf_schedules_path = base_path / 'ASHRAE901_OfficeSmall_STD2019_Buffalo.idf' - with open(self._idf_schedules_path, 'r') as f: - idf = parseidf.parse(f.read()) + with open(self._idf_schedules_path, 'r') as file: + idf = parseidf.parse(file.read()) self._load_schedule(idf, 'small_office') self._load_schedule(idf, 'residential') @@ -69,9 +69,8 @@ class DoeIdf: if hour == hour_formatted: hour_index += index break - else: - entry = days_schedules[hours_values + 1] - schedules_day[f'{days_schedules[day_index]}'].append(entry) + entry = days_schedules[hours_values + 1] + schedules_day[f'{days_schedules[day_index]}'].append(entry) if 'Weekdays' in days_schedules[day_index]: data['WD'] = [] @@ -101,9 +100,9 @@ class DoeIdf: data['Sat'].append(entry) elif 'Sunday' in days_schedules[day_index]: - data['Sun'] = [] - for entry in schedules_day[f'{days_schedules[day_index]}']: - data['Sun'].append(entry) + data['Sun'] = [] + for entry in schedules_day[f'{days_schedules[day_index]}']: + data['Sun'].append(entry) else: continue df = pd.DataFrame(data, index=rows) @@ -113,4 +112,3 @@ class DoeIdf: if usage_zone.schedules is None: usage_zone.schedules = {} usage_zone.schedules[schedule_type] = df - diff --git a/imports/schedules/helpers/schedules_helper.py b/imports/schedules/helpers/schedules_helper.py index ed5524de..75233cd9 100644 --- a/imports/schedules/helpers/schedules_helper.py +++ b/imports/schedules/helpers/schedules_helper.py @@ -9,6 +9,9 @@ import helpers.constants as cte class SchedulesHelper: + """ + Schedules helper + """ usage_to_comnet = { cte.RESIDENTIAL: 'C-12 Residential', cte.INDUSTRY: 'C-10 Warehouse', diff --git a/imports/sensors/concordia_energy_consumption.py b/imports/sensors/concordia_energy_consumption.py index ad8cd9aa..0b006045 100644 --- a/imports/sensors/concordia_energy_consumption.py +++ b/imports/sensors/concordia_energy_consumption.py @@ -9,7 +9,9 @@ from city_model_structure.iot.concordia_energy_sensor import ConcordiaEnergySens class ConcordiaEnergyConsumption(ConcordiaFileReport): - + """ + Concordia energy consumption sensor class + """ def __init__(self, city, end_point, base_path): super().__init__(city, end_point, base_path, 'concordia_energy_db.json') for city_object in city.city_objects: diff --git a/imports/sensors/concordia_file_report.py b/imports/sensors/concordia_file_report.py index 453d77fc..6f360f86 100644 --- a/imports/sensors/concordia_file_report.py +++ b/imports/sensors/concordia_file_report.py @@ -11,8 +11,10 @@ import pandas as pd class ConcordiaFileReport: + """ + Concordia file report for sensors base class + """ def __init__(self, city, end_point, base_path, db_file): - self._city_object = [] self._city_objects_cluster = [] self._sensors = [] @@ -34,7 +36,7 @@ class ConcordiaFileReport: buffer = "" with open(end_point.resolve()) as data: for line in data: - line = ConcordiaFileReport.clean_line(line) + line = ConcordiaFileReport._clean_line(line) if metadata: fields = line.split(',') if len(fields) > 2: @@ -45,34 +47,33 @@ class ConcordiaFileReport: if "End of Report" in line: content = False if content: - line = ConcordiaFileReport.merge_date_time(line) + line = ConcordiaFileReport._merge_date_time(line) buffer = buffer + line + '\n' - if line is '': + if line == '': metadata = False content = True measures = pd.read_csv(io.StringIO(buffer), sep=',') measures["Date time"] = pd.to_datetime(measures["Date time"]) - self._measures = ConcordiaFileReport.force_format(measures) + self._measures = ConcordiaFileReport._force_format(measures) @staticmethod - def clean_line(line): + def _clean_line(line): return line.replace('"', '').replace('\n', '') @staticmethod - def merge_date_time(line): + def _merge_date_time(line): fields = line.split(',') date = fields[0] time = fields[1] if '<>' in date: return line.replace(f'{date},{time}', 'Date time') - else: - date_fields = date.split('/') - format_date_time = f'"{int(date_fields[2])}-{int(date_fields[0]):02d}-{int(date_fields[1]):02d} {time}"' - return line.replace(f'{date},{time}', format_date_time) + date_fields = date.split('/') + format_date_time = f'"{int(date_fields[2])}-{int(date_fields[0]):02d}-{int(date_fields[1]):02d} {time}"' + return line.replace(f'{date},{time}', format_date_time) @staticmethod - def force_format(df): + def _force_format(df): for head in df.head(): if 'Date time' not in head: df = df.astype({head: 'float64'}) - return df \ No newline at end of file + return df diff --git a/imports/sensors/concordia_gas_flow.py b/imports/sensors/concordia_gas_flow.py index e88c4832..e9176fc7 100644 --- a/imports/sensors/concordia_gas_flow.py +++ b/imports/sensors/concordia_gas_flow.py @@ -9,6 +9,9 @@ from city_model_structure.iot.concordia_gas_flow_sensor import ConcordiaGasFlowS class ConcordiaGasFlow(ConcordiaFileReport): + """ + Concordia gas flow sensor class + """ def __init__(self, city, end_point, base_path): super().__init__(city, end_point, base_path, 'concordia_gas_flow_db.json') diff --git a/imports/sensors/concordia_temperature.py b/imports/sensors/concordia_temperature.py index a796955c..f4b937a8 100644 --- a/imports/sensors/concordia_temperature.py +++ b/imports/sensors/concordia_temperature.py @@ -9,7 +9,9 @@ from city_model_structure.iot.concordia_temperature_sensor import ConcordiaTempe class ConcordiaTemperature(ConcordiaFileReport): - + """ + Concordia temperature sensor class + """ def __init__(self, city, end_point, base_path): super().__init__(city, end_point, base_path, 'concordia_temperature_db.json') for city_object in city.city_objects: diff --git a/imports/usage/ca_usage_parameters.py b/imports/usage/ca_usage_parameters.py index 32006228..1bbe5d58 100644 --- a/imports/usage/ca_usage_parameters.py +++ b/imports/usage/ca_usage_parameters.py @@ -58,12 +58,12 @@ class CaUsageParameters(HftUsageInterface): # usage_zone.internal_gains when writing usage_zone.internal_gains = archetype.internal_gains. # Therefore, this walk around has been done. internal_gains = [] - for ig in archetype.internal_gains: + for archetype_internal_gain in archetype.internal_gains: internal_gain = InternalGains() - internal_gain.average_internal_gain = ig.average_internal_gain - internal_gain.convective_fraction = ig.convective_fraction - internal_gain.radiative_fraction = ig.radiative_fraction - internal_gain.latent_fraction = ig.latent_fraction + internal_gain.average_internal_gain = archetype_internal_gain.average_internal_gain + internal_gain.convective_fraction = archetype_internal_gain.convective_fraction + internal_gain.radiative_fraction = archetype_internal_gain.radiative_fraction + internal_gain.latent_fraction = archetype_internal_gain.latent_fraction internal_gains.append(internal_gain) usage_zone.internal_gains = internal_gains usage_zone.heating_setpoint = archetype.heating_setpoint diff --git a/imports/usage/helpers/usage_helper.py b/imports/usage/helpers/usage_helper.py index 1657d128..ffd87e23 100644 --- a/imports/usage/helpers/usage_helper.py +++ b/imports/usage/helpers/usage_helper.py @@ -8,6 +8,9 @@ import helpers.constants as cte class UsageHelper: + """ + Usage helpre class + """ usage_to_hft = { cte.RESIDENTIAL: 'residential', cte.INDUSTRY: 'industry', diff --git a/imports/usage/hft_usage_interface.py b/imports/usage/hft_usage_interface.py index 159cdfed..b6223295 100644 --- a/imports/usage/hft_usage_interface.py +++ b/imports/usage/hft_usage_interface.py @@ -13,6 +13,7 @@ class HftUsageInterface: """ HftUsageInterface abstract class """ + def __init__(self, base_path, usage_file='ca_library_reduced.xml'): path = str(base_path / usage_file) self._usage_archetypes = [] @@ -44,17 +45,17 @@ class HftUsageInterface: if 'domestic_hot_water' in zone_usage_type['endUses']: # liters to cubic meters dhw_average_volume_pers_day = float( - zone_usage_type['endUses']['domestic_hot_water']['averageVolumePerPersAndDay'])/1000 + zone_usage_type['endUses']['domestic_hot_water']['averageVolumePerPersAndDay']) / 1000 dhw_preparation_temperature = zone_usage_type['endUses']['domestic_hot_water']['preparationTemperature'] electrical_app_average_consumption_sqm_year = None if 'all_electrical_appliances' in zone_usage_type['endUses']: - if 'averageConsumptionPerSqmAndYear' in zone_usage_type['endUses']['all_electrical_appliances']: - # kWh to J - electrical_app_average_consumption_sqm_year = \ - float(zone_usage_type['endUses']['all_electrical_appliances']['averageConsumptionPerSqmAndYear'])/3.6 + if 'averageConsumptionPerSqmAndYear' in zone_usage_type['endUses']['all_electrical_appliances']: + # kWh to J + electrical_app_average_consumption_sqm_year = \ + float(zone_usage_type['endUses']['all_electrical_appliances']['averageConsumptionPerSqmAndYear']) / 3.6 -# todo: for internal_gain in usage_zone_variant['schedules']['internGains']:???????????????? -# There are no more internal gains? How is it saved when more than one??? + # todo: for internal_gain in usage_zone_variant['schedules']['internGains']:???????????????? + # There are no more internal gains? How is it saved when more than one??? internal_gains = [] if 'internGains' in zone_usage_type['occupancy']: latent_fraction = zone_usage_type['occupancy']['internGains']['latentFraction'] @@ -93,7 +94,7 @@ class HftUsageInterface: # todo: for internal_gain in usage_zone_variant['schedules']['internGains']:???????????????? # There are no more internal gains? How is it saved when more than one??? -# for internal_gain in usage_zone.internal_gains: + # for internal_gain in usage_zone.internal_gains: internal_gains = usage_zone.internal_gains[0] latent_fraction = internal_gains.latent_fraction convective_fraction = internal_gains.convective_fraction @@ -111,14 +112,15 @@ class HftUsageInterface: if 'ventilation' in usage_zone_variant['endUses'] and usage_zone_variant['endUses']['ventilation'] is not None: if 'mechanicalAirChangeRate' in usage_zone_variant['endUses']['ventilation']: mechanical_air_change = usage_zone_variant['endUses']['ventilation']['mechanicalAirChangeRate'] -# todo: for internal_gain in usage_zone_variant['schedules']['internGains']:???????????????? -# There are no more internal gains? How is it saved when more than one??? + # todo: for internal_gain in usage_zone_variant['schedules']['internGains']:???????????????? + # There are no more internal gains? How is it saved when more than one??? if 'schedules' in usage_zone_variant: if 'usageHoursPerDay' in usage_zone_variant['schedules']: hours_day = usage_zone_variant['schedules']['usageHoursPerDay'] if 'usageDaysPerYear' in usage_zone_variant['schedules']: days_year = usage_zone_variant['schedules']['usageDaysPerYear'] - if 'internalGains' in usage_zone_variant['schedules'] and usage_zone_variant['schedules']['internGains'] is not None: + if 'internalGains' in usage_zone_variant['schedules'] and usage_zone_variant['schedules'][ + 'internGains'] is not None: internal_gains = [] if 'latentFraction' in usage_zone_variant['schedules']['internGains']: latent_fraction = usage_zone_variant['schedules']['internGains']['latentFraction'] @@ -137,6 +139,3 @@ class HftUsageInterface: electrical_app_average_consumption_sqm_year=electrical_app_average_consumption_sqm_year, mechanical_air_change=mechanical_air_change) return usage_zone_archetype - - def enrich_buildings(self): - raise NotImplementedError diff --git a/imports/usage/hft_usage_parameters.py b/imports/usage/hft_usage_parameters.py index 40e79369..fc254aa2 100644 --- a/imports/usage/hft_usage_parameters.py +++ b/imports/usage/hft_usage_parameters.py @@ -58,12 +58,12 @@ class HftUsageParameters(HftUsageInterface): # usage_zone.internal_gains when writing usage_zone.internal_gains = archetype.internal_gains. # Therefore, this walk around has been done. internal_gains = [] - for ig in archetype.internal_gains: + for archetype_internal_gain in archetype.internal_gains: internal_gain = InternalGains() - internal_gain.average_internal_gain = ig.average_internal_gain - internal_gain.convective_fraction = ig.convective_fraction - internal_gain.radiative_fraction = ig.radiative_fraction - internal_gain.latent_fraction = ig.latent_fraction + internal_gain.average_internal_gain = archetype_internal_gain.average_internal_gain + internal_gain.convective_fraction = archetype_internal_gain.convective_fraction + internal_gain.radiative_fraction = archetype_internal_gain.radiative_fraction + internal_gain.latent_fraction = archetype_internal_gain.latent_fraction internal_gains.append(internal_gain) usage_zone.internal_gains = internal_gains usage_zone.heating_setpoint = archetype.heating_setpoint diff --git a/imports/weather/dat_weather_parameters.py b/imports/weather/dat_weather_parameters.py index 46011462..3554090a 100644 --- a/imports/weather/dat_weather_parameters.py +++ b/imports/weather/dat_weather_parameters.py @@ -4,9 +4,9 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca """ -import pandas as pd -from pathlib import Path import sys +from pathlib import Path +import pandas as pd import helpers.constants as cte @@ -14,6 +14,7 @@ class DatWeatherParameters: """ DatWeatherParameters class """ + def __init__(self, city, path): self._weather_values = None self._city = city @@ -23,7 +24,7 @@ class DatWeatherParameters: if self._weather_values is None: try: - self._weather_values = pd.read_csv(self._path, sep='\s+', header=None, + self._weather_values = pd.read_csv(self._path, sep=r'\s+', header=None, names=['hour', 'global_horiz', 'temperature', 'diffuse', 'beam', 'empty']) except SystemExit: sys.stderr.write(f'Error: weather file {self._path} not found\n') diff --git a/imports/weather/epw_weather_parameters.py b/imports/weather/epw_weather_parameters.py index 5c2af54f..89d7dab0 100644 --- a/imports/weather/epw_weather_parameters.py +++ b/imports/weather/epw_weather_parameters.py @@ -4,9 +4,9 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca """ -import pandas as pd -from pathlib import Path import sys +from pathlib import Path +import pandas as pd import helpers.constants as cte @@ -41,7 +41,7 @@ class EpwWeatherParameters: file.close() except SystemExit: sys.stderr.write(f'Error: weather file {self._path} not found. Please download it from ' - f'https://energyplus.net/weather and place it in folder data\weather\epw\n') + f'https://energyplus.net/weather and place it in folder data\\weather\\epw\n') sys.exit() try: diff --git a/imports/weather/helpers/weather.py b/imports/weather/helpers/weather.py index 1ec682b8..312c42ba 100644 --- a/imports/weather/helpers/weather.py +++ b/imports/weather/helpers/weather.py @@ -7,7 +7,7 @@ import math import helpers.constants as cte -class Weather(object): +class Weather: """ Weather class """ diff --git a/imports/weather/xls_weather_parameters.py b/imports/weather/xls_weather_parameters.py index 476870bb..93e92385 100644 --- a/imports/weather/xls_weather_parameters.py +++ b/imports/weather/xls_weather_parameters.py @@ -5,9 +5,9 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca """ -import pandas as pd -from pathlib import Path import sys +from pathlib import Path +import pandas as pd import helpers.constants as cte diff --git a/non_functional_tests/test_C40.py b/non_functional_tests/test_c40.py similarity index 72% rename from non_functional_tests/test_C40.py rename to non_functional_tests/test_c40.py index 1b73338b..a28a1dba 100644 --- a/non_functional_tests/test_C40.py +++ b/non_functional_tests/test_c40.py @@ -11,7 +11,7 @@ from imports.weather_factory import WeatherFactory from exports.exports_factory import ExportsFactory -class MyTestCase(TestCase): +class TestC40(TestCase): """ C40 TestCase 1 """ @@ -31,26 +31,29 @@ class MyTestCase(TestCase): return self._city_gml def test_c40_enrichment(self): + """ + Enrich the C40 building + """ file = 'C40_Final.gml' base_path = (Path(__file__).parent.parent / 'data' / 'weather').resolve() weather_file_name = 'CAN_PQ_Montreal.Intl.AP.716270_CWEC.epw' city = self._get_citygml(file) for building in city.buildings: - for tz in building.thermal_zones: - for tb in tz.thermal_boundaries: - tb.hi = 10 - tb.he = 25 - for opening in tb.thermal_openings: - opening.hi = 10 - opening.he = 25 + for thermal_zone in building.thermal_zones: + for thermal_boundary in thermal_zone.thermal_boundaries: + thermal_boundary.hi = 10 + thermal_boundary.he = 25 + for thermal_opening in thermal_boundary.thermal_openings: + thermal_opening.hi = 10 + thermal_opening.he = 25 building.function = 'residential' ConstructionFactory('nrel', city).enrich() for building in city.buildings: print(building.name, building.function, len(building.surfaces)) print(building.volume) - for tz in building.thermal_zones: - print(tz.volume) - print(tz.floor_area) + for thermal_zone in building.thermal_zones: + print(thermal_zone.volume) + print(thermal_zone.floor_area) WeatherFactory('epw', city, base_path=base_path, file_name=weather_file_name).enrich() - ExportsFactory('idf', city, 'c:\Documents\idf').export() + ExportsFactory('idf', city, r'c:\Documents\idf').export() diff --git a/non_functional_tests/test_construction_factory.py b/non_functional_tests/test_construction_factory.py index c308d09d..50fdd130 100644 --- a/non_functional_tests/test_construction_factory.py +++ b/non_functional_tests/test_construction_factory.py @@ -40,13 +40,13 @@ class TestConstructionFactory(TestCase): city = self._get_citygml(file) for building in city.buildings: building.function = GeometryHelper.pluto_to_function[building.function] - for tz in building.thermal_zones: - for tb in tz.thermal_boundaries: - tb.hi = 10 - tb.he = 25 - for opening in tb.thermal_openings: - opening.hi = 10 - opening.he = 25 + for thermal_zone in building.thermal_zones: + for thermal_boundary in thermal_zone.thermal_boundaries: + thermal_boundary.hi = 10 + thermal_boundary.he = 25 + for thermal_opening in thermal_boundary.thermal_openings: + thermal_opening.hi = 10 + thermal_opening.he = 25 # case 1: NREL ConstructionFactory('nrel', city).enrich() for building in city.buildings: @@ -62,12 +62,15 @@ class TestConstructionFactory(TestCase): self.assertIsNotNone(thermal_zone.infiltration_rate_system_off, 'infiltration_rate_system_off is none') self.assertIsNotNone(thermal_zone.thermal_boundaries, 'thermal_boundaries is none') for thermal_boundary in thermal_zone.thermal_boundaries: - if thermal_boundary.surface.type is not 'Ground': + if thermal_boundary.surface.type != 'Ground': self.assertIsNotNone(thermal_boundary.outside_solar_absorptance, 'outside_solar_absorptance is none') self.assertIsNotNone(thermal_boundary.window_ratio, 'window_ratio is none') print(thermal_boundary.layers) def test_city_with_construction_reduced_library(self): + """ + Enrich the city with the construction reduced library and verify it + """ file = 'one_building_in_kelowna.gml' city = self._get_citygml(file) for building in city.buildings: diff --git a/non_functional_tests/test_exports.py b/non_functional_tests/test_exports.py index 5bc4a8d0..5b25b4e1 100644 --- a/non_functional_tests/test_exports.py +++ b/non_functional_tests/test_exports.py @@ -64,13 +64,25 @@ class TestExports(TestCase): ExportsFactory(export_type, self._complete_city, self._output_path).export() def test_obj_export(self): + """ + export to obj + """ self._export('obj', False) def test_stl_export(self): + """ + export to stl + """ self._export('stl', False) def test_energy_ade_export(self): + """ + export to energy ADE + """ self._export('energy_ade') def test_sra_export(self): + """ + export to SRA + """ self._export('sra') diff --git a/non_functional_tests/test_geometry_factory.py b/non_functional_tests/test_geometry_factory.py index bdbdf1df..38f131bb 100644 --- a/non_functional_tests/test_geometry_factory.py +++ b/non_functional_tests/test_geometry_factory.py @@ -7,6 +7,7 @@ from pathlib import Path from unittest import TestCase from imports.geometry_factory import GeometryFactory from imports.geometry.helpers.geometry_helper import GeometryHelper +from city_model_structure.building_demand.thermal_opening import ThermalOpening class TestGeometryFactory(TestCase): @@ -156,6 +157,7 @@ class TestGeometryFactory(TestCase): for thermal_zone in building.thermal_zones: self.assertIsNot(len(thermal_zone.thermal_boundaries), 0, 'no building thermal_boundaries defined') for thermal_boundary in thermal_zone.thermal_boundaries: + thermal_opening: ThermalOpening for thermal_opening in thermal_boundary.thermal_openings: self.assertIsNone(thermal_opening.frame_ratio, 'thermal_opening frame_ratio was initialized') self.assertIsNone(thermal_opening.g_value, 'thermal_opening g_value was initialized') @@ -167,7 +169,8 @@ class TestGeometryFactory(TestCase): self.assertIsNone(thermal_opening.front_side_solar_transmittance_at_normal_incidence, 'thermal_opening front_side_solar_transmittance_at_normal_incidence was initialized') self.assertIsNone(thermal_opening.thickness, 'thermal_opening thickness_m was initialized') - self.assertRaises(Exception, lambda: thermal_opening.u_value, 'thermal_opening u_value was initialized') + self.assertRaises(Exception, lambda: thermal_opening.overall_u_value, + 'thermal_opening u_value was initialized') self.assertIsNone(thermal_opening.overall_u_value, 'thermal_opening overall_u_value was initialized') self.assertIsNone(thermal_opening.hi, 'thermal_opening hi was initialized') self.assertIsNone(thermal_opening.he, 'thermal_opening he was initialized') @@ -227,6 +230,9 @@ class TestGeometryFactory(TestCase): # obj def test_import_obj(self): + """ + Test obj import + """ file = 'kelowna.obj' city = self._get_obj(file) self.assertIsNotNone(city, 'city is none') @@ -240,6 +246,6 @@ class TestGeometryFactory(TestCase): :return: """ file_path = (self._example_path / 'subway.osm').resolve() - subway_entrances = self._features = GeometryFactory('osm_subway', file_path).features - self.assertIsNotNone(subway_entrances, 'subway entrances is none') - self.assertEqual(len(subway_entrances), 20, 'Wrong number of subway entrances') + city = GeometryFactory('osm_subway', 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/non_functional_tests/test_schedules_factory.py b/non_functional_tests/test_schedules_factory.py index 4072e184..07a8f482 100644 --- a/non_functional_tests/test_schedules_factory.py +++ b/non_functional_tests/test_schedules_factory.py @@ -23,20 +23,22 @@ class TestSchedulesFactory(TestCase): Configure test environment :return: """ - self._city_gml = None self._example_path = (Path(__file__).parent / 'tests_data').resolve() def _get_citygml(self, file): file_path = (self._example_path / file).resolve() - self._city = GeometryFactory('citygml', file_path).city - ConstructionFactory('nrel', self._city).enrich() - self.assertIsNotNone(self._city, 'city is none') - for building in self._city.buildings: + _city = GeometryFactory('citygml', file_path).city + ConstructionFactory('nrel', _city).enrich() + self.assertIsNotNone(_city, 'city is none') + for building in _city.buildings: building.function = GeometryHelper.hft_to_function[building.function] - UsageFactory('hft', self._city).enrich() - return self._city + UsageFactory('hft', _city).enrich() + return _city def test_comnet_archetypes(self): + """ + Enrich the city with commet schedule archetypes and verify it + """ file = (self._example_path / 'one_building_in_kelowna.gml').resolve() city = self._get_citygml(file) occupancy_handler = 'comnet' @@ -48,6 +50,9 @@ class TestSchedulesFactory(TestCase): self.assertTrue(usage_zone.schedules) 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' @@ -57,4 +62,3 @@ class TestSchedulesFactory(TestCase): for schedule in usage_zone.schedules: print(schedule) print(usage_zone.schedules[schedule]) - diff --git a/non_functional_tests/test_sensors_factory.py b/non_functional_tests/test_sensors_factory.py index cb5a08e3..55b13e01 100644 --- a/non_functional_tests/test_sensors_factory.py +++ b/non_functional_tests/test_sensors_factory.py @@ -5,10 +5,9 @@ Copyright © 2021 Project Author Guille Gutierrez guillermo.gutierrezmorote@conc """ from pathlib import Path from unittest import TestCase -import pandas as pd -from city_model_structure.city import City from city_model_structure.building import Building from city_model_structure.buildings_cluster import BuildingsCluster +from city_model_structure.city import City from imports.sensors_factory import SensorsFactory @@ -47,6 +46,9 @@ class TestSensorsFactory(TestCase): return city def test_city_with_sensors(self): + """ + Load concordia sensors and verify it + """ SensorsFactory('cec', self._city, self._end_point).enrich() SensorsFactory('cgf', self._city, self._end_point).enrich() SensorsFactory('ct', self._city, self._end_point).enrich() diff --git a/non_functional_tests/test_usage_factory.py b/non_functional_tests/test_usage_factory.py index cadc2578..82c692a1 100644 --- a/non_functional_tests/test_usage_factory.py +++ b/non_functional_tests/test_usage_factory.py @@ -20,14 +20,13 @@ class TestUsageFactory(TestCase): Configure test environment :return: """ - self._city_gml = None self._example_path = (Path(__file__).parent / 'tests_data').resolve() def _get_citygml(self, file): file_path = (self._example_path / file).resolve() - self._city = GeometryFactory('citygml', file_path).city - self.assertIsNotNone(self._city, 'city is none') - return self._city + _city = GeometryFactory('citygml', file_path).city + self.assertIsNotNone(_city, 'city is none') + return _city def test_city_with_usage(self): """ diff --git a/pylintrc b/pylintrc index cea6aab1..0536a885 100644 --- a/pylintrc +++ b/pylintrc @@ -325,6 +325,8 @@ good-names=i, y, z, s, + df, + id _ # Good variable names regexes, separated by a comma. If names match any regex, diff --git a/tests/test_building.py b/tests/test_building.py index d8a3ab97..6e03b6c7 100644 --- a/tests/test_building.py +++ b/tests/test_building.py @@ -8,7 +8,7 @@ from unittest import TestCase from imports.geometry_factory import GeometryFactory -class MyTestCase(TestCase): +class TestBuildings(TestCase): """ TestBuilding TestCase 1 """ @@ -28,6 +28,9 @@ class MyTestCase(TestCase): return self._city_gml def test_storeys_division(self): + """ + Test storeys division + """ file = 'kelowna.gml' city = self._get_citygml(file) i = 0 diff --git a/tests/test_geometry_factory.py b/tests/test_geometry_factory.py index 1a149836..42bf04c3 100644 --- a/tests/test_geometry_factory.py +++ b/tests/test_geometry_factory.py @@ -74,6 +74,9 @@ class TestGeometryFactory(TestCase): self.assertEqual(counter, 1, f'{counter} buildings had errors when triangulating surfaces') def test_stuttgart_gml(self): + """ + Tests stuttgart gml + """ file = '20190815_mitte_out_MC_FloursurfaceADD.gml' city = self._get_citygml(file) print(f'city name: {city.name}') diff --git a/tests/test_idf.py b/tests/test_idf.py deleted file mode 100644 index 53b832ef..00000000 --- a/tests/test_idf.py +++ /dev/null @@ -1,78 +0,0 @@ -""" -TestOccupancyFactory test and validate the city model structure schedules parameters -SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca -""" -from pathlib import Path -from unittest import TestCase -from imports.geometry_factory import GeometryFactory -from imports.construction_factory import ConstructionFactory -from imports.usage_factory import UsageFactory -from imports.schedules_factory import SchedulesFactory -from exports.exports_factory import ExportsFactory -import os -import glob - - -class TestIdf(TestCase): - """ - Test IDF Class - """ - - def setUp(self) -> None: - """ - Test setup - :return: None - """ - self._city_gml = None - self._example_path = (Path(__file__).parent / 'tests_data').resolve() - self._output_path = (Path(__file__).parent / 'tests_outputs').resolve() - - def _get_city(self): - if self._city_gml is None: - file_path = (self._example_path / 'pluto_building.gml').resolve() - self._city_gml = GeometryFactory('citygml', file_path).city - ConstructionFactory('us_new_york', self._city_gml) - UsageFactory('us_new_york', self._city_gml) - UsageFactory('us_new_york', self._city_gml) - SchedulesFactory('demo', self._city_gml) - return self._city_gml - - def test_idf_blocks(self): - idd_file_path = (self._example_path / 'energy+.idd').resolve() - idf_file_path = (self._example_path / 'minimal.idf').resolve() - epw_file_path = (self._example_path / 'montreal.epw').resolve() - idf = IdfHelper(idf_file_path, idd_file_path, epw_file_path) - city = self._get_city() - for building in city.buildings: - idf.add_block(building) - test_prefix = 'test_idf_blocks' - idf.run(self._output_path, output_prefix=test_prefix, keep_file=self._output_path) - eso_file_path = (self._output_path / f'{test_prefix}out.eso') - heating, cooling = idf.read_eso(str(eso_file_path)) - self.assertEqual(len(heating), len(cooling), "Cooling and Heating doesn't contains the same amount of values") - self.assertNotEqual(len(heating), 0, "Cooling and Heating series are empty") - file_list = glob.glob(str(Path(self._output_path / '*').resolve())) - for file_path in file_list: - os.remove(file_path) - - def test_idf_surfaces(self): - idd_file_path = (self._example_path / 'energy+.idd').resolve() - idf_file_path = (self._example_path / 'minimal.idf').resolve() - epw_file_path = (self._example_path / 'montreal.epw').resolve() - - idf = IdfHelper(idf_file_path, idd_file_path, epw_file_path) - city = self._get_city() - for building in city.buildings: - idf.add_surfaces(building) - idf.add_schedule(building) - idf.add_occupancy() - test_prefix = 'test_idf_blocks' - idf.run(self._output_path, output_prefix=test_prefix, keep_file=self._output_path) - eso_file_path = (self._output_path / f'{test_prefix}out.eso') - heating, cooling = idf.read_eso(str(eso_file_path)) - self.assertEqual(len(heating), len(cooling), "Cooling and Heating doesn't contains the same amount of values") - self.assertNotEqual(len(heating), 0, "Cooling and Heating series are empty") - file_list = glob.glob(str(Path(self._output_path / '*').resolve())) - for file_path in file_list: - os.remove(file_path) diff --git a/tests/test_weather_factory.py b/tests/test_weather_factory.py index 29233297..92b3dceb 100644 --- a/tests/test_weather_factory.py +++ b/tests/test_weather_factory.py @@ -31,6 +31,9 @@ class TestWeatherFactory(TestCase): return self._city_gml def test_weather_xls(self): + """ + Enrich the city with xls weather file and verify it + """ file_path = (Path(__file__).parent / 'tests_data' / 'iso_52016_1_2017_lod2.gml').resolve() city_with_weather = self._get_citygml(file_path) WeatherFactory('xls', city_with_weather, base_path=self._example_path).enrich() @@ -41,6 +44,9 @@ class TestWeatherFactory(TestCase): self.assertFalse(values.empty, 'wrong value global horizontal') def test_weather_epw(self): + """ + Enrich the city with epw weather and verify it + """ file_path = (Path(__file__).parent / 'tests_data' / 'one_building_in_kelowna.gml').resolve() city_with_weather = self._get_citygml(file_path) _file_name = 'CAN_BC_Summerland.717680_CWEC.epw'