Merge branch 'retrofit_project' into idf_lights
This commit is contained in:
commit
844835eabd
|
@ -44,6 +44,8 @@ class Building(CityObject):
|
||||||
self._lighting_electrical_demand = dict()
|
self._lighting_electrical_demand = dict()
|
||||||
self._appliances_electrical_demand = dict()
|
self._appliances_electrical_demand = dict()
|
||||||
self._domestic_hot_water_heat_demand = dict()
|
self._domestic_hot_water_heat_demand = dict()
|
||||||
|
self._heating_peak_load = dict()
|
||||||
|
self._cooling_peak_load = dict()
|
||||||
self._eave_height = None
|
self._eave_height = None
|
||||||
self._grounds = []
|
self._grounds = []
|
||||||
self._roofs = []
|
self._roofs = []
|
||||||
|
@ -362,6 +364,38 @@ class Building(CityObject):
|
||||||
"""
|
"""
|
||||||
self._domestic_hot_water_heat_demand = value
|
self._domestic_hot_water_heat_demand = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def heating_peak_load(self) -> dict:
|
||||||
|
"""
|
||||||
|
Get heating peak load in W
|
||||||
|
:return: dict{DataFrame(float)}
|
||||||
|
"""
|
||||||
|
return self._heating_peak_load
|
||||||
|
|
||||||
|
@heating_peak_load.setter
|
||||||
|
def heating_peak_load(self, value):
|
||||||
|
"""
|
||||||
|
Set heating peak load in W
|
||||||
|
:param value: dict{DataFrame(float)}
|
||||||
|
"""
|
||||||
|
self._heating_peak_load = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cooling_peak_load(self) -> dict:
|
||||||
|
"""
|
||||||
|
Get cooling peak load in W
|
||||||
|
:return: dict{DataFrame(float)}
|
||||||
|
"""
|
||||||
|
return self._cooling_peak_load
|
||||||
|
|
||||||
|
@cooling_peak_load.setter
|
||||||
|
def cooling_peak_load(self, value):
|
||||||
|
"""
|
||||||
|
Set peak load in W
|
||||||
|
:param value: dict{DataFrame(float)}
|
||||||
|
"""
|
||||||
|
self._cooling_peak_load = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def eave_height(self):
|
def eave_height(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -41,7 +41,7 @@ class City:
|
||||||
self._name = None
|
self._name = None
|
||||||
self._lower_corner = lower_corner
|
self._lower_corner = lower_corner
|
||||||
self._upper_corner = upper_corner
|
self._upper_corner = upper_corner
|
||||||
self._buildings = None
|
self._buildings = []
|
||||||
self._srs_name = srs_name
|
self._srs_name = srs_name
|
||||||
self._location = None
|
self._location = None
|
||||||
self._country_code = None
|
self._country_code = None
|
||||||
|
|
|
@ -110,6 +110,14 @@ class CityObject:
|
||||||
"""
|
"""
|
||||||
return self._surfaces
|
return self._surfaces
|
||||||
|
|
||||||
|
@surfaces.setter
|
||||||
|
def surfaces(self, value):
|
||||||
|
"""
|
||||||
|
Set city object surfaces
|
||||||
|
:return: [Surface]
|
||||||
|
"""
|
||||||
|
self._surfaces = value
|
||||||
|
|
||||||
def surface(self, name) -> Union[Surface, None]:
|
def surface(self, name) -> Union[Surface, None]:
|
||||||
"""
|
"""
|
||||||
Get the city object surface with a given name
|
Get the city object surface with a given name
|
||||||
|
|
26456
hub/data/geolocation/cities15000.txt
Normal file
26456
hub/data/geolocation/cities15000.txt
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -185,6 +185,7 @@ MIN_FLOAT = float('-inf')
|
||||||
# Tools
|
# Tools
|
||||||
SRA = 'sra'
|
SRA = 'sra'
|
||||||
INSEL_MEB = 'insel meb'
|
INSEL_MEB = 'insel meb'
|
||||||
|
PEAK_LOAD = 'peak load'
|
||||||
|
|
||||||
# Costs units
|
# Costs units
|
||||||
CURRENCY_PER_SQM = 'currency/m2'
|
CURRENCY_PER_SQM = 'currency/m2'
|
||||||
|
|
|
@ -6,9 +6,10 @@ Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
"""
|
"""
|
||||||
import math
|
import math
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import requests
|
from PIL import Image
|
||||||
from trimesh import Trimesh
|
from trimesh import Trimesh
|
||||||
from trimesh import intersections
|
from trimesh import intersections
|
||||||
|
|
||||||
|
@ -79,14 +80,15 @@ class GeometryHelper:
|
||||||
y = int((city.upper_corner[1] - city.lower_corner[1]) * 0.5) + 1
|
y = int((city.upper_corner[1] - city.lower_corner[1]) * 0.5) + 1
|
||||||
city_map = [['' for _ in range(y + 1)] for _ in range(x + 1)]
|
city_map = [['' for _ in range(y + 1)] for _ in range(x + 1)]
|
||||||
map_info = [[{} for _ in range(y + 1)] for _ in range(x + 1)]
|
map_info = [[{} for _ in range(y + 1)] for _ in range(x + 1)]
|
||||||
# img = Image.new('RGB', (x + 1, y + 1), "black") # create a new black image
|
img = Image.new('RGB', (x + 1, y + 1), "black") # create a new black image
|
||||||
# city_image = img.load() # create the pixel map
|
city_image = img.load() # create the pixel map
|
||||||
for building_name in building_names:
|
for building_name in building_names:
|
||||||
building = city.city_object(building_name)
|
building = city.city_object(building_name)
|
||||||
line = 0
|
line = 0
|
||||||
for ground in building.grounds:
|
for ground in building.grounds:
|
||||||
length = len(ground.perimeter_polygon.coordinates) - 1
|
length = len(ground.perimeter_polygon.coordinates) - 1
|
||||||
for i, coordinate in enumerate(ground.perimeter_polygon.coordinates):
|
for i, coordinate in enumerate(ground.perimeter_polygon.coordinates):
|
||||||
|
|
||||||
j = i + 1
|
j = i + 1
|
||||||
if i == length:
|
if i == length:
|
||||||
j = 0
|
j = 0
|
||||||
|
@ -106,7 +108,7 @@ class GeometryHelper:
|
||||||
'line_start': (coordinate[0], coordinate[1]),
|
'line_start': (coordinate[0], coordinate[1]),
|
||||||
'line_end': (next_coordinate[0], next_coordinate[1]),
|
'line_end': (next_coordinate[0], next_coordinate[1]),
|
||||||
}
|
}
|
||||||
# city_image[x, y] = (100, 0, 0)
|
city_image[x, y] = (100, 0, 0)
|
||||||
elif city_map[x][y] != building.name:
|
elif city_map[x][y] != building.name:
|
||||||
neighbour = city.city_object(city_map[x][y])
|
neighbour = city.city_object(city_map[x][y])
|
||||||
neighbour_info = map_info[x][y]
|
neighbour_info = map_info[x][y]
|
||||||
|
@ -166,8 +168,51 @@ class GeometryHelper:
|
||||||
elif building not in neighbour.neighbours:
|
elif building not in neighbour.neighbours:
|
||||||
neighbour.neighbours.append(building)
|
neighbour.neighbours.append(building)
|
||||||
line += 1
|
line += 1
|
||||||
# if plot:
|
|
||||||
# img.show()
|
if plot:
|
||||||
|
img.show()
|
||||||
|
return lines_information
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def fast_city_mapping(city, building_names=None):
|
||||||
|
lines_information = {}
|
||||||
|
if building_names is None:
|
||||||
|
building_names = [b.name for b in city.buildings]
|
||||||
|
x = int((city.upper_corner[0] - city.lower_corner[0]) * 0.5) + 1
|
||||||
|
y = int((city.upper_corner[1] - city.lower_corner[1]) * 0.5) + 1
|
||||||
|
city_map = [['' for _ in range(y + 1)] for _ in range(x + 1)]
|
||||||
|
for building_name in building_names:
|
||||||
|
building = city.city_object(building_name)
|
||||||
|
line = 0
|
||||||
|
for ground in building.grounds:
|
||||||
|
length = len(ground.perimeter_polygon.coordinates) - 1
|
||||||
|
for i, coordinate in enumerate(ground.perimeter_polygon.coordinates):
|
||||||
|
j = i + 1
|
||||||
|
if i == length:
|
||||||
|
j = 0
|
||||||
|
next_coordinate = ground.perimeter_polygon.coordinates[j]
|
||||||
|
point = GeometryHelper.coordinate_to_map_point(coordinate, city)
|
||||||
|
distance = int(GeometryHelper.distance_between_points(coordinate, next_coordinate))
|
||||||
|
if distance == 0:
|
||||||
|
continue
|
||||||
|
delta_x = (coordinate[0] - next_coordinate[0]) / (distance / 0.5)
|
||||||
|
delta_y = (coordinate[1] - next_coordinate[1]) / (distance / 0.5)
|
||||||
|
for k in range(0, distance):
|
||||||
|
x = MapPoint(point.x + (delta_x * k), point.y + (delta_y * k)).x
|
||||||
|
y = MapPoint(point.x + (delta_x * k), point.y + (delta_y * k)).y
|
||||||
|
if city_map[x][y] == '':
|
||||||
|
city_map[x][y] = building.name
|
||||||
|
elif city_map[x][y] != building.name:
|
||||||
|
neighbour = city.city_object(city_map[x][y])
|
||||||
|
if building.neighbours is None:
|
||||||
|
building.neighbours = [neighbour]
|
||||||
|
elif neighbour not in building.neighbours:
|
||||||
|
building.neighbours.append(neighbour)
|
||||||
|
if neighbour.neighbours is None:
|
||||||
|
neighbour.neighbours = [building]
|
||||||
|
elif building not in neighbour.neighbours:
|
||||||
|
neighbour.neighbours.append(building)
|
||||||
|
line += 1
|
||||||
return lines_information
|
return lines_information
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -254,19 +299,24 @@ class GeometryHelper:
|
||||||
"""
|
"""
|
||||||
Get Location from latitude and longitude
|
Get Location from latitude and longitude
|
||||||
"""
|
"""
|
||||||
url = 'https://nominatim.openstreetmap.org/reverse?lat={latitude}&lon={longitude}&format=json'
|
_data_path = Path(Path(__file__).parent.parent / 'data/geolocation/cities15000.txt').resolve()
|
||||||
response = requests.get(url.format(latitude=latitude, longitude=longitude))
|
latitude = float(latitude)
|
||||||
if response.status_code != 200:
|
longitude = float(longitude)
|
||||||
# This means something went wrong.
|
distance = math.inf
|
||||||
raise Exception('GET /tasks/ {}'.format(response.status_code))
|
country = 'Unknown'
|
||||||
|
|
||||||
response = response.json()
|
|
||||||
city = 'Unknown'
|
city = 'Unknown'
|
||||||
country = 'ca'
|
with open(_data_path, 'r') as f:
|
||||||
if 'city' in response['address']:
|
for line_number, line in enumerate(f):
|
||||||
city = response['address']['city']
|
fields = line.split('\t')
|
||||||
if 'country_code' in response['address']:
|
file_city_name = fields[2]
|
||||||
country = response['address']['country_code']
|
file_latitude = float(fields[4])
|
||||||
|
file_longitude = float(fields[5])
|
||||||
|
file_country_code = fields[8]
|
||||||
|
new_distance = math.sqrt(pow((latitude - file_latitude), 2) + pow((longitude - file_longitude), 2))
|
||||||
|
if distance > new_distance:
|
||||||
|
distance = new_distance
|
||||||
|
country = file_country_code
|
||||||
|
city = file_city_name
|
||||||
return Location(country, city)
|
return Location(country, city)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
@ -29,6 +29,7 @@ class Geojson:
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
path,
|
path,
|
||||||
|
name_field=None,
|
||||||
extrusion_height_field=None,
|
extrusion_height_field=None,
|
||||||
year_of_construction_field=None,
|
year_of_construction_field=None,
|
||||||
function_field=None,
|
function_field=None,
|
||||||
|
@ -41,6 +42,7 @@ class Geojson:
|
||||||
self._max_y = cte.MIN_FLOAT
|
self._max_y = cte.MIN_FLOAT
|
||||||
self._max_z = 0
|
self._max_z = 0
|
||||||
self._city = None
|
self._city = None
|
||||||
|
self._name_field = name_field
|
||||||
self._extrusion_height_field = extrusion_height_field
|
self._extrusion_height_field = extrusion_height_field
|
||||||
self._year_of_construction_field = year_of_construction_field
|
self._year_of_construction_field = year_of_construction_field
|
||||||
self._function_field = function_field
|
self._function_field = function_field
|
||||||
|
@ -69,8 +71,11 @@ class Geojson:
|
||||||
polygon = Polygon(points)
|
polygon = Polygon(points)
|
||||||
polygon.area = igh.ground_area(points)
|
polygon.area = igh.ground_area(points)
|
||||||
surface = Surface(polygon, polygon)
|
surface = Surface(polygon, polygon)
|
||||||
|
if len(buildings) == 1:
|
||||||
|
buildings[0].surfaces.append(surface)
|
||||||
|
else:
|
||||||
surfaces.append(surface)
|
surfaces.append(surface)
|
||||||
buildings.append(Building(f'{name}_zone_{zone}', surfaces, year_of_construction, function))
|
buildings.append(Building(f'{name}', surfaces, year_of_construction, function))
|
||||||
return buildings
|
return buildings
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -80,9 +85,10 @@ class Geojson:
|
||||||
buildings = []
|
buildings = []
|
||||||
|
|
||||||
for zone, lod0_building in enumerate(lod0_buildings):
|
for zone, lod0_building in enumerate(lod0_buildings):
|
||||||
|
# print(zone, lod0_building.name)
|
||||||
|
volume = 0
|
||||||
for surface in lod0_building.grounds:
|
for surface in lod0_building.grounds:
|
||||||
|
volume = volume + surface.solid_polygon.area * height
|
||||||
volume = surface.solid_polygon.area * height
|
|
||||||
surfaces.append(surface)
|
surfaces.append(surface)
|
||||||
roof_coordinates = []
|
roof_coordinates = []
|
||||||
# adding a roof means invert the polygon coordinates and change the Z value
|
# adding a roof means invert the polygon coordinates and change the Z value
|
||||||
|
@ -110,8 +116,7 @@ class Geojson:
|
||||||
polygon = Polygon(wall_coordinates)
|
polygon = Polygon(wall_coordinates)
|
||||||
wall = Surface(polygon, polygon)
|
wall = Surface(polygon, polygon)
|
||||||
surfaces.append(wall)
|
surfaces.append(wall)
|
||||||
|
building = Building(f'{name}', surfaces, year_of_construction, function)
|
||||||
building = Building(f'{name}_zone_{zone}', surfaces, year_of_construction, function)
|
|
||||||
building.volume = volume
|
building.volume = volume
|
||||||
buildings.append(building)
|
buildings.append(building)
|
||||||
|
|
||||||
|
@ -209,12 +214,14 @@ class Geojson:
|
||||||
else:
|
else:
|
||||||
building_name = f'building_{building_id}'
|
building_name = f'building_{building_id}'
|
||||||
building_id += 1
|
building_id += 1
|
||||||
|
if self._name_field is not None:
|
||||||
|
building_name = feature['properties'][self._name_field]
|
||||||
polygons = []
|
polygons = []
|
||||||
for part, coordinates in enumerate(geometry['coordinates']):
|
for part, coordinates in enumerate(geometry['coordinates']):
|
||||||
polygons = self._get_polygons(polygons, coordinates)
|
polygons = self._get_polygons(polygons, coordinates)
|
||||||
for zone, polygon in enumerate(polygons):
|
for polygon in polygons:
|
||||||
if extrusion_height == 0:
|
if extrusion_height == 0:
|
||||||
buildings = buildings + Geojson._create_buildings_lod0(f'{building_name}_part_{part}',
|
buildings = buildings + Geojson._create_buildings_lod0(f'{building_name}',
|
||||||
year_of_construction,
|
year_of_construction,
|
||||||
function,
|
function,
|
||||||
[polygon])
|
[polygon])
|
||||||
|
@ -222,14 +229,27 @@ class Geojson:
|
||||||
else:
|
else:
|
||||||
if self._max_z < extrusion_height:
|
if self._max_z < extrusion_height:
|
||||||
self._max_z = extrusion_height
|
self._max_z = extrusion_height
|
||||||
buildings = buildings + Geojson._create_buildings_lod1(f'{building_name}_part_{part}',
|
if part == 0:
|
||||||
|
buildings = buildings + Geojson._create_buildings_lod1(f'{building_name}',
|
||||||
year_of_construction,
|
year_of_construction,
|
||||||
function,
|
function,
|
||||||
extrusion_height,
|
extrusion_height,
|
||||||
[polygon])
|
[polygon])
|
||||||
|
else:
|
||||||
|
new_part = Geojson._create_buildings_lod1(f'{building_name}',
|
||||||
|
year_of_construction,
|
||||||
|
function,
|
||||||
|
extrusion_height,
|
||||||
|
[polygon])
|
||||||
|
surfaces = buildings[len(buildings) - 1].surfaces + new_part[0].surfaces
|
||||||
|
volume = buildings[len(buildings) - 1].volume + new_part[0].volume
|
||||||
|
buildings[len(buildings) - 1] = Building(f'{building_name}', surfaces, year_of_construction, function)
|
||||||
|
buildings[len(buildings) - 1].volume = volume
|
||||||
|
|
||||||
self._city = City([self._min_x, self._min_y, 0.0], [self._max_x, self._max_y, self._max_z], 'epsg:26911')
|
self._city = City([self._min_x, self._min_y, 0.0], [self._max_x, self._max_y, self._max_z], 'epsg:26911')
|
||||||
for building in buildings:
|
for building in buildings:
|
||||||
|
# Do not include "small building-like structures" to buildings
|
||||||
|
if building.floor_area >= 25:
|
||||||
self._city.add_city_object(building)
|
self._city.add_city_object(building)
|
||||||
self._city.level_of_detail.geometry = lod
|
self._city.level_of_detail.geometry = lod
|
||||||
if lod == 1:
|
if lod == 1:
|
||||||
|
|
|
@ -23,6 +23,7 @@ class GeometryFactory:
|
||||||
def __init__(self, file_type,
|
def __init__(self, file_type,
|
||||||
path=None,
|
path=None,
|
||||||
data_frame=None,
|
data_frame=None,
|
||||||
|
name_field=None,
|
||||||
height_field=None,
|
height_field=None,
|
||||||
year_of_construction_field=None,
|
year_of_construction_field=None,
|
||||||
function_field=None,
|
function_field=None,
|
||||||
|
@ -35,6 +36,7 @@ class GeometryFactory:
|
||||||
raise Exception(err_msg)
|
raise Exception(err_msg)
|
||||||
self._path = path
|
self._path = path
|
||||||
self._data_frame = data_frame
|
self._data_frame = data_frame
|
||||||
|
self._name_field = name_field
|
||||||
self._height_field = height_field
|
self._height_field = height_field
|
||||||
self._year_of_construction_field = year_of_construction_field
|
self._year_of_construction_field = year_of_construction_field
|
||||||
self._function_field = function_field
|
self._function_field = function_field
|
||||||
|
@ -77,6 +79,7 @@ class GeometryFactory:
|
||||||
:return: City
|
:return: City
|
||||||
"""
|
"""
|
||||||
return Geojson(self._path,
|
return Geojson(self._path,
|
||||||
|
self._name_field,
|
||||||
self._height_field,
|
self._height_field,
|
||||||
self._year_of_construction_field,
|
self._year_of_construction_field,
|
||||||
self._function_field,
|
self._function_field,
|
||||||
|
@ -104,6 +107,9 @@ class GeometryFactory:
|
||||||
Enrich the city given to the class using the class given handler
|
Enrich the city given to the class using the class given handler
|
||||||
:return: City
|
:return: City
|
||||||
"""
|
"""
|
||||||
if self._data_frame is None:
|
return Geojson(self._path,
|
||||||
self._data_frame = geopandas.read_file(self._path)
|
self._name_field,
|
||||||
return GPandas(self._data_frame).city
|
self._height_field,
|
||||||
|
self._year_of_construction_field,
|
||||||
|
self._function_field,
|
||||||
|
self._function_to_hub).city
|
||||||
|
|
|
@ -4,7 +4,6 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
Copyright © 2022 Concordia CERC group
|
Copyright © 2022 Concordia CERC group
|
||||||
Project Coder Guillermo.GutierrezMorote@concordia.ca
|
Project Coder Guillermo.GutierrezMorote@concordia.ca
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import csv
|
import csv
|
||||||
|
@ -40,60 +39,55 @@ class InselMonthlyEnergyBalance:
|
||||||
monthly_cooling = pd.DataFrame(cooling, columns=[cte.INSEL_MEB]).astype(float)
|
monthly_cooling = pd.DataFrame(cooling, columns=[cte.INSEL_MEB]).astype(float)
|
||||||
return monthly_heating, monthly_cooling
|
return monthly_heating, monthly_cooling
|
||||||
|
|
||||||
def _dhw_demand(self):
|
def _dhw_and_electric_demand(self):
|
||||||
for building in self._city.buildings:
|
for building in self._city.buildings:
|
||||||
domestic_hot_water_demand = []
|
domestic_hot_water_demand = []
|
||||||
if building.internal_zones[0].thermal_zones is None:
|
|
||||||
domestic_hot_water_demand = [0] * 12
|
|
||||||
else:
|
|
||||||
thermal_zone = building.internal_zones[0].thermal_zones[0]
|
|
||||||
area = thermal_zone.total_floor_area
|
|
||||||
cold_water = building.cold_water_temperature[cte.MONTH]['epw']
|
|
||||||
for month in range(0, 12):
|
|
||||||
total_dhw_demand = 0
|
|
||||||
for schedule in thermal_zone.domestic_hot_water.schedules:
|
|
||||||
total_day = 0
|
|
||||||
for value in schedule.values:
|
|
||||||
total_day += value
|
|
||||||
for day_type in schedule.day_types:
|
|
||||||
demand = thermal_zone.domestic_hot_water.peak_flow * cte.WATER_DENSITY * cte.WATER_HEAT_CAPACITY \
|
|
||||||
* (thermal_zone.domestic_hot_water.service_temperature - cold_water[month])
|
|
||||||
total_dhw_demand += total_day * cte.DAYS_A_MONTH[day_type][month] * demand
|
|
||||||
domestic_hot_water_demand.append(total_dhw_demand * area)
|
|
||||||
|
|
||||||
building.domestic_hot_water_heat_demand[cte.MONTH] = \
|
|
||||||
pd.DataFrame(domestic_hot_water_demand, columns=[cte.INSEL_MEB])
|
|
||||||
|
|
||||||
def _electrical_demand(self):
|
|
||||||
for building in self._city.buildings:
|
|
||||||
lighting_demand = []
|
lighting_demand = []
|
||||||
appliances_demand = []
|
appliances_demand = []
|
||||||
if building.internal_zones[0].thermal_zones is None:
|
if building.internal_zones[0].thermal_zones is None:
|
||||||
|
domestic_hot_water_demand = [0] * 12
|
||||||
lighting_demand = [0] * 12
|
lighting_demand = [0] * 12
|
||||||
appliances_demand = [0] * 12
|
appliances_demand = [0] * 12
|
||||||
else:
|
else:
|
||||||
thermal_zone = building.internal_zones[0].thermal_zones[0]
|
thermal_zone = building.internal_zones[0].thermal_zones[0]
|
||||||
area = thermal_zone.total_floor_area
|
area = thermal_zone.total_floor_area
|
||||||
|
cold_water = building.cold_water_temperature[cte.MONTH]['epw']
|
||||||
|
peak_flow = thermal_zone.domestic_hot_water.peak_flow
|
||||||
|
service_temperature = thermal_zone.domestic_hot_water.service_temperature
|
||||||
|
lighting_density = thermal_zone.lighting.density
|
||||||
|
appliances_density = thermal_zone.appliances.density
|
||||||
|
|
||||||
for month in range(0, 12):
|
for month in range(0, 12):
|
||||||
|
total_dhw_demand = 0
|
||||||
total_lighting = 0
|
total_lighting = 0
|
||||||
|
total_appliances = 0
|
||||||
|
|
||||||
for schedule in thermal_zone.lighting.schedules:
|
for schedule in thermal_zone.lighting.schedules:
|
||||||
total_day = 0
|
total_day = 0
|
||||||
for value in schedule.values:
|
for value in schedule.values:
|
||||||
total_day += value
|
total_day += value
|
||||||
for day_type in schedule.day_types:
|
for day_type in schedule.day_types:
|
||||||
total_lighting += total_day * cte.DAYS_A_MONTH[day_type][month] * thermal_zone.lighting.density
|
total_lighting += total_day * cte.DAYS_A_MONTH[day_type][month] * lighting_density
|
||||||
lighting_demand.append(total_lighting * area)
|
lighting_demand.append(total_lighting * area)
|
||||||
|
|
||||||
total_appliances = 0
|
|
||||||
for schedule in thermal_zone.appliances.schedules:
|
for schedule in thermal_zone.appliances.schedules:
|
||||||
total_day = 0
|
total_day = 0
|
||||||
for value in schedule.values:
|
for value in schedule.values:
|
||||||
total_day += value
|
total_day += value
|
||||||
for day_type in schedule.day_types:
|
for day_type in schedule.day_types:
|
||||||
total_appliances += total_day * cte.DAYS_A_MONTH[day_type][month] * thermal_zone.appliances.density
|
total_appliances += total_day * cte.DAYS_A_MONTH[day_type][month] * appliances_density
|
||||||
appliances_demand.append(total_appliances * area)
|
appliances_demand.append(total_appliances * area)
|
||||||
|
|
||||||
|
for schedule in thermal_zone.domestic_hot_water.schedules:
|
||||||
|
total_day = 0
|
||||||
|
for value in schedule.values:
|
||||||
|
total_day += value
|
||||||
|
for day_type in schedule.day_types:
|
||||||
|
demand = peak_flow * cte.WATER_DENSITY * cte.WATER_HEAT_CAPACITY * (service_temperature - cold_water[month])
|
||||||
|
total_dhw_demand += total_day * cte.DAYS_A_MONTH[day_type][month] * demand
|
||||||
|
domestic_hot_water_demand.append(total_dhw_demand * area)
|
||||||
|
|
||||||
|
building.domestic_hot_water_heat_demand[cte.MONTH] = pd.DataFrame(domestic_hot_water_demand, columns=[cte.INSEL_MEB])
|
||||||
building.lighting_electrical_demand[cte.MONTH] = pd.DataFrame(lighting_demand, columns=[cte.INSEL_MEB])
|
building.lighting_electrical_demand[cte.MONTH] = pd.DataFrame(lighting_demand, columns=[cte.INSEL_MEB])
|
||||||
building.appliances_electrical_demand[cte.MONTH] = pd.DataFrame(appliances_demand, columns=[cte.INSEL_MEB])
|
building.appliances_electrical_demand[cte.MONTH] = pd.DataFrame(appliances_demand, columns=[cte.INSEL_MEB])
|
||||||
|
|
||||||
|
@ -109,5 +103,4 @@ class InselMonthlyEnergyBalance:
|
||||||
building.cooling[cte.YEAR] = pd.DataFrame(
|
building.cooling[cte.YEAR] = pd.DataFrame(
|
||||||
[building.cooling[cte.MONTH][cte.INSEL_MEB].astype(float).sum()], columns=[cte.INSEL_MEB]
|
[building.cooling[cte.MONTH][cte.INSEL_MEB].astype(float).sum()], columns=[cte.INSEL_MEB]
|
||||||
)
|
)
|
||||||
self._dhw_demand()
|
self._dhw_and_electric_demand()
|
||||||
self._electrical_demand()
|
|
||||||
|
|
0
hub/imports/results/peak_calculation/__init__.py
Normal file
0
hub/imports/results/peak_calculation/__init__.py
Normal file
118
hub/imports/results/peak_calculation/loads_calculation.py
Normal file
118
hub/imports/results/peak_calculation/loads_calculation.py
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
"""
|
||||||
|
Calculation of loads for peak heating and cooling
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
import hub.helpers.constants as cte
|
||||||
|
|
||||||
|
|
||||||
|
class LoadsCalculation:
|
||||||
|
"""
|
||||||
|
LoadsCalculation class
|
||||||
|
"""
|
||||||
|
def __init__(self, building):
|
||||||
|
self._building = building
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_load_transmitted(thermal_zone, internal_temperature, ambient_temperature, ground_temperature):
|
||||||
|
load_transmitted_opaque = 0
|
||||||
|
load_transmitted_transparent = 0
|
||||||
|
for thermal_boundary in thermal_zone.thermal_boundaries:
|
||||||
|
if thermal_boundary.type == cte.GROUND:
|
||||||
|
external_temperature = ground_temperature
|
||||||
|
elif thermal_boundary.type == cte.INTERIOR_WALL:
|
||||||
|
external_temperature = internal_temperature
|
||||||
|
else:
|
||||||
|
external_temperature = ambient_temperature
|
||||||
|
|
||||||
|
load_transmitted_opaque += thermal_boundary.u_value * thermal_boundary.opaque_area \
|
||||||
|
* (internal_temperature - external_temperature)
|
||||||
|
for thermal_opening in thermal_boundary.thermal_openings:
|
||||||
|
load_transmitted_transparent += thermal_opening.overall_u_value \
|
||||||
|
* (internal_temperature - external_temperature)
|
||||||
|
load_transmitted_opaque += thermal_zone.additional_thermal_bridge_u_value * thermal_zone.footprint_area \
|
||||||
|
* (internal_temperature - ambient_temperature)
|
||||||
|
load_transmitted = load_transmitted_opaque + load_transmitted_transparent
|
||||||
|
return load_transmitted
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_load_ventilation(thermal_zone, internal_temperature, ambient_temperature):
|
||||||
|
load_renovation_sensible = 0
|
||||||
|
for usage in thermal_zone.usages:
|
||||||
|
load_renovation_sensible += cte.AIR_DENSITY * cte.AIR_HEAT_CAPACITY * usage.mechanical_air_change \
|
||||||
|
* thermal_zone.volume / cte.HOUR_TO_MINUTES / cte.MINUTES_TO_SECONDS \
|
||||||
|
* (internal_temperature - ambient_temperature)
|
||||||
|
|
||||||
|
load_infiltration_sensible = cte.AIR_DENSITY * cte.AIR_HEAT_CAPACITY * thermal_zone.infiltration_rate_system_off \
|
||||||
|
* thermal_zone.volume / cte.HOUR_TO_MINUTES / cte.MINUTES_TO_SECONDS \
|
||||||
|
* (internal_temperature - ambient_temperature)
|
||||||
|
|
||||||
|
load_ventilation = load_renovation_sensible + load_infiltration_sensible
|
||||||
|
|
||||||
|
return load_ventilation
|
||||||
|
|
||||||
|
def get_heating_transmitted_load(self, ambient_temperature, ground_temperature):
|
||||||
|
heating_load_transmitted = 0
|
||||||
|
for internal_zone in self._building.internal_zones:
|
||||||
|
for thermal_zone in internal_zone.thermal_zones:
|
||||||
|
internal_temperature = thermal_zone.thermal_control.mean_heating_set_point
|
||||||
|
heating_load_transmitted += self._get_load_transmitted(thermal_zone, internal_temperature, ambient_temperature,
|
||||||
|
ground_temperature)
|
||||||
|
return heating_load_transmitted
|
||||||
|
|
||||||
|
def get_cooling_transmitted_load(self, ambient_temperature, ground_temperature):
|
||||||
|
cooling_load_transmitted = 0
|
||||||
|
for internal_zone in self._building.internal_zones:
|
||||||
|
for thermal_zone in internal_zone.thermal_zones:
|
||||||
|
internal_temperature = thermal_zone.thermal_control.mean_cooling_set_point
|
||||||
|
cooling_load_transmitted += self._get_load_transmitted(thermal_zone, internal_temperature, ambient_temperature,
|
||||||
|
ground_temperature)
|
||||||
|
return cooling_load_transmitted
|
||||||
|
|
||||||
|
def get_heating_ventilation_load_sensible(self, ambient_temperature):
|
||||||
|
heating_ventilation_load = 0
|
||||||
|
for internal_zone in self._building.internal_zones:
|
||||||
|
for thermal_zone in internal_zone.thermal_zones:
|
||||||
|
internal_temperature = thermal_zone.thermal_control.mean_heating_set_point
|
||||||
|
heating_ventilation_load += self._get_load_ventilation(thermal_zone, internal_temperature, ambient_temperature)
|
||||||
|
return heating_ventilation_load
|
||||||
|
|
||||||
|
def get_cooling_ventilation_load_sensible(self, ambient_temperature):
|
||||||
|
cooling_ventilation_load = 0
|
||||||
|
for internal_zone in self._building.internal_zones:
|
||||||
|
for thermal_zone in internal_zone.thermal_zones:
|
||||||
|
internal_temperature = thermal_zone.thermal_control.mean_cooling_set_point
|
||||||
|
cooling_ventilation_load += self._get_load_ventilation(thermal_zone, internal_temperature, ambient_temperature)
|
||||||
|
return cooling_ventilation_load
|
||||||
|
|
||||||
|
def get_internal_load_sensible(self):
|
||||||
|
cooling_load_occupancy_sensible = 0
|
||||||
|
cooling_load_lighting = 0
|
||||||
|
cooling_load_equipment_sensible = 0
|
||||||
|
for internal_zone in self._building.internal_zones:
|
||||||
|
for thermal_zone in internal_zone.thermal_zones:
|
||||||
|
cooling_load_occupancy_sensible += (thermal_zone.occupancy.sensible_convective_internal_gain
|
||||||
|
+ thermal_zone.occupancy.sensible_radiative_internal_gain) \
|
||||||
|
* thermal_zone.footprint_area
|
||||||
|
cooling_load_lighting += (thermal_zone.lighting.density * thermal_zone.lighting.convective_fraction
|
||||||
|
+ thermal_zone.lighting.density * thermal_zone.lighting.radiative_fraction) \
|
||||||
|
* thermal_zone.footprint_area
|
||||||
|
cooling_load_equipment_sensible += (thermal_zone.appliances.density * thermal_zone.appliances.convective_fraction
|
||||||
|
+ thermal_zone.appliances.density * thermal_zone.appliances.radiative_fraction) \
|
||||||
|
* thermal_zone.footprint_area
|
||||||
|
internal_load = cooling_load_occupancy_sensible + cooling_load_lighting + cooling_load_equipment_sensible
|
||||||
|
return internal_load
|
||||||
|
|
||||||
|
def get_radiation_load(self, irradiance_format, hour):
|
||||||
|
cooling_load_radiation = 0
|
||||||
|
for internal_zone in self._building.internal_zones:
|
||||||
|
for thermal_zone in internal_zone.thermal_zones:
|
||||||
|
for thermal_boundary in thermal_zone.thermal_boundaries:
|
||||||
|
for thermal_opening in thermal_boundary.thermal_openings:
|
||||||
|
radiation = thermal_boundary.parent_surface.global_irradiance[cte.HOUR][irradiance_format][hour]
|
||||||
|
cooling_load_radiation += thermal_opening.area * (1 - thermal_opening.frame_ratio) * thermal_opening.g_value \
|
||||||
|
* radiation
|
||||||
|
return cooling_load_radiation
|
61
hub/imports/results/peak_load.py
Normal file
61
hub/imports/results/peak_load.py
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
import hub.helpers.constants as cte
|
||||||
|
from hub.imports.results.peak_calculation.loads_calculation import LoadsCalculation
|
||||||
|
|
||||||
|
|
||||||
|
class PeakLoad:
|
||||||
|
|
||||||
|
_MONTH_STARTING_HOUR = [0, 744, 1416, 2160, 2880, 3624, 4344, 5088, 5832, 6552, 7296, 8016]
|
||||||
|
|
||||||
|
def __init__(self, city):
|
||||||
|
self._city = city
|
||||||
|
self._weather_format = 'epw'
|
||||||
|
|
||||||
|
def enrich(self):
|
||||||
|
for building in self._city.buildings:
|
||||||
|
monthly_heating_loads = []
|
||||||
|
monthly_cooling_loads = []
|
||||||
|
ambient_temperature = building.external_temperature[cte.HOUR][self._weather_format]
|
||||||
|
for month in range(0, 12):
|
||||||
|
ground_temperature = building.ground_temperature[cte.MONTH]['2'][month]
|
||||||
|
heating_ambient_temperature = 100
|
||||||
|
cooling_ambient_temperature = -100
|
||||||
|
heating_calculation_hour = -1
|
||||||
|
cooling_calculation_hour = -1
|
||||||
|
start_hour = self._MONTH_STARTING_HOUR[month]
|
||||||
|
end_hour = 8760
|
||||||
|
if month < 11:
|
||||||
|
end_hour = self._MONTH_STARTING_HOUR[month + 1]
|
||||||
|
for hour in range(start_hour, end_hour):
|
||||||
|
temperature = ambient_temperature[hour]
|
||||||
|
if temperature < heating_ambient_temperature:
|
||||||
|
heating_ambient_temperature = temperature
|
||||||
|
heating_calculation_hour = hour
|
||||||
|
if temperature > cooling_ambient_temperature:
|
||||||
|
cooling_ambient_temperature = temperature
|
||||||
|
cooling_calculation_hour = hour
|
||||||
|
|
||||||
|
loads = LoadsCalculation(building)
|
||||||
|
heating_load_transmitted = loads.get_heating_transmitted_load(heating_ambient_temperature, ground_temperature)
|
||||||
|
heating_load_ventilation_sensible = loads.get_heating_ventilation_load_sensible(heating_ambient_temperature)
|
||||||
|
heating_load_ventilation_latent = 0
|
||||||
|
heating_load = heating_load_transmitted + heating_load_ventilation_sensible + heating_load_ventilation_latent
|
||||||
|
|
||||||
|
cooling_load_transmitted = loads.get_cooling_transmitted_load(cooling_ambient_temperature, ground_temperature)
|
||||||
|
cooling_load_renovation_sensible = loads.get_cooling_ventilation_load_sensible(cooling_ambient_temperature)
|
||||||
|
cooling_load_internal_gains_sensible = loads.get_internal_load_sensible()
|
||||||
|
cooling_load_radiation = loads.get_radiation_load(self._irradiance_format, cooling_calculation_hour)
|
||||||
|
cooling_load_sensible = cooling_load_transmitted + cooling_load_renovation_sensible - cooling_load_radiation \
|
||||||
|
- cooling_load_internal_gains_sensible
|
||||||
|
|
||||||
|
cooling_load_latent = 0
|
||||||
|
cooling_load = cooling_load_sensible + cooling_load_latent
|
||||||
|
if heating_load < 0:
|
||||||
|
heating_load = 0
|
||||||
|
if cooling_load > 0:
|
||||||
|
cooling_load = 0
|
||||||
|
monthly_heating_loads.append(heating_load)
|
||||||
|
monthly_cooling_loads.append(cooling_load)
|
||||||
|
|
||||||
|
self._results[building.name] = {'monthly heating peak load': monthly_heating_loads,
|
||||||
|
'monthly cooling peak load': monthly_cooling_loads}
|
||||||
|
self._print_results()
|
|
@ -9,6 +9,7 @@ from pathlib import Path
|
||||||
|
|
||||||
from hub.helpers.utils import validate_import_export_type
|
from hub.helpers.utils import validate_import_export_type
|
||||||
from hub.hub_logger import logger
|
from hub.hub_logger import logger
|
||||||
|
from hub.imports.results.peak_load import PeakLoad
|
||||||
from hub.imports.results.simplified_radiosity_algorithm import SimplifiedRadiosityAlgorithm
|
from hub.imports.results.simplified_radiosity_algorithm import SimplifiedRadiosityAlgorithm
|
||||||
from hub.imports.results.insel_monthly_energry_balance import InselMonthlyEnergyBalance
|
from hub.imports.results.insel_monthly_energry_balance import InselMonthlyEnergyBalance
|
||||||
from hub.imports.results.insel_heatpump_energy_demand import InselHeatPumpEnergyDemand
|
from hub.imports.results.insel_heatpump_energy_demand import InselHeatPumpEnergyDemand
|
||||||
|
@ -59,6 +60,12 @@ class ResultFactory:
|
||||||
"""
|
"""
|
||||||
InselMonthlyEnergyBalance(self._city, self._base_path).enrich()
|
InselMonthlyEnergyBalance(self._city, self._base_path).enrich()
|
||||||
|
|
||||||
|
def _peak_load(self):
|
||||||
|
"""
|
||||||
|
Enrich the city with peak load results
|
||||||
|
"""
|
||||||
|
PeakLoad(self._city).enrich()
|
||||||
|
|
||||||
def enrich(self):
|
def enrich(self):
|
||||||
"""
|
"""
|
||||||
Enrich the city given to the class using the usage factory given handler
|
Enrich the city given to the class using the usage factory given handler
|
||||||
|
|
|
@ -106,10 +106,12 @@ class TestExports(TestCase):
|
||||||
path=file_path,
|
path=file_path,
|
||||||
function_to_hub=Dictionaries().alkis_function_to_hub_function).city
|
function_to_hub=Dictionaries().alkis_function_to_hub_function).city
|
||||||
self.assertIsNotNone(city, 'city is none')
|
self.assertIsNotNone(city, 'city is none')
|
||||||
|
EnergyBuildingsExportsFactory('idf', city, self._output_path).export()
|
||||||
ConstructionFactory('nrcan', city).enrich()
|
ConstructionFactory('nrcan', city).enrich()
|
||||||
|
EnergyBuildingsExportsFactory('idf', city, self._output_path).export()
|
||||||
UsageFactory('nrcan', city).enrich()
|
UsageFactory('nrcan', city).enrich()
|
||||||
try:
|
try:
|
||||||
EnergyBuildingsExportsFactory('idf', city, self._output_path).export_debug()
|
EnergyBuildingsExportsFactory('idf', city, self._output_path).export()
|
||||||
except Exception:
|
except Exception:
|
||||||
self.fail("Idf ExportsFactory raised ExceptionType unexpectedly!")
|
self.fail("Idf ExportsFactory raised ExceptionType unexpectedly!")
|
||||||
|
|
||||||
|
|
|
@ -139,9 +139,13 @@ class TestGeometryFactory(TestCase):
|
||||||
path=(self._example_path / file).resolve(),
|
path=(self._example_path / file).resolve(),
|
||||||
height_field='building_height',
|
height_field='building_height',
|
||||||
year_of_construction_field='ANNEE_CONS',
|
year_of_construction_field='ANNEE_CONS',
|
||||||
|
name_field='ID_UEV',
|
||||||
function_field='CODE_UTILI',
|
function_field='CODE_UTILI',
|
||||||
function_to_hub=MontrealFunctionToHubFunction().dictionary).city
|
function_to_hub=MontrealFunctionToHubFunction().dictionary).city
|
||||||
self.assertEqual(2356, len(city.buildings), 'wrong number of buildings')
|
# include 25 square meter condition for a building reduces buildings number from 2289 to 2057
|
||||||
|
for building in city.buildings:
|
||||||
|
print(building.name)
|
||||||
|
self.assertEqual(2057, len(city.buildings), 'wrong number of buildings')
|
||||||
|
|
||||||
def test_map_neighbours(self):
|
def test_map_neighbours(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
__version__ = '0.1.7.10'
|
__version__ = '0.1.7.11'
|
||||||
|
|
|
@ -24,3 +24,5 @@ geopandas
|
||||||
triangle
|
triangle
|
||||||
psycopg2-binary
|
psycopg2-binary
|
||||||
Pillow
|
Pillow
|
||||||
|
pathlib
|
||||||
|
pickle5
|
2
setup.py
2
setup.py
|
@ -80,12 +80,14 @@ setup(
|
||||||
'hub.imports'
|
'hub.imports'
|
||||||
],
|
],
|
||||||
setup_requires=install_requires,
|
setup_requires=install_requires,
|
||||||
|
install_requires=install_requires,
|
||||||
data_files=[
|
data_files=[
|
||||||
('hub', glob.glob('requirements.txt')),
|
('hub', glob.glob('requirements.txt')),
|
||||||
('hub/config', glob.glob('hub/config/*.ini')),
|
('hub/config', glob.glob('hub/config/*.ini')),
|
||||||
('hub/catalog_factories/greenery/ecore_greenery', glob.glob('hub/catalog_factories/greenery/ecore_greenery/*.ecore')),
|
('hub/catalog_factories/greenery/ecore_greenery', glob.glob('hub/catalog_factories/greenery/ecore_greenery/*.ecore')),
|
||||||
('hub/data/construction.', glob.glob('hub/data/construction/*')),
|
('hub/data/construction.', glob.glob('hub/data/construction/*')),
|
||||||
('hub/data/customized_imports', glob.glob('hub/data/customized_imports/*.xml')),
|
('hub/data/customized_imports', glob.glob('hub/data/customized_imports/*.xml')),
|
||||||
|
('data/geolocation', glob.glob('hub/data/geolocation/*.txt')),
|
||||||
('hub/data/energy_systems', glob.glob('hub/data/energy_systems/*.xml')),
|
('hub/data/energy_systems', glob.glob('hub/data/energy_systems/*.xml')),
|
||||||
('hub/data/energy_systems', glob.glob('hub/data/energy_systems/*.insel')),
|
('hub/data/energy_systems', glob.glob('hub/data/energy_systems/*.insel')),
|
||||||
('hub/data/energy_systems', glob.glob('hub/data/energy_systems/*.xlsx')),
|
('hub/data/energy_systems', glob.glob('hub/data/energy_systems/*.xlsx')),
|
||||||
|
|
Loading…
Reference in New Issue
Block a user