Compare commits

..

No commits in common. "main" and "fix/multi-useage" have entirely different histories.

57 changed files with 1320 additions and 2970 deletions

View File

@ -15,20 +15,11 @@ class Archetype:
""" """
Archetype class Archetype class
""" """
def __init__(self, name, systems):
def __init__(self, name, systems, archetype_cluster_id=None):
self._cluster_id = archetype_cluster_id
self._name = name self._name = name
self._systems = systems self._systems = systems
@property
def cluster_id(self):
"""
Get id
:return: string
"""
return self._cluster_id
@property @property
def name(self): def name(self):
""" """
@ -52,7 +43,6 @@ class Archetype:
_systems.append(_system.to_dictionary()) _systems.append(_system.to_dictionary())
content = { content = {
'Archetype': { 'Archetype': {
'cluster_id': self.cluster_id,
'name': self.name, 'name': self.name,
'systems': _systems 'systems': _systems
} }

View File

@ -119,7 +119,7 @@ class ThermalStorageSystem(EnergyStorageSystem):
'height [m]': self.height, 'height [m]': self.height,
'layers': _layers, 'layers': _layers,
'maximum operating temperature [Celsius]': self.maximum_operating_temperature, 'maximum operating temperature [Celsius]': self.maximum_operating_temperature,
'storage_medium': _medias, 'storage_medium': self.storage_medium.to_dictionary(),
'heating coil capacity [W]': self.heating_coil_capacity 'heating coil capacity [W]': self.heating_coil_capacity
} }
} }

View File

@ -69,7 +69,7 @@ class MontrealCustomCatalog(Catalog):
storage_system = ThermalStorageSystem(equipment_id) storage_system = ThermalStorageSystem(equipment_id)
storage_systems = [storage_system] storage_systems = [storage_system]
if model_name == 'PV system': if model_name == 'PV system':
system_type = 'photovoltaic' system_type = 'Photovoltaic'
generation_system = PvGenerationSystem(equipment_id, generation_system = PvGenerationSystem(equipment_id,
name=None, name=None,
system_type= system_type, system_type= system_type,

View File

@ -30,8 +30,7 @@ class MontrealFutureSystemCatalogue(Catalog):
path = str(path / 'montreal_future_systems.xml') path = str(path / 'montreal_future_systems.xml')
with open(path, 'r', encoding='utf-8') as xml: with open(path, 'r', encoding='utf-8') as xml:
self._archetypes = xmltodict.parse(xml.read(), self._archetypes = xmltodict.parse(xml.read(),
force_list=['pv_generation_component', 'templateStorages', 'demand', force_list=['pv_generation_component', 'templateStorages', 'demand'])
'system', 'system_id'])
self._storage_components = self._load_storage_components() self._storage_components = self._load_storage_components()
self._generation_components = self._load_generation_components() self._generation_components = self._load_generation_components()
@ -50,7 +49,7 @@ class MontrealFutureSystemCatalogue(Catalog):
'non_pv_generation_component'] 'non_pv_generation_component']
if non_pv_generation_components is not None: if non_pv_generation_components is not None:
for non_pv in non_pv_generation_components: for non_pv in non_pv_generation_components:
system_id = non_pv['generation_system_id'] system_id = non_pv['system_id']
name = non_pv['name'] name = non_pv['name']
system_type = non_pv['system_type'] system_type = non_pv['system_type']
model_name = non_pv['model_name'] model_name = non_pv['model_name']
@ -182,7 +181,7 @@ class MontrealFutureSystemCatalogue(Catalog):
'pv_generation_component'] 'pv_generation_component']
if pv_generation_components is not None: if pv_generation_components is not None:
for pv in pv_generation_components: for pv in pv_generation_components:
system_id = pv['generation_system_id'] system_id = pv['system_id']
name = pv['name'] name = pv['name']
system_type = pv['system_type'] system_type = pv['system_type']
model_name = pv['model_name'] model_name = pv['model_name']
@ -382,7 +381,6 @@ class MontrealFutureSystemCatalogue(Catalog):
_system_archetypes = [] _system_archetypes = []
system_clusters = self._archetypes['EnergySystemCatalog']['system_archetypes']['system_archetype'] system_clusters = self._archetypes['EnergySystemCatalog']['system_archetypes']['system_archetype']
for system_cluster in system_clusters: for system_cluster in system_clusters:
archetype_id = system_cluster['@cluster_id']
name = system_cluster['name'] name = system_cluster['name']
systems = system_cluster['systems']['system_id'] systems = system_cluster['systems']['system_id']
integer_system_ids = [int(item) for item in systems] integer_system_ids = [int(item) for item in systems]
@ -390,7 +388,7 @@ class MontrealFutureSystemCatalogue(Catalog):
for system_archetype in self._systems: for system_archetype in self._systems:
if int(system_archetype.id) in integer_system_ids: if int(system_archetype.id) in integer_system_ids:
_systems.append(system_archetype) _systems.append(system_archetype)
_system_archetypes.append(Archetype(archetype_cluster_id=archetype_id, name=name, systems=_systems)) _system_archetypes.append(Archetype(name=name, systems=_systems))
return _system_archetypes return _system_archetypes
def _load_materials(self): def _load_materials(self):

View File

@ -93,7 +93,6 @@ class Building(CityObject):
logging.error('Building %s [%s] has an unexpected surface type %s.', self.name, self.aliases, surface.type) logging.error('Building %s [%s] has an unexpected surface type %s.', self.name, self.aliases, surface.type)
self._domestic_hot_water_peak_load = None self._domestic_hot_water_peak_load = None
self._fuel_consumption_breakdown = {} self._fuel_consumption_breakdown = {}
self._systems_archetype_cluster_id = None
self._pv_generation = {} self._pv_generation = {}
@property @property
@ -866,10 +865,9 @@ class Building(CityObject):
Get energy consumption of different sectors Get energy consumption of different sectors
return: dict return: dict
""" """
fuel_breakdown = {cte.ELECTRICITY: {cte.LIGHTING: self.lighting_electrical_demand[cte.YEAR][0] if self.lighting_electrical_demand else 0, fuel_breakdown = {cte.ELECTRICITY: {cte.LIGHTING: self.lighting_electrical_demand[cte.YEAR][0],
cte.APPLIANCES: self.appliances_electrical_demand[cte.YEAR][0] if self.appliances_electrical_demand else 0}} cte.APPLIANCES: self.appliances_electrical_demand[cte.YEAR][0]}}
energy_systems = self.energy_systems energy_systems = self.energy_systems
if energy_systems is not None:
for energy_system in energy_systems: for energy_system in energy_systems:
demand_types = energy_system.demand_types demand_types = energy_system.demand_types
generation_systems = energy_system.generation_systems generation_systems = energy_system.generation_systems
@ -885,8 +883,7 @@ class Building(CityObject):
if storage_systems: if storage_systems:
for storage_system in storage_systems: for storage_system in storage_systems:
if storage_system.type_energy_stored == 'thermal' and storage_system.heating_coil_energy_consumption: if storage_system.type_energy_stored == 'thermal' and storage_system.heating_coil_energy_consumption:
fuel_breakdown[cte.ELECTRICITY][f'{demand_type}'] += ( fuel_breakdown[cte.ELECTRICITY][f'{demand_type}'] += storage_system.heating_coil_energy_consumption[cte.YEAR][0]
storage_system.heating_coil_energy_consumption)[f'{demand_type}'][cte.YEAR][0]
#TODO: When simulation models of all energy system archetypes are created, this part can be removed #TODO: When simulation models of all energy system archetypes are created, this part can be removed
heating_fuels = [] heating_fuels = []
dhw_fuels = [] dhw_fuels = []
@ -901,52 +898,20 @@ class Building(CityObject):
if key == cte.ELECTRICITY and cte.COOLING not in fuel_breakdown[key]: if key == cte.ELECTRICITY and cte.COOLING not in fuel_breakdown[key]:
for energy_system in energy_systems: for energy_system in energy_systems:
if cte.COOLING in energy_system.demand_types and cte.COOLING not in fuel_breakdown[key]: if cte.COOLING in energy_system.demand_types and cte.COOLING not in fuel_breakdown[key]:
if self.cooling_consumption: for generation_system in energy_system.generation_systems:
fuel_breakdown[energy_system.generation_systems[0].fuel_type][cte.COOLING] = self.cooling_consumption[cte.YEAR][0] fuel_breakdown[generation_system.fuel_type][cte.COOLING] = self.cooling_consumption[cte.YEAR][0]
for fuel in heating_fuels: for fuel in heating_fuels:
if cte.HEATING not in fuel_breakdown[fuel]: if cte.HEATING not in fuel_breakdown[fuel]:
for energy_system in energy_systems: for energy_system in energy_systems:
if cte.HEATING in energy_system.demand_types: if cte.HEATING in energy_system.demand_types:
if self.heating_consumption: for generation_system in energy_system.generation_systems:
fuel_breakdown[energy_system.generation_systems[0].fuel_type][cte.HEATING] = self.heating_consumption[cte.YEAR][0] fuel_breakdown[generation_system.fuel_type][cte.HEATING] = self.heating_consumption[cte.YEAR][0]
for fuel in dhw_fuels: for fuel in dhw_fuels:
if cte.DOMESTIC_HOT_WATER not in fuel_breakdown[fuel]: if cte.DOMESTIC_HOT_WATER not in fuel_breakdown[fuel]:
for energy_system in energy_systems: for energy_system in energy_systems:
if cte.DOMESTIC_HOT_WATER in energy_system.demand_types: if cte.DOMESTIC_HOT_WATER in energy_system.demand_types:
if self.domestic_hot_water_consumption: for generation_system in energy_system.generation_systems:
fuel_breakdown[energy_system.generation_systems[0].fuel_type][cte.DOMESTIC_HOT_WATER] = self.domestic_hot_water_consumption[cte.YEAR][0] fuel_breakdown[generation_system.fuel_type][cte.DOMESTIC_HOT_WATER] = self.domestic_hot_water_consumption[cte.YEAR][0]
self._fuel_consumption_breakdown = fuel_breakdown self._fuel_consumption_breakdown = fuel_breakdown
return self._fuel_consumption_breakdown return self._fuel_consumption_breakdown
@property
def energy_systems_archetype_cluster_id(self):
"""
Get energy systems archetype id
:return: str
"""
return self._systems_archetype_cluster_id
@energy_systems_archetype_cluster_id.setter
def energy_systems_archetype_cluster_id(self, value):
"""
Set energy systems archetype id
:param value: str
"""
self._systems_archetype_cluster_id = value
@property
def pv_generation(self):
"""
temporary attribute to get the onsite pv generation in W
:return: dict
"""
return self._pv_generation
@pv_generation.setter
def pv_generation(self, value):
"""
temporary attribute to set the onsite pv generation in W
:param value: float
"""
self._pv_generation = value

View File

@ -157,7 +157,6 @@ class Surface:
if self._inclination is None: if self._inclination is None:
self._inclination = np.arccos(self.perimeter_polygon.normal[2]) self._inclination = np.arccos(self.perimeter_polygon.normal[2])
return self._inclination return self._inclination
@property @property
def type(self): def type(self):
""" """

View File

@ -28,7 +28,9 @@ class PvGenerationSystem(GenerationSystem):
self._height = None self._height = None
self._electricity_power_output = {} self._electricity_power_output = {}
self._tilt_angle = None self._tilt_angle = None
self._installed_capacity = None self._surface_azimuth = None
self._solar_altitude_angle = None
self._solar_azimuth_angle = None
@property @property
def nominal_electricity_output(self): def nominal_electricity_output(self):
@ -223,17 +225,33 @@ class PvGenerationSystem(GenerationSystem):
self._electricity_power_output = value self._electricity_power_output = value
@property @property
def installed_capacity(self): def tilt_angle(self):
""" """
Get the total installed nominal capacity in W Get tilt angle of PV system in degrees
:return: float :return: float
""" """
return self._installed_capacity return self._tilt_angle
@installed_capacity.setter @tilt_angle.setter
def installed_capacity(self, value): def tilt_angle(self, value):
""" """
Set the total installed nominal capacity in W Set PV system tilt angle in degrees
:param value: float :param value: float
""" """
self._installed_capacity = value self._tilt_angle = value
@property
def surface_azimuth(self):
"""
Get surface azimuth angle of PV system in degrees. 0 is North
:return: float
"""
return self._surface_azimuth
@surface_azimuth.setter
def surface_azimuth(self, value):
"""
Set PV system tilt angle in degrees
:param value: float
"""
self._surface_azimuth = value

View File

@ -0,0 +1,774 @@
{
"archetypes": [
{
"function": "Large multifamily building",
"period_of_construction": "2021_2050",
"climate_zone": "B3",
"average_storey_height": 3.57,
"thermal_capacity": 83.018,
"extra_loses_due_thermal_bridges": 0.1,
"infiltration_rate_for_ventilation_system_on": 0,
"infiltration_rate_for_ventilation_system_off": 0.9,
"constructions": {
"OutdoorsWall": {
"opaque_surface_name": "PA1_PA2_2021_2050_FACEXT",
"transparent_surface_name": "PA1_PA2_2021_2050_WIN1",
"transparent_ratio": {
"north": "60",
"east": "5",
"south": "60",
"west": "5"
}
},
"OutdoorsRoofCeiling": {
"opaque_surface_name": "PA1_PA2_2021_2050_ROOF",
"transparent_surface_name": null,
"transparent_ratio": {
"north": null,
"east": null,
"south": null,
"west": null
}
},
"GroundFloor": {
"opaque_surface_name": "PA1_PA2_2021_2050_FLOOR"
},
"GroundWall": {
"opaque_surface_name": "PA1_PA2_2021_2050_FACEXT"
},
"GroundRoofCeiling": {
"opaque_surface_name": "PA1_PA2_2021_2050_FLOORINT"
}
},
"infiltration_rate_area_for_ventilation_system_on": 0,
"infiltration_rate_area_for_ventilation_system_off": 0
},
{
"function": "Medium multifamily building",
"period_of_construction": "2021_2050",
"climate_zone": "B3",
"average_storey_height": 3.57,
"thermal_capacity": 83.018,
"extra_loses_due_thermal_bridges": 0.1,
"infiltration_rate_for_ventilation_system_on": 0,
"infiltration_rate_for_ventilation_system_off": 0.9,
"constructions": {
"OutdoorsWall": {
"opaque_surface_name": "PA1_PA2_2021_2050_FACEXT",
"transparent_surface_name": "PA1_PA2_2021_2050_WIN1",
"transparent_ratio": {
"north": "60",
"east": "5",
"south": "60",
"west": "5"
}
},
"OutdoorsRoofCeiling": {
"opaque_surface_name": "PA1_PA2_2021_2050_ROOF",
"transparent_surface_name": null,
"transparent_ratio": {
"north": null,
"east": null,
"south": null,
"west": null
}
},
"GroundFloor": {
"opaque_surface_name": "PA1_PA2_2021_2050_FLOOR"
},
"GroundWall": {
"opaque_surface_name": "PA1_PA2_2021_2050_FACEXT"
},
"GroundRoofCeiling": {
"opaque_surface_name": "PA1_PA2_2021_2050_FLOORINT"
}
},
"infiltration_rate_area_for_ventilation_system_on": 0,
"infiltration_rate_area_for_ventilation_system_off": 0
},
{
"function": "Small multifamily building",
"period_of_construction": "2021_2050",
"climate_zone": "B3",
"average_storey_height": 3.57,
"thermal_capacity": 83.018,
"extra_loses_due_thermal_bridges": 0.1,
"infiltration_rate_for_ventilation_system_on": 0,
"infiltration_rate_for_ventilation_system_off": 0.9,
"constructions": {
"OutdoorsWall": {
"opaque_surface_name": "PA1_PA2_2021_2050_FACEXT",
"transparent_surface_name": "PA1_PA2_2021_2050_WIN1",
"transparent_ratio": {
"north": "60",
"east": "5",
"south": "60",
"west": "5"
}
},
"OutdoorsRoofCeiling": {
"opaque_surface_name": "PA1_PA2_2021_2050_ROOF",
"transparent_surface_name": null,
"transparent_ratio": {
"north": null,
"east": null,
"south": null,
"west": null
}
},
"GroundFloor": {
"opaque_surface_name": "PA1_PA2_2021_2050_FLOOR"
},
"GroundWall": {
"opaque_surface_name": "PA1_PA2_2021_2050_FACEXT"
},
"GroundRoofCeiling": {
"opaque_surface_name": "PA1_PA2_2021_2050_FLOORINT"
}
},
"infiltration_rate_area_for_ventilation_system_on": 0,
"infiltration_rate_area_for_ventilation_system_off": 0
},
{
"function": "Single-family building",
"period_of_construction": "2021_2050",
"climate_zone": "B3",
"average_storey_height": 3.57,
"thermal_capacity": 83.018,
"extra_loses_due_thermal_bridges": 0.1,
"infiltration_rate_for_ventilation_system_on": 0,
"infiltration_rate_for_ventilation_system_off": 0.9,
"constructions": {
"OutdoorsWall": {
"opaque_surface_name": "PA1_PA2_2021_2050_FACEXT",
"transparent_surface_name": "PA1_PA2_2021_2050_WIN1",
"transparent_ratio": {
"north": "60",
"east": "5",
"south": "60",
"west": "5"
}
},
"OutdoorsRoofCeiling": {
"opaque_surface_name": "PA1_PA2_2021_2050_ROOF",
"transparent_surface_name": null,
"transparent_ratio": {
"north": null,
"east": null,
"south": null,
"west": null
}
},
"GroundFloor": {
"opaque_surface_name": "PA1_PA2_2021_2050_FLOOR"
},
"GroundWall": {
"opaque_surface_name": "PA1_PA2_2021_2050_FACEXT"
},
"GroundRoofCeiling": {
"opaque_surface_name": "PA1_PA2_2021_2050_FLOORINT"
}
},
"infiltration_rate_area_for_ventilation_system_on": 0,
"infiltration_rate_area_for_ventilation_system_off": 0
},
{
"function": "Large multifamily building",
"period_of_construction": "1961_1980",
"climate_zone": "B3",
"average_storey_height": 3.57,
"thermal_capacity": 3000,
"extra_loses_due_thermal_bridges": 0.1,
"infiltration_rate_for_ventilation_system_on": 0,
"infiltration_rate_for_ventilation_system_off": 0.9,
"constructions": {
"OutdoorsWall": {
"opaque_surface_name": "PA1_PA2_1961_1980_FACEXT1",
"transparent_surface_name": "PA1_PA2_1961_1980_WIN1",
"transparent_ratio": {
"north": "60",
"east": "60",
"south": "60",
"west": "60"
}
},
"OutdoorsRoofCeiling": {
"opaque_surface_name": "PA1_PA2_1961_1980_ROOF1",
"transparent_surface_name": null,
"transparent_ratio": {
"north": null,
"east": null,
"south": null,
"west": null
}
},
"GroundFloor": {
"opaque_surface_name": "PA1_PA2_1961_1980_FLOOR1"
},
"GroundWall": {
"opaque_surface_name": "PA1_PA2_1961_1980_FACEXT1"
},
"GroundRoofCeiling": {
"opaque_surface_name": "PA1_PA2_1961_1980_FLOOR4"
}
},
"infiltration_rate_area_for_ventilation_system_on": 0,
"infiltration_rate_area_for_ventilation_system_off": 0
},
{
"function": "Large multifamily building",
"period_of_construction": "1981_2007",
"climate_zone": "B3",
"average_storey_height": 3.2,
"thermal_capacity": 3179,
"extra_loses_due_thermal_bridges": 0.1,
"infiltration_rate_for_ventilation_system_on": 0,
"infiltration_rate_for_ventilation_system_off": 0.9,
"constructions": {
"OutdoorsWall": {
"opaque_surface_name": "E_1981_2007_FACEXT1",
"transparent_surface_name": "E_1981_2007_WIN1",
"transparent_ratio": {
"north": "45",
"east": "45",
"south": "45",
"west": "45"
}
},
"OutdoorsRoofCeiling": {
"opaque_surface_name": "E_1981_2007_ROOF1",
"transparent_surface_name": null,
"transparent_ratio": {
"north": null,
"east": null,
"south": null,
"west": null
}
},
"GroundFloor": {
"opaque_surface_name": "E_1981_2007_FLOORGR1"
}
},
"infiltration_rate_area_for_ventilation_system_on": 0,
"infiltration_rate_area_for_ventilation_system_off": 0
},
{
"function": "Medium multifamily building",
"period_of_construction": "1800_1900",
"climate_zone": "B3",
"average_storey_height": 4.39,
"thermal_capacity": 3330,
"extra_loses_due_thermal_bridges": 0.1,
"infiltration_rate_for_ventilation_system_on": 0,
"infiltration_rate_for_ventilation_system_off": 0.9,
"constructions": {
"OutdoorsWall": {
"opaque_surface_name": "A_B1900_FACEXT1",
"transparent_surface_name": "A_B1900_WIN2",
"transparent_ratio": {
"north": "20",
"east": "20",
"south": "20",
"west": "20"
}
},
"OutdoorsRoofCeiling": {
"opaque_surface_name": "A_B1900_ROOF1",
"transparent_surface_name": null,
"transparent_ratio": {
"north": null,
"east": null,
"south": null,
"west": null
}
},
"GroundFloor": {
"opaque_surface_name": "A_B1900_FLOORGR1"
}
},
"infiltration_rate_area_for_ventilation_system_on": 0,
"infiltration_rate_area_for_ventilation_system_off": 0
},
{
"function": "Medium multifamily building",
"period_of_construction": "1901_1940",
"climate_zone": "B3",
"average_storey_height": 3.65,
"thermal_capacity": 3420,
"extra_loses_due_thermal_bridges": 0.1,
"infiltration_rate_for_ventilation_system_on": 0,
"infiltration_rate_for_ventilation_system_off": 0.9,
"constructions": {
"OutdoorsWall": {
"opaque_surface_name": "B_1901_1940_FACEXT1",
"transparent_surface_name": "B_1901_1940_WIN1",
"transparent_ratio": {
"north": "40",
"east": "40",
"south": "40",
"west": "40"
}
},
"OutdoorsRoofCeiling": {
"opaque_surface_name": "B_1901_1940_ROOF1",
"transparent_surface_name": null,
"transparent_ratio": {
"north": null,
"east": null,
"south": null,
"west": null
}
},
"GroundFloor": {
"opaque_surface_name": "B_1901_1940_FLOORGR1"
}
},
"infiltration_rate_area_for_ventilation_system_on": 0,
"infiltration_rate_area_for_ventilation_system_off": 0
},
{
"function": "Medium multifamily building",
"period_of_construction": "1941_1960",
"climate_zone": "B3",
"average_storey_height": 3.6,
"thermal_capacity": 3000,
"extra_loses_due_thermal_bridges": 0.1,
"infiltration_rate_for_ventilation_system_on": 0,
"infiltration_rate_for_ventilation_system_off": 0.9,
"constructions": {
"OutdoorsWall": {
"opaque_surface_name": " C_1941_1960_FACEXT1",
"transparent_surface_name": "C_1941_1960_WIN1",
"transparent_ratio": {
"north": "30",
"east": "30",
"south": "30",
"west": "30"
}
},
"OutdoorsRoofCeiling": {
"opaque_surface_name": "C_1941_1960_ROOF1",
"transparent_surface_name": null,
"transparent_ratio": {
"north": null,
"east": null,
"south": null,
"west": null
}
},
"GroundFloor": {
"opaque_surface_name": "C_1941_1960_FLOORGR1"
}
},
"infiltration_rate_area_for_ventilation_system_on": 0,
"infiltration_rate_area_for_ventilation_system_off": 0
},
{
"function": "Medium multifamily building",
"period_of_construction": "1961_1980",
"climate_zone": "B3",
"average_storey_height": 4.5,
"thermal_capacity": 3540,
"extra_loses_due_thermal_bridges": 0.1,
"infiltration_rate_for_ventilation_system_on": 0,
"infiltration_rate_for_ventilation_system_off": 0.9,
"constructions": {
"OutdoorsWall": {
"opaque_surface_name": "PA1_PA2_1961_1980_FACEXT1",
"transparent_surface_name": "PA1_PA2_1961_1980_WIN1",
"transparent_ratio": {
"north": "55",
"east": "55",
"south": "55",
"west": "55"
}
},
"OutdoorsRoofCeiling": {
"opaque_surface_name": "PA1_PA2_1961_1980_ROOF1",
"transparent_surface_name": null,
"transparent_ratio": {
"north": null,
"east": null,
"south": null,
"west": null
}
},
"GroundFloor": {
"opaque_surface_name": "PA1_PA2_1961_1980_FLOOR1"
}
},
"infiltration_rate_area_for_ventilation_system_on": 0,
"infiltration_rate_area_for_ventilation_system_off": 0
},
{
"function": "Medium multifamily building",
"period_of_construction": "1981_2007",
"climate_zone": "B3",
"average_storey_height": 3.2,
"thermal_capacity": 3179,
"extra_loses_due_thermal_bridges": 0.1,
"infiltration_rate_for_ventilation_system_on": 0,
"infiltration_rate_for_ventilation_system_off": 0.9,
"constructions": {
"OutdoorsWall": {
"opaque_surface_name": "E_1981_2007_FACEXT1",
"transparent_surface_name": "E_1981_2007_WIN1",
"transparent_ratio": {
"north": "45",
"east": "45",
"south": "45",
"west": "45"
}
},
"OutdoorsRoofCeiling": {
"opaque_surface_name": "E_1981_2007_ROOF1",
"transparent_surface_name": null,
"transparent_ratio": {
"north": null,
"east": null,
"south": null,
"west": null
}
},
"GroundFloor": {
"opaque_surface_name": "E_1981_2007_FLOORGR1"
}
},
"infiltration_rate_area_for_ventilation_system_on": 0,
"infiltration_rate_area_for_ventilation_system_off": 0
},
{
"function": "Medium multifamily building",
"period_of_construction": "2008_2014",
"climate_zone": "B3",
"average_storey_height": 2.75,
"thermal_capacity": 3290,
"extra_loses_due_thermal_bridges": 0.1,
"infiltration_rate_for_ventilation_system_on": 0,
"infiltration_rate_for_ventilation_system_off": 0.9,
"constructions": {
"OutdoorsWall": {
"opaque_surface_name": "F_2008_2014_FACEXT1",
"transparent_surface_name": "F_2008_2014_WIN1",
"transparent_ratio": {
"north": "40",
"east": "40",
"south": "40",
"west": "40"
}
},
"OutdoorsRoofCeiling": {
"opaque_surface_name": "F_2008_2014_ROOF1",
"transparent_surface_name": null,
"transparent_ratio": {
"north": null,
"east": null,
"south": null,
"west": null
}
},
"GroundFloor": {
"opaque_surface_name": "F_2008_2014_FLOORGR1"
}
},
"infiltration_rate_area_for_ventilation_system_on": 0,
"infiltration_rate_area_for_ventilation_system_off": 0
},
{
"function": "Small multifamily building",
"period_of_construction": "1800_1980",
"climate_zone": "B3",
"average_storey_height": 3.8,
"thermal_capacity": 3527.9,
"extra_loses_due_thermal_bridges": 0.1,
"infiltration_rate_for_ventilation_system_on": 0,
"infiltration_rate_for_ventilation_system_off": 0.9,
"constructions": {
"OutdoorsWall": {
"opaque_surface_name": "PA3_PA4_1901_1940_FACEXT1",
"transparent_surface_name": "PA3_PA4_1901_1940_WIN1",
"transparent_ratio": {
"north": "40",
"east": "40",
"south": "40",
"west": "40"
}
},
"OutdoorsRoofCeiling": {
"opaque_surface_name": "PA3_PA4_1901_1940_ROOF1",
"transparent_surface_name": null,
"transparent_ratio": {
"north": null,
"east": null,
"south": null,
"west": null
}
},
"GroundFloor": {
"opaque_surface_name": "PA3_PA4_1901_1940_FLOORGR1"
}
},
"infiltration_rate_area_for_ventilation_system_on": 0,
"infiltration_rate_area_for_ventilation_system_off": 0
},
{
"function": "Small multifamily building",
"period_of_construction": "1981_2007",
"climate_zone": "B3",
"average_storey_height": 3.2,
"thermal_capacity": 3179,
"extra_loses_due_thermal_bridges": 0.1,
"infiltration_rate_for_ventilation_system_on": 0,
"infiltration_rate_for_ventilation_system_off": 0.9,
"constructions": {
"OutdoorsWall": {
"opaque_surface_name": "E_1981_2007_FACEXT1",
"transparent_surface_name": "E_1981_2007_WIN1",
"transparent_ratio": {
"north": "45",
"east": "45",
"south": "45",
"west": "45"
}
},
"OutdoorsRoofCeiling": {
"opaque_surface_name": "E_1981_2007_ROOF1",
"transparent_surface_name": null,
"transparent_ratio": {
"north": null,
"east": null,
"south": null,
"west": null
}
},
"GroundFloor": {
"opaque_surface_name": "E_1981_2007_FLOORGR1"
}
},
"infiltration_rate_area_for_ventilation_system_on": 0,
"infiltration_rate_area_for_ventilation_system_off": 0
},
{
"function": "Small multifamily building",
"period_of_construction": "2008_2014",
"climate_zone": "B3",
"average_storey_height": 2.75,
"thermal_capacity": 3290,
"extra_loses_due_thermal_bridges": 0.1,
"infiltration_rate_for_ventilation_system_on": 0,
"infiltration_rate_for_ventilation_system_off": 0.9,
"constructions": {
"OutdoorsWall": {
"opaque_surface_name": "F_2008_2014_FACEXT1",
"transparent_surface_name": "F_2008_2014_WIN1",
"transparent_ratio": {
"north": "40",
"east": "40",
"south": "40",
"west": "40"
}
},
"OutdoorsRoofCeiling": {
"opaque_surface_name": "F_2008_2014_ROOF1",
"transparent_surface_name": null,
"transparent_ratio": {
"north": null,
"east": null,
"south": null,
"west": null
}
},
"GroundFloor": {
"opaque_surface_name": "F_2008_2014_FLOORGR1"
}
},
"infiltration_rate_area_for_ventilation_system_on": 0,
"infiltration_rate_area_for_ventilation_system_off": 0
},
{
"function": "Small multifamily building",
"period_of_construction": "2015_2019",
"climate_zone": "B3",
"average_storey_height": 2.75,
"thermal_capacity": 3290,
"extra_loses_due_thermal_bridges": 0.1,
"infiltration_rate_for_ventilation_system_on": 0,
"infiltration_rate_for_ventilation_system_off": 0.9,
"constructions": {
"OutdoorsWall": {
"opaque_surface_name": "G_2015_2019_FACEXT1",
"transparent_surface_name": "G_2015_2019_WIN1",
"transparent_ratio": {
"north": "40",
"east": "40",
"south": "40",
"west": "40"
}
},
"OutdoorsRoofCeiling": {
"opaque_surface_name": "G_2015_2019_ROOF1",
"transparent_surface_name": null,
"transparent_ratio": {
"north": null,
"east": null,
"south": null,
"west": null
}
},
"GroundFloor": {
"opaque_surface_name": "G_2015_2019_FLOORGR1"
}
},
"infiltration_rate_area_for_ventilation_system_on": 0,
"infiltration_rate_area_for_ventilation_system_off": 0
},
{
"function": "Single-family building",
"period_of_construction": "1800_1980",
"climate_zone": "B3",
"average_storey_height": 3.68,
"thermal_capacity": 4400,
"extra_loses_due_thermal_bridges": 0.1,
"infiltration_rate_for_ventilation_system_on": 0,
"infiltration_rate_for_ventilation_system_off": 0.9,
"constructions": {
"OutdoorsWall": {
"opaque_surface_name": "PA3_PA4_1901_1940_FACEXT1",
"transparent_surface_name": "PA3_PA4_1901_1940_WIN1",
"transparent_ratio": {
"north": "40",
"east": "40",
"south": "40",
"west": "40"
}
},
"OutdoorsRoofCeiling": {
"opaque_surface_name": "PA3_PA4_1901_1940_ROOF1",
"transparent_surface_name": null,
"transparent_ratio": {
"north": null,
"east": null,
"south": null,
"west": null
}
},
"GroundFloor": {
"opaque_surface_name": "PA3_PA4_1901_1940_FLOORGR1"
}
},
"infiltration_rate_area_for_ventilation_system_on": 0,
"infiltration_rate_area_for_ventilation_system_off": 0
},
{
"function": "Single-family building",
"period_of_construction": "1981_2007",
"climate_zone": "B3",
"average_storey_height": 3.2,
"thermal_capacity": 3179,
"extra_loses_due_thermal_bridges": 0.1,
"infiltration_rate_for_ventilation_system_on": 0,
"infiltration_rate_for_ventilation_system_off": 0.9,
"constructions": {
"OutdoorsWall": {
"opaque_surface_name": "E_1981_2007_FACEXT1",
"transparent_surface_name": "E_1981_2007_WIN1",
"transparent_ratio": {
"north": "45",
"east": "45",
"south": "45",
"west": "45"
}
},
"OutdoorsRoofCeiling": {
"opaque_surface_name": "E_1981_2007_ROOF1",
"transparent_surface_name": null,
"transparent_ratio": {
"north": null,
"east": null,
"south": null,
"west": null
}
},
"GroundFloor": {
"opaque_surface_name": "E_1981_2007_FLOORGR1"
}
},
"infiltration_rate_area_for_ventilation_system_on": 0,
"infiltration_rate_area_for_ventilation_system_off": 0
},
{
"function": "Single-family building",
"period_of_construction": "2008_2014",
"climate_zone": "B3",
"average_storey_height": 3.75,
"thermal_capacity": 3200,
"extra_loses_due_thermal_bridges": 0.1,
"infiltration_rate_for_ventilation_system_on": 0,
"infiltration_rate_for_ventilation_system_off": 0.9,
"constructions": {
"OutdoorsWall": {
"opaque_surface_name": "F_2008_2014_FACEXT1",
"transparent_surface_name": "F_2008_2014_WIN1",
"transparent_ratio": {
"north": "60",
"east": "60",
"south": "60",
"west": "60"
}
},
"OutdoorsRoofCeiling": {
"opaque_surface_name": "F_2008_2014_ROOF1",
"transparent_surface_name": null,
"transparent_ratio": {
"north": null,
"east": null,
"south": null,
"west": null
}
},
"GroundFloor": {
"opaque_surface_name": "F_2008_2014_FLOORGR1"
}
},
"infiltration_rate_area_for_ventilation_system_on": 0,
"infiltration_rate_area_for_ventilation_system_off": 0
},
{
"function": "Single-family building",
"period_of_construction": "2015_2019",
"climate_zone": "B3",
"average_storey_height": 3.75,
"thermal_capacity": 3200,
"extra_loses_due_thermal_bridges": 0.1,
"infiltration_rate_for_ventilation_system_on": 0,
"infiltration_rate_for_ventilation_system_off": 0.9,
"constructions": {
"OutdoorsWall": {
"opaque_surface_name": "G_2015_2019_FACEXT1",
"transparent_surface_name": "G_2015_2019_WIN1",
"transparent_ratio": {
"north": "60",
"east": "60",
"south": "60",
"west": "60"
}
},
"OutdoorsRoofCeiling": {
"opaque_surface_name": "G_2015_2019_ROOF1",
"transparent_surface_name": null,
"transparent_ratio": {
"north": null,
"east": null,
"south": null,
"west": null
}
},
"GroundFloor": {
"opaque_surface_name": "G_2015_2019_FLOORGR1"
}
},
"infiltration_rate_area_for_ventilation_system_on": 0,
"infiltration_rate_area_for_ventilation_system_off": 0
}
]
}

View File

@ -198,7 +198,7 @@
<equipments> <equipments>
<generation_id>3</generation_id> <generation_id>3</generation_id>
<distribution_id>8</distribution_id> <distribution_id>8</distribution_id>
</equipments> g </equipments>
</system> </system>
<system id="5"> <system id="5">
<name>Single zone packaged rooftop unit with electrical resistance furnace and baseboards and fuel boiler for acs</name> <name>Single zone packaged rooftop unit with electrical resistance furnace and baseboards and fuel boiler for acs</name>
@ -240,7 +240,7 @@
<demand>domestic_hot_water</demand> <demand>domestic_hot_water</demand>
</demands> </demands>
<equipments> <equipments>
<generation_id>1</generation_id> <generation_id>2</generation_id>
<distribution_id>3</distribution_id> <distribution_id>3</distribution_id>
</equipments> </equipments>
</system> </system>
@ -302,7 +302,7 @@
</demands> </demands>
<equipments> <equipments>
<generation_id>5</generation_id> <generation_id>5</generation_id>
<distribution_id>4</distribution_id> <distribution_id>6</distribution_id>
</equipments> </equipments>
</system> </system>
<system id="15"> <system id="15">

File diff suppressed because it is too large Load Diff

View File

@ -253,7 +253,7 @@
<pv_generation_component> <pv_generation_component>
<system_id>7</system_id> <system_id>7</system_id>
<name>template Photovoltaic Module</name> <name>template Photovoltaic Module</name>
<system_type>photovoltaic</system_type> <system_type>Photovoltaic</system_type>
<model_name/> <model_name/>
<manufacturer/> <manufacturer/>
<nominal_electricity_output/> <nominal_electricity_output/>
@ -264,7 +264,7 @@
<standard_test_condition_cell_temperature>25</standard_test_condition_cell_temperature> <standard_test_condition_cell_temperature>25</standard_test_condition_cell_temperature>
<standard_test_condition_radiation>1000</standard_test_condition_radiation> <standard_test_condition_radiation>1000</standard_test_condition_radiation>
<standard_test_condition_maximum_power>500</standard_test_condition_maximum_power> <standard_test_condition_maximum_power>500</standard_test_condition_maximum_power>
<cell_temperature_coefficient>0.3</cell_temperature_coefficient> <cell_temperature_coefficient/>
<width>2.0</width> <width>2.0</width>
<height>1.0</height> <height>1.0</height>
<distribution_systems/> <distribution_systems/>
@ -274,7 +274,7 @@
<pv_generation_component> <pv_generation_component>
<system_id>8</system_id> <system_id>8</system_id>
<name>Photovoltaic Module</name> <name>Photovoltaic Module</name>
<system_type>photovoltaic</system_type> <system_type>Photovoltaic</system_type>
<model_name>RE400CAA Pure 2</model_name> <model_name>RE400CAA Pure 2</model_name>
<manufacturer>REC</manufacturer> <manufacturer>REC</manufacturer>
<nominal_electricity_output>305</nominal_electricity_output> <nominal_electricity_output>305</nominal_electricity_output>
@ -295,7 +295,7 @@
<pv_generation_component> <pv_generation_component>
<system_id>9</system_id> <system_id>9</system_id>
<name>Photovoltaic Module</name> <name>Photovoltaic Module</name>
<system_type>photovoltaic</system_type> <system_type>Photovoltaic</system_type>
<model_name>RE410CAA Pure 2</model_name> <model_name>RE410CAA Pure 2</model_name>
<manufacturer>REC</manufacturer> <manufacturer>REC</manufacturer>
<nominal_electricity_output>312</nominal_electricity_output> <nominal_electricity_output>312</nominal_electricity_output>
@ -316,7 +316,7 @@
<pv_generation_component> <pv_generation_component>
<system_id>10</system_id> <system_id>10</system_id>
<name>Photovoltaic Module</name> <name>Photovoltaic Module</name>
<system_type>photovoltaic</system_type> <system_type>Photovoltaic</system_type>
<model_name>RE420CAA Pure 2</model_name> <model_name>RE420CAA Pure 2</model_name>
<manufacturer>REC</manufacturer> <manufacturer>REC</manufacturer>
<nominal_electricity_output>320</nominal_electricity_output> <nominal_electricity_output>320</nominal_electricity_output>
@ -337,7 +337,7 @@
<pv_generation_component> <pv_generation_component>
<system_id>11</system_id> <system_id>11</system_id>
<name>Photovoltaic Module</name> <name>Photovoltaic Module</name>
<system_type>photovoltaic</system_type> <system_type>Photovoltaic</system_type>
<model_name>RE430CAA Pure 2</model_name> <model_name>RE430CAA Pure 2</model_name>
<manufacturer>REC</manufacturer> <manufacturer>REC</manufacturer>
<nominal_electricity_output>327</nominal_electricity_output> <nominal_electricity_output>327</nominal_electricity_output>
@ -358,7 +358,7 @@
<pv_generation_component> <pv_generation_component>
<system_id>12</system_id> <system_id>12</system_id>
<name>Photovoltaic Module</name> <name>Photovoltaic Module</name>
<system_type>photovoltaic</system_type> <system_type>Photovoltaic</system_type>
<model_name>REC600AA Pro M</model_name> <model_name>REC600AA Pro M</model_name>
<manufacturer>REC</manufacturer> <manufacturer>REC</manufacturer>
<nominal_electricity_output>457</nominal_electricity_output> <nominal_electricity_output>457</nominal_electricity_output>
@ -379,7 +379,7 @@
<pv_generation_component> <pv_generation_component>
<system_id>13</system_id> <system_id>13</system_id>
<name>Photovoltaic Module</name> <name>Photovoltaic Module</name>
<system_type>photovoltaic</system_type> <system_type>Photovoltaic</system_type>
<model_name>REC610AA Pro M</model_name> <model_name>REC610AA Pro M</model_name>
<manufacturer>REC</manufacturer> <manufacturer>REC</manufacturer>
<nominal_electricity_output>464</nominal_electricity_output> <nominal_electricity_output>464</nominal_electricity_output>
@ -400,7 +400,7 @@
<pv_generation_component> <pv_generation_component>
<system_id>14</system_id> <system_id>14</system_id>
<name>Photovoltaic Module</name> <name>Photovoltaic Module</name>
<system_type>photovoltaic</system_type> <system_type>Photovoltaic</system_type>
<model_name>REC620AA Pro M</model_name> <model_name>REC620AA Pro M</model_name>
<manufacturer>REC</manufacturer> <manufacturer>REC</manufacturer>
<nominal_electricity_output>472</nominal_electricity_output> <nominal_electricity_output>472</nominal_electricity_output>
@ -421,7 +421,7 @@
<pv_generation_component> <pv_generation_component>
<system_id>15</system_id> <system_id>15</system_id>
<name>Photovoltaic Module</name> <name>Photovoltaic Module</name>
<system_type>photovoltaic</system_type> <system_type>Photovoltaic</system_type>
<model_name>REC630AA Pro M</model_name> <model_name>REC630AA Pro M</model_name>
<manufacturer>REC</manufacturer> <manufacturer>REC</manufacturer>
<nominal_electricity_output>480</nominal_electricity_output> <nominal_electricity_output>480</nominal_electricity_output>
@ -442,7 +442,7 @@
<pv_generation_component> <pv_generation_component>
<system_id>16</system_id> <system_id>16</system_id>
<name>Photovoltaic Module</name> <name>Photovoltaic Module</name>
<system_type>photovoltaic</system_type> <system_type>Photovoltaic</system_type>
<model_name>REC640AA Pro M</model_name> <model_name>REC640AA Pro M</model_name>
<manufacturer>REC</manufacturer> <manufacturer>REC</manufacturer>
<nominal_electricity_output>487</nominal_electricity_output> <nominal_electricity_output>487</nominal_electricity_output>

View File

@ -1,248 +0,0 @@
"""
Cerc Idf exports one city or some buildings to idf format
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Guille Guillermo.GutierrezMorote@concordia.ca
Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
Oriol Gavalda Torrellas oriol.gavalda@concordia.ca
"""
import copy
import os
import shutil
import subprocess
import hub.exports.building_energy.idf_helper as idf_cte
import hub.helpers.constants as cte
from hub.city_model_structure.attributes.schedule import Schedule
from hub.exports.building_energy.idf_helper.idf_appliance import IdfAppliance
from hub.exports.building_energy.idf_helper.idf_base import IdfBase
from hub.exports.building_energy.idf_helper.idf_construction import IdfConstruction
from hub.exports.building_energy.idf_helper.idf_dhw import IdfDhw
from hub.exports.building_energy.idf_helper.idf_file_schedule import IdfFileSchedule
from hub.exports.building_energy.idf_helper.idf_heating_system import IdfHeatingSystem
from hub.exports.building_energy.idf_helper.idf_infiltration import IdfInfiltration
from hub.exports.building_energy.idf_helper.idf_lighting import IdfLighting
from hub.exports.building_energy.idf_helper.idf_material import IdfMaterial
from hub.exports.building_energy.idf_helper.idf_occupancy import IdfOccupancy
from hub.exports.building_energy.idf_helper.idf_schedule import IdfSchedule
from hub.exports.building_energy.idf_helper.idf_shading import IdfShading
from hub.exports.building_energy.idf_helper.idf_surfaces import IdfSurfaces
from hub.exports.building_energy.idf_helper.idf_thermostat import IdfThermostat
from hub.exports.building_energy.idf_helper.idf_ventilation import IdfVentilation
from hub.exports.building_energy.idf_helper.idf_window import IdfWindow
from hub.exports.building_energy.idf_helper.idf_windows_constructions import IdfWindowsConstructions
from hub.exports.building_energy.idf_helper.idf_windows_material import IdfWindowsMaterial
from hub.exports.building_energy.idf_helper.idf_zone import IdfZone
class CercIdf(IdfBase):
"""
Exports city to IDF
"""
_schedules_added_to_idf = {}
_materials_added_to_idf = {}
_windows_added_to_idf = {}
_constructions_added_to_idf = {}
_thermostat_added_to_idf = {}
def __init__(self, city, output_path, idf_file_path, idd_file_path, epw_file_path, target_buildings=None):
super().__init__(city, output_path, idf_file_path, idd_file_path, epw_file_path, target_buildings)
self._add_surfaces = IdfSurfaces.add
self._add_file_schedule = IdfFileSchedule.add
self._add_idf_schedule = IdfSchedule.add
self._add_construction = IdfConstruction.add
self._add_material = IdfMaterial.add
self._add_windows_material = IdfWindowsMaterial.add
self._add_windows_constructions = IdfWindowsConstructions.add
self._add_occupancy = IdfOccupancy.add
self._add_lighting = IdfLighting.add
self._add_appliance = IdfAppliance.add
self._add_infiltration = IdfInfiltration.add
self._add_infiltration_surface = IdfInfiltration.add_surface
self._add_ventilation = IdfVentilation.add
self._add_zone = IdfZone.add
self._add_thermostat = IdfThermostat.add
self._add_heating_system = IdfHeatingSystem.add
self._add_dhw = IdfDhw.add
self._add_shading = IdfShading.add
self._add_windows = IdfWindow.add
with open(self._idf_file_path, 'r', encoding='UTF-8') as base_idf:
lines = base_idf.readlines()
# Change city name
comment = f' !- Name'
field = f' Buildings in {self._city.name},'.ljust(26, ' ')
lines[15] = f'{field}{comment}\n'
with open(self._output_file_path, 'w', encoding='UTF-8') as self._idf_file:
self._idf_file.writelines(lines)
self._export()
def _create_geometry_rules(self):
file = self._files['constructions']
self._write_to_idf_format(file, idf_cte.GLOBAL_GEOMETRY_RULES)
self._write_to_idf_format(file, 'UpperLeftCorner', 'Starting Vertex Position')
self._write_to_idf_format(file, 'CounterClockWise', 'Vertex Entry Direction')
self._write_to_idf_format(file, 'World', 'Coordinate System', ';')
def _merge_files(self):
for file in self._files.values():
file.close()
for path in self._file_paths.values():
with open(path, 'r', encoding='UTF-8') as file:
lines = file.readlines()
self._idf_file.writelines(lines)
for path in self._file_paths.values():
os.unlink(path)
def _add_outputs(self):
with open(self._outputs_file_path, 'r', encoding='UTF-8') as base_idf:
lines = base_idf.readlines()
self._idf_file.writelines(lines)
@staticmethod
def _create_infiltration_schedules(thermal_zone):
_infiltration_schedules = []
if thermal_zone.thermal_control is None:
return []
for hvac_availability_schedule in thermal_zone.thermal_control.hvac_availability_schedules:
_schedule = Schedule()
_schedule.type = cte.INFILTRATION
_schedule.data_type = cte.FRACTION
_schedule.time_step = cte.HOUR
_schedule.time_range = cte.DAY
_schedule.day_types = copy.deepcopy(hvac_availability_schedule.day_types)
_infiltration_values = []
for hvac_value in hvac_availability_schedule.values:
if hvac_value == 0:
_infiltration_values.append(1.0)
else:
if thermal_zone.infiltration_rate_system_off == 0:
_infiltration_values.append(0.0)
else:
_infiltration_values.append(
thermal_zone.infiltration_rate_system_on / thermal_zone.infiltration_rate_system_off)
_schedule.values = _infiltration_values
_infiltration_schedules.append(_schedule)
return _infiltration_schedules
@staticmethod
def _create_ventilation_schedules(thermal_zone):
_ventilation_schedules = []
if thermal_zone.thermal_control is None:
return []
for hvac_availability_schedule in thermal_zone.thermal_control.hvac_availability_schedules:
_schedule = Schedule()
_schedule.type = cte.VENTILATION
_schedule.data_type = cte.FRACTION
_schedule.time_step = cte.HOUR
_schedule.time_range = cte.DAY
_schedule.day_types = copy.deepcopy(hvac_availability_schedule.day_types)
_ventilation_schedules = thermal_zone.thermal_control.hvac_availability_schedules
return _ventilation_schedules
@staticmethod
def _create_constant_value_schedules(value, amount):
_schedule = Schedule()
_schedule.type = ''
_schedule.data_type = cte.ANY_NUMBER
_schedule.time_step = cte.HOUR
_schedule.time_range = cte.DAY
_schedule.day_types = ['monday',
'tuesday',
'wednesday',
'thursday',
'friday',
'saturday',
'sunday',
'holiday',
'winter_design_day',
'summer_design_day']
_schedule.values = [value for _ in range(0, amount)]
return [_schedule]
def _export(self):
for building in self._city.buildings:
is_target = building.name in self._target_buildings or building.name in self._adjacent_buildings
for internal_zone in building.internal_zones:
if internal_zone.thermal_zones_from_internal_zones is None:
is_target = False
continue
for thermal_zone in internal_zone.thermal_zones_from_internal_zones:
if is_target:
service_temperature = thermal_zone.domestic_hot_water.service_temperature
usage = thermal_zone.usage_name
occ = thermal_zone.occupancy
if occ.occupancy_density == 0:
total_heat = 0
else:
total_heat = (
occ.sensible_convective_internal_gain +
occ.sensible_radiative_internal_gain +
occ.latent_internal_gain
) / occ.occupancy_density
self._add_idf_schedule(self, usage, 'Infiltration', self._create_infiltration_schedules(thermal_zone))
self._add_idf_schedule(self, usage, 'Ventilation', self._create_ventilation_schedules(thermal_zone))
self._add_idf_schedule(self, usage, 'Occupancy', thermal_zone.occupancy.occupancy_schedules)
self._add_idf_schedule(self, usage, 'HVAC AVAIL', thermal_zone.thermal_control.hvac_availability_schedules)
self._add_idf_schedule(self, usage, 'Heating thermostat',
thermal_zone.thermal_control.heating_set_point_schedules)
self._add_idf_schedule(self, usage, 'Cooling thermostat',
thermal_zone.thermal_control.cooling_set_point_schedules)
self._add_idf_schedule(self, usage, 'Lighting', thermal_zone.lighting.schedules)
self._add_idf_schedule(self, usage, 'Appliance', thermal_zone.appliances.schedules)
self._add_idf_schedule(self, usage, 'DHW_prof', thermal_zone.domestic_hot_water.schedules)
self._add_idf_schedule(self, usage, 'DHW_temp',
self._create_constant_value_schedules(service_temperature, 24))
self._add_idf_schedule(self, usage, 'Activity Level', self._create_constant_value_schedules(total_heat, 24))
self._add_file_schedule(self, usage, 'cold_temp',
self._create_constant_value_schedules(building.cold_water_temperature[cte.HOUR],
24))
for thermal_boundary in thermal_zone.thermal_boundaries:
self._add_material(self, thermal_boundary)
self._add_construction(self, thermal_boundary)
for thermal_opening in thermal_boundary.thermal_openings:
self._add_windows_material(self, thermal_boundary, thermal_opening)
self._add_windows_constructions(self, thermal_boundary)
self._add_zone(self, thermal_zone, building.name)
self._add_occupancy(self, thermal_zone, building.name)
self._add_lighting(self, thermal_zone, building.name)
self._add_appliance(self, thermal_zone, building.name)
if self._calculate_with_new_infiltration: # ToDo: Check with oriol if we want to keep the old method too
self._add_infiltration_surface(self, thermal_zone, building.name)
else:
self._add_infiltration(self, thermal_zone, building.name)
self._add_ventilation(self, thermal_zone, building.name)
self._add_thermostat(self, thermal_zone)
self._add_heating_system(self, thermal_zone, building.name)
self._add_dhw(self, thermal_zone, building.name)
if is_target:
self._add_surfaces(self, building, building.name)
self._add_windows(self, building)
else:
self._add_shading(self, building)
self._create_output_control_lighting() # Add lighting control to the lighting
# Create base values
self._create_geometry_rules()
# Merge files
self._merge_files()
self._add_outputs()
@property
def _energy_plus(self):
return shutil.which('energyplus')
def run(self):
cmd = [self._energy_plus,
'--weather', self._epw_file_path,
'--output-directory', self._output_path,
'--idd', self._idd_file_path,
'--expandobjects',
'--readvars',
'--output-prefix', f'{self._city.name}_',
self._output_file_path]
subprocess.run(cmd, cwd=self._output_path)

View File

@ -169,7 +169,7 @@ class EnergyAde:
def _building_geometry(self, building, building_dic, city): def _building_geometry(self, building, building_dic, city):
building_dic['bldg:Building']['bldg:function'] = building.function building_dic['bldg:Building']['bldg:function'] = building.function
building_dic['bldg:Building']['bldg:usage'] = building.usages building_dic['bldg:Building']['bldg:usage'] = building.usages_percentage
building_dic['bldg:Building']['bldg:yearOfConstruction'] = building.year_of_construction building_dic['bldg:Building']['bldg:yearOfConstruction'] = building.year_of_construction
building_dic['bldg:Building']['bldg:roofType'] = building.roof_type building_dic['bldg:Building']['bldg:roofType'] = building.roof_type
building_dic['bldg:Building']['bldg:measuredHeight'] = { building_dic['bldg:Building']['bldg:measuredHeight'] = {

View File

@ -8,12 +8,10 @@ Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concord
""" """
import copy import copy
import datetime import datetime
import shutil import glob
import subprocess import os
from pathlib import Path from pathlib import Path
from geomeppy import IDF from geomeppy import IDF
import hub.helpers.constants as cte import hub.helpers.constants as cte
from hub.city_model_structure.attributes.schedule import Schedule from hub.city_model_structure.attributes.schedule import Schedule
from hub.city_model_structure.building_demand.thermal_zone import ThermalZone from hub.city_model_structure.building_demand.thermal_zone import ThermalZone
@ -109,7 +107,6 @@ class Idf:
else: else:
for building_name in target_buildings: for building_name in target_buildings:
building = city.city_object(building_name) building = city.city_object(building_name)
print('Name: ', building_name)
if building.neighbours is not None: if building.neighbours is not None:
self._adjacent_buildings += building.neighbours self._adjacent_buildings += building.neighbours
self._export() self._export()
@ -447,7 +444,7 @@ class Idf:
subcategory = f'ELECTRIC EQUIPMENT#{zone_name}#InteriorEquipment' subcategory = f'ELECTRIC EQUIPMENT#{zone_name}#InteriorEquipment'
self._idf.newidfobject(self._APPLIANCES, self._idf.newidfobject(self._APPLIANCES,
Fuel_Type=fuel_type, Fuel_Type=fuel_type,
Name=zone_name, Name=f'{zone_name}_appliance',
Zone_or_ZoneList_or_Space_or_SpaceList_Name=zone_name, Zone_or_ZoneList_or_Space_or_SpaceList_Name=zone_name,
Schedule_Name=f'Appliance schedules {thermal_zone.usage_name}', Schedule_Name=f'Appliance schedules {thermal_zone.usage_name}',
Design_Level_Calculation_Method=method, Design_Level_Calculation_Method=method,
@ -470,7 +467,7 @@ class Idf:
def _add_infiltration_surface(self, thermal_zone, zone_name): def _add_infiltration_surface(self, thermal_zone, zone_name):
schedule = f'INF_CONST schedules {thermal_zone.usage_name}' schedule = f'INF_CONST schedules {thermal_zone.usage_name}'
_infiltration = thermal_zone.infiltration_rate_area_system_off* cte.INFILTRATION_75PA_TO_4PA _infiltration = thermal_zone.infiltration_rate_area_system_off*1
self._idf.newidfobject(self._INFILTRATION, self._idf.newidfobject(self._INFILTRATION,
Name=f'{zone_name}_infiltration', Name=f'{zone_name}_infiltration',
Zone_or_ZoneList_or_Space_or_SpaceList_Name=zone_name, Zone_or_ZoneList_or_Space_or_SpaceList_Name=zone_name,
@ -504,7 +501,7 @@ class Idf:
) )
def _rename_building(self, city_name): def _rename_building(self, city_name):
name = str(city_name.encode("utf-8")) name = str(str(city_name.encode("utf-8")))
for building in self._idf.idfobjects[self._BUILDING]: for building in self._idf.idfobjects[self._BUILDING]:
building.Name = f'Buildings in {name}' building.Name = f'Buildings in {name}'
building['Solar_Distribution'] = 'FullExterior' building['Solar_Distribution'] = 'FullExterior'
@ -531,12 +528,11 @@ class Idf:
self._remove_sizing_periods() self._remove_sizing_periods()
self._rename_building(self._city.name) self._rename_building(self._city.name)
self._lod = self._city.level_of_detail.geometry self._lod = self._city.level_of_detail.geometry
is_target = False
for building in self._city.buildings: for building in self._city.buildings:
is_target = building.name in self._target_buildings or building.name in self._adjacent_buildings is_target = building.name in self._target_buildings or building.name in self._adjacent_buildings
for internal_zone in building.internal_zones: for internal_zone in building.internal_zones:
if internal_zone.thermal_zones_from_internal_zones is None: if internal_zone.thermal_zones_from_internal_zones is None:
self._target_buildings.remove(building.name) self._target_buildings.remoidf_surface_typeve(building.name)
is_target = False is_target = False
continue continue
for thermal_zone in internal_zone.thermal_zones_from_internal_zones: for thermal_zone in internal_zone.thermal_zones_from_internal_zones:
@ -590,7 +586,9 @@ class Idf:
if self._export_type == "Surfaces": if self._export_type == "Surfaces":
if is_target: if is_target:
if building.thermal_zones_from_internal_zones is not None: if building.thermal_zones_from_internal_zones is not None:
start = datetime.datetime.now()
self._add_surfaces(building, building.name) self._add_surfaces(building, building.name)
print(f'add surfaces {datetime.datetime.now() - start}')
else: else:
self._add_pure_geometry(building, building.name) self._add_pure_geometry(building, building.name)
else: else:
@ -653,26 +651,14 @@ class Idf:
self._idf.removeidfobject(window) self._idf.removeidfobject(window)
self._idf.saveas(str(self._output_file)) self._idf.saveas(str(self._output_file))
for building in self._city.buildings:
if self._export_type == "Surfaces":
if is_target and building.thermal_zones_from_internal_zones is not None:
self._add_surfaces(building, building.name)
return self._idf return self._idf
@property
def _energy_plus(self):
return shutil.which('energyplus')
def run(self): def run(self):
cmd = [self._energy_plus, """
'--weather', self._epw_file_path, Start the energy plus simulation
'--output-directory', self._output_path, """
'--idd', self._idd_file_path, self._idf.run(expandobjects=False, readvars=True, output_directory=self._output_path,
'--expandobjects', output_prefix=f'{self._city.name}_')
'--readvars',
'--output-prefix', f'{self._city.name}_',
self._idf_file_path]
subprocess.run(cmd, cwd=self._output_path)
def _add_block(self, building): def _add_block(self, building):
_points = self._matrix_to_2d_list(building.foot_print.coordinates) _points = self._matrix_to_2d_list(building.foot_print.coordinates)
@ -741,10 +727,7 @@ class Idf:
else: else:
# idf only allows setting wwr for external walls # idf only allows setting wwr for external walls
wwr = 0 wwr = 0
try: self._idf.set_wwr(wwr)
self._idf.set_wwr(wwr, construction='window_construction_1')
except ValueError:
self._idf.set_wwr(0, construction='window_construction_1')
def _add_surfaces(self, building, zone_name): def _add_surfaces(self, building, zone_name):
for thermal_zone in building.thermal_zones_from_internal_zones: for thermal_zone in building.thermal_zones_from_internal_zones:
@ -775,11 +758,13 @@ class Idf:
else: else:
construction_name = f'{boundary.construction_name} {boundary.parent_surface.type}' construction_name = f'{boundary.construction_name} {boundary.parent_surface.type}'
_kwargs['Construction_Name'] = construction_name _kwargs['Construction_Name'] = construction_name
start = datetime.datetime.now()
surface = self._idf.newidfobject(self._SURFACE, **_kwargs) surface = self._idf.newidfobject(self._SURFACE, **_kwargs)
coordinates = self._matrix_to_list(boundary.parent_surface.solid_polygon.coordinates, coordinates = self._matrix_to_list(boundary.parent_surface.solid_polygon.coordinates,
self._city.lower_corner) self._city.lower_corner)
surface.setcoords(coordinates) surface.setcoords(coordinates)
if self._lod >= 3: if self._lod >= 3:
for internal_zone in building.internal_zones: for internal_zone in building.internal_zones:
for thermal_zone in internal_zone.thermal_zones_from_internal_zones: for thermal_zone in internal_zone.thermal_zones_from_internal_zones:
@ -791,10 +776,7 @@ class Idf:
for surface in building.surfaces: for surface in building.surfaces:
if surface.type == cte.WALL: if surface.type == cte.WALL:
wwr = surface.associated_thermal_boundaries[0].window_ratio wwr = surface.associated_thermal_boundaries[0].window_ratio
try:
self._idf.set_wwr(wwr, construction='window_construction_1') self._idf.set_wwr(wwr, construction='window_construction_1')
except ValueError:
self._idf.set_wwr(0, construction='window_construction_1')
def _add_windows_by_vertices(self, boundary): def _add_windows_by_vertices(self, boundary):
raise NotImplementedError raise NotImplementedError

View File

@ -1,62 +0,0 @@
!- Linux Line endings
Version,
24.1; !- Version Identifier
SimulationControl,
No, !- Do Zone Sizing Calculation
No, !- Do System Sizing Calculation
No, !- Do Plant Sizing Calculation
No, !- Run Simulation for Sizing Periods
Yes, !- Run Simulation for Weather File Run Periods
No, !- Do HVAC Sizing Simulation for Sizing Periods
1; !- Maximum Number of HVAC Sizing Simulation Passes
Building,
Buildings in #CITY#, !- Name
0, !- North Axis
Suburbs, !- Terrain
0.04, !- Loads Convergence Tolerance Value
0.4, !- Temperature Convergence Tolerance Value
FullExterior, !- Solar Distribution
25, !- Maximum Number of Warmup Days
6; !- Minimum Number of Warmup Days
Timestep,
4; !- Number of Timesteps per Hour
RunPeriod,
Run Period 1, !- Name
1, !- Begin Month
1, !- Begin Day of Month
, !- Begin Year
12, !- End Month
31, !- End Day of Month
, !- End Year
Tuesday, !- Day of Week for Start Day
Yes, !- Use Weather File Holidays and Special Days
Yes, !- Use Weather File Daylight Saving Period
No, !- Apply Weekend Holiday Rule
Yes, !- Use Weather File Rain Indicators
Yes; !- Use Weather File Snow Indicators
SCHEDULETYPELIMITS,
Any Number, !- Name
, !- Lower Limit Value
, !- Upper Limit Value
, !- Numeric Type
Dimensionless; !- Unit Type
SCHEDULETYPELIMITS,
Fraction, !- Name
0, !- Lower Limit Value
1, !- Upper Limit Value
Continuous, !- Numeric Type
Dimensionless; !- Unit Type
SCHEDULETYPELIMITS,
On/Off, !- Name
0, !- Lower Limit Value
1, !- Upper Limit Value
Discrete, !- Numeric Type
Dimensionless; !- Unit Type

View File

@ -1,74 +0,0 @@
Output:Table:SummaryReports,
AnnualBuildingUtilityPerformanceSummary, !- Report 1 Name
DemandEndUseComponentsSummary, !- Report 2 Name
SensibleHeatGainSummary, !- Report 3 Name
InputVerificationandResultsSummary, !- Report 4 Name
AdaptiveComfortSummary, !- Report 5 Name
Standard62.1Summary, !- Report 6 Name
ClimaticDataSummary, !- Report 7 Name
EquipmentSummary, !- Report 8 Name
EnvelopeSummary, !- Report 9 Name
LightingSummary, !- Report 10 Name
HVACSizingSummary, !- Report 11 Name
SystemSummary, !- Report 12 Name
ComponentSizingSummary, !- Report 13 Name
OutdoorAirSummary, !- Report 14 Name
ObjectCountSummary, !- Report 15 Name
EndUseEnergyConsumptionOtherFuelsMonthly, !- Report 16 Name
PeakEnergyEndUseOtherFuelsMonthly; !- Report 17 Name
OutputControl:Table:Style,
CommaAndHTML, !- Column Separator
JtoKWH; !- Unit Conversion
OUTPUT:VARIABLE,
*, !- Key Value
Zone Ideal Loads Supply Air Total Heating Energy, !- Variable Name
Hourly; !- Reporting Frequency
OUTPUT:VARIABLE,
*, !- Key Value
Zone Ideal Loads Supply Air Total Cooling Energy, !- Variable Name
Hourly; !- Reporting Frequency
OUTPUT:VARIABLE,
*, !- Key Value
Water Use Equipment Heating Rate, !- Variable Name
Hourly; !- Reporting Frequency
OUTPUT:VARIABLE,
*, !- Key Value
Zone Lights Electricity Rate, !- Variable Name
Hourly; !- Reporting Frequency
OUTPUT:VARIABLE,
*, !- Key Value
Other Equipment Electricity Rate, !- Variable Name
Hourly; !- Reporting Frequency
OUTPUT:VARIABLE,
*, !- Key Value
Zone Air Temperature, !- Variable Name
Hourly; !- Reporting Frequency
OUTPUT:VARIABLE,
*, !- Key Value
Zone Air Relative Humidity, !- Variable Name
Hourly; !- Reporting Frequency
Output:Meter,
DISTRICTHEATING:Facility, !- Key Name
hourly; !- Reporting Frequency
Output:Meter,
DISTRICTCOOLING:Facility, !- Key Name
hourly; !- Reporting Frequency
Output:Meter,
InteriorEquipment:Electricity, !- Key Name
hourly; !- Reporting Frequency
Output:Meter,
InteriorLights:Electricity, !- Key Name
hourly; !- Reporting Frequency

View File

@ -1,60 +0,0 @@
import hub.helpers.constants as cte
BUILDING_SURFACE = '\nBUILDINGSURFACE:DETAILED,\n'
WINDOW_SURFACE = '\nFENESTRATIONSURFACE:DETAILED,\n'
COMPACT_SCHEDULE = '\nSCHEDULE:COMPACT,\n'
FILE_SCHEDULE = '\nSCHEDULE:FILE,\n'
NOMASS_MATERIAL = '\nMATERIAL:NOMASS,\n'
SOLID_MATERIAL = '\nMATERIAL,\n'
WINDOW_MATERIAL = '\nWINDOWMATERIAL:SIMPLEGLAZINGSYSTEM,\n'
CONSTRUCTION = '\nCONSTRUCTION,\n'
ZONE = '\nZONE,\n'
GLOBAL_GEOMETRY_RULES = '\nGlobalGeometryRules,\n'
PEOPLE = '\nPEOPLE,\n'
LIGHTS = '\nLIGHTS,\n'
APPLIANCES = '\nOTHEREQUIPMENT,\n'
OUTPUT_CONTROL = '\nOutputControl:IlluminanceMap:Style,\n'
INFILTRATION = '\nZONEINFILTRATION:DESIGNFLOWRATE,\n'
VENTILATION = '\nZONEVENTILATION:DESIGNFLOWRATE,\n'
THERMOSTAT = '\nHVACTEMPLATE:THERMOSTAT,\n'
IDEAL_LOAD_SYSTEM = '\nHVACTEMPLATE:ZONE:IDEALLOADSAIRSYSTEM,\n'
DHW = '\nWATERUSE:EQUIPMENT,\n'
SHADING = '\nSHADING:BUILDING:DETAILED,\n'
AUTOCALCULATE = 'autocalculate'
ROUGHNESS = 'MediumRough'
OUTDOORS = 'Outdoors'
GROUND = 'Ground'
SURFACE = 'Surface'
SUN_EXPOSED = 'SunExposed'
WIND_EXPOSED = 'WindExposed'
NON_SUN_EXPOSED = 'NoSun'
NON_WIND_EXPOSED = 'NoWind'
EMPTY = ''
idf_surfaces_dictionary = {
cte.WALL: 'wall',
cte.GROUND: 'floor',
cte.ROOF: 'roof'
}
idf_type_limits = {
cte.ON_OFF: 'on/off',
cte.FRACTION: 'Fraction',
cte.ANY_NUMBER: 'Any Number',
cte.CONTINUOUS: 'Continuous',
cte.DISCRETE: 'Discrete'
}
idf_day_types = {
cte.MONDAY: 'Monday',
cte.TUESDAY: 'Tuesday',
cte.WEDNESDAY: 'Wednesday',
cte.THURSDAY: 'Thursday',
cte.FRIDAY: 'Friday',
cte.SATURDAY: 'Saturday',
cte.SUNDAY: 'Sunday',
cte.HOLIDAY: 'Holidays',
cte.WINTER_DESIGN_DAY: 'WinterDesignDay',
cte.SUMMER_DESIGN_DAY: 'SummerDesignDay'
}

View File

@ -1,26 +0,0 @@
import hub.exports.building_energy.idf_helper as idf_cte
from hub.exports.building_energy.idf_helper.idf_base import IdfBase
class IdfAppliance(IdfBase):
@staticmethod
def add(self, thermal_zone, zone_name):
schedule_name = f'Appliance schedules {thermal_zone.usage_name}'
storeys_number = int(thermal_zone.total_floor_area / thermal_zone.footprint_area)
watts_per_zone_floor_area = thermal_zone.appliances.density * storeys_number
subcategory = f'ELECTRIC EQUIPMENT#{zone_name}#InteriorEquipment'
file = self._files['appliances']
self._write_to_idf_format(file, idf_cte.APPLIANCES)
self._write_to_idf_format(file, zone_name, 'Name')
self._write_to_idf_format(file, 'Electricity', 'Fuel Type')
self._write_to_idf_format(file, zone_name, 'Zone or ZoneList or Space or SpaceList Name')
self._write_to_idf_format(file, schedule_name, 'Schedule Name')
self._write_to_idf_format(file, 'Watts/Area', 'Design Level Calculation Method')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Design Level')
self._write_to_idf_format(file, watts_per_zone_floor_area, 'Power per Zone Floor Area')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Power per Person')
self._write_to_idf_format(file, thermal_zone.appliances.latent_fraction, 'Fraction Latent')
self._write_to_idf_format(file, thermal_zone.appliances.radiative_fraction, 'Fraction Radiant')
self._write_to_idf_format(file, 0, 'Fraction Lost')
self._write_to_idf_format(file, 0, 'Carbon Dioxide Generation Rate')
self._write_to_idf_format(file, subcategory, 'EndUse Subcategory', ';')

View File

@ -1,78 +0,0 @@
import os
from pathlib import Path
import hub.exports.building_energy.idf_helper as idf_cte
class IdfBase:
def __init__(self, city, output_path, idf_file_path, idd_file_path, epw_file_path, target_buildings=None,
_calculate_with_new_infiltration=True):
self._city = city
self._output_path = str(output_path.resolve())
self._output_file_path = str((output_path / f'{city.name}.idf').resolve())
self._file_paths = {
'schedules': str((output_path / 'schedules.idf').resolve()),
'file_schedules': str((output_path / 'file_schedules.idf').resolve()),
'solid_materials': str((output_path / 'solid_materials.idf').resolve()),
'nomass_materials': str((output_path / 'nomass_materials.idf').resolve()),
'window_materials': str((output_path / 'window_materials.idf').resolve()),
'constructions': str((output_path / 'constructions.idf').resolve()),
'zones': str((output_path / 'zones.idf').resolve()),
'surfaces': str((output_path / 'surfaces.idf').resolve()),
'fenestration': str((output_path / 'fenestration.idf').resolve()),
'occupancy': str((output_path / 'occupancy.idf').resolve()),
'lighting': str((output_path / 'lights.idf').resolve()),
'appliances': str((output_path / 'appliances.idf').resolve()),
'shading': str((output_path / 'shading.idf').resolve()),
'infiltration': str((output_path / 'infiltration.idf').resolve()),
'ventilation': str((output_path / 'ventilation.idf').resolve()),
'thermostat': str((output_path / 'thermostat.idf').resolve()),
'ideal_load_system': str((output_path / 'ideal_load_system.idf').resolve()),
'dhw': str((output_path / 'dhw.idf').resolve()),
}
self._files = {}
for key, value in self._file_paths.items():
self._files[key] = open(value, 'w', encoding='UTF-8')
self._idd_file_path = str(idd_file_path)
self._idf_file_path = str(idf_file_path)
self._outputs_file_path = str(Path(idf_file_path).parent / 'outputs.idf')
self._epw_file_path = str(epw_file_path)
self._target_buildings = target_buildings
self._adjacent_buildings = []
if target_buildings is None:
self._target_buildings = [building.name for building in self._city.buildings]
else:
for building_name in target_buildings:
building = city.city_object(building_name)
if building.neighbours is not None:
self._adjacent_buildings += building.neighbours
self._calculate_with_new_infiltration = _calculate_with_new_infiltration
def _create_output_control_lighting(self):
file = self._files['appliances']
self._write_to_idf_format(file, idf_cte.OUTPUT_CONTROL)
self._write_to_idf_format(file, 'Comma', 'Column Separator', ';')
@staticmethod
def _write_to_idf_format(file, field, comment='', eol=','):
if comment != '':
comment = f' !- {comment}'
field = f' {field}{eol}'.ljust(26, ' ')
file.write(f'{field}{comment}\n')
else:
file.write(f'{field}{comment}')
@staticmethod
def _matrix_to_list(points, lower_corner):
lower_x = lower_corner[0]
lower_y = lower_corner[1]
lower_z = lower_corner[2]
points_list = []
for point in points:
point_tuple = (point[0] - lower_x, point[1] - lower_y, point[2] - lower_z)
points_list.append(point_tuple)
return points_list

View File

@ -1,56 +0,0 @@
import hub.exports.building_energy.idf_helper as idf_cte
from hub.city_model_structure.building_demand.layer import Layer
from hub.exports.building_energy.idf_helper.idf_base import IdfBase
class IdfConstruction(IdfBase):
@staticmethod
def _add_solid_material(self, layer):
file = self._files['solid_materials']
self._write_to_idf_format(file, idf_cte.SOLID_MATERIAL)
self._write_to_idf_format(file, layer.material_name, 'Name')
self._write_to_idf_format(file, idf_cte.ROUGHNESS, 'Roughness')
self._write_to_idf_format(file, layer.thickness, 'Thickness')
self._write_to_idf_format(file, layer.conductivity, 'Conductivity')
self._write_to_idf_format(file, layer.density, 'Density')
self._write_to_idf_format(file, layer.specific_heat, 'Specific Heat')
self._write_to_idf_format(file, layer.thermal_absorptance, 'Thermal Absorptance')
self._write_to_idf_format(file, layer.solar_absorptance, 'Solar Absorptance')
self._write_to_idf_format(file, layer.visible_absorptance, 'Visible Absorptance', ';')
@staticmethod
def _add_default_material(self):
layer = Layer()
layer.material_name = 'DefaultMaterial'
layer.thickness = 0.1
layer.conductivity = 0.1
layer.density = 1000
layer.specific_heat = 1000
layer.thermal_absorptance = 0.9
layer.solar_absorptance = 0.9
layer.visible_absorptance = 0.7
IdfConstruction._add_solid_material(self, layer)
return layer
@staticmethod
def add(self, thermal_boundary):
if thermal_boundary.layers is None:
thermal_boundary.layers = [IdfConstruction._add_default_material(self)]
name = f'{thermal_boundary.construction_name} {thermal_boundary.parent_surface.type}'
if name not in self._constructions_added_to_idf:
self._constructions_added_to_idf[name] = True
file = self._files['constructions']
self._write_to_idf_format(file, idf_cte.CONSTRUCTION)
self._write_to_idf_format(file, name, 'Name')
eol = ','
if len(thermal_boundary.layers) == 1:
eol = ';'
self._write_to_idf_format(file, thermal_boundary.layers[0].material_name, 'Outside Layer', eol)
for i in range(1, len(thermal_boundary.layers) - 1):
comment = f'Layer {i + 1}'
material_name = thermal_boundary.layers[i].material_name
if i == len(thermal_boundary.layers) - 2:
eol = ';'
self._write_to_idf_format(file, material_name, comment, eol)

View File

@ -1,21 +0,0 @@
import hub.exports.building_energy.idf_helper as idf_cte
from hub.exports.building_energy.idf_helper.idf_base import IdfBase
class IdfDhw(IdfBase):
@staticmethod
def add(self, thermal_zone, zone_name):
peak_flow_rate = thermal_zone.domestic_hot_water.peak_flow * thermal_zone.total_floor_area
flow_rate_schedule = f'DHW_prof schedules {thermal_zone.usage_name}'
dhw_schedule = f'DHW_temp schedules {thermal_zone.usage_name}'
cold_temp_schedule = f'cold_temp schedules {thermal_zone.usage_name}'
file = self._files['dhw']
self._write_to_idf_format(file, idf_cte.DHW)
self._write_to_idf_format(file, zone_name, 'Name')
self._write_to_idf_format(file, zone_name, 'EndUse Subcategory')
self._write_to_idf_format(file, peak_flow_rate, 'Peak Flow Rate')
self._write_to_idf_format(file, flow_rate_schedule, 'Flow Rate Fraction Schedule Name')
self._write_to_idf_format(file, dhw_schedule, 'Target Temperature Schedule Name')
self._write_to_idf_format(file, dhw_schedule, 'Hot Water Supply Temperature Schedule Name')
self._write_to_idf_format(file, cold_temp_schedule, 'Cold Water Supply Temperature Schedule Name')
self._write_to_idf_format(file, zone_name, 'Zone Name', ';')

View File

@ -1,30 +0,0 @@
from pathlib import Path
import hub.helpers.constants as cte
import hub.exports.building_energy.idf_helper as idf_cte
from hub.exports.building_energy.idf_helper.idf_base import IdfBase
class IdfFileSchedule(IdfBase):
@staticmethod
def add(self, usage, schedule_type, schedules):
schedule_name = f'{schedule_type} schedules {usage}'
for schedule in schedules:
if schedule_name not in self._schedules_added_to_idf:
self._schedules_added_to_idf[schedule_name] = True
file_name = str(
(Path(self._output_path) / f'{schedule_type} schedules {usage.replace("/", "_")}.csv').resolve())
with open(file_name, 'w', encoding='utf8') as file:
for value in schedule.values[0]:
file.write(f'{value},\n')
file = self._files['file_schedules']
self._write_to_idf_format(file, idf_cte.FILE_SCHEDULE)
self._write_to_idf_format(file, schedule_name, 'Name')
self._write_to_idf_format(file, idf_cte.idf_type_limits[schedule.data_type], 'Schedule Type Limits Name')
self._write_to_idf_format(file, Path(file_name).name, 'File Name')
self._write_to_idf_format(file, 1, 'Column Number')
self._write_to_idf_format(file, 0, 'Rows to Skip at Top')
self._write_to_idf_format(file, 8760, 'Number of Hours of Data')
self._write_to_idf_format(file, 'Comma', 'Column Separator')
self._write_to_idf_format(file, 'No', 'Interpolate to Timestep')
self._write_to_idf_format(file, '60', 'Minutes per Item')
self._write_to_idf_format(file, 'Yes', 'Adjust Schedule for Daylight Savings', ';')

View File

@ -1,41 +0,0 @@
import hub.exports.building_energy.idf_helper as idf_cte
from hub.exports.building_energy.idf_helper.idf_base import IdfBase
class IdfHeatingSystem(IdfBase):
@staticmethod
def add(self, thermal_zone, zone_name):
availability_schedule = f'HVAC AVAIL SCHEDULES {thermal_zone.usage_name}'
thermostat_name = f'Thermostat {thermal_zone.usage_name}'
file = self._files['ideal_load_system']
self._write_to_idf_format(file, idf_cte.IDEAL_LOAD_SYSTEM)
self._write_to_idf_format(file, zone_name, 'Zone Name')
self._write_to_idf_format(file, thermostat_name, 'Template Thermostat Name')
self._write_to_idf_format(file, availability_schedule, 'System Availability Schedule Name')
self._write_to_idf_format(file, 50, 'Maximum Heating Supply Air Temperature')
self._write_to_idf_format(file, 13, 'Minimum Cooling Supply Air Temperature')
self._write_to_idf_format(file, 0.0156, 'Maximum Heating Supply Air Humidity Ratio')
self._write_to_idf_format(file, 0.0077, 'Minimum Cooling Supply Air Humidity Ratio')
self._write_to_idf_format(file, 'NoLimit', 'Heating Limit')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Maximum Heating Air Flow Rate')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Maximum Sensible Heating Capacity')
self._write_to_idf_format(file, 'NoLimit', 'Cooling Limit')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Maximum Cooling Air Flow Rate')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Maximum Total Cooling Capacity')
self._write_to_idf_format(file, availability_schedule, 'Heating Availability Schedule Name')
self._write_to_idf_format(file, availability_schedule, 'Cooling Availability Schedule Name')
self._write_to_idf_format(file, 'ConstantSensibleHeatRatio', 'Dehumidification Control Type')
self._write_to_idf_format(file, 0.7, 'Cooling Sensible Heat Ratio')
self._write_to_idf_format(file, 60, 'Dehumidification Setpoint')
self._write_to_idf_format(file, 'None', 'Humidification Control Type')
self._write_to_idf_format(file, 30, 'Humidification Setpoint')
self._write_to_idf_format(file, 'None', 'Outdoor Air Method')
self._write_to_idf_format(file, 0.00944, 'Outdoor Air Flow Rate per Person')
self._write_to_idf_format(file, 0.0, 'Outdoor Air Flow Rate per Zone Floor Area')
self._write_to_idf_format(file, 0, 'Outdoor Air Flow Rate per Zone')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Design Specification Outdoor Air Object Name')
self._write_to_idf_format(file, 'None', 'Demand Controlled Ventilation Type')
self._write_to_idf_format(file, 'NoEconomizer', 'Outdoor Air Economizer Type')
self._write_to_idf_format(file, 'None', 'Heat Recovery Type')
self._write_to_idf_format(file, 0.70, 'Sensible Heat Recovery Effectiveness')
self._write_to_idf_format(file, 0.65, 'Latent Heat Recovery Effectiveness', ';')

View File

@ -1,32 +0,0 @@
import hub.exports.building_energy.idf_helper as idf_cte
import hub.helpers.constants as cte
from hub.exports.building_energy.idf_helper.idf_base import IdfBase
class IdfInfiltration(IdfBase):
@staticmethod
def add(self, thermal_zone, zone_name):
IdfInfiltration._add_infiltration(self, thermal_zone, zone_name, 'AirChanges/Hour', cte.HOUR_TO_SECONDS)
@staticmethod
def add_surface(self, thermal_zone, zone_name):
IdfInfiltration._add_infiltration(self, thermal_zone, zone_name, 'Flow/ExteriorWallArea', cte.INFILTRATION_75PA_TO_4PA)
@staticmethod
def _add_infiltration(self, thermal_zone, zone_name, calculation_method, multiplier):
schedule_name = f'Infiltration schedules {thermal_zone.usage_name}'
infiltration = thermal_zone.infiltration_rate_system_off * multiplier
file = self._files['infiltration']
self._write_to_idf_format(file, idf_cte.INFILTRATION)
self._write_to_idf_format(file, zone_name, 'Name')
self._write_to_idf_format(file, zone_name, 'Zone or ZoneList or Space or SpaceList Name')
self._write_to_idf_format(file, schedule_name, 'Schedule Name')
self._write_to_idf_format(file, calculation_method, 'Design Flow Rate Calculation Method')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Design Flow Rate')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Flow Rate per Floor Area')
self._write_to_idf_format(file, infiltration, 'Flow Rate per Exterior Surface Area')
self._write_to_idf_format(file, infiltration, 'Air Changes per Hour')
self._write_to_idf_format(file, 1, 'Constant Term Coefficient')
self._write_to_idf_format(file, 0, 'Temperature Term Coefficient')
self._write_to_idf_format(file, 0, 'Velocity Term Coefficient')
self._write_to_idf_format(file, 0, 'Velocity Squared Term Coefficient', ';')

View File

@ -1,28 +0,0 @@
import hub.exports.building_energy.idf_helper as idf_cte
from hub.exports.building_energy.idf_helper.idf_base import IdfBase
class IdfLighting(IdfBase):
@staticmethod
def add(self, thermal_zone, zone_name):
storeys_number = int(thermal_zone.total_floor_area / thermal_zone.footprint_area)
watts_per_zone_floor_area = thermal_zone.lighting.density * storeys_number
subcategory = f'ELECTRIC EQUIPMENT#{zone_name}#GeneralLights'
schedule_name = f'Lighting schedules {thermal_zone.usage_name}'
file = self._files['lighting']
self._write_to_idf_format(file, idf_cte.LIGHTS)
self._write_to_idf_format(file, f'{zone_name}_lights', 'Name')
self._write_to_idf_format(file, zone_name, 'Zone or ZoneList or Space or SpaceList Name')
self._write_to_idf_format(file, schedule_name, 'Schedule Name')
self._write_to_idf_format(file, 'Watts/Area', 'Design Level Calculation Method')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Lighting Level')
self._write_to_idf_format(file, watts_per_zone_floor_area, 'Watts per Zone Floor Area')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Watts per Person')
self._write_to_idf_format(file, 0, 'Return Air Fraction')
self._write_to_idf_format(file, thermal_zone.lighting.radiative_fraction, 'Fraction Radiant')
self._write_to_idf_format(file, 0, 'Fraction Visible')
self._write_to_idf_format(file, 1, 'Fraction Replaceable')
self._write_to_idf_format(file, subcategory, 'EndUse Subcategory')
self._write_to_idf_format(file, 'No', 'Return Air Fraction Calculated from Plenum Temperature')
self._write_to_idf_format(file, 0, 'Return Air Fraction Function of Plenum Temperature Coefficient 1')
self._write_to_idf_format(file, 0, 'Return Air Fraction Function of Plenum Temperature Coefficient 2', ';')

View File

@ -1,39 +0,0 @@
import hub.exports.building_energy.idf_helper as idf_cte
from hub.exports.building_energy.idf_helper.idf_base import IdfBase
class IdfMaterial(IdfBase):
@staticmethod
def _add_solid_material(self, layer):
file = self._files['solid_materials']
self._write_to_idf_format(file, idf_cte.SOLID_MATERIAL)
self._write_to_idf_format(file, layer.material_name, 'Name')
self._write_to_idf_format(file, idf_cte.ROUGHNESS, 'Roughness')
self._write_to_idf_format(file, layer.thickness, 'Thickness')
self._write_to_idf_format(file, layer.conductivity, 'Conductivity')
self._write_to_idf_format(file, layer.density, 'Density')
self._write_to_idf_format(file, layer.specific_heat, 'Specific Heat')
self._write_to_idf_format(file, layer.thermal_absorptance, 'Thermal Absorptance')
self._write_to_idf_format(file, layer.solar_absorptance, 'Solar Absorptance')
self._write_to_idf_format(file, layer.visible_absorptance, 'Visible Absorptance', ';')
@staticmethod
def _add_nomass_material(self, layer):
file = self._files['nomass_materials']
self._write_to_idf_format(file, idf_cte.NOMASS_MATERIAL)
self._write_to_idf_format(file, layer.material_name, 'Name')
self._write_to_idf_format(file, idf_cte.ROUGHNESS, 'Roughness')
self._write_to_idf_format(file, layer.thermal_resistance, 'Thermal Resistance')
self._write_to_idf_format(file, 0.9, 'Thermal Absorptance')
self._write_to_idf_format(file, 0.7, 'Solar Absorptance')
self._write_to_idf_format(file, 0.7, 'Visible Absorptance', ';')
@staticmethod
def add(self, thermal_boundary):
for layer in thermal_boundary.layers:
if layer.material_name not in self._materials_added_to_idf:
self._materials_added_to_idf[layer.material_name] = True
if layer.no_mass:
IdfMaterial._add_nomass_material(self, layer)
else:
IdfMaterial._add_solid_material(self, layer)

View File

@ -1,47 +0,0 @@
import hub.exports.building_energy.idf_helper as idf_cte
from hub.exports.building_energy.idf_helper.idf_base import IdfBase
class IdfOccupancy(IdfBase):
@staticmethod
def add(self, thermal_zone, zone_name):
number_of_people = thermal_zone.occupancy.occupancy_density * thermal_zone.total_floor_area
fraction_radiant = 0
total_sensible = (
thermal_zone.occupancy.sensible_radiative_internal_gain + thermal_zone.occupancy.sensible_convective_internal_gain
)
if total_sensible != 0:
fraction_radiant = thermal_zone.occupancy.sensible_radiative_internal_gain / total_sensible
occupancy_schedule = f'Occupancy schedules {thermal_zone.usage_name}'
activity_level_schedule = f'Activity Level schedules {thermal_zone.usage_name}'
file = self._files['occupancy']
self._write_to_idf_format(file, idf_cte.PEOPLE)
self._write_to_idf_format(file, f'{zone_name}_occupancy', 'Name')
self._write_to_idf_format(file, zone_name, 'Zone or ZoneList or Space or SpaceList Name')
self._write_to_idf_format(file, occupancy_schedule, 'Number of People Schedule Name')
self._write_to_idf_format(file, 'People', 'Number of People Calculation Method')
self._write_to_idf_format(file, number_of_people, 'Number of People')
self._write_to_idf_format(file, idf_cte.EMPTY, 'People per Floor Area')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Floor Area per Person')
self._write_to_idf_format(file, fraction_radiant, 'Fraction Radiant')
self._write_to_idf_format(file, idf_cte.AUTOCALCULATE, 'Sensible Heat Fraction')
self._write_to_idf_format(file, activity_level_schedule, 'Activity Level Schedule Name')
self._write_to_idf_format(file, '3.82e-08', 'Carbon Dioxide Generation Rate')
self._write_to_idf_format(file, 'No', 'Enable ASHRAE 55 Comfort Warnings')
self._write_to_idf_format(file, 'EnclosureAveraged', 'Mean Radiant Temperature Calculation Type')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Surface NameAngle Factor List Name')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Work Efficiency Schedule Name')
self._write_to_idf_format(file, 'ClothingInsulationSchedule', 'Clothing Insulation Calculation Method')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Clothing Insulation Calculation Method Schedule Name')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Clothing Insulation Schedule Name')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Air Velocity Schedule Name')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Thermal Comfort Model 1 Type')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Thermal Comfort Model 2 Type')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Thermal Comfort Model 3 Type')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Thermal Comfort Model 4 Type')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Thermal Comfort Model 5 Type')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Thermal Comfort Model 6 Type')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Thermal Comfort Model 7 Type')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Ankle Level Air Velocity Schedule Name')
self._write_to_idf_format(file, '15.56', 'Cold Stress Temperature Threshold')
self._write_to_idf_format(file, '30', 'Heat Stress Temperature Threshold', ';')

View File

@ -1,30 +0,0 @@
import hub.exports.building_energy.idf_helper as idf_cte
from hub.exports.building_energy.idf_helper.idf_base import IdfBase
class IdfSchedule(IdfBase):
@staticmethod
def add(self, usage, schedule_type, schedules):
if len(schedules) < 1:
return
schedule_name = f'{schedule_type} schedules {usage}'
if schedule_name not in self._schedules_added_to_idf:
self._schedules_added_to_idf[schedule_name] = True
file = self._files['schedules']
self._write_to_idf_format(file, idf_cte.COMPACT_SCHEDULE)
self._write_to_idf_format(file, schedule_name, 'Name')
self._write_to_idf_format(file, idf_cte.idf_type_limits[schedules[0].data_type], 'Schedule Type Limits Name')
self._write_to_idf_format(file, 'Through: 12/31', 'Field 1')
counter = 1
for j, schedule in enumerate(schedules):
_val = schedule.values
_new_field = ''
for day_type in schedule.day_types:
_new_field += f' {idf_cte.idf_day_types[day_type]}'
self._write_to_idf_format(file, f'For:{_new_field}', f'Field {j * 25 + 2}')
counter += 1
for i, _ in enumerate(_val):
self._write_to_idf_format(file, f'Until: {i + 1:02d}:00,{_val[i]}', f'Field {j * 25 + 3 + i}')
counter += 1
self._write_to_idf_format(file, 'For AllOtherDays', f'Field {counter + 1}')
self._write_to_idf_format(file, 'Until: 24:00,0.0', f'Field {counter + 2}', ';')

View File

@ -1,25 +0,0 @@
import hub.exports.building_energy.idf_helper as idf_cte
from hub.exports.building_energy.idf_helper.idf_base import IdfBase
class IdfShading(IdfBase):
@staticmethod
def add(self, building):
name = building.name
file = self._files['shading']
for s, surface in enumerate(building.surfaces):
self._write_to_idf_format(file, idf_cte.SHADING)
self._write_to_idf_format(file, f'{name}_{s}', 'Name')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Transmittance Schedule Name')
self._write_to_idf_format(file, idf_cte.AUTOCALCULATE, 'Number of Vertices')
eol = ','
coordinates = self._matrix_to_list(surface.solid_polygon.coordinates, self._city.lower_corner)
coordinates_length = len(coordinates)
for i, coordinate in enumerate(coordinates):
vertex = i + 1
if vertex == coordinates_length:
eol = ';'
self._write_to_idf_format(file, coordinate[0], f'Vertex {vertex} Xcoordinate')
self._write_to_idf_format(file, coordinate[1], f'Vertex {vertex} Ycoordinate')
self._write_to_idf_format(file, coordinate[2], f'Vertex {vertex} Zcoordinate', eol)

View File

@ -1,52 +0,0 @@
import hub.exports.building_energy.idf_helper as idf_cte
import hub.helpers.constants as cte
from hub.exports.building_energy.idf_helper.idf_base import IdfBase
class IdfSurfaces(IdfBase):
@staticmethod
def add(self, building, zone_name):
zone_name = f'{zone_name}'
file = self._files['surfaces']
for thermal_zone in building.thermal_zones_from_internal_zones:
for index, boundary in enumerate(thermal_zone.thermal_boundaries):
surface_type = idf_cte.idf_surfaces_dictionary[boundary.parent_surface.type]
outside_boundary_condition = idf_cte.OUTDOORS
sun_exposure = idf_cte.SUN_EXPOSED
wind_exposure = idf_cte.WIND_EXPOSED
outside_boundary_condition_object = idf_cte.EMPTY
name = f'Building_{building.name}_surface_{index}'
construction_name = f'{boundary.construction_name} {boundary.parent_surface.type}'
space_name = idf_cte.EMPTY
if boundary.parent_surface.type == cte.GROUND:
outside_boundary_condition = idf_cte.GROUND
sun_exposure = idf_cte.NON_SUN_EXPOSED
wind_exposure = idf_cte.NON_WIND_EXPOSED
if boundary.parent_surface.percentage_shared is not None and boundary.parent_surface.percentage_shared > 0.5:
outside_boundary_condition_object = f'Building_{building.name}_surface_{index}'
outside_boundary_condition = idf_cte.SURFACE
sun_exposure = idf_cte.NON_SUN_EXPOSED
wind_exposure = idf_cte.NON_WIND_EXPOSED
self._write_to_idf_format(file, idf_cte.BUILDING_SURFACE)
self._write_to_idf_format(file, name, 'Name')
self._write_to_idf_format(file, surface_type, 'Surface Type')
self._write_to_idf_format(file, construction_name, 'Construction Name')
self._write_to_idf_format(file, zone_name, 'Zone Name')
self._write_to_idf_format(file, space_name, 'Space Name')
self._write_to_idf_format(file, outside_boundary_condition, 'Outside Boundary Condition')
self._write_to_idf_format(file, outside_boundary_condition_object, 'Outside Boundary Condition Object')
self._write_to_idf_format(file, sun_exposure, 'Sun Exposure')
self._write_to_idf_format(file, wind_exposure, 'Wind Exposure')
self._write_to_idf_format(file, idf_cte.AUTOCALCULATE, 'View Factor to Ground')
self._write_to_idf_format(file, idf_cte.AUTOCALCULATE, 'Number of Vertices')
coordinates = self._matrix_to_list(boundary.parent_surface.solid_polygon.coordinates,
self._city.lower_corner)
eol = ','
coordinates_length = len(coordinates)
for i, coordinate in enumerate(coordinates):
vertex = i + 1
if vertex == coordinates_length:
eol = ';'
self._write_to_idf_format(file, coordinate[0], f'Vertex {vertex} Xcoordinate')
self._write_to_idf_format(file, coordinate[1], f'Vertex {vertex} Ycoordinate')
self._write_to_idf_format(file, coordinate[2], f'Vertex {vertex} Zcoordinate', eol)

View File

@ -1,18 +0,0 @@
import hub.exports.building_energy.idf_helper as idf_cte
from hub.exports.building_energy.idf_helper.idf_base import IdfBase
class IdfThermostat(IdfBase):
@staticmethod
def add(self, thermal_zone):
thermostat_name = f'Thermostat {thermal_zone.usage_name}'
heating_schedule = f'Heating thermostat schedules {thermal_zone.usage_name}'
cooling_schedule = f'Cooling thermostat schedules {thermal_zone.usage_name}'
if thermostat_name not in self._thermostat_added_to_idf:
self._thermostat_added_to_idf[thermostat_name] = True
file = self._files['thermostat']
self._write_to_idf_format(file, idf_cte.THERMOSTAT)
self._write_to_idf_format(file, thermostat_name, 'Name')
self._write_to_idf_format(file, heating_schedule, 'Heating Setpoint Schedule Name')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Constant Heating Setpoint')
self._write_to_idf_format(file, cooling_schedule, 'Cooling Setpoint Schedule Name', ';')

View File

@ -1,38 +0,0 @@
import hub.exports.building_energy.idf_helper as idf_cte
import hub.helpers.constants as cte
from hub.exports.building_energy.idf_helper.idf_base import IdfBase
class IdfVentilation(IdfBase):
@staticmethod
def add(self, thermal_zone, zone_name):
schedule_name = f'Ventilation schedules {thermal_zone.usage_name}'
air_change = thermal_zone.mechanical_air_change * cte.HOUR_TO_SECONDS
file = self._files['ventilation']
self._write_to_idf_format(file, idf_cte.VENTILATION)
self._write_to_idf_format(file, f'{zone_name}_ventilation', 'Name')
self._write_to_idf_format(file, zone_name, 'Zone or ZoneList or Space or SpaceList Name')
self._write_to_idf_format(file, schedule_name, 'Schedule Name')
self._write_to_idf_format(file, 'AirChanges/Hour', 'Design Flow Rate Calculation Method')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Design Flow Rate')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Flow Rate per Floor Area')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Flow Rate per Person')
self._write_to_idf_format(file, air_change, 'Air Changes per Hour')
self._write_to_idf_format(file, 'Natural', 'Ventilation Type')
self._write_to_idf_format(file, 0, 'Fan Pressure Rise')
self._write_to_idf_format(file, 1, 'Fan Total Efficiency')
self._write_to_idf_format(file, 1, 'Constant Term Coefficient')
self._write_to_idf_format(file, 0, 'Temperature Term Coefficient')
self._write_to_idf_format(file, 0, 'Velocity Term Coefficient')
self._write_to_idf_format(file, 0, 'Velocity Squared Term Coefficient')
self._write_to_idf_format(file, -100, 'Minimum Indoor Temperature')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Minimum Indoor Temperature Schedule Name')
self._write_to_idf_format(file, 100, 'Maximum Indoor Temperature')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Maximum Indoor Temperature Schedule Name')
self._write_to_idf_format(file, -100, 'Delta Temperature')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Delta Temperature Schedule Name')
self._write_to_idf_format(file, -100, 'Minimum Outdoor Temperature')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Minimum Outdoor Temperature Schedule Name')
self._write_to_idf_format(file, 100, 'Maximum Outdoor Temperature')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Maximum Outdoor Temperature Schedule Name')
self._write_to_idf_format(file, 40, 'Maximum Wind Speed', ';')

View File

@ -1,64 +0,0 @@
import logging
import hub.exports.building_energy.idf_helper as idf_cte
import hub.helpers.constants as cte
from hub.exports.building_energy.idf_helper.idf_base import IdfBase
class IdfWindow(IdfBase):
@staticmethod
def _to_window_surface(self, surface):
window_ratio = surface.associated_thermal_boundaries[0].window_ratio
x = 0
y = 1
z = 2
coordinates = self._matrix_to_list(surface.solid_polygon.coordinates, self._city.lower_corner)
min_z = surface.lower_corner[z]
max_z = surface.upper_corner[z]
middle = (max_z - min_z) / 2
distance = (max_z - min_z) * window_ratio
new_max_z = middle + distance / 2
new_min_z = middle - distance / 2
for index, coordinate in enumerate(coordinates):
if coordinate[z] == max_z:
coordinates[index] = (coordinate[x], coordinate[y], new_max_z)
elif coordinate[z] == min_z:
coordinates[index] = (coordinate[x], coordinate[y], new_min_z)
else:
logging.warning('Z coordinate not in top or bottom during window creation')
return coordinates
@staticmethod
def add(self, building):
file = self._files['fenestration']
for thermal_zone in building.thermal_zones_from_internal_zones:
for index, boundary in enumerate(thermal_zone.thermal_boundaries):
building_surface_name = f'Building_{building.name}_surface_{index}'
is_exposed = boundary.parent_surface.type == cte.WALL
if boundary.parent_surface.percentage_shared is not None and boundary.parent_surface.percentage_shared > 0.5 or boundary.window_ratio == 0:
is_exposed = False
if not is_exposed:
continue
name = f'Building_{building.name}_window_{index}'
construction_name = f'{boundary.construction_name}_window_construction'
self._write_to_idf_format(file, idf_cte.WINDOW_SURFACE)
self._write_to_idf_format(file, name, 'Name')
self._write_to_idf_format(file, 'Window', 'Surface Type')
self._write_to_idf_format(file, construction_name, 'Construction Name')
self._write_to_idf_format(file, building_surface_name, 'Building Surface Name')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Outside Boundary Condition Object')
self._write_to_idf_format(file, idf_cte.AUTOCALCULATE, 'View Factor to Ground')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Frame and Divider Name')
self._write_to_idf_format(file, '1.0', 'Multiplier')
self._write_to_idf_format(file, idf_cte.AUTOCALCULATE, 'Number of Vertices')
coordinates = IdfWindow._to_window_surface(self, boundary.parent_surface)
eol = ','
coordinates_length = len(coordinates)
for i, coordinate in enumerate(coordinates):
vertex = i + 1
if vertex == coordinates_length:
eol = ';'
self._write_to_idf_format(file, coordinate[0], f'Vertex {vertex} Xcoordinate')
self._write_to_idf_format(file, coordinate[1], f'Vertex {vertex} Ycoordinate')
self._write_to_idf_format(file, coordinate[2], f'Vertex {vertex} Zcoordinate', eol)

View File

@ -1,17 +0,0 @@
import hub.exports.building_energy.idf_helper as idf_cte
from hub.exports.building_energy.idf_helper.idf_base import IdfBase
class IdfWindowsConstructions(IdfBase):
@staticmethod
def add(self, thermal_boundary):
name = f'{thermal_boundary.construction_name}_window'
if name not in self._windows_added_to_idf:
return # Material not added or already assigned to construction
construction_name = f'{thermal_boundary.construction_name}_window_construction'
if construction_name not in self._constructions_added_to_idf:
self._constructions_added_to_idf[construction_name] = True
file = self._files['constructions']
self._write_to_idf_format(file, idf_cte.CONSTRUCTION)
self._write_to_idf_format(file, construction_name, 'Name')
self._write_to_idf_format(file, name, 'Outside Layer', ';')

View File

@ -1,15 +0,0 @@
import hub.exports.building_energy.idf_helper as idf_cte
from hub.exports.building_energy.idf_helper.idf_base import IdfBase
class IdfWindowsMaterial(IdfBase):
@staticmethod
def add(self, thermal_boundary, thermal_opening):
name = f'{thermal_boundary.construction_name}_window'
if name not in self._windows_added_to_idf:
self._windows_added_to_idf[name] = True
file = self._files['window_materials']
self._write_to_idf_format(file, idf_cte.WINDOW_MATERIAL)
self._write_to_idf_format(file, name, 'Name')
self._write_to_idf_format(file, thermal_opening.overall_u_value, 'UFactor')
self._write_to_idf_format(file, thermal_opening.g_value, 'Solar Heat Gain Coefficient', ';')

View File

@ -1,22 +0,0 @@
import hub.exports.building_energy.idf_helper as idf_cte
from hub.exports.building_energy.idf_helper.idf_base import IdfBase
class IdfZone(IdfBase):
@staticmethod
def add(self, thermal_zone, zone_name):
file = self._files['zones']
self._write_to_idf_format(file, idf_cte.ZONE)
self._write_to_idf_format(file, zone_name, 'Name')
self._write_to_idf_format(file, 0, 'Direction of Relative North')
self._write_to_idf_format(file, 0, 'X Origin')
self._write_to_idf_format(file, 0, 'Y Origin')
self._write_to_idf_format(file, 0, 'Z Origin')
self._write_to_idf_format(file, 1, 'Type')
self._write_to_idf_format(file, 1, 'Multiplier')
self._write_to_idf_format(file, idf_cte.AUTOCALCULATE, 'Ceiling Height')
self._write_to_idf_format(file, thermal_zone.volume, 'Volume')
self._write_to_idf_format(file, idf_cte.AUTOCALCULATE, 'Floor Area')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Zone Inside Convection Algorithm')
self._write_to_idf_format(file, idf_cte.EMPTY, 'Zone Outside Convection Algorithm')
self._write_to_idf_format(file, 'Yes', 'Part of Total Floor Area', ';')

View File

@ -11,7 +11,6 @@ import requests
from hub.exports.building_energy.energy_ade import EnergyAde from hub.exports.building_energy.energy_ade import EnergyAde
from hub.exports.building_energy.idf import Idf from hub.exports.building_energy.idf import Idf
from hub.exports.building_energy.cerc_idf import CercIdf
from hub.exports.building_energy.insel.insel_monthly_energy_balance import InselMonthlyEnergyBalance from hub.exports.building_energy.insel.insel_monthly_energy_balance import InselMonthlyEnergyBalance
from hub.helpers.utils import validate_import_export_type from hub.helpers.utils import validate_import_export_type
from hub.imports.weather.helpers.weather import Weather as wh from hub.imports.weather.helpers.weather import Weather as wh
@ -21,7 +20,6 @@ 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, weather_file=None):
self._city = city self._city = city
self._export_type = '_' + handler.lower() self._export_type = '_' + handler.lower()
@ -64,17 +62,6 @@ class EnergyBuildingsExportsFactory:
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'),
self._weather_file, target_buildings=self._target_buildings) self._weather_file, target_buildings=self._target_buildings)
@property
def _cerc_idf(self):
idf_data_path = (Path(__file__).parent / './building_energy/idf_files/').resolve()
url = wh().epw_file(self._city.region_code)
weather_path = (Path(__file__).parent.parent / f'data/weather/epw/{url.rsplit("/", 1)[1]}').resolve()
if not weather_path.exists():
with open(weather_path, 'wb') as epw_file:
epw_file.write(requests.get(url, allow_redirects=True).content)
return CercIdf(self._city, self._path, (idf_data_path / 'base.idf'), (idf_data_path / 'Energy+.idd'), weather_path,
target_buildings=self._target_buildings)
@property @property
def _insel_monthly_energy_balance(self): def _insel_monthly_energy_balance(self):
""" """

View File

@ -77,8 +77,8 @@ class CesiumjsTileset:
'function': { 'function': {
'type': 'STRING' 'type': 'STRING'
}, },
'usages': { 'usages_percentage': {
'type': 'LIST' 'type': 'STRING'
} }
} }
} }
@ -146,7 +146,7 @@ class CesiumjsTileset:
'max_height': building.max_height, 'max_height': building.max_height,
'year_of_construction': building.year_of_construction, 'year_of_construction': building.year_of_construction,
'function': building.function, 'function': building.function,
'usages': building.usages 'usages_percentage': building.usages_percentage
} }
}, },
'content': { 'content': {

View File

@ -26,6 +26,7 @@ WATTS_HOUR_TO_JULES = 3600
GALLONS_TO_QUBIC_METERS = 0.0037854117954011185 GALLONS_TO_QUBIC_METERS = 0.0037854117954011185
INFILTRATION_75PA_TO_4PA = (4/75)**0.65 INFILTRATION_75PA_TO_4PA = (4/75)**0.65
# time # time
SECOND = 'second' SECOND = 'second'
MINUTE = 'minute' MINUTE = 'minute'
@ -185,19 +186,6 @@ DAYS_A_MONTH = {JANUARY: 31,
NOVEMBER: 30, NOVEMBER: 30,
DECEMBER: 31} DECEMBER: 31}
HOURS_A_MONTH = {JANUARY: 744,
FEBRUARY: 672,
MARCH: 744,
APRIL: 720,
MAY: 744,
JUNE: 720,
JULY: 744,
AUGUST: 744,
SEPTEMBER: 720,
OCTOBER: 744,
NOVEMBER: 720,
DECEMBER: 744}
# data types # data types
ANY_NUMBER = 'any_number' ANY_NUMBER = 'any_number'
FRACTION = 'fraction' FRACTION = 'fraction'
@ -316,7 +304,6 @@ GRID = 'Grid'
ONSITE_ELECTRICITY = 'Onsite Electricity' ONSITE_ELECTRICITY = 'Onsite Electricity'
PHOTOVOLTAIC = 'Photovoltaic' PHOTOVOLTAIC = 'Photovoltaic'
BOILER = 'Boiler' BOILER = 'Boiler'
FURNACE = 'Furnace'
HEAT_PUMP = 'Heat Pump' HEAT_PUMP = 'Heat Pump'
BASEBOARD = 'Baseboard' BASEBOARD = 'Baseboard'
ELECTRICITY_GENERATOR = 'Electricity generator' ELECTRICITY_GENERATOR = 'Electricity generator'

View File

@ -17,7 +17,6 @@ class MontrealCustomFuelToHubFuel:
self._dictionary = { self._dictionary = {
'gas': cte.GAS, 'gas': cte.GAS,
'natural gas': cte.GAS, 'natural gas': cte.GAS,
'biomass': cte.BIOMASS,
'electricity': cte.ELECTRICITY, 'electricity': cte.ELECTRICITY,
'renewable': cte.RENEWABLE, 'renewable': cte.RENEWABLE,
'butane': cte.BUTANE, 'butane': cte.BUTANE,

View File

@ -15,10 +15,10 @@ class MontrealGenerationSystemToHubEnergyGenerationSystem:
def __init__(self): def __init__(self):
self._dictionary = { self._dictionary = {
'boiler': cte.BOILER, 'boiler': cte.BOILER,
'furnace': cte.FURNACE, 'furnace': cte.BASEBOARD,
'cooler': cte.CHILLER, 'cooler': cte.CHILLER,
'electricity generator': cte.ELECTRICITY_GENERATOR, 'electricity generator': cte.ELECTRICITY_GENERATOR,
'photovoltaic': cte.PHOTOVOLTAIC, 'Photovoltaic': cte.PHOTOVOLTAIC,
'heat pump': cte.HEAT_PUMP, 'heat pump': cte.HEAT_PUMP,
'joule': cte.JOULE, 'joule': cte.JOULE,
'split': cte.SPLIT, 'split': cte.SPLIT,

View File

@ -3,7 +3,6 @@ Montreal custom energy system importer
SPDX - License - Identifier: LGPL - 3.0 - or -later SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2023 Concordia CERC group Copyright © 2023 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 Contributor Saeed Ranjbar saeed.ranjbar@concordia.ca
""" """
import logging import logging
@ -84,9 +83,9 @@ class MontrealCustomEnergySystemParameters:
def _create_generation_systems(archetype_system): def _create_generation_systems(archetype_system):
_generation_systems = [] _generation_systems = []
for archetype_generation_system in archetype_system.generation_systems: for archetype_generation_system in archetype_system.generation_systems:
if archetype_generation_system.system_type == 'photovoltaic': if archetype_generation_system.system_type == 'Photovoltaic':
_generation_system = PvGenerationSystem() _generation_system = PvGenerationSystem()
_type = archetype_generation_system.system_type _type = 'Photovoltaic'
_generation_system.system_type = Dictionaries().montreal_generation_system_to_hub_energy_generation_system[ _generation_system.system_type = Dictionaries().montreal_generation_system_to_hub_energy_generation_system[
_type] _type]
_fuel_type = Dictionaries().montreal_custom_fuel_to_hub_fuel[archetype_generation_system.fuel_type] _fuel_type = Dictionaries().montreal_custom_fuel_to_hub_fuel[archetype_generation_system.fuel_type]
@ -137,13 +136,13 @@ class MontrealCustomEnergySystemParameters:
_distribution_system.distribution_consumption_variable_flow = \ _distribution_system.distribution_consumption_variable_flow = \
archetype_distribution_system.distribution_consumption_variable_flow archetype_distribution_system.distribution_consumption_variable_flow
_distribution_system.heat_losses = archetype_distribution_system.heat_losses _distribution_system.heat_losses = archetype_distribution_system.heat_losses
_generic_emission_system = None _emission_system = None
if archetype_distribution_system.emission_systems is not None: if archetype_distribution_system.emission_systems is not None:
_emission_systems = [] _emission_systems = []
for emission_system in archetype_distribution_system.emission_systems: for emission_system in archetype_distribution_system.emission_systems:
_generic_emission_system = EmissionSystem() _emission_system = EmissionSystem()
_generic_emission_system.parasitic_energy_consumption = emission_system.parasitic_energy_consumption _emission_system.parasitic_energy_consumption = emission_system.parasitic_energy_consumption
_emission_systems.append(_generic_emission_system) _emission_systems.append(_emission_system)
_distribution_system.emission_systems = _emission_systems _distribution_system.emission_systems = _emission_systems
_distribution_systems.append(_distribution_system) _distribution_systems.append(_distribution_system)
return _distribution_systems return _distribution_systems

View File

@ -43,7 +43,6 @@ class MontrealFutureEnergySystemParameters:
archetype_name = building.energy_systems_archetype_name archetype_name = building.energy_systems_archetype_name
try: try:
archetype = self._search_archetypes(montreal_custom_catalog, archetype_name) archetype = self._search_archetypes(montreal_custom_catalog, archetype_name)
building.energy_systems_archetype_cluster_id = archetype.cluster_id
except KeyError: except KeyError:
logging.error('Building %s has unknown energy system archetype for system name %s', building.name, logging.error('Building %s has unknown energy system archetype for system name %s', building.name,
archetype_name) archetype_name)
@ -88,7 +87,7 @@ class MontrealFutureEnergySystemParameters:
archetype_generation_systems = archetype_system.generation_systems archetype_generation_systems = archetype_system.generation_systems
if archetype_generation_systems is not None: if archetype_generation_systems is not None:
for archetype_generation_system in archetype_system.generation_systems: for archetype_generation_system in archetype_system.generation_systems:
if archetype_generation_system.system_type == 'photovoltaic': if archetype_generation_system.system_type == 'Photovoltaic':
_generation_system = PvGenerationSystem() _generation_system = PvGenerationSystem()
_generation_system.name = archetype_generation_system.name _generation_system.name = archetype_generation_system.name
_generation_system.model_name = archetype_generation_system.model_name _generation_system.model_name = archetype_generation_system.model_name
@ -104,21 +103,15 @@ class MontrealFutureEnergySystemParameters:
_generation_system.nominal_radiation = archetype_generation_system.nominal_radiation _generation_system.nominal_radiation = archetype_generation_system.nominal_radiation
_generation_system.standard_test_condition_cell_temperature = archetype_generation_system.standard_test_condition_cell_temperature _generation_system.standard_test_condition_cell_temperature = archetype_generation_system.standard_test_condition_cell_temperature
_generation_system.standard_test_condition_maximum_power = archetype_generation_system.standard_test_condition_maximum_power _generation_system.standard_test_condition_maximum_power = archetype_generation_system.standard_test_condition_maximum_power
_generation_system.standard_test_condition_radiation = archetype_generation_system.standard_test_condition_radiation
_generation_system.cell_temperature_coefficient = archetype_generation_system.cell_temperature_coefficient _generation_system.cell_temperature_coefficient = archetype_generation_system.cell_temperature_coefficient
_generation_system.width = archetype_generation_system.width _generation_system.width = archetype_generation_system.width
_generation_system.height = archetype_generation_system.height _generation_system.height = archetype_generation_system.height
_generation_system.tilt_angle = self._city.latitude _generation_system.tilt_angle = self._city.latitude
_generic_storage_system = None _generic_storage_system = None
if archetype_generation_system.energy_storage_systems is not None: if archetype_generation_system.energy_storage_systems is not None:
_storage_systems = []
for storage_system in archetype_generation_system.energy_storage_systems:
if storage_system.type_energy_stored == 'electrical':
_generic_storage_system = ElectricalStorageSystem() _generic_storage_system = ElectricalStorageSystem()
_generic_storage_system.type_energy_stored = 'electrical' _generic_storage_system.type_energy_stored = 'electrical'
_storage_systems.append(_generic_storage_system) _generation_system.energy_storage_systems = [_generic_storage_system]
_generation_system.energy_storage_systems = _storage_systems
else: else:
_generation_system = NonPvGenerationSystem() _generation_system = NonPvGenerationSystem()
_generation_system.name = archetype_generation_system.name _generation_system.name = archetype_generation_system.name
@ -192,13 +185,13 @@ class MontrealFutureEnergySystemParameters:
_distribution_system.distribution_consumption_variable_flow = \ _distribution_system.distribution_consumption_variable_flow = \
archetype_distribution_system.distribution_consumption_variable_flow archetype_distribution_system.distribution_consumption_variable_flow
_distribution_system.heat_losses = archetype_distribution_system.heat_losses _distribution_system.heat_losses = archetype_distribution_system.heat_losses
_generic_emission_system = None _emission_system = None
if archetype_distribution_system.emission_systems is not None: if archetype_distribution_system.emission_systems is not None:
_emission_systems = [] _emission_systems = []
for emission_system in archetype_distribution_system.emission_systems: for emission_system in archetype_distribution_system.emission_systems:
_generic_emission_system = EmissionSystem() _emission_system = EmissionSystem()
_generic_emission_system.parasitic_energy_consumption = emission_system.parasitic_energy_consumption _emission_system.parasitic_energy_consumption = emission_system.parasitic_energy_consumption
_emission_systems.append(_generic_emission_system) _emission_systems.append(_emission_system)
_distribution_system.emission_systems = _emission_systems _distribution_system.emission_systems = _emission_systems
_distribution_systems.append(_distribution_system) _distribution_systems.append(_distribution_system)
return _distribution_systems return _distribution_systems

View File

@ -87,7 +87,7 @@ class PalmaEnergySystemParameters:
archetype_generation_systems = archetype_system.generation_systems archetype_generation_systems = archetype_system.generation_systems
if archetype_generation_systems is not None: if archetype_generation_systems is not None:
for archetype_generation_system in archetype_system.generation_systems: for archetype_generation_system in archetype_system.generation_systems:
if archetype_generation_system.system_type == 'photovoltaic': if archetype_generation_system.system_type == 'Photovoltaic':
_generation_system = PvGenerationSystem() _generation_system = PvGenerationSystem()
_generation_system.name = archetype_generation_system.name _generation_system.name = archetype_generation_system.name
_generation_system.model_name = archetype_generation_system.model_name _generation_system.model_name = archetype_generation_system.model_name

View File

@ -1,12 +1,14 @@
""" """
Cerc Idf result import Insel monthly energy balance
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 Guille Guillermo.GutierrezMorote@concordia.ca Project Coder Saeed Ranjbar saeed.ranjbar@concordia.ca
Code contributors: Saeed Ranjbar saeed.ranjbar@concordia.ca Project collaborator Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
""" """
from pathlib import Path
import csv import csv
from hub.helpers.monthly_values import MonthlyValues
import hub.helpers.constants as cte import hub.helpers.constants as cte
@ -14,33 +16,62 @@ class EnergyPlus:
""" """
Energy plus class Energy plus class
""" """
def __init__(self, city, base_path):
def _extract_fields_from_headers(self, headers):
for header in headers:
header_parts = header.split(':')
building_name = header_parts[0]
variable = ':'.join(header_parts[1:]).strip() # concat the rest and ensure that : it's reintroduced just in case
if variable == '':
continue
if building_name not in self._summary_variables:
self._building_energy_demands[variable] = [] # initialize the list of variables
else:
self._building_energy_demands[header] = []
def __init__(self, city, file_path):
self._city = city self._city = city
self._building_energy_demands = {} self._base_path = base_path
self._lines = []
self._summary_variables = ['DistrictCooling:Facility [J](Hourly)',
'InteriorEquipment:Electricity [J](Hourly)',
'InteriorLights:Electricity [J](Hourly) ']
with open(file_path, 'r', encoding='utf8') as csv_file: @staticmethod
def _building_energy_demands(energy_plus_output_file_path):
with open(Path(energy_plus_output_file_path).resolve(), 'r', encoding='utf8') as csv_file:
csv_output = csv.reader(csv_file) csv_output = csv.reader(csv_file)
self._headers = next(csv_output) headers = next(csv_output)
self._extract_fields_from_headers(self._headers) building_energy_demands = {
'Heating (J)': [],
'Cooling (J)': [],
'DHW (J)': [],
'Appliances (J)': [],
'Lighting (J)': []
}
heating_column_index = []
cooling_column_index = []
dhw_column_index = []
appliance_column_index = []
lighting_column_index = []
for index, header in enumerate(headers):
if "Total Heating" in header:
heating_column_index.append(index)
elif "Total Cooling" in header:
cooling_column_index.append(index)
elif "DHW" in header:
dhw_column_index.append(index)
elif "InteriorEquipment" in header:
appliance_column_index.append(index)
elif "InteriorLights" in header:
lighting_column_index.append(index)
for line in csv_output: for line in csv_output:
self._lines.append(line) total_heating_demand = 0
total_cooling_demand = 0
total_dhw_demand = 0
total_appliance_demand = 0
total_lighting_demand = 0
for heating_index in heating_column_index:
total_heating_demand += float(line[heating_index])
building_energy_demands['Heating (J)'].append(total_heating_demand)
for cooling_index in cooling_column_index:
total_cooling_demand += float(line[cooling_index])
building_energy_demands['Cooling (J)'].append(total_cooling_demand)
for dhw_index in dhw_column_index:
total_dhw_demand += float(line[dhw_index]) * 3600
building_energy_demands['DHW (J)'].append(total_dhw_demand)
for appliance_index in appliance_column_index:
total_appliance_demand += float(line[appliance_index])
building_energy_demands['Appliances (J)'].append(total_appliance_demand)
for lighting_index in lighting_column_index:
total_lighting_demand += float(line[lighting_index])
building_energy_demands['Lighting (J)'].append(total_lighting_demand)
return building_energy_demands
def enrich(self): def enrich(self):
""" """
@ -48,58 +79,27 @@ class EnergyPlus:
:return: None :return: None
""" """
for building in self._city.buildings: for building in self._city.buildings:
_energy_demands = {} file_name = f'{building.name}_out.csv'
for header in self._building_energy_demands: energy_plus_output_file_path = Path(self._base_path / file_name).resolve()
print(header) if energy_plus_output_file_path.is_file():
if header == 'Zone Ideal Loads Supply Air Total Heating Energy [J](Hourly)': building_energy_demands = self._building_energy_demands(energy_plus_output_file_path)
field_name = f'{building.name} IDEAL LOADS AIR SYSTEM:{header}' building.heating_demand[cte.HOUR] = building_energy_demands['Heating (J)']
elif header == 'Zone Ideal Loads Supply Air Total Cooling Energy [J](Hourly)': building.cooling_demand[cte.HOUR] = building_energy_demands['Cooling (J)']
field_name = f'{building.name} IDEAL LOADS AIR SYSTEM:{header}' building.domestic_hot_water_heat_demand[cte.HOUR] = building_energy_demands['DHW (J)']
else: building.appliances_electrical_demand[cte.HOUR] = building_energy_demands['Appliances (J)']
field_name = f'{building.name}:{header}' building.lighting_electrical_demand[cte.HOUR] = building_energy_demands['Lighting (J)']
position = -1 # todo: @Saeed, this a list of ONE value with the total energy of the year, exactly the same as cte.YEAR.
if field_name in self._headers: # You have to use the method to add hourly values from helpers/monthly_values
position = self._headers.index(field_name) building.heating_demand[cte.MONTH] = MonthlyValues.get_total_month(building.heating_demand[cte.HOUR])
if position == -1: building.cooling_demand[cte.MONTH] = MonthlyValues.get_total_month(building.cooling_demand[cte.HOUR])
continue building.domestic_hot_water_heat_demand[cte.MONTH] = (
for line in self._lines: MonthlyValues.get_total_month(building.domestic_hot_water_heat_demand[cte.HOUR]))
if header not in _energy_demands.keys(): building.appliances_electrical_demand[cte.MONTH] = (
_energy_demands[header] = [] MonthlyValues.get_total_month(building.appliances_electrical_demand[cte.HOUR]))
_energy_demands[header].append(line[position]) building.lighting_electrical_demand[cte.MONTH] = (
# print(building_energy_demands['Zone Ideal Loads Supply Air Total Heating Energy [J](Hourly)']) MonthlyValues.get_total_month(building.lighting_electrical_demand[cte.HOUR]))
EnergyPlus._set_building_demands(building, _energy_demands) building.heating_demand[cte.YEAR] = [sum(building.heating_demand[cte.MONTH])]
building.cooling_demand[cte.YEAR] = [sum(building.cooling_demand[cte.MONTH])]
@staticmethod building.domestic_hot_water_heat_demand[cte.YEAR] = [sum(building.domestic_hot_water_heat_demand[cte.MONTH])]
def _set_building_demands(building, energy_demands): building.appliances_electrical_demand[cte.YEAR] = [sum(building.appliances_electrical_demand[cte.MONTH])]
print(energy_demands.keys()) building.lighting_electrical_demand[cte.YEAR] = [sum(building.lighting_electrical_demand[cte.MONTH])]
heating = [float(x) for x in energy_demands['Zone Ideal Loads Supply Air Total Heating Energy [J](Hourly)']]
cooling = [float(x) for x in energy_demands['Zone Ideal Loads Supply Air Total Cooling Energy [J](Hourly)']]
dhw = [float(x) * cte.WATTS_HOUR_TO_JULES for x in energy_demands['Water Use Equipment Heating Rate [W](Hourly)']]
appliances = [float(x) * cte.WATTS_HOUR_TO_JULES for x in energy_demands['Other Equipment Electricity Rate [W](Hourly)']]
lighting = [float(x) * cte.WATTS_HOUR_TO_JULES for x in energy_demands['Zone Lights Electricity Rate [W](Hourly)']]
building.heating_demand[cte.HOUR] = heating
building.cooling_demand[cte.HOUR] = cooling
building.domestic_hot_water_heat_demand[cte.HOUR] = dhw
building.appliances_electrical_demand[cte.HOUR] = appliances
building.lighting_electrical_demand[cte.HOUR] = lighting
building.heating_demand[cte.MONTH] = []
building.cooling_demand[cte.MONTH] = []
building.domestic_hot_water_heat_demand[cte.MONTH] = []
building.appliances_electrical_demand[cte.MONTH] = []
building.lighting_electrical_demand[cte.MONTH] = []
start = 0
for hours in cte.HOURS_A_MONTH.values():
end = hours + start
building.heating_demand[cte.MONTH].append(sum(building.heating_demand[cte.HOUR][start: end]))
building.cooling_demand[cte.MONTH].append(sum(building.cooling_demand[cte.HOUR][start: end]))
building.domestic_hot_water_heat_demand[cte.MONTH].append(sum(dhw[start: end]))
building.appliances_electrical_demand[cte.MONTH].append(sum(appliances[start: end]))
building.lighting_electrical_demand[cte.MONTH].append(sum(lighting[start: end]))
start = end
building.heating_demand[cte.YEAR] = [sum(building.heating_demand[cte.HOUR])]
building.cooling_demand[cte.YEAR] = [sum(building.cooling_demand[cte.HOUR])]
building.domestic_hot_water_heat_demand[cte.YEAR] = [sum(building.domestic_hot_water_heat_demand[cte.HOUR])]
building.appliances_electrical_demand[cte.YEAR] = [sum(building.appliances_electrical_demand[cte.HOUR])]
building.lighting_electrical_demand[cte.YEAR] = [sum(building.lighting_electrical_demand[cte.HOUR])]

View File

@ -22,11 +22,9 @@ class EnergyPlusMultipleBuildings:
with open(Path(energy_plus_output_file_path).resolve(), 'r', encoding='utf8') as csv_file: with open(Path(energy_plus_output_file_path).resolve(), 'r', encoding='utf8') as csv_file:
csv_output = list(csv.DictReader(csv_file)) csv_output = list(csv.DictReader(csv_file))
print(csv_output)
return
for building in self._city.buildings: for building in self._city.buildings:
building_name = building.name.upper() building_name = building.name.upper()
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)"])

View File

@ -8,7 +8,6 @@ Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concord
from pathlib import Path from pathlib import Path
from hub.helpers.utils import validate_import_export_type from hub.helpers.utils import validate_import_export_type
from hub.imports.results.energy_plus import EnergyPlus
from hub.imports.results.insel_monthly_energry_balance import InselMonthlyEnergyBalance from hub.imports.results.insel_monthly_energry_balance import InselMonthlyEnergyBalance
from hub.imports.results.simplified_radiosity_algorithm import SimplifiedRadiosityAlgorithm from hub.imports.results.simplified_radiosity_algorithm import SimplifiedRadiosityAlgorithm
@ -61,9 +60,6 @@ class ResultFactory:
""" """
EnergyPlusMultipleBuildings(self._city, self._base_path).enrich() EnergyPlusMultipleBuildings(self._city, self._base_path).enrich()
def _cerc_idf(self):
EnergyPlus(self._city, self._base_path).enrich()
def enrich(self): def enrich(self):
""" """
Enrich the city given to the class using the usage factory given handler Enrich the city given to the class using the usage factory given handler

View File

View File

@ -9,7 +9,6 @@ import datetime
import logging import logging
from sqlalchemy import Column, Integer, String, Sequence, ForeignKey, Float from sqlalchemy import Column, Integer, String, Sequence, ForeignKey, Float
from sqlalchemy.dialects.postgresql import JSON
from sqlalchemy import DateTime from sqlalchemy import DateTime
from hub.city_model_structure.building import Building from hub.city_model_structure.building import Building
@ -28,7 +27,7 @@ class CityObject(Models):
type = Column(String, nullable=False) type = Column(String, nullable=False)
year_of_construction = Column(Integer, nullable=True) year_of_construction = Column(Integer, nullable=True)
function = Column(String, nullable=True) function = Column(String, nullable=True)
usage = Column(JSON, nullable=True) usage = Column(String, nullable=True)
volume = Column(Float, nullable=False) volume = Column(Float, nullable=False)
area = Column(Float, nullable=False) area = Column(Float, nullable=False)
total_heating_area = Column(Float, nullable=False) total_heating_area = Column(Float, nullable=False)
@ -47,7 +46,7 @@ class CityObject(Models):
self.type = building.type self.type = building.type
self.year_of_construction = building.year_of_construction self.year_of_construction = building.year_of_construction
self.function = building.function self.function = building.function
self.usage = building.usages self.usage = building.usages_percentage
self.volume = building.volume self.volume = building.volume
self.area = building.floor_area self.area = building.floor_area
self.roof_area = sum(roof.solid_polygon.area for roof in building.roofs) self.roof_area = sum(roof.solid_polygon.area for roof in building.roofs)

View File

@ -1,4 +1,4 @@
""" """
Hub version number Hub version number
""" """
__version__ = '0.3.0.5' __version__ = '0.2.0.13'

View File

@ -1,5 +1,5 @@
xmltodict xmltodict
numpy numpy==1.26.4
trimesh[all] trimesh[all]
pyproj pyproj
pandas pandas

View File

@ -59,7 +59,6 @@ setup(
'hub.exports', 'hub.exports',
'hub.exports.building_energy', 'hub.exports.building_energy',
'hub.exports.building_energy.idf_files', 'hub.exports.building_energy.idf_files',
'hub.exports.building_energy.idf_helper',
'hub.exports.building_energy.insel', 'hub.exports.building_energy.insel',
'hub.exports.energy_systems', 'hub.exports.energy_systems',
'hub.exports.formats', 'hub.exports.formats',

View File

@ -17,7 +17,6 @@ from hub.exports.exports_factory import ExportsFactory
from hub.helpers.dictionaries import Dictionaries from hub.helpers.dictionaries import Dictionaries
from hub.imports.construction_factory import ConstructionFactory from hub.imports.construction_factory import ConstructionFactory
from hub.imports.geometry_factory import GeometryFactory from hub.imports.geometry_factory import GeometryFactory
from hub.imports.results_factory import ResultFactory
from hub.imports.usage_factory import UsageFactory from hub.imports.usage_factory import UsageFactory
from hub.imports.weather_factory import WeatherFactory from hub.imports.weather_factory import WeatherFactory
@ -137,49 +136,14 @@ class TestExports(TestCase):
year_of_construction_field='ANNEE_CONS', year_of_construction_field='ANNEE_CONS',
function_field='CODE_UTILI', function_field='CODE_UTILI',
function_to_hub=Dictionaries().montreal_function_to_hub_function).city function_to_hub=Dictionaries().montreal_function_to_hub_function).city
self.assertIsNotNone(city, 'city is none')
EnergyBuildingsExportsFactory('idf', city, self._output_path).export()
ConstructionFactory('nrcan', city).enrich()
EnergyBuildingsExportsFactory('idf', city, self._output_path).export()
UsageFactory('nrcan', city).enrich()
WeatherFactory('epw', city).enrich()
try:
_idf = EnergyBuildingsExportsFactory('idf', city, self._output_path).export()
_idf.run()
except Exception:
self.fail("Idf ExportsFactory raised ExceptionType unexpectedly!")
def test_cerc_idf_export(self):
"""
export to IDF
"""
file = 'test.geojson'
file_path = (self._example_path / file).resolve()
city = GeometryFactory('geojson',
path=file_path,
height_field='citygml_me',
year_of_construction_field='ANNEE_CONS',
function_field='CODE_UTILI',
function_to_hub=Dictionaries().montreal_function_to_hub_function).city
self.assertIsNotNone(city, 'city is none') self.assertIsNotNone(city, 'city is none')
EnergyBuildingsExportsFactory('idf', city, self._output_path).export()
ConstructionFactory('nrcan', city).enrich() ConstructionFactory('nrcan', city).enrich()
EnergyBuildingsExportsFactory('idf', city, self._output_path).export()
UsageFactory('nrcan', city).enrich() UsageFactory('nrcan', city).enrich()
WeatherFactory('epw', city).enrich() WeatherFactory('epw', city).enrich()
try: try:
idf = EnergyBuildingsExportsFactory('cerc_idf', city, self._output_path).export() EnergyBuildingsExportsFactory('idf', city, self._output_path, target_buildings=[1]).export()
idf.run()
csv_output_path = (self._output_path / f'{city.name}_out.csv').resolve()
ResultFactory('cerc_idf', city, csv_output_path).enrich()
self.assertTrue(csv_output_path.is_file())
for building in city.buildings:
self.assertIsNotNone(building.heating_demand)
self.assertIsNotNone(building.cooling_demand)
self.assertIsNotNone(building.domestic_hot_water_heat_demand)
self.assertIsNotNone(building.lighting_electrical_demand)
self.assertIsNotNone(building.appliances_electrical_demand)
total_demand = sum(building.heating_demand[cte.HOUR])
total_demand_month = sum(building.heating_demand[cte.MONTH])
self.assertAlmostEqual(total_demand, building.heating_demand[cte.YEAR][0], 2)
self.assertAlmostEqual(total_demand_month, building.heating_demand[cte.YEAR][0], 2)
except Exception: except Exception:
self.fail("Idf ExportsFactory raised ExceptionType unexpectedly!") self.fail("Idf ExportsFactory raised ExceptionType unexpectedly!")

View File

@ -92,3 +92,42 @@ class TestResultsImport(TestCase):
building.cooling_demand[cte.HOUR] = values building.cooling_demand[cte.HOUR] = values
self.assertIsNotNone(building.heating_peak_load) self.assertIsNotNone(building.heating_peak_load)
self.assertIsNotNone(building.cooling_peak_load) self.assertIsNotNone(building.cooling_peak_load)
def test_energy_plus_results_import(self):
ResultFactory('energy_plus_single_building', self._city, self._example_path).enrich()
for building in self._city.buildings:
csv_output_name = f'{building.name}_out.csv'
csv_output_path = (self._example_path / csv_output_name).resolve()
if csv_output_path.is_file():
self.assertEqual(building.name, '12')
self.assertIsNotNone(building.heating_demand)
self.assertIsNotNone(building.cooling_demand)
self.assertIsNotNone(building.domestic_hot_water_heat_demand)
self.assertIsNotNone(building.lighting_electrical_demand)
self.assertIsNotNone(building.appliances_electrical_demand)
total_demand = sum(building.heating_demand[cte.HOUR])
self.assertAlmostEqual(total_demand, building.heating_demand[cte.YEAR][0], 3)
total_demand = sum(building.heating_demand[cte.MONTH])
self.assertEqual(total_demand, building.heating_demand[cte.YEAR][0], 3)
if building.name != '12':
self.assertDictEqual(building.heating_demand, {})
self.assertDictEqual(building.cooling_demand, {})
self.assertDictEqual(building.domestic_hot_water_heat_demand, {})
self.assertDictEqual(building.lighting_electrical_demand, {})
self.assertDictEqual(building.appliances_electrical_demand, {})
def test_energy_plus_multiple_buildings_results_import(self):
ResultFactory('energy_plus_multiple_buildings', self._city, self._example_path).enrich()
csv_output_name = f'{self._city.name}_out.csv'
csv_output_path = (self._example_path / csv_output_name).resolve()
if csv_output_path.is_file():
for building in self._city.buildings:
self.assertIsNotNone(building.heating_demand)
self.assertIsNotNone(building.cooling_demand)
self.assertIsNotNone(building.domestic_hot_water_heat_demand)
self.assertIsNotNone(building.lighting_electrical_demand)
self.assertIsNotNone(building.appliances_electrical_demand)
total_demand = sum(building.heating_demand[cte.HOUR])
self.assertAlmostEqual(total_demand, building.heating_demand[cte.YEAR][0], 2)
total_demand = sum(building.heating_demand[cte.MONTH])
self.assertEqual(total_demand, building.heating_demand[cte.YEAR][0], 2)

View File

@ -39,11 +39,11 @@ class TestSystemsCatalog(TestCase):
catalog_categories = catalog.names() catalog_categories = catalog.names()
archetypes = catalog.names() archetypes = catalog.names()
self.assertEqual(34, len(archetypes['archetypes'])) self.assertEqual(15, len(archetypes['archetypes']))
systems = catalog.names('systems') systems = catalog.names('systems')
self.assertEqual(39, len(systems['systems'])) self.assertEqual(12, len(systems['systems']))
generation_equipments = catalog.names('generation_equipments') generation_equipments = catalog.names('generation_equipments')
self.assertEqual(49, len(generation_equipments['generation_equipments'])) self.assertEqual(27, len(generation_equipments['generation_equipments']))
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
catalog.names('unknown') catalog.names('unknown')
@ -55,7 +55,6 @@ class TestSystemsCatalog(TestCase):
with self.assertRaises(IndexError): with self.assertRaises(IndexError):
catalog.get_entry('unknown') catalog.get_entry('unknown')
def test_palma_catalog(self): def test_palma_catalog(self):
catalog = EnergySystemsCatalogFactory('palma').catalog catalog = EnergySystemsCatalogFactory('palma').catalog
catalog_categories = catalog.names() catalog_categories = catalog.names()

View File

@ -114,8 +114,7 @@ class TestSystemsFactory(TestCase):
ResultFactory('insel_monthly_energy_balance', self._city, self._output_path).enrich() ResultFactory('insel_monthly_energy_balance', self._city, self._output_path).enrich()
for building in self._city.buildings: for building in self._city.buildings:
building.energy_systems_archetype_name = ('Central Hydronic Air and Gas Source Heating System with Unitary Split ' building.energy_systems_archetype_name = 'PV+ASHP+GasBoiler+TES'
'Cooling and Air Source HP DHW and Grid Tied PV')
EnergySystemsFactory('montreal_future', self._city).enrich() EnergySystemsFactory('montreal_future', self._city).enrich()
# Need to assign energy systems to buildings: # Need to assign energy systems to buildings:
for building in self._city.buildings: for building in self._city.buildings:
@ -132,7 +131,6 @@ class TestSystemsFactory(TestCase):
self.assertLess(0, building.heating_consumption[cte.YEAR][0]) self.assertLess(0, building.heating_consumption[cte.YEAR][0])
self.assertLess(0, building.cooling_consumption[cte.YEAR][0]) self.assertLess(0, building.cooling_consumption[cte.YEAR][0])
self.assertLess(0, building.domestic_hot_water_consumption[cte.YEAR][0]) self.assertLess(0, building.domestic_hot_water_consumption[cte.YEAR][0])
if 'PV' in building.energy_systems_archetype_name:
self.assertLess(0, building.onsite_electrical_production[cte.YEAR][0]) self.assertLess(0, building.onsite_electrical_production[cte.YEAR][0])
def test_palma_system_results(self): def test_palma_system_results(self):