Merge branch 'main' into systems_catalog

This commit is contained in:
Pilar Monsalvete 2023-04-27 14:28:22 -04:00
commit 2650dccf57
78 changed files with 267016 additions and 1682 deletions

View File

@ -8,12 +8,13 @@ Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
class Catalog:
"""
Catalogs base class not implemented instance of the Catalog base class, catalog_factories will inherit from this class.
Catalogs base class not implemented instance of the Catalog base class,
catalog_factories will inherit from this class.
"""
def names(self, category=None):
"""
Base property to return the catalog entries names
Base property to return the catalog entries names.
:return: Catalog names filter by category if provided
"""
raise NotImplementedError

View File

@ -116,7 +116,7 @@ class NrcanCatalog(Catalog):
climate_zone = archetype['climate_zone']
construction_period = archetype['period_of_construction']
average_storey_height = archetype['average_storey_height']
thermal_capacity = str(float(archetype['thermal_capacity']) * 1000)
thermal_capacity = float(archetype['thermal_capacity']) * 1000
extra_loses_due_to_thermal_bridges = archetype['extra_loses_due_thermal_bridges']
infiltration_rate_for_ventilation_system_off = archetype['infiltration_rate_for_ventilation_system_off']
infiltration_rate_for_ventilation_system_on = archetype['infiltration_rate_for_ventilation_system_on']

View File

@ -1,21 +1,21 @@
"""
Cost catalog
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Atiya atiya.atiya@mail.concordia.ca
Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
Copyright © 2023 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
import xmltodict
from hub.catalog_factories.catalog import Catalog
from hub.catalog_factories.data_models.cost.capital_cost import CapitalCost
from hub.catalog_factories.data_models.cost.envelope import Envelope
from hub.catalog_factories.data_models.cost.systems import Systems
from hub.catalog_factories.data_models.cost.hvac import Hvac
from hub.catalog_factories.data_models.cost.operational_cost import OperationalCost
from hub.catalog_factories.data_models.cost.income import Income
from hub.catalog_factories.data_models.cost.archetype import Archetype
from hub.catalog_factories.data_models.cost.content import Content
from hub.catalog_factories.data_models.cost.capital_cost import CapitalCost
from hub.catalog_factories.data_models.cost.chapter import Chapter
from hub.catalog_factories.data_models.cost.item_description import ItemDescription
from hub.catalog_factories.data_models.cost.operational_cost import OperationalCost
from hub.catalog_factories.data_models.cost.fuel import Fuel
from hub.catalog_factories.data_models.cost.income import Income
from hub.catalog_factories.data_models.cost.cost_helper import CostHelper
class MontrealCustomCatalog(Catalog):
@ -28,90 +28,96 @@ class MontrealCustomCatalog(Catalog):
self._content = Content(self._load_archetypes())
@staticmethod
def _get_threesome(entry):
_reposition = float(entry['reposition']['#text'])
_investment = float(entry['initial_investment']['#text'])
_lifetime = float(entry['lifetime_equipment']['#text'])
return _reposition, _investment, _lifetime
def _item_with_threesome(entry, item_type):
_reposition = float(entry[item_type]['reposition']['#text'])
_reposition_unit = entry[item_type]['reposition']['@cost_unit']
_investment = float(entry[item_type]['investment_cost']['#text'])
_investment_unit = entry[item_type]['investment_cost']['@cost_unit']
_lifetime = float(entry[item_type]['lifetime_equipment']['#text'])
_item_description = ItemDescription(item_type,
initial_investment=_investment,
initial_investment_unit=_investment_unit,
reposition=_reposition,
reposition_unit=_reposition_unit,
lifetime=_lifetime)
return _item_description
@staticmethod
def _item_with_refurbishment_values(entry, item_type):
_refurbishment = float(entry[item_type]['refurbishment_cost']['#text'])
_refurbishment_unit = entry[item_type]['refurbishment_cost']['@cost_unit']
_item_description = ItemDescription(item_type,
refurbishment=_refurbishment,
refurbishment_unit=_refurbishment_unit)
return _item_description
def _get_capital_costs(self, entry):
structural = float(entry['structural']['#text'])
sub_structural = float(entry['sub_structural']['#text'])
surface_finish = float(entry['surface_finish']['#text'])
engineer = float(entry['engineer']['#text'])
opaque_reposition, opaque_initial_investment, opaque_lifetime = \
self._get_threesome(entry['envelope']['opaque'])
transparent_reposition, transparent_initial_investment, transparent_lifetime = \
self._get_threesome(entry['envelope']['transparent'])
envelope = Envelope(opaque_reposition,
opaque_initial_investment,
opaque_lifetime,
transparent_reposition,
transparent_initial_investment,
transparent_lifetime)
heating_equipment_reposition, heating_equipment_initial_investment, heating_equipment_lifetime = \
self._get_threesome(entry['systems']['hvac']['heating_equipment_cost'])
heating_equipment_reposition = heating_equipment_reposition / 1000
heating_equipment_initial_investment = heating_equipment_initial_investment / 1000
cooling_equipment_reposition, cooling_equipment_initial_investment, cooling_equipment_lifetime = \
self._get_threesome(entry['systems']['hvac']['cooling_equipment_cost'])
cooling_equipment_reposition = cooling_equipment_reposition / 1000
cooling_equipment_initial_investment = cooling_equipment_initial_investment / 1000
general_hvac_equipment_reposition, general_hvac_equipment_initial_investment, general_hvac_equipment_lifetime = \
self._get_threesome(entry['systems']['hvac']['general_hvac_equipment_cost'])
general_hvac_equipment_reposition = general_hvac_equipment_reposition * 3600
general_hvac_equipment_initial_investment = general_hvac_equipment_initial_investment * 3600
hvac = Hvac(heating_equipment_reposition, heating_equipment_initial_investment, heating_equipment_lifetime,
cooling_equipment_reposition, cooling_equipment_initial_investment, cooling_equipment_lifetime,
general_hvac_equipment_reposition, general_hvac_equipment_initial_investment,
general_hvac_equipment_lifetime)
general_chapters = []
chapters_titles = CostHelper().chapters_in_lod1
shell = entry['B_shell']
items_list = []
item_type = 'B10_superstructure'
item_description = self._item_with_refurbishment_values(shell, item_type)
items_list.append(item_description)
for item in shell['B20_envelope']:
item_type = item
item_description = self._item_with_refurbishment_values(shell['B20_envelope'], item_type)
items_list.append(item_description)
item_type = 'B3010_opaque_roof'
item_description = self._item_with_refurbishment_values(shell['B30_roofing'], item_type)
items_list.append(item_description)
general_chapters.append(Chapter('B_shell', items_list))
photovoltaic_system_reposition, photovoltaic_system_initial_investment, photovoltaic_system_lifetime = \
self._get_threesome(entry['systems']['photovoltaic_system'])
other_conditioning_systems_reposition, other_conditioning_systems_initial_investment, \
other_conditioning_systems_lifetime = self._get_threesome(entry['systems']['other_systems'])
lighting_reposition, lighting_initial_investment, lighting_lifetime = \
self._get_threesome(entry['systems']['lighting'])
systems = Systems(hvac,
photovoltaic_system_reposition,
photovoltaic_system_initial_investment,
photovoltaic_system_lifetime,
other_conditioning_systems_reposition,
other_conditioning_systems_initial_investment,
other_conditioning_systems_lifetime,
lighting_reposition,
lighting_initial_investment,
lighting_lifetime)
_capital_cost = CapitalCost(structural,
sub_structural,
envelope,
systems,
surface_finish,
engineer)
items_list = []
item_type = 'D301010_photovoltaic_system'
services = entry['D_services']
item_description = self._item_with_threesome(services['D30_hvac']['D3010_energy_supply'], item_type)
items_list.append(item_description)
item_type_list = ['D3020_heat_generating_systems', 'D3030_cooling_generation_systems', 'D3040_distribution_systems',
'D3080_other_hvac_ahu']
for item_type in item_type_list:
item_description = self._item_with_threesome(services['D30_hvac'], item_type)
items_list.append(item_description)
item_type = 'D5020_lighting_and_branch_wiring'
item_description = self._item_with_threesome(services['D50_electrical'], item_type)
items_list.append(item_description)
general_chapters.append(Chapter('D_services', items_list))
allowances = entry['Z_allowances_overhead_profit']
design_allowance = float(allowances['Z10_design_allowance']['#text']) / 100
overhead_and_profit = float(allowances['Z20_overhead_profit']['#text']) / 100
_capital_cost = CapitalCost(general_chapters, design_allowance, overhead_and_profit)
return _capital_cost
@staticmethod
def _get_operational_costs(entry):
fuel_type = entry['fuel']['@fuel_type']
fuel_fixed_operational_monthly = float(entry['fuel']['fixed']['fixed_monthly']['#text'])
fuel_fixed_operational_peak = float(entry['fuel']['fixed']['fixed_power']['#text']) / 1000
fuel_variable_operational = float(entry['fuel']['variable']['#text']) / 1000 / 3600
fuels = []
for item in entry['fuels']['fuel']:
fuel_type = item['@fuel_type']
fuel_variable = float(item['variable']['#text'])
fuel_variable_units = item['variable']['@cost_unit']
fuel_fixed_monthly = None
fuel_fixed_peak = None
if fuel_type == 'electricity':
fuel_fixed_monthly = float(item['fixed_monthly']['#text'])
fuel_fixed_peak = float(item['fixed_power']['#text']) / 1000
elif fuel_type == 'gas':
fuel_fixed_monthly = float(item['fixed_monthly']['#text'])
fuel = Fuel(fuel_type,
fixed_monthly=fuel_fixed_monthly,
fixed_power=fuel_fixed_peak,
variable=fuel_variable,
variable_units=fuel_variable_units)
fuels.append(fuel)
heating_equipment_maintenance = float(entry['maintenance']['heating_equipment']['#text']) / 1000
cooling_equipment_maintenance = float(entry['maintenance']['cooling_equipment']['#text']) / 1000
general_hvac_equipment_maintenance = float(entry['maintenance']['general_hvac_equipment']['#text']) * 3600
photovoltaic_system_maintenance = float(entry['maintenance']['photovoltaic_system']['#text'])
other_systems_maintenance = float(entry['maintenance']['other_systems']['#text'])
co2_emissions = float(entry['CO2_cost']['#text'])
_operational_cost = OperationalCost(fuel_type,
fuel_fixed_operational_monthly,
fuel_fixed_operational_peak,
fuel_variable_operational,
co2_emissions = float(entry['co2_cost']['#text'])
_operational_cost = OperationalCost(fuels,
heating_equipment_maintenance,
cooling_equipment_maintenance,
general_hvac_equipment_maintenance,
photovoltaic_system_maintenance,
other_systems_maintenance,
co2_emissions)
return _operational_cost
@ -121,25 +127,31 @@ class MontrealCustomCatalog(Catalog):
for archetype in archetypes:
function = archetype['@function']
municipality = archetype['@municipality']
currency = archetype['@currency']
country = archetype['@country']
lod = float(archetype['@lod'])
currency = archetype['currency']
capital_cost = self._get_capital_costs(archetype['capital_cost'])
operational_cost = self._get_operational_costs(archetype['operational_cost'])
end_of_life_cost = float(archetype['end_of_life_cost']['#text'])
construction = float(archetype['incomes']['subsidies']['construction_subsidy']['#text'])
hvac = float(archetype['incomes']['subsidies']['hvac_subsidy']['#text'])
photovoltaic_system = float(archetype['incomes']['subsidies']['photovoltaic_subsidy']['#text'])
electricity_exports = float(archetype['incomes']['energy_exports']['electricity']['#text']) / 1000 / 3600
heat_exports = float(archetype['incomes']['energy_exports']['heat']['#text']) / 1000 / 3600
co2 = float(archetype['incomes']['CO2_income']['#text'])
income = Income(construction, hvac, photovoltaic_system, electricity_exports, heat_exports, co2)
_catalog_archetypes.append(Archetype(function,
construction = float(archetype['incomes']['subsidies']['construction']['#text'])
hvac = float(archetype['incomes']['subsidies']['hvac']['#text'])
photovoltaic_system = float(archetype['incomes']['subsidies']['photovoltaic']['#text'])
electricity_exports = float(archetype['incomes']['electricity_export']['#text']) / 1000 / 3600
reduction_tax = float(archetype['incomes']['tax_reduction']['#text']) / 100
income = Income(construction_subsidy=construction,
hvac_subsidy=hvac,
photovoltaic_subsidy=photovoltaic_system,
electricity_export=electricity_exports,
reductions_tax=reduction_tax)
_catalog_archetypes.append(Archetype(lod,
function,
municipality,
country,
currency,
capital_cost,
operational_cost,
end_of_life_cost,
income))
return _catalog_archetypes
def names(self, category=None):

View File

@ -1,8 +1,8 @@
"""
Archetype catalog Cost
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Atiya atiya.atiya@mail.concordia.ca
Copyright © 2023 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
from hub.catalog_factories.data_models.cost.capital_cost import CapitalCost
@ -11,9 +11,21 @@ from hub.catalog_factories.data_models.cost.income import Income
class Archetype:
def __init__(self, function, municipality, currency, capital_cost, operational_cost, end_of_life_cost, income):
def __init__(self,
lod,
function,
municipality,
country,
currency,
capital_cost,
operational_cost,
end_of_life_cost,
income):
self._lod = lod
self._function = function
self._municipality = municipality
self._country = country
self._currency = currency
self._capital_cost = capital_cost
self._operational_cost = operational_cost
@ -26,7 +38,15 @@ class Archetype:
Get name
:return: string
"""
return f'{self._municipality}_{self._function}'
return f'{self._country}_{self._municipality}_{self._function}_{self._lod}'
@property
def lod(self):
"""
Get level of detail of the catalog
:return: string
"""
return self._lod
@property
def function(self):
@ -44,6 +64,14 @@ class Archetype:
"""
return self._municipality
@property
def country(self):
"""
Get country
:return: string
"""
return self._country
@property
def currency(self):
"""

View File

@ -1,68 +1,40 @@
"""
Cost catalog CapitalCost
Capital costs included in the catalog
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Atiya atiya.atiya@mail.concordia.ca
Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
Copyright © 2023 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
from hub.catalog_factories.data_models.cost.envelope import Envelope
from hub.catalog_factories.data_models.cost.systems import Systems
from typing import List
from hub.catalog_factories.data_models.cost.chapter import Chapter
class CapitalCost:
def __init__(self, structural, sub_structural, envelope, systems, surface_finish, engineer):
self._structural = structural
self._sub_structural = sub_structural
self._envelope = envelope
self._systems = systems
self._surface_finish = surface_finish
self._engineer = engineer
def __init__(self, general_chapters, design_allowance, overhead_and_profit):
self._general_chapters = general_chapters
self._design_allowance = design_allowance
self._overhead_and_profit = overhead_and_profit
@property
def structural(self):
def general_chapters(self) -> List[Chapter]:
"""
Get structural cost per building volume in currency/m3
Get general chapters in capital costs
:return: [Chapter]
"""
return self._general_chapters
@property
def design_allowance(self):
"""
Get design allowance in percentage (-)
:return: float
"""
return self._structural
return self._design_allowance
@property
def sub_structural(self):
def overhead_and_profit(self):
"""
Get sub structural cost per building foot-print in currency/m2
Get overhead profit in percentage (-)
:return: float
"""
return self._sub_structural
@property
def envelope(self) -> Envelope:
"""
Get envelope cost
:return: Envelope
"""
return self._envelope
@property
def systems(self) -> Systems:
"""
Get systems cost
:return: Systems
"""
return self._systems
@property
def surface_finish(self):
"""
Get surface finish cost per external surfaces areas in currency/m2
:return: float
"""
return self._surface_finish
@property
def engineer(self):
"""
Get engineer cost in %
:return: float
"""
return self._engineer
return self._overhead_and_profit

View File

@ -0,0 +1,32 @@
"""
Cost chapter description
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2023 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
from typing import List
from hub.catalog_factories.data_models.cost.item_description import ItemDescription
class Chapter:
def __init__(self, chapter_type, items):
self._chapter_type = chapter_type
self._items = items
@property
def chapter_type(self):
"""
Get chapter type
:return: str
"""
return self._chapter_type
@property
def items(self) -> List[ItemDescription]:
"""
Get list of items contained in the chapter
:return: [str]
"""
return self._items

View File

@ -0,0 +1,48 @@
"""
Cost helper
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2023 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
import hub.helpers.constants as cte
from typing import Dict
class CostHelper:
"""
Cost helper class
"""
_costs_units = {
'currency/m2': cte.CURRENCY_PER_SQM,
'currency/m3': cte.CURRENCY_PER_CBM,
'currency/kW': cte.CURRENCY_PER_KW,
'currency/kWh': cte.CURRENCY_PER_KWH,
'currency/month': cte.CURRENCY_PER_MONTH,
'currency/l': cte.CURRENCY_PER_LITRE,
'currency/kg': cte.CURRENCY_PER_KG,
'currency/(m3/h)': cte.CURRENCY_PER_CBM_PER_HOUR,
'%': cte.PERCENTAGE
}
_chapters_in_lod1 = {
'B_shell': cte.SUPERSTRUCTURE,
'D_services': cte.ENVELOPE,
'Z_allowances_overhead_profit': cte.ALLOWANCES_OVERHEAD_PROFIT
}
@property
def costs_units(self) -> Dict:
"""
List of supported costs units
:return: dict
"""
return self._costs_units
@property
def chapters_in_lod1(self) -> Dict:
"""
List of chapters included in lod 1
:return: dict
"""
return self._chapters_in_lod1

View File

@ -1,66 +0,0 @@
"""
Envelope costs from Cost catalog
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Atiya atiya.atiya@mail.concordia.ca
Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
class Envelope:
def __init__(self, opaque_reposition, opaque_initial_investment, opaque_lifetime,
transparent_reposition, transparent_initial_investment, transparent_lifetime):
self._opaque_reposition = opaque_reposition
self._opaque_initial_investment = opaque_initial_investment
self._opaque_lifetime = opaque_lifetime
self._transparent_reposition = transparent_reposition
self._transparent_initial_investment = transparent_initial_investment
self._transparent_lifetime = transparent_lifetime
@property
def opaque_reposition(self):
"""
Get reposition costs for opaque envelope per area of external opaque surfaces in currency/m2
:return: float
"""
return self._opaque_reposition
@property
def opaque_initial_investment(self):
"""
Get initial investment for opaque envelope per area of external opaque surfaces in currency/m2
:return: float
"""
return self._opaque_initial_investment
@property
def opaque_lifetime(self):
"""
Get lifetime of opaque envelope in years
:return: float
"""
return self._opaque_lifetime
@property
def transparent_reposition(self):
"""
Get reposition costs for transparent envelope per area of windows in currency/m2
:return: float
"""
return self._transparent_reposition
@property
def transparent_initial_investment(self):
"""
Get initial investment for transparent envelope per area of windows in currency/m2
:return: float
"""
return self._transparent_initial_investment
@property
def transparent_lifetime(self):
"""
Get lifetime of transparent envelope in years
:return: float
"""
return self._transparent_lifetime

View File

@ -0,0 +1,54 @@
"""
Cost fuel
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2023 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
from typing import Union
class Fuel:
def __init__(self, fuel_type,
fixed_monthly=None,
fixed_power=None,
variable=None,
variable_units=None):
self._fuel_type = fuel_type
self._fixed_monthly = fixed_monthly
self._fixed_power = fixed_power
self._variable = variable
self._variable_units = variable_units
@property
def type(self):
"""
Get fuel type
:return: str
"""
return self._fuel_type
@property
def fixed_monthly(self) -> Union[None, float]:
"""
Get fixed operational costs in currency per month
:return: None or float
"""
return self._fixed_monthly
@property
def fixed_power(self) -> Union[None, float]:
"""
Get fixed operational costs depending on the peak power consumed in currency per month per kW
:return: None or float
"""
return self._fixed_power
@property
def variable(self) -> Union[tuple[None, None], tuple[float, str]]:
"""
Get variable costs in given units
:return: None, None or float, str
"""
return self._variable, self._variable_units

View File

@ -1,96 +0,0 @@
"""
Hvac costs
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Pilar Monsalvete Álvarez de Uribarri pilar.monsalvete@concordia.ca
"""
class Hvac:
def __init__(self, heating_equipment_reposition, heating_equipment_initial_investment,
heating_equipment_lifetime, cooling_equipment_reposition,
cooling_equipment_initial_investment, cooling_equipment_lifetime,
general_hvac_equipment_reposition, general_hvac_equipment_initial_investment,
general_hvac_equipment_lifetime):
self._heating_equipment_reposition = heating_equipment_reposition
self._heating_equipment_initial_investment = heating_equipment_initial_investment
self._heating_equipment_lifetime = heating_equipment_lifetime
self._cooling_equipment_reposition = cooling_equipment_reposition
self._cooling_equipment_initial_investment = cooling_equipment_initial_investment
self._cooling_equipment_lifetime = cooling_equipment_lifetime
self._general_hvac_equipment_reposition = general_hvac_equipment_reposition
self._general_hvac_equipment_initial_investment = general_hvac_equipment_initial_investment
self._general_hvac_equipment_lifetime = general_hvac_equipment_lifetime
@property
def heating_equipment_reposition(self):
"""
Get reposition costs of heating equipment per peak-load in currency/W
:return: float
"""
return self._heating_equipment_reposition
@property
def heating_equipment_initial_investment(self):
"""
Get initial investment costs of heating equipment per peak-load in currency/W
:return: float
"""
return self._heating_equipment_initial_investment
@property
def heating_equipment_lifetime(self):
"""
Get lifetime of heating equipment in years
:return: float
"""
return self._heating_equipment_lifetime
@property
def cooling_equipment_reposition(self):
"""
Get reposition costs of cooling equipment per peak-load in currency/W
:return: float
"""
return self._cooling_equipment_reposition
@property
def cooling_equipment_initial_investment(self):
"""
Get initial investment costs of cooling equipment per peak-load in currency/W
:return: float
"""
return self._cooling_equipment_initial_investment
@property
def cooling_equipment_lifetime(self):
"""
Get lifetime of cooling equipment in years
:return: float
"""
return self._cooling_equipment_lifetime
@property
def general_hvac_equipment_reposition(self):
"""
Get reposition costs of general hvac equipment per peak-air-flow in currency/(m3/s)
:return: float
"""
return self._general_hvac_equipment_reposition
@property
def general_hvac_equipment_initial_investment(self):
"""
Get initial investment costs of cooling equipment per peak-air-flow in currency/(m3/s)
:return: float
"""
return self._general_hvac_equipment_initial_investment
@property
def general_hvac_equipment_lifetime(self):
"""
Get lifetime of cooling equipment in years
:return: float
"""
return self._general_hvac_equipment_lifetime

View File

@ -1,64 +1,62 @@
"""
Income from costs catalog
Incomes included in the costs catalog
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Pilar Monsalvete Álvarez de Uribarri pilar.monsalvete@concordia.ca
Copyright © 2023 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
from typing import Union
class Income:
def __init__(self, construction, hvac, photovoltaic_system, electricity_exports, heat_exports, co2):
self._construction = construction
self._hvac = hvac
self._photovoltaic_system = photovoltaic_system
self._electricity_exports = electricity_exports
self._heat_exports = heat_exports
self._co2 = co2
def __init__(self, construction_subsidy=None,
hvac_subsidy=None,
photovoltaic_subsidy=None,
electricity_export=None,
reductions_tax=None):
self._construction_subsidy = construction_subsidy
self._hvac_subsidy = hvac_subsidy
self._photovoltaic_subsidy = photovoltaic_subsidy
self._electricity_export = electricity_export
self._reductions_tax = reductions_tax
@property
def construction(self):
def construction_subsidy(self) -> Union[None, float]:
"""
Get construction subsidy in % of total investment construction cost
:return: float
Get subsidy for construction in percentage
:return: None or float
"""
return self._construction
return self._construction_subsidy
@property
def hvac(self):
def hvac_subsidy(self) -> Union[None, float]:
"""
Get hvac subsidy in % of total investment HVAC cost
:return: float
Get subsidy for HVAC system in percentage
:return: None or float
"""
return self._hvac
return self._hvac_subsidy
@property
def photovoltaic_system(self):
def photovoltaic_subsidy(self) -> Union[None, float]:
"""
Get photovoltaic system subsidy in % of total investment photovoltaic cost
:return: float
Get subsidy PV systems in percentage
:return: None or float
"""
return self._photovoltaic_system
return self._photovoltaic_subsidy
@property
def electricity_exports(self):
def electricity_export(self) -> Union[None, float]:
"""
Get electricity exports gains in currency/J
:return: float
Get electricity export incomes in currency per J
:return: None or float
"""
return self._construction
return self._construction_subsidy
@property
def heat_exports(self):
def reductions_tax(self) -> Union[None, float]:
"""
Get heat exports gains in currency/J
:return: float
Get reduction in taxes in percentage (-)
:return: None or float
"""
return self._heat_exports
@property
def co2(self):
"""
Get co2 income in currency/kg
:return: float
"""
return self._co2
return self._reductions_tax

View File

@ -0,0 +1,68 @@
"""
Cost item properties
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2023 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
from typing import Union
class ItemDescription:
def __init__(self, item_type,
initial_investment=None,
initial_investment_unit=None,
refurbishment=None,
refurbishment_unit=None,
reposition=None,
reposition_unit=None,
lifetime=None):
self._item_type = item_type
self._initial_investment = initial_investment
self._initial_investment_unit = initial_investment_unit
self._refurbishment = refurbishment
self._refurbishment_unit = refurbishment_unit
self._reposition = reposition
self._reposition_unit = reposition_unit
self._lifetime = lifetime
@property
def type(self):
"""
Get item type
:return: str
"""
return self._item_type
@property
def initial_investment(self) -> Union[tuple[None, None], tuple[float, str]]:
"""
Get initial investment of the specific item in given units
:return: None, None or float, str
"""
return self._initial_investment, self._initial_investment_unit
@property
def refurbishment(self) -> Union[tuple[None, None], tuple[float, str]]:
"""
Get refurbishment costs of the specific item in given units
:return: None, None or float, str
"""
return self._refurbishment, self._refurbishment_unit
@property
def reposition(self) -> Union[tuple[None, None], tuple[float, str]]:
"""
Get reposition costs of the specific item in given units
:return: None, None or float, str
"""
return self._reposition, self._reposition_unit
@property
def lifetime(self) -> Union[None, float]:
"""
Get lifetime in years
:return: None or float
"""
return self._lifetime

View File

@ -1,104 +1,58 @@
"""
Cost catalog OperationalCost
Operational costs included in the catalog
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Atiya atiya.atiya@mail.concordia.ca
Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
Copyright © 2023 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
from typing import List
from hub.catalog_factories.data_models.cost.fuel import Fuel
class OperationalCost:
def __init__(self, fuel_type, fuel_fixed_operational_monthly, fuel_fixed_operational_peak,
fuel_variable_operational, heating_equipment_maintenance, cooling_equipment_maintenance,
general_hvac_equipment_maintenance, photovoltaic_system_maintenance, other_systems_maintenance,
co2_emissions):
self._fuel_type = fuel_type
self._fuel_fixed_operational_monthly = fuel_fixed_operational_monthly
self._fuel_fixed_operational_peak = fuel_fixed_operational_peak
self._fuel_variable_operational = fuel_variable_operational
self._heating_equipment_maintenance = heating_equipment_maintenance
self._cooling_equipment_maintenance = cooling_equipment_maintenance
self._general_hvac_equipment_maintenance = general_hvac_equipment_maintenance
self._photovoltaic_system_maintenance = photovoltaic_system_maintenance
self._other_systems_maintenance = other_systems_maintenance
self._co2_emissions = co2_emissions
def __init__(self, fuels, maintenance_heating, maintenance_cooling, maintenance_pv, co2):
self._fuels = fuels
self._maintenance_heating = maintenance_heating
self._maintenance_cooling = maintenance_cooling
self._maintenance_pv = maintenance_pv
self._co2 = co2
@property
def fuel_type(self):
def fuels(self) -> List[Fuel]:
"""
Get fuel type
:return: string
Get fuels listed in capital costs
:return: [FUEL]
"""
return self._fuel_type
return self._fuels
@property
def fuel_fixed_operational_monthly(self):
def maintenance_heating(self):
"""
Get fuel fixed operational cost in currency/month
Get cost of maintaining the heating system in currency/W
:return: float
"""
return self._fuel_fixed_operational_monthly
return self._maintenance_heating
@property
def fuel_fixed_operational_peak(self):
def maintenance_cooling(self):
"""
Get fuel fixed operational cost per peak power in currency/W
Get cost of maintaining the cooling system in currency/W
:return: float
"""
return self._fuel_fixed_operational_peak
return self._maintenance_cooling
@property
def fuel_variable_operational(self):
def maintenance_pv(self):
"""
Get fuel variable operational cost in currency/J
Get cost of maintaining the PV system in currency/m2
:return: float
"""
return self._fuel_variable_operational
return self._maintenance_pv
@property
def heating_equipment_maintenance(self):
def co2(self):
"""
Get heating equipment maintenance cost per peak power in currency/W
Get cost of CO2 emissions in currency/kgCO2
:return: float
"""
return self._heating_equipment_maintenance
@property
def cooling_equipment_maintenance(self):
"""
Get cooling equipment maintenance cost per peak power in currency/W
:return: float
"""
return self._cooling_equipment_maintenance
@property
def general_hvac_equipment_maintenance(self):
"""
Get general hvac equipment maintenance cost per peak-air-flow in currency/(m3/s)
:return: float
"""
return self._general_hvac_equipment_maintenance
@property
def photovoltaic_system_maintenance(self):
"""
Get photovoltaic system maintenance cost per panels area in currency/m2
:return: float
"""
return self._photovoltaic_system_maintenance
@property
def other_systems_maintenance(self):
"""
Get other systems' maintenance cost per building's foot-print area in currency/m2
:return: float
"""
return self._other_systems_maintenance
@property
def co2_emissions(self):
"""
Get CO2 emissions cost in currency/kg
:return: float
"""
return self._co2_emissions
return self._co2

View File

@ -1,106 +0,0 @@
"""
Systems cost catalog
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Atiya atiya.atiya@mail.concordia.ca
Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
from hub.catalog_factories.data_models.cost.hvac import Hvac
class Systems:
def __init__(self, hvac, photovoltaic_system_reposition, photovoltaic_system_initial_investment,
photovoltaic_system_lifetime, other_conditioning_systems_reposition,
other_conditioning_systems_initial_investment, other_conditioning_systems_lifetime,
lighting_reposition, lighting_initial_investment, lighting_lifetime):
self._hvac = hvac
self._photovoltaic_system_reposition = photovoltaic_system_reposition
self._photovoltaic_system_initial_investment = photovoltaic_system_initial_investment
self._photovoltaic_system_lifetime = photovoltaic_system_lifetime
self._other_conditioning_systems_reposition = other_conditioning_systems_reposition
self._other_conditioning_systems_initial_investment = other_conditioning_systems_initial_investment
self._other_conditioning_systems_lifetime = other_conditioning_systems_lifetime
self._lighting_reposition = lighting_reposition
self._lighting_initial_investment = lighting_initial_investment
self._lighting_lifetime = lighting_lifetime
@property
def hvac(self) -> Hvac:
"""
Get hvac capital cost
:return: Hvac
"""
return self._hvac
@property
def photovoltaic_system_reposition(self):
"""
Get photovoltaic system reposition cost per area of panels in currency/m2
:return: float
"""
return self._photovoltaic_system_reposition
@property
def photovoltaic_system_initial_investment(self):
"""
Get photovoltaic system initial investment per area of panels in currency/m2
:return: float
"""
return self._photovoltaic_system_initial_investment
@property
def photovoltaic_system_lifetime(self):
"""
Get photovoltaic system lifetime in years
:return: float
"""
return self._photovoltaic_system_lifetime
@property
def other_conditioning_systems_reposition(self):
"""
Get other conditioning systems reposition cost per building's foot-print area in currency/m2
:return: float
"""
return self._other_conditioning_systems_reposition
@property
def other_conditioning_systems_initial_investment(self):
"""
Get other conditioning systems initial investment per building's foot-print area in currency/m2
:return: float
"""
return self._other_conditioning_systems_initial_investment
@property
def other_conditioning_systems_lifetime(self):
"""
Get other conditioning systems lifetime in years
:return: float
"""
return self._other_conditioning_systems_lifetime
@property
def lighting_reposition(self):
"""
Get lighting reposition cost per building's foot-print area in currency/m2
:return: float
"""
return self._lighting_reposition
@property
def lighting_initial_investment(self):
"""
Get lighting initial investment per building's foot-print area in currency/m2
:return: float
"""
return self._lighting_initial_investment
@property
def lighting_lifetime(self):
"""
Get lighting lifetime in years
:return: float
"""
return self._lighting_lifetime

View File

@ -14,8 +14,9 @@ class DomesticHotWater:
"""
DomesticHotWater class
"""
def __init__(self, density, service_temperature, schedules):
def __init__(self, density, peak_flow, service_temperature, schedules):
self._density = density
self._peak_flow = peak_flow
self._service_temperature = service_temperature
self._schedules = schedules
@ -27,6 +28,14 @@ class DomesticHotWater:
"""
return self._density
@property
def peak_flow(self) -> Union[None, float]:
"""
Get domestic hot water peak_flow density in m3 per second and m2
:return: None or float
"""
return self._peak_flow
@property
def service_temperature(self) -> Union[None, float]:
"""

View File

@ -104,6 +104,7 @@ class ComnetCatalog(Catalog):
density = float(density) * cte.BTU_H_TO_WATTS * occupancy_density
domestic_hot_water_service_temperature = self._schedules[schedule_name]['WtrHtrSetPt'][0].values[0]
domestic_hot_water = DomesticHotWater(density,
None,
domestic_hot_water_service_temperature,
self._schedules[schedule_name]['Service Hot Water']
)

View File

@ -53,7 +53,7 @@ class NrcanCatalog(Catalog):
def _load_schedules(self):
usage = self._metadata['nrcan']
url = f'{self._base_url}{usage["schedules_location"]}'
url = f'{self._base_url}{usage["schedules"]}'
_schedule_types = []
with urllib.request.urlopen(url) as json_file:
schedules_type = json.load(json_file)
@ -76,14 +76,40 @@ class NrcanCatalog(Catalog):
def _load_archetypes(self):
usages = []
name = self._metadata['nrcan']
url = f'{self._base_url}{name["space_types_location"]}'
with urllib.request.urlopen(url) as json_file:
url_1 = f'{self._base_url}{name["space_types"]}'
url_2 = f'{self._base_url}{name["space_types_compliance"]}'
with urllib.request.urlopen(url_1) as json_file:
space_types = json.load(json_file)['tables']['space_types']['table']
# space_types = [st for st in space_types if st['building_type'] == 'Space Function']
space_types = [st for st in space_types if st['space_type'] == 'WholeBuilding']
for space_type in space_types:
# usage_type = space_type['space_type']
with urllib.request.urlopen(url_2) as json_file:
space_types_compliance = json.load(json_file)['tables']['space_compliance']['table']
space_types_compliance = [st for st in space_types_compliance if st['space_type'] == 'WholeBuilding']
space_types_dictionary = {}
for space_type in space_types_compliance:
usage_type = space_type['building_type']
# people/m2
occupancy_density = space_type['occupancy_per_area_people_per_m2']
# W/m2
lighting_density = space_type['lighting_per_area_w_per_m2']
# W/m2
appliances_density = space_type['electric_equipment_per_area_w_per_m2']
# peak flow in gallons/h/m2
domestic_hot_water_peak_flow = space_type['service_water_heating_peak_flow_per_area'] \
* cte.GALLONS_TO_QUBIC_METERS / cte.HOUR_TO_SECONDS
space_types_dictionary[usage_type] = {'occupancy_per_area': occupancy_density,
'lighting_per_area': lighting_density,
'electric_equipment_per_area': appliances_density,
'service_water_heating_peak_flow_per_area': domestic_hot_water_peak_flow
}
for space_type in space_types:
usage_type = space_type['building_type']
space_type_compliance = space_types_dictionary[usage_type]
occupancy_density = space_type_compliance['occupancy_per_area']
lighting_density = space_type_compliance['lighting_per_area']
appliances_density = space_type_compliance['electric_equipment_per_area']
domestic_hot_water_peak_flow = space_type_compliance['service_water_heating_peak_flow_per_area']
occupancy_schedule_name = space_type['occupancy_schedule']
lighting_schedule_name = space_type['lighting_schedule']
appliance_schedule_name = space_type['electric_equipment_schedule']
@ -101,40 +127,27 @@ class NrcanCatalog(Catalog):
hvac_availability = self._get_schedules(hvac_schedule_name)
domestic_hot_water_load_schedule = self._get_schedules(domestic_hot_water_schedule_name)
occupancy_density = space_type['occupancy_per_area']
# ACH
mechanical_air_change = space_type['ventilation_air_changes']
# cfm/ft2 to m3/m2.s
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
ventilation_rate = space_type['ventilation_per_person'] / occupancy_density\
/ (cte.METERS_TO_FEET * cte.MINUTES_TO_SECONDS)
ventilation_rate = space_type['ventilation_per_person'] / (cte.METERS_TO_FEET * cte.MINUTES_TO_SECONDS)\
/ occupancy_density
# W/sqft to W/m2
lighting_density = space_type['lighting_per_area'] * cte.METERS_TO_FEET * cte.METERS_TO_FEET
lighting_radiative_fraction = space_type['lighting_fraction_radiant']
lighting_convective_fraction = 0
if lighting_radiative_fraction is not None:
lighting_convective_fraction = 1 - lighting_radiative_fraction
lighting_latent_fraction = 0
# W/sqft to W/m2
appliances_density = space_type['electric_equipment_per_area'] * cte.METERS_TO_FEET * cte.METERS_TO_FEET
appliances_radiative_fraction = space_type['electric_equipment_fraction_radiant']
appliances_latent_fraction = space_type['electric_equipment_fraction_latent']
appliances_convective_fraction = 0
if appliances_radiative_fraction is not None and appliances_latent_fraction is not None:
appliances_convective_fraction = 1 - appliances_radiative_fraction - appliances_latent_fraction
# peak flow in m3/day/m2
domestic_hot_water_peak_flow = space_type['service_water_heating_peak_flow_per_area']
domestic_hot_water_service_temperature = space_type['service_water_heating_target_temperature']
average_domestic_hot_water_inlet_temperature = 16.5
# result in W/m2
domestic_hot_water_density = domestic_hot_water_peak_flow / 24 / 3.6 * 4184 \
* (domestic_hot_water_service_temperature -
average_domestic_hot_water_inlet_temperature)
occupancy = Occupancy(occupancy_density,
None,
@ -157,7 +170,8 @@ class NrcanCatalog(Catalog):
hvac_availability,
heating_schedule,
cooling_schedule)
domestic_hot_water = DomesticHotWater(domestic_hot_water_density,
domestic_hot_water = DomesticHotWater(None,
domestic_hot_water_peak_flow,
domestic_hot_water_service_temperature,
domestic_hot_water_load_schedule)

View File

@ -26,8 +26,6 @@ class Polygon:
"""
Polygon class
"""
# todo: review with @Guille: Points, Coordinates, Vertices, Faces
def __init__(self, coordinates):
self._area = None
self._points = None
@ -152,6 +150,10 @@ class Polygon:
self._area += np.linalg.norm(np.cross(ab, ac)) / 2
return self._area
@area.setter
def area(self, value):
self._area = value
@property
def normal(self) -> np.ndarray:
"""

View File

@ -38,6 +38,7 @@ class Building(CityObject):
self._shell = None
self._alias = None
self._type = 'building'
self._cold_water_temperature = dict()
self._heating = dict()
self._cooling = dict()
self._lighting_electrical_demand = dict()
@ -265,6 +266,22 @@ class Building(CityObject):
if value is not None:
self._storeys_above_ground = int(value)
@property
def cold_water_temperature(self) -> {float}:
"""
Get cold water temperature in degrees Celsius
:return: dict{DataFrame(float)}
"""
return self._cold_water_temperature
@cold_water_temperature.setter
def cold_water_temperature(self, value):
"""
Set cold water temperature in degrees Celsius
:param value: dict{DataFrame(float)}
"""
self._cold_water_temperature = value
@property
def heating(self) -> dict:
"""

View File

@ -14,6 +14,7 @@ class DomesticHotWater:
"""
def __init__(self):
self._density = None
self._peak_flow = None
self._service_temperature = None
self._schedules = None
@ -34,6 +35,22 @@ class DomesticHotWater:
if value is not None:
self._density = float(value)
@property
def peak_flow(self) -> Union[None, float]:
"""
Get domestic hot water peak_flow density in m3 per second and m2
:return: None or float
"""
return self._peak_flow
@peak_flow.setter
def peak_flow(self, value):
"""
Set domestic hot water peak_flow density in m3 per second and m2
:return: None or float
"""
self._peak_flow = value
@property
def service_temperature(self) -> Union[None, float]:
"""

View File

@ -600,11 +600,14 @@ class ThermalZone:
"""
self._domestic_hot_water = DomesticHotWater()
_mean_peak_density_load = 0
_mean_peak_flow = 0
_mean_service_temperature = 0
for usage in self.usages:
_mean_peak_density_load += usage.percentage * usage.domestic_hot_water.density
_mean_peak_flow += usage.percentage * usage.domestic_hot_water.peak_flow
_mean_service_temperature += usage.percentage * usage.domestic_hot_water.service_temperature
self._domestic_hot_water.density = _mean_peak_density_load
self._domestic_hot_water.peak_flow = _mean_peak_flow
self._domestic_hot_water.service_temperature = _mean_service_temperature
_domestic_hot_water_reference = self.usages[0].domestic_hot_water

View File

@ -1,250 +0,0 @@
"""
UsageZone module
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
Code contributors: Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
"""
import uuid
from typing import Union, List
import hub.helpers.constants as cte
from hub.city_model_structure.building_demand.occupancy import Occupancy
from hub.city_model_structure.building_demand.lighting import Lighting
from hub.city_model_structure.building_demand.appliances import Appliances
from hub.city_model_structure.building_demand.thermal_control import ThermalControl
from hub.city_model_structure.building_demand.internal_gain import InternalGain
class UsageZone:
"""
UsageZone class
"""
def __init__(self):
self._id = None
self._usage = None
self._percentage = None
self._internal_gains = None
self._hours_day = None
self._days_year = None
self._mechanical_air_change = None
self._occupancy = None
self._lighting = None
self._appliances = None
self._thermal_control = None
@property
def id(self):
"""
Get usage zone id, a universally unique identifier randomly generated
:return: str
"""
if self._id is None:
self._id = uuid.uuid4()
return self._id
@property
def usage(self) -> Union[None, str]:
"""
Get usage zone usage
:return: None or str
"""
return self._usage
@usage.setter
def usage(self, value):
"""
Set usage zone usage
:param value: str
"""
if value is not None:
self._usage = str(value)
@property
def percentage(self):
"""
Get usage zone percentage in range[0,1]
:return: float
"""
return self._percentage
@percentage.setter
def percentage(self, value):
"""
Set usage zone percentage in range[0,1]
:param value: float
"""
if value is not None:
self._percentage = float(value)
@property
def internal_gains(self) -> List[InternalGain]:
"""
Calculates and returns the list of all internal gains defined
:return: InternalGains
"""
if self._internal_gains is None:
if self.occupancy is not None:
if self.occupancy.latent_internal_gain is not None:
_internal_gain = InternalGain()
_internal_gain.type = cte.OCCUPANCY
_total_heat_gain = (self.occupancy.sensible_convective_internal_gain
+ self.occupancy.sensible_radiative_internal_gain
+ self.occupancy.latent_internal_gain)
_internal_gain.average_internal_gain = _total_heat_gain
_internal_gain.latent_fraction = 0
_internal_gain.radiative_fraction = 0
_internal_gain.convective_fraction = 0
if _total_heat_gain != 0:
_internal_gain.latent_fraction = self.occupancy.latent_internal_gain / _total_heat_gain
_internal_gain.radiative_fraction = self.occupancy.sensible_radiative_internal_gain / _total_heat_gain
_internal_gain.convective_fraction = self.occupancy.sensible_convective_internal_gain / _total_heat_gain
_internal_gain.schedules = self.occupancy.occupancy_schedules
self._internal_gains = [_internal_gain]
if self.lighting is not None:
_internal_gain = InternalGain()
_internal_gain.type = cte.LIGHTING
_internal_gain.average_internal_gain = self.lighting.density
_internal_gain.latent_fraction = self.lighting.latent_fraction
_internal_gain.radiative_fraction = self.lighting.radiative_fraction
_internal_gain.convective_fraction = self.lighting.convective_fraction
_internal_gain.schedules = self.lighting.schedules
if self._internal_gains is not None:
self._internal_gains.append(_internal_gain)
else:
self._internal_gains = [_internal_gain]
if self.appliances is not None:
_internal_gain = InternalGain()
_internal_gain.type = cte.APPLIANCES
_internal_gain.average_internal_gain = self.appliances.density
_internal_gain.latent_fraction = self.appliances.latent_fraction
_internal_gain.radiative_fraction = self.appliances.radiative_fraction
_internal_gain.convective_fraction = self.appliances.convective_fraction
_internal_gain.schedules = self.appliances.schedules
if self._internal_gains is not None:
self._internal_gains.append(_internal_gain)
else:
self._internal_gains = [_internal_gain]
return self._internal_gains
@internal_gains.setter
def internal_gains(self, value):
"""
Set usage zone internal gains
:param value: [InternalGain]
"""
self._internal_gains = value
@property
def hours_day(self) -> Union[None, float]:
"""
Get usage zone usage hours per day
:return: None or float
"""
return self._hours_day
@hours_day.setter
def hours_day(self, value):
"""
Set usage zone usage hours per day
:param value: float
"""
if value is not None:
self._hours_day = float(value)
@property
def days_year(self) -> Union[None, float]:
"""
Get usage zone usage days per year
:return: None or float
"""
return self._days_year
@days_year.setter
def days_year(self, value):
"""
Set usage zone usage days per year
:param value: float
"""
if value is not None:
self._days_year = float(value)
@property
def mechanical_air_change(self) -> Union[None, float]:
"""
Get usage zone mechanical air change in air change per hour (ACH)
:return: None or float
"""
return self._mechanical_air_change
@mechanical_air_change.setter
def mechanical_air_change(self, value):
"""
Set usage zone mechanical air change in air change per hour (ACH)
:param value: float
"""
if value is not None:
self._mechanical_air_change = float(value)
@property
def occupancy(self) -> Union[None, Occupancy]:
"""
Get occupancy in the usage zone
:return: None or Occupancy
"""
return self._occupancy
@occupancy.setter
def occupancy(self, value):
"""
Set occupancy in the usage zone
:param value: Occupancy
"""
self._occupancy = value
@property
def lighting(self) -> Union[None, Lighting]:
"""
Get lighting information
:return: None or Lighting
"""
return self._lighting
@lighting.setter
def lighting(self, value):
"""
Set lighting information
:param value: Lighting
"""
self._lighting = value
@property
def appliances(self) -> Union[None, Appliances]:
"""
Get appliances information
:return: None or Appliances
"""
return self._appliances
@appliances.setter
def appliances(self, value):
"""
Set appliances information
:param value: Appliances
"""
self._appliances = value
@property
def thermal_control(self) -> Union[None, ThermalControl]:
"""
Get thermal control of this thermal zone
:return: None or ThermalControl
"""
return self._thermal_control
@thermal_control.setter
def thermal_control(self, value):
"""
Set thermal control for this thermal zone
:param value: ThermalControl
"""
self._thermal_control = value

View File

@ -41,7 +41,7 @@ class City:
self._name = None
self._lower_corner = lower_corner
self._upper_corner = upper_corner
self._buildings = None
self._buildings = []
self._srs_name = srs_name
self._location = None
self._country_code = None
@ -60,6 +60,7 @@ class City:
self._stations = []
self._lca_materials = None
self._level_of_detail = LevelOfDetail()
self._city_objects_dictionary = {}
@property
def fuels(self) -> [Fuel]:
@ -198,9 +199,8 @@ class City:
:param name:str
:return: None or CityObject
"""
for city_object in self.buildings:
if str(city_object.name) == str(name):
return city_object
if name in self._city_objects_dictionary:
return self.buildings[self._city_objects_dictionary[name]]
return None
def add_city_object(self, new_city_object):
@ -213,6 +213,7 @@ class City:
if self._buildings is None:
self._buildings = []
self._buildings.append(new_city_object)
self._city_objects_dictionary[new_city_object.name] = len(self._buildings) - 1
elif new_city_object.type == 'energy_system':
if self._energy_systems is None:
self._energy_systems = []
@ -233,6 +234,10 @@ class City:
else:
if city_object in self._buildings:
self._buildings.remove(city_object)
# regenerate hash map
self._city_objects_dictionary.clear()
for i, city_object in enumerate(self._buildings):
self._city_objects_dictionary[city_object.name] = i
@property
def srs_name(self) -> Union[None, str]:
@ -459,6 +464,7 @@ class City:
for surface in city_object.surfaces:
radiation = surface.global_irradiance
if 'year' not in radiation and 'month' not in radiation:
continue
elif "year" in radiation:
building_radiation += radiation["year"].iloc[0]

View File

@ -34,7 +34,9 @@ class CityObject:
self._max_y = ConfigurationHelper().min_coordinate
self._max_z = ConfigurationHelper().min_coordinate
self._centroid = None
self._volume = None
self._external_temperature = dict()
self._ground_temperature = dict()
self._global_horizontal = dict()
self._diffuse = dict()
self._beam = dict()
@ -63,7 +65,13 @@ class CityObject:
Get city object volume in cubic meters
:return: float
"""
return self.simplified_polyhedron.volume
if self._volume is None:
self._volume = self.simplified_polyhedron.volume
return self._volume
@volume.setter
def volume(self, value):
self._volume = value
@property
def detailed_polyhedron(self) -> Polyhedron:
@ -158,6 +166,24 @@ class CityObject:
"""
self._external_temperature = value
# todo: this is the new format we will use to get rid of the data frames
@property
def ground_temperature(self) -> dict:
"""
Get ground temperature under the city object in Celsius at different depths in meters for different time steps
example of use: {month: {0.5: [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10]}}
:return: dict{dict{[float]}}
"""
return self._ground_temperature
@ground_temperature.setter
def ground_temperature(self, value):
"""
Set ground temperature under the city object in Celsius at different depths
:param value: dict{dict{[float]}}
"""
self._ground_temperature = value
@property
def global_horizontal(self) -> dict:
"""

View File

@ -1,6 +1,5 @@
# These values are intended as configurable assumptions
[buildings]
max_location_distance_for_shared_walls = 5.0
min_coordinate = -1.7976931348623157e+308
max_coordinate = 1.7976931348623157e+308
comnet_lighting_latent = 0
@ -18,3 +17,7 @@ convective_heat_transfer_coefficient_exterior = 20
soil_conductivity = 3
#m
soil_thickness = 0.5
short_wave_reflectance = 0.3
#C
cold_water_temperature = 10

View File

@ -1,89 +1,202 @@
<archetypes>
<archetype function="residential" municipality="montreal" currency="CAD">
<archetype function="residential" municipality="montreal" country="CA" lod="1">
<currency>CAD</currency>
<capital_cost>
<structural cost_unit="currency/m3"> 56 </structural>
<sub_structural cost_unit="currency/m2"> 9.8 </sub_structural>
<envelope>
<opaque>
<reposition cost_unit="currency/m2"> 43.4 </reposition>
<initial_investment cost_unit="currency/m2"> 36 </initial_investment>
<lifetime_equipment lifetime="years"> 50 </lifetime_equipment>
</opaque>
<transparent>
<reposition cost_unit="currency/m2"> 78 </reposition>
<initial_investment cost_unit="currency/m2"> 984.5 </initial_investment>
<B_shell>
<B10_superstructure>
<refurbishment_cost cost_unit="currency/m2"> 0 </refurbishment_cost>
</B10_superstructure>
<B20_envelope>
<B2010_opaque_walls>
<refurbishment_cost cost_unit="currency/m2"> 304 </refurbishment_cost>
</B2010_opaque_walls>
<B2020_transparent>
<refurbishment_cost cost_unit="currency/m2"> 857.14 </refurbishment_cost>
</B2020_transparent>
</B20_envelope>
<B30_roofing>
<B3010_opaque_roof>
<refurbishment_cost cost_unit="currency/m2"> 118 </refurbishment_cost>
</B3010_opaque_roof>
</B30_roofing>
</B_shell>
<D_services>
<D30_hvac>
<D3010_energy_supply>
<D301010_photovoltaic_system>
<investment_cost cost_unit="currency/m2"> 800 </investment_cost>
<reposition cost_unit="currency/m2"> 800 </reposition>
<lifetime_equipment lifetime="years"> 25 </lifetime_equipment>
</D301010_photovoltaic_system>
</D3010_energy_supply>
<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_distribution_systems>
<investment_cost cost_unit="currency/kW"> 0 </investment_cost>
<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>
<investment_cost cost_unit="currency/kW"> 139 </investment_cost>
<reposition cost_unit="currency/kW"> 139 </reposition>
<lifetime_equipment lifetime="years"> 20 </lifetime_equipment>
</transparent>
</envelope>
<systems>
<hvac>
<heating_equipment_cost>
<initial_investment cost_unit="currency/kW"> 363.5 </initial_investment>
<reposition cost_unit="currency/kW"> 363.5 </reposition>
<lifetime_equipment lifetime="years"> 15 </lifetime_equipment>
</heating_equipment_cost>
<cooling_equipment_cost>
<initial_investment cost_unit="currency/kW"> 363.5 </initial_investment>
<reposition cost_unit="currency/kW"> 363.5 </reposition>
<lifetime_equipment lifetime="years"> 15 </lifetime_equipment>
</cooling_equipment_cost>
<general_hvac_equipment_cost>
<initial_investment cost_unit="currency/(m3/h)"> 363.5 </initial_investment>
<reposition cost_unit="currency/(m3/h)"> 363.5 </reposition>
<lifetime_equipment lifetime="years"> 15 </lifetime_equipment>
</general_hvac_equipment_cost>
</hvac>
<photovoltaic_system>
<initial_investment cost_unit="currency/m2"> 17 </initial_investment>
<reposition cost_unit="currency/m2"> 17 </reposition>
<lifetime_equipment lifetime="years"> 15 </lifetime_equipment>
</photovoltaic_system>
<other_systems>
<initial_investment cost_unit="currency/m2"> 365 </initial_investment>
<reposition cost_unit="currency/m2"> 365 </reposition>
<lifetime_equipment lifetime="years"> 15 </lifetime_equipment>
</other_systems>
<lighting>
<initial_investment cost_unit="currency/m2"> 365 </initial_investment>
<reposition cost_unit="currency/m2"> 365 </reposition>
<lifetime_equipment lifetime="years"> 15 </lifetime_equipment>
</lighting>
</systems>
<surface_finish cost_unit="currency/m2"> 88 </surface_finish>
<engineer cost_unit="%"> 2.5 </engineer>
</D5020_lighting_and_branch_wiring>
</D50_electrical>
</D_services>
<Z_allowances_overhead_profit>
<Z10_design_allowance cost_unit="%"> 2.5 </Z10_design_allowance>
<Z20_overhead_profit cost_unit="%"> 14 </Z20_overhead_profit>
</Z_allowances_overhead_profit>
</capital_cost>
<operational_cost>
<fuels>
<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>
<fixed_monthly cost_unit="currency/month"> 12.27 </fixed_monthly>
<fixed_power cost_unit="currency/month*kW"> 0 </fixed_power>
<variable cost_unit="currency/kWh"> 0.075 </variable>
</fuel>
<fuel fuel_type="gas">
<fixed_monthly cost_unit="currency/month"> 17.71 </fixed_monthly>
<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>
</fuels>
<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>
<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>
<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>
<construction cost_unit="%">2</construction>
<hvac cost_unit="%">1.5</hvac>
<photovoltaic cost_unit="%">3.6</photovoltaic>
</subsidies>
<energy_exports>
<electricity cost_unit="currency/kWh"> 0 </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>
<electricity_export cost_unit="currency/kWh">0</electricity_export>
<tax_reduction cost_unit="%">2</tax_reduction>
</incomes>
</archetype>
<archetype function="non-residential" municipality="montreal" country="CA" lod="1">
<currency>CAD</currency>
<capital_cost>
<B_shell>
<B10_superstructure>
<refurbishment_cost cost_unit="currency/m2"> 0 </refurbishment_cost>
</B10_superstructure>
<B20_envelope>
<B2010_opaque_walls>
<refurbishment_cost cost_unit="currency/m2"> 304 </refurbishment_cost>
</B2010_opaque_walls>
<B2020_transparent>
<refurbishment_cost cost_unit="currency/m2"> 857.14 </refurbishment_cost>
</B2020_transparent>
</B20_envelope>
<B30_roofing>
<B3010_opaque_roof>
<refurbishment_cost cost_unit="currency/m2"> 118 </refurbishment_cost>
</B3010_opaque_roof>
</B30_roofing>
</B_shell>
<D_services>
<D30_hvac>
<D3010_energy_supply>
<D301010_photovoltaic_system>
<investment_cost cost_unit="currency/m2"> 800 </investment_cost>
<reposition cost_unit="currency/m2"> 800 </reposition>
<lifetime_equipment lifetime="years"> 25 </lifetime_equipment>
</D301010_photovoltaic_system>
</D3010_energy_supply>
<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_distribution_systems>
<investment_cost cost_unit="currency/m2"> 0 </investment_cost>
<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>
<investment_cost cost_unit="currency/kW"> 139 </investment_cost>
<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>
<fuels>
<fuel fuel_type="electricity">
<fixed_monthly cost_unit="currency/month"> 12.27 </fixed_monthly>
<fixed_power cost_unit="currency/(month*kW)"> 0 </fixed_power>
<variable cost_unit="currency/kWh"> 0.075 </variable>
</fuel>
<fuel fuel_type="gas">
<fixed_monthly cost_unit="currency/month"> 17.71 </fixed_monthly>
<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>
</fuels>
<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 cost_unit="%">2</construction>
<hvac cost_unit="%">1.5</hvac>
<photovoltaic cost_unit="%">3.6</photovoltaic>
</subsidies>
<electricity_export cost_unit="currency/kWh">0</electricity_export>
<tax_reduction cost_unit="%">2</tax_reduction>
</incomes>
</archetype>
</archetypes>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?>
<nrcan base_url="https://raw.githubusercontent.com/NREL/openstudio-standards/master/lib/openstudio-standards/standards/necb/">
<space_types_location>NECB2020/data/space_types.json</space_types_location>
<schedules_location>NECB2015/data/schedules.json</schedules_location>
<space_types>NECB2015/data/space_types.json</space_types>
<space_types_compliance>NECB2015/qaqc/qaqc_data/space_compliance_2015.json</space_types_compliance>>
<schedules>NECB2015/data/schedules.json</schedules>
</nrcan>

View File

@ -187,6 +187,8 @@ class Idf:
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
@ -337,13 +339,17 @@ class Idf:
)
def _add_infiltration(self, thermal_zone, zone_name):
for zone in self._idf.idfobjects["ZONE"]:
if zone.Name == f'{zone_name}_infiltration':
return
schedule = f'Infiltration schedules {thermal_zone.usage_name}'
if schedule not in self._idf.idfobjects[self._HOURLY_SCHEDULE]:
return
self._idf.newidfobject(self._INFILTRATION,
Name=f'{zone_name}_infiltration',
Zone_or_ZoneList_Name=zone_name,
Schedule_Name=f'Infiltration schedules {thermal_zone.usage_name}',
Schedule_Name=schedule,
Design_Flow_Rate_Calculation_Method='AirChanges/Hour',
Air_Changes_per_Hour=thermal_zone.mechanical_air_change
)
@ -378,6 +384,8 @@ class Idf:
self._lod = self._city.level_of_detail.geometry
for building in self._city.buildings:
for internal_zone in building.internal_zones:
if internal_zone.thermal_zones is None:
continue
for thermal_zone in internal_zone.thermal_zones:
for thermal_boundary in thermal_zone.thermal_boundaries:
self._add_construction(thermal_boundary)
@ -388,19 +396,26 @@ class Idf:
usage = thermal_zone.usage_name
if building.name in self._target_buildings or building.name in self._adjacent_buildings:
self._add_infiltration_schedules(thermal_zone)
if thermal_zone.occupancy is not None:
self._add_schedules(usage, 'Occupancy', thermal_zone.occupancy.occupancy_schedules)
self._add_people_activity_level_schedules(thermal_zone)
self._add_occupancy(thermal_zone, building.name)
if thermal_zone.thermal_control is not None:
self._add_schedules(usage, 'HVAC AVAIL', thermal_zone.thermal_control.hvac_availability_schedules)
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)
self._add_people_activity_level_schedules(thermal_zone)
self._add_zone(thermal_zone, building.name)
self._add_heating_system(thermal_zone, building.name)
self._add_infiltration(thermal_zone, building.name)
self._add_occupancy(thermal_zone, building.name)
if self._export_type == "Surfaces":
if building.name in self._target_buildings or building.name in self._adjacent_buildings:
if building.internal_zones[0].thermal_zones is not None:
self._add_surfaces(building, building.name)
else:
self._add_pure_geometry(building, building.name)
else:
self._add_shading(building)
else:
@ -478,7 +493,34 @@ class Idf:
Diffuse_Solar_Reflectance_of_Unglazed_Part_of_Shading_Surface=solar_reflectance,
Fraction_of_Shading_Surface_That_Is_Glazed=0)
# todo: Add properties for that name
def _add_pure_geometry(self, building, zone_name):
for surface in building.surfaces:
idf_surface_type = self.idf_surfaces[surface.type]
outside_boundary_condition = 'Outdoors'
sun_exposure = 'SunExposed'
wind_exposure = 'WindExposed'
if surface.type == cte.GROUND:
outside_boundary_condition = 'Ground'
sun_exposure = 'NoSun'
wind_exposure = 'NoWind'
idf_surface = self._idf.newidfobject(self._SURFACE, Name=f'{surface.name}',
Surface_Type=idf_surface_type,
Zone_Name=zone_name,
Outside_Boundary_Condition=outside_boundary_condition,
Sun_Exposure=sun_exposure,
Wind_Exposure=wind_exposure)
coordinates = self._matrix_to_list(surface.solid_polygon.coordinates,
self._city.lower_corner)
idf_surface.setcoords(coordinates)
if self._lod >= 3:
for internal_zone in building.internal_zones:
for thermal_zone in internal_zone.thermal_zones:
for boundary in thermal_zone.thermal_boundaries:
self._add_windows_by_vertices(boundary)
else:
# idf only allows setting wwr for external walls
wwr = 0
self._idf.set_wwr(wwr)
def _add_surfaces(self, building, zone_name):
for internal_zone in building.internal_zones:
@ -505,7 +547,6 @@ class Idf:
Wind_Exposure=wind_exposure)
coordinates = self._matrix_to_list(boundary.parent_surface.solid_polygon.coordinates,
self._city.lower_corner)
surface.setcoords(coordinates)
if self._lod >= 3:

View File

@ -7,6 +7,8 @@ Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
import numpy as np
from pathlib import Path
import sys
from hub.hub_logger import logger
from hub.exports.formats.insel import Insel
from hub.imports.weather.helpers.weather import Weather
@ -31,6 +33,7 @@ class InselMonthlyEnergyBalance(Insel):
self._weather_format = weather_format
self._contents = []
self._insel_files_paths = []
self._sanity_check()
for building in city.buildings:
self._insel_files_paths.append(building.name + '.insel')
file_name_out = building.name + '.out'
@ -38,9 +41,13 @@ class InselMonthlyEnergyBalance(Insel):
if building.internal_zones is not None:
for internal_zone in building.internal_zones:
if internal_zone.thermal_zones is None:
logger.error(f'Building {building.name} has missing values. '
f'Monthly Energy Balance cannot be processed\n')
sys.stderr.write(f'Building {building.name} has missing values. '
f'Monthly Energy Balance cannot be processed\n')
break
self._contents.append(
self.generate_meb_template(building, output_path, self._radiation_calculation_method,self._weather_format)
self._generate_meb_template(building, output_path, self._radiation_calculation_method,self._weather_format)
)
self._export()
@ -51,8 +58,30 @@ class InselMonthlyEnergyBalance(Insel):
insel_file.write(content)
return
def _sanity_check(self):
levels_of_detail = self._city.level_of_detail
if levels_of_detail.geometry is None:
raise Exception(f'Level of detail of geometry not assigned')
if levels_of_detail.geometry < 1:
raise Exception(f'Level of detail of geometry = {levels_of_detail.geometry}. Required minimum level 1')
if levels_of_detail.construction is None:
raise Exception(f'Level of detail of construction not assigned')
if levels_of_detail.construction < 1:
raise Exception(f'Level of detail of construction = {levels_of_detail.construction}. Required minimum level 1')
if levels_of_detail.usage is None:
raise Exception(f'Level of detail of usage not assigned')
if levels_of_detail.usage < 1:
raise Exception(f'Level of detail of usage = {levels_of_detail.usage}. Required minimum level 1')
for building in self._city.buildings:
if cte.MONTH not in building.external_temperature:
raise Exception(f'Building {building.name} does not have external temperature assigned')
for surface in building.surfaces:
if surface.type != cte.GROUND:
if cte.MONTH not in surface.global_irradiance:
raise Exception(f'Building {building.name} does not have global irradiance on surfaces assigned')
@staticmethod
def generate_meb_template(building, insel_outputs_path, radiation_calculation_method, weather_format):
def _generate_meb_template(building, insel_outputs_path, radiation_calculation_method, weather_format):
file = ""
i_block = 1
parameters = ["1", "12", "1"]
@ -64,10 +93,11 @@ class InselMonthlyEnergyBalance(Insel):
for i in range(1, len(surfaces) + 1):
inputs.append(f"{str(100 + i)}.1 % Radiation surface {str(i)}")
number_of_storeys = int(building.eave_height / building.average_storey_height)
# BUILDING PARAMETERS
parameters = [f'{0.85 * 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.storeys_above_ground} % 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'{building.basement_heated} % BP(5) Cellar heating type (0=no room, 1=unheated, 2=heated, '
f'99=invalid)']
@ -87,11 +117,11 @@ class InselMonthlyEnergyBalance(Insel):
for i, usage in enumerate(internal_zone.usages):
percentage_usage = usage.percentage
parameters.append(f'{float(internal_zone.area) * percentage_usage} % BP(11) #1 Area of zone {i + 1} (m2)')
parameters.append(f'{internal_zone.thermal_zones[0].total_floor_area * percentage_usage} '
f'% BP(11) #1 Area of zone {i + 1} (m2)')
total_internal_gain = 0
for ig in usage.internal_gains:
total_internal_gain += float(ig.average_internal_gain) * \
(float(ig.convective_fraction) + float(ig.radiative_fraction))
total_internal_gain += ig.average_internal_gain * (ig.convective_fraction + ig.radiative_fraction)
parameters.append(f'{total_internal_gain} % BP(12) #2 Internal gains of zone {i + 1}')
parameters.append(f'{usage.thermal_control.mean_heating_set_point} % BP(13) #3 Heating setpoint temperature '
f'zone {i + 1} (degree Celsius)')
@ -101,7 +131,31 @@ class InselMonthlyEnergyBalance(Insel):
f'zone {i + 1} (degree Celsius)')
parameters.append(f'{usage.hours_day} % BP(16) #6 Usage hours per day zone {i + 1}')
parameters.append(f'{usage.days_year} % BP(17) #7 Usage days per year zone {i + 1}')
parameters.append(f'{usage.mechanical_air_change} % BP(18) #8 Minimum air change rate zone {i + 1} (ACH)')
ventilation = 0
infiltration = 0
for schedule in usage.thermal_control.hvac_availability_schedules:
ventilation_day = 0
infiltration_day = 0
for value in schedule.values:
if value == 0:
infiltration_day += internal_zone.thermal_zones[0].infiltration_rate_system_off / 24
ventilation_day += 0
else:
ventilation_value = usage.mechanical_air_change * value
infiltration_value = internal_zone.thermal_zones[0].infiltration_rate_system_off * value
if ventilation_value >= infiltration_value:
ventilation_day += ventilation_value / 24
infiltration_day += 0
else:
ventilation_day += 0
infiltration_day += infiltration_value / 24
for day_type in schedule.day_types:
infiltration += infiltration_day * cte.DAYS_A_YEAR[day_type] / 365
ventilation += ventilation_day * cte.DAYS_A_YEAR[day_type] / 365
ventilation_infiltration = ventilation + infiltration
parameters.append(f'{ventilation_infiltration} % BP(18) #8 Minimum air change rate zone {i + 1} (ACH)')
parameters.append(f'{len(thermal_zone.thermal_boundaries)} % Number of surfaces = BP(11+8z) \n'
f'% 1. Surface type (1=wall, 2=ground 3=roof, 4=flat roof)\n'
@ -117,17 +171,19 @@ class InselMonthlyEnergyBalance(Insel):
for thermal_boundary in thermal_zone.thermal_boundaries:
type_code = _CONSTRUCTION_CODE[thermal_boundary.type]
window_area = 0
if thermal_boundary.window_ratio < 1:
window_area = thermal_boundary.opaque_area * thermal_boundary.window_ratio / (1 - thermal_boundary.window_ratio)
wall_area = thermal_boundary.opaque_area * (1 + thermal_boundary.window_ratio)
if thermal_boundary.type == cte.WALL:
if thermal_boundary.parent_surface.percentage_shared is not None:
wall_area = wall_area * (1 - thermal_boundary.parent_surface.percentage_shared)
window_area = wall_area * thermal_boundary.window_ratio
parameters.append(type_code)
if thermal_boundary.type != cte.GROUND:
parameters.append(thermal_boundary.opaque_area + window_area)
parameters.append(wall_area)
parameters.append('0.0')
else:
parameters.append('0.0')
parameters.append(thermal_boundary.opaque_area + window_area)
parameters.append(wall_area)
parameters.append(thermal_boundary.u_value)
parameters.append(window_area)

View File

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

View File

@ -6,29 +6,52 @@ Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
"""
from pathlib import Path
import trimesh.exchange.obj
from hub.exports.formats.triangular import Triangular
from hub.imports.geometry_factory import GeometryFactory
class Obj(Triangular):
class Obj:
"""
Export to obj format
"""
def __init__(self, city, path):
super().__init__(city, path, 'obj')
self._city = city
self._path = path
self._export()
def to_ground_points(self):
"""
Move closer to the origin
"""
file_name_in = self._city.name + '.' + self._triangular_format
file_name_out = self._city.name + '_ground.' + self._triangular_format
file_path_in = (Path(self._path).resolve() / file_name_in).resolve()
file_path_out = (Path(self._path).resolve() / file_name_out).resolve()
obj = GeometryFactory('obj', path=file_path_in)
scene = obj.scene
scene.rezero()
obj_file = trimesh.exchange.obj.export_obj(scene)
with open(file_path_out, 'w') as file:
file.write(obj_file)
def _to_vertex(self, coordinate):
x = coordinate[0] - self._city.lower_corner[0]
y = coordinate[1] - self._city.lower_corner[1]
z = coordinate[2] - self._city.lower_corner[2]
return f'v {x} {y} {z}\n'
def _export(self):
if self._city.name is None:
self._city.name = 'unknown_city'
file_name = self._city.name + '.obj'
file_path = (Path(self._path).resolve() / file_name).resolve()
vertices = {}
with open(file_path, 'w') as obj:
obj.write("# cerc-hub export\n")
vertex_index = 0
faces = []
for building in self._city.buildings:
obj.write(f'# building {building.name}\n')
obj.write(f'g {building.name}\n')
obj.write('s off\n')
for surface in building.surfaces:
obj.write(f'# surface {surface.name}\n')
face = 'f '
for coordinate in surface.perimeter_polygon.coordinates:
vertex = self._to_vertex(coordinate)
if vertex not in vertices.keys():
vertex_index += 1
vertices[vertex] = vertex_index
current = vertex_index
obj.write(vertex)
else:
current = vertices[vertex]
face = f'{face} {current}'
faces.append(f'{face} {face.split(" ")[1]}\n')
obj.writelines(faces)
faces = []

View File

@ -8,6 +8,7 @@ import xmltodict
from hub.imports.weather_factory import WeatherFactory
import hub.helpers.constants as cte
from hub.helpers.configuration_helper import ConfigurationHelper
class SimplifiedRadiosityAlgorithm:
@ -88,10 +89,15 @@ class SimplifiedRadiosityAlgorithm:
'@Simulate': f'{simulate}'
}
walls, roofs, floors = [], [], []
default_short_wave_reflectance = ConfigurationHelper().short_wave_reflectance
for surface in building.surfaces:
if surface.short_wave_reflectance is None:
short_wave_reflectance = default_short_wave_reflectance
else:
short_wave_reflectance = surface.short_wave_reflectance
surface_dict = {
'@id': f'{surface.id}',
'@ShortWaveReflectance': f'{surface.short_wave_reflectance}'
'@ShortWaveReflectance': f'{short_wave_reflectance}'
}
for point_index, point in enumerate(surface.perimeter_polygon.coordinates):
point = self._correct_point(point)

View File

@ -17,14 +17,6 @@ class ConfigurationHelper:
self._config = configparser.ConfigParser()
self._config.read(config_file)
@property
def max_location_distance_for_shared_walls(self) -> float:
"""
Get configured maximal distance between attributes to consider that they may share walls in meters
:return: 5.0
"""
return self._config.getfloat('buildings', 'max_location_distance_for_shared_walls')
@property
def min_coordinate(self) -> float:
"""
@ -138,3 +130,19 @@ class ConfigurationHelper:
:return: 0.5
"""
return self._config.getfloat('buildings', 'soil_thickness').real
@property
def short_wave_reflectance(self) -> float:
"""
Get configured short wave reflectance for surfaces that don't have construction assigned
:return: 0.3
"""
return self._config.getfloat('buildings', 'short_wave_reflectance').real
@property
def cold_water_temperature(self) -> float:
"""
Get configured cold water temperature in Celsius
:return: 10
"""
return self._config.getfloat('buildings', 'cold_water_temperature').real

View File

@ -8,13 +8,21 @@ Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
# universal constants
KELVIN = 273.15
WATER_DENSITY = 1000 # kg/m3
WATER_HEAT_CAPACITY = 4182 # J/kgK
AIR_DENSITY = 1.293 # kg/m3
AIR_HEAT_CAPACITY = 1005.2 # J/kgK
# converters
HOUR_TO_MINUTES = 60
MINUTES_TO_SECONDS = 60
HOUR_TO_SECONDS = 3600
METERS_TO_FEET = 3.28084
BTU_H_TO_WATTS = 0.29307107
KILO_WATTS_HOUR_TO_JULES = 3600000
GALLONS_TO_QUBIC_METERS = 0.0037854117954011185
# time
SECOND = 'second'
@ -40,6 +48,24 @@ WEEK_DAYS = 'Weekdays'
WEEK_ENDS = 'Weekends'
ALL_DAYS = 'Alldays'
DAYS_A_MONTH = {'monday': [5, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 5],
'tuesday': [5, 4, 4, 4, 5, 4, 5, 4, 4, 5, 4, 4],
'wednesday': [5, 4, 4, 4, 5, 4, 4, 5, 4, 5, 4, 4],
'thursday': [4, 4, 5, 4, 5, 4, 4, 5, 4, 4, 5, 4],
'friday': [4, 4, 5, 4, 4, 5, 4, 5, 4, 4, 5, 4],
'saturday': [4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5],
'sunday': [4, 4, 4, 5, 4, 4, 5, 4, 5, 4, 4, 5],
'holiday': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}
DAYS_A_YEAR = {'monday': 53,
'tuesday': 52,
'wednesday': 52,
'thursday': 52,
'friday': 52,
'saturday': 52,
'sunday': 52,
'holiday': 0}
# data types
ANY_NUMBER = 'any_number'
FRACTION = 'fraction'
@ -158,3 +184,19 @@ MIN_FLOAT = float('-inf')
# Tools
SRA = 'sra'
INSEL_MEB = 'insel meb'
# Costs units
CURRENCY_PER_SQM = 'currency/m2'
CURRENCY_PER_CBM = 'currency/m3'
CURRENCY_PER_KW = 'currency/kW'
CURRENCY_PER_KWH = 'currency/kWh'
CURRENCY_PER_MONTH = 'currency/month'
CURRENCY_PER_LITRE = 'currency/l'
CURRENCY_PER_KG = 'currency/kg'
CURRENCY_PER_CBM_PER_HOUR = 'currency/(m3/h)'
PERCENTAGE = '%'
# Costs chapters
SUPERSTRUCTURE = 'B_shell'
ENVELOPE = 'D_services'
ALLOWANCES_OVERHEAD_PROFIT = 'Z_allowances_overhead_profit'

View File

@ -8,6 +8,7 @@ Project Coder Guille Gutierrez Guillermo.GutierrezMorote@concordia.ca
import hub.helpers.constants as cte
class AlkisFunctionToHubFunction:
def __init__(self):

View File

@ -34,23 +34,23 @@ class HubUsageToNrcanUsage:
cte.SECONDARY_SCHOOL: 'School/university',
cte.UNIVERSITY: 'School/university',
cte.LABORATORY_AND_RESEARCH_CENTER: 'School/university',
cte.STAND_ALONE_RETAIL: 'Retail',
cte.STAND_ALONE_RETAIL: 'Retail area',
cte.HOSPITAL: 'Hospital',
cte.OUT_PATIENT_HEALTH_CARE: 'Health-care clinic',
cte.HEALTH_CARE: 'Health-care clinic',
cte.RETIREMENT_HOME_OR_ORPHANAGE: 'Health-care clinic',
cte.COMMERCIAL: 'Retail',
cte.STRIP_MALL: 'Retail',
cte.SUPERMARKET: 'Retail',
cte.RETAIL_SHOP_WITHOUT_REFRIGERATED_FOOD: 'Retail',
cte.RETAIL_SHOP_WITH_REFRIGERATED_FOOD: 'Retail',
cte.RESTAURANT: 'Dining - bar/lounge',
cte.QUICK_SERVICE_RESTAURANT: 'Dining - cafeteria',
cte.FULL_SERVICE_RESTAURANT: 'Dining - bar/lounge',
cte.HOTEL: 'Hotel',
cte.HOTEL_MEDIUM_CLASS: 'Motel',
cte.SMALL_HOTEL: 'Motel',
cte.LARGE_HOTEL: 'Hotel',
cte.OUT_PATIENT_HEALTH_CARE: 'Health care clinic',
cte.HEALTH_CARE: 'Health care clinic',
cte.RETIREMENT_HOME_OR_ORPHANAGE: 'Health care clinic',
cte.COMMERCIAL: 'Retail area',
cte.STRIP_MALL: 'Retail area',
cte.SUPERMARKET: 'Retail area',
cte.RETAIL_SHOP_WITHOUT_REFRIGERATED_FOOD: 'Retail area',
cte.RETAIL_SHOP_WITH_REFRIGERATED_FOOD: 'Retail area',
cte.RESTAURANT: 'Dining - bar lounge/leisure',
cte.QUICK_SERVICE_RESTAURANT: 'Dining - cafeteria/fast food',
cte.FULL_SERVICE_RESTAURANT: 'Dining - bar lounge/leisure',
cte.HOTEL: 'Hotel/Motel',
cte.HOTEL_MEDIUM_CLASS: 'Hotel/Motel',
cte.SMALL_HOTEL: 'Hotel/Motel',
cte.LARGE_HOTEL: 'Hotel/Motel',
cte.DORMITORY: 'Dormitory',
cte.EVENT_LOCATION: 'Convention centre',
cte.CONVENTION_CENTER: 'Convention centre',
@ -59,17 +59,17 @@ class HubUsageToNrcanUsage:
cte.INDUSTRY: 'Manufacturing facility',
cte.WORKSHOP: 'Workshop',
cte.WAREHOUSE: 'Warehouse',
cte.WAREHOUSE_REFRIGERATED: 'Warehouse - refrigerated',
cte.WAREHOUSE_REFRIGERATED: 'Warehouse',
cte.SPORTS_LOCATION: 'Exercise centre',
cte.SPORTS_ARENA: 'Sports arena',
cte.GYMNASIUM: 'Gymnasium',
cte.MOTION_PICTURE_THEATRE: 'Motion picture theatre',
cte.MUSEUM: 'Museum',
cte.PERFORMING_ARTS_THEATRE: 'Performing arts theatre',
cte.TRANSPORTATION: 'Transportation',
cte.TRANSPORTATION: 'Transportation facility',
cte.AUTOMOTIVE_FACILITY: 'Automotive facility',
cte.PARKING_GARAGE: 'Parking garage',
cte.RELIGIOUS: 'Religious',
cte.PARKING_GARAGE: 'Storage garage',
cte.RELIGIOUS: 'Religious building',
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_nrcan_usage import HubUsageToNrcanUsage
class Dictionaries:
"""
Dictionaries class

View File

@ -9,6 +9,7 @@ import math
import numpy as np
import requests
from PIL import Image
from trimesh import Trimesh
from trimesh import intersections
@ -63,7 +64,7 @@ class GeometryHelper:
return MapPoint(((city.upper_corner[0] - coordinate[0]) * 0.5), ((city.upper_corner[1] - coordinate[1]) * 0.5))
@staticmethod
def city_mapping(city, building_names=None):
def city_mapping(city, building_names=None, plot=False):
"""
Returns a shared_information dictionary like
@ -78,6 +79,107 @@ class GeometryHelper:
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)]
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
city_image = img.load() # create the pixel map
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
map_info[x][y] = {
'line_start': (coordinate[0], coordinate[1]),
'line_end': (next_coordinate[0], next_coordinate[1]),
}
city_image[x, y] = (100, 0, 0)
elif city_map[x][y] != building.name:
neighbour = city.city_object(city_map[x][y])
neighbour_info = map_info[x][y]
# prepare the keys
neighbour_start_coordinate = f'{GeometryHelper.coordinate_to_map_point(neighbour_info["line_start"], city)}'
building_start_coordinate = f'{GeometryHelper.coordinate_to_map_point(coordinate, city)}'
neighbour_key = f'{neighbour.name}_{neighbour_start_coordinate}_{building_start_coordinate}'
building_key = f'{building.name}_{building_start_coordinate}_{neighbour_start_coordinate}'
# Add my neighbour info to my shared lines
if building.name in lines_information.keys() and neighbour_key in lines_information[building.name]:
shared_points = int(lines_information[building.name][neighbour_key]['shared_points'])
lines_information[building.name][neighbour_key]['shared_points'] = shared_points + 1
else:
if building.name not in lines_information.keys():
lines_information[building.name] = {}
lines_information[building.name][neighbour_key] = {
'neighbour_name': neighbour.name,
'line_start': (coordinate[0], coordinate[1]),
'line_end': (next_coordinate[0], next_coordinate[1]),
'neighbour_line_start': neighbour_info['line_start'],
'neighbour_line_end': neighbour_info['line_end'],
'coordinate_start': f"{GeometryHelper.coordinate_to_map_point(coordinate, city)}",
'coordinate_end': f"{GeometryHelper.coordinate_to_map_point(next_coordinate, city)}",
'neighbour_start': f"{GeometryHelper.coordinate_to_map_point(neighbour_info['line_start'], city)}",
'neighbour_end': f"{GeometryHelper.coordinate_to_map_point(neighbour_info['line_end'], city)}",
'shared_points': 1
}
# Add my info to my neighbour shared lines
if neighbour.name in lines_information.keys() and building_key in lines_information[neighbour.name]:
shared_points = int(lines_information[neighbour.name][building_key]['shared_points'])
lines_information[neighbour.name][building_key]['shared_points'] = shared_points + 1
else:
if neighbour.name not in lines_information.keys():
lines_information[neighbour.name] = {}
lines_information[neighbour.name][building_key] = {
'neighbour_name': building.name,
'line_start': neighbour_info['line_start'],
'line_end': neighbour_info['line_end'],
'neighbour_line_start': (coordinate[0], coordinate[1]),
'neighbour_line_end': (next_coordinate[0], next_coordinate[1]),
'neighbour_start': f"{GeometryHelper.coordinate_to_map_point(coordinate, city)}",
'neighbour_end': f"{GeometryHelper.coordinate_to_map_point(next_coordinate, city)}",
'coordinate_start': f"{GeometryHelper.coordinate_to_map_point(neighbour_info['line_start'], city)}",
'coordinate_end': f"{GeometryHelper.coordinate_to_map_point(neighbour_info['line_end'], city)}",
'shared_points': 1
}
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
if plot:
img.show()
return lines_information
@staticmethod
def fast_city_mapping(city, building_names=None):
lines_information = {}
if building_names is None:
building_names = [b.name for b in city.buildings]
x = int((city.upper_corner[0] - city.lower_corner[0]) * 0.5) + 1
y = int((city.upper_corner[1] - city.lower_corner[1]) * 0.5) + 1
city_map = [['' for _ in range(y + 1)] for _ in range(x + 1)]
for building_name in building_names:
building = city.city_object(building_name)
line = 0
@ -99,52 +201,8 @@ class GeometryHelper:
y = MapPoint(point.x + (delta_x * k), point.y + (delta_y * k)).y
if city_map[x][y] == '':
city_map[x][y] = building.name
map_info[x][y] = {
'line_start': (coordinate[0], coordinate[1]),
'line_end': (next_coordinate[0], next_coordinate[1]),
}
elif city_map[x][y] != building.name:
neighbour = city.city_object(city_map[x][y])
neighbour_info = map_info[x][y]
# prepare the keys
neighbour_start_coordinate = f'{neighbour_info["line_start"][0]}_{neighbour_info["line_start"][1]}'
building_start_coordinate = f'{coordinate[0]}_{coordinate[1]}'
neighbour_key = f'{neighbour.name}_{neighbour_start_coordinate}_{building_start_coordinate}'
building_key = f'{building.name}_{building_start_coordinate}_{neighbour_start_coordinate}'
# Add my neighbour info to my shared lines
if building.name in lines_information.keys() and neighbour_key in lines_information[building.name]:
shared_points = int(lines_information[building.name][neighbour_key]['shared_points'])
lines_information[building.name][neighbour_key]['shared_points'] = shared_points + 1
else:
if building.name not in lines_information.keys():
lines_information[building.name] = {}
lines_information[building.name][neighbour_key] = {
'neighbour_name': neighbour.name,
'line_start': (coordinate[0], coordinate[1]),
'line_end': (next_coordinate[0], next_coordinate[1]),
'neighbour_line_start': neighbour_info['line_start'],
'neighbour_line_end': neighbour_info['line_end'],
'shared_points': 1
}
# Add my info to my neighbour shared lines
if neighbour.name in lines_information.keys() and building_key in lines_information[neighbour.name]:
shared_points = int(lines_information[neighbour.name][building_key]['shared_points'])
lines_information[neighbour.name][building_key]['shared_points'] = shared_points + 1
else:
if neighbour.name not in lines_information.keys():
lines_information[neighbour.name] = {}
lines_information[neighbour.name][building_key] = {
'neighbour_name': building.name,
'line_start': neighbour_info['line_start'],
'line_end': neighbour_info['line_end'],
'neighbour_line_start': (coordinate[0], coordinate[1]),
'neighbour_line_end': (next_coordinate[0], next_coordinate[1]),
'shared_points': 1
}
if building.neighbours is None:
building.neighbours = [neighbour]
elif neighbour not in building.neighbours:

View File

@ -1,16 +1,33 @@
import logging as logger
from pathlib import Path
import os
import logging
import sys
log_dir = (Path(__file__).parent.parent / 'logs').resolve()
log_file = (log_dir / 'hub.log').resolve()
try:
def get_logger(file_logger=False):
"""
Returns a logging object
:param file_logger: a boolean to indicate the kind of logging
object to return, true (default) means a file logger is required
:return:
"""
log_format = "%(asctime)s:%(levelname)s:{%(pathname)s:%(funcName)s:%(lineno)d} - %(message)s"
if file_logger:
log_dir = (Path(__file__).parent.parent / 'logs').resolve()
log_file = (log_dir / 'hub.log').resolve()
try:
if not os.path.isfile(log_file):
if not os.path.exists:
if not os.path.exists(log_dir):
os.mkdir(log_dir)
with open(log_file, 'x'):
pass
logger.basicConfig(filename=log_file, format="%(asctime)s:%(levelname)s:{%(pathname)s:%(funcName)s:%(lineno)d} "
"- %(message)s", level=logger.DEBUG)
except IOError as err:
logger.basicConfig(filename=log_file, format=log_format, level=logger.DEBUG)
return logger
except IOError as err:
print(f'I/O exception: {err}')
else:
logging.getLogger().addHandler(logging.StreamHandler(stream=sys.stdout))
logging.getLogger().setLevel(logging.DEBUG)
return logger.getLogger()

View File

@ -4,6 +4,7 @@ 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 datetime
import sys
import math
import numpy as np
@ -39,7 +40,7 @@ class StoreysGeneration:
number_of_storeys, height = self._calculate_number_storeys_and_height(self._building.average_storey_height,
self._building.eave_height,
self._building.storeys_above_ground)
number_of_storeys = 1
if not self._divide_in_storeys or number_of_storeys == 1:
storey = Storey('storey_0', self._building.surfaces, [None, None], self._internal_zone.volume,
self._internal_zone, self._floor_area)
@ -55,7 +56,6 @@ class StoreysGeneration:
else:
thermal_zones = [storey.neighbours[1], storey.thermal_zone]
thermal_boundary.thermal_zones = thermal_zones
return [storey.thermal_zone]
if number_of_storeys == 0:

View File

@ -4,8 +4,10 @@ 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 datetime
import math
import sys
from hub.hub_logger import get_logger
import hub.helpers.constants as cte
from hub.catalog_factories.construction_catalog_factory import ConstructionCatalogFactory
@ -15,6 +17,8 @@ from hub.helpers.dictionaries import Dictionaries
from hub.imports.construction.helpers.construction_helper import ConstructionHelper
from hub.imports.construction.helpers.storeys_generation import StoreysGeneration
logger = get_logger()
class NrcanPhysicsParameters:
"""
@ -35,14 +39,21 @@ class NrcanPhysicsParameters:
for building in city.buildings:
try:
function = Dictionaries().hub_function_to_nrcan_construction_function[building.function]
archetype = self._search_archetype(nrcan_catalog, function, building.year_of_construction, self._climate_zone)
except KeyError:
sys.stderr.write(f'Building {building.name} has unknown construction archetype for building function: '
f'{building.function}, building year of construction: {building.year_of_construction} '
logger.error(f'Building {building.name} has unknown construction archetype for building function: '
f'{function} [{building.function}], building year of construction: {building.year_of_construction} '
f'and climate zone {self._climate_zone}\n')
return
sys.stderr.write(f'Building {building.name} has unknown construction archetype for building function: '
f'{function} [{building.function}], building year of construction: {building.year_of_construction} '
f'and climate zone {self._climate_zone}\n')
continue
# if building has no thermal zones defined from geometry, and the building will be divided in storeys,
# one thermal zone per storey is assigned
if len(building.internal_zones) == 1:
if building.internal_zones[0].thermal_zones is None:
self._create_storeys(building, archetype, self._divide_in_storeys)
@ -58,7 +69,6 @@ class NrcanPhysicsParameters:
for internal_zone in building.internal_zones:
for thermal_zone in internal_zone.thermal_zones:
thermal_zone.total_floor_area = thermal_zone.footprint_area
for internal_zone in building.internal_zones:
self._assign_values(internal_zone.thermal_zones, archetype)
for thermal_zone in internal_zone.thermal_zones:
@ -69,7 +79,7 @@ class NrcanPhysicsParameters:
nrcan_archetypes = nrcan_catalog.entries('archetypes')
for building_archetype in nrcan_archetypes:
construction_period_limits = building_archetype.construction_period.split('_')
if int(construction_period_limits[0]) <= year_of_construction < int(construction_period_limits[1]):
if int(construction_period_limits[0]) <= int(year_of_construction) <= int(construction_period_limits[1]):
if (str(function) == str(building_archetype.function)) and \
(climate_zone == str(building_archetype.climate_zone)):
return building_archetype

View File

@ -7,7 +7,7 @@ Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concord
"""
import sys
from hub.hub_logger import logger
from hub.hub_logger import get_logger
from hub.catalog_factories.construction_catalog_factory import ConstructionCatalogFactory
from hub.city_model_structure.building_demand.layer import Layer
from hub.city_model_structure.building_demand.material import Material
@ -15,6 +15,8 @@ from hub.helpers.dictionaries import Dictionaries
from hub.imports.construction.helpers.construction_helper import ConstructionHelper
from hub.imports.construction.helpers.storeys_generation import StoreysGeneration
logger = get_logger()
class NrelPhysicsParameters:
"""
@ -39,14 +41,14 @@ class NrelPhysicsParameters:
archetype = self._search_archetype(nrel_catalog, function, building.year_of_construction,
self._climate_zone)
except KeyError:
logger.error(f'Building {building.name} has unknown archetype for building function: {building.function} '
f'and building year of construction: {building.year_of_construction} '
logger.error(f'Building {building.name} has unknown construction archetype for building function: '
f'{building.function} and building year of construction: {building.year_of_construction} '
f'and climate zone reference norm {self._climate_zone}\n')
sys.stderr.write(f'Building {building.name} has unknown archetype for building function: {building.function} '
f'and building year of construction: {building.year_of_construction} '
sys.stderr.write(f'Building {building.name} has unknown construction archetype for building function: '
f'{building.function} and building year of construction: {building.year_of_construction} '
f'and climate zone reference norm {self._climate_zone}\n')
return
continue
# if building has no thermal zones defined from geometry, and the building will be divided in storeys,
# one thermal zone per storey is assigned
@ -78,7 +80,7 @@ class NrelPhysicsParameters:
construction_period_limits = building_archetype.construction_period.split(' - ')
if construction_period_limits[1] == 'PRESENT':
construction_period_limits[1] = 3000
if int(construction_period_limits[0]) <= year_of_construction < int(construction_period_limits[1]):
if int(construction_period_limits[0]) <= int(year_of_construction) < int(construction_period_limits[1]):
if (str(function) == str(building_archetype.function)) and \
(climate_zone == str(building_archetype.climate_zone)):
return building_archetype

View File

@ -7,11 +7,13 @@ Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concord
"""
from pathlib import Path
from hub.hub_logger import logger
from hub.hub_logger import get_logger
from hub.helpers.utils import validate_import_export_type
from hub.imports.construction.nrel_physics_parameters import NrelPhysicsParameters
from hub.imports.construction.nrcan_physics_parameters import NrcanPhysicsParameters
logger = get_logger()
class ConstructionFactory:
"""

View File

@ -9,7 +9,9 @@ from pathlib import Path
from hub.imports.energy_systems.air_source_hp_parameters import AirSourceHeatPumpParameters
from hub.imports.energy_systems.water_to_water_hp_parameters import WaterToWaterHPParameters
from hub.helpers.utils import validate_import_export_type
from hub.hub_logger import logger
from hub.hub_logger import get_logger
logger = get_logger()
class EnergySystemsFactory:

View File

@ -4,12 +4,12 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Guillermo Gutierrez Guillermo.GutierrezMorote@concordia.ca
"""
import json
import trimesh.creation
import numpy as np
from pyproj import Transformer
from shapely.geometry import Polygon as ShapelyPolygon
import hub.helpers.constants as cte
from hub.helpers.geometry_helper import GeometryHelper
@ -64,8 +64,12 @@ class Geojson:
buildings = []
for zone, surface_coordinates in enumerate(surfaces_coordinates):
points = igh.points_from_string(igh.remove_last_point_from_string(surface_coordinates))
# geojson provides the roofs, need to be transform into grounds
points = igh.invert_points(points)
polygon = Polygon(points)
surfaces.append(Surface(polygon, polygon, surface_type=cte.GROUND))
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
@ -74,22 +78,43 @@ class Geojson:
lod0_buildings = Geojson._create_buildings_lod0(name, year_of_construction, function, surface_coordinates)
surfaces = []
buildings = []
for zone, lod0_building in enumerate(lod0_buildings):
for surface in lod0_building.surfaces:
shapely_polygon = ShapelyPolygon(surface.solid_polygon.coordinates)
if not shapely_polygon.is_valid:
print(surface.solid_polygon.area)
print('error?', name, surface_coordinates)
continue
mesh = trimesh.creation.extrude_polygon(shapely_polygon, height)
for face in mesh.faces:
points = []
for vertex_index in face:
points.append(mesh.vertices[vertex_index])
polygon = Polygon(points)
surface = Surface(polygon, polygon)
for surface in lod0_building.grounds:
volume = surface.solid_polygon.area * height
surfaces.append(surface)
buildings.append(Building(f'{name}_zone_{zone}', surfaces, year_of_construction, function))
roof_coordinates = []
# adding a roof means invert the polygon coordinates and change the Z value
for coordinate in surface.solid_polygon.coordinates:
roof_coordinate = np.array([coordinate[0], coordinate[1], height])
# insert the roof rotated already
roof_coordinates.insert(0, roof_coordinate)
polygon = Polygon(roof_coordinates)
polygon.area = surface.solid_polygon.area
roof = Surface(polygon, 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'{name}_zone_{zone}', surfaces, year_of_construction, function)
building.volume = volume
buildings.append(building)
return buildings
def _get_polygons(self, polygons, coordinates):
@ -110,8 +135,9 @@ class Geojson:
@staticmethod
def _find_wall(line_1, line_2):
for i in range(0, 2):
j = 1 - i
point_1 = line_1[i]
point_2 = line_2[i]
point_2 = line_2[j]
distance = GeometryHelper.distance_between_points(point_1, point_2)
if distance > 1e-2:
return False
@ -119,6 +145,10 @@ class Geojson:
def _store_shared_percentage_to_walls(self, city, city_mapped):
for building in city.buildings:
if building.name not in city_mapped.keys():
for wall in building.walls:
wall.percentage_shared = 0
continue
building_mapped = city_mapped[building.name]
for wall in building.walls:
percentage = 0
@ -126,12 +156,8 @@ class Geojson:
for point in wall.perimeter_polygon.coordinates:
if point[2] < 0.5:
ground_line.append(point)
# todo: erase when we have no triangulation
if len(ground_line) < 2:
continue
# todo: erase down to here
for entry in building_mapped:
if building_mapped[entry]['shared_points'] <= 5:
if building_mapped[entry]['shared_points'] <= 3:
continue
line = [building_mapped[entry]['line_start'], building_mapped[entry]['line_end']]
neighbour_line = [building_mapped[entry]['neighbour_line_start'],
@ -204,10 +230,12 @@ class Geojson:
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:
# Do not include "small building-like structures" to buildings
if building.floor_area >= 25:
self._city.add_city_object(building)
self._city.level_of_detail.geometry = lod
if lod == 1:
lines_information = GeometryHelper.city_mapping(self._city)
lines_information = GeometryHelper.city_mapping(self._city, plot=False)
self._store_shared_percentage_to_walls(self._city, lines_information)
if len(missing_functions) > 0:
print(f'There are unknown functions {missing_functions}')

View File

@ -4,6 +4,8 @@ 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 math
import sys
import numpy as np
@ -45,3 +47,61 @@ class GeometryHelper:
array = points.split(' ')
res = " "
return res.join(array[0:len(array) - 3])
@staticmethod
def invert_points(points):
res = []
for point in points:
res.insert(0,point)
return res
@staticmethod
def ground_area(points):
"""
Get ground surface area in square meters
:return: float
"""
# New method to calculate area
if len(points) < 3:
sys.stderr.write('Warning: the area of a line or point cannot be calculated 1. Area = 0\n')
return 0
alpha = 0
vec_1 = points[1] - points[0]
for i in range(2, len(points)):
vec_2 = points[i] - points[0]
alpha += GeometryHelper.angle_between_vectors(vec_1, vec_2)
if alpha == 0:
sys.stderr.write('Warning: the area of a line or point cannot be calculated 2. Area = 0\n')
return 0
#
horizontal_points = points
area = 0
for i in range(0, len(horizontal_points) - 1):
point = horizontal_points[i]
next_point = horizontal_points[i + 1]
area += (next_point[1] + point[1]) / 2 * (next_point[0] - point[0])
next_point = horizontal_points[0]
point = horizontal_points[len(horizontal_points) - 1]
area += (next_point[1] + point[1]) / 2 * (next_point[0] - point[0])
_area = abs(area)
return _area
@staticmethod
def angle_between_vectors(vec_1, vec_2):
"""
angle between vectors in radians
:param vec_1: vector
:param vec_2: vector
:return: float
"""
if np.linalg.norm(vec_1) == 0 or np.linalg.norm(vec_2) == 0:
sys.stderr.write("Warning: impossible to calculate angle between planes' normal. Return 0\n")
return 0
cosine = np.dot(vec_1, vec_2) / np.linalg.norm(vec_1) / np.linalg.norm(vec_2)
if cosine > 1 and cosine - 1 < 1e-5:
cosine = 1
elif cosine < -1 and cosine + 1 > -1e-5:
cosine = -1
alpha = math.acos(cosine)
return alpha

View File

@ -13,7 +13,9 @@ from hub.imports.geometry.rhino import Rhino
from hub.imports.geometry.gpandas import GPandas
from hub.imports.geometry.geojson import Geojson
from hub.helpers.utils import validate_import_export_type
from hub.hub_logger import logger
from hub.hub_logger import get_logger
logger = get_logger()
class GeometryFactory:

View File

@ -11,7 +11,9 @@ from hub.imports.life_cycle_assessment.lca_vehicle import LcaVehicle
from hub.imports.life_cycle_assessment.lca_machine import LcaMachine
from hub.imports.life_cycle_assessment.lca_material import LcaMaterial
from hub.helpers.utils import validate_import_export_type
from hub.hub_logger import logger
from hub.hub_logger import get_logger
logger = get_logger()
class LifeCycleAssessment:

View File

@ -10,6 +10,7 @@ import pandas as pd
import csv
import hub.helpers.constants as cte
class InselMonthlyEnergyBalance:
"""
Import SRA results
@ -20,7 +21,7 @@ class InselMonthlyEnergyBalance:
self._base_path = base_path
@staticmethod
def _demand(insel_output_file_path):
def _conditioning_demand(insel_output_file_path):
heating = []
cooling = []
with open(Path(insel_output_file_path).resolve()) as csv_file:
@ -39,15 +40,74 @@ class InselMonthlyEnergyBalance:
monthly_cooling = pd.DataFrame(cooling, columns=[cte.INSEL_MEB]).astype(float)
return monthly_heating, monthly_cooling
def _dhw_demand(self):
for building in self._city.buildings:
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 = []
appliances_demand = []
if building.internal_zones[0].thermal_zones is None:
lighting_demand = [0] * 12
appliances_demand = [0] * 12
else:
thermal_zone = building.internal_zones[0].thermal_zones[0]
area = thermal_zone.total_floor_area
for month in range(0, 12):
total_lighting = 0
for schedule in thermal_zone.lighting.schedules:
total_day = 0
for value in schedule.values:
total_day += value
for day_type in schedule.day_types:
total_lighting += total_day * cte.DAYS_A_MONTH[day_type][month] * thermal_zone.lighting.density
lighting_demand.append(total_lighting * area)
total_appliances = 0
for schedule in thermal_zone.appliances.schedules:
total_day = 0
for value in schedule.values:
total_day += value
for day_type in schedule.day_types:
total_appliances += total_day * cte.DAYS_A_MONTH[day_type][month] * thermal_zone.appliances.density
appliances_demand.append(total_appliances * area)
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])
def enrich(self):
for building in self._city.buildings:
file_name = building.name + '.out'
insel_output_file_path = Path(self._base_path / file_name).resolve()
if insel_output_file_path.is_file():
building.heating[cte.MONTH], building.cooling[cte.MONTH] = self._demand(insel_output_file_path)
building.heating[cte.MONTH], building.cooling[cte.MONTH] = self._conditioning_demand(insel_output_file_path)
building.heating[cte.YEAR] = pd.DataFrame(
[building.heating[cte.MONTH][cte.INSEL_MEB].astype(float).sum()], columns=[cte.INSEL_MEB]
)
building.cooling[cte.YEAR] = pd.DataFrame(
[building.cooling[cte.MONTH][cte.INSEL_MEB].astype(float).sum()], columns=[cte.INSEL_MEB]
)
self._dhw_demand()
self._electrical_demand()

View File

@ -8,11 +8,13 @@ Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concord
from pathlib import Path
from hub.helpers.utils import validate_import_export_type
from hub.hub_logger import logger
from hub.hub_logger import get_logger
from hub.imports.results.simplified_radiosity_algorithm import SimplifiedRadiosityAlgorithm
from hub.imports.results.insel_monthly_energry_balance import InselMonthlyEnergyBalance
from hub.imports.results.insel_heatpump_energy_demand import InselHeatPumpEnergyDemand
logger = get_logger()
class ResultFactory:
"""

View File

@ -6,9 +6,11 @@ Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
from pathlib import Path
from hub.hub_logger import logger
from hub.hub_logger import get_logger
from hub.helpers.utils import validate_import_export_type
logger = get_logger()
class SensorsFactory:
"""

View File

@ -8,6 +8,7 @@ import copy
import sys
import numpy
from hub.hub_logger import get_logger
import hub.helpers.constants as cte
from hub.helpers.dictionaries import Dictionaries
from hub.city_model_structure.building_demand.usage import Usage
@ -20,6 +21,8 @@ from hub.city_model_structure.attributes.schedule import Schedule
from hub.city_model_structure.building_demand.internal_gain import InternalGain
from hub.catalog_factories.usage_catalog_factory import UsageCatalogFactory
logger = get_logger()
class ComnetUsageParameters:
"""
@ -41,9 +44,9 @@ class ComnetUsageParameters:
try:
archetype_usage = self._search_archetypes(comnet_catalog, usage_name)
except KeyError:
sys.stderr.write(f'Building {building.name} has unknown usage archetype for building function:'
f' {building.function}')
return
logger.error(f'Building {building.name} has unknown usage archetype for usage: {usage_name}')
sys.stderr.write(f'Building {building.name} has unknown usage archetype for usage: {usage_name}')
continue
for internal_zone in building.internal_zones:
if internal_zone.area is None:
@ -55,7 +58,7 @@ class ComnetUsageParameters:
volume_per_area = internal_zone.volume / internal_zone.area
usage = Usage()
usage.name = usage_name
self._assign_values(usage, archetype_usage, volume_per_area)
self._assign_values(usage, archetype_usage, volume_per_area, building.cold_water_temperature)
usage.percentage = 1
self._calculate_reduced_values_from_extended_library(usage, archetype_usage)
@ -70,12 +73,12 @@ class ComnetUsageParameters:
raise KeyError('archetype not found')
@staticmethod
def _assign_values(usage, archetype, volume_per_area):
def _assign_values(usage, archetype, volume_per_area, cold_water_temperature):
# Due to the fact that python is not a typed language, the wrong object type is assigned to
# usage.occupancy when writing usage.occupancy = archetype.occupancy.
# Same happens for lighting and appliances. Therefore, this walk around has been done.
usage.mechanical_air_change = archetype.ventilation_rate / volume_per_area \
* cte.HOUR_TO_MINUTES * cte.MINUTES_TO_SECONDS
* cte.HOUR_TO_SECONDS
_occupancy = Occupancy()
_occupancy.occupancy_density = archetype.occupancy.occupancy_density
_occupancy.sensible_radiative_internal_gain = archetype.occupancy.sensible_radiative_internal_gain
@ -105,6 +108,14 @@ class ComnetUsageParameters:
_domestic_hot_water = DomesticHotWater()
_domestic_hot_water.density = archetype.domestic_hot_water.density
_domestic_hot_water.service_temperature = archetype.domestic_hot_water.service_temperature
peak_flow = None
if len(cold_water_temperature) > 0:
cold_temperature = cold_water_temperature[cte.YEAR]['epw']
peak_flow = 0
if (archetype.domestic_hot_water.service_temperature - cold_temperature) > 0:
peak_flow = archetype.domestic_hot_water.density / cte.WATER_DENSITY / cte.WATER_HEAT_CAPACITY \
/ (archetype.domestic_hot_water.service_temperature - cold_temperature)
_domestic_hot_water.peak_flow = peak_flow
_domestic_hot_water.schedules = archetype.domestic_hot_water.schedules
usage.domestic_hot_water = _domestic_hot_water

View File

@ -7,6 +7,7 @@ Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
import sys
from hub.hub_logger import get_logger
import hub.helpers.constants as cte
from hub.helpers.dictionaries import Dictionaries
from hub.city_model_structure.building_demand.usage import Usage
@ -17,6 +18,8 @@ from hub.city_model_structure.building_demand.thermal_control import ThermalCont
from hub.city_model_structure.building_demand.domestic_hot_water import DomesticHotWater
from hub.catalog_factories.usage_catalog_factory import UsageCatalogFactory
logger = get_logger()
class NrcanUsageParameters:
"""
@ -40,17 +43,17 @@ class NrcanUsageParameters:
try:
archetype_usage = self._search_archetypes(nrcan_catalog, usage_name)
except KeyError:
sys.stderr.write(f'Building {building.name} has unknown usage archetype for building function:'
f' {building.function}')
return
logger.error(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
usage_name = Dictionaries().hub_usage_to_comnet_usage[building.function]
try:
comnet_archetype_usage = self._search_archetypes(comnet_catalog, usage_name)
except KeyError:
sys.stderr.write(f'Building {building.name} has unknown usage archetype for building function:'
f' {building.function}')
return
logger.error(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
for internal_zone in building.internal_zones:
if internal_zone.area is None:
@ -62,8 +65,8 @@ class NrcanUsageParameters:
volume_per_area = internal_zone.volume / internal_zone.area
usage = Usage()
usage.name = usage_name
self._assign_values(usage, archetype_usage, volume_per_area)
self._assign_comnet_extra_values(usage, comnet_archetype_usage)
self._assign_values(usage, archetype_usage, volume_per_area, building.cold_water_temperature)
self._assign_comnet_extra_values(usage, comnet_archetype_usage, archetype_usage.occupancy.occupancy_density)
usage.percentage = 1
self._calculate_reduced_values_from_extended_library(usage, archetype_usage)
@ -78,12 +81,11 @@ class NrcanUsageParameters:
raise KeyError('archetype not found')
@staticmethod
def _assign_values(usage, archetype, volume_per_area):
def _assign_values(usage, archetype, volume_per_area, cold_water_temperature):
if archetype.mechanical_air_change > 0:
usage.mechanical_air_change = archetype.mechanical_air_change
elif archetype.ventilation_rate > 0:
usage.mechanical_air_change = archetype.ventilation_rate / volume_per_area \
* cte.HOUR_TO_MINUTES * cte.MINUTES_TO_SECONDS
usage.mechanical_air_change = archetype.ventilation_rate / volume_per_area * cte.HOUR_TO_SECONDS
else:
usage.mechanical_air_change = 0
_occupancy = Occupancy()
@ -113,17 +115,26 @@ class NrcanUsageParameters:
_control.hvac_availability_schedules = archetype.thermal_control.hvac_availability_schedules
usage.thermal_control = _control
_domestic_hot_water = DomesticHotWater()
_domestic_hot_water.density = archetype.domestic_hot_water.density
_domestic_hot_water.peak_flow = archetype.domestic_hot_water.peak_flow
_domestic_hot_water.service_temperature = archetype.domestic_hot_water.service_temperature
density = None
if len(cold_water_temperature) > 0:
cold_temperature = cold_water_temperature[cte.YEAR]['epw']
density = archetype.domestic_hot_water.peak_flow * cte.WATER_DENSITY * cte.WATER_HEAT_CAPACITY \
* (archetype.domestic_hot_water.service_temperature - cold_temperature)
_domestic_hot_water.density = density
_domestic_hot_water.schedules = archetype.domestic_hot_water.schedules
usage.domestic_hot_water = _domestic_hot_water
@staticmethod
def _assign_comnet_extra_values(usage, archetype):
def _assign_comnet_extra_values(usage, archetype, occupancy_density):
_occupancy = usage.occupancy
_occupancy.sensible_radiative_internal_gain = archetype.occupancy.sensible_radiative_internal_gain
_occupancy.latent_internal_gain = archetype.occupancy.latent_internal_gain
_occupancy.sensible_convective_internal_gain = archetype.occupancy.sensible_convective_internal_gain
archetype_density = archetype.occupancy.occupancy_density
_occupancy.sensible_radiative_internal_gain = archetype.occupancy.sensible_radiative_internal_gain \
* occupancy_density / archetype_density
_occupancy.latent_internal_gain = archetype.occupancy.latent_internal_gain * occupancy_density / archetype_density
_occupancy.sensible_convective_internal_gain = archetype.occupancy.sensible_convective_internal_gain \
* occupancy_density / archetype_density
@staticmethod
def _calculate_reduced_values_from_extended_library(usage, archetype):

View File

@ -9,9 +9,11 @@ Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concord
from pathlib import Path
from hub.imports.usage.comnet_usage_parameters import ComnetUsageParameters
from hub.imports.usage.nrcan_usage_parameters import NrcanUsageParameters
from hub.hub_logger import logger
from hub.hub_logger import get_logger
from hub.helpers.utils import validate_import_export_type
logger = get_logger()
class UsageFactory:
"""

View File

@ -32,14 +32,13 @@ class EpwWeatherParameters:
_ = file.readline().split(',')
line = file.readline().split(',')
number_records = int(line[1])
depth_measurement_ground_temperature = []
ground_temperature = []
ground_temperature = {}
for i in range(0, number_records):
depth_measurement_ground_temperature.append(line[i*16+2])
depth_measurement_ground_temperature = line[i*16+2]
temperatures = []
for j in range(0, 12):
temperatures.append(line[i*16+j+6])
ground_temperature.append(temperatures)
temperatures.append(float(line[i*16+j+6]))
ground_temperature[depth_measurement_ground_temperature] = temperatures
file.close()
except SystemExit:
sys.stderr.write(f'Error: weather file {self._path} not found. Please download it from '
@ -74,6 +73,14 @@ class EpwWeatherParameters:
sys.exit()
for building in self._city.buildings:
building.ground_temperature[cte.MONTH] = ground_temperature
ground_temperature = {}
for set in building.ground_temperature[cte.MONTH]:
temperature = 0
for value in building.ground_temperature[cte.MONTH][set]:
temperature += value / 12
ground_temperature[set] = [temperature]
building.ground_temperature[cte.YEAR] = ground_temperature
if cte.HOUR in building.external_temperature:
del building.external_temperature[cte.HOUR]
new_value = pd.DataFrame(self._weather_values[['dry_bulb_temperature_c']].to_numpy(), columns=['epw'])
@ -111,10 +118,46 @@ class EpwWeatherParameters:
building.beam[cte.HOUR] = new_value
else:
pd.concat([building.beam[cte.HOUR], new_value], axis=1)
new_value = wh().cold_water_temperature(building.external_temperature[cte.HOUR]['epw'])
if cte.HOUR not in building.cold_water_temperature:
building.cold_water_temperature[cte.HOUR] = new_value
else:
pd.concat([building.cold_water_temperature[cte.HOUR], new_value], axis=1)
# create the monthly and yearly values out of the hourly
for building in self._city.buildings:
if cte.MONTH not in building.external_temperature:
building.external_temperature[cte.MONTH] = wh().get_monthly_mean_values(building.external_temperature[cte.HOUR][['epw']])
building.external_temperature[cte.MONTH] = \
wh().get_monthly_mean_values(building.external_temperature[cte.HOUR][['epw']])
if cte.YEAR not in building.external_temperature:
building.external_temperature[cte.YEAR] = wh(). get_yearly_mean_values(building.external_temperature[cte.HOUR][['epw']])
building.external_temperature[cte.YEAR] = \
wh(). get_yearly_mean_values(building.external_temperature[cte.HOUR][['epw']])
if cte.MONTH not in building.cold_water_temperature:
building.cold_water_temperature[cte.MONTH] = wh().get_monthly_mean_values(
building.cold_water_temperature[cte.HOUR][['epw']])
if cte.YEAR not in building.cold_water_temperature:
building.cold_water_temperature[cte.YEAR] = wh().get_yearly_mean_values(
building.cold_water_temperature[cte.HOUR][['epw']])
# If the usage has already being imported, the domestic hot water missing values must be calculated here that
# the cold water temperature is finally known
cold_temperature = building.cold_water_temperature[cte.YEAR]['epw']
for internal_zone in building.internal_zones:
if internal_zone.usages is not None:
for usage in internal_zone.usages:
if usage.domestic_hot_water.peak_flow is None:
if usage.domestic_hot_water.density is None:
continue
peak_flow = 0
if (usage.domestic_hot_water.service_temperature - cold_temperature) > 0:
peak_flow = usage.domestic_hot_water.density / cte.WATER_DENSITY / cte.WATER_HEAT_CAPACITY \
/ (usage.domestic_hot_water.service_temperature - cold_temperature)
usage.domestic_hot_water.peak_flow = peak_flow
if usage.domestic_hot_water.density is None:
if usage.domestic_hot_water.peak_flow is None:
continue
density = usage.domestic_hot_water.peak_flow * cte.WATER_DENSITY * cte.WATER_HEAT_CAPACITY \
* (usage.domestic_hot_water.service_temperature - cold_temperature)
usage.domestic_hot_water.density = density
self._city.level_of_detail.weather = 2

View File

@ -4,6 +4,7 @@ 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 math
import hub.helpers.constants as cte
import pandas as pd
@ -20,7 +21,7 @@ class Weather:
def sky_temperature(ambient_temperature):
"""
Get sky temperature from ambient temperature in Celsius
:return: float
:return: List[float]
"""
# Swinbank - Source sky model approximation(1963) based on cloudiness statistics(32 %) in United States
# ambient temperatures( in °C)
@ -32,6 +33,37 @@ class Weather:
values.append(value)
return values
@staticmethod
def cold_water_temperature(ambient_temperature):
"""
Get cold water temperature from ambient temperature in Celsius
:return: dict
"""
# Equation from "TOWARDS DEVELOPMENT OF AN ALGORITHM FOR MAINS WATER TEMPERATURE", 2004, Jay Burch
# and Craig Christensen, National Renewable Energy Laboratory
# ambient temperatures( in °C)
# cold water temperatures( in °C)
ambient_temperature_fahrenheit = []
average_temperature = 0
maximum_temperature = -1000
minimum_temperature = 1000
for temperature in ambient_temperature:
value = temperature * 9 / 5 + 32
ambient_temperature_fahrenheit.append(value)
average_temperature += value / 8760
if value > maximum_temperature:
maximum_temperature = value
if value < minimum_temperature:
minimum_temperature = value
delta_temperature = maximum_temperature - minimum_temperature
ratio = 0.4 + 0.01 * (average_temperature - 44)
lag = 35 - 1 * (average_temperature - 44)
cold_temperature = []
for temperature in ambient_temperature_fahrenheit:
radians = (0.986 * (temperature-15-lag) - 90) * math.pi / 180
cold_temperature.append((average_temperature + 6 + ratio * (delta_temperature/2) * math.sin(radians) - 32) * 5/9)
return pd.DataFrame(cold_temperature, columns=['epw'])
def get_monthly_mean_values(self, values):
out = None
if values is not None:
@ -41,7 +73,8 @@ class Weather:
del out['month']
return out
def get_yearly_mean_values(self, values):
@staticmethod
def get_yearly_mean_values(values):
return values.mean()
def get_total_month(self, values):

View File

@ -7,9 +7,11 @@ Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
from pathlib import Path
from hub.imports.weather.xls_weather_parameters import XlsWeatherParameters
from hub.imports.weather.epw_weather_parameters import EpwWeatherParameters
from hub.hub_logger import logger
from hub.hub_logger import get_logger
from hub.helpers.utils import validate_import_export_type
logger = get_logger()
class WeatherFactory:
"""

View File

@ -59,7 +59,6 @@ class CityLayerTest(TestCase):
return gdf, target_buildings, adjacent_buildings
def _genidf(self, bldgs_group):
t0 = time.time()
buildings_df, target_buildings, adjacent_buildings = self._prepare_buildings(bldgs_group)
output_path = (Path(__file__).parent / 'tests_outputs').resolve()
city = GeometryFactory('gpandas', data_frame=buildings_df).city
@ -70,7 +69,6 @@ class CityLayerTest(TestCase):
filepath = os.path.join(output_path, city.name + ".idf")
newfilepath = filepath[:-4] + "_" + uuid.uuid4().hex[:10] + ".idf"
os.rename(filepath, newfilepath)
print(f"It took {round((time.time() - t0), 0)} seconds")
return newfilepath
def test_city_layers(self):

View File

@ -63,7 +63,7 @@ class TestCityMerge(TestCase):
ResultFactory('sra', city_two, self._output_path).enrich()
merged_city = city_one.merge(city_two)
self.assertEqual(len(merged_city.buildings), 2)
self.assertEqual(merged_city.buildings[1].surfaces[0].global_irradiance['year'].iloc[0], 254.3453196347032)
self.assertEqual(round(merged_city.buildings[1].surfaces[0].global_irradiance['year'].iloc[0]), 254)
self.assertEqual(merged_city.buildings[0].surfaces[0].global_irradiance, {})
self.assertEqual(merged_city.buildings[0].surfaces[2].global_irradiance, {})
self.assertEqual(city_one.buildings[0].surfaces[0].global_irradiance, merged_city.buildings[0].surfaces[0].global_irradiance)

View File

@ -17,7 +17,7 @@ class TestCostsCatalog(TestCase):
catalog_categories = catalog.names()
self.assertIsNotNone(catalog, 'catalog is none')
content = catalog.entries()
self.assertTrue(len(content.archetypes) == 1)
self.assertTrue(len(content.archetypes) == 2)
# retrieving all the entries should not raise any exceptions
for category in catalog_categories:

View File

@ -4,7 +4,11 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Peter Yefi peteryefi@gmail.com
"""
import unittest
from unittest import TestCase
import sqlalchemy.exc
from hub.imports.geometry_factory import GeometryFactory
from hub.imports.db_factory import DBFactory
from hub.imports.user_factory import UserFactory
@ -16,6 +20,40 @@ from hub.persistence.models import User, UserRoles
from sqlalchemy.exc import ProgrammingError
import uuid
class Skip:
_value = False
_message = 'PostgreSQL not properly installed in host machine'
def __init__(self):
# Create test database
env = '/usr/local/etc/hub/.env'
repo = Repository(db_name='test_db', app_env='TEST', dotenv_path=env)
eng = create_engine(f'postgresql://{repo.configuration.get_db_user()}@/{repo.configuration.get_db_user()}')
try:
# delete test database if it exists
conn = eng.connect()
conn.execute('commit')
conn.execute('DROP DATABASE test_db')
conn.close()
except ProgrammingError as err:
print(f'Database does not exist. Nothing to delete')
except sqlalchemy.exc.OperationalError:
self._value = True
@property
def value(self):
return self._value
@property
def message(self):
return self._message
@value.setter
def value(self, skip_value):
self._value = skip_value
skip = Skip()
class TestDBFactory(TestCase):
"""
@ -32,7 +70,6 @@ class TestDBFactory(TestCase):
env = '/usr/local/etc/hub/.env'
repo = Repository(db_name='test_db', app_env='TEST', dotenv_path=env)
eng = create_engine(f'postgresql://{repo.configuration.get_db_user()}@/{repo.configuration.get_db_user()}')
try:
# delete test database if it exists
conn = eng.connect()
@ -41,7 +78,9 @@ class TestDBFactory(TestCase):
conn.close()
except ProgrammingError as err:
print(f'Database does not exist. Nothing to delete')
except sqlalchemy.exc.OperationalError:
skip.value = True
return
cnn = eng.connect()
cnn.execute('commit')
cnn.execute("CREATE DATABASE test_db")
@ -62,11 +101,13 @@ class TestDBFactory(TestCase):
cls._user = user_factory.create_user("Admin", cls.application.id, "Admin@123", UserRoles.Admin)
cls.pickle_path = 'tests_data/pickle_path.bz2'
@unittest.skipIf(skip.value, skip.message)
def test_save_application(self):
self.assertEqual(self.application.name, "test")
self.assertEqual(self.application.description, "test application")
self.assertEqual(str(self.application.application_uuid), self.unique_id)
@unittest.skipIf(skip.value, skip.message)
def test_save_city(self):
self.city.name = "Montréal"
saved_city = self._db_factory.persist_city(self.city, self.pickle_path, self.application.id, self._user.id)
@ -75,6 +116,7 @@ class TestDBFactory(TestCase):
self.assertEqual(saved_city.level_of_detail, self.city.level_of_detail.geometry)
self._db_factory.delete_city(saved_city.id)
@unittest.skipIf(skip.value, skip.message)
def test_get_city_by_name(self):
city = self._db_factory.persist_city(self.city, self.pickle_path, self.application.id, self._user.id)
retrieved_city = self._export_db_factory.get_city_by_name(city.name)
@ -82,18 +124,21 @@ class TestDBFactory(TestCase):
self.assertEqual(retrieved_city[0].user_id, self._user.id)
self._db_factory.delete_city(city.id)
@unittest.skipIf(skip.value, skip.message)
def test_get_city_by_user(self):
city = self._db_factory.persist_city(self.city, self.pickle_path, self.application.id, self._user.id)
retrieved_city = self._export_db_factory.get_city_by_user(self._user.id)
self.assertEqual(retrieved_city[0].pickle_path, self.pickle_path)
self._db_factory.delete_city(city.id)
@unittest.skipIf(skip.value, skip.message)
def test_get_city_by_id(self):
city = self._db_factory.persist_city(self.city, self.pickle_path, self.application.id, self._user.id)
retrieved_city = self._export_db_factory.get_city(city.id)
self.assertEqual(retrieved_city.level_of_detail, self.city.level_of_detail.geometry)
self._db_factory.delete_city(city.id)
@unittest.skipIf(skip.value, skip.message)
def test_get_update_city(self):
city = self._db_factory.persist_city(self.city, self.pickle_path, self.application.id, self._user.id)
self.city.name = "Ottawa"

View File

@ -82,9 +82,3 @@ class TestEnergySystemsFactory(TestCase):
df = pd.read_csv(self._output_path)
self.assertEqual(df.shape, (13, 3))
self.assertEqual(df.iloc[0, 1], 1031544.62)
def tearDown(self) -> None:
try:
os.remove(self._output_path)
except OSError:
pass

View File

@ -71,8 +71,6 @@ class TestExports(TestCase):
self._complete_city = self._get_complete_city(from_pickle)
EnergyBuildingsExportsFactory(export_type, self._complete_city, self._output_path).export()
def test_obj_export(self):
"""
export to obj
@ -102,17 +100,18 @@ class TestExports(TestCase):
"""
export to IDF
"""
city = self._get_citygml('EV_GM_MB_LoD2.gml')
for building in city.buildings:
building.year_of_construction = 2006
if building.function is None:
building.function = 'large office'
ConstructionFactory('nrel', city).enrich()
UsageFactory('comnet', city).enrich()
file = 'FZK_Haus_LoD_2.gml'
file_path = (self._example_path / file).resolve()
city = GeometryFactory('citygml',
path=file_path,
function_to_hub=Dictionaries().alkis_function_to_hub_function).city
self.assertIsNotNone(city, 'city is none')
EnergyBuildingsExportsFactory('idf', city, self._output_path).export()
ConstructionFactory('nrcan', city).enrich()
EnergyBuildingsExportsFactory('idf', city, self._output_path).export()
UsageFactory('nrcan', city).enrich()
try:
EnergyBuildingsExportsFactory('idf', city, self._output_path).export()
EnergyBuildingsExportsFactory('idf', city, self._output_path,
target_buildings=['gml_1066158', 'gml_1066159']).export()
except Exception:
self.fail("Idf ExportsFactory raised ExceptionType unexpectedly!")

View File

@ -4,12 +4,12 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
from pathlib import Path
from unittest import TestCase
from hub.helpers.geometry_helper import GeometryHelper
import hub.exports.exports_factory
from hub.helpers.dictionaries import MontrealFunctionToHubFunction
from hub.helpers.geometry_helper import GeometryHelper
from hub.imports.construction_factory import ConstructionFactory
from hub.imports.geometry_factory import GeometryFactory
@ -19,6 +19,7 @@ class TestGeometryFactory(TestCase):
Non-functional TestGeometryFactory
Load testing
"""
def setUp(self) -> None:
"""
Test setup
@ -34,7 +35,8 @@ class TestGeometryFactory(TestCase):
path=file_path,
height_field=height_field,
year_of_construction_field=year_of_construction_field,
function_field=function_field).city
function_field=function_field,
).city
self.assertIsNotNone(self._city, 'city is none')
return self._city
@ -116,7 +118,6 @@ class TestGeometryFactory(TestCase):
city = self._get_city(file, 'rhino')
self.assertIsNotNone(city, 'city is none')
self.assertTrue(len(city.buildings) == 36)
i = 0
def test_import_obj(self):
"""
@ -133,18 +134,15 @@ class TestGeometryFactory(TestCase):
"""
Test geojson import
"""
file = 'neighbours.geojson'
city = self._get_city(file, 'geojson',
height_field='citygml_me',
file = '2000_buildings.geojson'
city = GeometryFactory('geojson',
path=(self._example_path / file).resolve(),
height_field='building_height',
year_of_construction_field='ANNEE_CONS',
function_field='CODE_UTILI')
hub.exports.exports_factory.ExportsFactory('obj', city, self._output_path).export()
self.assertEqual(207, len(city.buildings), 'wrong number of buildings')
self._check_buildings(city)
for building in city.buildings:
for wall in building.walls:
self.assertIsNotNone(wall.percentage_shared, 'wall percentage shared is not assigned')
function_field='CODE_UTILI',
function_to_hub=MontrealFunctionToHubFunction().dictionary).city
# include 25 square meter condition for a building reduces buildings number from 2289 to 2057
self.assertEqual(2057, len(city.buildings), 'wrong number of buildings')
def test_map_neighbours(self):
"""
@ -155,14 +153,20 @@ class TestGeometryFactory(TestCase):
height_field='citygml_me',
year_of_construction_field='ANNEE_CONS',
function_field='LIBELLE_UT')
print(GeometryHelper.city_mapping(city))
info_lod1 = GeometryHelper.city_mapping(city, plot=False)
city = self._get_city(file, 'geojson',
year_of_construction_field='ANNEE_CONS',
function_field='LIBELLE_UT')
info_lod0 = GeometryHelper.city_mapping(city, plot=False)
hub.exports.exports_factory.ExportsFactory('obj', city, self._output_path).export()
self.assertEqual(info_lod0, info_lod1)
for building in city.buildings:
self.assertEqual(2, len(building.neighbours))
self.assertEqual('2_part_0_zone_0',city.city_object('1_part_0_zone_0').neighbours[0].name)
self.assertEqual('3_part_0_zone_0',city.city_object('1_part_0_zone_0').neighbours[1].name)
self.assertEqual('1_part_0_zone_0',city.city_object('2_part_0_zone_0').neighbours[0].name)
self.assertEqual('3_part_0_zone_0',city.city_object('2_part_0_zone_0').neighbours[1].name)
self.assertEqual('2_part_0_zone_0', city.city_object('1_part_0_zone_0').neighbours[0].name)
self.assertEqual('3_part_0_zone_0', city.city_object('1_part_0_zone_0').neighbours[1].name)
self.assertEqual('1_part_0_zone_0', city.city_object('2_part_0_zone_0').neighbours[0].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('2_part_0_zone_0', city.city_object('3_part_0_zone_0').neighbours[1].name)

View File

@ -51,6 +51,7 @@ class TestUsageFactory(TestCase):
self.assertIsNotNone(building.walls, 'building walls is none')
self.assertIsNotNone(building.roofs, 'building roofs is none')
for internal_zone in building.internal_zones:
if internal_zone.usages is not None:
self.assertTrue(len(internal_zone.usages) > 0, 'usage zones are not defined')
self.assertIsNone(internal_zone.thermal_zones, 'thermal zones are defined')
self.assertIsNone(building.basement_heated, 'building basement_heated is not none')
@ -66,7 +67,6 @@ class TestUsageFactory(TestCase):
self.assertIsNotNone(building.roof_type, 'building roof type is none')
self.assertIsNotNone(building.floor_area, 'building floor_area is none')
self.assertIsNone(building.households, 'building households is not none')
self.assertTrue(building.is_conditioned, 'building is not conditioned')
def _check_usage(self, usage):
self.assertIsNotNone(usage.name, 'usage is none')
@ -128,3 +128,59 @@ class TestUsageFactory(TestCase):
self.assertIsNotNone(usage.domestic_hot_water.service_temperature,
'domestic hot water service temperature is none')
self.assertIsNotNone(usage.domestic_hot_water.schedules, 'domestic hot water schedules is none')
def test_import_nrcan(self):
"""
Enrich the city with the usage information from nrcan and verify it
"""
file = 'selected_building.geojson'
file_path = (self._example_path / file).resolve()
city = GeometryFactory('geojson',
path=file_path,
height_field='building_height',
year_of_construction_field='ANNEE_CONS',
function_field='CODE_UTILI',
function_to_hub=Dictionaries().montreal_function_to_hub_function).city
UsageFactory('nrcan', city).enrich()
self._check_buildings(city)
for building in city.buildings:
for internal_zone in building.internal_zones:
if internal_zone.usages is not None:
self.assertIsNot(len(internal_zone.usages), 0, 'no building usage defined')
for usage in internal_zone.usages:
self._check_usage(usage)
self.assertIsNotNone(usage.mechanical_air_change, 'mechanical air change is none')
self.assertIsNotNone(usage.thermal_control.heating_set_point_schedules,
'control heating set point schedule is none')
self.assertIsNotNone(usage.thermal_control.cooling_set_point_schedules,
'control cooling set point schedule is none')
self.assertIsNotNone(usage.occupancy, 'occupancy is none')
occupancy = usage.occupancy
self.assertIsNotNone(occupancy.occupancy_density, 'occupancy density is none')
self.assertIsNotNone(occupancy.latent_internal_gain, 'occupancy latent internal gain is none')
self.assertIsNotNone(occupancy.sensible_convective_internal_gain,
'occupancy sensible convective internal gain is none')
self.assertIsNotNone(occupancy.sensible_radiative_internal_gain,
'occupancy sensible radiant internal gain is none')
self.assertIsNotNone(occupancy.occupancy_schedules, 'occupancy schedule is none')
self.assertIsNotNone(usage.lighting, 'lighting is none')
lighting = usage.lighting
self.assertIsNotNone(lighting.density, 'lighting density is none')
self.assertIsNotNone(lighting.latent_fraction, 'lighting latent fraction is none')
self.assertIsNotNone(lighting.convective_fraction, 'lighting convective fraction is none')
self.assertIsNotNone(lighting.radiative_fraction, 'lighting radiant fraction is none')
self.assertIsNotNone(lighting.schedules, 'lighting schedule is none')
self.assertIsNotNone(usage.appliances, 'appliances is none')
appliances = usage.appliances
self.assertIsNotNone(appliances.density, 'appliances density is none')
self.assertIsNotNone(appliances.latent_fraction, 'appliances latent fraction is none')
self.assertIsNotNone(appliances.convective_fraction, 'appliances convective fraction is none')
self.assertIsNotNone(appliances.radiative_fraction, 'appliances radiant fraction is none')
self.assertIsNotNone(appliances.schedules, 'appliances schedule is none')
self.assertIsNotNone(usage.thermal_control.hvac_availability_schedules,
'control hvac availability is none')
self.assertIsNotNone(usage.domestic_hot_water.peak_flow, 'domestic hot water peak flow is none')
self.assertIsNotNone(usage.domestic_hot_water.service_temperature,
'domestic hot water service temperature is none')
self.assertIsNotNone(usage.domestic_hot_water.schedules, 'domestic hot water schedules is none')

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -12,18 +12,18 @@
-73.580414175680588,
45.497641136608358
],
[
-73.581414175680588,
45.497641136608358
],
[
-73.581414175680588,
45.498641136608358
],
[
-73.580414175680588,
45.498641136608358
],
[
-73.581414175680588,
45.498641136608358
],
[
-73.581414175680588,
45.497641136608358
],
[
-73.580414175680588,
45.497641136608358
@ -204,19 +204,20 @@
[
-73.581414175680588,
45.497641136608358
]
,
[
-73.581414175680588,
45.498441136608358
],
[
-73.582214175680588,
45.498441136608358
],
[
-73.582214175680588,
45.497641136608358
],
[
-73.582214175680588,
45.498441136608358
],
[
-73.581414175680588,
45.498441136608358
],
[
-73.581414175680588,
45.497641136608358
@ -399,31 +400,30 @@
-73.581914175680588,
45.498441136608358
],
[
-73.581914175680588,
45.499641136608358
],
[
-73.580914175680588,
45.499641136608358
],
[
-73.580914175680588,
45.498641136608358
],
[
-73.581414175680588,
45.498641136608358
],
[
-73.581414175680588,
45.498441136608358
],
[
-73.581414175680588,
45.498641136608358
],
[
-73.580914175680588,
45.498641136608358
],
[
-73.580914175680588,
45.499641136608358
],
[
-73.581914175680588,
45.499641136608358
],
[
-73.581914175680588,
45.498441136608358
]
]
]
},

View File

@ -0,0 +1,14 @@
,Monthly HP Electricity Demand (kWh),Monthly Fuel Consumption of Auxiliary Heater (m3)
Jan,1031544.62,24276356.0
Feb,874352.562,19785768.0
Mar,691775.25,117312.656
Apr,280416.469,-0.0
May,0.0,40314676.0
Jun,0.0,5447721.0
Jul,0.0,1187115.88
Aug,0.0,1961530.88
Sept,0.0,20623850.0
Oct,191220.531,-0.0
Nov,423974.062,-0.0
Dec,848334.875,6793204.5
Total,4341618.369,120507534.91600001
1 Monthly HP Electricity Demand (kWh) Monthly Fuel Consumption of Auxiliary Heater (m3)
2 Jan 1031544.62 24276356.0
3 Feb 874352.562 19785768.0
4 Mar 691775.25 117312.656
5 Apr 280416.469 -0.0
6 May 0.0 40314676.0
7 Jun 0.0 5447721.0
8 Jul 0.0 1187115.88
9 Aug 0.0 1961530.88
10 Sept 0.0 20623850.0
11 Oct 191220.531 -0.0
12 Nov 423974.062 -0.0
13 Dec 848334.875 6793204.5
14 Total 4341618.369 120507534.91600001

View File

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

View File

@ -23,3 +23,5 @@ shapely
geopandas
triangle
psycopg2-binary
Pillow
pathlib

View File

@ -5,13 +5,12 @@ from distutils.util import convert_path
import pkg_resources
from setuptools import setup
with pathlib.Path('hub/requirements.txt').open() as r:
with pathlib.Path('requirements.txt').open() as r:
install_requires = [
str(requirement)
for requirement
in pkg_resources.parse_requirements(r)
]
install_requires.append('setuptools')
main_ns = {}
@ -81,8 +80,9 @@ setup(
'hub.imports'
],
setup_requires=install_requires,
install_requires=install_requires,
data_files=[
('hub', glob.glob('hub/requirements.txt')),
('hub', glob.glob('requirements.txt')),
('hub/config', glob.glob('hub/config/*.ini')),
('hub/catalog_factories/greenery/ecore_greenery', glob.glob('hub/catalog_factories/greenery/ecore_greenery/*.ecore')),
('hub/data/construction.', glob.glob('hub/data/construction/*')),