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
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-12 17:09 +0000
1"""Visualize EnergyPlus Comfort Results.
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
12import pandas as pd
13from pathlib import Path
15from matplotlib.colors import ListedColormap, Normalize
17import bim2sim
18from bim2sim import run_project, ConsoleDecisionHandler, Project
20from OCC.Core.Quantity import Quantity_Color, Quantity_TOC_RGB
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
34import matplotlib as mpl
35mpl.use('Agg')
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__)
44class ComfortVisualization(ITask):
45 """Visualize Comfort Results of an EnergyPlus Simulation.
47 Task to Visualize EnergyPlus Comfort Measures.
48 """
50 reads = ('elements',)
52 def __init__(self, playground):
53 super().__init__(playground)
54 self.idf = None
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'])
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())
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 = []
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))
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])
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.))
138 display.FitAll()
139 start_display()
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()
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 })
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()
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
200 def calendar_heatmap(ax, df, color_only):
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
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)
215 i, j, calendar = calendar_array(df_dates, df_data)
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)
229 # Gridlines based on minor ticks
230 ax.grid(which='minor', color='w', linestyle='-', linewidth=0.5)
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)
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')
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]
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)
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()
270if __name__ == "__main__":
271 """Execute comfort visualization on existing project. Loads existing
272 .ifc and .idf files for visualization purposes. """
274 # set path to existing bim2sim project directory
275 project_path = Path(f'E:/bim2sim_temp/bim2sim_comfort/')
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())