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

1import json 

2from pathlib import Path 

3 

4import pandas as pd 

5from geomeppy import IDF 

6 

7from bim2sim.elements.bps_elements import ThermalZone 

8from bim2sim.tasks.base import ITask 

9from bim2sim.utilities.common_functions import filter_elements 

10 

11 

12class IdfPostprocessing(ITask): 

13 """Idf Postprocessin task. 

14 

15 See run function for further details. """ 

16 reads = ('elements', 'idf', 'ifc_files', 'sim_results_path') 

17 

18 def run(self, elements: dict, idf: IDF, ifc_files: list, 

19 sim_results_path: Path): 

20 """EnergyPlus postprocessing for further evaluation and debugging. 

21 

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. 

25 

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 """ 

33 

34 self.logger.info("IDF Postprocessing started...") 

35 

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!") 

42 

43 

44 @staticmethod 

45 def write_zone_names(idf, elements, exportpath: Path): 

46 """ 

47 Write a dictionary of the bim2sim ThermalZone names and usages. 

48 

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. 

53 

54 Args: 

55 idf: eppy idf 

56 elements: bim2sim elements 

57 exportpath: base path to place the resulting zone_dict.json 

58 

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) 

79 

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] 

97 

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") 

110 

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 = [] 

128 

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 

148 

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 

175 

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") 

189 

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. 

193 

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 != ''] 

249 

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")