Compare commits
No commits in common. "6f9c7397df3689960f1d7c2c9df2717c3401a799" and "0fcc9a2a5fb85fc7749db8b46962f0c6999cf4d2" have entirely different histories.
6f9c7397df
...
0fcc9a2a5f
@ -22,7 +22,6 @@ class EilatCatalog(Catalog):
|
|||||||
"""
|
"""
|
||||||
Eilat catalog class
|
Eilat catalog class
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
_path_archetypes = Path(path / 'eilat_archetypes.json').resolve()
|
_path_archetypes = Path(path / 'eilat_archetypes.json').resolve()
|
||||||
_path_constructions = (path / 'eilat_constructions.json').resolve()
|
_path_constructions = (path / 'eilat_constructions.json').resolve()
|
||||||
@ -122,10 +121,8 @@ class EilatCatalog(Catalog):
|
|||||||
construction_period = archetype['period_of_construction']
|
construction_period = archetype['period_of_construction']
|
||||||
average_storey_height = archetype['average_storey_height']
|
average_storey_height = archetype['average_storey_height']
|
||||||
extra_loses_due_to_thermal_bridges = archetype['extra_loses_due_thermal_bridges']
|
extra_loses_due_to_thermal_bridges = archetype['extra_loses_due_thermal_bridges']
|
||||||
infiltration_rate_for_ventilation_system_off = archetype[
|
infiltration_rate_for_ventilation_system_off = archetype['infiltration_rate_for_ventilation_system_off'] / cte.HOUR_TO_SECONDS
|
||||||
'infiltration_rate_for_ventilation_system_off'] / cte.HOUR_TO_SECONDS
|
infiltration_rate_for_ventilation_system_on = archetype['infiltration_rate_for_ventilation_system_on'] / cte.HOUR_TO_SECONDS
|
||||||
infiltration_rate_for_ventilation_system_on = archetype[
|
|
||||||
'infiltration_rate_for_ventilation_system_on'] / cte.HOUR_TO_SECONDS
|
|
||||||
|
|
||||||
archetype_constructions = []
|
archetype_constructions = []
|
||||||
for archetype_construction in archetype['constructions']:
|
for archetype_construction in archetype['constructions']:
|
||||||
@ -163,9 +160,7 @@ class EilatCatalog(Catalog):
|
|||||||
extra_loses_due_to_thermal_bridges,
|
extra_loses_due_to_thermal_bridges,
|
||||||
None,
|
None,
|
||||||
infiltration_rate_for_ventilation_system_off,
|
infiltration_rate_for_ventilation_system_off,
|
||||||
infiltration_rate_for_ventilation_system_on,
|
infiltration_rate_for_ventilation_system_on))
|
||||||
0,
|
|
||||||
0))
|
|
||||||
return _catalog_archetypes
|
return _catalog_archetypes
|
||||||
|
|
||||||
def names(self, category=None):
|
def names(self, category=None):
|
||||||
|
@ -128,12 +128,6 @@ class NrcanCatalog(Catalog):
|
|||||||
infiltration_rate_for_ventilation_system_on = (
|
infiltration_rate_for_ventilation_system_on = (
|
||||||
archetype['infiltration_rate_for_ventilation_system_on'] / cte.HOUR_TO_SECONDS
|
archetype['infiltration_rate_for_ventilation_system_on'] / cte.HOUR_TO_SECONDS
|
||||||
)
|
)
|
||||||
infiltration_rate_area_for_ventilation_system_off = (
|
|
||||||
archetype['infiltration_rate_area_for_ventilation_system_off'] * 1
|
|
||||||
)
|
|
||||||
infiltration_rate_area_for_ventilation_system_on = (
|
|
||||||
archetype['infiltration_rate_area_for_ventilation_system_on'] * 1
|
|
||||||
)
|
|
||||||
|
|
||||||
archetype_constructions = []
|
archetype_constructions = []
|
||||||
for archetype_construction in archetype['constructions']:
|
for archetype_construction in archetype['constructions']:
|
||||||
@ -159,6 +153,7 @@ class NrcanCatalog(Catalog):
|
|||||||
_window)
|
_window)
|
||||||
archetype_constructions.append(_construction)
|
archetype_constructions.append(_construction)
|
||||||
break
|
break
|
||||||
|
|
||||||
_catalog_archetypes.append(Archetype(archetype_id,
|
_catalog_archetypes.append(Archetype(archetype_id,
|
||||||
name,
|
name,
|
||||||
function,
|
function,
|
||||||
@ -170,10 +165,7 @@ class NrcanCatalog(Catalog):
|
|||||||
extra_loses_due_to_thermal_bridges,
|
extra_loses_due_to_thermal_bridges,
|
||||||
None,
|
None,
|
||||||
infiltration_rate_for_ventilation_system_off,
|
infiltration_rate_for_ventilation_system_off,
|
||||||
infiltration_rate_for_ventilation_system_on,
|
infiltration_rate_for_ventilation_system_on))
|
||||||
infiltration_rate_area_for_ventilation_system_off,
|
|
||||||
infiltration_rate_area_for_ventilation_system_on
|
|
||||||
))
|
|
||||||
return _catalog_archetypes
|
return _catalog_archetypes
|
||||||
|
|
||||||
def names(self, category=None):
|
def names(self, category=None):
|
||||||
|
@ -162,9 +162,7 @@ class NrelCatalog(Catalog):
|
|||||||
extra_loses_due_to_thermal_bridges,
|
extra_loses_due_to_thermal_bridges,
|
||||||
indirect_heated_ratio,
|
indirect_heated_ratio,
|
||||||
infiltration_rate_for_ventilation_system_off,
|
infiltration_rate_for_ventilation_system_off,
|
||||||
infiltration_rate_for_ventilation_system_on,
|
infiltration_rate_for_ventilation_system_on))
|
||||||
0,
|
|
||||||
0))
|
|
||||||
return _catalog_archetypes
|
return _catalog_archetypes
|
||||||
|
|
||||||
def names(self, category=None):
|
def names(self, category=None):
|
||||||
|
@ -23,10 +23,7 @@ class Archetype:
|
|||||||
extra_loses_due_to_thermal_bridges,
|
extra_loses_due_to_thermal_bridges,
|
||||||
indirect_heated_ratio,
|
indirect_heated_ratio,
|
||||||
infiltration_rate_for_ventilation_system_off,
|
infiltration_rate_for_ventilation_system_off,
|
||||||
infiltration_rate_for_ventilation_system_on,
|
infiltration_rate_for_ventilation_system_on):
|
||||||
infiltration_rate_area_for_ventilation_system_off,
|
|
||||||
infiltration_rate_area_for_ventilation_system_on
|
|
||||||
):
|
|
||||||
self._id = archetype_id
|
self._id = archetype_id
|
||||||
self._name = name
|
self._name = name
|
||||||
self._function = function
|
self._function = function
|
||||||
@ -39,8 +36,6 @@ class Archetype:
|
|||||||
self._indirect_heated_ratio = indirect_heated_ratio
|
self._indirect_heated_ratio = indirect_heated_ratio
|
||||||
self._infiltration_rate_for_ventilation_system_off = infiltration_rate_for_ventilation_system_off
|
self._infiltration_rate_for_ventilation_system_off = infiltration_rate_for_ventilation_system_off
|
||||||
self._infiltration_rate_for_ventilation_system_on = infiltration_rate_for_ventilation_system_on
|
self._infiltration_rate_for_ventilation_system_on = infiltration_rate_for_ventilation_system_on
|
||||||
self._infiltration_rate_area_for_ventilation_system_off = infiltration_rate_area_for_ventilation_system_off
|
|
||||||
self._infiltration_rate_area_for_ventilation_system_on = infiltration_rate_area_for_ventilation_system_on
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def id(self):
|
def id(self):
|
||||||
@ -138,22 +133,6 @@ class Archetype:
|
|||||||
"""
|
"""
|
||||||
return self._infiltration_rate_for_ventilation_system_on
|
return self._infiltration_rate_for_ventilation_system_on
|
||||||
|
|
||||||
@property
|
|
||||||
def infiltration_rate_area_for_ventilation_system_off(self):
|
|
||||||
"""
|
|
||||||
Get archetype infiltration rate for ventilation system off in m3/sm2
|
|
||||||
:return: float
|
|
||||||
"""
|
|
||||||
return self._infiltration_rate_area_for_ventilation_system_off
|
|
||||||
|
|
||||||
@property
|
|
||||||
def infiltration_rate_area_for_ventilation_system_on(self):
|
|
||||||
"""
|
|
||||||
Get archetype infiltration rate for ventilation system on in m3/sm2
|
|
||||||
:return: float
|
|
||||||
"""
|
|
||||||
return self._infiltration_rate_for_ventilation_system_on
|
|
||||||
|
|
||||||
def to_dictionary(self):
|
def to_dictionary(self):
|
||||||
"""Class content to dictionary"""
|
"""Class content to dictionary"""
|
||||||
_constructions = []
|
_constructions = []
|
||||||
@ -170,8 +149,6 @@ class Archetype:
|
|||||||
'indirect heated ratio': self.indirect_heated_ratio,
|
'indirect heated ratio': self.indirect_heated_ratio,
|
||||||
'infiltration rate for ventilation off [1/s]': self.infiltration_rate_for_ventilation_system_off,
|
'infiltration rate for ventilation off [1/s]': self.infiltration_rate_for_ventilation_system_off,
|
||||||
'infiltration rate for ventilation on [1/s]': self.infiltration_rate_for_ventilation_system_on,
|
'infiltration rate for ventilation on [1/s]': self.infiltration_rate_for_ventilation_system_on,
|
||||||
'infiltration rate area for ventilation off [m3/sm2]': self.infiltration_rate_area_for_ventilation_system_off,
|
|
||||||
'infiltration rate area for ventilation on [m3/sm2]': self.infiltration_rate_area_for_ventilation_system_on,
|
|
||||||
'constructions': _constructions
|
'constructions': _constructions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,7 +180,7 @@ class Surface:
|
|||||||
@property
|
@property
|
||||||
def global_irradiance(self) -> dict:
|
def global_irradiance(self) -> dict:
|
||||||
"""
|
"""
|
||||||
Get global irradiance on surface in W/m2
|
Get global irradiance on surface in J/m2
|
||||||
:return: dict
|
:return: dict
|
||||||
"""
|
"""
|
||||||
return self._global_irradiance
|
return self._global_irradiance
|
||||||
@ -188,7 +188,7 @@ class Surface:
|
|||||||
@global_irradiance.setter
|
@global_irradiance.setter
|
||||||
def global_irradiance(self, value):
|
def global_irradiance(self, value):
|
||||||
"""
|
"""
|
||||||
Set global irradiance on surface in W/m2
|
Set global irradiance on surface in J/m2
|
||||||
:param value: dict
|
:param value: dict
|
||||||
"""
|
"""
|
||||||
self._global_irradiance = value
|
self._global_irradiance = value
|
||||||
@ -390,7 +390,7 @@ class Surface:
|
|||||||
@property
|
@property
|
||||||
def global_irradiance_tilted(self) -> dict:
|
def global_irradiance_tilted(self) -> dict:
|
||||||
"""
|
"""
|
||||||
Get global irradiance on a tilted surface in W/m2
|
Get global irradiance on a tilted surface in J/m2
|
||||||
:return: dict
|
:return: dict
|
||||||
"""
|
"""
|
||||||
return self._global_irradiance_tilted
|
return self._global_irradiance_tilted
|
||||||
@ -398,7 +398,7 @@ class Surface:
|
|||||||
@global_irradiance_tilted.setter
|
@global_irradiance_tilted.setter
|
||||||
def global_irradiance_tilted(self, value):
|
def global_irradiance_tilted(self, value):
|
||||||
"""
|
"""
|
||||||
Set global irradiance on a tilted surface in W/m2
|
Set global irradiance on a tilted surface in J/m2
|
||||||
:param value: dict
|
:param value: dict
|
||||||
"""
|
"""
|
||||||
self._global_irradiance_tilted = value
|
self._global_irradiance_tilted = value
|
||||||
|
@ -20,8 +20,6 @@ class ThermalArchetype:
|
|||||||
self._indirect_heated_ratio = None
|
self._indirect_heated_ratio = None
|
||||||
self._infiltration_rate_for_ventilation_system_off = None
|
self._infiltration_rate_for_ventilation_system_off = None
|
||||||
self._infiltration_rate_for_ventilation_system_on = None
|
self._infiltration_rate_for_ventilation_system_on = None
|
||||||
self._infiltration_rate_area_for_ventilation_system_off=None
|
|
||||||
self._infiltration_rate_area_for_ventilation_system_on=None
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def constructions(self) -> [Construction]:
|
def constructions(self) -> [Construction]:
|
||||||
@ -134,35 +132,3 @@ class ThermalArchetype:
|
|||||||
:param value: float
|
:param value: float
|
||||||
"""
|
"""
|
||||||
self._infiltration_rate_for_ventilation_system_on = value
|
self._infiltration_rate_for_ventilation_system_on = value
|
||||||
|
|
||||||
@property
|
|
||||||
def infiltration_rate_area_for_ventilation_system_off(self):
|
|
||||||
"""
|
|
||||||
Get infiltration rate for ventilation system off in l/s/m2
|
|
||||||
:return: float
|
|
||||||
"""
|
|
||||||
return self._infiltration_rate_for_ventilation_system_off
|
|
||||||
|
|
||||||
@infiltration_rate_area_for_ventilation_system_off.setter
|
|
||||||
def infiltration_rate_area_for_ventilation_system_off(self, value):
|
|
||||||
"""
|
|
||||||
Set infiltration rate for ventilation system off in l/s/m2
|
|
||||||
:param value: float
|
|
||||||
"""
|
|
||||||
self._infiltration_rate_for_ventilation_system_off = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def infiltration_rate_area_for_ventilation_system_on(self):
|
|
||||||
"""
|
|
||||||
Get infiltration rate for ventilation system on in l/s/m2
|
|
||||||
:return: float
|
|
||||||
"""
|
|
||||||
return self._infiltration_rate_for_ventilation_system_on
|
|
||||||
|
|
||||||
@infiltration_rate_area_for_ventilation_system_on.setter
|
|
||||||
def infiltration_rate_area_for_ventilation_system_on(self, value):
|
|
||||||
"""
|
|
||||||
Set infiltration rate for ventilation system on in l/s/m2
|
|
||||||
:param value: float
|
|
||||||
"""
|
|
||||||
self._infiltration_rate_for_ventilation_system_on = value
|
|
||||||
|
@ -44,8 +44,6 @@ class ThermalZone:
|
|||||||
self._indirectly_heated_area_ratio = None
|
self._indirectly_heated_area_ratio = None
|
||||||
self._infiltration_rate_system_on = None
|
self._infiltration_rate_system_on = None
|
||||||
self._infiltration_rate_system_off = None
|
self._infiltration_rate_system_off = None
|
||||||
self._infiltration_rate_area_system_on = None
|
|
||||||
self._infiltration_rate_area_system_off = None
|
|
||||||
self._volume = volume
|
self._volume = volume
|
||||||
self._ordinate_number = None
|
self._ordinate_number = None
|
||||||
self._view_factors_matrix = None
|
self._view_factors_matrix = None
|
||||||
@ -168,24 +166,6 @@ class ThermalZone:
|
|||||||
self._infiltration_rate_system_off = self._parent_internal_zone.thermal_archetype.infiltration_rate_for_ventilation_system_off
|
self._infiltration_rate_system_off = self._parent_internal_zone.thermal_archetype.infiltration_rate_for_ventilation_system_off
|
||||||
return self._infiltration_rate_system_off
|
return self._infiltration_rate_system_off
|
||||||
|
|
||||||
@property
|
|
||||||
def infiltration_rate_area_system_on(self):
|
|
||||||
"""
|
|
||||||
Get thermal zone infiltration rate system on in air changes per second (1/s)
|
|
||||||
:return: None or float
|
|
||||||
"""
|
|
||||||
self._infiltration_rate_area_system_on = self._parent_internal_zone.thermal_archetype.infiltration_rate_area_for_ventilation_system_on
|
|
||||||
return self._infiltration_rate_area_system_on
|
|
||||||
|
|
||||||
@property
|
|
||||||
def infiltration_rate_area_system_off(self):
|
|
||||||
"""
|
|
||||||
Get thermal zone infiltration rate system off in air changes per second (1/s)
|
|
||||||
:return: None or float
|
|
||||||
"""
|
|
||||||
self._infiltration_rate_area_system_off = self._parent_internal_zone.thermal_archetype.infiltration_rate_area_for_ventilation_system_off
|
|
||||||
return self._infiltration_rate_area_system_off
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def volume(self):
|
def volume(self):
|
||||||
"""
|
"""
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -331,7 +331,6 @@ class Idf:
|
|||||||
for material in self._idf.idfobjects[self._MATERIAL]:
|
for material in self._idf.idfobjects[self._MATERIAL]:
|
||||||
if material.Name == "DefaultMaterial":
|
if material.Name == "DefaultMaterial":
|
||||||
return
|
return
|
||||||
|
|
||||||
self._idf.set_default_constructions()
|
self._idf.set_default_constructions()
|
||||||
return
|
return
|
||||||
for layer in thermal_boundary.layers:
|
for layer in thermal_boundary.layers:
|
||||||
@ -393,9 +392,9 @@ class Idf:
|
|||||||
thermostat = self._add_thermostat(thermal_zone)
|
thermostat = self._add_thermostat(thermal_zone)
|
||||||
self._idf.newidfobject(self._IDEAL_LOAD_AIR_SYSTEM,
|
self._idf.newidfobject(self._IDEAL_LOAD_AIR_SYSTEM,
|
||||||
Zone_Name=zone_name,
|
Zone_Name=zone_name,
|
||||||
System_Availability_Schedule_Name=f'Thermostat_availability schedules {thermal_zone.usage_name}',
|
System_Availability_Schedule_Name=f'HVAC AVAIL SCHEDULES {thermal_zone.usage_name}',
|
||||||
Heating_Availability_Schedule_Name=f'Thermostat_availability schedules {thermal_zone.usage_name}',
|
Heating_Availability_Schedule_Name=f'HVAC AVAIL SCHEDULES {thermal_zone.usage_name}',
|
||||||
Cooling_Availability_Schedule_Name=f'Thermostat_availability schedules {thermal_zone.usage_name}',
|
Cooling_Availability_Schedule_Name=f'HVAC AVAIL SCHEDULES {thermal_zone.usage_name}',
|
||||||
Template_Thermostat_Name=thermostat.Name)
|
Template_Thermostat_Name=thermostat.Name)
|
||||||
|
|
||||||
def _add_occupancy(self, thermal_zone, zone_name):
|
def _add_occupancy(self, thermal_zone, zone_name):
|
||||||
@ -455,7 +454,7 @@ class Idf:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _add_infiltration(self, thermal_zone, zone_name):
|
def _add_infiltration(self, thermal_zone, zone_name):
|
||||||
schedule = f'INF_CONST schedules {thermal_zone.usage_name}'
|
schedule = f'Infiltration schedules {thermal_zone.usage_name}'
|
||||||
_infiltration = thermal_zone.infiltration_rate_system_off * cte.HOUR_TO_SECONDS
|
_infiltration = thermal_zone.infiltration_rate_system_off * cte.HOUR_TO_SECONDS
|
||||||
self._idf.newidfobject(self._INFILTRATION,
|
self._idf.newidfobject(self._INFILTRATION,
|
||||||
Name=f'{zone_name}_infiltration',
|
Name=f'{zone_name}_infiltration',
|
||||||
@ -465,17 +464,6 @@ class Idf:
|
|||||||
Air_Changes_per_Hour=_infiltration
|
Air_Changes_per_Hour=_infiltration
|
||||||
)
|
)
|
||||||
|
|
||||||
def _add_infiltration_surface(self, thermal_zone, zone_name):
|
|
||||||
schedule = f'INF_CONST schedules {thermal_zone.usage_name}'
|
|
||||||
_infiltration = thermal_zone.infiltration_rate_area_system_off*1
|
|
||||||
self._idf.newidfobject(self._INFILTRATION,
|
|
||||||
Name=f'{zone_name}_infiltration',
|
|
||||||
Zone_or_ZoneList_or_Space_or_SpaceList_Name=zone_name,
|
|
||||||
Schedule_Name=schedule,
|
|
||||||
Design_Flow_Rate_Calculation_Method='Flow/ExteriorWallArea',
|
|
||||||
Flow_Rate_per_Exterior_Surface_Area=_infiltration
|
|
||||||
)
|
|
||||||
|
|
||||||
def _add_ventilation(self, thermal_zone, zone_name):
|
def _add_ventilation(self, thermal_zone, zone_name):
|
||||||
schedule = f'Ventilation schedules {thermal_zone.usage_name}'
|
schedule = f'Ventilation schedules {thermal_zone.usage_name}'
|
||||||
_air_change = thermal_zone.mechanical_air_change * cte.HOUR_TO_SECONDS
|
_air_change = thermal_zone.mechanical_air_change * cte.HOUR_TO_SECONDS
|
||||||
@ -561,12 +549,9 @@ class Idf:
|
|||||||
self._add_schedules(usage, 'DHW_prof', thermal_zone.domestic_hot_water.schedules)
|
self._add_schedules(usage, 'DHW_prof', thermal_zone.domestic_hot_water.schedules)
|
||||||
_new_schedules = self._create_yearly_values_schedules('cold_temp', building.cold_water_temperature[cte.HOUR])
|
_new_schedules = self._create_yearly_values_schedules('cold_temp', building.cold_water_temperature[cte.HOUR])
|
||||||
self._add_schedules(usage, 'cold_temp', _new_schedules)
|
self._add_schedules(usage, 'cold_temp', _new_schedules)
|
||||||
|
|
||||||
_new_schedules = self._create_constant_value_schedules('DHW_temp', service_temperature)
|
_new_schedules = self._create_constant_value_schedules('DHW_temp', service_temperature)
|
||||||
self._add_schedules(usage, 'DHW_temp', _new_schedules)
|
self._add_schedules(usage, 'DHW_temp', _new_schedules)
|
||||||
_new_schedules = self._create_constant_value_schedules('INF_CONST', 1)
|
|
||||||
self._add_schedules(usage, 'INF_CONST', _new_schedules)
|
|
||||||
_new_schedules = self._create_constant_value_schedules('Thermostat_availability', 1)
|
|
||||||
self._add_schedules(usage, 'Thermostat_availability', _new_schedules)
|
|
||||||
_occ = thermal_zone.occupancy
|
_occ = thermal_zone.occupancy
|
||||||
if _occ.occupancy_density == 0:
|
if _occ.occupancy_density == 0:
|
||||||
_total_heat = 0
|
_total_heat = 0
|
||||||
@ -577,7 +562,7 @@ class Idf:
|
|||||||
self._add_schedules(usage, 'Activity Level', _new_schedules)
|
self._add_schedules(usage, 'Activity Level', _new_schedules)
|
||||||
self._add_zone(thermal_zone, building.name)
|
self._add_zone(thermal_zone, building.name)
|
||||||
self._add_heating_system(thermal_zone, building.name)
|
self._add_heating_system(thermal_zone, building.name)
|
||||||
self._add_infiltration_surface(thermal_zone, building.name)
|
self._add_infiltration(thermal_zone, building.name)
|
||||||
self._add_ventilation(thermal_zone, building.name)
|
self._add_ventilation(thermal_zone, building.name)
|
||||||
self._add_occupancy(thermal_zone, building.name)
|
self._add_occupancy(thermal_zone, building.name)
|
||||||
self._add_lighting(thermal_zone, building.name)
|
self._add_lighting(thermal_zone, building.name)
|
||||||
@ -626,18 +611,6 @@ class Idf:
|
|||||||
Reporting_Frequency="Hourly",
|
Reporting_Frequency="Hourly",
|
||||||
)
|
)
|
||||||
|
|
||||||
self._idf.newidfobject(
|
|
||||||
"OUTPUT:VARIABLE",
|
|
||||||
Variable_Name="Zone Air Temperature",
|
|
||||||
Reporting_Frequency="Hourly",
|
|
||||||
)
|
|
||||||
|
|
||||||
self._idf.newidfobject(
|
|
||||||
"OUTPUT:VARIABLE",
|
|
||||||
Variable_Name="Zone Air Relative Humidity",
|
|
||||||
Reporting_Frequency="Hourly",
|
|
||||||
)
|
|
||||||
|
|
||||||
# post-process to erase windows associated to adiabatic walls
|
# post-process to erase windows associated to adiabatic walls
|
||||||
windows_list = []
|
windows_list = []
|
||||||
for window in self._idf.idfobjects[self._WINDOW]:
|
for window in self._idf.idfobjects[self._WINDOW]:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
!IDD_Version 24.1.0
|
!IDD_Version 23.2.0
|
||||||
!IDD_BUILD 7636e6b3e9
|
!IDD_BUILD 7636e6b3e9
|
||||||
! ***************************************************************************
|
! ***************************************************************************
|
||||||
! This file is the Input Data Dictionary (IDD) for EnergyPlus.
|
! This file is the Input Data Dictionary (IDD) for EnergyPlus.
|
||||||
@ -30002,10 +30002,10 @@ People,
|
|||||||
A7 , \field Mean Radiant Temperature Calculation Type
|
A7 , \field Mean Radiant Temperature Calculation Type
|
||||||
\note optional (only required for thermal comfort runs)
|
\note optional (only required for thermal comfort runs)
|
||||||
\type choice
|
\type choice
|
||||||
\key EnclosureAveraged
|
\key ZoneAveraged
|
||||||
\key SurfaceWeighted
|
\key SurfaceWeighted
|
||||||
\key AngleFactor
|
\key AngleFactor
|
||||||
\default EnclosureAveraged
|
\default ZoneAveraged
|
||||||
A8 , \field Surface Name/Angle Factor List Name
|
A8 , \field Surface Name/Angle Factor List Name
|
||||||
\type object-list
|
\type object-list
|
||||||
\object-list AllHeatTranAngFacNames
|
\object-list AllHeatTranAngFacNames
|
||||||
|
@ -20,10 +20,9 @@ class EnergyBuildingsExportsFactory:
|
|||||||
"""
|
"""
|
||||||
Energy Buildings exports factory class
|
Energy Buildings exports factory class
|
||||||
"""
|
"""
|
||||||
def __init__(self, handler, city, path, custom_insel_block='d18599', target_buildings=None, weather_file=None):
|
def __init__(self, handler, city, path, custom_insel_block='d18599', target_buildings=None):
|
||||||
self._city = city
|
self._city = city
|
||||||
self._export_type = '_' + handler.lower()
|
self._export_type = '_' + handler.lower()
|
||||||
self._weather_file = weather_file
|
|
||||||
validate_import_export_type(EnergyBuildingsExportsFactory, handler)
|
validate_import_export_type(EnergyBuildingsExportsFactory, handler)
|
||||||
if isinstance(path, str):
|
if isinstance(path, str):
|
||||||
path = Path(path)
|
path = Path(path)
|
||||||
@ -54,13 +53,12 @@ class EnergyBuildingsExportsFactory:
|
|||||||
"""
|
"""
|
||||||
idf_data_path = (Path(__file__).parent / './building_energy/idf_files/').resolve()
|
idf_data_path = (Path(__file__).parent / './building_energy/idf_files/').resolve()
|
||||||
url = wh().epw_file(self._city.region_code)
|
url = wh().epw_file(self._city.region_code)
|
||||||
if self._weather_file is None:
|
weather_path = (Path(__file__).parent.parent / f'data/weather/epw/{url.rsplit("/", 1)[1]}').resolve()
|
||||||
self._weather_file = (Path(__file__).parent.parent / f'data/weather/epw/{url.rsplit("/", 1)[1]}').resolve()
|
if not weather_path.exists():
|
||||||
if not self._weather_file.exists():
|
with open(weather_path, 'wb') as epw_file:
|
||||||
with open(self._weather_file, 'wb') as epw_file:
|
|
||||||
epw_file.write(requests.get(url, allow_redirects=True).content)
|
epw_file.write(requests.get(url, allow_redirects=True).content)
|
||||||
return Idf(self._city, self._path, (idf_data_path / 'Minimal.idf'), (idf_data_path / 'Energy+.idd'),
|
return Idf(self._city, self._path, (idf_data_path / 'Minimal.idf'), (idf_data_path / 'Energy+.idd'), weather_path,
|
||||||
self._weather_file, target_buildings=self._target_buildings)
|
target_buildings=self._target_buildings)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _insel_monthly_energy_balance(self):
|
def _insel_monthly_energy_balance(self):
|
||||||
|
@ -25,7 +25,6 @@ KILO_WATTS_HOUR_TO_JULES = 3600000
|
|||||||
WATTS_HOUR_TO_JULES = 3600
|
WATTS_HOUR_TO_JULES = 3600
|
||||||
GALLONS_TO_QUBIC_METERS = 0.0037854117954011185
|
GALLONS_TO_QUBIC_METERS = 0.0037854117954011185
|
||||||
|
|
||||||
|
|
||||||
# time
|
# time
|
||||||
SECOND = 'second'
|
SECOND = 'second'
|
||||||
MINUTE = 'minute'
|
MINUTE = 'minute'
|
||||||
|
@ -3,7 +3,6 @@ NrcanPhysicsParameters import the construction and material information defined
|
|||||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
Copyright © 2022 Concordia CERC group
|
Copyright © 2022 Concordia CERC group
|
||||||
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
Project Collaborator Saeed Ranjbar saeed.ranjbar@concordia.ca
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
@ -33,21 +32,10 @@ class NrcanPhysicsParameters:
|
|||||||
city = self._city
|
city = self._city
|
||||||
nrcan_catalog = ConstructionCatalogFactory('nrcan').catalog
|
nrcan_catalog = ConstructionCatalogFactory('nrcan').catalog
|
||||||
for building in city.buildings:
|
for building in city.buildings:
|
||||||
main_function = None
|
if building.function not in Dictionaries().hub_function_to_nrcan_construction_function:
|
||||||
functions = building.function.split('_')
|
logging.error('Building %s has an unknown building function %s', building.name, building.function)
|
||||||
if len(functions) > 1:
|
|
||||||
maximum_percentage = 0
|
|
||||||
for function in functions:
|
|
||||||
percentage_and_function = function.split('-')
|
|
||||||
if float(percentage_and_function[0]) > maximum_percentage:
|
|
||||||
maximum_percentage = float(percentage_and_function[0])
|
|
||||||
main_function = percentage_and_function[-1]
|
|
||||||
else:
|
|
||||||
main_function = functions[-1]
|
|
||||||
if main_function not in Dictionaries().hub_function_to_nrcan_construction_function:
|
|
||||||
logging.error('Building %s has an unknown building function %s', building.name, main_function)
|
|
||||||
continue
|
continue
|
||||||
function = Dictionaries().hub_function_to_nrcan_construction_function[main_function]
|
function = Dictionaries().hub_function_to_nrcan_construction_function[building.function]
|
||||||
try:
|
try:
|
||||||
archetype = self._search_archetype(nrcan_catalog, function, building.year_of_construction, self._climate_zone)
|
archetype = self._search_archetype(nrcan_catalog, function, building.year_of_construction, self._climate_zone)
|
||||||
|
|
||||||
@ -79,8 +67,6 @@ class NrcanPhysicsParameters:
|
|||||||
thermal_archetype.indirect_heated_ratio = 0
|
thermal_archetype.indirect_heated_ratio = 0
|
||||||
thermal_archetype.infiltration_rate_for_ventilation_system_on = catalog_archetype.infiltration_rate_for_ventilation_system_on
|
thermal_archetype.infiltration_rate_for_ventilation_system_on = catalog_archetype.infiltration_rate_for_ventilation_system_on
|
||||||
thermal_archetype.infiltration_rate_for_ventilation_system_off = catalog_archetype.infiltration_rate_for_ventilation_system_off
|
thermal_archetype.infiltration_rate_for_ventilation_system_off = catalog_archetype.infiltration_rate_for_ventilation_system_off
|
||||||
thermal_archetype.infiltration_rate_area_for_ventilation_system_on = catalog_archetype.infiltration_rate_area_for_ventilation_system_on
|
|
||||||
thermal_archetype.infiltration_rate_area_for_ventilation_system_off = catalog_archetype.infiltration_rate_area_for_ventilation_system_off
|
|
||||||
_constructions = []
|
_constructions = []
|
||||||
for catalog_construction in catalog_archetype.constructions:
|
for catalog_construction in catalog_archetype.constructions:
|
||||||
construction = Construction()
|
construction = Construction()
|
||||||
|
@ -127,27 +127,6 @@ class Geojson:
|
|||||||
function = None
|
function = None
|
||||||
if self._function_field is not None:
|
if self._function_field is not None:
|
||||||
function = str(feature['properties'][self._function_field])
|
function = str(feature['properties'][self._function_field])
|
||||||
if function == 'Mixed use' or function == 'mixed use':
|
|
||||||
function_parts = []
|
|
||||||
if 'usages' in feature['properties']:
|
|
||||||
usages = feature['properties']['usages']
|
|
||||||
for usage in usages:
|
|
||||||
if self._function_to_hub is not None and usage['usage'] in self._function_to_hub:
|
|
||||||
function_parts.append(f"{usage['percentage']}-{self._function_to_hub[usage['usage']]}")
|
|
||||||
else:
|
|
||||||
function_parts.append(f"{usage['percentage']}-{usage['usage']}")
|
|
||||||
else:
|
|
||||||
for key, value in feature['properties'].items():
|
|
||||||
if key.startswith("mixed_type_") and not key.endswith("_percentage"):
|
|
||||||
type_key = key
|
|
||||||
percentage_key = f"{key}_percentage"
|
|
||||||
if percentage_key in feature['properties']:
|
|
||||||
if self._function_to_hub is not None and feature['properties'][type_key] in self._function_to_hub:
|
|
||||||
usage_function = self._function_to_hub[feature['properties'][type_key]]
|
|
||||||
function_parts.append(f"{feature['properties'][percentage_key]}-{usage_function}")
|
|
||||||
else:
|
|
||||||
function_parts.append(f"{feature['properties'][percentage_key]}-{feature['properties'][type_key]}")
|
|
||||||
function = "_".join(function_parts)
|
|
||||||
if self._function_to_hub is not None:
|
if self._function_to_hub is not None:
|
||||||
# use the transformation dictionary to retrieve the proper function
|
# use the transformation dictionary to retrieve the proper function
|
||||||
if function in self._function_to_hub:
|
if function in self._function_to_hub:
|
||||||
|
@ -24,7 +24,7 @@ class EnergyPlusMultipleBuildings:
|
|||||||
csv_output = list(csv.DictReader(csv_file))
|
csv_output = list(csv.DictReader(csv_file))
|
||||||
|
|
||||||
for building in self._city.buildings:
|
for building in self._city.buildings:
|
||||||
building_name = building.name.upper()
|
building_name = building.name
|
||||||
buildings_energy_demands[f'Building {building_name} Heating Demand (J)'] = [
|
buildings_energy_demands[f'Building {building_name} Heating Demand (J)'] = [
|
||||||
float(
|
float(
|
||||||
row[f"{building_name} IDEAL LOADS AIR SYSTEM:Zone Ideal Loads Supply Air Total Heating Energy [J](Hourly)"])
|
row[f"{building_name} IDEAL LOADS AIR SYSTEM:Zone Ideal Loads Supply Air Total Heating Energy [J](Hourly)"])
|
||||||
@ -36,7 +36,7 @@ class EnergyPlusMultipleBuildings:
|
|||||||
for row in csv_output
|
for row in csv_output
|
||||||
]
|
]
|
||||||
buildings_energy_demands[f'Building {building_name} DHW Demand (W)'] = [
|
buildings_energy_demands[f'Building {building_name} DHW Demand (W)'] = [
|
||||||
float(row[f"DHW {building_name}:Water Use Equipment Heating Rate [W](Hourly)"])
|
float(row[f"DHW {building.name}:Water Use Equipment Heating Rate [W](Hourly)"])
|
||||||
for row in csv_output
|
for row in csv_output
|
||||||
]
|
]
|
||||||
buildings_energy_demands[f'Building {building_name} Appliances (W)'] = [
|
buildings_energy_demands[f'Building {building_name} Appliances (W)'] = [
|
||||||
@ -58,15 +58,14 @@ class EnergyPlusMultipleBuildings:
|
|||||||
if energy_plus_output_file_path.is_file():
|
if energy_plus_output_file_path.is_file():
|
||||||
building_energy_demands = self._building_energy_demands(energy_plus_output_file_path)
|
building_energy_demands = self._building_energy_demands(energy_plus_output_file_path)
|
||||||
for building in self._city.buildings:
|
for building in self._city.buildings:
|
||||||
building_name = building.name.upper()
|
building.heating_demand[cte.HOUR] = building_energy_demands[f'Building {building.name} Heating Demand (J)']
|
||||||
building.heating_demand[cte.HOUR] = building_energy_demands[f'Building {building_name} Heating Demand (J)']
|
building.cooling_demand[cte.HOUR] = building_energy_demands[f'Building {building.name} Cooling Demand (J)']
|
||||||
building.cooling_demand[cte.HOUR] = building_energy_demands[f'Building {building_name} Cooling Demand (J)']
|
|
||||||
building.domestic_hot_water_heat_demand[cte.HOUR] = \
|
building.domestic_hot_water_heat_demand[cte.HOUR] = \
|
||||||
[x * cte.WATTS_HOUR_TO_JULES for x in building_energy_demands[f'Building {building_name} DHW Demand (W)']]
|
[x * cte.WATTS_HOUR_TO_JULES for x in building_energy_demands[f'Building {building.name} DHW Demand (W)']]
|
||||||
building.appliances_electrical_demand[cte.HOUR] = \
|
building.appliances_electrical_demand[cte.HOUR] = \
|
||||||
[x * cte.WATTS_HOUR_TO_JULES for x in building_energy_demands[f'Building {building_name} Appliances (W)']]
|
[x * cte.WATTS_HOUR_TO_JULES for x in building_energy_demands[f'Building {building.name} Appliances (W)']]
|
||||||
building.lighting_electrical_demand[cte.HOUR] = \
|
building.lighting_electrical_demand[cte.HOUR] = \
|
||||||
[x * cte.WATTS_HOUR_TO_JULES for x in building_energy_demands[f'Building {building_name} Lighting (W)']]
|
[x * cte.WATTS_HOUR_TO_JULES for x in building_energy_demands[f'Building {building.name} Lighting (W)']]
|
||||||
building.heating_demand[cte.MONTH] = MonthlyValues.get_total_month(building.heating_demand[cte.HOUR])
|
building.heating_demand[cte.MONTH] = MonthlyValues.get_total_month(building.heating_demand[cte.HOUR])
|
||||||
building.cooling_demand[cte.MONTH] = MonthlyValues.get_total_month(building.cooling_demand[cte.HOUR])
|
building.cooling_demand[cte.MONTH] = MonthlyValues.get_total_month(building.cooling_demand[cte.HOUR])
|
||||||
building.domestic_hot_water_heat_demand[cte.MONTH] = (
|
building.domestic_hot_water_heat_demand[cte.MONTH] = (
|
||||||
|
@ -34,7 +34,7 @@ class SimplifiedRadiosityAlgorithm:
|
|||||||
for key in self._results:
|
for key in self._results:
|
||||||
_irradiance = {}
|
_irradiance = {}
|
||||||
header_name = key.split(':')
|
header_name = key.split(':')
|
||||||
result = [x for x in self._results[key]]
|
result = [x * cte.WATTS_HOUR_TO_JULES for x in self._results[key]]
|
||||||
city_object_name = header_name[1]
|
city_object_name = header_name[1]
|
||||||
building = self._city.city_object(city_object_name)
|
building = self._city.city_object(city_object_name)
|
||||||
surface_id = header_name[2]
|
surface_id = header_name[2]
|
||||||
|
@ -3,7 +3,6 @@ ComnetUsageParameters extracts the usage properties from Comnet catalog and assi
|
|||||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
Copyright © 2022 Concordia CERC group
|
Copyright © 2022 Concordia CERC group
|
||||||
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
Project Collaborator Saeed Ranjbar saeed.ranjbar@concordia.ca
|
|
||||||
"""
|
"""
|
||||||
import copy
|
import copy
|
||||||
import logging
|
import logging
|
||||||
@ -19,8 +18,6 @@ from hub.city_model_structure.building_demand.domestic_hot_water import Domestic
|
|||||||
from hub.city_model_structure.attributes.schedule import Schedule
|
from hub.city_model_structure.attributes.schedule import Schedule
|
||||||
from hub.city_model_structure.building_demand.internal_gain import InternalGain
|
from hub.city_model_structure.building_demand.internal_gain import InternalGain
|
||||||
from hub.catalog_factories.usage_catalog_factory import UsageCatalogFactory
|
from hub.catalog_factories.usage_catalog_factory import UsageCatalogFactory
|
||||||
from hub.catalog_factories.construction_catalog_factory import ConstructionCatalogFactory
|
|
||||||
from hub.imports.construction.helpers.construction_helper import ConstructionHelper
|
|
||||||
|
|
||||||
|
|
||||||
class ComnetUsageParameters:
|
class ComnetUsageParameters:
|
||||||
@ -38,62 +35,29 @@ class ComnetUsageParameters:
|
|||||||
city = self._city
|
city = self._city
|
||||||
comnet_catalog = UsageCatalogFactory('comnet').catalog
|
comnet_catalog = UsageCatalogFactory('comnet').catalog
|
||||||
for building in city.buildings:
|
for building in city.buildings:
|
||||||
usages = []
|
usage_name = Dictionaries().hub_usage_to_comnet_usage[building.function]
|
||||||
comnet_archetype_usages = []
|
try:
|
||||||
building_functions = building.function.split('_')
|
archetype_usage = self._search_archetypes(comnet_catalog, usage_name)
|
||||||
for function in building_functions:
|
except KeyError:
|
||||||
usages.append(function.split('-'))
|
logging.error('Building %s has unknown usage archetype for usage %s', building.name, usage_name)
|
||||||
for usage in usages:
|
continue
|
||||||
comnet_usage_name = Dictionaries().hub_usage_to_comnet_usage[usage[-1]]
|
|
||||||
try:
|
for internal_zone in building.internal_zones:
|
||||||
comnet_archetype_usage = self._search_archetypes(comnet_catalog, comnet_usage_name)
|
if internal_zone.area is None:
|
||||||
comnet_archetype_usages.append(comnet_archetype_usage)
|
raise TypeError('Internal zone area not defined, ACH cannot be calculated')
|
||||||
except KeyError:
|
if internal_zone.volume is None:
|
||||||
logging.error('Building %s has unknown usage archetype for usage %s', building.name, comnet_usage_name)
|
raise TypeError('Internal zone volume not defined, ACH cannot be calculated')
|
||||||
continue
|
if internal_zone.area <= 0:
|
||||||
for (i, internal_zone) in enumerate(building.internal_zones):
|
raise TypeError('Internal zone area is zero, ACH cannot be calculated')
|
||||||
internal_zone_usages = []
|
volume_per_area = internal_zone.volume / internal_zone.area
|
||||||
if len(building.internal_zones) > 1:
|
usage = Usage()
|
||||||
volume_per_area = 0
|
usage.name = usage_name
|
||||||
if internal_zone.area is None:
|
self._assign_values(usage, archetype_usage, volume_per_area, building.cold_water_temperature)
|
||||||
logging.error('Building %s has internal zone area not defined, ACH cannot be calculated for usage %s',
|
usage.percentage = 1
|
||||||
building.name, usages[i][-1])
|
self._calculate_reduced_values_from_extended_library(usage, archetype_usage)
|
||||||
continue
|
|
||||||
if internal_zone.volume is None:
|
internal_zone.usages = [usage]
|
||||||
logging.error('Building %s has internal zone volume not defined, ACH cannot be calculated for usage %s',
|
|
||||||
building.name, usages[i][-1])
|
|
||||||
continue
|
|
||||||
if internal_zone.area <= 0:
|
|
||||||
logging.error('Building %s has internal zone area equal to 0, ACH cannot be calculated for usage %s',
|
|
||||||
building.name, usages[i][-1])
|
|
||||||
continue
|
|
||||||
volume_per_area += internal_zone.volume / internal_zone.area
|
|
||||||
usage = Usage()
|
|
||||||
usage.name = usages[i][-1]
|
|
||||||
self._assign_values(usage, comnet_archetype_usages[i], volume_per_area, building.cold_water_temperature)
|
|
||||||
usage.percentage = 1
|
|
||||||
self._calculate_reduced_values_from_extended_library(usage, comnet_archetype_usages[i])
|
|
||||||
internal_zone_usages.append(usage)
|
|
||||||
else:
|
|
||||||
storeys_above_ground = building.storeys_above_ground
|
|
||||||
if storeys_above_ground is None:
|
|
||||||
logging.error('Building %s no number of storeys assigned, ACH cannot be calculated for usage %s. '
|
|
||||||
'NRCAN construction data for the year %s is used to calculated number of storeys above '
|
|
||||||
'ground', building.name, usages, building.year_of_construction)
|
|
||||||
storeys_above_ground = self.average_storey_height_calculator(self._city, building)
|
|
||||||
volume_per_area = building.volume / building.floor_area / storeys_above_ground
|
|
||||||
for (j, mixed_usage) in enumerate(usages):
|
|
||||||
usage = Usage()
|
|
||||||
usage.name = mixed_usage[-1]
|
|
||||||
if len(usages) > 1:
|
|
||||||
usage.percentage = float(mixed_usage[0]) / 100
|
|
||||||
else:
|
|
||||||
usage.percentage = 1
|
|
||||||
self._assign_values(usage, comnet_archetype_usages[j], volume_per_area, building.cold_water_temperature)
|
|
||||||
self._calculate_reduced_values_from_extended_library(usage, comnet_archetype_usages[j])
|
|
||||||
internal_zone_usages.append(usage)
|
|
||||||
|
|
||||||
internal_zone.usages = internal_zone_usages
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _search_archetypes(comnet_catalog, usage_name):
|
def _search_archetypes(comnet_catalog, usage_name):
|
||||||
comnet_archetypes = comnet_catalog.entries('archetypes').usages
|
comnet_archetypes = comnet_catalog.entries('archetypes').usages
|
||||||
@ -265,37 +229,3 @@ class ComnetUsageParameters:
|
|||||||
_mean_internal_gain.schedules = _schedules
|
_mean_internal_gain.schedules = _schedules
|
||||||
|
|
||||||
return [_mean_internal_gain]
|
return [_mean_internal_gain]
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def average_storey_height_calculator(city, building):
|
|
||||||
climate_zone = ConstructionHelper.city_to_nrcan_climate_zone(city.climate_reference_city)
|
|
||||||
nrcan_catalog = ConstructionCatalogFactory('nrcan').catalog
|
|
||||||
main_function = None
|
|
||||||
functions = building.function.split('_')
|
|
||||||
if len(functions) > 1:
|
|
||||||
maximum_percentage = 0
|
|
||||||
for function in functions:
|
|
||||||
percentage_and_function = function.split('-')
|
|
||||||
if float(percentage_and_function[0]) > maximum_percentage:
|
|
||||||
maximum_percentage = float(percentage_and_function[0])
|
|
||||||
main_function = percentage_and_function[-1]
|
|
||||||
else:
|
|
||||||
main_function = functions[-1]
|
|
||||||
if main_function not in Dictionaries().hub_function_to_nrcan_construction_function:
|
|
||||||
logging.error('Building %s has an unknown building function %s', building.name, main_function)
|
|
||||||
function = Dictionaries().hub_function_to_nrcan_construction_function[main_function]
|
|
||||||
construction_archetype = None
|
|
||||||
average_storey_height = None
|
|
||||||
nrcan_archetypes = nrcan_catalog.entries('archetypes')
|
|
||||||
for building_archetype in nrcan_archetypes:
|
|
||||||
construction_period_limits = building_archetype.construction_period.split('_')
|
|
||||||
if int(construction_period_limits[0]) <= int(building.year_of_construction) <= int(construction_period_limits[1]):
|
|
||||||
if str(function) == str(building_archetype.function) and climate_zone == str(building_archetype.climate_zone):
|
|
||||||
construction_archetype = building_archetype
|
|
||||||
average_storey_height = building_archetype.average_storey_height
|
|
||||||
if construction_archetype is None:
|
|
||||||
logging.error('Building %s has unknown construction archetype for building function: %s '
|
|
||||||
'[%s], building year of construction: %s and climate zone %s', building.name, function,
|
|
||||||
building.function, building.year_of_construction, climate_zone)
|
|
||||||
|
|
||||||
return average_storey_height
|
|
@ -3,13 +3,11 @@ NrcanUsageParameters extracts the usage properties from NRCAN catalog and assign
|
|||||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
Copyright © 2022 Concordia CERC group
|
Copyright © 2022 Concordia CERC group
|
||||||
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
Project Collaborator Saeed Ranjbar saeed.ranjbar@concordia.ca
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import hub.helpers.constants as cte
|
import hub.helpers.constants as cte
|
||||||
from hub.catalog_factories.construction_catalog_factory import ConstructionCatalogFactory
|
|
||||||
from hub.helpers.dictionaries import Dictionaries
|
from hub.helpers.dictionaries import Dictionaries
|
||||||
from hub.city_model_structure.building_demand.usage import Usage
|
from hub.city_model_structure.building_demand.usage import Usage
|
||||||
from hub.city_model_structure.building_demand.lighting import Lighting
|
from hub.city_model_structure.building_demand.lighting import Lighting
|
||||||
@ -18,7 +16,6 @@ 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.thermal_control import ThermalControl
|
||||||
from hub.city_model_structure.building_demand.domestic_hot_water import DomesticHotWater
|
from hub.city_model_structure.building_demand.domestic_hot_water import DomesticHotWater
|
||||||
from hub.catalog_factories.usage_catalog_factory import UsageCatalogFactory
|
from hub.catalog_factories.usage_catalog_factory import UsageCatalogFactory
|
||||||
from hub.imports.construction.helpers.construction_helper import ConstructionHelper
|
|
||||||
|
|
||||||
|
|
||||||
class NrcanUsageParameters:
|
class NrcanUsageParameters:
|
||||||
@ -36,75 +33,53 @@ class NrcanUsageParameters:
|
|||||||
city = self._city
|
city = self._city
|
||||||
nrcan_catalog = UsageCatalogFactory('nrcan').catalog
|
nrcan_catalog = UsageCatalogFactory('nrcan').catalog
|
||||||
comnet_catalog = UsageCatalogFactory('comnet').catalog
|
comnet_catalog = UsageCatalogFactory('comnet').catalog
|
||||||
for building in city.buildings:
|
|
||||||
usages = []
|
|
||||||
nrcan_archetype_usages = []
|
|
||||||
comnet_archetype_usages = []
|
|
||||||
building_functions = building.function.split('_')
|
|
||||||
for function in building_functions:
|
|
||||||
usages.append(function.split('-'))
|
|
||||||
for usage in usages:
|
|
||||||
usage_name = Dictionaries().hub_usage_to_nrcan_usage[usage[-1]]
|
|
||||||
try:
|
|
||||||
archetype_usage = self._search_archetypes(nrcan_catalog, usage_name)
|
|
||||||
nrcan_archetype_usages.append(archetype_usage)
|
|
||||||
except KeyError:
|
|
||||||
logging.error('Building %s has unknown usage archetype for usage %s', building.name, usage_name)
|
|
||||||
continue
|
|
||||||
comnet_usage_name = Dictionaries().hub_usage_to_comnet_usage[usage[-1]]
|
|
||||||
try:
|
|
||||||
comnet_archetype_usage = self._search_archetypes(comnet_catalog, comnet_usage_name)
|
|
||||||
comnet_archetype_usages.append(comnet_archetype_usage)
|
|
||||||
except KeyError:
|
|
||||||
logging.error('Building %s has unknown usage archetype for usage %s', building.name, comnet_usage_name)
|
|
||||||
continue
|
|
||||||
|
|
||||||
for (i, internal_zone) in enumerate(building.internal_zones):
|
for building in city.buildings:
|
||||||
internal_zone_usages = []
|
usage_name = Dictionaries().hub_usage_to_nrcan_usage[building.function]
|
||||||
|
try:
|
||||||
|
archetype_usage = self._search_archetypes(nrcan_catalog, usage_name)
|
||||||
|
except KeyError:
|
||||||
|
logging.error('Building %s has unknown usage archetype for usage %s', building.name, usage_name)
|
||||||
|
continue
|
||||||
|
|
||||||
|
comnet_usage_name = Dictionaries().hub_usage_to_comnet_usage[building.function]
|
||||||
|
try:
|
||||||
|
comnet_archetype_usage = self._search_archetypes(comnet_catalog, comnet_usage_name)
|
||||||
|
except KeyError:
|
||||||
|
logging.error('Building %s has unknown usage archetype for usage %s', building.name, comnet_usage_name)
|
||||||
|
continue
|
||||||
|
|
||||||
|
for internal_zone in building.internal_zones:
|
||||||
if len(building.internal_zones) > 1:
|
if len(building.internal_zones) > 1:
|
||||||
volume_per_area = 0
|
volume_per_area = 0
|
||||||
if internal_zone.area is None:
|
if internal_zone.area is None:
|
||||||
logging.error('Building %s has internal zone area not defined, ACH cannot be calculated for usage %s',
|
logging.error('Building %s has internal zone area not defined, ACH cannot be calculated for usage %s',
|
||||||
building.name, usages[i][-1])
|
building.name, usage_name)
|
||||||
continue
|
continue
|
||||||
if internal_zone.volume is None:
|
if internal_zone.volume is None:
|
||||||
logging.error('Building %s has internal zone volume not defined, ACH cannot be calculated for usage %s',
|
logging.error('Building %s has internal zone volume not defined, ACH cannot be calculated for usage %s',
|
||||||
building.name, usages[i][-1])
|
building.name, usage_name)
|
||||||
continue
|
continue
|
||||||
if internal_zone.area <= 0:
|
if internal_zone.area <= 0:
|
||||||
logging.error('Building %s has internal zone area equal to 0, ACH cannot be calculated for usage %s',
|
logging.error('Building %s has internal zone area equal to 0, ACH cannot be calculated for usage %s',
|
||||||
building.name, usages[i][-1])
|
building.name, usage_name)
|
||||||
continue
|
continue
|
||||||
volume_per_area += internal_zone.volume / internal_zone.area
|
volume_per_area += internal_zone.volume / internal_zone.area
|
||||||
usage = Usage()
|
|
||||||
usage.name = usages[i][-1]
|
|
||||||
self._assign_values(usage, nrcan_archetype_usages[i], volume_per_area, building.cold_water_temperature)
|
|
||||||
self._assign_comnet_extra_values(usage, comnet_archetype_usages[i], nrcan_archetype_usages[i].occupancy.occupancy_density)
|
|
||||||
usage.percentage = 1
|
|
||||||
self._calculate_reduced_values_from_extended_library(usage, nrcan_archetype_usages[i])
|
|
||||||
internal_zone_usages.append(usage)
|
|
||||||
else:
|
else:
|
||||||
storeys_above_ground = building.storeys_above_ground
|
if building.storeys_above_ground is None:
|
||||||
if storeys_above_ground is None:
|
logging.error('Building %s no number of storeys assigned, ACH cannot be calculated for usage %s',
|
||||||
logging.error('Building %s no number of storeys assigned, ACH cannot be calculated for usage %s. '
|
building.name, usage_name)
|
||||||
'NRCAN construction data for the year %s is used to calculated number of storeys above '
|
|
||||||
'ground', building.name, usages, building.year_of_construction)
|
|
||||||
storeys_above_ground = self.average_storey_height_calculator(self._city, building)
|
|
||||||
continue
|
continue
|
||||||
volume_per_area = building.volume / building.floor_area / storeys_above_ground
|
volume_per_area = building.volume / building.floor_area / building.storeys_above_ground
|
||||||
for (j, mixed_usage) in enumerate(usages):
|
|
||||||
usage = Usage()
|
|
||||||
usage.name = mixed_usage[-1]
|
|
||||||
if len(usages) > 1:
|
|
||||||
usage.percentage = float(mixed_usage[0]) / 100
|
|
||||||
else:
|
|
||||||
usage.percentage = 1
|
|
||||||
self._assign_values(usage, nrcan_archetype_usages[j], volume_per_area, building.cold_water_temperature)
|
|
||||||
self._assign_comnet_extra_values(usage, comnet_archetype_usages[j], nrcan_archetype_usages[j].occupancy.occupancy_density)
|
|
||||||
self._calculate_reduced_values_from_extended_library(usage, nrcan_archetype_usages[j])
|
|
||||||
internal_zone_usages.append(usage)
|
|
||||||
|
|
||||||
internal_zone.usages = internal_zone_usages
|
usage = Usage()
|
||||||
|
usage.name = usage_name
|
||||||
|
self._assign_values(usage, archetype_usage, volume_per_area, building.cold_water_temperature)
|
||||||
|
self._assign_comnet_extra_values(usage, comnet_archetype_usage, archetype_usage.occupancy.occupancy_density)
|
||||||
|
usage.percentage = 1
|
||||||
|
self._calculate_reduced_values_from_extended_library(usage, archetype_usage)
|
||||||
|
|
||||||
|
internal_zone.usages = [usage]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _search_archetypes(catalog, usage_name):
|
def _search_archetypes(catalog, usage_name):
|
||||||
@ -222,39 +197,3 @@ class NrcanUsageParameters:
|
|||||||
usage.thermal_control.mean_heating_set_point = max_heating_setpoint
|
usage.thermal_control.mean_heating_set_point = max_heating_setpoint
|
||||||
usage.thermal_control.heating_set_back = min_heating_setpoint
|
usage.thermal_control.heating_set_back = min_heating_setpoint
|
||||||
usage.thermal_control.mean_cooling_set_point = min_cooling_setpoint
|
usage.thermal_control.mean_cooling_set_point = min_cooling_setpoint
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def average_storey_height_calculator(city, building):
|
|
||||||
climate_zone = ConstructionHelper.city_to_nrcan_climate_zone(city.climate_reference_city)
|
|
||||||
nrcan_catalog = ConstructionCatalogFactory('nrcan').catalog
|
|
||||||
main_function = None
|
|
||||||
functions = building.function.split('_')
|
|
||||||
if len(functions) > 1:
|
|
||||||
maximum_percentage = 0
|
|
||||||
for function in functions:
|
|
||||||
percentage_and_function = function.split('-')
|
|
||||||
if float(percentage_and_function[0]) > maximum_percentage:
|
|
||||||
maximum_percentage = float(percentage_and_function[0])
|
|
||||||
main_function = percentage_and_function[-1]
|
|
||||||
else:
|
|
||||||
main_function = functions[-1]
|
|
||||||
if main_function not in Dictionaries().hub_function_to_nrcan_construction_function:
|
|
||||||
logging.error('Building %s has an unknown building function %s', building.name, main_function)
|
|
||||||
function = Dictionaries().hub_function_to_nrcan_construction_function[main_function]
|
|
||||||
construction_archetype = None
|
|
||||||
average_storey_height = None
|
|
||||||
nrcan_archetypes = nrcan_catalog.entries('archetypes')
|
|
||||||
for building_archetype in nrcan_archetypes:
|
|
||||||
construction_period_limits = building_archetype.construction_period.split('_')
|
|
||||||
if int(construction_period_limits[0]) <= int(building.year_of_construction) <= int(construction_period_limits[1]):
|
|
||||||
if str(function) == str(building_archetype.function) and climate_zone == str(building_archetype.climate_zone):
|
|
||||||
construction_archetype = building_archetype
|
|
||||||
average_storey_height = building_archetype.average_storey_height
|
|
||||||
if construction_archetype is None:
|
|
||||||
logging.error('Building %s has unknown construction archetype for building function: %s '
|
|
||||||
'[%s], building year of construction: %s and climate zone %s', building.name, function,
|
|
||||||
building.function, building.year_of_construction, climate_zone)
|
|
||||||
|
|
||||||
return average_storey_height
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"""
|
"""
|
||||||
Hub version number
|
Hub version number
|
||||||
"""
|
"""
|
||||||
__version__ = '0.2.0.12'
|
__version__ = '0.2.0.6'
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
xmltodict
|
xmltodict
|
||||||
numpy==1.26.4
|
numpy
|
||||||
trimesh[all]
|
trimesh[all]
|
||||||
pyproj
|
pyproj
|
||||||
pandas
|
pandas
|
||||||
@ -25,4 +25,3 @@ psycopg2-binary
|
|||||||
Pillow
|
Pillow
|
||||||
pathlib
|
pathlib
|
||||||
sqlalchemy_utils
|
sqlalchemy_utils
|
||||||
build
|
|
@ -83,7 +83,7 @@ class TestUsageFactory(TestCase):
|
|||||||
city = self._get_citygml(file)
|
city = self._get_citygml(file)
|
||||||
for building in city.buildings:
|
for building in city.buildings:
|
||||||
building.function = Dictionaries().pluto_function_to_hub_function[building.function]
|
building.function = Dictionaries().pluto_function_to_hub_function[building.function]
|
||||||
ConstructionFactory('nrcan', city).enrich()
|
|
||||||
UsageFactory('comnet', city).enrich()
|
UsageFactory('comnet', city).enrich()
|
||||||
self._check_buildings(city)
|
self._check_buildings(city)
|
||||||
for building in city.buildings:
|
for building in city.buildings:
|
||||||
|
142
texttest
Normal file
142
texttest
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
ZoneControl:Thermostat,
|
||||||
|
Room_180_7ad8616b Thermostat, !- Name
|
||||||
|
Room_180_7ad8616b, !- Zone or ZoneList Name
|
||||||
|
Room_180_7ad8616b Thermostat Schedule, !- Control Type Schedule Name
|
||||||
|
ThermostatSetpoint:DualSetpoint, !- Control 1 Object Type
|
||||||
|
LargeOffice Building_Setpoint 26, !- Control 1 Name
|
||||||
|
, !- Control 2 Object Type
|
||||||
|
, !- Control 2 Name
|
||||||
|
, !- Control 3 Object Type
|
||||||
|
, !- Control 3 Name
|
||||||
|
, !- Control 4 Object Type
|
||||||
|
, !- Control 4 Name
|
||||||
|
0; !- Temperature Difference Between Cutout And Setpoint {deltaC}
|
||||||
|
|
||||||
|
Schedule:Compact,
|
||||||
|
Room_180_7ad8616b Thermostat Schedule, !- Name
|
||||||
|
Room_180_7ad8616b Thermostat Schedule Type Limits, !- Schedule Type Limits Name
|
||||||
|
Through: 12/31, !- Field 1
|
||||||
|
For: AllDays, !- Field 2
|
||||||
|
Until: 24:00, !- Field 3
|
||||||
|
4; !- Field 4
|
||||||
|
|
||||||
|
ScheduleTypeLimits,
|
||||||
|
Room_180_7ad8616b Thermostat Schedule Type Limits, !- Name
|
||||||
|
0, !- Lower Limit Value {BasedOnField A3}
|
||||||
|
4, !- Upper Limit Value {BasedOnField A3}
|
||||||
|
DISCRETE; !- Numeric Type
|
||||||
|
|
||||||
|
ThermostatSetpoint:DualSetpoint,
|
||||||
|
LargeOffice Building_Setpoint 26, !- Name
|
||||||
|
LargeOffice Building_Setpoint_HtgSetp Schedule, !- Heating Setpoint Temperature Schedule Name
|
||||||
|
LargeOffice Building_Setpoint_ClgSetp Schedule; !- Cooling Setpoint Temperature Schedule Name
|
||||||
|
|
||||||
|
ZoneHVAC:EquipmentConnections,
|
||||||
|
Room_180_7ad8616b, !- Zone Name
|
||||||
|
Room_180_7ad8616b Equipment List, !- Zone Conditioning Equipment List Name
|
||||||
|
Room_180_7ad8616b Inlet Node List, !- Zone Air Inlet Node or NodeList Name
|
||||||
|
, !- Zone Air Exhaust Node or NodeList Name
|
||||||
|
Node 27, !- Zone Air Node Name
|
||||||
|
Room_180_7ad8616b Return Node List; !- Zone Return Air Node or NodeList Name
|
||||||
|
|
||||||
|
NodeList,
|
||||||
|
Room_180_7ad8616b Inlet Node List, !- Name
|
||||||
|
Node 305; !- Node Name 1
|
||||||
|
|
||||||
|
NodeList,
|
||||||
|
Room_180_7ad8616b Return Node List, !- Name
|
||||||
|
Node 308; !- Node Name 1
|
||||||
|
|
||||||
|
ZoneHVAC:Baseboard:Convective:Electric,
|
||||||
|
Elec Baseboard 1, !- Name
|
||||||
|
Always On Discrete hvac_library, !- Availability Schedule Name
|
||||||
|
, !- Heating Design Capacity Method
|
||||||
|
Autosize, !- Heating Design Capacity {W}
|
||||||
|
, !- Heating Design Capacity Per Floor Area {W/m2}
|
||||||
|
, !- Fraction of Autosized Heating Design Capacity
|
||||||
|
1; !- Efficiency
|
||||||
|
|
||||||
|
AirTerminal:SingleDuct:ConstantVolume:NoReheat,
|
||||||
|
Diffuser 21, !- Name
|
||||||
|
Always On Discrete hvac_library, !- Availability Schedule Name
|
||||||
|
Node 307, !- Air Inlet Node Name
|
||||||
|
Node 305, !- Air Outlet Node Name
|
||||||
|
AutoSize; !- Maximum Air Flow Rate {m3/s}
|
||||||
|
|
||||||
|
ZoneHVAC:AirDistributionUnit,
|
||||||
|
ADU Diffuser 21, !- Name
|
||||||
|
Node 305, !- Air Distribution Unit Outlet Node Name
|
||||||
|
AirTerminal:SingleDuct:ConstantVolume:NoReheat, !- Air Terminal Object Type
|
||||||
|
Diffuser 21; !- Air Terminal Name
|
||||||
|
|
||||||
|
ZoneHVAC:EquipmentList,
|
||||||
|
Room_180_7ad8616b Equipment List, !- Name
|
||||||
|
SequentialLoad, !- Load Distribution Scheme
|
||||||
|
ZoneHVAC:Baseboard:Convective:Electric, !- Zone Equipment Object Type 1
|
||||||
|
Elec Baseboard 1, !- Zone Equipment Name 1
|
||||||
|
1, !- Zone Equipment Cooling Sequence 1
|
||||||
|
1, !- Zone Equipment Heating or No-Load Sequence 1
|
||||||
|
, !- Zone Equipment Sequential Cooling Fraction Schedule Name 1
|
||||||
|
, !- Zone Equipment Sequential Heating Fraction Schedule Name 1
|
||||||
|
ZoneHVAC:AirDistributionUnit, !- Zone Equipment Object Type 2
|
||||||
|
ADU Diffuser 21, !- Zone Equipment Name 2
|
||||||
|
2, !- Zone Equipment Cooling Sequence 2
|
||||||
|
2, !- Zone Equipment Heating or No-Load Sequence 2
|
||||||
|
, !- Zone Equipment Sequential Cooling Fraction Schedule Name 2
|
||||||
|
; !- Zone Equipment Sequential Heating Fraction Schedule Name 2
|
||||||
|
|
||||||
|
Sizing:Zone,
|
||||||
|
Room_180_7ad8616b, !- Zone or ZoneList Name
|
||||||
|
SupplyAirTemperature, !- Zone Cooling Design Supply Air Temperature Input Method
|
||||||
|
14, !- Zone Cooling Design Supply Air Temperature {C}
|
||||||
|
11.11, !- Zone Cooling Design Supply Air Temperature Difference {deltaC}
|
||||||
|
SupplyAirTemperature, !- Zone Heating Design Supply Air Temperature Input Method
|
||||||
|
40, !- Zone Heating Design Supply Air Temperature {C}
|
||||||
|
11.11, !- Zone Heating Design Supply Air Temperature Difference {deltaC}
|
||||||
|
0.0085, !- Zone Cooling Design Supply Air Humidity Ratio {kgWater/kgDryAir}
|
||||||
|
0.008, !- Zone Heating Design Supply Air Humidity Ratio {kgWater/kgDryAir}
|
||||||
|
Room_180_7ad8616b DSOA Space List, !- Design Specification Outdoor Air Object Name
|
||||||
|
, !- Zone Heating Sizing Factor
|
||||||
|
, !- Zone Cooling Sizing Factor
|
||||||
|
DesignDay, !- Cooling Design Air Flow Method
|
||||||
|
0, !- Cooling Design Air Flow Rate {m3/s}
|
||||||
|
0.000762, !- Cooling Minimum Air Flow per Zone Floor Area {m3/s-m2}
|
||||||
|
0, !- Cooling Minimum Air Flow {m3/s}
|
||||||
|
0, !- Cooling Minimum Air Flow Fraction
|
||||||
|
DesignDay, !- Heating Design Air Flow Method
|
||||||
|
0, !- Heating Design Air Flow Rate {m3/s}
|
||||||
|
0.002032, !- Heating Maximum Air Flow per Zone Floor Area {m3/s-m2}
|
||||||
|
0.1415762, !- Heating Maximum Air Flow {m3/s}
|
||||||
|
0.3, !- Heating Maximum Air Flow Fraction
|
||||||
|
, !- Design Specification Zone Air Distribution Object Name
|
||||||
|
No, !- Account for Dedicated Outdoor Air System
|
||||||
|
, !- Dedicated Outdoor Air System Control Strategy
|
||||||
|
, !- Dedicated Outdoor Air Low Setpoint Temperature for Design {C}
|
||||||
|
, !- Dedicated Outdoor Air High Setpoint Temperature for Design {C}
|
||||||
|
Sensible Load Only No Latent Load, !- Zone Load Sizing Method
|
||||||
|
HumidityRatioDifference, !- Zone Latent Cooling Design Supply Air Humidity Ratio Input Method
|
||||||
|
, !- Zone Dehumidification Design Supply Air Humidity Ratio {kgWater/kgDryAir}
|
||||||
|
0.005, !- Zone Cooling Design Supply Air Humidity Ratio Difference {kgWater/kgDryAir}
|
||||||
|
HumidityRatioDifference, !- Zone Latent Heating Design Supply Air Humidity Ratio Input Method
|
||||||
|
, !- Zone Humidification Design Supply Air Humidity Ratio {kgWater/kgDryAir}
|
||||||
|
0.005; !- Zone Humidification Design Supply Air Humidity Ratio Difference {kgWater/kgDryAir}
|
||||||
|
|
||||||
|
DesignSpecification:OutdoorAir:SpaceList,
|
||||||
|
Room_180_7ad8616b DSOA Space List, !- Name
|
||||||
|
Room_180_7ad8616b_Space, !- Space Name 1
|
||||||
|
MidriseApartment Apartment Ventilation; !- Space Design Specification Outdoor Air Object Name 1
|
||||||
|
|
||||||
|
Zone,
|
||||||
|
Room_181_3a411b5d, !- Name
|
||||||
|
, !- Direction of Relative North {deg}
|
||||||
|
0, !- X Origin {m}
|
||||||
|
0, !- Y Origin {m}
|
||||||
|
0, !- Z Origin {m}
|
||||||
|
, !- Type
|
||||||
|
1, !- Multiplier
|
||||||
|
4, !- Ceiling Height {m}
|
||||||
|
291.62935408288, !- Volume {m3}
|
||||||
|
, !- Floor Area {m2}
|
||||||
|
, !- Zone Inside Convection Algorithm
|
||||||
|
, !- Zone Outside Convection Algorithm
|
||||||
|
Yes; !- Part of Total Floor Area
|
Loading…
Reference in New Issue
Block a user