diff --git a/hub/catalog_factories/catalog.py b/hub/catalog_factories/catalog.py
index 96b61726..a7e3cd85 100644
--- a/hub/catalog_factories/catalog.py
+++ b/hub/catalog_factories/catalog.py
@@ -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
diff --git a/hub/catalog_factories/cost/montreal_custom_catalog.py b/hub/catalog_factories/cost/montreal_custom_catalog.py
index d4cf0c10..a0679b89 100644
--- a/hub/catalog_factories/cost/montreal_custom_catalog.py
+++ b/hub/catalog_factories/cost/montreal_custom_catalog.py
@@ -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,93 @@ 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
+ items_list = []
+ item_type = 'B10_superstructure'
+ item_description = self._item_with_refurbishment_values(entry, item_type)
+ items_list.append(item_description)
+ for item in entry['B20_envelope']:
+ item_type = item
+ item_description = self._item_with_refurbishment_values(entry['B20_envelope'], item_type)
+ items_list.append(item_description)
+ item_type = 'B30_roofing'
+ item_description = self._item_with_refurbishment_values(entry, 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'
+ item_description = self._item_with_threesome(entry['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(entry['D30_hvac'], item_type)
+ items_list.append(item_description)
+ item_type = 'D5020lighting_and_branch_wiring'
+ item_description = self._item_with_threesome(entry['D50_electrical'], item_type)
+ items_list.append(item_description)
+ general_chapters.append(Chapter('D_services', items_list))
+
+ design_allowance = float(entry['Z_allowances_overhead_profit']['Z10_design_allowance']['#text']) / 100
+ overhead_and_profit = float(entry['Z_allowances_overhead_profit']['Z10_overhead_and_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_type = item['fuel']['@fuel_type']
+ fuel_variable = float(entry['fuel']['variable']['#text'])
+ fuel_variable_units = float(entry['fuel']['variable']['@cost_unit'])
+ fuel_fixed_monthly = None
+ fuel_fixed_peak = None
+ if fuel_type == 'electricity':
+ fuel_fixed_monthly = float(entry['fuel']['fixed']['fixed_monthly']['#text'])
+ fuel_fixed_peak = float(entry['fuel']['fixed']['fixed_power']['#text']) / 1000
+ elif fuel_type == 'gas':
+ fuel_fixed_monthly = float(entry['fuel']['fixed']['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,
+ _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,7 +124,10 @@ 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']
+ print(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'])
@@ -129,17 +135,21 @@ class MontrealCustomCatalog(Catalog):
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,
+ 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):
diff --git a/hub/catalog_factories/data_models/cost/archetype.py b/hub/catalog_factories/data_models/cost/archetype.py
index 69ba2654..cf24c6ee 100644
--- a/hub/catalog_factories/data_models/cost/archetype.py
+++ b/hub/catalog_factories/data_models/cost/archetype.py
@@ -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):
"""
diff --git a/hub/catalog_factories/data_models/cost/capital_cost.py b/hub/catalog_factories/data_models/cost/capital_cost.py
index 6d5f2504..828b5cfc 100644
--- a/hub/catalog_factories/data_models/cost/capital_cost.py
+++ b/hub/catalog_factories/data_models/cost/capital_cost.py
@@ -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
diff --git a/hub/catalog_factories/data_models/cost/chapter.py b/hub/catalog_factories/data_models/cost/chapter.py
new file mode 100644
index 00000000..bf393cbb
--- /dev/null
+++ b/hub/catalog_factories/data_models/cost/chapter.py
@@ -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
diff --git a/hub/catalog_factories/data_models/cost/cost_helper.py b/hub/catalog_factories/data_models/cost/cost_helper.py
new file mode 100644
index 00000000..fb0d551e
--- /dev/null
+++ b/hub/catalog_factories/data_models/cost/cost_helper.py
@@ -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
diff --git a/hub/catalog_factories/data_models/cost/envelope.py b/hub/catalog_factories/data_models/cost/envelope.py
deleted file mode 100644
index c2d2d419..00000000
--- a/hub/catalog_factories/data_models/cost/envelope.py
+++ /dev/null
@@ -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
diff --git a/hub/catalog_factories/data_models/cost/fuel.py b/hub/catalog_factories/data_models/cost/fuel.py
new file mode 100644
index 00000000..82b47477
--- /dev/null
+++ b/hub/catalog_factories/data_models/cost/fuel.py
@@ -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
diff --git a/hub/catalog_factories/data_models/cost/hvac.py b/hub/catalog_factories/data_models/cost/hvac.py
deleted file mode 100644
index 01b6c7b7..00000000
--- a/hub/catalog_factories/data_models/cost/hvac.py
+++ /dev/null
@@ -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
diff --git a/hub/catalog_factories/data_models/cost/income.py b/hub/catalog_factories/data_models/cost/income.py
index 9bc975f2..0a8c0442 100644
--- a/hub/catalog_factories/data_models/cost/income.py
+++ b/hub/catalog_factories/data_models/cost/income.py
@@ -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
diff --git a/hub/catalog_factories/data_models/cost/item_description.py b/hub/catalog_factories/data_models/cost/item_description.py
new file mode 100644
index 00000000..45b24bbb
--- /dev/null
+++ b/hub/catalog_factories/data_models/cost/item_description.py
@@ -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
diff --git a/hub/catalog_factories/data_models/cost/operational_cost.py b/hub/catalog_factories/data_models/cost/operational_cost.py
index b1a7b3aa..f7cbcc6f 100644
--- a/hub/catalog_factories/data_models/cost/operational_cost.py
+++ b/hub/catalog_factories/data_models/cost/operational_cost.py
@@ -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
diff --git a/hub/catalog_factories/data_models/cost/systems.py b/hub/catalog_factories/data_models/cost/systems.py
deleted file mode 100644
index 7f2dc34e..00000000
--- a/hub/catalog_factories/data_models/cost/systems.py
+++ /dev/null
@@ -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
diff --git a/hub/catalog_factories/data_models/usages/domestic_hot_water.py b/hub/catalog_factories/data_models/usages/domestic_hot_water.py
new file mode 100644
index 00000000..a34a4c65
--- /dev/null
+++ b/hub/catalog_factories/data_models/usages/domestic_hot_water.py
@@ -0,0 +1,54 @@
+"""
+Usage catalog domestic hot water
+SPDX - License - Identifier: LGPL - 3.0 - or -later
+Copyright © 2023 Concordia CERC group
+Project Coder Pilar Monsalvete Álvarez de Uribarri pilar.monsalvete@concordia.ca
+"""
+
+from typing import Union, List
+
+from hub.catalog_factories.data_models.usages.schedule import Schedule
+
+
+class DomesticHotWater:
+ """
+ DomesticHotWater class
+ """
+ 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
+
+ @property
+ def density(self) -> Union[None, float]:
+ """
+ Get domestic hot water load density in Watts per m2
+ :return: None or float
+ """
+ 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]:
+ """
+ Get service temperature in degrees Celsius
+ :return: None or float
+ """
+ return self._service_temperature
+
+ @property
+ def schedules(self) -> Union[None, List[Schedule]]:
+ """
+ Get schedules
+ dataType = fraction of loads
+ :return: None or [Schedule]
+ """
+ return self._schedules
diff --git a/hub/catalog_factories/data_models/usages/usage.py b/hub/catalog_factories/data_models/usages/usage.py
index 95868b21..07ac17bc 100644
--- a/hub/catalog_factories/data_models/usages/usage.py
+++ b/hub/catalog_factories/data_models/usages/usage.py
@@ -10,6 +10,7 @@ from hub.catalog_factories.data_models.usages.appliances import Appliances
from hub.catalog_factories.data_models.usages.lighting import Lighting
from hub.catalog_factories.data_models.usages.ocupancy import Occupancy
from hub.catalog_factories.data_models.usages.thermal_control import ThermalControl
+from hub.catalog_factories.data_models.usages.domestic_hot_water import DomesticHotWater
class Usage:
@@ -21,7 +22,8 @@ class Usage:
occupancy,
lighting,
appliances,
- thermal_control):
+ thermal_control,
+ domestic_hot_water):
self._name = name
self._hours_day = hours_day
self._days_year = days_year
@@ -32,6 +34,7 @@ class Usage:
self._lighting = lighting
self._appliances = appliances
self._thermal_control = thermal_control
+ self._domestic_hot_water = domestic_hot_water
@property
def name(self) -> Union[None, str]:
@@ -108,7 +111,15 @@ class Usage:
@property
def thermal_control(self) -> Union[None, ThermalControl]:
"""
- Get thermal control of this thermal zone
+ Get thermal control information
:return: None or ThermalControl
"""
return self._thermal_control
+
+ @property
+ def domestic_hot_water(self) -> Union[None, DomesticHotWater]:
+ """
+ Get domestic hot water information
+ :return: None or DomesticHotWater
+ """
+ return self._domestic_hot_water
diff --git a/hub/catalog_factories/energy_systems/nrcan_catalog.py b/hub/catalog_factories/energy_systems/nrcan_catalog.py
new file mode 100644
index 00000000..baf8a2c2
--- /dev/null
+++ b/hub/catalog_factories/energy_systems/nrcan_catalog.py
@@ -0,0 +1,189 @@
+"""
+NRCAN energy systems catalog
+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 json
+import urllib.request
+import xmltodict
+
+import hub.helpers.constants as cte
+from hub.catalog_factories.catalog import Catalog
+from hub.catalog_factories.data_models.usages.appliances import Appliances
+from hub.catalog_factories.data_models.usages.content import Content
+from hub.catalog_factories.data_models.usages.lighting import Lighting
+from hub.catalog_factories.data_models.usages.ocupancy import Occupancy
+from hub.catalog_factories.data_models.usages.schedule import Schedule
+from hub.catalog_factories.data_models.usages.thermal_control import ThermalControl
+from hub.catalog_factories.data_models.usages.usage import Usage
+from hub.catalog_factories.usage.usage_helper import UsageHelper
+
+
+class NrcanCatalog(Catalog):
+ def __init__(self, path):
+ path = str(path / 'nrcan.xml')
+ self._content = None
+ self._schedules = {}
+ with open(path) as xml:
+ self._metadata = xmltodict.parse(xml.read())
+ self._base_url = self._metadata['nrcan']['@base_url']
+ self._load_schedules()
+ self._content = Content(self._load_archetypes())
+
+ 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:
+ 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']
+ usage_type = space_type['building_type']
+ occupancy_schedule_name = space_type['occupancy_schedule']
+ lighting_schedule_name = space_type['lighting_schedule']
+ appliance_schedule_name = space_type['electric_equipment_schedule']
+ hvac_schedule_name = space_type['exhaust_schedule']
+ if 'FAN' in hvac_schedule_name:
+ hvac_schedule_name = hvac_schedule_name.replace('FAN', 'Fan')
+ heating_setpoint_schedule_name = space_type['heating_setpoint_schedule']
+ cooling_setpoint_schedule_name = space_type['cooling_setpoint_schedule']
+ occupancy_schedule = self._get_schedules(occupancy_schedule_name)
+ lighting_schedule = self._get_schedules(lighting_schedule_name)
+ appliance_schedule = self._get_schedules(appliance_schedule_name)
+ heating_schedule = self._get_schedules(heating_setpoint_schedule_name)
+ cooling_schedule = self._get_schedules(cooling_setpoint_schedule_name)
+ hvac_availability = self._get_schedules(hvac_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)
+
+ # 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
+
+ occupancy = Occupancy(occupancy_density,
+ None,
+ None,
+ None,
+ occupancy_schedule)
+ lighting = Lighting(lighting_density,
+ lighting_convective_fraction,
+ lighting_radiative_fraction,
+ lighting_latent_fraction,
+ lighting_schedule)
+ appliances = Appliances(appliances_density,
+ appliances_convective_fraction,
+ appliances_radiative_fraction,
+ appliances_latent_fraction,
+ appliance_schedule)
+ thermal_control = ThermalControl(None,
+ None,
+ None,
+ hvac_availability,
+ heating_schedule,
+ cooling_schedule)
+ hours_day = None
+ days_year = None
+ usages.append(Usage(usage_type,
+ hours_day,
+ days_year,
+ mechanical_air_change,
+ ventilation_rate,
+ occupancy,
+ lighting,
+ appliances,
+ thermal_control))
+ return usages
+
+ def names(self, category=None):
+ """
+ Get the catalog elements names
+ :parm: optional category filter
+ """
+ if category is None:
+ _names = {'archetypes': [], 'constructions': [], 'materials': [], 'windows': []}
+ for archetype in self._content.archetypes:
+ _names['archetypes'].append(archetype.name)
+ for construction in self._content.constructions:
+ _names['constructions'].append(construction.name)
+ for material in self._content.materials:
+ _names['materials'].append(material.name)
+ for window in self._content.windows:
+ _names['windows'].append(window.name)
+ else:
+ _names = {category: []}
+ if category.lower() == 'archetypes':
+ for archetype in self._content.archetypes:
+ _names[category].append(archetype.name)
+ elif category.lower() == 'constructions':
+ for construction in self._content.constructions:
+ _names[category].append(construction.name)
+ elif category.lower() == 'materials':
+ for material in self._content.materials:
+ _names[category].append(material.name)
+ elif category.lower() == 'windows':
+ for window in self._content.windows:
+ _names[category].append(window.name)
+ else:
+ raise ValueError(f'Unknown category [{category}]')
+
+ def entries(self, category=None):
+ """
+ Get the catalog elements
+ :parm: optional category filter
+ """
+ if category is None:
+ return self._content
+ else:
+ if category.lower() == 'archetypes':
+ return self._content.archetypes
+ elif category.lower() == 'constructions':
+ return self._content.constructions
+ elif category.lower() == 'materials':
+ return self._content.materials
+ elif category.lower() == 'windows':
+ return self._content.windows
+ else:
+ raise ValueError(f'Unknown category [{category}]')
+
+ def get_entry(self, name):
+ """
+ Get one catalog element by names
+ :parm: entry name
+ """
+ for entry in self._content.archetypes:
+ if entry.name.lower() == name.lower():
+ return entry
+ for entry in self._content.constructions:
+ if entry.name.lower() == name.lower():
+ return entry
+ for entry in self._content.materials:
+ if entry.name.lower() == name.lower():
+ return entry
+ for entry in self._content.windows:
+ if entry.name.lower() == name.lower():
+ return entry
+ raise IndexError(f"{name} doesn't exists in the catalog")
diff --git a/hub/catalog_factories/energy_systems_catalog_factory.py b/hub/catalog_factories/energy_systems_catalog_factory.py
new file mode 100644
index 00000000..0bfc9459
--- /dev/null
+++ b/hub/catalog_factories/energy_systems_catalog_factory.py
@@ -0,0 +1,42 @@
+"""
+Usage catalog factory, publish the usage information
+SPDX - License - Identifier: LGPL - 3.0 - or -later
+Copyright © 2022 Concordia CERC group
+Project Coder Pilar Monsalvete Álvarez de Uribarri pilar.monsalvete@concordia.ca
+"""
+
+from pathlib import Path
+from typing import TypeVar
+from hub.catalog_factories.energy_systems.nrcan_catalog import NrcanCatalog
+from hub.hub_logger import logger
+from hub.helpers.utils import validate_import_export_type
+Catalog = TypeVar('Catalog')
+
+
+class UsageCatalogFactory:
+ def __init__(self, file_type, base_path=None):
+ if base_path is None:
+ base_path = Path(Path(__file__).parent.parent / 'data/energy_systems')
+ self._catalog_type = '_' + file_type.lower()
+ class_funcs = validate_import_export_type(UsageCatalogFactory)
+ if self._catalog_type not in class_funcs:
+ err_msg = f"Wrong import type. Valid functions include {class_funcs}"
+ logger.error(err_msg)
+ raise Exception(err_msg)
+ self._path = base_path
+
+ @property
+ def _nrcan(self):
+ """
+ Retrieve NRCAN catalog
+ """
+ # nrcan retrieves the data directly from github
+ return NrcanCatalog(self._path)
+
+ @property
+ def catalog(self) -> Catalog:
+ """
+ Enrich the city given to the class using the class given handler
+ :return: Catalog
+ """
+ return getattr(self, self._catalog_type, lambda: None)
diff --git a/hub/catalog_factories/usage/comnet_catalog.py b/hub/catalog_factories/usage/comnet_catalog.py
index 90a54314..7c226722 100644
--- a/hub/catalog_factories/usage/comnet_catalog.py
+++ b/hub/catalog_factories/usage/comnet_catalog.py
@@ -14,6 +14,7 @@ from hub.catalog_factories.data_models.usages.appliances import Appliances
from hub.catalog_factories.data_models.usages.content import Content
from hub.catalog_factories.data_models.usages.lighting import Lighting
from hub.catalog_factories.data_models.usages.ocupancy import Occupancy
+from hub.catalog_factories.data_models.usages.domestic_hot_water import DomesticHotWater
from hub.catalog_factories.data_models.usages.schedule import Schedule
from hub.catalog_factories.data_models.usages.thermal_control import ThermalControl
from hub.catalog_factories.data_models.usages.usage import Usage
@@ -51,6 +52,7 @@ class ComnetCatalog(Catalog):
ventilation_rate = self._archetypes['ventilation rate'][comnet_usage]
# convert cfm/ft2 to m3/m2.s
ventilation_rate = ventilation_rate / (cte.METERS_TO_FEET * cte.MINUTES_TO_SECONDS)
+ domestic_hot_water_archetype = self._archetypes['water heating'][comnet_usage]
# get occupancy
occupancy_density = occupancy_archetype[0] / pow(cte.METERS_TO_FEET, 2)
@@ -96,6 +98,16 @@ class ComnetCatalog(Catalog):
self._schedules[schedule_name]['ClgSetPt']
)
+ # get domestic hot water
+ density = domestic_hot_water_archetype
+ # convert Btu/h/occ to W/m2
+ 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']
+ )
usages.append(Usage(comnet_usage,
hours_day,
days_year,
@@ -104,7 +116,8 @@ class ComnetCatalog(Catalog):
occupancy,
lighting,
appliances,
- thermal_control))
+ thermal_control,
+ domestic_hot_water))
self._content = Content(usages)
@@ -136,7 +149,7 @@ class ComnetCatalog(Catalog):
_schedule_values[day] = _extracted_data.iloc[start:end, 3:27].to_numpy().tolist()[0]
_schedule = []
for day in _schedule_values:
- if schedule_name == 'ClgSetPt' or schedule_name == 'HtgSetPt':
+ if schedule_name == 'ClgSetPt' or schedule_name == 'HtgSetPt' or schedule_name == 'WtrHtrSetPt':
# to celsius
if 'n.a.' in _schedule_values[day]:
_schedule_values[day] = None
diff --git a/hub/catalog_factories/usage/nrcan_catalog.py b/hub/catalog_factories/usage/nrcan_catalog.py
index 6e12b682..0f60dc68 100644
--- a/hub/catalog_factories/usage/nrcan_catalog.py
+++ b/hub/catalog_factories/usage/nrcan_catalog.py
@@ -16,6 +16,7 @@ from hub.catalog_factories.data_models.usages.appliances import Appliances
from hub.catalog_factories.data_models.usages.content import Content
from hub.catalog_factories.data_models.usages.lighting import Lighting
from hub.catalog_factories.data_models.usages.ocupancy import Occupancy
+from hub.catalog_factories.data_models.usages.domestic_hot_water import DomesticHotWater
from hub.catalog_factories.data_models.usages.schedule import Schedule
from hub.catalog_factories.data_models.usages.thermal_control import ThermalControl
from hub.catalog_factories.data_models.usages.usage import Usage
@@ -36,7 +37,7 @@ class NrcanCatalog(Catalog):
@staticmethod
def _extract_schedule(raw):
nrcan_schedule_type = raw['category']
- if 'Heating' in raw['name']:
+ if 'Heating' in raw['name'] and 'Water' not in raw['name']:
nrcan_schedule_type = f'{nrcan_schedule_type} Heating'
elif 'Cooling' in raw['name']:
nrcan_schedule_type = f'{nrcan_schedule_type} Cooling'
@@ -45,8 +46,8 @@ class NrcanCatalog(Catalog):
hub_type = UsageHelper().nrcan_schedule_type_to_hub_schedule_type[nrcan_schedule_type]
data_type = UsageHelper().nrcan_data_type_to_hub_data_type[raw['units']]
time_step = UsageHelper().nrcan_time_to_hub_time[raw['type']]
- # nrcan only uses yearly range for the schedules
- time_range = cte.YEAR
+ # nrcan only uses daily range for the schedules
+ time_range = cte.DAY
day_types = UsageHelper().nrcan_day_type_to_hub_days[raw['day_types']]
return Schedule(hub_type, raw['values'], data_type, time_step, time_range, day_types)
@@ -78,10 +79,8 @@ class NrcanCatalog(Catalog):
url = f'{self._base_url}{name["space_types_location"]}'
with urllib.request.urlopen(url) 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']
usage_type = space_type['building_type']
occupancy_schedule_name = space_type['occupancy_schedule']
lighting_schedule_name = space_type['lighting_schedule']
@@ -91,12 +90,14 @@ class NrcanCatalog(Catalog):
hvac_schedule_name = hvac_schedule_name.replace('FAN', 'Fan')
heating_setpoint_schedule_name = space_type['heating_setpoint_schedule']
cooling_setpoint_schedule_name = space_type['cooling_setpoint_schedule']
+ domestic_hot_water_schedule_name = space_type['service_water_heating_schedule']
occupancy_schedule = self._get_schedules(occupancy_schedule_name)
lighting_schedule = self._get_schedules(lighting_schedule_name)
appliance_schedule = self._get_schedules(appliance_schedule_name)
heating_schedule = self._get_schedules(heating_setpoint_schedule_name)
cooling_schedule = self._get_schedules(cooling_setpoint_schedule_name)
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']
@@ -124,6 +125,11 @@ class NrcanCatalog(Catalog):
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 gallons/h/ft2
+ domestic_hot_water_peak_flow = space_type['service_water_heating_peak_flow_per_area'] \
+ * cte.GALLONS_TO_QUBIC_METERS / cte.HOUR_TO_SECONDS * pow(cte.METERS_TO_FEET, 2)
+ domestic_hot_water_service_temperature = space_type['service_water_heating_target_temperature']
+
occupancy = Occupancy(occupancy_density,
None,
None,
@@ -145,6 +151,11 @@ class NrcanCatalog(Catalog):
hvac_availability,
heating_schedule,
cooling_schedule)
+ domestic_hot_water = DomesticHotWater(None,
+ domestic_hot_water_peak_flow,
+ domestic_hot_water_service_temperature,
+ domestic_hot_water_load_schedule)
+
hours_day = None
days_year = None
usages.append(Usage(usage_type,
@@ -155,7 +166,8 @@ class NrcanCatalog(Catalog):
occupancy,
lighting,
appliances,
- thermal_control))
+ thermal_control,
+ domestic_hot_water))
return usages
def names(self, category=None):
diff --git a/hub/catalog_factories/usage/usage_helper.py b/hub/catalog_factories/usage/usage_helper.py
index e2886bc9..57d58d84 100644
--- a/hub/catalog_factories/usage/usage_helper.py
+++ b/hub/catalog_factories/usage/usage_helper.py
@@ -19,7 +19,8 @@ class UsageHelper:
'Equipment': cte.APPLIANCES,
'Thermostat Setpoint Cooling': cte.COOLING_SET_POINT, # Compose 'Thermostat Setpoint' + 'Cooling'
'Thermostat Setpoint Heating': cte.HEATING_SET_POINT, # Compose 'Thermostat Setpoint' + 'Heating'
- 'Fan': cte.HVAC_AVAILABILITY
+ 'Fan': cte.HVAC_AVAILABILITY,
+ 'Service Water Heating': cte.DOMESTIC_HOT_WATER
}
_nrcan_data_type_to_hub_data_type = {
'FRACTION': cte.FRACTION,
diff --git a/hub/city_model_structure/building.py b/hub/city_model_structure/building.py
index d5129142..327cc0c7 100644
--- a/hub/city_model_structure/building.py
+++ b/hub/city_model_structure/building.py
@@ -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:
"""
diff --git a/hub/city_model_structure/building_demand/domestic_hot_water.py b/hub/city_model_structure/building_demand/domestic_hot_water.py
new file mode 100644
index 00000000..d91977e5
--- /dev/null
+++ b/hub/city_model_structure/building_demand/domestic_hot_water.py
@@ -0,0 +1,87 @@
+"""
+Domestic Hot Water module
+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, List
+from hub.city_model_structure.attributes.schedule import Schedule
+
+
+class DomesticHotWater:
+ """
+ DomesticHotWater class
+ """
+ def __init__(self):
+ self._density = None
+ self._peak_flow = None
+ self._service_temperature = None
+ self._schedules = None
+
+ @property
+ def density(self) -> Union[None, float]:
+ """
+ Get domestic hot water load density in Watts per m2
+ :return: None or float
+ """
+ return self._density
+
+ @density.setter
+ def density(self, value):
+ """
+ Set domestic hot water load density in Watts per m2
+ :param value: float
+ """
+ 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]:
+ """
+ Get service temperature in degrees Celsius
+ :return: None or float
+ """
+ return self._service_temperature
+
+ @service_temperature.setter
+ def service_temperature(self, value):
+ """
+ Set service temperature in degrees Celsius
+ :param value: float
+ """
+ if value is not None:
+ self._service_temperature = float(value)
+
+ @property
+ def schedules(self) -> Union[None, List[Schedule]]:
+ """
+ Get schedules
+ dataType = fraction
+ :return: None or [Schedule]
+ """
+ return self._schedules
+
+ @schedules.setter
+ def schedules(self, value):
+ """
+ Set schedules
+ dataType = fraction
+ :param value: [Schedule]
+ """
+ self._schedules = value
diff --git a/hub/city_model_structure/building_demand/thermal_zone.py b/hub/city_model_structure/building_demand/thermal_zone.py
index beb26045..65b40b29 100644
--- a/hub/city_model_structure/building_demand/thermal_zone.py
+++ b/hub/city_model_structure/building_demand/thermal_zone.py
@@ -15,6 +15,7 @@ from hub.city_model_structure.building_demand.appliances import Appliances
from hub.city_model_structure.building_demand.lighting import Lighting
from hub.city_model_structure.building_demand.internal_gain import InternalGain
from hub.city_model_structure.building_demand.thermal_control import ThermalControl
+from hub.city_model_structure.building_demand.domestic_hot_water import DomesticHotWater
from hub.city_model_structure.attributes.schedule import Schedule
import hub.helpers.constants as cte
@@ -53,6 +54,7 @@ class ThermalZone:
self._appliances = None
self._internal_gains = None
self._thermal_control = None
+ self._domestic_hot_water = None
self._usages = None
@property
@@ -590,6 +592,46 @@ class ThermalZone:
return self._thermal_control
+ @property
+ def domestic_hot_water(self) -> Union[None, DomesticHotWater]:
+ """
+ Get domestic hot water information of this thermal zone
+ :return: None or DomesticHotWater
+ """
+ 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
+ if _domestic_hot_water_reference.schedules is not None:
+ _schedules = []
+ for i_schedule in range(0, len(_domestic_hot_water_reference.schedules)):
+ schedule = Schedule()
+ schedule.type = _domestic_hot_water_reference.schedules[i_schedule].type
+ schedule.day_types = _domestic_hot_water_reference.schedules[i_schedule].day_types
+ schedule.data_type = _domestic_hot_water_reference.schedules[i_schedule].data_type
+ schedule.time_step = _domestic_hot_water_reference.schedules[i_schedule].time_step
+ schedule.time_range = _domestic_hot_water_reference.schedules[i_schedule].time_range
+
+ new_values = []
+ for i_value in range(0, len(_domestic_hot_water_reference.schedules[i_schedule].values)):
+ _new_value = 0
+ for usage in self.usages:
+ _new_value += usage.percentage * usage.domestic_hot_water.schedules[i_schedule].values[i_value]
+ new_values.append(_new_value)
+ schedule.values = new_values
+ _schedules.append(schedule)
+ self._domestic_hot_water.schedules = _schedules
+ return self._domestic_hot_water
+
@property
def total_floor_area(self):
"""
diff --git a/hub/city_model_structure/building_demand/usage.py b/hub/city_model_structure/building_demand/usage.py
index 36240038..c3eea9fe 100644
--- a/hub/city_model_structure/building_demand/usage.py
+++ b/hub/city_model_structure/building_demand/usage.py
@@ -12,6 +12,7 @@ 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.domestic_hot_water import DomesticHotWater
from hub.city_model_structure.building_demand.internal_gain import InternalGain
@@ -31,6 +32,7 @@ class Usage:
self._lighting = None
self._appliances = None
self._thermal_control = None
+ self._domestic_hot_water = None
@property
def id(self):
@@ -236,7 +238,7 @@ class Usage:
@property
def thermal_control(self) -> Union[None, ThermalControl]:
"""
- Get thermal control of this thermal zone
+ Get thermal control information
:return: None or ThermalControl
"""
return self._thermal_control
@@ -244,7 +246,23 @@ class Usage:
@thermal_control.setter
def thermal_control(self, value):
"""
- Set thermal control for this thermal zone
+ Set thermal control information
:param value: ThermalControl
"""
self._thermal_control = value
+
+ @property
+ def domestic_hot_water(self) -> Union[None, DomesticHotWater]:
+ """
+ Get domestic hot water information
+ :return: None or ThermalControl
+ """
+ return self._domestic_hot_water
+
+ @domestic_hot_water.setter
+ def domestic_hot_water(self, value):
+ """
+ Set domestic hot water information
+ :return: None or ThermalControl
+ """
+ self._domestic_hot_water = value
diff --git a/hub/city_model_structure/building_demand/usage_zone.py b/hub/city_model_structure/building_demand/usage_zone.py
deleted file mode 100644
index 6357cff8..00000000
--- a/hub/city_model_structure/building_demand/usage_zone.py
+++ /dev/null
@@ -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
diff --git a/hub/city_model_structure/city_object.py b/hub/city_model_structure/city_object.py
index 549a3362..234c50b4 100644
--- a/hub/city_model_structure/city_object.py
+++ b/hub/city_model_structure/city_object.py
@@ -36,6 +36,7 @@ class CityObject:
self._centroid = None
self._volume = None
self._external_temperature = dict()
+ self._ground_temperature = dict()
self._global_horizontal = dict()
self._diffuse = dict()
self._beam = dict()
@@ -165,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:
"""
diff --git a/hub/config/configuration.ini b/hub/config/configuration.ini
index 891d18d0..4ae34882 100644
--- a/hub/config/configuration.ini
+++ b/hub/config/configuration.ini
@@ -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
@@ -19,3 +18,6 @@ soil_conductivity = 3
#m
soil_thickness = 0.5
short_wave_reflectance = 0.3
+
+#C
+cold_water_temperature = 10
\ No newline at end of file
diff --git a/hub/data/costs/montreal_costs.xml b/hub/data/costs/montreal_costs.xml
index 8cb0f0da..1b40f6b8 100644
--- a/hub/data/costs/montreal_costs.xml
+++ b/hub/data/costs/montreal_costs.xml
@@ -1,89 +1,204 @@
-
+
+ CAD
- 56
- 9.8
-
-
- 43.4
- 36
- 50
-
-
- 78
- 984.5
- 20
-
-
-
-
-
- 363.5
- 363.5
+
+
+ 0
+
+
+
+ 304
+
+
+ 857.14
+
+
+
+
+ 118
+
+
+
+
+
+
+
+ 800
+ 800
+ 25
+
+
+
+ 622.86
+ 622.86
+ 25
+
+
+ 622.86
+ 622.86
15
-
-
- 363.5
- 363.5
+
+
+ 0
+ 0
15
-
-
- 363.5
- 363.5
+
+
+ 47.62
+ 47.62
15
-
-
-
- 17
- 17
- 15
-
-
- 365
- 365
- 15
-
-
- 365
- 365
- 15
-
-
- 88
- 2.5
+
+
+
+
+ 139
+ 139
+ 20
+
+
+
+
+ 2.5
+ 14
+
+
+
+
+
+ 12.27
+ 0
+ 0.075
+
+
+ 17.71
+ 0.640
+
+
+ 1.2
+
+
+ 0.09
+
+
+
+ 40
+ 40
+ 1
+
+ 30
+
+ 6.3
+
+
+ 2
+ 1.5
+ 3.6
+
+
+ 0
+
+ 2
+
+
+
+ CAD
+
+
+
+ 0
+
+
+
+ 304
+
+
+ 857.14
+
+
+
+
+ 118
+
+
+
+
+
+
+
+ 800
+ 800
+ 25
+
+
+
+ 622.86
+ 622.86
+ 25
+
+
+ 622.86
+ 622.86
+ 15
+
+
+ 0
+ 0
+ 15
+
+
+ 47.62
+ 47.62
+ 15
+
+
+
+
+ 139
+ 139
+ 20
+
+
+
+
+ 6
+ 14
+
-
- 0
- 0
-
- 5.6
+ 12.27
+ 0
+ 0.075
-
- 40
- 40
- 0.05
- 1
- 4.6
-
+
+ 17.71
+ 0.640
+
+
+ 1.2
+
+
+ 0.09
+
+
+ 40
+ 40
+ 1
+
30
6.3
-
- 2
- 1.5
- 3.6
-
+
+ 2
+ 1.5
+ 3.6
+
- 0
- 0
+ 0
-
- 2
-
- 0
+ 2
-
+
\ No newline at end of file
diff --git a/hub/data/energy_systems/nrcan.xml b/hub/data/energy_systems/nrcan.xml
new file mode 100644
index 00000000..d9843ae5
--- /dev/null
+++ b/hub/data/energy_systems/nrcan.xml
@@ -0,0 +1,11 @@
+
+
+ boiler_set.json
+ chiller_set.json
+ curves.json
+ furnace_set.json
+ heat_pumps.json
+ shw_set.json
+ pv.json
+ unitary_acs.json
+
diff --git a/hub/helpers/configuration_helper.py b/hub/helpers/configuration_helper.py
index f00a9081..936d46c7 100644
--- a/hub/helpers/configuration_helper.py
+++ b/hub/helpers/configuration_helper.py
@@ -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:
"""
@@ -146,3 +138,11 @@ class ConfigurationHelper:
: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
diff --git a/hub/helpers/constants.py b/hub/helpers/constants.py
index 4792130f..2a20c263 100644
--- a/hub/helpers/constants.py
+++ b/hub/helpers/constants.py
@@ -8,13 +8,17 @@ Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
# universal constants
KELVIN = 273.15
+WATER_DENSITY = 1000
+WATER_HEAT_CAPACITY = 4182
# 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'
@@ -137,6 +141,7 @@ HEATING_SET_POINT = 'HtgSetPt'
EQUIPMENT = 'Equipment'
ACTIVITY = 'Activity'
PEOPLE_ACTIVITY_LEVEL = 'People Activity Level'
+DOMESTIC_HOT_WATER = 'Domestic Hot Water'
# Geometry
EPSILON = 0.0000001
@@ -157,3 +162,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'
diff --git a/hub/helpers/dictionaries.py b/hub/helpers/dictionaries.py
index 3561d682..1fc73fde 100644
--- a/hub/helpers/dictionaries.py
+++ b/hub/helpers/dictionaries.py
@@ -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
diff --git a/hub/imports/geometry/geojson.py b/hub/imports/geometry/geojson.py
index 50c5583d..aa63e0e0 100644
--- a/hub/imports/geometry/geojson.py
+++ b/hub/imports/geometry/geojson.py
@@ -7,10 +7,8 @@ Project Coder Guillermo Gutierrez Guillermo.GutierrezMorote@concordia.ca
import json
import numpy as np
-import trimesh.creation
from pyproj import Transformer
-from shapely.geometry import Polygon as ShapelyPolygon
import hub.helpers.constants as cte
from hub.helpers.geometry_helper import GeometryHelper
@@ -133,8 +131,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
@@ -143,6 +142,8 @@ 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:
@@ -151,12 +152,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'],
diff --git a/hub/imports/results/insel_monthly_energry_balance.py b/hub/imports/results/insel_monthly_energry_balance.py
index 50b5f34d..3ded75d2 100644
--- a/hub/imports/results/insel_monthly_energry_balance.py
+++ b/hub/imports/results/insel_monthly_energry_balance.py
@@ -10,6 +10,7 @@ import pandas as pd
import csv
import hub.helpers.constants as cte
+
class InselMonthlyEnergyBalance:
"""
Import SRA results
diff --git a/hub/imports/usage/comnet_usage_parameters.py b/hub/imports/usage/comnet_usage_parameters.py
index c4f99640..163bd3fe 100644
--- a/hub/imports/usage/comnet_usage_parameters.py
+++ b/hub/imports/usage/comnet_usage_parameters.py
@@ -8,6 +8,7 @@ import copy
import sys
import numpy
+from hub.hub_logger import logger
import hub.helpers.constants as cte
from hub.helpers.dictionaries import Dictionaries
from hub.city_model_structure.building_demand.usage import Usage
@@ -15,6 +16,7 @@ from hub.city_model_structure.building_demand.lighting import Lighting
from hub.city_model_structure.building_demand.occupancy import Occupancy
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.domestic_hot_water import DomesticHotWater
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
@@ -40,8 +42,8 @@ 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}')
+ 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:
@@ -54,7 +56,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)
@@ -69,7 +71,7 @@ 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.
@@ -101,6 +103,17 @@ class ComnetUsageParameters:
_control.heating_set_point_schedules = archetype.thermal_control.heating_set_point_schedules
_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.service_temperature = archetype.domestic_hot_water.service_temperature
+ 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
@staticmethod
def _calculate_reduced_values_from_extended_library(usage, archetype):
diff --git a/hub/imports/usage/nrcan_usage_parameters.py b/hub/imports/usage/nrcan_usage_parameters.py
index 816cce38..67a5e380 100644
--- a/hub/imports/usage/nrcan_usage_parameters.py
+++ b/hub/imports/usage/nrcan_usage_parameters.py
@@ -7,6 +7,7 @@ Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
import sys
+from hub.hub_logger import logger
import hub.helpers.constants as cte
from hub.helpers.dictionaries import Dictionaries
from hub.city_model_structure.building_demand.usage import Usage
@@ -14,6 +15,7 @@ from hub.city_model_structure.building_demand.lighting import Lighting
from hub.city_model_structure.building_demand.occupancy import Occupancy
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.domestic_hot_water import DomesticHotWater
from hub.catalog_factories.usage_catalog_factory import UsageCatalogFactory
@@ -39,16 +41,16 @@ 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}')
+ 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}')
+ 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:
@@ -61,7 +63,7 @@ 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_values(usage, archetype_usage, volume_per_area, building.cold_water_temperature)
self._assign_comnet_extra_values(usage, comnet_archetype_usage)
usage.percentage = 1
self._calculate_reduced_values_from_extended_library(usage, archetype_usage)
@@ -77,7 +79,7 @@ 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:
@@ -111,6 +113,14 @@ class NrcanUsageParameters:
_control.heating_set_point_schedules = archetype.thermal_control.heating_set_point_schedules
_control.hvac_availability_schedules = archetype.thermal_control.hvac_availability_schedules
usage.thermal_control = _control
+ _domestic_hot_water = DomesticHotWater()
+ _domestic_hot_water.peak_flow = archetype.domestic_hot_water.peak_flow
+ _domestic_hot_water.service_temperature = archetype.domestic_hot_water.service_temperature
+ cold_temperature = cold_water_temperature[cte.YEAR]['epw']
+ _domestic_hot_water.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.schedules = archetype.domestic_hot_water.schedules
+ usage.domestic_hot_water = _domestic_hot_water
@staticmethod
def _assign_comnet_extra_values(usage, archetype):
diff --git a/hub/imports/weather/epw_weather_parameters.py b/hub/imports/weather/epw_weather_parameters.py
index 47ddb5fe..89315ab0 100644
--- a/hub/imports/weather/epw_weather_parameters.py
+++ b/hub/imports/weather/epw_weather_parameters.py
@@ -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)
+ 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,7 @@ class EpwWeatherParameters:
sys.exit()
for building in self._city.buildings:
+ building.ground_temperature[cte.MONTH] = 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 +111,24 @@ 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']])
self._city.level_of_detail.weather = 2
diff --git a/hub/imports/weather/helpers/weather.py b/hub/imports/weather/helpers/weather.py
index ae4c9ad2..f595f41e 100644
--- a/hub/imports/weather/helpers/weather.py
+++ b/hub/imports/weather/helpers/weather.py
@@ -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):
diff --git a/hub/unittests/test_exports.py b/hub/unittests/test_exports.py
index 283c9595..7fa84103 100644
--- a/hub/unittests/test_exports.py
+++ b/hub/unittests/test_exports.py
@@ -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
diff --git a/hub/unittests/test_usage_factory.py b/hub/unittests/test_usage_factory.py
index 4cefef8c..6ff43b60 100644
--- a/hub/unittests/test_usage_factory.py
+++ b/hub/unittests/test_usage_factory.py
@@ -124,3 +124,7 @@ class TestUsageFactory(TestCase):
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.density, 'domestic hot water density 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')