Bugfixing in idf_helper, changing the method exposure so now adding surfaces add materials and layers automatically, added surface getter for ThermalBoundary

This commit is contained in:
Guille 2020-11-12 13:50:43 -05:00
parent ad9b40be48
commit fcbee21199
4 changed files with 95 additions and 53 deletions

View File

@ -9,6 +9,7 @@ from city_model_structure.attributes.layer import Layer
from city_model_structure.attributes.thermal_opening import ThermalOpening from city_model_structure.attributes.thermal_opening import ThermalOpening
from city_model_structure.attributes.thermal_zone import ThermalZone from city_model_structure.attributes.thermal_zone import ThermalZone
from helpers.configuration_helper import ConfigurationHelper from helpers.configuration_helper import ConfigurationHelper
from city_model_structure.attributes.surface import Surface
class ThermalBoundary: class ThermalBoundary:
@ -30,6 +31,14 @@ class ThermalBoundary:
self._shortwave_reflectance = 1 - self._outside_solar_absorptance self._shortwave_reflectance = 1 - self._outside_solar_absorptance
self._construction_name = None self._construction_name = None
@property
def surface(self) -> Surface:
"""
Get the surface that belongs to the thermal boundary
:return: Surface
"""
return self._surface
@property @property
def delimits(self) -> List[ThermalZone]: def delimits(self) -> List[ThermalZone]:
""" """

View File

@ -9,4 +9,4 @@ time_scale = {
'month': 'month', 'month': 'month',
'year': 'year' 'year': 'year'
} }
roughness="MediumRough" roughness = "MediumRough"

View File

@ -7,12 +7,16 @@ from geomeppy import IDF
import os import os
import esoreader import esoreader
from pathlib import Path from pathlib import Path
import helpers import helpers.constants as cte
class IdfHelper: class IdfHelper:
_THERMOSTAT = 'HVACTEMPLATE:THERMOSTAT' _THERMOSTAT = 'HVACTEMPLATE:THERMOSTAT'
_IDEAL_LOAD_AIR_SYSTEM = 'HVACTEMPLATE:ZONE:IDEALLOADSAIRSYSTEM' _IDEAL_LOAD_AIR_SYSTEM = 'HVACTEMPLATE:ZONE:IDEALLOADSAIRSYSTEM'
_SURFACE = 'BUILDINGSURFACE:DETAILED' _SURFACE = 'BUILDINGSURFACE:DETAILED'
_CONSTRUCTION = 'CONSTRUCTION'
_MATERIAL = 'MATERIAL'
_MATERIAL_NOMASS = 'MATERIAL:NOMASS'
idf_surfaces = { idf_surfaces = {
'Wall': 'wall', 'Wall': 'wall',
@ -31,34 +35,67 @@ class IdfHelper:
self._idf = IDF(self._idf_file_path, self._epw_file_path) self._idf = IDF(self._idf_file_path, self._epw_file_path)
self._idf.epw = self._epw_file_path self._idf.epw = self._epw_file_path
def add_material(self, layer): def _add_material(self, layer):
materials = self._idf.newidfobject("MATERIAL".upper()) for material in self._idf.idfobjects[self._MATERIAL]:
materials.Name = layer.material.name if material.Name == layer.material.name:
materials.Roughness = helpers.roughness return
materials.Thickness = layer.thickness for material in self._idf.idfobjects[self._MATERIAL_NOMASS]:
materials.Conductivity = layer.material.conductivity if material.Name == layer.material.name:
materials.Density = layer.material.density return
materials.Specific_Heat = layer.material.specific_heat if layer.material.no_mass:
materials.Thermal_Absorptance = layer.material.thermal_absorptance self._idf.newidfobject(self._MATERIAL_NOMASS,
materials.Solar_Absorptance = layer.material.solar_absorptance Name=layer.material.name,
materials.Visible_Absorptance = layer.material.visible_absorptance Roughness=cte.roughness,
Thermal_Resistance=layer.material.thermal_resistance,
def add_construction(self, thermal_boundary): Thermal_Absorptance=layer.material.thermal_absorptance,
for boundary in thermal_boundary: Solar_Absorptance=layer.material.solar_absorptance,
for layer in boundary: Visible_Absorptance=layer.material.visible_absorptance
if len(layer) == 2: )
self._idf.newidfobject("CONSTRUCTION", Name=boundary.construction_name,
Outside_Layer=layer[0].material.name, Layer_2=layer[1].material.name)
elif len(layer) == 3:
self._idf.newidfobject("CONSTRUCTION", Name=boundary.construction_name,
Outside_Layer=layer[0].material.name, Layer_2=layer[1].material.name, Layer_3=layer[2].material.name)
elif len(layer) == 4:
self._idf.newidfobject("CONSTRUCTION", Name=boundary.construction_name,
Outside_Layer=layer[0].material.name, Layer_2=layer[1].material.name, Layer_3=layer[2].material.name, Layer_4=layer[3].material.name)
else: else:
print("Could not find the true construction") self._idf.newidfobject(self._MATERIAL,
Name=layer.material.name,
Roughness=cte.roughness,
Thickness=layer.thickness,
Conductivity=layer.material.conductivity,
Density=layer.material.density,
Specific_Heat=layer.material.specific_heat,
Thermal_Absorptance=layer.material.thermal_absorptance,
Solar_Absorptance=layer.material.solar_absorptance,
Visible_Absorptance=layer.material.visible_absorptance
)
print(f"Add material {layer.material.name}")
def add_heating_system(self, building): def _add_construction(self, thermal_boundary):
for construction in self._idf.idfobjects[self._CONSTRUCTION]:
if construction.Name == thermal_boundary.construction_name:
return
for layer in thermal_boundary.layers:
self._add_material(layer)
layers = thermal_boundary.layers
if len(layers) == 2:
self._idf.newidfobject(self._CONSTRUCTION, Name=thermal_boundary.construction_name,
Outside_Layer=layers[0].material.name, Layer_2=layers[1].material.name)
debug = f'{self._CONSTRUCTION}, Name={thermal_boundary.construction_name}, Outside_Layer={layers[0].material.name}' \
f', Layer_2={layers[1].material.name}'
elif len(thermal_boundary.layers) == 3:
self._idf.newidfobject(self._CONSTRUCTION, Name=thermal_boundary.construction_name,
Outside_Layer=layers[0].material.name, Layer_2=layers[1].material.name,
Layer_3=layers[2].material.name)
debug = f'{self._CONSTRUCTION}, Name={thermal_boundary.construction_name}, ' \
f'Outside_Layer={layers[0].material.name}, Layer_2={layers[1].material.name}, ' \
f'Layer_3={layers[2].material.name}'
elif len(thermal_boundary.layers) == 4:
self._idf.newidfobject(self._CONSTRUCTION, Name=thermal_boundary.construction_name,
Outside_Layer=layers[0].material.name, Layer_2=layers[1].material.name,
Layer_3=layers[2].material.name, Layer_4=layers[3].material.name)
debug = f'{self._CONSTRUCTION}, Name={thermal_boundary.construction_name}, ' \
f'Outside_Layer={layers[0].material.name}, Layer_2={layers[1].material.name}, ' \
f'Layer_3={layers[2].material.name}, Layer_4={layers[3].material.name}'
else:
raise Exception("This libs version cannot handle more than 4 layers")
print(debug)
def _add_heating_system(self, building):
for usage_zone in building.usage_zones: for usage_zone in building.usage_zones:
thermostat_name = f'Thermostat {building.name}' thermostat_name = f'Thermostat {building.name}'
# todo: this will fail for more than one usage zone # todo: this will fail for more than one usage zone
@ -71,7 +108,7 @@ class IdfHelper:
if zone.Name.find(building.name) != -1: if zone.Name.find(building.name) != -1:
self._idf.newidfobject(self._IDEAL_LOAD_AIR_SYSTEM, self._idf.newidfobject(self._IDEAL_LOAD_AIR_SYSTEM,
Zone_Name=zone.Name, Zone_Name=zone.Name,
Template_Thermostat_Name=static_thermostat.Name,) Template_Thermostat_Name=static_thermostat.Name, )
@staticmethod @staticmethod
def _matrix_to_list(points): def _matrix_to_list(points):
@ -93,7 +130,7 @@ class IdfHelper:
_points = IdfHelper._matrix_to_2d_list(building.foot_print.points) _points = IdfHelper._matrix_to_2d_list(building.foot_print.points)
self._idf.add_block(name=building.name, coordinates=_points, height=building.max_height, self._idf.add_block(name=building.name, coordinates=_points, height=building.max_height,
num_stories=int(building.storeys_above_ground)) num_stories=int(building.storeys_above_ground))
self.add_heating_system(building) self._add_heating_system(building)
self._idf.intersect_match() self._idf.intersect_match()
def add_surfaces(self, building): def add_surfaces(self, building):
@ -101,14 +138,16 @@ class IdfHelper:
for zone in building.thermal_zones: for zone in building.thermal_zones:
zone_name = f'Building {building.name} usage zone {index}' zone_name = f'Building {building.name} usage zone {index}'
self._idf.newidfobject('ZONE', Name=zone_name) self._idf.newidfobject('ZONE', Name=zone_name)
for surface in zone.surfaces: for boundary in zone.bounded:
idf_surface = self.idf_surfaces[surface.type] self._add_construction(boundary)
wall = self._idf.newidfobject(self._SURFACE, Name=f'{building.name}-{surface.name}', Surface_Type=idf_surface, idf_surface = self.idf_surfaces[boundary.surface.type]
Zone_Name=zone_name) wall = self._idf.newidfobject(self._SURFACE, Name=f'{building.name}-{boundary.surface.name}',
coordinates = IdfHelper._matrix_to_list(surface.points) Surface_Type=idf_surface, Zone_Name=zone_name,
Construction_Name=boundary.construction_name)
coordinates = IdfHelper._matrix_to_list(boundary.surface.points)
wall.setcoords(coordinates) wall.setcoords(coordinates)
index += 1 index += 1
self.add_heating_system(building) self._add_heating_system(building)
self._idf.intersect_match() self._idf.intersect_match()
def run(self, output_directory, window_ratio=0.35, display_render=False, output_prefix=None, keep_file=None): def run(self, output_directory, window_ratio=0.35, display_render=False, output_prefix=None, keep_file=None):
@ -117,12 +156,13 @@ class IdfHelper:
self._idf.translate_to_origin() self._idf.translate_to_origin()
if display_render: if display_render:
self._idf.view_model() self._idf.view_model()
# Run # Run
self._idf.newidfobject("OUTPUT:METER", Key_Name="Heating:DistrictHeating", Reporting_Frequency="hourly") self._idf.newidfobject("OUTPUT:METER", Key_Name="Heating:DistrictHeating", Reporting_Frequency="hourly")
self._idf.newidfobject("OUTPUT:METER", Key_Name="Cooling:DistrictCooling", Reporting_Frequency="hourly") self._idf.newidfobject("OUTPUT:METER", Key_Name="Cooling:DistrictCooling", Reporting_Frequency="hourly")
idf_path = None idf_path = None
if keep_file is not None: if keep_file is not None:
idf_path = (keep_file / 'in.idf').resolve() idf_path = (keep_file / 'in.idf').resolve()
print(str(idf_path))
self._idf.saveas(str(idf_path)) self._idf.saveas(str(idf_path))
if idf_path is None: if idf_path is None:
idf_path = (Path(__file__).parent / 'in.idf').resolve() idf_path = (Path(__file__).parent / 'in.idf').resolve()

View File

@ -39,23 +39,17 @@ class TestIdf(TestCase):
idd_file_path = (self._example_path / 'energy+.idd').resolve() idd_file_path = (self._example_path / 'energy+.idd').resolve()
idf_file_path = (self._example_path / 'minimal.idf').resolve() idf_file_path = (self._example_path / 'minimal.idf').resolve()
epw_file_path = (self._example_path / 'montreal.epw').resolve() epw_file_path = (self._example_path / 'montreal.epw').resolve()
_idf = IdfHelper(idf_file_path, idd_file_path, epw_file_path) idf = IdfHelper(idf_file_path, idd_file_path, epw_file_path)
city = self._get_city() city = self._get_city()
for building in city.buildings: for building in city.buildings:
_idf.add_block(building) idf.add_block(building)
for thermal_zone in building.thermal_zones:
for bound in thermal_zone.bounded:
for layer in bound.layers:
print(layer.thickness)
print(layer.material.density)
test_prefix = 'test_idf_blocks' test_prefix = 'test_idf_blocks'
_idf.run(self._output_path, output_prefix=test_prefix, keep_file=self._output_path, display_render=True) idf.run(self._output_path, output_prefix=test_prefix, keep_file=self._output_path)
eso_file_path = (self._output_path / f'{test_prefix}out.eso') eso_file_path = (self._output_path / f'{test_prefix}out.eso')
heating, cooling = _idf.read_eso(str(eso_file_path)) heating, cooling = idf.read_eso(str(eso_file_path))
self.assertEqual(len(heating), len(cooling), "Cooling and Heating doesn't contains the same amount of values") self.assertEqual(len(heating), len(cooling), "Cooling and Heating doesn't contains the same amount of values")
self.assertNotEqual(len(heating), 0, "Cooling and Heating series are empty") self.assertNotEqual(len(heating), 0, "Cooling and Heating series are empty")
file_list = glob.glob(Path(self._output_path / '*').resolve()) file_list = glob.glob(str(Path(self._output_path / '*').resolve()))
for file_path in file_list: for file_path in file_list:
os.remove(file_path) os.remove(file_path)
@ -64,15 +58,14 @@ class TestIdf(TestCase):
idf_file_path = (self._example_path / 'minimal.idf').resolve() idf_file_path = (self._example_path / 'minimal.idf').resolve()
epw_file_path = (self._example_path / 'montreal.epw').resolve() epw_file_path = (self._example_path / 'montreal.epw').resolve()
_idf = IdfHelper(idf_file_path, idd_file_path, epw_file_path) idf = IdfHelper(idf_file_path, idd_file_path, epw_file_path)
city = self._get_city() city = self._get_city()
for building in city.buildings: for building in city.buildings:
_idf.add_surfaces(building) idf.add_surfaces(building)
break
test_prefix = 'test_idf_blocks' test_prefix = 'test_idf_blocks'
_idf.run(self._output_path, output_prefix=test_prefix, keep_file=self._output_path) idf.run(self._output_path, output_prefix=test_prefix, keep_file=self._output_path)
eso_file_path = (self._output_path / f'{test_prefix}out.eso') eso_file_path = (self._output_path / f'{test_prefix}out.eso')
heating, cooling = _idf.read_eso(str(eso_file_path)) heating, cooling = idf.read_eso(str(eso_file_path))
self.assertEqual(len(heating), len(cooling), "Cooling and Heating doesn't contains the same amount of values") self.assertEqual(len(heating), len(cooling), "Cooling and Heating doesn't contains the same amount of values")
self.assertNotEqual(len(heating), 0, "Cooling and Heating series are empty") self.assertNotEqual(len(heating), 0, "Cooling and Heating series are empty")
file_list = glob.glob(str(Path(self._output_path / '*').resolve())) file_list = glob.glob(str(Path(self._output_path / '*').resolve()))