diff --git a/city_model_structure/attributes/edge.py b/city_model_structure/attributes/edge.py index 2235f096..3a156ed8 100644 --- a/city_model_structure/attributes/edge.py +++ b/city_model_structure/attributes/edge.py @@ -1,7 +1,7 @@ """ Node module SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca +Copyright © 2021 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca Contributor Milad milad.aghamohamadnia@concordia.ca """ import uuid diff --git a/city_model_structure/attributes/node.py b/city_model_structure/attributes/node.py index 65e769fd..86e6b514 100644 --- a/city_model_structure/attributes/node.py +++ b/city_model_structure/attributes/node.py @@ -1,13 +1,13 @@ """ Node module SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca +Copyright © 2021 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca Contributor Milad milad.aghamohamadnia@concordia.ca """ import uuid from typing import List, TypeVar - +from city_model_structure.attributes.time_series import TimeSeries Edge = TypeVar('Edge') @@ -21,6 +21,7 @@ class Node: self._name = name self._id = None self._edges = edges + self._time_series = None @property def name(self): @@ -43,7 +44,15 @@ class Node: @property def edges(self) -> List[Edge]: """ - get edges delimited by the node + Get edges delimited by the node :return: [Edge] """ return self._edges + + @property + def time_series(self) -> TimeSeries: + """ + Add explanation here + :return: add type of variable here + """ + return self._time_series diff --git a/city_model_structure/attributes/record.py b/city_model_structure/attributes/record.py new file mode 100644 index 00000000..e7261ddb --- /dev/null +++ b/city_model_structure/attributes/record.py @@ -0,0 +1,40 @@ +""" +Record module +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2021 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +""" + + +class Record: + """ + Record class + """ + + def __init__(self, time=None, value=None, flag=None): + self._time = time + self._value = value + self._flag = flag + + @property + def time(self): + """ + Add explanation here + :return: add type of variable here + """ + return self._time + + @property + def value(self): + """ + Add explanation here + :return: add type of variable here + """ + return self._value + + @property + def flag(self): + """ + Add explanation here + :return: add type of variable here + """ + return self._flag diff --git a/city_model_structure/attributes/time_series.py b/city_model_structure/attributes/time_series.py new file mode 100644 index 00000000..20c4d5dd --- /dev/null +++ b/city_model_structure/attributes/time_series.py @@ -0,0 +1,34 @@ +""" +Time series module +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2021 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +""" + +from typing import List +from city_model_structure.attributes.record import Record + + +class TimeSeries: + """ + TimeSeries class + """ + + def __init__(self, time_series_type=None, records=None): + self._time_series_type = time_series_type + self._records = records + + @property + def time_series_type(self): + """ + Add explanation here + :return: add type of variable here + """ + return self._time_series_type + + @property + def records(self) -> List[Record]: + """ + Add explanation here + :return: List[Record] + """ + return self._records diff --git a/city_model_structure/building.py b/city_model_structure/building.py index 9e144141..423d5ab8 100644 --- a/city_model_structure/building.py +++ b/city_model_structure/building.py @@ -174,6 +174,8 @@ class Building(CityObject): :return: [ThermalZone] """ if len(self._thermal_zones) == 0: + if self.storeys is None: + return [] for storey in self.storeys: self._thermal_zones.append(storey.thermal_zone) return self._thermal_zones @@ -194,6 +196,15 @@ class Building(CityObject): """ return self._year_of_construction + @year_of_construction.setter + def year_of_construction(self, value): + """ + Set building year of construction + :param value: int + """ + if value is not None: + self._year_of_construction = value + @property def function(self) -> Union[None, str]: """ diff --git a/city_model_structure/building_demand/internal_gains.py b/city_model_structure/building_demand/internal_gains.py index d2342a98..75e60cb7 100644 --- a/city_model_structure/building_demand/internal_gains.py +++ b/city_model_structure/building_demand/internal_gains.py @@ -13,11 +13,29 @@ class InternalGains: """ def __init__(self): + self._type = None self._average_internal_gain = None self._convective_fraction = None self._radiative_fraction = None self._latent_fraction = None + @property + def type(self) -> Union[None, str]: + """ + Get internal gains type + :return: None or string + """ + return self._type + + @type.setter + def type(self, value): + """ + Set internal gains type + :param value: string + """ + if value is not None: + self._type = str(value) + @property def average_internal_gain(self) -> Union[None, float]: """ @@ -30,7 +48,7 @@ class InternalGains: def average_internal_gain(self, value): """ Set internal gains average internal gain in W/m2 - :param value:float + :param value: float """ if value is not None: self._average_internal_gain = float(value) diff --git a/city_model_structure/bus_system.py b/city_model_structure/bus_system.py new file mode 100644 index 00000000..c254a55e --- /dev/null +++ b/city_model_structure/bus_system.py @@ -0,0 +1,56 @@ +""" +Bus system module +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2021 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +""" + +from typing import List +from city_model_structure.city_object import CityObject +from city_model_structure.attributes.polygon import Polygon +from city_model_structure.transport.bus_network import BusNetwork +from city_model_structure.transport.bus_node import BusNode +from city_model_structure.transport.bus import Bus + + +class BusSystem(CityObject): + """ + BusSystem(CityObject) class + """ + def __init__(self, name, lod, surfaces, city_lower_corner): + super().__init__(name, lod, surfaces, city_lower_corner) + self._bus_routes = None + self._bus_network = None + self._buses = None + self._restricted_polygons = None + + @property + def bus_routes(self) -> List[BusNode]: + """ + Add explanation here + :return: [BusNode] + """ + return self._bus_routes + + @property + def bus_network(self) -> BusNetwork: + """ + Add explanation here + :return: BusNetwork + """ + return self._bus_network + + @property + def buses(self) -> List[Bus]: + """ + Add explanation here + :return: [Bus] + """ + return self._buses + + @property + def restricted_polygons(self) -> List[Polygon]: + """ + Add explanation here + :return: [Polygon] + """ + return self._restricted_polygons diff --git a/city_model_structure/city.py b/city_model_structure/city.py index 9f6b0e0a..9465f368 100644 --- a/city_model_structure/city.py +++ b/city_model_structure/city.py @@ -2,7 +2,6 @@ City module SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca -Contributor Peter Yefi peteryefi@gmail.com """ from __future__ import annotations import sys @@ -19,361 +18,372 @@ from city_model_structure.city_objects_cluster import CityObjectsCluster from city_model_structure.buildings_cluster import BuildingsCluster from city_model_structure.parts_consisting_building import PartsConsistingBuilding from city_model_structure.subway_entrance import SubwayEntrance +from city_model_structure.fuel import Fuel from helpers.geometry_helper import GeometryHelper from helpers.location import Location from city_model_structure.energy_system import EnergySystem class City: - """ + """ City class """ - def __init__(self, lower_corner, upper_corner, srs_name): - self._name = None - self._lower_corner = lower_corner - self._upper_corner = upper_corner - self._buildings = None - self._subway_entrances = None - self._srs_name = srs_name - self._geometry = GeometryHelper() - # todo: right now extracted at city level, in the future should be extracted also at building level if exist - self._location = None - self._country_code = None - self._climate_reference_city = None - self._climate_file = None - self._latitude = None - self._longitude = None - self._time_zone = None - self._buildings_clusters = None - self._parts_consisting_buildings = None - self._city_objects_clusters = None - self._city_objects = None - self._energy_systems = None + def __init__(self, lower_corner, upper_corner, srs_name): + self._name = None + self._lower_corner = lower_corner + self._upper_corner = upper_corner + self._buildings = None + self._subway_entrances = None + self._srs_name = srs_name + self._geometry = GeometryHelper() + # todo: right now extracted at city level, in the future should be extracted also at building level if exist + self._location = None + self._country_code = None + self._climate_reference_city = None + self._climate_file = None + self._latitude = None + self._longitude = None + self._time_zone = None + self._buildings_clusters = None + self._parts_consisting_buildings = None + self._city_objects_clusters = None + self._city_objects = None + self._energy_systems = None + self._fuels = None - def _get_location(self) -> Location: - if self._location is None: - gps = pyproj.CRS('EPSG:4326') # LatLon with WGS84 datum used by GPS units and Google Earth - try: - input_reference = pyproj.CRS(self.srs_name) # Projected coordinate system from input data - except pyproj.exceptions.CRSError: - sys.stderr.write('Invalid projection reference system, please check the input data. ' - '(e.g. in CityGML files: srs_name)\n') - sys.exit() - transformer = Transformer.from_crs(input_reference, gps) - coordinates = transformer.transform(self.lower_corner[0], self.lower_corner[1]) - self._location = GeometryHelper.get_location(coordinates[0], coordinates[1]) - return self._location + @property + def fuels(self) -> [Fuel]: + return self._fuels - @property - def country_code(self): - """ + @fuels.setter + def fuels(self, value): + self._fuels = value + + + def _get_location(self) -> Location: + if self._location is None: + gps = pyproj.CRS('EPSG:4326') # LatLon with WGS84 datum used by GPS units and Google Earth + try: + input_reference = pyproj.CRS(self.srs_name) # Projected coordinate system from input data + except pyproj.exceptions.CRSError: + sys.stderr.write('Invalid projection reference system, please check the input data. ' + '(e.g. in CityGML files: srs_name)\n') + sys.exit() + transformer = Transformer.from_crs(input_reference, gps) + coordinates = transformer.transform(self.lower_corner[0], self.lower_corner[1]) + self._location = GeometryHelper.get_location(coordinates[0], coordinates[1]) + return self._location + + @property + def country_code(self): + """ Get city country code :return: str """ - return self._get_location().country + return self._get_location().country - @property - def name(self): - """ + @property + def name(self): + """ Get city name :return: str """ - return self._get_location().city + return self._get_location().city - @property - def climate_reference_city(self) -> Union[None, str]: - """ + @property + def climate_reference_city(self) -> Union[None, str]: + """ Get the name for the climatic information reference city :return: None or str """ - return self._climate_reference_city + return self._climate_reference_city - @climate_reference_city.setter - def climate_reference_city(self, value): - """ + @climate_reference_city.setter + def climate_reference_city(self, value): + """ Set the name for the climatic information reference city :param value: str """ - if value is not None: - self._climate_reference_city = str(value) + if value is not None: + self._climate_reference_city = str(value) - @property - def climate_file(self) -> Union[None, Path]: - """ + @property + def climate_file(self) -> Union[None, Path]: + """ Get the climate file full path :return: None or Path """ - return self._climate_file + return self._climate_file - @climate_file.setter - def climate_file(self, value): - """ + @climate_file.setter + def climate_file(self, value): + """ Set the climate file full path :param value: Path """ - if value is not None: - self._climate_file = Path(value) + if value is not None: + self._climate_file = Path(value) - @property - def city_objects(self) -> Union[List[CityObject], None]: - """ + @property + def city_objects(self) -> Union[List[CityObject], None]: + """ Get the city objects belonging to the city :return: None or [CityObject] """ - if self._city_objects is None: - if self.city_objects_clusters is None: - self._city_objects = [] - else: - self._city_objects = self.city_objects_clusters - if self.buildings is not None: - for building in self.buildings: - self._city_objects.append(building) - if self.subway_entrances is not None: - for subway_entrance in self.subway_entrances: - self._city_objects.append(subway_entrance) - if self.energy_systems is not None: - for energy_system in self.energy_systems: - self._city_objects.append(energy_system) - return self._city_objects + if self._city_objects is None: + if self.city_objects_clusters is None: + self._city_objects = [] + else: + self._city_objects = self.city_objects_clusters + if self.buildings is not None: + for building in self.buildings: + self._city_objects.append(building) + if self.subway_entrances is not None: + for subway_entrance in self.subway_entrances: + self._city_objects.append(subway_entrance) + if self.energy_systems is not None: + for energy_system in self.energy_systems: + self._city_objects.append(energy_system) + return self._city_objects - @property - def buildings(self) -> Union[List[Building], None]: - """ + @property + def buildings(self) -> Union[List[Building], None]: + """ Get the buildings belonging to the city :return: None or [Building] """ - return self._buildings + return self._buildings - @property - def subway_entrances(self) -> Union[List[SubwayEntrance], None]: - """ + @property + def subway_entrances(self) -> Union[List[SubwayEntrance], None]: + """ Get the subway entrances belonging to the city :return: a list of subway entrances objects or none """ - return self._subway_entrances + return self._subway_entrances - @property - def lower_corner(self) -> List[float]: - """ + @property + def lower_corner(self) -> List[float]: + """ Get city lower corner :return: [x,y,z] """ - return self._lower_corner + return self._lower_corner - @property - def upper_corner(self) -> List[float]: - """ + @property + def upper_corner(self) -> List[float]: + """ Get city upper corner :return: [x,y,z] """ - return self._upper_corner + return self._upper_corner - def city_object(self, name) -> Union[CityObject, None]: - """ + def city_object(self, name) -> Union[CityObject, None]: + """ Retrieve the city CityObject with the given name :param name:str :return: None or CityObject """ - for city_object in self.buildings: - if city_object.name == name: - return city_object - return None + for city_object in self.buildings: + if city_object.name == name: + return city_object + return None - def add_city_object(self, new_city_object): - """ + def add_city_object(self, new_city_object): + """ Add a CityObject to the city :param new_city_object:CityObject :return: None or not implemented error """ - if new_city_object.type == 'building': - if self._buildings is None: - self._buildings = [] - self._buildings.append(new_city_object) - elif new_city_object.type == 'subway_entrance': - if self._subway_entrances is None: - self._subway_entrances = [] - self._subway_entrances.append(new_city_object) - elif new_city_object.type == 'energy_system': - if self._energy_systems is None: - self._energy_systems = [] - self._energy_systems.append(new_city_object) - else: - raise NotImplementedError(new_city_object.type) + if new_city_object.type == 'building': + if self._buildings is None: + self._buildings = [] + self._buildings.append(new_city_object) + elif new_city_object.type == 'subway_entrance': + if self._subway_entrances is None: + self._subway_entrances = [] + self._subway_entrances.append(new_city_object) + elif new_city_object.type == 'energy_system': + if self._energy_systems is None: + self._energy_systems = [] + self._energy_systems.append(new_city_object) + else: + raise NotImplementedError(new_city_object.type) - def remove_city_object(self, city_object): - """ + def remove_city_object(self, city_object): + """ Remove a CityObject from the city :param city_object:CityObject :return: None """ - if city_object.type != 'building': - raise NotImplementedError(city_object.type) - if self._buildings is None or self._buildings == []: - sys.stderr.write('Warning: impossible to remove city_object, the city is empty\n') - else: - if city_object in self._buildings: - self._buildings.remove(city_object) + if city_object.type != 'building': + raise NotImplementedError(city_object.type) + if self._buildings is None or self._buildings == []: + sys.stderr.write('Warning: impossible to remove city_object, the city is empty\n') + else: + if city_object in self._buildings: + self._buildings.remove(city_object) - @property - def srs_name(self) -> Union[None, str]: - """ + @property + def srs_name(self) -> Union[None, str]: + """ Get city srs name :return: None or str """ - return self._srs_name + return self._srs_name - @name.setter - def name(self, value): - """ + @name.setter + def name(self, value): + """ Set city name :param value:str """ - if value is not None: - self._name = str(value) + if value is not None: + self._name = str(value) - @staticmethod - def load(city_filename) -> City: - """ + @staticmethod + def load(city_filename) -> City: + """ Load a city saved with city.save(city_filename) :param city_filename: city filename :return: City """ - with open(city_filename, 'rb') as file: - return pickle.load(file) + with open(city_filename, 'rb') as file: + return pickle.load(file) - def save(self, city_filename): - """ + def save(self, city_filename): + """ Save a city into the given filename :param city_filename: destination city filename :return: None """ - with open(city_filename, 'wb') as file: - pickle.dump(self, file) + with open(city_filename, 'wb') as file: + pickle.dump(self, file) - def region(self, center, radius) -> City: - """ + def region(self, center, radius) -> City: + """ Get a region from the city :param center: specific point in space [x, y, z] :param radius: distance to center of the sphere selected in meters :return: selected_region_city """ - selected_region_lower_corner = [center[0] - radius, center[1] - radius, center[2] - radius] - selected_region_upper_corner = [center[0] + radius, center[1] + radius, center[2] + radius] - selected_region_city = City(selected_region_lower_corner, selected_region_upper_corner, srs_name=self.srs_name) - selected_region_city.climate_file = self.climate_file - # selected_region_city.climate_reference_city = self.climate_reference_city - for city_object in self.city_objects: - location = city_object.centroid - if location is not None: - distance = math.sqrt(math.pow(location[0] - center[0], 2) + math.pow(location[1] - center[1], 2) - + math.pow(location[2] - center[2], 2)) - if distance < radius: - selected_region_city.add_city_object(city_object) - return selected_region_city + selected_region_lower_corner = [center[0] - radius, center[1] - radius, center[2] - radius] + selected_region_upper_corner = [center[0] + radius, center[1] + radius, center[2] + radius] + selected_region_city = City(selected_region_lower_corner, selected_region_upper_corner, srs_name=self.srs_name) + selected_region_city.climate_file = self.climate_file +# selected_region_city.climate_reference_city = self.climate_reference_city + for city_object in self.city_objects: + location = city_object.centroid + if location is not None: + distance = math.sqrt(math.pow(location[0]-center[0], 2) + math.pow(location[1]-center[1], 2) + + math.pow(location[2]-center[2], 2)) + if distance < radius: + selected_region_city.add_city_object(city_object) + return selected_region_city - @property - def latitude(self) -> Union[None, float]: - """ + @property + def latitude(self) -> Union[None, float]: + """ Get city latitude in degrees :return: None or float """ - return self._latitude + return self._latitude - @latitude.setter - def latitude(self, value): - """ + @latitude.setter + def latitude(self, value): + """ Set city latitude in degrees :parameter value: float """ - if value is not None: - self._latitude = float(value) + if value is not None: + self._latitude = float(value) - @property - def longitude(self) -> Union[None, float]: - """ + @property + def longitude(self) -> Union[None, float]: + """ Get city longitude in degrees :return: None or float """ - return self._longitude + return self._longitude - @longitude.setter - def longitude(self, value): - """ + @longitude.setter + def longitude(self, value): + """ Set city longitude in degrees :parameter value: float """ - if value is not None: - self._longitude = float(value) + if value is not None: + self._longitude = float(value) - @property - def time_zone(self) -> Union[None, float]: - """ + @property + def time_zone(self) -> Union[None, float]: + """ Get city time_zone :return: None or float """ - return self._time_zone + return self._time_zone - @time_zone.setter - def time_zone(self, value): - """ + @time_zone.setter + def time_zone(self, value): + """ Set city time_zone :parameter value: float """ - if value is not None: - self._time_zone = float(value) + if value is not None: + self._time_zone = float(value) - @property - def buildings_clusters(self) -> Union[List[BuildingsCluster], None]: - """ + @property + def buildings_clusters(self) -> Union[List[BuildingsCluster], None]: + """ Get buildings clusters belonging to the city :return: None or [BuildingsCluster] """ - return self._buildings_clusters + return self._buildings_clusters - @property - def parts_consisting_buildings(self) -> Union[List[PartsConsistingBuilding], None]: - """ + @property + def parts_consisting_buildings(self) -> Union[List[PartsConsistingBuilding], None]: + """ Get parts consisting buildings belonging to the city :return: None or [PartsConsistingBuilding] """ - return self._parts_consisting_buildings + return self._parts_consisting_buildings - @property - def energy_systems(self) -> Union[List[EnergySystem], None]: - """ - Get energy systems belonging to the city - :return: None or [EnergySystem] - """ - return self._energy_systems + @property + def energy_systems(self) -> Union[List[EnergySystem], None]: + """ + Get energy systems belonging to the city + :return: None or [EnergySystem] + """ + return self._energy_systems - @property - def city_objects_clusters(self) -> Union[List[CityObjectsCluster], None]: - """ + @property + def city_objects_clusters(self) -> Union[List[CityObjectsCluster], None]: + """ Get city objects clusters belonging to the city :return: None or [CityObjectsCluster] """ - if self.buildings_clusters is None: - self._city_objects_clusters = [] - else: - self._city_objects_clusters = self.buildings_clusters - if self.parts_consisting_buildings is not None: - self._city_objects_clusters.append(self.parts_consisting_buildings) - return self._city_objects_clusters + if self.buildings_clusters is None: + self._city_objects_clusters = [] + else: + self._city_objects_clusters = self.buildings_clusters + if self.parts_consisting_buildings is not None: + self._city_objects_clusters.append(self.parts_consisting_buildings) + return self._city_objects_clusters - def add_city_objects_cluster(self, new_city_objects_cluster): - """ + def add_city_objects_cluster(self, new_city_objects_cluster): + """ Add a CityObject to the city :param new_city_objects_cluster:CityObjectsCluster :return: None or NotImplementedError """ - if new_city_objects_cluster.type == 'buildings': - if self._buildings_clusters is None: - self._buildings_clusters = [] - self._buildings_clusters.append(new_city_objects_cluster) - elif new_city_objects_cluster.type == 'building_parts': - if self._parts_consisting_buildings is None: - self._parts_consisting_buildings = [] - self._parts_consisting_buildings.append(new_city_objects_cluster) - else: - raise NotImplementedError + if new_city_objects_cluster.type == 'buildings': + if self._buildings_clusters is None: + self._buildings_clusters = [] + self._buildings_clusters.append(new_city_objects_cluster) + elif new_city_objects_cluster.type == 'building_parts': + if self._parts_consisting_buildings is None: + self._parts_consisting_buildings = [] + self._parts_consisting_buildings.append(new_city_objects_cluster) + else: + raise NotImplementedError diff --git a/city_model_structure/energy_system.py b/city_model_structure/energy_system.py index f1815bac..7fb7d2d1 100644 --- a/city_model_structure/energy_system.py +++ b/city_model_structure/energy_system.py @@ -1,7 +1,7 @@ """ EnergySystem module SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +Copyright © 2021 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca Contributor Peter Yefi peteryefi@gmail.com """ @@ -11,54 +11,54 @@ from city_model_structure.energy_systems.water_to_water_hp import WaterToWaterHP class EnergySystem(CityObject): - """ + """ EnergySystem(CityObject) class """ - def __init__(self, name, lod, surfaces, city_lower_corner): - super().__init__(name, lod, surfaces, city_lower_corner) - self._air_source_hp = None - self._water_to_water_hp = None - self._type = 'energy_system' + def __init__(self, name, lod, surfaces, city_lower_corner): + super().__init__(name, lod, surfaces, city_lower_corner) + self._air_source_hp = None + self._water_to_water_hp = None + self._type = 'energy_system' - @property - def air_source_hp(self) -> AirSourceHP: - """ - Heat pump energy system - :return: - """ - return self._air_source_hp + @property + def air_source_hp(self) -> AirSourceHP: + """ + Heat pump energy system + :return: + """ + return self._air_source_hp - @air_source_hp.setter - def air_source_hp(self, value): - """ - Set heat pump for energy system - :param value: AirSourceHP - """ - if self._air_source_hp is None: - self._air_source_hp = value + @air_source_hp.setter + def air_source_hp(self, value): + """ + Set heat pump for energy system + :param value: AirSourceHP + """ + if self._air_source_hp is None: + self._air_source_hp = value - @property - def water_to_water_hp(self) -> WaterToWaterHP: - """ - Water to water heat pump energy system - :return: - """ - return self._water_to_water_hp + @property + def water_to_water_hp(self) -> WaterToWaterHP: + """ + Water to water heat pump energy system + :return: + """ + return self._water_to_water_hp - @water_to_water_hp.setter - def water_to_water_hp(self, value): - """ - Set water to water heat pump for energy system - :param value: WaterToWaterHP - """ - if self._water_to_water_hp is None: - self._water_to_water_hp = value + @water_to_water_hp.setter + def water_to_water_hp(self, value): + """ + Set water to water heat pump for energy system + :param value: WaterToWaterHP + """ + if self._water_to_water_hp is None: + self._water_to_water_hp = value - @property - def type(self) -> str: - """ - Type of city object - :return: str - """ - return self._type + @property + def type(self) -> str: + """ + Type of city object + :return: str + """ + return self._type diff --git a/city_model_structure/fuel.py b/city_model_structure/fuel.py new file mode 100644 index 00000000..e78a9f76 --- /dev/null +++ b/city_model_structure/fuel.py @@ -0,0 +1,41 @@ +""" +ConstructionFactory (before PhysicsFactory) retrieve the specific construction module for the given region +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2020 Project Author Atiya +""" + +class Fuel: + def __init__(self, fuel_id, name, carbon_emission_factor, unit): + self._fuel_id = fuel_id + self._name = name + self._carbon_emission_factor = carbon_emission_factor + self._unit = unit + + @property + def id(self): + """ + Get fuel id + """ + return self._fuel_id + + @property + def name(self): + """ + Get fuel name + """ + return self._name + + @property + def carbon_emission_factor(self): + """ + Get fuel carbon emission factor + """ + return self._carbon_emission_factor + + @property + def unit(self): + """ + Get fuel units + """ + return self._unit + diff --git a/city_model_structure/lca_calculations.py b/city_model_structure/lca_calculations.py new file mode 100644 index 00000000..032edcd6 --- /dev/null +++ b/city_model_structure/lca_calculations.py @@ -0,0 +1,23 @@ +""" +LifeCycleAssessment retrieve the specific Life Cycle Assessment module for the given region +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2020 Project Author Atiya +""" +from city_model_structure.machine import Machine + +class LcaCalculations: + """ + LCA Calculations class + """ + + def __init__(self): + print("lca calculations class") + + def emission_disposal_machines(self, ): + return Machine.work_efficiency * Machine.energy_consumption_rate * Machine.carbon_emission_factor + + def emission_transportation(self, weight, distance ): + return weight * distance * Machine.energy_consumption_rate * Machine.carbon_emission_factor + + + diff --git a/city_model_structure/machine.py b/city_model_structure/machine.py new file mode 100644 index 00000000..5c25fc71 --- /dev/null +++ b/city_model_structure/machine.py @@ -0,0 +1,78 @@ +""" +LifeCycleAssessment retrieve the specific Life Cycle Assessment module for the given region +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2020 Project Author Atiya +""" + +class Machine: + """ + Machine class + """ + + def __init__(self, machine_id, name, work_efficiency, work_efficiency_unit, energy_consumption_rate, energy_consumption_unit, + carbon_emission_factor, carbon_emission_unit): + self._machine_id = machine_id + self._name = name + self._work_efficiency = work_efficiency + self._work_efficiency_unit = work_efficiency_unit + self._energy_consumption_rate = energy_consumption_rate + self._energy_consumption_unit = energy_consumption_unit + self._carbon_emission_factor = carbon_emission_factor + self._carbon_emission_unit = carbon_emission_unit + + @property + def id(self): + """ + Get machine id + """ + return self._machine_id + + @property + def name(self): + """ + Get machine name + """ + return self._name + + @property + def work_efficiency(self): + """ + Get machine work efficiency + """ + return self._work_efficiency + + @property + def work_efficiency_unit(self): + """ + Get machine work efficiency unit + """ + return self._work_efficiency_unit + + @property + def energy_consumption_rate(self): + """ + Get energy consumption rate + """ + return self._energy_consumption_rate + + @property + def energy_consumption_unit(self): + """ + Get energy consumption unit + """ + return self._energy_consumption_unit + + @property + def carbon_emission_factor(self): + """ + Get carbon emission factor + """ + return self._carbon_emission_factor + + @property + def carbon_emission_unit(self): + """ + Get carbon emission unit + """ + return self._carbon_emission_unit + diff --git a/city_model_structure/material.py b/city_model_structure/material.py new file mode 100644 index 00000000..d0ef2c53 --- /dev/null +++ b/city_model_structure/material.py @@ -0,0 +1,118 @@ +""" +LifeCycleAssessment retrieve the specific Life Cycle Assessment module for the given region +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2020 Project Author Atiya +""" + +class Material: + """ + LCA Material class + """ + + def __init__(self, material_name, material_id, type, density, density_unit, embodied_carbon, embodied_carbon_unit, recycling_ratio, + onsite_recycling_ratio, company_recycling_ratio, landfilling_ratio, cost, cost_unit): + self._material_name = material_name + self._material_id = material_id + self._type = type + self._density = density + self._density_unit = density_unit + self._embodied_carbon = embodied_carbon + self._embodied_carbon_unit = embodied_carbon_unit + self._recycling_ratio = recycling_ratio + self._onsite_recycling_ratio = onsite_recycling_ratio + self._company_recycling_ratio = company_recycling_ratio + self._landfilling_ratio = landfilling_ratio + self._cost = cost + self._cost_unit = cost_unit + + @property + def material_name(self): + """ + Get material name + """ + return self._material_name + + @property + def id(self): + """ + Get material id + """ + return self._material_id + + @property + def type(self): + """ + Get material type + """ + return self._type + + @property + def density(self): + """ + Get material density + """ + return self._density + + @property + def density_unit(self): + """ + Get material density unit + """ + return self._density_unit + + @property + def embodied_carbon(self): + """ + Get material embodied carbon + """ + return self._embodied_carbon + + @property + def embodied_carbon_unit(self): + """ + Get material embodied carbon unit + """ + return self._embodied_carbon_unit + + @property + def recycling_ratio(self): + """ + Get material recycling ratio + """ + return self._recycling_ratio + + @property + def onsite_recycling_ratio(self): + """ + Get material onsite recycling ratio + """ + return self._onsite_recycling_ratio + + @property + def company_recycling_ratio(self): + """ + Get material company recycling ratio + """ + return self._company_recycling_ratio + + @property + def landfilling_ratio(self): + """ + Get material landfilling ratio + """ + return self._landfilling_ratio + + @property + def cost(self): + """ + Get material cost + """ + return self._cost + + @property + def cost_unit(self): + """ + Get material cost unit + """ + return self._cost_unit + diff --git a/city_model_structure/network.py b/city_model_structure/network.py index 869c7107..ae3d0186 100644 --- a/city_model_structure/network.py +++ b/city_model_structure/network.py @@ -1,9 +1,10 @@ """ Network module SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca +Copyright © 2021 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca Contributor Milad milad.aghamohamadnia@concordia.ca """ + import uuid from typing import List diff --git a/city_model_structure/transport/bus.py b/city_model_structure/transport/bus.py new file mode 100644 index 00000000..ea7acb47 --- /dev/null +++ b/city_model_structure/transport/bus.py @@ -0,0 +1,114 @@ +""" +Bus module +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2021 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +""" + +from city_model_structure.attributes.schedule import Schedule + + +class Bus: + """ + Bus class + """ + + def __init__(self): + self._maintenance_time = None + self._charging_time = None + self._recovery_time = None + self._vehicle_type = None + self._energy_consumption = None + self._trips_schedule = None + self._capacity = None + self._maintenance_cost = None + self._investment_cost = None + self._charging_range = None + self._maximum_travel_range = None + + @property + def maintenance_time(self): + """ + Add explanation here + :return: add type of variable here + """ + return self._maintenance_time + + @property + def charging_time(self): + """ + Add explanation here + :return: add type of variable here + """ + return self._charging_time + + @property + def recovery_time(self): + """ + Add explanation here + :return: add type of variable here + """ + return self.maintenance_time + self.charging_time + + @property + def vehicle_type(self): + """ + Add explanation here + :return: add type of variable here + """ + return self._vehicle_type + + @property + def energy_consumption(self): + """ + Add explanation here + :return: add type of variable here + """ + return self._energy_consumption + + @property + def trips_schedule(self) -> Schedule: + """ + Add explanation here + :return: add type of variable here + """ + return self._trips_schedule + + @property + def capacity(self): + """ + Add explanation here + :return: add type of variable here + """ + return self._capacity + + @property + def maintenance_cost(self): + """ + Add explanation here + :return: add type of variable here + """ + return self._maintenance_cost + + @property + def investment_cost(self): + """ + Add explanation here + :return: add type of variable here + """ + return self._investment_cost + + @property + def charging_range(self): + """ + Add explanation here + :return: add type of variable here + """ + return self._charging_range + + @property + def maximum_travel_range(self): + """ + Add explanation here + :return: add type of variable here + """ + return self._maximum_travel_range diff --git a/city_model_structure/transport/bus_depot.py b/city_model_structure/transport/bus_depot.py new file mode 100644 index 00000000..0b617275 --- /dev/null +++ b/city_model_structure/transport/bus_depot.py @@ -0,0 +1,34 @@ +""" +Bus depot module +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2021 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +""" + +from city_model_structure.transport.bus_node import BusNode + + +class BusDepot(BusNode): + """ + BusDepot class + """ + + def __init__(self, name, coordinates, edges=None): + super().__init__(name, coordinates, edges=edges, node_type='BusDepot') + self._number_of_charging_poles = None + self._number_of_available_buses = None + + @property + def number_of_charging_poles(self): + """ + Add explanation here + :return: add type of variable here + """ + return self._number_of_charging_poles + + @property + def number_of_available_buses(self): + """ + Add explanation here + :return: add type of variable here + """ + return self._number_of_available_buses diff --git a/city_model_structure/transport/bus_edge.py b/city_model_structure/transport/bus_edge.py new file mode 100644 index 00000000..98c31dc0 --- /dev/null +++ b/city_model_structure/transport/bus_edge.py @@ -0,0 +1,46 @@ +""" +Bus edge module +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2021 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +""" + +from typing import List, TypeVar +from city_model_structure.attributes.edge import Edge + +BusNode = TypeVar('BusNode') + + +class BusEdge(Edge): + """ + BusEdge class + Each edge is unidirectional and starts at the "from" node and ends at the "to" node + """ + + def __init__(self, name, nodes, edge_type='BusEdge'): + super().__init__(name, nodes) + self._edge_type = edge_type + self._average_travel_time = None + + @property + def edge_type(self): + """ + Get the edge type + :return: str + """ + return self._edge_type + + @property + def nodes(self) -> List[BusNode]: + """ + Get delimiting nodes for the edge + :return: [BusNode] + """ + return self._nodes + + @property + def average_travel_time(self): + """ + Add explanation here + :return: add type of variable here + """ + return self._average_travel_time diff --git a/city_model_structure/transport/bus_network.py b/city_model_structure/transport/bus_network.py new file mode 100644 index 00000000..6f3dfbd8 --- /dev/null +++ b/city_model_structure/transport/bus_network.py @@ -0,0 +1,43 @@ +""" +Bus network module +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2021 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +""" + +from typing import List +from city_model_structure.network import Network +from city_model_structure.transport.bus_edge import BusEdge +from city_model_structure.transport.bus_node import BusNode + + +class BusNetwork(Network): + """ + BusNetwork(Network) class + """ + def __init__(self, name, edges=None, nodes=None): + super().__init__(name, edges, nodes) + self._type = "BusNetwork" + + @property + def type(self): + """ + Get network type + :return: str + """ + return self._type + + @property + def edges(self) -> List[BusEdge]: + """ + Get network edges + :return: [BusEdge] + """ + return self._edges + + @property + def nodes(self) -> List[BusNode]: + """ + Get network nodes + :return: [BusNode] + """ + return self._nodes diff --git a/city_model_structure/transport/bus_node.py b/city_model_structure/transport/bus_node.py new file mode 100644 index 00000000..17274a4f --- /dev/null +++ b/city_model_structure/transport/bus_node.py @@ -0,0 +1,55 @@ +""" +Bus node module +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2021 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +""" + +from typing import List, TypeVar + +from city_model_structure.attributes.node import Node +from city_model_structure.attributes.point import Point + +BusEdge = TypeVar('BusEdge') + + +class BusNode(Node): + """ + BusNode class + """ + + def __init__(self, name, coordinates, node_type='BusNode', edges=None): + super().__init__(name, edges) + self._coordinates = coordinates + self._node_type = node_type + + @property + def node_type(self): + """ + Get node type + :return: str + """ + return self._node_type + + @property + def coordinates(self) -> Point: + """ + Get node coordinates + :return: Point + """ + return self._coordinates + + @coordinates.setter + def coordinates(self, value): + """ + Set node coordinates + :param value: Point + """ + self._coordinates = value + + @property + def edges(self) -> List[BusEdge]: + """ + get edges delimited by the node + :return: [BusEdge] + """ + return self._edges diff --git a/city_model_structure/transport/bus_stop.py b/city_model_structure/transport/bus_stop.py new file mode 100644 index 00000000..b4fccd62 --- /dev/null +++ b/city_model_structure/transport/bus_stop.py @@ -0,0 +1,55 @@ +""" +Bus stop module +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2021 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +""" + +from typing import Union +from city_model_structure.transport.bus_node import BusNode +from city_model_structure.transport.fast_charging_infrastructure import FastChargingInfrastructure +from city_model_structure.attributes.schedule import Schedule + + +class BusStop(BusNode): + """ + BusStop class + """ + + def __init__(self, name, coordinates, edges=None): + super().__init__(name, coordinates, edges=edges, node_type='BusStop') + self._time_table = None + self._average_hourly_passengers_demand = None + self._fast_charging_infrastructure = None + self._waiting_time = None + + @property + def time_table(self): + """ + Add explanation here + :return: add type of variable here + """ + return self._time_table + + @property + def average_hourly_passengers_demand(self) -> Schedule: + """ + Add explanation here + :return: Schedule + """ + return self._average_hourly_passengers_demand + + @property + def fast_charging_infrastructure(self) -> Union[None, FastChargingInfrastructure]: + """ + Add explanation here + :return: FastChargingInfrastructure + """ + return self._fast_charging_infrastructure + + @property + def waiting_time(self): + """ + Add explanation here + :return: add type of variable here + """ + return self._waiting_time diff --git a/city_model_structure/transport/connection.py b/city_model_structure/transport/connection.py index 093fb4b8..0f023cd4 100644 --- a/city_model_structure/transport/connection.py +++ b/city_model_structure/transport/connection.py @@ -1,7 +1,7 @@ """ Connection module SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +Copyright © 2021 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca Contributor Milad milad.aghamohamadnia@concordia.ca Contributor Guille guille.gutierrezmorote@concordia.ca """ diff --git a/city_model_structure/transport/crossing.py b/city_model_structure/transport/crossing.py index 9a28c8f8..0b740355 100644 --- a/city_model_structure/transport/crossing.py +++ b/city_model_structure/transport/crossing.py @@ -1,7 +1,7 @@ """ Crossing module SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +Copyright © 2021 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca Contributor Milad milad.aghamohamadnia@concordia.ca Contributor Guille guille.gutierrezmorote@concordia.ca """ diff --git a/city_model_structure/transport/fast_charging_infrastructure.py b/city_model_structure/transport/fast_charging_infrastructure.py new file mode 100644 index 00000000..a3369a91 --- /dev/null +++ b/city_model_structure/transport/fast_charging_infrastructure.py @@ -0,0 +1,31 @@ +""" +Fast charging infrastructure module +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2021 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +""" + + +class FastChargingInfrastructure: + """ + FastChargingInfrastructure class + """ + + def __init__(self): + self._electrical_demand = None + self._losses_in_grid = None + + @property + def electrical_demand(self): + """ + Add explanation here + :return: add type of variable here + """ + return self._electrical_demand + + @property + def losses_in_grid(self): + """ + Add explanation here + :return: add type of variable here + """ + return self._losses_in_grid diff --git a/city_model_structure/transport/join.py b/city_model_structure/transport/join.py index a7f609b8..3e1cd143 100644 --- a/city_model_structure/transport/join.py +++ b/city_model_structure/transport/join.py @@ -1,7 +1,7 @@ """ Join module SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +Copyright © 2021 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca Contributor Milad milad.aghamohamadnia@concordia.ca Contributor Guille guille.gutierrezmorote@concordia.ca """ diff --git a/city_model_structure/transport/lane.py b/city_model_structure/transport/lane.py index d68bcca0..417f986a 100644 --- a/city_model_structure/transport/lane.py +++ b/city_model_structure/transport/lane.py @@ -1,7 +1,7 @@ """ Lane module SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +Copyright © 2021 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca Contributor Milad milad.aghamohamadnia@concordia.ca """ diff --git a/city_model_structure/transport/origin_destination_edge.py b/city_model_structure/transport/origin_destination_edge.py index c3d3f3c1..32fa7cbb 100644 --- a/city_model_structure/transport/origin_destination_edge.py +++ b/city_model_structure/transport/origin_destination_edge.py @@ -1,13 +1,16 @@ """ Origin-Destination edge module SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +Copyright © 2021 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca """ +from typing import List, TypeVar from city_model_structure.attributes.edge import Edge from city_model_structure.attributes.schedule import Schedule +OriginDestinationNode = TypeVar('OriginDestinationNode') + class OriginDestinationEdge(Edge): """ @@ -28,6 +31,14 @@ class OriginDestinationEdge(Edge): """ return self._edge_type + @property + def nodes(self) -> List[OriginDestinationNode]: + """ + Get delimiting nodes for the edge + :return: [OriginDestinationNode] + """ + return self._nodes + @property def movement_schedule(self) -> Schedule: """ diff --git a/city_model_structure/transport/origin_destination_network.py b/city_model_structure/transport/origin_destination_network.py index b23bb01c..9090f983 100644 --- a/city_model_structure/transport/origin_destination_network.py +++ b/city_model_structure/transport/origin_destination_network.py @@ -1,10 +1,13 @@ """ Origin-Destination network module SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +Copyright © 2021 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca """ +from typing import List from city_model_structure.network import Network +from city_model_structure.transport.origin_destination_edge import OriginDestinationEdge +from city_model_structure.transport.origin_destination_node import OriginDestinationNode class OriginDestinationNetwork(Network): @@ -22,3 +25,19 @@ class OriginDestinationNetwork(Network): :return: str """ return self._type + + @property + def edges(self) -> List[OriginDestinationEdge]: + """ + Get network edges + :return: [OriginDestinationEdge] + """ + return self._edges + + @property + def nodes(self) -> List[OriginDestinationNode]: + """ + Get network nodes + :return: [OriginDestinationNode] + """ + return self._nodes diff --git a/city_model_structure/transport/origin_destination_node.py b/city_model_structure/transport/origin_destination_node.py index 5e46e92d..ea84bd5c 100644 --- a/city_model_structure/transport/origin_destination_node.py +++ b/city_model_structure/transport/origin_destination_node.py @@ -1,15 +1,17 @@ """ Origin-Destination node module SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +Copyright © 2021 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca """ -from typing import List +from typing import List, TypeVar from city_model_structure.attributes.node import Node from city_model_structure.attributes.point import Point from city_model_structure.attributes.polygon import Polygon from city_model_structure.city_object import CityObject +OriginDestinationEdge = TypeVar('OriginDestinationEdge') + class OriginDestinationNode(Node): """ @@ -48,6 +50,14 @@ class OriginDestinationNode(Node): """ self._coordinates = value + @property + def edges(self) -> List[OriginDestinationEdge]: + """ + get edges delimited by the node + :return: [OriginDestinationEdge] + """ + return self._edges + @property def polygon(self) -> Polygon: """ diff --git a/city_model_structure/transport/phase.py b/city_model_structure/transport/phase.py index 559e0834..66d2d59b 100644 --- a/city_model_structure/transport/phase.py +++ b/city_model_structure/transport/phase.py @@ -1,7 +1,7 @@ """ Phase module SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +Copyright © 2021 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca Contributor Milad milad.aghamohamadnia@concordia.ca """ diff --git a/city_model_structure/transport/traffic_edge.py b/city_model_structure/transport/traffic_edge.py index 1fd77299..e507e0ba 100644 --- a/city_model_structure/transport/traffic_edge.py +++ b/city_model_structure/transport/traffic_edge.py @@ -1,13 +1,14 @@ """ Traffic edge module SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +Copyright © 2021 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca Contributor Milad milad.aghamohamadnia@concordia.ca Contributor Guille guille.gutierrezmorote@concordia.ca """ from typing import List, Union from city_model_structure.attributes.edge import Edge +from city_model_structure.transport.traffic_node import TrafficNode from city_model_structure.transport.lane import Lane @@ -37,6 +38,14 @@ class TrafficEdge(Edge): """ return self._edge_type + @property + def nodes(self) -> List[TrafficNode]: + """ + Get delimiting nodes for the edge + :return: [TrafficNode] + """ + return self._nodes + @property def lanes(self) -> List[Lane]: """ diff --git a/city_model_structure/transport/traffic_light.py b/city_model_structure/transport/traffic_light.py index 7b7d4621..89827a62 100644 --- a/city_model_structure/transport/traffic_light.py +++ b/city_model_structure/transport/traffic_light.py @@ -1,7 +1,7 @@ """ Traffic light module SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +Copyright © 2021 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca Contributor Milad milad.aghamohamadnia@concordia.ca Contributor Guille guille.gutierrezmorote@concordia.ca """ diff --git a/city_model_structure/transport/traffic_network.py b/city_model_structure/transport/traffic_network.py index 27e30096..eef5a4e9 100644 --- a/city_model_structure/transport/traffic_network.py +++ b/city_model_structure/transport/traffic_network.py @@ -1,12 +1,15 @@ """ Traffic network module SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +Copyright © 2021 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca Contributor Milad milad.aghamohamadnia@concordia.ca Contributor Guille guille.gutierrezmorote@concordia.ca """ +from typing import List from city_model_structure.network import Network +from city_model_structure.transport.traffic_edge import TrafficEdge +from city_model_structure.transport.traffic_node import TrafficNode class TrafficNetwork(Network): @@ -24,3 +27,19 @@ class TrafficNetwork(Network): :return: str """ return self._type + + @property + def edges(self) -> List[TrafficEdge]: + """ + Get network edges + :return: [TrafficEdge] + """ + return self._edges + + @property + def nodes(self) -> List[TrafficNode]: + """ + Get network nodes + :return: [TrafficNode] + """ + return self._nodes diff --git a/city_model_structure/transport/traffic_node.py b/city_model_structure/transport/traffic_node.py index 3008cf97..6188e6e5 100644 --- a/city_model_structure/transport/traffic_node.py +++ b/city_model_structure/transport/traffic_node.py @@ -1,18 +1,19 @@ """ TrafficNode module SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +Copyright © 2021 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca Contributor Milad milad.aghamohamadnia@concordia.ca Contributor Guille guille.gutierrezmorote@concordia.ca """ -from typing import List + +from typing import List, TypeVar from city_model_structure.attributes.edge import Edge from city_model_structure.attributes.node import Node from city_model_structure.attributes.point import Point - -from city_model_structure.transport.connection import Connection +Connection = TypeVar('Connection') +TrafficEdge = TypeVar('TrafficEdge') class TrafficNode(Node): @@ -55,6 +56,14 @@ class TrafficNode(Node): """ self._coordinates = value + @property + def edges(self) -> List[TrafficEdge]: + """ + get edges delimited by the node + :return: [TrafficEdge] + """ + return self._edges + @property def prohibitions(self) -> [(Edge, Edge)]: """ diff --git a/city_model_structure/transport/walkway_node.py b/city_model_structure/transport/walkway_node.py index 36ec90c6..e884f976 100644 --- a/city_model_structure/transport/walkway_node.py +++ b/city_model_structure/transport/walkway_node.py @@ -1,7 +1,7 @@ """ Walkway node module SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +Copyright © 2021 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca Contributor Milad milad.aghamohamadnia@concordia.ca Contributor Guille guille.gutierrezmorote@concordia.ca """ diff --git a/city_model_structure/vehicle.py b/city_model_structure/vehicle.py new file mode 100644 index 00000000..95aff94a --- /dev/null +++ b/city_model_structure/vehicle.py @@ -0,0 +1,61 @@ +""" +LifeCycleAssessment retrieve the specific Life Cycle Assessment module for the given region +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2020 Project Author Atiya +""" + +class Vehicle: + """ + Vehicle class + """ + + def __init__(self, vehicle_id, name, fuel_consumption_rate, fuel_consumption_unit, carbon_emission_factor, carbon_emission_factor_unit): + self._vehicle_id = vehicle_id + self._name = name + self._fuel_consumption_rate = fuel_consumption_rate + self._fuel_consumption_unit = fuel_consumption_unit + self._carbon_emission_factor = carbon_emission_factor + self._carbon_emission_factor_unit = carbon_emission_factor_unit + + @property + def id(self): + """ + Get vehicle id + """ + return self._vehicle_id + + @property + def name(self): + """ + Get vehicle name + """ + return self._name + + @property + def fuel_consumption_rate(self): + """ + Get vehicle fuel consumption rate + """ + return self._fuel_consumption_rate + + @property + def fuel_consumption_unit(self): + """ + Get fuel consumption unit + """ + return self._fuel_consumption_unit + + @property + def carbon_emission_factor(self): + """ + Get vehicle carbon emission factor + """ + return self._carbon_emission_factor + + @property + def carbon_emission_unit(self): + """ + Get carbon emission units + """ + return self._carbon_emission_unit + diff --git a/config/configuration.ini b/config/configuration.ini index 394c1c61..b2ddfb04 100644 --- a/config/configuration.ini +++ b/config/configuration.ini @@ -3,3 +3,11 @@ max_location_distance_for_shared_walls = 5.0 min_coordinate = -1.7976931348623157e+308 max_coordinate = 1.7976931348623157e+308 +comnet_lighting_latent = 0 +comnet_lighting_convective = 0.5 +comnet_lighting_radiant = 0.5 +comnet_occupancy_sensible_convective = 0.9 +comnet_occupancy_sensible_radiant = 0.1 +comnet_plugs_latent = 0 +comnet_plugs_convective = 0.75 +comnet_plugs_radiant = 0.25 diff --git a/data/customized_imports/ashrae_archetypes.xml b/data/customized_imports/ashrae_archetypes.xml new file mode 100644 index 00000000..df281f3a --- /dev/null +++ b/data/customized_imports/ashrae_archetypes.xml @@ -0,0 +1,123 @@ + + + Ashrae values + Library created by Sanam from ANSI/ASHRAE Standard 62-2001 + + assembly + + 0.15 + + + + 5 + + + + + health + + 0.1 + + + + 20 + + + + + hotel + + 0.11 + + + + 5 + + + + + manufacturing + + 0.07 + + + + 10 + + + + + office + + 0.05 + + + + 5 + + + + + restaurant + + 0.7 + + + + 7.5 + + + + + retail + + 0.2 + + + + 7.5 + + + + + school + + 0.25 + + + + 10 + + + + + lab + + 0.25 + + + + 10 + + + + + residential + + + 5 + + + + + gymnasium + + 0.3 + + + + 7.5 + + + + diff --git a/data/life_cycle_assessment/lca_data.xml b/data/life_cycle_assessment/lca_data.xml new file mode 100644 index 00000000..bc3ab8ee --- /dev/null +++ b/data/life_cycle_assessment/lca_data.xml @@ -0,0 +1,573 @@ + + + + + 0.32 + + + 0.4 + + + 0.4 + + + 0.5 + + + 0.18 + + + 0.39 + + + 0.27 + + + 4.16 + + + 2.24 + + + 0.2 + + + 3.19 + + + 3.53 + + + 0.27 + + + 0.21 + + + 1.69 + + + 0.21 + + + 0.35 + + + 0.18 + + + 0.81 + + + 1.21 + + + 1.61 + + + 0.11 + + + 0.3 + + + 1.16 + + + 0.61 + + + + + 0.347 + 16.5 + 0.918 + + + 0.033 + 25.2 + 4.16 + + + 0.027 + 16.8 + 2.239 + + + 0.023 + 16.8 + 2.239 + + + 0.109 + 25.2 + 2.239 + + + 0.003 + 16.4 + 4.16 + + + 0.002 + 11 + 0.918 + + + 0.002 + 90 + 0.918 + + + 0.002 + 10 + 0.918 + + + 0.002 + 11 + 0.918 + + + 0.002 + 132 + 0.918 + + + 0.002 + 15 + 0.918 + + + 0.002 + 5.5 + 0.918 + + + 0.002 + 22.5 + 0.918 + + + + + 0.0123 + 2.239 + + + 0.042 + 0.918 + + + 0.01 + 1.00000 + + + 1.3 + 1.00000 + + + + + + 1.8 + 560 + 0.8 + 0.3 + 0.7 + 0.2 + .... + + + 1.2 + 310 + 0.8 + 0.3 + 0.7 + 0.2 + .... + + + 2 + 3080 + 0.8 + 0.3 + 0.7 + 0.2 + .... + + + 1.4 + 300 + 0.8 + 0.3 + 0.7 + 0.2 + .... + + + + + 1.6 + 900 + 0.8 + 0 + 1 + 0.2 + .... + + + 1.6 + 2340 + 0.8 + 0 + 1 + 0.2 + .... + + + 1.6 + 1570 + 0.8 + 0 + 1 + 0.2 + .... + + + 1.4 + 1840 + 0.8 + 0 + 1 + 0.2 + .... + + + 1.3 + 410 + 0.8 + 0 + 1 + 0.2 + .... + + + 2.3 + 160 + 0.8 + 0 + 1 + 0.2 + .... + + + 2.3 + 170 + 0.8 + 0 + 1 + 0.2 + .... + + + 2.3 + 230 + 0.8 + 0 + 1 + 0.2 + .... + + + 2.4 + 240 + 0.8 + 0 + 1 + 0.2 + .... + + + 2.4 + 280 + 0.8 + 0 + 1 + 0.2 + .... + + + 2.3 + 170 + 0.8 + 0 + 1 + 0.2 + .... + + + 1.2 + 440 + 0.8 + 0 + 1 + 0.2 + .... + + + + + 2.58 + 2660 + 0.95 + 0 + 1 + 0.05 + .... + + + 2.58 + 5260 + 0.95 + 0 + 1 + 0.05 + .... + + + + + 0.06 + 1760 + 0.9 + 0 + 1 + 0.1 + .... + + + 0.122 + 3080 + 0.9 + 0 + 1 + 0.1 + .... + + + 0.028 + 3180 + 0.9 + 0 + 1 + 0.1 + .... + + + 0.024 + 5140 + 0.9 + 0 + 1 + 0.1 + .... + + + 0.1 + 6040 + 0.9 + 0 + 1 + 0.1 + .... + + + 0.3 + 5380 + 0.9 + 0 + 1 + 0.1 + .... + + + 0.032 + 2150 + 0.9 + 0 + 1 + 0.1 + .... + + + + + 0.9 + 3420 + 0.6 + 0 + 1 + 0.4 + .... + + + 0.7 + 1430 + 0.6 + 0 + 1 + 0.4 + .... + + + 0.65 + 2780 + 0.6 + 0 + 1 + 0.4 + .... + + + 0.72 + 2190 + 0.6 + 0 + 1 + 0.4 + .... + + + + + 1.43 + 1070 + 0 + 0 + 0 + 1 + .... + + + 1.43 + 240 + 0 + 0 + 0 + 1 + .... + + + 1.43 + 430 + 0 + 0 + 0 + 1 + .... + + + 1.43 + 340 + 0 + 0 + 0 + 1 + .... + + + 1.2 + 440 + 0.8 + 0 + 1 + 0.2 + .... + + + 2.1 + 1410 + 0.8 + 0 + 1 + 0.2 + .... + + + 1.43 + 250 + 0 + 0 + 0 + 1 + .... + + + 1.44 + 1480 + 0.8 + 0 + 1 + 0.2 + .... + + + 1.44 + 2220 + 0.8 + 0 + 1 + 0.2 + .... + + + 1.27 + 3960 + 0.8 + 0 + 1 + 0.2 + .... + + + 1.15 + 760 + 0.8 + 0 + 1 + 0.2 + .... + + + + + 8 + 3160 + 0.98 + 0 + 1 + 0.02 + .... + + + 2.7 + 5370 + 0.98 + 0 + 1 + 0.02 + .... + + + 7.85 + 3910 + 0.98 + 0 + 1 + 0.02 + .... + + + + \ No newline at end of file diff --git a/data/usage/comnet_archetypes.xlsx b/data/usage/comnet_archetypes.xlsx new file mode 100644 index 00000000..8e04fc17 Binary files /dev/null and b/data/usage/comnet_archetypes.xlsx differ diff --git a/helpers/configuration_helper.py b/helpers/configuration_helper.py index 0a9b2d6c..85677ceb 100644 --- a/helpers/configuration_helper.py +++ b/helpers/configuration_helper.py @@ -38,4 +38,70 @@ class ConfigurationHelper: Get configured maximal coordinate value :return: 1.7976931348623157e+308 """ - return self._config.getfloat('buildings', 'max_coordinate') + return self._config.getfloat('buildings', 'max_coordinate').real + + @property + def comnet_lighting_latent(self) -> float: + """ + Get configured latent ratio of internal gains do to lighting used for Comnet (ASHRAE) standard + :return: 0 + """ + return self._config.getfloat('buildings', 'comnet_lighting_latent').real + + @property + def comnet_lighting_convective(self) -> float: + """ + Get configured convective ratio of internal gains do to lighting used for Comnet (ASHRAE) standard + :return: 0.5 + """ + return self._config.getfloat('buildings', 'comnet_lighting_convective').real + + @property + def comnet_lighting_radiant(self) -> float: + """ + Get configured radiant ratio of internal gains do to lighting used for Comnet (ASHRAE) standard + :return: 0.5 + """ + return self._config.getfloat('buildings', 'comnet_lighting_radiant').real + + @property + def comnet_plugs_latent(self) -> float: + """ + Get configured latent ratio of internal gains do to electrical appliances used for Comnet (ASHRAE) standard + :return: 0 + """ + return self._config.getfloat('buildings', 'comnet_plugs_latent').real + + @property + def comnet_plugs_convective(self) -> float: + """ + Get configured convective ratio of internal gains do to electrical appliances used for Comnet (ASHRAE) standard + :return: 0.75 + """ + return self._config.getfloat('buildings', 'comnet_plugs_convective').real + + @property + def comnet_plugs_radiant(self) -> float: + """ + Get configured radiant ratio of internal gains do to electrical appliances used for Comnet (ASHRAE) standard + :return: 0.25 + """ + return self._config.getfloat('buildings', 'comnet_plugs_radiant').real + + @property + def comnet_occupancy_sensible_convective(self) -> float: + """ + Get configured convective ratio of the sensible part of internal gains do to occupancy + used for Comnet (ASHRAE) standard + :return: 0.9 + """ + return self._config.getfloat('buildings', 'comnet_occupancy_sensible_convective').real + + @property + def comnet_occupancy_sensible_radiant(self) -> float: + """ + Get configured radiant ratio of the sensible part of internal gains do to occupancy + used for Comnet (ASHRAE) standard + :return: 0.1 + """ + return self._config.getfloat('buildings', 'comnet_occupancy_sensible_radiant').real diff --git a/helpers/constants.py b/helpers/constants.py index 0f571a50..00416177 100644 --- a/helpers/constants.py +++ b/helpers/constants.py @@ -7,6 +7,11 @@ Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.mons # universal constants KELVIN = 273.15 +# converters +HOUR_TO_MINUTES = 60 +METERS_TO_FEET = 3.28084 +BTU_H_TO_WATTS = 0.29307107 + # time SECOND = 'second' MINUTE = 'minute' @@ -36,7 +41,6 @@ TEMPERATURE = 'temperature' HUMIDITY = 'humidity' CONTROL_TYPE = 'control_type' -# todo: modify code to use global constants instead of hard-coded values # surface types WALL = 'Wall' GROUND_WALL = 'Ground wall' @@ -76,7 +80,10 @@ HALL = 'hall' RESTAURANT = 'restaurant' EDUCATION = 'education' - - - - +LIGHTING = 'Lights' +OCCUPANCY = 'Occupancy' +RECEPTACLE = 'Receptacle' +HVAC_AVAILABILITY = 'HVAC Avail' +INFILTRATION = 'Infiltration' +COOLING_SET_POINT = 'ClgSetPt' +HEATING_SET_POINT = 'HtgSetPt' diff --git a/helpers/enrich_city.py b/helpers/enrich_city.py index e19bcf41..cc5067a1 100644 --- a/helpers/enrich_city.py +++ b/helpers/enrich_city.py @@ -55,10 +55,13 @@ class EnrichCity: def _construction(self, construction_format): ConstructionFactory(construction_format, self._city).enrich() + for building in self._city.buildings: # infiltration_rate_system_off is a mandatory parameter. # If it is not returned, extract the building from the calculation list - if building.thermal_zones[0].infiltration_rate_system_off is None: + if len(building.thermal_zones) == 0: + self._city.remove_city_object(building) + elif building.thermal_zones[0].infiltration_rate_system_off is None: self._city.remove_city_object(building) if self._city.buildings is None: self._errors.append('no archetype found per construction') diff --git a/helpers/yearly_from_daily_schedules.py b/helpers/yearly_from_daily_schedules.py new file mode 100644 index 00000000..c7c3bac5 --- /dev/null +++ b/helpers/yearly_from_daily_schedules.py @@ -0,0 +1,44 @@ +""" +Yearly from daily schedules module +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2021 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +""" +import calendar as cal +import helpers.constants as cte +from city_model_structure.attributes.schedule import Schedule + + +class YearlyFromDailySchedules: + """ + YearlyFromDailySchedules class + """ + def __init__(self, daily_schedules, year): + self._daily_schedules = daily_schedules + self._year = year + + @property + def yearly_schedule(self) -> Schedule: + """ + Creates a yearly schedule out of a set of daily schedules + :return: Schedule + """ + yearly_schedule = Schedule() + weekly_schedules = [0, 0, 0, 0, 0, 0, 0] + day_types = dict({cte.MONDAY: 0, cte.TUESDAY: 1, cte.WEDNESDAY: 2, cte.THURSDAY: 3, + cte.FRIDAY: 4, cte.SATURDAY: 5, cte.SUNDAY: 6}) + for daily_schedule in self._daily_schedules: + for day_type in daily_schedule.day_types: + weekly_schedules[day_types[day_type]] = daily_schedule.values + + values = [] + for month in range(1, 13): + for day in range(1, cal.monthlen(self._year, month)+1): + week_day = cal.weekday(self._year, month, day) + values.extend(weekly_schedules[week_day]) + yearly_schedule.type = self._daily_schedules[0].type + yearly_schedule.data_type = self._daily_schedules[0].data_type + yearly_schedule.time_range = cte.YEAR + yearly_schedule.time_step = cte.HOUR + yearly_schedule.values = values + + return yearly_schedule diff --git a/imports/customized_imports/helpers/sanam_customized_usage_helper.py b/imports/customized_imports/helpers/sanam_customized_usage_helper.py new file mode 100644 index 00000000..a0d981b8 --- /dev/null +++ b/imports/customized_imports/helpers/sanam_customized_usage_helper.py @@ -0,0 +1,38 @@ +""" +Sanam's customized importer Usage helper +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +""" +import sys +import helpers.constants as cte + + +class SanamCustomizedUsageHelper: + """ + SanamCustomizedUsage class + """ + usage_to_customized = { + cte.RESIDENTIAL: 'residential', + cte.INDUSTRY: 'manufacturing', + cte.OFFICE_ADMINISTRATION: 'office', + cte.HOTEL: 'hotel', + cte.HEALTH_CARE: 'health', + cte.RETAIL: 'retail', + cte.HALL: 'assembly', + cte.RESTAURANT: 'restaurant', + cte.EDUCATION: 'school' + } + customized_default_value = 'residential' + + @staticmethod + def customized_from_usage(usage): + """ + Get customized usage from the given internal usage key + :param usage: str + :return: str + """ + try: + return SanamCustomizedUsageHelper.usage_to_customized[usage] + except KeyError: + sys.stderr.write('Error: keyword not found. Returned default HfT usage "residential"\n') + return SanamCustomizedUsageHelper.customized_default_value diff --git a/imports/customized_imports/sanam_customized_usage_parameters.py b/imports/customized_imports/sanam_customized_usage_parameters.py new file mode 100644 index 00000000..6604a8e5 --- /dev/null +++ b/imports/customized_imports/sanam_customized_usage_parameters.py @@ -0,0 +1,82 @@ +""" +SanamCustomizedUsageParameters add two parameters to usage properties from ASHRAE +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +""" + +import sys +import xmltodict +from imports.geometry.helpers.geometry_helper import GeometryHelper as gh +from imports.usage.data_classes.hft_usage_zone_archetype import HftUsageZoneArchetype as huza +import helpers.constants as cte + + +class SanamCustomizedUsageParameters: + """ + SanamCustomizedUsageParameters class + """ + def __init__(self, city, base_path): + file = 'ashrae_archetypes.xml' + path = str(base_path / file) + self._usage_archetypes = [] + with open(path) as xml: + self._archetypes = xmltodict.parse(xml.read(), force_list=('zoneUsageVariant', 'zoneUsageType')) + for zone_usage_type in self._archetypes['buildingUsageLibrary']['zoneUsageType']: + usage = zone_usage_type['id'] + usage_archetype = self._parse_zone_usage_type(usage, zone_usage_type) + self._usage_archetypes.append(usage_archetype) + + self._city = city + + def enrich_buildings(self): + """ + Returns the city with the usage parameters assigned to the buildings + :return: + """ + city = self._city + for building in city.buildings: + archetype = self._search_archetype(building.function) # todo: building.function or other translation??????? + height = building.average_storey_height + if height is None: + raise Exception('Average storey height not defined, ACH cannot be calculated') + if height <= 0: + raise Exception('Average storey height is zero, ACH cannot be calculated') + if archetype is None: + sys.stderr.write(f'Building {building.name} has unknown archetype for building function:' + f' {building.function}, that assigns building usage as ' + f'{gh.usage_from_function(building.function)}\n') + continue + mix_usage = False + if not mix_usage: + # just one usage_zone + for usage_zone in building.usage_zones: + self._assign_values(usage_zone, archetype, height) + + def _search_archetype(self, building_usage): + for building_archetype in self._usage_archetypes: + if building_archetype.usage == building_usage: + return building_archetype + return None + + @staticmethod + def _assign_values(usage_zone, archetype, height): + usage_zone.usage = archetype.usage + # Due to the fact that python is not a typed language, the wrong object type is assigned to + # usage_zone.internal_gains when writing usage_zone.internal_gains = archetype.internal_gains. + # Therefore, this walk around has been done. + if archetype.occupancy_density is not None: + usage_zone.occupancy_density = archetype.occupancy_density + archetype_mechanical_air_change = float(archetype.mechanical_air_change) * float(usage_zone.occupancy_density) \ + * cte.HOUR_TO_MINUTES / cte.METERS_TO_FEET**3 / height + usage_zone.mechanical_air_change = archetype_mechanical_air_change + + @staticmethod + def _parse_zone_usage_type(usage, zone_usage_type): + mechanical_air_change = zone_usage_type['endUses']['ventilation']['minimumVentilationRate']['#text'] + if 'occupancy' in zone_usage_type: + occupancy_density = zone_usage_type['occupancy']['occupancyDensity']['#text'] + usage_zone_archetype = huza(usage=usage, occupancy_density=occupancy_density, + mechanical_air_change=mechanical_air_change) + else: + usage_zone_archetype = huza(usage=usage, mechanical_air_change=mechanical_air_change) + return usage_zone_archetype diff --git a/imports/customized_imports_factory.py b/imports/customized_imports_factory.py new file mode 100644 index 00000000..bca8a87c --- /dev/null +++ b/imports/customized_imports_factory.py @@ -0,0 +1,31 @@ +""" +CustomizedImportsFactory is used to import any information using user customized formats +This factory can only be called after calling the construction factory. +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +""" +from pathlib import Path + + +class CustomizedImportsFactory: + """ + CustomizedImportsFactory class + """ + def __init__(self, importer_class, city, base_path=None): + if base_path is None: + base_path = Path(Path(__file__).parent.parent / 'data/customized_imports') + self._importer_class = importer_class + self._city = city + self._base_path = base_path + for building in city.buildings: + if len(building.thermal_zones) == 0: + raise Exception('It seems that the customized imports factory is being called before the construction factory. ' + 'Please ensure that the construction factory is called first.') + + def enrich(self): + """ + Returns the class that will enrich the city given + :return: Class + """ + importer = self._importer_class(self._city, self._base_path) + return importer.enrich_buildings() diff --git a/imports/geometry/obj.py b/imports/geometry/obj.py index 5c7fdd5b..6349921b 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 = [] - _bound_corner_max = [] + _bound_corner_min = None + _bound_corner_max = None for corner in self._corners: if _bound_corner_min is None: _bound_corner_min = corner diff --git a/imports/geometry/rhino.py b/imports/geometry/rhino.py new file mode 100644 index 00000000..66f200d8 --- /dev/null +++ b/imports/geometry/rhino.py @@ -0,0 +1,77 @@ +""" +Rhino module parses rhino files and import the geometry into the city model structure +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca +""" +from rhino3dm import * +from rhino3dm._rhino3dm import MeshType +import numpy as np + +import helpers.configuration_helper +from city_model_structure.attributes.polygon import Polygon +from city_model_structure.building import Building +from city_model_structure.city import City +from city_model_structure.building_demand.surface import Surface as LibsSurface +from imports.geometry.helpers.geometry_helper import GeometryHelper +from helpers.configuration_helper import ConfigurationHelper + + +class Rhino: + def __init__(self, path): + self._model = File3dm.Read(str(path)) + max_float = 1.7976931348623157e+308 + min_float = -1.7976931348623157e+308 + self._min_x = self._min_y = self._min_z = max_float + self._max_x = self._max_y = self._max_z = min_float + print('init') + + @staticmethod + def _solid_points(coordinates) -> np.ndarray: + solid_points = np.fromstring(coordinates, dtype=float, sep=' ') + solid_points = GeometryHelper.to_points_matrix(solid_points) + return solid_points + + def _lower_corner(self, x, y, z): + if x < self._min_x: + self._min_x = x + if y < self._min_y: + self._min_y = y + if z < self._min_z: + self._min_z = z + if x > self._max_x: + self._max_x = x + if y > self._max_y: + self._max_y = y + if z > self._max_z: + self._max_z = z + + @property + def city(self) -> City: + buildings = [] + print('city') + for obj in self._model.Objects: + name = obj.Attributes.Id + surfaces = [] + for face in obj.Geometry.Faces: + if face is None: + break + _mesh = face.GetMesh(MeshType.Default) + for i in range(0, len(_mesh.Faces)): + faces = _mesh.Faces[i] + _points = '' + + for index in faces: + self._lower_corner(_mesh.Vertices[index].X, _mesh.Vertices[index].Y, _mesh.Vertices[index].Z) + _points = _points + f'{_mesh.Vertices[index].X} {_mesh.Vertices[index].Y} {_mesh.Vertices[index].Z} ' + polygon_points = Rhino._solid_points(_points.strip()) + print(_points) + surfaces.append(LibsSurface(Polygon(polygon_points), Polygon(polygon_points))) + buildings.append(Building(name, 3, surfaces, 'unknown', 'unknown', (self._min_x, self._min_y, self._min_z), [])) + lower_corner = (self._min_x, self._min_y, self._min_z) + upper_corner = (self._max_x, self._max_y, self._max_z) + city = City(lower_corner, upper_corner, 'Montreal') + for building in buildings: + city.add_city_object(building) + return city + + diff --git a/imports/geometry_factory.py b/imports/geometry_factory.py index ae2c3e29..da35c334 100644 --- a/imports/geometry_factory.py +++ b/imports/geometry_factory.py @@ -8,6 +8,7 @@ from city_model_structure.city import City from imports.geometry.citygml import CityGml from imports.geometry.obj import Obj from imports.geometry.osm_subway import OsmSubway +#from imports.geometry.rhino import Rhino class GeometryFactory: @@ -42,6 +43,14 @@ class GeometryFactory: """ return OsmSubway(self._path).city +# @property +# def _rhino(self) -> City: +# """ +# Enrich the city by using OpenStreetMap information as data source +# :return: City +# """ +# return Rhino(self._path).city + @property def city(self) -> City: """ @@ -49,3 +58,11 @@ class GeometryFactory: :return: City """ return getattr(self, self._file_type, lambda: None) + +# @property +# def city_debug(self) -> City: +# """ +# Enrich the city given to the class using the class given handler +# :return: City +# """ +# return Rhino(self._path).city diff --git a/imports/life_cycle_assessment/lca_fuel.py b/imports/life_cycle_assessment/lca_fuel.py new file mode 100644 index 00000000..c35e0749 --- /dev/null +++ b/imports/life_cycle_assessment/lca_fuel.py @@ -0,0 +1,24 @@ +""" +CityGml module parses citygml_classes files and import the geometry into the city model structure +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2020 Project Author Atiya +""" +import xmltodict +from pathlib import Path +from city_model_structure.fuel import Fuel + +class LcaFuel: + def __init__(self, city, base_path): + self._city = city + self._base_path = base_path + self._lca = None + + def enrich(self): + self._city.fuels = [] + path = Path(self._base_path / 'lca_data.xml').resolve() + + with open(path) as xml: + self._lca = xmltodict.parse(xml.read()) + for fuel in self._lca["library"]["Fuels"]['fuel']: + self._city.fuels.append(Fuel(fuel['@id'], fuel['@name'], fuel['carbon_emission_factor']['#text'], + fuel['carbon_emission_factor']['@unit'])) diff --git a/imports/life_cycle_assessment/lca_machine.py b/imports/life_cycle_assessment/lca_machine.py new file mode 100644 index 00000000..0ecae0c8 --- /dev/null +++ b/imports/life_cycle_assessment/lca_machine.py @@ -0,0 +1,26 @@ +""" +CityGml module parses citygml_classes files and import the geometry into the city model structure +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2020 Project Author Atiya +""" +import xmltodict +from pathlib import Path +from city_model_structure.machine import Machine + +class LcaMachine: + def __init__(self, city, base_path): + self._city = city + self._base_path = base_path + self._lca = None + + def enrich(self): + self._city.machines = [] + path = Path(self._base_path / 'lca_data.xml').resolve() + + with open(path) as xml: + self._lca = xmltodict.parse(xml.read()) + for machine in self._lca["library"]["Machines"]['machine']: + self._city.machines.append(Machine(machine['@id'], machine['@name'], machine['work_efficiency']['#text'], + machine['work_efficiency']['@unit'], machine['energy_consumption_rate']['#text'], + machine['energy_consumption_rate']['@unit'], machine['carbon_emission_factor']['#text'], + machine['carbon_emission_factor']['@unit'])) diff --git a/imports/life_cycle_assessment/lca_vehicle.py b/imports/life_cycle_assessment/lca_vehicle.py new file mode 100644 index 00000000..55cef922 --- /dev/null +++ b/imports/life_cycle_assessment/lca_vehicle.py @@ -0,0 +1,25 @@ +""" +CityGml module parses citygml_classes files and import the geometry into the city model structure +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2020 Project Author Atiya +""" +import xmltodict +from pathlib import Path +from city_model_structure.vehicle import Vehicle + +class LcaVehicle: + def __init__(self, city, base_path): + self._city = city + self._base_path = base_path + self._lca = None + + def enrich(self): + self._city.vehicles = [] + path = Path(self._base_path / 'lca_data.xml').resolve() + + with open(path) as xml: + self._lca = xmltodict.parse(xml.read()) + for vehicle in self._lca["library"]["Vehicles"]['vehicle']: + self._city.vehicles.append(Vehicle(vehicle['@id'], vehicle['@name'], vehicle['fuel_consumption_rate']['#text'], + vehicle['fuel_consumption_rate']['@unit'], vehicle['carbon_emission_factor']['#text'], + vehicle['carbon_emission_factor']['@unit'])) diff --git a/imports/life_cycle_assessment_factory.py b/imports/life_cycle_assessment_factory.py new file mode 100644 index 00000000..66e1f609 --- /dev/null +++ b/imports/life_cycle_assessment_factory.py @@ -0,0 +1,54 @@ +""" +ConstructionFactory (before PhysicsFactory) retrieve the specific construction module for the given region +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2020 Project Author Atiya +""" + +from pathlib import Path +from imports.life_cycle_assessment.lca_fuel import LcaFuel +from imports.life_cycle_assessment.lca_vehicle import LcaVehicle +from imports.life_cycle_assessment.lca_machine import LcaMachine +# from imports.life_cycle_assessment.lca_material import LcaMaterial + + +class LifeCycleAssessment: + """ + Life cicle analize factory class + """ + def __init__(self, handler, city, base_path=None): + if base_path is None: + base_path = Path(Path(__file__).parent.parent / 'data/life_cycle_assessment') + self._handler = '_' + handler.lower().replace(' ', '_') + self._city = city + self._base_path = base_path + + def _fuel(self): + """ + Enrich the city by adding the fuel carbon information + """ + LcaFuel(self._city, self._base_path).enrich() + + def _vehicle(self): + """ + Enrich the city by adding the vehicle carbon information + """ + LcaVehicle(self._city, self._base_path).enrich() + + def _machine(self): + """ + Enrich the city by adding the machine carbon information + """ + LcaMachine(self._city, self._base_path).enrich() + + def _material(self): + """ + Enrich the city by adding the material carbon information + """ + LcaMaterial(self._city, self._base_path).enrich() + + def enrich(self): + """ + Enrich the city given to the class using the class given handler + :return: None + """ + getattr(self, self._handler, lambda: None)() diff --git a/imports/schedules/helpers/schedules_helper.py b/imports/schedules/helpers/schedules_helper.py index a36c2fa6..fa47b343 100644 --- a/imports/schedules/helpers/schedules_helper.py +++ b/imports/schedules/helpers/schedules_helper.py @@ -33,28 +33,26 @@ class SchedulesHelper: # usage function_to_usage = { - 'full service restaurant': 'restaurant', + 'full service restaurant': cte.RESTAURANT, 'high-rise apartment': cte.RESIDENTIAL, - 'hospital': 'health care', - 'large hotel': 'hotel', - 'large office': 'office and administration', - 'medium office': 'office and administration', + 'hospital': cte.HEALTH_CARE, + 'large hotel': cte.HOTEL, + 'large office': cte.OFFICE_ADMINISTRATION, + 'medium office': cte.OFFICE_ADMINISTRATION, 'midrise apartment': cte.RESIDENTIAL, - 'outpatient healthcare': 'health care', - 'primary school': 'education', - 'quick service restaurant': 'restaurant', - 'secondary school': 'education', - 'small hotel': 'hotel', - 'small office': 'office and administration', - 'stand-alone-retail': 'retail', - 'strip mall': 'hall', - 'supermarket': 'retail', - 'warehouse': 'industry', + 'outpatient healthcare': cte.HEALTH_CARE, + 'primary school': cte.EDUCATION, + 'quick service restaurant': cte.RESTAURANT, + 'secondary school': cte.EDUCATION, + 'small hotel': cte.HOTEL, + 'small office': cte.OFFICE_ADMINISTRATION, + 'stand-alone-retail': cte.RETAIL, + 'strip mall': cte.HALL, + 'supermarket': cte.RETAIL, + 'warehouse': cte.INDUSTRY, 'residential': cte.RESIDENTIAL } - - @staticmethod def comnet_from_usage(usage): """ diff --git a/imports/usage/ashrae_usage_parameters.py b/imports/usage/ashrae_usage_parameters.py deleted file mode 100644 index 1bfb98bd..00000000 --- a/imports/usage/ashrae_usage_parameters.py +++ /dev/null @@ -1,55 +0,0 @@ -""" -AshraeUsageParameters model the usage properties -SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca -""" -import sys - -from imports.geometry.helpers.geometry_helper import GeometryHelper as gh -from city_model_structure.building_demand.usage_zone import UsageZone - - -class AshraeUsageParameters: - """ - AshraeUsageParameters class - """ - def __init__(self, city, base_path): - super().__init__(base_path, 'ashrae_archetypes.xml') - self._city = city - self._usage_archetypes = None - - def enrich_buildings(self): - """ - Returns the city with the usage parameters assigned to the buildings - :return: - """ - city = self._city - for building in city.buildings: - archetype = self._search_archetype(building.function) - if archetype is None: - sys.stderr.write(f'Building {building.name} has unknown archetype for building function:' - f' {building.function}, that assigns building usage as ' - f'{gh.usage_from_function(building.function)}\n') - continue - # todo: what to do with mix-usage usage from gml? - mix_usage = False - if not mix_usage: - # just one usage_zone - for thermal_zone in building.thermal_zones: - usage_zone = UsageZone() - usage_zone.volume = thermal_zone.volume - self._assign_values(usage_zone, archetype) - thermal_zone.usage_zones = [usage_zone] - - def _search_archetype(self, building_usage): - for building_archetype in self._usage_archetypes: - if building_archetype.usage == building_usage: - return building_archetype - return None - - @staticmethod - def _assign_values(usage_zone, archetype): - usage_zone.usage = archetype.usage - usage_zone.occupancy_density = archetype.occupancy_density - # todo: should I use this value: self._min_air_change?? - usage_zone.minimum_ventilation_rate = archetype.minimum_ventilation_rate diff --git a/imports/usage/ca_usage_parameters.py b/imports/usage/ca_usage_parameters.py index 1bbe5d58..01cc85a5 100644 --- a/imports/usage/ca_usage_parameters.py +++ b/imports/usage/ca_usage_parameters.py @@ -41,8 +41,8 @@ class CaUsageParameters(HftUsageInterface): # just one usage_zone for thermal_zone in building.thermal_zones: usage_zone = UsageZone() - usage_zone.volume = thermal_zone.volume self._assign_values(usage_zone, archetype) + usage_zone.volume = thermal_zone.volume thermal_zone.usage_zones = [usage_zone] def _search_archetype(self, building_usage): diff --git a/imports/usage/comnet_usage_parameters.py b/imports/usage/comnet_usage_parameters.py new file mode 100644 index 00000000..5d10312c --- /dev/null +++ b/imports/usage/comnet_usage_parameters.py @@ -0,0 +1,162 @@ +""" +ComnetUsageParameters model the usage properties +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2021 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +""" +import sys +from typing import Dict +import pandas as pd + +import helpers.constants as cte +from helpers.configuration_helper import ConfigurationHelper as ch +from imports.geometry.helpers.geometry_helper import GeometryHelper +from imports.usage.helpers.usage_helper import UsageHelper +from city_model_structure.building_demand.usage_zone import UsageZone +from city_model_structure.building_demand.internal_gains import InternalGains +from imports.usage.data_classes.hft_usage_zone_archetype import HftUsageZoneArchetype as huza +from imports.usage.data_classes.hft_internal_gains_archetype import HftInternalGainsArchetype as higa + + +class ComnetUsageParameters: + """ + ComnetUsageParameters class + """ + def __init__(self, city, base_path): + self._city = city + self._base_path = str(base_path / 'comnet_archetypes.xlsx') + self._usage_archetypes = [] + data = self._read_file() + for item in data['lighting']: + for usage in UsageHelper.usage_to_comnet: + comnet_usage = UsageHelper.usage_to_comnet[usage] + if comnet_usage == item: + usage_archetype = self._parse_zone_usage_type(comnet_usage, data) + self._usage_archetypes.append(usage_archetype) + + def _read_file(self) -> Dict: + """ + reads xlsx file containing usage information into a dictionary + :return : Dict + """ + number_usage_types = 33 + xl_file = pd.ExcelFile(self._base_path) + file_data = pd.read_excel(xl_file, sheet_name="Modeling Data", skiprows=[0, 1, 2], + nrows=number_usage_types, usecols="A:Z") + + lighting_data = {} + plug_loads_data = {} + occupancy_data = {} + ventilation_rate = {} + water_heating = {} + process_data = {} + + for j in range(0, number_usage_types): + usage_parameters = file_data.iloc[j] + usage_type = usage_parameters[0] + lighting_data[usage_type] = usage_parameters[1:6].values.tolist() + plug_loads_data[usage_type] = usage_parameters[8:13].values.tolist() + occupancy_data[usage_type] = usage_parameters[17:20].values.tolist() + ventilation_rate[usage_type] = usage_parameters[20:21].values.tolist() + water_heating[usage_type] = usage_parameters[23:24].values.tolist() + process_data[usage_type] = usage_parameters[24:26].values.tolist() + + return {'lighting': lighting_data, + 'plug loads': plug_loads_data, + 'occupancy': occupancy_data, + 'ventilation rate': ventilation_rate, + 'water heating': water_heating, + 'process': process_data} + + @staticmethod + def _parse_zone_usage_type(usage, data): + if data['occupancy'][usage][0] <= 0: + occupancy_density = 0 + else: + occupancy_density = 1 / data['occupancy'][usage][0] + mechanical_air_change = data['ventilation rate'][usage][0] + internal_gains = [] + # lighting + latent_fraction = ch().comnet_lighting_latent + convective_fraction = ch().comnet_lighting_convective + radiative_fraction = ch().comnet_lighting_radiant + average_internal_gain = data['lighting'][usage][4] + internal_gains.append(higa(internal_gains_type=cte.LIGHTING, average_internal_gain=average_internal_gain, + convective_fraction=convective_fraction, radiative_fraction=radiative_fraction, + latent_fraction=latent_fraction)) + # occupancy + latent_fraction = data['occupancy'][usage][2] / (data['occupancy'][usage][1] + data['occupancy'][usage][2]) + sensible_fraction = float(1 - latent_fraction) + convective_fraction = sensible_fraction * ch().comnet_occupancy_sensible_convective + radiative_fraction = sensible_fraction * ch().comnet_occupancy_sensible_radiant + average_internal_gain = (data['occupancy'][usage][1] + data['occupancy'][usage][2]) \ + * occupancy_density * cte.BTU_H_TO_WATTS + internal_gains.append(higa(internal_gains_type=cte.OCCUPANCY, average_internal_gain=average_internal_gain, + convective_fraction=convective_fraction, radiative_fraction=radiative_fraction, + latent_fraction=latent_fraction)) + # plug loads + if data['plug loads'][usage][0] != 'n.a.': + latent_fraction = ch().comnet_plugs_latent + convective_fraction = ch().comnet_plugs_convective + radiative_fraction = ch().comnet_plugs_radiant + average_internal_gain = data['plug loads'][usage][0] + internal_gains.append(higa(internal_gains_type=cte.RECEPTACLE, average_internal_gain=average_internal_gain, + convective_fraction=convective_fraction, radiative_fraction=radiative_fraction, + latent_fraction=latent_fraction)) + + usage_zone_archetype = huza(usage=usage, internal_gains=internal_gains, + occupancy_density=occupancy_density, + mechanical_air_change=mechanical_air_change) + return usage_zone_archetype + + def enrich_buildings(self): + """ + Returns the city with the usage parameters assigned to the buildings + :return: + """ + city = self._city + for building in city.buildings: + usage = GeometryHelper.usage_from_function(building.function) + height = building.average_storey_height + if height is None: + raise Exception('Average storey height not defined, ACH cannot be calculated') + if height <= 0: + raise Exception('Average storey height is zero, ACH cannot be calculated') + archetype = self._search_archetype(UsageHelper.comnet_from_usage(usage)) + if archetype is None: + sys.stderr.write(f'Building {building.name} has unknown archetype for building function:' + f' {building.function}, that assigns building usage as ' + f'{GeometryHelper.usage_from_function(building.function)}\n') + continue + + # just one usage_zone + for thermal_zone in building.thermal_zones: + usage_zone = UsageZone() + usage_zone.usage = usage + self._assign_values(usage_zone, archetype, height) + usage_zone.volume = thermal_zone.volume + thermal_zone.usage_zones = [usage_zone] + + def _search_archetype(self, building_usage): + for building_archetype in self._usage_archetypes: + if building_archetype.usage == building_usage: + return building_archetype + return None + + @staticmethod + def _assign_values(usage_zone, archetype, height): + # Due to the fact that python is not a typed language, the wrong object type is assigned to + # usage_zone.internal_gains when writing usage_zone.internal_gains = archetype.internal_gains. + # Therefore, this walk around has been done. + internal_gains = [] + for archetype_internal_gain in archetype.internal_gains: + internal_gain = InternalGains() + internal_gain.type = archetype_internal_gain.type + internal_gain.average_internal_gain = archetype_internal_gain.average_internal_gain * cte.METERS_TO_FEET**2 + 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.occupancy_density = archetype.occupancy_density * cte.METERS_TO_FEET**2 + usage_zone.mechanical_air_change = archetype.mechanical_air_change * usage_zone.occupancy_density \ + * cte.HOUR_TO_MINUTES / cte.METERS_TO_FEET**3 / height diff --git a/imports/usage/data_classes/hft_internal_gains_archetype.py b/imports/usage/data_classes/hft_internal_gains_archetype.py index c9de13b0..cc21a77a 100644 --- a/imports/usage/data_classes/hft_internal_gains_archetype.py +++ b/imports/usage/data_classes/hft_internal_gains_archetype.py @@ -9,13 +9,30 @@ class HftInternalGainsArchetype: """ HftInternalGainsArchetype class """ - def __init__(self, average_internal_gain=None, convective_fraction=None, radiative_fraction=None, - latent_fraction=None): + def __init__(self, internal_gains_type=None, average_internal_gain=None, convective_fraction=None, \ + radiative_fraction=None, latent_fraction=None): + self._type = internal_gains_type self._average_internal_gain = average_internal_gain self._convective_fraction = convective_fraction self._radiative_fraction = radiative_fraction self._latent_fraction = latent_fraction + @property + def type(self): + """ + Get internal gains type + :return: string + """ + return self._type + + @type.setter + def type(self, value): + """ + Set internal gains type + :param value: string + """ + self._type = value + @property def average_internal_gain(self): """ diff --git a/imports/usage/helpers/usage_helper.py b/imports/usage/helpers/usage_helper.py index b46b08d3..310b4888 100644 --- a/imports/usage/helpers/usage_helper.py +++ b/imports/usage/helpers/usage_helper.py @@ -36,3 +36,29 @@ class UsageHelper: except KeyError: sys.stderr.write('Error: keyword not found. Returned default HfT usage "residential"\n') return UsageHelper.hft_default_value + + usage_to_comnet = { + cte.RESIDENTIAL: 'BA Multifamily', + cte.INDUSTRY: 'BA Manufacturing Facility', + cte.OFFICE_ADMINISTRATION: 'BA Office', + cte.HOTEL: 'BA Hotel', + cte.HEALTH_CARE: 'BA Hospital', + cte.RETAIL: 'BA Retail', + cte.HALL: 'BA Town Hall', + cte.RESTAURANT: 'BA Dining: Bar Lounge/Leisure', + cte.EDUCATION: 'BA School/University' + } + comnet_default_value = 'BA Multifamily' + + @staticmethod + def comnet_from_usage(usage): + """ + Get Comnet usage from the given internal usage key + :param usage: str + :return: str + """ + try: + return UsageHelper.usage_to_comnet[usage] + except KeyError: + sys.stderr.write('Error: keyword not found. Returned default Comnet usage "BA Multifamily"\n') + return UsageHelper.comnet_default_value diff --git a/imports/usage/hft_usage_interface.py b/imports/usage/hft_usage_interface.py index b6223295..3da6457d 100644 --- a/imports/usage/hft_usage_interface.py +++ b/imports/usage/hft_usage_interface.py @@ -68,7 +68,8 @@ class HftUsageInterface: average_internal_gain = 0 radiative_fraction = 0 - internal_gains.append(higa(average_internal_gain, convective_fraction, radiative_fraction, latent_fraction)) + internal_gains.append(higa(average_internal_gain=average_internal_gain, convective_fraction=convective_fraction, + radiative_fraction=radiative_fraction, latent_fraction=latent_fraction)) usage_zone_archetype = huza(usage=usage, internal_gains=internal_gains, heating_set_point=heating_setpoint, heating_set_back=heating_setback, cooling_set_point=cooling_setpoint, occupancy_density=occupancy_density, hours_day=hours_day, days_year=days_year, @@ -130,7 +131,8 @@ class HftUsageInterface: average_internal_gain = usage_zone_variant['schedules']['internGains']['averageInternGainPerSqm'] if 'radiantFraction' in usage_zone_variant['schedules']['internGains']: radiative_fraction = usage_zone_variant['schedules']['internGains']['radiantFraction'] - internal_gains.append(higa(average_internal_gain, convective_fraction, radiative_fraction, latent_fraction)) + internal_gains.append(higa(average_internal_gain=average_internal_gain, convective_fraction=convective_fraction, + radiative_fraction=radiative_fraction, latent_fraction=latent_fraction)) usage_zone_archetype = huza(usage=usage, internal_gains=internal_gains, heating_set_point=heating_setpoint, heating_set_back=heating_setback, cooling_set_point=cooling_setpoint, occupancy_density=occupancy_density, hours_day=hours_day, days_year=days_year, diff --git a/imports/usage_factory.py b/imports/usage_factory.py index 763a6dba..0e2875de 100644 --- a/imports/usage_factory.py +++ b/imports/usage_factory.py @@ -8,7 +8,7 @@ Contributors Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca from pathlib import Path from imports.usage.hft_usage_parameters import HftUsageParameters from imports.usage.ca_usage_parameters import CaUsageParameters -from imports.usage.ashrae_usage_parameters import AshraeUsageParameters +from imports.usage.comnet_usage_parameters import ComnetUsageParameters # todo: handle missing lambda and rise error. @@ -39,11 +39,11 @@ class UsageFactory: """ return CaUsageParameters(self._city, self._base_path).enrich_buildings() - def _ashrae(self): + def _comnet(self): """ - Enrich the city by using ASHRAE information + Enrich the city with COMNET usage library """ - AshraeUsageParameters(self._city, self._base_path).enrich_buildings() + return ComnetUsageParameters(self._city, self._base_path).enrich_buildings() def enrich(self): """ diff --git a/requirements.txt b/requirements.txt index 8a6287d3..15593f09 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,9 +9,9 @@ geomeppy~=0.11.8 pathlib~=1.0.1 PyWavefront~=1.3.3 xlrd~=2.0.1 -openpyxl~=3.0.7 networkx~=2.5.1 parseidf~=1.0.0 ply~=3.11 scipy==1.7.1 -PyYAML==6.0 \ No newline at end of file +PyYAML==6.0 +rhino3dm~=7.7.0 \ No newline at end of file diff --git a/unittests/test_customized_imports_factory.py b/unittests/test_customized_imports_factory.py new file mode 100644 index 00000000..80373480 --- /dev/null +++ b/unittests/test_customized_imports_factory.py @@ -0,0 +1,49 @@ +""" +TestCustomizedImportsFactory tests and validates the factory to import customized data +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2021 Project Author 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.construction_factory import ConstructionFactory +from imports.usage_factory import UsageFactory +from imports.customized_imports_factory import CustomizedImportsFactory +from imports.customized_imports.sanam_customized_usage_parameters import SanamCustomizedUsageParameters as scp + + +class TestCustomizedImportsFactory(TestCase): + """ + TestCustomizedImportsFactory 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', file_path).city + self.assertIsNotNone(_city, 'city is none') + ConstructionFactory('nrel', _city).enrich() + UsageFactory('hft', _city).enrich() + + return _city + + def test_city_with_customized_data(self): + """ + Enrich the city with the usage information and verify it + :return: None + """ + + file = 'one_building_in_kelowna.gml' + city = self._get_citygml(file) + + CustomizedImportsFactory(scp, city).enrich() + for building in city.buildings: + self.assertIsNot(len(building.usage_zones), 0, 'no building usage_zones defined') + for usage_zone in building.usage_zones: + self.assertIsNotNone(usage_zone.mechanical_air_change, 'usage is none') diff --git a/unittests/test_exports.py b/unittests/test_exports.py index 5b25b4e1..b4c38be1 100644 --- a/unittests/test_exports.py +++ b/unittests/test_exports.py @@ -45,7 +45,7 @@ class TestExports(TestCase): else: file_path = (self._example_path / 'one_building_in_kelowna.gml').resolve() self._complete_city = self._get_citygml(file_path) - ConstructionFactory('ca', self._complete_city).enrich() + ConstructionFactory('nrel', self._complete_city).enrich() UsageFactory('ca', self._complete_city).enrich() SchedulesFactory('comnet', self._complete_city).enrich() cli = 'C:\\Users\\Pilar\\PycharmProjects\\monthlyenergybalance\\tests_data\\weather\\inseldb_Summerland.cli' diff --git a/unittests/test_life_cycle_assessment_factory.py b/unittests/test_life_cycle_assessment_factory.py new file mode 100644 index 00000000..3245d56b --- /dev/null +++ b/unittests/test_life_cycle_assessment_factory.py @@ -0,0 +1,58 @@ +""" +Building test +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2020 Project Author Guille Gutierrez Morote Guillermo.GutierrezMorote@concordia.ca +""" +from pathlib import Path +from unittest import TestCase +from imports.geometry_factory import GeometryFactory +from imports.life_cycle_assessment_factory import LifeCycleAssessment + + +class TestLifeCycleAssessment(TestCase): + """ + TestBuilding TestCase 1 + """ + def setUp(self) -> None: + """ + Test setup + :return: None + """ + self._city_gml = None + self._example_path = (Path(__file__).parent / 'tests_data').resolve() + + def test_fuel(self): + city_file = "../unittests/tests_data/C40_Final.gml" + city = GeometryFactory('citygml', city_file).city + LifeCycleAssessment('fuel', city).enrich() + for fuel in city.fuels: + # print(fuel.name) + self.assertTrue(len(city.fuels) > 0) + + def test_vehicle(self): + city_file = "../unittests/tests_data/C40_Final.gml" + city = GeometryFactory('citygml', city_file).city + LifeCycleAssessment('vehicle', city).enrich() + for vehicle in city.vehicles: + # print(vehicle.name) + self.assertTrue(len(city.vehicles) > 0) + + def test_machine(self): + city_file = "../unittests/tests_data/C40_Final.gml" + city = GeometryFactory('citygml', city_file).city + LifeCycleAssessment('machine', city).enrich() + for machine in city.machines: + # print(machine.name) + self.assertTrue(len(city.machines) > 0) + + def test_material(self): + city_file = "../unittests/tests_data/C40_Final.gml" + city = GeometryFactory('citygml', city_file).city + LifeCycleAssessment('material', city).enrich() + for material in city.materials: + print(material.material_name) + self.assertTrue(len(city.materials) > 0) + + + + diff --git a/unittests/test_usage_factory.py b/unittests/test_usage_factory.py index 82c692a1..7281f86d 100644 --- a/unittests/test_usage_factory.py +++ b/unittests/test_usage_factory.py @@ -9,6 +9,7 @@ from unittest import TestCase from imports.geometry_factory import GeometryFactory from imports.usage_factory import UsageFactory from imports.geometry.helpers.geometry_helper import GeometryHelper +from imports.construction_factory import ConstructionFactory class TestUsageFactory(TestCase): @@ -38,7 +39,7 @@ class TestUsageFactory(TestCase): city = self._get_citygml(file) for building in city.buildings: building.function = GeometryHelper.pluto_to_function[building.function] - + ConstructionFactory('nrel', city).enrich() UsageFactory('hft', city).enrich() for building in city.buildings: self.assertIsNot(len(building.usage_zones), 0, 'no building usage_zones defined') @@ -61,7 +62,7 @@ class TestUsageFactory(TestCase): # case 2: CA file = 'one_building_in_kelowna.gml' city = self._get_citygml(file) - + ConstructionFactory('nrel', city).enrich() UsageFactory('ca', city).enrich() for building in city.buildings: self.assertIsNot(len(building.usage_zones), 0, 'no building usage_zones defined') diff --git a/unittests/tests_data/lca_data.xml b/unittests/tests_data/lca_data.xml new file mode 100644 index 00000000..bc3ab8ee --- /dev/null +++ b/unittests/tests_data/lca_data.xml @@ -0,0 +1,573 @@ + + + + + 0.32 + + + 0.4 + + + 0.4 + + + 0.5 + + + 0.18 + + + 0.39 + + + 0.27 + + + 4.16 + + + 2.24 + + + 0.2 + + + 3.19 + + + 3.53 + + + 0.27 + + + 0.21 + + + 1.69 + + + 0.21 + + + 0.35 + + + 0.18 + + + 0.81 + + + 1.21 + + + 1.61 + + + 0.11 + + + 0.3 + + + 1.16 + + + 0.61 + + + + + 0.347 + 16.5 + 0.918 + + + 0.033 + 25.2 + 4.16 + + + 0.027 + 16.8 + 2.239 + + + 0.023 + 16.8 + 2.239 + + + 0.109 + 25.2 + 2.239 + + + 0.003 + 16.4 + 4.16 + + + 0.002 + 11 + 0.918 + + + 0.002 + 90 + 0.918 + + + 0.002 + 10 + 0.918 + + + 0.002 + 11 + 0.918 + + + 0.002 + 132 + 0.918 + + + 0.002 + 15 + 0.918 + + + 0.002 + 5.5 + 0.918 + + + 0.002 + 22.5 + 0.918 + + + + + 0.0123 + 2.239 + + + 0.042 + 0.918 + + + 0.01 + 1.00000 + + + 1.3 + 1.00000 + + + + + + 1.8 + 560 + 0.8 + 0.3 + 0.7 + 0.2 + .... + + + 1.2 + 310 + 0.8 + 0.3 + 0.7 + 0.2 + .... + + + 2 + 3080 + 0.8 + 0.3 + 0.7 + 0.2 + .... + + + 1.4 + 300 + 0.8 + 0.3 + 0.7 + 0.2 + .... + + + + + 1.6 + 900 + 0.8 + 0 + 1 + 0.2 + .... + + + 1.6 + 2340 + 0.8 + 0 + 1 + 0.2 + .... + + + 1.6 + 1570 + 0.8 + 0 + 1 + 0.2 + .... + + + 1.4 + 1840 + 0.8 + 0 + 1 + 0.2 + .... + + + 1.3 + 410 + 0.8 + 0 + 1 + 0.2 + .... + + + 2.3 + 160 + 0.8 + 0 + 1 + 0.2 + .... + + + 2.3 + 170 + 0.8 + 0 + 1 + 0.2 + .... + + + 2.3 + 230 + 0.8 + 0 + 1 + 0.2 + .... + + + 2.4 + 240 + 0.8 + 0 + 1 + 0.2 + .... + + + 2.4 + 280 + 0.8 + 0 + 1 + 0.2 + .... + + + 2.3 + 170 + 0.8 + 0 + 1 + 0.2 + .... + + + 1.2 + 440 + 0.8 + 0 + 1 + 0.2 + .... + + + + + 2.58 + 2660 + 0.95 + 0 + 1 + 0.05 + .... + + + 2.58 + 5260 + 0.95 + 0 + 1 + 0.05 + .... + + + + + 0.06 + 1760 + 0.9 + 0 + 1 + 0.1 + .... + + + 0.122 + 3080 + 0.9 + 0 + 1 + 0.1 + .... + + + 0.028 + 3180 + 0.9 + 0 + 1 + 0.1 + .... + + + 0.024 + 5140 + 0.9 + 0 + 1 + 0.1 + .... + + + 0.1 + 6040 + 0.9 + 0 + 1 + 0.1 + .... + + + 0.3 + 5380 + 0.9 + 0 + 1 + 0.1 + .... + + + 0.032 + 2150 + 0.9 + 0 + 1 + 0.1 + .... + + + + + 0.9 + 3420 + 0.6 + 0 + 1 + 0.4 + .... + + + 0.7 + 1430 + 0.6 + 0 + 1 + 0.4 + .... + + + 0.65 + 2780 + 0.6 + 0 + 1 + 0.4 + .... + + + 0.72 + 2190 + 0.6 + 0 + 1 + 0.4 + .... + + + + + 1.43 + 1070 + 0 + 0 + 0 + 1 + .... + + + 1.43 + 240 + 0 + 0 + 0 + 1 + .... + + + 1.43 + 430 + 0 + 0 + 0 + 1 + .... + + + 1.43 + 340 + 0 + 0 + 0 + 1 + .... + + + 1.2 + 440 + 0.8 + 0 + 1 + 0.2 + .... + + + 2.1 + 1410 + 0.8 + 0 + 1 + 0.2 + .... + + + 1.43 + 250 + 0 + 0 + 0 + 1 + .... + + + 1.44 + 1480 + 0.8 + 0 + 1 + 0.2 + .... + + + 1.44 + 2220 + 0.8 + 0 + 1 + 0.2 + .... + + + 1.27 + 3960 + 0.8 + 0 + 1 + 0.2 + .... + + + 1.15 + 760 + 0.8 + 0 + 1 + 0.2 + .... + + + + + 8 + 3160 + 0.98 + 0 + 1 + 0.02 + .... + + + 2.7 + 5370 + 0.98 + 0 + 1 + 0.02 + .... + + + 7.85 + 3910 + 0.98 + 0 + 1 + 0.02 + .... + + + + \ No newline at end of file