Merge remote-tracking branch 'origin/master' into usage_catalog

This commit is contained in:
Guille Gutierrez 2022-05-16 17:33:26 -04:00
commit fbf6005748
27 changed files with 681 additions and 207 deletions

View File

@ -5,6 +5,7 @@ Copyright © 2022 Concordia CERC group
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
"""
class Content:
def __init__(self, vegetations, plants, soils):
self._vegetations = vegetations

View File

@ -5,7 +5,7 @@ Copyright © 2022 Concordia CERC group
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
"""
from catalog_factories.data_models.greenery.soil import Soil as libs_soil
from catalog_factories.data_models.greenery.soil import Soil as hub_soil
class Plant:
@ -20,12 +20,13 @@ class Plant:
self._co2_sequestration = plant.co2Sequestration
self._grows_on = []
for soil in plant.growsOn:
self._grows_on.append(libs_soil(soil))
self._grows_on.append(hub_soil(soil))
@property
def name(self):
"""
Get plant name
:return: string
"""
return self._name
@ -33,13 +34,15 @@ class Plant:
def category(self):
"""
Get plant category name
:return: string
"""
return self._category
@property
def height(self):
"""
Get plant height
Get plant height in m
:return: float
"""
return self._height
@ -47,6 +50,7 @@ class Plant:
def leaf_area_index(self):
"""
Get plant leaf area index
:return: float
"""
return self._leaf_area_index
@ -54,6 +58,7 @@ class Plant:
def leaf_reflectivity(self):
"""
Get plant leaf area index
:return: float
"""
return self._leaf_reflectivity
@ -61,26 +66,30 @@ class Plant:
def leaf_emissivity(self):
"""
Get plant leaf emissivity
:return: float
"""
return self._leaf_emissivity
@property
def minimal_stomatal_resistance(self):
"""
Get plant minimal stomatal resistance
Get plant minimal stomatal resistance in s/m
:return: float
"""
return self._minimal_stomatal_resistance
@property
def co2_sequestration(self):
"""
Get plant co2 sequestration capacity
Get plant co2 sequestration capacity in kg CO2 equivalent
:return: float
"""
return self._co2_sequestration
@property
def grows_on(self) -> [libs_soil]:
def grows_on(self) -> [hub_soil]:
"""
Get plant compatible soils
:return: [Soil]
"""
return self._grows_on

View File

@ -7,6 +7,7 @@ Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
from catalog_factories.data_models.greenery.plant import Plant as libs_plant
class PlantPercentage(libs_plant):
def __init__(self, percentage, plant_category, plant):
@ -15,4 +16,8 @@ class PlantPercentage(libs_plant):
@property
def percentage(self):
"""
Get plant percentage
:return: float
"""
return self._percentage

View File

@ -5,6 +5,7 @@ Copyright © 2022 Concordia CERC group
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
"""
class Soil:
def __init__(self, soil):
self._name = soil.name
@ -23,6 +24,7 @@ class Soil:
def name(self):
"""
Get soil name
:return: string
"""
return self._name
@ -30,27 +32,31 @@ class Soil:
def roughness(self):
"""
Get soil roughness
:return: string
"""
return self._roughness
@property
def dry_conductivity(self):
"""
Get soil dry conductivity
Get soil dry conductivity in W/mK
:return: float
"""
return self._dry_conductivity
@property
def dry_density(self):
"""
Get soil dry density
Get soil dry density in kg/m3
:return: float
"""
return self._dry_density
@property
def dry_specific_heat(self):
"""
Get soil dry specific heat
Get soil dry specific heat in J/kgK
:return: float
"""
return self._dry_specific_heat
@ -58,6 +64,7 @@ class Soil:
def thermal_absorptance(self):
"""
Get soil thermal absortance
:return: float
"""
return self._thermal_absorptance
@ -65,6 +72,7 @@ class Soil:
def solar_absorptance(self):
"""
Get soil solar absortance
:return: float
"""
return self._solar_absorptance
@ -72,6 +80,7 @@ class Soil:
def visible_absorptance(self):
"""
Get soil visible absortance
:return: float
"""
return self._visible_absorptance
@ -79,6 +88,7 @@ class Soil:
def saturation_volumetric_moisture_content(self):
"""
Get soil saturation volumetric moisture content
:return: float
"""
return self._saturation_volumetric_moisture_content
@ -86,6 +96,7 @@ class Soil:
def residual_volumetric_moisture_content(self):
"""
Get soil residual volumetric moisture content
:return: float
"""
return self._residual_volumetric_moisture_content
@ -93,5 +104,6 @@ class Soil:
def initial_volumetric_moisture_content(self):
"""
Get soil initial volumetric moisture content
:return: float
"""
return self._initial_volumetric_moisture_content

View File

@ -7,6 +7,7 @@ Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
from catalog_factories.data_models.greenery.plant_percentage import PlantPercentage
class Vegetation:
def __init__(self, category, vegetation, plant_percentages):
self._name = vegetation.name
@ -31,6 +32,7 @@ class Vegetation:
def name(self):
"""
Get vegetation name
:return: string
"""
return self._name
@ -38,13 +40,15 @@ class Vegetation:
def category(self):
"""
Get vegetation category
:return: string
"""
return self._category
@property
def soil_thickness(self):
"""
Get soil thickness
Get soil thickness in m
:return: float
"""
return self._soil_thickness
@ -52,13 +56,15 @@ class Vegetation:
def management(self):
"""
Get management
:return: string
"""
return self._management
@property
def air_gap(self):
"""
Get air gap
Get air gap in m
:return: float
"""
return self._air_gap
@ -66,6 +72,7 @@ class Vegetation:
def plant_percentages(self) -> [PlantPercentage]:
"""
Get plant percentages
:return: [PlantPercentage]
"""
percentage = 0.0
for plant_percentage in self._plant_percentages:
@ -78,6 +85,7 @@ class Vegetation:
def soil_name(self):
"""
Get soil name
:return: string
"""
return self._soil_name
@ -85,27 +93,31 @@ class Vegetation:
def soil_roughness(self):
"""
Get soil roughness
:return: float
"""
return self._soil_roughness
@property
def dry_soil_conductivity(self):
"""
Get soil dry conductivity
Get soil dry conductivity in W/mK
:return: float
"""
return self._dry_soil_conductivity
@property
def dry_soil_density(self):
"""
Get soil dry density
Get soil dry density in kg/m3
:return: float
"""
return self._dry_soil_density
@property
def dry_soil_specific_heat(self):
"""
Get soil dry specific heat
Get soil dry specific heat in J/kgK
:return: float
"""
return self._dry_soil_specific_heat
@ -113,6 +125,7 @@ class Vegetation:
def soil_thermal_absorptance(self):
"""
Get soil thermal absortance
:return: float
"""
return self._soil_thermal_absorptance
@ -120,6 +133,7 @@ class Vegetation:
def soil_solar_absorptance(self):
"""
Get soil solar absortance
:return: float
"""
return self._soil_solar_absorptance
@ -127,6 +141,7 @@ class Vegetation:
def soil_visible_absorptance(self):
"""
Get soil visible absortance
:return: float
"""
return self._soil_visible_absorptance
@ -134,6 +149,7 @@ class Vegetation:
def soil_saturation_volumetric_moisture_content(self):
"""
Get soil saturation volumetric moisture content
:return: float
"""
return self._soil_saturation_volumetric_moisture_content
@ -141,6 +157,7 @@ class Vegetation:
def soil_residual_volumetric_moisture_content(self):
"""
Get soil residual volumetric moisture content
:return: float
"""
return self._soil_residual_volumetric_moisture_content
@ -148,5 +165,6 @@ class Vegetation:
def soil_initial_volumetric_moisture_content(self):
"""
Get soil initial volumetric moisture content
:return: float
"""
return self._soil_initial_volumetric_moisture_content

View File

@ -32,7 +32,7 @@ class Edge:
@property
def id(self):
"""
Get edge id, an universally unique identifier randomly generated
Get edge id, a universally unique identifier randomly generated
:return: str
"""
if self._id is None:

View File

@ -34,7 +34,7 @@ class Node:
@property
def id(self):
"""
Get node id, an universally unique identifier randomly generated
Get node id, a universally unique identifier randomly generated
:return: str
"""
if self._id is None:

View File

@ -26,7 +26,7 @@ class Schedule:
@property
def id(self):
"""
Get schedule id, an universally unique identifier randomly generated
Get schedule id, a universally unique identifier randomly generated
:return: str
"""
if self._id is None:

View File

@ -30,7 +30,7 @@ class InternalZone:
@property
def id(self):
"""
Get internal zone id, an universally unique identifier randomly generated
Get internal zone id, a universally unique identifier randomly generated
:return: str
"""
if self._id is None:

View File

@ -21,7 +21,7 @@ class Layer:
@property
def id(self):
"""
Get layer id, an universally unique identifier randomly generated
Get layer id, a universally unique identifier randomly generated
:return: str
"""
if self._id is None:

View File

@ -40,8 +40,7 @@ class Surface:
self._solid_polygon = solid_polygon
self._pv_system_installed = None
self._inverse = None
# todo: do I need it???
self._associated_thermal_boundaries = None
# todo: create self._associated_thermal_boundaries and bring the vegetation here instead of in thermal_boundary
@property
def name(self):

View File

@ -5,6 +5,7 @@ Copyright © 2022 Concordia CERC group
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
import uuid
from typing import List, Union
from helpers.configuration_helper import ConfigurationHelper as ch
@ -12,6 +13,7 @@ from city_model_structure.building_demand.layer import Layer
from city_model_structure.building_demand.thermal_opening import ThermalOpening
from city_model_structure.building_demand.thermal_zone import ThermalZone
from city_model_structure.building_demand.surface import Surface
from city_model_structure.greenery.vegetation import Vegetation
class ThermalBoundary:
@ -41,12 +43,13 @@ class ThermalBoundary:
self._alpha_coefficient = None
self._radiative_coefficient = None
self._window_ratio = None
self._calculated = False
self._window_ration_is_calculated = False
self._vegetation = None
@property
def id(self):
"""
Get thermal zone id, an universally unique identifier randomly generated
Get thermal zone id, a universally unique identifier randomly generated
:return: str
"""
if self._id is None:
@ -249,7 +252,7 @@ class ThermalBoundary:
:return: float
"""
if self.windows_areas is not None:
if not self._calculated:
if not self._window_ration_is_calculated:
_calculated = True
if len(self.windows_areas) == 0:
self._window_ratio = 0
@ -266,7 +269,7 @@ class ThermalBoundary:
Set thermal boundary window ratio
:param value: str
"""
if self._calculated:
if self._window_ration_is_calculated:
raise ValueError('Window ratio cannot be assigned when the windows are defined in the geometry.')
self._window_ratio = float(value)
@ -421,3 +424,19 @@ class ThermalBoundary:
"""
if value is not None:
self._radiative_coefficient = float(value)
@property
def vegetation(self) -> Union[None, Vegetation]:
"""
Get the vegetation construction at the external surface of the thermal boundary
:return: None or Vegetation
"""
return self._vegetation
@vegetation.setter
def vegetation(self, value):
"""
Set the vegetation construction at the external surface of the thermal boundary
:param value: Vegetation
"""
self._vegetation = value

View File

@ -37,7 +37,7 @@ class ThermalOpening:
@property
def id(self):
"""
Get thermal zone id, an universally unique identifier randomly generated
Get thermal zone id, a universally unique identifier randomly generated
:return: str
"""
if self._id is None:

View File

@ -5,14 +5,15 @@ Copyright © 2022 Concordia CERC group
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
import uuid, sys
import uuid
import sys
import copy
from typing import List, Union, TypeVar
from city_model_structure.building_demand.occupancy import Occupancy
from city_model_structure.building_demand.appliances import Appliances
from city_model_structure.building_demand.lighting import Lighting
from city_model_structure.building_demand.internal_gain import InternalGain
from city_model_structure.attributes.schedule import Schedule
from city_model_structure.building_demand.thermal_control import ThermalControl
from city_model_structure.energy_systems.hvac_system import HvacSystem
import helpers.constants as cte
@ -54,7 +55,7 @@ class ThermalZone:
@property
def id(self):
"""
Get thermal zone id, an universally unique identifier randomly generated
Get thermal zone id, a universally unique identifier randomly generated
:return: str
"""
if self._id is None:

View File

@ -12,17 +12,18 @@ from city_model_structure.building_demand.occupancy import Occupancy
from city_model_structure.building_demand.lighting import Lighting
from city_model_structure.building_demand.appliances import Appliances
from city_model_structure.building_demand.thermal_control import ThermalControl
import helpers.constants as cte
class UsageZone:
"""
UsageZone class
"""
def __init__(self):
def __init__(self, not_detailed_source_mean_annual_internal_gains=None):
self._id = None
self._usage = None
self._percentage = None
self._not_detailed_source_mean_annual_internal_gains = None
self._not_detailed_source_mean_annual_internal_gains = not_detailed_source_mean_annual_internal_gains
self._hours_day = None
self._days_year = None
# self._electrical_app_average_consumption_sqm_year = None
@ -31,11 +32,12 @@ class UsageZone:
self._lighting = None
self._appliances = None
self._thermal_control = None
self._internal_gains = None
@property
def id(self):
"""
Get usage zone id, an universally unique identifier randomly generated
Get usage zone id, a universally unique identifier randomly generated
:return: str
"""
if self._id is None:
@ -76,22 +78,6 @@ class UsageZone:
if value is not None:
self._percentage = float(value)
@property
def not_detailed_source_mean_annual_internal_gains(self) -> List[InternalGain]:
"""
Get usage zone internal gains with unknown energy source
:return: [InternalGain]
"""
return self._not_detailed_source_mean_annual_internal_gains
@not_detailed_source_mean_annual_internal_gains.setter
def not_detailed_source_mean_annual_internal_gains(self, value):
"""
Set usage zone internal gains with unknown energy source
:param value: [InternalGain]
"""
self._not_detailed_source_mean_annual_internal_gains = value
@property
def hours_day(self) -> Union[None, float]:
"""
@ -143,23 +129,6 @@ class UsageZone:
if value is not None:
self._mechanical_air_change = float(value)
@property
def electrical_app_average_consumption_sqm_year(self) -> Union[None, float]:
"""
Get average consumption of electrical appliances in Joules hour per square meter and year (J/m2yr)
:return: None or float
"""
return self._electrical_app_average_consumption_sqm_year
@electrical_app_average_consumption_sqm_year.setter
def electrical_app_average_consumption_sqm_year(self, value):
"""
Set average consumption of electrical appliances in Joules per square meter and year (J/m2yr)
:param value: float
"""
if value is not None:
self._electrical_app_average_consumption_sqm_year = float(value)
@property
def occupancy(self) -> Union[None, Occupancy]:
"""
@ -223,3 +192,64 @@ class UsageZone:
:param value: ThermalControl
"""
self._thermal_control = value
@property
def internal_gains(self) -> List[InternalGain]:
"""
Calculates and returns the list of all internal gains defined
:return: [InternalGain]
"""
if self._not_detailed_source_mean_annual_internal_gains is not None:
self._internal_gains = []
for _internal_gain in self._not_detailed_source_mean_annual_internal_gains:
self._internal_gains.append(_internal_gain)
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
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
else:
_internal_gain.latent_fraction = 0
_internal_gain.radiative_fraction = 0
_internal_gain.convective_fraction = 0
_internal_gain.schedules = self.occupancy.occupancy_schedules
if self._internal_gains is not None:
self._internal_gains.append(_internal_gain)
else:
self._internal_gains = [_internal_gain]
if self.lighting is not None:
_internal_gain = InternalGain()
_internal_gain.type = cte.LIGHTING
self._add_internal_gain(self.lighting, _internal_gain)
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
self._add_internal_gain(self.appliances, _internal_gain)
if self._internal_gains is not None:
self._internal_gains.append(_internal_gain)
else:
self._internal_gains = [_internal_gain]
return self._internal_gains
@staticmethod
def _add_internal_gain(internal_gain_type, _internal_gain):
_internal_gain.average_internal_gain = internal_gain_type.density
_internal_gain.latent_fraction = internal_gain_type.latent_fraction
_internal_gain.radiative_fraction = internal_gain_type.radiative_fraction
_internal_gain.convective_fraction = internal_gain_type.convective_fraction
_internal_gain.schedules = internal_gain_type.schedules

View File

@ -0,0 +1,103 @@
"""
Plant class
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
from typing import List
from city_model_structure.greenery.soil import Soil
class Plant:
def __init__(self, name, height, leaf_area_index, leaf_reflectivity, leaf_emissivity, minimal_stomatal_resistance,
co2_sequestration, grows_on_soils):
self._name = name
self._percentage = None
self._height = height
self._leaf_area_index = leaf_area_index
self._leaf_reflectivity = leaf_reflectivity
self._leaf_emissivity = leaf_emissivity
self._minimal_stomatal_resistance = minimal_stomatal_resistance
self._co2_sequestration = co2_sequestration
self._grows_on = grows_on_soils
@property
def name(self):
"""
Get plant name
:return: string
"""
return self._name
@property
def percentage(self):
"""
Get percentage of plant in vegetation
:return: float
"""
return self._percentage
@percentage.setter
def percentage(self, value):
"""
Set percentage of plant in vegetation
:param value: float
"""
self._percentage = value
@property
def height(self):
"""
Get plant height in m
:return: float
"""
return self._height
@property
def leaf_area_index(self):
"""
Get plant leaf area index
:return: float
"""
return self._leaf_area_index
@property
def leaf_reflectivity(self):
"""
Get plant leaf area index
:return: float
"""
return self._leaf_reflectivity
@property
def leaf_emissivity(self):
"""
Get plant leaf emissivity
:return: float
"""
return self._leaf_emissivity
@property
def minimal_stomatal_resistance(self):
"""
Get plant minimal stomatal resistance in s/m
:return: float
"""
return self._minimal_stomatal_resistance
@property
def co2_sequestration(self):
"""
Get plant co2 sequestration capacity in kg CO2 equivalent
:return: float
"""
return self._co2_sequestration
@property
def grows_on(self) -> List[Soil]:
"""
Get plant compatible soils
:return: [Soil]
"""
return self._grows_on

View File

@ -0,0 +1,127 @@
"""
Soil class
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
class Soil:
def __init__(self, name, roughness, dry_conductivity, dry_density, dry_specific_heat, thermal_absorptance,
solar_absorptance, visible_absorptance, saturation_volumetric_moisture_content,
residual_volumetric_moisture_content):
self._name = name
self._roughness = roughness
self._dry_conductivity = dry_conductivity
self._dry_density = dry_density
self._dry_specific_heat = dry_specific_heat
self._thermal_absorptance = thermal_absorptance
self._solar_absorptance = solar_absorptance
self._visible_absorptance = visible_absorptance
self._saturation_volumetric_moisture_content = saturation_volumetric_moisture_content
self._residual_volumetric_moisture_content = residual_volumetric_moisture_content
self._initial_volumetric_moisture_content = None
@property
def name(self):
"""
Get soil name
:return: string
"""
return self._name
@property
def roughness(self):
"""
Get soil roughness
:return: string
"""
return self._roughness
@property
def dry_conductivity(self):
"""
Get soil dry conductivity in W/mK
:return: float
"""
return self._dry_conductivity
@property
def dry_density(self):
"""
Get soil dry density in kg/m3
:return: float
"""
return self._dry_density
@property
def dry_specific_heat(self):
"""
Get soil dry specific heat in J/kgK
:return: float
"""
return self._dry_specific_heat
@property
def thermal_absorptance(self):
"""
Get soil thermal absortance
:return: float
"""
return self._thermal_absorptance
@property
def solar_absorptance(self):
"""
Get soil solar absortance
:return: float
"""
return self._solar_absorptance
@property
def visible_absorptance(self):
"""
Get soil visible absortance
:return: float
"""
return self._visible_absorptance
@property
def saturation_volumetric_moisture_content(self):
"""
Get soil saturation volumetric moisture content
:return: float
"""
return self._saturation_volumetric_moisture_content
@property
def residual_volumetric_moisture_content(self):
"""
Get soil residual volumetric moisture content
:return: None or float
"""
return self._residual_volumetric_moisture_content
@residual_volumetric_moisture_content.setter
def residual_volumetric_moisture_content(self, value):
"""
Set soil residual volumetric moisture content
:param value: float
"""
self._residual_volumetric_moisture_content = value
@property
def initial_volumetric_moisture_content(self):
"""
Get soil initial volumetric moisture content
:return: None or float
"""
return self._initial_volumetric_moisture_content
@initial_volumetric_moisture_content.setter
def initial_volumetric_moisture_content(self, value):
"""
Set soil initial volumetric moisture content
:param value: float
"""
self._initial_volumetric_moisture_content = value

View File

@ -0,0 +1,84 @@
"""
Vegetation class
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
from typing import List
from city_model_structure.greenery.soil import Soil
from city_model_structure.greenery.plant import Plant
class Vegetation:
def __init__(self, name, soil, soil_thickness, plants):
self._name = name
self._management = None
self._air_gap = None
self._soil = soil
self._soil_thickness = soil_thickness
self._plants = plants
@property
def name(self):
"""
Get vegetation name
:return: string
"""
return self._name
@property
def management(self):
"""
Get management
:return: string
"""
return self._management
@management.setter
def management(self, value):
"""
Set management
:param value: string
"""
self._management = value
@property
def air_gap(self):
"""
Get air gap in m
:return: float
"""
return self._air_gap
@air_gap.setter
def air_gap(self, value):
"""
Set air gap in m
:param value: float
"""
self._air_gap = value
@property
def soil(self) -> Soil:
"""
Get soil
:return: Soil
"""
return self._soil
@property
def soil_thickness(self):
"""
Get soil thickness in m
:return: float
"""
return self._soil_thickness
@property
def plants(self) -> List[Plant]:
"""
Get list plants in the vegetation
:return: List[Plant]
"""
return self._plants

View File

@ -30,7 +30,7 @@ class Network(CityObject):
@property
def id(self):
"""
Get network id, an universally unique identifier randomly generated
Get network id, a universally unique identifier randomly generated
:return: str
"""
if self._id is None:

View File

@ -44,6 +44,8 @@ class Idf:
_LOCATION = 'SITE:LOCATION'
_WINDOW_MATERIAL_SIMPLE = 'WINDOWMATERIAL:SIMPLEGLAZINGSYSTEM'
_WINDOW = 'WINDOW'
_MATERIAL_ROOFVEGETATION = 'MATERIAL:ROOFVEGETATION'
_SIMPLE = 'Simple'
idf_surfaces = {
# todo: make an enum for all the surface types
@ -224,9 +226,12 @@ class Idf:
def _add_construction(self, thermal_boundary):
for construction in self._idf.idfobjects[self._CONSTRUCTION]:
if construction.Name == thermal_boundary.construction_name:
return
if thermal_boundary.vegetation is not None:
if construction.Name == f'{thermal_boundary.construction_name}_{thermal_boundary.vegetation.name}':
return
else:
if construction.Name == thermal_boundary.construction_name:
return
if thermal_boundary.layers is None:
for material in self._idf.idfobjects[self._MATERIAL]:
if material.Name == "DefaultMaterial":
@ -237,9 +242,15 @@ class Idf:
self._add_material(layer)
layers = thermal_boundary.layers
# The constructions should have at least one layer
_kwargs = {'Name': thermal_boundary.construction_name, 'Outside_Layer': layers[0].material.name}
for i in range(1, len(layers) - 1):
_kwargs[f'Layer_{i + 1}'] = layers[1].material.name
if thermal_boundary.vegetation is not None:
_kwargs = {'Name': f'{thermal_boundary.construction_name}_{thermal_boundary.vegetation.name}',
'Outside_Layer': thermal_boundary.vegetation.name}
for i in range(0, len(layers) - 1):
_kwargs[f'Layer_{i + 2}'] = layers[i].material.name
else:
_kwargs = {'Name': thermal_boundary.construction_name, 'Outside_Layer': layers[0].material.name}
for i in range(1, len(layers) - 1):
_kwargs[f'Layer_{i + 1}'] = layers[i].material.name
self._idf.newidfobject(self._CONSTRUCTION, **_kwargs)
def _add_window_construction_and_material(self, thermal_opening):
@ -342,6 +353,8 @@ class Idf:
for thermal_zone in internal_zone.thermal_zones:
for thermal_boundary in thermal_zone.thermal_boundaries:
self._add_construction(thermal_boundary)
if thermal_boundary.vegetation is not None:
self._add_vegetation_material(thermal_boundary.vegetation)
for thermal_opening in thermal_boundary.thermal_openings:
self._add_window_construction_and_material(thermal_opening)
usage = thermal_zone.usage
@ -355,7 +368,6 @@ class Idf:
self._add_heating_system(thermal_zone)
self._add_infiltration(thermal_zone)
self._add_occupancy(thermal_zone)
if self._export_type == "Surfaces":
self._add_surfaces(building)
else:
@ -404,10 +416,13 @@ class Idf:
for thermal_zone in internal_zone.thermal_zones:
for boundary in thermal_zone.thermal_boundaries:
idf_surface_type = self.idf_surfaces[boundary.parent_surface.type]
# todo: thermal boundary vs. surfaces??
if boundary.vegetation is not None:
construction_name = f'{boundary.construction_name}_{boundary.vegetation.name}'
else:
construction_name = boundary.construction_name
surface = self._idf.newidfobject(self._SURFACE, Name=f'{boundary.parent_surface.name}',
Surface_Type=idf_surface_type, Zone_Name=thermal_zone.id,
Construction_Name=boundary.construction_name)
Construction_Name=construction_name)
coordinates = self._matrix_to_list(boundary.parent_surface.solid_polygon.coordinates,
self._city.lower_corner)
surface.setcoords(coordinates)
@ -433,3 +448,44 @@ class Idf:
material['Solar_Heat_Gain_Coefficient'] == opening.g_value:
return True
return False
def _add_vegetation_material(self, vegetation):
for vegetation_material in self._idf.idfobjects[self._MATERIAL_ROOFVEGETATION]:
if vegetation_material.Name == vegetation.name:
return
soil = vegetation.soil
height = 0
leaf_area_index = 0
leaf_reflectivity = 0
leaf_emissivity = 0
minimal_stomatal_resistance = 0
for plant in vegetation.plants:
height += plant.percentage * plant.height
leaf_area_index += plant.percentage * plant.leaf_area_index
leaf_reflectivity += plant.percentage * plant.leaf_reflectivity
leaf_emissivity += plant.percentage * plant.leaf_emissivity
minimal_stomatal_resistance += plant.percentage * plant.minimal_stomatal_resistance
self._idf.newidfobject(self._MATERIAL_ROOFVEGETATION,
Name=vegetation.name,
Height_of_Plants=height,
Leaf_Area_Index=leaf_area_index,
Leaf_Reflectivity=leaf_reflectivity,
Leaf_Emissivity=leaf_emissivity,
Minimum_Stomatal_Resistance=minimal_stomatal_resistance,
Soil_Layer_Name=soil.name,
Roughness=soil.roughness,
Thickness=vegetation.soil_thickness,
Conductivity_of_Dry_Soil=soil.dry_conductivity,
Density_of_Dry_Soil=soil.dry_density,
Specific_Heat_of_Dry_Soil=soil.dry_specific_heat,
Thermal_Absorptance=soil.thermal_absorptance,
Solar_Absorptance=soil.solar_absorptance,
Visible_Absorptance=soil.visible_absorptance,
Saturation_Volumetric_Moisture_Content_of_the_Soil_Layer=
soil.saturation_volumetric_moisture_content,
Residual_Volumetric_Moisture_Content_of_the_Soil_Layer=
soil.residual_volumetric_moisture_content,
Initial_Volumetric_Moisture_Content_of_the_Soil_Layer=
soil.initial_volumetric_moisture_content,
Moisture_Diffusion_Calculation_Method=self._SIMPLE
)

View File

@ -9,14 +9,9 @@ import sys
from imports.geometry.helpers.geometry_helper import GeometryHelper
from imports.usage.hft_usage_interface import HftUsageInterface
from imports.usage.helpers.usage_helper import UsageHelper
from city_model_structure.building_demand.usage_zone import UsageZone
from city_model_structure.building_demand.internal_gain import InternalGain
from city_model_structure.building_demand.occupancy import Occupancy
from city_model_structure.building_demand.appliances import Appliances
from city_model_structure.building_demand.thermal_control import ThermalControl
class CaUsageParameters(HftUsageInterface):
class HftUsageParameters(HftUsageInterface):
"""
CaUsageParameters class
"""
@ -36,48 +31,11 @@ class CaUsageParameters(HftUsageInterface):
archetype = self._search_archetype(usage)
except KeyError:
sys.stderr.write(f'Building {building.name} has unknown archetype for building function:'
f' {building.function}\n')
f' {building.function}, that assigns building usage as '
f'{GeometryHelper().libs_usage_from_libs_function(building.function)}\n')
return
for internal_zone in building.internal_zones:
usage_zone = UsageZone()
usage_zone.usage = building.function
usage_zone = self._assign_values(building.function, archetype)
usage_zone.percentage = 1
self._assign_values_usage_zone(usage_zone, archetype)
internal_zone.usage_zones = [usage_zone]
def _search_archetype(self, libs_usage):
building_usage = UsageHelper().hft_from_libs_usage(libs_usage)
for building_archetype in self._usage_archetypes:
if building_archetype.usage == building_usage:
return building_archetype
return None
@staticmethod
def _assign_values_usage_zone(usage_zone, archetype):
# Due to the fact that python is not a typed language, the wrong object type is assigned to
# usage_zone.internal_gains when writing usage_zone.internal_gains = archetype.internal_gains.
# Therefore, this walk around has been done.
usage_zone.mechanical_air_change = archetype.mechanical_air_change
_occupancy = Occupancy()
_occupancy.occupancy_density = archetype.occupancy.occupancy_density
usage_zone.occupancy = _occupancy
usage_zone.hours_day = archetype.hours_day
usage_zone.days_year = archetype.days_year
_appliances = Appliances()
_appliances.density = archetype.appliances.density
usage_zone.appliances = _appliances
_control = ThermalControl()
_control.mean_heating_set_point = archetype.thermal_control.mean_heating_set_point
_control.heating_set_back = archetype.thermal_control.heating_set_back
_control.mean_cooling_set_point = archetype.thermal_control.mean_cooling_set_point
usage_zone.thermal_control = _control
_internal_gains = []
for archetype_internal_gain in archetype.not_detailed_source_mean_annual_internal_gains:
_internal_gain = InternalGain()
_internal_gain.average_internal_gain = archetype_internal_gain.average_internal_gain
_internal_gain.convective_fraction = archetype_internal_gain.convective_fraction
_internal_gain.radiative_fraction = archetype_internal_gain.radiative_fraction
_internal_gain.latent_fraction = archetype_internal_gain.latent_fraction
_internal_gains.append(_internal_gain)
usage_zone.not_detailed_source_mean_annual_internal_gains = _internal_gains

View File

@ -14,6 +14,7 @@ from city_model_structure.building_demand.appliances import Appliances
from city_model_structure.building_demand.thermal_control import ThermalControl
from city_model_structure.attributes.schedule import Schedule
import helpers.constants as cte
from imports.usage.helpers.usage_helper import UsageHelper
class HftUsageInterface:
@ -38,15 +39,9 @@ class HftUsageInterface:
@staticmethod
def _parse_zone_usage_type(usage, zone_usage_type):
usage_zone_archetype = UsageZone()
usage_zone_archetype.usage = usage
if 'occupancy' in zone_usage_type:
_occupancy = Occupancy()
_occupancy.occupancy_density = zone_usage_type['occupancy']['occupancyDensity'] #todo: check units
usage_zone_archetype.hours_day = zone_usage_type['occupancy']['usageHoursPerDay']
usage_zone_archetype.days_year = zone_usage_type['occupancy']['usageDaysPerYear']
usage_zone_archetype.occupancy = _occupancy
if 'internGains' in zone_usage_type['occupancy']:
_internal_gain = InternalGain()
@ -72,7 +67,18 @@ class HftUsageInterface:
_schedule.values = _values_float
_internal_gain.schedules = [_schedule]
usage_zone_archetype.not_detailed_source_mean_annual_internal_gains = [_internal_gain]
_not_detailed_source_mean_annual_internal_gains = [_internal_gain]
usage_zone_archetype = UsageZone(_not_detailed_source_mean_annual_internal_gains)
else:
usage_zone_archetype = UsageZone()
usage_zone_archetype.hours_day = zone_usage_type['occupancy']['usageHoursPerDay']
usage_zone_archetype.days_year = zone_usage_type['occupancy']['usageDaysPerYear']
usage_zone_archetype.occupancy = _occupancy
else:
usage_zone_archetype = UsageZone()
usage_zone_archetype.usage = usage
if 'endUses' in zone_usage_type:
_thermal_control = ThermalControl()
@ -256,3 +262,24 @@ class HftUsageInterface:
usage_zone_archetype.appliances = _appliances
return usage_zone_archetype
def _search_archetype(self, libs_usage):
building_usage = UsageHelper().hft_from_libs_usage(libs_usage)
for building_archetype in self._usage_archetypes:
if building_archetype.usage == building_usage:
return building_archetype
return None
@staticmethod
def _assign_values(usage, archetype):
_not_detailed_source_mean_annual_internal_gains = \
copy.deepcopy(archetype.internal_gains)
usage_zone = UsageZone(_not_detailed_source_mean_annual_internal_gains)
usage_zone.usage = usage
usage_zone.mechanical_air_change = archetype.mechanical_air_change
usage_zone.occupancy = copy.deepcopy(archetype.occupancy)
usage_zone.appliances = copy.deepcopy(archetype.appliances)
usage_zone.thermal_control = copy.deepcopy(archetype.thermal_control)
usage_zone.days_year = archetype.days_year
usage_zone.hours_day = archetype.hours_day
return usage_zone

View File

@ -5,12 +5,10 @@ Copyright © 2022 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
import sys
import copy
from imports.geometry.helpers.geometry_helper import GeometryHelper
from imports.usage.hft_usage_interface import HftUsageInterface
from imports.usage.helpers.usage_helper import UsageHelper
from city_model_structure.building_demand.usage_zone import UsageZone
class HftUsageParameters(HftUsageInterface):
@ -38,58 +36,7 @@ class HftUsageParameters(HftUsageInterface):
return
for internal_zone in building.internal_zones:
usage_zone = UsageZone()
libs_usage = GeometryHelper().libs_usage_from_libs_function(building.function)
usage_zone.usage = UsageHelper().hft_from_libs_usage(libs_usage)
self._assign_values(usage_zone, archetype)
usage_zone = self._assign_values(UsageHelper().hft_from_libs_usage(libs_usage), archetype)
usage_zone.percentage = 1
internal_zone.usage_zones = [usage_zone]
def _search_archetype(self, libs_usage):
building_usage = UsageHelper().hft_from_libs_usage(libs_usage)
for building_archetype in self._usage_archetypes:
if building_archetype.usage == building_usage:
return building_archetype
return None
@staticmethod
def _assign_values(usage_zone, archetype):
""" # Due to the fact that python is not a typed language, the wrong object type is assigned to
# usage_zone.internal_gains when writing usage_zone.internal_gains = archetype.internal_gains.
# Therefore, this walk around has been done.
# Due to the fact that python is not a typed language, the wrong object type is assigned to
# usage_zone.occupancy when writing usage_zone.occupancy = archetype.occupancy.
# Same happens for lighting and appliances. Therefore, this walk around has been done.
usage_zone.mechanical_air_change = archetype.mechanical_air_change
_occupancy = Occupancy()
_occupancy.occupancy_density = archetype.occupancy.occupancy_density
usage_zone.occupancy = _occupancy
_appliances = Appliances()
_appliances.appliances_density = archetype.appliances.appliances_density
usage_zone.appliances = _appliances
_control = ThermalControl()
_control.mean_heating_set_point = archetype.thermal_control.mean_heating_set_point
_control.heating_set_back = archetype.thermal_control.heating_set_back
_control.mean_cooling_set_point = archetype.thermal_control.mean_cooling_set_point
_control.cooling_set_point_schedules = archetype.thermal_control.cooling_set_point_schedules
_control.heating_set_point_schedules = archetype.thermal_control.heating_set_point_schedules
usage_zone.thermal_control = _control
_internal_gains = []
for archetype_internal_gain in archetype.not_detailed_source_mean_annual_internal_gains:
_internal_gain = InternalGain()
_internal_gain.average_internal_gain = archetype_internal_gain.average_internal_gain
_internal_gain.convective_fraction = archetype_internal_gain.convective_fraction
_internal_gain.radiative_fraction = archetype_internal_gain.radiative_fraction
_internal_gain.latent_fraction = archetype_internal_gain.latent_fraction
_internal_gain.schedules = archetype_internal_gain.schedules
_internal_gains.append(_internal_gain)
usage_zone.not_detailed_source_mean_annual_internal_gains = _internal_gains
"""
usage_zone.mechanical_air_change = archetype.mechanical_air_change
usage_zone.occupancy = copy.deepcopy(archetype.occupancy)
usage_zone.appliances = copy.deepcopy(archetype.appliances)
usage_zone.thermal_control = copy.deepcopy(archetype.thermal_control)
usage_zone.not_detailed_source_mean_annual_internal_gains = \
copy.deepcopy(archetype.not_detailed_source_mean_annual_internal_gains)
usage_zone.days_year = archetype.days_year
usage_zone.hours_day = archetype.hours_day

View File

@ -8,9 +8,7 @@ Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concord
"""
from pathlib import Path
from imports.usage.hft_usage_parameters import HftUsageParameters
from imports.usage.ca_usage_parameters import CaUsageParameters
from imports.usage.comnet_usage_parameters import ComnetUsageParameters
# todo: handle missing lambda and rise error.
class UsageFactory:
@ -30,12 +28,6 @@ class UsageFactory:
"""
return HftUsageParameters(self._city, self._base_path).enrich_buildings()
def _ca(self):
"""
Enrich the city with Canada usage library
"""
return CaUsageParameters(self._city, self._base_path).enrich_buildings()
def _comnet(self):
"""
Enrich the city with COMNET usage library

View File

@ -0,0 +1,103 @@
"""
Greenery in idf test
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
from pathlib import Path
import csv
import helpers.constants as cte
from unittest import TestCase
from imports.geometry_factory import GeometryFactory
from imports.usage_factory import UsageFactory
from imports.construction_factory import ConstructionFactory
from exports.exports_factory import ExportsFactory
from city_model_structure.greenery.vegetation import Vegetation
from city_model_structure.greenery.soil import Soil
from city_model_structure.greenery.plant import Plant
class GreeneryInIdf(TestCase):
"""
GreeneryInIdf TestCase 1
"""
@staticmethod
def test_greenery_in_idf():
city_file = "../unittests/tests_data/one_building_in_kelowna.gml"
output_path = Path('../unittests/tests_outputs/').resolve()
city = GeometryFactory('citygml', city_file).city
for building in city.buildings:
building.year_of_construction = 2006
ConstructionFactory('nrel', city).enrich()
UsageFactory('comnet', city).enrich()
vegetation_name = 'BaseEco'
soil_thickness = 0.18
soil_name = 'EcoRoofSoil'
roughness = 'MediumSmooth'
dry_conductivity = 0.4
dry_density = 641
dry_specific_heat = 1100
thermal_absorptance = 0.95
solar_absorptance = 0.8
visible_absorptance = 0.7
saturation_volumetric_moisture_content = 0.4
residual_volumetric_moisture_content = 0.01
soil = Soil(soil_name, roughness, dry_conductivity, dry_density, dry_specific_heat, thermal_absorptance,
solar_absorptance, visible_absorptance, saturation_volumetric_moisture_content,
residual_volumetric_moisture_content)
soil.initial_volumetric_moisture_content = 0.2
plant_name = 'plant'
height = 0.5
leaf_area_index = 5
leaf_reflectivity = 0.2
leaf_emissivity = 0.95
minimal_stomatal_resistance = 180
co2_sequestration = 0
grows_on_soils = [soil]
plant = Plant(plant_name, height, leaf_area_index, leaf_reflectivity, leaf_emissivity, minimal_stomatal_resistance,
co2_sequestration, grows_on_soils)
plant.percentage = 1
plants = [plant]
vegetation = Vegetation(vegetation_name, soil, soil_thickness, plants)
for building in city.buildings:
for internal_zone in building.internal_zones:
for thermal_zone in internal_zone.thermal_zones:
for thermal_boundary in thermal_zone.thermal_boundaries:
if thermal_boundary.type == cte.ROOF:
thermal_boundary.vegetation = vegetation
_idf_2 = ExportsFactory('idf', city, output_path).export_debug()
_idf_2.run()
with open((output_path / f'{city.name}_out.csv').resolve()) as f:
reader = csv.reader(f, delimiter=',')
heating = 0
cooling = 0
for row in reader:
if '00:00' in row[0]:
heating += float(row[8]) / 3600000
cooling += float(row[9]) / 3600000
print('With greenery')
print(f'heating: {heating} MWh/yr, cooling: {cooling} MWh/yr')
city = GeometryFactory('citygml', city_file).city
for building in city.buildings:
building.year_of_construction = 2006
ConstructionFactory('nrel', city).enrich()
UsageFactory('comnet', city).enrich()
_idf = ExportsFactory('idf', city, output_path).export()
_idf.run()
with open((output_path / f'{city.name}_out.csv').resolve()) as f:
reader = csv.reader(f, delimiter=',')
heating = 0
cooling = 0
for row in reader:
if '00:00' in row[0]:
heating += float(row[8]) / 3600000
cooling += float(row[9]) / 3600000
print('Without greenery')
print(f'heating: {heating} MWh/yr, cooling: {cooling} MWh/yr')

View File

@ -127,23 +127,6 @@ class TestUsageFactory(TestCase):
self.assertIsNotNone(usage_zone.thermal_control.hvac_availability_schedules,
'control hvac availability is none')
def test_import_ca(self):
"""
Enrich the city with the usage information from canada and verify it
"""
file = 'one_building_in_kelowna.gml'
city = self._get_citygml(file)
UsageFactory('ca', city).enrich()
self._check_buildings(city)
for building in city.buildings:
for internal_zone in building.internal_zones:
self.assertIsNot(len(internal_zone.usage_zones), 0, 'no building usage_zones defined')
for usage_zone in internal_zone.usage_zones:
self._check_usage_zone(usage_zone)
self.assertIsNotNone(usage_zone.mechanical_air_change, 'mechanical air change is none')
self.assertIsNotNone(usage_zone.not_detailed_source_mean_annual_internal_gains,
'not detailed internal gains is none')
def test_import_hft(self):
"""
Enrich the city with the usage information from hft and verify it