Coverage for bim2sim/plugins/PluginComfort/bim2sim_comfort/task/ep_comfort_visualization.py: 0%

142 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-03-12 17:09 +0000

1"""Visualize EnergyPlus Comfort Results. 

2 

3This module includes all functions for visualization of Comfort 

4Results. Geometric preprocessing (includes EnergyPlus specific space 

5boundary enrichment), EnergyPlus Input File export and EnergyPlus simulation 

6must be executed before this module. 

7""" 

8import json 

9import logging 

10import os 

11 

12import pandas as pd 

13from pathlib import Path 

14 

15from matplotlib.colors import ListedColormap, Normalize 

16 

17import bim2sim 

18from bim2sim import run_project, ConsoleDecisionHandler, Project 

19 

20from OCC.Core.Quantity import Quantity_Color, Quantity_TOC_RGB 

21 

22from bim2sim.elements.bps_elements import ThermalZone 

23from bim2sim.plugins.PluginComfort.bim2sim_comfort.task.ep_load_idf import \ 

24 LoadIdf 

25from bim2sim.plugins.PluginEnergyPlus.bim2sim_energyplus.utils import \ 

26 PostprocessingUtils 

27from bim2sim.tasks import common 

28from bim2sim.tasks.base import ITask 

29from bim2sim.utilities.common_functions import filter_elements 

30import numpy as np 

31import OCC.Display.SimpleGui 

32from matplotlib import cm, pyplot as plt 

33 

34import matplotlib as mpl 

35mpl.use('Agg') 

36 

37PLOT_PATH = Path(r'C:\Users\richter\sciebo\03-Paperdrafts' 

38 r'\MDPI_SpecialIssue_Comfort_Climate\img' 

39 r'\generated_plots') 

40INCH = 2.54 

41logger = logging.getLogger(__name__) 

42 

43 

44class ComfortVisualization(ITask): 

45 """Visualize Comfort Results of an EnergyPlus Simulation. 

46 

47 Task to Visualize EnergyPlus Comfort Measures. 

48 """ 

49 

50 reads = ('elements',) 

51 

52 def __init__(self, playground): 

53 super().__init__(playground) 

54 self.idf = None 

55 

56 def run(self, elements=None): 

57 """Execute all methods to visualize comfort results.""" 

58 logger.info("Visualization of Comfort Results started ...") 

59 df_ep_res = pd.read_csv(self.paths.export / 'EP-results/eplusout.csv') 

60 # convert to date time index 

61 df_ep_res["Date/Time"] = df_ep_res["Date/Time"].apply( 

62 PostprocessingUtils._string_to_datetime) 

63 op_temp_cols = [col for col in df_ep_res.columns if 

64 'Operative Temperature' 

65 in col] 

66 op_temp_df = df_ep_res[op_temp_cols].round(2) 

67 mean_temp_df = df_ep_res[[col for col in df_ep_res.columns 

68 if 'Mean Air Temperature' in col]] 

69 pmv_temp_df = df_ep_res[[col for col in df_ep_res.columns 

70 if 'Fanger Model PMV' in col]] 

71 ppd_temp_df = df_ep_res[[col for col in df_ep_res.columns 

72 if 'Fanger Model PPD' in col]] 

73 pmv_temp_df = pmv_temp_df.set_index(df_ep_res['Date/Time']) 

74 

75 for col in pmv_temp_df.columns: 

76 self.visualize_calendar(pd.DataFrame(pmv_temp_df[col])) 

77 # fig = plt.figure(figsize=(10/INCH,10/INCH)) 

78 # for i in range(len(pmv_temp_df.columns)): 

79 # plt.scatter(df_ep_res[df_ep_res.columns[1]], df_ep_res[ 

80 # pmv_temp_df.columns[i]], marker='.', s=(72./fig.dpi), 

81 # label=pmv_temp_df.columns[i]) 

82 # plt.legend() 

83 # plt.draw() 

84 # plt.close() 

85 spaces = filter_elements(elements, ThermalZone) 

86 space_shapes = [shp.space_shape for shp in spaces] 

87 # VisualizationUtils.display_occ_shapes(space_shapes) 

88 self.visualize_comfort(spaces, pmv_temp_df.mean()) 

89 self.visualize_comfort(spaces, ppd_temp_df.mean()) 

90 self.visualize_comfort(spaces, op_temp_df.mean()) 

91 self.visualize_comfort(spaces, mean_temp_df.mean()) 

92 

93 @staticmethod 

94 def visualize_comfort(spaces, mean_temp_df): 

95 # Create a list to store the spaces and their corresponding temperature 

96 # values 

97 spaces_and_temps = [] 

98 

99 # Iterate over the spaces 

100 for space in spaces: 

101 # Get the name of the space 

102 name = space.guid 

103 # If the space has a name and a corresponding temperature value, 

104 # add it to the list 

105 if any(name.upper() in k.upper() for k in mean_temp_df.keys()): 

106 key = [k for k in mean_temp_df.keys() if name.upper() in 

107 k.upper()] 

108 spaces_and_temps.append((space.space_shape, mean_temp_df[ 

109 key].values.sum(), space.space_center)) 

110 

111 # Create a display window 

112 display, start_display, add_menu, add_function_to_menu = \ 

113 OCC.Display.SimpleGui.init_display() 

114 # Create the color map 

115 cmap = cm.get_cmap('viridis') 

116 legend_max_temp = max([float(temp) for space, temp, center 

117 in spaces_and_temps]) 

118 legend_min_temp = min([float(temp) for space, temp, center 

119 in spaces_and_temps]) 

120 

121 

122 # Assign the colors to the objects 

123 # Iterate over the spaces and temperature values and add them to the 

124 # display 

125 for space, temp, center in spaces_and_temps: 

126 # Set the color of the space based on the temperature value 

127 # Normalize the values to the range of 0 to 1 

128 normalized_value = (float(temp) - legend_min_temp) / ( 

129 legend_max_temp - legend_min_temp) 

130 color = cmap(normalized_value) 

131 color = Quantity_Color(*color[:-1], 

132 Quantity_TOC_RGB) 

133 display.DisplayShape(space, color=color, transparency=0.7) 

134 # display the rounded temperature value at the space center 

135 display.DisplayMessage(center, str(np.round(temp, 3)), 

136 message_color=(0., 0., 0.)) 

137 

138 display.FitAll() 

139 start_display() 

140 

141 # # Add a legend for the temperature values 

142 # legend_num_steps = 10 

143 # legend_step_size = (legend_max_temp - legend_min_temp) / legend_num_steps 

144 # legend_x_pos = 0.1 

145 # legend_y_pos = 0.9 

146 # legend_box_width = 0.01 

147 # legend_box_height = 0.1 

148 # for i in range(legend_num_steps + 1): 

149 # temp = legend_min_temp + i * legend_step_size 

150 # color = (1.0, (100 - temp) / 100.0, (100 - temp) / 100.0) 

151 # display.DisplayShape( 

152 # OCC.Core.BRepPrimAPI.BRepPrimAPI_MakeBox( 

153 # gp_Pnt(legend_x_pos, legend_y_pos, 0.0), gp_Pnt( 

154 # legend_x_pos+legend_box_width, legend_y_pos+legend_box_height, 

155 # 0.0), color=color)) 

156 # display.FitAll() 

157 # start_display() 

158 

159 @staticmethod 

160 def visualize_calendar(df, year='', color_only=False, save_as='', 

161 construction='', skip_legend=False, 

162 add_title=False): 

163 def visualize(): 

164 plt.rcParams.update(mpl.rcParamsDefault) 

165 plt.rcParams.update({ 

166 "lines.linewidth": 0.4, 

167 "font.family": "serif", # use serif/main font for text elements 

168 "text.usetex": True, # use inline math for ticks 

169 "pgf.rcfonts": True, # don't setup fonts from rc parameters 

170 "font.size": 8 

171 }) 

172 

173 fig, ax = plt.subplots(figsize=(7.6/INCH, 8/INCH)) 

174 daily_mean = df.resample('D').mean() 

175 calendar_heatmap(ax, daily_mean, color_only) 

176 if add_title: 

177 plt.title(str(year) + ' - ' + df.columns[0]) 

178 if save_as: 

179 plt.savefig(PLOT_PATH / str(construction + save_as + 

180 df.columns[0] + '.pdf'), 

181 bbox_inches='tight') 

182 if skip_legend: 

183 plt.savefig(PLOT_PATH / 'subplots' / str(construction + 

184 save_as + df.columns[0] 

185 + '.pdf'), 

186 bbox_inches='tight') 

187 plt.draw() 

188 plt.close() 

189 

190 def calendar_array(dates, data): 

191 i, j = zip(*[(d.day, d.month) for d in dates]) 

192 i = np.array(i) - min(i) 

193 j = np.array(j) - 1 

194 ni = max(i) + 1 

195 calendar = np.empty([ni, 12])#, dtype='S10') 

196 calendar[:] = np.nan 

197 calendar[i, j] = data 

198 return i, j, calendar 

199 

200 def calendar_heatmap(ax, df, color_only): 

201 

202 color_schema = ['#0232c2', '#028cc2', '#03ffff', 

203 '#02c248', '#bbc202', '#c27f02'] 

204 # Labels and their corresponding indices 

205 labels = ['-3 to -2', '-2 to -1', '-1 to 0', 

206 '0 to 1', '1 to 2', '2 to 3'] 

207 label_indices = np.arange(len(labels)+1) - 3 

208 

209 # Create a ListedColormap from the color schema 

210 cmap = ListedColormap(color_schema) 

211 df_dates = df.index 

212 df_data = df[df.columns[0]].values 

213 norm = Normalize(vmin=-3, vmax=3) 

214 

215 i, j, calendar = calendar_array(df_dates, df_data) 

216 

217 im = ax.imshow(calendar, aspect='auto', interpolation='none', 

218 cmap=cmap, norm=norm) 

219 label_days(ax, df_dates, i, j, calendar) 

220 if not color_only: 

221 label_data(ax, calendar) 

222 label_months(ax, df_dates, i, j, calendar) 

223 if not skip_legend: 

224 cbar = ax.figure.colorbar(im, ticks=label_indices) 

225 # Minor ticks 

226 ax.set_xticks(np.arange(-.5, len(calendar[0]), 1), minor=True) 

227 ax.set_yticks(np.arange(-.5, len(calendar[:,0]), 1), minor=True) 

228 

229 # Gridlines based on minor ticks 

230 ax.grid(which='minor', color='w', linestyle='-', linewidth=0.5) 

231 

232 # Remove minor ticks 

233 ax.tick_params(which='minor', bottom=False, left=False) # ax.get_yaxis().set_ticks(label_indices) 

234 # ax.get_yaxis().set_ticklabels(labels) 

235 

236 def label_data(ax, calendar): 

237 for (i, j), data in np.ndenumerate(calendar): 

238 if type(data) == str: 

239 ax.text(j, i, data, ha='center', va='center') 

240 elif np.isfinite(data): 

241 ax.text(j, i, round(data,1), ha='center', va='center') 

242 

243 def label_days(ax, dates, i, j, calendar): 

244 ni, nj = calendar.shape 

245 day_of_month = np.nan * np.zeros((ni, nj)) 

246 day_of_month[i, j] = [d.day for d in dates] 

247 

248 yticks = np.arange(31) 

249 yticklabels = [i+1 for i in yticks] 

250 ax.set_yticks(yticks) 

251 ax.set_yticklabels(yticklabels, fontsize=6) 

252 # ax.set(yticks=yticks, 

253 # yticklabels=yticklabels) 

254 

255 

256 def label_months(ax, dates, i, j, calendar): 

257 month_labels = np.array(['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 

258 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']) 

259 months = np.array([d.month for d in dates]) 

260 uniq_months = sorted(set(months)) 

261 # xticks = [i[months == m].mean() for m in uniq_months] 

262 xticks = [i-1 for i in uniq_months] 

263 labels = [month_labels[m - 1] for m in uniq_months] 

264 ax.set(xticks=xticks) 

265 ax.set_xticklabels(labels, rotation=90) 

266 ax.xaxis.tick_top() 

267 visualize() 

268 

269 

270if __name__ == "__main__": 

271 """Execute comfort visualization on existing project. Loads existing  

272 .ifc and .idf files for visualization purposes. """ 

273 

274 # set path to existing bim2sim project directory 

275 project_path = Path(f'E:/bim2sim_temp/bim2sim_comfort/') 

276 

277 # existing ifc_file is loaded from project directory (ifc_path=None) 

278 project = Project.create(project_folder=project_path, ifc_path=None, 

279 plugin='comfort') 

280 # set EnergyPlus installation path (version should match idf version) 

281 project.sim_settings.ep_install_path = f'C:/EnergyPlusV9-4-0/' 

282 # set tasks for comfort visualization (loads ifc and idf from project) 

283 project.default_plugin.default_tasks = [common.LoadIFC, 

284 common.CreateElementsOnIfcTypes, 

285 LoadIdf, 

286 ComfortVisualization] 

287 # run project based on new default tasks. 

288 run_project(project, ConsoleDecisionHandler())