Coverage for bim2sim/plugins/PluginEnergyPlus/bim2sim_energyplus/task/ep_idf_postprocessing.py: 0%
80 statements
« prev ^ index » next coverage.py v7.10.7, created at 2025-10-01 10:24 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2025-10-01 10:24 +0000
1import json
2from pathlib import Path
4import pandas as pd
5from geomeppy import IDF
7from bim2sim.elements.bps_elements import ThermalZone
8from bim2sim.tasks.base import ITask
9from bim2sim.utilities.common_functions import filter_elements
12class IdfPostprocessing(ITask):
13 """Idf Postprocessin task.
15 See run function for further details. """
16 reads = ('elements', 'idf', 'ifc_files', 'sim_results_path')
18 def run(self, elements: dict, idf: IDF, ifc_files: list,
19 sim_results_path: Path):
20 """EnergyPlus postprocessing for further evaluation and debugging.
22 This task holds export functions for further evaluation and
23 debugging. Information on spaces and space boundaries are exported
24 and the zone names are exported in json format.
26 Args:
27 elements (dict): dictionary in the format dict[guid: element],
28 holds preprocessed elements including space boundaries.
29 idf (IDF): eppy idf, EnergyPlus input file.
30 ifc_files (list): list of ifc files used in this project
31 sim_results_path (Path): path to simulation results.
32 """
34 self.logger.info("IDF Postprocessing started...")
36 self._export_surface_areas(elements, idf)
37 self._export_space_info(elements, idf)
38 self._export_boundary_report(elements, idf, ifc_files)
39 self.write_zone_names(idf, elements,
40 sim_results_path / self.prj_name)
41 self.logger.info("IDF Postprocessing finished!")
44 @staticmethod
45 def write_zone_names(idf, elements, exportpath: Path):
46 """
47 Write a dictionary of the bim2sim ThermalZone names and usages.
49 This method creates a dict and exports it to a json file (
50 zone_dict.json) to the path defined in exportpath. This dict
51 includes the zone name and the selected usage within bim2sim. All
52 zones are considered that are created within the bim2sim elements.
54 Args:
55 idf: eppy idf
56 elements: bim2sim elements
57 exportpath: base path to place the resulting zone_dict.json
59 """
60 zones = idf.idfobjects['ZONE']
61 zone_dict = {}
62 ifc_zones = filter_elements(elements, ThermalZone)
63 zone_dict_ifc_names = {}
64 for zone in zones:
65 usage = [z.usage for z in ifc_zones if z.guid == zone.Name]
66 zone_dict.update({zone.Name: usage[0]})
67 zone_dict_ifc_names.update({
68 zone.Name: {
69 'ZoneUsage': usage[0],
70 'Name': elements[zone.Name].ifc.Name,
71 'LongName': elements[zone.Name].ifc.LongName,
72 'StoreyName': elements[zone.Name].storeys[0].ifc.Name,
73 'StoreyElevation': elements[zone.Name].storeys[
74 0].ifc.Elevation}})
75 with open(exportpath / 'zone_dict.json', 'w+') as file:
76 json.dump(zone_dict, file, indent=4)
77 with open(exportpath / 'zone_dict_ifc_names.json', 'w+') as file:
78 json.dump(zone_dict_ifc_names, file, indent=4)
80 def _export_surface_areas(self, elements, idf):
81 """ combines sets of area sums and exports to csv """
82 area_df = pd.DataFrame(
83 columns=["granularity", "ID", "long_name", "out_bound_cond",
84 "area_wall", "area_ceiling", "area_floor",
85 "area_roof", "area_window", "area_door",
86 "total_surface_area", "total_opening_area"])
87 surf = [s for s in idf.idfobjects['BuildingSurface:Detailed'.upper()]
88 if s.Construction_Name != 'Air Wall']
89 glazing = [g for g in idf.idfobjects[
90 'FenestrationSurface:Detailed'.upper()]]
91 area_df = self._append_set_of_area_sum(area_df, granularity="GLOBAL",
92 guid="GLOBAL",
93 long_name="GLOBAL",
94 surface=surf, glazing=glazing)
95 zones = [z for z in idf.idfobjects['zone'.upper()]]
96 zone_names = [z.Name for z in zones]
98 for z_name in zone_names:
99 surf_zone = [s for s in surf if s.Zone_Name == z_name]
100 surf_names = [s.Name for s in surf_zone]
101 long_name = elements[z_name].ifc.LongName
102 glazing_zone = [g for g in glazing for s_name in surf_names if
103 g.Building_Surface_Name == s_name]
104 area_df = self._append_set_of_area_sum(area_df, granularity="ZONE",
105 guid=z_name,
106 long_name=long_name,
107 surface=surf_zone,
108 glazing=glazing_zone)
109 area_df.to_csv(path_or_buf=str(self.paths.export) + "/area.csv")
111 def _append_set_of_area_sum(self, area_df, granularity, guid, long_name,
112 surface, glazing):
113 """ generate set of area sums for a given granularity for outdoor,
114 surface and adiabatic boundary conditions.
115 Appends set to a given dataframe.
116 """
117 surf_outdoors = [s for s in surface if s.Outside_Boundary_Condition ==
118 "Outdoors" or s.Outside_Boundary_Condition == "Ground"]
119 surf_surface = [s for s in surface if s.Outside_Boundary_Condition ==
120 "Surface"]
121 surf_adiabatic = [s for s in surface if s.Outside_Boundary_Condition ==
122 "Adiabatic"]
123 glazing_outdoors = [g for g in glazing if
124 g.Outside_Boundary_Condition_Object == ""]
125 glazing_surface = [g for g in glazing if
126 g.Outside_Boundary_Condition_Object != ""]
127 glazing_adiabatic = []
129 area_df = pd.concat([area_df, pd.DataFrame.from_records([
130 self._sum_of_surface_area(
131 granularity=granularity, guid=guid, long_name=long_name,
132 out_bound_cond="ALL", surface=surface, glazing=glazing),
133 self._sum_of_surface_area(
134 granularity=granularity, guid=guid, long_name=long_name,
135 out_bound_cond="Outdoors",surface=surf_outdoors,
136 glazing=glazing_outdoors),
137 self._sum_of_surface_area(
138 granularity=granularity, guid=guid, long_name=long_name,
139 out_bound_cond="Surface", surface=surf_surface,
140 glazing=glazing_surface),
141 self._sum_of_surface_area(
142 granularity=granularity, guid=guid, long_name=long_name,
143 out_bound_cond="Adiabatic", surface=surf_adiabatic,
144 glazing=glazing_adiabatic)
145 ])],
146 ignore_index=True)
147 return area_df
149 @staticmethod
150 def _sum_of_surface_area(granularity, guid, long_name, out_bound_cond,
151 surface, glazing):
152 """ generate row with sum of surface and opening areas to be appended
153 to larger dataframe"""
154 row = {
155 "granularity": granularity,
156 "ID": guid,
157 "long_name": long_name,
158 "out_bound_cond": out_bound_cond,
159 "area_wall": sum(s.area for s in surface if s.Surface_Type ==
160 "Wall"),
161 "area_ceiling": sum(s.area for s in surface if s.Surface_Type ==
162 "Ceiling"),
163 "area_floor": sum(s.area for s in surface if s.Surface_Type ==
164 "Floor"),
165 "area_roof": sum(s.area for s in surface if s.Surface_Type ==
166 "Roof"),
167 "area_window": sum(g.area for g in glazing if g.Surface_Type ==
168 "Window"),
169 "area_door": sum(g.area for g in glazing if g.Surface_Type ==
170 "Door"),
171 "total_surface_area": sum(s.area for s in surface),
172 "total_opening_area": sum(g.area for g in glazing)
173 }
174 return row
176 def _export_space_info(self, elements, idf):
177 space_df = pd.DataFrame(
178 columns=["ID", "long_name", "space_center", "space_volume"])
179 spaces = filter_elements(elements, 'ThermalZone')
180 for space in spaces:
181 space_df = pd.concat([space_df, pd.DataFrame.from_records([{
182 "ID": space.guid,
183 "long_name": space.ifc.LongName,
184 "space_center": space.space_center.XYZ().Coord(),
185 "space_volume": space.space_shape_volume.m
186 }])],
187 ignore_index=True)
188 space_df.to_csv(path_or_buf=str(self.paths.export) + "/space.csv")
190 def _export_boundary_report(self, elements, idf, ifc_files):
191 """Export a report on the number of space boundaries.
192 Creates a report as a DataFrame and exports it to csv.
194 Columns:
195 IFC_SB_all: Number of IfcRelSpaceBoundary elements included in
196 the given IFC files,
197 IFC_SB_2a: Number of IfcRelSpaceBoundary elements included in
198 the given IFC files of type 2a,
199 IFC_SB_2b: Number of IfcRelSpaceBoundary elements included in
200 the given IFC files of type 2b,
201 BIM2SIM_SB_2b: Number of SpaceBoundary elements created within
202 the bim2sim tool generated from gaps within the IfcSpaces,
203 IDF_all: Total number of FENESTRATIONSURFACE:DETAILED and
204 BUILDINGSURFACE:DETAILED elements in the resulting IDF,
205 IDF_all_B: Total number of BUILDINGSURFACE:DETAILED elements in
206 the resulting IDF,
207 IDF_ADB: Number of BUILDINGSURFACE:DETAILED elements with
208 adiabatic boundary conditions,
209 IDF_SFB: Number of BUILDINGSURFACE:DETAILED elements with
210 "surface" boundary conditions,
211 IDF_ODB: Number of BUILDINGSURFACE:DETAILED elements with
212 outdoor boundary conditions,
213 IDF_GDB: Number of BUILDINGSURFACE:DETAILED elements with
214 ground boundary conditions,
215 IDF_VTB: Number of BUILDINGSURFACE:DETAILED elements with
216 an air wall construction,
217 IDF_all_F: Total number of FENESTRATIONSURFACE:DETAILED elements in
218 the resulting IDF,
219 IDF_ODF: Number of FENESTRATIONSURFACE:DETAILED elements in
220 the resulting IDF without outside boundary condition object,
221 IDF_INF: Total number of FENESTRATIONSURFACE:DETAILED elements in
222 the resulting IDF with an outside boundary condition object.
223 """
224 bound_count = pd.DataFrame(
225 columns=["IFC_SB_all", "IFC_SB_2a", "IFC_SB_2b", "BIM2SIM_SB_2b",
226 "IDF_all", "IDF_all_B", "IDF_ADB", "IDF_SFB",
227 "IDF_ODB", "IDF_GDB", "IDF_VTB", "IDF_all_F",
228 "IDF_ODF", "IDF_INF"])
229 ifc_bounds = []
230 for ifc in ifc_files:
231 ifc_bounds.extend(ifc.file.by_type('IfcRelSpaceBoundary'))
232 bounds_2b = filter_elements(elements, 'SpaceBoundary2B')
233 idf_all_b = [s for s in idf.idfobjects["BUILDINGSURFACE:DETAILED"]]
234 idf_adb = [s for s in idf.idfobjects["BUILDINGSURFACE:DETAILED"]
235 if s.Outside_Boundary_Condition == "Adiabatic"]
236 idf_sfb = [s for s in idf.idfobjects["BUILDINGSURFACE:DETAILED"]
237 if s.Outside_Boundary_Condition == "Surface"]
238 idf_odb = [s for s in idf.idfobjects["BUILDINGSURFACE:DETAILED"]
239 if s.Outside_Boundary_Condition == "Outdoors"]
240 idf_gdb = [s for s in idf.idfobjects["BUILDINGSURFACE:DETAILED"]
241 if s.Outside_Boundary_Condition == "Ground"]
242 idf_vtb = [s for s in idf.idfobjects["BUILDINGSURFACE:DETAILED"]
243 if s.Construction_Name == "Air Wall"]
244 idf_all_f = [f for f in idf.idfobjects["FENESTRATIONSURFACE:DETAILED"]]
245 idf_odf = [f for f in idf.idfobjects["FENESTRATIONSURFACE:DETAILED"] if
246 f.Outside_Boundary_Condition_Object == '']
247 idf_inf = [f for f in idf.idfobjects["FENESTRATIONSURFACE:DETAILED"] if
248 f.Outside_Boundary_Condition_Object != '']
250 bound_count = pd.concat([bound_count, pd.DataFrame.from_records([{
251 "IFC_SB_all": len(ifc_bounds),
252 "IFC_SB_2a": len([b for b in ifc_bounds if b.Description ==
253 "2a"]),
254 "IFC_SB_2b": len([b for b in ifc_bounds if b.Description == "2b"]),
255 "BIM2SIM_SB_2b": len(bounds_2b),
256 "IDF_all": len(idf_all_b) + len(idf_all_f),
257 "IDF_all_B": len(idf_all_b),
258 "IDF_ADB": len(idf_adb),
259 "IDF_SFB": len(idf_sfb),
260 "IDF_ODB": len(idf_odb),
261 "IDF_GDB": len(idf_gdb),
262 "IDF_VTB": len(idf_vtb),
263 "IDF_all_F": len(idf_all_f),
264 "IDF_ODF": len(idf_odf),
265 "IDF_INF": len(idf_inf)
266 }])],
267 ignore_index=True)
268 bound_count.to_csv(
269 path_or_buf=str(self.paths.export) + "/bound_count.csv")