Merge branch 'retrofit_project' into idf_lights

This commit is contained in:
Guille Gutierrez 2023-04-18 12:23:25 -04:00
commit 844835eabd
18 changed files with 26836 additions and 72 deletions

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View 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()

View File

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

View File

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

View File

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

View File

@ -1 +1 @@
__version__ = '0.1.7.10' __version__ = '0.1.7.11'

View File

@ -24,3 +24,5 @@ geopandas
triangle triangle
psycopg2-binary psycopg2-binary
Pillow Pillow
pathlib
pickle5

View File

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