Merge branch 'geojson' into systems_catalog

# Conflicts:
#	hub/catalog_factories/energy_systems/nrcan_catalog.py
#	hub/helpers/dictionaries.py
#	hub/imports/construction/nrcan_physics_parameters.py
This commit is contained in:
Pilar Monsalvete 2023-05-04 10:48:19 -04:00
commit ef7addd5b5
49 changed files with 28460 additions and 25590 deletions

View File

@ -20,7 +20,7 @@ from hub.catalog_factories.data_models.cost.cost_helper import CostHelper
class MontrealCustomCatalog(Catalog): class MontrealCustomCatalog(Catalog):
def __init__(self, path): def __init__(self, path):
path = str(path / 'montreal_costs.xml') path = (path / 'montreal_costs.xml')
with open(path) as xml: with open(path) as xml:
self._archetypes = xmltodict.parse(xml.read(), force_list='archetype') self._archetypes = xmltodict.parse(xml.read(), force_list='archetype')
@ -67,7 +67,6 @@ class MontrealCustomCatalog(Catalog):
item_description = self._item_with_refurbishment_values(shell['B30_roofing'], item_type) item_description = self._item_with_refurbishment_values(shell['B30_roofing'], item_type)
items_list.append(item_description) items_list.append(item_description)
general_chapters.append(Chapter('B_shell', items_list)) general_chapters.append(Chapter('B_shell', items_list))
items_list = [] items_list = []
item_type = 'D301010_photovoltaic_system' item_type = 'D301010_photovoltaic_system'
services = entry['D_services'] services = entry['D_services']
@ -82,7 +81,6 @@ class MontrealCustomCatalog(Catalog):
item_description = self._item_with_threesome(services['D50_electrical'], item_type) item_description = self._item_with_threesome(services['D50_electrical'], item_type)
items_list.append(item_description) items_list.append(item_description)
general_chapters.append(Chapter('D_services', items_list)) general_chapters.append(Chapter('D_services', items_list))
allowances = entry['Z_allowances_overhead_profit'] allowances = entry['Z_allowances_overhead_profit']
design_allowance = float(allowances['Z10_design_allowance']['#text']) / 100 design_allowance = float(allowances['Z10_design_allowance']['#text']) / 100
overhead_and_profit = float(allowances['Z20_overhead_profit']['#text']) / 100 overhead_and_profit = float(allowances['Z20_overhead_profit']['#text']) / 100
@ -127,9 +125,9 @@ class MontrealCustomCatalog(Catalog):
for archetype in archetypes: for archetype in archetypes:
function = archetype['@function'] function = archetype['@function']
municipality = archetype['@municipality'] municipality = archetype['@municipality']
country = archetype['@country'] country = 'CA'#archetype['@country']
lod = float(archetype['@lod']) lod = 0 #float(archetype['@lod'])
currency = archetype['currency'] currency = 'CAD'#archetype['currency']
capital_cost = self._get_capital_costs(archetype['capital_cost']) capital_cost = self._get_capital_costs(archetype['capital_cost'])
operational_cost = self._get_operational_costs(archetype['operational_cost']) operational_cost = self._get_operational_costs(archetype['operational_cost'])
end_of_life_cost = float(archetype['end_of_life_cost']['#text']) end_of_life_cost = float(archetype['end_of_life_cost']['#text'])

View File

@ -38,7 +38,7 @@ class Archetype:
Get name Get name
:return: string :return: string
""" """
return f'{self._country}_{self._municipality}_{self._function}_{self._lod}' return f'{self._country}_{self._municipality}_{self._function}_lod{self._lod}'
@property @property
def lod(self): def lod(self):

View File

@ -38,3 +38,13 @@ class CapitalCost:
:return: float :return: float
""" """
return self._overhead_and_profit return self._overhead_and_profit
def chapter(self, name) -> Chapter:
"""
Get specific chapter by name
:return: Chapter
"""
for chapter in self.general_chapters:
if chapter.chapter_type == name:
return chapter
raise KeyError(f'Chapter name {name} not found')

View File

@ -30,3 +30,13 @@ class Chapter:
:return: [str] :return: [str]
""" """
return self._items return self._items
def item(self, name) -> ItemDescription:
"""
Get specific item by name
:return: ItemDescription
"""
for item in self.items:
if item.type == name:
return item
raise KeyError(f'Item name {name} not found')

View File

@ -18,6 +18,8 @@ class ThermalControl:
hvac_availability_schedules, hvac_availability_schedules,
heating_set_point_schedules, heating_set_point_schedules,
cooling_set_point_schedules): cooling_set_point_schedules):
#todo: eliminate negative value
deltaTsetpoint=0
self._mean_heating_set_point = mean_heating_set_point self._mean_heating_set_point = mean_heating_set_point
self._heating_set_back = heating_set_back self._heating_set_back = heating_set_back
self._mean_cooling_set_point = mean_cooling_set_point self._mean_cooling_set_point = mean_cooling_set_point

View File

@ -131,10 +131,9 @@ class NrcanCatalog(Catalog):
mechanical_air_change = space_type['ventilation_air_changes'] mechanical_air_change = space_type['ventilation_air_changes']
# cfm/ft2 to m3/m2.s # cfm/ft2 to m3/m2.s
ventilation_rate = space_type['ventilation_per_area'] / (cte.METERS_TO_FEET * cte.MINUTES_TO_SECONDS) ventilation_rate = space_type['ventilation_per_area'] / (cte.METERS_TO_FEET * cte.MINUTES_TO_SECONDS)
if ventilation_rate == 0: # cfm/person to m3/m2.s
# cfm/person to m3/m2.s ventilation_rate += space_type['ventilation_per_person'] / (pow(cte.METERS_TO_FEET, 3) * cte.MINUTES_TO_SECONDS)\
ventilation_rate = space_type['ventilation_per_person'] / (cte.METERS_TO_FEET * cte.MINUTES_TO_SECONDS)\ * occupancy_density
/ occupancy_density
lighting_radiative_fraction = space_type['lighting_fraction_radiant'] lighting_radiative_fraction = space_type['lighting_fraction_radiant']
lighting_convective_fraction = 0 lighting_convective_fraction = 0

View File

@ -9,8 +9,11 @@ Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concord
import sys import sys
from typing import List, Union from typing import List, Union
import numpy as np import numpy as np
import pandas as pd
from hub.hub_logger import logger from hub.hub_logger import logger
import hub.helpers.constants as cte import hub.helpers.constants as cte
import hub.helpers.peak_loads as pl
from hub.city_model_structure.building_demand.surface import Surface from hub.city_model_structure.building_demand.surface import Surface
from hub.city_model_structure.city_object import CityObject from hub.city_model_structure.city_object import CityObject
from hub.city_model_structure.building_demand.household import Household from hub.city_model_structure.building_demand.household import Household
@ -363,6 +366,36 @@ 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)}
"""
results = {}
if cte.HOUR in self.heating:
monthly_values = pl.peak_loads_from_hourly(self.heating[cte.HOUR][next(iter(self.heating[cte.HOUR]))].values)
else:
monthly_values = pl.heating_peak_loads_from_methodology(self)
results[cte.MONTH] = pd.DataFrame(monthly_values, columns=['heating peak loads'])
results[cte.YEAR] = pd.DataFrame([max(monthly_values)], columns=['heating peak loads'])
return results
@property
def cooling_peak_load(self) -> dict:
"""
Get cooling peak load in W
:return: dict{DataFrame(float)}
"""
results = {}
if cte.HOUR in self.cooling:
monthly_values = pl.peak_loads_from_hourly(self.cooling[cte.HOUR][next(iter(self.cooling[cte.HOUR]))])
else:
monthly_values = pl.cooling_peak_loads_from_methodology(self)
results[cte.MONTH] = pd.DataFrame(monthly_values, columns=['cooling peak loads'])
results[cte.YEAR] = pd.DataFrame([max(monthly_values)], columns=['cooling peak loads'])
return results
@property @property
def eave_height(self): def eave_height(self):
""" """

View File

@ -78,7 +78,7 @@ class InternalZone:
def usages(self) -> [Usage]: def usages(self) -> [Usage]:
""" """
Get internal zone usage zones Get internal zone usage zones
:return: [UsageZone] :return: [Usage]
""" """
return self._usages return self._usages
@ -86,7 +86,7 @@ class InternalZone:
def usages(self, value): def usages(self, value):
""" """
Set internal zone usage zones Set internal zone usage zones
:param value: [UsageZone] :param value: [Usage]
""" """
self._usages = value self._usages = value

View File

@ -630,6 +630,7 @@ class ThermalZone:
schedule.values = new_values schedule.values = new_values
_schedules.append(schedule) _schedules.append(schedule)
self._domestic_hot_water.schedules = _schedules self._domestic_hot_water.schedules = _schedules
return self._domestic_hot_water return self._domestic_hot_water
@property @property

View File

@ -122,6 +122,8 @@ class City:
Get the name for the climatic information reference city Get the name for the climatic information reference city
:return: None or str :return: None or str
""" """
if self._climate_reference_city is None:
self._climate_reference_city = self._get_location().city
return self._climate_reference_city return self._climate_reference_city
@climate_reference_city.setter @climate_reference_city.setter
@ -130,8 +132,7 @@ class City:
Set the name for the climatic information reference city Set the name for the climatic information reference city
:param value: str :param value: str
""" """
if value is not None: self._climate_reference_city = str(value)
self._climate_reference_city = str(value)
@property @property
def climate_file(self) -> Union[None, Path]: def climate_file(self) -> Union[None, Path]:

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

@ -0,0 +1,166 @@
<archetypes>
<archetype function="residential" municipality="montreal" currency="CAD">
<capital_cost>
<ASubstructure>
<A10sub_structural cost_unit="currency/m2"> 15.89 </A10sub_structural>
<A20structural cost_unit="currency/m3"> 215.90 </A20structural>
</ASubstructure>
<BShell>
<B10superstructure>
<reposition cost_unit="currency/m2"> 0 </reposition>
<initial_investment cost_unit="currency/m2"> 0 </initial_investment>
<lifetime_equipment lifetime="years"> 50 </lifetime_equipment>
</B10superstructure>
<B20envelope>
<B2010opaquewalls>
<reposition cost_unit="currency/m2"> 304 </reposition>
<initial_investment cost_unit="currency/m2"> 304 </initial_investment>
<lifetime_equipment lifetime="years"> 50 </lifetime_equipment>
</B2010opaquewalls>
<B2020transparent>
<reposition cost_unit="currency/m2"> 857.14 </reposition>
<initial_investment cost_unit="currency/m2"> 857.14 </initial_investment>
<lifetime_equipment lifetime="years"> 20 </lifetime_equipment>
</B2020transparent>
</B20envelope>
<B30roofing>
<B3010opaqueroof>
<reposition cost_unit="currency/m2"> 118 </reposition>
<initial_investment cost_unit="currency/m2"> 118 </initial_investment>
<lifetime_equipment lifetime="years"> 50 </lifetime_equipment>
</B3010opaqueroof>
<B3020transparentroof>
<reposition cost_unit="currency/m2"> 857.14 </reposition>
<initial_investment cost_unit="currency/m2"> 857.14 </initial_investment>
<lifetime_equipment lifetime="years"> 20 </lifetime_equipment>
</B3020transparentroof>
</B30roofing>
</BShell>
<CInteriors>
<C10Interiorconstruction>
<reposition cost_unit="currency/m2"> 0 </reposition>
<initial_investment cost_unit="currency/m2"> 0 </initial_investment>
<lifetime_equipment lifetime="years"> 50 </lifetime_equipment>
</C10Interiorconstruction>
<C20Stairs>
<reposition cost_unit="currency/m2"> 0 </reposition>
<initial_investment cost_unit="currency/m2"> 0 </initial_investment>
<lifetime_equipment lifetime="years"> 50 </lifetime_equipment>
</C20Stairs>
<C30Interiorfinishes>
<C3010Walls>
<reposition cost_unit="currency/m2"> 50 </reposition>
<initial_investment cost_unit="currency/m2"> 50 </initial_investment>
<lifetime_equipment lifetime="years"> 20 </lifetime_equipment>
</C3010Walls>
<C3020Floors>
<reposition cost_unit="currency/m2"> 62 </reposition>
<initial_investment cost_unit="currency/m2"> 62 </initial_investment>
<lifetime_equipment lifetime="years"> 20 </lifetime_equipment>
</C3020Floors>
<C3030Ceilings>
<reposition cost_unit="currency/m2"> 70 </reposition>
<initial_investment cost_unit="currency/m2"> 70 </initial_investment>
<lifetime_equipment lifetime="years"> 20 </lifetime_equipment>
</C3030Ceilings>
</C30Interiorfinishes>
</CInteriors>
<DServices>
<D10Conveying cost_unit="currency/m2"> 0 </D10Conveying>
<D20Plumbing cost_unit="currency/m2"> 100 </D20Plumbing>
<D30HVAC>
<D3010EnergySupply>
<D301010photovoltaic_system>
<initial_investment cost_unit="currency/m2"> 800 </initial_investment>
<reposition cost_unit="currency/m2"> 800 </reposition>
<lifetime_equipment lifetime="years"> 25 </lifetime_equipment>
</D301010photovoltaic_system>
</D3010EnergySupply>
<D3020Heatgeneratingsystems>
<initial_investment cost_unit="currency/kW"> 622.86 </initial_investment>
<reposition cost_unit="currency/kW"> 622.86 </reposition>
<lifetime_equipment lifetime="years"> 25 </lifetime_equipment>
</D3020Heatgeneratingsystems>
<D3030Coolinggenerationsystems>
<initial_investment cost_unit="currency/kW"> 622.86 </initial_investment>
<reposition cost_unit="currency/kW"> 622.86 </reposition>
<lifetime_equipment lifetime="years"> 15 </lifetime_equipment>
</D3030Coolinggenerationsystems>
<D3040Distributionsystems>
<initial_investment cost_unit="currency/kW"> 0 </initial_investment>
<reposition cost_unit="currency/kW"> 0 </reposition>
<lifetime_equipment lifetime="years"> 15 </lifetime_equipment>
</D3040Distributionsystems>
<D3060Controlsandinstrumentation>
<initial_investment cost_unit="currency/kW"> 0 </initial_investment>
<reposition cost_unit="currency/kW"> 0 </reposition>
<lifetime_equipment lifetime="years"> 15 </lifetime_equipment>
</D3060Controlsandinstrumentation>
<D3080OtherHVAC_AHU>
<initial_investment cost_unit="currency/kW"> 47.62 </initial_investment>
<reposition cost_unit="currency/kW"> 47.62 </reposition>
<lifetime_equipment lifetime="years"> 15 </lifetime_equipment>
</D3080OtherHVAC_AHU>
</D30HVAC>
<D50Electrical>
<D5010Electricalservicesanddistribution>
<initial_investment cost_unit="currency/m2"> 171.43 </initial_investment>
<reposition cost_unit="currency/m2"> 171.43 </reposition>
<lifetime_equipment lifetime="years"> 20 </lifetime_equipment>
</D5010Electricalservicesanddistribution>
<D5020Lightingandbranchwiring>
<initial_investment cost_unit="currency/kW"> 139 </initial_investment>
<reposition cost_unit="currency/kW"> 139 </reposition>
<lifetime_equipment lifetime="years"> 20 </lifetime_equipment>
</D5020Lightingandbranchwiring>
</D50Electrical>
</DServices>
<EEquimentsandfurnishing>
<E10Equipments>
<initial_investment cost_unit="currency/m2"> 0 </initial_investment>
<reposition cost_unit="currency/m2"> 0 </reposition>
<lifetime_equipment lifetime="years"> 15 </lifetime_equipment>
</E10Equipments>
<E10Furnishing>
<initial_investment cost_unit="currency/m2"> 0 </initial_investment>
<reposition cost_unit="currency/m2"> 0 </reposition>
<lifetime_equipment lifetime="years"> 15 </lifetime_equipment>
</E10Furnishing>
</EEquimentsandfurnishing>
<engineer cost_unit="%"> 2.5 </engineer>
</capital_cost>
<operational_cost>
<fuel fuel_type="electricity">
<fixed>
<fixed_monthly cost_unit="currency/month"> 0 </fixed_monthly>
<fixed_power cost_unit="currency/kW"> 0 </fixed_power>
</fixed>
<variable cost_unit="currency/kWh"> 5.6 </variable>
</fuel>
<maintenance>
<heating_equipment cost_unit="currency/kW"> 40 </heating_equipment>
<cooling_equipment cost_unit="currency/kW"> 40 </cooling_equipment>
<general_hvac_equipment cost_unit="currency/(m3/h)"> 0.05 </general_hvac_equipment>
<photovoltaic_system cost_unit="currency/m2"> 1 </photovoltaic_system>
<other_systems cost_unit="currency/m2"> 4.6 </other_systems>
</maintenance>
<CO2_cost cost_unit="currency/kgCO2"> 30 </CO2_cost>
</operational_cost>
<end_of_life_cost cost_unit="currency/m2"> 6.3 </end_of_life_cost>
<incomes>
<subsidies>
<construction_subsidy cost_unit="%"> 2 </construction_subsidy>
<hvac_subsidy cost_unit="%"> 1.5 </hvac_subsidy>
<photovoltaic_subsidy cost_unit="%"> 3.6 </photovoltaic_subsidy>
</subsidies>
<energy_exports>
<electricity cost_unit="currency/kWh"> hourlydatatable </electricity>
<heat cost_unit="currency/kWh"> 0 </heat>
</energy_exports>
<tax_reductions>
<reductions_taxes cost_unit="%"> 2 </reductions_taxes>
</tax_reductions>
<CO2_income cost_unit="currency/kgCO2exported"> 0 </CO2_income>
</incomes>
</archetype>
</archetypes>

View File

@ -0,0 +1,212 @@
<archetypes>
<archetype function="residential" municipality="montreal" currency="CAD">
<capital_cost>
<B_Shell>
<B10_superstructure>
<refurbishment_cost_basement cost_unit="currency/m2"> 0 </refurbishment_cost_basement>
</B10_superstructure>
<B20_envelope>
<B2010_opaquewalls>
<refurbishment_cost cost_unit="currency/m2"> 304 </refurbishment_cost>
</B2010_opaquewalls>
<B2020_transparent>
<refurbishment_cost cost_unit="currency/m2"> 857.14 </refurbishment_cost>
</B2020_transparent>
</B20_envelope>
<B30_roofing>
<B3010_opaqueroof>
<refurbishment_cost cost_unit="currency/m2"> 118 </refurbishment_cost>
</B3010_opaqueroof>
</B30_roofing>
</B_Shell>
<D_Services>
<D30_HVAC>
<D3010_EnergySupply>
<D301010_Photovoltaic_system>
<initial_investment cost_unit="currency/m2"> 800 </initial_investment>
<reposition cost_unit="currency/m2"> 800 </reposition>
<lifetime_equipment lifetime="years"> 25 </lifetime_equipment>
</D301010_Photovoltaic_system>
</D3010_EnergySupply>
<D3020_Heat_generating_systems>
<investment_cost cost_unit="currency/kW"> 622.86 </investment_cost>
<reposition cost_unit="currency/kW"> 622.86 </reposition>
<lifetime_equipment lifetime="years"> 25 </lifetime_equipment>
</D3020_Heat_generating_systems>
<D3030_Cooling_generation_systems>
<investment_cost cost_unit="currency/kW"> 622.86 </investment_cost>
<reposition cost_unit="currency/kW"> 622.86 </reposition>
<lifetime_equipment lifetime="years"> 15 </lifetime_equipment>
</D3030_Cooling_generation_systems>
<D3040_Distributionsystems>
<investment_cost cost_unit="currency/kW"> 0 </investment_cost>
<reposition cost_unit="currency/kW"> 0 </reposition>
<lifetime_equipment lifetime="years"> 15 </lifetime_equipment>
</D3040_Distributionsystems>
<D3080_OtherHVAC_AHU>
<investment_cost cost_unit="currency/kW"> 47.62 </investment_cost>
<reposition cost_unit="currency/kW"> 47.62 </reposition>
<lifetime_equipment lifetime="years"> 15 </lifetime_equipment>
</D3080_OtherHVAC_AHU>
</D30_HVAC>
<D50_Electrical>
<D5020Lightingandbranchwiring>
<refurbishmentcost cost_unit="currency/kW"> 139 </refurbishmentcost>
<reposition cost_unit="currency/kW"> 139 </reposition>
<lifetime_equipment lifetime="years"> 20 </lifetime_equipment>
</D5020Lightingandbranchwiring>
</D50_Electrical>
</D_Services>
<Z_Allowances_overhead_profit>
<Z10_Design_allowance cost_unit="%"> 2.5 </Z10_Design_allowance>
<Z10_Overhead_and_profit cost_unit="%"> 14 </Z10_Overhead_and_profit>
</Z_Allowances_overhead_profit>
</capital_cost>
<operational_cost>
<fuel fuel_type="electricity">
<fixed>
<fixed_monthly cost_unit="currency/month"> 12.27 </fixed_monthly>
<fixed_power cost_unit="currency/month*kW"> 0 </fixed_power>
</fixed>
<variable cost_unit="currency/kWh"> 0.075 </variable>
</fuel>
<fuel fuel_type="gas">
<fixed>
<fixed_monthly cost_unit="currency/month"> 17.71 </fixed_monthly>
</fixed>
<variable cost_unit="currency/kWh"> 0.640 </variable>
</fuel>
<fuel fuel_type="diesel">
<variable cost_unit="currency/l"> 1.2 </variable>
</fuel>
<fuel fuel_type="biomass">
<variable cost_unit="currency/kg"> 0.09 </variable>
</fuel>
<maintenance>
<heating_equipment cost_unit="currency/kW"> 40 </heating_equipment>
<cooling_equipment cost_unit="currency/kW"> 40 </cooling_equipment>
<photovoltaic_system cost_unit="currency/m2"> 1 </photovoltaic_system>
</maintenance>
<CO2_cost cost_unit="currency/kgCO2"> 30 </CO2_cost>
</operational_cost>
<end_of_life_cost cost_unit="currency/m2"> 6.3 </end_of_life_cost>
<incomes>
<subsidies>
<construction_subsidy cost_unit="%"> 2 </construction_subsidy>
<hvac_subsidy cost_unit="%"> 1.5 </hvac_subsidy>
<photovoltaic_subsidy cost_unit="%"> 3.6 </photovoltaic_subsidy>
</subsidies>
<energy_exports>
<electricity cost_unit="currency/kWh"> 0 </electricity>
</energy_exports>
<tax_reductions>
<reductions_taxes cost_unit="%"> 2 </reductions_taxes>
</tax_reductions>
</incomes>
</archetype>
<archetype function="non-residential" municipality="montreal" currency="CAD">
<capital_cost>
<B_Shell>
<B10_superstructure>
<refurbishmentcostbasement cost_unit="currency/m2"> 0 </refurbishmentcostbasement>
</B10_superstructure>
<B20_envelope>
<B2010_opaque_walls>
<refurbishmentcost cost_unit="currency/m2"> 304 </refurbishmentcost>
</B2010_opaque_walls>
<B2020_transparent>
<refurbishmentcost cost_unit="currency/m2"> 857.14 </refurbishmentcost>
</B2020_transparent>
</B20_envelope>
<B30_roofing>
<B3010_opaqueroof>
<refurbishmentcost cost_unit="currency/m2"> 118 </refurbishmentcost>
</B3010_opaqueroof>
</B30_roofing>
</B_Shell>
<D_Services>
<D30_HVAC>
<D3010EnergySupply>
<D301010photovoltaic_system>
<initial_investment cost_unit="currency/m2"> 800 </initial_investment>
<reposition cost_unit="currency/m2"> 800 </reposition>
<lifetime_equipment lifetime="years"> 25 </lifetime_equipment>
</D301010photovoltaic_system>
</D3010EnergySupply>
<D3020Heatgeneratingsystems>
<investment_cost cost_unit="currency/kW"> 622.86 </investment_cost>
<reposition cost_unit="currency/kW"> 622.86 </reposition>
<lifetime_equipment lifetime="years"> 25 </lifetime_equipment>
</D3020Heatgeneratingsystems>
<D3030_Cooling_generation_systems>
<investment_cost cost_unit="currency/kW"> 622.86 </investment_cost>
<reposition cost_unit="currency/kW"> 622.86 </reposition>
<lifetime_equipment lifetime="years"> 15 </lifetime_equipment>
</D3030_Cooling_generation_systems>
<D3040_Distribution_systems>
<refurbishmentcost cost_unit="currency/m2"> 0 </refurbishmentcost>
<reposition cost_unit="currency/kW"> 0 </reposition>
<lifetime_equipment lifetime="years"> 15 </lifetime_equipment>
</D3040_Distribution_systems>
<D3080_Other_HVAC_AHU>
<investment_cost cost_unit="currency/kW"> 47.62 </investment_cost>
<reposition cost_unit="currency/kW"> 47.62 </reposition>
<lifetime_equipment lifetime="years"> 15 </lifetime_equipment>
</D3080_Other_HVAC_AHU>
</D30_HVAC>
<D50_Electrical>
<D5020_Lighting_and_branch_wiring>
<refurbishmentcost cost_unit="currency/kW"> 139 </refurbishmentcost>
<reposition cost_unit="currency/kW"> 139 </reposition>
<lifetime_equipment lifetime="years"> 20 </lifetime_equipment>
</D5020_Lighting_and_branch_wiring>
</D50_Electrical>
</D_Services>
<Z_Allowances_overhead_profit>
<Z10_Design_allowance cost_unit="%"> 6 </Z10_Design_allowance>
<Z20_Overhead_profit cost_unit="%"> 14 </Z20_Overhead_profit>
</Z_Allowances_overhead_profit>
</capital_cost>
<operational_cost>
<fuel fuel_type="electricity">
<fixed>
<fixed_monthly cost_unit="currency/month"> 12.27 </fixed_monthly>
<fixed_power cost_unit="currency/(month*kW)"> 0 </fixed_power>
</fixed>
<variable cost_unit="currency/kWh"> 0.075 </variable>
</fuel>
<fuel fuel_type="gas">
<fixed>
<fixed_monthly cost_unit="currency/month"> 17.71 </fixed_monthly>
</fixed>
<variable cost_unit="currency/m3"> 0.640 </variable>
</fuel>
<fuel fuel_type="diesel">
<variable cost_unit="currency/l"> 1.2 </variable>
</fuel>
<fuel fuel_type="biomass">
<variable cost_unit="currency/kg"> 0.09 </variable>
</fuel>
<maintenance>
<heating_equipment cost_unit="currency/kW"> 40 </heating_equipment>
<cooling_equipment cost_unit="currency/kW"> 40 </cooling_equipment>
<photovoltaic_system cost_unit="currency/m2"> 1 </photovoltaic_system>
</maintenance>
<CO2_cost cost_unit="currency/kgCO2"> 30 </CO2_cost>
</operational_cost>
<end_of_life_cost cost_unit="currency/m2"> 6.3 </end_of_life_cost>
<incomes>
<subsidies>
<construction_subsidy cost_unit="%"> 2 </construction_subsidy>
<hvac_subsidy cost_unit="%"> 1.5 </hvac_subsidy>
<photovoltaic_subsidy cost_unit="%"> 3.6 </photovoltaic_subsidy>
</subsidies>
<energy_exports>
<electricity cost_unit="currency/kWh"> 0 </electricity>
</energy_exports>
<tax_reductions>
<reductions_taxes cost_unit="%"> 2 </reductions_taxes>
</tax_reductions>
</incomes>
</archetype>
</archetypes>

View File

@ -0,0 +1,178 @@
<archetypes>
<archetype function="residential" municipality="montreal" currency="CAD">
<capital_cost>
<ASubstructure>
<A10sub_structural cost_unit="currency/m2"> 15.89 </A10sub_structural>
<A20structural cost_unit="currency/m3"> 215.90 </A20structural>
</ASubstructure>
<BShell>
<B10superstructure>
<reposition cost_unit="currency/m2"> 0 </reposition>
<initial_investment cost_unit="currency/m2"> 0 </initial_investment>
<lifetime_equipment lifetime="years"> 50 </lifetime_equipment>
</B10superstructure>
<B20envelope>
<B2010opaquewalls>
<reposition cost_unit="currency/m2"> 304 </reposition>
<initial_investment cost_unit="currency/m2"> 304 </initial_investment>
<lifetime_equipment lifetime="years"> 50 </lifetime_equipment>
</B2010opaquewalls>
<B2020transparent>
<reposition cost_unit="currency/m2"> 857.14 </reposition>
<initial_investment cost_unit="currency/m2"> 857.14 </initial_investment>
<lifetime_equipment lifetime="years"> 20 </lifetime_equipment>
</B2020transparent>
</B20envelope>
<B30roofing>
<B3010opaqueroof>
<reposition cost_unit="currency/m2"> 118 </reposition>
<initial_investment cost_unit="currency/m2"> 118 </initial_investment>
<lifetime_equipment lifetime="years"> 50 </lifetime_equipment>
</B3010opaqueroof>
<B3020transparentroof>
<reposition cost_unit="currency/m2"> 857.14 </reposition>
<initial_investment cost_unit="currency/m2"> 857.14 </initial_investment>
<lifetime_equipment lifetime="years"> 20 </lifetime_equipment>
</B3020transparentroof>
</B30roofing>
</BShell>
<CInteriors>
<C10Interiorconstruction>
<reposition cost_unit="currency/m2"> 0 </reposition>
<initial_investment cost_unit="currency/m2"> 0 </initial_investment>
<lifetime_equipment lifetime="years"> 50 </lifetime_equipment>
</C10Interiorconstruction>
<C20Stairs>
<reposition cost_unit="currency/m2"> 0 </reposition>
<initial_investment cost_unit="currency/m2"> 0 </initial_investment>
<lifetime_equipment lifetime="years"> 50 </lifetime_equipment>
</C20Stairs>
<C30Interiorfinishes>
<C3010Walls>
<reposition cost_unit="currency/m2"> 50 </reposition>
<initial_investment cost_unit="currency/m2"> 50 </initial_investment>
<lifetime_equipment lifetime="years"> 20 </lifetime_equipment>
</C3010Walls>
<C3020Floors>
<reposition cost_unit="currency/m2"> 62 </reposition>
<initial_investment cost_unit="currency/m2"> 62 </initial_investment>
<lifetime_equipment lifetime="years"> 20 </lifetime_equipment>
</C3020Floors>
<C3030Ceilings>
<reposition cost_unit="currency/m2"> 70 </reposition>
<initial_investment cost_unit="currency/m2"> 70 </initial_investment>
<lifetime_equipment lifetime="years"> 20 </lifetime_equipment>
</C3030Ceilings>
</C30Interiorfinishes>
</CInteriors>
<DServices>
<D10Conveying cost_unit="currency/m2"> 0 </D10Conveying>
<D20Plumbing cost_unit="currency/m2"> 100 </D20Plumbing>
<D30HVAC>
<D3010EnergySupply>
<D301010photovoltaic_system>
<initial_investment cost_unit="currency/m2"> 800 </initial_investment>
<reposition cost_unit="currency/m2"> 800 </reposition>
<lifetime_equipment lifetime="years"> 25 </lifetime_equipment>
</D301010photovoltaic_system>
</D3010EnergySupply>
<D3020Heatgeneratingsystems>
<initial_investment cost_unit="currency/kW"> 622.86 </initial_investment>
<reposition cost_unit="currency/kW"> 622.86 </reposition>
<lifetime_equipment lifetime="years"> 25 </lifetime_equipment>
</D3020Heatgeneratingsystems>
<D3030Coolinggenerationsystems>
<initial_investment cost_unit="currency/kW"> 622.86 </initial_investment>
<reposition cost_unit="currency/kW"> 622.86 </reposition>
<lifetime_equipment lifetime="years"> 15 </lifetime_equipment>
</D3030Coolinggenerationsystems>
<D3040Distributionsystems>
<initial_investment cost_unit="currency/kW"> 0 </initial_investment>
<reposition cost_unit="currency/kW"> 0 </reposition>
<lifetime_equipment lifetime="years"> 15 </lifetime_equipment>
</D3040Distributionsystems>
<D3060Controlsandinstrumentation>
<initial_investment cost_unit="currency/kW"> 0 </initial_investment>
<reposition cost_unit="currency/kW"> 0 </reposition>
<lifetime_equipment lifetime="years"> 15 </lifetime_equipment>
</D3060Controlsandinstrumentation>
<D3080OtherHVAC_AHU>
<initial_investment cost_unit="currency/kW"> 47.62 </initial_investment>
<reposition cost_unit="currency/kW"> 47.62 </reposition>
<lifetime_equipment lifetime="years"> 15 </lifetime_equipment>
</D3080OtherHVAC_AHU>
</D30HVAC>
<D50Electrical>
<D5010Electricalservicesanddistribution>
<initial_investment cost_unit="currency/m2"> 171.43 </initial_investment>
<reposition cost_unit="currency/m2"> 171.43 </reposition>
<lifetime_equipment lifetime="years"> 20 </lifetime_equipment>
</D5010Electricalservicesanddistribution>
<D5020Lightingandbranchwiring>
<initial_investment cost_unit="currency/kW"> 139 </initial_investment>
<reposition cost_unit="currency/kW"> 139 </reposition>
<lifetime_equipment lifetime="years"> 20 </lifetime_equipment>
</D5020Lightingandbranchwiring>
</D50Electrical>
</DServices>
<EEquimentsandfurnishing>
<E10Equipments>
<initial_investment cost_unit="currency/m2"> 0 </initial_investment>
<reposition cost_unit="currency/m2"> 0 </reposition>
<lifetime_equipment lifetime="years"> 15 </lifetime_equipment>
</E10Equipments>
<E10Furnishing>
<initial_investment cost_unit="currency/m2"> 0 </initial_investment>
<reposition cost_unit="currency/m2"> 0 </reposition>
<lifetime_equipment lifetime="years"> 15 </lifetime_equipment>
</E10Furnishing>
</EEquimentsandfurnishing>
<engineer cost_unit="%"> 2.5 </engineer>
</capital_cost>
<operational_cost>
<fuel fuel_type="electricity">
<fixed>
<fixed_monthly cost_unit="currency/month"> 12.27 </fixed_monthly>
</fixed>
<variable_base cost_unit="currency/kWh"> hourlydatatable1 </variable_base>
<variable_peak cost_unit="currency/kWh"> hourlydatatable2 </variable_peak>
</fuel>
<fuel fuel_type="gaz">
<fixed>
<fixed_monthly cost_unit="currency/month"> 17.71 </fixed_monthly>
</fixed>
<variable cost_unit="currency/m3"> 0.640 </variable>
</fuel>
<fuel fuel_type="diesel">
<variable cost_unit="currency/l"> 1.2 </variable>
</fuel>
<fuel fuel_type="biomass">
<variable cost_unit="currency/kg"> 0.09 </variable>
</fuel>
<maintenance>
<heating_equipment cost_unit="currency/kW"> 40 </heating_equipment>
<cooling_equipment cost_unit="currency/kW"> 40 </cooling_equipment>
<general_hvac_equipment cost_unit="currency/(m3/h)"> 0.05 </general_hvac_equipment>
<photovoltaic_system cost_unit="currency/m2"> 1 </photovoltaic_system>
<other_systems cost_unit="currency/m2"> 4.6 </other_systems>
</maintenance>
<CO2_cost cost_unit="currency/kgCO2"> 30 </CO2_cost>
</operational_cost>
<end_of_life_cost cost_unit="currency/m2"> 6.3 </end_of_life_cost>
<incomes>
<subsidies>
<construction_subsidy cost_unit="%"> 2 </construction_subsidy>
<hvac_subsidy cost_unit="%"> 1.5 </hvac_subsidy>
<photovoltaic_subsidy cost_unit="%"> 3.6 </photovoltaic_subsidy>
</subsidies>
<energy_exports>
<electricity cost_unit="currency/kWh"> hourlydatatable </electricity>
<heat cost_unit="currency/kWh"> 0 </heat>
</energy_exports>
<tax_reductions>
<reductions_taxes cost_unit="%"> 2 </reductions_taxes>
</tax_reductions>
<CO2_income cost_unit="currency/kgCO2exported"> 0 </CO2_income>
</incomes>
</archetype>
</archetypes>

File diff suppressed because it is too large Load Diff

View File

@ -4,13 +4,14 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group Copyright © 2022 Concordia CERC group
Project Coder Guille Guillermo.GutierrezMorote@concordia.ca Project Coder Guille 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
Soroush Samareh Abolhassani soroush.samarehabolhassani@mail.concordia.ca Oriol Gavaldà Torrellas oriol.gavalda@concordia.ca
""" """
import copy import copy
from pathlib import Path from pathlib import Path
from geomeppy import IDF from geomeppy import IDF
import hub.helpers.constants as cte import hub.helpers.constants as cte
from hub.city_model_structure.attributes.schedule import Schedule from hub.city_model_structure.attributes.schedule import Schedule
from hub.city_model_structure.building_demand.thermal_zone import ThermalZone
class Idf: class Idf:
@ -20,7 +21,9 @@ class Idf:
_BUILDING = 'BUILDING' _BUILDING = 'BUILDING'
_ZONE = 'ZONE' _ZONE = 'ZONE'
_LIGHTS = 'LIGHTS' _LIGHTS = 'LIGHTS'
_APPLIANCES = 'OTHEREQUIPMENT'
_PEOPLE = 'PEOPLE' _PEOPLE = 'PEOPLE'
_DHW = 'WATERUSE:EQUIPMENT'
_THERMOSTAT = 'HVACTEMPLATE:THERMOSTAT' _THERMOSTAT = 'HVACTEMPLATE:THERMOSTAT'
_IDEAL_LOAD_AIR_SYSTEM = 'HVACTEMPLATE:ZONE:IDEALLOADSAIRSYSTEM' _IDEAL_LOAD_AIR_SYSTEM = 'HVACTEMPLATE:ZONE:IDEALLOADSAIRSYSTEM'
_SURFACE = 'BUILDINGSURFACE:DETAILED' _SURFACE = 'BUILDINGSURFACE:DETAILED'
@ -36,6 +39,7 @@ class Idf:
_WINDOW_MATERIAL_SIMPLE = 'WINDOWMATERIAL:SIMPLEGLAZINGSYSTEM' _WINDOW_MATERIAL_SIMPLE = 'WINDOWMATERIAL:SIMPLEGLAZINGSYSTEM'
_ROUGHNESS = 'MediumRough' _ROUGHNESS = 'MediumRough'
_INFILTRATION = 'ZONEINFILTRATION:DESIGNFLOWRATE' _INFILTRATION = 'ZONEINFILTRATION:DESIGNFLOWRATE'
_VENTILATION = 'ZONEVENTILATION:DESIGNFLOWRATE'
_HOURLY_SCHEDULE = 'SCHEDULE:DAY:HOURLY' _HOURLY_SCHEDULE = 'SCHEDULE:DAY:HOURLY'
_COMPACT_SCHEDULE = 'SCHEDULE:COMPACT' _COMPACT_SCHEDULE = 'SCHEDULE:COMPACT'
@ -77,7 +81,7 @@ class Idf:
} }
def __init__(self, city, output_path, idf_file_path, idd_file_path, epw_file_path, export_type="Surfaces", def __init__(self, city, output_path, idf_file_path, idd_file_path, epw_file_path, export_type="Surfaces",
target_buildings=None, adjacent_buildings=None): target_buildings=None):
self._city = city self._city = city
self._output_path = str(output_path.resolve()) self._output_path = str(output_path.resolve())
self._output_file = str((output_path / f'{city.name}.idf').resolve()) self._output_file = str((output_path / f'{city.name}.idf').resolve())
@ -93,11 +97,13 @@ class Idf:
self._idf.newidfobject(self._SCHEDULE_LIMIT, Name=self._ON_OFF, Lower_Limit_Value=0, Upper_Limit_Value=1, self._idf.newidfobject(self._SCHEDULE_LIMIT, Name=self._ON_OFF, Lower_Limit_Value=0, Upper_Limit_Value=1,
Numeric_Type=self._DISCRETE) Numeric_Type=self._DISCRETE)
self._target_buildings = target_buildings self._target_buildings = target_buildings
self._adjacent_buildings = []
if target_buildings is None: if target_buildings is None:
self._target_buildings = [building.name for building in self._city.buildings] self._target_buildings = [building.name for building in self._city.buildings]
self._adjacent_buildings = adjacent_buildings else:
if self._adjacent_buildings is None: for building_name in target_buildings:
self._adjacent_buildings = [] building = city.city_object(building_name)
self._adjacent_buildings += building.neighbours
self._export() self._export()
@staticmethod @staticmethod
@ -145,6 +151,72 @@ class Idf:
Visible_Absorptance=layer.material.visible_absorptance Visible_Absorptance=layer.material.visible_absorptance
) )
@staticmethod
def _create_infiltration_schedules(thermal_zone):
_infiltration_schedules = []
if thermal_zone.thermal_control is None:
return []
for hvac_availability_schedule in thermal_zone.thermal_control.hvac_availability_schedules:
_schedule = Schedule()
_schedule.type = cte.INFILTRATION
_schedule.data_type = cte.FRACTION
_schedule.time_step = cte.HOUR
_schedule.time_range = cte.DAY
_schedule.day_types = copy.deepcopy(hvac_availability_schedule.day_types)
_infiltration_values = []
for hvac_value in hvac_availability_schedule.values:
if hvac_value == 0:
_infiltration_values.append(1.0)
else:
if thermal_zone.infiltration_rate_system_off == 0:
_infiltration_values.append(0.0)
else:
_infiltration_values.append(
thermal_zone.infiltration_rate_system_on / thermal_zone.infiltration_rate_system_off)
_schedule.values = _infiltration_values
_infiltration_schedules.append(_schedule)
return _infiltration_schedules
@staticmethod
def _create_yearly_values_schedules(schedule_type, values):
_schedule = Schedule()
_schedule.type = schedule_type
_schedule.data_type = cte.ANY_NUMBER
_schedule.time_step = cte.HOUR
_schedule.time_range = cte.YEAR
_schedule.day_types = ['monday',
'tuesday',
'wednesday',
'thursday',
'friday',
'saturday',
'sunday',
'holiday',
'winter_design_day',
'summer_design_day']
_schedule.values = values
return [_schedule]
@staticmethod
def _create_constant_value_schedules(schedule_type, value):
_schedule = Schedule()
_schedule.type = schedule_type
_schedule.data_type = cte.ANY_NUMBER
_schedule.time_step = cte.HOUR
_schedule.time_range = cte.DAY
_schedule.day_types = ['monday',
'tuesday',
'wednesday',
'thursday',
'friday',
'saturday',
'sunday',
'holiday',
'winter_design_day',
'summer_design_day']
_schedule.values = [value for _ in range(0, 24)]
return [_schedule]
def _add_standard_compact_hourly_schedule(self, usage, schedule_type, schedules): def _add_standard_compact_hourly_schedule(self, usage, schedule_type, schedules):
for schedule in self._idf.idfobjects[self._COMPACT_SCHEDULE]: for schedule in self._idf.idfobjects[self._COMPACT_SCHEDULE]:
if schedule.Name == f'{schedule_type} schedules {usage}': if schedule.Name == f'{schedule_type} schedules {usage}':
@ -185,49 +257,9 @@ class Idf:
_schedule.Interpolate_to_Timestep = 'No' _schedule.Interpolate_to_Timestep = 'No'
_schedule.Minutes_per_Item = 60 _schedule.Minutes_per_Item = 60
def _add_infiltration_schedules(self, thermal_zone):
_infiltration_schedules = []
if thermal_zone.thermal_control is None:
return
for hvac_availability_schedule in thermal_zone.thermal_control.hvac_availability_schedules:
_schedule = Schedule()
_schedule.type = cte.INFILTRATION
_schedule.data_type = cte.FRACTION
_schedule.time_step = cte.HOUR
_schedule.time_range = cte.DAY
_schedule.day_types = copy.deepcopy(hvac_availability_schedule.day_types)
_infiltration_values = []
for hvac_value in hvac_availability_schedule.values:
if hvac_value == 0:
_infiltration_values.append(thermal_zone.infiltration_rate_system_off)
else:
_infiltration_values.append(thermal_zone.infiltration_rate_system_on)
_schedule.values = _infiltration_values
_infiltration_schedules.append(_schedule)
for schedule in self._idf.idfobjects[self._HOURLY_SCHEDULE]:
if schedule.Name == f'Infiltration schedules {thermal_zone.usage_name}':
return
return self._add_standard_compact_hourly_schedule(thermal_zone.usage_name, 'Infiltration', _infiltration_schedules)
def _add_people_activity_level_schedules(self, thermal_zone):
_occ = thermal_zone.occupancy
if _occ.occupancy_density == 0:
_total_heat = 0
else:
_total_heat = (_occ.sensible_convective_internal_gain + _occ.sensible_radiative_internal_gain
+ _occ.latent_internal_gain) / _occ.occupancy_density
for schedule in self._idf.idfobjects[self._COMPACT_SCHEDULE]:
if schedule.Name == f'Activity Level schedules {thermal_zone.usage_name}':
return
_kwargs = {'Name': f'Activity Level schedules {thermal_zone.usage_name}',
'Schedule_Type_Limits_Name': self.idf_type_limits[cte.ANY_NUMBER],
'Field_1': 'Through: 12/31',
'Field_2': 'For AllDays',
'Field_3': f'Until: 24:00,{_total_heat}'}
self._idf.newidfobject(self._COMPACT_SCHEDULE, **_kwargs)
return
def _add_schedules(self, usage, schedule_type, new_schedules): def _add_schedules(self, usage, schedule_type, new_schedules):
if len(new_schedules) < 1:
return
schedule_from_file = False schedule_from_file = False
for schedule in new_schedules: for schedule in new_schedules:
if len(schedule.values) > 168: # Hours in one week if len(schedule.values) > 168: # Hours in one week
@ -276,7 +308,7 @@ class Idf:
def _add_window_construction_and_material(self, thermal_opening): def _add_window_construction_and_material(self, thermal_opening):
for window_material in self._idf.idfobjects[self._WINDOW_MATERIAL_SIMPLE]: for window_material in self._idf.idfobjects[self._WINDOW_MATERIAL_SIMPLE]:
if window_material['UFactor'] == thermal_opening.overall_u_value and \ if window_material['UFactor'] == thermal_opening.overall_u_value and \
window_material['Solar_Heat_Gain_Coefficient'] == thermal_opening.g_value: window_material['Solar_Heat_Gain_Coefficient'] == thermal_opening.g_value:
return return
order = str(len(self._idf.idfobjects[self._WINDOW_MATERIAL_SIMPLE]) + 1) order = str(len(self._idf.idfobjects[self._WINDOW_MATERIAL_SIMPLE]) + 1)
@ -293,7 +325,6 @@ class Idf:
for zone in self._idf.idfobjects['ZONE']: for zone in self._idf.idfobjects['ZONE']:
if zone.Name == name: if zone.Name == name:
return return
# todo: what do we need to define a zone in energy plus?
self._idf.newidfobject(self._ZONE, Name=name, Volume=thermal_zone.volume) self._idf.newidfobject(self._ZONE, Name=name, Volume=thermal_zone.volume)
self._add_heating_system(thermal_zone, name) self._add_heating_system(thermal_zone, name)
@ -302,7 +333,6 @@ class Idf:
for thermostat in self._idf.idfobjects[self._THERMOSTAT]: for thermostat in self._idf.idfobjects[self._THERMOSTAT]:
if thermostat.Name == thermostat_name: if thermostat.Name == thermostat_name:
return thermostat return thermostat
# todo: change schedules to schedule name and create schedules using the add_schedule function
return self._idf.newidfobject(self._THERMOSTAT, return self._idf.newidfobject(self._THERMOSTAT,
Name=thermostat_name, Name=thermostat_name,
Heating_Setpoint_Schedule_Name=f'Heating thermostat schedules {thermal_zone.usage_name}', Heating_Setpoint_Schedule_Name=f'Heating thermostat schedules {thermal_zone.usage_name}',
@ -338,6 +368,43 @@ class Idf:
Activity_Level_Schedule_Name=f'Activity Level schedules {thermal_zone.usage_name}' Activity_Level_Schedule_Name=f'Activity Level schedules {thermal_zone.usage_name}'
) )
def _add_lighting(self, thermal_zone: ThermalZone, zone_name: str):
fraction_radiant = thermal_zone.lighting.radiative_fraction
method = 'Watts/Area'
storeys_number = int(thermal_zone.total_floor_area / thermal_zone.footprint_area)
watts_per_zone_floor_area = thermal_zone.lighting.density * storeys_number
subcategory = f'ELECTRIC EQUIPMENT#{zone_name}#GeneralLights'
self._idf.newidfobject(self._LIGHTS,
Name=f'{zone_name}_lights',
Zone_or_ZoneList_Name=zone_name,
Schedule_Name=f'Lighting schedules {thermal_zone.usage_name}',
Design_Level_Calculation_Method=method,
Watts_per_Zone_Floor_Area=watts_per_zone_floor_area,
Fraction_Radiant=fraction_radiant,
EndUse_Subcategory=subcategory
)
def _add_appliances(self, thermal_zone, zone_name):
fuel_type = 'Electricity'
fraction_radiant = thermal_zone.appliances.radiative_fraction
fraction_latent = thermal_zone.appliances.latent_fraction
method = 'Watts/Area'
storeys_number = int(thermal_zone.total_floor_area / thermal_zone.footprint_area)
watts_per_zone_floor_area = thermal_zone.appliances.density * storeys_number
subcategory = f'ELECTRIC EQUIPMENT#{zone_name}#InteriorEquipment'
self._idf.newidfobject(self._APPLIANCES,
Fuel_Type=fuel_type,
Name=f'{zone_name}_appliance',
Zone_or_ZoneList_Name=zone_name,
Schedule_Name=f'Appliance schedules {thermal_zone.usage_name}',
Design_Level_Calculation_Method=method,
Power_per_Zone_Floor_Area=watts_per_zone_floor_area,
Fraction_Latent=fraction_latent,
Fraction_Radiant=fraction_radiant,
EndUse_Subcategory=subcategory
)
def _add_infiltration(self, thermal_zone, zone_name): def _add_infiltration(self, thermal_zone, zone_name):
for zone in self._idf.idfobjects["ZONE"]: for zone in self._idf.idfobjects["ZONE"]:
@ -351,7 +418,36 @@ class Idf:
Zone_or_ZoneList_Name=zone_name, Zone_or_ZoneList_Name=zone_name,
Schedule_Name=schedule, Schedule_Name=schedule,
Design_Flow_Rate_Calculation_Method='AirChanges/Hour', Design_Flow_Rate_Calculation_Method='AirChanges/Hour',
Air_Changes_per_Hour=thermal_zone.mechanical_air_change Air_Changes_per_Hour=thermal_zone.infiltration_rate_system_off
)
def _add_ventilation(self, thermal_zone, zone_name):
# for zone in self._idf.idfobjects["ZONE"]:
# if zone.Name == f'{zone_name}_infiltration':
# return
schedule = f'Ventilation schedules {thermal_zone.usage_name}'
# if schedule not in self._idf.idfobjects[self._HOURLY_SCHEDULE]:
# return
# todo: revise ventilation with Pilar
self._idf.newidfobject(self._VENTILATION,
Name=f'{zone_name}_ventilation',
Zone_or_ZoneList_Name=zone_name,
Schedule_Name=schedule,
Design_Flow_Rate_Calculation_Method='Flow/Zone',
Flow_Rate_per_Zone_Floor_Area=thermal_zone.infiltration_rate_system_off
)
def _add_dhw(self, thermal_zone, zone_name):
peak_flow_rate = thermal_zone.domestic_hot_water.peak_flow * thermal_zone.total_floor_area
self._idf.newidfobject(self._DHW,
Name=f'DHW {zone_name}',
Peak_Flow_Rate=peak_flow_rate,
Flow_Rate_Fraction_Schedule_Name=f'DHW_prof schedules {thermal_zone.usage_name}',
Target_Temperature_Schedule_Name=f'DHW_temp schedules {thermal_zone.usage_name}',
Hot_Water_Supply_Temperature_Schedule_Name=f'DHW_temp schedules {thermal_zone.usage_name}',
Cold_Water_Supply_Temperature_Schedule_Name=f'cold_temp schedules {zone_name}',
EndUse_Subcategory=f'DHW {zone_name}',
Zone_Name=zone_name
) )
def _rename_building(self, city_name): def _rename_building(self, city_name):
@ -395,21 +491,36 @@ class Idf:
self._add_window_construction_and_material(thermal_opening) self._add_window_construction_and_material(thermal_opening)
usage = thermal_zone.usage_name usage = thermal_zone.usage_name
if building.name in self._target_buildings or building.name in self._adjacent_buildings: if building.name in self._target_buildings or building.name in self._adjacent_buildings:
self._add_infiltration_schedules(thermal_zone) _new_schedules = self._create_infiltration_schedules(thermal_zone)
if thermal_zone.occupancy is not None: self._add_schedules(usage, 'Infiltration', _new_schedules)
self._add_schedules(usage, 'Occupancy', thermal_zone.occupancy.occupancy_schedules) self._add_schedules(usage, 'Occupancy', thermal_zone.occupancy.occupancy_schedules)
self._add_people_activity_level_schedules(thermal_zone) self._add_schedules(usage, 'HVAC AVAIL', thermal_zone.thermal_control.hvac_availability_schedules)
self._add_occupancy(thermal_zone, building.name) self._add_schedules(usage, 'Heating thermostat', thermal_zone.thermal_control.heating_set_point_schedules)
self._add_schedules(usage, 'Cooling thermostat', thermal_zone.thermal_control.cooling_set_point_schedules)
if thermal_zone.thermal_control is not None: self._add_schedules(usage, 'Lighting', thermal_zone.lighting.schedules)
self._add_schedules(usage, 'HVAC AVAIL', thermal_zone.thermal_control.hvac_availability_schedules) self._add_schedules(usage, 'Appliance', thermal_zone.appliances.schedules)
self._add_schedules(usage, 'Heating thermostat', thermal_zone.thermal_control.heating_set_point_schedules) self._add_schedules(usage, 'DHW_prof', thermal_zone.domestic_hot_water.schedules)
self._add_schedules(usage, 'Cooling thermostat', thermal_zone.thermal_control.cooling_set_point_schedules) _new_schedules = self._create_yearly_values_schedules('cold_temp',
building.cold_water_temperature[cte.HOUR]['epw'])
self._add_schedules(building.name, 'cold_temp', _new_schedules)
value = thermal_zone.domestic_hot_water.service_temperature
_new_schedules = self._create_constant_value_schedules('DHW_temp', value)
self._add_schedules(usage, 'DHW_temp', _new_schedules)
_occ = thermal_zone.occupancy
if _occ.occupancy_density == 0:
_total_heat = 0
else:
_total_heat = (_occ.sensible_convective_internal_gain + _occ.sensible_radiative_internal_gain
+ _occ.latent_internal_gain) / _occ.occupancy_density
_new_schedules = self._create_constant_value_schedules('Activity Level', _total_heat)
self._add_schedules(usage, 'Activity Level', _new_schedules)
self._add_zone(thermal_zone, building.name) self._add_zone(thermal_zone, building.name)
self._add_heating_system(thermal_zone, building.name) self._add_heating_system(thermal_zone, building.name)
self._add_infiltration(thermal_zone, building.name) self._add_infiltration(thermal_zone, building.name)
self._add_occupancy(thermal_zone, building.name)
self._add_lighting(thermal_zone, building.name)
self._add_appliances(thermal_zone, building.name)
self._add_dhw(thermal_zone, building.name)
if self._export_type == "Surfaces": if self._export_type == "Surfaces":
if building.name in self._target_buildings or building.name in self._adjacent_buildings: if building.name in self._target_buildings or building.name in self._adjacent_buildings:
if building.internal_zones[0].thermal_zones is not None: if building.internal_zones[0].thermal_zones is not None:
@ -420,27 +531,24 @@ class Idf:
self._add_shading(building) self._add_shading(building)
else: else:
self._add_block(building) self._add_block(building)
# todo: this should change to specific variables per zone to process only the ones in the buildings_to_calculate
for building in self._target_buildings:
continue
self._idf.newidfobject( self._idf.newidfobject(
"OUTPUT:VARIABLE", "OUTPUT:VARIABLE",
Variable_Name="Zone Ideal Loads Supply Air Total Heating Energy", Variable_Name="Zone Ideal Loads Supply Air Total Heating Energy",
Reporting_Frequency="Hourly", Reporting_Frequency="Monthly",
) )
self._idf.newidfobject( self._idf.newidfobject(
"OUTPUT:VARIABLE", "OUTPUT:VARIABLE",
Variable_Name="Zone Ideal Loads Supply Air Total Cooling Energy", Variable_Name="Zone Ideal Loads Supply Air Total Cooling Energy",
Reporting_Frequency="Hourly", Reporting_Frequency="Monthly",
) )
self._idf.match()
try: self._idf.newidfobject(
self._idf.intersect_match() "OUTPUT:VARIABLE",
except IndexError: Variable_Name="Water Use Equipment Heating Rate",
# seems to be a bug from geomeppy when surfaces cannot be intersected Reporting_Frequency="Monthly",
pass )
# post-process to erase windows associated to adiabatic walls # post-process to erase windows associated to adiabatic walls
windows_list = [] windows_list = []
@ -495,20 +603,29 @@ class Idf:
def _add_pure_geometry(self, building, zone_name): def _add_pure_geometry(self, building, zone_name):
for surface in building.surfaces: for surface in building.surfaces:
idf_surface_type = self.idf_surfaces[surface.type]
outside_boundary_condition = 'Outdoors' outside_boundary_condition = 'Outdoors'
sun_exposure = 'SunExposed' sun_exposure = 'SunExposed'
wind_exposure = 'WindExposed' wind_exposure = 'WindExposed'
idf_surface_type = self.idf_surfaces[surface.type]
_kwargs = {'Name': f'{surface.name}',
'Surface_Type': idf_surface_type,
'Zone_Name': zone_name}
if surface.type == cte.GROUND: if surface.type == cte.GROUND:
outside_boundary_condition = 'Ground' outside_boundary_condition = 'Ground'
sun_exposure = 'NoSun' sun_exposure = 'NoSun'
wind_exposure = 'NoWind' wind_exposure = 'NoWind'
idf_surface = self._idf.newidfobject(self._SURFACE, Name=f'{surface.name}', if surface.percentage_shared is not None and surface.percentage_shared > 0.5:
Surface_Type=idf_surface_type, outside_boundary_condition = 'Surface'
Zone_Name=zone_name, outside_boundary_condition_object = surface.name
Outside_Boundary_Condition=outside_boundary_condition, sun_exposure = 'NoSun'
Sun_Exposure=sun_exposure, wind_exposure = 'NoWind'
Wind_Exposure=wind_exposure) _kwargs['Outside_Boundary_Condition_Object'] = outside_boundary_condition_object
_kwargs['Outside_Boundary_Condition'] = outside_boundary_condition
_kwargs['Sun_Exposure'] = sun_exposure
_kwargs['Wind_Exposure'] = wind_exposure
idf_surface = self._idf.newidfobject(self._SURFACE, **_kwargs)
coordinates = self._matrix_to_list(surface.solid_polygon.coordinates, coordinates = self._matrix_to_list(surface.solid_polygon.coordinates,
self._city.lower_corner) self._city.lower_corner)
idf_surface.setcoords(coordinates) idf_surface.setcoords(coordinates)
@ -530,21 +647,31 @@ class Idf:
outside_boundary_condition = 'Outdoors' outside_boundary_condition = 'Outdoors'
sun_exposure = 'SunExposed' sun_exposure = 'SunExposed'
wind_exposure = 'WindExposed' wind_exposure = 'WindExposed'
_kwargs = {'Name': f'{boundary.parent_surface.name}',
'Surface_Type': idf_surface_type,
'Zone_Name': zone_name}
if boundary.parent_surface.type == cte.GROUND: if boundary.parent_surface.type == cte.GROUND:
outside_boundary_condition = 'Ground' outside_boundary_condition = 'Ground'
sun_exposure = 'NoSun' sun_exposure = 'NoSun'
wind_exposure = 'NoWind' wind_exposure = 'NoWind'
if boundary.parent_surface.percentage_shared is not None and boundary.parent_surface.percentage_shared >= 0.5:
outside_boundary_condition = 'Surface'
outside_boundary_condition_object = boundary.parent_surface.name
sun_exposure = 'NoSun'
wind_exposure = 'NoWind'
_kwargs['Outside_Boundary_Condition_Object'] = outside_boundary_condition_object
_kwargs['Outside_Boundary_Condition'] = outside_boundary_condition
_kwargs['Sun_Exposure'] = sun_exposure
_kwargs['Wind_Exposure'] = wind_exposure
if boundary.parent_surface.vegetation is not None: if boundary.parent_surface.vegetation is not None:
construction_name = f'{boundary.construction_name}_{boundary.parent_surface.vegetation.name}' construction_name = f'{boundary.construction_name}_{boundary.parent_surface.vegetation.name}'
else: else:
construction_name = boundary.construction_name construction_name = boundary.construction_name
surface = self._idf.newidfobject(self._SURFACE, Name=f'{boundary.parent_surface.name}', _kwargs['Construction_Name'] = construction_name
Surface_Type=idf_surface_type,
Zone_Name=zone_name, surface = self._idf.newidfobject(self._SURFACE, **_kwargs)
Construction_Name=construction_name,
Outside_Boundary_Condition=outside_boundary_condition,
Sun_Exposure=sun_exposure,
Wind_Exposure=wind_exposure)
coordinates = self._matrix_to_list(boundary.parent_surface.solid_polygon.coordinates, coordinates = self._matrix_to_list(boundary.parent_surface.solid_polygon.coordinates,
self._city.lower_corner) self._city.lower_corner)
surface.setcoords(coordinates) surface.setcoords(coordinates)
@ -570,7 +697,7 @@ class Idf:
for material in self._idf.idfobjects[self._WINDOW_MATERIAL_SIMPLE]: for material in self._idf.idfobjects[self._WINDOW_MATERIAL_SIMPLE]:
if material['Name'] == glazing: if material['Name'] == glazing:
if material['UFactor'] == opening.overall_u_value and \ if material['UFactor'] == opening.overall_u_value and \
material['Solar_Heat_Gain_Coefficient'] == opening.g_value: material['Solar_Heat_Gain_Coefficient'] == opening.g_value:
return True return True
return False return False

View File

@ -127,26 +127,31 @@
No, !- Do HVAC Sizing Simulation for Sizing Periods No, !- Do HVAC Sizing Simulation for Sizing Periods
1; !- Maximum Number of HVAC Sizing Simulation Passes 1; !- Maximum Number of HVAC Sizing Simulation Passes
Output:VariableDictionary,Regular; Output:Table:SummaryReports, AnnualBuildingUtilityPerformanceSummary,
DemandEndUseComponentsSummary,
SensibleHeatGainSummary,
InputVerificationandResultsSummary,
AdaptiveComfortSummary,
Standard62.1Summary,
ClimaticDataSummary,
EquipmentSummary,
EnvelopeSummary,
LightingSummary,
HVACSizingSummary,
SystemSummary,
ComponentSizingSummary,
OutdoorAirSummary,
ObjectCountSummary,
EndUseEnergyConsumptionOtherFuelsMonthly,
PeakEnergyEndUseOtherFuelsMonthly;
Output:Variable,*,Site Outdoor Air Drybulb Temperature,Timestep;
Output:Variable,*,Site Outdoor Air Wetbulb Temperature,Timestep; OutputControl:Table:Style, CommaAndHTML,JtoKWH;
Output:Variable,*,Site Outdoor Air Dewpoint Temperature,Timestep; Output:Meter,DISTRICTHEATING:Facility,monthly;
Output:Meter,DISTRICTCOOLING:Facility,monthly;
Output:Meter,InteriorEquipment:Electricity,monthly;
Output:Meter,InteriorLights:Electricity,monthly;
Output:Variable,*,Site Solar Azimuth Angle,Timestep; OutputControl:IlluminanceMap:Style,
Comma; !- Column separator
Output:Variable,*,Site Solar Altitude Angle,Timestep;
Output:Variable,*,Site Direct Solar Radiation Rate per Area,Timestep;
Output:Variable,*,Site Diffuse Solar Radiation Rate per Area,Timestep;
OutputControl:Table:Style,
HTML; !- Column Separator
Output:Table:SummaryReports,
AllSummary; !- Report 1 Name
Output:Diagnostics,DisplayUnusedSchedules;

View File

@ -51,6 +51,7 @@ class InselMonthlyEnergyBalance(Insel):
) )
self._export() self._export()
def _export(self): def _export(self):
for i_file, content in enumerate(self._contents): for i_file, content in enumerate(self._contents):
file_name = self._insel_files_paths[i_file] file_name = self._insel_files_paths[i_file]
@ -62,8 +63,8 @@ class InselMonthlyEnergyBalance(Insel):
levels_of_detail = self._city.level_of_detail levels_of_detail = self._city.level_of_detail
if levels_of_detail.geometry is None: if levels_of_detail.geometry is None:
raise Exception(f'Level of detail of geometry not assigned') raise Exception(f'Level of detail of geometry not assigned')
if levels_of_detail.geometry < 1: if levels_of_detail.geometry < 0.5:
raise Exception(f'Level of detail of geometry = {levels_of_detail.geometry}. Required minimum level 1') raise Exception(f'Level of detail of geometry = {levels_of_detail.geometry}. Required minimum level 0.5')
if levels_of_detail.construction is None: if levels_of_detail.construction is None:
raise Exception(f'Level of detail of construction not assigned') raise Exception(f'Level of detail of construction not assigned')
if levels_of_detail.construction < 1: if levels_of_detail.construction < 1:
@ -94,12 +95,19 @@ class InselMonthlyEnergyBalance(Insel):
inputs.append(f"{str(100 + i)}.1 % Radiation surface {str(i)}") inputs.append(f"{str(100 + i)}.1 % Radiation surface {str(i)}")
number_of_storeys = int(building.eave_height / building.average_storey_height) number_of_storeys = int(building.eave_height / building.average_storey_height)
attic_heated = building.attic_heated
basement_heated = building.basement_heated
if building.attic_heated is None:
attic_heated = 0
if building.basement_heated is None:
basement_heated = 0
# BUILDING PARAMETERS # BUILDING PARAMETERS
parameters = [f'{building.volume} % BP(1) Heated Volume (m3)', parameters = [f'{building.volume} % BP(1) Heated Volume (m3)',
f'{building.average_storey_height} % BP(2) Average storey height (m)', f'{building.average_storey_height} % BP(2) Average storey height (m)',
f'{number_of_storeys} % BP(3) Number of storeys above ground', f'{number_of_storeys} % BP(3) Number of storeys above ground',
f'{building.attic_heated} % BP(4) Attic heating type (0=no room, 1=unheated, 2=heated)', f'{attic_heated} % BP(4) Attic heating type (0=no room, 1=unheated, 2=heated)',
f'{building.basement_heated} % BP(5) Cellar heating type (0=no room, 1=unheated, 2=heated, ' f'{basement_heated} % BP(5) Cellar heating type (0=no room, 1=unheated, 2=heated, '
f'99=invalid)'] f'99=invalid)']
# todo: this method and the insel model have to be reviewed for more than one internal zone # todo: this method and the insel model have to be reviewed for more than one internal zone

View File

@ -17,7 +17,7 @@ class EnergyBuildingsExportsFactory:
""" """
Energy Buildings exports factory class Energy Buildings exports factory class
""" """
def __init__(self, export_type, city, path, target_buildings=None, adjacent_buildings=None): def __init__(self, export_type, city, path, target_buildings=None):
self._city = city self._city = city
self._export_type = '_' + export_type.lower() self._export_type = '_' + export_type.lower()
class_funcs = validate_import_export_type(EnergyBuildingsExportsFactory) class_funcs = validate_import_export_type(EnergyBuildingsExportsFactory)
@ -29,7 +29,6 @@ class EnergyBuildingsExportsFactory:
path = Path(path) path = Path(path)
self._path = path self._path = path
self._target_buildings = target_buildings self._target_buildings = target_buildings
self._adjacent_buildings = adjacent_buildings
@property @property
def _energy_ade(self): def _energy_ade(self):
@ -56,7 +55,7 @@ class EnergyBuildingsExportsFactory:
# todo: create a get epw file function based on the city # todo: create a get epw file function based on the city
weather_path = (Path(__file__).parent / '../data/weather/epw/CAN_PQ_Montreal.Intl.AP.716270_CWEC.epw').resolve() weather_path = (Path(__file__).parent / '../data/weather/epw/CAN_PQ_Montreal.Intl.AP.716270_CWEC.epw').resolve()
return Idf(self._city, self._path, (idf_data_path / 'Minimal.idf'), (idf_data_path / 'Energy+.idd'), weather_path, return Idf(self._city, self._path, (idf_data_path / 'Minimal.idf'), (idf_data_path / 'Energy+.idd'), weather_path,
target_buildings=self._target_buildings, adjacent_buildings=self._adjacent_buildings) target_buildings=self._target_buildings)
@property @property
def _insel_monthly_energy_balance(self): def _insel_monthly_energy_balance(self):

View File

@ -65,14 +65,6 @@ class ExportsFactory:
""" """
return Obj(self._city, self._path) return Obj(self._city, self._path)
@property
def _grounded_obj(self):
"""
Export the city geometry to obj with grounded coordinates
:return: None
"""
return Obj(self._city, self._path)
@property @property
def _sra(self): def _sra(self):
""" """

View File

@ -4,6 +4,8 @@ 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
import xmltodict import xmltodict
from hub.imports.weather_factory import WeatherFactory from hub.imports.weather_factory import WeatherFactory
@ -32,9 +34,12 @@ class SimplifiedRadiosityAlgorithm:
self._end_month = end_month self._end_month = end_month
self._end_day = end_day self._end_day = end_day
self._city = city self._city = city
self._city.climate_file = str((Path(file_name).parent / f'{city.name}.cli').resolve())
self._city.climate_reference_city = city.location
self._target_buildings = target_buildings self._target_buildings = target_buildings
self._weather_format = weather_format self._weather_format = weather_format
self._weather_file = weather_file self._weather_file = weather_file
self._export() self._export()
def _correct_point(self, point): def _correct_point(self, point):
@ -45,8 +50,8 @@ class SimplifiedRadiosityAlgorithm:
return [x, y, z] return [x, y, z]
def _export(self): def _export(self):
self._export_sra_xml()
self._export_sra_cli() self._export_sra_cli()
self._export_sra_xml()
def _export_sra_cli(self): def _export_sra_cli(self):
file = self._city.climate_file file = self._city.climate_file

View File

@ -201,6 +201,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

@ -0,0 +1,78 @@
"""
Dictionaries module for hub function to Montreal custom costs function
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 HubFunctionToMontrealCustomCostsFunction:
def __init__(self):
self._dictionary = {
cte.RESIDENTIAL: 'residential',
cte.SINGLE_FAMILY_HOUSE: 'residential',
cte.MULTI_FAMILY_HOUSE: 'residential',
cte.ROW_HOUSE: 'residential',
cte.MID_RISE_APARTMENT: 'residential',
cte.HIGH_RISE_APARTMENT: 'residential',
cte.OFFICE_AND_ADMINISTRATION: 'non-residential',
cte.SMALL_OFFICE: 'non-residential',
cte.MEDIUM_OFFICE: 'non-residential',
cte.LARGE_OFFICE: 'non-residential',
cte.COURTHOUSE: 'non-residential',
cte.FIRE_STATION: 'non-residential',
cte.PENITENTIARY: 'non-residential',
cte.POLICE_STATION: 'non-residential',
cte.POST_OFFICE: 'non-residential',
cte.LIBRARY: 'non-residential',
cte.EDUCATION: 'non-residential',
cte.PRIMARY_SCHOOL: 'non-residential',
cte.PRIMARY_SCHOOL_WITH_SHOWER: 'non-residential',
cte.SECONDARY_SCHOOL: 'non-residential',
cte.UNIVERSITY: 'non-residential',
cte.LABORATORY_AND_RESEARCH_CENTER: 'non-residential',
cte.STAND_ALONE_RETAIL: 'non-residential',
cte.HOSPITAL: 'non-residential',
cte.OUT_PATIENT_HEALTH_CARE: 'non-residential',
cte.HEALTH_CARE: 'non-residential',
cte.RETIREMENT_HOME_OR_ORPHANAGE: 'non-residential',
cte.COMMERCIAL: 'non-residential',
cte.STRIP_MALL: 'non-residential',
cte.SUPERMARKET: 'non-residential',
cte.RETAIL_SHOP_WITHOUT_REFRIGERATED_FOOD: 'non-residential',
cte.RETAIL_SHOP_WITH_REFRIGERATED_FOOD: 'non-residential',
cte.RESTAURANT: 'full service restaurant',
cte.QUICK_SERVICE_RESTAURANT: 'non-residential',
cte.FULL_SERVICE_RESTAURANT: 'non-residential',
cte.HOTEL: 'non-residential',
cte.HOTEL_MEDIUM_CLASS: 'non-residential',
cte.SMALL_HOTEL: 'non-residential',
cte.LARGE_HOTEL: 'non-residential',
cte.DORMITORY: 'non-residential',
cte.EVENT_LOCATION: 'non-residential',
cte.CONVENTION_CENTER: 'non-residential',
cte.HALL: 'non-residential',
cte.GREEN_HOUSE: 'non-residential',
cte.INDUSTRY: 'non-residential',
cte.WORKSHOP: 'non-residential',
cte.WAREHOUSE: 'non-residential',
cte.WAREHOUSE_REFRIGERATED: 'non-residential',
cte.SPORTS_LOCATION: 'non-residential',
cte.SPORTS_ARENA: 'non-residential',
cte.GYMNASIUM: 'non-residential',
cte.MOTION_PICTURE_THEATRE: 'non-residential',
cte.MUSEUM: 'non-residential',
cte.PERFORMING_ARTS_THEATRE: 'non-residential',
cte.TRANSPORTATION: 'non-residential',
cte.AUTOMOTIVE_FACILITY: 'non-residential',
cte.PARKING_GARAGE: 'non-residential',
cte.RELIGIOUS: 'non-residential',
cte.NON_HEATED: 'non-residential'
}
@property
def dictionary(self) -> dict:
return self._dictionary

View File

@ -67,7 +67,7 @@ class HubFunctionToNrelConstructionFunction:
cte.MUSEUM: 'n/a', cte.MUSEUM: 'n/a',
cte.PERFORMING_ARTS_THEATRE: 'n/a', cte.PERFORMING_ARTS_THEATRE: 'n/a',
cte.TRANSPORTATION: 'n/a', cte.TRANSPORTATION: 'n/a',
cte.AUTOMOTIVE_FACILITY: 'n/aquebec_to_hub', cte.AUTOMOTIVE_FACILITY: 'n/a',
cte.PARKING_GARAGE: 'n/a', cte.PARKING_GARAGE: 'n/a',
cte.RELIGIOUS: 'n/a', cte.RELIGIOUS: 'n/a',
cte.NON_HEATED: 'n/a' cte.NON_HEATED: 'n/a'

View File

@ -15,6 +15,7 @@ from hub.helpers.data.hub_usage_to_comnet_usage import HubUsageToComnetUsage
from hub.helpers.data.hub_usage_to_hft_usage import HubUsageToHftUsage from hub.helpers.data.hub_usage_to_hft_usage import HubUsageToHftUsage
from hub.helpers.data.hub_usage_to_nrcan_usage import HubUsageToNrcanUsage from hub.helpers.data.hub_usage_to_nrcan_usage import HubUsageToNrcanUsage
from hub.helpers.data.montreal_system_to_hub_energy_generation_system import MontrealSystemToHubEnergyGenerationSystem from hub.helpers.data.montreal_system_to_hub_energy_generation_system import MontrealSystemToHubEnergyGenerationSystem
from hub.helpers.data.hub_function_to_montreal_custom_costs_function import HubFunctionToMontrealCustomCostsFunction
class Dictionaries: class Dictionaries:
@ -98,3 +99,11 @@ class Dictionaries:
Get montreal custom system names to hub energy system names, transformation dictionary Get montreal custom system names to hub energy system names, transformation dictionary
""" """
return MontrealSystemToHubEnergyGenerationSystem().dictionary return MontrealSystemToHubEnergyGenerationSystem().dictionary
@property
def hub_function_to_montreal_custom_costs_function(self) -> dict:
"""
Get hub function to Montreal custom costs function, transformation dictionary
:return: dict
"""
return HubFunctionToMontrealCustomCostsFunction().dictionary

View File

@ -6,9 +6,9 @@ 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 PIL import Image
from trimesh import Trimesh from trimesh import Trimesh
from trimesh import intersections from trimesh import intersections
@ -55,16 +55,22 @@ class GeometryHelper:
'urn:adv:crs:ETRS89_UTM32*DE_DHHN92_NH': 'epsg:25832' 'urn:adv:crs:ETRS89_UTM32*DE_DHHN92_NH': 'epsg:25832'
} }
@staticmethod
def factor():
return 0.5
def __init__(self, delta=0, area_delta=0): def __init__(self, delta=0, area_delta=0):
self._delta = delta self._delta = delta
self._area_delta = area_delta self._area_delta = area_delta
@staticmethod @staticmethod
def coordinate_to_map_point(coordinate, city): def coordinate_to_map_point(coordinate, city):
return MapPoint(((city.upper_corner[0] - coordinate[0]) * 0.5), ((city.upper_corner[1] - coordinate[1]) * 0.5)) factor = GeometryHelper.factor()
return MapPoint(((coordinate[0] - city.lower_corner[0]) * factor), ((coordinate[1] - city.lower_corner[1]) * factor))
@staticmethod @staticmethod
def city_mapping(city, building_names=None, plot=False): def city_mapping(city, building_names=None, plot=False):
""" """
Returns a shared_information dictionary like Returns a shared_information dictionary like
@ -75,8 +81,9 @@ class GeometryHelper:
lines_information = {} lines_information = {}
if building_names is None: if building_names is None:
building_names = [b.name for b in city.buildings] building_names = [b.name for b in city.buildings]
x = int((city.upper_corner[0] - city.lower_corner[0]) * 0.5) + 1 factor = GeometryHelper.factor()
y = int((city.upper_corner[1] - city.lower_corner[1]) * 0.5) + 1 x = math.ceil((city.upper_corner[0] - city.lower_corner[0]) * factor) + 1
y = math.ceil((city.upper_corner[1] - city.lower_corner[1]) * factor) + 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
@ -92,15 +99,18 @@ class GeometryHelper:
if i == length: if i == length:
j = 0 j = 0
next_coordinate = ground.perimeter_polygon.coordinates[j] next_coordinate = ground.perimeter_polygon.coordinates[j]
point = GeometryHelper.coordinate_to_map_point(coordinate, city) distance = GeometryHelper.distance_between_points(coordinate, next_coordinate)
distance = int(GeometryHelper.distance_between_points(coordinate, next_coordinate)) steps = int(distance * factor * 2)
if distance == 0: if steps == 0:
continue continue
delta_x = (coordinate[0] - next_coordinate[0]) / (distance / 0.5) delta_x = (next_coordinate[0] - coordinate[0]) / steps
delta_y = (coordinate[1] - next_coordinate[1]) / (distance / 0.5) delta_y = (next_coordinate[1] - coordinate[1]) / steps
for k in range(0, distance):
x = MapPoint(point.x + (delta_x * k), point.y + (delta_y * k)).x for k in range(0, steps):
y = MapPoint(point.x + (delta_x * k), point.y + (delta_y * k)).y new_coordinate = (coordinate[0] + (delta_x * k), coordinate[1] + (delta_y * k))
point = GeometryHelper.coordinate_to_map_point(new_coordinate, city)
x = point.x
y = point.y
if city_map[x][y] == '': if city_map[x][y] == '':
city_map[x][y] = building.name city_map[x][y] = building.name
map_info[x][y] = { map_info[x][y] = {
@ -172,48 +182,6 @@ class GeometryHelper:
img.show() img.show()
return lines_information 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
@staticmethod @staticmethod
def segment_list_to_trimesh(lines) -> Trimesh: def segment_list_to_trimesh(lines) -> Trimesh:
""" """
@ -298,19 +266,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', encoding='utf-8') 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

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

71
hub/helpers/peak_loads.py Normal file
View File

@ -0,0 +1,71 @@
import math
import hub.helpers.constants as cte
from hub.helpers.peak_calculation.loads_calculation import LoadsCalculation
_MONTH_STARTING_HOUR = [0, 744, 1416, 2160, 2880, 3624, 4344, 5088, 5832, 6552, 7296, 8016, math.inf]
def peak_loads_from_hourly(hourly_values):
month = 1
peaks = [0 for _ in range(12)]
for i, value in enumerate(hourly_values):
if _MONTH_STARTING_HOUR[month] <= i:
month += 1
if value > peaks[month-1]:
peaks[month-1] = value
return peaks
def heating_peak_loads_from_methodology(building):
monthly_heating_loads = []
ambient_temperature = building.external_temperature[cte.HOUR]['epw']
for month in range(0, 12):
ground_temperature = building.ground_temperature[cte.MONTH]['2'][month]
heating_ambient_temperature = 100
start_hour = _MONTH_STARTING_HOUR[month]
end_hour = 8760
if month < 11:
end_hour = _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
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
if heating_load < 0:
heating_load = 0
monthly_heating_loads.append(heating_load)
return monthly_heating_loads
def cooling_peak_loads_from_methodology(building):
monthly_cooling_loads = []
ambient_temperature = building.external_temperature[cte.HOUR]['epw']
for month in range(0, 12):
ground_temperature = building.ground_temperature[cte.MONTH]['2'][month]
cooling_ambient_temperature = -100
cooling_calculation_hour = -1
start_hour = _MONTH_STARTING_HOUR[month]
end_hour = 8760
if month < 11:
end_hour = _MONTH_STARTING_HOUR[month + 1]
for hour in range(start_hour, end_hour):
temperature = ambient_temperature[hour]
if temperature > cooling_ambient_temperature:
cooling_ambient_temperature = temperature
cooling_calculation_hour = hour
loads = LoadsCalculation(building)
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('sra', 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 cooling_load > 0:
cooling_load = 0
monthly_cooling_loads.append(abs(cooling_load))
return monthly_cooling_loads

View File

@ -4,9 +4,11 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group Copyright © 2022 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
""" """
import math import math
import sys import sys
from hub.hub_logger import get_logger from hub.hub_logger import logger
import hub.helpers.constants as cte import hub.helpers.constants as cte
from hub.catalog_factories.construction_catalog_factory import ConstructionCatalogFactory from hub.catalog_factories.construction_catalog_factory import ConstructionCatalogFactory
@ -16,8 +18,6 @@ from hub.helpers.dictionaries import Dictionaries
from hub.imports.construction.helpers.construction_helper import ConstructionHelper from hub.imports.construction.helpers.construction_helper import ConstructionHelper
from hub.imports.construction.helpers.storeys_generation import StoreysGeneration from hub.imports.construction.helpers.storeys_generation import StoreysGeneration
logger = get_logger()
class NrcanPhysicsParameters: class NrcanPhysicsParameters:
""" """

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
@ -59,63 +61,51 @@ class Geojson:
self._min_y = y self._min_y = y
@staticmethod @staticmethod
def _create_buildings_lod0(name, year_of_construction, function, surfaces_coordinates): def _create_building_lod0(name, year_of_construction, function, surface_coordinates):
surfaces = [] points = igh.points_from_string(igh.remove_last_point_from_string(surface_coordinates))
buildings = [] points = igh.invert_points(points)
for zone, surface_coordinates in enumerate(surfaces_coordinates): polygon = Polygon(points)
points = igh.points_from_string(igh.remove_last_point_from_string(surface_coordinates)) polygon.area = igh.ground_area(points)
# geojson provides the roofs, need to be transform into grounds surface = Surface(polygon, polygon, name=f'{name}_ground')
points = igh.invert_points(points) return Building(f'{name}', [surface], year_of_construction, function)
polygon = Polygon(points)
polygon.area = igh.ground_area(points)
surface = Surface(polygon, polygon)
surfaces.append(surface)
buildings.append(Building(f'{name}_zone_{zone}', surfaces, year_of_construction, function))
return buildings
@staticmethod @staticmethod
def _create_buildings_lod1(name, year_of_construction, function, height, surface_coordinates): def _create_building_lod1(name, year_of_construction, function, height, surface_coordinates):
lod0_buildings = Geojson._create_buildings_lod0(name, year_of_construction, function, surface_coordinates) building = Geojson._create_building_lod0(name, year_of_construction, function, surface_coordinates)
surfaces = [] surfaces = []
buildings = [] volume = 0
for ground in building.grounds:
for zone, lod0_building in enumerate(lod0_buildings): volume += ground.solid_polygon.area * height
for surface in lod0_building.grounds: surfaces.append(ground)
roof_coordinates = []
volume = surface.solid_polygon.area * height # adding a roof means invert the polygon coordinates and change the Z value
surfaces.append(surface) for coordinate in ground.solid_polygon.coordinates:
roof_coordinates = [] roof_coordinate = np.array([coordinate[0], coordinate[1], height])
# adding a roof means invert the polygon coordinates and change the Z value # insert the roof rotated already
for coordinate in surface.solid_polygon.coordinates: roof_coordinates.insert(0, roof_coordinate)
roof_coordinate = np.array([coordinate[0], coordinate[1], height]) roof_polygon = Polygon(roof_coordinates)
# insert the roof rotated already roof_polygon.area = ground.solid_polygon.area
roof_coordinates.insert(0, roof_coordinate) roof = Surface(roof_polygon, roof_polygon)
polygon = Polygon(roof_coordinates) surfaces.append(roof)
polygon.area = surface.solid_polygon.area # adding a wall means add the point coordinates and the next point coordinates with Z's height and 0
roof = Surface(polygon, polygon) coordinates_length = len(roof.solid_polygon.coordinates)
surfaces.append(roof) for i, coordinate in enumerate(roof.solid_polygon.coordinates):
# adding a wall means add the point coordinates and the next point coordinates with Z's height and 0 j = i + 1
coordinates_length = len(roof.solid_polygon.coordinates) if j == coordinates_length:
for i, coordinate in enumerate(roof.solid_polygon.coordinates): j = 0
j = i + 1 next_coordinate = roof.solid_polygon.coordinates[j]
if j == coordinates_length: wall_coordinates = [
j = 0 np.array([coordinate[0], coordinate[1], 0.0]),
next_coordinate = roof.solid_polygon.coordinates[j] np.array([next_coordinate[0], next_coordinate[1], 0.0]),
wall_coordinates = [ np.array([next_coordinate[0], next_coordinate[1], next_coordinate[2]]),
np.array([coordinate[0], coordinate[1], 0.0]), np.array([coordinate[0], coordinate[1], coordinate[2]])
np.array([next_coordinate[0], next_coordinate[1], 0.0]), ]
np.array([next_coordinate[0], next_coordinate[1], next_coordinate[2]]), polygon = Polygon(wall_coordinates)
np.array([coordinate[0], coordinate[1], coordinate[2]]) wall = Surface(polygon, polygon)
] surfaces.append(wall)
polygon = Polygon(wall_coordinates) building = Building(f'{name}', surfaces, year_of_construction, function)
wall = Surface(polygon, polygon) building.volume = volume
surfaces.append(wall) return building
building = Building(f'{name}_zone_{zone}', surfaces, year_of_construction, function)
building.volume = volume
buildings.append(building)
return buildings
def _get_polygons(self, polygons, coordinates): def _get_polygons(self, polygons, coordinates):
if type(coordinates[0][self.X]) != float: if type(coordinates[0][self.X]) != float:
@ -157,7 +147,7 @@ class Geojson:
if point[2] < 0.5: if point[2] < 0.5:
ground_line.append(point) ground_line.append(point)
for entry in building_mapped: for entry in building_mapped:
if building_mapped[entry]['shared_points'] <= 3: if building_mapped[entry]['shared_points'] <= 2:
continue continue
line = [building_mapped[entry]['line_start'], building_mapped[entry]['line_end']] line = [building_mapped[entry]['line_start'], building_mapped[entry]['line_end']]
neighbour_line = [building_mapped[entry]['neighbour_line_start'], neighbour_line = [building_mapped[entry]['neighbour_line_start'],
@ -181,14 +171,13 @@ class Geojson:
Get city out of a Geojson file Get city out of a Geojson file
""" """
if self._city is None: if self._city is None:
missing_functions = []
buildings = [] buildings = []
building_id = 0 lod = 0
lod = 1
for feature in self._geojson['features']: for feature in self._geojson['features']:
extrusion_height = 0 extrusion_height = 0
if self._extrusion_height_field is not None: if self._extrusion_height_field is not None:
extrusion_height = float(feature['properties'][self._extrusion_height_field]) extrusion_height = float(feature['properties'][self._extrusion_height_field])
lod = 0.5
year_of_construction = None year_of_construction = None
if self._year_of_construction_field is not None: if self._year_of_construction_field is not None:
year_of_construction = int(feature['properties'][self._year_of_construction_field]) year_of_construction = int(feature['properties'][self._year_of_construction_field])
@ -199,44 +188,207 @@ class Geojson:
# use the transformation dictionary to retrieve the proper function # use the transformation dictionary to retrieve the proper function
if function in self._function_to_hub: if function in self._function_to_hub:
function = self._function_to_hub[function] function = self._function_to_hub[function]
else:
if function not in missing_functions:
missing_functions.append(function)
function = function
geometry = feature['geometry'] geometry = feature['geometry']
if 'id' in feature: if 'id' in feature:
building_name = feature['id'] building_name = feature['id']
else: if self._name_field is not None:
building_name = f'building_{building_id}' building_name = feature['properties'][self._name_field]
building_id += 1
polygons = []
for part, coordinates in enumerate(geometry['coordinates']):
polygons = self._get_polygons(polygons, coordinates)
for zone, polygon in enumerate(polygons):
if extrusion_height == 0:
buildings = buildings + Geojson._create_buildings_lod0(f'{building_name}_part_{part}',
year_of_construction,
function,
[polygon])
lod = 0
else:
if self._max_z < extrusion_height:
self._max_z = extrusion_height
buildings = buildings + Geojson._create_buildings_lod1(f'{building_name}_part_{part}',
year_of_construction,
function,
extrusion_height,
[polygon])
if str(geometry['type']).lower() == 'polygon':
buildings.append(self._parse_polygon(geometry['coordinates'],
building_name,
function,
year_of_construction,
extrusion_height))
elif str(geometry['type']).lower() == 'multipolygon':
buildings.append(self._parse_multi_polygon(geometry['coordinates'],
building_name,
function,
year_of_construction,
extrusion_height))
else:
raise NotImplementedError(f'Geojson geometry type [{geometry["type"]}] unknown')
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 # Do not include "small building-like structures" to buildings
if building.floor_area >= 25: 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 > 0:
lines_information = GeometryHelper.city_mapping(self._city, plot=False) lines_information = GeometryHelper.city_mapping(self._city, plot=False)
self._store_shared_percentage_to_walls(self._city, lines_information) self._store_shared_percentage_to_walls(self._city, lines_information)
if len(missing_functions) > 0:
print(f'There are unknown functions {missing_functions}')
return self._city return self._city
def _polygon_coordinates_to_3d(self, polygon_coordinates):
transformed_coordinates = ''
for coordinate in polygon_coordinates:
transformed = self._transformer.transform(coordinate[self.Y], coordinate[self.X])
self._save_bounds(transformed[self.X], transformed[self.Y])
transformed_coordinates = f'{transformed_coordinates} {transformed[self.X]} {transformed[self.Y]} 0.0'
return transformed_coordinates.lstrip(' ')
def _parse_polygon(self, coordinates, building_name, function, year_of_construction, extrusion_height):
surfaces = []
for polygon_coordinates in coordinates:
points = igh.points_from_string(
igh.remove_last_point_from_string(
self._polygon_coordinates_to_3d(polygon_coordinates)
)
)
points = igh.invert_points(points)
polygon = Polygon(points)
polygon.area = igh.ground_area(points)
surface = Surface(polygon, polygon)
print(surface.type, polygon.area)
if surface.type == cte.GROUND:
surfaces.append(surface)
else:
distance = cte.MAX_FLOAT
hole_connect = 0
surface_connect = 0
for hole_index, hole_coordinate in enumerate(polygon.coordinates):
for surface_index, ground_coordinate in enumerate(surfaces[-1].solid_polygon.coordinates):
current_distance = GeometryHelper.distance_between_points(hole_coordinate, ground_coordinate)
if current_distance < distance:
distance = current_distance
hole_connect = hole_index
surface_connect = surface_index
hole = polygon.coordinates[hole_connect:] + polygon.coordinates[:hole_connect]
prefix_coordinates = surfaces[-1].solid_polygon.coordinates[:surface_connect]
trail_coordinates = surfaces[-1].solid_polygon.coordinates[surface_connect:]
coordinates = prefix_coordinates + hole + [hole[0], surfaces[-1].solid_polygon.coordinates[surface_connect-1]] + trail_coordinates
polygon = Polygon(coordinates)
polygon.area = igh.ground_area(coordinates)
surfaces[-1] = Surface(polygon, polygon)
if len(surfaces) > 1:
raise ValueError('too many surfaces!!!!')
building = Building(building_name, surfaces, year_of_construction, function)
print(extrusion_height)
if extrusion_height == 0:
return building
else:
volume = 0
for ground in building.grounds:
volume += ground.solid_polygon.area * extrusion_height
surfaces.append(ground)
roof_coordinates = []
# adding a roof means invert the polygon coordinates and change the Z value
for coordinate in ground.solid_polygon.coordinates:
roof_coordinate = np.array([coordinate[0], coordinate[1], extrusion_height])
# insert the roof rotated already
roof_coordinates.insert(0, roof_coordinate)
roof_polygon = Polygon(roof_coordinates)
roof_polygon.area = ground.solid_polygon.area
roof = Surface(roof_polygon, roof_polygon)
surfaces.append(roof)
# adding a wall means add the point coordinates and the next point coordinates with Z's height and 0
coordinates_length = len(roof.solid_polygon.coordinates)
for i, coordinate in enumerate(roof.solid_polygon.coordinates):
j = i + 1
if j == coordinates_length:
j = 0
next_coordinate = roof.solid_polygon.coordinates[j]
wall_coordinates = [
np.array([coordinate[0], coordinate[1], 0.0]),
np.array([next_coordinate[0], next_coordinate[1], 0.0]),
np.array([next_coordinate[0], next_coordinate[1], next_coordinate[2]]),
np.array([coordinate[0], coordinate[1], coordinate[2]])
]
polygon = Polygon(wall_coordinates)
wall = Surface(polygon, polygon)
surfaces.append(wall)
building = Building(f'{building_name}', surfaces, year_of_construction, function)
building.volume = volume
return building
"""
coordinates_3d = self._polygon_coordinates_to_3d(polygon_coordinates)
if extrusion_height == 0:
building = Geojson._create_building_lod0(f'{building_name}',
year_of_construction,
function,
coordinates_3d)
else:
if self._max_z < extrusion_height:
self._max_z = extrusion_height
print(building_name)
building = Geojson._create_building_lod1(f'{building_name}',
year_of_construction,
function,
extrusion_height,
coordinates_3d)
return building
"""
def _parse_multi_polygon(self, polygons_coordinates, building_name, function, year_of_construction, extrusion_height):
surfaces = []
for coordinates in polygons_coordinates:
for polygon_coordinates in coordinates:
points = igh.points_from_string(
igh.remove_last_point_from_string(
self._polygon_coordinates_to_3d(polygon_coordinates)
)
)
points = igh.invert_points(points)
polygon = Polygon(points)
polygon.area = igh.ground_area(points)
surface = Surface(polygon, polygon)
if surface.type == cte.GROUND:
surfaces.append(surface)
else:
distance = cte.MAX_FLOAT
hole_connect = 0
surface_connect = 0
for hole_index, hole_coordinate in enumerate(polygon.coordinates):
for surface_index, ground_coordinate in enumerate(surfaces[-1].solid_polygon.coordinates):
current_distance = GeometryHelper.distance_between_points(hole_coordinate, ground_coordinate)
if current_distance < distance:
distance = current_distance
hole_connect = hole_index
surface_connect = surface_index
hole = polygon.coordinates[hole_connect:] + polygon.coordinates[:hole_connect]
prefix_coordinates = surfaces[-1].solid_polygon.coordinates[:surface_connect]
trail_coordinates = surfaces[-1].solid_polygon.coordinates[surface_connect:]
coordinates = prefix_coordinates + hole + [hole[0]] + trail_coordinates
polygon = Polygon(coordinates)
polygon.area = igh.ground_area(coordinates)
surfaces[-1] = Surface(polygon, polygon)
building = Building(building_name, surfaces, year_of_construction, function)
if extrusion_height == 0:
return building
else:
volume = 0
for ground in building.grounds:
volume += ground.solid_polygon.area * extrusion_height
surfaces.append(ground)
roof_coordinates = []
# adding a roof means invert the polygon coordinates and change the Z value
for coordinate in ground.solid_polygon.coordinates:
roof_coordinate = np.array([coordinate[0], coordinate[1], extrusion_height])
# insert the roof rotated already
roof_coordinates.insert(0, roof_coordinate)
roof_polygon = Polygon(roof_coordinates)
roof_polygon.area = ground.solid_polygon.area
roof = Surface(roof_polygon, roof_polygon)
surfaces.append(roof)
# adding a wall means add the point coordinates and the next point coordinates with Z's height and 0
coordinates_length = len(roof.solid_polygon.coordinates)
for i, coordinate in enumerate(roof.solid_polygon.coordinates):
j = i + 1
if j == coordinates_length:
j = 0
next_coordinate = roof.solid_polygon.coordinates[j]
wall_coordinates = [
np.array([coordinate[0], coordinate[1], 0.0]),
np.array([next_coordinate[0], next_coordinate[1], 0.0]),
np.array([next_coordinate[0], next_coordinate[1], next_coordinate[2]]),
np.array([coordinate[0], coordinate[1], coordinate[2]])
]
polygon = Polygon(wall_coordinates)
wall = Surface(polygon, polygon)
surfaces.append(wall)
building = Building(f'{building_name}', surfaces, year_of_construction, function)
building.volume = volume
return building

View File

@ -25,6 +25,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,
@ -37,6 +38,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
@ -79,6 +81,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,
@ -106,6 +109,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
@ -13,7 +12,7 @@ import hub.helpers.constants as cte
class InselMonthlyEnergyBalance: class InselMonthlyEnergyBalance:
""" """
Import SRA results Import insel monthly energy balance results
""" """
def __init__(self, city, base_path): def __init__(self, city, base_path):
@ -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

@ -55,7 +55,7 @@ class ResultFactory:
""" """
InselHeatPumpEnergyDemand(self._city, self._base_path, self._hp_model).enrich() InselHeatPumpEnergyDemand(self._city, self._base_path, self._hp_model).enrich()
def _insel_meb(self): def _insel_monthly_energy_balance(self):
""" """
Enrich the city with insel monthly energy balance results Enrich the city with insel monthly energy balance results
""" """

View File

@ -46,12 +46,12 @@ class NrcanUsageParameters:
sys.stderr.write(f'Building {building.name} has unknown usage archetype for usage: {usage_name}\n') sys.stderr.write(f'Building {building.name} has unknown usage archetype for usage: {usage_name}\n')
continue continue
usage_name = Dictionaries().hub_usage_to_comnet_usage[building.function] comnet_usage_name = Dictionaries().hub_usage_to_comnet_usage[building.function]
try: try:
comnet_archetype_usage = self._search_archetypes(comnet_catalog, usage_name) comnet_archetype_usage = self._search_archetypes(comnet_catalog, comnet_usage_name)
except KeyError: except KeyError:
logger.error(f'Building {building.name} has unknown usage archetype for usage: {usage_name}\n') logger.error(f'Building {building.name} has unknown usage archetype for usage: {comnet_usage_name}\n')
sys.stderr.write(f'Building {building.name} has unknown usage archetype for usage: {usage_name}\n') sys.stderr.write(f'Building {building.name} has unknown usage archetype for usage: {comnet_usage_name}\n')
continue continue
for internal_zone in building.internal_zones: for internal_zone in building.internal_zones:
@ -82,8 +82,10 @@ class NrcanUsageParameters:
@staticmethod @staticmethod
def _assign_values(usage, archetype, volume_per_area, cold_water_temperature): def _assign_values(usage, archetype, volume_per_area, cold_water_temperature):
if archetype.mechanical_air_change > 0: if archetype.mechanical_air_change > 0:
# ACH
usage.mechanical_air_change = archetype.mechanical_air_change usage.mechanical_air_change = archetype.mechanical_air_change
elif archetype.ventilation_rate > 0: elif archetype.ventilation_rate > 0:
# m3/m2.s to ACH
usage.mechanical_air_change = archetype.ventilation_rate / volume_per_area * cte.HOUR_TO_SECONDS usage.mechanical_air_change = archetype.ventilation_rate / volume_per_area * cte.HOUR_TO_SECONDS
else: else:
usage.mechanical_air_change = 0 usage.mechanical_air_change = 0

View File

@ -71,7 +71,6 @@ class EpwWeatherParameters:
except SystemExit: except SystemExit:
sys.stderr.write(f'Error: wrong formatting of weather file {self._path}\n') sys.stderr.write(f'Error: wrong formatting of weather file {self._path}\n')
sys.exit() sys.exit()
for building in self._city.buildings: for building in self._city.buildings:
building.ground_temperature[cte.MONTH] = ground_temperature building.ground_temperature[cte.MONTH] = ground_temperature
ground_temperature = {} ground_temperature = {}

View File

@ -54,3 +54,7 @@ class WeatherFactory:
:return: None :return: None
""" """
getattr(self, self._handler, lambda: None)() getattr(self, self._handler, lambda: None)()
def enrich_debug(self):
_path = Path(self._base_path / 'epw').resolve()
return EpwWeatherParameters(self._city, _path, self._file_name)

View File

@ -18,11 +18,13 @@ class TestCostsCatalog(TestCase):
self.assertIsNotNone(catalog, 'catalog is none') self.assertIsNotNone(catalog, 'catalog is none')
content = catalog.entries() content = catalog.entries()
self.assertTrue(len(content.archetypes) == 2) self.assertTrue(len(content.archetypes) == 2)
print(catalog)
# retrieving all the entries should not raise any exceptions # retrieving all the entries should not raise any exceptions
for category in catalog_categories: for category in catalog_categories:
for value in catalog_categories[category]: for value in catalog_categories[category]:
catalog.get_entry(value) catalog.get_entry(value)
print(value)
with self.assertRaises(IndexError): with self.assertRaises(IndexError):
catalog.get_entry('unknown') catalog.get_entry('unknown')

View File

@ -13,6 +13,7 @@ from hub.imports.geometry_factory import GeometryFactory
from hub.helpers.dictionaries import Dictionaries from hub.helpers.dictionaries import Dictionaries
from hub.imports.construction_factory import ConstructionFactory from hub.imports.construction_factory import ConstructionFactory
from hub.imports.usage_factory import UsageFactory from hub.imports.usage_factory import UsageFactory
from hub.imports.weather_factory import WeatherFactory
from hub.exports.exports_factory import ExportsFactory from hub.exports.exports_factory import ExportsFactory
from hub.exports.energy_building_exports_factory import EnergyBuildingsExportsFactory from hub.exports.energy_building_exports_factory import EnergyBuildingsExportsFactory
import hub.helpers.constants as cte import hub.helpers.constants as cte
@ -89,7 +90,6 @@ class TestExports(TestCase):
""" """
self._export_building_energy('energy_ade') self._export_building_energy('energy_ade')
def test_sra_export(self): def test_sra_export(self):
""" """
export to SRA export to SRA
@ -110,6 +110,7 @@ class TestExports(TestCase):
ConstructionFactory('nrcan', city).enrich() ConstructionFactory('nrcan', city).enrich()
EnergyBuildingsExportsFactory('idf', city, self._output_path).export() EnergyBuildingsExportsFactory('idf', city, self._output_path).export()
UsageFactory('nrcan', city).enrich() UsageFactory('nrcan', city).enrich()
WeatherFactory('epw', city, file_name='CAN_PQ_Montreal.Intl.AP.716270_CWEC.epw').enrich()
try: try:
EnergyBuildingsExportsFactory('idf', city, self._output_path).export() EnergyBuildingsExportsFactory('idf', city, self._output_path).export()
except Exception: except Exception:

View File

@ -8,7 +8,10 @@ from pathlib import Path
from unittest import TestCase from unittest import TestCase
import hub.exports.exports_factory import hub.exports.exports_factory
from hub.helpers.dictionaries import MontrealFunctionToHubFunction from hub.imports.usage_factory import UsageFactory
from hub.exports.energy_building_exports_factory import EnergyBuildingsExportsFactory
from hub.helpers.dictionaries import MontrealFunctionToHubFunction, Dictionaries
from hub.helpers.geometry_helper import GeometryHelper from hub.helpers.geometry_helper import GeometryHelper
from hub.imports.construction_factory import ConstructionFactory from hub.imports.construction_factory import ConstructionFactory
from hub.imports.geometry_factory import GeometryFactory from hub.imports.geometry_factory import GeometryFactory
@ -134,15 +137,21 @@ class TestGeometryFactory(TestCase):
""" """
Test geojson import Test geojson import
""" """
file = '2000_buildings.geojson' file = '72.geojson'
city = GeometryFactory('geojson', city = GeometryFactory('geojson',
path=(self._example_path / file).resolve(), path=(self._example_path / file).resolve(),
height_field='building_height', height_field='citygml_me',
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
# include 25 square meter condition for a building reduces buildings number from 2289 to 2057 for building in city.buildings:
self.assertEqual(2057, len(city.buildings), 'wrong number of buildings') volume = building.volume
print(volume)
if f'{volume}' == 'inf':
print(building.name, 'is not closed')
hub.exports.exports_factory.ExportsFactory('obj', city, self._output_path).export()
self.assertEqual(1964, len(city.buildings), 'wrong number of buildings')
def test_map_neighbours(self): def test_map_neighbours(self):
""" """
@ -170,3 +179,7 @@ class TestGeometryFactory(TestCase):
self.assertEqual('3_part_0_zone_0', city.city_object('2_part_0_zone_0').neighbours[1].name) self.assertEqual('3_part_0_zone_0', city.city_object('2_part_0_zone_0').neighbours[1].name)
self.assertEqual('1_part_0_zone_0', city.city_object('3_part_0_zone_0').neighbours[0].name) self.assertEqual('1_part_0_zone_0', city.city_object('3_part_0_zone_0').neighbours[0].name)
self.assertEqual('2_part_0_zone_0', city.city_object('3_part_0_zone_0').neighbours[1].name) self.assertEqual('2_part_0_zone_0', city.city_object('3_part_0_zone_0').neighbours[1].name)

View File

@ -11,6 +11,7 @@ from unittest import TestCase
from hub.imports.geometry_factory import GeometryFactory from hub.imports.geometry_factory import GeometryFactory
from hub.imports.usage_factory import UsageFactory from hub.imports.usage_factory import UsageFactory
from hub.imports.construction_factory import ConstructionFactory from hub.imports.construction_factory import ConstructionFactory
from hub.imports.weather_factory import WeatherFactory
from hub.exports.energy_building_exports_factory import EnergyBuildingsExportsFactory from hub.exports.energy_building_exports_factory import EnergyBuildingsExportsFactory
from hub.city_model_structure.greenery.vegetation import Vegetation from hub.city_model_structure.greenery.vegetation import Vegetation
from hub.city_model_structure.greenery.soil import Soil from hub.city_model_structure.greenery.soil import Soil
@ -32,6 +33,7 @@ class GreeneryInIdf(TestCase):
building.year_of_construction = 2006 building.year_of_construction = 2006
ConstructionFactory('nrel', city).enrich() ConstructionFactory('nrel', city).enrich()
UsageFactory('comnet', city).enrich() UsageFactory('comnet', city).enrich()
WeatherFactory('epw', city, file_name='CAN_PQ_Montreal.Intl.AP.716270_CWEC.epw').enrich()
vegetation_name = 'BaseEco' vegetation_name = 'BaseEco'
soil_thickness = 0.18 soil_thickness = 0.18
soil_name = 'EcoRoofSoil' soil_name = 'EcoRoofSoil'
@ -85,6 +87,7 @@ class GreeneryInIdf(TestCase):
building.year_of_construction = 2006 building.year_of_construction = 2006
ConstructionFactory('nrel', city).enrich() ConstructionFactory('nrel', city).enrich()
UsageFactory('comnet', city).enrich() UsageFactory('comnet', city).enrich()
WeatherFactory('epw', city, file_name='CAN_PQ_Montreal.Intl.AP.716270_CWEC.epw').enrich()
_idf = EnergyBuildingsExportsFactory('idf', city, output_path).export() _idf = EnergyBuildingsExportsFactory('idf', city, output_path).export()
_idf.run() _idf.run()
with open((output_path / f'{city.name}_out.csv').resolve()) as f: with open((output_path / f'{city.name}_out.csv').resolve()) as f:
@ -98,4 +101,3 @@ class GreeneryInIdf(TestCase):
print('Without greenery') print('Without greenery')
print(f'heating: {heating} MWh/yr, cooling: {cooling} MWh/yr') print(f'heating: {heating} MWh/yr, cooling: {cooling} MWh/yr')

View File

@ -0,0 +1,115 @@
"""
TestExports test and validate the city export formats
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
"""
import subprocess
from pathlib import Path
from unittest import TestCase
import pandas as pd
import hub.helpers.constants as cte
from hub.exports.energy_building_exports_factory import EnergyBuildingsExportsFactory
from hub.exports.exports_factory import ExportsFactory
from hub.helpers.dictionaries import Dictionaries
from hub.imports.construction_factory import ConstructionFactory
from hub.imports.geometry_factory import GeometryFactory
from hub.imports.results_factory import ResultFactory
from hub.imports.usage_factory import UsageFactory
class TestResultsImport(TestCase):
"""
TestImports class contains the unittest for import functionality
"""
def setUp(self) -> None:
"""
Test setup
:return: None
"""
self._example_path = (Path(__file__).parent / 'tests_data').resolve()
self._gml_path = (self._example_path / 'FZK_Haus_LoD_2.gml').resolve()
self._output_path = (Path(__file__).parent / 'tests_outputs').resolve()
self._city = GeometryFactory('citygml',
self._gml_path,
function_to_hub=Dictionaries().alkis_function_to_hub_function).city
ConstructionFactory('nrcan', self._city).enrich()
UsageFactory('nrcan', self._city).enrich()
def test_sra_import(self):
weather_file = (self._example_path / 'CAN_PQ_Montreal.Intl.AP.716270_CWEC.epw').resolve()
ExportsFactory('sra', self._city, self._output_path, weather_file=weather_file, weather_format='epw').export()
sra_path = (self._output_path / f'{self._city.name}_sra.xml').resolve()
subprocess.run(['sra', str(sra_path)])
ResultFactory('sra', self._city, self._output_path).enrich()
# Check that all the buildings have radiance in the surfaces
for building in self._city.buildings:
for surface in building.surfaces:
self.assertIsNotNone(surface.global_irradiance)
def test_meb_import(self):
weather_file = (self._example_path / 'CAN_PQ_Montreal.Intl.AP.716270_CWEC.epw').resolve()
ExportsFactory('sra', self._city, self._output_path, weather_file=weather_file, weather_format='epw').export()
sra_path = (self._output_path / f'{self._city.name}_sra.xml').resolve()
subprocess.run(['sra', str(sra_path)])
ResultFactory('sra', self._city, self._output_path).enrich()
EnergyBuildingsExportsFactory('insel_monthly_energy_balance', self._city, self._output_path).export()
for building in self._city.buildings:
insel_path = (self._output_path / f'{building.name}.insel')
subprocess.run(['insel', str(insel_path)])
ResultFactory('insel_monthly_energy_balance', self._city, self._output_path).enrich()
# Check that all the buildings have heating and cooling values
for building in self._city.buildings:
self.assertIsNotNone(building.heating[cte.MONTH][cte.INSEL_MEB])
self.assertIsNotNone(building.cooling[cte.MONTH][cte.INSEL_MEB])
self.assertIsNotNone(building.heating[cte.YEAR][cte.INSEL_MEB])
self.assertIsNotNone(building.cooling[cte.YEAR][cte.INSEL_MEB])
def test_peak_loads(self):
# todo: this is not technically a import
weather_file = (self._example_path / 'CAN_PQ_Montreal.Intl.AP.716270_CWEC.epw').resolve()
ExportsFactory('sra', self._city, self._output_path, weather_file=weather_file, weather_format='epw').export()
sra_path = (self._output_path / f'{self._city.name}_sra.xml').resolve()
subprocess.run(['sra', str(sra_path)])
ResultFactory('sra', self._city, self._output_path).enrich()
for building in self._city.buildings:
self.assertIsNotNone(building.heating_peak_load)
self.assertIsNotNone(building.cooling_peak_load)
values = [0 for _ in range(8760)]
values[0] = 1000
expected_yearly = pd.DataFrame([1000], columns=['expected'])
expected_monthly_list = [0 for _ in range(12)]
expected_monthly_list[0] = 1000
expected_monthly = pd.DataFrame(expected_monthly_list, columns=['expected'])
for building in self._city.buildings:
building.heating[cte.HOUR] = pd.DataFrame(values, columns=['dummy'])
building.cooling[cte.HOUR] = pd.DataFrame(values, columns=['dummy'])
self.assertIsNotNone(building.heating_peak_load)
self.assertIsNotNone(building.cooling_peak_load)
pd.testing.assert_series_equal(
building.heating_peak_load[cte.YEAR]['heating peak loads'],
expected_yearly['expected'],
check_names=False
)
pd.testing.assert_series_equal(
building.cooling_peak_load[cte.YEAR]['cooling peak loads'],
expected_yearly['expected'],
check_names=False
)
pd.testing.assert_series_equal(
building.heating_peak_load[cte.MONTH]['heating peak loads'],
expected_monthly['expected'],
check_names=False
)
pd.testing.assert_series_equal(
building.cooling_peak_load[cte.MONTH]['cooling peak loads'],
expected_monthly['expected'],
check_names=False
)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,2 @@
# Ignore everything in this directory
.gitignore
# Except this file # Except this file
!.gitignore !.gitignore

View File

@ -25,3 +25,4 @@ triangle
psycopg2-binary psycopg2-binary
Pillow Pillow
pathlib pathlib
pickle5

View File

@ -87,6 +87,7 @@ setup(
('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')),